Verilog非阻塞赋值添加#1延迟设计讨论-FPGA常见问题社区-FPGA CPLD-ChipDebug

Verilog非阻塞赋值添加#1延迟设计讨论

导言:

在进入文章前,先看一段代码如下,这段代码是上次发的IIC的IP核中的一段。

 

// generate clk enable signal
    always @(posedge clk or negedge nReset)
      if (~nReset)
          begin
              cnt <= #1 16'h0;
              clk_en <= #1 1'b1;
          end
      else if (rst || ~|cnt || !ena || scl_sync)
          begin
              cnt <= #1 clk_cnt;
              clk_en <= #1 1'b1;
          end
      else if (slave_wait)
          begin
              cnt <= #1 cnt;
              clk_en <= #1 1'b0; 
          end
      else
          begin
              cnt <= #1 cnt - 16'h1;
              clk_en <= #1 1'b0;
          end

 

值得注意的是,这段代码的非阻塞赋值都添加了#1(延迟一个单元时间,取决于`timescale 1ns/1ns,一般会取1ns),尽管在RTL中添加延迟在综合的时候会被编译器忽略,但为什么有些代码还是添加呢?这篇文章主要讨论添加延迟#1的原因,利弊。

 

阻塞与非阻塞

出于严谨的考虑,先简单回顾一下,阻塞赋值和非阻塞赋值。

阻塞赋值运算符是等号(“ =”)。阻塞分配之所以得名,是因为阻塞分配必须评估RHS参数并完成分配,而不会受到任何其他Verilog语句的干扰。据说该分配会“阻止”其他分配,直到当前分配完成为止一个例外是阻塞分配,该分配在阻塞运算符的RHS上具有定时延迟,这被认为是较差的编码风格。

非阻塞赋值运算符与小于或等于运算符(“ <=”)相同。非阻塞分配之所以得名,是因为该分配在一个时间步长的开始评估一个非阻塞语句的RHS表达式,并计划在该时间步长的末尾进行LHS更新。在评估RHS表达式和更新LHS表达式之间,可以评估和更新其他Verilog语句,还可以评估其他Verilog非阻塞分配的RHS表达式并安排LHS更新。非阻塞分配不会阻止其他Verilog语句被评估。

因为阻塞和非阻塞更新步骤不同,阻塞赋值不会受Verilog其它语句约束,持续的更新,而非阻塞则会受时间约束。这也是为什么很多教程会告诉你,时序逻辑用非阻塞,组合逻辑用阻塞。

 

非阻塞赋值的编码原则

(Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill!-Clifford E. Cummings)

准则1:对时序逻辑建模时,请使用非阻塞赋值。

准则2:对锁存建模时,请使用非阻塞赋值。

准则3:在使用Always块对组合逻辑进行建模时,请使用阻塞赋值。

准则4:在同一Always 模块中同时对时序逻辑和组合逻辑进行建模时,请使用非阻塞赋值。

准则5:请勿将阻塞赋值和非阻塞赋值混入同一Always模块中。

准则6:不要在不同Always模块中对同一变量进行赋值。

准则7:使用$ strobe显示使用非阻塞分配的值。

准则8:请勿使用#0延迟进行分配。

 

准则#1-#4 现在被普遍认为是 RTL 编码的良好和安全的编码风格。关于准则#5详细的讨论,请参阅(Verilog Nonblocking Assignments With Delays, Myths & Mysteries)这篇文章,一般只要知道规则就行,感兴趣可以详细研究这篇论文。

违反准则 #6 通常会在综合前和综合后仿真之间产生奇怪的不匹配,并且总额和前和综合后仿真通常在功能上都不准确。

准则#7 解释了如何在与非阻塞赋值相同的时间步长中显示非阻塞赋值的值。

准则#8 基警告说,#0 分配会导致将事件安排在不必要的中间事件队列中,结果常常令人困惑。一般而言,#0 赋值不是必需的,也不应该使用。

 

#1延迟

To delay or not to delay, that is the question!

积极的理由 #1:将 #1 添加到非阻塞赋值会导致输出更改延迟 1 个时间单位。当使用波形查看器时,这通常会减轻调试任务。(这也是我问过大多数人的回答)

 

`timescale 1ns / 1ns
module reg8 (
 output reg [7:0] q,
 input [7:0] d,
 input clk, rst_n
 );
 always @(posedge clk or negedge rst_n)
 if (!rst_n) q <= #1 8'b0;
 else q <= #1 d;
endmodule

 

这两个模型将在一个posedge clk或一个negedge rst_n之后表现出1ns的输出延迟。该延迟有效地实现了 1ns clk-to-q 或 rst_n-to-q 延迟,使用波形查看器查看时可以轻松解释。对于一些工程师来说,波形显示中上升时钟和输出变化之间的小延迟有时比在同一波形时间 tic 中显示时钟边沿和输出变化时更容易解释。

波形查看器中的小延迟还可以很容易地看到时序逻辑输出的值在时钟边沿之前是什么,通过将波形查看器光标放在时钟边沿本身,大多数波形查看工具将显示相应的靠近波形显示左侧的信号名称旁边的二进制、十进制或十六进制值。

 

积极的理由#2:大多数高性能触发器的保持时间在 0ps 到 800ps 之间。将 #1 添加到驱动门级模型的 RTL 模型通常会解决与混合 RTL 和门级模拟相关的任何问题(假设时间尺度时间步长为 1ns)。例外情况包括要求保持时间大于 1ns 的任何门级模型或时滞大于 1ns 的时钟分布模型。

 

消极的理由#1:如果在不知道添加延迟的正确原因的情况下向非阻塞分配的 RHS 添加延迟,在某些时候您可能会遇到混合 RTL 和门级模拟的问题,其中门级模型的保持时间延迟超过 1ns,或者时钟分配网络的偏斜大于1ns,仿真会失败。

 

消极的理由#2:VCS 具有针对高速循环仿真的内置优化,并且一些基于循环的仿真器(如 VCS)在将 #1 延迟添加到非阻塞分配的 RHS 时会显着减慢。

 

请登录后发表评论

    没有回复内容