【问题标题】:Verilog Blocking AssignmentVerilog 阻塞分配
【发布时间】:2014-04-29 05:12:10
【问题描述】:

我对 Verilog 有点陌生。我知道在时钟进程中我们应该使用非阻塞分配,而在非时钟进程中,我们使用阻塞分配。

我在阅读别人的代码时遇到了这个代码。

reg iowrb_int,iowrb_met;
reg iordb_int,iordb_met;
always@(*)
begin
  iowrb_int <= iowrb_met;
  iordb_int <= iordb_met;

  iowrb_met <= iowr_bar;
  iordb_met <= iord_bar;
end

我真的不确定上面的代码!我认为它没有进行任何注册,对吗?在 always@(*) 语句中使用非阻塞是否意味着什么?

在 always@(*) 语句中使用阻塞和非阻塞有什么区别吗?

【问题讨论】:

标签: verilog blocking nonblocking


【解决方案1】:

主要区别在于:

  • 在下一个赋值之前执行阻塞赋值,即阻塞下一条语句的执行。
  • 非阻塞赋值并行执行,即它们不会阻塞后面的语句的执行。

假设 a = 2 和 b = 3 然后是非阻塞赋值:

a <= 4;
b <= a; 

导致 a = 4 和 b = 2 - 赋值前 a 的值

但是

a = 4;
b = a;

将导致 a=4 和 b=4 - 阻塞分配完成后的 a 值。

合成到寄存器(锁存器或触发器)与组合逻辑的变量取决于 always 块的敏感度列表。它不依赖于使用阻塞或非阻塞赋值。

例如:

always @(*) begin
  if (enable)
     q = d;
end

这将导致 D 锁存器,因为在 enable==0 时未指定对 q 的分配,因此它需要记住是最后一次分配。

虽然

always @(*) begin
  if (enable)
    q = d;
  else
    q = f;
end

这将产生一个多路复用器(组合逻辑),因为对 q 的赋值是为启用的两种情况指定的,因此 q 不需要记住任何内容。

【讨论】:

  • 谢谢。所以在我提供的例子中,既然是always@(*)语句,那么它不会生成寄存器,对吧?
  • 这取决于always语句中的代码。我会更新答案。
  • @jamish 不能从 always @* 暗示触发器
  • @Morgan 是正确的。没有触发器,但可以从 always @* 中隐含闩锁
  • 我会避免询问寄存器,因为它很容易与reg 类型混淆,并使用触发器和锁存器
【解决方案2】:

阻塞与非阻塞是为了让您的门级(综合)与您的 RTL 模拟相匹配。据我所知,使用不同的方法来改变模拟的行为不会影响综合,因此不会影响门级的行为。

&lt;= 非阻塞有效地获取副本右侧的临时副本,并在时间步结束时进行 = 阻塞分配。

a <= b;
b <= a;

相当于:

a_temp = b;
b_temp = a;
//
a = a_temp;
b = b_temp;

该示例使用组合逻辑,即它不包含任何状态,因此所有输入都必须由所有输出定义。

always@* begin
  iowrb_int <= iowrb_met;
  iordb_int <= iordb_met;
  iowrb_met <= iowr_bar;
  iordb_met <= iord_bar;
end

当右侧更新块时,应重新触发。由于 iowrb_met 位于两侧,我不确定这对电气连接意味着什么。

虽然&lt;= 意味着复制到临时位置,但组合逻辑不具备此功能,它始终由分配驱动。

我认为在模拟中你实际上有这个:

always@* begin
  iowrb_int_temp = iowrb_met;
  iordb_int_temp = iordb_met;
  iowrb_met      = iowr_bar;
  iordb_met      = iord_bar;
  iowrb_int      = iowrb_int_temp;
  iordb_int      = iordb_int_temp;
end

在硬件中你会拥有:

always@* begin
  iowrb_int = iowrb_met;  //= iowr_bar;
  iordb_int = iordb_met;  //= iord_bar;
  iowrb_met = iowr_bar;
  iordb_met = iord_bar;
end

其中iowrb_intiowrb_met 实际上相同

使用always @(posedge clk暗示触发器
使用always @* 隐含组合逻辑,但当输出未完全从输入定义时,可以隐含锁存器。

【讨论】:

  • 这很有意义。谢谢。
  • 请再问一个问题:iowrb_int = iowr_bar 在您的上一个示例中是真的吗?
  • @user35443,是的,在硬件中,或者在综合之后,所有语句都是并行的。当 iowrb_met 改变值时,块将被重新评估,导致iowrb_int = iowr_bar
【解决方案3】:

通过将代码更改为阻塞分配,它可能会综合到锁存器和/或根据工具句柄创建逻辑等效检查不匹配。

这是通过调度程序的外观:

  • 有阻塞:

    1. *_int 信号已分配
    2. *_met 信号已分配
    3. 进入下一个时间步。
      • *_int 保留 *_met 的未更新值
  • 非阻塞:

    1. *_int 信号已分配
    2. *_met 信号已分配
    3. 检测到对*_met 的更改会导致循环返回调度程序的活动区域
    4. 重新分配*_int 信号
    5. 重新分配*_int 信号
    6. 进入下一个时间步。
      • *_int*_met 具有相同的值
      • 将 CPU 时间浪费在重新处理上。这对小型项目并不重要,但会显着增加整个大型项目的开销。

正确、逻辑等效且 CPU 友好的方法是颠倒分配顺序(在 *_int 之前分配 *_met):

always@(*)
begin
  iowrb_met = iowr_bar;
  iordb_met = iord_bar;

  iowrb_int = iowrb_met;
  iordb_int = iordb_met;
end
  1. *_int 信号已分配
  2. *_met 信号已分配
  3. 进入下一个时间步。
    • *_int*_met 具有相同的值

OR 使用*_bar 作为赋值(即如果a==b 和b==c,那么a==b 和a==c) :

always@(*)
begin
  iowrb_int = iowr_bar;
  iordb_int = iord_bar;

  iowrb_met = iowr_bar;
  iordb_met = iord_bar;
end
  1. 分配了*_int*_met 信号
  2. 进入下一个时间步。
    • *_int*_met 具有相同的值

【讨论】:

    【解决方案4】:

    正如其他人所说,在这里更改为阻塞分配实际上是行不通的。在组合 always 块中使用阻塞分配(这是建议)要求您按正确的顺序放置分配。

    在组合 always 块中使用非阻塞分配可能看起来很有吸引力,因为您可以按任何顺序进行分配,就像在 VHDL 中一样。除了性能之外,避免这种情况的一个很好的理由是它不适用于always_comb。此代码不起作用:

    always_comb begin
      tmp <= in;
      out <= tmp;
    end
    

    原因是tmp 不会成为敏感列表的一部分。如果您使用 always @(*)always @(tmp, in) 或替换为阻塞分配,它将按预期工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-04-20
      • 2011-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-27
      相关资源
      最近更新 更多