ZYNQ学习笔记(一):基于ZYNQ7020、AN108的DDS实验(VIO可控频率字)

 

使用VIO设定频率字,生成某频率的正弦波形
在ILA中观察波形,观察设定的波形的周期,来验证频率是否设定正确
把ILA波形导出到CSV文件
在Matlab里分析波形的频谱,验证生成波形的正确性。

二、DDS 基本原理、

        DDS 的基本结构主要由相位累加器、相位调制器、波形数据表 ROM、D/A 转换器等四大结构组成,DDS 基本结构图如下所示。

3199b5b9351c44a7af75817ace5aee52.png

 

      如图可以看出主要有四部分:

      相位累加器(Phase Accumulator):相位累加器是DDS的核心组件之一。它由N位加法器和N位寄存器构成。在每个时钟脉冲到达时,相位累加器将频率控制字(或相位增量值)与累加寄存器中的相位数据相加。累加的结果反馈回累加寄存器的数据输入,以便在下一个时钟脉冲到达时继续相加。这种过程导致了线性相位累加,也就是相位随时间线性增加。当相位累加器的值达到最大值时,会产生一次溢出,这表示完成了一个完整的信号周期。

      相位调制器(Phase Modulator):相位调制器允许你控制输出信号的相位参数。通过改变相位控制字(P_WORD),可以引入相位调制,从而产生相移的效果。用相位调制器输出的数据,作为波形存储器的相位采样地址,这样就可以把存储在波形存储器里的波形采样值经查表找出,完后相位到幅度的转换。

      波形数据表(Waveform Lookup Table):波形数据表存储了DDS输出波形的样本点。每个样本点对应于相位累加器的不同值。通过查找表,DDS可以获取相应相位的幅度信息。这允许DDS生成不同波形,包括正弦波、锯齿波、方波等。

      D/A转换器(Digital-to-Analog Converter):DDS输出的数据是数字形式的,D/A转换器将数字信号转换为模拟信号,以便在外部电路中使用。DDS的输出信号随着时间变化,D/A转换器将其转换为模拟波形,供其他设备或电路使用。 

       此外,如图可以看出有三个输入端:系统时钟CLOCK;频率字输入(后面用F_WORD表示);相位字输入(后面用P_WORD表示)。一个输出端:信号输出。

       在相位调制器之前的两个累加寄存器的作用是,使频率字和相位字输入的时候不会影响到相位累加器和相位调制器的运行。相位调制器的位数与ROM地址线位数相同,即输出时按照相位调制器输出的值对照ROM地址,输出该地址对应数据。假设相位累加器位数为N,相位调制器位数为M。N一般大于M,所以N使用时,将高M位传入相位调制器,低N-M位用于F_WORD值计数。

       DDS系统运行流程为:F_WORD值输入暂存于累加寄存器,在相位累加器中不断累加直到计满低N-M位后溢出,将高M位传入相位调制器,相位调制器接到P_WORD值再在此基础上加上P_WORD值(P值为定值的时候输出波形的形状不变,相位平移P值),此时根据相位调制器输出的M位值查找对应ROM地址,输出地址对应数据。

      举个例子,假设输入F_WORD=1,N=32,M=12,ROM地址线12位,ROM数据线8位,P_WORD值暂不考虑。

      那么数据传输如下:

  (1)相位累加器:N-M=20,即N低20位用来F_WORD值自加,直至累加到2^20后溢出到高12位。共需要累加eq?2%5E%7B20%7D/1次。即得到一个进位所需时间

e0cafe4e8e074fdb9fdec542ed197b7c.png

(T_CLOCK为系统时钟周期)

   (2)相位调制器:接到相位累加器高12位数据,与P_WORD相加后寻址。12位计满(相位调制器计满)需要eq?2%5E%7B12%7D次进位。经过eq?2%5E%7B12%7D次进位后将储存在ROM内的波形完全读出,所以:

        输出周期:

204600c568dc478c8bdec2b5885e0ce0.png

        输出频率:

491200ac200d404ba6673048bdb5c54a.png

 (F_CLOCK为系统时钟频率)

       至此,我们得到了输出频率的公式,根据公式我们可以配置一些相应的固定参数,进而可以通过VIO模拟按键来控制DDS的频率控制字,也就是F_WORD的值,从而得到不同频率的正弦波。

三、Vivado工程建立、

点击“Create Project ”来创建一个新的工程

