对于频率测量,有很多的测试方法,这里我们介绍一种采用基准频率计数的方法测试频率。该测试方法非常容易理解。首先设计两个计数器BASE_CLK_CNT和CLK_TST_CNT,基准时钟BASE_CLK作为计数器BASE_CLK_CNT的输入时钟,被测信号CLK_TST作为CLK_TST_CNT的输入时钟。两个计数器同时开始计数,当CLK_TST_CNT计数到n时计数结束,这个时候根据BASE_CLK_CNT的值m和BASE_CLK的频率,可以计算出CLK_TST的频率。
在上述的测频方法中,两个计数器同时开始计数是整个设计的难点。由于CLK_TST和BASE_CLK是两个异步信号,如果不对信号做特定的处理,就很难做到两个计数器同时开始计数。而且由于两个信号时异步信号,所以在做电路设计时,如果不做相应的处理,容易引起系统的不稳定。
这里我们采用的方法是,用最基本的CDC电路来处理CLK_TST,使之转换为与BASE_CLK在同一时钟域的信号,然后再进行频率测量。这里最基本的CDC电路也就是把CLK_TST用BASE_CLK锁两拍,然后生成新的CLK_TST信号。下面是具体的verilog实现:CLK_TST为输入信号,CLK_TST_2Q为输出的与BASE_CLK同时钟域的信号。经过这样的处理,整个频率测量电路的设计就可以在同一个时钟域中进行。
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
不过这样做的结果就是引入测量误差,其绝对值小于4个BASE_CLK周期(测试周期开始的点0~2 BASE_CLK周期,测试周期结束的点0~2BASE_CLK周期,综合起来,应该小于4个BASE_CLK周期,一般情况下,应该是小于2个BASE_CLK周期)。下图是上述电路的仿真波形:
通常情况下,带来的误差是两条竖线之间的长度,比较差的情况下,误差会再加上一个BASE_CLK的周期。
CLK_TST_2Q的被测信号与BASE_CLK有相同的上升沿,所以我们在CLK_TST_2Q上升沿的时候让两个计数器都开始计数。当CLK_TST_CNT计数到n时,两个计数器同时停止计数。而对于n的选取,则要根据测试精度,CLK_TST的稳定度,BASE_CLK的频率等各方面的要求来设定。不过建议数值最好选用2*x。这样便于后继的数据处理,在计算CLK_TST的时候,可以直接对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的值,CLK_TST的频率值还需要换算电路做进一步的处理,但这里不再给出。
下面是测频电路的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。