news 2026/4/18 11:56:47

从零实现FPGA上的加法器电路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零实现FPGA上的加法器电路

手把手教你用FPGA从零搭建一个加法器:不只是“1+1=2”

你有没有想过,计算机里最简单的“1+1”,背后其实是一场精密的硬件协奏?

在如今动辄讨论AI大模型、GPU加速的时代,我们很容易忽略——所有复杂的运算,最终都建立在像加法器这样最基础的数字电路之上。而如果你想真正理解硬件是怎么“算数”的,最好的方式不是背公式,而是亲手在FPGA上搭一个出来。

本文就带你从零开始,不用任何IP核,不调现成模块,一行行写Verilog代码,一步步把两个二进制数相加的功能实现在真实的FPGA开发板上。你会看到:
- 为什么说全加器是数字世界的“原子”单元
- 如何用几个逻辑门拼出“3+5=8”?
- 为什么看似简单的“进位”会成为性能瓶颈?
- 最后,如何把你的设计烧进FPGA,用拨码开关输入、LED灯输出结果,亲眼见证硬件在“思考”。

这不仅是一个教学项目,更是一次对数字系统底层逻辑的深度还原。


加法器的本质:不只是数学,更是电路行为

我们先抛开FPGA工具链和代码,回到最原始的问题:怎么让硬件做加法?

软件里一句a + b编译后可能变成一条CPU指令,但在硬件层面,它必须被拆解为一系列物理信号的流动——高电平代表1,低电平代表0,通过晶体管组成的逻辑门完成判断与传递。

以两个1位二进制数为例:

A = 1, B = 1 → Sum = 0, Carry = 1

这个过程不能靠“记忆”,只能靠当前输入决定输出。换句话说,这是一个典型的组合逻辑电路:没有寄存器、没有状态机,只有即时响应。

于是我们定义一种基本结构:全加器(Full Adder),它可以同时处理两个数据位和一个来自低位的进位(Cin),输出本位和(Sum)与新的进位(Cout)。它的真值表长这样:

ABCinSumCout
00000
01010
10010
11001
11111

别急着记,我们可以从中推导出关键公式:

  • Sum = A ⊕ B ⊕ Cin
  • Cout = (A & B) | (Cin & (A ^ B))

这两个表达式就是整个加法器的“灵魂”。它们意味着什么?
- 异或(XOR)决定了是否产生“和”;
- 与(AND)和或(OR)则捕捉了两种进位场景:两数都是1,或者其中一者为1且已有进位。

这些操作都可以用电路上可实现的基本门来构建。也就是说,只要你有足够多的与门、或门、异或门,就能造出任意精度的加法器。

而这,正是FPGA的魅力所在:你可以直接操控硬件的行为,而不是等待操作系统调度。


第一步:打造最小单元——一位全加器

我们现在进入实战环节。目标很明确:用Verilog HDL描述一个功能正确的全加器。

module full_adder( input A, input B, input Cin, output Sum, output Cout ); wire xor1_out; wire and1_out; wire and2_out; assign xor1_out = A ^ B; assign Sum = xor1_out ^ Cin; assign and1_out = A & B; assign and2_out = Cin & xor1_out; assign Cout = and1_out | and2_out; endmodule

这段代码看起来简单,但每一步都有讲究:

  • 使用wire定义中间信号,保持组合逻辑特性;
  • 先计算A ^ B,避免重复运算;
  • 进位分为两个部分:“本位进位”(A & B)和“传递进位”(Cin & (A^B)),最后合并。

📌 小贴士:虽然现代综合器能自动优化冗余逻辑,但手动展开有助于理解内部结构,在调试时也能更快定位问题。

这个模块可以独立仿真验证。比如测试A=1, B=1, Cin=1,应得Sum=1, Cout=1—— 即十进制中的1+1+1=3,二进制表示为11

一旦单个全加器跑通,下一步就是把它当成“积木块”,搭更大的系统。


第二步:级联升级——构建4位波纹进位加法器

现在我们要处理的是真正的数值了。比如想算3 + 5,对应的二进制是:

0011 + 0101 ------- 1000

这就需要四个全加器串联起来,形成所谓的波纹进位加法器(Ripple Carry Adder, RCA)

它的结构非常直观:
- 每一位对应一个全加器;
- 上一级的Cout接下一级的Cin
- 最低位的Cin可设为0(除非用于减法补码运算);

下面是Verilog实现:

module four_bit_adder( input [3:0] A, input [3:0] B, input Cin, output [3:0] Sum, output Cout ); wire c1, c2, c3; full_adder fa0 (.A(A[0]), .B(B[0]), .Cin(Cin), .Sum(Sum[0]), .Cout(c1)); full_adder fa1 (.A(A[1]), .B(B[1]), .Cin(c1), .Sum(Sum[1]), .Cout(c2)); full_adder fa2 (.A(A[2]), .B(B[2]), .Cin(c2), .Sum(Sum[2]), .Cout(c3)); full_adder fa3 (.A(A[3]), .B(B[3]), .Cin(c3), .Sum(Sum[3]), .Cout(Cout)); endmodule

