基于VHDL語言分頻器電路程式設計(彙總)
分頻器簡介:
分頻器是數字電路中最常用的電路之一,在 FPGA 的設計中也是使用效率非常高的基本設計。基于 FPGA 實作的分頻電路一般有兩種方法:一是使用FPGA 晶片内部提供的鎖相環電路,如 ALTERA 提供的 PLL(Phase Locked
Loop),Xilinx 提供的 DLL(Delay Locked Loop);二是使用硬體描述語言,如VHDL、Verilog HDL 等。使用鎖相環電路有許多優點,如可以實作倍頻;相位偏移;占空比可調等。但 FPGA 提供的鎖相環個數極為有限,不能滿足使用要求。是以使用硬體描述語言實作分頻電路經常使用在數字電路設計中,消耗不多的邏輯單元就可以實作對時鐘的操作,具有成本低、可程式設計等優點。
計數器
計數器是實作分頻電路的基礎,計數器有普通計數器和約翰遜計數器兩種。這兩種計數器均可應用在分頻電路中。
- 普通計數器: 最普通的計數器是加法(或減法)計數器。
- 約翰遜計數器: 約翰遜計數器是一種移位計數器,采用的是把輸出的最高位取非,然後回報送到最低位觸發器的輸入端。約翰遜計數器在每個時鐘下隻有一個輸出發生變化。
分頻器
如前所述,分頻器的基礎是計數器,設計分頻器的關鍵在于輸出電平翻轉的時機。下面使用加法計數器分别描述各種分頻器的實作。
- 偶數分頻器:偶數分頻最易于實作,欲實作占空比為 50%的偶數 N 分頻,一般來說有兩種方案:一是當計數器計數到N/2-1 時,将輸出電平進行一次翻轉,同時給計數器一個複位信号,如此循環下去;二是當計數器輸出為 0 到 N/2-1 時,時鐘輸出為 0 或 1,計數器輸出為 N/2 到 N-1 時,時鐘輸出為 1 或 0,當計數器計數到N-1 時,複位計數器,如此循環下去。需要說明的是,第一種方案僅僅能實作占空比為 50%的分頻器,第二種方案可以有限度的調整占空比,參考非 50%占空比的奇數分頻實作。
- 奇數分頻器:實作非50%占空比的奇數分頻,如實作占空比為 20%(1/5)、40%(2/5)、60%(3/5)、80%(4/5)的 5 分頻器,可以采用似偶數分頻的第二種方案;但如果實作占空比為 50%的奇數分頻,就不能使用偶數分頻中所采用的方案了。
- 半整數分頻器:僅僅采用數字分頻,不可能獲得占空比為 50%的 N+0.5 分頻,我們隻可以設計出占空比為(M+0.5)/(N+0.5)或者 M/(N+0.5)的分頻器,M 小于 N。這種半整數分頻方法是對輸入時鐘進行操作,讓計數器計數到某一個數值時,将輸入時鐘電平進行一次反轉,這樣,該計數值隻保持了半個時鐘周期,是以實作半整數分頻。
- 小數分頻器:小數分頻是通過可變分頻和多次平均的方法實作的。例如要實作 4.7 分頻,隻要在 10 次分頻中,做 7 次 5 分頻,3 次 4 分頻就可以得到。再如要實作 5.67 分頻,隻要在 100 次分頻中,做 67 次6 分頻,33 次 5 分頻即可。考慮到小數分頻器要進行多次兩種頻率的分頻,必須設法将兩種分頻均勻。
- 分數分頻器:将小數分頻的方法進行擴充,可以得到形如M (L/N )的分數分頻的方法,例如, 2(7/13)等于分母的,進行分頻,隻要在 13 次分頻中,進行 7 次 3 分頻,6 次 2 分頻就可以得到。同樣,為了将兩種分頻均勻,将分子部分累加,小于分母的,進行M分頻,大于(M+1)分頻。
- 積分分頻器:積分分頻器用于實作形如 2 m − 1 / N 2^{m-1}/N 2m−1/N的分頻,例如 8/3 分頻。我們當然可以使用上面提到的分數分頻的方法,但對于這種形式的分頻,使用積分分頻的方法綜合往往占用更少的 FPGA 資源。積分分頻法基于下述原理:一個 m 位的二進制數字每次累加 N,假定累加x 次累加值最低m 位回到 0,同時越過 2 m y 2^my 2my 次,那麼,目前累加的數字應該是Nx= 2 m y 2^my 2my;每越過 2 m 2^m 2m一次,最高位變化 2 次,是以,累加 x 次,最高位變化 2y次,得到 x / 2 y = 2 m − 1 / N x/2y=2^{m-1}/N x/2y=2m−1/N分頻的分頻器例如,取 m 為 4,N 為 3,當累加 16 次時,累加值為 48,最低 m 位變回到 0,同時越過 16 三次,最高位變化 6 次,由此得到 16/6=8/3 分頻的分頻器。
注意: 以上分頻器程式設計的案例将會在下邊進行一一分析。
軟體說明: ModelSimSetup-13.1.0.162,QuartusSetup-13.1.0.162。
建立工程:
第一步:打開Quartus軟體。
第二步:點選New Project Wizard -> next.
第三步:選擇工程檔案的存放位置,輸入工程名 -> next -> next。
第四步:在family欄選擇晶片型号-Cyclone IV E,在Name欄選擇EP4CE115F29C7,選擇完之後點選next。(如果不進行硬體調試時,此處預設即可)
第五步:檢查工程有沒有建錯,點選完成。如下圖:

