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参考设计
这里有一篇文章不错,如果还是不理解,可以去学习一下
带你快速入门AXI4总线–AXI4-Stream篇(3)----详解XILINX IP AXI4 STREAM DATA FIFO
流式传输和基于地址的传输有什么区别?
直观的看,基于地址的数据传输就像是CPU访问内存,每次访问数据都需要一个地址。基于流的数据传输方式类似于FIFO,队列的访问,每次读取会把数据“读走”,下次再读取的时候已经是下一个数据,写入也是类似。
FPGA中,流式数据传输是以时钟为驱动,加一些控制信号。stream常见的实例就是AD/DA转换,AD/DA转换器在采样时钟的驱动下,不停的产生数据流,外界根据一个ready信号来决定是否读取数据。address形式常见的例子例如MCU读取AT24C04,每次发生数据的读写都会提前设置地址。
可以看出来,基于stream的数据传输更适合批量数据的传输,基于Address的数据传输适合位置型的数据。
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代表成功,其他为错误。
顺序为主与从进行读地址通道握手并传输地址内容,然后在读数据通道握手并传输所读内容以及读取操作的回应,时钟上升沿有效。如图所示:
顺序为主与从进行写地址通道握手并传输地址内容,然后在写数据通道握手并传输所读内容,最后再写回应通道握手,并传输写回应数据,时钟上升沿有效。如图所示:
面的时序图进行点说明,在AXI-STREAM总线中,有两个握手信号很关键,明白了握手信号,根据这两个信号就可以进行数据的传输。
在说信号之前,先说一下AXI-stream是分为主设备和从设备的,主设备会发出clk信号和data,从设备在条件允许的情况下,进行数据的读取。
这种握手机制,相互确定了对方是否准备好进行数据传输,一旦双方都确认,数据传输就会开始
总结
1、从机拉高tready信号,表示此刻从机空闲,可以进行数据传输;
2、主机把tdata、tkeep、tuser准备就绪以后,再把tvalid拉高,此时数据在clk的驱动下进行传输;
3、TKEEP是字节修饰符。用来表明TDATA相关字节的内容是否作为数据流的一部分被处理。TKEEP字节修饰符未被确认的那些相关字节是空字节,可以从数据流中去除。
4、tlast 数据包结束;
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为相应的位宽。
关于这些信号的具体含义以及时序关系,可以通过仿真观察。
其中除了标志信号、时钟、复位信号外,就是两个接口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
文档中几乎涵盖了全部的说明,这里针对我们所涉及到的进行阅读和理解
//----------- 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 ---------
系统的时钟是50Mhz,所以这里输入clk in是50
按照实验任务要求,DDS工作在100MHz时钟,DAC和AD的接口电路工作在50MHz,所以输出50 和 100
//----------- 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 ---------
设置虚拟按键,来控制DDS输出的波形频率
这里只需要两位长度就可以了
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的配置,相关的详细内容已经在前面介绍过了
这里选择FIFO的位深为1024
选择异步,也就是输入输出时钟频率不同
TDATA Width数据宽度1 Byte,也就是8 bit
这个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核设置输入频率 100 MHz,输出数据位宽8 bit,相位宽度16bit
参数控制,仅输出sine波形
DDS带TREADY
配置完成后的示意图
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 ---------
//----------- 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 ---------
首先贴一下全部的代码
`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
`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
############## 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]
可以看到两个不同频率的CLK,均正常输入到了指定模块
结构逻辑正确,接下来验证ILA波形
通过抓取,可以看到波形可以正常的输出,这里是1MHz的DDS正弦波
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');
12.DAC FIFO实验
https://download.csdn.net/download/szm1234/85072334
带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
`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采样频率是25MHz,所以一个单位是 0.04 微秒(μs),40 纳秒(ns)
VIO输入 00
通过观察的方法,我们可以知晓一个周期是25个时钟信号,计算周期时间为 40 ns* 25 = 1000 ns = 1 μs,即频率为 1 MHZ
计算周期时间为 40 ns* 8 = 320 ns = 0.32 μs,即频率为 3 MHZ
导出波形数据到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');
为了验证Matlab程序的可靠性,通过信号分析器得到的结果也是一样的结果
13.带FIFO的ADDA实验
https://download.csdn.net/download/szm1234/85072336
理解DDS的工作原理,为何它能生成不同频率的波形
根据时钟频率和输出波形的频率来计算DDS的频率字
下图展示了在一个 5比特的相位累加器,4比特的波形ROM地址线宽的DDS中,相位增量为1、2、4的情况下,输出波形和相位累加器以及ROM地址的关系
图中的红色数字是相位累加器的数值,黑色数字是相位累加值截断之后的ROM地址
%
% 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表的大小成正比,而增大ROM又会使系统功率消耗增大,稳定性降低。
因此,为了既能满足信号的性能指标又能减少系统的开销,很多人开始寻求更好的ROM压缩方法
因为使用了ROM压缩,所以我们就会看到频率字大于ROM表的现象
这里的原理类似于插值和抽取的概念,采用一种映射的方式实现了较小的ROM空间存储更多的信息,但是对于数字电路而言,实现过程牵扯到的绝非插值和抽取
假设ACC的频率字是32位,ROM是10位(10位指ROM的地址线宽)
实现过程中,将32位的ACC中一部分拿出来做ROM地址,这时候该怎么做呢?
答案其实就是把高10个比特,ACC[31:22]拿出来做rom地址就是了,在Matlab的仿真代码中可以深刻体会
但是吧,由于DAC是带有阶梯保持的功能的,所以这么做完了,不是一个简单的抽取,而是等于对一个理想正弦信号的抽样序列,在时域卷积了一个理想方波,所以等于在频域乘了一个sin(x)/x ,这个就是所谓的频域的sinc 衰落,这些是需要仔细抠指标的时候才会考虑的事情了