Verilog 延时控制

前言

在 Verilog 中,延迟控制语句允许您指定某些操作在执行之前应该等待多长时间,这对于准确建模和仿真硬件行为至关重要。您可以将延迟控制分类为不同的类型,包括 # delays、wait 语句和事件控制。理解这些机制将增强您有效地设计和测试硬件模型的能力。让我们深入研究这些延迟控制结构,并探索它们如何提高 Verilog 设计的精度和功能。

在 Verilog 中,延迟控制指定了数字设计中操作的时间和顺序。它通过模拟传播延迟和时序等时间方面,在精确建模数字电路行为方面起着至关重要的作用。在仿真和综合中都使用延迟控制,尽管它通常侧重于仿真以测试和验证设计行为。

Verilog 中延时控制的种类

# Delays

Verilog 中的 # 符号引入了仿真时间的延迟,称为延迟控制语句。它指定在执行下一条语句之前仿真应该等待多长时间,延迟通常以时间单位表示,如纳秒(ns)或微秒(us)。

语法

#delay_time statement;

示例

initial begin
    #10;          // Wait for 10 time units
    a = 1;        // Assign value to 'a' after the delay
    #5;           // Wait for an additional 5 time units
    b = 0;        // Assign value to 'b' after the second delay
end

在这个例子中,仿真在为 a 赋值之前等待 10 个时间单位,然后在为 b 赋值之前等待额外的 5 个时间单位。

wait Statements

wait 语句用于暂停执行,直到指定的条件变为真。这是一种事件控制形式,允许您将操作与仿真中的某些事件或条件同步。

语法

wait(condition) statement;

示例

initial begin
    a = 0;
    b = 0;
    wait (a == 1);  // Wait until 'a' is equal to 1
    b = 1;          // Assign value to 'b' after 'a' becomes 1
end

在这个例子中,仿真将等待,直到条件 a == 1 为真,然后才将值赋给 b。

Event Control

事件控制允许您等待信号值的更改或转换,这对于硬件设计中的同步和异步行为建模至关重要。最常见的事件控制构造是用于建模时钟边缘的 @(posedge_clk) 和 @(negedge_clk) 语句。

语法

@(event) statement;

示例

module event_control_example(
    input clk,
    input reset,
    input d,
    output reg q
);

    always @(posedge clk or posedge reset) begin
        if (reset) begin
            q <= 0;       // On reset, set 'q' to 0
        end else begin
            q <= d;       // On the rising edge of 'clk', update 'q' with 'd'
        end
    end
endmodule

@(posedge clk or posedge reset) 意味着代码块将在时钟信号的上升沿(clk)或复位信号的上升沿(reset)上执行。
在 always 块内部:如果断言 reset(高),q 被设置为 0。否则,在时钟的每个上升沿上,用 d 的值更新 q。

为什么我们需要 Verilog 编程语言中的延迟控制?

Verilog 中的延迟控制对于精确建模和仿真数字电路的时序行为至关重要。以下是延迟控制在 Verilog 中至关重要的原因:

1. 精确时间仿真

  • 建模传播延迟:数字电路具有固有的传播延迟,这是信号通过门和其他组件所需的时间。延迟控制允许您模拟这些延迟,更准确的表示电路在现实中的行为方式。
  • 时序验证:精确的时序仿真有助于验证设计是否满足时序限制,例如建立和保持时间、时钟到输出延迟以及其他关键时序要求。这确保了电路在硬件中实现时能够正确工作。

2. 测试与调试

  • 真实的测试场景:延迟控制使您能够通过在事件之间引入特定的时间延迟来创建真实的测试场景。这有助于测试设计如何响应不同的时序条件并识别潜在的时序问题。
  • 调试:在调试设计时,您通常需要观察信号是如何随时间变化的。延迟控制允许您插入延迟和同步事件,从而更容易跟踪和分析信号的行为并排除问题。

3. 同步

  • 时钟边缘灵敏度:在同步设计中,动作通常由时钟边缘触发。使用事件控件,如 @(posedge_clk) 和 @(negedge_clk),可确保操作与时钟信号同步,在电路的不同部分保持适当的时序对齐。
  • 时序逻辑:延迟控制有助于对时序逻辑进行建模,其中操作依赖于先前事件的时间。通过指定延迟,您可以确保时序操作以正确的顺序和适当的时间发生。