b1e742b554b74d54bd25244b52b2f6d7.png

 
弹出的页面点击Nest
下面输入工程的名称和路径

bdb0fa76eebb4fb8b3d508071ffe10d4.png

工程类型选择“RTL Project”

302a6b8786fc4e4f9f2e10e672fa344f.png

之后的两个页面均直接点击Nest ,因为使用的是黑金7020 开发板,可以在搜索栏直接输入“xc7z020clg400-2”,单击选中“Part”一栏的芯片型号,然后点击“Next”按钮。

6d8950fea8da4d3fb162985025da6e6b.png

在弹出的页面直接点击“Finish”按钮完成工程的创建。
 

四、添加设计源文件、模块例化、

3.1 添加设计源文件

工程创建完成后,就进入了 Vivado 的工程主界面,点击“Sources”窗口中的“+”号,选择添加设计源文件,然后点击“Next”按钮。
423f1054e5794d16800b35b1ca060ef8.jpeg
对创建的设计文件进行命名,然后点击“OK”按钮
 
14d03f4856284895b5413ff783b2606f.png
后面弹出的页面依次点击Finish — OK — Yes,这时工程主界面的“Sources”窗口中就出现了我们刚刚创建的源文件,按相同的步骤再创建一个频率控制模块的设计文件,创建完成后如图所示

0497ee575eeb4416a890f097993bb9a7.png

3.2 模块例化

添加DDS IP核,搜索后发现DDS IP核有很多个,这里先对几个IP核注释一下:

  1. Modulation下的DDS Compiler: 这种IP核通常用于生成带调制的信号。如果你的应用需要生成调制后的信号,比如AM(幅度调制)或FM(频率调制)信号,那么选择这个选项可能是合适的。

  2. Trig Functions下的DDS Compiler: 这个选项可能更适合需要执行三角函数运算(如正弦、余弦、正切等)的应用。如果你需要在DDS中执行特定的三角函数计算,可以选择这个选项。

  3. Waveform Synthesis下的DDS Compiler: 如果你只需生成基本的波形信号,如正弦波、方波、锯齿波等,而不需要调制或复杂的三角函数运算,那么选择这个选项可能更简单和适用

这里我们先选择第三个,如图所示

94c3c78bf2804917bbb8e49e80cb44f2.png

双击所选中的IP核,如图所示进行修改

fa5d18355c214433ab95f6759749f8d7.png

首先是使用板载PL 50MHz时钟,对应第二节中的CLOCK。

这里的"Phase Width"选项允许你设置DDS输出信号中相位累加器的位数或相位分辨率,相位累加器的位数决定了DDS输出信号的相位精度,更高的位数会提供更高的相位分辨率,允许DDS生成更精确的输出信号。它所对应的也就是公式中的N,这里我们设置N=20。

9e82587bfeeb407c83328c579b1e1147.png

经过配置输出的是16位宽的正弦信号。

接下来弹出的页面依次点击 OK—Generate—OK

下面添加VIO IP核,同理先搜索再双击选中,

0d7d69869c86486c9c33c9d51d0b1099.png

如图修改

3987d69b2fdb412ea0bc63b54841564b.png

6ef3f2472cf447658a0929b560a05614.png

接下来弹出的页面依次点击 OK—Generate—OK

我们还要添加一个ILA核来观察波形

23d98345e3b241948f0fcacfc87952c5.png

6c7379d8055345bbb47a0ab7e77f3b06.png

在这里设置了三个探针,至于采集哪些信号程序设计那一小结再讲~

eab25f8abae448c09960f0b19add5668.png

接下来弹出的页面依次点击 OK—Generate—OK

此时窗口如图所示:

d9c3be65be1347499834d7ef716c93b4.png

五、程序设计、

顶层模块vio_dds程序如下:

程序主要设置了四个模块

      首先是vio_0模块:key_PING是一个2位宽度的输入信号,表示通过外部VIO模块模拟的按键状态。根据 key_PING的值,不同的频率配置会被应用到 Fword上。这个模块通过输入的时钟sys_clk,控制按键并将按键状态输出到key_PING,按键状态可以用于选择要合成的频率。

       vio_control模块:在代码中定义的一个名为Fword的 24位宽度的信号线(或者说是一个 24位的寄存器),用于存储频率控制字F_WORD。这个信号被用作频率控制模块vio_contral_lg的输出,并且在 DDS 模块中被用作输入。这个模块通过接收key_PING 和 Fword信号,用于配置 DDS 模块以产生所需的频率。它通过控制 Fword 信号来实现频率选择。

       dds_compiler_0模块:这个模块是 DDS 模块的实例化,接收一些信号,以产生输出信号,它的输出m_axis_data_tdata包含合成信号的主体部分,即DDS生成的数据信号。     

       ila_0模块:用于采集和分析信号,以便调试和验证系统的功能。以key_PING、Fword 和 m_axis_data_tdata等信号作为采样点。

`timescale 1ns / 1ps

module vio_dds(
    input       sys_clk,            
    input       rst_n              
    );

wire [1 : 0]    key_PINC;

vio_0 u_vio_0 (
    .clk(sys_clk),                // input wire clk
    .probe_out0(key_PINC)         // output wire [1 : 0] probe_out0
);


wire [23 : 0]   Fword;         

vio_contral  vio_contral_lg(
    .clk(sys_clk), 
    .rst_n(rst_n), 
    .key_PINC(key_PINC), 
    .Fword(Fword)
);
wire [0:0]      fre_ctrl_word_en;    

//output
wire [0 : 0]    m_axis_data_tvalid;
wire [31 : 0]   m_axis_data_tdata;
wire [0 : 0]    m_axis_phase_tvalid;
wire [23 : 0]   m_axis_phase_tdata;

assign          fre_ctrl_word_en = 1'b1;

dds_compiler_0 u_dds_compiler_0 (
    .aclk(sys_clk), 
    .s_axis_config_tvalid(fre_ctrl_word_en), 
    .s_axis_config_tdata(Fword),  
    .m_axis_data_tvalid(m_axis_data_tvalid),
    .m_axis_data_tdata(m_axis_data_tdata),
    .m_axis_phase_tvalid(m_axis_phase_tvalid),
    .m_axis_phase_tdata(m_axis_phase_tdata)  
);

// ILA
ila_0 u_ila (
	.clk(sys_clk), // input wire clk


	.probe0(key_PINC), // input wire [1:0]  probe0  
	.probe1(Fword), // input wire [23:0]  probe1 
	.probe2(m_axis_data_tdata) // input wire [31:0]  probe2
);


endmodule

  频率控制模块程序设计如下:

`timescale 1ns / 1ps

module vio_contral(
    input           clk,
    input           rst_n,
    input [1 : 0]   key_PINC,
    output reg [23 : 0] Fword

    );
always@(*)
begin
    case(key_PINC)
        0:  Fword <= 'had37;      //2Mhz 41943.04 取整41943
        1:  Fword <= 'h147ae;     //4Mhz 83886.08 取整83886
        2:  Fword <= 'h1eb85;     //6Mhz 125829.12 取整125829
        3:  Fword <= 'h28f5c;     //8Mhz 167772.16 取整167772
        
    endcase
end


endmodule

 Fword的值是如何确定的呢?是由上文提到的公式:

491200ac200d404ba6673048bdb5c54a.png

 当我想设置能够生成频率为2Mhz的正弦波时,我们只需要确定F_WORD的值就可以,把之前设置DDS IP核时的各种数据带入:

                             102df545b1594a30941c1c1cea742435.png

再转化为16进制,则为ad37,生成其他频率的正弦波同理~

保存文件后~此时窗口更新为:

931d530191334a328dfc3c33350f20b9.png

六、分析与综合、约束输入、

6.1 分析与综合过程

代码输入完毕之后,就可以对设计进行分析(Elaborated)了。点击vivado页面最左侧的“Flow Navigator”窗口中的“Open Elaborated Design”按钮,此时,Vivado 会编译 RTL 源文件并进行全面语法检查,同时Vivado 会生成顶层原理图视图。

82b21e0a3c654ba8a6c5f31d49c73eb7.png

右击上图最顶部的蓝色区域,点击Close关闭分析过程。

接下来点击“Flow Navigator”窗口中的“Run Synthesis”按钮,来对代码进行综合。弹出的窗口依次

均点击“OK”按钮。

7890ea016dcc427188f8776fef71da46.png

最后右击最顶部的蓝色区域,点击Close关闭分析。完成综合过程。

6.2  约束输入

a6407f8a477842899319564210952c58.png

弹出的窗口点击Greate File

5cca8c654bee4c12bf7d0a5d2feec0bf.png

输入文件名,点击“OK”,“Finish”。找到刚创建的约束文件,

ccb1774fcae940b499da169ff41225e0.png

打开输入:

create_clock -period 20.000 [get_ports sys_clk]
set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]
set_property PACKAGE_PIN U18 [get_ports sys_clk]

set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property PACKAGE_PIN K16 [get_ports rst_n]
约束输入完毕之后,就可以开始实现设计了。点击“Flow Navigator”窗口中的“Run Implementation”按钮,在弹出的界面中直接点击 OK 即可,在等段一段时间实现设计完成后会弹出提示窗口,我们直接点击Cancel来关闭窗口。
 
实现设计完成后,我们可以看一下各模块资源的占用情况:点击Open Implemented Design,然后选择Report Utilization。之后会自动弹出资源报告窗口,使用默认配置,点击OK。

c0e6a0ba46e748b79e4fba099775b802.png

运行结果如下:

8cf592d07f3145f08744d6ce92ad11a2.png

工程在逻辑单元(LUT)、LUTRAM资源、以及寄存器资源(FF)和BRAM资源的资源利用率相对较低,输入/输出资源(IO)的利用率相对较高,这可能是因为设计需要连接到外部设备或接口。总的来说,从资源利用率的角度来看,工程在几个方面都具有较低的资源消耗。这可能是一个良好的迹象,表明设计在资源利用方面有很大的余地,可以进一步优化或添加更多的功能,而不会达到FPGA的资源限制。
 
生成与下载比特流过程不再细说~

七、连接测试、

将生成的比特流下载进入开发板后,Vivado 会自动打开 ILA 的调试窗口,点击运行触发的图标,如下图所示:

8a1a7f2a6ad54f659ae852a0a6490ec4.png

查看波形~

9bfa8106018a44069dd00e7335b417a5.png

波形呈现为这种状态是因为原因是Vivado中大多数的IP核采用有符号二进制补码的编码方式,默认为十六进制数值不会进行补码计算。但如果不进行补码计算,那么所有的数都被当作正数处理,所以波形会出现这种形式,我们可以更改"Radix"选项用于指定在波形视图中显示信号时的数据格式。

以有符号十进制格式显示信号的值。这对于有符号数据类型的信号很有用。

4a8e09403d3d41099685e21659f9ed34.png

可以看到生成的DDS正弦信号~

下面测试VIO模块更改频率是否生效,切换到hw_vios窗口,把key_PINC信号添加进去,更改key_PINC[1]与key_PINC[0]的值,修改完成后,再切换到hw_ila_1页面,点击运行重新采集信号,观察波形

3bc92a8b42284604a20877157cb470f3.png

ddbba5cd7f454e4da3432948c32a4969.png

  对应2Mhz。

7048a9cda2354196ab7ee566ee53612b.png

77b20b149d0c4d66a2f026d735de0d84.png

对应4Mhz。

577eadf2f9354ee4934a8f19dcd51f33.png

b30ee6172c6240079cf592feefda271f.png

对应6Mhz。

6699b55bd90b40c8a09eb78714eb2a7b.png

0e8fcdd246ec43deb9e8132a116a9d37.png

对应8Mhz。

八、数据导出与matlab分析、

导出2Mhz的DDS信号数据

30bdd3e0e54a48e498f322595c613ac6.png

66819c4d65fd4bf7940192719883b941.png

 

打开Matlab,新建脚本文件并写入如下代码

close all;
clc;
clear all;

% 从CSV文件加载信号数据
data = xlsread('iladata.csv');

x = data(:, 6);  % 提取信号向量
fs = 50000000; % 设置采样频率为50MHz

% 调用 spectrum_plot 函数来绘制频谱图
tt_str = '频谱图'; % 设置图表标题
spectrum_plot(x, fs, tt_str);

% 绘制DDS信号波形
figure;
plot(data(:, 1), data(:, 6));
title('DDS信号波形');
xlabel('Time');
ylabel('Amplitude');
xlim([0 200]);

grid on;

% spectrum_plot 函数定义
function spectrum_plot(x, fs, tt_str)
  vec_win = kaiser(length(x),10);
  y    = fftshift(fft(x .* vec_win));
  y_dB = 20*log10(abs(y));
  N_f = length(y);
  f_idx = [0:N_f-1].'/N_f * fs - (fs/2);
  figure;plot(f_idx/1E3, y_dB);xlabel('kHz');title(tt_str);
