news 2026/3/31 21:45:53

用Verilog描述半加器:FPGA设计操作指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用Verilog描述半加器:FPGA设计操作指南

从半加器开始:用Verilog点亮你的第一个FPGA逻辑

你有没有想过,计算机是怎么做加法的?
不是打开计算器点两下那种“加法”,而是真正意义上——硬件层面的二进制相加
答案就藏在一个看似简单的电路里:半加器(Half Adder)

在 FPGA 设计的世界中,我们不写“程序”来运行任务,而是用代码去“搭建电路”。而Verilog HDL就是这门“造芯语言”中最常用的一种。当你写下一行assign Sum = A ^ B;,其实是在告诉综合工具:“请给我造一个异或门。”

今天,我们就从最基础、也最重要的起点出发——用 Verilog 实现一个半加器,带你走完从逻辑理解到仿真验证的完整设计流程。别小看它,所有复杂的 CPU 加法器,都是由这样一个个小小的“1+1=?”单元搭起来的。


半加器是什么?为什么它是数字系统的“第一块积木”?

想象你在纸上做二进制加法:

1 + 1 ---- 10

结果是10,也就是十进制的 2。这里有两个输出:
-和(Sum)是低位的结果 →0
-进位(Carry)是高位的溢出 →1

这个过程就是半加器要完成的任务:对两个 1 位二进制数进行相加,输出它们的和与进位。

它的输入只有两个:AB;输出也只有两个:SumCarry
但它没有考虑来自更低一位的进位输入(Cin),所以叫“半”加器。
全加器才会处理 Cin,但那个可以由两个半加器拼出来。

它的真值表长这样:

ABSumCarry
0000
0110
1010
1101

一眼就能看出规律:
-Sum = A ⊕ B(异或)
-Carry = A & B(与)

就这么简单?没错!可正是这种简洁性,让它成为学习 FPGA 设计的最佳入口。


三种写法,三种思维:如何用 Verilog 描述同一个电路?

在 Verilog 中,描述同一个功能可以有不同的“风格”。就像写作有散文、诗歌、说明文之分,HDL 也有不同建模方式。掌握它们,等于掌握了看待硬件的不同视角。

方法一:数据流建模 —— “我想表达的是关系”

这是最直观、最常用的写法。你不再关心用了什么门,只关心信号之间怎么算。

module half_adder ( input wire A, input wire B, output wire Sum, output wire Carry ); assign Sum = A ^ B; assign Carry = A & B; endmodule
  • assign是连续赋值语句,适用于组合逻辑。
  • 所有wire类型,因为它是“连线”,不是存储。
  • 综合后直接映射为一个 XOR 门和一个 AND 门。

优点:简洁清晰,易读易维护,适合大多数场景。
🧠思维方式:我关注的是“数据如何流动”。


方法二:结构化建模 —— “我要亲手搭出每根线”

如果你想看得更“底层”一点,可以直接调用 FPGA 内部的原始门级元件(primitives)。这种方式像是在画电路图。

module half_adder_structural ( input wire A, input wire B, output wire Sum, output wire Carry ); xor (Sum, A, B); // xor(out, in1, in2) and (Carry, A, B); endmodule

你看,连模块都没有名字,这就是原语实例化语法。Xilinx 和 Intel 的器件库都支持这些基本门。

优势:完全掌控硬件结构,教学时特别有用。
🔍应用场景:当你想分析最终网表结构,或者做低功耗优化时,这种写法能让你看到“真实世界”的门。

不过实际项目中很少这么写,毕竟综合工具自己就能生成最优门级结构。但知道它存在,很重要。


方法三:行为级建模 —— “当条件满足时,该做什么?”

虽然半加器是纯组合逻辑,但我们也可以用时序块always @(*)来描述它。这是构建复杂数字系统的核心范式。

module half_adder_behavioral ( input wire A, input wire B, output reg Sum, output reg Carry ); always @(*) begin Sum = A ^ B; Carry = A & B; end endmodule

注意变化:
- 输出变成了reg类型。因为在always块里赋值的信号必须声明为reg
- 敏感列表用了@(*),表示自动包含所有输入,避免遗漏导致锁存器误生成。

⚠️ 虽然功能一样,但初学者常在这里犯错:
- 忘记写敏感列表 → 综合出错误逻辑
- 在组合逻辑中使用非阻塞赋值<=→ 可能引入意外时序
- 不完整分支导致锁存器插入

所以记住一句口诀:组合逻辑用 always @(*),阻塞赋值 =,变量声明为 reg

🧠 这种写法的价值在于——它是通往状态机、ALU、CPU 等复杂模块的大门。


综合之后发生了什么?看看 FPGA 到底“造”了啥

你以为写的只是代码?其实在综合阶段,EDA 工具已经把它变成了真正的硬件结构。

以 Xilinx Artix-7 为例:
- 每个 LUT6(查找表)可以实现任意 6 输入以下的组合函数。
- 半加器只需要两个 2 输入函数:
- XOR2 → 占用 1 个 LUT
- AND2 → 占用 1 个 LUT
- 总共消耗2 个 LUT,零寄存器,零布线资源(理想情况下)

而且现代综合器非常聪明。如果你写了三个半加器,它可能会把重复逻辑合并,甚至将部分逻辑折叠进同一个 LUT。

💡 实际项目中,像这样的小模块往往不会单独保留,而是被优化掉或内联到上级模块中。这也是为什么我们强调:不要为了省资源刻意拆分小模块,让工具去做优化


别急着烧板子!先仿真:你的第一份 Testbench

在 FPGA 开发中,有一条铁律:先仿真,再下载。否则你可能花半小时等布局布线,结果发现逻辑错了。

下面是你需要掌握的第一个测试平台(Testbench):

module tb_half_adder; reg A, B; wire Sum, Carry; // 实例化被测模块 half_adder uut (.A(A), .B(B), .Sum(Sum), .Carry(Carry)); initial begin $monitor("Time=%0t | A=%b B=%b | Sum=%b Carry=%b", $time, A, B, Sum, Carry); // 测试全部输入组合 A = 0; B = 0; #10; A = 0; B = 1; #10; A = 1; B = 0; #10; A = 1; B = 1; #10; $finish; end endmodule

关键点解析:
-$monitor:实时打印信号值,调试神器。
-#10:延迟 10 个时间单位(比如 ns),用于推进仿真时间。
-$finish:结束仿真。

运行结果应与真值表一致:

Time= 0 | A=0 B=0 | Sum=0 Carry=0 Time=10 | A=0 B=1 | Sum=1 Carry=0 Time=20 | A=1 B=0 | Sum=1 Carry=0 Time=30 | A=1 B=1 | Sum=0 Carry=1

✅ 全部匹配 → 设计正确!

这时候你才可以放心进入下一步:综合、实现、生成比特流.bit文件,最后下载到 FPGA 开发板上,接 LED 观察输出。


那么,半加器真的只能用来教学吗?

当然不是。

尽管单个半加器无法独立构成多位加法器,但它是构建更复杂算术单元的基石。例如:

如何用半加器组成全加器?

全加器需要处理三个输入:A、B、Cin。我们可以这样构造:

First HA: Second HA: ┌──────┐ ┌──────┐ A ─────┤ XOR ├─── S1 ─────┤ XOR ├──→ Sum └──────┘ └──────┘ │ ↑ B ─────────┘ ┌───┴───┐ │ AND │ └───┬───┘ ┌─────▼─────┐ Cin ────────────────────┤ OR ├──→ Carry Out └───────────┘
  • 第一个半加器计算 A+B,得到 S1 和 C1
  • 第二个半加器将 S1 与 Cin 相加,得到最终 Sum
  • 两个进位(C1 和中间与门输出)通过或门合并为最终 Carry

虽然这不是最优结构(现代 FPGA 通常用进位链 carry-chain 实现高速加法),但这个思路展示了模块化设计的力量:大功能 = 小模块 + 连接逻辑。


实战建议:写好每一个“Hello World”级别的模块

哪怕是最简单的半加器,也有值得遵循的最佳实践:

建议说明
命名规范使用sum_out,carry_outs,c更清晰
添加注释模块顶部写明功能、作者、日期,方便团队协作
保持可综合性避免在always块中使用#5这类延迟(仅仿真可用)
优先使用标准语法减少对特定厂商原语的依赖,提升跨平台兼容性
参数化预留扩展性即使当前是 1bit,也可尝试定义parameter WIDTH=1