4. 真实的硬件行为

  • 模拟物理延迟:由于信号通过电线和组件传播的速度,真实的硬件具有物理延迟。延迟控制允许您模拟这些物理延迟,提供硬件将如何执行的更准确的模型。
  • 设计权衡:通过使用延迟控制,设计人员可以探索各种时间权衡,并优化设计的性能,功耗和面积。它有助于评估不同的延迟值如何影响设计的整体功能和效率。

5. 设计验证

  • 功能验证:验证设计的功能包括确保它在各种时间条件下正确地工作。延迟控制有助于验证设计是否符合预期,即使有时间变化或延迟。
  • 时间限制:设计通常具有特定的时间限制,必须满足这些时间限制才能正常运行。延迟控制有助于检查这些约束是否满足,并确保设计符合其时序规范。

6. 模拟异步行为

  • 处理异步输入:在异步输入的设计中,延迟控制有助于仿真设计如何响应独立于时钟变化的信号。这对于确保设计能够正确处理异步事件至关重要。

7. 提高设计精度

  • 微调时序:延迟控制允许设计人员微调操作的时序,提高仿真的准确性,并确保设计在各种时序条件下的行为符合预期。
  • 验证时序约束:延迟控制有助于验证时序约束是否满足,例如保持时间和建立时间,这对于设计的可靠运行至关重要。

Verilog 语言中延迟控制的优点

Verilog中的延迟控制提供了几个显著的优点,使其成为设计和仿真数字电路的强大工具。以下是其主要优点的详细介绍:

1. 精确时间仿真

  • 建模传播延迟:延迟控制允许设计人员通过门和其他组件对信号的传播延迟进行建模,以反映现实世界的行为。这种精度有助于理解信号如何通过电路,并确保满足时序限制。
  • 逼真的行为:通过引入延迟,您可以更逼真地模拟设计的时序行为,考虑信号在电路中传播和相互作用所需的时间。

2. 有效的测试和调试

  • 创建测试场景:延迟控制使您能够通过引入特定的延迟来创建精确的测试场景。这有助于测试设计如何响应不同的时序条件,并确保它在各种情况下正确运行。
  • 简化调试:当对时序问题进行故障排除时,延迟控制允许您在设计的各个点插入延迟。这有助于隔离问题和理解时序变化如何影响电路的运行。

3. 操作同步

  • 时钟边缘灵敏度:延迟控制机制,如事件控制(@(posedgeclk)),确保操作与时钟边缘同步。这对于在同步设计中保持适当的时序对齐并确保电路的不同部分和谐运行至关重要。
  • 时序逻辑:在时序电路中,延迟控制有助于管理基于先前事件完成的操作时间。这确保了时序逻辑正确地按照预定的顺序运行。

4. 逼真的硬件仿真

  • 模拟物理延迟:真实的硬件组件具有固有的物理延迟,因为它提供了更准确的硬件执行方式表示。信号通过电线和门传播。延迟控制允许你模拟这些延迟。
  • 评估设计权衡:通过调整延迟值,设计人员可以探索各种时间权衡,并优化设计的性能,功耗和面积。这有助于基于现实的时间考虑做出明智的设计决策。

5. 设计验证

  • 时序约束验证:延迟控制有助于验证设计是否满足时序约束,如建立时间、保持时间和时钟到输出延迟。这是确保设计在其预期应用中正确可靠地运行的必要保证。
  • 功能验证:验证设计的功能包括检查它在各种时间条件下的行为是否正确。延迟控制允许您测试设计对不同时序场景的响应,并确保其按预期运行。

6. 处理异步行为

  • 模拟异步输入:延迟控制有助于建模设计如何响应异步输入和独立于时钟发生的事件。这对于确保设计能够正确处理异步信号并保持适当的功能非常重要。

7. 提高设计精度

  • 微调时序:延迟控制允许设计人员微调操作的时序,提高仿真的精度,并确保设计满足其时序要求。这有助于实现更可靠和健壮的设计。
  • 测试边缘用例:通过引入延迟,您可以测试可能在真实硬件中出现的边缘用例和边缘场景。这有助于识别潜在的问题,并确保设计可以处理所有可能的时间条件。

Verilog 语言中延迟控制的缺点

虽然 Verilog 中的延迟控制提供了几个优点,但它也有一些缺点。了解这些限制对于在数字设计和仿真中有效地使用延迟控制非常重要。以下是一些主要的缺点:

