当不完全分配一个值时,我得到一个闩锁。但是为什么在下面的示例中会出现闩锁?我认为不需要F
输出锁存器,因为它定义为的所有值SEL
。
always @ (ENB or D or A or B pr SEL)
if (ENB)
begin
Q=D;
if (SEL)
F=A;
else
F=B;
end
Run Code Online (Sandbox Code Playgroud)
尽管在的所有值中都定义了SEL
,但并非在的所有值中都定义了ENB
。如果为ENB = 0
,则您的代码会同时声明Q
和,并F
应保留上一个周期的值。这也是您链接的图像中所推断出的内容:仅更新Q
,F
如果为ENB = 1
。
如果您不想Q
成为闩锁F
,则可以执行以下操作:
always @ (ENB or D or A or B or SEL)
begin
if (ENB)
Q=D;
if (SEL)
F=A;
else
F=B;
end
Run Code Online (Sandbox Code Playgroud)
正如评论中指出的那样,我仅展示了如何实现组合逻辑和锁存器,而无需过多修改代码。但是,有些事情可以做得更好。因此,非TL; DR版本:
对锁存器建模时,请使用非阻塞分配而不是阻塞分配。Clifford E. Cummings撰写了一篇出色的论文,介绍了阻塞式分配和非阻塞式分配之间的区别,以及了解区别的重要性。我还将在这里使用本文作为源:Verilog综合中的非阻塞分配,令人难以置信的编码风格!
首先,重要的是要了解Verilog中的竞争条件是什么(Cummings):
如果按照IEEE Verilog标准的要求,更改了语句执行的顺序时,计划在同一模拟时间步执行的两个或多个语句将给出不同的结果,则会发生Verilog竞争条件。
简而言之:always
块可以以任意顺序执行,这可能会导致竞争状况,从而导致意外行为。
要了解如何防止这种情况,重要的是要了解阻塞分配和非阻塞分配之间的区别。使用阻塞赋值(=
)时,在不中断任何其他Verilog语句(即,)的情况下,对右侧(在代码A
中B
,和D
)的求值和对左侧(在代码中和)的赋值就可以完成。 ,“立即发生”)。但是,当使用无阻塞分配()时,左侧只会在时间步长结束时进行更新。Q
F
<=
可以想象,后一种分配类型有助于防止出现竞争状况,因为您可以确定分配的左侧何时更新。
在对此问题进行分析之后,卡明斯得出以下结论:
准则1:对顺序逻辑建模时,请使用非阻塞分配。
准则2:在对闩锁进行建模时,请使用非阻塞分配。
准则3:在使用Always块对组合逻辑进行建模时,请使用块分配。
我要从上述论文中强调的最后一点是“为什么”。除了可以确定推断出正确的硬件这一事实之外,在将预合成模拟与实际硬件的行为相关联时,它也有帮助:
但为什么?通常,答案与仿真有关。忽略上述指导原则[关于使用阻塞或非阻塞分配在本文的第2页上],仍可以推断出正确的合成逻辑,但是合成前的仿真可能与合成电路的行为不匹配。
如果您要严格遵守Verilog2001,那么最后一点是不可能的,但是如果您可以自由选择Verilog版本,请尝试将其always_comb
用于组合逻辑和always_latch
闩锁。这两个关键字都会自动推断出敏感度列表,并且工具可以更轻松地找出您是否实际编写了要设计的逻辑。
该
always_latch
构造与该构造相同,always_comb
除了软件工具应执行额外的检查并在always_latch构造中的行为不代表锁存逻辑时发出警告,而在always_comb构造中,工具应检查并警告行为是否不代表组合逻辑。
有了这些技巧,您的逻辑将如下所示:
always_latch
begin
if (ENB)
Q <= D;
end
always_comb
begin
if (SEL)
F = A;
else
F = B;
end
Run Code Online (Sandbox Code Playgroud)