FPGA控制DDS产生1CLK周期误差的分析(二)

前文简短的介绍了DDS的产生原理,其实相当的简单,所以也不需要多做解释,本文详细阐述一下在调试DDS的过程中所产生的一个bug

问题发现

正如上文所述,再用FPGA控制存储在rom中的波形信号输出之后,在上板之前,我们可以先用仿真软件仿真一下,看看波形是否有误。我们的测试条件要求为:从2550Hz开始,每间隔1s的周期以20Hz的步长向下递减至2310Hz的频率,仿真波形如下所示(为节省仿真时间,我们将扫频间隔缩短):
整体时序图
不知大家是否能看清上图中的周期结果分析,按照上图所示,2550Hz、2530Hz除开频率交接处会有一定的抖动之外,在扫频周期中的频率是比较稳定的,但是当我们看到2510Hz的扫频周期过程中却发现,波形的周期呈现为398420ns、398400ns的周期变化,虽然说二者的周期对应基本上都为2510Hz,但是对频率信号敏感的场合是不能接受的。本文便对这一问题进行考究。
问题时序

问题探究

首先,我们追根溯源,DDS信号发生的来源就是ROM当中存储的波形数据,而波形数据的来源是所输入的地址addr,这么一来我们就将目光放在addr上面。
addr是一个12位的变量,来源于频率信号叠加寄存器的高12位,为什么要设置叠加器为32位,就像前文所述,为了提高但个周期的分辨率,使得输出更加精确。接下来让我们分别来看看398420 ns和398400 ns的波形细节。
误差细节时序图
我们发现多的20ns(一个CLK周期)来源于高电平期间的差异,所以我们进一步的查看高电平的细节。先来看看199220ns的这个高电平。
多一个CLK周期高电平开始细节
多一个CLK周期高电平结束细节
可见,高电平的持续时间应该为 20ns × 5 × (2048-0) = 204800ns,这和我们的波形图上实现显示的差了204800ns - 199220ns = 5,580ns。我们记为error1。
然后再来看199200ns的这个高电平。
少一个CLK周期高电平开始细节
少一个CLK周期高电平结束细节
上面两幅图的高电平的持续时间应该为 20ns × 5 × (2048-0) = 204800ns,这和我们的波形图上实现显示的差了204800ns - 199200ns = 5600ns。我们记为error2。
好了,通过分析波形我们发现逻辑设计上是没有问题的,该在什么时候下降,什么时候上升都符合要求,那么其中的error1、2是从何而来的呢?这时候我们注意到上面的这个5,代表5个时钟周期变化一次地址,那么此时我们不禁要问,这个5个时钟周期变化一次,它真的那么诚实可靠,人畜无害吗,答案是否定的。
问题图1
问题图2
像上面两幅这样的问题时序还有很多,相信数量也是5600ns / 20ns = 280个。好了接下来问题就是分析问什么会少一个了。
讲了这么久,终于该回到代码层面了。让我们看一下地址递增的部分代码。

reg     [31:0]  freq_cnt;
assign addr = freq_cnt[31:20] + phase_offset;
always @(posedge sys_clk or posedge sys_rst)begin
	if(sys_rst)
		freq_cnt <= 32'd0;
	else
		freq_cnt <= freq_cnt + freq_k;
end

从上图中可以看到。此时的freq_k为215607,而我们的地址是从220开始进位的,也就是说计数220 = 1,048,576次之后,addr + 1。那么215607需要加多少次呢?如下表所示:(表中标红表示进位)
在这里插入图片描述
上表我想已经表示的很明白了,因为每一轮都会有一个误差叠加,最后不断累积起来就会造成每一个周期输出的周期数不一样,虽然每一次都只是一个CLK的误差,但是总归是有误差的。目前我们给的频率参数是2510Hz,有这个现象,那前面2550、2530Hz有没有这个情况呢?答案是有的,如下图所示:
2550Hz的情况高电平开始时
2550Hz的情况高电平结束时
同样的,2048 × 20 × 5 = 204800ns,但是实际上仿真结果为:196080ns,也是有这个误差的。至于为什么此时没有出现前面那样子一个CLK周期的误差,原因我想就是刚刚好误差的次数为偶数,根据差分原理消除了。不论怎样,这个问题我们都应该消除,下面探讨一下解决办法。

问题解决

知道了问题所在,接下来找找解决办法,既然已经确定这个是算法的问题,就从算法设计上下功夫。首先可以确定的一点是,我们既然已经得到对应的freq_k即每一次叠加的值,那么每一次就应该叠加这么多,也就是说每一次就应该加这么多才能得到正确的输出频率,如果修改了这个输出的频率值也就是错的了。
解决办法1:一开始我想的办法是,依旧按照这个计数值叠加,如果叠加的值超过了220 = 1,048,576马上让叠加的值清0,然后让addr+1。这样子相当于舍弃了每一次的叠加误差。但是后来想了想,这样子的频率输出误差也太大了,分析如下:假设我刚好要计数5次,那么每一次计数的值就是1048576 / 5 = 209,715.2 ≈209716(必须要向上取整);而如若要计数计数6次,对应的就是174,763;4次对应262,144。这样子按照之前根据频率计算次数的公式对应回去的频率分别为:3,051.8Hz、2,441Hz和2,034.5Hz,也就是说,位于这三个点之间的设定频率都会被忽略,然后变成上述的固定点的频率值,有点类似于模数转换中的量化误差。而如若要减少量化误差,办法是降低addr跳变的临界值也就是1048576,即在N不变的前提下,提高ROM的深度,但这总归不是个好办法。
解决办法2:通过上述分析我们知道,叠加误差不能够省略,那么我们又如何让叠加误差“进场”呢?通过思考,我发现叠加误差本来就要进去的,那么转变枪头,将目光从叠加误差转移到波形误差,为什么上一个周期的叠加误差和下一个周期的叠加误差会不同?如何才能让它们变得相同?为什么会不同,因为每一个周期在结束的时候,对应的freq_cnt的不为0,这边就也会有一个误差(freq_cnt就是那个32位的寄存器,取高12位作为ROM的寻址信息)。还是上面那个例子,地址+1所需要的叠加量为1 048 576,然后我们每一次始终到来时给它 +215 607,但是如果每一个周期结束时都会有一个会累加的误差,当这个误差累加到215 607时,就省了一个时钟周期的叠加,此时误差重新回0,这就是为什么会出现周期之间会有一个CLK周期的误差的原因,之所以前面2550Hz和2530Hz没有出现,是因为他们每一次叠加的误差量太小,而仿真的时间太短,还没来得及叠加上去就跳到下一个频率周期了!针对这个周期间的误差,我想到解决办法是,检测到一个周期输出完毕之后,手动的将叠加器变量给清空,也就是说人为消除周期叠加误差。这个误差和上面那个不同,上面那个累加误差决定着输出的精度,这个周期误差的值不会超过一个地址周期,可以忽略不计,虽然精度会受损,但总归是消除了这个误差了。
误差消除

结语

起始本次设计的bug来源就是算法的问题,最后的解决办法也想但简单就是清个0就好了。啰嗦了这么多总归是讲完了,说实话,当初在调DDS的时候,根本就没想这么多,只觉得输出的时候频率正确即可。也根本发现不了这个问题,毕竟1个CLK周期的误差,真的太难发现了。所幸一位朋友发现了这个问题,在与他交流许久后才做了这次的实验,最终也是不负使命,找出了问题所在,也于此感谢他的细心观察的结果。

资源链接

百度网盘链接:链接:https://pan.baidu.com/s/1LqOMz0f9FBW-6bxZFUzlQA?pwd=wvq6
提取码:wvq6
CSDN链接:FPGA控制AD9767实现DDS信号发生器

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值