阻塞赋值和非阻塞赋值

发布于:2025-02-28 ⋅ 阅读:(14) ⋅ 点赞:(0)

1. 阻塞赋值(Blocking Assignment)

  • 语法=

  • 行为

    • 顺序执行:代码逐行执行,赋值操作会阻塞后续代码,直到当前赋值完成。

    • 立即生效赋值后的新值在当前仿真时间步即可被后续代码使用

  • 适用场景

    • 组合逻辑:用于描述组合逻辑(如 always @(*)),需要即时更新信号。

    • Testbench中的顺序操作:在测试平台中按顺序生成激励信号。

  • 代码示例

    always @(*) begin
        a = x;    // 立即赋值,a的值立即更新
        b = a + y; // 使用更新后的a计算b
    end

2. 非阻塞赋值(Non-blocking Assignment)

  • 语法<=

  • 行为

    • 并行执行:所有右侧表达式同时计算,赋值操作在当前时间步结束时统一更新

    • 保持旧值在同一个 always 块中,后续代码仍使用赋值前的旧值。

  • 适用场景

    • 时序逻辑:用于描述触发器或寄存器(如 always @(posedge clk),避免竞争条件。

    • 跨时钟域同步:确保多个寄存器的值同步更新。

  • 代码示例

    always @(posedge clk) begin
        a <= x;    // 计算x的值,但a在时间步结束时更新
        b <= a + y; // 使用赋值前的旧a值计算b
    end

 

关键区别对比

特性 阻塞赋值 (=) 非阻塞赋值 (<=)
执行顺序 顺序执行,阻塞后续代码 并行执行,不阻塞后续代码
生效时机 立即生效(当前时间步) 延迟生效(时间步结束时)
适用逻辑 组合逻辑 时序逻辑
硬件映射 组合电路(如多路选择器) 时序电路(如触发器、寄存器)
竞争风险 高(可能导致仿真与综合不一致) 低(仿真行为接近实际硬件)

经典示例

示例1:交换两个值
// 阻塞赋值(顺序执行)
always @(posedge clk) begin
    a = b;   // a立即变为b的值
    b = a;   // b变为新的a值(即原b的值)
end
// 结果:a和b的值互换!

// 非阻塞赋值(并行执行)
always @(posedge clk) begin
    a <= b;  // 记录b的值,但a尚未更新
    b <= a;  // 记录a的旧值
end
// 结果:a和b的值互换!

 

  • 阻塞赋值:实际交换(因为立即生效)。

  • 非阻塞赋值:实际交换(因为并行记录旧值)。

总结

阻塞赋值:用于组合逻辑或测试平台的顺序操作。

非阻塞赋值:用于时序逻辑的寄存器更新。

正确使用两种赋值方式是避免Verilog代码出现竞争条件和功能错误的关键!

    • 黄金规则

      • 在 always @(posedge clk) 中始终使用非阻塞赋值

      • 在 always @(*) 中始终使用阻塞赋值