为什么driver中使用non-blocking赋值,而monitor需要使用blocking赋值?
首先回顾一下 SystemVerilog 的 Scheduler 中的相关概念。
使用 blocking 或者 assign,在Active中更新左值,然后会触发新的评估事件,持续引发事件的评估。
使用 non-blocking 在 Active 中计算右值,在NBA中更新左值,左值的更新不会触发新的评估事件。
在使用 clocking 时进行 input 采样和 output 驱动,其中 input 如果没有任何显示的 skew 采样约束,它默认使用 #1 step
; output 如果没有任何的驱动约束,它其实是加的 #0
;
input 的几种 skew 约束:
#0: 在当前timeslot 的 Observed region 中采样
#1step: 在上一个timeslot 的postpone region 中采样,其实和当前 timeslot 中的 preponed region中采样的效果是相同的
#xps,#xns: 在时钟事件之前的特定时间采样
#x: 相对于上面的写法,它没有显示的单位,使用当前域的时钟单位,在时钟事件之前的特定时间采样
output的几种 skew 约束:
#0:在当前timeslot Re-NBA region驱动
#xps,#xns:在时钟事件之后的特定时间开始驱动
#x:相对于上面的写法,它没有显示的单位,使用当前域的时钟单位,在时钟事件之后的特定时间驱动,可以在波形上观测出来
所以 output 在 Re-NBA region 中驱动,它要求是 非阻塞赋值。对于 input 采样,它都是在特定的区域中采样,而且不会进行驱动,也就是只观测数据,并不会影响 scheduler 的重新返回,即不会引发新的评估事件,所以此时它是阻塞和非阻塞其实都可以采集到值。但是如果使用非阻塞赋值来采集数据,通常的做法是采集数据之后就发送给 scoreboard 或者其他的组件,那么可能在还没有采集完之前这个对象已经被发送给目标了,所以此时为了对方能够收到完整的值,我们一般都会使用阻塞赋值来采集 DUT 的信息。
此外我们看到在Re-NBA region 中驱动的信号,但是monitor 并不会采样到当前 timeslot 的最新值,除非在 Re-NBA region 中触发了新的 clocking event,所以 monitor 一般只会采样一次而且是当前 timeslot 中 driver 驱动前的旧值。
综合以上来看,将 driver 和 monitor 放在不同的 region 中进行,这样他们之间的顺序就是确定的。
以下是一个简单的demo:
driver 中驱动数据:
task run_phase( uvm_phase phase );
int top_idx = 0;
// Default conditions:
ADPCM.frame <= 0;
ADPCM.data <= 0;
forever begin
@(posedge ADPCM.clk);
ADPCM.frame <= 1; // Start of frame
for(int i = 0; i < 8; i++) begin // Send nibbles
@(posedge ADPCM.clk);
ADPCM.data <= req.data[3:0];
req.data = req.data >> 4;
end
ADPCM.frame <= 0; // End of frame
seq_item_port.item_done(); // Indicates that the sequence_item has been consumed
end
endtask: run
monitor 中采集数据
task run_phase( uvm_phase phase );
wb_txn txn, txn_clone;
txn = wb_txn::type_id::create("txn"); // Create once and reuse
forever @ (posedge m_v_wb_bus_if.clk)
if(m_v_wb_bus_if.s_cyc) begin // Is there a valid wb cycle?
txn.adr = m_v_wb_bus_if.s_addr; // get address
txn.count = 1; // set count to one read or write
...//You get the idea
drv 使用非阻塞赋值,会在 NBA region 中调度更新左值。monitor 中使用时钟事件触发,是在 Observed region 才会被触发,此时 DUT 中全部的阻塞和非阻塞都应该已经更新完毕,monitor 看到的就是最新值,此时它的采集是固定的,不需要使用非阻塞。 但是当前 timeslot 中driver 驱动的该数据对 monitor 是不可见。
另外一个问题是不同的clocking事件,下面是几种不同的写法,每种写法sv都会为他们生成不同的clocking event,虽然都是对时钟上升沿敏感,但是他们都有自己的clock event。
interface test(input clk);
logic data;
clocking dr_cb @(posedge clk);
output data;
endclocking
clocking mon_cb @(posedge clk);
input data;
endclocking
endinterface
1. @(vif.drv_cb);
vif.drv_cb.data <= 1'b0;
2. @(posedge vif.clk);
vif.data <= 1'b0;
3. @(vif.mon_cb);
tr.data = vif.mon_cb.data;
4. @(posedge vif.clk);
tr.data = vif.data;
5. @(posedge vif.clk);
tr.data = vif.mon_cb.data; //非法的
在上面列出的配对使用过程中前4种组合都是合法的,会有轻微的不同,但是第5个是非法的使用组合。虽然都是对时钟上升沿敏感,但是仿真过程中是会为每一种写法都创建一个clock event,第5中可能会出现clock event已经被调度到了,但是因为它是在 mon_cb clocking 中采样的,很可能会出现采样不到最新数据的情况。(很遗憾, 因为现在的EDA工具都是闭源的,我们并不能详细了解他们之间的区别,只能建议不能混合使用clocking。在SystemVerilog 2012 LRM 中14.13 “All clocking block inputs (input or inout) are sampled at the corresponding clocking event. When the same signal is an input to multiple clocking blocks, the semantics is straightforward; each
clocking block samples the corresponding signal with its own clocking event.”)