更重要的是:养成“先仿真”的习惯。哪怕你觉得逻辑很简单,也可能因为笔误导致功能异常。仿真是最快、最安全的验证手段。


结尾:从“1+1”走向“亿级门电路”

你可能觉得,一个只能算两位加法的电路,有什么意义?

但请记住:
- 每一台电脑的 ALU,都始于类似的加法单元;
- 每一块 GPU 的矩阵运算核心,背后是成千上万个并行加法器;
- 每一次神经网络推理,都在反复执行乘累加(MAC),而乘法本质也是加法的迭代。

半加器,是你踏入数字世界的第一步。
Verilog,是你与硬件对话的语言。
FPGA,是你亲手构建系统的沙盘。

当你第一次看到 LED 按照你的逻辑点亮时,你会明白:那不只是光,那是你设计的电路在呼吸。

而现在,你已经准备好迈出下一步了——试试用四个半加器做一个 4 位串行加法器?或者挑战一下超前进位加法器(CLA)?

路很长,但从这一刻起,你已经在路上了。

如果你在实现过程中遇到了问题,欢迎留言讨论。我们一起 debug,一起成长。

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

冷启动优化:首次加载时间缩短至10秒以内

冷启动优化&#xff1a;首次加载时间缩短至10秒以内 在如今内容创作工具追求“即时响应”的时代&#xff0c;用户早已不再容忍漫长的等待。尤其是在播客、有声书和虚拟访谈这类需要生成多角色对话的场景中&#xff0c;AI语音系统不仅要输出自然流畅的音频&#xff0c;更要做到…

作者头像 李华
网站建设 2026/3/31 17:24:01

快速理解PCB设计规则:新手必备认知型指南

从零开始读懂PCB设计&#xff1a;新手也能掌握的实战思维你是不是也曾经以为&#xff0c;画一块电路板就是“把元器件连上线”&#xff1f;我第一次做PCB时也是这么想的——结果板子打回来一通电&#xff0c;MCU不启动、Wi-Fi掉线、ADC读数乱跳……一头雾水&#xff0c;查了三天…

作者头像 李华
网站建设 2026/3/27 2:23:08

异地恋情侣纪念:用VibeVoice合成两人未来生活的对话

异地恋情侣纪念&#xff1a;用VibeVoice合成两人未来生活的对话 在视频通话早已习以为常的今天&#xff0c;我们却越来越难听到彼此真实的声音——不是因为距离&#xff0c;而是因为情感被压缩成了文字和表情包。一条“想你了”的消息背后&#xff0c;是千言万语的沉默。有没有…

作者头像 李华
网站建设 2026/3/23 8:40:57

法律证据效力:VibeVoice生成的录音能否作为法庭呈堂证供

法律证据效力&#xff1a;VibeVoice生成的录音能否作为法庭呈堂证供 在一场虚拟法庭模拟中&#xff0c;一段长达45分钟的“当事人陈述”音频被提交为关键证据。声音自然、语调起伏得当&#xff0c;甚至能听到轻微的呼吸声和停顿节奏——然而&#xff0c;经技术鉴定&#xff0c;…

作者头像 李华
网站建设 2026/3/27 17:19:14

c++环境下spidev0.0读取255的工业设备响应问题一文说清

为什么你的 C 程序从/dev/spidev0.0读出全是 0xFF&#xff1f;工业 SPI 通信踩坑实录在嵌入式开发的日常中&#xff0c;你是否也遇到过这样的场景&#xff1a;Linux 下打开/dev/spidev0.0&#xff0c;写了一段看似干净利落的 C 代码调用read()&#xff0c;结果返回的数据每一个…

作者头像 李华
网站建设 2026/3/27 6:30:57

传输层安全:TLS 1.3加密客户端与服务端通信

传输层安全与高效语音生成&#xff1a;TLS 1.3 在 VibeVoice-WEB-UI 中的深度整合 在当今内容创作高度自动化的时代&#xff0c;AI语音系统已不再局限于单句朗读或机械播报。以 VibeVoice-WEB-UI 为代表的新型语音生成平台&#xff0c;正推动播客、教育音频和虚拟角色对话向更自…

作者头像 李华