UVM 寄存器常规方法
uvm_reg的访问方法,更全面了解uvm_reg_block、uvm_reg、uvm_reg_field三个类提供的用于访问寄存器的方法:

将uvm_reg_sequence提供的方法(均是针对寄存器对象的,而不是寄存器块或者寄存器域)整理如下:

结合着mirrored value、desired value和actual value,我们需要理解这四种方法在调用时,三种数值的变化时序关系:
- 对于前门访问的read()和write(),在总线事务完成时,镜像值和期望值才会更新为与总线上相同的值,这种预测方式是显示预测。
- 对于peek()和poke(),以及后门访问模式下的read()和write(),由于不通过总线,默认采取自动预测的方式,因此在零时刻方法调用返回后,镜像值和期望值也相应修改。
reset()和get_reset()
关于reset()和get_reset()的用法,例如硬件在复位触发时,会将内部寄存器值复位,而寄存器模型在捕捉到复位时间时,为了保持同硬件行为一致,也应当对其复位。这里注意的是,复位的对象是寄存器模型,而不是硬件。
@(negedge p_sequencer.vif.rstn);
rgm.reset();
rgm.chnl0_ctrl_reg.reset();
rgm.chnl0_ctrl_reg.pkt_len.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()方法与read()方法类似,也可以选择前门访问或者后门访问,不同的是,mirror()不会返回读回的值,但是会将对应的镜像值修改。在修改镜像值之前,用户还可以选择是否将读回的值与模型中的原镜像值进行比较。
下面的例码在更新镜像值之前,首先将读会的值与上一次镜像值做了比对,随后再更新镜像值。譬如,对于配置寄存器,可以采用这种方法来检查上一次的配置是否生效,又或者对于状态寄存器,可以选择只更新镜像值不做比较,这是因为状态寄存器随时可能被硬件内部逻辑修改。
//get register value and check
rgm.chnl0_ctrl_reg.mirror(status,UVM_CHECK,UVM_FRONTDOOR,.parent(this));
set()和update()
下面的方法是运用set()和updata()对寄存器做批量修改。
- 首先set()方法的对象是寄存器模型自身,通过set()可以修改期望值,而在寄存器配置时不妨先对其模型随机化,再配置个别寄存器或者域,当寄存器的期望值与镜像值不相同时,可以通过update()方法来将不相同的寄存器通过前门访问或者后门访问的方式做全部修改。
- 这种set()和update()的方式较write()和poke()的写寄存器方式更为灵活的是,它可以实现随机化寄存器配置值(先随机化寄存器模型,后将随机值结合某些域的指定值写入到寄存器),继而模拟更多不可预知的寄存器应用场景,另外update()强大的批量操作寄存器功能使得修改寄存器更为便捷。
//randomize register model,set register/field value and update to hardware actual value
void'(rgm.chnl0_ctrl_reg.randomize());
rgm.chnl0_ctrl_reg.pkt_len.set('h3);
rgm.chnl0_ctrl_reg.update(status,UVM_FRONTDOOR,.parent(this));
void'(rgm.chnl1_ctrl_reg.randomize());
rgm.chnl0_ctrl_reg.set('h22);
rgm.update(status,UVM_FRONTDOOR,.parent(this));
mem与reg的联系和差别
UVM寄存器模型也可以用来对存储建模。uvm_mem类可以用来模拟RW(读写)、RO(只读)和WO(只写)类型的存储,并且可以配置存储模型的数据宽度和地址范围。
- uvm_mem不同于uvm_reg的地方在于,考虑到物理存储一旦映射到uvm_mem会带来更大的资源消耗,因此uvm_mem并不支持预测和影子存储(shadow storage)功能,即没有镜像值和期望值。
- uvm_mem可以提供的功能就是利用自带的方法去访问硬件存储,相比于直接利用硬件总线UVC进行访问,这么做的好处在于:
1.类似于寄存器模型访问寄存器,利用存储模型访问硬件存储便于维护和复用。
2.在访问过程中,可以利用模型的地址范围来测试硬件的地址范围是否全部覆盖。
3.由于uvm_mem也同时提供前门访问和后门访问,这使得存储测试可以考虑先通过后门访问预先加载存储内容,而后通过前门访问读取存储内容,继而做数据比对,这样做不但节省时间,同时也在测试方式上保持了前后一致性。同时这种方式相比于传统测试方法(利用系统函数或者仿真器实现存储加载),要在UVM框架中更为统一。 - 与uvm_reg相比,uvm_mem不但拥有常规的访问方法read()、write()、peek()和poke(),也提供了burst_read()和burst_write()。之所以额外提供这两种方法,不但是为了可以更高速通过总线BURST方式连续存储,也是为了贴合实际访问存储中的场景。
- 要实现BURST访问形式,需要考虑下面这些因素:
1.目前挂载的总线UVC是否支持BURST形式访问,例如APB不能支持BURST访问模式。
2.与read()、write()方法相比,burst_read()和burst_write()的参数列表中的一项uvm_reg_data_t value[]采用的是数组形式,不再是单一变量,即表示用户可以传递多个数据。而在后台,这些数据首先需要挂载到uvm_reg_item对象中,挂载时value数组可以直接写入,另外两个成员需要分别指定为element_kind=UVM_MEM,kind=UVM_BURST_READ。
mem中burst的访问形式
要实现BURST的访问形式,需要考虑下面这些因素:
- 在adapter实现中,也需要考虑到存储模型BURST访问的情形,实现四种访问类型的转换,即UVM_READ、UVM_WRITE、UVM_BURST_READ和UVM_BURST_WRITE。
- 对于UVM_READ和UVM_WRITE的桥接,已经在寄存器模型访问中实现,而UVM_BURST_READ和UVM_BURST_WRITE的转换,往往需要考虑写入的数据长度,例如长度是否是,4,8,16或者其它。
- 此外还需要考虑不同总线的其它控制参数,例如AHB支持WRAP模式,AXI支持out-of-order模式等,如果想要将更多的总线控制封装在adapter的桥接功能里,需要将更多的配置作为扩展配置,在调用访问方法时作为扩展信息类,传入到形式参数uvm_object_extension。
- 对于更为复杂的BURST形式,如果想要实现更多的协议配置要求,那么推荐直接在总线UVC层面去驱动,这样做的灵活性更大,且更能充分全面的测试存储接口的协议层完备性。
关注作者
- 自述
作者是一位中科大数字设计专业的研究生,水平有限,如有错误,请大家指正,想要与大家一同进步。 - 经历
曾获得国家奖学金,“高教社杯”数学建模国家二等奖等 - 陆续更新:
1.与UVM验证相关的system verilog后续内容;
2.与verilog数字设计相关的一些基础模块设计,例如FIFO,UART,I2C等的书写。
3.保研与竞赛经历等 - 微信公众号
欢迎大家关注公众号“数字IC小白的日常修炼”,期待与大家一同仗剑遨游数字IC世界。`
版权声明:本文为qq_42419590原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。