关于AXI4-STREAM DATA FIFO的理解及带FIFO的ADDA测试

实验要求

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端口的变化。
    10.用VIO配置频率字,分别生成1MHz和3MHz的DDS正弦波形,用system ILA抓取DAC的输入数据,用Matlab分析频谱,验证频率正确。
  • 本实验是一个典型的带反向流控的跨时钟域传输信号的例子

    带FIFO的ADDA实验

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

由于ADDA实验相较于DAC仅多出一个AD数据接收模块,故将两个实验在一篇文章中进行总结

AXI4-STREAM DATA FIFO

AXI4-Stream总线

关于AXI4-Stream总线的认识,可以参考带你快速入门AXI4总线--AXI4-Stream篇(1)----AXI4-Stream总线这篇文章

AXI4-Stream协议是AXI4三个协议中最简单的一个部分,面向流的传输,不涉及内存地址,主要用于往FIFO等没有地址的数据缓冲区传送大量数据时使用。AXIS的通信过程主要基于握手机制。

握手机制,即主从设备之间借助tvalid、tready信号的状态太判断双方是否能发送或接受数据。

tvalid:该信号拉高时,表示主机可以传输数据给从机。

tready:该信号拉高时,表示从机可以接收来自主机的数据。

AXI4-STREAM DATA FIFO IP核

FIFO模块能够为AXI4-Stream数据流提供临时存储(缓冲区),多用于一下两种情况:

需要比寄存器更多的缓存单元

存储和转发:主机上积累一定数量的字节后,再转发给从机(包模式)。

FIFO,first in,first out,即先入先出的数据缓存队列,其结构如下如所示

如图所示,FIFO IP核的主要操作是对数据进行写入,读出。虽然AXI4-Stream数据流不涉及内存地址,且FIFO没有地址线,但FIFO的内部含地址操作,其读写过程如下图所示:

其中深度值为FIFO内部可以存储的最大数据量,根据读写始终,FIFO可分为同步FIFO与异步FIFO,本实验中所用到的是异步FIFO。

在IP catalog搜索,AXI4-STREAM DATA FIFO,双击出现其配置界面:

首先我们知道的是,在AXI协议中,数据通过写通道实现master到slave的传输,读通道实现slave到master的传输。因此,在FIFO IP核中,接收数据的端口S_AXIS用来将数据写入IP核,而发送数据的端口M_AXIS用来将数据读出IP核。

关于IP核中各个信号的详解,可翻阅官方手册pg085-axi4stream-infrastructure.pdf第43页,以下对该实验中所需信号进行解释。

General

FIFO depth:FIFO深度,根据需要设定,可选范围16-32768,必须为2的整数次幂

Memory type:存储资源的使用类型–不同模式下可支持的功能不同,手册有具体介绍,自动即可,需要注意的是UltraRAM模式不支持异步时钟。

Independent clocks:独立时钟设置,该实验需要使用异步时钟

CDC sync stages:跨时钟域处理的同步阶数,自动即可。

Enable Packet Mode:设置为Yes将启动FIFO的数据包模式,且启用TLAST信号(该信号表示一个数据包的结尾)。FIFO的操作在包模式下被修改为存储传送的数据,直到TLAST信号被置位,我们这里先不使用。

ACLKEN Conversion Mode:此选项用来选择ACLKEN信号的转换模式,这里暂不使用。

Signal properties

TDATA Width(bytes):此参数指定AXI4上TDATA信号的宽度(以字节为单位)。整数,可以在0到512之间变化。端口位宽*8得到以bit为单位的数据位宽,这里设置为1.

 Enable TSTRB, Enable TKEEP:TDATA WITH(字节)参数大于0时,才能使用这两项。改两项用来区分AXI协议中的三种流数据字节,包括数据字节(Data Byte),占位字节 (Position byte),空字节(Null byte),本次实验从简不使用。

Enable TLAST:TLAST信号,当FIFO启用数据包模式时,该信号指示一次传输数据流的最后一个数据,标志着该数据流的结束,本次实验从简不使用。

Flags

Enable write data count:写入数据(S_AXIS)计数输出端口,同步于写时钟。

Enable almost full:写入将满使能输出端口,同步于写时钟,Packet Mode模式下不允许使用,本实验从简不使用。

Enable programmable full:使能可编程满,同步于写时钟。可以自己设置阈值来提醒当前FIFO的写入状态。

Programmable full threshold:可编程满阈值。

Enable read data count:读出数据(M_AXIS)计数输出端口,同步于读时钟。

Enable almost empty:读出将空,同步于读时钟,Packet Mode模式下不允许使用,本实验从简不使用。

Enable programmable empty:使能可编程空,同步于读时钟。可以自己设置阈值来提醒当前FIFO的读取状态

Programmable empty threshold:可编程空阈值

此时的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
  .s_axis_aclk(s_axis_aclk),                // input wire s_axis_aclk
  .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_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_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实验模块

