uvm_reg_file的作用

1.1 uvm_reg_file的作用

uvm_reg_field, uvm_reg与uvm_reg_block三者的组合已经能够组成一个可以使用的寄存器模型; uvm_reg_file的引入主要用于区分不同的hdl路径;

1.2 uvm_reg_file特点 

(1) uvm_reg_file是一个纯虚类,不能直接使用,而必须使用其派生类;

(2) reg files可以包含任意数量的uvm_regs以及其他reg files;uvm_reg_file 是uvm_regs和 其他reg files的集合,用于创建规则的重复结构。

(3) uvm_reg_file在uvm_reg_block或其他uvm_reg_file的build函数内例化;

1.3 uvm_reg_file源码

virtual class uvm_reg_file extends uvm_object;

   local uvm_reg_block     parent;//parent表示此uvm_reg_file所在的uun_ reg_ block;
   local uvm_reg_file   m_rf;//m_rf示此uvm_reg_ file所在的uvn_reg_File
   local string            default_hdl_path = "RTL";//hdl_paths_poo1用于记录路径信息:
   local uvm_object_string_pool #(uvm_queue #(string)) hdl_paths_pool;
   extern function                  new        (string name="");
   extern function void     configure  (uvm_reg_block blk_parent,
                                        uvm_reg_file regfile_parent,
                                        string hdl_path = "");
   extern virtual function string        get_full_name();
   extern virtual function uvm_reg_block get_parent ();
   extern virtual function uvm_reg_block get_block  ();
   extern virtual function uvm_reg_file  get_regfile     ();
   extern function void clear_hdl_path    (string kind = "RTL");
   extern function void add_hdl_path      (string path, string kind = "RTL");
   extern function bit  has_hdl_path      (string kind = "");
   extern function void get_hdl_path      (ref string paths[$], input string kind = "");
...
endclass

1.4 uvm_reg_file的使用

1.4.1 使用方法一(参考uvm_user_guide)

(1) 实现一个虚的build函数;

(2) 实现一个虚的map函数;