1. 不可综合

  • 有限的综合使用:延迟控制结构,如 # delays 和 wait 语句,主要用于仿真,不能综合。这意味着它们不能以直接在物理硬件上实现的方式指定硬件行为。
  • 潜在的误用:设计师使用延迟控制的方式可能不能很好地转化为硬件,从而导致潜在的误解或对设计在综合时的行为的错误假设。

2. 仅可仿真的构造

  • 非代表性时序:延迟控制结构仅在仿真期间有效。它们不能准确地表示实际硬件实现中出现的真实延迟。因此,仿真可能不能完全反映硬件的真实行为。
  • 抽象表征:仿真中使用的延迟值可能与实际延迟不对应,这可能导致仿真和实际硬件性能之间的差异。

3. 潜在的过度规格

  • 过多的细节:过度使用延迟控制可能导致过于详细的仿真,这可能不会增加显著的价值。这可能会导致更复杂和更难维护的代码,并且可能并不总是必要的大量延迟语句。
  • 使设计复杂化:添加多个延迟控制会使设计复杂化,使其更难以理解和调试。它还可能掩盖了需要在更高级别上解决的潜在时间问题。

4. 时序分析的难点

  • 复杂的时序分析:延迟控制可以使时序分析更加复杂,特别是在处理复杂的时序约束和设计不同部分之间的相互作用时。这可能会使确保设计满足所有时间要求的过程复杂化。
  • 与时序约束不一致:仿真中指定的延迟可能与现实世界的时序约束不完全一致,导致仿真结果和硬件行为之间的潜在差异。

5. 对仿真性能的影响

  • 增加仿真时间:引入大量延迟控制会增加仿真时间,因为仿真器需要考虑这些延迟并管理各种事件的时间。这会减慢仿真过程,使其效率降低。
  • 性能瓶颈:在某些情况下,过度使用延迟控制可能会在仿真中产生性能瓶颈,使其难以实现实时仿真速度或有效地分析大型复杂设计。

6. 潜在的时间漏洞

  • 仿真中的时序错误:不正确地使用延迟控制会在仿真中引入时序错误。例如,如果延迟设置不正确,它们可能会导致意外行为或信号时序失调。
  • 错误的安全感:过于依赖延迟控制可能会给设计的时间正确性带来错误的安全感。只有当设计在实际硬件上进行综合或测试时,问题才会变得明显。

7. 对异步行为的有限控制

  • 处理异步信号:延迟控制并不总是提供对异步信号或事件的精确控制。这使得准确地为复杂的异步行为建模并确保设计在所有场景中都能正确运行变得非常困难。

Verilog 非阻塞赋值添加 #1 延迟

请看下面的代码:

/////////////////////////////////////////////////////////////////////
////                                                             ////
////  WISHBONE rev.B2 compliant I2C Master byte-controller       ////
////                                                             ////
////                                                             ////
////  Author: Richard Herveille                                  ////
////          richard@asics.ws                                   ////
////          www.asics.ws                                       ////
////                                                             ////
////  Downloaded from: http://www.opencores.org/projects/i2c/    ////
////                                                             ////
/////////////////////////////////////////////////////////////////////
////                                                             ////
//// Copyright (C) 2001 Richard Herveille                        ////
////                    richard@asics.ws                         ////
////                                                             ////
//// This source file may be used and distributed without        ////
//// restriction provided that this copyright statement is not   ////
//// removed from the file and that any derivative work contains ////
//// the original copyright notice and the associated disclaimer.////
////                                                             ////
////     THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY     ////
//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   ////
//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS   ////
//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR      ////
//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,         ////
//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    ////
//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE   ////
//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR        ////
//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  ////
//// LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT  ////
//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  ////
//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE         ////
//// POSSIBILITY OF SUCH DAMAGE.                                 ////
////                                                             ////
/////////////////////////////////////////////////////////////////////

