转载自:http://blog.sina.com.cn/s/blog_4dbde8ed0100v54x.html
在接口电路中,时钟信号的作用至关重要。一般FPGA的外部时钟信号可达几十MHz,但由于一些接口电路的特性所致,这样高频率的时钟不适合电路工作,所以应该引入时钟分频电路,产生频率适合接口电路工作的时钟信号,这样才能便于接口电路工作
一、内容说明
在数字逻辑电路设计中,分频器是一种基本电路,通常用来对某个给定频率进行分频,得到所需的频率。整数分频器的实现非常简单,可采用标准的计数器,也可以采用可编程逻辑器件设计实现,但在某些场合下,时钟源与所需的频率不成整数倍关系,此时可采用小数分频器进行分频。比如:分频系数为2.5、3.5、7.5等半整数分频器。
所以对于基于FPGA的时钟分频,可以简单分为奇数分频与偶数分频,而且分频后时钟的占空比也是可变的,最简单的时钟分频就是对时钟进行奇数,然后输出时钟信号等于计数各位相与,这样得到的分频信号,其占空比很小,即时钟高电平只有一个外部时钟周期,但由于FPGA设计的对时钟跳变沿的判断的精确性,故这种分频方式也是可取的。本文章说明了如何产生P占空比的时钟信号的方法。
二、硬件电路分析
1.半整数分频器
分频系数为N=0.5整数倍的分频器电路可由一个异或门、一个模N计数器和一个2分频器组成。在实现时,模N计数器可设计成带预置的计数器,这样可以实现任意分频系数为N=0.5整数倍的分频器,图1给出了通用半整数分频器的电路组成。
基于图1的电路,可以采用VHDL硬件描述语言,先实现任意模N的计数器,并可产生模N逻辑电路。然后,用原理图输入方式将模N逻辑电路、异或门和D触发器连接起来,便可实现半整数(N-0.5)分频器及2N-1的分频,在通用半整数分频器中,占空比是固定的。
图1 通用的半整数分频器电路图
2.偶数与奇数分频器
图2为N位数字分频器的设计结构图。
在图中,分频输入信号和位分频系数同时输入到偶数倍分频器和奇数倍分频器中。当分频系数为偶数时,偶数倍分频器工作,其输出为分频后的信号,而奇数倍分频器不工作,其输出为0;而当分频系数为奇数时,奇数倍分频器工作,其输出为分频后的信号,而偶数倍分频器不工作,其输出为0.最后两个分频器出来的信号通过一个或门进行或运算而得到了最终的分频输出信号,这样即完成了对分频输入信号的N位数字分频。
图2 N(偶数或奇数)分频器
三、程序设计与仿真
1.VHDL程序
1) 偶数分频
对时钟进行偶数分频,使占空比达到P很简单,只要使用一个计数器,在计数器的前一半时间里,使输出电平为高电平,在计数的后一半时间里使输出的电平为低电平,这样分频出来的时钟信号就是占空比为P的时钟信号。
偶数分频的VHDL程序代码与注释如下:
--evev frequency division
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity fredivn is
GENERIC(N:integer:=8);- -8分频,偶数分频
port (clk:in std_logic;
outclk:out std_logic);
end fredivn;
architecture rtl of fredivn is
signal count:integer;
begin
process(clk)
begin
if(clk'event and clk='1') then
if(count=N-1)then- - 计数周期
count<=0;
else
count<=count+1;
if count<(integer(N/2))then- -产生分频脉冲,占空比达到50%
outclk<='0';
else
outclk<='1';
end if;
end if;
end if;
end process;
end rtl;
该程序的技术要点只要有两点。第一点是内部计数信号count的赋值语句,程序中主要通过if语句来实现。该语句实现的功能是:当count小于N/2时,输出低电平,当count大于N/2时,输出高电平,因为N是偶数,所以占空比是P,并且count循环计数。
if count<(integer(N/2))then- -产生分频脉冲,占空比达到50%
outclk<='0';
else
outclk<='1';
end if;
end if;
程序的另一个技术要点是使用了generic语句,在entity的定义语句中有:
GENERIC (N:integer:=8);
这个语句定义了分频数N,并且由于运用了generic语句,故在其他程序调用的时候,可以任意更改分频数,使得对此模块的调用非常方便。
2) 奇数分频
奇数分频相对于偶数分频来说,如果不要求占空比的话,难度是一样的,但是如果要使占空比为50%,则计数分频比偶数分频要复杂一些。
奇数分频与偶数分频思路是一样的,都是使输出信号在前一半计数时间里为低电平,在后一半计数时间里为高电平。如果要求占空比为50%,则 要使用一些技巧。可以先对输入时钟的上升沿进行计数,然后让一个内部信号在前一半时间里为低电平,后一半时间里为高电平。同时对输入时钟的下降沿进行计数,让另一个内部信号在前一半时间里为高电平,后一半时间里为低电平。然后让两个内部信号相与,得到了半个时钟周期的一个高电平,再让信号与第一个内部信号相或,就得到了占空比为50%的输出时钟。
奇数分频的VHDL程序代码如下:
--odd frequency division
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity fredivn1 is
GENERIC(N:integer:=7);//奇分频数
port (clk:in std_logic;
outclk:out std_logic);
end fredivn1;
architecture rtl of fredivn1 is
signal count1,count2:integer;
signal q,outclk1,outclk2:std_logic;
begin
q<=outclk1 and outclk2;
outclk<=q xor outclk1;
process(clk)//上升沿进行计数
begin
if(clk'event and clk='1') then
if(count1=N-1)then
count1<=0;
else
count1<=count1+1;
if count1<(integer(N/2))then//产生分频脉冲1
outclk1<='0';
else
outclk1<='1';
end if;
end if;
end if;
end process;
process(clk)//下降沿进行计数
begin
if(clk'event and clk='0') then
if(count2=N-1)then
count2<=0;
else
count2<=count2+1;
if count2<(integer(N/2))then//产生分频脉冲2
outclk2<='1';
else
outclk2<='0';
end if;
end if;
end if;
endprocess;
end rtl;
以上程序的技术要点就是如何保证占空比为50%。在此程序中用到了两个进程process,第一个进程是对上升沿进行计数,然后让outclk1在计数的前一半时间里为低电平,在计数的后一半时间为高电平。第二个进程对下降沿计数,在计数的前一半时间里让outclk2为高电平,在计数的后一半时间里让outclk2为低电平,这样outclk1与outclk2就有了半个时钟周期的时间上重合的高电平,让outclk1与outclk2相与,结果赋给q,q就是一个只有半个时钟周期是高电平,其余时间都是低电平的信号,最后让q与outclk1相或,得到的波形正好是占空比为50%的分频时钟波形。
在实体的定义部分都用到了generic语句,用这个语句,可以使程序的一些参数在其他模块调用时直接修改即可,这使得模块的重复利用非常方便。
3) 对分频器的调用
其中任意偶数N的分频器的VHDL描述如下:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity fredivn is
GENERIC(N:integer);
port (clk:instd_logic;- -分频后脉冲
outclk:out std_logic);
end fredivn;
architecture behav of fredivn is
signal count:integer;
begin
process(clk)
begin
if(clk'event and clk='1')then --将CLK信号N分频
if(count=N-1)then
count<=0;
else
count<=count+1;
if count<(integer(N/2))then- -产生分频后时钟
outclk<='0';
else
outclk<='1';
end if;
end if;
end if;
end process;
end behav;
以上例子是一个分频器的例子,可以对输入时钟进行N分频,注意在实体声明中,generic语句中的参数N是分频参数,把频率分为几分之一就由这个参数决定。在其他对这个模块进行调用的程序中,只要把N赋予不同的参数值,就可以实现不同的分频,下面是对fredivn进行调用的例子。
package test_con is
constantN1:integer:=16;//16分频
end test_con;
use work.test_con.all;
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity frediv16 is
port(clkin :in std_logic;
clkout:out std_logic);
end frediv16;
architecture behav of frediv16 is
componentfredivn//调用分频函数
generic(N:positive);
port (clk:in std_logic;
outclk:out std_logic);
END component;
begin
u1:fredivn//匹配端口、参数
generic map(N=>N1)
port map(clkin,clkout);
end behav;
如果在该例子中给N赋值16,就实现了一个16分频的分频器。可以看到,通过generic语句可以实现同一模块的不同调用,非常方便。
4) 数控分频器
数控分频器的功能就是当在输入端给定不同输入数据时,将对输入的时钟信号有不同的分频比。以下的数控分频器就是用计数值可并行预置的加法计数器设计完成,方法是将计数溢出位与预置数加载输入信号相接即可,其中D为8位预置数,CLK是Clock输入时钟,输出为FOUT。
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY PULSE IS
PORT (CLK : IN STD_LOGIC;
D : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
FOUT : OUT STD_LOGIC );
END PULSE;
ARCHITECTURE beh OF PULSE IS
SIGNAL FULL :STD_LOGIC;
BEGIN
P_REG: PROCESS(CLK)
VARIABLE CNT8 : STD_LOGIC_VECTOR(7 DOWNTO 0);
BEGIN
IF CLK'EVENT AND CLK = '1' THEN
IF CNT8 = "11111111"THEN
CNT8 :=D;--当CNT8计数计满时,输入数据D被同步预置给计数器CNT8
FULL <='1';--同时使溢出标志信号FULL输出为高电平
ELSE CNT8 := CNT8+ 1; --否则继续作加1计数
FULL<='0';--且输出溢出标志信号FULL为低电平
END IF;
END IF;
END PROCESS P_REG ;
P_DIV: PROCESS(FULL)
VARIABLE CNT2 : STD_LOGIC;
BEGIN
IF FULL'EVENT AND FULL = '1'
THEN CNT2 := NOTCNT2;--如果溢出标志信号FULL为高电平,D触发器输出取反
IF CNT2 = '1' THEN
FOUT <= '1';
ELSE
FOUT <= '0';
END IF;
END IF;
END PROCESS P_DIV ;
END beh;
2.时序仿真
1) 偶数分频
偶数分频程序中端口参数N是分频数,由它来决定多少分频。在程序中预置该参数为8,即8分频,该程序的仿真波形如图3所示。
2) 奇数分频
该程序的仿真波形如图4(a)所示,在图中可以看到最后的分频输出信号周期为70ns,正好频率是分频输入信号的1/7,且其占空比为50%,由此验证了所设计电路的正确性。如果改变不同的奇数N的值,还可以得到不同的仿真波形图4(b)。
3) 数控分频器
4)输入不同的CLK时钟频率和预置值D,得到的仿真波形图如图5所示。该模块还能满足其他预置值分频的设计要求,并可以被上一级模块调用。
四、总结
分频器是一种基本数字电路,通常包括偶数分频及奇数分频两种方式。本章分别讨论了分频系数为偶数或奇数时,且占空比为50%的分频器的设计方法。在设计中分频器满足N值范围为2~2的7次方,这种电路在频率合成及各类数字逻辑电路中有广泛的应用。