推断 Verilog/SystemVerilog 中的锁存器

Inferring latches in Verilog/SystemVerilog

程序块中的语句顺序执行,那么为什么块 1、块 2 或块 3 中的任何一个都没有推断出锁存器?

module testing(
    input  logic a, b, c,
    output logic x, y, z, v
    );

    logic tmp_ref, tmp1, tmp2, tmp3;

    //reference
    always_comb begin: ref_block
        tmp_ref = a & b;
        x = tmp_ref ^ c;
    end

    always_comb begin: block1
        y = tmp1 ^ c;
        tmp1 = a & b;
    end

    always @(*) begin: block2
        tmp2 <= a & b;
        z = tmp2 ^ c;
    end

    always @(c) begin: block3
        tmp3 = a & b;
        v = tmp3 ^ c;
    end
endmodule: testing

在 block1 中,在 tmp1 的新值可用之前使用分块赋值计算 y。

在 block2 中,tmp2 是使用非阻塞分配计算的,它应该将分配推迟到 always 块完成时。同时,z 是使用阻塞分配计算的,tmp2 的新值尚不可用。

块 3 中有一个不完整的敏感列表,仍然没有闩锁。

这是 Quartus II 14.1 的综合结果:

只有当我添加这个块时,才会推断出闩锁:

//infers a latch
always @(*) begin: block4
    if (c == 1'b1) begin
        tmp4 = a & b;
        w = tmp4 ^ c;
    end
end

有人可以解释为什么不完整的敏感度列表或在更新值之前使用变量不能推断出组合块中的锁存器吗?

组合块中使用的赋值类型不会影响合成。使用非阻塞 (<=) 可能会导致 RTL(预综合)到门(post-综合)模拟器不匹配。

敏感列表也是如此,合成会给出自动生成或完整列表的行为。

在时钟进程(@(posedge clk))中使用非阻塞(<=)来获得触发器的模拟行为。可以使用阻塞 (=) 以及在时钟进程中使用组合代码,但混合样式被认为是一种糟糕的编码实践。组合部分代码只是被移动到一个单独的组合块(always @*)。

latch是一种基本的记忆元件,如果电路不需要记忆就不推论了。

例如:

always @* begin: 
    v = (a & b) ^ c;
end

v完全由输入定义,不涉及内存。与 :

相比
always @* begin
  if (c == 1'b1) begin
    w = (a & b) ^ c;
  end
end

c 为 0 时,w 必须保持其值,因此推断为锁存器。

值得注意的是,虽然锁存器并不坏,但必须注意打开和关闭的时间,以确保它们捕获正确的数据。因此,推断出的锁存器通常被认为是错误的并且来自糟糕的编码。

SystemVerilog 具有以下语义暗示设计意图的语法:

always_latch begin
  if (c == 1'b1) begin
    w = (a & b) ^ c;
  end
end