第七章 寄存器模型
1.读取寄存器模型的方法
有了寄存器之后,可以在任何耗时的phase中使用寄存器模型以前门访问或后门访问的方式来读取寄存器的值;可以在不耗时的phase中使用后门访问的方式来读取寄存器的值;
前门访问:通常模拟在总线上发出指令,进行读写操作,这个过程中,仿真时间是一直往前走的
后门访问:直接通过层次化的引用来改变寄存器的值;
简而言之,前门访问耗时,后门访问不耗时
2.寄存器的模型的单位
Uvm_reg_field
Uvm_reg
Uvm_reg_block
Uvm_reg_map:每一个reg_block中至少有一个,也通常只有一个ucm_reg_map
Map用以储存每个寄存器的偏移地址;当寄存器模型使用前门访问方式来实现读或写操作时,map就会将地址转换成绝对地址,启动一个读或写的seq,并将结果返回;
3.建造一个寄存器
在new()函数中,要将寄存器的宽度作为参数传递给super.new函数;super.new函数的另一个参数是是否要加入覆盖率的支持;
每一个派生自uvm_reg的类都有一个build函数,他与component的build_phase并不一样,它不会自动执行,需要手工调用;
Configure的参数
第一个参数:此域的父辈,即此域位于哪一个寄存器中,一般填this;
第二个参数:此域的宽度
第三个参数:此域最低位在整个寄存器的位置
第四个参数:字段的存取方式
第五个参数:是否是易失的
第六个参数:此域上电复位后的默认值
第七个参数:此域是否有复位,一般都填1
第八个参数:此域是否可以随机化
第九个参数:这个域是否可以单独存放
4.在reg_block派生的类中将reg例化
5.reg_adapter是服务于前门访问的
寄存器模式的前门访问,寄存器模型会通过seq产生一个uvm_reg_bus_op的变量;
转换器要定义好两个函数,第一个是reg2bus,其作用是将寄存器模型通过sequence发出的uvm_reg_bus_op型的变量转化成bus_sequencer能够接受的形式;第二个是bus2reg,其作用是当监测到总线上有操作时,它将收集来的tr转化成寄存器模型能够接受的形式,以便寄存器模型能够更新相应的寄存器的值。
通过前门访问进行读操作:
- 参考模型调用寄存器模型的读任务
- 寄存器模型产生seq,并产生uvm_reg_item :rw(应该就是产生一个uvm_reg_bus_op的变量)
- 产生driver能够接受的tr:bus_req=adapter.reg2bus(rw)
- 把bus_req交给bus_sequencer
- Driver得到bus_req后驱动它,得到读取的值,并将读取值放入bus_req中,调用item_done
- 寄存器模型调用adapter.bus2reg(bus_req,rw)将bus_rq的读取值传递给rw
- 将rw中的读数据返回参考模型
6.将寄存器模型集成到base_test中
至少需要在base_test中定义两个成员变量,一是reg_model,另一个是reg_sqr_adapter;
例化reg_model后,第一步要调用configure函数,其中第一个参数是parent block,由于是最顶层的reg_block,因此填写null,第二个参数是后门访问路径;第二步是调用build函数,将所有的寄存器实例化;第三步是调用lock_model函数,调用此函数后,reg_model中就不能在加入新的寄存器;第四个是调用reset函数,如果不调用此函数,那么reg_model中所有寄存器的值都是0,调用此函数后,所有寄存器额值都将变为设置的复位值;第四步需要设置好根路径hdl_root;
寄存器模型的前门访问操作最终都将有uvm_reg_map完成,因此在connect_phase中,需要将转换器和bus_sequencer通过set_sequencer函数告知reg_model的default_map,并将default_map设置为自动预测状态;
7.寄存器模型的读写
通常在refersmodel对寄存器模型进行读操作
首先参考模型中要申明寄存器模型
P_rm.s_reg.read(status,value,UVM_FRONTDOOR);
第一个参数是uvm_status_e型变量,这是一个输出用以表明读操作是否成功;第二个是读取的数值,也是一个输出;第三个是读取的方式,可以选择前门或者后门
在v_seq中对寄存器模型进行写操作
P_sequencer.p_rm.s_reg.write(status,1,UVM_FRONTDOOR);
8.后门访问的操作接口
第一类:UVM_BACKDOOR形式的read和write
第二类:peek读和poke写
二者的区别:1.使用第一类的时候要在参数中写明是前门还是后门访问,第二类是专门提供给后门访问使用的2.第一类会在进行操作时模仿DUT的行为,第二类不会理会DUT的行为;比如,对一个只读的寄存器,第一类是写不进去的,第二类可以写进去;
9.uvm_reg_file的引入主要是区分不同的hdl路径
10.create_map的参数
第一个参数是名字;第二个参数是基地址;第三个参数是系统总线的宽度,这里的单位是byte,第四个参数是大小端,最后一个参数表示是否能够按照byte寻址
11.在寄存器模型中加入一个人1024*16的存储器
12.存储器的读写操作
可以通过调用read,write,peek,poke实现;这四个任务在调用的时候需要额外加入一个offset参数,说明读取此存储器的哪个地址;
13.寄存器模型中的期望值与镜像值
镜像值用以最大可能的与DUT保持同步;
期望值可以用以更改dut中寄存器的值;
如果希望向寄存器中写入一个‘h1,方法一是直接调用write任务,将‘h1写入,这时期望值与镜像值都更新为‘h1;
另一种方法是通过set函数将期望值设置为‘h1,之后调用update任务,该任务会检查期望值和镜像值是否一致,若不一致,那么将会把期望值写入dut中,并且更新镜像值;
Get函数可以得到寄存器的期望值;
Get_mirrored_value可以得到镜像值;
14.常用操作对期望值和镜像值的影响
Read&write操作:无论通过前门还是后门访问对dut进行读写,在操作完成后,寄存器模型会根据读写的结果更新期望值和镜像值;
Peek&poke操作:在操作完成后,寄存器模型会根据读写的结果更新期望值和镜像值;
Get&set操作:set操作会更新期望值,但是镜像值不会改变;get操作会返回寄存器模型中前寄存器的期望值;
Update操作:该任务会检查期望值和镜像值是否一致,若不一致,那么将会把期望值写入dut中,并且更新镜像值;
Randomize操作:一般randomize和update一起使用;如dut上电复位后,需要配置一些寄存器的值。这些寄存器的值通过randomize获得,并使用updata任务配置到dut中
15.寄存器模型中一些内建的sequence
(1)Uvm_reg_mem_hdl_paths_seq 用以检查hdl路径的正确性
这个seq会试图读取hdl所指向的寄存器,如果无法读取,则给出错误提示;
(2)uvm_reg_hw_reset_seq用以检查上电复位后寄存器模型与dut中寄存器的默认值是否相同
(3)uvm_reg_access_seq用于检查寄存器的读写功能;
(4)uvm_mem_access_seq用于检查寄存器的读写功能;
16.predictor的使用
自动预测:依赖于driver,当driver将读取值返回后,寄存器模型会更新寄存器的镜像值和期望值;
主动预测:由monitor将从总线上收集到的transaction交给寄存器模型,后者更新相应寄存器的值
17.寄存器模型的随机化与update
对于rm来说,在uvm_reg_block,uvm_reg,uvm_reg_field级别都可以进行随机化;
但是对于某个field而言,想要随机化需要完成三个步骤;
- 在uvm_reg中定义field时,要设置为rand类型
- 在调用此field的configure函数时,第八个参数设置为0;
- 此field要支持写操作
Randomize会更新寄存器模型中的预期值,randomize完成之后调用updata任务,将随机化后的参数更新到dut中,这特别适用于在仿真开始时随机化配置参数;