时钟控制模块MMCM

 按照实验要求,DDS的工作时钟以及FIFO写时钟为100MHz,DAC的工作时钟以及FIFO读时钟为50MHz。

DDS模块

关于DDS模块的详情可参考第一次实验记录DDS的理解及IP核的使用

DDS IP核的设置如下

 

 

 FIFO模块

 ILA模块

主要观察的信号为FIFO 两端的数据端口以及读写计数端口。

 顶层模块

代码如下:

`timescale 1ns / 1ps
module dac_fifo(
    input   sys_clk,
    input   rst_n,
    //DA芯片接口
    output                da_clk,  //DA(AD9708)驱动时钟,最大支持125Mhz时钟
    output    [7:0]       da_data,  //输出给DA的数据
        //AD芯片接口
    input     [7:0]       ad_data,  //AD输入数据
    output                ad_clk         //AD(AD9280)驱动时钟,最大支持32Mhz时钟 
    );
// -----------0、时钟控制模块-----------// 
//PLL IP核例化
wire    clk_50Mhz;
wire    clk_100Mhz;
wire    clk_25Mhz;
clk_wiz_0  clk_wiz_0_inst(
    // Clock out ports
    .clk_out1(clk_50Mhz),     // 给DAC与FIFO读数据端提供50MHz采样时钟
    .clk_out2(clk_100Mhz),     // 给DDS与FIFO写数据段提供100MHz采样时钟
    .clk_out3(clk_25Mhz),     // output clk_out3
    // Status and control signals
    .reset(~rst_n), // input reset
   // Clock in ports
    .clk_in1(sys_clk));      // input clk_in1    
// -----------1、DDS模块-----------// 
wire    [1:0]   key_PINC;
vio_0 vio_0_inst (
  .clk(sys_clk),                // input wire clk
  .probe_out0(key_PINC)  // output wire [1 : 0] probe_out0
);

wire    [15:0]  Fword;
Fword_set   Fword_set_inst(
     .clk       (clk_100Mhz),
     .rst_n     (rst_n),
     .key_PINC  (key_PINC),
     .Fword     (Fword)
);
//DDS IP核例化
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 [15:0]  dds_m_axis_phase_tdata    ;
wire [0:0]   dds_s_axis_config_tready    ;

assign fre_ctrl_word_en=1'b1;

dds_compiler_0 dds_compiler_0_inst (
  .aclk(clk_100Mhz),                                  // input wire aclk
  .s_axis_config_tvalid(fre_ctrl_word_en),  // input wire s_axis_config_tvalid
  .s_axis_config_tready(dds_s_axis_config_tready ),  // output wire s_axis_config_tready
  .s_axis_config_tdata(Fword),    // input wire [15 : 0] s_axis_config_tdata
  .m_axis_data_tvalid(dds_m_axis_data_tvalid),      // output wire m_axis_data_tvalid
  .m_axis_data_tready(fifo_s_axis_tready),      // input wire m_axis_data_tready
  .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_tready(fifo_s_axis_tready),    // input wire m_axis_phase_tready
  .m_axis_phase_tdata(dds_m_axis_phase_tdata)      // output wire [15 : 0] m_axis_phase_tdata
);
// -----------2、fifo模块----------// 
// 写FIFO端口			
wire  	[31 : 0] 	fifo_axis_wr_data_count;
wire			    fifo_s_axis_tready;               // FIFO tready	

// 读FIFO端口	
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;


axis_data_fifo_0 axis_data_fifo_0_inst (
  .s_axis_aresetn(rst_n),          // input wire s_axis_aresetn
  .s_axis_aclk(clk_100Mhz),                // input wire s_axis_aclk
  .m_axis_aclk(clk_50Mhz),                // input wire m_axis_aclk
  
  .s_axis_tvalid(dds_m_axis_phase_tvalid),            // input wire s_axis_tvalid
  .s_axis_tready(fifo_s_axis_tready),            // output wire s_axis_tready
  .s_axis_tdata(dds_m_axis_data_tdata),              // input wire [7 : 0] s_axis_tdata
  
  .m_axis_tvalid(fifo_m_axis_tvalid),            // output wire m_axis_tvalid
  .m_axis_tready(da_m_axis_tready),            // input wire m_axis_tready
  .m_axis_tdata(fifo_m_axis_tdata),              // output wire [7 : 0] m_axis_tdata
  
  .axis_wr_data_count(fifo_axis_wr_data_count),  // output wire [31 : 0] axis_wr_data_count
  .axis_rd_data_count(fifo_axis_rd_data_count)  // output wire [31 : 0] axis_rd_data_count
);

// -----------3、AD9708-----------// 
AD9708 AD9708_inst(
    .clk         (clk_50Mhz), 
    .rst_n       (rst_n),
    .data_in     (fifo_m_axis_tdata),
    .da_clk      (da_clk),  
    .da_data     (da_data),
    .da_m_axis_tready(da_m_axis_tready)
    );
    
// -----------4、ILA模块---------// 
//ILA IP核例化
ila_0 ila_0_inst (
	.clk(clk_50Mhz), // input wire clk


	.probe0(dds_m_axis_data_tdata), // input wire [7:0]  probe0  
	.probe1(fifo_m_axis_tdata), // input wire [7:0]  probe1
	.probe3(fifo_axis_wr_data_count), // input wire [31:0]  probe3
	.probe4(fifo_axis_rd_data_count) // input wire [31:0]  probe4
);                  

endmodule

DDS模块中的频率控制代码如下:

`timescale 1ns / 1ps
//通过按键来选择对应的频率控制字,进而选择对应的信号频率
module Fword_set(
     input              clk,
     input              rst_n,
     input  [1:0]       key_PINC,
     output reg  [15:0] Fword
    );

