news 2026/4/18 5:50:46

数字电路实验图解说明:Quartus中时序逻辑电路设计流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
数字电路实验图解说明:Quartus中时序逻辑电路设计流程

从零开始搞懂FPGA:用Quartus设计一个会“记数”的电路

你有没有想过,电脑是怎么记住当前状态的?键盘敲下的每一个字符、程序运行的每一步跳转,背后都离不开一类特殊的数字电路——时序逻辑电路。它不像组合逻辑那样“健忘”,而是能记住过去的状态,并据此决定下一步动作。

在电子工程实验中,我们常通过 FPGA(现场可编程门阵列)来实现这类电路。而Intel Quartus Prime就是打开这扇大门的钥匙。今天,我们就以一个经典的“四位计数器”为例,带你一步步走过从写代码到点亮数码管的全过程,把抽象的时序逻辑变成看得见、摸得着的结果。


为什么需要“记忆”?时序电路的本质是什么?

先问一个问题:如果只给你一堆与门、或门、非门,你能做出一个自动递增的计数器吗?

答案是——不能。因为这些元件属于组合逻辑,输出完全由当前输入决定,没有“记忆力”。比如一个加法器,你给它3+4,它就出7;但如果你不告诉它上一次是多少,它永远不知道“下一个该是多少”。

要让电路具备记忆能力,就必须引入触发器(Flip-Flop)。最常见的就是D触发器:它会在每个时钟上升沿把输入 D 的值“锁”进内部,然后从 Q 输出。只要时钟不来,Q 就一直保持不变。

这就构成了时序逻辑的核心机制:

输出 = f(当前输入, 当前状态)
而“当前状态”是由触发器保存的,靠时钟统一驱动更新。

于是我们可以构建出:
- 计数器(每拍加一)
- 状态机(按流程切换状态)
- 寄存器堆(暂存数据)
- 甚至CPU控制单元

它们共同的特点是:有序、可控、可预测,而这正是现代数字系统稳定运行的基础。


开发工具怎么选?Quartus到底强在哪?

市面上做FPGA开发的工具有不少,Xilinx有Vivado,Lattice有Diamond……但我们这里用的是Quartus Prime,原因很简单:高校教学用得多、资料全、免费版够用

更重要的是,它对初学者非常友好。哪怕你是第一次接触HDL语言,也能快速上手完成一次完整的设计闭环。

它能干什么?

功能说明
✅ 写代码支持 Verilog 和 VHDL
✅ 画图可以用原理图方式搭建模块(适合入门)
✅ 综合把你的代码翻译成实际的逻辑门和寄存器
✅ 引脚分配拖拽式指定哪个信号连哪个物理引脚
✅ 仿真看波形验证功能是否正确
✅ 下载一键烧录到开发板

而且它自带和ModelSim的联动功能,仿真起来毫不费力。

最关键的是,它的Lite Edition(免费版)完全支持 Cyclone IV/V 系列芯片,正好匹配很多学校实验室用的 DE0-CV、DE10-Lite 这类开发板。


实战案例:做个会自己数数的电路

我们现在就动手做一个带清零和使能的四位同步计数器,让它接在七段数码管上,自动从0数到9再归零,循环往复。

系统结构长什么样?

整个系统的信号流如下:

[50MHz晶振] ↓ [分频器] → 产生1Hz时钟(太快三位看不清) ↓ [计数器] → 每秒+1,到9后归零 ↓ [BCD译码器] → 转换成七段码 ↓ [数码管显示]

同时,我们还会用4个LED灯直接显示低四位二进制值,方便调试。

目标开发板是DE0-CV,主控为 Cyclone V FPGA(型号 EP4CE22E22C7)。


第一步:写代码——让电路“学会计数”

我们用 Verilog 来描述这个计数器的行为。别怕,不需要精通语法,关键是要理解逻辑意图。

