请看以下代码段(同一块):
A <= 1
A <= 2
变量A将始终被分配为2吗?还是会出现竞争条件,分配1或2?
我理解的非阻塞分配是由硬件在未来的时间分配变量A,因此结果可能是随机的。但是,这并不直观。模拟显示2总是被分配,但我想知道这对于硬件综合是否肯定如此。
如果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
在你的代码中,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 <= 0
和a <= 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
)来编码特殊情况。