Acquire a Register Model
寄存器模型一般可以使用工具生成或者从头开始编写,寄存器模型示例如下:
Filename regmodel.sv
class dummy_reg extends uvm_reg;
`uvm_object_utils(dummy_reg)
rand uvm_reg_field F;
...
virtual function void build();
F = uvm_reg_field::create("F");
F.configure(this, 8, 0, "RW", 1, 8'h00, 1, 1, 1);
endfunction
endclass
class bus_reg_block extends uvm_reg_block;
`uvm_object_utils(bus_reg_block)
rand dummy_reg reg0;
uvm_reg_map bus_map;
...
virtual function void build();
reg0 = dummy_reg::create("reg0");
reg0.configure(this);
reg0.build();
bus_map = create_map("bus_map", 'h0, 1, UVM_LITTLE_ENDIAN);
default_map = bus_map;
bus_map.add_reg(reg0, 'h0, "RW");
lock_model();
endfunction
endclass
class top_reg_block extends uvm_reg_block;
`uvm_object_utils(top_reg_block)
bus_reg_block bus;
uvm_reg_map bus_map;
...
virtual function void build();
bus = bus_reg_block::create("bus");
bus.configure(this);
bus.build();
bus_map = create_map("bus_map", 'h0, 1, UVM_LITTLE_ENDIAN);
default_map = bus_map;
bus_map.add_submap(bus.bus_map, 'h0);
lock_model();
endfunction
endclass
Add Register Access to the Interface Template File
我们从interface template file的主要部分开始,其中指定了事务和接口中的变量。为了使示例简单,我们省略monitor和focv,只包含driver。
Filename bus.tpl agent_name = bus trans_item = bus_tx trans_var = rand bit cmd; trans_var = rand byte addr; trans_var = rand byte data; driver_inc = bus_do_drive.sv if_port = logic clk; if_port = bit cmd; if_port = byte addr; if_port = byte data; if_clock = clk
现在,我们通过指定register layer具有的访问类型以及寄存器层和agent之间的映射来扩展interface template file。这是通过标识register layer用于读取和写入 DUT 中的寄存器的command, address和data:
reg_access_mode = WR reg_access_block_type = bus_reg_block uvm_reg_kind = cmd uvm_reg_addr = addr uvm_reg_data = data
reg_access_mode指定是否允许register layer对寄存器进行写/读(WR)、只写 (WO) 或只读 (RO) 访问。
reg_access_block_type在寄存器模型文件中指定 uvm_reg_block 类型,该文件包含要读取或写入的寄存器。
Add Register Access to the Common Template File
top-level register model必须在 common template file中向uvm代码生成器描述:
Filename common.tpl regmodel_file = regmodel.sv top_reg_block_type = top_reg_block
top_reg_block_type 参数必须为top-level register model的类名。
Generate and Run
uvm代码生成器将创建以下结构:
top_tb (module)
↳ top_th (module instance)
↳ bus_if (interface instance)
mydut (module instance)
↳ top_test (object, class uvm_test)
↳ top_config (created in build_phase, class uvm_object)
top_env (uvm_env)
↳ bus_env_config (uvm_object)
top_reg_block (uvm_reg_block)
bus_env (uvm_env)
↳ bus_config (uvm_object)
bus_reg_block (uvm_reg_block)
reg2bus_adapter
uvm_reg_predictor
bus_agent (uvm_agent)
↳ bus_sequencer
bus_driver (uvm_driver)
bus_monitor (uvm_monitor)
bus_coverage (uvm_subscriber)
bus_env_coverage (uvm_subscriber)
↳ top_default_seq (created in run_phase, class uvm_sequence)
↳ bus_env_default_seq (uvm_sequence)
↳ registers.update()
实例化register model时,使用register model的每个agent都在其自己的 env 中实例化。在上面的结构中,可以看到:
top_test
instantiates top_env
instantiates bus_env
instantiates bus_agent
top_env具有对top-level register block top_reg_block的引用,register model就是在这个层次实例化的。
bus_env引用该agent的register model bus_reg_block,并实例化adapter和predictor,该adapter和predictor将该register model连接到agent。
uvm代码生成器在使用register model的default sequence 中向相应register model中的每个寄存器写入一个随机值。
Filename bus_env_seq_lib.sv
task bus_env_default_seq::body();
super.body();
`uvm_info(get_type_name(), "default sequence starting", UVM_HIGH)
regmodel.get_registers(data_regs);
data_regs.shuffle();
foreach(data_regs[i])
begin
// Randomize register content and then update
if(!data_regs[i].randomize())
`uvm_error(get_type_name(), $sformatf("Randomization error for data_regs[%0d]", i))
data_regs[i].update(status, .path(UVM_FRONTDOOR), .parent(this));
end
`uvm_info(get_type_name(), "default sequence completed", UVM_HIGH)
endtask : body
添加用户定义的寄存器sequence
与前面的文章一样,可以通过扩展uvm代码生成器创建的default sequence来创建自己的sequence。这次它是一个专门的register sequence:
Filename bus_env_reg_seq.sv
class bus_env_reg_seq extends bus_env_default_seq;
`uvm_object_utils(bus_env_reg_seq)
...
task body();
regmodel.reg0.write(status, .value('hab), .parent(this));
assert(status == UVM_IS_OK);
regmodel.reg0.write(status, .value('hcd), .parent(this));
assert(status == UVM_IS_OK);
regmodel.reg0.write(status, .value('hef), .parent(this));
assert(status == UVM_IS_OK);
regmodel.reg0.read(status, .value(data), .parent(this));
assert(status == UVM_IS_OK);
endtask: body
endclass : bus_env_reg_seq
同样需要在 interface template file添加 factory override:
Filename bus.tpl ... reg_seq_inc = bus_env_reg_seq.sv agent_factory_set = bus_env_default_seq bus_env_reg_seq
然后可以对生成的现成代码运行仿真,应该看到仿真日志中包含以下打印信息:
# @10000 mydut bus_cmd = W, bus_addr = 00, bus_data = ab # @30000 mydut bus_cmd = W, bus_addr = 00, bus_data = cd # @50000 mydut bus_cmd = W, bus_addr = 00, bus_data = ef # @70000 mydut bus_cmd = R, bus_addr = 00, bus_data = 00
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !