在學習FPGA的過程中,最簡單最基本的實驗應該就是分頻器了,
同時分頻器也是FPGA設計中使用頻率非常高的基本設計之一,
盡管在芯片廠家提供的IDE中集成了鎖相環IP,
如altera 的PLL,Xilinx ISE的DLL或者vivado中的clock來進行時鍾的分頻,倍頻以及相移。
但是對於時鍾要求不高的邏輯,通過語言進行時鍾的分頻相移顯得十分方便,
這種方法可以節省芯片內部的鎖相環資源,再者,通過語言設計進行時鍾分頻,可以鍛煉我們對verilog的熟練和理解程度。
-
偶數倍分頻:實現起來比較簡單,這里略過;
-
奇數倍分頻:
如果不要求占空比為50%的話,也比較容易實現,
如進行三分頻,通過待分頻時鍾上升沿觸發計數器進行模三計數,
當計數器計數到鄰近值進行兩次翻轉,比如可以在計數器計數到1時,
輸出時鍾進行翻轉,計數到2時再次進行翻轉。
即在計數值在鄰近的1和2進行了兩次翻轉。
這樣實現的三分頻占空比為1/3或者2/3。對於實現占空比為50%的N倍奇數分頻,我們可以分解為兩個通道:
- 上升沿觸發進行模N計數,計數選定到某一個值進行輸出時鍾翻轉,
然后經過(N-1)/2再次進行翻轉得到一個占空比為非50%奇數N分頻時鍾; - 下降沿觸發進行模N計數,到和上升沿觸發輸出時鍾翻轉選定值相同值時,
進行輸出時鍾時鍾翻轉,同樣經過(N-1)/2時,
輸出時鍾再次翻轉生成占空比非50%的奇數N分頻時鍾。
將這兩個占空比非50%的N分頻時鍾或運算,得到占空比為50%的奇數n分頻時鍾。
具體例子:5分頻等占空比,可以通過待分頻時鍾下降沿和上升沿觸發0~4計數,
- 對於待分頻時鍾的上升沿,當計數器cnt1計數到1時,
clk_p翻轉;當計數器計數到3(1 + (5 - 1) / 2 = 3)時,clk_p再次反轉; - 對於待分頻時鍾的下降沿,當計數器cnt2計數到1時,
clk_n翻轉;當計數器計數到3(1 + (5 - 1) / 2 = 3)時,clk_n再次反轉; - 然后下降沿產生的5分頻時鍾和上升沿產生的5分頻時鍾進行或運算,
即可得到占空比為50%的N分頻時鍾。
這種方法可以實現任意的奇數分頻。
- 上升沿觸發進行模N計數,計數選定到某一個值進行輸出時鍾翻轉,
下面給出5分頻的具體代碼:
`timescale 1ns/1ps
module CLK_DIV5(
input clk_i,
input rst_n,
output clk_o
);
reg [2:0] cnt1,cnt2;
reg clk_p,clk_n;
//*********************
//MAIN CORE
//*********************
always @(posedge clk_i,negedge rst_n)
if(!rst_n) begin
cnt1 <= 3'b0;
clk_p <= 1'b0;
end
else begin
if(cnt1 == 3'b100) begin
cnt1 <= 3'b0;
clk_p <= clk_p;
end
else begin
cnt1 <= cnt1 + 1'b1;
if(cnt1 == 3'b1 || cnt1 == 3'b11)
clk_p <= ~clk_p;
end
end
always @(negedge clk_i,negedge rst_n)
if(!rst_n) begin
cnt2 <= 3'b0;
clk_n <= 1'b0;
end
else begin
if(cnt2 == 3'b100) begin
cnt2 <= 3'b0;
clk_n <= clk_n;
end
else begin
cnt2 <= cnt2 + 1'b1;
if(cnt2 == 3'b1 || cnt2 == 3'b11)
clk_n <= ~clk_n;
end
end
assign clk_o = clk_p | clk_n;
endmodule
測試激勵模塊:
`timescale 1ns/1ps
module TB_TOP;
reg rst_n ;
reg clk ;
CLK_DIV5 U_CLK_DIV5(
.clk_i(clk),
.rst_n(rst_n),
.clk_o(clk_o)
);
//*********************
//MAIN CORE
//*********************
initial begin
rst_n =1'b1;
clk =1'b0;
#5
rst_n = 1'b0;
#5
rst_n = 1'b1;
#500
$finish;
end
always #1 clk = ~clk;
endmodule
modelsim仿真圖: