Quick Start
在 Vivado 或 Quartus 中新建一个 Verilog 工程,选择任意 FPGA 器件(如 XC7A35T)。创建一个顶层模块 blocking_nonblocking,包含时钟 clk、复位 rst_n、输入 d 和输出 q_blocking、q_nonblocking。编写两个 2 级移位寄存器:一个使用阻塞赋值(=),另一个使用非阻塞赋值(<=)。
前置条件
- 熟悉 Verilog 基本语法,了解 always 块与敏感列表。
- 已安装 Vivado 或 Quartus 开发环境,并具备基本工程创建能力。
- 理解寄存器传输级(RTL)设计概念,包括时钟边沿触发行为。
目标与验收标准
- 目标:通过对比实验,深入理解阻塞赋值与非阻塞赋值在时序逻辑中的行为差异,掌握各自的适用场景。
- 验收标准:
实施步骤
步骤 1:创建工程与模块
- 在 Vivado 中新建工程,器件选择 XC7A35T-1CSG324C。
- 添加设计源文件
blocking_nonblocking.v。 - 定义端口:
input clk, rst_n, d;output reg [1:0] q_blocking, q_nonblocking。
步骤 2:编写阻塞赋值移位寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
q_blocking[0] <= 1'b0;
q_blocking[1] <= 1'b0;
end else begin
q_blocking[0] = d;
q_blocking[1] = q_blocking[0];
end
end步骤 3:编写非阻塞赋值移位寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
q_nonblocking[0] <= 1'b0;
q_nonblocking[1] <= 1'b0;
end else begin
q_nonblocking[0] <= d;
q_nonblocking[1] <= q_nonblocking[0];
end
end步骤 4:编写仿真测试文件
- 生成时钟周期 10 ns,复位低有效。
- 输入
d在复位释放后依次变为 1、0、1,间隔 2 个时钟周期。 - 运行仿真至少 100 ns,观察波形。
步骤 5:综合与实现
- 运行综合,查看 RTL 原理图。
- 对比两个移位寄存器的硬件结构差异。
- 运行实现,检查时序报告,确认无建立时间违例。
验证结果
仿真波形分析
- 非阻塞赋值:
q_nonblocking[0]在时钟上升沿后延迟一个 delta 周期更新为d,q_nonblocking[1]在同一个时钟沿后更新为q_nonblocking[0]的旧值,实现正确移位。 - 阻塞赋值:
q_blocking[0]立即更新为d,然后q_blocking[1]立即更新为q_blocking[0]的新值,导致两个寄存器在同一个时钟沿同时获得d的值,移位功能失效。
综合结果对比
- 非阻塞赋值版本综合为两个级联的 D 触发器,符合预期。
- 阻塞赋值版本综合后可能产生组合逻辑反馈或锁存器,取决于综合工具优化策略。
排障指南
- 问题:仿真波形显示非阻塞赋值移位寄存器输出未按预期延迟
原因:测试文件中未正确设置输入d的变化时机,或时钟边沿对齐错误。确保d在时钟上升沿之前稳定。 - 问题:综合后网表出现锁存器警告
原因:阻塞赋值在 always 块中未覆盖所有分支,或敏感列表不完整。检查条件语句是否完备。 - 问题:时序报告显示建立时间违例
原因:阻塞赋值导致的组合逻辑路径过长。改用非阻塞赋值并重新综合。
扩展应用
- 多级流水线设计:在流水线寄存器中必须使用非阻塞赋值,确保数据在时钟边沿同步传递。
- 组合逻辑建模:在描述组合逻辑的 always 块(如敏感列表为
@(*))中,应使用阻塞赋值,以模拟硬件中连续赋值的行为。 - 混合赋值风格:同一个 always 块中不要混用阻塞与非阻塞赋值,否则会导致仿真与综合行为不一致。
参考资源
- IEEE Std 1364-2001, Verilog Hardware Description Language
- Clifford E. Cummings, "Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill!"
- Xilinx Vivado Design Suite User Guide: Synthesis (UG901)
附录:完整代码示例
module blocking_nonblocking (
input clk,
input rst_n,
input d,
output reg [1:0] q_blocking,
output reg [1:0] q_nonblocking
);
// 阻塞赋值移位寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
q_blocking[0] <= 1'b0;
q_blocking[1] <= 1'b0;
end else begin
q_blocking[0] = d;
q_blocking[1] = q_blocking[0];
end
end
// 非阻塞赋值移位寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
q_nonblocking[0] <= 1'b0;
q_nonblocking[1] <= 1'b0;
end else begin
q_nonblocking[0] <= d;
q_nonblocking[1] <= q_nonblocking[0];
end
end
endmodule


