Verilog阻塞与非阻塞赋值综合结果深度对比:移位寄存器设计与验证指南

FPGA小白
文章2026-05-09
44

Quick Start(快速上手)

  • 打开Vivado 2025.2(或更高版本),创建新工程,选择器件xc7a35ticsg324-1L(Artix-7)。
  • 新建两个Verilog源文件:blocking_example.vnonblocking_example.v
  • blocking_example.v 中编写阻塞赋值移位寄存器(a = b; c = a;)。
  • nonblocking_example.v 中编写相同逻辑,但使用非阻塞赋值(a <= b; c <= a;)。
  • 分别对两个模块进行综合(Synthesis),查看综合后的RTL原理图。
  • 对比两者综合出的寄存器级数:阻塞赋值版本可能只产生1级寄存器,而非阻塞版本产生2级寄存器。

前置条件

  • 已安装Vivado 2025.2或更新版本(推荐使用2025.2及以上,以兼容最新器件库)。
  • 具备Verilog基础语法知识,了解阻塞(=)与非阻塞(<=)赋值的基本定义。
  • 熟悉Vivado的基本操作流程:新建工程、添加源文件、运行综合、查看原理图。
  • 目标器件:xc7a35ticsg324-1L(Artix-7系列),但本实验对器件无特殊依赖,其他FPGA也可复现。

目标与验收标准

  • 核心目标:通过综合结果直观理解阻塞赋值与非阻塞赋值在硬件生成上的本质差异。
  • 验收标准
    • 阻塞赋值版本综合后仅生成1级D触发器(移位寄存器深度为1)。
    • 非阻塞赋值版本综合后生成2级D触发器(移位寄存器深度为2)。
    • 能够解释两种赋值方式导致不同硬件结构的原因。

实施步骤

步骤1:创建工程与源文件

  • 打开Vivado,点击“Create Project”,选择“RTL Project”,器件选择xc7a35ticsg324-1L。
  • 在“Add Sources”步骤中,新建两个Verilog文件:blocking_example.vnonblocking_example.v
  • 确保工程顶层模块为空或暂不指定,后续将两个模块分别设为顶层进行综合。

步骤2:编写阻塞赋值模块

module blocking_example (
    input clk,
    input rst_n,
    input b,
    output reg c
);
    reg a;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            a <= 1'b0;
            c <= 1'b0;
        end else begin
            a = b;   // 阻塞赋值:立即更新a
            c = a;   // 阻塞赋值:使用更新后的a
        end
    end
endmodule

逐行说明

  • 第1行:定义模块 blocking_example,端口包括时钟 clk、复位 rst_n、输入 b 和输出 c
  • 第2行:声明 reg a 作为内部寄存器。
  • 第3行always 块敏感列表为时钟上升沿或复位下降沿。
  • 第4行:异步复位条件:rst_n 为低电平。
  • 第5-6行:复位时将 ac 清零。
  • 第7行else 分支,时钟上升沿有效。
  • 第8行:阻塞赋值 a = b,立即将 b 的值赋给 a
  • 第9行:阻塞赋值 c = a,此时 a 已是更新后的值(即 b),因此 c 直接等于 b,等价于 c = b。综合后只产生1级寄存器。
  • 第10行end 结束 always 块。
  • 第11行endmodule 结束模块定义。

步骤3:编写非阻塞赋值模块

module nonblocking_example (
    input clk,
    input rst_n,
    input b,
    output reg c
);
    reg a;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            a <= 1'b0;
            c <= 1'b0;
        end else begin
            a <= b;   // 非阻塞赋值:在时钟沿采样b,但不立即更新a
            c <= a;   // 非阻塞赋值:使用采样时刻的旧值a
        end
    end
endmodule

逐行说明

  • 第1行:定义模块 nonblocking_example,端口与阻塞版本完全一致。
  • 第2行:声明内部寄存器 a
  • 第3行always 块敏感列表相同。
  • 第4行:异步复位条件。
  • 第5-6行:复位清零。
  • 第7行:时钟上升沿有效。
  • 第8行:非阻塞赋值 a <= b,在时钟沿采样 b,但 a 的更新发生在该 always 块结束之后。
  • 第9行:非阻塞赋值 c <= a,使用采样时刻 a 的旧值(即上一个时钟周期的值)。因此 cb 延迟两个时钟周期,综合后产生2级寄存器。
  • 第10行end
  • 第11行endmodule

步骤4:分别综合并查看RTL原理图

  • 在Vivado中,将 blocking_example 设为顶层模块,运行综合(Synthesis)。
  • 综合完成后,打开“Schematic”查看RTL原理图:应看到 b 直接连接到 c 的D触发器输入,中间无额外寄存器。
  • 将顶层模块切换为 nonblocking_example,再次综合并查看原理图:应看到 b 经过两级D触发器后才到达 c
  • 若使用Vivado的“Elaborated Design”模式,可在综合前查看行为级仿真结果,但最终综合结果以Schematic为准。

验证结果

  • 阻塞赋值版本:综合后RTL原理图显示只有1个D触发器(寄存器),输入 b 经过组合逻辑直接连接到触发器D端,输出 c 为触发器Q端。移位寄存器深度为1。
  • 非阻塞赋值版本:综合后RTL原理图显示有2个D触发器级联,输入 b 连接到第一级触发器D端,第一级Q端连接到第二级D端,第二级Q端输出 c。移位寄存器深度为2。
  • 结论:阻塞赋值在同一个 always 块内顺序执行,导致 c = a 使用了更新后的 a,等效于直接赋值 c = b;非阻塞赋值则并行采样,c <= a 使用旧值,形成真正的两级流水。

排障指南

  • 问题1:综合后原理图显示两级寄存器但预期为一级

    原因:可能在 always 块中混用了阻塞与非阻塞赋值,或敏感列表不完整。检查代码是否严格遵循“时序逻辑用非阻塞、组合逻辑用阻塞”的规则。

  • 问题2:综合后原理图显示组合逻辑环或锁存器

    原因:阻塞赋值在组合逻辑中未完整赋值所有分支,导致推断出锁存器。确保每个 if 分支都有赋值。

  • 问题3:Vivado版本过低导致器件不支持

    建议升级至2025.2或更新版本,或选择其他Artix-7器件(如xc7a35t-cpg236-1)。

  • 问题4:仿真结果与综合结果不一致

    仿真使用行为模型,综合使用实际硬件。务必以综合后的Schematic为准,仿真仅作功能验证。

扩展实践

  • 多级移位寄存器对比:将代码扩展为3级或4级移位寄存器,观察阻塞赋值是否会“短路”中间级。
  • 混合赋值风格:在同一个 always 块中同时使用阻塞和非阻塞赋值,观察综合工具的行为(通常会产生警告或不可预测结果)。
  • 综合报告分析:使用Vivado的“Report Utilization”查看两种赋值方式下的寄存器数量差异,并对比时序余量(Setup/Hold Slack)。
  • 跨时钟域应用:非阻塞赋值常用于跨时钟域同步器(如双级触发器同步),本实验的二级寄存器结构正是同步器的基本单元。

参考资源

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

附录:完整代码清单

// blocking_example.v
module blocking_example (
    input clk,
    input rst_n,
    input b,
    output reg c
);
    reg a;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            a <= 1'b0;
            c <= 1'b0;
        end else begin
            a = b;
            c = a;
        end
    end
endmodule

// nonblocking_example.v
module nonblocking_example (
    input clk,
    input rst_n,
    input b,
    output reg c
);
    reg a;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            a <= 1'b0;
            c <= 1'b0;
        end else begin
            a <= b;
            c <= a;
        end
    end
endmodule

逐行说明(附录代码)

  • 第1-11行(blocking_example):与步骤2中代码完全一致,阻塞赋值导致 c 直接等于 b,综合为1级寄存器。
  • 第13-23行(nonblocking_example):与步骤3中代码完全一致,非阻塞赋值形成2级寄存器链。
  • 关键差异:第8行(阻塞)与第20行(非阻塞)的赋值运算符不同,导致硬件结构差异。

通过本指南,您应能独立复现阻塞与非阻塞赋值的综合差异,并理解其背后的硬件生成机制。在实际设计中,请严格遵循“时序逻辑用非阻塞、组合逻辑用阻塞”的编码规范,以避免综合结果与预期不符。

分类
技术分享
标签
Verilog阻塞赋值非阻塞赋值
浏览 44
分享:

相关推荐

同频道 · 相近分类

暂无相关推荐

作者

FPGA小白查看主页

同分类阅读

文章

延伸阅读与实操

  • 文章 + 课程联动深度文章常对应体系课章节,可一键选课。
  • 学习产出可参考笔记与作业案例在学习产出广场持续更新。

探索全站