// 文件名:counter_4bit.v module counter_4bit ( input clk, // 50MHz 主时钟 input rst_n, // 低电平有效复位 input en, // 计数使能 output reg [3:0] count // 4位计数输出 ); always @(posedge clk) begin if (!rst_n) count <= 4'b0000; // 异步清零 else if (en) begin if (count == 4'd9) count <= 4'b0000; // 数到9就归零 else count <= count + 1'b1; end end endmodule

这段代码的关键点在于:

  • always @(posedge clk)表示“只有当时钟上升沿到来时才执行”
  • !rst_n判断复位是否按下(低电平有效),优先级最高
  • en是使能信号,相当于“启动开关”
  • count == 4'd9实现模10计数,正好对应一位数码管
  • 使用非阻塞赋值<=,这是时序逻辑的标准写法

⚠️ 注意:如果你在else分支里漏写了en的情况,综合器可能会误判并生成锁存器(latch),带来功耗和时序问题!一定要确保所有路径都有明确赋值。


第二步:测试一下——别急着下载,先仿真!

代码写完不能直接下板子,万一逻辑错了呢?我们要先做功能仿真,看看它是不是真的会“数数”。

写个 Testbench(测试激励)

// 文件名:tb_counter.v `timescale 1ns / 1ps module tb_counter; reg clk, rst_n, en; wire [3:0] count; // 实例化被测模块 counter_4bit uut ( .clk(clk), .rst_n(rst_n), .en(en), .count(count) ); // 生成50MHz时钟(周期20ns) initial begin clk = 0; forever #10 clk = ~clk; // 半周期10ns end // 施加测试信号 initial begin rst_n = 0; // 上电复位 en = 0; #25; // 延迟25ns rst_n = 1; // 释放复位 #100; en = 1; // 启动计数 #400; // 观察四个周期 $stop; // 停止仿真 end endmodule

这个 testbench 干了三件事:
1. 生成一个稳定的 50MHz 方波作为时钟;
2. 模拟真实上电过程:先拉低复位,再释放;
3. 在适当时间打开使能,观察计数行为。

然后我们在 Quartus 中点击Tools → Run Simulation Tool → RTL Simulation,自动调起 ModelSim,就能看到波形了。

波形结果说明什么?

你会看到这样的关键信号变化:

  • clk:规整的方波,周期20ns
  • rst_n:开始为0,约25ns后变高
  • count:复位期间为0;使能后开始递增,每次+1
  • count=9后,下一拍回到0,完美循环

✅ 至此,功能正确性已验证。但这只是理想情况,还没考虑走线延迟、竞争冒险等问题。


第三步:编译 + 时序分析——现实世界没那么理想

接下来执行全编译(Processing → Start Compilation)。这个过程包括:

  1. 综合(Synthesis):将Verilog转为逻辑网表
  2. 布局布线(Fitter):把逻辑映射到FPGA的具体资源上
  3. 时序分析(Timing Analyzer):检查能否跑在目标频率下

编译完成后,打开Timing Analysis Report,重点关注两个指标:

项目含义
Minimum Clock Period最小时钟周期,本例约为8ns → 支持约125MHz
Setup Slack建立时间裕量,必须 >0 才安全

我们的设计工作在50MHz(周期20ns),远低于极限,所以肯定没问题。

但如果出现负的 slack(即时序违例),你就得优化设计了——比如拆分成更多级流水、减少组合逻辑层级,或者降低频率。


第四步:连接真实世界——引脚分配与下载

现在可以把它“焊”到开发板上了。当然不是真焊接,而是通过Pin Planner把信号绑定到具体引脚。

打开 Assignment → Pin Planner,填入以下典型配置:

信号FPGA引脚对应硬件
clkPIN_Y2板载50MHz晶振
rst_nPIN_R8KEY0 按键(低电平触发)
enPIN_T8SW0 拨码开关
count[0]PIN_A13LEDR0
count[1]PIN_B13LEDR1
count[2]PIN_C13LEDR2
count[3]PIN_E13LEDR3

📌 提醒:务必核对开发板手册,避免误用配置引脚或电源引脚!

配置完成后重新编译,生成.sof文件。

