在Quartus中实现半加器:Verilog工程创建指南
从零开始在Quartus中实现半加器:Verilog设计与仿真实战指南
你有没有试过,只用两行代码就“造出”一个能做加法的电路?听起来像魔法,但在FPGA的世界里,这就是现实。
今天我们就来动手实现一个最基础但至关重要的数字电路—— 半加器(Half Adder) 。别看它简单,这可是所有现代计算机算术运算的起点。我们将使用 Intel Quartus Prime 搭配 Verilog HDL ,从创建工程、写代码、综合分析,到用 ModelSim 做功能仿真,完整走一遍 FPGA 开发的标准流程。
无论你是电子类专业的学生,还是刚入门嵌入式逻辑设计的工程师,这篇文章都会带你一步步避开新手常踩的坑,真正理解“硬件描述语言”到底是在描述什么。
半加器:不只是两个门电路那么简单
我们先不急着打开 Quartus,先搞清楚我们要做什么。
半加器的功能非常明确:对两个一位二进制数 A 和 B 相加,输出它们的和(Sum)以及是否产生进位(Carry)。它的真值表是这样的:
| A | B | Sum | Carry |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 1 | 1 | 0 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 1 |
你会发现:
- 当 A 和 B 不同时,Sum = 1,Carry = 0;
- 只有当两者都为 1 时,才产生进位 Carry = 1,而本位结果 Sum = 0。
这背后其实藏着两个基本逻辑门:
-
Sum = A ⊕ B
→ 异或门
-
Carry = A · B
→ 与门
看起来很简单,对吧?但关键在于:这是一个 纯组合逻辑电路 ,没有寄存器、没有时钟,输出完全由当前输入决定。这种特性让它成为学习 Verilog 行为级建模的理想切入点。
更重要的是,它是构建全加器的基础。想象一下四位加法器的第一级,往往就是由一个半加器起头——因为它不需要处理来自更低位的进位输入。所以,掌握半加器的设计方法,实际上是在搭建通往复杂算术单元的第一块跳板。
在Quartus中创建工程:别让第一步卡住你
打开 Quartus Prime 后,第一步是创建新工程。很多人在这里就出了问题:路径带中文、名字乱取、器件选错……这些小细节后期会让你调试到崩溃。
正确操作步骤如下:
-
点击
File → New Project Wizard -
设置工程目录(建议路径全英文,比如
D:\fpga_lab\half_adder_project) -
工程名称填
half_adder_project,顶层实体名也保持一致 - 添加文件时选择“Add all files in directory”,暂时为空也没关系
-
选择目标器件。如果你手头是 DE0-Nano 或类似的教学开发板,推荐选择:
- Family: Cyclone IV E
- Specific device: EP4CE6E22C8
✅ 小贴士:第一次选器件时容易眼花缭乱。记住,只要你的开发板型号确定了,对应的芯片也就唯一了。查手册比猜靠谱得多。
完成向导后,你会看到一个干净的工程界面。接下来,我们需要添加 Verilog 源文件。
写出第一个可综合的Verilog模块
右键左侧栏的 “Files” 区域 →
New...
→ 选择 “Verilog HDL File”,保存为
half_adder.v
。
粘贴以下代码:
//==========================================
// 文件名:half_adder.v
// 功能:实现一位半加器(Half Adder)
// 输入:A, B(单比特)
// 输出:Sum(和),Carry(进位)
// 作者:YourName
// 日期:2025-04-05
//==========================================
module half_adder (
input A,
input B,
output Sum,
output Carry
);
assign Sum = A ^ B; // XOR:生成本位和
assign Carry = A & B; // AND:生成进位
endmodule
这段代码虽然短,但它体现了 Verilog 设计的核心思想—— 我们不是在写程序,而是在描述硬件连接 。
-
assign关键字表示这是一个连续赋值语句,适用于组合逻辑。 -
每一条
assign都对应一条物理连线:一条通向 XOR 门,另一条通向 AND 门。 -
整个模块没有任何
always块或reg类型变量,说明这是纯粹的组合逻辑,不会被综合成触发器。
编译前检查要点:
- 所有端口方向正确吗?A/B 是 input,Sum/Carry 是 output。
-
运算符有没有打错?
^是异或,不是~或|。 - 模块名和文件名是否一致?这关系到后续例化和编译识别。
确认无误后,点击主菜单的
Processing → Start Compilation
,或者直接按快捷键
Ctrl+L
开始全流程编译。
如果一切顺利,你会看到最终报告:“Full Compilation successful”。
看得见的逻辑:RTL视图帮你理解综合结果
很多人写完代码就直接下载,其实错过了最重要的一步—— 验证综合结果是否符合预期 。
在 Quartus 中,你可以直观地看到你的 Verilog 被“翻译”成了什么样的电路结构。
操作路径:
Tools → Netlist Viewers → RTL Viewer
几秒后弹出的窗口中,你会看到类似下面的图形:
A ----\
XOR ---- Sum
B ----/
A ----\
AND ---- Carry
B ----/
没错!这就是你写的那两行
assign
语句的真实映射。Quartus 把它综合成了两个独立的逻辑门,并共享输入信号 A 和 B。
这个图的意义重大:
- 它证明了你的代码确实是可综合的;
- 它让你“看见”了硬件长什么样;
- 对于初学者来说,这是建立“软硬对应”直觉的关键一步。
功能仿真:别跳过这道安全阀
我见过太多人省略仿真环节,直接烧录到 FPGA 上去“碰运气”。结果灯不亮,不知道是代码错了、引脚配错了,还是板子坏了。
正确的做法永远是: 先仿真,再下载 。
我们来写一个简单的 Testbench,验证半加器在四种输入组合下的行为是否正确。
新建一个 Verilog 文件,命名为
tb_half_adder.v
,内容如下:
`timescale 1ns / 1ps
module tb_half_adder;
// 信号声明
reg A, B;
wire Sum, Carry;
// 实例化被测模块(Unit Under Test)
half_adder uut (
.A(A),
.B(B),
.Sum(Sum),
.Carry(Carry)
);
initial begin
// 初始化波形记录
$dumpfile("half_adder_sim.vcd");
$dumpvars(0, tb_half_adder);
// 初始状态
A = 0; B = 0;
#20;
// 测试 0+1
B = 1;
#20;
// 测试 1+0
A = 1;
B = 0;
#20;
// 测试 1+1
B = 1;
#20;
// 结束仿真
$finish;
end
endmodule
关键点解析:
-
reg用于激励输入(需要主动驱动),wire接收输出; -
.uut()是实例化语法,注意端口要一一对应; -
$dumpfile和$dumpvars生成 VCD 波形文件,可在外部工具查看; -
时间控制用
#20表示每个测试状态持续 20ns,足够观察变化。
使用ModelSim进行仿真验证
回到 Quartus 主界面:
Tools → Run Simulation Tool → RTL Simulation
Quartus 会自动调用 ModelSim-Altera,并加载 testbench 和被测模块。
几秒钟后 ModelSim 启动,进入交互式命令行。你可以在 TCL 控制台输入:
run 100ns
然后切换到 Wave 窗口,右键点击信号 →
Add to Wave
,把 A、B、Sum、Carry 全部加进去。
运行结束后,你应该看到清晰的波形:
- 当 A=1 且 B=1 时,Carry 上升为高电平,Sum 回落到 0;
- 其他三种情况,Carry 始终为低,Sum 跟随 A^B 的结果变化。
对比真值表,完全吻合!
这意味着:你的设计在逻辑层面已经通过验证。现在可以放心进入下一步——硬件实现。
下载到FPGA:让理论变成现实
假设你想把半加器下载到 DE0-Nano 开发板上验证:
- 打开 Assignment Editor(Assignments → Pins)
-
分配引脚:
- A → GPIO_0 (如 PIN_1,对应某个拨码开关 SW0)
- B → GPIO_1 (SW1)
- Sum → LEDR[0]
- Carry → LEDR[1]
⚠️ 注意:务必查阅开发板原理图,确保所选引脚是通用 I/O,不要占用专用配置引脚(如 CLK、nSTATUS 等)。
-
再次执行全编译,生成
.sof文件 - 打开 Programmer 工具,通过 USB-Blaster 下载配置
完成后,手动拨动开关 SW0 和 SW1,观察 LED 是否按预期点亮:
- 只有一个开关闭合 → LEDR[0] 亮(Sum=1)
- 两个都闭合 → LEDR[1] 亮(Carry=1)
恭喜你!你现在拥有了一个真正能做加法的硬件电路。
常见问题与避坑指南
❌ 仿真波形没变化?
-
检查 testbench 中是否漏写了
$dumpvars - 查看 ModelSim 是否真的加载了正确的 design unit
-
确保
initial块中有实际的赋值语句
❌ 综合失败提示“Unknown identifier”?
- 很可能是模块名拼写错误,或 testbench 中实例化的模块未被包含进工程
-
检查 File → Add/Remove Files in Project… 是否已加入
half_adder.v
❌ 下载后LED不响应?
- 引脚分配是否保存并重新编译?
- 是否选择了正确的硬件(Hardware Setup 中选对 USB-Blaster)
- 板子供电正常吗?
❌ 误用了 reg 类型导致锁存器?
-
在组合逻辑中不要轻易使用
always @(*)+reg,除非你知道自己在做什么 -
本例中全部用
assign最安全
写在最后:为什么你应该重视这个“玩具项目”
也许你会觉得:“就为了做个异或和与操作,折腾这么多?”
但请记住:
每一个复杂的CPU,都是从这样一个半加器开始生长出来的。
通过这个看似简单的练习,你实际上掌握了:
- 如何创建标准 Quartus 工程
- 如何编写可综合的 Verilog 代码
- 如何利用 RTL Viewer 理解综合结果
- 如何构建 testbench 并完成功能仿真
- 如何将设计部署到真实硬件
这套工作流(Design → Simulate → Implement)正是所有工业级 FPGA 开发的基石。未来你要做的状态机、UART、图像处理、甚至神经网络加速器,都不过是这个流程的延伸和扩展。
所以,不要轻视任何一个“简单”的例子。正是这些点滴积累,构成了你作为硬件工程师的技术底色。
如果你正在学习数字逻辑、准备课程实验,或者想转行进入 FPGA 领域,不妨现在就打开 Quartus,亲手敲一遍这段代码。当你看到 LED 按照二进制规则亮起的那一刻,你会真正体会到—— 硬件,是可以被编程的 。
欢迎在评论区分享你的实现过程和遇到的问题,我们一起讨论进步。
更多推荐
所有评论(0)