FPGA之简易频率计的设计
文章目录
- 前言
- 一、等精度测量法
- 二、频率计算模块设计
- 1.波形图分析
- 2.RTL代码
- 三、数码管显示模块设计
- 1.数码管之二进制转8421BCD码部分
- 2.数码管显示部分
- 四、顶层代码设计
- 1.RTL代码
- 2.RTL视图
- 3.仿真测试模块
- 五、上板验证
- 总结
前言
频率测量在电子设计领域和测量领域经常被使用,本篇文章将使用基于等精度测量的方法设计一个建议频率计,计算出输入信号的频率并显示在数码管上。
一、等精度测量法
原理:等精度测量法是在实际门控信号下,同时对标准时钟和被测时钟的时钟周期进行计数,再通过公式计算得到被测信号的时钟频率。
误差分析:结合原理和原理图得知,被测时钟信号的时钟频率fx的相对误差与被测时钟信号无关;增大软件闸门的有效范围或者提高标准时钟信号的频率fs,可以减小误差,提高测量精度。
计算方法:分别对实际闸门下被测时钟信号和标准时钟信号的周期数进行计数。
设实际闸门下被测时钟信号周期数为X,设被测信号时钟周期为Tx,它的时钟频率fx=1/Tx,由此:XTx=X/fx=Tx。
设实际闸门下标准时钟信号周期数为Y,设被测信号时钟周期为Ts,它的时钟频率fs=1/Ts,由此:YTs=X/fs=Ts。
等式变换得到被测时钟信号频率计算公式:fx=Xfs/Y。
二、频率计算模块设计
1.波形图分析
注:图片摘自《FPGA Verilog开发实战指南》
通过波形图的设计可以起到事半功倍的效果。
- clk_stand:标准时钟,通过PLL IP核产生。
- 软件闸门gate_s:gate_s,设计一个总长度为1.5s,前低电平长度为0.25s,高电平长度为2s,后低电平长度为0.25s的软件闸门。
- 闸门计数器cnt_gate_s:闸门需要时间控制,所以必须使用到计数器,因为开发板自带晶振是50MHz的,所以由低电平到达高电平的时间计数值为CNT_RISE_MAX=28’d12_499_999,总长度计数值为CNT_GATE_S_MAX=28’d74_999_999,所以有效时间计数值为CNT_GATE_S_MAX-CNT_RISE_MAX
- gate_a:软件闸门需要与系统时钟进行同步处理,用一级寄存器gate_a实现
- gate_a_stand:将软件闸门与标准时钟同步,可以产生下降沿信号gate_a_fall_s
- gate_a_fall_s:标准时钟周期计数停止标志位
- cnt_clk_stand:标准时钟周期计数器
- cnt_clk_stand_reg:标准时钟计数值寄存器
- gate_a_test:将软件闸门与待测时钟同步,可产生下降沿信号gate_a_fall_t
- gate_a_fall_t:待测时钟周期计数停止标志位
- cnt_clk_test:待测时钟周期计数器
- cnt_clk_test_reg:待测时钟计数值寄存器
- calc_flag:开始计算待测信号频率标志位,在闸门计数器记满时产生一个脉冲
- freq_reg:频率计数值寄存器
- calc_flag_reg:将calc_flag信号延迟一拍,此时频率计算已经结束,可以产生输出标志。
- freq:输出频率计算值
2.RTL代码
module freq_meter_calc
#(parameter CNT_RISE_MAX=28'd12_499_999,parameter CNT_GATE_S_MAX=28'd74_999_999,parameter CLK_STAND_FREQ=28'd100_000_000
)
(input wire clk,input wire rst_n,input wire clk_test,output reg [33:0]freq
);reg [27:0]cnt_gate_s;reg gate_s;reg gate_a;reg gate_a_stand;wire gate_a_fall_s;reg [47:0]cnt_clk_stand;reg [47:0]cnt_clk_stand_reg;reg gate_a_test;wire gate_a_fall_t;reg [47:0]cnt_clk_test;reg [47:0]cnt_clk_test_reg;reg calc_flag;reg [63:0]freq_reg;reg calc_flag_reg;wire clk_stand;clk_stand_pll clk_stand_pll0(.areset(~rst_n),.inclk0(clk),.c0(clk_stand));//闸门计数器always @(posedge clk or negedge rst_n)if(!rst_n)cnt_gate_s<=1'b0;else if(cnt_gate_s==CNT_GATE_S_MAX)cnt_gate_s<=1'b0;else cnt_gate_s<=cnt_gate_s+1'b1;//闸门控制,always @(posedge clk or negedge rst_n)if(!rst_n)gate_s<=1'b0;else if((cnt_gate_s>=CNT_RISE_MAX) && cnt_gate_s<=(CNT_GATE_S_MAX-CNT_RISE_MAX))gate_s<=1'b1;else gate_s<=1'b0;//将闸门打一拍,与时钟同步always @(posedge clk or negedge rst_n)if(!rst_n)gate_a<=1'b0;else gate_a<=gate_s;//将标准时钟闸门再打一拍,以便生成标志信号gate_a_fall_salways @(posedge clk_stand or negedge rst_n)if(!rst_n)gate_a_stand<=1'b0;else gate_a_stand<=gate_a;//标准频率计数截止信号assign gate_a_fall_s=(gate_a_stand && !gate_a) ? 1'b1:1'b0;//标准时钟频率计数器always @(posedge clk_stand or negedge rst_n)if(!rst_n)cnt_clk_stand<=1'b0;else if(!gate_a)cnt_clk_stand<=1'b0;else if(gate_a)cnt_clk_stand<=cnt_clk_stand+1'b1;//标准时钟频率周期个数寄存器always @(posedge clk_stand or negedge rst_n)if(!rst_n)cnt_clk_stand_reg<=1'b0;else if(gate_a_fall_s)cnt_clk_stand_reg<=cnt_clk_stand;//将待测时钟打一拍,以便生成gate_a_fall_talways @(posedge clk_test or negedge rst_n)if(!rst_n)gate_a_test<=1'b0;else gate_a_test<=gate_a;//待测时钟计数截止信号assign gate_a_fall_t=(gate_a_test && !gate_a) ? 1'b1:1'b0;//待测时钟频率计数器always @(posedge clk_test or negedge rst_n)if(!rst_n)cnt_clk_test<=1'b0;else if(!gate_a)cnt_clk_test<=1'b0;else if(gate_a)cnt_clk_test<=cnt_clk_test+1'b1;//待测时钟频率周期个数寄存器always @(posedge clk_test or negedge rst_n)if(!rst_n)cnt_clk_test_reg<=1'b0;else if(gate_a_fall_t)cnt_clk_test_reg<=cnt_clk_test;//频率计算标志信号always @(posedge clk or negedge rst_n)if(!rst_n)calc_flag<=1'b0;else if(cnt_gate_s==CNT_GATE_S_MAX)calc_flag<=1'b1;elsecalc_flag<=1'b0;//待测时钟频率计算always @(posedge clk or negedge rst_n)if(!rst_n)freq_reg<=1'b0;else if(calc_flag)freq_reg<=(CLK_STAND_FREQ*cnt_clk_test_reg/cnt_clk_stand_reg);//将计算标志信号打一拍,不然freq输出的是计算之前的值always @(posedge clk or negedge rst_n)if(!rst_n)calc_flag_reg<=1'b0;else calc_flag_reg<=calc_flag;//频率值输出always @(posedge clk or negedge rst_n)if(!rst_n)freq<=1'b0;else if(calc_flag_reg)freq<=freq_reg[33:0];endmodule
三、数码管显示模块设计
因为数码管部分代码不是本次的重点,所以只给出代码,不给出设计过程
1.数码管之二进制转8421BCD码部分
module bcd_8421(input wire clk,input wire rst_n,input wire [19:0]data,output reg [3:0]unit,output reg [3:0]ten,output reg [3:0]hun,output reg [3:0]tho,output reg [3:0]t_tho,output reg [3:0]h_hun);reg [4:0]cnt_shift;reg [43:0]data_shift;reg shift_flag;always @(posedge clk or negedge rst_n)if(!rst_n)cnt_shift<=1'b0;else if(cnt_shift==5'd21 && shift_flag)cnt_shift<=1'b0;else if(shift_flag)cnt_shift<=cnt_shift+1'b1;elsecnt_shift<=cnt_shift;always @(posedge clk or negedge rst_n)if(!rst_n)data_shift<=1'b0;else if(!cnt_shift)data_shift<={24'd0,data};else if(cnt_shift<=20 && (!shift_flag))begindata_shift[23:20]<=(data_shift[23:20]>4)? (data_shift[23:20]+2'd3):(data_shift[23:20]);data_shift[27:24]<=(data_shift[27:24]>4)? (data_shift[27:24]+2'd3):(data_shift[27:24]);data_shift[31:28]<=(data_shift[31:28]>4)? (data_shift[31:28]+2'd3):(data_shift[31:28]);data_shift[35:32]<=(data_shift[35:32]>4)? (data_shift[35:32]+2'd3):(data_shift[35:32]);data_shift[39:36]<=(data_shift[39:36]>4)? (data_shift[39:36]+2'd3):(data_shift[39:36]);data_shift[43:40]<=(data_shift[43:40]>4)? (data_shift[43:40]+2'd3):(data_shift[43:40]);endelse if(cnt_shift<=5'd20 && shift_flag)data_shift<=data_shift<<1;elsedata_shift<=data_shift;always @(posedge clk or negedge rst_n)if(!rst_n)shift_flag<=1'b0;elseshift_flag<=~shift_flag;always @(posedge clk or negedge rst_n)if(!rst_n)beginunit<=1'b0;ten<=1'b0;hun<=1'b0;tho<=1'b0;t_tho<=1'b0;h_hun<=1'b0;endelse if(cnt_shift==5'd21)beginunit<=data_shift[23:20];ten<=data_shift[27:24];hun<=data_shift[31:28];tho<=data_shift[35:32];t_tho<=data_shift[39:36];h_hun<=data_shift[43:40];endendmodule
2.数码管显示部分
module seg
#(parameter CNT_MAX=16'd49999 //49999
)
(input wire clk,input wire rst_n,input wire [5:0]point,input wire [19:0]data,input wire seg_en,input wire sign,output reg [5:0]sel,output reg [7:0]seg);wire [3:0]unit;wire [3:0]ten;wire [3:0]hun;wire [3:0]tho;wire [3:0]t_tho;wire [3:0]h_hun;bcd_8421 bcd_8421(.clk(clk),.rst_n(rst_n),.data(data),.unit(unit),.ten(ten),.hun(hun),.tho(tho),.t_tho(t_tho),.h_hun(h_hun));reg [23:0]data_reg;reg [15:0]cnt_1ms;reg flag_1ms;reg [2:0]cnt_sel;reg [5:0]sel_reg;reg [3:0]data_disp;reg dot_disp;//控制数码管显示always @(posedge clk or negedge rst_n)if(!rst_n)data_reg<=1'b0;//若显示的十万位为非零数据或需要显示小数点,六个数码管全显示else if(h_hun || point[5])data_reg<={h_hun,t_tho,tho,hun,ten,unit};//若显示的万位数为非零数据或需要显示小数点,数值显示在5个数码管上else if((t_tho || point[4]) && sign)//显示负号data_reg<={4'd10,t_tho,tho,hun,ten,unit};//定义4'd10为显示负号else if((t_tho || point[4]) && !sign)data_reg<={4'd11,t_tho,tho,hun,ten,unit};//定义4‘d11为不显示//若显示的千位数为非零数据或需要显示小数点,数值显示在4个数码管上else if((tho || point[3]) && sign)data_reg<={4'd11,4'd10,tho,hun,ten,unit};else if((tho || point[3]) && !sign)data_reg<={4'd11,4'd11,tho,hun,ten,unit};//若显示的百位数为非零数据或需要显示小数点,数值显示在3个数码管上else if((hun || point[2]) && sign)data_reg<={4'd11,4'd11,4'd10,hun,ten,unit};else if((hun || point[2]) && !sign)data_reg<={4'd11,4'd11,4'd11,hun,ten,unit};//若显示的十位数为非零数据或需要显示小数点,数值显示在2个数码管上else if((ten || point[2]) && sign)data_reg<={4'd11,4'd11,4'd11,4'd10,ten,unit};else if((ten || point[2]) && !sign)data_reg<={4'd11,4'd11,4'd11,4'd11,ten,unit};//若显示的个位数为非零数据或需要显示小数点,数值显示在1个数码管上else if((unit || point[1]) && sign)data_reg<={4'd11,4'd11,4'd11,4'd11,4'd10,unit};else data_reg<={4'd11,4'd11,4'd11,4'd11,4'd11,unit};//计数器计数1msalways @(posedge clk or negedge rst_n)if(!rst_n)cnt_1ms<=1'b0;else if(cnt_1ms==CNT_MAX)cnt_1ms<=1'b0;else cnt_1ms<=cnt_1ms+1'b1;//计数标志位always @(posedge clk or negedge rst_n)if(!rst_n)flag_1ms<=1'b0;else if(cnt_1ms==CNT_MAX-1'b1)flag_1ms<=1'b1;elseflag_1ms<=1'b0;//cnt_sel:从0到5的循环,用于选择当前显示的数码管always @(posedge clk or negedge rst_n)if(!rst_n)cnt_sel<=1'b0;else if(cnt_sel==3'b101 && flag_1ms)cnt_sel<=1'b0;else if(flag_1ms)cnt_sel<=cnt_sel+1'b1;elsecnt_sel<=cnt_sel;//数码管位选信号寄存器always @(posedge clk or negedge rst_n)if(!rst_n)sel_reg<=6'b000_000;else if(!cnt_sel && flag_1ms)sel_reg<=6'b000_001;else if(flag_1ms)sel_reg<=sel_reg<<1;elsesel_reg<=sel_reg;//控制数码管的位选信号,使六个数码管轮流显示always @(posedge clk or negedge rst_n)if(!rst_n)data_disp<=1'b0;else if(seg_en && flag_1ms)case(cnt_sel)3'd0:data_disp<=data_reg[3:0];3'd1:data_disp<=data_reg[7:4];3'd2:data_disp<=data_reg[11:8];3'd3:data_disp<=data_reg[15:12];3'd4:data_disp<=data_reg[19:16];3'd5:data_disp<=data_reg[23:20];default:data_disp<=1'b0;endcaseelsedata_disp<=data_disp;//dot_disp:小数点低电平点亮,需对小数点有效信号取反always @(posedge clk or negedge rst_n)if(!rst_n)dot_disp<=1'b1;else if(flag_1ms)dot_disp<=~point[cnt_sel];elsedot_disp<=dot_disp;//控制数码管段选信号,显示数字always @(posedge clk or negedge rst_n)if(!rst_n)seg<=8'b1111_1111;elsecase(data_disp)4'd0:seg<={dot_disp,7'b100_0000};4'd1:seg<={dot_disp,7'b111_1001};4'd2:seg<={dot_disp,7'b010_0100};4'd3:seg<={dot_disp,7'b011_0000};4'd4:seg<={dot_disp,7'b001_1001};4'd5:seg<={dot_disp,7'b001_0010};4'd6:seg<={dot_disp,7'b000_0010};4'd7:seg<={dot_disp,7'b111_1000};4'd8:seg<={dot_disp,7'b000_0000};4'd9:seg<={dot_disp,7'b001_0000};4'd10:seg<=8'b1011_1111;4'd11:seg<=8'b1111_1111;default:seg<=8'b1100_0000;endcase//sel:数码管位选信号赋值always @(posedge clk or negedge rst_n)if(!rst_n)sel<=6'b000_000;elsesel<=~sel_reg;
endmodule
四、顶层代码设计
顶层代码中包含三个部分:待测时钟输出模块,待测时钟频率计算模块,数码管显示模块
其中待测时钟输出模块我们直接用PLL产生的100MHz,也可以在PLL中再添加新的时钟信号来输出。
1.RTL代码
module freq_meter_top(input wire clk,input wire rst_n,input wire clk_test,output wire clk_out,output wire [5:0]sel,output wire [7:0]seg
);wire [33:0]freq;clk_stand_pll clk_stand_pll0(.areset(~rst_n),.inclk0(clk),.c0(clk_out));seg seg0(.clk(clk),.rst_n(rst_n),.point(6'b001000),.data(freq/1000),.seg_en(1'b1),.sign(1'b0),.sel(sel),.seg(seg));freq_meter_calc freq_meter_calc0(.clk(clk),.rst_n(rst_n),.clk_test(clk_test),.freq(freq));endmodule
2.RTL视图
3.仿真测试模块
`timescale 1ns/1ns
`define clk_period 20module freq_meter_top_tb;reg clk;reg rst_n;reg clk_test;wire clk_out;wire [5:0]sel;wire [7:0]seg;freq_meter_top freq_meter_top0(.clk(clk),.rst_n(rst_n),.clk_test(clk_test),.clk_out(clk_out),.sel(sel),.seg(seg));initial clk=1'b1;always #(`clk_period/2) clk=~clk;initial clk_test=1'b1;always #100 clk_test=~clk_test;initial beginrst_n=1'b0;#(`clk_period*20+1);rst_n=1'b1;enddefparam freq_meter_top0.freq_meter_calc0.CNT_GATE_S_MAX=240;defparam freq_meter_top0.freq_meter_calc0.CNT_RISE_MAX=40;endmodule
仿真波形图:
仿真激励文件中我们设计了一个5MHz的待测信号输入,可以看到计数值freq输出为4968944hz,与5MHz有些许误差,不过这足以证明我们的实验已经成功了,下面就可以开始分配引脚、布局布线、烧录开发板了
五、上板验证
我们设数码管的单位是MHz,可以看到,我们输入100MHz时,输出就是100MHz,验证成功。
总结
频率计的设计相对于前面的设计是复杂了很多的,但是也可以很大程度的锻炼能力,同时设计中的一些细节问题,比如对应不同时钟上升沿的计数值需要注意。每个开发板上的数码管配置都不一定相同,所以在调用数码管的时候,如果发现无法正常显示,可以 查看一下自己数码管的配置,再对代码进行修改。
FPGA之简易频率计的设计相关推荐
- FPGA之简易DDS信号发生器设计
文章目录 前言 一.DDS信号发生器 1.DDS是什么 2.DDS工作原理 二.模块代码 1.调用rom模块储存波形图 2.按键控制模块 2.按键消抖模块 3.DDS生成模块 4.顶层模块 5.RTL ...
- FPGA—简易频率计(附代码)
目录 1. 内容概要 2. 理论学习 3. 实操 3.1 整体设计 3.2 频率计算模块 3.2.1 模块框图 3.2.2 波形图绘制 3.2.3 RTL代码 3.3 顶层模块 3.4 仿真验证 ...
- 基于FPGA的简易DDS信号发生器的设计与验证
基于FPGA的简易DDS信号发生器的设计与验证 一,理论介绍 补充:举例理解 二,代码实现 1,实验目标 2,MATLAB代码 3,verilog代码及实现思路 一,理论介绍 DDS 是直接数字式频率 ...
- 基于FPGA的简易DDS信号发生器的设计(一)
写这篇文章的本意不是为了探讨AD9767怎么使用,因为9767的控制实在是太简单了,准备好数据直接输出即可,和网上大多数的并行DA输出基本上一模一样,更麻烦的反而是硬件方面.发文的原因是最近一位很细心 ...
- 基于FPGA的简易数字频率计+上板测试(小梅哥AC620FPGA开发板)
基于FPGA的简易数字频率计+上板测试(小梅哥AC620FPGA开发板 目录 主要架构 1.计数模块 2.数码显示模块 3.控制信号模块 4.分频模块 例化模块 上板测试图 附:74HC595移位寄存 ...
- 利用UltraScale和UltraScale+FPGA和MPSOC加速DSP设计生产力
利用UltraScale和UltraScale+FPGA和MPSOC加速DSP设计生产力 Accelerating DSP Design Productivity with UltraScale an ...
- 基于FPGA的SPI FLASH控制器设计
1.SPI FLASH的基本特征 本文实现用FPGA来设计SPI FLASH,FLASH型号为W25Q128BV.支持3种通信方式,SPI.Dual SPI和Quad SPI.FLASH的存储单元无法 ...
- 【FPGA】FIFO的Verilog设计之同步FIFO的设计
这个同步FIFO的设计方法是调用异步读写双端口RAM来实现的. 关于异步读写双端口RAM的设计,前面博文已经讲到过了:[FPGA]双端口RAM的设计(异步读写) 此时使用双端口RAM来设计FIFO,可 ...
- 【FPGA】ROM/EPROM的设计(使用case的方式初始化)
上篇博文:[FPGA]ROM/EPROM的设计(使用加载文件的方式初始化),提到了这篇博文中要用的方式初始化ROM,在代码中用case语句的方式,给一个地址,给一个数据. 很容易,通过异步的方式来给出 ...
最新文章
- 看懂 ,学会 .NET 事件的正确姿势
- Java集合(一)、什么是Java集合?
- 操作系统内存管理之 内部碎片vs外部碎片
- android构建过程
- 基础知识---汇编学习笔记
- 四十九、深入了解两个并发接口Callable和Runnable的区别
- 快速傅里叶变换(FFT)相关内容汇总
- 静态类 c# 1614532739
- mysql的主主复制模型
- ffplay.exe操作方式
- 拓端tecdat|用R语言用Nelson Siegel和线性插值模型对债券价格和收益率建模
- Install/RemoveoftheServiceDenied!
- xlinx ISE的程序下载
- matlab r2008a下载,Matlab+R2008a下载地址及安装教程
- excel查找空值快捷键_Excel快捷键查询
- windows server2019共享选项中网络发现无法启用
- 数据分析【实践】——教育行业指标体系搭建和生命周期维护
- 【学渣告诉你】到底神马是傅里叶级数!!!!!!
- 美国华盛顿州立大学计算机排名,2020年华盛顿州立大学排名TFE Times美国最佳计算机科学硕士专业排名第38...
- rviz显示矩形框BoundingBox
热门文章
- 千里马 android framework之MotionEvent.ACTION_CANCEL怎么产生-讨厌的android触摸面试题
- c语言程序设计教程刘三满答案,清华大学出版社-图书详情-《C语言程序设计教程》...
- android 录像静音,Android – 静音麦克风录制视频时
- matlab中simple函数怎么用,matlab里simple函数
- Struts2 学习记录(2)
- 选择“正激”还是“反激”?这份宝典请收好~
- linux DSA 开发(一)
- 湖北大学计算机系2020录取分数线,2020湖北大学本科投档录取分数线
- 亚马逊爆款的流量密码原来这么简单 六个步骤打造爆款
- 湖南省第六届大学生计算机程序设计竞赛---弟弟的作业