1.概述
dut值:寄存器的硬件真实值
镜像值&期望值:存在于寄存器模型中,是软件设置的值。镜像值(m_mirrored)会尽可能保证与DUT值一致(也有不一致的情况),期望值(m_desired)是我们希望设置修改的值。寄存器模型会为每一个寄存器创建并保存这两个值。
不同的API函数对DUT值、镜像值和期望值的影响不同。同时,不同API函数可操作的范围不同:
2. 原理介绍
2.1 read&write
read()和write()分别是对DUT寄存器进行读操作和写操作。
read()和write()完成操作后,会将DUT值更新到寄存器模型的镜像值和期望值中去。
2.1.1 read
(1)格式
前门 | ***.reg_block.register.read(参数列表); |
后门 | ***.reg_block.register.read(参数列表); |
***.reg_block.register.field.read(参数列表); |
(2)举例
(3)参数分析
status:表示操作的状态,包含三种状态:
value:表示从DUT读取到的值,是一个64bit的无符号数据
path:通常表示通过前门访问还是后门访问
(4)read工作流程(前门)
read(frontdoor)工作流程可大致分为10个步骤:
1.在程序的某处调用该方法时首先进入uvm_reg的read(frontdoor)任务,该任务的核心方法是XreadX()
2.进入到uvm_reg::XreadX()任务,核心方法是do_read()。进入到do_read()任务,首先会生成一个uvm_reg_item类型的transaction:rw,这个transaction是ral model内建的一种transaction,存储着很多寄存器相关的信息,包含地址、数值等,接着会调用map的do_read()方法,传入uvm_reg_item类型的transaction:rw
3.进入到uvm_reg_map::do_read()任务,在map里会获取到配对的adapter和sequencer,这是由用户自己设置,如:
这样,就将map、sequencer和apapter进行了绑定。接着会调用的核心方法是do_bus_read()。
4.进入到uvm_reg_map::do_bus_read()任务,这个任务首先会将传进来的uvm_reg_item类型的rw对象里的value按照bus_width切分成很多个bytes,每一byte的value存入uvm_reg_bus_op 类型的rw_access对象的data里,并组成一个队列。如下:
bus_width也是由用户自己设置,如:
ceate_map的第三个参数8就是bus_width。所以,传输给adapter的transaction并不是原始的uvm_reg_item类型的rw对象,而是切片后的uvm_reg_bus_op 类型的rw_access对象。
该对象产生之后,调用reg2bus()利用adapter传输信息。
5.adapter里的reg2bus会将uvm_reg_bus_op 类型的rw_access对象转换成uvm_sequence_item类型的tr。
6.uvm_sequence_item类型的tr通过sequencer发送给driver,driver通过get_next_item()从sequencer获取transaction并将其驱动到DUT
7.driver从DUT拿到读取的数据经过sequencer传回,apapter的bus2reg()将其转化为uvm_reg_bus_op 类型的transaction,返回到uvm_reg_map::do_bus_read()后多个以bus_width为单位的transaction里的data会合并成uvm_reg_item类型的rw对象里的value,组成从DUT寄存器读取到的值。
8.value值拿到后标志着总线操作的完成,接着会进入到更新寄存器模型的镜像值和期望值过程中。该更新过程被称为预测机制,预测机制一般分为自动预测和显示预测两种。当设置了:
采用的便是自动预测方式。自动预测方式会调用do_predict()
9.进入到uvm_reg::do_predict(),会针对寄存器的不同field遍历,每一个field都会do_predict()
10.进入到uvm_reg_field::do_predict(),会将读取的value值对应的filed_val更新给m_mirrored和m_desired
2.1.2 write
(1)格式
前门 | ***.reg_block.register.write(参数列表); |
后门 | ***.reg_block.register.write(参数列表); |
***.reg_block.register.field.write(参数列表); |
(2)举例
(3)参数分析
常用前三个参数。第二个参数是需要向DUT寄存器写入的值,其他参数见read参数分析。
2.2 set&get&update
set()和get()操作的对象只是寄存器模型中的期望值。并不会更新其他值。要想将期望值更新到DUT中,需要借助update()。
update()的作用是先比较寄存器模型中期望值和镜像值是否一致,如若不一致,则将期望值写入DUT中,并同步更新寄存器模型的镜像值。
2.2.1 set
(1)格式
***.reg_block.register.set(参数列表); | |
***.reg_block.register.field.set(参数列表); |
(2)举例
2.2.2 get
调用get()的对象可以是uvm_reg,也可以是uvm_reg_field,不能是uvm_reg_block。
(1)格式
***.reg_block.register.get(); | |
***.reg_block.register.field.get(); |
注:无论是register还是field调用get,返回的均是64bit的无符号数据。
(2)举例
2.2.3 update
(1)格式
前门 | ***.reg_block.register. update(参数列表); |
***.reg_block. update(参数列表); | |
后门 | ***.reg_block.register. update(参数列表); |
***.reg_block. update(参数列表); |
(2)举例
(3)update()工作流程
1.update()首先会判断是否needs_update,比较的是期望值和镜像值,不相等时才会执行后续动作。
2.调用write()方法(见write()工作流程),写入的是期望值,即将期望值写入DUT,并同步更新镜像值。
2.3 mirror&predict
mirror()和read()操作十分相似,也会从DUT中读取值,读取后也会将读取到的DUT值更新到寄存器模型的期望值和镜像值中。不同的是,mirror()不会将读取到的DUT值输出,同时,mirror还有check功能,当check打开时,比较DUT值和镜像值,若不同,会给出error信息。需要注意的是,无论是否check,无论check的结果是一致还是不一致,mirror()都会更新镜像值和期望值。
在一些具有计数功能的寄存器中,DUT计数器会不断累加,但是配对的寄存器模型中的寄存器是静止的,为了保证DUT寄存器和寄存器模型的寄存器计数保持一致,需要另一种专门的方法只对寄存器模型的镜像值和期望值更新,而不会对DUT值更新,这就是predict。
2.3.1 mirror
(1)格式
前门 | ***.reg_block.register. mirror(参数列表); |
***.reg_block. mirror(参数列表); | |
后门 | ***.reg_block.register. mirror(参数列表); |
***.reg_block. mirror(参数列表); |
(2)举例
(3)mirror()工作流程
- mirror()读取镜像值(旧)
- mirror()调用XreadX()方法(见read工作流程),读取DUT值,并更新镜像值和期望值
- 是否check?是,将镜像值(旧)和DUT值进行比较,不一致给出error
2.3.2 predict
(1)格式
***.reg_block.register.predict(参数列表); | |
***.reg_block.register.field.predict(参数列表); |
(2)举例
(3)predict()工作流程
(1)predict()将要写入的值写入到m_mirrored和m_desired。
2.4 peek&poke
peek&poke可以理解为后门操作的read&write,不同的是,前两者对DUT寄存器的访问不管寄存器的属性,后者的后门访问需要考虑寄存器的属性,即需要根据DUT寄存器的属性读写合适的值(例如:寄存器是RC类型的,poke也能写进去,但是write backdoor写不进去;寄存器是RC类型的,peek会读但不会清,read backdoor会读清)。
2.4.1 peek
(1)格式
后门 | ***.reg_block.register.peek(参数列表); |
***.reg_block.register.field.peek(参数列表); |
(2)举例
2.4.2 poke
(1)格式
后门 | ***.reg_block.register.poke(参数列表); |
***.reg_block.register.field.poke(参数列表); |
(2)举例
2.5 get_registers
作用:得到ral model的所有寄存器
举例:
2.6 get_name
作用:得到寄存器的名字
举例:
2.7 get_reset
作用:得到ral model寄存器的复位值
举例:
2.8 get_fields
作用:获取寄存器的所有域
举例:
2.9 get_access
作用:获取寄存器域的访问属性
举例:
2.10 get_n_bits
作用:获取域的width
举例:
2.11 get_reg_by_offset
作用:通过给出寄存器地址的形式获取寄存器
举例: