目录

  1. 过程赋值概述
  2. Verilog 中的赋值类型
    • 阻塞赋值(Blocking Assignment)
    • 非阻塞赋值(Non-blocking Assignment)
  3. 阻塞赋值与非阻塞赋值的区别
  4. 赋值语句应用实例
  5. 参考资料

过程赋值概述

在 Verilog 中,赋值语句通常用于对信号或变量进行值的更新。在过程结构中,赋值是用来描述信号随时间的变化行为。过程赋值的类型主要有两种:阻塞赋值(Blocking Assignment)和非阻塞赋值(Non-blocking Assignment)

  • 阻塞赋值:是常见的赋值形式,按照顺序执行赋值,后续赋值操作会被阻塞直到当前赋值完成。
  • 非阻塞赋值:允许赋值语句并行执行,赋值的更新会在当前仿真时间段结束后生效。

Verilog 中的赋值类型

阻塞赋值(Blocking Assignment)

阻塞赋值使用 = 符号进行,赋值语句会按顺序执行。当前赋值语句会阻塞后续赋值的执行,直到赋值完成。

语法:

variable = expression;

  • 执行顺序:按顺序执行,每个赋值会立即生效,后续赋值会等当前赋值完成后才执行。

示例:

module blocking_example;
    reg a, b, c;

    initial begin
        a = 0;   // 阻塞赋值:先给 a 赋值为 0
        b = 1;   // 阻塞赋值:接着给 b 赋值为 1
        c = a & b; // 阻塞赋值:最后计算 c = a & b, 结果为 0
    end
endmodule

在这个例子中,c 的值会在 ab 都被赋值后才会计算。阻塞赋值按顺序执行,导致赋值结果严格按照代码中的顺序进行。

非阻塞赋值(Non-blocking Assignment)

非阻塞赋值使用 <= 符号进行,它允许多个赋值语句并行执行。赋值的更新会在当前时间段结束后生效,而不是立即生效。

语法:

variable <= expression;

  • 执行顺序:赋值会被推迟到仿真时间片结束时应用,这意味着赋值的结果不会影响同一时间段内的后续赋值。

示例:

module non_blocking_example;
    reg clk, a, b, c;

    always @ (posedge clk) begin
        a <= b;    // 非阻塞赋值:a 的值在下一个时钟周期更新
        b <= c;    // 非阻塞赋值:b 的值也在下一个时钟周期更新
        c <= a & b; // 非阻塞赋值:c 的值在下一个时钟周期更新
    end
endmodule

在这个例子中,abc 的赋值是非阻塞的,因此它们会在同一时钟周期结束时更新。此时,c 的计算不会立即反映在下一次时钟周期,而是等待所有赋值都完成之后一起更新。


阻塞赋值与非阻塞赋值的区别

特性阻塞赋值 (=)非阻塞赋值 (<=)
执行顺序按顺序执行赋值,后续赋值在前一个赋值完成后才会执行。赋值操作并行进行,结果在当前仿真时间段结束后更新。
适用场景多用于组合逻辑和需要严格顺序的情况。多用于时序逻辑,描述寄存器或时钟边沿触发的行为。
对其他信号的影响当前赋值会立即影响后续赋值。赋值结果不会立即生效,保持当前值直到下一个时间步。
赋值更新时机赋值立即生效。赋值会在当前仿真时间步结束后生效。

示例对比:

module blocking_vs_nonblocking;
    reg clk, a, b, c;

    always @ (posedge clk) begin
        a = b;     // 阻塞赋值
        b = c;     // 阻塞赋值
        c = a & b; // 阻塞赋值
    end

    always @ (posedge clk) begin
        a <= b;    // 非阻塞赋值
        b <= c;    // 非阻塞赋值
        c <= a & b; // 非阻塞赋值
    end
endmodule

在阻塞赋值情况下:

  1. a 会在当前时钟周期内立即更新为 b 的值。
  2. b 会在当前时钟周期内立即更新为 c 的值。
  3. c 的值会立即更新为 a & b 的结果。

在非阻塞赋值情况下:

  1. a 会在当前时钟周期结束后更新为 b 的值。
  2. b 会在当前时钟周期结束后更新为 c 的值。
  3. c 会在当前时钟周期结束后更新为 a & b 的结果。

赋值语句应用实例

1. 使用阻塞赋值描述组合逻辑

module comb_logic_example(input a, b, output reg c);
    always @ (a or b) begin
        c = a & b; // 阻塞赋值:执行逻辑与运算
    end
endmodule

2. 使用非阻塞赋值描述时序逻辑

module seq_logic_example(input clk, reset, output reg [3:0] counter);
    always @ (posedge clk or posedge reset) begin
        if (reset)
            counter <= 4'b0000;  // 非阻塞赋值:计数器重置
        else
            counter <= counter + 1;  // 非阻塞赋值:计数器加 1
    end
endmodule

在该例中,counter 的值会在时钟上升沿触发时更新,并且通过非阻塞赋值确保多个信号的更新不会相互阻塞。


参考资料