频率计
频率计又称频率计数器,是一种专门对被测信号频率进行测量的电子测量仪器。
基准时钟:通常就是FPGA板上的晶振,一般FPGA开发板会提供一个50Mhz 的晶振作为时钟源。
计数法
计数法:直接计数 单位时间内被测信号的脉冲数量;这种方法测量精度高、速度快,适合不同频率、不同精度的测频需要。
适合不同频率指的是一般的频率计在测高频信号和低频信号时的误差不一致,而采用计数法就能很好地适应不同频率的要求。
理想条件下:
频率检测模块也是在面试或者笔试的时候时常会遇到的一个问题,在这里对自己学习过程中遇到的一些方法做一些总结
频率测量的方法有很多:
这里介绍一种采用基准时钟(基准时钟)计数的方法来检测待测时钟。具体的方法是采用两个计数器BASE_CLK_CNT 和UT_CLK_CNT,基准时钟BASE_CLK驱动BASE_CLK_CNT,而待测时钟UT_CLK驱动UT_CLK_CNT。两个计数器同时开始计数,当UT_CLK_CNT计数至N时,停止计数;此时,根据基准时钟计数器BASE_CLK_CNT的值以及基准时钟BASE_CLK的频率可以计算出待测时钟UT_CLK的频率值。
上述方法必须要求两个计数器同时开始计数,这也是该方法设计的难点所在。由于基准时钟和待测时钟通常都是异步,若是不作处理就难以做到两个计数器同时开始计数,并且会引起系统的不稳定。
对待测时钟UT_CLK同步到基准时钟BASE_CLK下:
reg UT_CLK_D1;
reg UT_CLK_D2;
always @(posedge BASE_CLK or negedge RESET_n)
begin
if(~RESET_n) begin
UT_CLK_D1 <= 1'b0;
UT_CLK_D2 <= 1'b0;
end
else begin
UT_CLK_D1 <= UT_CLK;
UT_CLK_D2 <= UT_CLK_D1;
end
end
不过这样做的结果就是引入测量误差,其绝对值小于4个BASE_CLK周期(测试周期开始的点0~2 BASE_CLK周期,测试周期结束的点0~2BASE_CLK周期,综合起来,应该小于4个BASE_CLK周期,一般情况下,应该是小于2个BASE_CLK周期)。
通常情况下,带来的误差是两条竖线之间的长度,比较差的情况下,误差会再加上一个BASE_CLK的周期。
UT_CLK_D2 的被测信号与BASE_CLK有相同的 上升沿 ,所以我们在UT_CLK_D2上升沿的时候再让两个计数器都开始计数。当UT_CLK_TCNT计数到 N 时,两个计数器 同时 停止计数。而对于 N 的选取,则要根据 测试精度 ,UT_CLK的 稳定 度,BASE_CLK的 频率 等各方面的要求来设定。不过建议数值最好选用2*X,这样便于后继的数据处理,在计算UT_CLK的时候,可以直接对BASE_CLK_CNT进行移位处理。
采用一个简单的状态机来实现这个测频电路。状态机有三个状态,FT_IDLE ,FT_TST和FT_DOUT。系统复位或者一次测量结束以后,状态机进入FT_IDLE状态,当TST_EN信号有效的时候,开始进行频率测量。当CLK_TST_CNT的计数值达到N(在该例子中,我们选择64)的时候,进入FT_DOUT状态。FT_DOUT状态持续的时间,由一个小的计数器的计数值来决定,我们可以通过改变这个计数器的值来让数据输出的时间符合后继处理电路的要求。
这个例程中直接给出BASE_CLK_CNT的值,UT_CLK的频率值还需要换算电路做进一步的处理,但这里不再给出。
下面是测频电路的verilog实现:
module FRE_TST(
BASE_CLK, //base clock,default is 100MHz
CLK_TST, //clock on test
RESET, //async system reset
TST_EN, //test enable signal
DATA_OUT, //24bit test data out to uart
DATA_OUT_EN //24bit test data out enable
);
input BASE_CLK;
input CLK_TST;
input RESET;
input TST_EN;
output [23:0] DATA_OUT;
output DATA_OUT_EN;
parameter FT_IDLE = 3'b001,
FT_TST = 3'b010,
FT_DOUT = 3'b100;
reg [2:0] FTSTSM, FTSTSMNXT;
wire PHASE_FT_IDLE = FTSTSM[0];
wire PHASE_FT_TST = FTSTSM[1];
wire PHASE_FT_DOUT = FTSTSM[2];
wire PHASENXT_FT_DOUT = FTSTSMNXT[2];
wire PHASENXT_FT_TST = FTSTSMNXT[1];
reg CLK_TST_Q;
reg CLK_TST_2Q;
always @(posedge BASE_CLK or negedge RESET)
begin
if(~RESET)
begin
CLK_TST_Q <= 1'b0;
CLK_TST_2Q <= 1'b0;
end
else
begin
CLK_TST_Q <= CLK_TST;
CLK_TST_2Q <= CLK_TST_Q;
end
end
reg [23:0] BASE_CLK_CNT;
reg [11:0] CLK_TST_CNT;
always @(posedge CLK_TST_2Q or negedge RESET) begin
if(~RESET)
CLK_TST_CNT <= 12'b0;
else if(PHASE_FT_IDLE)
CLK_TST_CNT <= 12'b0;
else if(CLK_TST_CNT == 12'b0000_0100_0001)
CLK_TST_CNT <= 12'b0;
else if(PHASE_FT_TST )
CLK_TST_CNT <= CLK_TST_CNT + 12'b1;
else
CLK_TST_CNT <= CLK_TST_CNT;
end
wire TST_END_TMP = (CLK_TST_CNT == 12'b0000_0100_0001) ; //count 65
reg TST_END_TMP_Q;
always @(posedge BASE_CLK or negedge RESET)
begin
if(~RESET)
TST_END_TMP_Q <= 1'b0;
else
TST_END_TMP_Q <= TST_END_TMP;
end
wire TST_END = ~TST_END_TMP_Q & TST_END_TMP;
reg [2:0] data_out_cnt;
always @(posedge BASE_CLK or negedge RESET) begin
if(~RESET)
data_out_cnt <= 3'b0;
else if(PHASE_FT_IDLE)
data_out_cnt <= 3'b0;
else if(PHASE_FT_DOUT)
data_out_cnt <= data_out_cnt + 3'b1;
else
data_out_cnt <= data_out_cnt;
end
wire DATA_OUT_END;
assign DATA_OUT_END = (data_out_cnt == 3'd5);
always @(posedge BASE_CLK or negedge RESET) begin
if(~RESET)
BASE_CLK_CNT <= 24'h0;
else if( PHASE_FT_TST )
BASE_CLK_CNT <= BASE_CLK_CNT + 24'b1;
else if(PHASE_FT_IDLE)
BASE_CLK_CNT <= 24'h0;
else
BASE_CLK_CNT <= BASE_CLK_CNT;
end
always @(posedge BASE_CLK or negedge RESET) begin
if(~RESET)
FTSTSM <= FT_IDLE;
else
FTSTSM <= FTSTSMNXT;
end
always @(FTSTSM or TST_EN or CLK_TST_2Q or TST_END or DATA_OUT_END) begin
FTSTSMNXT = FTSTSM;
case (FTSTSM)
FT_IDLE:
if(TST_EN & CLK_TST_2Q)
FTSTSMNXT = FT_TST;
else
FTSTSMNXT = FT_IDLE;
FT_TST:
if(TST_END)
FTSTSMNXT = FT_DOUT;
else
FTSTSMNXT = FT_TST;
FT_DOUT:
if(DATA_OUT_END)
FTSTSMNXT = FT_IDLE;
else
FTSTSMNXT = FT_DOUT;
default:
FTSTSMNXT = FT_IDLE;
endcase
end
assign DATA_OUT_EN = PHASE_FT_DOUT;
assign DATA_OUT = BASE_CLK_CNT;
endmodule
下面是仿真的结果:
BASE_CLK的周期是5ns,CLK_TST的周期是1000ns。
编写一个频率检测模块,输入端口:50M时钟,待检测时钟(范围1M-200M)
输入端口A:50M时钟
输入端口B:被测时钟
输入端口C:复位信号
输出端口D:频率值(单位Mhz)
module time_detect(
input clk_ref_in , //参考时钟50M
input clk_in , //待测时钟
input rst ,
output [15:0] number //
);
//=======================================================================\
//**************************** 寄存器定义 *************************
reg [7:0] clk_cnt = 0;
reg high_lev = 0;
reg high_lev_ff1;
reg high_lev_ff2;
reg high_lev_ff3;
reg [15:0] clk_ref_cnt = 0;
reg [15:0] clk_ref_cnt_lat = 0;
reg [15:0] clk_F = 0
//============================ clk_in时钟域 ============================\
/******** clk_in:1~200M,先降频,经200倍分频后的时钟不大于1M ************/
//计数器
always @ (posedge clk_in or posedge rst)
begin
if(rst)
clk_cnt <= 0
else if(clk_cnt == 199)
clk_cnt <= 0;
else
clk_cnt <= clk_cnt + 1;
end
//分频输出
always @ (posedge clk_in or posedge rst)
begin
if(rst)
high_lev <= 0;
else if(clk_cnt == 99)
high_lev <= 1;
else if(clk_cnt == 199)
high_lev <= 0;
else;
end
//========================= clk_ref_in参考时钟域 =========================\
/*对输入分频时钟作跨时钟域处理:大三拍*/
always @ (posedge clk_ref_in)
begin
high_lev_ff1 <= high_lev;
high_lev_ff2 <= high_lev_ff1;
high_lev_ff3 <= high_lev_ff2;
end
/*ref时钟域下对分频后的时钟高电平进行计数*/
always @ (posedge clk_ref_in or posedge rst)
begin
if(rst)
clk_ref_cnt <= 0;
else if(high_lev_ff3)
clk_ref_cnt <= clk_ref_cnt + 1;
else
clk_ref_cnt <= 0;
end
/*用一个锁存器clk_ref_cnt_lat锁存高电平计数最大值:即将分频时钟下降沿达到最大值*/
always @ (posedge clk_ref_in or posedge rst)
begin
if(rst)
clk_ref_cnt_lat <= 0;
else if({high_lev_ff3,high_lev_ff2} == 2'b10)
clk_ref_cnt_lat <= clk_ref_cnt;
else
end
/*计算频率*/
always @ (posedge clk_ref_in or posedge rst)
begin
if(rst)
clk_F <= 0;
else
clk_F <= 5000/clk_ref_cnt_lat;
end
assign number = clk_F;
emdmodule