// 根据IP核的summery, phase width=16bits   Frequency per channel=100MHz
// 输出频率的计算公式f_out=f_clk*deta_theta/(2^B)=100M* 1966/(2^16 )= 3M   
always @(*)begin
    case(key_PINC)
        0:  Fword <= 'h51e;     //1Mhz  1310  每次相位增加的值  deta_theta
        1:  Fword <= 'hf5c;     //3Mhz  3932
    endcase
end

endmodule

DA数据发送模块AD9708代码如下:

`timescale 1ns / 1ps
module AD9708(
     input                 clk    ,  //时钟
     input                 rst_n  ,  //复位信号,低电平有效
     //AXI-S接口
     input        [7:0]    data_in,  //DDS读出的数据
     output da_m_axis_tready,    // 由于DAC的工作频率小于DDS工作频率,所以DAC接口控制器给FIFO的RDY信号应该一直为高。
     //DA芯片接口
     output                da_clk ,  //DA(AD9708)驱动时钟,最大支持125Mhz时钟
     output       [7:0]    da_data   //输出给DA的数据  
     );
 
 
 //*****************************************************
 //**                    main code
 //*****************************************************
 
 //数据data_in是在clk的上升沿更新的,所以DA芯片在clk的下降沿
 //而DA实际上在da_clk的上升沿锁存数据,所以时钟取反,这样clk的下降沿相当于da_clk的上升沿
assign  da_clk = ~clk;  
assign  da_data = data_in^'h0x80;   //将读到的DDS数据与0x80取异或赋值给DA数据端口
assign  da_m_axis_tready = 1'b1;
endmodule

由于该实验效果与带FIFO的ADDA实验相差不多,所以放在下部分一并讨论

带FIFO的ADDA实验

相较于DAC FIFO实验,该实验仅需要在顶层模块中例化AD数据接收模块AD9280,并且ILA需要添加观察信号ad_data。

// -----------4、AD9280-----------// 
AD9280 AD9280_inst(
    .clk         (clk_25Mhz), 
    .rst_n       (rst_n),
    .ad_clk      (ad_clk),  
    .ad_data     (ad_data)
    );
`timescale 1ns / 1ps
module AD9280(
     input                 clk         ,  //时钟
     input                 rst_n       ,  //复位信号,低电平有效
     
     input         [7:0]   ad_data     ,  //AD输入数据
     output                ad_clk         //AD(AD9280)驱动时钟,最大支持32Mhz时钟
     );   
assign ad_clk = ~clk;
endmodule

实验结果

RTL图

由RTL图可知 带FIFO的ADDA实验相较于DAC FIFO实验多出了AD数据接受模块FIFO两端数据的写入与读出分别接到了DDS的输出以及DA数据发送模块AD9708,MMCM为DDS以及FIFO写端口提供了100MHz,DAC以及FIFO读端口提供了50MHz。

ILA波形图

下载验证,观察ILA波形,1Mhz正弦波如下:

 3Mhz正弦波如下:

Matlab分析验证

filename = 'C:\zynq\dac_fifo\dac_fifo.srcs\csv1\iladata.csv';
M = readmatrix(filename,'Range','D3:D4098');  %读取4096个数据
fs = 50000000;      %采样率
N = length(M);      %采样点数
n = 0:N-1;
t = n/fs;           %时间序列
f = n*fs/N;         %频率序列
Y = fft(M);         %对M进行FFT变换
fshift = (-N/2:N/2-1)*fs/N;
y = fftshift(Y);    %以0为中心循环平移
y_abs = abs(y);
figure;
subplot(2,1,1);
plot(t,M);          %时域波形
title('时域波形');xlabel('时间/s');ylabel('幅值');
subplot(2,1,2);
plot(fshift,y_abs);          %频域波形
title('频域波形');xlabel('频率/s');ylabel('幅值');

 

 

参考资料

[1]AXI总线介绍_theboynoName的博客-CSDN博客

[2]带你快速入门AXI4总线--AXI4-Stream篇(1)----AXI4-Stream总线_孤独的单刀的博客-CSDN博客_axi stream

[3]pg085-axi4stream-infrastructure.pdf

  • 5
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值