[FPGA]DDS与并行ADC、DAC
文章目录
基础知识
高速数据转换器的时钟管理
-
ACLK和DCLK的区别
-
用于进行数据转换的ACLK,应尽量高质量
-
最好不要经过FPGA,否则该时钟的波形质量会下降
-
用同步数据的DCLK,只要满足数据的建立保持特性即可
-
-
本实验配套电路板的局限性
-
由于成本问题,本实验配套电路板没有采用时钟管理芯片
-
DAC、ADC的数据转换时钟经过了FPGA芯片内部
-
对于高指标的设计不能这样做
-
本设计的时钟方案
-
使用FPGA片内的锁相环
由50MHz晶振的时钟信号,倍频得到80MHz的DAC时钟,分频得到20MHz的ADC时钟
-
用FPGA的逻辑对内部的80MHz时钟反相作为DAC芯片时钟,用于满足DAC数据的建立-保持时序
-
ADC芯片在时钟的上跳锁存数据
ADC时钟同样进行反相,作为ADC接口逻辑的驱动时钟
-
注意:这种用FPGA的内部逻辑电路进行ADC、 DAC的时钟相位调整的方法,其时钟的抖动和相位噪声不好。高质量设计中,通常用专门的时钟管理芯片完成时钟的相位调节
转换器的垂直域(电压、数值)问题
-
AD9762是无符号的DAC器件
- 有符号补码需要先把高位取反再送给DAC
- 数据转换的对应关系如图:
-
AD9200是无符号的ADC
- 最大电压对应MAX值,0电压对应0值
-
注意ADC芯片还有一个溢出 out of range 信号
- ADC输入的电压如果超限,则溢出信号置位1
- 数据溢出对于ADC的采样结果是灾难性的,尤其是对于频域处理算法,数据溢出效果会在后面说明
- 一旦检测到ADC溢出,必须先调整其不溢出之后, 再进行其他处理动作。
工程设计
在之前的基础之上,本设计增加了DAC和ADC的芯片来输出出入波形,所以需要增加对应需要的功能即可。向DAC送入数据和从ADC接收数据需要进行处理,所以需要增加两个接口程序。此外,本实验用的外扩子卡没有时钟管理芯片,所以需要使用pll进行时钟获得所需的时钟并送入子卡。
DAC接口
上文所述,数据送入DAC需要将补码转化为无符号数,接口需有数据转化的功能,即将最高位取反。输出信号幅度要可调,以此来观察数据溢出对ADC的影响,所以需要将数据大小根据要求等比例调整。这里选择开关作为输入,ROM数据除以输入作为输出的方式来调整输出信号的幅度。为了将开关的00映射为1,即不压缩幅度,选择了右移输入位数的方法。此外,本实验用到的DAC芯片为AD9762,输入为12位,而之前写的DDS输出为8位,所以还需要进行移位补零工作。
module DAC_interface( CLKIN , DATIN , SCALE , DAT2DAC );
input CLKIN;
input [8-1:0] DATIN ;
input [2-1:0] SCALE ;
output [12-1:0] DAT2DAC ;
reg [12-1:0] DAT2DAC ;
reg [12-1:0] datin_R1;
always @ (posedge CLKIN) begin
datin_R1 [11] <= ~ DATIN[7]; // inverse the msb to unsigned
datin_R1 [10: 4] <= DATIN[6:0];
datin_R1 [3 : 0] <= 0;
DAT2DAC <= (datin_R1 >> SCALE);
end
endmodule
ADC接口
需要先从ADC接收ADC时钟信号,然后按ADC时钟信号来接收数据信号,此外还要注意ADC的溢出信号OTR。此外把STBY_ADC信号置1,则ADC一直处于工作状态。
module ADC_interface( CLK_ADC , DAT_ADC , OTR_ADC , OTR_OUT , STBY_ADC , DOUT );
input CLK_ADC ;
input [9:0] DAT_ADC ;
input OTR_ADC ;
output OTR_OUT ;
output STBY_ADC ;
output [9:0] DOUT ;
reg [9:0] DOUT ;
reg OTR_OUT;
always @ (posedge CLK_ADC) begin
DOUT <= DAT_ADC ;
OTR_OUT <= OTR_ADC;
end
assign STBY_ADC = 1'b0;
endmodule
锁相环
锁相环部分就是对FPGA内部ATLPLL锁相环模块进行配置,从而生成我们所需的20MHz和80MHz时钟信号即可。
ATLPPL的端口有点多,代码太长了,放一个配置后的锁相环的RTL视图:
顶层设计
按照需求连接各部分并分配管脚即可。连接到子卡的端口按照子卡每个端口的功能去找FPGA板子上对应管脚的编号。
左上角为开关和高低电平的输入,其中SW9和SW8控制DAC的输出幅度,其余开关用于控制DDS频率字。
左下角为PPL,输入50MHz晶振振生成80MHz的DAC时钟和20MHzADC时钟。
右上角为DDS,生成补码正弦波,转成无符号后送至DAC。
右下角为ADC的数据接收接口。
两个子卡的时钟需要反相,所以使用了两个非门来控制。
数据测量与分析
SignalTap检测数据
首先使用SMA同轴电缆连接DAC的输出和ADC的输入。其实这样直接相连是不严谨的,实际应用中应该接一个低通滤波或带通滤波。本实验只是为了对DAC和ADC有一个基础的认识。
拨动开关调整为SW9、SW0闭合,其他断开。此时输出幅度被压缩至一半,输出频率为 f = f d a c × C N T 2 n = f d a c × 1 2 10 = 78125 f=f_{dac}\times\frac{CNT}{2^n}=f_{dac}\times\frac1{2^{10}}=78125 f=fdac×2nCNT=fdac×2101=78125Hz。
SignalTap采样数据为:
SIgnalTap选择的采样频率为ADC的时钟,数周期得输出信号的周期为时钟信号的256倍,即 f = 1 256 f a d c = 78125 f=\frac 1{256}f_{adc}=78125 f=2561fadc=78125Hz,与理论推导相同。
观察到此时ADC采样数据已经接近ADC的上下限,不难猜想得如果将SW9断开,输出信号幅值变回原值,则ADC采样会产生溢出。断开SW9后采样得:
如图,出现了溢出产生的失真。其中顶部切割失真是由于信号幅值过高产生了数据溢出。可以看到顶部切割失真的部分对应周期的OTR_OUT信号输出高电平,即存在数据溢出。底部切割失真应该是超出了芯片的转换范围,数据手册太长了,我没找到。
当存在数据溢出时,ADC采到的信号均为高电平,那么就只知道数据超出ADC的转换范围了,基本数据丢失了属于是,获得的数据基本没有意义。所以当ADC出现数据溢出时必须先处理数据使其不溢出,才能够继续下一步的操作。
频谱分析
用matlab进行频谱分析画图得:
DAC输出直接连到ADC,对于DAC中的所有信号频率来说是不满足奈奎斯特采样理论的。但高频率分量其实都是杂波和噪音,并且幅值非常小,远小于所需的频率,混叠部分可以忽略不计。而我们所需的都是小于10MHz的,对于它们来说是满足奈奎斯特采样理论的。所以可以直接将DAC输入连到ADC上。
频率为0处有幅值,查阅ADC的数据手册可知是因为ADC含基准电压。此外幅值最大的频率分量便是我们所要生成的信号。
频谱的最高频率为采样频率的一半,这是奈奎斯特采样理论的内容。
观察DAC的频谱,噪音的包络类似一个个小的包连成。这是因为数字信号是间断的,相当于时域为正弦包络的一串冲激卷积门函数,频域即为乘上Sa函数。
参数修改
ROM数据位数修改
修改目标
程序本来设计的ROM存储数据为8位,而本工程中使用的DAC可以输入十二位,所以进行修改提高精度。将ROM数据提高到16位并输出12位数据到DAC。
操作步骤
首先需要将ROM的数据更新到16位。然后需要匹配余下的传输数据的部分。
DDS文件中rom_rdW的带宽改为十六位,将ROM的位数全部输出到rom_rdW。然后在输出语句中添加输出位宽,即输出rom_rdW[16-1:16-12]。
因为输入为12位,与DAC输入位宽匹配,所以不需要在DAC接口模块进行移位,直接最高位取反即可。不要忘记缩放DAC的输出缩放。修改后的代码为:
always @ (posedge CLKIN) begin
datin_R1 [11] <= ~ DATIN[11]; // inverse the msb to unsigned
datin_R1 [10: 0] <= DATIN[10:0];
DAT2DAC <= (datin_R1 >> SCALE);
end
最后将两个文件重新生成bdf符号并在顶层文件中进行替换。
频率控制修改
修改目标
只需要用到0.5MHz,1MHz,1.5MHz,2MHz,2.5MHz,3.5MHz,4.5MHz,5.5MHz八个频率的正弦波,所以只用三个拨码开关控制,000至111分别映射为这八个输出频率。
操作步骤
首先要算八个频率对应的频率字,然后在DDS中加上一个频率字的选择即可。
计算公式即为之前得出的 f = f s y s × C N T 2 n f=f_{sys}\times\frac{CNT}{2^n} f=fsys×2nCNT,因为Verilog中没有小数,所以取位宽n为最大值32来使误差尽可能小。由公式计算出频率增量CNT,取整,转化为二进制,然后修改原代码中频率计算的部分,由输入为频率字赋对应的值。
修改后的代码为:
always @ (posedge CLK or posedge RST) begin
if(RST) begin
phase_acc_R <= 0;
freqin_R <= 0;
end
else begin
if(FREQEN) begin
// freqin_R <= FREQIN;
//bgein
case(FREQIN[24:22])
3'b000: freqin_R <= 32'b 0000_0001_1001_1001_1001_1001_1001_1010;
3'b001: freqin_R <= 32'b 0000_0011_0011_0011_0011_0011_0011_0011;
3'b010: freqin_R <= 32'b 0000_0100_1100_1100_1100_1100_1100_1101;
3'b011: freqin_R <= 32'b 0000_0110_0110_0110_0110_0110_0110_0110;
3'b100: freqin_R <= 32'b 0000_1000_0000_0000_0000_0000_0000_0000;
3'b101: freqin_R <= 32'b 0000_1011_0011_0011_0011_0011_0011_0011;
3'b110: freqin_R <= 32'b 0000_1110_0110_0110_0110_0110_0110_0110;
3'b111: freqin_R <= 32'b 0001_0001_1001_1001_1001_1001_1001_1010;
endcase
//end
end // if(FREQEN)
if(DDSEN) begin
phase_acc_R <= phase_acc_R + freqin_R;
end // if(DDSEN)
if(ddsen_R2) begin
DDSOUT <= rom_rdW[16-1:16-12];
end
end
end
assign rom_raW = phase_acc_R[32-1: 32-1-7+1];
结果验证
SignalTap数据采集验证
这里采集的是DAC的输出数据,时钟频率为80MHz,以此来计算实际获得的信号的频率。
将SignalTap采集到的数据放大,可以数得/算得一个信号周期对应的时钟周期,以此比例关系估算实际信号的频率。
测量计算结果为:
频谱分析验证
SignTap数周期只是估算,精确计算需要像上文一样导出数据进行频谱分析。有8组数据需要分析,可以录制宏来减少体力劳动。
MATLAB频谱分析画图结果如图:
采样计算结果并不十分准确,因为最小分辨率的存在,会有误差,可以用示波器测一测。频谱分析中使用的采样频率为20MHz,采样点数为8192个, F 0 = 1 T 0 = 1 1 f s × 2 13 ≈ 2440 F_0=\frac1{T_0}=\frac1{\frac1{f_s}\times2^{13}}\approx2440 F0=T01=fs1×2131≈2440,所以最小分辨率为2440Hz。
示波器测试也有误差,误差是看示波器价格的。因为示波器触发电平的存在,可以多几个周期来测。大概屏幕上显示5到10个周期左右测出来的频率就会比较稳定,否则会有较大的波动。
时钟修改
在本例中,ADC的20MHz时钟和DAC的80MHz时钟是从一个晶振用PLL分出来的,这种时钟叫做同步时钟。这样设计的电路含有两个时钟,为多周期电路。在DAC及之前的部分为80MHz,在ADC及之后的部分为20MHz。ADC芯片的时钟要求为20MHz。为了提高计算密度和需求,想要在ADC之后的电路也使用80MHz的时钟,即用80MHz的时钟处理ADC送入的20MHz的数据流。
按照设想的修改,则电路可认为只有一个80MHz的时钟。其中在ADC的部分,用80MHz的时钟和时间基准电路分出一个20MHz的送入ADC使用。
修改后的时钟为:
修改后ADC所使用的20MHz并不算是系统的时钟,对于系统来说只是一个普通的信号。系统以20MHz的频率从ADC采样读取数据,然后继续以80MHz的时钟处理数据。
修改后的电路使用时钟示意: