Verilog非阻塞赋值的顺序

13

请看以下代码段(同一块):

A <= 1
A <= 2

变量A将始终被分配为2吗?还是会出现竞争条件,分配1或2?

我理解的非阻塞分配是由硬件在未来的时间分配变量A,因此结果可能是随机的。但是,这并不直观。模拟显示2总是被分配,但我想知道这对于硬件综合是否肯定如此。

4个回答

14

如果A在仿真中有两个定义,那么最后一个定义会生效。如果它们不在同一个块中,则取决于仿真器调度程序,可能会存在竞争条件,即哪个定义在仿真中是最后被定义的。

我经常看到使用这种技术,合成后从未看到过任何意外结果。

出自Verilog IEEE 1364-2005第11.4.1节确定性

begin-end块中的语句应按照它们在该begin-end块中出现的顺序执行。特定begin-end块中语句的执行可以被挂起以优先考虑模型中的其他过程; 然而,在任何情况下,begin-end块中的语句都不得以与源代码中出现的顺序不同的任何顺序执行

SystemVerilog-IEEE1800 2012也有相似规定,作为第4.6节确定性

这也适用于稀疏定义其输出的FSM。

always @(posedge clk) begin
  out_one <= 1'b0;
  out_two <= 1'b0;
  out_thr <= 1'b0;
  case (state)
    2'd1 : out_one <= 1'b1;
    2'd2 : out_two <= 1'b1;
    2'd3 : out_thr <= 1'b1;
  endcase
end

1
是的,这基本上就是我提出这个问题的原因,因为我有默认输出,只有在特定状态下才会更改。 - nehz
“这不被Verilog规范所保证或涵盖。”并不准确:SystemVerilog IEEE1800-2017第4.6节规定,“位于begin-end块中的语句应按照它们在该begin-end块中出现的顺序被执行”。 - Axel Bregnsbo

9

在你的代码中,A 的最终值与确定性有关,无论是在模拟还是综合过程中。

然而,要完全准确,如果设计中包含对 A 的触发器,则可能存在模拟综合不一致的情况。请考虑以下示例:

module test(input clk, output reg a, b);
  always @(posedge clk) begin
    a <= 0;
    a <= 1;
  end

  initial b = 0;
  always @(posedge a) begin
    b <= !b;
  end
endmodule

还有一个测试台:

module tb;
  reg clk = 0;
  always #5 clk = ~clk;

  wire a, b;
  test uut (clk, a, b);

  initial begin
    $monitor("clk=%b a=%b b=%b", clk, a, b);
    repeat (100) @(posedge clk);
    $finish;
  end
endmodule

在模拟过程中,a <= 0a <= 1两个更新操作都被推送到NBA事件区域并按顺序执行,因此a始终被设置。然而,由于a <= 0也被执行了,所以每个时钟周期都会在a上产生一个宽度为零的负脉冲。这个脉冲触发了第二个always块。以下是模拟输出(使用Icarus Verilog和Modelsim进行测试):

clk=0 a=x b=0
clk=1 a=1 b=1
clk=0 a=1 b=1
clk=1 a=1 b=0
clk=0 a=1 b=0
clk=1 a=1 b=1
clk=0 a=1 b=1
clk=1 a=1 b=0
clk=0 a=1 b=0
clk=1 a=1 b=1
clk=0 a=1 b=1
clk=1 a=1 b=0
clk=0 a=1 b=0
...

然而,在综合中,这将简单地分配a常量值1和b常量值0。(在Yosys和Xilinx Vivado上测试过)。因此,后综合仿真输出如下:
clk=0 a=1 b=0
clk=1 a=1 b=0
clk=0 a=1 b=0
clk=1 a=1 b=0
clk=0 a=1 b=0
clk=1 a=1 b=0
clk=0 a=1 b=0
clk=1 a=1 b=0
clk=0 a=1 b=0
clk=1 a=1 b=0
clk=0 a=1 b=0
clk=1 a=1 b=0
clk=0 a=1 b=0
clk=1 a=1 b=0

理论上第一行仍然可以说a=x,但每个合成工具都会优化掉a触发器,因为测试中的两个工具都这样做了。除此之外,该代码没有潜在问题,并且正如@Morgan在他的回答中正确指出的那样,这是一种非常常见的编码技术,用于定义输出信号的“默认值”,然后使用条件分配(使用if 和/或case)来编码特殊情况。

1
根据IEEE标准(例如1800-2009年版)中的“决定论”部分,如果这些语句位于begin-end块中,则在模拟中A将始终被赋值为2。
然而,该标准不保证代码将如何被合成。结果门可能取决于综合工具。但是,一个好的RTL检查工具将识别出这种错误的编码。Cadence的Hal lint工具会发出警告。

-5
从RTL的角度来看。 "A"将被分配为1和2,可以先是1再是2,也可以反过来,但你无法确定最终在begin-end块结束时会分配哪个值,它可能是1或2(作为分配的第二个值)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接