FPGA实验3:DAC FIFO实验
1. 实验内容
- 基于“FPGA实验1:DDS IP 数字波形合成DAC ”和 “FPGA实验2: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分析频谱,验证频率正确。
- 本实验是一个典型的带反向流控的跨时钟域传输信号的例子
2. 实验背景知识
2.1. FIFO
FIFO即First In First Out,是一种先进先出数据存储、缓冲器,我们知道一般的存储器是用外部的读写地址来进行读写,而FIFO这种存储器的结构并不需要外部的读写地址而是通过自动的加一操作来控制读写,这也就决定了FIFO只能顺序的读写数据。
FIFO 的典型结构如下,主要分为读和写两部分,另外就是状态信号,空和满信号,同时还有数据的数量状态信号,与RAM 最大的不同是 FIFO 没有地址线,不能进行随机地址读取数据,什么是随机读取数据呢,也就是可以任意读取某个地址的数据。而 FIFO 则不同,不能进行随机读取,这样的好处是不用频繁地控制地址
线。
虽然用户看不到地址线,但是在 FIFO 内部还是有地址的操作的,用来控制 RAM 的读写接口。其地址在读写操作时如下图所示,其中==深度值也就是一个 FIFO 里最大可以存放多少个数据。==初始状态下,读写地址都为 0,在向 FIFO 中写入一个数据后,写地址加 1,从 FIFO 中读出一个数据后,读地址加 1。此时 FIFO 的状态即为空,因为写了一个数据,又读出了一个数据。
可以把 FIFO 想象成一个水池,写通道即为加水,读通道即为放水,假如不间断的加水和放水,如果加水速度比放水速度快,那么 FIFO 就会有满的时候,如果满了还继续加水就会溢出overflow,如果放水速度比加水速度快,那么 FIFO 就会有空的时候,所以把握好加水与放水的时机和速度,保证水池一直有水是一项很艰巨的任务。也就是判断空与满的状态,择机写数据或读数据。
2.2. 同步FIFO和异步FIFO
根据读写时钟,可以分为同步 FIFO(读写时钟相同)和异步 FIFO(读写时钟不同)。
同步FIFO,读和写应用同一个时钟。它的作用一般是做交互数据的一个缓冲,也就是说它的主要作用就是一个buffer。
异步FIFO,读写应用不同的时钟,它有两个主要的作用,一个是实现数据在不同时钟域进行传递,另一个作用就是实现不同数据宽度的数据接口。
3. 实验步骤
1. 本实验基于“FPGA实验1:DDS IP 数字波形合成DAC ” 和“FPGA实验2:ADDA测试”做进一步实验,使用vivado的IP Catalog工具,配置IP参数,其中VIO参数配置与实验2相同。
DDS参数配置:
Clocking wizard参数配置:
其余参数配置与前两个实验相同。
AXI4-Stream Data FIFO参数配置:
FIFO depth
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信号也会省略。如图设置为1则可以看到位宽为8bit。
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为相应的位宽。
ILA参数配置:
ILA观察的波形:FIFO输出波形,DAC接收到的数据,DDS生成的数据,FIFO data count信号。
2. 设计顶层(top)模块,DA数据发送(da_wave_send)模块和Fword_set(频率控制字)模块,在前两个实验基础上例化AXI4-Stream Data FIFO,并将接口对应连接起来。
程序结构
顶层(top)设计
`timescale 1ns / 1psmodule top(input sys_clk ,//系统时钟 50MHz T=20nsinput sys_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、VIO按键控制频率控制字(key_PINC)wire [1:0] key_PINC;wire [7:0] ping_0x80;vio_0 vio_0 (.clk(sys_clk), // input wire clk.probe_out0(key_PINC), // output wire [1 : 0] probe_out0.probe_out1(ping_0x80) );//---------------1、MMCM模块--------------//
//output
wire [0:0] clk_out100M;
wire [0:0] clk_out25M;//---------------2、信号频率控制模块--------------//
wire [23:0] Fword ; //频率字
Fword_set Fword_set(//input.clk (clk_out100M ),.rst_n (sys_rst_n ),.key_PINC (key_PINC ),//output.Fword (Fword ));//---------------3、DDS模块--------------//
//input
wire [0:0] fre_ctrl_word_en ;
wire dds_m_axis_data_tready;
wire dds_m_axis_phase_tready;
//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 ;assign fre_ctrl_word_en=1'b1;//---------------4、axis_data_fifo模块--------------//
//output
wire [31:0] fifo_axis_wr_data_count;
wire [31:0] fifo_axis_rd_data_count;
wire [31:0] fifo_axis_data_count;
wire [7:0] fifo_m_axis_tdata;
wire fifo_m_axis_tvalid;
wire fifo_s_axis_tready;
wire da_m_axis_tready;//例化MMCM IP clk_wiz_0 clk_wiz_0(// Clock out ports.clk_out1(clk_out100M), // output clk_out1.clk_out2(clk_out25M), // output clk_out2// Status and control signals.reset(~sys_rst_n), // input reset// Clock in ports.clk_in1(sys_clk)); // input clk_in1//例化DDS IP
dds_compiler_0 dds_compiler_0 (.aclk (clk_out100M ), // 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 [23: 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 [15 : 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 [23 : 0] m_axis_phase_tdata
); //例化axis_data_fifo_0 IP
axis_data_fifo_0 axis_data_fifo_0 (.s_axis_aresetn (sys_rst_n), // input wire s_axis_aresetn 低电平有效.m_axis_aresetn (sys_rst_n), // input wire m_axis_aresetn 低电平有效.s_axis_aclk (clk_out100M), // input wire s_axis_aclk 连接DDS时钟.s_axis_tvalid (dds_m_axis_data_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 连接DDS输出.m_axis_aclk (sys_clk), // input wire m_axis_aclk 连接DAC时钟.m_axis_tvalid (fifo_m_axis_tvalid), // output wire m_axis_tvalid.m_axis_tready (dds_s_axis_config_tready), // input wire m_axis_tready 连接DDS的tready.m_axis_tdata (fifo_m_axis_tdata), // output wire [7 : 0] m_axis_tdata.axis_data_count (fifo_axis_data_count), // output wire [31 : 0] axis_data_count.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
);da_wave_send da_wave_send(.clk (sys_clk), .rst_n (sys_rst_n),.ping_0x80 (ping_0x80),.rd_data (fifo_m_axis_tdata),.da_m_axis_tready (da_m_axis_tready),.da_clk (da_clk), .da_data (da_data)); //例化ILA IP
ila_0 ila_0 (.clk(sys_clk), // input wire clk.probe0(fifo_m_axis_tdata), // input wire [7:0] .probe1(da_data), // input wire [7:0].probe2(dds_m_axis_data_tdata), // input wire [7:0] .probe3(fifo_axis_data_count) // input wire [31: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@(*)
begincase(key_PINC)0: Fword <= 'h28f5; //1Mhz 10485 每次相位增加的值 deta_theta1: Fword <= 'h51eb; //2Mhz 209712: Fword <= 'h7ae1; //3Mhz 314573: Fword <= 'ha3d7; //4Mhz 41943endcase
end
endmodule
明明已经改变了DDS的工作时钟为100Mhz,为什么这里的相位增量还是与前两个实验保持一致,我理解的原因是板载固定50Mhz,所以即使DDS的工作时钟为100Mhz,它实际还是只能达到50Mhz。
DA数据发送(da_wave_send)模块
`timescale 1ns / 1psmodule da_wave_send(input clk , //时钟input rst_n , //复位信号,低电平有效input [7:0] ping_0x80, //将有符号DDS数据+128使其变为无符号数符合DAC输入范围 input [7:0] rd_data, //读出的数据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//*****************************************************//数据rd_data是在clk的上升沿更新的,所以DA芯片在clk的下降沿锁存数据是稳定的时刻//而DA实际上在da_clk的上升沿锁存数据,所以时钟取反,这样clk的下降沿相当于da_clk的上升沿assign da_clk = ~clk; assign da_data = rd_data^ping_0x80; //将读到的数据变成无符号数据赋值给DA数据端口assign da_m_axis_tready = 1'b1;endmodule
3. 设计约束文件
这里约束文件与前两个实验相同。
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]set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN F19} [get_ports {da_data[7]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN G20} [get_ports {da_data[6]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN G19} [get_ports {da_data[5]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN H18} [get_ports {da_data[4]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN J18} [get_ports {da_data[3]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN L20} [get_ports {da_data[2]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN L19} [get_ports {da_data[1]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN M20} [get_ports {da_data[0]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN F20} [get_ports da_clk]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN E18} [get_ports {ad_data[7]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN E19} [get_ports {ad_data[6]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN D19} [get_ports {ad_data[5]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN D20} [get_ports {ad_data[4]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN M17} [get_ports {ad_data[3]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN M18} [get_ports {ad_data[2]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN L16} [get_ports {ad_data[1]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN L17} [get_ports {ad_data[0]}]
set_property -dict {IOSTANDARD LVCMOS33 PACKAGE_PIN G18} [get_ports ad_clk]#时序约束
create_clock -period 20.000 -name sys_clk -waveform {0.000 10.000} [get_ports sys_clk]
4. RTL图
5. 生成bitstream,连接开发板测试,利用VIO控制频率控制字,利用ILA观察DAC的输入数据。
ILA观察的波形:FIFO输出波形,DAC接收到的数据,DDS生成的数据,FIFO data count信号。
DDS和FIFO_data波形要将 Waveform Style 设置为 Analog(模拟),Radix 设置为 Signed Decimal(有符号十进制)。
DA波形要将 Waveform Style 设置为 Analog(模拟),Radix 设置为 Unsigned Decimal(无符号十进制)。
1Mhz正弦波:
key_PINC=0
3Mhz正弦波:
key_PINC=2
从ila观察到的波形看出,FIFO和DDS的输出波形的相位并不相同,原因可以从AXI握手时序图理解。
数据只有在主设备的VALID和从设备的READY信号都有效时才开始传输,所以产生了主设备和从设备之间数据的相位差。
6. 把ILA波形导出到CSV文件,在Matlab里分析波形的频谱,验证生成波形的正确性。
导出csv文件,并将导出的csv文件拖入matlab工作区中导入数据
matlab代码:
da_data_OBUF70 = iladatafifo1Mhz{:,5}; %取出数据
SampleinWindow = iladatafifo1Mhz{:,2};
fs=50000000; %采样频率50Mhz
N=4096; %采样点数
n=0:N-1;
t=n/fs; %时间序列
f=n*fs/N; %频率序列
figure;
subplot(2,1,1)
plot(t,da_data_OBUF70); %时域波形
title('时域波形');xlabel('时间/s');ylabel('幅值');
y=abs(fft(da_data_OBUF70,N));
subplot(2,1,2)
plot(f,y); %频域波形
title('频域波形');xlabel('频率/hz');ylabel('幅值');
1Mhz正弦波:
key_PINC=0
频域波形中有两个明显的尖峰,第一个尖峰位于1Mhz,第二个尖峰位于49Mhz(共轭对称特性)。从波形中可验证输出波形频率为1Mhz。
3Mhz正弦波:
key_PINC=2
从波形中可验证输出波形频率为3Mhz。
参考文献
[1] https://www.cnblogs.com/xuqing125/p/8337586.html
[2] course_s1_ZYNQ那些事儿-FPGA实验篇V1.06.pdf
[3] CSDN博客:https://blog.csdn.net/QQ286615275/article/details/90297888
[4] 官方文档:pg085-axi4stream-infrastructure.pdf
https://china.xilinx.com/content/dam/xilinx/support/documents/ip_documentation/axis_infrastructure_ip_suite/v1_1/pg085-axi4stream-infrastructure.pdf
[5] CSDN博客:https://blog.csdn.net/szm1234/article/details/123454871
FPGA实验3:DAC FIFO实验相关推荐
- ZYNQ FPGA实验——DAC FIFO实验
文章目录 前言 一.添加AXI4-Stream Data FIFO IP核 二.添加PLL IP核 三.添加DDS IP核 四.添加VIO IP核 五.添加ILA IP核 六.编写测试程序 七.管脚分 ...
- 异步fifo_正点原子开拓者FPGA开发板资料连载第十五章 IP核之FIFO实验
1)实验平台:正点原子开拓者FPGA 开发板 2)摘自<开拓者FPGA开发指南>关注官方微信号公众号,获取更多资料:正点原子 3)全套实验源码+手册+视频下载地址:http://www.o ...
- 【正点原子FPGA连载】第十五章 IP核之FIFO实验 -摘自【正点原子】领航者ZYNQ之FPGA开发指南_V2.0
1)实验平台:正点原子领航者ZYNQ开发板 2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761 3)全套实验源码+手册+视频下 ...
- STM32学习心得二十六:DAC数模转换实验
记录一下,方便以后翻阅~ 主要内容: 1) DAC数模转换原理: 2) 寄存器和库函数介绍: 3) 相关实验代码解读. 实验功能:系统启动后,按WK_UP键,输出电压加200点,对应电压值200*3. ...
- 计算机组成 vhdl cpu 实验 西安交大,基于FPGA的VHDL计算机组成实验平台的设计与实现...
摘要: <计算机组成原理>是计算机系的一门核心课程.但是它涉及的知识面非常广,内容包括中央处理器,指令系统,存储系统,总线和输入输出系统等方面,学生在学习该课程时,普遍觉得内容抽象难于理解 ...
- 基于FPGA实现的流水灯实验
版权声明:如需转载,请注明出处 https://blog.csdn.net/chengfengwenalan/article/details/79606351 基于FPGA实现的流水灯实验 一.开发环 ...
- 51单片机 | DAC数模转换实验
文章目录 一.DAC介绍 2.DAC 工作原理 二.PWM介绍 三.硬件设计 四.软件设计 1.PWM实现函数 2. 主函数 五.实验现象 这一节来介绍下如何使用 51 单片机输出模拟信号,要让 ...
- FPGA学习之DDR3读写实验
FPGA学习之DDR3读写实验 原理 简介 配置原理 程序代码 约束文件 设计文件 参考正点原子视频 原理 简介 DDR3 SDRAM常 简称 DDR3 是当今较为常见的一种储存器,在计算机及嵌入式产 ...
- 基于FPGA的SSRAM读写速度测试实验
概述 本人第一次接触FPGA的设计与开发,在老师的指导下完成EP3C25Q240的实验,耗时大概3个月,两个月从零开始做实验板,一个月调试板子上的SSRAM,虽然参考了altera的EP3C25F32 ...
最新文章
- 英国脱欧但网络安全领域重视未减
- JavaScript随机生成颜色以及十六进制颜色 与RGB颜色值的相互转换
- 解决IE不支持Data.parse()的问题
- 光复用技术中三种重要技术_传感器在机器人技术研究发展历程中扮演着重要角色...
- linux命令之tee,技术|为初学者介绍的 Linux tee 命令(6 个例子)
- Linux课程笔记 Crond介绍
- 大学物理实验长度的测量实验报告_大学物理实验教案长度和质量的测量两篇
- 用jQuery实现页面遮罩弹出框
- 面试题 02.06. 回文链表
- 278. First Bad Version
- 【poj3263】Tallest Cow(差分数组)
- mysql自动备份linux_Mysql for linux mysql自动备份脚本
- 易语言c语言哪个做游戏脚本,游戏简易脚本制作教程
- SRIO学习笔记之SRIO简介与Xilinx SRIO ip核例程详解
- 简单扑克牌游戏C语言,【算法】C语言实现简易的扑克牌游戏
- ES文件浏览器曝严重漏洞,或影响数亿Android用户
- (EKL)elasticsearch
- 华为煤矿军团首登央视 | 发布会金句爆棚
- Uni-app登录态管理(vuex)
- 记人生第一次工作面试 - 小米前端实习