1 理论学习
1.1 三种测量频率测量方法
1 常用的频率测量方法有两种--频率测量法和周期测量法
频率测量法 | 周期测量法 |
适合于测量高频时钟信号 | 适合于低频时钟信号的测量 |
频率测量法?频率测量法适合于高频时钟信号的测量?
在时间 t 内对被测时钟信号的时钟周期 N 进行计数,然后求出单位时间内的时钟周期数,即为被测时钟信号的时钟频率。在测量时间t一定的情况下,频率越高,个被测时钟误差对频率测量的结果越小。
周期测量法?周期测量法适合于低频时钟信号的测量?
先测量出被测时钟信号的时钟周期 T,然后根据频率 f = 1/ T 求出被测时钟信号的频率
2等精度测量法
测量的实际门控时间不是一个固定值,它与被测信号相关,是被测信号周期的整数倍。怎么做到实际门控时间=被测信号周期的整数倍?
在实际门控信号下,同时对标准时钟和被测时钟信号的时钟周期进行计数,再通过公式计算得到被测信号的时钟频率。
实际门控信号是被测时钟周期的整数倍,就消除了被测信号产生的±1时钟周期的误差。增大“软件闸门”的有效范围或者提高“标准时钟信号”的时钟频率fs,可以减小误差,提高测量精度。(两者的目的都是提高阀门内波形的个数来减小测量的误差)
注意:检测输入信号的触发条件为上升沿触发(即刻触发,不考虑FPGA的时钟频率与待测时钟信号频率之间的关系)
always@(posedge clk_test or negedge sys_rst_n)
if(~sys_rst_n)
cnt_X <= 1'b0;
else if(gate_actual)
cnt_X <= cnt_X + 1'b1;
else if(cnt_gate_soft == (6*MAX_CLK_REF - 2))
cnt_X <= 1'b0;
1.2 计算方法
目的:通过标准时钟信号时钟频率fs和实际闸门下被测时钟信号周期数X、实际闸门下标准时钟信号周期数Y来计算被测时钟信号时钟频率fx。
2 实战演练
2.1 实验目标
设计一个基于等精度测量原理的简易频率计,对输入的未知时钟信号做频率测量,并将测量结果在数码管上显示
要求:标准时钟信号频率为100MHz,实际闸门时间大于或等于1s,目的是减小误差,提高测量精度。
这里100MHz是不是要进行锁相环倍频?-是的
硬件设置:使用板卡引出I/O口F14作为模拟测试时钟输出端口;使用板卡引出I/O口F15作为待测试时钟输入端口
2.2 程序设计与问题解答
1 模块框图
2 Freq_meter_calc模块波形
(1)进行软件阀门的表示:对cnt_gate_soft通过设置参数MAX的方式能够快速定位到软件阀门的节点位置坐标,这是一种不错的标记方式。
(2)软件阀门下的1s计时:软件阀门gate_soft开放一秒的时间(5*MAX对应1s的时间),因为时序逻辑所以打一拍得到实际的软件阀门gate_soft,在此期间内完成X和Y的计数。其中X代表1s内对待测脉冲采样的个数cnt_X,Y表示1s内对标准时钟脉冲(这里由pll产生)采样的个数cnt_Y。
(3)软件阀门1s计时结束(下降沿时刻)进行X和Y的存储:在检测到flag_gateFall_actual和flag_gateFall_stand的标志信号(也就是检测到软件阀门的下降沿)是进行X和Y的存储,也就是store_X=cnt_X; store_Y=cnt_Y;
(4)软件阀门1s计时结束(下降沿时刻)的判断:gate_actual_delay1 &gate_actual_delay2的产生是为了检测待测信号的下降沿,同理gate_stand_delay1 &gate_stand_delay2的产生是为了标准时钟脉冲信号的下降沿。
(5)cnt_X cnt_Y的清零&输出标志信号置1:在一个cnt_gate_soft的计数周期末尾(6*MAX-2)处进行cnt_X,cnt_Y的清零和flag_readData的置1
(6)数据的输出:代入公式进行计算频率
注意:
(1) 上面的(6)中没有对输出信号进行延迟一拍的操作,只需在例程中将freq的位宽重新设置为64位宽即可(也就是说不需要freq_reg和flag_readData_delay1这两个寄存器)
(2) 软件闸门的时钟是系统时钟
3 问题解答
(1)输出为什么要延迟一拍?
不打拍 | |
打一拍 |
其实也是没有必要,这里其实是因为视频的作者一开始设置的freq的位宽太小了,这样导致频率计算出现问题(乘法赋值溢出,下面有介绍),因为.v文件中多次调用32’dfreq,如果改变freq的宽度则需要在文件的多个位置改变。视频的作者为了防止修改遗漏,通过先存入更大宽度的寄存器64’dfreq_reg的方式首先得到准确的频率值,然后打一拍后令freq<=freq_reg[31:0],最后的freq_reg[31:0]是为了宽度的统一。但是读者要知道,还有一种解决办法就是直接32’dfreq-->64’dfreq(不打拍)
(2)FPGA中乘法赋值溢出
赋值号右边两个运算寄存器变量相乘,如果赋值号左边变量的宽度放不下相乘后的结果,则会把相乘的结果放在右边宽度更大的寄存器中也就发生了乘法赋值溢出
上面也就解释了计算出来freq的数值不接近104629632而是等于483891的原因
(3)Freq的位宽
reg [33:0] freq 为什么需要34位宽?999_999_999对应30位宽,这里考虑其他因素又增加了几位
reg [47:0] cnt_clk_actual 为什么要48位这么宽?-尽量扩大频率测量的范围
(4)不要让ip核名字和里面的变量命名一样
clk_stand clk_stand_inst (
.areset ( ~sys_rst_n ),
.inclk0 ( sys_clk ),
.c0 ( clk_stand )
);
3 总结
FPGA的学习重要的思想:模块化设计。对系统的整体模块和具体模块进行设计后,可以把系统分为freq_meter顶层模块和freq_meter_calc子模块,clk_test子模块,seg_595_dynamic子模块。其中freq_meter_calc子模块是我们需要主要设计的模块,clk_test子模块可以由IP核快速生成,seg_595_dynamic子模块是之前动态数码管章节学习的内容,这里直接进行调用。
进行freq_meter_calc子模块波形的设计是本工程的核心内容。在freq_meter_calc子模块的波形分析中要明确设计的目的---计算的数值,然后中间的寄存器变量就围绕着三个参数展开。在设计过程中特别注意最后代入公式时,等式左边的赋值变量freq的位宽,需要满足 的最大值* (这里是100Mhz)能用freq的合理位宽表示。
最后进行系统各个子模块的在顶层文件的实例化。
以上学习笔记有不少自己的理解,当然也参考了野火FPGA教程,在这里再次致谢。笔者水平有限,希望读者发现有问题的话可以与我交流,一起进步~~