//  CVS Log
//
//  $Id: i2c_master_byte_ctrl.v 1191 2014-04-11 14:44:15Z Marco $
//
//  $Date: 2014-04-11 17:44:15 +0300 (Fri, 11 Apr 2014) $
//  $Revision: 1191 $
//  $Author: Marco $
//  $Locker:  $
//  $State: Exp $
//
// Change History:
//               $Log: i2c_master_byte_ctrl.v,v $
//
//               2014/04/11 Added DisplayPort MOT support - Bitec - MDe
//
//               Revision 1.7  2004/02/18 11:40:46  rherveille
//               Fixed a potential bug in the statemachine. During a 'stop' 2 cmd_ack signals were generated. Possibly canceling a new start command.
//
//               Revision 1.6  2003/08/09 07:01:33  rherveille
//               Fixed a bug in the Arbitration Lost generation caused by delay on the (external) sda line.
//               Fixed a potential bug in the byte controller's host-acknowledge generation.
//
//               Revision 1.5  2002/12/26 15:02:32  rherveille
//               Core is now a Multimaster I2C controller
//
//               Revision 1.4  2002/11/30 22:24:40  rherveille
//               Cleaned up code
//
//               Revision 1.3  2001/11/05 11:59:25  rherveille
//               Fixed wb_ack_o generation bug.
//               Fixed bug in the byte_controller statemachine.
//               Added headers.
//

// synopsys translate_off
`timescale 1ns / 10ps
// synopsys translate_on

// bitcontroller states
`define I2C_CMD_NOP   4'b0000
`define I2C_CMD_START 4'b0001
`define I2C_CMD_STOP  4'b0010
`define I2C_CMD_WRITE 4'b0100
`define I2C_CMD_READ  4'b1000