end

5585880fa15b4164b1cb2edc0d60dab3.png

 

可以看到信号频率存在误差但基本一致,至此关于利用VIO可更改频率字的DDS信号生成实验就此结束了。

九、实验改进、

前面完成了DDS信号的生成,以及验证了通过VIO模拟按键来控制DDS的频率控制字,从而得到不同频率的正弦波的可行性,

那么我该如何使生成的DDS波形信号在示波器上显示呢?这就要用到AN108这个ADDA模块了,同时需要在再增加几个模块设计~

 9.1、模块例化

对DDS IP核进行修改:

38d0608e655547beac603250616511f2.png

因为AD108中AD9708是8位,125MSPS的DA转换芯片,AD9280是8位,32MSPS的AD转换芯片,所以我们把这里的输出DDS信号的输出波形位宽由原来的16位改为8位~

对ILA IP核进行修改:

3970dc23d2f74d37a709ccc5d8c46a4f.png

aa0e66eb46534b7685b539fe88d80f88.png

这里我们再多添加一个探针,同时把PROBE2、3的采样深度改为8位,分别采集DA输出以及AD输入信号。

添加clk_wiz IP核:

40bba195397c4f97954a2a25401e851b.png

在顶层模块代码例化了 clk_wiz的 ip核, 产生出25MHz的时钟,用于给ADC提供采样时钟。

a30482c603bf48e49f0c783022feb9d2.png

6bf9eb763e5746d9b105208e3e9ec9ba.png

9.2、程序改进

顶层文件vio_dds:

相较于之前的顶层程序,改进后的程序主要是添加了一个时钟模块与DA数据发送模块,分别用于产生一个输出时钟信号ad_clk这可以用于控制AD芯片的采样速率),以及用于将数据发送到DA芯片。此外更改了输出信号的位宽(适应AD108)以及多添加了一个ILA探点。

`timescale 1ns / 1ps
module vio_dds(
    input       sys_clk,            
    input       rst_n,
     //DA芯片接口
    output                da_clk,  
    output    [7:0]       da_data,  //输出给DA的数据
    //AD芯片接口
    input     [7:0]       ad_data,  //AD输入数据
    output                ad_clk                
    );

wire [1 : 0]    key_PINC;
vio_0 u_vio_0 (
  .clk(sys_clk),                // input wire clk
  .probe_out0(key_PINC)         // output wire [1 : 0] probe_out0
);


wire [23 : 0]   Fword;         
vio_contral  vio_contral_lg(
    .clk(sys_clk), 
    .rst_n(rst_n), 
    .key_PINC(key_PINC), 
    .Fword(Fword)
);


wire [0:0]      fre_ctrl_word_en;    
//output
wire [0 : 0]    m_axis_data_tvalid;
wire [7 : 0]   m_axis_data_tdata;
wire [0 : 0]    m_axis_phase_tvalid;
wire [23 : 0]   m_axis_phase_tdata;

assign          fre_ctrl_word_en = 1'b1;
dds_compiler_0 u_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(m_axis_data_tvalid),        // output wire m_axis_data_tvalid
  .m_axis_data_tdata(m_axis_data_tdata),          // output wire [7 : 0] m_axis_data_tdata
  .m_axis_phase_tvalid(m_axis_phase_tvalid),      // output wire m_axis_phase_tvalid
  .m_axis_phase_tdata(m_axis_phase_tdata)         // output wire [23 : 0] m_axis_phase_tdata
);

//例化PLL
clk_wiz_0 pll_inst
   (
    // Clock out ports
    .clk_out1(ad_clk),     // 给ad_clk 25MHz的频率
    // Status and control signals
    .reset(~rst_n), // input reset
    .locked(locked),       // output locked
   // Clock in ports
    .clk_in1(sys_clk)     // input clk_in1
 );

//DA数据发送
da_wave_send u_da_wave_send(
    .clk         (sys_clk), 
    .rst_n       (rst_n),
    .rd_data     (m_axis_data_tdata),
    .da_clk      (da_clk),  
    .da_data     (da_data)
    );

// ILA
ila_0 u_ila (
	.clk(ad_clk), // input wire clk

	.probe0(key_PINC), // input wire [1:0]  probe0  
	.probe1(Fword), // input wire [23:0]  probe1 
	.probe2(da_data), // input wire [7:0]  probe2
	.probe3(ad_data) // input wire [7:0]  probe3

);


endmodule

DA数据发送模块da_wave_send:

module da_wave_send(
    input                 clk    ,  //时钟
    input                 rst_n  ,  //复位信号,低电平有效    
    input        [7:0]    rd_data,  //读出的数据
    //DA芯片接口
    output                da_clk ,  //DA(AD9708)驱动时钟,最大支持125Mhz时钟
    output       [7:0]    da_data   //输出给DA的数据  
);

//parameter 频率调节控制
parameter  FREQ_ADJ = 8'd5;  //频率调节,FREQ_ADJ的值越大,最终输出的频率越低,范围0~255

//reg define
reg    [7:0]    freq_cnt  ;  //频率调节计数器


assign  da_clk = ~clk;       
assign  da_data = rd_data;   //将读到的数据赋值给DA数据端口

//频率调节计数器
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        freq_cnt <= 8'd0;
    else if(freq_cnt == FREQ_ADJ)    
        freq_cnt <= 8'd0;
    else         
        freq_cnt <= freq_cnt + 8'd1;
end

endmodule

9.3、约束输入

在经过分析与综合过程后,进行约束输入:

############## clock and reset define##################
create_clock -period 20.000 [get_ports sys_clk]
set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]
set_property PACKAGE_PIN U18 [get_ports sys_clk]

set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property PACKAGE_PIN K16 [get_ports rst_n]

########AN108 ON AX7020 and AX7010  J11##################
set_property PACKAGE_PIN F20 [get_ports da_clk]
set_property PACKAGE_PIN F19 [get_ports {da_data[7]}]
set_property PACKAGE_PIN G20 [get_ports {da_data[6]}]
set_property PACKAGE_PIN G19 [get_ports {da_data[5]}]
set_property PACKAGE_PIN H18 [get_ports {da_data[4]}]
set_property PACKAGE_PIN J18 [get_ports {da_data[3]}]
set_property PACKAGE_PIN L20 [get_ports {da_data[2]}]
set_property PACKAGE_PIN L19 [get_ports {da_data[1]}]

set_property PACKAGE_PIN M20 [get_ports {da_data[0]}]
set_property PACKAGE_PIN L17 [get_ports {ad_data[0]}]
set_property PACKAGE_PIN L16 [get_ports {ad_data[1]}]
set_property PACKAGE_PIN M18 [get_ports {ad_data[2]}]
set_property PACKAGE_PIN M17 [get_ports {ad_data[3]}]
set_property PACKAGE_PIN D20 [get_ports {ad_data[4]}]
set_property PACKAGE_PIN D19 [get_ports {ad_data[5]}]
set_property PACKAGE_PIN E19 [get_ports {ad_data[6]}]
set_property PACKAGE_PIN E18 [get_ports {ad_data[7]}]
set_property PACKAGE_PIN G18 [get_ports ad_clk]

set_property IOSTANDARD LVCMOS33 [get_ports da_clk]
set_property IOSTANDARD LVCMOS33 [get_ports {da_data[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {da_data[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {da_data[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {da_data[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {da_data[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {da_data[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {da_data[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {da_data[0]}]

set_property IOSTANDARD LVCMOS33 [get_ports {ad_data[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ad_data[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ad_data[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ad_data[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ad_data[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ad_data[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ad_data[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {ad_data[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports ad_clk]

9.4、连接测试

将 DA 输出通道与示波器相连。
将工程生成的比特流文件下载到 ZYNQ 中后,连接后在 ILA 中观察da_data 数据的变化,因为DA 输出通道和 AD 输入通道并没有相连,所以ad_data数据的波形暂不观察。
观察到的da_data 数据波形如下图所示。
 
a0631dc8325749189e73945577f3ff87.png
 
此刻我们更改"Radix"选项指定显示信号时的数据格式,固然可以在ILA波形视图中显示出正常的正弦波(如上面第六节)。
但是问题也随之出现~示波器上的波形形式并不会出现改变
 
050987eb1191482ab5127d79f5645d9c.png
 
对此我找了好久才发现~
原因是Vivado中大多数的IP核采用有符号二进制补码的编码方式,通过FPGA输出的有符号数与AD9708(AN108的DA芯片)数据格式不匹配。
因为DAC是无符号数编码,这就导致DA部分不认识有符号数,所以需要将有符号数平移,将其平移到无符号数范围,故解决方法是将8位有符号数-128~+127平移到0~255转为无符号数。
因此修改DA数据发送模块da_wave_send的代码:
module da_wave_send(
    input                 clk    ,  //时钟
    input                 rst_n  ,  //复位信号,低电平有效    
    input        [7:0]    rd_data,  //读出的数据
    //DA芯片接口
    output                da_clk ,  //DA(AD9708)驱动时钟,最大支持125Mhz时钟
    output       [7:0]    da_data   //输出给DA的数据  
);

//parameter 频率调节控制
parameter  FREQ_ADJ = 8'd5;  //频率调节,FREQ_ADJ的值越大,最终输出的频率越低,范围0~255

//reg define
reg    [7:0]    freq_cnt  ;  //频率调节计数器
reg    [7:0]    da_data;  // 修改为 reg 类型

assign  da_clk = ~clk;

// 进行偏移和缩放
always @(*) begin
    // 偏移:将最小的负数偏移到0
    if (rd_data < 8'h80)
        da_data = rd_data + 8'h80;
    else
        da_data = rd_data - 8'h80;

    // 缩放:如果需要,将数据缩放到0到255的范围

end

//频率调节计数器
always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)
        freq_cnt <= 8'd0;
    else if(freq_cnt == FREQ_ADJ)    
        freq_cnt <= 8'd0;
    else         
        freq_cnt <= freq_cnt + 8'd1;
end

endmodule

修改完成后生成比特流下载

df04bee3dc694cac836ab62b9ccbe122.png

可以发现不再需要更改"Radix"就可以看到正弦波形~

生成6Mhz的DDS信号

577eadf2f9354ee4934a8f19dcd51f33.png

连接观察示波器:

ad9a03284f894137a6c4c68e1ae00b15.png

可以看到示波器显示6Mhz~
 
将 DA 输出通道和 AD 输入通道连接起来
 

dca5332e5fba4df9a821e3ab32f81753.jpeg

将工程生成的比特流文件下载到 ZYNQ 中后,连接后在 ILA 中观察 ad_data、da_data 数据的变化,观察到的波形如下图所示。
 
fd9aff1f3be74d6f8acbb9034a625b0d.png

可以看到DA输出与AD输入波形一致。

我们可以导出数据再用Matlab分析一下:

close all;
clc;
clear all;

% 从CSV文件加载信号数据
data_da = xlsread('iladata_da_6Mhz.csv');
data_ad = xlsread('iladata_ad_6Mhz.csv');

x_da = data_da(:, 6);  % 提取DA信号向量
x_ad = data_ad(:, 6);  % 提取AD信号向量
fs = 25000000; % 设置采样频率为25MHz

% 调用 spectrum_plot 函数来绘制频谱图
tt_str_da = 'DA频谱图'; % 设置图表标题
tt_str_ad = 'AD频谱图'; % 设置图表标题
spectrum_plot(x_da, fs, tt_str_da);
spectrum_plot(x_ad, fs, tt_str_ad);

% 绘制DDS信号波形
figure;
subplot(211);
plot(data_da(:, 1), data_da(:, 6));
title('DA信号波形');
xlabel('Time');
ylabel('Amplitude');
xlim([0 200]);

subplot(212);
plot(data_ad(:, 1), data_ad(:, 6));
title('AD信号波形');
xlabel('Time');
ylabel('Amplitude');
xlim([0 200]);
grid on;

% spectrum_plot 函数定义
function spectrum_plot(x, fs, tt_str)
  vec_win = kaiser(length(x), 8);
  y    = fftshift(fft(x .* vec_win));
  y_dB = 20*log10(abs(y));
  N_f = length(y);
  f_idx = [0:N_f-1].'/N_f * fs - (fs/2);
  figure;
  plot(f_idx/1E3, y_dB);
  xlabel('kHz');
  title(tt_str);
end

21cf1202ec75474a990c2bf69c18ec42.png

b9c70501d5974c5c8db19dc2c803a2bd.png

可以看到信号频率存在误差但基本一致~


总结、

       关于基于ALINX 7020、AN108的DDS实验(VIO可控频率字)就结束了,本实验的设计需求是使用VIO模块来设定频率字,生成特定频率的正弦波形,并通过多个步骤来验证生成波形的正确性,这个实验的设计需求也已基本完成,至此算是开始正式踏入了FPGA的学习~

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星空lg

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值