用Verilog描述半加器结构:FPGA初学实践

从零开始:用Verilog在FPGA上实现半加器——新手也能懂的硬件入门实战

你有没有想过,计算机是怎么做加法的?
不是打开计算器点几下,而是 从最底层的晶体管和逻辑门出发 ,靠电流“算”出来的那种。

今天我们就来动手实现一个最简单的加法单元—— 半加器(Half Adder) 。它虽然小,却是所有现代处理器中加法功能的起点。更重要的是,我们将用 Verilog HDL 把这个电路“写”出来,并部署到真实的 FPGA 芯片上运行。

这不仅是一次编码练习,更是一场从软件思维向硬件设计跃迁的启蒙之旅。


为什么从半加器开始?

初学 FPGA 或数字电路时,很多人一上来就想搞图像处理、跑神经网络。结果呢?卡在第一个时钟信号就动不了了。

其实,真正该做的第一件事是: 理解组合逻辑的本质

而半加器,就是通往这个世界的钥匙。

它只做一件简单的事:把两个比特 A B 相加,输出它们的“和”与“进位”。听起来像小学数学题,但它背后藏着三个关键知识点:

  1. 布尔代数的实际应用 (异或、与运算)
  2. 无状态的组合逻辑行为
  3. 模块化设计的基本范式

更重要的是,它的结构清晰、验证简单、结果直观——非常适合点亮你的第一个LED灯前先点亮大脑。


半加器的工作原理:不只是公式

我们来看一组真值表:

A B Sum Carry
0 0 0 0
0 1 1 0
1 0 1 0
1 1 0 1

观察一下:
- 当输入不同(0+1),Sum=1,Carry=0;
- 当输入相同且为1(1+1),Sum=0,Carry=1 —— 这正是二进制进位!

所以我们可以写出两个表达式:

Sum = A ⊕ B Carry = A · B 

是不是很简洁?但这不是重点。重点是: 这些符号如何变成真实世界里的电路?

答案就是——Verilog。


用 Verilog 描述硬件:数据流建模方式

在 FPGA 设计中,我们不“调用函数”,而是“搭建电路”。下面这段代码就是在“画一张电路图”:

module half_adder ( input wire A, input wire B, output wire Sum, output wire Carry ); assign Sum = A ^ B; // 异或门出“和” assign Carry = A & B; // 与门出“进位” endmodule 

关键细节解读:

  • input wire / output wire :声明端口方向和类型。注意这里都用了 wire ,因为这是纯组合逻辑输出。
  • assign :连续赋值语句,专用于 wire 类型。只要 A 或 B 变化,Sum 和 Carry 就立即重新计算。
  • 没有 always 块,没有时钟,也没有寄存器 —— 完全符合组合逻辑特征。
✅ 提示:如果你在这里用了 reg 并放在 always @(*) 中赋值,也不是错,但对初学者容易混淆组合/时序逻辑边界。建议从 assign 开始建立正确直觉。

这段代码经过综合工具(如 Vivado 或 Quartus)处理后,会被映射成 FPGA 内部的 LUT(查找表)资源,物理上等效于一个异或门加一个与门。


更贴近硬件的写法:门级描述

如果你想看得更“透”一点,可以用内置原语直接例化逻辑门:

module half_adder_gate ( input wire A, input wire B, output wire Sum, output wire Carry ); xor(Sum, A, B); // 使用Verilog内置XOR原语 and(Carry, A, B); // 使用AND原语 endmodule 

这种写法更像是在搭积木:
- xor(Sum, A, B) 表示:“接一个异或门,输入是 A 和 B,输出连到 Sum”
- 工具会直接将其绑定到底层硬件单元

📌 适用场景 :教学演示、精确控制门延迟分析、或者你想告诉别人“我确实只用了两个门”。

但在实际工程中, 推荐使用数据流建模( assign ,因为它更简洁、可读性强,且综合效果一样优秀。


如何验证你的设计?别跳过仿真!

写完代码就烧进板子?No no no。 硬件开发的第一法则:能仿则仿。

我们需要一个测试平台(Testbench),来自动检查四种输入组合是否全部通过。

`timescale 1ns / 1ps 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 

说说这个 Testbench 的精妙之处:

  • $monitor :每当时序推进,自动打印当前状态。比手动插入 $display 更省事。
  • #10 :表示等待10个时间单位(由 timescale 定义为1ns),模拟信号切换间隔。
  • 最终输出应如下:
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 

如果看到最后一行 Sum=0、Carry=1,恭喜你!你的半加器已经通过了功能验证。

🔍 小技巧:很多初学者漏掉某个输入组合,导致 Carry 错误未被发现。一定要遍历全部可能输入!

常见坑点与避坑指南

我在带学生做这个实验时,发现以下几个问题反复出现:

❌ 误区一:给组合逻辑输出声明为 reg ,却不放在 always 块里

比如这样写:

output reg Sum; // 错误!声明为reg却没有过程赋值 

但后面还是用 assign 赋值 → 综合报错或产生意外锁存器。

✅ 正确做法:
- 输出是组合逻辑 → 用 wire
- 若必须用 reg (例如在 always 中赋值),则要配合 always @(*)

❌ 误区二:误加时钟,把组合逻辑写成时序逻辑

有人习惯性加上 clk always @(posedge clk) ,结果输出延迟一个周期。

记住一句话: 半加器没有记忆,不需要时钟

一旦加了时钟,你就不再是“即时响应”的加法器,而是一个带延迟的状态机了。

❌ 误区三:忽略端口连接顺序,例化时出错

尤其是在使用门级原语时:

xor(Sum, A, B); // 正确:第一个是输出,后两个是输入 

但如果写反了: xor(A, Sum, B); —— 那就完全乱套了。


在真实FPGA上跑起来:下一步做什么?

当你完成仿真并确认逻辑正确后,就可以进入物理实现阶段了:

  1. 创建工程,添加 half_adder.v 和约束文件(.xdc 或 .qsf)
  2. 分配引脚:比如让 A 接按键 SW0,B 接 SW1,Sum 和 Carry 接 LED0、LED1
  3. 综合 → 实现 → 生成比特流
  4. 下载到开发板

然后动手试试:
- 按下不同组合的开关,观察LED亮灭情况
- 当两个输入都为高时,Carry 对应的 LED 应该亮起

那一刻你会感受到一种独特的成就感: 我写的代码,真的在“通电运算”


从半加器出发,你能走多远?

别看它简单, 全加器(Full Adder)其实就是两个半加器拼起来再加一个或门

你可以尝试扩展:
- 把两个 half_adder 组合成 full_adder
- 级联四个 full_adder 构成 4 位加法器
- 加入进位链优化,挑战超前进位加法器(Carry-Lookahead Adder)

每一步都在复刻真实CPU内部的ALU演化路径。

甚至在未来,你可以基于这套思想去实现:
- 快速乘法器
- CRC校验生成器
- 浮点运算单元的定点部分


写在最后:硬件思维的觉醒

写完这篇,我想强调一点:

学FPGA不是学会写Verilog语法就够了,而是要学会 用硬件的方式思考问题

当你看到 A + B ,不再想到“调用add函数”,而是想到“一组并行传播的逻辑门网络”,你就入门了。

而这一切,可以从一个只有两行赋值语句的半加器开始。


💡 动手建议清单
- [ ] 编写 half_adder 模块
- [ ] 搭建 testbench 并运行仿真
- [ ] 观察波形图(可用 GTKWave 或 Vivado Simulator)
- [ ] 下载到开发板验证
- [ ] 尝试改写为门级结构
- [ ] 用两个半加器构建全加器(进阶挑战)

如果你正在学习数字系统设计,不妨把今天的代码保存下来。未来某天回看,你会感谢那个认真对待“最简单电路”的自己。

欢迎在评论区分享你的实现截图或遇到的问题,我们一起调试、一起成长。

Read more

彻底关闭Win10中烦人的365 Copilot弹窗的6种方法

快速体验 1. 打开 InsCode(快马)平台 https://www.inscode.net 2. 点击'项目生成'按钮,等待项目生成完整后预览效果 输入框输入如下内容 帮我开发一个Windows系统优化小工具,用于帮助普通用户一键禁用各类系统弹窗和推送功能。系统交互细节:1.提供常见弹窗类型选择 2.显示当前系统状态 3.一键禁用功能 4.支持恢复默认设置。注意事项:需要管理员权限运行 最近很多Win10用户在系统升级后都遇到了Microsoft 365 Copilot频繁弹窗的问题,这个功能虽然智能,但频繁的打扰确实影响工作效率。经过实测,我总结了6种有效的关闭方法,从简单隐藏到彻底禁用一应俱全。 1. 任务栏临时隐藏是最简单的解决方案,只需右键任务栏取消勾选相关选项。但这个方法只是隐藏入口,Copilot功能仍在后台运行。 2. 组策略彻底禁用是最推荐的方式,通过系统内置的组策略编辑器可以完全关闭Copilot。操作时需要管理员权限,设置完成后需要重启生效。这个方法禁用后连快捷键都会失效,

本地AI绘画新选择:麦橘超然Flux控制台使用心得

本地AI绘画新选择:麦橘超然Flux控制台使用心得 麦橘超然 - Flux 离线图像生成控制台 基于 DiffSynth-Studio 构建的 Flux.1 图像生成 Web 服务。集成了“麦橘超然”模型(majicflus_v1),采用 float8 量化技术,大幅优化了显存占用。界面简单直观,支持自定义提示词、种子和步数,适合在中低显存设备上进行高质量 AI 绘画测试。 1. 为什么我愿意为它腾出一块显存? 你有没有过这样的经历:想在家用RTX 3060跑一个Flux模型,结果刚加载完DiT主干就爆显存?或者打开网页UI,等了三分钟才看到“Loading…”——而你的风扇已经唱起了交响乐? 我试过不下五种本地Flux方案,直到遇见“麦橘超然”。它不是又一个套壳WebUI,而是一次真正面向普通创作者的工程减负:不靠堆显存,不靠换硬件,而是用float8量化+DiT分层卸载+Gradio极简交互,把原本需要24GB显存的任务,

AIGC实战测评:蓝耘元生代通义万相2.1图生视频的完美部署~

AIGC实战测评:蓝耘元生代通义万相2.1图生视频的完美部署~

文章目录 * 👏什么是图生视频? * 👏通义万相2.1图生视频 * 👏开源仓库代码 * 👏蓝耘元生代部署通义万相2.1图生视频 * 👏平台注册 * 👏部署通义万相2.1图生视频 * 👏使用通义万相2.1图生视频 * 👏总结 👏什么是图生视频? 图生视频是一种通过图像生成技术,结合文本信息生成视频的创新方式。通过输入一张图像和相关的描述文本,系统能够根据这些输入生成一个符合描述的视频。该技术利用深度学习和计算机视觉技术,将静态图像转化为动态视频,实现视觉内容的快速生成。这种技术的应用广泛,涵盖了内容创作、影视制作、广告生成等多个领域。 👏通义万相2.1图生视频 阿里巴巴旗下“通义”品牌宣布,其AI视频生成模型“通义万相Wan”正式推出独立网站,标志着其生成式AI技术的重大进展。新网站现已开放(网址:wan.video),用户可直接登录体验“文本生成视频”和“图像生成视频”功能,无需本地部署,极大降低了使用门槛。此外,每天登录网站还可获赠积分,激励用户持续探索。 文章链接:https:

小米温湿度计智能家居改造:从痛点到解决方案的实战指南

小米温湿度计智能家居改造:从痛点到解决方案的实战指南 【免费下载链接】ATC_MiThermometerCustom firmware for the Xiaomi Thermometers and Telink Flasher 项目地址: https://gitcode.com/gh_mirrors/atc/ATC_MiThermometer 智能家居时代,如何让普通温湿度计升级为低功耗蓝牙传感器?本文将带你从零开始,通过ATC_MiThermometer固件改造小米温湿度计,打造专业级家庭环境监测系统,解决官方固件数据延迟、功耗高、兼容性差等问题。 一、智能家居监测的三大痛点与解决方案 痛点1:电池频繁更换,维护成本高 普通温湿度计通常每2-3个月需要更换电池,尤其在多个房间部署时,更换电池成为繁琐的日常任务。 痛点2:数据不同步,自动化响应延迟 官方固件的蓝牙广播间隔固定且较长,导致Home Assistant等平台无法实时获取环境数据,影响自动化场景的响应速度。 痛点3:数据精度不足,决策参考价值低 原厂固件在极端环境下测量误差可达±