我正在使用 verilator 测试 SystemVerilog 代码,如下所示。
output [31:0] data_out1;
reg [31:0] data_out1;
always @(rst_b or addr1 or data_in1 or write_mask1)
begin
if((write_mask1 != 32'b0) && (rst_b == 1'b1))
begin
data_out1 <= 32'hxxxxxxxx;
... do something ....
end
always @(posedge clk)
begin
if((write_mask1 != 32'b0) && (rst_b == 1'b1))
begin
data_out1 <= 32'hxxxxxxxx;
... do something ..
end
它有两个
always
块,并且在每个块中,data_out1
以非阻塞方式分配。但是,它显示“对同一变量的阻塞和非阻塞赋值”错误。
我没有使用阻塞赋值,我无法理解为什么会出现这样的错误。如果有人可以提供帮助,我将不胜感激。
有两个问题:
帖子中的代码从两个always块分配相同的信号。这是不可能的,data_out1 必须由单个always 块驱动。
没有时钟的always块推断组合逻辑。组合逻辑建模的正确方法是使用阻塞赋值 (=)。
当我在您的代码片段上运行 verilator 时,我收到如下消息:
%Warning-COMBDLY: Delayed assignments (<=) in non-clocked (non flop or latch) block; suggest blocking assignments (=).
%Warning-COMBDLY: *** See the manual before disabling this,
%Warning-COMBDLY: else you may end up with different sim results.
%Error-BLKANDNBLK: Unsupported: Blocked and non-blocking assignments to same variable: data_out1
第一条警告消息表明 verilator 将您的第一个
always
块解释为组合逻辑,而不是顺序逻辑,并且它将非阻塞赋值运算符 (<=
) 视为阻塞赋值。
由于您的第一个块不使用
@(posedge clk)
,因此它被视为组合逻辑。
此外,您不应该对多个模块中的同一信号进行分配。
最后,您不应该使用非阻塞赋值来推断组合逻辑。
第二个始终块没问题(但不干净,因为 rst_b 并不单独处于第一个条件,它可能会导致某些模拟器出现问题),作为具有同步低电平有效复位的 FF 工作。问题是第一个总是块。
根据您在第一个块中使用
<=
进行的建模,如果 write_mask1
更改以触发敏感度列表,您需要根据 data_out1
的时间点
write_mask1
的 old值来分配
write_mask1
已更新为新值。这种行为是推断锁存,因为它需要内存来保存事件发生之前的旧值,但它也不是 FF 的有效行为。看来 Verilator 仍然将这个推断的锁解释为“阻塞分配”。 (顺便说一句,锁存器本身不会导致错误,因为如果您有锁存器用于时钟门控等目的,那么它确实是一种正确的建模方法)
如果是SystemVerilog(而不是Verilog),我会尝试使用
always_comb
进行组合逻辑,使用always_ff
进行顺序逻辑。 (以及 always_latch
表示闩锁)。就您而言,最简单的干净建模方法是了解哪个部分是组合的,哪个部分是顺序的。我将其建模为:
always_ff @(posedge clk) // to add "or negedge rst_b" if you want async reset
begin
if(!rst_b)
begin
data_out1 <= 32'hxxxxxxxx; // some reset value
end
else if(write_mask1 != 32'b0)
begin
data_out1 <= 32'hxxxxxxxx; // the value you want to update
end
end
// if there is no other condition, it will keep the old value based on the FF behavior
... do something ..
end
如果你真的想要有 2 个块,一个进行组合,一个进行顺序,我们在这里:
always_comb begin
if(write_mask1 != 32'b0) begin
data_out1_next = 32'hxxxxxxxx;
end
else begin
// now it's necessary to write else-clause because it's not a FF -- it has no memory
data_out1_next = data_out1;
end
end
always_ff @(posedge clk) // to add "or negedge rst_b" if you want async reset
begin
if(!rst_b) begin
data_out1 <= 32'hxxxxxxxx; // some reset value
end
else begin
data_out1 <= data_out1_next;
end
end