初學 Verilog 時許多模塊都是由計數器與分頻器組成的,例如 PWM 脈寬調制、頻率計等。分頻邏輯也往往通過計數邏輯完成。本節(jié)主要對偶數分頻、奇數分頻、半整數分頻以及小數分頻進行簡單的總結。
采用觸發(fā)器反向輸出端連接到輸入端的方式,可構成簡單的 2 分頻電路。
以此為基礎進行級聯,可構成 4 分頻,8 分頻電路。
電路實現如下圖所示,用 Verilog 描述時只需使用簡單的取反邏輯即可。
如果偶數分頻系數過大,就需要對分頻系數 N 循環(huán)計數進行分頻。在計數周期達到分頻系數中間數值 N/2 時進行時鐘翻轉,可保證分頻后時鐘的占空比為 50%。因為是偶數分頻,也可以對分頻系數中間數值 N/2 進行循環(huán)計數。
偶數分頻的 Verilog 描述舉例如下:
module even_divisor
# (parameter DIV_CLK = 10 )
(
input rstn ,
input clk,
output clk_div2,
output clk_div4,
output clk_div10
);
//2 分頻
reg clk_div2_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
clk_div2_r <= 'b0 ;
end
else begin
clk_div2_r <= ~clk_div2_r ;
end
end
assign clk_div2 = clk_div2_r ;
//4 分頻
reg clk_div4_r ;
always @(posedge clk_div2 or negedge rstn) begin
if (!rstn) begin
clk_div4_r <= 'b0 ;
end
else begin
clk_div4_r <= ~clk_div4_r ;
end
end
assign clk_div4 = clk_div4_r ;
//N/2 計數
reg [3:0] cnt ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
cnt <= 'b0 ;
end
else if (cnt == (DIV_CLK/2)-1) begin
cnt <= 'b0 ;
end
else begin
cnt <= cnt + 1'b1 ;
end
end
//輸出時鐘
reg clk_div10_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
clk_div10_r <= 1'b0 ;
end
else if (cnt == (DIV_CLK/2)-1 ) begin
clk_div10_r <= ~clk_div10_r ;
end
end
assign clk_div10 = clk_div10_r ;
endmodule
testbench 中只需給入激勵時鐘等信號即可,這里不再列出。
仿真結果如下:
奇數分頻如果不要求占空比為 50%,可按照偶數分頻的方法進行分頻。即計數器對分頻系數 N 進行循環(huán)計算,然后根據計數值選擇一定的占空比輸出分頻時鐘。
如果奇數分頻輸出時鐘的高低電平只差一個 cycle ,則可以利用源時鐘雙邊沿特性并采用"與操作"或"或操作"的方式將分頻時鐘占空比調整到 50%。
采用"或操作"產生占空比為 50% 的 3 分頻時序圖如下所示。
利用源時鐘上升沿分頻出高電平為 1 個 cycle、低電平為 2 個 cycle 的 3 分頻時鐘。
利用源時鐘下降沿分頻出高電平為 1 個 cycle、低電平為 2 個 cycle 的 3 分拼時鐘。
兩個 3 分頻時鐘應該在計數器相同數值、不同邊沿下產生,相位差為半個時鐘周期。然后將 2 個時鐘進行"或操作",便可以得到占空比為 50% 的 3 分頻時鐘。
同理,9 分頻時,則需要在上升沿和下降沿分別產生 4 個高電平、5 個低電平的 9 分頻時鐘,然后再對兩個時鐘做"或操作"即可。Verilog 描述如下:
module odo_div_or
#(parameter DIV_CLK = 9)
(
input rstn ,
input clk,
output clk_div9
);
//計數器
reg [3:0] cnt ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
cnt <= 'b0 ;
end
else if (cnt == DIV_CLK-1) begin
cnt <= 'b0 ;
end
else begin
cnt <= cnt + 1'b1 ;
end
end
//在上升沿產生9分頻
reg clkp_div9_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
clkp_div9_r <= 1'b0 ;
end
else if (cnt == (DIV_CLK>>1)-1 ) begin //計數4-8位低電平
clkp_div9_r <= 0 ;
end
else if (cnt == DIV_CLK-1) begin //計數 0-3 為高電平
clkp_div9_r <= 1 ;
end
end
//在下降沿產生9分頻
reg clkn_div9_r ;
always @(negedge clk or negedge rstn) begin
if (!rstn) begin
clkn_div9_r <= 1'b0 ;
end
else if (cnt == (DIV_CLK>>1)-1 ) begin
clkn_div9_r <= 0 ;
end
else if (cnt == DIV_CLK-1) begin
clkn_div9_r <= 1 ;
end
end
//或操作,往往使用基本邏輯單元庫
// or (clk_div9, clkp_div9_r, clkn_div9_r) ;
assign clk_div9 = clkp_div9_r | clkn_div9_r ;
endmodule
仿真結果如下:
采用"與操作"產生占空比為 50% 的 3 分頻時序圖如下所示。
利用源時鐘上升沿分頻出高電平為 2 個 cycle、低電平為 1 個 cycle 的 3 分頻時鐘。
利用源時鐘下降沿分頻出高電平為 2 個 cycle、低電平為 1 個 cycle 的 3 分拼時鐘。
兩個 3 分頻時鐘應該在計數器相同數值、不同邊沿下產生,相位差為半個時鐘周期。然后將 2 個時鐘進行"與操作",便可以得到占空比為 50% 的 3 分頻時鐘。
同理,9 分頻時,則需要在上升沿和下降沿分別產生 5 個高電平、4 個低電平的 9 分頻時鐘,然后再對兩個時鐘做"與操作"即可。Verilog 描述如下:
module odo_div_and
#( parameter DIV_CLK = 9 )
(
input rstn ,
input clk,
output clk_div9
);
//計數器
reg [3:0] cnt ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
cnt <= 'b0 ;
end
else if (cnt == DIV_CLK-1) begin
cnt <= 'b0 ;
end
else begin
cnt <= cnt + 1'b1 ;
end
end
//在上升沿產生9分頻
reg clkp_div9_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
clkp_div9_r <= 1'b0 ;
end
else if (cnt == (DIV_CLK>>1) ) begin //計數5-8位低電平
clkp_div9_r <= 0 ;
end
else if (cnt == DIV_CLK-1) begin //計數 0-4 為高電平
clkp_div9_r <= 1 ;
end
end
//在下降沿產生9分頻
reg clkn_div9_r ;
always @(negedge clk or negedge rstn) begin
if (!rstn) begin
clkn_div9_r <= 1'b0 ;
end
else if (cnt == (DIV_CLK>>1) ) begin
clkn_div9_r <= 0 ;
end
else if (cnt == DIV_CLK-1) begin
clkn_div9_r <= 1 ;
end
end
//與操作,往往使用基本邏輯單元庫
//and (clk_div9, clkp_div9_r, clkn_div9_r) ;
assign clk_div9 = clkp_div9_r & clkn_div9_r ;
endmodule
仿真結果如下:
利用時鐘的雙邊沿邏輯,可以對時鐘進行半整數的分頻。但是無論怎么調整,半整數分頻的占空比不可能是 50%。半整數分頻的方法有很多,這里只介紹一種和奇數分頻調整占空比類似的方法。
3.5 倍時鐘分頻的 Verilog 描述如下:
module half_divisor(
input rstn ,
input clk,
output clk_div3p5
);
//計數器
parameter MUL2_DIV_CLK = 7 ;
reg [3:0] cnt ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
cnt <= 'b0 ;
end
else if (cnt == MUL2_DIV_CLK-1) begin //計數2倍分頻比
cnt <= 'b0 ;
end
else begin
cnt <= cnt + 1'b1 ;
end
end
reg clk_ave_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
clk_ave_r <= 1'b0 ;
end
//first cycle: 4 source clk cycle
else if (cnt == 0) begin
clk_ave_r <= 1 ;
end
//2nd cycle: 3 source clk cycle
else if (cnt == (MUL2_DIV_CLK/2)+1) begin
clk_ave_r <= 1 ;
end
else begin
clk_ave_r <= 0 ;
end
end
//adjust
reg clk_adjust_r ;
always @(negedge clk or negedge rstn) begin
if (!rstn) begin
clk_adjust_r <= 1'b0 ;
end
//本次時鐘只為調整一致的占空比
else if (cnt == 1) begin
clk_adjust_r <= 1 ;
end
//本次時鐘只為調整一致的精確分頻比
else if (cnt == (MUL2_DIV_CLK/2)+1 ) begin
clk_adjust_r <= 1 ;
end
else begin
clk_adjust_r <= 0 ;
end
end
assign clk_div3p5 = clk_adjust_r | clk_ave_r ;
endmodule
仿真結果如下:
不規(guī)整的小數分頻不能做到分頻后的每個時鐘周期都是源時鐘周期的小數分頻倍,更不能做到分頻后的時鐘占空比均為 50%,因為 Verilog 不能對時鐘進行小數計數。和半整數分頻中第一次分頻時引入的"平均頻率"概念類似,小數分頻也是基于可變分頻和多次平均的方法實現的。
例如進行 7.6 倍分頻,則保證源時鐘 76 個周期的時間等于分頻時鐘 10 個周期的時間即可。此時需要在 76 個源時鐘周期內進行 6 次 8 分頻,4 次 7 分頻。再例如進行 5.76 分頻,需要在 576 個源時鐘周期內進行 76 次 6 分頻,24 次 5 分頻。
下面闡述下這些分頻參數的計算過程。
當進行 7 分頻時,可以理解為 70 個源時鐘周期內進行 10 次 7 分頻。在 76 個源時鐘周期內仍然進行 10 次分頻,相當于將多余的 6 個源時鐘周期增加、分配到 70 個源時鐘周期內,即完成了 7.6 倍分頻操作。分頻過程中必然有 6 個分頻時鐘是 8 分頻得到的,剩下的 4 個分頻時鐘則仍然會保持原有的 7 分頻狀態(tài)。
很多地方給出了計算這些分頻參數的兩元一次方程組,如下所示。其原理和上述分析一致,建議掌握上述計算過程。
7N + 8M = 76
N + M = 10
其中 7 為整數分頻,N 整數分頻的次數;8 與 M 為整數分頻加一后的分頻系數及其分頻次數。
以 7.6 倍分頻為例,7 分頻和 8 分頻的實現順序一般有以下 4 種:
前兩種方法時鐘頻率不均勻,相位抖動較大,所以一般會采用后兩種平均插入的方法進行小數分頻操作。
平均插入可以通過分頻次數差累計的方法實現,7.6 分頻的實現過程如下:
以此類推,完成將 6 次 8 分頻平均插入到 4 次 7 分頻的過程。
下表展示了平均插入法的分頻過程。
分頻次數 | 差值累加 | 差值修改 | 分頻周期 |
---|---|---|---|
1 | 6 | 6 | 7 |
2 | 6+6=12 | 2 | 8 |
3 | 2+6=8 | 8 | 7 |
4 | 8+6=14 | 4 | 8 |
5 | 4+6=10 | 0 | 8 |
6 | 6 | 6 | 7 |
7 | 6+6=12 | 2 | 8 |
8 | 2+6=8 | 8 | 7 |
9 | 8+6=14 | 4 | 8 |
10 | 4+6=10 | 0 | 8 |
基于上述小數分頻實現方法的 Verilog 描述如下:
module frac_divisor
#(
parameter SOURCE_NUM = 76 , //cycles in source clock
parameter DEST_NUM = 10 //cycles in destination clock
)
(
input rstn ,
input clk,
output clk_frac
);
//7分頻參數、8分頻參數、次數差值
parameter SOURCE_DIV = SOURCE_NUM/DEST_NUM ;
parameter DEST_DIV = SOURCE_DIV + 1;
parameter DIFF_ACC = SOURCE_NUM - SOURCE_DIV*DEST_NUM ;
reg [3:0] cnt_end_r ; //可變分頻周期
reg [3:0] main_cnt ; //主計數器
reg clk_frac_r ; //時鐘輸出,高電平周期數為1
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
main_cnt <= 'b0 ;
clk_frac_r <= 1'b0 ;
end
else if (main_cnt == cnt_end_r) begin
main_cnt <= 'b0 ;
clk_frac_r <= 1'b1 ;
end
else begin
main_cnt <= main_cnt + 1'b1 ;
clk_frac_r <= 1'b0 ;
end
end
//輸出時鐘
assign clk_frac = clk_frac_r ;
//差值累加器使能控制
wire diff_cnt_en = main_cnt == cnt_end_r ;
//差值累加器邏輯
reg [4:0] diff_cnt_r ;
wire [4:0] diff_cnt = diff_cnt_r >= DEST_NUM ?
diff_cnt_r -10 + DIFF_ACC :
diff_cnt_r + DIFF_ACC ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
diff_cnt_r <= 0 ;
end
else if (diff_cnt_en) begin
diff_cnt_r <= diff_cnt ;
end
end
//分頻周期變量的控制邏輯
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
cnt_end_r <= SOURCE_DIV-1 ;
end
//差值累加器溢出時,修改分頻周期
else if (diff_cnt >= 10) begin
cnt_end_r <= DEST_DIV-1 ;
end
else begin
cnt_end_r <= SOURCE_DIV-1 ;
end
end
endmodule
仿真結果如下:
偶數分頻不使用時鐘雙邊沿邏輯即可完成占空比為 50% 的時鐘分頻,是最理想的分頻狀況。
奇數分頻如果要產生 50% 占空比的分頻時鐘,則需要使用時鐘的雙邊沿邏輯。如果不要求占空比的話,實現方法和偶數分頻類似。
半整數分頻屬于特殊的小數分頻,可以用雙邊沿邏輯進行設計。通過一定邏輯將兩個雙邊沿時鐘信號整合為最后的一路輸出時鐘時,建議不要使用選擇邏輯。因為容易出現毛刺現象,電路中又會增加一定的不確定性。例如下面描述是不建議的。
assign clk_div3p5 = (cnt == 1 || cnt ==2) ? clk_ave_r
: clk_adjust_r ;
小數分頻的基本思想是,輸出時鐘在一段時間內的平均頻率達到分頻要求即可。但是考慮到相位抖動,還需要對分頻系數變化的分頻邏輯進行平均操作。
點擊這里下載源碼
更多建議: