參考博文:https://www.cnblogs.com/mingmingruyue99/p/7202000.html
1.偶分頻模塊設計
偶分頻意思是時鍾模塊設計最為簡單。首先得到分頻系數M和計數器值N。
M = 時鍾輸入頻率 / 時鍾輸出頻率
N = M / 2
如輸入時鍾為50M,輸出時鍾為25M,則M=2,N=1。偶分頻則意味着M為偶數。
以M=4,N=2為例,我們希望得到的輸出時鍾時序如下:

因此只需要將counter以clk_in為時鍾驅動計數,當counter = (N-1)時,clk_out翻轉即可。
verilog代碼如下,其中WIDTH為(N的位寬-1):
module time_adv_even #(
parameter N = 2,
WIDTH = 7
)
(
input clk,
input rst,
output reg clk_out
);
reg [WIDTH:0]counter;
always @(posedge clk or posedge rst) begin
if (rst) begin
// reset
counter <= 0;
end
else if (counter == N-1) begin
counter <= 0;
end
else begin
counter <= counter + 1;
end
end
always @(posedge clk or posedge rst) begin
if (rst) begin
// reset
clk_out <= 0;
end
else if (counter == N-1) begin
clk_out <= !clk_out;
end
end
endmodule
testbench測試8分頻即N=4,ISE仿真結果如下:

2.奇分頻模塊設計
奇分頻需要通過兩個時鍾共同得到。首先得到分頻系數M和計數器值N。
M = 時鍾輸入頻率 / 時鍾輸出頻率
N = (M-1) / 2
如輸入時鍾為50M,輸出時鍾為10M,則M=5,N=2。奇分頻則意味着M為奇數。
以M=5,N=2為例,我們希望得到的輸出時鍾時序如下:

其中clk_out為最終輸出時鍾,clk_out1和clk_out2為輔助時鍾生成。
計數器counter由0技術至(M-1)。
clk_out1在在clk_in的上升延跳變,條件是counter==(N-1)或(M-1)。
clk_out2在在clk_in的下降延跳變,條件是counter==(N-1)或(M-1)。
之后clk_out = clk_out1 & clk_out2即可得到M分頻的時鍾。
verilog代碼如下,其中WIDTH為(N的位寬-1):
module time_adv_odd #(
parameter N = 2,
WIDTH = 7
)(
input clk,
input rst,
output clk_out
);
reg [WIDTH:0]counter;
always @(posedge clk or posedge rst) begin
if (rst) begin
// reset
counter <= 0;
end
else if (counter == (N << 1)) begin
counter <= 0;
end
else begin
counter <= counter + 1;
end
end
reg clk_out1;
always @(posedge clk or posedge rst) begin
if (rst) begin
// reset
clk_out1 <= 0;
end
else if (counter == N-1) begin
clk_out1 <= !clk_out1;
end
else if (counter == (N << 1)) begin
clk_out1 <= !clk_out1;
end
end
reg clk_out2;
always @(negedge clk or posedge rst) begin
if (rst) begin
// reset
clk_out2 <= 0;
end
else if (counter == N-1) begin
clk_out2 <= !clk_out2;
end
else if (counter == (N << 1)) begin
clk_out2 <= !clk_out2;
end
end
assign clk_out = clk_out1 & clk_out2;
endmodule
testbench測試9分頻即N=4,ISE仿真結果如下:

3.半分頻模塊設計
半分頻即2.5分頻等,設計最為復雜。首先得到分頻系數M:
M = 時鍾輸入頻率 / 時鍾輸出頻率
如輸入為50M,輸入為20M則分頻系數為2.5。此次設計未能完成占空比為50%的半分頻。
以M=2.5為例,我們希望得到的輸出時鍾時序如下:

