1. 实验内容
- 使用 Vivado的IPI工具,例化DDS IP;
- DDS需要能够配置频率字(相位增量);
- DDS工作时钟使用PL的板载50MHz时钟;
- 使用ILA工具观察波形, 使用VIO设定频率字;
- 在ILA的波形窗口里,观察你设定的波形的周期,验证你频率字设定的正确性;
- 把ILA波形导出到CSV文件,波形样点长度不小于2048点,在Matlab里分析波形的频谱,验证你生成波形的正确性;
- 使用VIO更改频率字,分别生成1MHz和3MHz的正弦波形。使用以上流程,验证你输出波形的正确性。
2. 实验背景知识
2.1. DDS的概念
DDS(Direct Digital Synthesis):直接数字频率合成技术,是从相位概念出发直接合成所需波形的一种频率合成技术,通过控制相位的变化速度,直接产生各种不同频率、不同波形信号的一种频率合成方法。
2.2. DDS系统结构
DDS系统主要由相位累加器、波形存储器、数模(D/A)转换器和低通滤波器等四个大的结构组成,其结构框图如下图所示。
图中,参考频率为固定值;频率控制字k,用来调整输出信号的频率;相位累加器由N位加法器与N位累加寄存器构成,它根据频率控制字k,完成相位值的累加,并将此累加值输入到波形存储器中;波形存储器将相位累积器的值作为当地址,查找与相位值对应的信号数据,输出到D/A转换器;D/A转换器,将波形存储器输出的数字量转换为与之对应的模拟量;由于D/A转换器存在量化误差,输出波形中存在混叠,需要在输出端使用低通滤波器进行滤波,提高信号的输出性能。
DDS输出波形精度
DDS原理输出波形的精度与DAC分辨率、波形细分、输出滤波电路性能密切相关。
DAC分辨率:DAC将数字量转换为模拟量,在转换过程中存在量化误差,如图5所示。量化误差代表着波形在幅值上对要求波形的接近程度,DAC位数越多,即分辨率越高,DAC生成的信号越接近与所要求的模拟值。
波形细分:波形细分精度影响着波形形状的精度,对于一个波形来说,采样间隔越小,采样点数越多,越能够获得关于波形更多的信息。同理,DDS的波形数据的细分精度越高、点数越多,生成的波形效果越好。
输出滤波电路: DAC输出会存在混叠噪声,为了提高输出的性能需要使用滤波电路滤除掉其中的高频噪声,提高输出精度。
2.3. vio的使用
使用场景:在使用In system debug时需要使用按键触发查看相关信号,但不想用板子上的按键。
VIO:Virtual input output,即虚拟IO。
主要用作虚拟IO使用;VIO的输出可以控制模块的输入,VIO的输入可以显示模块的输出值。
连接如下图:
3. 实验步骤
1. 使用vivado的IP Catalog工具,配置DDS,ILA,VIO参数
DDS参数配置:
Output中的Polarity取消勾选Negative Sine(默认是勾选的)。这个勾选上的话输出的数值极性相反。
VIO参数配置:
位数为2,说明可以控制四种频率控制字,得到四种不同频率的正弦波。
ILA参数配置:
波形样点数为4096
一个时钟输入clk,其他三个输入分别为:
probe0:连接VIO按键控制频率控制字(key_PINC)
probe1:连接频率字(Fword)
probe2:连接输出正弦波形
2. 设计顶层(top)模块和按键控制频率控制字(Fword_set)模块,例化DDS,ILA,VIO,并将它们的接口对应连接起来。
程序结构:
顶层(top)模块:
`timescale 1ns / 1ps
module top(
input sys_clk ,//系统时钟 50MHz T=20ns
input sys_rst_n //系统复位
);
// -----------0、VIO按键控制频率控制字(key_PINC)
wire [1:0] key_PINC;
vio_0 vio_0 (
.clk(sys_clk), // input wire clk
.probe_out0(key_PINC) // output wire [1 : 0] probe_out0
);
//---------------1、信号频率控制模块--------------//
wire [23:0] Fword ; //频率字
Fword_set Fword_set(
//input
.clk (sys_clk ),
.rst_n (sys_rst_n ),
.key_PINC (key_PINC ),
//output
.Fword (Fword )
);
//---------------2、DDS模块--------------//
//input
wire [0:0] fre_ctrl_word_en ;
//output
wire [0:0] dds_m_axis_data_tvalid ;
wire [7:0] dds_m_axis_data_tdata ;
wire [0:0] dds_m_axis_phase_tvalid ;
wire [23:0] dds_m_axis_phase_tdata ;
assign fre_ctrl_word_en=1'b1;
//例化DDS IP
dds_compiler_0 dds_compiler_0 (
.aclk (sys_clk ), // input wire aclk
.s_axis_config_tvalid (fre_ctrl_word_en ), // input wire s_axis_config_tvalid
.s_axis_config_tdata (Fword ), // input wire [23: 0] s_axis_config_tdata
.m_axis_data_tvalid (dds_m_axis_data_tvalid ), // output wire m_axis_data_tvalid
.m_axis_data_tdata (dds_m_axis_data_tdata ), // output wire [15 : 0] m_axis_data_tdata
.m_axis_phase_tvalid (dds_m_axis_phase_tvalid ), // output wire m_axis_phase_tvalid
.m_axis_phase_tdata (dds_m_axis_phase_tdata ) // output wire [23 : 0] m_axis_phase_tdata
);
//例化ILA IP
ila_0 ila_0 (
.clk(sys_clk), // input wire clk
.probe0(key_PINC), // input wire [1:0]
.probe1(Fword), // input wire [23:0]
.probe2(dds_m_axis_data_tdata) // input wire [7:0]
);
endmodule
按键控制频率控制字(Fword_set)模块:
`timescale 1ns / 1ps
//
// 通过按键来选择对应的频率控制字,进而选择对应的信号频率
//
module Fword_set(
input clk ,
input rst_n ,
input [1:0] key_PINC ,
output reg [23:0] Fword
);
//always@(posedge clk or negedge rst_n)
//begin
// if(!rst_n)
// key_sel <= 4'd0;
// else
// key_sel <= key_sel;
//end
// The output frequency(f_out ) , of the DDS waveform is a function of the system clock frequency(f_clk ) .
// the phase width, that is, number of bits (B ) in the phase accumulator
// and the phase increment value (deta_theta) .
// The output frequency in Hertz is defined by:f_out=f_clk*deta_theta/(2^B)
// fre_ctrl_word是如何确定的?
// 根据IP核的summery, phase width=20bits Frequency per channel=100MHz
// 输出频率的计算公式f_out=f_clk*deta_theta/(2^B)=100M* 104857/(2^20 )= 10M
always@(*)
begin
case(key_PINC)
0: Fword <= 'h28f5; //1Mhz 10485 每次相位增加的值 deta_theta
1: Fword <= 'h51eb; //2Mhz 20971
2: Fword <= 'h7ae1; //3Mhz 31457
3: Fword <= 'ha3d7; //4Mhz 41943
endcase
end
endmodule
输出频率的计算公式:
f
o
u
t
=
f
c
l
k
∗
△
θ
(
2
B
)
,
f
c
l
k
:
工
作
时
钟
△
θ
:
相
位
增
量
,
B
:
p
h
a
s
e
w
i
d
t
h
f_{out}=\frac{f_{clk}*\bigtriangleup \theta}{(2^B)},f_{clk}:工作时钟 \bigtriangleup \theta :相位增量,B:phase width
fout=(2B)fclk∗△θ,fclk:工作时钟△θ:相位增量,B:phasewidth
例如:
f
o
u
t
=
50
M
∗
10485
2
19
=
1
M
,
2
20
=
10
M
=
104857
f_{out}=\frac{50M* 10485}{2^{19}} = 1M,2^{20}=10M=104857
fout=21950M∗10485=1M,220=10M=104857
约束文件:
set_property PACKAGE_PIN U18 [get_ports sys_clk]
set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]
set_property PACKAGE_PIN N15 [get_ports sys_rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports sys_rst_n]
#时序约束
create_clock -period 20.000 -name sys_clk -waveform {0.000 10.000} [get_ports sys_clk]
3. RTL图
4. 生成bitstream,利用VIO控制频率控制字,利用ILA观察生成的波形。
1Mhz正弦波:
key_PINC=0
观察波形时要将 Waveform Style 设置为 Analog(模拟),Radix 设置为 Signed Decimal(有符号十进制)。
通过这步,可以看出DDS产生的是有符号数据,范围为[-128,127]。
3Mhz正弦波:
key_PINC=2
5. 把ILA波形导出到CSV文件,在Matlab里分析波形的频谱,验证生成波形的正确性。
导出csv文件,并将导出的csv文件拖入matlab工作区中导入数据
新建脚本,输入如下代码:
m_axis_data_tdata150 = iladata1Mhz{:,6}; %取出数据
SampleinWindow = iladata1Mhz{:,2};
fs=50000000; %采样频率50Mhz
N=4096; %采样点数
n=0:N-1;
t=n/fs; %时间序列
f=n*fs/N; %频率序列
figure;
subplot(2,1,1)
plot(t,m_axis_data_tdata150); %时域波形
title('时域波形');xlabel('时间/s');ylabel('幅值');
y=abs(fft(m_axis_data_tdata150,N));
subplot(2,1,2)
plot(f,y); %频域波形
title('频域波形');xlabel('频率/hz');ylabel('幅值');
1Mhz时域和频域波形:
频域波形中有两个明显的尖峰,第一个尖峰位于1Mhz,第二个尖峰位于49Mhz(共轭对称特性)。从波形中可验证输出波形频率为1Mhz。
时域波形也可验证可验证输出波形频率为1Mhz:从时域波形中可看出输出的正弦波总共有82个周期,由采样频率和采样点数可推算出总的采样时间 :
t
=
4096
50
M
=
8.192
×
1
0
−
5
t=\frac{4096}{50M}=8.192\times10^{-5}
t=50M4096=8.192×10−5
所以一个周期占用的时间即输出频率
f
o
u
t
=
82
8.192
×
1
0
−
5
=
1
M
h
z
f_{out}=\frac{82}{8.192\times10^{-5}}=1Mhz
fout=8.192×10−582=1Mhz
3Mhz时域和频域波形:
同理,从频域波形中可验证输出波形频率为3Mhz。
参考资料:
[1] CSDN:https://blog.csdn.net/weixin_43358703/article/details/103492234
[2] CSDN:https://www.cnblogs.com/kingstacker/p/9810704.html
[3] 官方手册:pg141-dds-compiler.pdf
[4] CSDN:https://blog.csdn.net/qq_34070723/article/details/91549184