考虑一个问题,当验证平台向DUT发了某些激励后,我们期望DUT中的某些状态寄存器会对我们的激励有一定的反应。我们想在scoreboard中查看此寄存器的值是否与我们期望的值一样,应该怎么做?
就目前我们所掌握的知识来说,要查看一个寄存器的值只能通过使用cpu_driver,向总线上发送读指令,并给出要读的寄存器地址来完成。要实现这个过程,需要启动一个sequence,这个sequence会发送一个transaction给cpu_driver。所以问题归结到如何在scoreboard的控制下启动一个sequence以读取寄存器。
一个简单的想法就是设置一个全局事件,然后在scoreboard中触发这个时间。在virtual sequence中则等待这个时间的到来,然后启动sequence,这里又用到了全局变量,这是相当忌讳的。
如果不用全部变量,那么可以用一个非全局事件来代替。利用config机制,分别给virtual sequence和scoreboard设置一个config_object,在此object中设置一个时间,如rd_reg_event,然后在scoreboard中触发这个事件,在virtual sequence要等到这个事件的到来:
@p_sequence.config_object.rd_reg_event;
等到了这个事件后就启动一个sequence,开始读寄存器。
上面的这两种方法都比较麻烦。如果有了register model,那么这个过程就可以简化为:
task scoreboard::main_phase(uvm_phase phase);
...
reg_model.STATUS_REGread(status,value,UVM_FRONTDOOR);
...
endtask
只要一句话就可以实现上述复杂的过程。像启动sequence,并把读取结果返回这些事情,都可以由register model来完成。
uvm_reg_field:这是register model中最小的单位,什么是reg_field?加入有一个状态寄存器,它的各个位的含意如下:
上面的这个状态寄存器一个有四个域,分别是empty,full,overflow,underflow。这4个域也就对应register model中uvm_reg_field,名字为”reserved”的并不是一个域。
uvm_reg:它比uvm_reg_field高一个级别,但是依然是比较小的单位。一个寄存器中至少有一个uvm_reg_field组成。
uvm_reg_block:它是一个比较大的单位,在其中可以加入许多uvm_reg,也可以加入其他的uvm_reg_block。一个register model中至少包含一个uvm_reg_block。
UVM_FRONTDOOR:它代表的是寄存器的访问方式,即通过模拟cpu,在总线上发出读指令,进行读写操作。在这个过程中,仿真时间(不是花费的cpu时间,而$time函数得到的时间)是一直往前走的。
UVM_BACKDOOR:它并不是通过总线进行读写操作,而是通过直接层次化的引用来改变寄存器的值。
上图表示读取寄存器的过程,其中作图为不适用register model,右图为使用register model。其中红线为读取的寄存器的值。
在没有register model之前,只能启动sequence通过FRONTDOOR的方式来读取寄存器,局限较大,在scoreboard(或者其他component)中难以控制。而有了register model之后,scoreboard只能与register model打交道,无论是发送读的指令还是读取额返回值,都可以由register model完成。有了register model后,可以在任何耗费时间的phase中使用register model以FRONTDOOR和BACKDOOR的方式来读取寄存器的值,同时还能在某些不耗费时间的phase(如check_phase)中使用BACKDOOR的方式来读取寄存器的值。
另外,register model还提供了一些任务,如mirror,updata,可以批量完成register model与DUT中相关寄存器的交互。
可见,UVM register model的本质就是重新定义了验证平台与DUT的寄存器接口,让验证人员更好的组织及配置寄存器,简化流程,减少工作量。