【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)

文章目录

  • DAC FIFO实验要求
  • AXI-stream FIFO介绍
    • 基于地址形式的交互与基于流形式的交互
    • AXI-stream总线读写协议
      • axis工作模式
      • 读操作
      • 写操作
      • READY,VALID握手
    • AXI-stream FIFO IP核
  • DAC FIFO实验例化模块
    • CLK
    • 添加VIO IP核
    • 添加AXI-Stream-Data FIFO IP核
    • 添加DDS IP核
    • 添加ILA IP核
  • DAC FIFO实验程序代码
    • 顶层模块
    • 频率控制模块
    • DAC发送模块
    • IO约束
  • DAC FIFO实验实验效果
    • RTL
    • ILA波形图
    • Matlab频率分析
  • DAC FIFO实验工程下载
  • 进阶:AD/DA FIFO实验
    • 程序代码
      • 顶层模块
      • 频率字控制
      • DA发送模块
    • ILA波形
    • Matlab频率分析
  • AD/DA FIFO实验工程下载
  • 补充:DDS
    • DDS设计要点
    • Matlab代码
    • DDS的ROM压缩概念

DAC FIFO实验要求

DAC FIFO实验

基于“DDS IP 数字波形合成DAC ” “ ADDA测试” 实验方案

用MMCM 把 合成出100MHz的时钟,让DDS工作在100MHz时钟

让DAC和DAC的接口电路工作在50MHz,此时DAC的采样率为50MHz

在DDS和DAC接口电路之间,放置一个带独立时钟的AXI-Stream-Data FIFO,FIFO两端的时钟分别为DDS的工作时钟100MHz和DAC的工作时钟50MHz

生成FIFO需要带data count信号(本实验仅用于观察,以后的实验中这些信号有用。)

DDS的数据输出接口需要有TREADY信号

DAC接口电路需要将FIFO输出端的AXI-S接口转换成DAC的接口格式,自行编写RTL代码完成该功能。另外由于DAC的工作频率小于DDS工作频率,所以DAC接口控制器给FIFO的RDY信号应该一直为高。

以上结构的意义在于,把接口电路和信号处理电路分离在不同的时钟域,从而使得各部分保持独立

本实验添加2个system ILA,分别观察FIFO两端接口的信号时序,注意观察 data count端口的变化。

用VIO配置频率字,分别生成1MHz和3MHz的DDS正弦波形,用system ILA抓取DAC的输入数据,用Matlab分析频谱,验证频率正确。
本实验是一个典型的带反向流控的跨时钟域传输信号的例子

关于DDS还有频率字的一些概念,见传送门

基于FPGA的DDS参考设计

AXI-stream FIFO介绍

这里有一篇文章不错,如果还是不理解,可以去学习一下

带你快速入门AXI4总线–AXI4-Stream篇(3)----详解XILINX IP AXI4 STREAM DATA FIFO

基于地址形式的交互与基于流形式的交互

流式传输和基于地址的传输有什么区别?

直观的看,基于地址的数据传输就像是CPU访问内存,每次访问数据都需要一个地址。基于流的数据传输方式类似于FIFO,队列的访问,每次读取会把数据“读走”,下次再读取的时候已经是下一个数据,写入也是类似。

FPGA中,流式数据传输是以时钟为驱动,加一些控制信号。stream常见的实例就是AD/DA转换,AD/DA转换器在采样时钟的驱动下,不停的产生数据流,外界根据一个ready信号来决定是否读取数据。address形式常见的例子例如MCU读取AT24C04,每次发生数据的读写都会提前设置地址。

可以看出来,基于stream的数据传输更适合批量数据的传输,基于Address的数据传输适合位置型的数据。

AXI-stream总线读写协议

axis工作模式

axis分为:

  • tready信号:从告诉主做好传输准备;

  • tvalid信号:主告诉从数据传输有效;

  • tlast信号:主告诉从该次传输为突发传输结尾;

  • tdata信号:数据,可选宽度32,64,128,256bit

  • tstrb信号:为1的bit为对应tdata有效字节,宽度为tdata/8

  • tuser信号 :用户定义信号,宽度为128bit

  • aclk信号:总线时钟,上升沿有效;

  • aresetn信号:总线复位,低电平有效;

axi总线分为五个通道:

  • 读地址通道,包含ARVALID, ARADDR, ARREADY信号;

  • 写地址通道,包含AWVALID,AWADDR, AWREADY信号;

  • 读数据通道,包含RVALID, RDATA, RREADY, RRESP信号;

  • 写数据通道,包含WVALID, WDATA,WSTRB, WREADY信号;

  • 写应答通道,包含BVALID, BRESP, BREADY信号;

  • 系统通道,包含:ACLK,ARESETN信号;

其中ACLK为axi总线时钟,ARESETN是axi总线复位信号,低电平有效;读写数据与读写地址类信号宽度都为32bit;READY与VALID是对应的通道握手信号;WSTRB信号为1的bit对应WDATA有效数据字节,WSTRB宽度是32bit/8=4bit;BRESP与RRESP分别为写回应信号,读回应信号,宽度都为2bit,‘h0代表成功,其他为错误。

读操作

顺序为主与从进行读地址通道握手并传输地址内容,然后在读数据通道握手并传输所读内容以及读取操作的回应,时钟上升沿有效。如图所示:

【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第1张图片

写操作

顺序为主与从进行写地址通道握手并传输地址内容,然后在写数据通道握手并传输所读内容,最后再写回应通道握手,并传输写回应数据,时钟上升沿有效。如图所示:
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第2张图片

READY,VALID握手

【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第3张图片
面的时序图进行点说明,在AXI-STREAM总线中,有两个握手信号很关键,明白了握手信号,根据这两个信号就可以进行数据的传输。

在说信号之前,先说一下AXI-stream是分为主设备和从设备的,主设备会发出clk信号和data,从设备在条件允许的情况下,进行数据的读取。

  1. tvalid信号:此信号说明主机的数据已经准备好了,数据允许被从机读取。
  2. tready信号:此信号是从机目前可以读取的信号,当该信号置位时说明从机准备好了读取主机的信号;
    【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第4张图片
    上图是可以看出tready & tvalid = 1的时刻,数据开始被传输 。

这种握手机制,相互确定了对方是否准备好进行数据传输,一旦双方都确认,数据传输就会开始

总结

1、从机拉高tready信号,表示此刻从机空闲,可以进行数据传输;

2、主机把tdata、tkeep、tuser准备就绪以后,再把tvalid拉高,此时数据在clk的驱动下进行传输;

