在当前仿真时间内读取无阻塞更新的值

时间:2020-02-06 15:24:49

标签: verilog

在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;

此代码的作用如下:

  1. non_blocking_reg是一个在每个时钟周期同步更新的寄存器;
  2. updates_done是按程序分配给non_blocking_reg的寄存器。这将导致更新non_blocking_reg来调度updates_done的更新事件。明确的零延迟(#0)确保此更新事件在非活动事件队列中进行调度,并在所有非阻塞分配事件之后执行;
  3. 因此,等待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中编写测试用例时的常见问题,所以我的第一个问题是这样做是否有更好的方法?如果不是,我的方法正确吗?

1 个答案:

答案 0 :(得分:2)

这正是return tf.math.reduce_mean(keras.metrics.mae(y_true , y_PredChanged)) 的用途。 $strobe$strobe都等待,直到推迟事件区域呈现其输出。 已推迟事件区域是模拟时间更新为下一个时间戳之前的最后一点。