好久没更了,百忙之中写一篇so easy的代码——基于FPGA的频率计设计。
一、简介
废话不多说,下面是百度搜索关于频率计的简洁概念。
数字频率计是一种基本的测量仪器,被广泛应用于航天、电子、测控等领域。基于传统测频原理的频率计的测量精度将随被测信号频率的下降而降低,在使用中有较大的局限性,而等精度频率计不但具有较高的测量精度,而且在整个频率区域能保持恒定的测试精度。
二、设计方案
对于频率计的设计,常用的方法有三种:测周法、测频法以及等精度测量法。
(1)测周法
测周法的测量对象一般是频率较低的被测信号,一般是选择取被测时钟的一个周期的上升沿或者下降沿都是可以的,下图是选择被测时钟一个周期的上升沿。
由波形图中可以很快速的写出测周法的代码:
module cymometer
#(parameter SYS_CLK = 32'd100_000_000) // 基准时钟频率值
( //system clock
input sys_clk , // 基准时钟信号
input rst_n , // 复位信号
//cymometer interface
input in_signal , // 被测时钟信号
// output wire [31:0] fs_cnt_t/* synthesis syn_keep=1 */,
output reg [31:0] fre // 被测时钟频率输出
);
//测周法(低频)
reg [31:0] signal_cnt; //对被测信号高电平进行计数的计数器
//在测信号高电平期间进行计数
always @(posedge sys_clk or negedge rst_n)begin
if(!rst_n)
signal_cnt <= 0;
else if(low_flag)
signal_cnt <= signal_cnt + 1;
else if(low_flag == 1'b0)
signal_cnt <= signal_cnt;
end
reg [31:0] low_cnt;
reg low_flag;
always @(negedge in_signal or negedge rst_n )begin
if(!rst_n) begin
low_cnt <= 4'd0;
low_flag <= 1'b0;
end
else if(low_cnt < 4'd3) begin
low_cnt <= low_cnt + 1'b1;
low_flag <= 1'b0;
end
else if(low_cnt == 4'd3) begin
low_cnt <= low_cnt + 1'b1;
low_flag <= 1'b1;
end
else if(low_cnt == 4'd4) begin
low_cnt <= low_cnt;
low_flag <= 1'b0;
end
end
//低频除法器
//在测信号低电平期输出测量数据
always @(negedge low_flag or negedge rst_n )begin
if(!rst_n)
fre <= 0;
else
fre <= SYS_CLK / signal_cnt;
end
一直比较忙,所以仿真图暂时不更新了,之前验证过了没啥问题,如果有问题需要的小伙伴可以自己调试哦。
(2)测频法
一般是使用比被测信号高得多的标准信号,基准时钟可以取一个固定值(如1S),然后被测时钟以上升沿或者下降沿计数,计数就为该被测时钟的频率,波形图如下。
代码如下:
parameter CNT_2S = 32'd199_999_999;
//高频
reg [31:0] cnt_high;
reg flag_1s;
always @(posedge sys_clk or negedge rst_n) begin
if(!rst_n) begin
cnt_high <= 32'd0;
flag_1s <= 1'd0;
end
else if(cnt_high < 32'd10) begin
cnt_high <= cnt_high + 32'd1;
flag_1s <= 1'd0;
end
else if(cnt_high == 32'd10) begin
cnt_high <= cnt_high + 32'd1;
flag_1s <= 1'd1;
end
else if(cnt_high < 32'd10 + SYS_CLK) begin
cnt_high <= cnt_high + 32'd1;
flag_1s <= 1'd1;
end
else if(cnt_high == 32'd10 + SYS_CLK) begin
cnt_high <= cnt_high + 32'd1;
flag_1s <= 1'd0;
end
else if(cnt_high == 32'd10 + SYS_CLK + CNT_2S) begin
cnt_high <= 32'd0;
flag_1s <= 1'd0;
end
end
reg [31:0] fre_high;
always @(posedge in_signal or negedge rst_n) begin
if(!rst_n)
fre_high <= 32'd0;
else if(flag_1s == 1'b1)
fre_high <= fre_high + 1'd1;
else if(flag_1s == 1'b0)
fre_high <= fre_high;
end
always@(negedge flag_1s or negedge rst_n) begin
if(!rst_n)
fre <= 32'd0;
else
fre <= fre_high;
end
(3)等精度测量法:
等精度测量法与前两种方式不同,其最大的特点是,测量的实际门控时间不是一个固
定值,它与被测时钟信号相关,是被测时钟信号周期的整数倍。在实际门控信号下,同时
对标准时钟和被测时钟信号的时钟周期进行计数,再通过公式计算得到被测信号的时钟频
率。
等精度测量法与前两种方式不同,其最大的特点是,测量的实际门控时间不是一个固
定值,它与被测时钟信号相关,是被测时钟信号周期的整数倍。在实际门控信号下,同时
对标准时钟和被测时钟信号的时钟周期进行计数,再通过公式计算得到被测信号的时钟频
率。
结合等精度测量原理和原理示意图可得:被测时钟信号的时钟频率 fx 的相对误差与被测时钟信号无关;增大“软件闸门”的有效范围或者提高“标准时钟信号”的时钟频率fs,可以减小误差,提高测量精度。
了解了等精度测量原理之后,我们来说明一下被测时钟信号的计算方法。首先我们先分别对实际闸门下被测时钟信号和标准时钟信号的时钟周期进行计数。
我们设实际闸门被测时钟的计数为x,被测信号时钟周期为Tfx,他的时钟频率fx=1/Tfx,由此可以得公式:X * Tfx = X / fx = Tx(实际闸门)。
实际闸门下标准时钟信号周期数为 Y,设被测信号时钟周期为 Tfs,它的时钟频率 fs =1/Tfs,由此可得等式: Y * Tfs = Y / fs = Tx(实际闸门)。
其次,将两等式结合得到只包含各自时钟周期计数和时钟频率的等式: X / fx = Y / fs =Tx(实际闸门),等式变换, 得到被测时钟信号时钟频率计算公式: fx = X * fs / Y。最后,将已知量标准时钟信号时钟频率 fs 和测量量 X、 Y 带入计算公式, 得到被测时钟信号时钟频率 fx。
根据上面的分析,我们可以设计一个等精度的频率计模块。首先是进行波形图的绘制:
文章来源地址https://www.toymoban.com/news/detail-662388.html
module frequency(
input sys_clk ,
input sys_rst_n ,
input clk_test ,
input clk_100M ,
output reg [33:0] freq
);
parameter CLK = 100_000_000;
parameter CNT_X = 28'd12_499_999,
CNT_5X = 28'd62_499_999,
CNT_6X = 28'd74_999_999;
wire gate_fx_neg; //被测闸门下降沿
wire gate_fs_neg; //基准闸门下降沿
wire locked;
assign locked = 1'b1;
reg [27:0] cnt; //
reg gate_soft; //软件闸门
reg gate_a ; //实际闸门
reg gate_fx_reg; //被测闸门
reg gate_fs_reg; //基准闸门
reg [31:0] cnt_gate_fx; //被测闸门计数器
reg [31:0] cnt_gate_fs; //基准时钟闸门计数器
reg [31:0] fx_cnt ; //被测频率计数
reg [31:0] fs_cnt ; //标准频率计数
reg cala_flag ; //计算标志位
reg [63:0] fre_cnt ; //频率计数器
reg cala_flag_reg ; //计算标志寄存器
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
cnt <= 28'd0;
else if(cnt == CNT_6X)
cnt <= 28'd0;
else
cnt <= cnt + 28'd1;
//软件闸门
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
gate_soft <= 1'b0;
else if(cnt <= CNT_X || cnt > CNT_5X)
gate_soft <= 1'b0;
else
gate_soft <= 1'b1;
//实际闸门
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
gate_a <= 1'b0;
else
gate_a <= gate_soft;
//被测闸门
always@(posedge clk_test or negedge sys_rst_n)
if(!sys_rst_n)
gate_fx_reg <= 1'b0;
else
gate_fx_reg <= gate_a;
//被测计数器
always@(posedge clk_test or negedge sys_rst_n)
if(!sys_rst_n)
cnt_gate_fx <= 32'd0;
else if(gate_a == 1'b0)
cnt_gate_fx <= 32'd0;
else if(gate_a == 1'b1)
cnt_gate_fx <= cnt_gate_fx + 32'd1;
//取下降沿
assign gate_fx_neg = gate_fx_reg && ~gate_a;
//被测计数器
always@(posedge clk_test or negedge sys_rst_n)
if(!sys_rst_n)
fx_cnt <= 32'd0;
else if(gate_fx_neg == 1'b1)
fx_cnt <= cnt_gate_fx;
//基准闸门
always@(posedge clk_100M or negedge sys_rst_n)
if(!sys_rst_n)
gate_fs_reg <= 1'b0;
else
gate_fs_reg <= gate_a;
//基准闸门计数器
always@(posedge clk_100M or negedge sys_rst_n)
if(!sys_rst_n)
cnt_gate_fs <= 32'd0;
else if(gate_a == 1'b0)
cnt_gate_fs <= 32'd0;
else if(gate_a == 1'b1)
cnt_gate_fs <= cnt_gate_fs + 32'd1;
//取下降沿
assign gate_fs_neg = gate_fs_reg && ~gate_a;
//标准时钟计数
always@(posedge clk_100M or negedge sys_rst_n)
if(!sys_rst_n)
fs_cnt <= 32'd0;
else if(gate_fs_neg == 1'b1)
fs_cnt <= cnt_gate_fs;
//频率计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
fre_cnt <= 64'd0;
else if(cala_flag == 1'b1)
fre_cnt <= CLK*fx_cnt/fs_cnt;
//计算标志位
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n) begin
cala_flag <= 1'b0;
cala_flag_reg <= 1'b0;
end
else if(cnt == CNT_6X) begin
cala_flag <= 1'b1;
cala_flag_reg <= cala_flag;
end
always@(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
freq <= 34'd0;
else if(cala_flag_reg == 1'b1)
freq <= fre_cnt;
endmodule
仿真代码:
`timescale 1ns / 1ns
module tb_freq();
reg sys_clk ;
reg sys_rst_n ;
reg clk_test ;
wire [33:0] freq ;
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
clk_test = 1'b1;
#201
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
always #30 clk_test = ~clk_test;
defparam frequency_inst.CNT_X = 12_4,
frequency_inst.CNT_5X = 62_4,
frequency_inst.CNT_6X = 74_9;
frequency frequency_inst(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.clk_test (clk_test ),
.freq (freq )
);
endmodule
然后我们看一下仿真波形:
首先是被测时钟的计数波形
其次是标准时钟计数波形
最后看一下计算的结果
计算的结果为100_000_000*167/1000=16_700_000,结果是没有问题的,我们再看一下上板验证的结果:
这里测试时钟设置的频率为34.15926,PLL会保留小数点后三位也就是34.146,然后我们看一下上板之后的结果:
结果跟我们设置的锁相环结果是一致的,我们再来试一下高频的测试:
文章来源:https://www.toymoban.com/news/detail-662388.html
锁相环输出的结果为109.091,而我们频率计显示的频率为109.09,结果有一丢丢差异,就跟我们前面讲的一样,如果为了计算的精确,可以调节基准时钟频率或软件闸门的保持时间对同一信号进行频率测量。但相对于测频和测周法来说,等精度的精度相度更加灵活和精准。
到了这里,关于基于FPGA的频率计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!