rgm_pkg的内容是每个寄存器都是一个单独的class,再加上一个顶层rgm继承于uvm_reg_block来对每一个寄存器做例化、配置、build,再将所有的reg加入到map中,做后门访问的配置。
首先是注册,域的声明,声明成随机变量rand uvm_reg_field slv_en;
然后写覆盖组covergroup value_cg;写明覆盖点以及位数;
new函数的参数要包含寄存器位数32和打开覆盖率的选项UVM_CVR_ALL;然后设置覆盖率选项,在has的时候例化覆盖组;
build函数是virtual的,做域的例化和配置,配置参数一共有9个,第一个是this,第二和第三个是位数,第四个是状态RW/RO,后面的五个参数固定为0初始值100。
最后是sample函数,参数是寄存器自己操作的trans——uvm_reg_bus_op中的两个data_t(data, byte_en)、bit is_read、uvm_reg_map map,一共四个参数,先写类型后写名,内容是调用super的后调用自定义的sample_values()函数;
sample_values()函数也调用super的,然后用if判断是否get了coverage,是的话就调用覆盖组的sample()采样函数。
slv_en_reg
第一个版本
覆盖组需要触发条件吗?域的位数怎么声明在覆盖组中?
(这里不需要;在覆盖点中域名.value[位数])
域的例化用new还是工厂?配置怎么写?
(用工厂,类型就是声明时的uvm_reg_field;域.configure(9个参数),reserved的属性是RO)
class slv_en_reg extends uvm_reg;
`uvm_object_utils(slv_en_reg)
rand uvm_reg_field slv_en;
rand uvm_reg_field reserved;
covergroup value_cg @(posedge clk iff rstn);
slv_en: coverpoint slv_en{
type_option.weight=0;
bins en={1};
bins reserved={0};
}
function new(32, UVM_CVR_ALL);
endfunction
virtual function void build();
slv_en=
slv_en.configure(this,3,0,"RW",0,'h0,1,0,0);
endfunction
function void sample( uvm_reg_data_t data,
uvm_reg_data_t byte_en,
bit is_read,
uvm_reg_map map);
super.sample(data, byte_en, is_read, map);
sample_values();
endfunction
function void sample_values();
super.sample_values();
if(get_coverage()) value_cg.sample();
endfunction
endclass: slv_en_reg
get_coverage()的参数是UVM_CVR_FIELD_VALS
第二个版本
class slv_en_reg extends uvm_reg;
`uvm_object_utils(slv_en_reg)
rand uvm_reg_field en;
rand uvm_reg_field reserved;
covergroup value_cg;
option.per_instance=1;
en: coverpoint en.value[3:0];
reserved: coverpoint reserved.value[31:4];
endgroup
function new(string name="slv_en_reg");
super.new(name, 32, UVM_CVR_ALL);
void'(set_coverage(UVM_CVR_FIELD_VALS));
if(has_coverage(UVM_CVR_FIELD_VALS)) begin
value_cg=new(); end
endfunction
virtual function void build();
en=uvm_reg_field::type_id::create("en");
reserved=uvm_reg_field::type_id::create("reserved");
en.configure(this,3,0,"RW",0,'h0,1,0,0);
reserved.configure(this,31,4,"RO",0,'h0,1,0,0);
endfunction
function void sample( uvm_reg_data_t data,
uvm_reg_data_t byte_en,
bit is_read,
uvm_reg_map map);
super.sample(data, byte_en, is_read, map);
sample_values();
endfunction
function void sample_values();
super.sample_values();
if(get_coverage(UVM_CVR_FIELD_VALS)) begin
value_cg.sample(); end
endfunction
endclass: slv_en_reg
parity_err_clr_reg
按照上面的模式写,覆盖组的例化是在new函数且条件是has cvr,sample_values函数是调用覆盖组的采样函数;
class parity_err_clr_reg extends uvm_reg;
`uvm_object_utils(parity_err_clr_reg)
rand uvm_reg_field parity_err_clr;
rand uvm_reg_field reserved;
covergroup value_cg;
option.per_instance=1;
parity_err_clr: coverpoint parity_err_clr.value[3:0];
reserved: coverpoint reserved.value[31:4];
endgroup
function new(string name="parity_err_clr_reg");
super.new(name, 32, UVM_CVR_ALL);
void'(set_coverage(UVM_CVR_FIELD_VALS));
if(has_coverage(UVM_CVR_FIELD_VALS)) begin
value_cg=new(); end
endfunction
virtual function void build();
parity_err_clr=uvm_reg_field::type_id::create("parity_err_clr");
reserved=uvm_reg_field::type_id::create("reserved");
parity_err_clr.configure(this,3,0,"RW",0,'h0,1,0,0);
reserved.configure(this,31,4,"RO",0,'h0,1,0,0);
endfunction
function void sample( uvm_reg_data_t data,
uvm_reg_data_t byte_en,
bit is_read,
uvm_reg_map map);
super.sample(data, byte_en, is_read, map);
sample_values();
endfunction
function void sample_values();
super.sample_values();
if(get_coverage(UVM_CVR_FIELD_VALS)) begin
value_cg.sample(); end
endfunction
endclass: parity_err_clr_reg
slv_id_reg
配置中的第二三个参数到底是不是表示位数?这个一次过了。
配置的第二个参数表示该域的总位数,所以都是8,第三个表示最低位,依次是24,16,8,0
class slv_id_reg extends uvm_reg;
`uvm_object_utils(slv_id_reg)
rand uvm_reg_field slv3_id;
rand uvm_reg_field slv2_id;
rand uvm_reg_field slv1_id;
rand uvm_reg_field slv0_id;
covergroup value_cg;
option.per_instance=1;
slv3_id: coverpoint slv3_id.value[31:24];
slv2_id: coverpoint slv2_id.value[23:16];
slv1_id: coverpoint slv1_id.value[15: 8];
slv0_id: coverpoint slv0_id.value[ 7: 0];
endgroup
function new(string name="slv_id_reg");
super.new(name, 32, UVM_CVR_ALL);
void'(set_coverage(UVM_CVR_FIELD_VALS));
if(has_coverage(UVM_CVR_FIELD_VALS)) begin
value_cg=new(); end
endfunction
virtual function void build();
slv3_id=uvm_reg_field::type_id::create("slv3_id");
slv2_id=uvm_reg_field::type_id::create("slv2_id");
slv1_id=uvm_reg_field::type_id::create("slv1_id");
slv0_id=uvm_reg_field::type_id::create("slv0_id");
slv3_id.configure(this,31,24,"RW",0,'h3,1,0,0);
slv2_id.configure(this,23,16,"RW",0,'h2,1,0,0);
slv1_id.configure(this,15, 8,"RW",0,'h1,1,0,0);
slv0_id.configure(this, 7, 0,"RW",0,'h0,1,0,0);
endfunction
function void sample(
uvm_reg_data_t data,
uvm_reg_data_t byte_en,
bit is_read,
uvm_reg_map map);
super.sample(data, byte_en, is_read, map);
sample_values();
endfunction
function void sample_values();
super.sample_values();
if(get_coverage(UVM_CVR_FIELD_VALS)) begin
value_cg.sample(); end
endfunction
endclass: slv_id_reg
slv_len_reg
初始值全部为0,其他一模一样。
free_slot_reg
覆盖组要加option;
余量的初始值是'h20,为什么是32?
4个都一样。
parity_err_reg
初始值为0,其他同上
顶层环境rgm
顶层rgm继承于uvm_reg_block;
声明每一个reg为随机变量;同时声明map;
new函数不收集覆盖率;
build函数对每一个寄存器做例化(类型是那个reg)、配置(参数为this)、调用build;
为什么这里的配置的参数是this?
例化map,用的create_map(),参数是“map”,基地址0,总线宽度4(单位是byte,32位就是4byte),UVM_LITTLE_ENDIAN表示低位字节排放在内存的低地址端,高位字节排放在内存的高地址端;
将所有的reg加入到map中,调用map的add_reg方法,参数是例化时的寄存器名,偏移地址,RO/RW;
后门访问:寄存器调用add_hdl_path_slice,参数是“???”,0,32;最后是add_hdl_path(“???”)和lock_model()
最后再定义两个函数:根据通道id获取域的长度和id,根据输入的ch选择对应reg中的field,调用get(),否则报错,最后返回fd;
第一个版本
class mcdf_rgm extends uvm_reg_block;
`uvm_object_utils(mcdf_rgm)
rand uvm_reg_field slv_en_reg;
rand uvm_reg_field parity_err_clr_reg;
rand uvm_reg_field slv_id_reg;
rand uvm_reg_field slv_len_reg;
rand uvm_reg_field slv0_free_slot_reg;
rand uvm_reg_field slv1_free_slot_reg;
rand uvm_reg_field slv2_free_slot_reg;
rand uvm_reg_field slv3_free_slot_reg;
rand uvm_reg_field slv0_parity_err_reg;
rand uvm_reg_field slv1_parity_err_reg;
rand uvm_reg_field slv2_parity_err_reg;
rand uvm_reg_field slv3_parity_err_reg;
uvm_reg_map map;
function new(string name="mcdf_rgm");
super.new(name, 32, UVM_NO_CVR);
endfunction
virtual function void build();
slv_en_reg=slv_en_reg::type_id::create("slv_en_reg");
slv_en_reg.configure(this);
slv_en_reg.build();
parity_err_clr_reg=parity_err_clr_reg::type_id::create("parity_err_clr_reg");
parity_err_clr_reg.configure(this);
parity_err_clr_reg.build();
slv_id_reg=slv_id_reg::type_id::create("slv_id_reg");
slv_id_reg.configure(this);
slv_id_reg.build();
slv_len_reg=slv_len_reg::type_id::create("slv_len_reg");
slv_len_reg.configure(this);
slv_len_reg.build();
slv0_free_slot_reg=slv0_free_slot_reg::type_id::create("slv0_free_slot_reg");
slv0_free_slot_reg.configure(this);
slv0_free_slot_reg.build();
slv1_free_slot_reg=slv1_free_slot_reg::type_id::create("slv1_free_slot_reg");
slv1_free_slot_reg.configure(this);
slv1_free_slot_reg.build();
slv2_free_slot_reg=slv2_free_slot_reg::type_id::create("slv2_free_slot_reg");
slv2_free_slot_reg.configure(this);
slv2_free_slot_reg.build();
slv3_free_slot_reg=slv3_free_slot_reg::type_id::create("slv3_free_slot_reg");
slv3_free_slot_reg.configure(this);
slv3_free_slot_reg.build();
slv0_parity_err_reg=slv0_parity_err_reg::type_id::create("slv0_parity_err_reg");
slv0_parity_err_reg.configure(this);
slv0_parity_err_reg.build();
slv1_parity_err_reg=slv1_parity_err_reg::type_id::create("slv1_parity_err_reg");
slv1_parity_err_reg.configure(this);
slv1_parity_err_reg.build();
slv2_parity_err_reg=slv2_parity_err_reg::type_id::create("slv2_parity_err_reg");
slv2_parity_err_reg.configure(this);
slv2_parity_err_reg.build();
slv3_parity_err_reg=slv3_parity_err_reg::type_id::create("slv3_parity_err_reg");
slv3_parity_err_reg.configure(this);
slv3_parity_err_reg.build();
//例化map
map=create_map("map", 'h0, 4, UVM_LITTLE_ENDIAN);
map.add_reg("slv_en_reg", 'h0, "RW");
map.add_reg("parity_err_clr_reg", 'h4, "RW");
map.add_reg("slv_id_reg", 'h8, "RW");
map.add_reg("slv_len_reg", 'hC, "RW");
map.add_reg("slv0_free_slot_reg", 'h80, "RO");
map.add_reg("slv1_free_slot_reg", 'h84, "RO");
map.add_reg("slv2_free_slot_reg", 'h88, "RO");
map.add_reg("slv3_free_slot_reg", 'h8C, "RO");
map.add_reg("slv0_parity_err_reg", 'h90, "RO");
map.add_reg("slv1_parity_err_reg", 'h94, "RO");
map.add_reg("slv2_parity_err_reg", 'h98, "RO");
map.add_reg("slv3_parity_err_reg", 'h9C, "RO");
//后门访问
slv_en_reg.add_hdl_path_slice("???", 0, 32);
parity_err_clr_reg.add_hdl_path_slice("???", 0, 32);
slv_id_reg.add_hdl_path_slice("???", 0, 32);
slv_len_reg.add_hdl_path_slice("???", 0, 32);
slv0_free_slot_reg.add_hdl_path_slice("???", 0, 32);
slv1_free_slot_reg.add_hdl_path_slice("???", 0, 32);
slv2_free_slot_reg.add_hdl_path_slice("???", 0, 32);
slv3_free_slot_reg.add_hdl_path_slice("???", 0, 32);
slv0_parity_err_reg.add_hdl_path_slice("???", 0, 32);
slv1_parity_err_reg.add_hdl_path_slice("???", 0, 32);
slv2_parity_err_reg.add_hdl_path_slice("???", 0, 32);
slv3_parity_err_reg.add_hdl_path_slice("???", 0, 32);
add_hdl_path("???");
lock_model();
endfunction
function int get_ch_field_len(int ch);
int fl;
case(ch)
0:fl=slv_len_reg.slv0_len.get();
1:fl=slv_len_reg.slv1_len.get();
2:fl=slv_len_reg.slv2_len.get();
3:fl=slv_len_reg.slv3_len.get();
default: $error("get_ch_field_len error!");
return fl;
endfunction
function int get_ch_field_id(int ch);
int fd;
case(ch)
0:fd=slv_id_reg.slv0_id.get();
1:fd=slv_id_reg.slv1_id.get();
2:fd=slv_id_reg.slv2_id.get();
3:fd=slv_id_reg.slv3_id.get();
default: $error("get_ch_field_id error!");
return fd;
endfunction
endclass: mcdf_rgm
声明寄存器的时候是直接寄存器+新的名称;试试不加新名称怎么样,等下编译看看。
new函数不需要加位数了;
添加寄存器,reg不需要加引号,偏移地址要标明32位数,
报错要用uvm的消息机制;
注册的名称写错
寄存器声明的时候还是要有新的名称,解答上面的问题了。
然后是少了个endcase;
sample函数由于是继承于父类uvm_reg,参数必须是data,byte_en这个顺序,修改就好了;
顶层的new不收集覆盖率是UVM_NO_COVERAGE;