一个计算频率的模块
先上代码
module _sync_reg #(
parameter INIT = 0,
parameter ASYNC_RESET = 0
) (
input clk,
input rst,
input in,
output out
);
(* ASYNC_REG = "TRUE" *) reg sync1;
(* ASYNC_REG = "TRUE" *) reg sync2;
assign out = sync2;
generate
if (ASYNC_RESET) begin
always @(posedge clk or posedge rst) begin
if (rst) begin
sync1 <= INIT;
sync2 <= INIT;
end else begin
sync1 <= in;
sync2 <= sync1;
end
end
end else begin
always @(posedge clk) begin
if (rst) begin
sync1 <= INIT;
sync2 <= INIT;
end else begin
sync1 <= in;
sync2 <= sync1;
end
end
end
endgenerate
endmodule
/*
clk_estimator #( .EST_BITS ( 24 )) clk_estimator (
.rst(),
.clk(),
.meas_clk(),
.cntr_data()
);
*/
module clk_estimator #(
parameter FRQ = 100*1000*1000
)(
input rst,
input clk,
input meas_clk,
output reg [32-1:0] dout ,
output reg update
);
reg [31:0] c;
reg f = 0 ;
wire of = c >= (FRQ/20) -1 ;
always @(posedge clk) begin
if (rst ) begin c <= 0; f<=0; end
else if (of)begin f<=~f; c<=0; end
else begin c <= c + 1 ;end
end
wire out_dclk;
_sync_reg self_estim(
.clk(meas_clk),
.rst(rst),
.in(f),
.out(out_dclk)
);
reg [32-1:0] cntr_clk;
reg [32-1:0] ref_cntr_data_r;
reg prev_out_dclk;
reg update_w ;
always @(posedge meas_clk) begin
if (rst) begin
update_w <=0;
cntr_clk <= 0;
prev_out_dclk <= 0;
ref_cntr_data_r <= 0;
end else begin
prev_out_dclk <= out_dclk;
if (prev_out_dclk == 0 && out_dclk == 1'b1) begin
cntr_clk <= 0;
ref_cntr_data_r <= cntr_clk;
update_w <=1;
end else begin
cntr_clk <= cntr_clk + 1'b1;
update_w <=0;
end
end
end
(* ASYNC_REG = "TRUE" *) reg [4:0] updater; always @ (posedge clk ) updater <= { updater[3:0], update_w };
(* ASYNC_REG = "TRUE" *) reg[31:0] r; reg[31:0] rr,rrr,rrrr;
always @ (posedge clk ) rrrr<=rrr;
always @ (posedge clk ) rrr<=rr;
always @ (posedge clk ) rr<=r;
always @ (posedge clk ) r<=ref_cntr_data_r;
reg [31:0]watch_dog_cntr ;
wire update1= updater[4:3] == 2'b01 ;
always @ (posedge clk ) if ( update1 | rst )watch_dog_cntr<=0; else watch_dog_cntr<= ( watch_dog_cntr>= FRQ/4 )?0: (watch_dog_cntr+1);
wire watch_dog_act = watch_dog_cntr >= FRQ/4 ;
always @ (posedge clk ) if (watch_dog_act) dout <= 0 ;else if (update1) dout<= rrrr ;
always @ (posedge clk ) update <= watch_dog_act ==1 || update1 ==1 ;
endmodule
当外部meas_clk有频率信号接入时,每1/10秒就会输出更新一下时钟计数,这里的计数是输入meas_clk的1/10。如果没有频率接入每1/4秒输出一下结果0。
原理是我们已知频率为FRQ的时钟clk每1/20秒翻转一次,这样得到的每次上升边缘就是1/0秒。被测试的时钟meas_clk在1/10秒内数数,数到到多少,之后再传到clk时钟域,输出出来。另外设置了1/4秒的看门狗,当时meas_clk丢失,超过1/4秒没有更新,就触犯看门狗电路自动更新输出0.
这个代码实验通过了,非常好用。可以在编译时候有跨时钟区域的警告,可以通过设置虚假路径来解决。