news 2026/4/16 19:20:09

Verilog阻塞与非阻塞赋值的实战应用与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Verilog阻塞与非阻塞赋值的实战应用与避坑指南

1. Verilog赋值的两种方式:阻塞与非阻塞

刚接触Verilog时,很多人都会被这两种赋值方式搞得晕头转向。我自己刚开始学的时候,就经常把阻塞赋值(=)和非阻塞赋值(<=)用混,结果仿真出来的波形完全不对,调试了大半天才发现问题所在。这两种赋值方式看似简单,但用错了会导致电路功能完全异常。

阻塞赋值就像排队买奶茶,你必须等前面的人买完才能轮到你。在Verilog中,这意味着当前语句执行完成后,才会执行下一条语句。比如下面这段代码:

always @(posedge clk) begin a = b; c = a; end

这里c最终得到的值是b的值,因为a=b执行完后,a的值立即更新,然后c=a才会执行。

而非阻塞赋值则像自助餐厅,所有人可以同时取餐。在Verilog中,所有非阻塞赋值语句会同时计算右边的值,然后在always块结束时统一更新左边的值。看这个例子:

always @(posedge clk) begin a <= b; c <= a; end

这种情况下,c得到的是a原来的值,而不是b的值,因为a和c的更新是同时发生的。

2. 阻塞赋值的深入解析与使用场景

2.1 阻塞赋值的工作原理

阻塞赋值的特点是"立即生效"。当执行到阻塞赋值语句时,会立即计算右边的表达式,并将结果赋给左边的变量。这个过程会阻塞后续语句的执行,直到当前赋值完成。

这种特性使得阻塞赋值非常适合用来描述组合逻辑。比如我们要实现一个简单的与门:

always @(*) begin c = a & b; end

这里使用阻塞赋值非常自然,因为组合逻辑的输出应该立即响应输入的变化。

2.2 阻塞赋值的常见陷阱

虽然阻塞赋值用起来简单直接,但有几个坑需要特别注意:

  1. 在时序逻辑中使用阻塞赋值:这会导致仿真结果与综合后的实际电路行为不一致。比如:
always @(posedge clk) begin a = b; c = a; end

仿真时看起来可能没问题,但实际电路会表现出不可预测的行为。

  1. 多个always块对同一变量进行阻塞赋值:这会产生多驱动问题,导致综合错误。

  2. 阻塞赋值的顺序依赖性:由于阻塞赋值的顺序执行特性,改变语句顺序可能会完全改变电路行为。

我在一个项目中就遇到过这样的问题:原本想用阻塞赋值实现一个简单的数据通路,但因为语句顺序安排不当,导致最终综合出来的电路完全不是我想要的。后来改用非阻塞赋值才解决了问题。

3. 非阻塞赋值的深入解析与使用场景

3.1 非阻塞赋值的工作原理

非阻塞赋值的工作过程可以分为两个阶段:

  1. 计算阶段:在always块执行时计算所有非阻塞赋值右边的表达式
  2. 更新阶段:在always块结束时统一更新左边的变量

这种"先计算,后更新"的机制完美模拟了时序电路中寄存器的工作方式。每个时钟沿到来时,寄存器同时采样输入,然后在时钟沿结束时更新输出。

3.2 非阻塞赋值的优势

非阻塞赋值最大的优势在于它能准确描述时序电路的行为。考虑一个简单的流水线寄存器:

always @(posedge clk) begin stage1 <= input; stage2 <= stage1; stage3 <= stage2; end

这种写法能正确实现三级流水线,因为所有赋值都是同时更新的。如果改用阻塞赋值,就会变成单级寄存器,完全破坏了流水线的功能。

另一个优势是代码的顺序不影响功能。由于所有更新都是同时进行的,调整非阻塞赋值语句的顺序不会改变电路行为。这使得代码更易于维护和修改。

4. 混合使用阻塞与非阻塞赋值的危险

4.1 绝对禁止的混用模式

在同一个always块中混合使用阻塞和非阻塞赋值是Verilog设计的大忌。比如:

always @(posedge clk) begin a = b; // 阻塞赋值 c <= d; // 非阻塞赋值 end

这种写法会导致仿真和综合结果不一致,可能产生难以调试的竞争条件。我在早期的一个项目中就犯过这个错误,仿真时一切正常,但烧写到FPGA后电路完全不能工作,花了整整两天才找到这个混用赋值的问题。

4.2 允许的混合使用场景

唯一相对安全的混合使用场景是在不同的always块中,分别用阻塞赋值描述组合逻辑,用非阻塞赋值描述时序逻辑。例如:

// 组合逻辑部分 always @(*) begin comb_out = a & b; // 阻塞赋值 end // 时序逻辑部分 always @(posedge clk) begin reg_out <= comb_out; // 非阻塞赋值 end

即使如此,也要确保两个always块之间没有循环依赖,否则仍可能导致问题。

5. 实际工程中的最佳实践

5.1 可综合代码的赋值选择原则

根据多年项目经验,我总结了以下黄金法则:

  1. 时序电路一律使用非阻塞赋值:包括寄存器、状态机、计数器等。
  2. 组合电路一律使用阻塞赋值:如多路选择器、译码器等。
  3. 不要在同一个always块中混合两种赋值:这是万恶之源。
  4. 尽量不在多个always块中对同一变量赋值:即使使用非阻塞赋值也可能导致问题。

