【问题标题】:If statements causing latch inference in Verilog?如果语句导致 Verilog 中的闩锁推断?
【发布时间】:2013-11-17 21:28:50
【问题描述】:

我正在编写一个用于合成算法的 Verilog 代码,我对哪些情况可能导致推断出锁存器感到有些困惑。下面是一段这样的代码,虽然它在模拟中运行良好,但我担心它可能会导致硬件问题。

always@(b1 or b2)

.....
// b1_map,b2_map,m1_map & m2_map are derived from combinational functions using b1 & b2
.....

if(b1_map<=20 && m2_map<=20 && b1_map>=0 && m2_map>=0)



begin
accumulator1[b1_map][m2_map]= accumulator1[b1_map][m2_map] + 1;
end

if(b2_map<=20 && m2_map<=20 && b2_map>=0 && m2_map>=0)

begin
accumulator2[b2_map][m2_map]= accumulator2[b2_map][m2_map] + 1;
end

//accumulator1  & accumulator2 are 2d registers mapped like 2d arrays

因此,在这种情况下,我希望仅在数据处于指定范围内时才映射数据。是否会因为没有定义“其他”场景而推断出闩锁?我没有添加 else 语句,因为如果数据不在限制范围内,我不想对它做任何事情。

【问题讨论】:

  • 这对我有什么帮助?我希望这个块只在“b1”和“b2”数据存在时运行,这只会在程序的后面发生。
  • 您的代码看起来会生成大量锁存器,并且可能会导致时序收敛等问题。看起来您基本上是在对同一位置执行内存读取和写入操作。这更适合在同步的 always @(posedge clk) 块中实现。是否有一个非常令人信服的理由必须是异步的?
  • 用于将值映射到内存寄存器的数据“b1”和“b2”并不总是可用的(每500~600ps可用一次)。所以,我认为如果我在单独的“always(b1 or b2)”块中使用它会更好。
  • 你似乎有一个根本的误解。综合创造硬件。您不会“运行”硬件,它总是在那里做某事。说信号“不总是可用”是没有意义的,因为信号总是存在于硬件中并且具有一定的价值。您需要告诉合成器您希望硬件在所有可能的条件下做什么,否则它会做出(可能不正确的)假设。

标签: verilog fpga


【解决方案1】:

如果你正确地编写你的 if 语句,你会没事的。当通过 if 语句的路径不更新一个或多个输出时,会生成锁存器。如下代码会生成一个锁存器:

always @* begin
    if (a) begin
        b <= c & d;
        e <= f;
    end else begin
        b <= c | d;
    end
end

注意只有当 a 为真时才分配 e?这需要一个锁存器才能正确实现。

另一种会产生锁存器的可能性是当敏感度列表不包含代码中使用的信号时。

always @(a) begin
    if (a) begin
        b <= c & d;
    end else begin
        b <= c | d;
    end
end

此代码将在 c 和 d 或 b 上生成锁存器,因为它只允许在 a 更改时更新 b。

【讨论】:

  • 我明白了,但是你认为我的代码也有问题吗?模拟在 Modelsim 中完美运行,但 Quartus 2 给了我关于累加器 1 和累加器 2 的闩锁推断警告。
  • accumulator1 和 accumulator2 不在敏感列表中,所以会产生锁存器。但是,由于您将 accumulator1 分配回 accumulator1,因此不能将其包含在敏感度列表中。因此,您要么必须使用锁存器,要么编写代码,以免在异步块中将信号分配回自身。
  • 可以用闩锁住吗?
  • 除非没有其他好的选择,否则您通常希望避免使用它们。通常异步设计在 FPGA 上表现不佳。您的设计需要异步是否有令人信服的理由?如果不是,那么您应该重写它,以便您的设计是同步的。这通常可以让您以更快的时钟运行您的设计。
  • 我没有看到为什么我必须保持异步的原因,我发现这是一种更简单的方法。可能会尝试重写代码以避免这种情况。如果我能让这部分在 always@(posedge clk) 块中正常工作,就可以避免闩锁,对吧?
【解决方案2】:

你误解了你的问题(你不应该接受答案) - 这个问题与“闩锁”没有根本性的关系。你的代码没有 感觉(用于综合)。 Quartus 知道这一点,它基本上是在告诉你 重写你的代码。

你有一个组合块,当 信号变化。两个问题:(1)这肯定不是你真正想要的 硬件,并且 (2) 当 b1 时,数字必须保持不变,并且不能增加 b2 不变。第二个问题是 Quartus 报告的问题 - 您的 电路需要某种内存,它报告为“锁存器”。它不是 足够聪明地报告第一个问题,这是真正的问题。

尝试使用真实硬件将您的电路绘制为原理图。 'any' 是什么意思 更改 b1 或 b2' 实际上是什么意思?你将如何保持价值 b1 和 b2 不变时的累加器?电路不是不可能的, 但这远远超出了 SO 问题。

让你的电路同步,在时钟边沿触发,只有一个时钟 (可能是重置)在敏感度列表中,并准确保留内脏 相同。您的 if 声明没有任何问题,因为您实际上 如果没有发生任何有趣的事情,希望累加器保持不变 在 b1/b2 上。

【讨论】:

  • 谢谢。实际上,我现在已经对我的程序进行了很多更改,并且 Quartus 不再警告我任何锁存器。现在看看我的问题,我知道我把一些基本原理搞混了。从那时起我就学会了,并且能够将上述块合并到我的同步块中。
【解决方案3】:

推断的锁存器可能来自不完整的敏感度列表或不完整的分配。

组合块的敏感度列表通常应使用always @* 编写。这样可以避免更新代码时出现编码错误。组合块(不包括边缘敏感度的块)一旦合成,将以always @* 的方式执行。具体命名信号会增加工作量,并可能导致 RTL 到门级(综合后)错误。

暗示值必须保持的不完整分配将推断出一个锁存器。闩锁本身并不坏,但需要仔细考虑。以这种方式推断出一个人会消除你对它的想法的充实和控制。这可能导致复杂的时序问题。因为锁存器是电平敏感的而不是边沿敏感的。理想情况下,您希望锁存器在时钟周期的第一个周期内打开,以便在从中读取数据时将其关闭。推断的锁存器移除了这个控制。

用 else 来完成 if 语句,默认情况下,case 响板设置一个合理的值(0)可以帮助避免这些意外的闩锁。

【讨论】:

  • 可以在同时包含组合和顺序的块中使用 always@(*) 吗?
  • @user2045143 不,总是@* 表示组合,总是@(posedge clk) 是顺序的。这些决定了何时触发块执行、何时信号更改(* 表示通配符任何信号)或时钟边缘。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多