module i2c_master_byte_ctrl (
  clk, rst, nReset, ena, clk_cnt, start, stop, read, write, ack_in, din, mot,
  cmd_ack, ack_out, dout, i2c_busy, i2c_al, scl_i, scl_o, scl_oen, sda_i, sda_o, sda_oen );

  //
  // inputs & outputs
  //
  input clk;     // master clock
  input rst;     // synchronous active high reset
  input nReset;  // asynchronous active low reset
  input ena;     // core enable signal

  input [15:0] clk_cnt; // 4x SCL

  // control inputs
  input       start;
  input       stop;
  input       read;
  input       write;
  input       ack_in;
  input [7:0] din;
  input       mot; // Middle Of Transaction - used to delay ACK/NACK output for Reads

  // status outputs
  output       cmd_ack;
  reg cmd_ack;
  output       ack_out;
  reg ack_out;
  output       i2c_busy;
  output       i2c_al;
  output [7:0] dout;

  // I2C signals
  input  scl_i;
  output scl_o;
  output scl_oen;
  input  sda_i;
  output sda_o;
  output sda_oen;


  //
  // Variable declarations
  //

  // statemachine
  parameter [4:0] ST_IDLE  = 5'b0_0000;
  parameter [4:0] ST_START = 5'b0_0001;
  parameter [4:0] ST_READ  = 5'b0_0010;
  parameter [4:0] ST_WRITE = 5'b0_0100;
  parameter [4:0] ST_ACK   = 5'b0_1000;
  parameter [4:0] ST_STOP  = 5'b1_0000;

  // signals for bit_controller
  reg  [3:0] core_cmd;
  reg        core_txd;
  wire       core_ack, core_rxd;

  // signals for shift register
  reg [7:0] sr; //8bit shift register
  reg       shift, ld;

  // signals for state machine
  wire       go;
  reg  [2:0] dcnt;
  wire       cnt_done;
  reg        mot_mem;
  
  //
  // Module body
  //

  // hookup bit_controller
  i2c_master_bit_ctrl bit_controller (
    .clk     ( clk      ),
    .rst     ( rst      ),
    .nReset  ( nReset   ),
    .ena     ( ena      ),
    .clk_cnt ( clk_cnt  ),
    .cmd     ( core_cmd ),
    .cmd_ack ( core_ack ),
    .busy    ( i2c_busy ),
    .al      ( i2c_al   ),
    .din     ( core_txd ),
    .dout    ( core_rxd ),
    .scl_i   ( scl_i    ),
    .scl_o   ( scl_o    ),
    .scl_oen ( scl_oen  ),
    .sda_i   ( sda_i    ),
    .sda_o   ( sda_o    ),
    .sda_oen ( sda_oen  )
  );

  // generate go-signal
  assign go = (read | write | stop) & ~cmd_ack;

  // assign dout output to shift-register
  assign dout = sr;

  // generate shift register
  always @(posedge clk or negedge nReset)
    if (!nReset)
      sr <= #1 8'h0;
    else if (rst)
      sr <= #1 8'h0;
    else if (ld)
      sr <= #1 din;
    else if (shift)
      sr <= #1 {sr[6:0], core_rxd};

  // generate counter
  always @(posedge clk or negedge nReset)
    if (!nReset)
      dcnt <= #1 3'h0;
    else if (rst)
      dcnt <= #1 3'h0;
    else if (ld)
      dcnt <= #1 3'h7;
    else if (shift)
      dcnt <= #1 dcnt - 3'h1;

  assign cnt_done = ~(|dcnt);

  //
  // state machine
  //
  reg [4:0] c_state; // synopsis enum_state

  always @(posedge clk or negedge nReset)
    if (!nReset)
      begin
          core_cmd <= #1 `I2C_CMD_NOP;
          core_txd <= #1 1'b0;
          shift    <= #1 1'b0;
          ld       <= #1 1'b0;
          cmd_ack  <= #1 1'b0;
          c_state  <= #1 ST_IDLE;
          ack_out  <= #1 1'b0;
          mot_mem  <= #1 1'b0;
      end
    else if (rst | i2c_al)
     begin
         core_cmd <= #1 `I2C_CMD_NOP;
         core_txd <= #1 1'b0;
         shift    <= #1 1'b0;
         ld       <= #1 1'b0;
         cmd_ack  <= #1 1'b0;
         c_state  <= #1 ST_IDLE;
         ack_out  <= #1 1'b0;
         mot_mem  <= #1 1'b0;
     end
  else
    begin
        // initially reset all signals
        core_txd <= #1 sr[7];
        shift    <= #1 1'b0;
        ld       <= #1 1'b0;
        cmd_ack  <= #1 1'b0;

        case (c_state) // synopsys full_case parallel_case
          ST_IDLE:
            if (go & mot_mem)
              begin
                // Send the ACK/NACK still left from the previous transaction
                if (stop)
                  core_txd <= #1 1'b1; // Send a NACK
                else
                  core_txd <= #1 1'b0; // Send a ACK
                core_cmd <= #1 `I2C_CMD_WRITE;
                c_state  <= #1 ST_STOP; // Go waiting for core_ack
              end
            else if (go)
              begin
                  if (start)
                    begin
                        c_state  <= #1 ST_START;
                        core_cmd <= #1 `I2C_CMD_START;
                    end
                  else if (read)
                    begin
                        c_state  <= #1 ST_READ;
                        core_cmd <= #1 `I2C_CMD_READ;
                    end
                  else if (write)
                    begin
                        c_state  <= #1 ST_WRITE;
                        core_cmd <= #1 `I2C_CMD_WRITE;
                    end
                  else // stop
                    begin
                        c_state  <= #1 ST_STOP;
                        core_cmd <= #1 `I2C_CMD_STOP;
                    end

                  ld <= #1 1'b1;
              end

          ST_START:
            if (core_ack)
              begin
                  if (read)
                    begin
                        c_state  <= #1 ST_READ;
                        core_cmd <= #1 `I2C_CMD_READ;
                    end
                  else
                    begin
                        c_state  <= #1 ST_WRITE;
                        core_cmd <= #1 `I2C_CMD_WRITE;
                    end

                  ld <= #1 1'b1;
              end

          ST_WRITE:
            if (core_ack)
              if (cnt_done)
                begin
                    c_state  <= #1 ST_ACK;
                    core_cmd <= #1 `I2C_CMD_READ;
                end
              else
                begin
                    c_state  <= #1 ST_WRITE;       // stay in same state
                    core_cmd <= #1 `I2C_CMD_WRITE; // write next bit
                    shift    <= #1 1'b1;
                end

          ST_READ:
            if (core_ack)
              begin
                  if (cnt_done)
                  begin
                    if(mot)
                    begin
                      // Delay sending ACK/NACK to beginning of next transaction
                      mot_mem <= #1 1'b1;
                      c_state  <= #1 ST_IDLE;
                      core_cmd <= #1 `I2C_CMD_NOP;

                      // generate command acknowledge signal
                      cmd_ack  <= #1 1'b1;
                    end
                    else
                    begin
                        // Proceed normally sending ACK/NACK
                        c_state  <= #1 ST_ACK;
                        core_cmd <= #1 `I2C_CMD_WRITE;
                    end
                  end
                  else
                    begin
                        c_state  <= #1 ST_READ;       // stay in same state
                        core_cmd <= #1 `I2C_CMD_READ; // read next bit
                    end

                  shift    <= #1 1'b1;
                  core_txd <= #1 ack_in;
              end

          ST_ACK:
            if (core_ack)
              begin
                 if (stop)
                   begin
                       c_state  <= #1 ST_STOP;
                       core_cmd <= #1 `I2C_CMD_STOP;
                   end
                 else
                   begin
                       c_state  <= #1 ST_IDLE;
                       core_cmd <= #1 `I2C_CMD_NOP;

                       // generate command acknowledge signal
                       cmd_ack  <= #1 1'b1;
                   end

                   // assign ack_out output to bit_controller_rxd (contains last received bit)
                   ack_out <= #1 core_rxd;

                   core_txd <= #1 1'b1;
               end
             else
               core_txd <= #1 ack_in;

          ST_STOP:
            if (core_ack)
              begin
                  c_state  <= #1 ST_IDLE;
                  core_cmd <= #1 `I2C_CMD_NOP;

                  // generate command acknowledge signal
                  if (mot_mem)
                    mot_mem  <= #1 1'b0; // Do not generate acknowledge as a new command is waiting
                  else
                    cmd_ack  <= #1 1'b1;
              end
             else
               core_txd <= #1 core_txd;

        endcase
    end
