前面发过三篇有关dds/nco方面的博文:
1)有关DDS的一些初步理解:相位噪声和无杂散动态范围 (http://blog.csdn.net/jbb0523/article/details/7879999)
2)DDS的对称性质——参考时钟是100M,则产生的90MHz与10MHz的正弦信号频率相同,相位相反 (http://blog.csdn.net/jbb0523/article/details/7563185)
3)数控振荡器NCO使用verilog实现时NCO初始值和上限值设定时的注意事项 (http://blog.csdn.net/jbb0523/article/details/6752150)
这次讨论的是第三篇讨论的内容的扩展。
下面直接先写一段代码:
always @ (posedge clk) if(!rst)begin dds_accum1 <= 33'd0; pulse1 <= 1'b0; end else begin if(dds_accum1 >= dds_up1)begin dds_accum1 <= dds_accum1 - dds_up1; pulse1 <= 1'b1; end else begin dds_accum1 <= dds_accum1 + dds_word; pulse1 <= 1'b0; end end always @ (posedge clk) if(!rst)begin dds_accum2 <= 33'd0; pulse2 <= 1'b0; end else begin if(dds_accum2[32])begin dds_accum2 <= {1'b0,dds_accum2[31:0]} + {1'b0,dds_word}; pulse2 <= 1'b1; end else begin dds_accum2 <= {1'b0,dds_accum2[31:0]} + {1'b0,dds_word}; pulse2 <= 1'b0; end end
两个verilog HDL程序的always块是在clk时钟的驱动下产生pulse1和pulse2两个脉冲,其中均产生32位的频率控制字dds_word,累加器dds_accum1和dds_accum2则采用33位,dds_up1=2^32-dds_word ,以这两种方式产生的pulse1和pulse2频率是相同的,只是pulse2要晚于pulse1一个时钟,如图所示:
为了解释这一个现象,我们来举一个简单的例子,若累加器上限取为2^3=8,即累加器取为4bit,而频率控制字取为3bit,我们以频率控制字等于1来说明情况:
对于第一个always块的情况,则dds_up1=7,则累加器将是以下数字的循环:0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 ……当累加器到7时则减去上限7,所以是这样的一个循环;
对于第二个always块的情况,则累加器是以下数字的循环:0 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8……当累加器到8时最高位为1,输出一个pulse,由于累加时截去了累加器的最高位,因为再累加1,除了首次有0以外,以后都是1~8的一个循环,因此频率与第一种情况一样,只是晚了一个时钟,其实如果初始化累加器值时初始化成1,则两个就同步了吧?
细心的人发现上图中的pulse并不是均匀的,有的隔三个时钟,有的相隔两个时钟,甚至有的还相隔了四个时钟,因此这个频率只能是统计上的一个频率,即多个脉冲出输的平均上是某一个频率,但具体到某一个脉冲时抖动很大,有一到两个时钟的抖动,这是不是相位噪声的概念呢?
至于说统计上的频率误差有多大呢?这个就要看你的上限值和频率控制字了,比如说,基于100MHz的时钟,要产生一个30MHz的脉冲,采用32位累加器,则可以计算出它的频率控制字为30*2^32/100=1288490188.8,实际中只能使用整数部分,则频率控制字取为1288490189,可计算出实际的频率为30.0000000046566Mhz,即存在0.0046566Hz的频差,其实可以计算出在100MHz驱动时钟下,使用32位累加器所产生的最大频差为正负0.5*1*100e6/2^32=0.0116415321826935Hz,这里就有一个频率分辨率的概念了,你需要什么频率分辨率,则根据驱动时钟和分辨率要求去选择一下累加器的位宽。