3、TKEEP是字节修饰符。用来表明TDATA相关字节的内容是否作为数据流的一部分被处理。TKEEP字节修饰符未被确认的那些相关字节是空字节,可以从数据流中去除。

4、tlast 数据包结束;

AXI-stream FIFO IP核

【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第5张图片

FIFO的深度

可以在16到32768之间变化,具体情况视情况而定,但要是2的n次幂。

Enable packet mode

使能包模式:此项设定需要TLAST信号被使能。FIFO的操作在包模式下被修改为存储传送的数据,直到TLAST信号被响应。当TLAST信号被响应或者FIFO满了,存储的数据将被送至AXI4-Stream master interface.

Asynchronous Clocks

异步时钟:启用后S_AXIS_ACLK和M_AXIS_ACLK将会是异步时钟。

Synchronization Stages across Cross Clock Domain Logic

当启用异步时钟后,才会有该选项,其作用相当于跨时钟域时的打拍操作。一般默认即可。

ACLKEN Conversion Mode

此选项用来选择ACLKEN信号的转换模式。

None 没有和这个IP相关的ACLKEN 信号相关

S AXIS Only 有一个与S_AXIS_ACLKEN 相关联的 S_AXIS_ACLK信号,但没有M_AXIS_ACLKEN信号

M AXIS Only 有一个与M_AXIS_ACLKEN 相关联的 M_AXIS_ACLK信号,但没有S_AXIS_ACLKEN 信号

S AXIS & M AXIS 两个时钟都有与它们相关的ACLKEN信号。

Signal Properties
信号特性:可以看到,软件可以自动计算,当然我们也可以手动修改。

TDATA width (bytes)

