欢迎光临
我们一直在努力

阻塞赋值与非阻塞赋值(Verilog/SystemVerilog)

一、核心区别(速记表)

对比维度阻塞赋值(=)非阻塞赋值(<=)
执行顺序 逐行执行,前一句完成才执行后一句 先读取所有右值,时钟沿结束后同时更新所有左值
生效时间 立即生效(同一时间步) 延迟生效(时钟沿后统一更新)
电路映射 组合逻辑(无寄存器、实时响应) 时序逻辑(寄存器、一拍延迟)
竞争冒险 时序逻辑中极易出现 时序逻辑中可避免竞争冒险
适用always块 always @(*)(组合逻辑) always @(posedge/negedge clk)(时序逻辑)

二、阻塞赋值(=):逐行执行、立即生效

1. 核心机制

阻塞赋值的关键是 “阻塞后续执行”—— 赋值语句执行时,左值会立即被右值覆盖,后续语句必须等待当前赋值完成才能启动,类似手工操作:完成前一步后,才能开展下一步。

2. 正确用法:组合逻辑(必用阻塞赋值)

verilog

// 组合逻辑:两数相加后再加1
module blocking_comb_demo(
input [2:0] in1,
input [2:0] in2,
output reg [3:0] out1,
output reg [3:0] out2
);

// 组合逻辑敏感列表:所有输入变化触发
always @(*) begin
out1 = in1 + in2; // 立即赋值,out1实时更新
out2 = out1 + 1; // 使用更新后的out1计算
end

endmodule

代入数值验证:设in1=3(011)、in2=2(010):

  • 执行out1 = in1 + in2,out1立即变为5(0101);
  • 执行out2 = out1 + 1,out2使用更新后的out1,变为6(0110);
  • 最终结果:out1=5,out2=6,符合组合逻辑实时响应特性。
  • 3. 错误用法:时序逻辑用阻塞赋值

    verilog

    // 时序逻辑中错误使用阻塞赋值
    module blocking_seq_bad(
    input clk,
    input [2:0] din,
    output reg [2:0] a,
    output reg [2:0] b
    );

    always @(posedge clk) begin
    a = din; // 时钟沿触发后,a立即更新为din
    b = a; // 立即使用更新后的a赋值给b
    end

    endmodule

    执行结果:时钟上升沿时,a先被赋值为din,b随即被赋值为更新后的a,最终a与b始终相等,综合电路易出现竞争冒险(仿真与硬件行为不一致),违背时序电路设计规范。

    三、非阻塞赋值(<=):先读右值、时钟沿后统一更新

    1. 核心机制

    非阻塞赋值的关键是 “不阻塞、延迟更新”—— 时钟沿触发时,先读取所有赋值语句的右值并暂存(不立即更新左值);待所有右值读取完成后,再将暂存的右值同时赋值给对应左值,类似批量下单:先选好所有商品(读右值),再统一付款发货(更新左值),选品顺序不影响最终结果。

    2. 正确用法:时序逻辑(必用非阻塞赋值)

    verilog

    // 时序逻辑:寄存器延迟一拍
    module nonblocking_seq_good(
    input clk,
    input [2:0] din,
    output reg [2:0] a,
    output reg [2:0] b
    );

    // 初始值:a=0,b=0
    always @(posedge clk) begin
    a <= din; // 读取din并暂存,不立即更新a
    b <= a; // 读取当前a的旧值并暂存,不立即更新b
    // 时钟沿结束后,a、b同时更新
    end

    endmodule

    按时钟周期(拍)推导:设din按1→2→3→4变化,初始a=0、b=0:

    时钟拍数触发前 a/b 值读取的右值(din/a 旧值)触发后 a/b 更新值核心结论
    第 1 拍 a=0, b=0 din=1,a 旧值 = 0 a=1, b=0 b 为上一拍的 a 值
    第 2 拍 a=1, b=0 din=2,a 旧值 = 1 a=2, b=1 b 比 a 慢一拍
    第 3 拍 a=2, b=1 din=3,a 旧值 = 2 a=3, b=2 符合寄存器延迟特性
    第 4 拍 a=3, b=2 din=4,a 旧值 = 3 a=4, b=3 无竞争冒险

    3. 错误用法:组合逻辑用非阻塞赋值

    verilog

    // 组合逻辑中错误使用非阻塞赋值
    module nonblocking_comb_bad(
    input [2:0] in1,
    input [2:0] in2,
    output reg [3:0] out1,
    output reg [3:0] out2
    );

    always @(*) begin
    out1 <= in1 + in2; // 非阻塞赋值,不立即更新out1
    out2 <= out1 + 1; // 读取out1的旧值,而非本次计算值
    end

    endmodule

    问题分析:组合逻辑无时钟,非阻塞赋值的 “延迟更新” 会导致out2读取out1的旧值,仿真易出现未知值(x),综合工具会报错或生成错误电路。

    四、关键避坑规则

  • 同一always块内禁止混用阻塞与非阻塞赋值,否则执行逻辑混乱,易引发仿真与硬件行为不一致;
  • 组合逻辑(always @(*))仅用阻塞赋值,时序逻辑(时钟触发always块)仅用非阻塞赋值;
  • 时序逻辑中使用阻塞赋值,会破坏寄存器 “一拍延迟” 的特性,引发竞争冒险。
  • 五、总结

    • 阻塞赋值(=):立即更新、顺序执行,适配组合逻辑;
    • 非阻塞赋值(<=):先读右值、时钟沿后同步更新,适配时序逻辑;
    • 核心原则:按逻辑类型选择赋值方式,杜绝混用,避免竞争冒险与电路错误。                               
    赞(0)
    未经允许不得转载:171主机测评 » 阻塞赋值与非阻塞赋值(Verilog/SystemVerilog)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址