FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
登录
首页-技术文章/快讯-技术分享-正文

Verilog 阻塞与非阻塞赋值深度对比:移位寄存器仿真验证指南

二牛学FPGA二牛学FPGA
技术分享
4小时前
0
0
2

Quick Start:10 分钟直观对比

本指南通过一个简单的 3 位移位寄存器示例,帮助你在 10 分钟内直观理解阻塞赋值与非阻塞赋值的核心区别。请严格按以下步骤操作。

  1. 创建工程:在 Vivado 或 Quartus 中新建工程,器件任意(如 xc7a35t)。
  2. 编写阻塞赋值代码:新建 Verilog 文件,输入阻塞赋值移位寄存器代码(见下文)。
  3. 编写非阻塞赋值代码:新建另一个 Verilog 文件,输入非阻塞赋值移位寄存器代码。
  4. 编写 Testbench:新建仿真文件,例化两个模块,施加相同的时钟和复位信号。
  5. 运行行为仿真:使用 Vivado Simulator 或 Modelsim,仿真时长设为 1 μs。
  6. 观察波形:将内部信号 q0、q1、q2 添加到波形窗口。
  7. 对比结果:观察时钟上升沿前后的赋值行为——阻塞赋值在同一时钟沿立即更新,非阻塞赋值在时钟沿后统一更新。
  8. 验证预期:阻塞赋值移位寄存器在第一个时钟沿后所有位同时变化(并非真正移位),非阻塞赋值则正确实现逐位移位。

前置条件与环境

项目推荐值说明替代方案
器件/板卡任意 FPGA(如 Xilinx Artix-7)纯仿真验证,无硬件依赖Altera Cyclone IV
EDA 版本Vivado 2023.1 或 Quartus Prime 20.1行为仿真功能一致Modelsim / Questa / VCS
仿真器Vivado Simulator内置于 Vivado,无需额外安装Modelsim / Questa / VCS
时钟/复位时钟周期 10 ns,异步复位高有效时钟周期可调,复位极性可改
接口依赖无外部接口纯仿真验证,无需约束文件

注意:仿真无需添加时序约束;若后续进行综合,则需添加时钟约束。

目标与验收标准

  • 功能点:实现 3 位移位寄存器,输入 din 在时钟驱动下逐位右移。
  • 性能指标:无(纯教学对比)。
  • 验收方式
    非阻塞赋值波形:q0 在时钟沿后变为 din,q1 在下一时钟沿后变为 q0 旧值,q2 类似,呈现逐位移位。
    阻塞赋值波形:所有 q 位在第一个时钟沿后同时变为 din,后续时钟沿无变化(因为 q0、q1、q2 在同一时刻被赋值为同一个值)。

实施步骤

1. 工程结构与代码

创建两个 Verilog 模块,分别使用阻塞赋值和非阻塞赋值实现 3 位移位寄存器。

// 阻塞赋值移位寄存器(错误示范)
module shift_reg_blocking (
    input clk,
    input rst,
    input din,
    output reg [2:0] q
);
always @(posedge clk or posedge rst) begin
    if (rst)
        q <= 3'b0;
    else begin
        q[0] = din;   // 阻塞赋值
        q[1] = q[0];  // 立即使用 q[0] 的新值
        q[2] = q[1];  // 立即使用 q[1] 的新值
    end
end
endmodule

// 非阻塞赋值移位寄存器(正确示范)
module shift_reg_nonblocking (
    input clk,
    input rst,
    input din,
    output reg [2:0] q
);
always @(posedge clk or posedge rst) begin
    if (rst)
        q <= 3'b0;
    else begin
        q[0] <= din;   // 非阻塞赋值
        q[1] <= q[0];  // 使用 q[0] 的旧值
        q[2] <= q[1];  // 使用 q[1] 的旧值
    end
end
endmodule

关键机制分析

  • 阻塞赋值:语句顺序执行。q[0] 先被赋值为 din,然后 q[1] 立即使用 q[0] 的新值,q[2] 也立即使用 q[1] 的新值。最终所有位在同一时钟沿被赋值为 din,失去移位功能。
  • 非阻塞赋值:所有右值在时钟沿被同时采样(读取旧值),然后在当前仿真时间步的末尾统一更新左值。因此 q[1] 使用 q[0] 的旧值,q[2] 使用 q[1] 的旧值,正确实现移位。

2. 编写 Testbench

module tb_shift_reg;
    reg clk, rst, din;
    wire [2:0] q_blk, q_nonblk;

    shift_reg_blocking    u_blk    (.clk(clk), .rst(rst), .din(din), .q(q_blk));
    shift_reg_nonblocking u_nonblk (.clk(clk), .rst(rst), .din(din), .q(q_nonblk));

    initial begin
        clk = 0;
        forever #5 clk = ~clk;
    end

    initial begin
        rst = 1; din = 0;
        #20 rst = 0;
        #10 din = 1;
        #20 din = 0;
        #50 $finish;
    end
endmodule

3. 仿真与波形观察

运行仿真后,观察波形:

  • 非阻塞赋值波形
    在第一个时钟上升沿(t=30 ns),q_nonblk[0] 变为 1,q_nonblk[1] 和 q_nonblk[2] 保持 0;
    第二个时钟沿(t=40 ns),q_nonblk[1] 变为 1,q_nonblk[2] 仍为 0;
    第三个时钟沿(t=50 ns),q_nonblk[2] 变为 1。正确实现移位。
  • 阻塞赋值波形
    在第一个时钟上升沿(t=30 ns),q_blk[0]、q_blk[1]、q_blk[2] 同时从 0 变为 1,之后保持不变。未实现移位。

验证结果