参数指定axi4流上TData信号的宽度(以字节为单位接口。此参数是一个整数,可以从0到512不等。设置为0以忽略TDATA信号。如果省略了tdata信号,则tkeep和tstb信号也会省略。如图设置为4则可以看到位宽为32bit。

Enable TSTRB

是否使能TSTRB信号,只有当TData width(bytes)参数大于0时,才能启用此选项。

Enable TKEEP

是否使能TKEEP信号,只有当TData width(bytes)参数大于0时,才能启用此选项。

Enable TLAST

是否使能TKEEP信号,只有当TData width(bytes)参数大于0时,才能启用此选项。

TID width (bits)

用来指定TID信号的位宽,0为忽略,1~32为相应的位宽。

TDEST width (bits)

用来指定TDEST 信号的位宽,0为忽略,1~32为相应的位宽。

TUSER Width (bits)

用来指定TUSER 信号的位宽,0为忽略,1~32为相应的位宽。
关于这些信号的具体含义以及时序关系,可以通过仿真观察。

再来看一下此时的FIFO框图:
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第6张图片

其中除了标志信号、时钟、复位信号外,就是两个接口M_AXIS、S_AXIS。

我们知道AXIS是一种半双工的总线,数据传输永远是从MASTER发送给SLAVE,所以可以判断出M_AXIS是发送接口来发送FIFO中的数据,即FIFO读取端;S_AXIS是接收接口来将数据写入FIFO中,即FIFO写入端。

我们在例化IP核之后,会发现生成了这么一大段的代码,我们要怎么塞进去我们的变量

//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
axis_data_fifo_0 your_instance_name (
  .s_axis_aresetn(s_axis_aresetn),          // input wire s_axis_aresetn
  .m_axis_aresetn(m_axis_aresetn),          // input wire m_axis_aresetn
  .s_axis_aclk(s_axis_aclk),                // input wire s_axis_aclk
  .s_axis_aclken(s_axis_aclken),            // input wire s_axis_aclken
  .s_axis_tvalid(s_axis_tvalid),            // input wire s_axis_tvalid
  .s_axis_tready(s_axis_tready),            // output wire s_axis_tready
  .s_axis_tdata(s_axis_tdata),              // input wire [7 : 0] s_axis_tdata
  .m_axis_aclk(m_axis_aclk),                // input wire m_axis_aclk
  .m_axis_aclken(m_axis_aclken),            // input wire m_axis_aclken
  .m_axis_tvalid(m_axis_tvalid),            // output wire m_axis_tvalid
  .m_axis_tready(m_axis_tready),            // input wire m_axis_tready
  .m_axis_tdata(m_axis_tdata),              // output wire [7 : 0] m_axis_tdata
  .axis_data_count(axis_data_count),        // output wire [31 : 0] axis_data_count
  .axis_wr_data_count(axis_wr_data_count),  // output wire [31 : 0] axis_wr_data_count
  .axis_rd_data_count(axis_rd_data_count)  // output wire [31 : 0] axis_rd_data_count
);
// INST_TAG_END ------ End INSTANTIATION Template ---------

这就要从手册说起,具体的细节可以去看官方英文文档pg085-axi4stream-infrastructure.pdf
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第7张图片
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第8张图片
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第9张图片
文档中几乎涵盖了全部的说明,这里针对我们所涉及到的进行阅读和理解

//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
axis_data_fifo_0 u_axis_data_fifo_0 (
  .s_axis_aresetn(s_axis_aresetn),          // input wire s_axis_aresetn  复位,低电平有效;当复位信号拉高后的第三个时钟上升沿s_axis_tready信号会自动拉高,该fifo处于等待接收数据状态。
  .s_axis_aclk(s_axis_aclk),                // input wire s_axis_aclk     写入数据时钟
  .s_axis_aclken(s_axis_aclken),            // input wire s_axis_aclken   输入时钟使能信号
  .s_axis_tvalid(s_axis_tvalid),            // input wire s_axis_tvalid   数据有效标志端,将s_axis_tvalid信号置高,在下个时钟上升沿,STREAM FIFO便开始收数
  .s_axis_tdata(s_axis_tdata),              // input wire [7 : 0] s_axis_tdata  数据输入端
  
  .m_axis_aresetn(m_axis_aresetn),          // input wire m_axis_aresetn 复位,低电平有效
  .m_axis_aclk(m_axis_aclk),                // input wire m_axis_aclk    读取数据时钟
  .m_axis_aclken(m_axis_aclken),            // input wire m_axis_aclken  输出时钟使能信号
  .m_axis_tready(m_axis_tready),            // input wire m_axis_tready  当FIFO的后端将m_axis_tready拉高时,MASTER接口便会将数据送出去。
  
  .s_axis_tready(s_axis_tready),            // output wire s_axis_tready  当STREAM FIFO的前端有数据需要发送时,在s_axis_tready为高时将s_axis_tvalid信号置高,在下个时钟上升沿,STREAM FIFO便开始收数。接收进最后一个数据的同时,s_axis_tready将会变为低,告诉前级fifo已满,不能再收数据了。
  .m_axis_tvalid(m_axis_tvalid),            // output wire m_axis_tvalid  当STREAM FIFO接收到数据并传到MASTER接口上时,m_axis_tvalid便会拉高,由于使用的STREAM FIFO为异步时钟模式,数据写入时钟比数据读出时钟要快,而读数据计数器的刷新是在读数据时钟的上升沿,所以可以从仿真图中看到读数据计数器的值是跳跃上升的。
  .m_axis_tdata(m_axis_tdata),              // output wire [7 : 0] m_axis_tdata  数据输出端;

  .axis_data_count(axis_data_count),        // output wire [31 : 0] axis_data_count     指示数据FIFO内的写入计数。使用数据包模式FIFO时不产生有效输出。当在接口之间使用公共时钟时,可以使用该信号。
  .axis_wr_data_count(axis_wr_data_count),  // output wire [31 : 0] axis_wr_data_count  写数据计数器
  .axis_rd_data_count(axis_rd_data_count)   // output wire [31 : 0] axis_rd_data_count  读数据计数器
);
// INST_TAG_END ------ End INSTANTIATION Template ---------

DAC FIFO实验例化模块

CLK

系统的时钟是50Mhz,所以这里输入clk in是50
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第10张图片
按照实验任务要求,DDS工作在100MHz时钟,DAC和AD的接口电路工作在50MHz,所以输出50 和 100
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第11张图片

//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG

clk_wiz_0 u_clk_wiz_0
 (
  // Clock out ports
  .clk_out1(clk_50M),     // output clk_out1
  .clk_out2(clk_100M),     // output clk_out2
  // Status and control signals
  .reset(~sys_rst_n), // input reset
  .locked(locked),       // output locked
 // Clock in ports
  .clk_in1(sys_clk));      // input clk_in1
// INST_TAG_END ------ End INSTANTIATION Template ---------

添加VIO IP核

设置虚拟按键,来控制DDS输出的波形频率
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第12张图片
这里只需要两位长度就可以了
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第13张图片

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

添加AXI-Stream-Data FIFO IP核

核心就是AXI-Stream-Data FIFO的配置,相关的详细内容已经在前面介绍过了

这里选择FIFO的位深为1024

选择异步,也就是输入输出时钟频率不同

TDATA Width数据宽度1 Byte,也就是8 bit
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第14张图片
这个IP核最让我头疼的就是这个部分了,涉及到的参数太多了,并且是一个典型的AXI通信

捣鼓了两三天后,起始也没有想象中那么复杂,其实就按照AXI通信的原理,还有说明往里面填写变量就可以了,并且FIFO是个半双工,这就比较好缕清一些关系

//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
axis_data_fifo_0 u_axis_data_fifo_0 (
    // 系统端口
  .s_axis_aresetn(fifo_s_axis_aresetn),          // input wire s_axis_aresetn        复位,低电平有效;当复位信号拉高后的第三个时钟上升沿s_axis_tready信号会自动拉高,该fifo处于等待接收数据状态。
  .s_axis_aclk(fifo_s_axis_aclk),                // input wire s_axis_aclk           写入数据时钟
  .m_axis_aclk(fifo_m_axis_aclk),                // input wire m_axis_aclk           读取数据时钟

    //写FIFO端口
  .s_axis_tvalid(dds_m_axis_data_tvalid),        // input wire s_axis_tvalid         数据有效标志端,将s_axis_tvalid信号置高,在下个时钟上升沿,STREAM FIFO便开始收数
  .s_axis_tready(fifo_s_axis_tready),            // output wire s_axis_tready        FIFO满了拉低,当FIFO的后端将m_axis_tready拉高时,MASTER接口便会将数据送出去。
  .s_axis_tdata(dds_m_axis_data_tdata),          // input wire [7 : 0] s_axis_tdata  数据输入端
  .axis_wr_data_count(fifo_axis_wr_data_count),  // output wire [31 : 0] axis_wr_data_count  已经写入的数据量

    //读FIFO端口
  .m_axis_aresetn(fifo_m_axis_aresetn),          // input wire m_axis_aresetn
  .m_axis_tready(fifo_m_axis_tready),            // input wire m_axis_tready
  .m_axis_tvalid(fifo_m_axis_tvalid),            // output wire m_axis_tvalid
  .m_axis_tdata(fifo_m_axis_tdata),              // output wire [7 : 0] m_axis_tdata         数据输出端;
  .axis_rd_data_count(fifo_axis_rd_data_count),  // output wire [31 : 0] axis_rd_data_count  可读数据量

  .axis_data_count(fifo_axis_data_count)         // output wire [31 : 0] axis_data_count     指示数据FIFO内的写入计数

);
// INST_TAG_END ------ End INSTANTIATION Template ---------

可以分成三个部分

第一个就是系统端口

这里就包括了复位,还有写入和读取的时钟


  // 系统端口
  .s_axis_aresetn(fifo_s_axis_aresetn),          // input wire s_axis_aresetn        复位,低电平有效;当复位信号拉高后的第三个时钟上升沿s_axis_tready信号会自动拉高,该fifo处于等待接收数据状态。
  .s_axis_aclk(fifo_s_axis_aclk),                // input wire s_axis_aclk           写入数据时钟
  .m_axis_aclk(fifo_m_axis_aclk),                // input wire m_axis_aclk           读取数据时钟

第二部分就是写端口,这里指的是DDS生成的数据,写到我们的FIFO中

.s_axis_tvalid(dds_m_axis_data_tvalid),当dds_m_axis_data_tvalid是高电平时候,FIFO就开始接收数据,这个变量需要从外部输入,分析我们的整体设计,这个变量应该是DDS IP核产生,DDS IP核的.m_axis_data_tvalid(dds_m_axis_data_tvalid),产生输出,然后输入到FIFO

.s_axis_tready(fifo_s_axis_tready),当fifo_s_axis_tready是高电平时侯,代表FIFO还没有满,当FIFO满了的时候,fifo_s_axis_tready会拉低,这个是输出信号,要反馈给DDS,告诉DDS IP核FIFO已经满了

.s_axis_tdata(dds_m_axis_data_tdata),,其中dds_m_axis_data_tdata就是我们要真正写入FIFO中的数据,这里就是DDS生成的数据

   //写FIFO端口
  .s_axis_tvalid(dds_m_axis_data_tvalid),        // input wire s_axis_tvalid         数据有效标志端,将s_axis_tvalid信号置高,在下个时钟上升沿,STREAM FIFO便开始收数
  .s_axis_tready(fifo_s_axis_tready),            // output wire s_axis_tready        FIFO满了拉低,当FIFO的后端将m_axis_tready拉高时,MASTER接口便会将数据送出去。
  .s_axis_tdata(dds_m_axis_data_tdata),          // input wire [7 : 0] s_axis_tdata  数据输入端
  .axis_wr_data_count(fifo_axis_wr_data_count),  // output wire [31 : 0] axis_wr_data_count  已经写入的数据量

第三个部分就是读FIFO端口,这里指的是从FIFO中读取数据,也就是经过存储调节速率后的数据要传入下一个目标模块

.m_axis_aresetn(fifo_m_axis_aresetn),,当fifo_m_axis_aresetn为高电平时,拉高复位,系统进入工作状态

.m_axis_tready(fifo_m_axis_tready),fifo_m_axis_tready为高电平时,FIFO需要保持向外读取,因为输入时钟比输出时钟高,所以这里读取要一直为高才可以

.m_axis_tvalid(fifo_m_axis_tvalid),,这里的fifo_m_axis_tvalid是输出,告诉下一个模块数据开始传输

.m_axis_tdata(fifo_m_axis_tdata), ,数据输出fifo_m_axis_tdata

   //读FIFO端口
  .m_axis_aresetn(fifo_m_axis_aresetn),          // input wire m_axis_aresetn
  .m_axis_tready(fifo_m_axis_tready),            // input wire m_axis_tready
  .m_axis_tvalid(fifo_m_axis_tvalid),            // output wire m_axis_tvalid
  .m_axis_tdata(fifo_m_axis_tdata),              // output wire [7 : 0] m_axis_tdata         数据输出端;
  .axis_rd_data_count(fifo_axis_rd_data_count),  // output wire [31 : 0] axis_rd_data_count  可读数据量

添加DDS IP核

对于DDS IP核设置输入频率 100 MHz,输出数据位宽8 bit,相位宽度16bit
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第15张图片
参数控制,仅输出sine波形
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第16张图片
DDS带TREADY
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第17张图片
配置完成后的示意图
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第18张图片
DDS IP核的配置核FIFO IP核的配置联系紧密,这里重点关注的就是两个输入信号,两个输出信号

输入信号一个是跟频率控制有关,DDS作为从机,接受频率控制模块DDS_frequency_value的控制

 .s_axis_config_tdata(DDS_frequency_value),        // input wire [15 : 0] s_axis_config_tdata

另一个输入信号就是产生信号的标志,FIFO模块发出fifo_s_axis_tready为高电平时,表示FIFO目前可以继续接收数据, .m_axis_data_tready(fifo_s_axis_tready), 便可以继续生成正弦信号

  .m_axis_data_tready(fifo_s_axis_tready),           // input wire m_axis_data_tready

输出信号dds_m_axis_data_tvalid为高电平表示DDS可以生成正弦信号,输入到FIFO的指定端口,高速FIFO这边正弦信号正常生成

  .m_axis_data_tvalid(dds_m_axis_data_tvalid),      // output wire m_axis_data_tvalid

输出信号dds_m_axis_data_tdata就是我们实际需要的正弦波数据

  .m_axis_data_tdata(dds_m_axis_data_tdata),        // output wire [7 : 0] m_axis_data_tdata

然后就是完整的DDS配置代码

//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
dds_compiler_0 u_dds_compiler_0 (
  .aclk(clk_100M),                                  // input wire aclk
  
  .s_axis_config_tvalid(dds_s_axis_config_tvalid),  // input wire s_axis_config_tvalid
  .s_axis_config_tdata(DDS_frequency_value),        // input wire [15 : 0] s_axis_config_tdata
  
  .m_axis_data_tready(fifo_s_axis_tready),           // input wire m_axis_data_tready
  .m_axis_phase_tready(fifo_s_axis_tready),         // input wire m_axis_phase_tready
  .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 [7 : 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 [15 : 0] m_axis_phase_tdata

  .s_axis_config_tready(dds_s_axis_config_tready)  // output wire s_axis_config_tready
  
);
// INST_TAG_END ------ End INSTANTIATION Template ---------

添加ILA IP核

【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第19张图片
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第20张图片

//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG

ila_0 u_ila_0 (
	.clk(clk_50M), // input wire clk

	.probe0(key_PINC), // input wire [1:0]  probe0  
	.probe1(dds_m_axis_data_tdata), // input wire [7:0]  probe1 
	.probe2(fifo_m_axis_tdata), // input wire [7:0]  probe2 
    .probe3(da_data), // input wire [7:0]  probe3 
	.probe4(fifo_axis_data_count) // input wire [31:0]  probe4
);

// INST_TAG_END ------ End INSTANTIATION Template ---------

DAC FIFO实验程序代码

顶层模块

首先贴一下全部的代码

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/03/13 10:26:26
// Design Name: 
// Module Name: da_fifo
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module da_fifo(
    input sys_clk,
    input sys_rst_n,
    
    // DA芯片接口
    output          da_clk,
    output [7 : 0]  da_data,

    // AD芯片接口
    output          ad_clk,
    input [7 : 0]   ad_data
    );

wire            clk_50M;               // PLL产生50Mhz时钟
wire            clk_100M;                // PLL产生100Mhz时钟
wire            locked;                 // PLL lock信号,可作为系统复位 高电平有效

//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG

clk_wiz_0 u_clk_wiz_0
 (
  // Clock out ports
  .clk_out1(clk_50M),     // output clk_out1
  .clk_out2(clk_100M),     // output clk_out2
  // Status and control signals
  .reset(~sys_rst_n), // input reset
  .locked(locked),       // output locked
 // Clock in ports
  .clk_in1(sys_clk));      // input clk_in1
// INST_TAG_END ------ End INSTANTIATION Template ---------

// VIO按键控制频率
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
);

//input
wire [0:0]         fre_m_axis_config_tvalid;    

// 信号频率控制模块
wire [15 : 0]   DDS_frequency_value;          // 频率字

Fword_set u_Fword_set  (
    .clk(clk_100M), 
    .rst_n(sys_rst_n), 
    .key_PINC(key_PINC), 
    .fre_m_axis_config_tvalid(fre_m_axis_config_tvalid),
    .DDS_frequency_value(DDS_frequency_value)
);


//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 [15 : 0]      dds_m_axis_phase_tdata;
wire [0 : 0]       dds_s_axis_config_tready;

//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
dds_compiler_0 u_dds_compiler_0 (
  .aclk(clk_100M),                                  // input wire aclk
  
  .s_axis_config_tvalid(fre_m_axis_config_tvalid),  // input wire s_axis_config_tvalid
  .s_axis_config_tdata(DDS_frequency_value),        // input wire [15 : 0] s_axis_config_tdata
  .s_axis_config_tready(dds_s_axis_config_tready),  // output wire s_axis_config_tready
  
  .m_axis_data_tready(fifo_s_axis_tready),           // input wire m_axis_data_tready
  .m_axis_phase_tready(fifo_s_axis_tready),         // input wire m_axis_phase_tready
  .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 [7 : 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 [15 : 0] m_axis_phase_tdata
  
);
// INST_TAG_END ------ End INSTANTIATION Template ---------


wire				fifo_s_axis_aresetn;	
wire				fifo_s_axis_aclk;
wire				fifo_m_axis_aclk;

// 写FIFO端口			
wire  	[31 : 0] 	fifo_axis_wr_data_count;
wire			    fifo_s_axis_tready;               // FIFO tready	

// 读FIFO端口
wire				da_m_axis_aresetn;	
wire				da_m_axis_tready;		

wire				fifo_m_axis_tvalid;		
wire  	[7 : 0]	    fifo_m_axis_tdata;			
wire  	[31 : 0]	fifo_axis_rd_data_count;

wire  	[31 : 0]	fifo_axis_data_count;


assign  fifo_s_axis_aresetn = 1'b1;               //拉高复位,系统进入工作状态

assign  fifo_s_axis_aclk = clk_100M;              //S_AXIS是接收接口来将数据写入FIFO中,即FIFO写入端。
assign  fifo_m_axis_aclk = clk_50M;               //M_AXIS发送接口来发送FIFO中的数据,即FIFO读取端;
//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
axis_data_fifo_0 u_axis_data_fifo_0 (
    // 系统端口
  .s_axis_aresetn(fifo_s_axis_aresetn),          // input wire s_axis_aresetn        复位,低电平有效;当复位信号拉高后的第三个时钟上升沿s_axis_tready信号会自动拉高,该fifo处于等待接收数据状态。
  .s_axis_aclk(fifo_s_axis_aclk),                // input wire s_axis_aclk           写入数据时钟
  .m_axis_aclk(fifo_m_axis_aclk),                // input wire m_axis_aclk           读取数据时钟

    //写FIFO端口
  .s_axis_tvalid(dds_m_axis_data_tvalid),        // input wire s_axis_tvalid         数据有效标志端,将s_axis_tvalid信号置高,在下个时钟上升沿,STREAM FIFO便开始收数
  .s_axis_tready(fifo_s_axis_tready),            // output wire s_axis_tready        FIFO满了拉低,当FIFO的后端将m_axis_tready拉高时,MASTER接口便会将数据送出去。
  .s_axis_tdata(dds_m_axis_data_tdata),          // input wire [7 : 0] s_axis_tdata  数据输入端
  .axis_wr_data_count(fifo_axis_wr_data_count),  // output wire [31 : 0] axis_wr_data_count  已经写入的数据量

    //读FIFO端口
  .m_axis_aresetn(da_m_axis_aresetn),          // input wire m_axis_aresetn
  .m_axis_tready(da_m_axis_tready),             // input wire m_axis_tready
  .m_axis_tvalid(fifo_m_axis_tvalid),            // output wire m_axis_tvalid
  .m_axis_tdata(fifo_m_axis_tdata),              // output wire [7 : 0] m_axis_tdata         数据输出端;
  .axis_rd_data_count(fifo_axis_rd_data_count),  // output wire [31 : 0] axis_rd_data_count  可读数据量

  .axis_data_count(fifo_axis_data_count)         // output wire [31 : 0] axis_data_count     指示数据FIFO内的写入计数

);
// INST_TAG_END ------ End INSTANTIATION Template ---------



// DA数据发送
da_wave_send u_da_wave_send(
    .clk                (clk_50M), 
    .rst_n              (sys_rst_n),
    .rd_data            (fifo_m_axis_tdata),
    .da_m_axis_tready   (da_m_axis_tready),
    .da_m_axis_aresetn  (da_m_axis_aresetn), 
    .da_clk             (da_clk),
    .da_data            (da_data)
);


//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG

ila_0 u_ila_0 (
	.clk(clk_50M), // input wire clk

	.probe0(key_PINC), // input wire [1:0]  probe0  
	.probe1(dds_m_axis_data_tdata), // input wire [7:0]  probe1 
	.probe2(fifo_m_axis_tdata), // input wire [7:0]  probe2 
    .probe3(da_data), // input wire [7:0]  probe3 
	.probe4(fifo_axis_data_count) // input wire [31:0]  probe4
);

// INST_TAG_END ------ End INSTANTIATION Template ---------


endmodule


频率控制模块

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/03/17 21:29:20
// Design Name: 
// Module Name: DDS_frequency_value_set
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module Fword_set(

    input                   clk,
    input                   rst_n,
    input       [1 : 0]     key_PINC,

    output                  fre_m_axis_config_tvalid,

    output reg  [15 : 0]    DDS_frequency_value
    );

//  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)

//  deta_theta是如何确定的?
// f_clk是系统时钟
// 输出频率的计算公式f_out=f_clk*deta_theta/(2^B)=50M * DDS_frequency_value /(2^16 )= 10M  

always@(*)
begin
    case(key_PINC)
        0:  DDS_frequency_value <= 'h51e;     //1Mhz  1310.72  每次相位增加的值  deta_theta
        1:  DDS_frequency_value <= 'ha3d;     //2Mhz  2621.44
        2:  DDS_frequency_value <= 'hf5c;     //3Mhz  3932.16
        3:  DDS_frequency_value <= 'h3333;    //10Mhz  13107.2
    endcase
end

assign             fre_m_axis_config_tvalid = 1'b1;


endmodule



DAC发送模块

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/03/17 21:23:31
// Design Name: 
// Module Name: da_wave_send
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module da_wave_send(

    input clk,                  // 时钟
    input rst_n,                // 复位信号,低电平有效

    input [7 : 0] rd_data,      // DDS读出数据

    // AXI-S接口
    output da_m_axis_tready,    // 由于DAC的工作频率小于DDS工作频率,所以DAC接口控制器给FIFO的RDY信号应该一直为高。
    output da_m_axis_aresetn,
    // DA 芯片接口
    output da_clk,              // DA(AD9708)驱动时钟,最大支持125MHz时钟
    output [7 : 0] da_data      // 输出给DA的数据

    );

// *****************************
// ** main code
// *****************************

// 数据rd_data是在clk的上升沿更新的,所以DA芯片在clk的下降沿锁频存数据是最稳定的时刻
// 而DA实际上在da_clk的上升沿锁存数据,所以时钟取反,这样clk的下降沿相当于da_clk的上升沿
assign da_clk = ~clk;
assign da_data = rd_data;       // 将读到的ROM数据幅值给DA数据端口

assign  da_m_axis_tready = 1'b1;
assign  da_m_axis_aresetn = 1'b1;


endmodule



IO约束

############## 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 sys_rst_n]
set_property PACKAGE_PIN K16 [get_ports sys_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]

DAC FIFO实验实验效果

RTL

首先来看一下我们RTL图
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第21张图片

可以看到两个不同频率的CLK,均正常输入到了指定模块

这个是50MHz
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第22张图片

100 MHz的时钟输入
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第23张图片

FIFO两端数据的输入和输出
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第24张图片

ILA波形图

结构逻辑正确,接下来验证ILA波形

通过抓取,可以看到波形可以正常的输出,这里是1MHz的DDS正弦波
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第25张图片

【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第26张图片

3MHz正弦波
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第27张图片

【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第28张图片

Matlab频率分析

close all;clc;clear all;
fs = 50000000; %设置采样频率为50MHz
iladata = xlsread('1MHz_iladata.csv'); 
iladata_1MHz = iladata(:,5);
N = length(iladata_1MHz);
n = 0:N-1;
f = n*fs/N;       %频率序列
fft_data = abs(fft(iladata_1MHz, N)); 
figure
subplot(2,1,1);plot(f, fft_data);grid on;title('1MHz DDS');
fft_data = abs(fft(iladata(:,6), N));
subplot(2,1,2);plot(f, fft_data);grid on;title('1MHz DA');

iladata = xlsread('3MHz_iladata.csv'); 
iladata_3MHz = iladata(:,5);
% A = 20*log10(abs(Y)); 
fft_data = abs(fft(iladata_3MHz, N)); 
figure
subplot(2,1,1);plot(f, fft_data);grid on;title('3MHz DDS');
fft_data = abs(fft(iladata(:,6), N));
subplot(2,1,2);plot(f, fft_data);grid on;title('3MHz DA');

【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第29张图片

DAC FIFO实验工程下载

12.DAC FIFO实验

https://download.csdn.net/download/szm1234/85072334

进阶:AD/DA FIFO实验

带FIFO的ADDA实验
本实验在DAC FIFO实验的基础上完成
把DAC输出模拟信号自环给ADC的模拟输入
ADC使用25MHz的时钟信号采样
ADC的输出的数据信号,用ILA抓取观察波形
用VIO配置频率字,分别生成1MHz和3MHz的DDS正弦波形,用Matlab分析频谱,验证频率的正确性。

程序代码

相较于前面的代码,稍微添加一点内容

顶层模块

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/03/13 10:26:26
// Design Name: 
// Module Name: da_fifo
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module da_fifo(
    input           sys_clk,
    input           sys_rst_n,
    
    // DA芯片接口
    output          da_clk,
    output [7 : 0]  da_data,

    // AD芯片接口
    output          ad_clk,
    input [7 : 0]   ad_data
    );

wire            clk_50M;               // PLL产生50Mhz时钟
wire            clk_100M;                // PLL产生100Mhz时钟
wire            locked;                 // PLL lock信号,可作为系统复位 高电平有效

//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG

clk_wiz_0 u_clk_wiz_0
 (
  // Clock out ports
  .clk_out1(clk_50M),     // output clk_out1
  .clk_out2(clk_100M),     // output clk_out2
  .clk_out3(ad_clk),     // output clk_out3
  // Status and control signals
  .reset(~sys_rst_n), // input reset
  .locked(locked),       // output locked
 // Clock in ports
  .clk_in1(sys_clk));      // input clk_in1
// INST_TAG_END ------ End INSTANTIATION Template ---------

// VIO按键控制频率
wire [1 : 0]    key_PINC;

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

//input
wire [0:0]         fre_m_axis_config_tvalid;    

// 信号频率控制模块
wire [23 : 0]   DDS_frequency_value;          // 频率字

Fword_set u_Fword_set  (
    .clk(clk_100M),                            // 输入系统时钟频率
    .rst_n(sys_rst_n), 
    .key_PINC(key_PINC), 
    .fre_m_axis_config_tvalid(fre_m_axis_config_tvalid),
    .DDS_frequency_value(DDS_frequency_value)
);

//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;
wire [0 : 0]       dds_s_axis_config_tready;

//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
dds_compiler_0 u_dds_compiler_0 (
  .aclk(clk_100M),                                  // input wire aclk
  
  .s_axis_config_tvalid(fre_m_axis_config_tvalid),  // input wire s_axis_config_tvalid
  // .s_axis_config_tdata(DDS_frequency_value),        // input wire [15 : 0] s_axis_config_tdata
  .s_axis_config_tdata(DDS_frequency_value),    // input wire [23 : 0] s_axis_config_tdata

  .s_axis_config_tready(dds_s_axis_config_tready),  // output wire s_axis_config_tready
  
  .m_axis_data_tready(fifo_s_axis_tready),           // input wire m_axis_data_tready
  .m_axis_phase_tready(fifo_s_axis_tready),         // input wire m_axis_phase_tready
  .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 [7 : 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 [15 : 0] m_axis_phase_tdata
  .m_axis_phase_tdata(dds_m_axis_phase_tdata)      // output wire [23 : 0] m_axis_phase_tdata

);
// INST_TAG_END ------ End INSTANTIATION Template ---------


wire				fifo_s_axis_aresetn;	
wire				fifo_s_axis_aclk;
wire				fifo_m_axis_aclk;

// 写FIFO端口			
wire  	[31 : 0] 	fifo_axis_wr_data_count;
wire			    fifo_s_axis_tready;               // FIFO tready	

// 读FIFO端口
wire				da_m_axis_aresetn;	
wire				da_m_axis_tready;		

wire				fifo_m_axis_tvalid;		
wire  	[7 : 0]	    fifo_m_axis_tdata;			
wire  	[31 : 0]	fifo_axis_rd_data_count;

wire  	[31 : 0]	fifo_axis_data_count;


assign  fifo_s_axis_aresetn = 1'b1;               //拉高复位,系统进入工作状态

assign  fifo_s_axis_aclk = clk_100M;              //S_AXIS是接收接口来将数据写入FIFO中,即FIFO写入端。
assign  fifo_m_axis_aclk = clk_50M;               //M_AXIS发送接口来发送FIFO中的数据,即FIFO读取端;
//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
axis_data_fifo_0 u_axis_data_fifo_0 (
    // 系统端口
  .s_axis_aresetn(fifo_s_axis_aresetn),          // input wire s_axis_aresetn        复位,低电平有效;当复位信号拉高后的第三个时钟上升沿s_axis_tready信号会自动拉高,该fifo处于等待接收数据状态。
  .s_axis_aclk(fifo_s_axis_aclk),                // input wire s_axis_aclk           写入数据时钟
  .m_axis_aclk(fifo_m_axis_aclk),                // input wire m_axis_aclk           读取数据时钟

    //写FIFO端口
  .s_axis_tvalid(dds_m_axis_data_tvalid),        // input wire s_axis_tvalid         数据有效标志端,将s_axis_tvalid信号置高,在下个时钟上升沿,STREAM FIFO便开始收数
  .s_axis_tready(fifo_s_axis_tready),            // output wire s_axis_tready        FIFO满了拉低,当FIFO的后端将m_axis_tready拉高时,MASTER接口便会将数据送出去。
  .s_axis_tdata(dds_m_axis_data_tdata),          // input wire [7 : 0] s_axis_tdata  数据输入端
  .axis_wr_data_count(fifo_axis_wr_data_count),  // output wire [31 : 0] axis_wr_data_count  已经写入的数据量

    //读FIFO端口
  .m_axis_aresetn(da_m_axis_aresetn),          // input wire m_axis_aresetn
  .m_axis_tready(da_m_axis_tready),             // input wire m_axis_tready
  .m_axis_tvalid(fifo_m_axis_tvalid),            // output wire m_axis_tvalid
  .m_axis_tdata(fifo_m_axis_tdata),              // output wire [7 : 0] m_axis_tdata         数据输出端;
  .axis_rd_data_count(fifo_axis_rd_data_count),  // output wire [31 : 0] axis_rd_data_count  可读数据量

  .axis_data_count(fifo_axis_data_count)         // output wire [31 : 0] axis_data_count     指示数据FIFO内的写入计数

);
// INST_TAG_END ------ End INSTANTIATION Template ---------



// DA数据发送
da_wave_send u_da_wave_send(
    .clk                (clk_50M), 
    .rst_n              (sys_rst_n),
    .rd_data            (fifo_m_axis_tdata),
    .da_m_axis_tready   (da_m_axis_tready),
    .da_m_axis_aresetn  (da_m_axis_aresetn), 
    .da_clk             (da_clk),
    .da_data            (da_data)
);


//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG

ila_0 u_ila_0 (
	.clk(ad_clk),                  // input wire clk

	.probe0(key_PINC),              // input wire [1:0]  probe0  
	.probe1(dds_m_axis_data_tdata), // input wire [7:0]  probe1 
	.probe2(fifo_m_axis_tdata),     // input wire [7:0]  probe2 
  .probe3(da_data),               // input wire [7:0]  probe3 
	.probe4(fifo_axis_data_count),  // input wire [31:0]  probe4
  .probe5(ad_data)                // input wire [7:0]  probe5
);

// INST_TAG_END ------ End INSTANTIATION Template ---------


endmodule


频率字控制

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/03/17 21:29:20
// Design Name: 
// Module Name: DDS_frequency_value_set
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module Fword_set(

    input                   clk,
    input                   rst_n,
    input       [1 : 0]     key_PINC,

    output                  fre_m_axis_config_tvalid,

    output reg  [23 : 0]    DDS_frequency_value
    );

//  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)

//  f_out是如何确定的?
//  根据IP核的 summery, phase width=16bits   Frequency per channel = 100MHz
//  输出频率的计算公式 f_out = f_clk*deta_theta/(2^B) = 50M * DDS_frequency_value / (2^16 )= 10M  

always@(*)
begin
    case(key_PINC)
        0:  DDS_frequency_value <= 'h51eb8;     //1Mhz  335544.32
        1:  DDS_frequency_value <= 'ha3d70;     //2Mhz  671088.64
        2:  DDS_frequency_value <= 'hf5c28;     //3Mhz  1006632.96
        3:  DDS_frequency_value <= 'h333333;    //10Mhz 3355443.2
    endcase
end

assign             fre_m_axis_config_tvalid = 1'b1;


endmodule




DA发送模块

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/03/17 21:23:31
// Design Name: 
// Module Name: da_wave_send
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module da_wave_send(

    input clk,                  // 时钟
    input rst_n,                // 复位信号,低电平有效

    input [7 : 0] rd_data,      // DDS读出数据

    // AXI-S接口
    output da_m_axis_tready,    // 由于DAC的工作频率小于DDS工作频率,所以DAC接口控制器给FIFO的RDY信号应该一直为高。
    output da_m_axis_aresetn,
    // DA 芯片接口
    output da_clk,              // DA(AD9708)驱动时钟,最大支持125MHz时钟
    output [7 : 0] da_data      // 输出给DA的数据

    );

// *****************************
// ** main code
// *****************************

// 数据rd_data是在clk的上升沿更新的,所以DA芯片在clk的下降沿锁频存数据是最稳定的时刻
// 而DA实际上在da_clk的上升沿锁存数据,所以时钟取反,这样clk的下降沿相当于da_clk的上升沿
assign da_clk = ~clk;
assign da_data = rd_data;       // 将读到的ROM数据幅值给DA数据端口

assign  da_m_axis_tready = 1'b1;
assign  da_m_axis_aresetn = 1'b1;


endmodule

ILA波形

ILA采样频率是25MHz,所以一个单位是 0.04 微秒(μs),40 纳秒(ns)

VIO输入 00
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第30张图片
通过观察的方法,我们可以知晓一个周期是25个时钟信号,计算周期时间为 40 ns* 25 = 1000 ns = 1 μs,即频率为 1 MHZ

【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第31张图片
VIO输入 10
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第32张图片

计算周期时间为 40 ns* 8 = 320 ns = 0.32 μs,即频率为 3 MHZ
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第33张图片

Matlab频率分析

导出波形数据到CSV文件,通过Matlab进行分析

close all;clc;clear all;
fs = 25000000; %设置采样频率为25MHz
iladata_1MHz = xlsread('1MHz_iladata.csv'); 
iladata_1MHz = iladata_1MHz(:,9);
N = length(iladata_1MHz);
n = 0:N-1;
f = n*fs/N;       %频率序列
fft_data = abs(fft(iladata_1MHz, N)); 
figure
subplot(2,1,1);plot(f, fft_data);grid on;title('1MHz AD FFT');

iladata_3MHz = xlsread('3MHz_iladata.csv'); 
iladata_3MHz = iladata_3MHz(:,9);
% A = 20*log10(abs(Y)); 
fft_data = abs(fft(iladata_3MHz, N)); 
% figure
subplot(2,1,2);plot(f, fft_data);grid on;title('3MHz AD FFT');

【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第34张图片

为了验证Matlab程序的可靠性,通过信号分析器得到的结果也是一样的结果
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第35张图片

AD/DA FIFO实验工程下载

13.带FIFO的ADDA实验

https://download.csdn.net/download/szm1234/85072336

补充:DDS

DDS设计要点

理解DDS的工作原理,为何它能生成不同频率的波形

根据时钟频率和输出波形的频率来计算DDS的频率字

下图展示了在一个 5比特的相位累加器,4比特的波形ROM地址线宽的DDS中,相位增量为1、2、4的情况下,输出波形和相位累加器以及ROM地址的关系

图中的红色数字是相位累加器的数值,黑色数字是相位累加值截断之后的ROM地址
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第36张图片

Matlab代码

%
% USUAGE    : demo the dds principle
% AUTHOR    : duweitao AT cuc.edu.cn
% DATE      : 2011-11-15
%
close all;clc;clear all;
N_rom   = 16;   % number of rom data
N_plot  = 32;   % number of sine wave plot points
idx_N = [0:N_rom-1] ; % rom index

acc_WL = 5; % phase acc word length in bits 5比特相位累加器
rom_WL = log2(N_rom); % wave table rom word length 4比特波形
acc_inc_val_1 = 1;  % acc increase value 相位增量1
acc_inc_val_2 = 2;  %相位增量2
acc_inc_val_3 = 4;  %相位增量4
max_acc_val = 2^acc_WL - 1; % 最大相位增量 2^5-1 = 31 
rom_data = sin(2*pi*idx_N/N_rom);%生成16点正弦波

acc_val_plot = zeros(3,N_plot);
idx_plot = [0:N_plot-1]; % plot index
acc_val_plot(1,:) = mod(idx_plot*acc_inc_val_1, max_acc_val);%除后的余数,取模运算
acc_val_plot(2,:) = mod(idx_plot*acc_inc_val_2, max_acc_val);
acc_val_plot(3,:) = mod(idx_plot*acc_inc_val_3, max_acc_val);
wav_val_plot = zeros(3,N_plot);
rom_addr = bitshift(acc_val_plot, rom_WL - acc_WL);%位移指定位数 将数据部分作为rom地址,二进制一画就能明白了
wav_val_plot(1,:) = rom_data(rom_addr(1,:)+1);
wav_val_plot(2,:) = rom_data(rom_addr(2,:)+1);
wav_val_plot(3,:) = rom_data(rom_addr(3,:)+1);


figure; subplot(3,1,1);
stem(idx_plot, wav_val_plot(1,:), 'fill');
title_str = sprintf('DDS Sine Wave, %dbit ACC, %dbit ROM ADDRESS, ACC INCREASE VAL is %d ', ...
 acc_WL, rom_WL, acc_inc_val_1);
title(title_str, 'FontSize', 14);
for(i_loop = 0:N_plot-1) 
  text(i_loop +0.1,0.1*sign(wav_val_plot(1,i_loop+1)+1E-10),...
    num2str(acc_val_plot(1,i_loop+1)),'FontSize',12, 'color', 'r') ;
  text(i_loop +0.1,0.3*sign(wav_val_plot(1,i_loop+1)+1E-10),...
    num2str(rom_addr(1,i_loop+1)),'FontSize',12, 'color', 'k') ;
end;
subplot(3,1,2);
stem(idx_plot, wav_val_plot(2,:), 'fill');
title_str = sprintf('DDS Sine Wave, %dbit ACC, %dbit ROM ADDRESS, ACC INCREASE VAL is %d ', ...
 acc_WL, rom_WL, acc_inc_val_2);
title(title_str, 'FontSize', 14);
for(i_loop = 0:N_plot-1) 
  text(i_loop +0.1,0.1*sign(wav_val_plot(2,i_loop+1)+1E-10),...
    num2str(acc_val_plot(2,i_loop+1)),'FontSize',12, 'color', 'r') ;
  text(i_loop +0.1,0.3*sign(wav_val_plot(2,i_loop+1)+1E-10),...
    num2str(rom_addr(2,i_loop+1)),'FontSize',12, 'color', 'k') ;
end;
subplot(3,1,3);
stem(idx_plot, wav_val_plot(3,:), 'fill');
title_str = sprintf('DDS Sine Wave, %dbit ACC, %dbit ROM ADDRESS, ACC INCREASE VAL is %d ', ...
  acc_WL, rom_WL, acc_inc_val_3);
title(title_str, 'FontSize', 14);
for(i_loop = 0:N_plot-1) 
  text(i_loop +0.1,0.1*sign(wav_val_plot(3,i_loop+1)+1E-10),...
    num2str(acc_val_plot(3,i_loop+1)),'FontSize',12, 'color', 'r') ;
  text(i_loop +0.1,0.3*sign(wav_val_plot(3,i_loop+1)+1E-10),...
    num2str(rom_addr(3,i_loop+1)),'FontSize',12, 'color', 'k') ;
end;

DDS的ROM压缩概念

直接数字频率合成器采用只读存储器来完成相位到幅度的映射由于DDS的频谱纯度与ROM表的大小成正比,而增大ROM又会使系统功率消耗增大,稳定性降低。
【ZYNQ】从入门到秃头11 DAC FIFO实验(AXI-stream FIFO IP核配置)_第37张图片

因此,为了既能满足信号的性能指标又能减少系统的开销,很多人开始寻求更好的ROM压缩方法

因为使用了ROM压缩,所以我们就会看到频率字大于ROM表的现象

这里的原理类似于插值和抽取的概念,采用一种映射的方式实现了较小的ROM空间存储更多的信息,但是对于数字电路而言,实现过程牵扯到的绝非插值和抽取

假设ACC的频率字是32位,ROM是10位(10位指ROM的地址线宽)

实现过程中,将32位的ACC中一部分拿出来做ROM地址,这时候该怎么做呢?

答案其实就是把高10个比特,ACC[31:22]拿出来做rom地址就是了,在Matlab的仿真代码中可以深刻体会

但是吧,由于DAC是带有阶梯保持的功能的,所以这么做完了,不是一个简单的抽取,而是等于对一个理想正弦信号的抽样序列,在时域卷积了一个理想方波,所以等于在频域乘了一个sin(x)/x ,这个就是所谓的频域的sinc 衰落,这些是需要仔细抠指标的时候才会考虑的事情了

你可能感兴趣的:(ZYNQ,fpga开发,dds,zynq)