Quick Start
- 准备EDA环境:安装Vivado 2021.2或更高版本(也可使用ModelSim/QuestaSim)。
- 创建工程:新建RTL项目,目标器件选择XC7A35T(Artix-7)。
- 编写测试模块:分别用阻塞赋值(
=)和非阻塞赋值(<=)实现同一逻辑(如移位寄存器或计数器),对比仿真波形。 - 运行仿真:观察两种赋值方式下信号更新的时序差异。
- 验证结论:阻塞赋值产生组合逻辑,非阻塞赋值产生时序逻辑。
前置条件
- 熟悉Verilog基本语法,了解always块和敏感列表。
- 具备基础数字电路知识(组合逻辑与时序逻辑区别)。
- 已安装并配置好仿真工具(Vivado/ModelSim/QuestaSim任一即可)。
- 建议准备一个空项目模板,便于快速开始。
目标与验收标准
- 理解阻塞赋值与非阻塞赋值的核心区别:阻塞赋值立即更新,非阻塞赋值在块结束时统一更新。
- 能够通过仿真波形区分两种赋值方式产生的电路类型(组合 vs 时序)。
- 掌握在实际设计中正确选择赋值方式的原则,避免竞争与冒险。
- 验收:在仿真中观察到阻塞赋值导致信号链式传播,非阻塞赋值产生寄存器延迟。
实施步骤
步骤1:编写阻塞赋值测试模块
module blocking_test(
input clk,
input rst_n,
input d,
output reg q1, q2
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
q1 <= 1'b0;
q2 <= 1'b0;
end else begin
q1 = d; // 阻塞赋值
q2 = q1; // 阻塞赋值,q2立即得到q1更新后的值
end
end
endmodule此代码中,q2 = q1 在同一个always块内顺序执行,q1 先被赋值为 d,然后 q2 立即得到 q1 的新值。综合后,q1 和 q2 实际上是同一个寄存器(或直接连线),因为阻塞赋值会覆盖中间状态。
步骤2:编写非阻塞赋值测试模块
module nonblocking_test(
input clk,
input rst_n,
input d,
output reg q1, q2
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
q1 <= 1'b0;
q2 <= 1'b0;
end else begin
q1 <= d; // 非阻塞赋值
q2 <= q1; // 非阻塞赋值,q2得到q1更新前的值
end
end
endmodule非阻塞赋值时,所有右值在块开始时被采样,左值在块结束时统一更新。因此,q2 <= q1 采样的是 q1 的旧值(上一个时钟周期的值),实现了一个两拍移位寄存器。
步骤3:编写测试平台并仿真
module tb;
reg clk, rst_n, d;
wire q1_blk, q2_blk;
wire q1_nblk, q2_nblk;
blocking_test u_blk(.clk(clk), .rst_n(rst_n), .d(d), .q1(q1_blk), .q2(q2_blk));
nonblocking_test u_nblk(.clk(clk), .rst_n(rst_n), .d(d), .q1(q1_nblk), .q2(q2_nblk));
initial begin
clk = 0;
forever #5 clk = ~clk;
end
initial begin
rst_n = 0; d = 0;
#20 rst_n = 1;
#10 d = 1;
#10 d = 0;
#20 $finish;
end
endmodule运行仿真后,观察波形:在阻塞赋值模块中,q1_blk 和 q2_blk 在同一个时钟沿同时变化,且 q2_blk 紧随 q1_blk 变化;在非阻塞模块中,q2_nblk 比 q1_nblk 延迟一个时钟周期。
验证结果
- 阻塞赋值:
q1_blk和q2_blk波形完全重叠,综合后为同一寄存器或组合逻辑链。 - 非阻塞赋值:
q2_nblk比q1_nblk延迟一个时钟周期,综合后为两级串联的寄存器。 - 关键差异:阻塞赋值在组合逻辑中模拟数据流,非阻塞赋值在时序逻辑中模拟寄存器传输。
排障指南
- 问题1:仿真中非阻塞赋值结果与预期不符。检查是否在同一个always块中混用了阻塞与非阻塞赋值,这会导致仿真行为不可预测。
- 问题2:综合后电路面积异常。阻塞赋值在组合逻辑中产生大量LUT,而非阻塞赋值产生寄存器;若预期为时序电路却用了阻塞赋值,可能导致逻辑错误。
- 问题3:仿真波形中出现毛刺。检查敏感列表是否完整(如缺少
negedge rst_n),或组合逻辑中使用了非阻塞赋值。
扩展:深入理解赋值机制
原因与机制分析
阻塞赋值(=)本质是“立即执行”操作:赋值语句会阻塞后续语句的执行,直到当前赋值完成。在综合时,它通常映射为组合逻辑(如多路选择器或连线),因为赋值行为不依赖时钟沿的同步。非阻塞赋值(<=)则遵循“采样-更新”两阶段模型:所有右值在时钟沿到来时被采样,然后所有左值在块结束时同时更新。这种机制保证了多个非阻塞赋值之间不会产生数据竞争,因此适合描述寄存器传输。
落地路径
- 在时序逻辑(如always @(posedge clk))中,始终使用非阻塞赋值,避免产生锁存器或竞争。
- 在组合逻辑(如always @(*))中,使用阻塞赋值,确保信号传播顺序正确。
- 避免在同一个always块中混用两种赋值方式,除非有特殊设计意图(如混合逻辑,但风险较高)。
风险边界
- 阻塞赋值在时序逻辑中可能导致综合结果与仿真不一致,因为综合工具可能将其解释为组合逻辑,而仿真中却表现为顺序执行。
- 非阻塞赋值在组合逻辑中会导致仿真出现“延迟一个时间步”的假象,综合后可能产生意外的寄存器。
- 在复杂设计中,混用赋值方式可能增加调试难度,建议通过代码审查和仿真波形双重验证。
参考资源
- IEEE Std 1364-2001, Verilog HDL语言参考手册。
- Clifford E. Cummings, "Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill!"
- Xilinx Vivado Design Suite用户指南(UG901)。
附录:常见误区总结
- 误区1:认为阻塞赋值“更快”。实际上,综合后的电路速度取决于逻辑深度,而非赋值方式。
- 误区2:在always块中随意混用赋值方式。这可能导致仿真通过但综合失败。
- 误区3:忽略敏感列表完整性。组合逻辑的敏感列表应包含所有输入信号,否则综合会生成锁存器。




