使用 VHDL 进行分频器设计
作者:ChongyangLee
摘要
使用 VHDL 进行分频器设计
作者:ChongyangLee
本文使用实例描述了在 FPGA/CPLD 上使用 VHDL 进行分频器设
计,包括偶数分频、非 50%占空比和 50%占空比的奇数分频、半整数
(N+0.5)分频、小数分频、分数分频以及积分分频。所有实现均可
通过 Synplify Pro 或 FPGA 生产厂商的综合器进行综合,形成可使
用的电路,并在 ModelSim 上进行验证。
目录
概述.......................................................................................................................................1
计数器 ..................................................................................................................................1
普通计数器..................................................................................................................1
约翰逊计数器.............................................................................................................3
分频器 ..................................................................................................................................4
偶数分频器..................................................................................................................4
奇数分频器..................................................................................................................6
半整数分频器.............................................................................................................9
小数分频器................................................................................................................11
分数分频器................................................................................................................15
积分分频器................................................................................................................18
概述
分频器是数字电路中最常用的电路之一,在 FPGA 的设计中也是使用效率
非常高的基本设计。基于 FPGA 实现的分频电路一般有两种方法:一是使用
FPGA 芯片内部提供的锁相环电路,如 ALTERA 提供的 PLL(Phase Locked
Loop),Xilinx 提供的 DLL(Delay Locked Loop);二是使用硬件描述语言,如
VHDL、Verilog HDL 等。使用锁相环电路有许多优点,如可以实现倍频;相位
偏移;占空比可调等。但 FPGA 提供的锁相环个数极为有限,不能满足使用要
求。因此使用硬件描述语言实现分频电路经常使用在数字电路设计中,消耗不
多的逻辑单元就可以实现对时钟的操作,具有成本低、可编程等优点。
计数器
计数器是实现分频电路的基础,计数器有普通计数器和约翰逊计数器两
种。这两种计数器均可应用在分频电路中。
普通计数器
最普通的计数器是加法(或减法)计数器。下面是加法计数器的VHDL实
现,其Synplify Pro下的RTL View如图 1所示。
--file Name: ripple.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;
1
end if;
end process;
cnt <= cntQ;
end a;
代码 1 加法计数器 VHDL 代码
图 1 加法计数器 RTL 视图
加法计数器的Test Bench代码如下所示,在ModelSim下进行功能仿真,仿真
波形结果如图 2所示。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity ripple_tb is
end ripple_tb;
architecture testbench of ripple_tb is
component ripple
generic(width: integer := 4);
port(clk, rst: in std_logic;
cnt: out std_logic_vector(width - 1 downto 0));
end component;
signal clk_tb: std_logic := '0';
signal rst_tb: std_logic := '0';
signal cnt_tb: std_logic_vector(3 downto 0);
begin
UUT: ripple generic map(width => 4)
port map(clk => clk_tb, rst => rst_tb, cnt => cnt_tb);
clk_tb <= not clk_tb after 50 ns;
process
begin
rst_tb <= transport '1';
wait for 200 ns;
2
rst_tb <= transport '0';
wait for 2000 ns;
end process;
end testbench;
代码 2 加法计数器的 test bench 代码
图 2 加法计数器的仿真结果波形
在同一时刻,加法计数器的输出可能有多位发生变化,因此,当使用组合
逻辑对输出进行译码时,会导致尖峰脉冲信号。使用约翰逊计数器可以避免这
个问题。
约翰逊计数器
约翰逊计数器是一种移位计数器,采用的是把输出的最高位取非,然后反
馈送到最低位触发器的输入端。约翰逊计数器在每个时钟下只有一个输出发生
变化。下面是约翰逊计数器的 VHDL 实现代码。
--file Name: ripple.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;
3
cnt <= cntQ;
end a;
代码 3 约翰逊计数器 VHDL 代码
图 3 约翰逊计数器 RTL 视图
显然,约翰逊计数器没有有效利用寄存器的所有状态,假设最初值或复位
状态为
0000,则依次为 0000、0001、0011、0111、1111、1110、1100、1000、
0000 如 循环。再者,如果由于干扰噪声引入一个无效状态,如 0010,则无法
此
恢复到有效到循环中去,需要我们加入错误恢复处理,在此不再赘述。
分频器
如前所述,分频器的基础是计数器,设计分频
器的关键在于输出电平翻转
的时机。下面使用加法计数器分别描述各种分频器的实现。
偶数分频器
偶数分
频最易于实现,欲实现占空比为 50%的偶数 N 分频,一般来说有两
种方案:一是当计数器计数到
N/2-1 时,将输出电平进行一次翻转,同时给计
数器一个复位信号,如此循环下去;二是当计数器输出为 0 到 N/2-1 时,时钟输
出为 0 或 1,计数器输出为 N/2 到 N-1 时,时钟输出为 1 或 0
,当计数器计数到
N-1 时,复位计数器,如此循环下去。需要说明的是,第一种方案仅仅能实现占
空比为 50%的分频器,第二种方案可以有限度的调整占空比,参考非 50%占空
比的奇数分频实现。
在如下所示的以 6 分频为例的 VHDL 代码中, architecture a 使用的是第一种
方案,
architecture b 使用的是第二种方案。更改 configuration 可查看不同方案的
综合结果。
--filename clk_div1.vhd
4
;
t and clk_in = '1') then
;
ess(clk_in)
k_in'even
d_logic; clk_out: out std_logic);
--description: 占空比为 50%的 6 分频
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all
tity clk_div1 is
en
port(clk_in: in st
end clk_div1;
--
使用第一种方案
of clk_div1 is
architecture a
'0';--赋初始值仅供仿真使用
signal clk_outQ: std_logic :=
signal countQ: std_logic_vector(2 downto 0) := "000";
begin
proc
begin
if(cl
if(countQ /= 2) then
CountQ <= CountQ + 1
else
outQ <= not clk_outQ;
clk_
CountQ <= (others =>'0');
end if;
end if;
end process
;
clk_out <= clk
end a;
种方案
--
of clk_div1 is
architecture b
signal countQ: std_logic_vect
begin
proce
begin
if(c
if(countQ < 5) then
countQ <= countQ +
else
Coun
end if;
end if;
;
end process
begin
if(co
clk_out <= '0';
else
out <= '1';
clk_
process(countQ)
untQ < 3)
1;
tQ <= (others =>'0');
_outQ;
then
使用第二
or(2 downto 0);
ss(clk_in)
lk_in'event
and clk_in = '1') then
5