指标阻塞赋值非阻塞赋值
移位功能失败(所有位同时变化)正确(逐位移位)
仿真行为与硬件一致性不一致一致
综合后资源(LUT/FF)可能相同(取决于综合工具)正确

测量条件:Vivado 2023.1 行为仿真,时钟周期 10 ns,输入 din 在 30 ns 时变为 1。

波形特征:非阻塞赋值波形显示 q[0]、q[1]、q[2] 依次在连续时钟沿变化,延迟一个时钟周期。阻塞赋值波形显示所有位在同一时钟沿同时变化。

故障排查(Troubleshooting)

现象原因检查点修复建议
所有寄存器同时变化使用了阻塞赋值检查 always 块中赋值符号改为非阻塞赋值
寄存器值不确定(X)未初始化或复位未正确施加检查复位信号和 initial 块在 Testbench 中给寄存器赋初值或确保复位有效
信号出现毛刺组合逻辑中使用了非阻塞赋值检查组合逻辑 always 块改为阻塞赋值
综合后功能正确,但仿真错误仿真与综合语义差异确认是否遵循编码规范严格区分时序/组合逻辑赋值方式
信号延迟多个时钟周期非阻塞赋值在多个 always 块中链式传递检查跨时钟域逻辑使用同步器或调整设计
仿真运行缓慢仿真时间步过长或无限循环检查 Testbench 中的 forever 语句添加 $finish 或限制仿真时间

原理与设计说明

为什么非阻塞赋值是时序逻辑的标准?

Verilog 仿真基于事件驱动。非阻塞赋值(<=)将赋值事件分为两步:在时钟沿计算右值(读取旧值),然后在当前仿真时间步的末尾统一更新左值。这模拟了硬件中寄存器在时钟沿采样输入、并在时钟沿后输出的行为。阻塞赋值(=)则立即计算并更新,类似于组合逻辑的连续赋值。

关键矛盾:在时序逻辑中,如果使用阻塞赋值,多个赋值语句的顺序执行会导致硬件中不存在的“瞬时传播”,产生错误的仿真行为。例如,移位寄存器中所有位同时更新,而非逐位移位。

可执行方案

  • 时序逻辑(always @(posedge clk))中始终使用非阻塞赋值。
  • 组合逻辑(always @(*))中始终使用阻塞赋值。
  • 避免在同一个 always 块中混合两种赋值方式。

风险边界:即使代码风格错误,综合工具也可能生成正确的硬件(因为综合工具忽略赋值顺序,只关注逻辑关系),但仿真结果会与硬件行为不一致,导致验证失败。因此,必须严格遵守编码规范,确保仿真与硬件一致。

扩展与下一步

  • 参数化移位寄存器:使用 parameter 定义位宽,使代码可重用。
  • 加入异步复位同步释放:在移位寄存器中实现异步复位同步释放,避免亚稳态。
  • 实现双向移位:添加左移/右移控制信号。
  • 使用 SystemVerilog:使用 always_ffalways_comb 明确区分时序和组合逻辑。
  • 加入断言:在 Testbench 中使用 assert 检查移位正确性。

参考与信息来源

  • IEEE Std 1364-2005, Verilog Hardware Description Language.
  • Clifford E. Cummings, "Nonblocking Assignments in Verilog Synthesis, Coding Styles That Kill!", SNUG 2000.
  • Xilinx Vivado Design Suite User Guide: Simulation (UG937).

技术附录

术语表

  • 阻塞赋值(=):立即计算并更新左值,后续语句使用新值。
  • 非阻塞赋值(<=):在时钟沿计算右值,在当前仿真时间步末尾统一更新左值。
  • 仿真时间步:仿真器处理事件的最小时间单位。

检查清单

  • [ ] 时序逻辑 always 块中只使用非阻塞赋值。
  • [ ] 组合逻辑 always 块中只使用阻塞赋值。
  • [ ] 不在同一个 always 块中混合两种赋值。
  • [ ] Testbench 中提供正确的时钟和复位。
  • [ ] 仿真波形与预期行为一致。

关键约束速查

场景推荐赋值方式
时序逻辑(DFF)<= (非阻塞)
组合逻辑(LUT)= (阻塞)
锁存器(Latch)避免,若必须用则用 =
Testbench 中时钟生成= (阻塞) 或 forever
标签:
本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/36568.html
二牛学FPGA

二牛学FPGA

初级工程师
这家伙真懒,几个字都不愿写!
51417.24W3.93W3.67W
分享:
成电国芯FPGA赛事课即将上线
FPGA学习路径指南:从数字电路基础到系统级设计的进阶实践
FPGA学习路径指南:从数字电路基础到系统级设计的进阶实践上一篇
FPGA在5G基站信号处理中的关键角色与实现指南(2026趋势)下一篇
FPGA在5G基站信号处理中的关键角色与实现指南(2026趋势)
相关文章
总数:545
异步FIFO设计实施指南:深度计算与格雷码应用实践

异步FIFO设计实施指南:深度计算与格雷码应用实践

异步FIFO是处理FPGA跨时钟域数据通信的核心组件,其深度设计与指针同…
技术分享
10天前
0
0
42
0
基于FPGA的实时视频缩放算法:双线性插值的Verilog实现与上手指南

基于FPGA的实时视频缩放算法:双线性插值的Verilog实现与上手指南

QuickStart下载并安装Vivado2020.1及以上版本,确…
技术分享
5小时前
0
0
3
0
基于FPGA的DDR3/DDR4控制器接口设计实战与调试技巧

基于FPGA的DDR3/DDR4控制器接口设计实战与调试技巧

本文旨在提供一份关于在FPGA中集成与调试DDR3/DDR4控制器接口的…
技术分享
12天前
0
0
20
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容