endmodule

值得注意的是,这段代码的非阻塞赋值都添加了 #1 延迟(延迟一个单元时间,取决于 `timescale 1ns / 10ps,即 1ns),尽管在 RTL 中添加延迟在综合的时候会被编译器忽略,但为什么有些代码还是添加呢?

优点:

  • 将 #1 添加到非阻塞赋值会导致输出更改延迟 1
    个时间单位。当使用波形查看器时,这通常会减轻调试任务。对于一些工程师来说,波形显示中上升时钟和输出变化之间的小延迟有时比在同一波形时间中显示时钟边沿和输出变化时更容易理解。
  • 许多高性能触发器的 hold 时间是在 0~800ps 之间。在那些驱动门级模型的 RTL 模型上加上 #1 延迟后,通常就可以修正很多与 RTL 和门级混合仿真相关的问题。当然也有例外,例如门级模型要求的 hold 时间大于 1ns,或者时钟树偏差大于 1ns。

缺点:

  • 即使没有 #1 的延迟,非阻塞赋值也会工作得很好。如果在不知道添加延迟原因的情况下在非阻塞赋值上添加 #1 延迟,那么有可能会陷入到与 RTL 和门级混合仿真相关的问题中,例如门级模型要求的 hold 时间大于 1ns,或者时钟树偏差大于 1ns,你的仿真就会失败。
  • 增加仿真时间,引入大量延迟控制会增加仿真时间,因为仿真器需要考虑这些延迟并管理各种事件的时间。这会减慢仿真过程,使其效率降低。

赋值内延迟语句(Intra-Assignment Timing Controls)和赋值间延迟语句(Inter-Assignment Timing Controls)的区别

赋值内延迟语句(Intra-Assignment Timing Controls)

reg_a = #10 reg_b;

这种情况下,是先计算 RHS(右侧方程),延时完成后再更新阻塞赋值的 LHS(左侧表达式)。

赋值间延迟语句(Inter-Assignment Timing Controls)

#10 rega = regb;

这种情况下,赋值语句需要等待延时完成,然后计算 RHS(右侧方程)并更新阻塞赋值的 LHS(左侧表达式)。

示例

`timescale 1ns / 10ps

module test;

reg a,b;
wire c1,c2;
reg c3,c4,c5,c6;

initial begin
	a = 0; b = 0;
	c3 = 0; c4 = 0; c5 = 0; c6 = 0;
    #30;
	#6 a = 1;
	#2 a = 0;
	#1 a = 1;
	#1 a = 0;
	#2 a = 1;
	#6 a = 0;
	#30;
	$stop;
end

// 赋值间延迟语句 连续赋值
assign #5 c1 = a + b;

// 赋值内延迟语句 连续赋值
//assign  c2 = #5 a +b; // 语法错误,不能这样使用

// 赋值间延迟语句 阻塞赋值
always @(*) begin 
    #5 c3 = a + b;
end

// 赋值内延迟语句 阻塞赋值
always @(*) begin
    c4 = #5 a + b;
end

// 赋值间延迟语句 非阻塞赋值
always @(*) begin
    #5 c5 <= a + b;
end

// 赋值内延迟语句 非阻塞赋值
always @(*) begin
    c6 <= #5 a + b;
end

endmodule

仿真波形如下图所示,可以看出不同延时类型的差异:
在这里插入图片描述

参考资料

关于 Verilog 延时控制的更多内容,可查看这篇文章:Clifford E. Cummings 论文详解(四)Correct Methods For Adding Delays To Verilog Behavioral Models

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值