在Verilog中编写用于同步寄存器更新的测试用例时,我想验证在当前时间步中分配给这些寄存器的值。例如(尽管在此示例中使用了$display
,但我希望能够用任何评估counter
的代码来代替它)
reg clk = 'b0;
always #10 clk = !clk;
reg [7:0] counter = 0;
always @(posedge clk) counter <= counter + 1;
initial begin
$monitor("monitor %0t, %0d", $time, counter);
@(posedge clk);
$display("display %0t, %0d", $time, counter);
@(posedge clk);
$display("display %0t, %0d", $time, counter);
$finish;
end
将模拟此代码
monitor 0, 0 display 10, 0 monitor 10, 1 display 30, 1 monitor 30, 2
因此,在任何特定时间步,$display
看到的counter
值与$monitor
不同。重要的是,$display
看到的值是寄存器的上一个值。这使得编写测试用例很困难。
此行为的原因是,在非阻塞分配事件(例如,counter
的更新)之前执行了评估事件(例如,在display
中对counter
的评估)然后执行$monitor
事件(IEEE Standard Verilog® HardwareDescription Language,第5章)
我想出了以下解决方法:
reg non_blocking_reg = 'b0;
always @(posedge clk) non_blocking_reg <= !non_blocking_reg;
reg updates_done;
always @* #0 updates_done = non_blocking_reg;
此代码的作用如下:
non_blocking_reg
是一个在每个时钟周期同步更新的寄存器; updates_done
是按程序分配给non_blocking_reg
的寄存器。这将导致更新non_blocking_reg
来调度updates_done
的更新事件。明确的零延迟(#0
)确保此更新事件在非活动事件队列中进行调度,并在所有非阻塞分配事件之后执行; updates_done
的更改事件一直等到所有同步寄存器更新执行完毕。使用此替代方法,我的原始代码可以编写如下:
initial begin
$monitor("monitor %0t, %0d", $time, counter);
@(updates_done);
@(updates_done);
$display("display %0t, %0d", $time, counter);
@(updates_done);
$display("display %0t, %0d", $time, counter);
$finish;
end
并打印我想要的内容:
monitor 0, 0 display 10, 1 monitor 10, 1 display 30, 2 monitor 30, 2
由于假设这是在Verilog中编写测试用例时的常见问题,所以我的第一个问题是这样做是否有更好的方法?如果不是,我的方法正确吗?
答案 0 :(得分:2)
这正是return tf.math.reduce_mean(keras.metrics.mae(y_true , y_PredChanged))
的用途。 $strobe
和$strobe
都等待,直到推迟事件区域呈现其输出。 已推迟事件区域是模拟时间更新为下一个时间戳之前的最后一点。