如何在生成的代码中使用UVM Register Layer?

描述

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

 





审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分