可以看出輸出時鍾的兩個上升沿之間為2.5個輸入時鍾周期。
設計的關鍵在於信號維持半個周期的處理,因此引入了輔助信號clk_cnt,clk_vld。
clk_vld信號受到clk_out的驅動,檢測到clk_out的上升沿時,信號翻轉。
clk_cnt信號受到clk_in驅動,受clk_vld控制,當clk_vld==0時,clk_cnt = clk_in;當clk_vld==1時,clk_cnt = !clk_in。
counter信號受clk_cnt驅動,計數(M-0.5)時歸零。
clk_out信號受clk_cnt驅動,當counter == (M-1.5)或counter == (M-0.5)時翻轉。(此處在仿真時做了改變,見下方)
M=2.5時,時序分析如下:
第一步:reset之后,clk_vld==0,clk_cnt = clk_in,counter由0開始計數;
第二步:counter == 1時,clk_out在clk_cnt的上升沿處跳變為1,引起clk_vld->1,進而clk_cnt = !clk_in,這意味着clk_cnt立即由1歸為0;
第三步:半個周期后,clk_cnt 在此迎來上升沿,此時counter == 2,clk_out在clk_cnt的上升沿處跳變為0,counter也歸0;(實現了信號維持半個周期)
第四步:繼續正常計數,counter == 1時,clk_out在clk_cnt的上升沿處跳變為1,引起clk_vld->0,進而clk_cnt = clk_in,這意味着clk_cnt立即由1歸為0;
第五步:重復至第一步。

然而在有一次的電路仿真中,可能受到仿真工具時鍾采樣影響,調整為
當counter == (M-0.5)或counter == 0時翻轉
實現了正確的時鍾分頻。因此該值可能需要根據工具和開發板調整,或者說這樣的設計是不可靠的,當然了如果需要半分頻時候最好還是通過pll實現吧。
理論上是當counter == (M-0.5)或counter == (M-1.5)時翻轉沒錯,給出的代碼和波形也是這樣的。
verilog代碼如下,參數M實際為分頻系數-0.5(即3.5->3),WIDTH為(M的位寬-1):
module time_adv_half #(
parameter M = 2,
WIDTH = 7
)(
input clk,
input rst,
output reg clk_out
);
wire clk_cnt;
assign clk_cnt = (clk_vld) ? !clk : clk;
reg [WIDTH : 0]counter;
always @(posedge clk_cnt or posedge rst) begin
if (rst) begin
// reset
counter <= 0;
end
else if (counter == M) begin
counter <= 0;
end
else begin
counter <= counter + 1;
end
end
reg clk_vld;
always @(posedge clk_out or posedge rst) begin
if (rst) begin
// reset
clk_vld <= 0;
end
else begin
clk_vld <= !clk_vld;
end
end
always @(posedge clk_cnt or posedge rst) begin
if (rst) begin
// reset
clk_out <= 0;
end
else if (counter == M-1) begin
clk_out <= !clk_out;
end
else if (counter == M) begin
clk_out <= !clk_out;
end
end
endmodule
M=3時候的仿真波形如下:

可以看出clk_out兩個上升沿之間為3.5個輸入時鍾周期。
附:testbench
`timescale 1 ns / 1 ps
module TEST_gate;
reg clk, rst;
wire clk_out_even, clk_out_odd, clk_out_half;
initial begin
clk = 1'b0;
forever #10 clk = ~clk;
end
initial begin
rst = 1'b0;
#2 rst = 1'b1;
#9 rst = 1'b0;
end
time_adv_even #(
.N(4)
,.WIDTH(5)
)u0
(
.clk (clk)
,.rst (rst)
,.clk_out (clk_out_even)
);
time_adv_odd #(
.N(4)
,.WIDTH(5)
)u1
(
.clk (clk)
,.rst (rst)
,.clk_out (clk_out_odd)
);
time_adv_half #(
.M(3)
,.WIDTH(5)
)u2
(
.clk (clk)
,.rst (rst)
,.clk_out (clk_out_half)
);
endmodule
波形
