Quick Start(快速上手)
- 打开Vivado 2025.2(或更高版本),创建新工程,选择器件xc7a35ticsg324-1L(Artix-7)。
- 新建两个Verilog源文件:
blocking_example.v和nonblocking_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:创建工程与源文件
- 打开Vivado,点击“Create Project”,选择“RTL Project”,器件选择xc7a35ticsg324-1L。
- 在“Add Sources”步骤中,新建两个Verilog文件:
blocking_example.v和nonblocking_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行:复位时将
a和c清零。 - 第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的旧值(即上一个时钟周期的值)。因此c比b延迟两个时钟周期,综合后产生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行(非阻塞)的赋值运算符不同,导致硬件结构差异。
通过本指南,您应能独立复现阻塞与非阻塞赋值的综合差异,并理解其背后的硬件生成机制。在实际设计中,请严格遵循“时序逻辑用非阻塞、组合逻辑用阻塞”的编码规范,以避免综合结果与预期不符。



