news 2026/4/15 21:48:39

使用Verilog在FPGA上实现门电路深度剖析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用Verilog在FPGA上实现门电路深度剖析

从门电路到FPGA:用Verilog写最“硬”的逻辑

你有没有想过,一行简单的assign y = a & b;到底在芯片里变成了什么?

它不是教科书上那两个背靠背的三角形符号,也不是电路图里的抽象框图。在一块Xilinx或Intel的FPGA内部,这行代码会被“翻译”成查找表(LUT)中的一串比特配置、布线开关的选择,甚至可能被优化得无影无踪——因为它太简单了,简单到可以和其他逻辑合并。

这就是数字设计的魅力:你写的每一行Verilog,都在和硅片上的物理资源对话。而这一切的起点,正是那些我们再熟悉不过的门电路:与、或、非、异或……

本文不讲高级综合,也不谈AI加速器架构。我们要做的,是沉下来,回到原点,用工程师的眼光重新审视这些“最基础”的单元——看看它们是如何从纸面定义,一步步变成运行在千万赫兹时钟下的真实硬件逻辑。


为什么还要手动写门电路?HLS不是更香吗?

确实,现在有Vitis HLS、Intel HLS这些工具,能让你用C++写出乘法器,自动生成RTL代码。听起来很美好,对吧?

但现实往往没那么理想:

  • 延迟不可控:HLS生成的路径可能多绕了几级寄存器,关键路径超了200ps,你就得回头改算法结构。
  • 资源利用率黑箱:你以为只是个简单的判断逻辑,结果综合出来占了十几个LUT,因为编译器没识别出你的意图。
  • 调试困难:仿真波形里一堆自动命名的中间信号,根本看不出哪段对应你原始代码的哪个分支。

这时候,掌握底层门级建模能力的价值就体现出来了。尤其是在以下场景:

  • 构建超低延迟的数据通路(比如金融交易引擎)
  • 实现确定性行为的控制逻辑(如安全关断机制)
  • 教学与原型验证(让学生真正理解布尔代数如何映射为硬件)

换句话说,当你需要精确掌控每一个门延迟、每一份资源开销时,Verilog依然是不可替代的语言


最小的积木:从AND门说起

先看一个最简单的例子——两输入与门。

module and_gate ( input a, input b, output y ); assign y = a & b; endmodule

就这么四行代码。但它背后发生的事可不少。

它真的用了“一个门”吗?

在FPGA中,并没有独立的“与门”硬件模块。所有的组合逻辑都由查找表(LUT)实现。以Xilinx Artix-7为例,每个Slice包含多个6输入LUT。也就是说,这个两输入与门只会占用LUT的4个有效条目(00→0, 01→0, 10→0, 11→1),其余592个配置位都是闲置的。

但这没关系。现代综合器足够聪明,会把多个小逻辑打包进同一个LUT。比如,如果你还写了另一个或门a | b,只要输入相同且输出不冲突,它们很可能共用同一个6-LUT!

🔍 小知识:Xilinx综合器(Vivado Synthesis)默认启用shreg_min_sizelut_combining等优化选项,自动合并冗余逻辑。

所以别担心“浪费”,除非你在顶层强制例化原语(primitive),否则综合器不会傻乎乎地为每个门分配完整资源。


OR、NOT、XOR:风格统一,命运各异

继续往下走,其他基本门的写法几乎如出一辙:

// 或门 assign y_or = a | b; // 非门 assign y_not = ~a; // 异或门 assign y_xor = a ^ b;

语法上高度一致,但在FPGA中的待遇却大相径庭。

NOT门:最小的存在感

非门是最简单的单输入函数。它的真值表只有两项。在实际实现中,综合器往往会把它“吸收”进前级或后级逻辑中。

举个例子:

wire not_a = ~a; assign y = not_a & b;

这段代码几乎肯定会被优化成一个“与非”功能直接放在LUT里,连中间信号not_a都不会保留。

这也提醒我们一点:不要依赖中间信号名进行调试,除非你加了(* keep *)(* mark_debug *)属性。

XOR门:被偏爱的那个

如果说哪个门在FPGA里过得最好,那一定是异或门。

原因很简单:它是加法器的核心。每一位全加器的求和输出 $ S = A \oplus B \oplus C_{in} $ 都依赖XOR。因此,Xilinx 7系列及以后的FPGA,在每个LUT输出端都集成了一位专用异或门,专门用于构建高效的进位链。

这意味着什么?

  • 多位异或运算(如奇偶校验)可以用树状结构高效实现
  • 延迟远低于普通组合逻辑路径
  • 综合器会优先使用该硬核路径,提升性能

所以当你写:

assign parity = ^data[7:0]; // 归约异或

综合器不会傻傻地串接8个XOR,而是生成平衡树结构,充分利用FPGA的异或网络优势。


NAND和NOR:教学意义大于工程价值?

我们都知道,NAND和NOR是功能完备的——仅用其中之一就能构造任何逻辑函数。这也是为什么CMOS工艺中,NAND门比AND门更常见:它的晶体管结构更高效。

但在FPGA世界里,这一点并不重要。

因为无论是AND还是NAND,最终都要通过LUT实现。而LUT不在乎你是“先与后非”还是“直接查表”。所以写成:

assign y = ~(a & b); // NAND

assign y = a & b; assign yn = ~y;

在综合后可能完全一样,也可能不一样,取决于上下文和优化策略。

不过,单独封装NAND/NOR模块仍有其价值:

module nand_gate(input a, input b, output y); assign y = ~(a & b); endmodule

这种做法虽然不影响硬件结果,但能清晰表达设计意图,尤其在教学或协议实现中很有用。比如你要模拟某个老式TTL芯片的行为,就必须忠实地还原其门级结构。


FPGA里的“门”到底长什么样?一张图说清楚

想象一下,你在Verilog里写了五个门:

wire w1 = a & b; wire w2 = c | d; wire w3 = ~e; wire w4 = f ^ g; wire y = w1 & w2;

你以为它们是五个独立元件?

错。

实际上,综合器看到的是这样一个过程:

  1. 分析所有输入输出关系
  2. 提取布尔函数:y = (a·b) · (c+d)
  3. 发现w3w4没被使用(假设),直接剪枝
  4. 将剩余逻辑映射为一个5输入函数(a,b,c,d,e → y)
  5. 分配到一个6-LUT中,初始化对应的真值表(INIT值)

于是,原本看似“五级门链”的逻辑,最后只用了一个LUT + 若干布线资源

这才是现代FPGA逻辑综合的真实面貌:你描述的是行为,工具决定实现方式


常见陷阱:你以为的组合逻辑,其实是锁存器

新手最容易犯的一个错误,就是无意中生成了锁存器(Latch)。

比如这段代码:

