寄存器模型
- MCDF的寄存器模块描述,将Ox00功能寄存器和Ox10状态寄存器位用图来表示。
- 通常来讲,一个寄存器有32位宽,寄存器按照地址索引的关系是按字对齐的(word-align) ,上图中的寄存器有多个域,每个域的属性也可以不相同,reserved域表示的是该域所包含的比特位暂时保留以作日后功能的扩展使用,而对保留域的读写不起任何作用,即无法写入而且读出值也是它的复位值。
- 上面的这些寄存器按照地址排列,即可构成寄存器列表,我们称之为寄存器块(register block) 。
- 实际上,寄存器块除了包含寄存器,也可以包含存储器,因为它们的属性都近乎于读写功能,以及表示为同外界通信的接口
class ctrl_reg extends uvm_reg ;
`uvm_object_utils (ctrl_reg)
uvm_reg _field reserved;
rand uvm_reg_field pkt_len;
rand uvm_reg_field prio_level;
rand uvm_reg field chnl_en ;
function new (string name = "ctrl_reg" ) ;
super.new (name,32,UVM_NO_COVERAGE);
endfunction
virtual function build() ;
reserved = uvm_reg_field: : type_id: : create ( "reserved" ) ;
pkt_len = uvm_reg_field: : type_id: : create ( "pkt_len" ) ;
prio_level = uvm_reg_field: : type_id: : create ( "prio_level");
chnl_en = uvm_reg_field: : type_id: : create ( "chnl_en" );
reserved.configure (this,26,6,"RO",0,26'h0,1,0,0);
pkt_len.configure (this,3,3,"RW",0,3'h0,1,1,0);
prio_level.configure(this,2,1,"RW",0,2 'h3,1,1,0);
chnl_en.configure(this,1,0,"RW",0,1'h0,1,1,0);
endfunction
endclass
class stat_reg extends uvm_reg;
`uvm_object_utils (stat_reg)
uvm reg _field reserved;
rand uvm reg _field fifo_avail;
function new ( string name = "stat_reg" ) ;
super.new (name, 32,UVM_NOo_cOVERAGE);
endfunction
virtual function build() ;
reserved = uvm_reg_field: : type_id: : create ( "reserved" ) ;
fifo_avail = uvm_reg_field: : type_id: :create( "fifo_avail");
reserved.configure(this,24,8,"RO",0,24'h0,1,0,0);
fifo avail.configure (this, 8,0,"RO",0, 8'h0,1,1,0);
endfunction
endclass
class mcdf_rgm extends uvm_reg_block ;
uvm_object_utils (mcdf_rgm)
rand ctrl_reg chnl0_ctrl_reg;
rand ctrl_reg chnl1_ctrl_reg;
rand ctrl_reg chnl2_ctrl_reg;
rand stat_reg chnl0_stat_reg;
rand stat_reg chnl1_stat_reg;
rand stat_reg chn12_stat_reg;
uvm reg_map map ;
function new ( string name = "mcdf_rgm" ) ;
super.new(name, UVM_NO_COVERAGE);
endfunction
virtual function build () ;
chnl0_ctrl_reg = ctrl_reg : : type_id: : create ("chn10_ctrl_reg" )
chn10_ctrl_reg.configure (this) ;
chn10_ctrl_reg.build () ;
chnl1_ctrl_reg = ctrl_reg : :type_id: : create ( "chnl1_ctrl_reg")
chnl1_ctrl_reg.configure (this) ;
chnl1_ctrl_reg.build( ) ;
chn12_ctrl_reg = ctrl_reg : : type_id : :create ( "chn12_ctrl_reg")
chnl2_ctrl_reg.configure (this) ;
chnl2_ctrl_reg.build() ;
chn10_stat_reg = stat_reg : : type_id : :create ( "chnl0_stat_reg")
chn10_stat_reg. configure (this) ;
chnl0_stat_reg.build( ) ;
chnl1_stat_reg = stat_reg : : type_id: :create ( "chnl1_stat_reg" )
chnl1_stat_reg.configure (this) ;
chnl1_stat _reg.build ( ) ;
chn12_stat_reg = stat_reg : : type_id: :create ("chnl2_stat_reg")
chnl2_stat_reg.configure ( this) ;
chnl2_stat _reg.build() ;
//map name, offset, number of bytes, endianess
map = create_map ( "map" , 'h0,4, UVM_LITTLE_ENDIAN);
map.add_reg(chnl0_ctrl_reg,32 'h00000000,"RW");
map.add_reg (chnl1_ctrl_reg,32 'h00000004,"RW");
map.add_reg (chnl2_ctrl_reg,32 'h00000008,"RW");
map.add_reg(chnl0_stat _reg,32 'h00000010,"RO");
map.add_reg(chnl1_stat_reg, 32 ' h00000014, "RO");
map. add_reg(chnl2_stat_reg,32 ' h00000018,"RO");
lock model();
endfunction
endclass: mcdf_rgm
关于寄存器建模的基本要点和顺序:
- 在定义单个寄存器时,需要将寄存器的各个域整理出来,在创建之后还应当通过uvm_reg_field::configure()函数来进一步配置各自属性。
- 在定义uvm_reg_block时,读者需要注意reg_block与uvm_mem、uvm_reg以及uvm_reg_map的包含关系。首先uvm_reg和uvm_mem分别对应着硬件中独立的寄存器或者存储,而一个uvm_reg_block可以用来模拟一个功能模块的寄存器模型,其中可以容纳多个uvm_reg和uvm_mem实例;其次map的作用一方面用来表示寄存器和存储对应的偏移地址,同时由于一个reg_block可以包含多个map,各个map可以分别对应不同总线或者不同地址段。在reg_block中创建了各个uvm_reg之后,需要调用uvm_reg:configure()去配置各个uvm_reg实例的属性。
- 考虑到uvm reg map也会在uvm _reg block中例化,在例化之后需要通过uvm_reg_mapadd_reg()函数来添加各个uvm_reg对应的偏移地址和访问属性等。只有规定了这些属性,才可以在稍后的前门访问(frontdoor)中给出正确的地址。
- uvm_reg_block可以对更大的系统做寄存器建模,这意味着uvm_reg_block之间也可以存在层次关系,上层uvm_reg_block的uvm_reg_map可以添加子一级uvm_reg_block的uvm_reg_map,用来构建更全局的“版图”,继而通过uvm_reg_block与uvm_reg_map之间的层次关系来构建更系统的寄存器模型。