目录
1.什么是寄存器模型的前后门访问
前门访问:寄存器模型上做的读写操作,通过总线UVC实现总线上的物理时序访问,是真实的物理操作。常规的验证平台上在对寄存器进行访问的时候都是使用的前门访问。
UVM寄存器模型提供了用户一种更加灵活的寄存器访问方式,即后门访问方式。后门访问指的是利用UVM DPI(uvm_hdl_deposit()),将寄存器的操作直接作用到DUT内的寄存器变量,而不通过物理总线访问。
2.为什么要用后门访问
相比较与前门访问的方式,后门访问可以更加迅速得完成配置操作。因为它可以通过使用DPI的方式直接把数值写入RTL对应的寄存器中。事实上,后门访问和前门访问在实际应用当中是穿插使用的。因为他们各有优点,以下是摘自刘斌老师的红皮书的表格:
前门访问 | 后门访问 |
通过总线协议访问需要消耗时间,且在总线访问结束时才能结束前门访问。 | 通过UVM DPI关联硬件寄存器信号路径,直接读取或修改硬件,不需要访问时间,零时刻响应 |
一般读写只能按字(word)读写,无法直接读写寄存器域 | 可以对寄存器或寄存器域直接读写 |
依靠检测总线来对寄存器内容做预判 | 依靠auto prediction方式自动对寄存器内容做预判 |
正确反应了时序关系 | 不受硬件时序控制,对硬件做的后门访问可能发生时序冲突 |
不受总线时序功能影响 | 通过总线协议,可以有效捕捉总线错误,继而验证总线访问路径 |
3.采取后门访问时可能出现的问题
在采取后门访问配置寄存器的时候可能会出现以下的几个问题:
3.1.寄存器的映射路径不完全
有的IP模块的寄存器在做配置的时候必须把配置路径精确到field级别但有的则不必。精确到reg级别还是field级别在于该寄存器可配置的field是否可以整段被访问。例如:某个寄存器有4个field,而且这4个field是相互独立的,分别属于不同的reg类型变量或者在不同的RTL子模块中,那么这种寄存器在做后门访问的时候,配置路径必须精确到field级别。如果某个寄存器只有1个field,那么配置路径精确到reg级即可。
示例:
add_hdl_path("my_tb.my_dut");
my_block.my_reg.add_hdl_path_slice(field1 , 0 , 32 );
以上示例可以使寄存器位于my_tb.my_dut.my_block.my_reg.field1路径下的寄存器字段被后门访问。精确到字段field级别。如果要精确到reg级别不用这么麻烦,可以不必使用add_hdl_path_slice()。路径可以直接在reg做configure操作的时候写明。
my_reg.configure(my_tb.my_dut.my_block.my_reg, 3 , 3 , "RW" , 0 , 3'h0 , 1 , 1 ,0 );
如果您能够确保您的寄存器映射没有问题,但是后门访问还是不能成功,那么还有一种可能。
3.2.控制信号未激活
有的寄存器在配置完成后准备进行使用的时候可能会依赖某个控制信号,而这个控制信号不会凭空产生。有一部分控制信号是依赖从前门通过写寄存器的操作进行激活的。后门访问的寄存器配置并不消耗仿真时间,所以通过后门访问把寄存器的值配置进去,可能会使该控制信号不被激活。这就造成了寄存器可能配置成功了,但是该寄存器的操作不会起作用。这种情况下要么人为作用这个控制信号,要么只能通过前门访问配置寄存器。
我们来看一段逻辑代码:
reg [31:0] start;
assign write_e = mci_ack & mci_val &(mci_addr == 2'b11);
always @(`CSR_RST)
if(!rst_n)
start <= `LOW;
else if( mci_val & reg_addr == `MDC_ADDR)
start <= mci_data;
......
assign start_up= write_e ?start :mci_data;
在以上示例中,如果是前门访问的操做,那么在start寄存器写入的时候write_e个信号会因为匹配上了写入时的地址以及valid和ack信号而被拉起。当start信号要传到下游信号“start_up”的时候需要依赖于控制信号write_e。如果通过后门访问直接将数值配置到start寄存器则write_e不会为高。则start信号的配置虽然成功了,但是无法向下游作用。
3.3.路径映射到wire变量
请注意,如果您在做寄存器的后门访问映射的时候没有把路径映射到reg类型的变量上,而是映射到了wire类型或其他无法保持数值的变量上,则后门访问的操作也不能成功。 每一个RTL中的reg在进行上下游赋值的时候难免会传到一些wire变量上,如您在做映射的时候把路径映射到了wire类型的变量上则数值写进去后无法保持。reg和wire在时序逻辑中的区别不在这里赘述。
例如在RTL中常见的寄存器写法:
wire [31:0] ctrl_reg;
reg [7:0] field0 ;
reg [7:0] field1 ;
reg [7:0] field2 ;
reg [7:0] field3 ;
.....
assign ctrl_reg = {field0 , field1 , field2 ,field3} ;
如果将寄存器字段映射为...my_block.ctrl_reg[7:0]则该映射是无效的。因为 ctrl_reg是wire类型的变量,无法将写入的值保持下来。所以必须要映射为...my_block.field3。