class reg_slave_SESSION extends uvm_reg_file;
   rand reg_slave_SOCKET SRC; //是一个uvm_reg型的类,class reg_slave_SOCKET extends uvm_reg;
   rand reg_slave_SOCKET DST;
   function new(string name = "slave_SESSION");
      super.new(name);
   endfunction: new

   virtual function void build();
      this.SRC = reg_slave_SOCKET::type_id::create("SRC");
      this.DST = reg_slave_SOCKET::type_id::create("DST");

      this.SRC.configure(get_block(), this, "SRC");
      this.DST.configure(get_block(), this, "DST");

      this.SRC.build();
      this.DST.build();
   endfunction

   virtual function void map(uvm_reg_map    mp,
                             uvm_reg_addr_t offset);
      mp.add_reg(SRC, offset+'h00);
      mp.add_reg(DST, offset+'h08);
   endfunction
   
   virtual function void set_offset(uvm_reg_map    mp,
                               uvm_reg_addr_t offset);
      SRC.set_offset(mp, offset+'h00);
      DST.set_offset(mp, offset+'h08);
   endfunction
   
   `uvm_object_utils(reg_slave_SESSION)
   
endclass

1.4.2 使用方法二之区分hdl路径

一些环境中只是把一些寄存器加入到uvm_reg_block中,然后在最后的env中例化此reg_block。在现实应用中,一般的会把个uvm_reg_block再加入到一个uvm_reg_block中上,然后在env中例化后者。从逻辑关系上看,呈现出的是两级的register model,如图所示:

 

 

 层次化的register model

一般的,只会在第一级的uvm_reg_block中加入寄存器,而第二级的uvm_reg_block通常只添加uvm_reg_block。这样从整体上来说呈现出一个比较清晰的结构。

假如一个DUT分了三个子模块:用于缓存数据的buf模块,用于接收发送以太网帧的mac模块,buf部分的寄存器地址为0x1000~0x1FFF,mac部分的为0x2000~0x2FFF,另外全局寄存器地址为0x0000~0x0FFF.

那么可以如下定义register model:

class global_blk extends uvm_reg_block;
…
endclass

class buf_blk extends uvm_reg_block;
…
endclass

class mac_blk extends uvm_reg_block;
…
endclass

class register_model extends uvm_reg_block;
global_blk gb_ins;
buf_blk bb_ins;
mac_blk mb_ins;
function void build();
default_map = create_map("default_map", 0, 2, UVM_LITTLE_ENDIAN, 0); 
gb_ins = global_blk::type_id::create("gb_ins", , get_full_name());   //实例化
gb_ins.configure(this, "global_reg");  //调用子reg_block的configure函数
gb_ins.build();  //调用子reg_block的build函数
gb_ins.lock_model();  //调用子reg_block的lock_model函数
default_map.add_submap(gb_ins.default_map, 16'h0);
bb_ins = buf_blk::type_id::create(“bb_ins”, , get_full_name());  //实例化
bb_ins.configure(this, “buf_reg”);  //调用子reg_block的configure函数
bb_ins.build();  //调用子reg_block的build函数
bb_ins.lock_model();  //调用子reg_block的lock_model函数
default_map.add_submap(bb_ins.default_map, 16’h1000);
mb_ins = mac_blk::type_id::create(“mb_ins”, , get_full_name());  //实例化
mb_ins.configure(this, “mac_reg”);  //调用子reg_block的configure函数
mb_ins.build();  //调用子reg_block的build函数
mb_ins.lock_model();  //调用子reg_block的lock_model函数
default_map.add_submap(mb_ins.default_map, 16’h2000);
endfunction
…
endclass

要把一个子reg_block加入到父reg_block中,第一步是先实例化子reg_block; 第二步是调用子reg_block的configure函数,在这个函数中要说明这个子reg_block的路径,这个路径不是绝对路径,而是相对于父reg_block来说的路径;  第三步是调用子reg_block的build函数;   第四步是调用子reg_block的lock_model函数;  第五步则是把子reg_block的default_map以子map的形式加入到父reg_block的default_map中。这是可以理解的,因为一般在子reg_block中定义寄存器的时候,我们给定的都是寄存器的偏移地址,其实际物理地址还要再加上一个基地址。前面说过,寄存器的FRONTDOOR的读写操作最终都要通过default_map来完成。很显然,子reg_block的default_map是不知道寄存器的基地址的,它只知道寄存器的偏移地址,只有把它加入到父reg_block的default_map,同时在加入的时候告诉这个子map的偏移地址,这样父reg_block的default_map就可以完成FRONTDOOR操作。

一般的把具有同一基地址的寄存器作为整体加入到一个uvm_reg_block中,而不同的基地址对应不同的uvm_reg_block。每个uvm_reg_block一般都有与其对应的物理地址空间。对于本节的所说的子reg_block,其里面还可以加入小的reg_block,这相当于是把地址空间再次细化。

而引入vm_reg_file的概念主要是用于区分不同的hdl路径。

假设有两个寄存器regA和regB,它们的hdl路径为top_tb.mac_reg.fileA.regA,top_tb.mac_inst.fileB.regB,设top_tb.mac_reg下面所有寄存器的基地址为0x2000,这样在第二级的reg_block中,加入mac模块的时候,其hdl路径要写成:

mb_ins.configure(this, “mac_reg”);

相应的,在mac_blk的build中,要通过如下方式把regA和regB的路径告知register model:

regA.configure(this, null, "fileA.regA");
…
regB.configure(this, null, "fileB.regB");

假如fileA中有几十个寄存器时,那么很显然,fileA.*会几十次的出现在这几十个寄存器的configure函数里。假如有一天,fileA的名字忽然变为了filea_inst,那么就需要把这几十行中所有fileA替换成filea_inst,这个过程很容易出错。
为了适应这种情况,UVM的register model中引入了uvm_reg_file的概念。uvm_reg_file同uvm_reg一样是一个纯虚类,不能直接使用,必须使用其派生类:

class regfile extends uvm_reg_file;
function new(input string name="unnamed_regfile");
super.new(name);
endfunction
`uvm_object_utils(regfile)
endclass
class mac_blk extends uvm_reg_block;
rand regfile file_a;
rand regfile file_b;
rand my_reg regA;
rand my_reg regB;
function void build();
…
file_a = regfile::type_id::create(“file_a”, , get_full_name());
file_a.configure(this, null, “fileA”);
file_b = regfile::type_id::create(“file_b”, , get_full_name());
file_b.configure(this, null, “fileA”);
…
regA.configure(this, file_a, “regA”);
…
regB.configure(this, file_b, “regB”);
…
endfunction
endclass

如上所示:

(1)先从uvm_reg_file派生一个类user_defined_uvm_reg_file(regfile);

(2)然后在mac_blk中实例化此类,在uvm_reg_block的build函数中例化user_defined_uvm_reg_file(regfile);

(3)在mac_blk的build函数中调用(regfile)configure函数,在uvm_reg_block的build函数中调用user_defined_uvm_reg_file的configure函数;

注: uvm_reg_file configure函数参数的含义:

参数1含义:uvm_reg_file所在reg_block的指针;

参数2含义:假设此reg_file是另外一个reg_file的parent,那么这里就填写该reg_file的父reg_file指针,uvm_reg_file的父reg_file指针,该处只有一级reg_file,因此填写null;

参数3含义:此reg_file的hdl路径,uvm_reg_file的hdl path;

当把reg_file定义好了后,在调用uvm_reg的configure函数时,就可以把其第二个参数设为reg_file的指针。
注: 加入uvm_reg_file的概念后,当uvm_reg regA的hdl层次由fileA变为filea_inst时,只需要修改uvm_reg_file file_a的configure参数,其他不需要做任何改变,大大减小了出错的概率;

 

posted @ 2023-02-10 20:01  luckylan  阅读(408)  评论(0)    收藏  举报