跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
汇编算法

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

如何使用 Verilog HDL 在 FPGA 上实现半加器。首先解释了半加器的布尔代数原理及真值表,展示了数据流建模(assign)和门级描述两种代码实现方式。接着提供了完整的 Testbench 测试平台代码,指导如何进行功能仿真验证。文章还总结了常见的编程误区,如组合逻辑输出类型错误、误加时钟以及端口连接顺序问题。最后引导读者将设计综合下载至硬件,并建议从半加器扩展到全加器及更复杂的算术逻辑单元设计,旨在帮助初学者建立正确的硬件思维模式。

ArchDesign发布于 2026/4/6更新于 2026/10/24102 浏览

Verilog 实现半加器结构:FPGA 初学实践

为什么从半加器开始?

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

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

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

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

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

更重要的是,它的结构清晰、验证简单、结果直观——非常适合初学者建立硬件直觉。

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

我们来看一组真值表:

ABSumCarry
0000
0110
1010
1101

观察一下:

  • 当输入不同(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 函数',而是想到'一组并行传播的逻辑门网络',你就入门了。

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

目录

  1. Verilog 实现半加器结构:FPGA 初学实践
  2. 为什么从半加器开始?
  3. 半加器的工作原理:不只是公式
  4. 用 Verilog 描述硬件:数据流建模方式
  5. 关键细节解读:
  6. 更贴近硬件的写法:门级描述
  7. 如何验证你的设计?别跳过仿真!
  8. 说说这个 Testbench 的精妙之处:
  9. 常见坑点与避坑指南
  10. ❌ 误区一:给组合逻辑输出声明为 reg,却不放在 always 块里
  11. ❌ 误区二:误加时钟,把组合逻辑写成时序逻辑
  12. ❌ 误区三:忽略端口连接顺序,例化时出错
  13. 在真实 FPGA 上跑起来:下一步做什么?
  14. 从半加器出发,你能走多远?
  15. 写在最后:硬件思维的觉醒

更多推荐文章

查看全部
  • 基于官方 API 搭建 QQ 群聊机器人实战指南
  • Capacitor 实战指南:将 Web 项目打包为跨平台应用
  • OpenVLA 模型微调及机器人端部署实战
  • VSCode GitHub Copilot 安装与使用指南
  • 2023年12月GESP C++七级真题解析:商品交易
  • C++备忘录模式:优雅实现对象状态保存与恢复
  • IndexTTS-2-LLM 定制化部署与语音库构建
  • Rust WebAssembly 与 Three.js 结合实现高性能 3D 粒子系统
  • Python 爬虫实战:7 个经典案例与源码解析
  • C++ 四十年演进:引用、内联与空指针的三大基石
  • macOS 环境下 OpenClaw 部署与配置指南
  • GitHub Copilot Plan 模式核心优势与适用场景
  • 循环神经网络(RNN)与序列数据处理实战
  • Formality 原语(Primitive)概念解析与等价性检查
  • 国产 AI 智能体工具横向测评:腾讯、字节、阿里等主流方案
  • 从长期记忆角度解析 Agent 技术
  • 基于 Rokid 灵珠平台的旅游 AR 智能体搭建指南
  • OpenClaw 本地推理方案:基于 Ollama 部署开源模型替代云端 Token 消耗
  • 2025 年人工智能发展趋势与学习路径指南
  • 前端 AI 工具实战:Claude、OpenCode 与 A2UI 协议

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online