分頻概念
分頻就是生成一個(gè)新時(shí)鐘,該新時(shí)鐘的頻率是原有時(shí)鐘頻率的整數(shù)分之一倍,新周期是原有周期的整數(shù)倍。
再簡(jiǎn)單來(lái)說(shuō),讓你手撕一個(gè)四分頻電路,就是寫(xiě)代碼生成一個(gè)周期是原來(lái)四倍的時(shí)鐘,如果手撕一個(gè)三分頻電路,就是寫(xiě)代碼生成一個(gè)周期是原來(lái)三倍的時(shí)鐘。
如圖為四分頻波形圖,clk_out的頻率是clk的1/4,但周期是clk的4倍。
分頻主要分為偶數(shù)分頻、奇數(shù)分頻、小數(shù)分頻。
偶數(shù)分頻
二分頻
二分頻引入,在每個(gè)時(shí)鐘上升沿來(lái)到時(shí),翻轉(zhuǎn)新時(shí)鐘
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
clk_2 <= 0;
else
clk_2 <= ~clk_2;
end
得到的結(jié)果如下:
任意偶數(shù)
代碼:
module div
#(
parameter num = 8
)
(
input clk,
input rst_n,
output reg clk_out
);
reg [2:0]cnt;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
clk_out <= 0;
end
else if(cnt == num/2 -1)begin
cnt <= 0;
clk_out <= ~clk_out;
end
else
cnt <= cnt + 1'b1;
end
endmodule
Testbench
module div_tb();
reg clk;
reg rst_n;
wire clk_out;
div u0(
.clk(clk),
.rst_n(rst_n),
.clk_out(clk_out)
);
always #10 clk = ~clk;
initial begin
clk = 0;
rst_n = 0;
#15
rst_n = 1;
#50000
$stop;
end
endmodule
仿真結(jié)果:
每4各clk,clk_out翻轉(zhuǎn)一次,即8個(gè)clk周期的為clk_out的一個(gè)周期。
在Testbench中使用defparam語(yǔ)句覆蓋原始rtl中的num初始值,改為4分頻。
defparam num = 4;
結(jié)果為:
占空比問(wèn)題
上面寫(xiě)的任意偶數(shù)分頻代碼的占空比都是50%,實(shí)際上面試手撕代碼不會(huì)讓你50%占空比
方法:==進(jìn)行計(jì)數(shù),對(duì)于一個(gè)八分頻,開(kāi)始就把時(shí)鐘設(shè)為高電平,我用cnt 計(jì)數(shù)到兩個(gè)時(shí)鐘上升沿后再把它拉低,計(jì)數(shù)到7后cnt 拉低 時(shí)鐘拉高,這樣就實(shí)現(xiàn)了兩個(gè)周期高,六個(gè)周期低,占空比為2/8 即 25% 的八分頻。 ==
奇分頻
任意奇數(shù)分頻代碼:
module div_2
#(
parameter N = 7
)
(
input clk,
input rst_n,
output reg clk_out
);
reg [2:0] cnt;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 3'd0;
else
cnt <= (cnt == (N-1))?3'd0:cnt + 1'd1;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
clk_out <= 1'b0;
else if((cnt == N-1)|(cnt == (N-1)/2))
clk_out <= ~clk_out;
else
clk_out <= clk_out;
end
endmodule
Testbench:
`timescale 1ns/1ns
module div_2_tb();
reg clk;
reg rst_n;
wire clk_out;
div_2 inst(
.clk(clk),
.rst_n(rst_n),
.clk_out(clk_out)
);
always #10 clk = ~clk;
initial begin
clk = 0;
rst_n = 0;
#15
rst_n = 1;
#3000
$stop;
end
endmodule
仿真結(jié)果:
在cnt = 4 和cnt = 6的時(shí)候?qū)lk_out翻轉(zhuǎn),可以看到每經(jīng)過(guò)7個(gè)clk,生成一個(gè)clk_out完整周期。但是計(jì)算clk_out的占空比可得為3/7,并不是規(guī)整的50%占空比。
實(shí)現(xiàn)50%占空代碼:
module div_2
#(
parameter N = 7
)
(
input clk,
input rst_n,
output wire clk_out
);
reg [2:0] cnt_pos;
reg [2:0] cnt_neg;
reg clk_pos,clk_neg;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_pos <= 3'd0;
else
cnt_pos <= (cnt_pos == (N-1))?3'd0:cnt_pos + 1'd1;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
clk_pos <= 1'b0;
else if((cnt_pos == N-1)|(cnt_pos == (N-1)/2))
clk_pos <= ~clk_pos;
else
clk_pos <= clk_pos;
end
always@(negedge clk or negedge rst_n)begin
if(!rst_n)
cnt_neg <= 3'd0;
else
cnt_neg <= (cnt_neg == (N-1))?3'd0:cnt_neg + 1'd1;
end
always@(negedge clk or negedge rst_n)begin
if(!rst_n)
clk_neg <= 1'b0;
else if((cnt_neg == N-1)|(cnt_neg == (N-1)/2))
clk_neg <= ~clk_neg;
else
clk_neg <= clk_neg;
end
assign clk_out = clk_pos | clk_neg;
endmodule
Testbench:
`timescale 1ns/1ns
module div_2_tb();
reg clk;
reg rst_n;
wire clk_out;
div_2 inst(
.clk(clk),
.rst_n(rst_n),
.clk_out(clk_out)
);
always #10 clk = ~clk;
initial begin
clk = 0;
rst_n = 0;
#15
rst_n = 1;
#3000
$stop;
end
endmodule
仿真結(jié)果:
非常規(guī)占空比的奇分頻
非常規(guī)占空比的奇數(shù)分頻器,比如 3/10占空比的五分頻, 5/18占空比的九分頻?
與上面實(shí)現(xiàn)50%任意奇數(shù)分頻器的原理是類似的,但是采用與運(yùn)算。用占空比為 2/5 上升沿采樣的信號(hào)和 2/5占空比下降沿采樣的信號(hào)相與。
所以相與后,占空比就為 2/5 - 1/10 = 3/10 ,示意圖如下:
5/18占空比的九分頻就是用上升沿采樣的3/9占空比九分頻 和下降沿采樣的3/9占空比九分頻相與,最后結(jié)果為3/9-1/18 = 5/18 占空比。具體修改只需要改cnt判斷數(shù)值以及把clk_out 的賦值從clk_out1,clk_out2相或改成相與。
代碼參考:verilog代碼
分頻時(shí)鐘的使用
在其他模塊利用分頻時(shí)鐘時(shí),分頻得到的時(shí)鐘并未連接到FPGA內(nèi)部的全局時(shí)鐘樹(shù),因此使用分頻時(shí)鐘的模塊與其他使用系統(tǒng)時(shí)鐘clk的模塊存在時(shí)鐘到達(dá)時(shí)間的偏差,而在設(shè)計(jì)過(guò)程中,我們希望的使各個(gè)模塊的時(shí)鐘達(dá)到時(shí)間使相近的,減少時(shí)序問(wèn)題。
在一些低速系統(tǒng)或模塊中,使用分頻時(shí)鐘出現(xiàn)問(wèn)題的概率較低,但在高速系統(tǒng)和模塊中,使用分頻時(shí)鐘就會(huì)容易出現(xiàn)問(wèn)題,導(dǎo)致各模塊的時(shí)鐘到達(dá)時(shí)間存在較大的偏差,為解決這個(gè)問(wèn)題,分頻時(shí)生成脈沖時(shí)鐘。
以7分頻為例:
module div_3
#(
parameter N = 7
)
(
input clk,
input rst_n,
output reg clk_flag
);
reg [2:0] cnt;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 3'd0;
else
cnt <= (cnt == (N-1))?3'd0:cnt + 1'd1;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
clk_flag <= 1'b0;
else if(cnt == N-2)
clk_flag <= 1'b1;
else
clk_flag <= 1'b0;
end
endmodule
Testbench與上面一樣
`timescale 1ns/1ns
module div_3_tb();
reg clk;
reg rst_n;
wire clk_flag;
div_3 inst(
.clk(clk),
.rst_n(rst_n),
.clk_flag(clk_flag)
);
always #10 clk = ~clk;
initial begin
clk = 0;
rst_n = 0;
#15
rst_n = 1;
#3000
$stop;
end
endmodule
仿真結(jié)果:
在Testbench中利用defparam對(duì)N進(jìn)行修改,改為4分頻
defparam inst.N = 4;
由此,在利用分頻后的脈沖信號(hào)clk_flag 進(jìn)行判斷和處理,例如:
module div_3
#(
parameter N = 7
)
(
input clk,
input rst_n,
output reg clk_out,
output reg clk_flag
);
reg [2:0] cnt;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 3'd0;
else
cnt <= (cnt == (N-1))?3'd0:cnt + 1'd1;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
clk_out <= 1'b0;
else if((cnt == N-1)|(cnt == (N-1)/2))
clk_out <= ~clk_out;
else
clk_out <= clk_out;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
clk_flag <= 1'b0;
else if(cnt == N-2)
clk_flag <= 1'b1;
else
clk_flag <= 1'b0;
end
reg [2:0] a,b;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
a <= 3'b0;
else if(clk_flag == 1)
a <= a + 1'b1;
end
always@(posedge clk_out or negedge rst_n)begin
if(!rst_n)
b <= 3'b0;
else
b <= a + 1'b1;
end
endmodule
其中a計(jì)數(shù)器是由clk_flag控制的,b計(jì)數(shù)器是由clk_out控制的,而本質(zhì)上clk_flag是由系統(tǒng)時(shí)鐘clk進(jìn)行控制的,因此a由系統(tǒng)時(shí)鐘clk控制,與其他采用系統(tǒng)時(shí)鐘clk的模塊都保持著相同的時(shí)鐘關(guān)系,b由clk_out控制,與系統(tǒng)時(shí)鐘clk存在一定的偏差,因此推薦使用脈沖信號(hào)的寫(xiě)法。
本質(zhì)上來(lái)說(shuō),脈沖信號(hào)clk_flag是降頻寫(xiě)法,無(wú)法對(duì)占空比進(jìn)行設(shè)計(jì),clk_out是分頻寫(xiě)法,達(dá)到的效果是一樣的,但在時(shí)序問(wèn)題上,脈沖信號(hào)clk_flag寫(xiě)法更好!
小數(shù)分頻
編碼小數(shù)分頻,就不能看微觀了,要用宏觀的眼界去看,比如實(shí)現(xiàn)一個(gè) 17/3 分頻,表達(dá)成:17 除以 3 得商為 5 余2。
那么我們就可以通過(guò)5(商)分頻和7(商+余數(shù))來(lái)實(shí)現(xiàn) 17/3 分頻。 確定5分頻和7分頻的次數(shù),設(shè):5分頻的次數(shù)為a , 7分頻的次數(shù)為b,那么應(yīng)該有:
a+b=3(除數(shù))
5a+7b = 17(被除數(shù))
得a=2,b=1,也就是說(shuō)通過(guò)2次5分頻和1次7分頻可得到 17/3 分頻。
宏觀來(lái)看,總共是17個(gè)時(shí)鐘周期,由三個(gè)分頻器均分,那么平均每個(gè)分頻器就是分到17/3了,這就是小數(shù)分頻,這是一個(gè)宏觀的平均概念。
代碼:
module div_M_N(
input clk,
input rst_n,
output reg clk_out
);
reg [4:0] cnt;
reg [2:0] cnt1;
reg [2:0] cnt2;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 5'd0;
else
cnt <= (cnt == 5'd16)?5'd0:cnt + 1'b1;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt1 <= 3'd0;
cnt2 <= 3'd0;
clk_out <= 1'b0;
end
else if(cnt < 5'd10)begin //cnt<5'd10中包含兩個(gè)5分頻。占空比為1/5
if(cnt1 == 3'd4)begin
cnt1 <= 3'd0;
clk_out <= 1'b1;
end
else if(cnt1 == 3'd0)begin
cnt1 <= cnt1 + 1'b1;
clk_out <= 1'b0;
end
else begin
cnt1 <= cnt1 + 1'b1;
end
end
else begin //5'd10=<cnt<=5'd17中包含一個(gè)7分頻。占空比為1/5
if(cnt2 == 3'd6)begin
cnt2 <= 3'd0;
clk_out <= 1'b1;
end
else if(cnt2 == 3'd0)begin
cnt2 <= cnt2 + 1'b1;
clk_out <= 1'd0;
end
else begin
cnt2 <= cnt2 + 1'b1;
end
end
end
endmodule
Testbench:
module div_M_N_tb();
reg clk;
reg rst_n;
wire clk_out;
div_M_N inst(
.clk(clk),
.rst_n(rst_n),
.clk_out(clk_out)
);
always #10 clk = ~clk;
initial begin
clk = 0;
rst_n = 0;
#20
rst_n = 1;
#5000
$stop;
end
endmodule
仿真結(jié)果:
可以看出,兩個(gè)五分頻,一個(gè)七分頻,每十七個(gè)周期循環(huán)一次。即每十七個(gè)周期有三個(gè)分頻器,平攤下來(lái)就是 17/3 。
小數(shù)分頻的缺點(diǎn)就是 占空比不為50%,要想實(shí)現(xiàn)50%占空比的小數(shù)分頻,涉及很多算法,具體算法十分復(fù)雜。
可參考:任意小數(shù)分頻(50%占空比)
8.7分頻verilog代碼:
module div_M_N_2(
input clk,
input rst_n,
output clk_out
);
parameter M_N = 8'd87; //總周期
parameter c89 = 8'd24; //8分頻9分頻切換點(diǎn)
parameter div_e = 5'd8; //偶分頻周期
parameter div_o = 5'd9; //奇分頻周期
reg [7:0] cnt;
reg [3:0] cnt_8;
reg [3:0] cnt_9;
reg clk_out_r;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt <= 8'd0;
else if(cnt == M_N - 1)
cnt <= 8'd0;
else
cnt <= cnt + 1'b1;
end
reg div_flag;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
div_flag <= 1'b0;
else if((cnt == (M_N -1)) | (cnt == (c89 - 1)))
div_flag <= ~div_flag;
else
div_flag <= div_flag;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_8 <= 3'd0;
cnt_9 <= 4'd0;
end
else if(div_flag == 0)begin
cnt_8 <= (cnt_8 == (div_e - 1))?0:cnt_8 + 1'b1;
end
else if(div_flag == 1)begin
cnt_9 <= (cnt_9 == (div_o - 1))?0:cnt_9 + 1'b1;
end
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
clk_out_r <= 1'b0;
else if( ((cnt_8 == 4 | cnt_8 == 0)&&(div_flag == 0)) | ( (cnt_9 == 0 | cnt_9 == 4)&&(div_flag == 1 )) )
clk_out_r <= ~clk_out_r;
end
assign clk_out = clk_out_r;
endmodule
Testbeench:
module div_M_N_tb();
reg clk;
reg rst_n;
wire clk_out;
div_M_N_2 inst(
.clk(clk),
.rst_n(rst_n),
.clk_out(clk_out)
);
always #10 clk = ~clk;
initial begin
clk = 0;
rst_n = 0;
#20
rst_n = 1;
#5000
$stop;
end
endmodule
仿真結(jié)果:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-715244.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-715244.html
到了這里,關(guān)于Verilog手撕代碼(6)分頻器的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!