系列文章目录
注意事项
- UVM寄存器模型实现方式大部分都是范式,可以先理解其使用方法。感兴趣的可以深入源码更进一步的学习
- 有些概念为了方便理解,可能不是很严谨
- 文章中所有代码依据寄存器表单,请依据寄存器表单理解代码
基本概念
寄存器,寄存器域,寄存器表单的概念传送门:
寄存器模型基本原理
说明:下面所说的基本概念先不涉及寄存器模型嵌套,关于嵌套后续有专门章节进行说明
uvm_reg_field:寄存器域
uvm_reg:寄存器,一个寄存器可以包含多个uvm_reg_field
uvm_reg_block:一个uvm_reg_block可以包含多个uvm_reg,通常与寄存器表单对应
uvm_reg_map:包含base_addr,offset等信息
创建简单的寄存器模型
创建uvm_reg类
class uart_data extends uvm_reg;
rand uvm_reg_field overrun_error;
rand uvm_reg_field break_error;
rand uvm_reg_field parity_error;
rand uvm_reg_field framing_error;
rand uvm_reg_field data;
virtual function void build();
overrun_error = uvm_reg_field::type_id::create("overrun_error");
break_error = uvm_reg_field::type_id::create("break_error");
parity_erro r= uvm_reg_field::type_id::create("parity_error");
framing_error = uvm_reg_field::type_id::create("framing_error");
data = uvm_reg_field::type_id::create("data");
overrun_error.configure(this,1,11,"RW",0,1'd0,1,1,0);
break_error.configure(this,1,10,"RW",0,1'd0,1,1,0);
parity_error.configure(this,1,9,"RW",0,1'd0,1,1,0);
framing_error.configure(this,1,8,"RW",0,1'd0,1,1,0);
data.configure(this,8,0,"RW",0,8'd0,1,1,0);
`uvm_object_utils(uart_data )
function new(string name);
super.new(name,32,UVM_CRV_ALL);
endfunction
endfunction
endclass
第2-6行声明uvm_reg_field数据类型的变量,这里添加了rand属性说明可以通过randomize去随机化,然后将随机化的寄存器/寄存器域值更新到DUT中。这种方式常用于测试环境在真正跑业务之前,随机化配置DUT寄存器。有关随机化的例子传送门:
(留个坑位)
第9-13行创建uvm_reg_field实例
第15-19行配置uvm_reg_field,配置每个参数含义如下,红色框图可以先忽略。以parity_error为例进行说明:
第一个参数:指定父类指针,uvm_reg_field父类句柄类型为uvm_reg,上述代码中parity_error就在uart_data中生成,其父类即this表示uart_data句柄本身。
第二个参数:域位宽,单位为bit。parity_error域只有1bit,域位宽为1
第三个参数:域起始bit位,parity_error从bit9开始,所以值为9
第四个参数:访问类型,parity_error可读可写,为RW类型
第五个参数:volatile,在IEEE 1685-2009 IP-XACT标准中,定义寄存器volatile的含义是:
比如寄存器是RW的,DUT内部逻辑也有可能更改寄存器的值。如果验证环境发起一次先写后读操作,可能读取的值不一定是写入的值。
遗留:如果设置volatile参数为1,uvm_reg_single_bit_bash_seq和uvm_reg_access_seq序列比对是否会出错?
第六个参数:复位值,parity_error复位值为0
第七个参数:是否有复位,一般情况下都有复位,值为1
第八个参数:是否随机开关,1表示可以对reg field随机化
第九个参数:individually_accessible,具体怎么用不知道?
这样我们就创建好了uart_data寄存器模型。
创建uvm_reg_block类
class reg_model extends uvm_reg_block;
rand uvm_reg uart_data;
rand uvm_reg baud_div;
virtual function void build();
uart_data = uvm_reg::type_id::create("uart_data", ,get_full_name());
baud_div= uvm_reg::type_id::create("baud_div", ,get_full_name());
uart_data .configure(this,null,"");
baud_div.configure(this,null,"");
uart_data.build();
baud_div.build();
map = create_map("map",0,4,UVM_CRV_ALL);
map.add_reg(uart_data ,
`uvm_object_utils(reg_model )
function new(string name);
super.new(name,UVM_CRV_ALL);
endfunction
endfunction
endclass
第2-3行声明uvm_reg数据类型的变量,为什么用rand?
第6-7行创建uvm_reg_field实例。
第一个参数:指定生成实例的名称,用字符串表示
第二个参数:
第三个参数:
第9-10行配置uvm_reg,配置每个参数含义如下,红色框图可以先忽略。以uart_data为例进行说明:
第一个参数:指定父类,一般uvm_reg父类即uvm_reg_block,上述代码中uart_data就在reg_model中生成,其父类即reg_model,所以用this表示本身。
第二个参数:暂时忽略
第三个参数:暂时忽略
第12-13行调用uvm_reg中的build函数。
第14行创建一个uvm_reg_map实例。
第一个参数:指定生成实例的名称,用字符串表示
第二个参数:uvm_reg_block的基地址,与寄存器表单中的base_addr值一致
第三个参数:数据位宽,单位为Byte,与寄存器表单中的data_width值一致
第四个参数:暂时忽略
第五个参数:暂时忽略
第15-16行将uvm_reg添加到uvm_reg_map中。
第一个参数:uvm_reg的实例名称
第二个参数:相对基地址偏移,与寄存器表单中的offset值一致
第三个参数:访问类型
至此,基本的RAL寄存器模型已经生成完毕。