5.2 常见错误案例分析

案例1:错误的移位寄存器实现

// 错误写法 always @(posedge clk) begin reg1 = din; reg2 = reg1; reg3 = reg2; end

这个"移位寄存器"实际上只会保存din的值,因为阻塞赋值会立即更新reg1,然后reg2得到的是更新后的reg1值。

正确写法

always @(posedge clk) begin reg1 <= din; reg2 <= reg1; reg3 <= reg2; end

案例2:组合逻辑中的非阻塞赋值

// 错误写法 always @(*) begin out <= a & b; end

这会导致out不能及时响应a和b的变化,违背了组合逻辑的特性。

正确写法

always @(*) begin out = a & b; end

6. 仿真与调试技巧

6.1 使用$strobe观察非阻塞赋值

由于非阻塞赋值的更新是延后的,使用普通的$display可能看不到预期的值。这时应该使用$strobe:

always @(posedge clk) begin a <= b; $strobe("At time %0t: a = %b", $time, a); end

$strobe会在所有非阻塞赋值完成后才执行,因此能显示更新后的值。

6.2 避免使用#0延迟

新手常犯的一个错误是使用#0延迟来"调整"赋值时机:

always @(posedge clk) begin a <= b; #0 c <= a; end

这种做法极其危险,会导致仿真与综合结果不一致,应该完全避免。

7. 从RTL到综合的思考

7.1 赋值方式对综合结果的影响

正确的赋值方式不仅影响仿真结果,更直接影响综合出的电路结构。非阻塞赋值综合后通常对应触发器(Flip-Flop),而阻塞赋值综合后通常形成组合逻辑。

我曾对比过两种写法综合后的网表:

  • 使用非阻塞赋值的计数器综合出标准的寄存器链
  • 错误使用阻塞赋值的计数器则综合出一堆锁存器和组合环路

7.2 性能考量

在高速设计中,赋值方式的选择还会影响时序性能。非阻塞赋值描述的时序电路更容易满足建立保持时间,而阻塞赋值如果用在时序逻辑中,可能导致保持时间违例。

在一个400MHz的SerDes设计中,我们就因为部分寄存器错误使用了阻塞赋值,导致时序无法收敛。改为非阻塞赋值后,时序立即满足了要求。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 19:17:11

lanqiao498 回文日期

题目描述2020 年春节期间&#xff0c;有一个特殊的日期引起了大家的注意&#xff1a;2020 年 2 月 2 日。因为如果将这个日期按 “yyyymmdd” 的格式写成一个 8 位数是 20200202&#xff0c;恰好是一个回文数。我们称这样的日期是回文日期。有人表示 20200202 是 “千年一遇” …

作者头像 李华
网站建设 2026/4/16 19:16:03

打造你的专属桌面伙伴:DyberPet低代码虚拟宠物开发指南

打造你的专属桌面伙伴&#xff1a;DyberPet低代码虚拟宠物开发指南 【免费下载链接】DyberPet Desktop Cyber Pet Framework based on PySide6 项目地址: https://gitcode.com/GitHub_Trending/dy/DyberPet 你是否曾渴望在单调的工作环境中拥有一个充满活力的数字伙伴&a…

作者头像 李华
网站建设 2026/4/16 19:15:42

5分钟掌握Electron日志记录:electron-log 5.4.3实战指南

5分钟掌握Electron日志记录&#xff1a;electron-log 5.4.3实战指南 【免费下载链接】electron-log Simple logging module Electron/Node.js/NW.js application. No dependencies. No complicated configuration. 项目地址: https://gitcode.com/gh_mirrors/el/electron-log…

作者头像 李华
网站建设 2026/4/16 19:15:17

C++ STL算法实战:巧用count与count_if提升数据统计效率

1. 为什么需要count和count_if&#xff1f; 在日常开发中&#xff0c;数据统计是最常见的需求之一。比如电商平台要统计某商品的销量&#xff0c;游戏服务器要计算在线玩家数量&#xff0c;数据分析系统要汇总特定条件的日志条目。如果每次都手动写循环来计数&#xff0c;不仅代…

作者头像 李华
网站建设 2026/4/16 19:12:38

精益数字化转型推进中,你最头疼的是哪个环节?

在制造业高质量发展的背景下&#xff0c;精益数字化转型已成为工厂突破瓶颈、提质增效的核心路径——它不是精益管理与数字化工具的简单叠加&#xff0c;而是以精益理念为核心&#xff0c;用数字化技术赋能生产、管理全流程&#xff0c;实现消除浪费、持续改善、数据驱动的目标…

作者头像 李华
网站建设 2026/4/16 19:12:37

PHP伪协议实战:从BUUCTF Secret File 1看如何用php://filter读取flag.php源码

PHP伪协议实战&#xff1a;深入剖析php://filter的源码读取机制 在CTF竞赛和渗透测试中&#xff0c;文件包含漏洞一直是高频考点。当遇到无法直接读取的PHP文件时&#xff08;如flag.php&#xff09;&#xff0c;php://filter伪协议往往能成为突破的关键。本文将从一个典型场景…

作者头像 李华