Verilog阻止分配

时间:2014-03-21 17:24:32

标签: verilog blocking nonblocking

我对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 @(*)语句中使用阻塞与非阻塞有什么区别吗?

4 个答案:

答案 0 :(得分:2)

主要区别在于:

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

假设a = 2且b = 3然后是非阻塞分配:

a <= 4;
b <= a; 

导致a = 4且b = 2 - 分配前的值

但是

a = 4;
b = a;

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

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

例如:

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

这将导致D锁存器,因为当enable == 0时没有指定为q赋值,所以它需要记住是最后一次赋值。

虽然

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

这将导致多路复用器(组合逻辑),因为为两种情况都指定了q的赋值,因此q不需要记住任何内容。

答案 1 :(得分:1)

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

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

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是双方的,我不确定这意味着什么是电气连接。

虽然<=意味着复制到临时位置,但组合逻辑没有此功能,它始终由分配驱动。

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

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_int实际上与iowrb_met

相同

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

答案 2 :(得分:1)

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

这是调度程序的外观:

  • 阻止:

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

    1. 分配了*_int个信号
    2. 分配了*_met个信号
    3. 检测到*_met的更改会导致循环回调度程序的 Active 区域
    4. 重新分配*_int信号
    5. 重新分配*_int信号
    6. 转到下一个时间步。
      • *_int*_met
      • 的值相同
      • 浪费CPU时间进行重新处理。这对于一个小项目来说并不重要,但可以在整个大型项目中添加明显的开销。

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

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
    • 的值相同
  4. 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
      • 的值相同

答案 3 :(得分:0)

正如其他人所说,在此处更改为阻止分配实际上是行不通的。在组合始终阻止中使用阻止分配(这是建议),要求您以正确的顺序放置分配。

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

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

原因是tmp将不属于敏感度列表。如果您使用always @(*)always @(tmp, in)或替换为阻止分配,它将按预期工作。