Verilog设计分频器(面试必看)
分頻器是指使輸出信號頻率為輸入信號頻率整數(shù)分之一的電子電路。在許多電子設(shè)備中如電子鐘、頻率合成器等,需要各種不同頻率的信號協(xié)同工作,常用的方法是以穩(wěn)定度高的晶體振蕩器為主振源,通過變換得到所需要的各種頻率成分,分頻器是一種主要變換手段。
早期的分頻器多為正弦分頻器,隨著數(shù)字集成電路的發(fā)展,脈沖分頻器(又稱數(shù)字分頻器)逐漸取代了正弦分頻器。
下面以Verilog HDL 語言為基礎(chǔ)介紹占空比為50%的分頻器。
1、偶分頻
偶分頻電路指的是分頻系數(shù)為 2、4、6、8 ... 等偶數(shù)整數(shù)的分頻電路,我們可以直接進行分頻。
例如下面 divider.v 中,對輸入時鐘進行6分頻,即假設(shè)clk 為 50MHz ,分頻后的時鐘頻率為 (50/6) MHz。程序如下:
設(shè)計代碼:
//rtl
module divider(
clk,
rst_n,
clk_div
);
input clk;
input rst_n;
output clk_div;
reg clk_div; parameter NUM_DIV = ;
reg [:] cnt; always @(posedge clk or negedge rst_n)
if(!rst_n) begin
cnt <= 'd0;
clk_div <= 'b0;
end
else if(cnt < NUM_DIV / - ) begin
cnt <= cnt + 'b1;
clk_div <= clk_div;
end
else begin
cnt <= 'd0;
clk_div <= ~clk_div;
end
endmodule
仿真程序:
//tb
module divider_tb();
reg clk;
reg rst_n;
wire clk_div;
parameter DELY=;
divider U_divider(
.clk (clk ),
.rst_n (rst_n ),
.clk_div(clk_div)
);
always #(DELY/) clk=~clk;//產(chǎn)生時鐘波形
initial begin
$fsdbDumpfile("divider_even.fsdb");
$fsdbDumpvars(,U_divider);
end
initial begin
clk=;rst_n=;
#DELY rst_n=;
#((DELY*)) $finish;
end
endmodule
可以看到,clk的上升沿,采樣到cnt=2的時候,就翻轉(zhuǎn),采樣到0和1的時候,保持。這樣就可以做到一半高電平,一半低電平。
由于奇分頻需要保持分頻后的時鐘占空比為 50% ,所以不能像偶分頻那樣直接在分頻系數(shù)的一半時使時鐘信號翻轉(zhuǎn)(高電平一半,低電平一半)。
在此我們需要利用輸入時鐘上升沿和下降沿來進行設(shè)計。
clk_div1 和clk_div2 的上升沿到來時間相差半個輸入周期,所以將這兩個信號進行或操作,即可得到占空比為 50% 的5分頻時鐘。程序如下:
//rtl
module divider(
clk,
rst_n,
clk_div
);
input clk;
input rst_n;
output clk_div;
reg clk_div; parameter NUM_DIV = ;
reg[:] cnt1;
reg[:] cnt2;
reg clk_div1, clk_div2; always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt1 <= ;
else if(cnt1 < NUM_DIV - )
cnt1 <= cnt1 + 'b1;
else
cnt1 <= ; always @(posedge clk or negedge rst_n)
if(!rst_n)
clk_div1 <= 'b1;
else if(cnt1 < NUM_DIV / )
clk_div1 <= 'b1;
else
clk_div1 <= 'b0; always @(negedge clk or negedge rst_n)
if(!rst_n)
cnt2 <= ;
else if(cnt2 < NUM_DIV - )
cnt2 <= cnt2 + 'b1;
else
cnt2 <= ; always @(negedge clk or negedge rst_n)
if(!rst_n)
clk_div2 <= 'b1;
else if(cnt2 < NUM_DIV / )
clk_div2 <= 'b1;
else
clk_div2 <= 'b0; assign clk_div = clk_div1 | clk_div2;
endmodule
仿真代碼:
//tb
module divider_tb();
reg clk;
reg rst_n;
wire clk_div;
parameter DELY=;
divider U_divider(
.clk (clk ),
.rst_n (rst_n ),
.clk_div(clk_div)
);
always #(DELY/) clk=~clk;//產(chǎn)生時鐘波形
initial begin
$fsdbDumpfile("divider_odd.fsdb");
$fsdbDumpvars(,U_divider);
end
initial begin
clk=;rst_n=;
#DELY rst_n=;
#((DELY*)) $finish;
end
endmodule
對其進行測試和驗證(此仿真波形是三分頻,占空比50%),即上述程序吧NUM_DIV改成3即可,得到如下波形:
3.任意占空比的任意分頻
在verilog程序設(shè)計中,我們往往要對一個頻率進行任意分頻,而且占空比也有一定的要求這樣的話,對于程序有一定的要求。
現(xiàn)在在前面兩個實驗的基礎(chǔ)上做一個簡單的總結(jié),實現(xiàn)對一個頻率的任意占空比的任意分頻。
比如: FPGA系統(tǒng)時鐘是50M Hz,而我們要產(chǎn)生的頻率是880Hz,那么,我們需要對系統(tǒng)時鐘進行分頻。很容易想到用計數(shù)的方式來分頻:50000000/880 = 56818。
顯然這個數(shù)字不是2的整冪次方,那么我們可以設(shè)定一個參數(shù),讓它到56818的時候重新計數(shù)就可以實現(xiàn)了。程序如下:
設(shè)計代碼:
//rtl
module div(
clk,
rst_n,
clk_div
);
input clk,rst_n;
output clk_div;
reg clk_div; reg [:] counter; always @(posedge clk or negedge rst_n)
if(!rst_n)
counter <= ;
else if(counter==)
counter <= ;
else
counter <= counter+; assign clk_div = counter[];
endmodule
仿真代碼:
//tb
module div_tb();
reg clk;
reg rst_n;
wire clk_div;
parameter DELY=;
div U_div(
.clk (clk ),
.rst_n (rst_n),
.clk_div(clk_div)
);
always #(DELY/) clk=~clk;//產(chǎn)生時鐘波形
initial begin
$fsdbDumpfile("div_any.fsdb");
$fsdbDumpvars(,U_div);
end
initial begin
clk=;rst_n=;
#DELY rst_n=;
#((DELY*)) $finish;
end
endmodule
分頻的應用很廣泛,一般的做法是先用高頻時鐘計數(shù),然后使用計數(shù)器的某一位輸出作為工作時鐘進行其他的邏輯設(shè)計,上面的程序就是一個體現(xiàn)。
下面我們來算一下它的占空比:
我們清楚地知道,這個輸出波形在counter為0到32767(2的14次方)的時候為低,在32768到56817的時候為高,占空比為40%多一些,
如果我們需要占空比為50%,那么我們需要再設(shè)定一個參數(shù),使它為56817的一半,使達到它的時候波形翻轉(zhuǎn),就可以實現(xiàn)結(jié)果了。
程序如下:28408=56818/2-1,計數(shù)到28408就清零,翻轉(zhuǎn),其余的計數(shù)期間,保持不變。
設(shè)計代碼:
//rtl
module div(
clk,
rst_n,
clk_div
);
input clk,rst_n;
output clk_div;
reg clk_div;
reg [:] counter;
always @(posedge clk or negedge rst_n)
if(!rst_n)
counter <= ;
else if(counter==)
counter <= ;
else
counter <= counter+; always @(posedge clk or negedge rst_n)
if(!rst_n)
clk_div <= ;
else if(counter==)
clk_div <= ~clk_div;
endmodule
仿真代碼:
//tb
module div_tb();
reg clk;
reg rst_n=;
wire clk_div;
parameter DELY=;
div U_div(
.clk (clk ),
.rst_n (rst_n),
.clk_div(clk_div)
);
always #(DELY/) clk=~clk;//產(chǎn)生時鐘波形
initial begin
$fsdbDumpfile("div_any.fsdb");
$fsdbDumpvars(,U_div);
end
initial begin
clk=;rst_n=;
#DELY rst_n=;
#((DELY*)) $finish;
end
endmodule
//rtl
module div(
clk,
rst_n,
clk_div,
counter
);
input clk,rst_n;
output clk_div;
reg clk_div;
output [:] counter;
reg [:] counter; always @(posedge clk)
if(!rst_n)
counter <= ;
else if(counter==)
counter <= ;
else counter <= counter+; always @(posedge clk)
if(!rst_n)
clk_div <= ;
else if(counter<)
clk_div <= ;
else
clk_div <= ;
endmodule
仿真代碼:
//tb
module div_tb();
reg clk;
reg rst_n;
wire clk_div;
wire [:] counter;
parameter DELY=;
div U_div(
.clk (clk ),
.rst_n (rst_n ),
.counter(counter),
.clk_div(clk_div)
);
always #(DELY/) clk=~clk;//產(chǎn)生時鐘波形
initial begin
$fsdbDumpfile("div_any.fsdb");
$fsdbDumpvars(,U_div);
end
initial begin
clk=;rst_n=;
#DELY rst_n=;
#((DELY*)) $finish;
end
endmodule
通過以上的學習,對分頻器有了比較深刻的認識,將在以后的學習中會有廣泛的應用。
原出處:https://www.chipist.cn/article/166 如有什么疑問,歡迎討論:QQ:447574829
總結(jié)
以上是生活随笔為你收集整理的Verilog设计分频器(面试必看)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CodeForces - 163B Le
- 下一篇: primeng 中 pickList组件