此頻率計是用 50MHz 作採樣,所以解析度以 20us 為一個單位,其構思為抓取量測訊號的正緣,直到下一個正緣來臨前使用計數器計數,下一個正緣到達時則清空計數器且重新計算,而計數器的長度決定可以抓取多慢的週期。
實作方法如下圖,PL 負責抓取資料透過 AXI-Lite 傳送資料至 PS,PS 負責把 raw data 轉為頻率或週期。

PL 的 RTL 如下 :
module freq_cnt (
input clk,
input rstN,
input iClkDet,
output [23:0] ovClkCnt
);
reg [1:0] rvEdge_d, rvEdge_q;
wire wRising;
reg [23:0] rvCnt_d, rvCnt_q;
wire AddCnt, KeepCnt, ClrCnt;
reg [23:0] rvLatchCnt_d, rvLatchCnt_q;
//edge detection
always @(posedge clk or negedge rstN) begin
if(!rstN)
rvEdge_q <= 2'h0;
else
rvEdge_q <= rvEdge_d;
end
always @(*) begin
rvEdge_d = {rvEdge_q[0], iClkDet};
end
assign wRising = (rvEdge_q == 2'b01) ? 1'b1 : 1'b0;
//period counter
assign AddCnt = 1'b1;
assign KeepCnt = rvCnt_q == (24'hFFFFFF - 1'h1);
assign ClrCnt = wRising;
always @(posedge clk or negedge rstN) begin
if(!rstN)
rvCnt_q <= 24'h0;
else
rvCnt_q <= rvCnt_d;
end
always @(*) begin
if(ClrCnt)
rvCnt_d = 24'h0;
else if(KeepCnt)
rvCnt_d = rvCnt_q;
else if(AddCnt)
rvCnt_d = rvCnt_q + 1'b1;
else
rvCnt_d = rvCnt_q;
end
//latch data
always @(posedge clk or negedge rstN) begin
if(!rstN)
rvLatchCnt_q <= 24'h0;
else
rvLatchCnt_q <= rvLatchCnt_d;
end
always @(*) begin
if(KeepCnt)
rvLatchCnt_d = 24'h0;
else if(wRising)
rvLatchCnt_d = rvCnt_q;
else
rvLatchCnt_d = rvLatchCnt_q;
end
assign ovClkCnt = rvLatchCnt_q;
endmodule
有了PS的加持讓我可以容易處理週期及頻率的轉換,測試效果看起來還可以,測試如下:
(1) 測試1
(2) 測試2
(3) 測試3