注意这里的连接顺序:c1 → c2 → c3 → Cout,构成了清晰的进位链。

这种设计体现了FPGA开发的核心思想之一:模块化复用。你不需要每次都重新设计逻辑,只要确保子模块正确,就可以像搭乐高一样构建复杂系统。

不过也要意识到它的局限性——速度受限于进位传播延迟。因为第4位必须等第3位算完才能开始,就像接力赛跑一样,每一棒都要等前一棒交棒。

对于4位来说还好,延迟微乎其微;但如果是32位甚至64位整数加法,这种结构就会严重拖慢整体性能。这也是后来出现超前进位加法器(CLA)的原因。

但现在,我们的目标是“从零实现”,所以RCA是最合适的选择。


第三步:仿真验证——让代码先在虚拟世界跑通

写完代码不等于万事大吉。下一步是功能仿真,确保逻辑无误再上板。

我们写一个简单的Testbench来驱动four_bit_adder

module four_bit_adder_tb; reg [3:0] A, B; reg Cin; wire [3:0] Sum; wire Cout; // 实例化被测模块 four_bit_adder uut ( .A(A), .B(B), .Cin(Cin), .Sum(Sum), .Cout(Cout) ); initial begin $monitor("T=%0t | A=%b (%d), B=%b (%d), Cin=%b | Sum=%b (%d), Cout=%b", $time, A, A, B, B, Cin, Sum, Sum, Cout); // 测试用例 #10 A = 4'b0011; B = 4'b0101; Cin = 0; // 3 + 5 = 8 #10 A = 4'b1111; B = 4'b0001; Cin = 0; // 15 + 1 = 16 → 应产生进位 #10 A = 4'b1010; B = 4'b0110; Cin = 1; // 10 + 6 + 1 = 17 → 多进位测试 #10 $finish; end endmodule

运行ModelSim或Vivado Simulator后,你会看到类似输出:

T=0 | A=0011 (3), B=0101 (5), Cin=0 | Sum=1000 (8), Cout=0 T=10 | A=1111 (15), B=0001 (1), Cin=0 | Sum=0000 (0), Cout=1 T=20 | A=1010 (10), B=0110 (6), Cin=1 | Sum=0001 (1), Cout=1

全部符合预期!说明我们的加法器在逻辑层面已经可靠。

💡 坑点提醒:如果发现Sum错乱,优先检查引脚映射是否错位;若Cout始终为0,可能是最后一级Cout未正确连接到模块输出。


第四步:部署到真实FPGA——让硬件“亮”起来

终于到了激动人心的时刻:把设计下载到实际的FPGA芯片上。

这里以Xilinx Artix-7系列开发板(如Nexys A7)为例,使用Vivado工具链完成全流程。

1. 创建工程并添加源文件

  • 新建RTL工程,选择目标器件(如XC7A35TCSG324-1);
  • 添加full_adder.vfour_bit_adder.v
  • 添加测试平台用于仿真。

2. 引脚约束(XDC文件)

为了让FPGA知道哪个引脚接开关、哪个接LED,我们需要编写约束文件:

## 输入:使用8位拨码开关控制 A[3:0] 和 B[3:0] set_property PACKAGE_PIN J15 [get_ports {A[0]}] # 对应SW0 set_property PACKAGE_PIN L16 [get_ports {A[1]}] # SW1 set_property PACKAGE_PIN M13 [get_ports {A[2]}] # SW2 set_property PACKAGE_PIN R15 [get_ports {A[3]}] # SW3 set_property PACKAGE_PIN R17 [get_ports {B[0]}] # SW4 set_property PACKAGE_PIN T18 [get_ports {B[1]}] # SW5 set_property PACKAGE_PIN U17 [get_ports {B[2]}] # SW6 set_property PACKAGE_PIN W16 [get_ports {B[3]}] # SW7 set_property PACKAGE_PIN H18 [get_ports Cin] # SW8 控制进位输入 ## 输出:使用LED显示结果 set_property PACKAGE_PIN U16 [get_ports {Sum[0]}] # LD0 set_property PACKAGE_PIN E19 [get_ports {Sum[1]}] # LD1 set_property PACKAGE_PIN F18 [get_ports {Sum[2]}] # LD2 set_property PACKAGE_PIN D17 [get_ports {Sum[3]}] # LD3 set_property PACKAGE_PIN D18 [get_ports Cout] # LD4 显示进位 ## 设置电气标准 set_property IOSTANDARD LVCMOS33 [get_ports]

✅ 提示:务必对照开发板原理图确认引脚编号,否则可能导致烧录失败或IO损坏。

