首页 / 资讯中心 / 文章详情

FPGA数字频率计设计:从Verilog源码到板级调试的完整实战

FPGA数字频率计设计:从Verilog源码到板级调试的完整实战 ★ FEATURED ARTICLE
简介这份资源是本科EDA课程期末大作业《基于FPGA的数字频率计设计》的完整提交包面向电子信息、自动化等专业正在学习EDA与FPGA开发的学生以及需要参考测频模块设计思路的初学者。项目基于Quartus II开发环境采用Verilog编写核心测频模块遵循自上而下的设计方法按功能划分为多个子模块再通过顶层文件的元件例化语句连接成完整系统配套完整报告、代码、PCB与原理图可直接对照学习工程组织与实现流程。压缩包共744个文件约28.3MB以cdb、hdb、ddb等Quartus工程数据库文件tdf、v、vhd等源码文件rpt、qmsg、summary等编译报告以及sof、qsf、qpf等下载与工程配置文件为主另含schdoc、pcbdoc等原理图与PCB文档。目前已有4695人学习下载适合作为课程设计参考、模块化设计范例与工程文件结构的学习样本。1. 从一份 EDA 课设压缩包说起FPGA 数字频率计到底能跑多快每年到了期末后台总有人问同一个问题老师扔过来一个.rar里面是「基于 FPGA 的数字频率计设计」要求两周内跑通、写报告、答辩。打开一看Verilog 源码、约束文件、仿真波形、还有一份说明文档东西不少但真上手就懵——时钟怎么接、闸门时间怎么定、数码管为什么乱跳。这份资源就是冲着这个场景来的它把数字频率计从原理到实现完整打包适合正在做 EDA 课程设计的学生也适合想拿一个完整小项目练手 FPGA 的工程师。数字频率计本身不复杂核心就一句话在固定闸门时间内数被测信号的周期数再换算成频率。但真到板子上跑量程切换、精度取舍、显示刷新这几件事每一个都能让你调一晚上。下面我按自己拆包复现的顺序把这份资源讲透。2. 数字频率计的测频原理与整体方案选型2.1 直接测频法为什么它是课设的首选数字频率计最常用的方法是直接测频法也叫计数法。原理很朴素用一个已知频率的标准时钟去产生一个固定宽度的闸门信号闸门打开期间被测信号经过整形后送入计数器计数值就是这段时间内的脉冲个数。设闸门时间为 $T_g$计数值为 $N$则被测频率 $f_x N / T_g$。举个例子闸门时间取 1 秒计数器读到 50000那频率就是 50 kHz。这个公式简单到不需要推导但它的误差特性决定了整个设计的走向。计数法在测高频时相对误差小因为 $N$ 大$\pm 1$ 个数的量化误差占比就小测低频时 $N$ 很小误差就上去了。所以课设里通常要求分档高频档用短闸门低频档用长闸门或者干脆用等精度测频法来补。这份资源采用的是经典直接测频法配合量程切换。为什么不用等精度法因为课设的评分点往往在「完整实现 报告清晰」等精度法虽然精度高但多机同步、握手逻辑对初学者不友好调试成本高。直接测频法逻辑清晰、模块划分干净是性价比最高的选择。2.2 系统框图与模块划分整个设计可以拆成五个模块这也是资源里源码的组织方式模块功能关键信号分频模块把板载高频时钟分频成 1 Hz 闸门信号clk_in, clk_gate闸门控制产生闸门使能、锁存脉冲、清零脉冲gate_en, latch, rst_cnt计数模块在闸门内对被测信号计数sig_in, count_val锁存模块闸门结束时锁存计数值避免显示抖动count_val, data_out显示驱动动态扫描数码管把计数值转成七段码data_out, seg, sel这个划分的好处是每个模块职责单一仿真时能单独验证。我一般会先把分频和计数两个模块跑通再接显示最后整体联调。资源里的顶层文件就是把这五个模块例化连起来端口命名比较规范读起来不费劲。2.3 闸门时间与量程的取舍闸门时间不是随便定的。它直接决定两个东西测量精度和刷新率。闸门时间越长计数值越大量化误差越小但显示刷新越慢。1 秒闸门意味着每秒才更新一次读数人眼看着还行如果做 10 秒闸门精度上去了但读数半天不动体验很差。常见做法是分档高频档闸门 0.1 s适合测 1 MHz 以上信号中频档闸门 1 s适合 1 kHz 到 1 MHz低频档闸门 10 s适合 1 kHz 以下资源里默认用的是 1 s 闸门对应板载 50 MHz 时钟需要分频到 1 Hz。分频系数是 50,000,000这个数在 Verilog 里要算清楚位宽否则计数器溢出。我见过有人用 24 位计数器去装 50M结果高位截断闸门周期直接错乱波形看着像玄学。3. 从源码到板级分频、计数、锁存、显示四步落地3.1 分频模块把 50 MHz 变成 1 Hz 闸门分频模块是整个系统的时间基准写错了后面全崩。资源里的分频逻辑是标准的计数器翻转法// 50MHz - 1Hz 分频计数到 25_000_000 翻转一次 module clk_div( input wire clk, // 板载 50MHz 时钟 input wire rst_n, // 低电平复位 output reg clk_gate // 输出 1Hz 闸门信号 ); reg [25:0] cnt; // 26 位足够装下 25_000_000 always (posedge clk or negedge rst_n) begin if (!rst_n) begin cnt 26d0; clk_gate 1b0; end else if (cnt 26d24_999_999) begin cnt 26d0; clk_gate ~clk_gate; // 每半周期翻转得到 1Hz end else begin cnt cnt 1b1; end end endmodule逻辑说明计数器从 0 累加到 24,999,999 正好是 0.5 秒翻转一次输出再数 0.5 秒再翻转合起来一个完整周期就是 1 秒。参数说明cnt位宽必须满足 $2^{26} 67,108,864 25,000,000$所以 26 位够用。如果你板子时钟不是 50 MHz把比较值改成clk_freq/2 - 1即可。注意复位用低电平有效和大多数开发板按键一致。3.2 计数与闸门控制别让计数器和闸门打架计数模块要在闸门使能期间对被测信号计数闸门一关立刻停止同时产生锁存脉冲把数据送出去。这里最容易翻车的地方是闸门信号和被测信号不同步导致多计或少计一个数。资源里的做法是用闸门信号做使能被测信号做时钟module counter( input wire sig_in, // 被测信号已整形 input wire gate_en, // 闸门使能高有效 input wire rst_cnt, // 计数清零 output reg [31:0] count_val // 计数值 ); always (posedge sig_in or posedge rst_cnt) begin if (rst_cnt) count_val 32d0; else if (gate_en) count_val count_val 1b1; end endmodule逻辑说明gate_en为高时每个被测信号上升沿计数加一rst_cnt用来在每次测量前清零。参数说明count_val用 32 位是留余量实际 1 秒闸门测 50 MHz 也才 50,000,00032 位足够。注意这里用的是异步复位仿真时要注意复位释放时刻避免亚稳态。常见做法是在闸门控制模块里用标准时钟同步后再输出rst_cnt资源里也是这么处理的。3.3 锁存模块解决数码管跳变的关键如果没有锁存计数器每个周期都在变数码管显示会疯狂跳动根本读不出数。锁存模块的作用是在闸门结束时把当前计数值存下来保持到下一次测量结束module latch( input wire clk, input wire latch_en, // 闸门结束时的锁存脉冲 input wire [31:0] count_val, output reg [31:0] data_out ); always (posedge clk) begin if (latch_en) data_out count_val; end endmodule逻辑说明latch_en是一个单周期脉冲在闸门下降沿后产生把计数值锁进data_out。参数说明latch_en的时序很关键必须在计数器停止之后、下一次清零之前产生。资源里用了一个状态机来生成这个脉冲顺序是闸门开 → 计数 → 闸门关 → 锁存 → 清零 → 下一个闸门。这个顺序错了要么锁到旧数据要么锁到零。3.4 数码管动态扫描显示驱动的时序要求数码管动态扫描的原理是快速轮流点亮各位利用人眼视觉暂留形成稳定显示。资源里用的是 6 位数码管扫描频率一般在 1 kHz 左右太快亮度不够太慢会闪module seg_driver( input wire clk, // 扫描时钟建议 1kHz input wire [31:0] data_out, // 锁存的频率值 output reg [7:0] seg, // 段选 output reg [5:0] sel // 位选 ); reg [2:0] scan_cnt; reg [3:0] disp_num; always (posedge clk) begin scan_cnt scan_cnt 1b1; case (scan_cnt) 3d0: begin sel 6b111110; disp_num data_out[3:0]; end 3d1: begin sel 6b111101; disp_num data_out[7:4]; end 3d2: begin sel 6b111011; disp_num data_out[11:8]; end 3d3: begin sel 6b110111; disp_num data_out[15:12]; end 3d4: begin sel 6b101111; disp_num data_out[19:16]; end 3d5: begin sel 6b011111; disp_num data_out[23:20]; end default: sel 6b111111; endcase end // 七段码译码共阳极低有效 always (*) begin case (disp_num) 4d0: seg 8hc0; 4d1: seg 8hf9; 4d2: seg 8ha4; 4d3: seg 8hb0; 4d4: seg 8h99; 4d5: seg 8h92; 4d6: seg 8h82; 4d7: seg 8hf8; 4d8: seg 8h80; 4d9: seg 8h90; default: seg 8hff; endcase end endmodule逻辑说明scan_cnt循环计数每次选中一位数码管同时把对应位的 BCD 码送译码器。参数说明sel是位选低有效seg是段选共阳极低有效。如果你的板子是高有效把译码表取反即可。注意扫描时钟不能直接用 50 MHz否则亮度极低需要再分频到 1 kHz 左右。4. 仿真、约束与上板调试那些让你调一晚上的坑4.1 仿真波形怎么看重点盯三个时刻仿真不是跑完就行要看关键时序。我一般重点看三个时刻闸门上升沿、闸门下降沿、锁存脉冲。闸门上升沿要看计数器是否从零开始下降沿要看计数是否立刻停止锁存脉冲要看是否在计数停止后产生。资源里带了 testbench被测信号用 1 kHz 方波模拟闸门 1 秒理论上计数值应该是 1000。如果仿真读到 999 或 1001说明闸门和被测信号有相位差属于正常量化误差不用慌。4.2 引脚约束管脚分配错了板子就是砖约束文件是把 Verilog 端口映射到物理引脚的关键。资源里给的是某常见开发板的约束模板但不同板子引脚不同直接抄会翻车。常见做法是# 时钟引脚 set_location_assignment PIN_E1 -to clk # 复位按键 set_location_assignment PIN_M1 -to rst_n # 被测信号输入 set_location_assignment PIN_A1 -to sig_in # 数码管段选 set_location_assignment PIN_C14 -to seg[0] # ... 其余引脚按板子手册填逻辑说明每个set_location_assignment把端口绑定到具体引脚。参数说明引脚编号必须查你手上板子的原理图不能照搬。我见过有人把时钟绑到普通 IO 上结果板子跑不起来查了半天以为是代码问题其实是引脚错了。4.3 常见问题排查现象、原因、解决现象一数码管显示全亮或全灭。原因通常是位选和段选极性搞反了。共阳数码管段选低有效共阴高有效位选也一样。解决方法是查板子手册确认极性把译码表或位选信号取反。现象二读数始终为零。先看被测信号有没有进来用示波器或逻辑分析仪量一下输入引脚。如果信号正常再看闸门使能有没有产生仿真里确认gate_en有波形。常见原因是分频模块比较值算错闸门周期变成几微秒计数器根本来不及计。现象三读数跳动很大。多半是锁存没做好或者闸门和清零时序重叠。检查latch_en和rst_cnt是否在同一周期产生如果是计数器可能被清零后又锁存读到零。解决方法是把锁存和清零错开一个周期。现象四高频测不准低频反而准。这是直接测频法的固有特性高频时量化误差占比小低频时占比大。如果要求全量程高精度需要换等精度测频法但那超出课设范围了。现象五综合报错说时钟资源不够。多半是把多个时钟信号都接到了全局时钟引脚。常见做法是用一个主时钟其他时钟通过分频产生不要用多个外部晶振。5. 进阶技巧把频率计从「能跑」做到「好用」5.1 量程自动切换的实现思路默认 1 秒闸门只能覆盖有限范围想做好用可以加自动量程。思路是先用一个短闸门粗测根据计数值判断量级再切换到合适闸门精测。比如先 0.01 秒闸门测一次如果计数值小于 100说明频率低于 10 kHz切到 1 秒闸门如果大于 10000说明高于 1 MHz切到 0.1 秒闸门。这个逻辑用一个简单状态机就能实现资源里没带但接口留了扩展空间。5.2 用等精度法提升低频精度等精度测频法的核心是让闸门信号和被测信号同步实际闸门时间是被测信号周期的整数倍这样低频时也能保证 $\pm 1$ 个标准时钟的精度。实现上需要两个计数器一个数被测信号一个数标准时钟闸门由被测信号开启和关闭。代价是逻辑复杂一些但对低频测量提升明显。如果你的课设要求全量程等精度可以在这个框架上改。5.3 验证方法用信号发生器扫一遍量程板子跑起来只是第一步验证要系统。我一般会用信号发生器从 10 Hz 扫到 10 MHz每个量级取几个点记录读数误差。重点看三个区间低频段1 kHz误差是否在可接受范围中频段1 kHz~1 MHz是否稳定高频段1 MHz是否丢数。如果某个区间误差突然变大多半是闸门时间不匹配换档再测。5.4 一个具体技巧用锁相环替代分频如果板子有 PLL 资源可以用 PLL 产生一个干净的闸门时钟比计数器分频更稳定。PLL 输出的时钟抖动小闸门边沿更陡计数误差更小。配置方法是在 Quartus 或 Vivado 里调 IP 核输入 50 MHz输出 1 Hz 或 0.1 Hz。注意 PLL 输出低频时可能超出锁定范围常见做法是 PLL 输出 1 MHz再分频到 1 Hz。从那以后我每次做时序相关的课设都强制先把仿真波形看三遍再上板省得对着板子发呆。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站