说明
验证需要包含对寄存器和存储器的验证,寄存器用来描述芯片配置信息和状态信息
自己实现
可通过启动读写寄存器的sequence来完成
sequence产生的寄存器事务传递给driver,driver来写入寄存器的值或者读取寄存器的值,monitor再从接口上获取信息,判断是哪个寄存器和它的值,这是一种简单的检测寄存器的方法,但是在其他组件中难以启动或者查看某些寄存器的值,在某种情况下 scoreboard想查看寄存器中某个的状态,则不知道什么时候去触发这个,只能通过事务状态来启动sequence 驱动到driver,然后用monitor获取,这种做法相当繁琐。

寄存器建模RAL
RAL提供了一套数据结构,对寄存器进行建模,实例化后作为组件的一部分。scoreboard就可以通过寄存器模型来访问DUT中的寄存器。使用RAL提供的API来访问,write写, read读。这个过程是寄存器模型自己完成的。
寄存器模型想要访问DUT中的寄存器还需要一个事务转换器adapter,因为寄存器模型产生的事务类型可能与sequencer能接收的类型不一致。转换器需要自己完成。RAL模型产生的事务借助agent来访问DUT中寄存器/存储器,这种为frontdoor模式,第二种是RAL通过DUT的设计层次路径直接访问DUT中的寄存器,为backdoor模式。
工作模式
frontdoor
当在scoreboard或其他组件中调用api的时候,register_model.reg_name.read(…)或者register_model.reg_name.write(…)的时候,寄存器会根据寄存器名称产生uvm_reg_item的事务对象,产生的名字会根据寄存器名称reg_name自动生成,接着将对象类型转换为uvm_reg_bus_op类型对象,并将该对象传入转换器中,然后转换为sequencer可以接收的事务类型(用户自定义类型),sequencer将数据发给driver,进而操作DUT,操作的结果会在寄存器模型中体现,如读出的值会返回给寄存器模型,这里的整个过程由寄存器模型实现,需要消耗仿真时间。
backdoor
通过寄存器的层次结构直接引用DUT中的寄存器,将返回值返给寄存器模型,backdoor模式不需要消耗仿真时间
register model在env中进行例化,其他组件使用句柄引用
寄存器模型
寄存器模型的构成图如下:
转换器
转换器继承UVM的基类uvm_reg_adapter。
转换器类需要重载两个函数
1、reg2bus 由寄存器模型的事务转换成自己定义类型的事务,转换为自动发给sequencer,
2、bus2reg 将自定义事务对象转换为寄存器模型类型的事务,发给寄存器模型,
这两个函数UVM平台自动调用,
register model 和adapter以及 sequencer的链接
借住寄存器模型中的map来实现,其map中有一个adpter句柄和一个sequencer句柄,将adpater对象赋值给该句柄即可。
在测试平台中加入寄存器模型
为DUT创建寄存器模型,包含每一个寄存器
比如DUT有两个寄存器和一个memary,如下图所示
封装reg类
//为每个寄存器进行建模型
class config_reg_c externs uvm_reg
rand uvm_reg_filed f1 //建立第一个域
rand uvm_reg_filed f2
rand uvm_reg_filed f3
rand uvm_reg_filed f4
virtual function void bulid();//bulid方法,非phase
//实例化
f1 = uvm_reg_field::type_id::create(“f1”);
f2 = uvm_reg_field::type_id::create(“f2”);
f3 = uvm_reg_field::type_id::create(“f3”);
f4 = uvm_reg_field::type_id::create(“f4”);
//调用域的config,
//配置域的一些属性
f1.configure(this,1,0,”RW”,0,’h0,1,1,1);//第一个参数是哪个寄存器,第二个是该域有几位,
//第三个该field最低位在寄存器中位置,第四个参
//数表示域访问类型,第五个复位后的默认值,第
//六个表示是否可以被复位,第七个表示该域是否
//可以随机化,最后一个该field是否可以单独操作
`uvm_object_utils(config_reg_c),
function new(string name = “config_reg_c”)
//第二个参数是寄存器总位宽,第三个是是否支持覆盖率统计,
super.new(name,8,UVM_NO_COVERAGE);
endfunction
endclass
其中存储器需要从uvm_mem扩展
已上就完成寄存器建模的工作。
封装寄存器模型类
class reg_model_c extends uvm_reg_blocks;
rand config_reg_c config_reg;
rand mode_reg_c model_reg;
data_mem_c data_mem;
virtual function void bulid();//bulid方法,非phase
//实例化
config_reg = config_reg_c::type_id::create(“config_reg”,get_full_name());
config_reg. configure(this,null ,” config_reg”); //第三个参数是指定该寄存器
//在HDL路径, 这个参数路径
//用于backdoor的访问,
config_reg.build();
//其他寄存器用同样方法创建
//创建map,第一个参数为名字,第二个为基地址,第三个系统总线位宽,
//单位为字节,第四个为大小端
default_map = create_map(“default_map”,0,1,UVM_LITTLE_ENDIAN);
//添加寄存器,第一个参数为添加的reg名,第二个为reg偏移地址,
//用基地址和偏移地址结合表示实际地址,
default_map.add_reg(“config_reg”,‘h001c,”RW”);
`uvm_object_utils(reg_model_c),
function new(string name = “reg_model_c”)
super.new(name,UVM_NO_COVERAGE),第二个参数是是否支持覆盖率统计,
endfunction
end class
创建为实现前门操作的转换器
从uvm_reg_adapter扩展,然后重载两个函数
在env中实例化寄存器模型和转换器
在env中实例化,创建,调用config_reg.bulid()函数就是在执行reg类中的bulid方法。
调用lock函数则锁定,不可再编辑reg model ,调用reset函数完成初始化
在connect_phase中,调用map函数进行连接,最后调用reg_model.default_map.set_auto_predict(1).开启预测功能,
最后在env中,将转换器、sequencer、与寄存器的map建立关联。在需要进行寄存器读写的地方进行API访问,比如在scoreboard调用寄存器模型,则在env中还需要将寄存器模型赋值给scoreboard中例化的寄存器模型句柄。
寄存器模型的基本数据结构
寄存器模型API
read/write 方法 均支持前门后门访问,
使用前门访问的时候,read/write会产生事务,通过转换器送到sequencer再送到driver,最终到达DUT,访问DUT内部寄存器值之后,将返回来的结果通过predict自动更改期望值和镜像值,最终使得期望值和镜像值,与DUT内部的寄存器值保持一致。
后门操作的时候,直接通过层次路径,读取写入DUT内部的值,然后直接修改期望值和镜像值,
peek/poke
peek几乎相当于read的后门操作,poke几乎相当于write的后门模式,
peek/poke不模拟寄存器的行为而是直接进行操作,
randomize : 随机化后会赋值给期望值
update: 将期望值写入DUT内部寄存器当中,然后比较期望值和镜像值是否相同,不同则更新镜像值。
mirror,:从DUT中读出,写入期望值和镜像值。