使用VHDL进行分频器设计.docx
《使用VHDL进行分频器设计.docx》由会员分享,可在线阅读,更多相关《使用VHDL进行分频器设计.docx(30页珍藏版)》请在冰豆网上搜索。
使用VHDL进行分频器设计
使用VHDL进行分频器设计
作者:
ChongyangLee
摘要
使用VHDL进行分频器设计
作者:
ChongyangLee
本文使用实例描述了在FPGA/CPLD上使用VHDL进行分频器设
计,包括偶数分频、非50%占空比和50%占空比的奇数分频、半整数
(N+0.5)分频、小数分频、分数分频以及积分分频。
所有实现均可
通过SynplifyPro或FPGA生产厂商的综合器进行综合,形成可使
用的电路,并在ModelSim上进行验证。
概述.......................................................................................................................................1
计数器..................................................................................................................................1
普通计数器..................................................................................................................1
约翰逊计数器.............................................................................................................3
分频器..................................................................................................................................4
偶数分频器..................................................................................................................4
奇数分频器..................................................................................................................6
半整数分频器.............................................................................................................9
小数分频器................................................................................................................11
分数分频器................................................................................................................15
积分分频器................................................................................................................18
概述
分频器是数字电路中最常用的电路之一,在FPGA的设计中也是使用效率
非常高的基本设计。
基于FPGA实现的分频电路一般有两种方法:
一是使用
FPGA芯片内部提供的锁相环电路,如ALTERA提供的PLL(PhaseLocked
Loop),Xilinx提供的DLL(DelayLockedLoop);二是使用硬件描述语言,如
VHDL、VerilogHDL等。
使用锁相环电路有许多优点,如可以实现倍频;相位
偏移;占空比可调等。
但FPGA提供的锁相环个数极为有限,不能满足使用要
求。
因此使用硬件描述语言实现分频电路经常使用在数字电路设计中,消耗不
多的逻辑单元就可以实现对时钟的操作,具有成本低、可编程等优点。
计数器
计数器是实现分频电路的基础,计数器有普通计数器和约翰逊计数器两
种。
这两种计数器均可应用在分频电路中。
普通计数器
最普通的计数器是加法(或减法)计数器。
下面是加法计数器的VHDL实
现,其SynplifyPro下的RTLView如图1所示。
--fileName:
ripple.vhd
--Description:
带复位功能的加法计数器
libraryieee;
useieee.std_logic_1164.all;
useieee.std_logic_arith.all;
useieee.std_logic_unsigned.all;
entityrippleis
generic(width:
integer:
=4);
port(clk,rst:
instd_logic;
cnt:
outstd_logic_vector(width-1downto0));
endripple;
architectureaofrippleis
signalcntQ:
std_logic_vector(width-1downto0);
begin
process(clk,rst)
begin
if(rst='1')then
cntQ<=(others=>'0');
elsif(clk'eventandclk='1')then
cntQ<=cntQ+1;
1
endif;
endprocess;
cnt<=cntQ;
enda;
代码1加法计数器VHDL代码
图1加法计数器RTL视图
加法计数器的TestBench代码如下所示,在ModelSim下进行功能仿真,仿真
波形结果如图2所示。
libraryieee;
useieee.std_logic_1164.all;
useieee.std_logic_arith.all;
useieee.std_logic_unsigned.all;
entityripple_tbis
endripple_tb;
architecturetestbenchofripple_tbis
componentripple
generic(width:
integer:
=4);
port(clk,rst:
instd_logic;
cnt:
outstd_logic_vector(width-1downto0));
endcomponent;
signalclk_tb:
std_logic:
='0';
signalrst_tb:
std_logic:
='0';
signalcnt_tb:
std_logic_vector(3downto0);
begin
UUT:
ripplegenericmap(width=>4)
portmap(clk=>clk_tb,rst=>rst_tb,cnt=>cnt_tb);
clk_tb<=notclk_tbafter50ns;
process
begin
rst_tb<=transport'1';
waitfor200ns;
2
rst_tb<=transport'0';
waitfor2000ns;
endprocess;
endtestbench;
代码2加法计数器的testbench代码
图2加法计数器的仿真结果波形
在同一时刻,加法计数器的输出可能有多位发生变化,因此,当使用组合
逻辑对输出进行译码时,会导致尖峰脉冲信号。
使用约翰逊计数器可以避免这
个问题。
约翰逊计数器
约翰逊计数器是一种移位计数器,采用的是把输出的最高位取非,然后反
馈送到最低位触发器的输入端。
约翰逊计数器在每个时钟下只有一个输出发生
变化。
下面是约翰逊计数器的VHDL实现代码。
--fileName:
ripple.vhd
--Description:
带复位功能的约翰逊计数器
libraryieee;
useieee.std_logic_1164.all;
useieee.std_logic_arith.all;
useieee.std_logic_unsigned.all;
entityjohnsonis
generic(width:
integer:
=4);
port(clk,rst:
instd_logic;
cnt:
outstd_logic_vector(width-1downto0));
endjohnson;
architectureaofjohnsonis
signalcntQ:
std_logic_vector(width-1downto0);
begin
process(clk,rst)
begin
if(rst='1')then
cntQ<=(others=>'0');
elsif(rising_edge(clk))then
cntQ(width-1downto1)<=cntQ(width-2downto0);
cntQ(0)<=notcntQ(width-1);
endif;
endprocess;
3
cnt<=cntQ;
enda;
代码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代码中,architecturea使用的是第一种
方案,architectureb使用的是第二种方案。
更改configuration可查看不同方案的
综合结果。
--filenameclk_div1.vhd
4
--description:
占空比为50%的6分频
libraryieee;
useieee.std_logic_1164.all;
useieee.std_logic_arith.all;
useieee.std_logic_unsigned.all;
entityclk_div1is
port(clk_in:
instd_logic;clk_out:
outstd_logic);
endclk_div1;
--使用第一种方案
architecturea
ofclk_div1is
signalclk_outQ:
std_logic:
='0';--赋初始值仅供仿真使用
signalcountQ:
std_logic_vector(2downto0):
="000";
begin
process(clk_in)
begin
if(clk_in'eventandclk_in='1')then
if(countQ/=2)then
CountQ<=CountQ+1;
else
clk_outQ<=notclk_outQ;
CountQ<=(others=>'0');
endif;
endif;
endprocess;
clk_out<=clk_outQ;
enda;
--使用第二种方案
architectureb
ofclk_div1is
signalcountQ:
std_logic_vector(2downto0);
begin
process(clk_in)
begin
if(clk_in'eventandclk_in='1')then
if(countQ<5)then
countQ<=countQ+1;
else
CountQ<=(others=>'0');
endif;
endif;
endprocess;
process(countQ)
begin
if(countQ<3)then
clk_out<='0';
else
clk_out<='1';
5
endif;
endprocess;
endb;
configurationcfgofclk_div1is
fora
endfor;
endcfg;
代码4偶数分频的VHDL代码
图4图5所示的分别是使用architecturea和architectureb的仿真结果波形。
两者均
正确的实现了50%占空比的6分频。
图4architecturea的仿真结果
图5architectureb的仿真结果
奇数分频器
实现非
50%占空比的奇数分频,如实现占空比为20%(1/5)、40%
(2/5)、60%(3/5)、80%(4/5)的5
类
种方案;但如果实现占空比为50%的奇数分频,就不能使用偶数分频中所采用
的方案了。
非50%占空比的奇数分频
下面就以实现占空比为40%的5分频分频器为例,说明非50%占空比的奇数
分频器的实现。
该分频器的实现对于我们实现50%占空比的分频器有一定的借
鉴意义。
--filenameclk_div2.vhd
--description:
占空比为40%的5分频
libraryieee;
useieee.std_logic_1164.all;
useieee.std_logic_arith.all;
useieee.std_logic_unsigned.all;
entityclk_div2is
port(clk_in:
instd_logic;clk_out:
outstd_logic);
endclk_div2;
6
architectureaofclk_div2is
signalcountQ:
std_logic_vector(2downto0);
begin
process(clk_in)
begin
if(clk_in'eventandclk_in='1')then
if(countQ<4)then
countQ<=countQ+1;
else
CountQ<=(others=>'0');
endif;
endif;
endprocess;
process(countQ)
begin
if(countQ<3)then
clk_out<='0';
else
clk_out<='1';
endif;
endprocess;
enda;
代码5占空比为40%的5分频
仿真波形如图6所示。
图6占空比为40%的5分频仿真波形
50%占空比的奇数分频
通过待分频时钟下降沿触发计数,产生一个占空比为40%(2/5)的5分频
的时钟相或,即可得到一个占空比为50%
器。
将产生的时钟与上升沿触发产生
的5分频器。
推广为一般方法:
欲实现占空比为50%的2N+1分频器,则需要对待分频时
钟上升沿和下降沿分别进行N/(2N+1)分频,然后将两个分频所得的时钟信号相
或得到占空比为50%的2N+1分频器。
下面的代码就是利用上述思想获得占空比为50%的7分频器。
需要我们分别
对上升沿和下降沿进行3/7分频,再将分频获得的信号相或。
7
--filenameclk_div3.vhd
--description:
占空比为50%的7分频
libraryieee;
useieee.std_logic_1164.all;
useieee.std_logic_arth.all;
useieee.std_logic_unsigned.all;
entityclk_div3is
port(clk_in:
instd_logic;clk_out:
outstd_logic);
endclk_div3;
architectureaofclk_div3is
signalcnt1,cnt2:
integerrange0to6;
signalclk1,
begin
clk2:
std_logic;
process(clk_in)--上升沿
begin
if(rising_edge(clk_in))then
if(cnt1<6)then
cnt1<=cnt1+1;
else
cnt1<=0;
endif;
if(cnt1<3)then
clk1<='1';
else
clk1<='0';
endif;
endif;
endprocess;
process(clk_in)--下降沿
begin
if(falling_edge(clk_in))then
if(cnt2<6)then
cnt2<=cnt2+1;
else
cnt2<=0;
endif;
if(cnt2<3)then
clk2<='1';
else
clk2<='0';
endif;
endif;
endprocess;
clk_out<=clk1orclk2;
8
enda;
代码6占空比为50%的7分频VHDL代码
综合得到的RTL视图如图7所示,仿真结果如图8所示。
图7占空比为50%的7分频RTL视图
图8占空比为50%的7分频仿真波形
占空比为50%的奇数分频可以帮助我们实现半整数分频。
半整数分频器
仅仅采用数字分频,不可能获得占空比为50%的N+0.5分频,我们只可以
设计出占空比为(M+0.5)/(N+0.5)或者M/(N+0.5)的分频器,M小于N。
这种半整
数分频方法是对输入时钟进行操作,让计数器计数到某一个数值时,将输入时
钟电平进行一次反转,这样,该计数值只保持了半个时钟周期,因此实现半整
数分频。
如上所述,占空比为50%的奇数分频可以帮助我们实现半整数分频,将占
空比为50%的奇数分频与待分频时钟异或得到计数脉冲,下面的代码就是依靠
占空比为50%的5分频实现2.5分频器的。
libraryieee;
useieee.std_logic_1164.all;
useieee.std_logic_arith.all;
useieee.std_logic_unsigned.all;
entityclk_div4is
9
port(clk_in:
instd_logic;clk_out:
outstd_logic);
endclk_div4;
architectureaofclk_div4is
signalcnt1,cnt2:
integerrange0to4;
signalclk1,clk2:
std_logic;
signalPclk,
signalcnt3:
begin
Lclk:
std_logic;
integerrange0to2;
process(clk_in)
begin
if(rising_edge(clk_in))then
if(cnt1<4)then
cnt1<=cnt1+1;
else
cnt1<=0;
endif;
endif;
endprocess;
process(clk_in)
begin
if(falling_edge(clk_in))then
if(cnt2<4)then
cnt2<=cnt2+1;
else
cnt2<=0;
endif;
endif;
endprocess;
process(cnt1)
begin
if(cnt1<
3)then
clk1<='0';
else
clk1<='1';
endif;
endprocess;
process(cnt2)
begin
if(cnt2<3)then
clk2<='0';
else
clk2<='1';
endif;
endprocess;
process(Lclk)
begin
10
if(rising_edge(Lclk))then
if(cnt3<2)then
cnt3<=cnt3+1;
else
cnt3<=0;
endif;
endif;
endprocess;
process(cnt3)
begin
if(cnt3<1)then
clk_out<='0';
else
clk_out<='1';
endif;
endprocess;
Pclk