程式設計:
普通計數器:
--檔案名:ADDER8B.vhd 應與工程名保持一緻:
--Description: 帶複位功能的加法計數器
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity ripple is
generic (width: integer := 4);
port(clk, rst: in std_logic;
cnt: out std_logic_vector(width - 1 downto 0));
end ripple;
architecture a of ripple is
signal cntQ: std_logic_vector(width - 1 downto 0);
begin
process(clk, rst)
begin
if (rst = '1') then
cntQ <= (others => '0');
elsif (clk'event and clk = '1') then
cntQ <= cntQ + 1;
end if;
end process;
cnt <= cntQ;
end a;
在同一時刻,加法計數器的輸出可能有多位發生變化,是以,當使用組合邏輯對輸出進行譯碼時,會導緻尖峰脈沖信号。使用約翰遜計數器可以避免這個問題。
檔案仿真(這裡采用modelsim仿真波形):
- 選擇File-> New -> Verification/Debugging Files ->University Program VWF。
2.打開測試檔案。(右鍵點選添加端口,對輸入信号初始化,指派。)
3.仿真結果:
邏輯電路圖:
約翰遜計數器:
--file Name: johnson.vhd
--Description: 帶複位功能的約翰遜計數器
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity johnson is
generic (width: integer := 4);
port (clk, rst: in std_logic;
cnt: out std_logic_vector(width - 1 downto 0));
end johnson;
architecture a of johnson is
signal cntQ: std_logic_vector(width - 1 downto 0);
begin
process(clk, rst)
begin
if(rst = '1') then
cntQ <= (others => '0');
elsif (rising_edge(clk)) then
cntQ(width - 1 downto 1) <= cntQ(width - 2 downto 0);
cntQ(0) <= not cntQ(width - 1);
end if;
end process;
cnt <= cntQ;
end a;
邏輯電路圖:
仿真結果:
顯然,約翰遜計數器沒有有效利用寄存器的所有狀态,假設最初值或複位狀态為0000,則依次為 0000、0001、0011、0111、1111、1110、1100、1000、0000 如 循環。再者,如果由于幹擾噪聲引入一個無效狀态,如 0010,則無法恢複到有效到循環中去,需要我們加入錯誤恢複處理.
偶數分頻器:(6 分頻)
architecture a 使用的是第一種方案,architecture b 使用的是第二種方案。更改 configuration 可檢視不同方案的綜合結果。
--filename clk_div6.vhd
--description: 占空比為 50%的 6 分頻
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity clk_div6 is
port(clk_in: in std_logic; clk_out: out std_logic);
end clk_div6;
--使用第一種方案
architecture a of clk_div6 is
signal clk_outQ: std_logic := '0';--賦初始值僅供仿真使用
signal countQ: std_logic_vector(2 downto 0) := "000";
begin
process(clk_in)
begin
if(clk_in'event and clk_in = '1') then
if(countQ /= 2) then
CountQ <= CountQ + 1;
else
clk_outQ <= not clk_outQ;
CountQ <= (others =>'0');
end if;
end if;
end process;
clk_out <= clk_outQ;
end a;
--使用第二種方案
architecture b of clk_div6 is
signal countQ: std_logic_vector(2 downto 0);
begin
process(clk_in)
begin
if(clk_in'event and clk_in = '1') then
if(countQ < 5) then
countQ <= countQ + 1;
else
CountQ <= (others =>'0');
end if;
end if;
end process;
process(countQ)
begin
if(countQ < 3) then
clk_out <= '0';
else
clk_out <= '1';
end if;
end process;
end b;
configuration cfg of clk_div6 is
for a
end for;
end cfg;
邏輯電路圖:
architecture a:
architecture b:
仿真結果:
architecture a、b:
奇數分頻器:
非 50%占空比:
下面就以實作占空比為40%的 5 分頻分頻器為例,說明非 50%占空比的奇數分頻器的實作。該分頻器的實作對于我們實作 50%占空比的分頻器有一定的借鑒意義。
--filename clk_div5.vhd
--description: 占空比為 40%的 5 分頻
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity clk_div5 is
port(clk_in: in std_logic; clk_out: out std_logic);
end clk_div5;
architecture a of clk_div5 is
signal countQ: std_logic_vector(2 downto 0);
begin
process(clk_in)
begin
if(clk_in'event and clk_in = '1') then
if(countQ < 4) then
countQ <= countQ + 1;
else
CountQ <= (others =>'0');
end if;
end if;
end process;
process(countQ)
begin
if(countQ < 3) then
clk_out <= '0';
else
clk_out <= '1';
end if;
end process;
end a;
邏輯電路圖:
仿真結果:
50%占空比的奇數分頻:
通過待分頻時鐘下降沿觸發計數,産生一個占空比為40%(2/5)的 5 分頻器。将産生的時鐘與上升沿觸發産生的時鐘相或,即可得到一個占空比 50%的 5 分頻器。
推廣為一般方法:欲實作占空比為 50%的 2N+1 分頻器,則需要對待分頻時鐘上升沿和下降沿分别進行N/(2N+1)分頻,然後将兩個分頻所得的時鐘信号相或得到占空比為 50%的 2N+1 分頻器。
下面的代碼就是利用上述思想獲得占空比為 50%的 7 分頻器。需要我們分别對上升沿和下降沿進行 3/7 分頻,再将分頻獲得的信号相或。
--filename clk_div7.vhd
--description: 占空比為 50%的 7 分頻
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity clk_div7 is
port(clk_in: in std_logic; clk_out: out std_logic);
end clk_div7;
architecture a of clk_div7 is
signal cnt1, cnt2: integer range 0 to 6;
signal clk1,clk2: std_logic;
begin
process(clk_in)--上升沿
begin
if(rising_edge(clk_in)) then
if(cnt1 < 6)then
cnt1 <= cnt1 + 1;
else
cnt1 <= 0;
end if;
if(cnt1 < 3) then
clk1 <= '1';
else
clk1 <= '0';
end if;
end if;
end process;
process(clk_in)--下降沿
begin
if(falling_edge(clk_in)) then
if(cnt2 < 6) then
cnt2 <= cnt2 + 1;
else
cnt2 <= 0;
end if;
if(cnt2 < 3) then
clk2 <= '1';
else
clk2 <= '0';
end if;
end if;
end process;
clk_out <= clk1 or clk2;
end a;
邏輯電路圖:
仿真結果:
半整數分頻器:
如上所述,占空比為 50%的奇數分頻可以幫助我們實作半整數分頻,将占空比為50%的奇數分頻與待分頻時鐘異或得到計數脈沖,下面的代碼就是依靠占空比為 50%的 5 分頻實作 2.5 分頻器的。
--filename clk_div2_5.vhd
--description: 占空比為 1/1.5,即 60%。的 2.5分頻
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity clk_div2_5 is
port(clk_in: in std_logic; clk_out: out std_logic);
end clk_div2_5;
architecture a of clk_div2_5 is
signal cnt1, cnt2: integer range 0 to 4;
signal clk1, clk2: std_logic;
signal Pclk, Lclk: std_logic;
signal cnt3:integer range 0 to 2;
begin
process(clk_in)
begin
if(rising_edge(clk_in)) then
if(cnt1 < 4) then
cnt1 <= cnt1 + 1;
else
cnt1 <= 0;
end if;
end if;
end process;
process(clk_in)
begin
if(falling_edge(clk_in)) then
if(cnt2 <4) then
cnt2 <= cnt2 + 1;
else
cnt2 <= 0;
end if;
end if;
end process;
process(cnt1)
begin
if (cnt1 <3) then
clk1 <= '0';
else
clk1 <= '1';
end if;
end process;
process(cnt2)
begin
if (cnt2 < 3) then
clk2 <= '0';
else
clk2 <= '1';
end if;
end process;
process(Lclk)
begin
if(rising_edge(Lclk)) then
if(cnt3 < 2) then
cnt3 <= cnt3 + 1;
else
cnt3 <= 0;
end if;
end if;
end process;
process(cnt3)
begin
if(cnt3 < 2) then
clk_out <= '0';
else
clk_out <='1';
end if;
end process;
Pclk <= clk1 or clk2;
Lclk <= clk_in xor Pclk;--對輸入時鐘進行處理
end a;
仿真結果:
小數分頻器:
表 1以 2.7 分頻為例,小數部分進行累加,如果大于等于10,則進行 3 分頻,如果小于 10,進行
2 分頻。
表一:小數分頻系數序列
序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
---|---|---|---|---|---|---|---|---|---|---|
累加值 | 7 | 14 | 11 | 8 | 15 | 12 | 9 | 16 | 13 | 10 |
分頻 系數 | 2 | 3 | 3 | 2 | 3 | 3 | 2 | 3 | 3 | 3 |
下加器面的代碼就是基于上述原理實作 2.7 分頻。architecture b 是使用累加器計算分頻系數選則時機, chitectur a 是直接使用已計算好的結果。
--file name: clk_div2_7.vhd
--description: 2.7 分頻 ,占空比應為 10/27。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity clk_div2_7 is
port(clk_in: in std_logic; clk_out: out std_logic);
end clk_div2_7;
architecture b of clk_div2_7 is
signal clkoutQ: std_logic;
signal ctrl: std_logic;
signal cnt1: integer range 0 to 1;
signal cnt2: integer range 0 to 2;
begin
clk_out <= clkoutQ;
process(clkoutQ)
variable tmp: integer range 0 to 20;
begin
if(rising_edge(clkoutQ)) then
tmp := tmp + 7;
if(tmp < 10) then
ctrl <= '1';
else
ctrl <= '0';
tmp := tmp - 10;
end if;
end if;
end process;
process(clk_in)
begin
if(clk_in'event and clk_in = '1') then
if(ctrl = '1') then
if(cnt1 < 1) then
cnt1 <= cnt1 + 1;
else
cnt1 <= 0;
end if;
if(cnt1 < 1) then
clkoutQ <= '1';
else
clkoutQ <= '0';
end if;
else
if(cnt2 < 2) then
cnt2 <= cnt2 + 1;
else
cnt2 <= 0;
end if;
if(cnt2 < 1) then
clkoutQ <= '1';
else
clkoutQ <= '0';
end if;
end if;
end if;
end process;
end b;
architecture a of clk_div2_7 is
signal cnt: integer range 0 to 9;
signal clkoutQ: std_logic;
signal cnt1: integer range 0 to 1;
signal cnt2: integer range 0 to 2;
begin
clk_out <= clkoutQ;
process(clkOutQ)
begin
if(clkoutQ'event and clkoutQ = '1') then
if (cnt < 9) then
cnt <= cnt + 1;
else
cnt <= 0;
end if;
end if;
end process;
process(clk_in)
begin
if(clk_in'event and clk_in = '1') then
case cnt is
when 0|3|6 =>
if(cnt1 < 1) then
cnt1 <= cnt1 + 1;
else
cnt1 <= 0;
end if;
if(cnt1 < 1) then
clkoutQ <= '1';
else
clkoutQ <='0';
end if;
when others =>
if(cnt2 < 2) then
cnt2 <= cnt2 + 1;
else
cnt2 <= 0;
end if;
if(cnt2 < 1) then
clkoutQ <= '1';
else
clkoutQ <= '0';
end if;
end case;
end if;
end process;
end a;
configuration cfg of clk_div2_7 is
for a
end for;
end cfg;
仿真結果:
分數分頻器:
表 2顯示了 2(7/13)的分頻次序。仿照小數分頻器代碼,給出 2(7/13) 分頻的代碼如下:
表 2 分數分頻系數序列
序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
累加值 | 7 | 14 | 8 | 15 | 9 | 16 | 10 | 17 | 11 | 18 | 12 | 19 | 13 |
分頻 系數 | 2 | 3 | 2 | 3 | 2 | 3 | 2 | 3 | 2 | 3 | 2 | 3 | 3 |
--file name: clk_div2_7_13.vhd
--description: 33/13分頻
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity clk_div2_7 is
port(clk_in: in std_logic; clk_out: out std_logic);
end clk_div2_7;
architecture b of clk_div2_7 is
signal clkoutQ: std_logic;
signal ctrl: std_logic;
signal cnt1: integer range 0 to 1;
signal cnt2: integer range 0 to 2;
begin
clk_out <= clkoutQ;
process(clkoutQ)
variable tmp: integer range 0 to 26;
begin
if(rising_edge(clkoutQ)) then
tmp := tmp + 7;
if(tmp < 10) then
ctrl <= '1';
else
ctrl <= '0';
tmp := tmp - 13;
end if;
end if;
end process;
process(clk_in)
begin
if(clk_in'event and clk_in = '1') then
if(ctrl = '1') then
if(cnt1 < 1) then
cnt1 <= cnt1 + 1;
else
cnt1 <= 0;
end if;
if(cnt1 < 1) then
clkoutQ <= '1';
else
clkoutQ <= '0';
end if;
else
if(cnt2 < 2) then
cnt2 <= cnt2 + 1;
else
cnt2 <= 0;
end if;
if(cnt2 < 1) then
clkoutQ <= '1';
else
clkoutQ <= '0';
end if;
end if;
end if;
end process;
end b;
architecture a of clk_div2_7 is
signal cnt: integer range 0 to 12;
signal clkoutQ: std_logic;
signal cnt1: integer range 0 to 1;
signal cnt2: integer range 0 to 2;
begin
clk_out <= clkoutQ;
process(clkOutQ)
begin
if(clkoutQ'event and clkoutQ = '1') then
if (cnt < 9) then
cnt <= cnt + 1;
else
cnt <= 0;
end if;
end if;
end process;
process(clk_in)
begin
if(clk_in'event and clk_in = '1') then
case cnt is
when 0|2|4|6|8|10 =>
if(cnt1 < 1) then
cnt1 <= cnt1 + 1;
else
cnt1 <= 0;
end if;
if(cnt1 < 1) then
clkoutQ <= '1';
else
clkoutQ <='0';
end if;
when others =>
if(cnt2 < 2) then
cnt2 <= cnt2 + 1;
else
cnt2 <= 0;
end if;
if(cnt2 < 1) then
clkoutQ <= '1';
else
clkoutQ <= '0';
end if;
end case;
end if;
end process;
end a;
configuration cfg of clk_div2_7 is
for b
end for;
end cfg;
仿真結果:
積分分頻器:
例如,取 m 為 4,N 為 3,當累加 16 次時,累加值為 48,最低 m 位變回到 0,同時越過 16 三次,最高位變化 6 次,由此得到 16/6=8/3 分頻的分頻器。
--file name: clk_div8.vhd
--description: 使用積分分頻實作 8/3 分頻
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity clk_div_8f3 is
port(clk_in: in std_logic; clk_out: out std_logic);
end clk_div_8f3;
architecture a of clk_div_8f3 is
signal cnt: std_logic_vector(3 downto 0) := (others => '0');
signal dly: std_logic;
begin
process(clk_in)
begin
if(clk_in'event and clk_in = '1') then
dly <= cnt(3);
cnt <= cnt + 3;
end if;
end process;
clk_out <= dly xor cnt(3);
end a;
仿真結果: