Quartus-II 9.0实战:从半加器到4位加法器的完整设计流程(附仿真避坑指南)
Quartus-II 9.0实战:从半加器到4位加法器的完整设计流程(附仿真避坑指南)
如果你刚开始接触数字逻辑设计或FPGA开发,面对Quartus-II这样功能强大的软件,可能会感到有些无从下手。尤其是在实验室环境下,当理论课上学到的半加器、全加器需要亲手在软件中实现并验证时,一个看似简单的仿真步骤报错,就足以让整个下午的进度停滞不前。这篇文章就是为你准备的。我们不谈空洞的理论,直接从打开软件开始,手把手带你完成从最基础的半加器,到构建1位全加器,最终搭建一个4位加法器的完整设计流程。更重要的是,我会把那些教程里常常一笔带过、但实际操作中几乎人人都会踩的“坑”——比如那个恼人的“Generate Functional Simulation Netlist”问题——掰开揉碎了讲清楚,让你不仅能做出东西,更能理解背后的原因,真正掌握Quartus-II进行数字系统设计的方法论。
1. 设计基石:理解半加器与全加器的核心逻辑
在动手操作软件之前,我们必须先厘清要设计的对象究竟是什么。数字世界的基础是二进制,而加法则是所有算术运算的根基。半加器(Half Adder)就是这个根基中最简单的单元。它的任务非常纯粹:对两个一位的二进制数(A和B)进行加法运算,输出一个和位(Sum, 简称S)和一个进位位(Carry, 简称C)。关键在于,它“目光短浅”,只处理当前位的两个输入,完全不关心来自更低位的进位。
它的行为可以用一张极其简洁的真值表来定义:
| 输入 A | 输入 B | 和输出 S | 进位输出 C |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 1 | 1 | 0 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 1 |
观察上表,你会发现和输出S只有在A和B不同时为1时才为1,这正好是异或(XOR)逻辑;而进位输出C只有在A和B同时为1时才为1,这对应与(AND)逻辑。因此,半加器的逻辑表达式可以优雅地写为:
S = A ⊕ B
C = A · B
这就是我们用Quartus-II画原理图时,直接调用一个异或门和一个与门就能构成半加器的理论依据。
然而,现实世界的加法不可能总是孤立的一位运算。当处理多位数字时,每一位的加法都必须考虑来自低位的进位。这个能处理三个输入(两个加数A、B和一个来自低位的进位Cin)的“完全体”,就是全加器(Full Adder)。它输出一个和(Sum)以及一个向高位的进位(Cout)。
全加器的真值表稍微复杂一些,但规律清晰:
| Cin | A | B | Sum | Cout |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 1 | 0 |
| 0 | 1 | 0 | 1 | 0 |
| 0 | 1 | 1 | 0 | 1 |
| 1 | 0 | 0 | 1 | 0 |
| 1 | 0 | 1 | 0 | 1 |
| 1 | 1 | 0 | 0 | 1 |
| 1 | 1 | 1 | 1 | 1 |
从真值表可以推导出全加器的逻辑表达式:
Sum = A ⊕ B ⊕ Cin
Cout = (A · B) + (Cin · (A ⊕ B))
仔细观察Cout的表达式,它由两部分组成:(A · B) 以及 (Cin · (A ⊕ B))。这给了我们一个绝妙的启示:一个全加器可以由两个半加器和一个或门构成。第一个半加器计算A和B的“半加”结果,产生一个临时和S_temp和一个进位C1;第二个半加器将S_temp与Cin相加,产生最终的和Sum以及另一个进位C2;最终的进位Cout则是C1和C2的或运算结果。这个“自底向上”的构建思想,正是我们后续在Quartus中采用层次化设计的基础。
提示:务必在纸上亲手推导一遍半加器和全加器的真值表与逻辑表达式。这个看似简单的过程,能帮你建立起对组合逻辑电路的直觉,后续无论用原理图还是HDL语言描述,都会得心应手。
2. 环境搭建与第一个工程:在Quartus-II 9.0中实现半加器
好了,理论热身完毕,现在打开你的Quartus-II 9.0。这个版本虽然界面不如新版华丽,但用于学习数字逻辑和FPGA入门,其核心功能完全足够且稳定。我们的第一个目标,是用原理图输入法实现一个半加器。
2.1 创建新工程与原理图编辑
启动软件后,点击 File -> New Project Wizard。这里有几个关键点需要注意:
- 工程目录:建议为你每个实验单独建立一个文件夹,路径不要有中文或特殊字符,避免不必要的麻烦。
- 器件选择:对于学习仿真而言,具体型号影响不大。你可以选择一个常见的Cyclone系列芯片,例如
EP2C5T144C8。如果实验室有开发板,务必选择与板上一致的FPGA型号。 - 设计文件:在“Add Files”步骤,因为我们从零开始,直接点Next跳过。
工程创建好后,我们需要创建原理图文件。点击 File -> New,在弹出的对话框中选择 Design Files 标签页下的 Block Diagram/Schematic File,点击OK。一个空白的绘图区域就出现了。
接下来,我们将逻辑门和输入输出端口“放置”到图纸上。在空白处双击,会弹出符号库对话框。在Libraries列表中,展开primitives下的logic,这里存放着基本的逻辑门。我们需要找到并放置以下符号:
xor:异或门,用于生成和S。and2:2输入与门,用于生成进位C。input:输入端口。output:输出端口。
放置两个input,分别命名为A和B;放置两个output,分别命名为S和C。然后,用鼠标拖动连线工具(或按快捷键Ctrl+鼠标拖动)将这些元件按照半加器的逻辑连接起来:A和B连接到xor的两个输入端,xor的输出连接到S;同时,A和B也连接到and2的两个输入端,and2的输出连接到C。
你的原理图应该看起来像这样(文字描述):左侧并排两个INPUT,名为A和B,各自引出一根线,同时连接到右侧一个XOR门和一个AND2门;XOR门的输出连接到一个名为S的OUTPUT;AND2门的输出连接到一个名为C的OUTPUT。
2.2 编译、分析与关键设置
画好图后,务必先保存文件(例如half_adder.bdf)。保存后,就可以进行编译了。点击工具栏上那个蓝色的三角形“Start Compilation”按钮,或者通过 Processing -> Start Compilation 启动。
编译过程包括分析、综合、布局布线等多个阶段。如果原理图没有错误,你会在右下角的消息窗口看到“Full Compilation was successful”的提示。如果有错误,通常会提示连线错误、命名冲突等,根据提示返回原理图检查即可。
编译成功意味着我们的设计在逻辑和语法上被工具接受了,并生成了可以用于后续仿真和下载的网表文件。但这里有一个至关重要的概念:功能仿真和时序仿真需要不同的网表。功能仿真只验证逻辑正确性,忽略所有门电路的延迟;而时序仿真则考虑了综合后实际电路的布线延迟,更接近硬件真实行为。在Quartus-II中,进行功能仿真前,必须手动生成一个用于功能仿真的网表,这就是我们“避坑指南”的核心之一。
3. 仿真实战与核心“避坑”:功能仿真与时序仿真的正确姿势
编译成功后,我们迫切想知道这个半加器是否按真值表工作。这就需要用到仿真。
3.1 创建波形仿真文件
点击 File -> New,选择 Vector Waveform File。会打开一个空白的波形编辑器。首先,我们需要设置仿真时间长度。点击 Edit -> End Time,对于一个简单的半加器,设置1us(微秒)通常足够了。
接下来,将我们设计的输入输出端口导入到这个波形文件中。在左侧空白区域右键,选择 Insert -> Insert Node or Bus。在弹出的窗口中点击 Node Finder。在Node Finder窗口中,点击List按钮,你应该能看到A, B, S, C四个节点。选中它们,点击右箭头将其添加到右侧,然后点击OK。
现在,波形窗口中出现了这四条信号线。我们需要为输入信号A和B设置激励波形,也就是告诉仿真器,在什么时间点给A和B什么值。为了全面测试,我们让A和B遍历所有4种可能的组合(00, 01, 10, 11)。
- 选中
A信号的那一行,利用左侧工具栏的时钟波形、高低电平设置工具,或者直接鼠标拖动绘制,使其在0-250ns为0,250-500ns为0,500-750ns为1,750ns-1us为1。 - 同理,设置
B信号在0-250ns为0,250-500ns为1,500-750ns为0,750ns-1us为1。
这样,在0-250ns区间,A=0, B=0;250-500ns,A=0, B=1,以此类推。设置好后,保存这个波形文件(例如half_adder.vwf),建议与工程文件放在同一目录。
3.2 执行功能仿真与关键步骤
现在来到最关键的一步。点击 Processing -> Simulator Tool,会打开仿真工具设置窗口。
- 仿真模式:在
Simulation mode下拉菜单中,选择Functional。 - 仿真输入:在
Simulation input中,选择你刚才保存的波形文件half_adder.vwf。 - 核心操作:必须勾选下方
Generate Functional Simulation Netlist选项框! 这就是很多新手会忽略导致仿真报错的“坑”。Quartus需要基于你的设计,专门生成一个不带时序信息的网表用于功能仿真。如果不勾选,工具会尝试使用之前全编译生成的(包含时序信息的)网表,从而导致仿真失败。 - 点击
Generate Functional Simulation Netlist按钮。等待其生成完成。 - 最后,点击
Start按钮开始功能仿真。
仿真结束后,点击Report按钮或直接打开波形文件,你就能看到仿真波形。S和C的波形应该完全符合半加器真值表:当A和B不同时为1时S为高,同时为1时C为高。
3.3 执行时序仿真
时序仿真的流程类似,但目的不同。在Simulator Tool窗口中,将Simulation mode改为Timing。注意,此时不需要也不能勾选Generate Functional Simulation Netlist,因为时序仿真就是要使用包含实际延迟的网表。直接点击Start。
时序仿真的结果波形中,你可能会观察到S和C的输出变化相对于输入变化有一个微小的延迟,并且在信号跳变边缘可能有一些短暂的毛刺(glitch)。这是实际电路中的传输延迟和竞争冒险现象,功能仿真是看不到的。对于简单的半加器,这些现象不明显,但对于复杂电路,时序仿真至关重要,它能验证电路在给定时钟频率下是否能稳定工作。
注意:如果功能仿真时遇到“Can‘t generate functional simulation netlist”或类似错误,请首先检查工程是否已成功全编译。只有全编译通过后,才能生成功能仿真网表。
4. 层次化设计进阶:从半加器模块到4位加法器
掌握了半加器和仿真流程后,我们就可以玩点更复杂的了。我们将采用“自底向上”的层次化设计方法,用已经验证过的半加器作为基础模块,来构建更复杂的系统。
4.1 创建并封装半加器模块
首先,我们需要把刚才设计的半加器原理图变成一个可重复调用的“零件”。在half_adder.bdf文件打开的情况下,点击 File -> Create/Update -> Create Symbol Files for Current File。Quartus会生成一个half_adder.bsf的符号文件。现在,这个半加器就像库里的与门、或门一样,可以在其他原理图中被调用了。
4.2 用两个半加器构建1位全加器
新建一个原理图文件(full_adder.bdf)。双击空白处添加符号,现在除了基本门电路,你可以在Libraries的Project目录下找到刚刚创建的half_adder符号!放置两个half_adder符号和一个or2(2输入或门)。
按照全加器的构成原理进行连接:
- 第一个半加器(HA1)的输入接
A和B,输出和(S)连接到一个内部节点(比如命名为temp_s),输出进位(C)连接到内部节点C1。 - 第二个半加器(HA2)的输入接
temp_s和来自低位的进位Cin,输出和就是全加器的最终和Sum,输出进位连接到内部节点C2。 - 最后,将
C1和C2连接到or2门的两个输入,or2的输出就是全加器的最终进位Cout。
别忘了添加input端口A, B, Cin和output端口Sum, Cout并正确连接。保存、编译这个全加器设计。同样,为其创建符号文件(full_adder.bsf),以便后续调用。
4.3 级联四个全加器构建4位加法器
思路一脉相承。再新建一个原理图文件(adder_4bit.bdf)。这次,我们需要处理多位信号。放置四个full_adder符号。
连接的关键在于进位链:
- 第一个全加器(FA0)的
Cin接整个4位加法器的进位输入C_in。它的A和B分别接输入总线A[0]和B[0](代表最低位)。它的Sum输出作为结果的最低位Sum[0]。 - FA0的
Cout连接到第二个全加器FA1的Cin。FA1的A和B接A[1]和B[1],Sum输出接Sum[1]。 - 以此类推,FA1的
Cout连FA2的Cin,处理A[2],B[2]和Sum[2]。 - FA2的
Cout连FA3的Cin,处理最高位A[3],B[3]和Sum[3]。FA3的Cout作为整个4位加法器的进位输出C_out。
在原理图中,对于多位的输入输出,我们可以使用“总线”来简化连线。例如,放置一个名为A[3..0]的input端口,它代表一根4位宽的线。在连线时,通过“节点名”连接,或者将其“分裂”成单个位与每个全加器连接。
完成连接后,保存并编译整个4位加法器工程。对其进行仿真时,波形文件中A和B的输入值可以设置为多位二进制数或十六进制数(如A = 4‘b0101, B = 4’b0011),观察Sum和C_out的输出是否符合加法结果(本例中0101+0011=1000,C_out=0)。
4.4 扩展思考:串行进位与超前进位
我们刚才构建的4位加法器,进位信号像波浪一样从最低位依次传递到最高位,这种结构称为串行进位加法器或行波进位加法器。它的优点是结构简单直观,但缺点也很明显:高位必须等待低位的进位产生后才能开始计算,导致运算速度随位数增加而变慢。
为了提高速度,工程师们发明了超前进位加法器。其核心思想是:通过额外的逻辑电路,直接根据所有位的输入(A, B)和初始进位Cin,提前计算出每一位的进位,而不是等待前一级传递过来。这需要更复杂的逻辑(例如,利用进位生成G = A·B和进位传播P = A⊕B的概念),但换来了几乎与位数无关的极高运算速度。
在Quartus中,你可以尝试用逻辑门直接实现超前进位逻辑,或者更简单地,用硬件描述语言(如Verilog)直接写一句 assign {C_out, Sum} = A + B + C_in;,综合工具通常会自动识别并优化成一个高效的加法器结构。这引向了数字设计的另一个广阔天地:硬件描述语言设计。从图形化的原理图输入过渡到代码化的HDL描述,是FPGA工程师成长的必经之路。
通过这个从半加器到4位加法器的完整流程,你不仅学会了Quartus-II的基本操作,更重要的是体验了数字系统设计中“分治”与“层次化”的核心思想。下次当你在仿真工具窗口看到Generate Functional Simulation Netlist这个选项时,你会会心一笑,因为你知道它不再是拦路虎,而是帮助你深入理解设计验证流程的钥匙。
更多推荐
所有评论(0)