3. 综合、实现、生成比特流

  • 点击Run Synthesis→ 查看资源使用情况(典型情况下:约8~10个LUTs);
  • 执行Implementation→ 工具会进行布局布线;
  • 查看Timing Summary→ 确保无建立/保持时间违例;
  • 生成.bit文件并通过JTAG下载到板子。

4. 实物验证

打开电源,拨动开关设置A=0011,B=0101,观察LED:
- LD3~LD0 应显示1000(即8)
- LD4(Cout)熄灭

再试一组:A=1111, B=0001→ 结果应为0000并点亮进位灯,表示溢出。

这一刻,你不再是“调别人写的代码”,而是真正创造了能计算的硬件


性能与优化:当“进位”成了瓶颈

虽然RCA结构简单易懂,但它有个致命弱点:延迟随位宽线性增长

假设每个全加器的进位延迟为 Δt,那么n位加法器的最大延迟就是 n×Δt。这对高速应用来说不可接受。

解决办法是什么?

超前进位加法器(Carry Look-Ahead Adder, CLA)

其核心思想是:提前预测每一位是否会生成或传播进位,从而打破串行依赖。

引入两个新概念:
-Generate (G)= A & B → 无论Cin如何都会产生进位
-Propagate (P)= A ^ B → 当Cin=1时将进位传下去

然后可以直接写出各级进位:
- C1 = G0 | (P0 & Cin)
- C2 = G1 | (P1 & G0) | (P1 & P0 & Cin)
- …

这样就不必逐级等待,大大缩短关键路径。

虽然实现稍复杂,但在高位宽场景下性能提升显著。这也是现代处理器ALU中普遍采用的技术。

但对于初学者而言,先掌握RCA的意义在于:你清楚地看到了性能瓶颈的根源在哪里


写在最后:这不是终点,而是起点

当你第一次用手拨动开关,看着LED亮起那一刻的结果,你会有一种特别的成就感——这不是程序输出,而是电流在硅片中流动的真实痕迹

这个小小的加法器项目,涵盖了许多深远的主题:
-组合逻辑设计原则
-模块化与层次化构建方法
-HDL编码风格与可综合性
-仿真验证流程
-FPGA工具链实战
-硬件调试技巧

更重要的是,它教会你一种思维方式:自底向上,层层抽象

未来的ALU、状态机、流水线CPU、甚至神经网络加速器,本质上都是由这样的基础单元堆叠而来。

所以别小看这个“只会加法”的电路。它是通往数字世界深处的第一扇门。


如果你正在学习FPGA,不妨今晚就动手试试。找一块入门级开发板,照着这篇教程走一遍。遇到问题没关系,调试的过程本身就是最好的学习。

毕竟,最好的硬件工程师,从来都不是只懂理论的人,而是能让灯亮起来的那个。

欢迎在评论区晒出你的实物照片或仿真截图,一起交流踩过的坑、绕过的弯路。我们一起,把“不可能”变成“已实现”。

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

GSE宏编译器:魔兽世界自动化技能编排革命性工具

GSE宏编译器:魔兽世界自动化技能编排革命性工具 【免费下载链接】GSE-Advanced-Macro-Compiler GSE is an alternative advanced macro editor and engine for World of Warcraft. It uses Travis for UnitTests, Coveralls to report on test coverage and the Cur…

作者头像 李华
网站建设 2026/4/18 8:04:15

Figma HTML转换器:打破设计与开发壁垒的终极解决方案

Figma HTML转换器:打破设计与开发壁垒的终极解决方案 【免费下载链接】figma-html Builder.io for Figma: AI generation, export to code, import from web 项目地址: https://gitcode.com/gh_mirrors/fi/figma-html 在数字产品开发的日常工作中&#xff0c…

作者头像 李华
网站建设 2026/4/18 7:05:13

5分钟极速上手:B站视频批量下载神器完全指南

想要轻松下载B站视频进行离线观看?BilibiliDown作为一款专业的B站视频下载工具,支持Windows、Linux和Mac全平台运行,提供从单个视频到UP主全集、收藏夹内容的全面下载解决方案。这款免费开源软件能够帮助用户快速获取各类B站视频资源&#xf…

作者头像 李华
网站建设 2026/4/18 8:20:49

B站视频下载神器BilibiliDown:从零开始到精通的全方位指南

还在为无法离线观看B站优质内容而烦恼吗?想要轻松保存喜欢的UP主视频、收藏夹内容或者稍后再看列表?今天为你揭秘BilibiliDown这款强大的B站视频下载工具,让你的视频保存体验变得简单高效! 【免费下载链接】BilibiliDown (GUI-多平…

作者头像 李华
网站建设 2026/4/18 10:19:41

fre:ac音频转换器终极教程:从新手到专家的完整使用指南

fre:ac音频转换器终极教程:从新手到专家的完整使用指南 【免费下载链接】freac The fre:ac audio converter project 项目地址: https://gitcode.com/gh_mirrors/fr/freac 在数字音乐时代,音频格式转换已成为音乐管理和分享的重要环节。fre:ac作为…

作者头像 李华