uvm中寄存器模型的访问方法详解
uvm_reg_field/uvm_reg/uvm_reg_block的访问方法
在给出寄存器模型的常见应用模式之前,首先从下表中更全面地了解uvm_reg_ block、 uvm_reg 和uvm_reg_ field 三个类提供的用于访问寄存器的方法。

extern virtual task read(output uvm_status_e status,
output uvm_reg_data_t value,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
它有多个参数,常用的是其前三个参数。其中第一个是uvm_status_e型的变量,这是一个输出,用于表明读操作是否成功;
第二个是读取的数值,也是一个输出;
第三个是读取的方式,可选UVM_FRONTDOOR和UVM_BACKDOOR。
extern virtual task write(output uvm_status_e status,
input uvm_reg_data_t value,
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
它有多个参数,但是与read类似。其中第一个是uvm_status_e型的变量,这是一个输出,用于表明写操作是否成功;
第二个是写入的数值,这是一个输入;
第三个是写操作的方式,可选UVM_FRONTDOOR和UVM_BACKDOOR。
// Enum: uvm_path_e
//
// Path used for register operation
//
// UVM_FRONTDOOR - Use the front door
// UVM_BACKDOOR - Use the back door
// UVM_PREDICT - Operation derived from observations by a bus monitor via
// the <uvm_reg_predictor> class.
// UVM_DEFAULT_PATH - Operation specified by the context
//
typedef enum {
UVM_FRONTDOOR,
UVM_BACKDOOR,
UVM_PREDICT,
UVM_DEFAULT_PATH
} uvm_path_e;
// Enum: uvm_status_e
//
// Return status for register operations
//
// UVM_IS_OK - Operation completed successfully
// UVM_NOT_OK - Operation completed with error
// UVM_HAS_X - Operation completed successfully bit had unknown bits.
//
typedef enum {
UVM_IS_OK,
UVM_NOT_OK,
UVM_HAS_X
} uvm_status_e;
1 read和write操作,在操作完成后,寄存器模型都会根据读写的结果更新期望值和镜像值(二者相等);
2 peek操作通过后门访问方式读取寄存器的值,不关心DUT的行为,即使寄存器的读写类型是不能读,也可以将值读出来。对于read clear类型的field,peek读操作不会clear。所以有的时候peek和read操作结果不一样;更新镜像值;
3 poke操作通过后门访问方式写入寄存器的值,不关心DUT的行为,即使寄存器的读写类型是不能写,也可以将值写进去。对于write clear类型的filed,poke操作不会clear,所以有的时候poke和write操作结果不一样;更新镜像值;
4 set操作会更新寄存器模型中的期望值,但是不会改变镜像值。
5 get操作会返回寄存器模型中当前的期望值;
6 update操作会检查模型中的期望值和镜像值,如果两者不相等,那么将期望值更新到DUT中,并且更新镜像值。如果镜像值和期望值相同,那么不会写DUT寄存器,也就不会产生总线transaction。update与mirror操作相反。
7 mirror操作会读DUT中寄存器的值,与update操作相反。如果第二个参数check为UVM_CHECK,那么会检查读取的值与镜像值是否一样,如果不一样报错。通过mirror读取DUT的寄存器值之后,会调用predict函数,更新镜像值。mirror有两种应用场景:一是在仿真中不断调用,但此时是UVM_NO_CHECK,保证镜像值与DUT中的值相等;二是在仿真结束的时候调用,这时是UVM_CHECK检查模型中的镜像值与DUT中的寄存器值是否一致。
8 reset操作更新寄存器模型中的镜像值和期望值;
9 get_reset获取复位值;(是寄存器模型中的还是dut中的);
10 get_mirrored_value返回寄存器模型中的镜像值;
11 predict更新模型中的镜像值。新的镜像值通过value参数传入。
例如当在DUT中实现一个计数器的时候,模型中的计数器是静止的。如果想在模型中得到DUT的技术值,这就需要手动更新镜像值,又不能对DUT进行操作,这可以通过predict函数。
第三个参数是uvm_predict_e枚举类型,他有如下三个元素:

如果想要更新镜像值又不对DUT进行操作,要用UVM_PREDICT_DIRECT。
** write、read、peek和poke在完成对DUT的读写之后也会调用这个函数,更新镜像值。
12 randomize操作,寄存器模型提供randmoize接口。randomize之后,期望值将会变为随机出的数值,镜像值不会改变。但是并不是寄存器模型中所有寄存器都支持此函数。如果不支持,则randomize调用后期望值不变。若要关闭随机化功能,在reg的build中调用reg.configure时将其第八个参数设置为0即可。一般的,randomize不会单独使用,而是和update一起。如在DUT上电复位后,需要配置一些寄存器的值。这些寄存器的值通过randomize获得,并使用update任务配置到DUT中。
但是一个field能够被随机化,需要:
- 在filed的configure第八个参数设为1.
- filed为rand类型。
- filed的读写类型为可写的。
uvm_reg_ sequence 提供的方法(均是针对寄存器对象的, 而不是寄存器块或寄存器域)如下表。

结合mirrored value、 desired value 和 actual value, 我们需要理解这4种方法在调用时, 三种数值的变化时序关系:
• 对于前门访问的 read()和 write(),在总线事务完成时, 镜像值和期望值才会更新为与总线上相同的值,这种预测方式是显式预测。
• 对于 peek()和 poke(), 以及后门访问模式下的 read()和 write(),由于不通过总线,默认采取自动预测的方式,因此在方法调用返回后,镜像值和期望值也相应修改。
关于 reset()和 get_reset()的用法, 下面也给出部分例码。例如硬件在复位触发时, 会将内部寄存器值复位, 而寄存器模型在捕捉到复位事件时, 为了保持同硬件行为一致, 也应当对其复位。这里注意的是, 复位的对象是寄存器模型, 而不是硬件。
@(negedge p_sequencer.vif.rstn);
rgm. reset(); / / register block reset for mirrored value and desired value
rgm.chnl0_ctrl_reg.reset(); // register level reset
rgm.chnl0_ctrl_reg.pkt_len.reset(); // register field reset
在复位之后, 用户也可以通过读取寄存器模型的复位值(与寄存器描述文件一致), 与前门访问获取的寄存器复位值进行比较, 以此判断硬件各个寄存器的复位值是否按照寄存器描述去实现。这里的 get_reset()方法指的也是寄存器模型的复位值, 而不是硬件。
// register model reset value get and check
rstval = rgm.chnl0_ctrl_reg. get_reset() ;
rgm.chnl0_ctrl_reg.read (status, data, UVM_BACKDOOR, .parent(this));
if(rstval != data)
`uvm_error ("RSTERR", "reset value read is not the desired reset value")
mirror()方法与 read()方法类似, 也可以选择前门访问或后门访问, 不同的是, mirror()不会返回读回的数值, 但是会将对应的镜像值修改。在修改镜像值之前, 用户还可以选择是否将读回的值与模型中的原镜像值进行比较。下面的例码在更新镜像值之前, 首先将读回的值与上一次镜像值做了比对, 随后再更新镜像值。比如, 对于配置寄存器, 可以采用这种方法来检查上一次的配置是否生效, 又或者对于状态寄存器可以选择只更新镜像值不做比较, 这是因为状态寄存器随时可能被硬件内部逻辑修改。
// get register value and check
rgm.chnl0_ctrl_reg.mirror(status, UVM_CHECK, UVM_FRONTDOOR,parent(this));
下面的方法是运用 set()和 update()对寄存器做批量修改。首先 set()方法的对象是寄存器模型自身,通过set()可以修改期望值, 而在寄存器配置时不妨先对其模型随机化,再配置个别寄存器或域, 当寄存器的期望值与镜像值不相同时,可以通过update()方法来将不相同的寄存器通过前门访问或后门访问的方式做全部修改。
这种 set()和 update()的方式较 write()和 poke()的写寄存器方式更为灵活的是,它可以实现随机化寄存器配置值(先随机化寄存器模型,后将随机值结合某些域的指定值写入到寄存器),继而模拟更多不可预知的寄存器应用场景。另外,update()强大的批量操作寄存器功能使得修改寄存器更为便捷。
// randomize register model, set register/field value and update to
// hardware actual value
void'(rgm. chnlO_ctrl_reg. randomize());
rgm.chnlO ctrl reg.pkt len.set('h3);
rgm.chnlO ctrl reg. update (status, UVM FRONTDOOR, .parent (this)); void'(rgm.chnll_ctrl_reg.randomize());
rgm. chnl0_ctrl_reg. set ('h22);
rgm.update(status, UVM FRONTDOOR, .parent(this));
浙公网安备 33010602011771号