always @(*) begin if (sel == 1'b1) out = a & b; end

看起来没问题?其实不然。当sel == 0时,out没有赋值,综合器就会推断出电平敏感的锁存行为——这是组合逻辑的大忌,极易导致时序违例和毛刺传播。

正确做法是覆盖所有分支:

always @(*) begin if (sel) out = a & b; else out = 1'b0; end

或者干脆继续用assign做连续赋值,避免过程块带来的歧义。

💡 秘籍:只要你坚持使用assign实现纯组合逻辑,就不会踩这个坑。


性能优化实战:如何让门级逻辑跑得更快?

别以为“门电路”就不涉及性能调优。恰恰相反,越是底层,越容易影响全局。

技巧一:善用归约操作符

对于多输入逻辑,别这么写:

assign y = a | b | c | d | e | f | g | h;

虽然能综合,但可能生成串行结构,延迟随输入数量线性增长。

推荐写法:

assign y = |vec[7:0]; // 归约或

综合器会将其展开为平衡树结构,显著降低关键路径延迟。

技巧二:避免不必要的层级拆分

有些人喜欢把每个门都做成独立模块,然后层层例化:

nand_gate u1 (.a(a), .b(b), .y(t1)); not_gate u2 (.a(t1), .y(y));

这会导致综合器难以跨模块优化,还可能引入额外布线延迟。

除非你需要做形式验证或IP封装,否则建议保持扁平化设计,让综合器自由重组逻辑。

技巧三:关注布线延迟而非门延迟

在FPGA中,真正的瓶颈往往不是门本身,而是信号在芯片上走过的距离

一个LUT的内在延迟可能只有0.1ns,但跨SLICE布线可能增加0.3ns以上。

因此:
- 关键路径上的逻辑尽量集中布局
- 使用Pblock或RLOC约束物理位置(高级技巧)
- 必要时插入流水级打破长组合路径


调试经验:如何确认你的门电路真的“存在”?

有时候你会怀疑:我写的这个门,到底有没有被保留?

有两个方法可以验证:

方法一:查看综合后的原理图(Schematic)

在Vivado中打开Synthesized Design → Schematic,搜索你的模块名。如果被优化掉了,就找不到;如果还在,你会看到清晰的门符号或LUT表示。

方法二:使用(* keep *)保留信号

wire keep_this = a ^ b; (* keep = "true" *) wire keep_this; // 强制保留 assign y = keep_this & c;

这样即使逻辑上可优化,综合器也会为你保留该节点,方便观测。


回归本质:门电路的现代意义

今天我们花了大量篇幅讲“如何实现一个与门”,似乎有点小题大做。

但请记住:每一个复杂的FPGA系统,都是由无数这样的基础单元堆叠而成

你在UART里做的起始位检测?用到了异或门比较采样值。
你在I2C控制器里生成ACK信号?依赖与非门完成漏极开路模拟。
你在图像处理流水线中做阈值判断?本质是一个巨大的比较器网络。

而所有这些,归根结底,都是门电路的排列组合。

更重要的是,理解门级行为,能让你在面对时序问题、资源瓶颈、仿真差异时,拥有更强的直觉和排查能力

当你知道“为什么这个地方不能收敛”,而不是只会盲目加寄存器,你就离真正的硬件专家更近了一步。


如果你正在学习FPGA开发,不妨试试这个练习:

写一个四位全加器,不用+操作符,只用与、或、非、异或门来搭建。然后观察综合后的资源占用和路径延迟,再对比直接写assign sum = a + b的结果。

你会发现,工具比你想象的聪明,但也比你想象的脆弱——它需要你提供正确的“意图”,才能给出最优的“实现”

而这,正是数字设计的艺术所在。

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

3种高效方法解决Navicat试用期限制:Mac版重置完全指南

3种高效方法解决Navicat试用期限制:Mac版重置完全指南 【免费下载链接】navicat_reset_mac navicat16 mac版无限重置试用期脚本 项目地址: https://gitcode.com/gh_mirrors/na/navicat_reset_mac 还在为Navicat Premium试用期结束而苦恼吗?作为Ma…

作者头像 李华
网站建设 2026/4/13 8:56:02

从选择作曲家到生成乐谱|NotaGen大模型镜像全链路解析

从选择作曲家到生成乐谱|NotaGen大模型镜像全链路解析 在AI音乐生成技术快速演进的今天,传统符号化音乐创作正迎来一场由大语言模型(LLM)驱动的范式变革。不同于仅生成音频波形的TTS系统,NotaGen 开创性地将LLM架构应…

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

AKShare金融数据接口库终极指南:Python投资分析完整攻略

AKShare金融数据接口库终极指南:Python投资分析完整攻略 【免费下载链接】akshare 项目地址: https://gitcode.com/gh_mirrors/aks/akshare 在金融投资和数据分析领域,获取准确、实时的市场数据是每个投资者和分析师面临的首要挑战。传统数据获取…

作者头像 李华
网站建设 2026/3/21 7:44:48

金融数据接口终极指南:3大核心模块与5个实战场景深度解析

金融数据接口终极指南:3大核心模块与5个实战场景深度解析 【免费下载链接】akshare 项目地址: https://gitcode.com/gh_mirrors/aks/akshare 在量化投资和金融科技领域,稳定可靠的数据源是成功的关键基石。面对复杂多变的金融市场,如…

作者头像 李华
网站建设 2026/4/11 4:15:06

AI写作大师Qwen3-4B代码案例:数据分析报告生成

AI写作大师Qwen3-4B代码案例:数据分析报告生成 1. 引言 1.1 业务场景描述 在现代数据驱动的决策体系中,自动化生成高质量的数据分析报告已成为企业提升效率的关键环节。无论是市场部门的周报、运营团队的用户行为总结,还是技术团队的日志分…

作者头像 李华
网站建设 2026/4/10 16:30:43

DSView信号分析工具高效使用完整教程

DSView信号分析工具高效使用完整教程 【免费下载链接】DSView An open source multi-function instrument for everyone 项目地址: https://gitcode.com/gh_mirrors/ds/DSView DSView是一款基于sigrok框架的开源多平台信号分析软件,支持逻辑分析仪、示波器等…

作者头像 李华