插入 USB-Blaster 下载线,打开 Programmer 工具,选择 JTAG 模式,点击 “Start” —— 几秒钟后,你就会看到LED灯开始闪烁,数码管也开始自动递增!

🎉 成功了!你的第一个时序逻辑电路跑起来了。


高阶技巧与避坑指南

别高兴太早,实战中还有很多细节需要注意。下面是一些老工程师都不会轻易告诉你的经验。

1. 模块化设计才是王道

不要把所有逻辑写在一个文件里。建议拆分为:
-counter_4bit.v:核心计数
-seg7_decoder.v:BCD转七段码
-clock_divider.v:分频器(把50MHz降到1Hz)

这样便于复用和调试。

2. 参数化让你更灵活

别写死宽度和模值,改成参数:

module counter_param #( parameter WIDTH = 4, MODULUS = 10 )(...);

以后想做个8位计数器?改个参数就行。

3. 时钟要用全局网络

高频时钟信号一定要走专用全局时钟线(Global Clock Buffer),否则偏移太大容易出错。Quartus 通常会自动推断,但最好显式使用 PLL 或create_clock约束。

4. 调试神器:SignalTap II

你想看内部某个中间信号?可以用SignalTap II Logic Analyzer,相当于在FPGA里嵌入一台示波器。

设置触发条件,抓取内部寄存器值,特别适合查状态机卡死、信号异常等问题。

5. 防止锁存器意外生成

再次强调:always 块中必须覆盖所有分支赋值

错误示范:

if (sel) out = a; // else 没写 → 综合出锁存器!

正确做法是补上else out = b;或默认赋值。


写在最后:从实验走向系统设计

通过这样一个简单的计数器实验,你已经走完了FPGA开发的完整流程:

写代码 → 仿真 → 编译 → 引脚约束 → 下载验证

这不是终点,而是起点。有了这套方法论,你可以继续挑战:

  • 设计一个交通灯控制器(有限状态机)
  • 实现UART串口收发(时序+状态管理)
  • 构建流水线CPU(多模块协同)

每一次成功下载,都是你对数字世界的又一次掌控。

如果你也曾在仿真波形里找bug到深夜,也曾在引脚配错时怀疑人生——欢迎留言分享你的“踩坑史”。毕竟,每个优秀的FPGA工程师,都是从一次次$stop中走出来的。

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

从零实现vivado许可证在容器化开发中的支持

如何让 Vivado 在 Docker 容器里“合法”运行&#xff1f;——深度破解许可证兼容难题你有没有遇到过这种情况&#xff1a;花了几小时把 Vivado 打包进 Docker 镜像&#xff0c;信心满满地docker run启动容器&#xff0c;结果一执行vivado -version就报错&#xff1a;ERROR: No…

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

3分钟上手!B站直播录制神器全方位使用手册

3分钟上手&#xff01;B站直播录制神器全方位使用手册 【免费下载链接】BililiveRecorder 录播姬 | mikufans 生放送录制 项目地址: https://gitcode.com/gh_mirrors/bi/BililiveRecorder 当你心爱的主播突然开播&#xff0c;而你却因为开会、上课错过精彩内容时&#xf…

作者头像 李华
网站建设 2026/4/6 6:40:08

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

手把手教你用FPGA从零搭建一个加法器&#xff1a;不只是“112”你有没有想过&#xff0c;计算机里最简单的“11”&#xff0c;背后其实是一场精密的硬件协奏&#xff1f;在如今动辄讨论AI大模型、GPU加速的时代&#xff0c;我们很容易忽略——所有复杂的运算&#xff0c;最终都…

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

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

GSE宏编译器&#xff1a;魔兽世界自动化技能编排革命性工具 【免费下载链接】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转换器&#xff1a;打破设计与开发壁垒的终极解决方案 【免费下载链接】figma-html Builder.io for Figma: AI generation, export to code, import from web 项目地址: https://gitcode.com/gh_mirrors/fi/figma-html 在数字产品开发的日常工作中&#xff0c…

作者头像 李华