本帖最后由 ucx 于 2018-4-14 21:22 编辑
DF_general.rar
(1.21 KB)
020帖介绍了8B10B编码器,接下来应该讲8B10B解码以及编解码实际应用举例。我们把这件事先放一放,转而介绍一下人尽皆知的分频器。
万能分频器性能关于分频器的应用和必要性,不打算在此浪费笔墨介绍。分频器分为整数分频和小数分频,小数分频也称为分数分频。前一段时间,本人需要一个由50M产生1843200=115200×16的频率信号,用来实现和115200波特率的串口通信,设计制作了一个DF_general通用分频器,和大家分享一下。
如果一个分频器同时满足以下4个条件: 1. 只要给出时钟频率和输出频率就能生成相应的分频电路。 2. 消耗的资源最少,或者某些情况下比精心定制设计消耗资源略高一点。 3. 输出抖动频率最小:整数分频时钟无抖动,分数分频时始终小于一个时钟周期。 4. 适用于任何输出频率小于时钟频率的情况。 那么,我们是不是可以说这个分频器叫万能分频器呢?DF_general恰好满足这4个条件,故称之为万能分频器。 分数分频原理实现分数分频方式不同,阐述原理的方法也不同。本帖用带分数的形式实现分数分频,所以先把时钟clock频率与输出q频率的比值用带分数表示。 记clk_frq和q_frq为时钟频率和输出频率。令clk_frq/q_frq= qut + num/den。式中num和den互素,且num<den。num==0时为整数qut分频。下面叙述num!=0的情况。 1. 设计一个模qut的受控计数器cn_qut,时钟使用clock,控制信号为enQut。 2. enQut==0时cn_qut要停一拍计数,所以称enQut==0为停拍脉冲,并称cn_qut的周期为整周期。一个整周期内最多存在一个停拍脉冲,存在停拍脉冲的整周期称为慢周期,不存在停拍脉冲的整周期为快周期。显然,快周期长度是qut个时钟周期,慢周期是qut+1个时钟周期。此时令整周期的溢出率为输出频率。那么,全部是快周期时为qut整数分频,快慢周期交替时,则输出平均速率为(qut, qu+1)内的分数分频。快周期比例高则输出频率接近qut整数分频,慢周期比重高则接近qut+1分频。 3. 在den个整周期内,溢出次数为den。如果选取num个作为慢周期,则den个整周期的总时间为den×qut + num个时钟周期。此时,时钟频率与溢出率的比值刚好是qut + num/den,即实现了clk_frq/q_frq分频。 4. 通过以上分析,定义一个分母计数器den_cn,完成以整周期的溢出信号ov_qut为使能的模den计数。剩下的问题是在den个整周期内如何均匀选取num个周期。 5. 对于num==1的简单情况,选取den_cn == 0的整周期为慢周期即可,同样当num == den-1时只有den_cn == 0为快周期,其他为慢周期即可。 6. 再去掉num==2和num==den-2两种简单情况。选取2个周期为慢周期或快周期。 7. 其他情况,den一定>=7且num>=3且num <=den-3,采用DDS办法均匀选取num个周期作为慢周期。 7.1 确定表示den-1的二进制整数的位长den_len。 7.2 定义一个位长为den_len的DDS累加器DDS_acc。 7.3 计算DDS累加字DDS_frq=2**den_len * num / den。式中**表示幂运算。 7.4 累加器在den个整周期开始装载den-1值(整周期溢出时刻),否则在其他整周期溢出时累加DDS_frq计数。那么,累加器在den个整周期时间刚好均匀溢出num次。 到此,万能分频器就诞生了。下面简单说说DF_general的具体实现步骤。
万能分频器设计实现
模块以VHDL语言形式表示如下。 Entity DF_general is generic( clk_frq : integer := 50e6; q_frq : integer := 1843200); Port( clock : in std_logic; q : outstd_logic ); 参数clk_frq默认为50MHz,参数q_frq默认为115200×16=1843200Hz。 为了使den和num互素,需要编写一个供编译器使用的求最大公约数函数gcd(a, b)。 为实现步骤7.3并确保×运算不溢出,设计一个函数cal_frq(f, fs)。 然后定义常数如下: const int qut= clk_frq / q_frq; const int residu= clk_frq % q_frq; const int den= q_frq / gcd(q_frq, residu); const int num= residu / gcd(q_frq, residu); const int qut_len= LOG2(qut-1)+1; #if qut==1 //整数部分为1时不需要cn_qut ov_qut = enQut; #elsif qut==2 //此时,cn_qut退化为一个比特 @clock if(enQut) cn_qut = ~cn_qut; //步骤1 ov_qut = ~cn_qut; #else cn_qut[0 : qut_len-1]; @clock if(enQut) cn_qut = cn_qut==qut-1? 0 : cn_qut+1; //步骤1, 相当于cn_qut =( cn_qut+1)%qut, @clock ov_qut = ~ov_qut && cn_qut==0; //确保一个整周期一次溢出 #endif #if residu==0 //整数分频,不需要den计数 enQut =1; #else const int den_len= LOG2(den-1)+1; const int DDS_frq=cal_frq(num,den); DDS_nxt[0 : den_len]; DDS_acc[0 :den_len-1]; den_dis,y_dis,ld_dds; #if num==1 ||num==den-1 // 步骤5 @clock if(ov_qut) den_dis = (den_cn==0) != num; #elsif num==2 ||num==den-2 //步骤6 @clock if(ov_qut) den_dis = (den_cn==0 || den_cn==den/2) !=( num!=2); #else DDS_nxt =DDS_acc+DDS_frq; @clock if(ov_qut) { ld_dds= den_cn==0; DDS_acc= ld_dds ?den-1: DDS_nxt(1 to den_len); //步骤7.4 den_dis= DDS_nxt[0]; } #endif @clock if(ov_qut) den_cn= den_cn==den-1? 0 : den_cn+1; //步骤3中模den计数 @clock y_dis = qut==1?~y_dis&&den_dis: ~ov_qut; enQut = y_dis ||~den_dis; //与上一行一起确保停拍脉冲不连续 #endif
万能分频器应用举例例:用50M时钟osc50M产生2.048M的时钟信号clk2M: DF_general generic map(50e6, 4096e3) port map(osc50M, en); @osc50M if(en) clk2M <= ~clk2M; 是不是很万能呢? DF_general.vhd完整代码见附件。 DF_general的应用限制
clk_frq和q_frq是静态参数,不可动态配置。
|