
歡迎大家關注“數字IC劍指offer”,分享秋招實用幹貨
在數字IC秋招過程中我們發現,不論是在筆試還是面試中,針對數字邏輯電路基礎知識的考察還是不少的,其中就包括一些常用電路的分析、設計以及Verilog代碼實作。從本期開始,數字前端版塊開始推出”常用電路設計“專題,本期将詳細介紹分頻電路的設計。
分頻電路是數字電路中常見的邏輯電路類型。在時序邏輯電路中,時鐘是必不可少的,但對于時鐘要求不高的基本設計,自行設計的分頻電路,也就是時鐘分頻器,有時候比采用外部PLL更為簡單、有效、快速。
本期目錄: 1、偶數分頻 (1) 用D觸發器級聯實作 (2) 用計數器實作 2、奇數分頻 (1) 占空比非50%的奇數分頻 用Moore狀态機實作 用計數器實作 (2) 占空比50%的奇數分頻 (3) 利用基本邏輯單元直接搭建占空比為50%的奇數分頻電路 3、小數/分數分頻1、偶數分頻
(1)用D觸發器級聯實作将主時鐘以2為幂次進行分割可以得到同步偶數分頻時鐘,即21,22,23...分頻。電路上可采用D觸發器實作,n個觸發器可以構成2n次偶數分頻。如圖1所示,為2分頻、4分頻電路設計及波形。
(a)2分頻電路及波形
(b)4分頻電路及波形
圖1 偶數分頻電路設計
(2)用計數器實作用D觸發器級聯搭建分頻電路隻能實作2,4,8,16等分頻,對于一般的偶數分頻,可以通過計數器實作:若要實作N分頻(N為偶數),隻需将計數器在待分頻時鐘上升沿觸發下循環計數,從0計數到(N/2 -1)後将輸出時鐘翻轉即可實作。Verilog代碼如下:
// N分頻器,N為偶數,偶數分頻,占空比為50%
module Even_Freq_Div_N (
input clk_in,
input rst_n,
output reg clk_out
);
reg [3:0] cnt;
parameter N = 6;
always @(posedge clk_in or negedge rst_n)
begin
if(!rst_n)
begin
cnt <= 4'b0000;
clk_out <= 1'b0;
end
else if (cnt == (N/2-1))
begin
clk_out <= ~clk_out;
cnt <= 4'b0000;
end
else
cnt <= cnt + 1'b1;
end
endmodule
上述代碼實作的是占空比為50%的6分頻電路,若實作占空比為50%的其他偶數分頻,隻需修改參數N及cnt的位寬即可。
2、奇數分頻
(1)占空比非50%的奇數分頻 ① 用Moore狀态機實作以7分頻為例,通過如圖2所示Moore狀态機即可實作輸入時鐘的7分頻。
圖2 7分頻電路Moore狀态機
值得注意的是,上圖實作的7分頻的占空比并非50%。具體的Verilog如下:
//N分頻器,N為奇數,奇數分頻,占空比非50%
module Odd_Freq_Div_N (
input clk_in,
input rst_n,
output clk_out
);
parameter State_0 = 3'b000;
parameter State_1 = 3'b001;
parameter State_2 = 3'b010;
parameter State_3 = 3'b011;
parameter State_4 = 3'b100;
parameter State_5 = 3'b101;
parameter State_6 = 3'b110;
reg [2:0] CS,NS;
always @(posedge clk_in or negedge rst_n)
begin
if(!rst_n)
CS <= State_0;
else
CS <= NS;
end
always @(*)
begin
if(!rstn)
NS = State_0;
else
begin
case(CS)
State_0: NS = State_1;
State_1: NS = State_2;
State_2: NS = State_3;
State_3: NS = State_4;
State_4: NS = State_5;
State_5: NS = State_6;
State_6: NS = State_0;
default: NS = State_0;
endcase
end
end
always @(posedge clk_in or negedge rst_n)
begin
if(!rst_n)
clk_out <= 1'b0;
else
begin
case(NS)
State_0: clk_out <= 1'b0;
State_1: clk_out <= 1'b0;
State_2: clk_out <= 1'b0;
State_3: clk_out <= 1'b0;
State_4: clk_out <= 1'b1;
State_5: clk_out <= 1'b1;
State_6: clk_out <= 1'b1;
default: clk_out <= 1'b0;
endcase
end
end
endmodule
② 用計數器實作 其實用狀态機實作占空比非50%的分頻電路還是比較麻煩的,可以采用計數器的方法:若要實作N分頻(N為奇數),隻需将計數器在待分頻時鐘上升沿觸發下循環計數,從0計數到(N-1)後計數器清零。當計數到(N-1)/2後将輸出時鐘翻轉,計數到0後再次翻轉即可實作。7分頻的Verilog代碼如下:
//N分頻器,N為奇數,奇數分頻,占空比非50%
module Odd_Freq_Div_N (
input clk_in,
input rst_n,
output clk_out
);
parameter N = 7;
reg [2:0] cnt_p;
reg clk_p;
always @(posedge clk_in or negedge rst_n)
begin
if(!rst_n)
cnt_p <= 3'b000;
else if (cnt_p == N-1)
cnt_p <= 3'b000;
else
cnt_p <= cnt_p + 1'b1;
end
always @(posedge clk_in or negedge rst_n)
begin
if(!rst_n)
clk_p <= 1'b0;
else if (cnt_p == (N-1)/2)
clk_p <= ~clk_p;
else if (cnt_p <= 3'b000)
clk_p <= ~clk_p;
else
clk_p <= clk_p;
end
endmodule
不管是用狀态機實作,還是用計數器實作,本質上都是控制輸出時鐘在每N個待分頻時鐘周期内完成一次周期性輸出。
(2)占空比50%的奇數分頻對于奇數分頻,就是分别利用待分頻時鐘的上升沿觸發生成一個時鐘,然後用下降沿觸發生成另一個時鐘,然後将兩個時鐘信号進行或/與運算得到占空比為50%的奇數分頻。上面一小節已經介紹了如何利用待分頻時鐘的上升沿觸發生成占空比非50%的時鐘,隻需再類似地利用待分頻時鐘的下降沿觸發生成占空比非50%的時鐘。具體方法詳述如下:
① 設計2個分别用上升、下降沿觸發的計數器定義2個計數器cnt_p和cnt_n,分别利用時鐘的上升沿和下降進行觸發計數
② 利用上升、下降沿計數器生成兩個分頻時鐘clk_p和clk_n定義2個時鐘信号clk_p和clk_n, 對于上升沿計數器cnt_p,當計數到0或者(N-1)/2時,均翻轉clk_p信号;對于下降沿計數器cnt_n,當計數到0或者(N-1)/2時,均翻轉clk_n信号。
③ 利用clk_p和clk_n通過邏輯運算生成占空比為50%的分頻時鐘若clk_p和clk_n初始複位為0,将2個時鐘clk_p和clk_n通過或運算即可生成占空比為50%的分頻時鐘,且clk_out上升沿和原時鐘上升沿對齊。
讀者可以思考下,如果clk_p和clk_n初始複位為1,需要使用什麼邏輯運算才能生成占空比50%的分頻時鐘?
占空比為50%的奇數分頻(以7分頻為例)的Verilog代碼如下:
//N分頻器,N為奇數,奇數分頻,占空比50%
module Odd_Freq_Div_N (
input clk_in,
input rst_n,
output clk_out
);
parameter N = 7;
reg [3:0] cnt_p, cnt_n;
reg clk_p, clk_n;
always @(posedge clk_in or negedge rst_n)
begin
if(!rst_n)
cnt_p <= 4'b0000;
else if (cnt_p == N-1)
cnt_p <= 4'b0000;
else
cnt_p <= cnt_p + 1'b1;
end
always @(negedge clk_in or negedge rst_n)
begin
if(!rst_n)
cnt_n <= 4'b0000;
else if (cnt_n == N-1)
cnt_n <= 4'b0000;
else
cnt_n <= cnt_n + 1'b1;
end
always @(posedge clk_in or negedge rst_n)
begin
if(!rst_n)
clk_p <= 1'b0;
else if (cnt_p == (N-1)/2)
clk_p <= ~clk_p;
else if (cnt_p <= 4'b0000)
clk_p <= ~clk_p;
else
clk_p <= clk_p;
end
always @(negedge clk_in or negedge rst_n)
begin
if(!rst_n)
clk_n <= 1'b0;
else if (cnt_n == (N-1)/2)
clk_n <= ~clk_n;
else if (cnt_n == 4'b0000)
clk_n <= ~clk_n;
else
clk_n <= clk_n;
end
assign clk_out = clk_p | clk_n;
endmodule
詳細的波形圖如圖3所示:
圖3 占空比為50%的7分頻電路波形生成
還有一種更簡便的方法隻需要一個計數器cnt_p就可以,通過cnt_p産生clk_p時鐘,然後直接用待分頻時鐘下降沿對時鐘clk_p打半拍得到時鐘clk_n,最後将clk_p和clk_n相或就可以得到占空比為50%的7分頻時鐘。
(3)利用基本邏輯單元直接搭建占空比為50%的奇數分頻電路上面我們從波形生成方法及Verilog實作的角度思考了如何設計占空比為50%的奇數分頻電路,這裡我們直接從電路角度出發進行設計。
首先思考如何用D觸發器群組合邏輯實作占空比為50%的三分頻電路?
思路:先使用觸發器構成序列生成器,輸出001循環脈沖,實作占空比非50%的三分頻,然後用負沿觸發器打一拍,再相或。由于001循環共三個狀态,故需2個D觸發器。通過列狀态表、畫卡諾圖,得到由兩個D觸發器及邏輯門構成的001序列生成器,後接負沿觸發器打一拍并将其輸出與序列生成器的輸出相或,即得到占空比為50%的三分頻電路。
下面貼出三分頻和五分頻電路的簡略設計思路及過程僅供參考,如圖4、圖5所示。
圖4 三分頻電路設計思路及過程
圖5 五分頻電路設計思路及過程
3、小數/分數分頻
小數分頻電路可以轉化為特定分頻比電路設計問題。如19/9分頻,意味着在輸入時鐘clk_in的19個周期内,輸出需産生9個脈沖。因為19/9 = 2.11..., 是以可以用2分頻和3分頻配合實作,設待分頻時鐘的19個周期内共有x個二分頻時鐘周期,y個三分頻時鐘周期,則有:
x+y=9
2x+3y=19
解得x=8,y=1。即隻要在待分頻時鐘的19個周期内控制輸出8個二分頻時鐘周期和1個三分頻時鐘周期即可。具體代碼思路:
1)首先一個總的計數器,在0-18循環;
2)其次設計兩個分别生成2分頻和3分頻的計數器,根據總計數器的數值範圍分别在0-1和0-2循環;
3)最後是波形生成邏輯,根據總計數器和2、3分頻計數器的數值控制輸出脈沖翻轉生成期望分頻比的時鐘。
19/9的分頻電路代碼如下,其餘分頻比也可參考:
// M/N小數分頻,M為分子,N為分母
module Dec_Freq_Div_M_N(
input clk,
input rstn,
output clk_out
);
reg [5:0] cnt;
reg [3:0] cnt_a;
reg [3:0] cnt_b;
reg clk_out_reg;
assign clk_out = clk_out_reg;
// div_a和div_b分别為根據前述公式計算出來的基準分頻系數
// change為2、3分頻時鐘的切換點
parameter M = 6'd19;
parameter change = 6'd16;
parameter div_a = 5'd2;
parameter div_b = 5'd3;
//總計數器
always @(posedge clk or negedge rstn) begin
if(!rstn)
cnt <= 6'b0;
else begin
if(cnt == M - 1'b1)
cnt <= 6'b0;
else
cnt <= cnt + 1'b1;
end
end
//産生2、3分頻的計數器
always @(posedge clk or negedge rstn) begin
if(!rstn) begin
cnt_a <= 4'b0;
cnt_b <= 4'b0;
end
else if(cnt<=change-1'b1) begin
cnt_b <= 4'd0;
if(cnt_a == div_a - 1'b1)
cnt_a <= 4'd0;
else
cnt_a <= cnt_a + 1'b1;
end
else if(cnt>change-1'b1) begin
cnt_a <= 4'd0;
if(cnt_b == div_b - 1'b1)
cnt_b <= 4'd0;
else
cnt_b <= cnt_b + 1'b1;
end
//輸出時鐘産生邏輯
always @(posedge clk or negedge rstn) begin
if(!rstn)
clk_out_reg <= 1'b0;
else if(cnt<change) begin
if(cnt_a == 4'd0 || cnt_a == div_a/2)
clk_out_reg <= ~clk_out_reg;
else
clk_out_reg <= clk_out_reg;
end
else if(cnt>=change) begin
if(cnt_b == 4'd0 || cnt_b == (div_b - 1'b1)/2)
clk_out_reg <= ~clk_out_reg;
else
clk_out_reg <= clk_out_reg;
end
end
endmodule