基于FPGA的频率计

好久没更了,百忙之中写一篇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。

        根据上面的分析,我们可以设计一个等精度的频率计模块。首先是进行波形图的绘制:

 

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,然后我们看一下上板之后的结果:

 结果跟我们设置的锁相环结果是一致的,我们再来试一下高频的测试:

 

 

        锁相环输出的结果为109.091,而我们频率计显示的频率为109.09,结果有一丢丢差异,就跟我们前面讲的一样,如果为了计算的精确,可以调节基准时钟频率或软件闸门的保持时间对同一信号进行频率测量。但相对于测频和测周法来说,等精度的精度相度更加灵活和精准。

 

 

 

 

 

  • 7
    点赞
  • 74
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伊藤诚诚诚诚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值