xilinx fpga fft 应用笔记xilinx.eetrend.com/files-eetrend-xilinx/forum/201507/...2012/04/24 ·...
TRANSCRIPT
一、标定的过程简单介绍:
放大板包含 3 级程控放大器的放大电路、4 个可选择的高频滤波器、1 个
50Hz 的工频滤波器,放大板主要是针对低频信号,放大频率范围:0-20KHz,
标定示意图:
放大器板ADS1274
24位ADSpartan 6
FPGA
AD5664r
16位DA
放大器控制
模拟输出数字输出
控制信号 电压输出
offset电压输出
低通滤波
◎offset 调节:控制放大板的输入接地,将放大器的放大倍数设置为 1000
倍,测试 AD 的输出电压是否为 0V,如果不是,则说明放大器内部有直流偏置,
通过调节 DA 的 offset 输出电压,使得放大板在输入为 0v 时,输出也为 0v。
◎直流标定:直流标定挺简单的,让 DA 输出固定的电位,设置放大器的倍
数,看看输出是否与理论值一致。
例:输入为 0.1mv,放大倍数为 1000 倍,检查输出是否为:0.1V ,目的主要是
为了检查放大板对直流的响应。
◎交流标定:交流标定比较复杂,为了测量放大板对交流信号的响应,主要
体现在放大器对信号的相频与幅频特性。
◎具体的做法:FPGA 控制 DA 产生一个正弦波,再通过 AD 采集放大后的数
据,并对其做 FFT(快速傅里叶变换),计算出信号的相位与幅度,与输入的波
形对比,检测放大板的对交流信号的响应。
例:①输入 f=1Hz,A=10mv,Phase = 0°的正弦波,测量输出的
信号的 A、Phase、f。
②输入 f=2Hz,A=10mv,Phase = 0°的正弦波,测量输出的
信号的 A、Phase、f
③输入 f =3Hz……
………
⑩输入 f =2000Hz……
其实简单点,就是一个频谱分析仪,求放大板的频谱图,看看放大器对交流
信号有没有产生频移与幅度的衰减。
二、FGPA 的工作
◎控制放大器,简单的 IO 应用
◎驱动 ADS1274
◎驱动 AD5664r
◎在内部生成一个 DDS(直接数字频率合成器),输出频率、相位可调的正弦
波。
◎对 AD 采集的数据进行 FFT 的转换。
三、DFT(离散傅里叶变换)的理解
3.1 傅里叶变换的基础知识
其实,大学的时候几乎每个童鞋都学过傅里叶变换,应该算是必修的课程,
但是当时我也就为考试,背背公式,不知道大家是什么心情,但是没有想过有一
天居然真的用到了,表示很无力啊。
在网上找了一下资料,也看了一些书。把一些感觉好的东西记录一下,加上
自己的一些理解。
傅立叶是一位法国数学家和物理学家的名字,英语原名是:Jean Baptiste
Joseph Fourier(1768-1830)Fourier 对热传递很感兴趣,于 1807 年在法国科学学会上
发表了一篇论文,运用正弦曲线来描述温度分布,论文里有个在当时具有争议性
的决断:任何连续周期信号可以由一组适当的正弦曲线组合而成。
。举个的例子理解下:
在数学上,关于一个信号最基本的问题在于如何将它表示和描述出来。按照
上面所说的办法,把一个信号理解成一个定义在时间或空间上的函数是一种自然
而然的表示方式,但是它对理解这一信号的内容来说常常不够。例如一段声音,
如果单纯按照定义在时间上的函数来表示,它画出来是这个样子的:
这通常被称为波形图。毫无疑问,它包含了关于这段声音的全部信息。但是
同样毫无疑问的是,这些信息几乎没法从上面这个「函数」中直接看出来,事实
上,它只不过是巴赫的小提琴无伴奏 Partita No.3 的序曲开头几个小节。下面是
巴赫的手稿,从某种意义上说来,它也构成了对上面那段声音的一个「描述」:
这两种描述之间的关系是怎样的呢?第一种描述刻划的是具体的信号数值,
第二种描述刻划的是声音的高低(即声音震动的频率)。人们直到十九世纪才渐
渐意识到,在这两种描述之间,事实上存在着一种对偶的关系,而这一点并不显
然。
根据原信号的不同类型,我们可以把傅立叶变换分为四种类别:
四种不同信号的变换结果:
。提问:数字信号处理只能处理离散的信号,所以在以上的 4 种情况中,只
有第 4 种是可行的,但是同时也参生了一个问题,就是信号必须是周期的,但是
在实际的数据中,不一定都是周期的啊,这个怎么办呢!以下是在《Digital signal
processing》(一个老外写的特别好的书)的图,看完图后应该就能明白了。
就是把有限长的信号进行复制,让其变成一个周期信号进行处理。
说到这,上一下离散傅里叶级数的公式:
上面是 DFT 的公式,至于怎么来的,我也尝试过搞明白,但是一头雾水,大
致的情况是:
2
2
1( ) ( )
p
p
t
jm t
a
tp
X m x t e dtt
(连续周期信号的傅里叶变换)
通过对连续的周期的信号的公式进行抽样处理,然后推到出来的,推的过程
就很复杂了,也不太好理解。
3.2 这样理解 DFT 的公式(不知道对不对??)
当我看到离散傅里叶级数的公式:
我的第一个感觉就是,他代表的是什么意思啊,凭什么这个公式就是对的啊,
怎么样理解这个公式呢。
⑴先说说公式各项:
◎x[i]:表示待处理的离散数据序列,其中 i 的范围为{0,N-1}
◎Re [ ]X k :Re 是 Real 的缩写,表示实部(复数), [ ]X k 表示离散的频率序列,
X 表示周期信号,K 的范围为{0,N-1}
◎cos(2 / )ki N :cos 函数
⑵公式的意义:任何连续周期信号可以由一组适当的正弦曲线组合而成。
把连续改成离散的:任何离散周期信号可以由一组适当的离散正弦曲线组合而
成。
为什么可以算就是对的呢???
在网上看到了这样一段话,感觉挺好的:
傅立叶变换是一个数学上极为精美的对象:
它是完全可逆的,任何能量有限的时域或空域信号都存在唯一的频域表达,
反之亦然;它完全不损伤信号的内在结构;
任何两个信号之间有多少相关程度(即内积);它们的频域表达之间也一
定有同样多的相关程度;
它不改变信号之间的关联性:一组信号收敛到一个特定的极限,它们的频
域表达也一定收敛到那个极限函数的频域表达。
那怎么计算相关性呢,在网上看见了一个很不错的例子:
http://blog.csdn.net/v_JULY_v/article/details/6196862
以下是他举的例子:
利用第一种方法、信号的相关性(correlation)可以从噪声背景中检测出已知的
信号,我们也可以利用这个方法检测信号波中是否含有某个频率的信号波:把一
个待检测信号波乘以另一个信号波,得到一个新的信号波,再把这个新的信号波
所有的点进行相加,从相加的结果就可以判断出这两个信号的相似程度。如下图:
上面 a 和 b 两个图是待检测信号波,图 a 很明显可以看出是个 3 个周期的正
弦信号波,图 b 的信号波则看不出是否含有正弦或余弦信号,图 c 和 d 都是个 3
个周期的正弦信号波,图 e 和 f 分别是 a、b 两图跟 c、d 两图相乘后的结果,图
e 所有点的平均值是 0.5,说明信号 a 含有振幅为 1 的正弦信号 c,但图 f 所有点
的平均值是 0,则说明信号 b 不含有信号 d。这个就是通过信号相关性来检测是
否含有某个信号的方法。
通过上面的例子,可以进一步确定:任何两个信号之间有多少相关程度(即
内积),它们的频域表达之间也一定有同样多的相关程度。
再来个实际的例子:
这个是圈圈写的,在此引用:
一个模拟信号,经过 ADC 采样之后,就变成了数字信号。采样定理告诉我
们,采样频率要大于信号频率的两倍,这些我就不在此罗嗦了。
采样得到的数字信号,就可以做 FFT 变换了。N 个采样点,经过 FFT 之后,
就可以得到 N 个点的 FFT 结果。为了方便进行 FFT 运算,通常 N 取 2 的整数次
方。
假设采样频率为 Fs,信号频率 F,采样点数为 N。那么 FFT 之后结果就是一
个为 N 点的复数。每一个点就对应着一个频率点。这个点的模值,就是该频率
值下的幅度特性。具体跟原始信号的幅度有什么关系呢?假设原始信号的峰值为
A,那么 FFT 的结果的每个点(除了第一个点直流分量之外)的模值就是 A 的
N/2 倍。而第一个点就是直流分量,它的模值就是直流分量的 N 倍。而每个点
的相位呢,就是在该频率下的信号的相位。第一个点表示直流分量(即 0Hz),
而最后一个点 N 的再下一个点(实际上这个点是不存在的,这里是假设的第 N+1
个点,也可以看做是将第一个点分做两半分,另一半移到最后)则表示采样频率
Fs,这中间被 N-1 个点平均分成 N 等份,每个点的频率依次增加.n 所表示的频
率为:Fn=(n-1)*Fs/N。由上面的公式可以看出,Fn 所能分辨到频率为为 Fs/N,
如果采样频率 Fs 为 1024Hz,采样点数为 1024 点,则可以分辨到 1Hz。1024Hz
的采样率采样 1024 点,刚好是 1 秒,也就是说,采样 1 秒时间的信号并做 FFT,
则结果可以分析到 1Hz,如果采样 2 秒时间的信号并做 FFT,则结果可以分析到
0.5Hz。如果要提高频率分辨力,则必须增加采样点数,也即采样时间。频率分
辨率和采样时间是倒数关系。
假设 FFT 之后某点 n 用复数 a+bi 表示,那么这个复数的模就是 An=根号
a*a+b*b,相位就是 Pn=atan2(b,a)。根据以上的结果,就可以计算出 n 点(n≠1,
且 n<=N/2)对应的信号的表达式为: An/(N/2)*cos(2*pi*Fn*t+Pn) ,即
2*An/N*cos(2*pi*Fn*t+Pn)。对于 n=1 点的信号,是直流分量,幅度即为 A1/N。
由于 FFT 结果的对称性,通常我们只使用前半部分的结果,即小于采样频率一
半的结果。
好了,说了半天,看着公式也晕,下面圈圈以一个实际的信号来做说明。假
设我们有一个信号,它含有 2V 的直流分量,频率为 50Hz、相位为-30 度、幅度
为 3V 的交流信号,以及一个频率为 75Hz、相位为 90 度、幅度为 1.5V 的交流信
号。用数学表达式就是如下:
S=2+3*cos(2*pi*50*t-pi*30/180)+1.5*cos(2*pi*75*t+pi*90/180)
式中 cos 参数为弧度,所以-30 度和 90 度要分别换算成弧度。我们以 256Hz
的采样率对这个信号进行采样,总共采样 256 点。按照我们上面的分析,
Fn=(n-1)*Fs/N,我们可以知道,每两个点之间的间距就是 1Hz,第 n 个点的频
率就是 n-1。我们的信号有 3 个频率:0Hz、50Hz、75Hz,应该分别在第 1 个点、
第 51 个点、第 76 个点上出现峰值,其它各点应该接近 0。实际情况如何呢?我
们来看看 FFT 的结果的模值如图所示。
图 1 FFT 结果
从图中我们可以看到,在第 1 点、第 51 点、和第 76 点附近有比较大的值。我们
分别将这三个点附近的数据拿上来细看:
1 点: 512+0i
2 点: -2.6195E-14 - 1.4162E-13i
3 点: -2.8586E-14 - 1.1898E-13i
50 点:-6.2076E-13 - 2.1713E-12i
51 点:332.55 - 192i
52 点:-1.6707E-12 - 1.5241E-12i
75 点:-2.2199E-13 -1.0076E-12i
76 点:3.4315E-12 + 192i
77 点:-3.0263E-14 +7.5609E-13i
很明显,1 点、51 点、76 点的值都比较大,它附近的点值都很小,可以认为
是 0,即在那些频率点上的信号幅度为 0。接着,我们来计算各点的幅度值。分
别计算这三个点的模值,结果如下:
1 点: 512
51 点:384
76 点:192
按照公式,可以计算出直流分量为:512/N=512/256=2;50Hz 信号的幅度为:
384/(N/2)=384/(256/2)=3;75Hz 信号的幅度为 192/(N/2)=192/(256/2)=1.5。可
见,从频谱分析出来的幅度是正确的。
附上自己对 DFT 的理解:
。假设采样率为 N,输入点的个数也为 N,
。根据奈奎斯特定理,DFT 能分解出来的最高频率为:NHz,
。分辨率为:1Hz,
DFT的意义就是将:0Hz,1Hz,2Hz……N/2Hz的信号与输入的离散序列做内积,
求每一个频率与输入的离散序列的相关程度。相关程度越大,表示这个频率的信
号在组成输入序列的所有频率信号中占有的比重越大,他的幅值也越大。(不知
道对不对??)
3.3 FFT 与 DFT
快速傅氏变换(FFT)是离散傅氏变换的快速算法,它是根据离散傅氏变换
的奇、偶、虚、实等特性,对离散傅立叶变换的算法进行改进获得的。它对傅氏
变换的理论并没有新的发现,但是对于在计算机系统或者说数字系统中应用离散
傅立叶变换,可以说是进了一大步。
设 x(n)为 N 项的复数序列,由 DFT 变换,任一 X(m)的计算都需要 N 次
复数乘法和 N-1 次复数加法,而一次复数乘法等于四次实数乘法和两次实数加法,
一次复数加法等于两次实数加法,即使把一次复数乘法和一次复数加法定义成一
次“运算”(四次实数乘法和四次实数加法),那么求出 N 项复数序列的 X(m),
即 N 点 DFT 变换大约就需要2N 次运算。当 N=1024 点甚至更多的时候,需要
2N
=1048576 次运算,在 FFT 中,利用 WN 的周期性和对称性,把一个 N 项序列(设
N=2k,k为正整数),分为两个N/2项的子序列,每个N/2点DFT变换需要(N/2)
2 次运算,再用 N 次运算把两个 N/2 点的 DFT 变换组合成一个 N 点的 DFT 变
换。这样变换以后,总的运算次数就变成
222( )
2 2
N NN N
。继续上面的例
子,N=1024 时,总的运算次数就变成了 525312 次,节省了大约 50%的运算量。
而如果我们将这种“一分为二”的思想不断进行下去,直到分成两两一组的DFT
运算单元,那么 N 点的 DFT 变换就只需要2logN N 次的运算,N 在 1024 点时,
运算量仅有 10240 次,是先前的直接算法的 1%,点数越多,运算量的节约就越
大,这就是 FFT 的优越性。
四、用 FPGA 做 FFT 的仿真
芯片为:xilinxSpartan 6
软件:ise 12.2
IP core FFT 版本:7.1
在做 FFT 之前,读了一下 IP core FFT 的数据手册,他的输出端口比较简单,
但是也有一些疑惑的地方。
ISE 提供了FFT/IFFT 的IP Core,可以完成实数、复数信号的FFT 以及IFFT
运算。FFT 的
IP Core 提供三种结构,分别为:
(1) 流水线,Streaming I/O 结构:允许连续的数据处理;
(2) 基4,Burst I/O 结构:提供数据导入/导出阶段和处理阶段。此结构拥有
较小的结构,但转换时间较长;
(3) 基2,Burst I/O 结构:使用最少的逻辑资源,同Radix-4 相同,提供两阶段
的过程。
其配置界面有3 页,第一页如图5-57所示,主要用于配置实现结构;第二页配
置数据位宽以及数据处理操作;第三页配置数据缓存空间。在实际硬件操作中,
模块的执行速度是很重要的参数,所以本文分析第一种结构,即流水线Streaming
I/O结构,以进行连续的数据处理。在进行当前帧的N 点数据时,可加载下一帧
的N 点数据,同时输出前一帧的N点数据。此结构由多个基2的蝶形处理单元构
成,每个单元都有自己的存储单元来存储输入和中间处理的数据。FFT 的计算
单元具有丰富的控制信号,其详细说明见下文。
XN_RE、XN_IM :输入操作数,分别为实部和虚部,以2 的补码输入。在
使用时应当确定其位宽。
START:FFT 开始信号,高有效。当此信号变高时,开始输入数据,随后直
接进行FFT 转换操作和数据输出。一个START 脉冲,允许对一帧进行FFT 转换。
如果每N 个时钟有一个START 脉冲或者START 始终为高,,则都可以连续进
行FFT 。如果在最初的START 前,还没有NFFT_WE ,FWD_INV_WE,
SCALE_SCH_WE信号,则START 变高后就使用这些信号的默认值。由于此IP
Core 支持非连续的数据流,因此在任何时间输入START,即可开始数据的加载。
当加载N 个数据结束后,就开始FFT 转换运算。
UNLOAD:对于Burst I/O 结构,此信号将开始输出处理的结果。对于流水
线结构和比特逆序输出的情况,此端口不是必要的。
NFFT :此端口只对实时可配置应用时有用。
NFFT_WE :此端口是NFFT 端口的使能信号。
FWD_INV :用以指示IP Core 为FFT 还是IFFT,其等于1 时IP Core 进行
FFT 运算,否则进行IFFT 运算。至于采用哪种转换运算是可以逐帧变化的。这
一端口给FFT 的使用提供了很大的方便。
FWD_INV_WE :作为FWD_INV 端口的使能信号。
SCALE_SCH:(1) 在IP Core 设计时,如果选择在计算过程中进行中间数据
的缩减,那么此信号才可起作用;(2) 输入的位宽等于2*ceil(NFFT/2),其中NFFT
= log2(point size)。(3) 流水线结构中,将每个基2 的蝶形处理单元视为一个阶段,
每个阶段进行一次数据的缩减,缩减的比例以此输入中对应阶段的两比特表示。
(4)每阶段的两比特数可以是3,2,1 或0 :它们表示了数据所需要移动的比特数。
SCALE_SCH_WE :作为SCALE_SCH 的使能信号。
SCLR :可选端口。
Reset :重置信号端口。Reset=1 时,所有工作都停止且初始化。但内部的
帧缓存保留其内容。
CE :可选端口。
CLK :输入时钟。
XK_RE,XK_IM :输出数据总线,以2 的补码输出。SCALE_SCH_WE 有
效时,输出位宽等于输入;否则,输出位宽= 输入位宽+NFFT+1。
XN_INDEX :位宽等于log2(point size),输入数据的下标。
XK_INDEX :位宽等于log2(point size),输出数据的下标。
RFD :数据有效信号,高有效,在加载数据时为高电平。
BUSY :IP Core 工作状态的指示信号,在计算FFT 转换时为高电平。
DV :数据有效指示信号,当输出端口存在有效数据时变高。
EDONE :高有效。在DONE 信号变高的前一个时钟变为高电平。
DONE :高有效。在FFT 完成后变高,且只存在一个时钟。在DONE 变高后,
IP Core 开始输出计算结果。
BLK_EXP :当使用Burst I/O 结构时可用,若选择流水线,则此端口无效
OVFLO :算法溢出指示。在数据输出时,如每帧有溢出,此信号变高。在每
帧开始处,此信号重置。
总体说这个核功能很强大,用的时候最重要的就是注意start 信号要早于输
入信号4个周期,但是实际应用中并不需要4个周期就已经开始加载数据了,可以
这样做:START 一直开,认为第一次加载数据是初始化,即从第二次开始正式
工作,从而简化了设计。
Xilinx FFT IP 核 V7.1 支持三种算法类型:全精度无压缩、块浮点型和定点
压缩(压缩比由用户自定义)。
对于全精度无压缩结构,数据通道内任意一位有意义的整数都将被保留,在
运算过程中产生的小数部分都被截断或者取整。此种结构,对于定点算法,经过
多级乘法操作以后,数据位宽将加倍递增,其输出位宽为(输入位宽+log2(数据
转换长度)+1)bits。
对于块浮点型,对于一帧数据里面的任何一数据点有相同的压缩比,这个压
缩比值由块指数(Block Exponent)作为输出值显示,而且只有在 FFT IP 核检测到
将会产生数据溢出的时候,才会进行压缩运算。
本文所采用的是定点压缩结构。该结构相对于全精度无压缩结构,能够大大
减少 FPGA 内部资源 Xtreme DSP Slices 和块 RAM 的使用,而相对于块浮点型,
可灵活调节压缩比。定点压缩结构的压缩比例表(Scale_SCH)完全由用户自定义得
到。压缩比例是按照 1、2、4 或者 8 对每一阶进行压缩,即对应于分别向右移位
0、1、2 或者 3。如果压缩不充分,则蝶形输出结果会超出其动态范围,引起数
据溢出。对于 Burst I/O 结构,Scale_SCH 的表示方法:对于每一阶的压缩比都由
指定的一个 2bits 的数表示,零阶的 2bits 数为最低位,具体形式为
[N4,N3,N2,N1,N0],每一个 2bits 数分别对应着相应阶数的压缩比。例:对于基 4
结构,数据转换长度 N=1024,Scale_SCH=[01 10 00 1110]则表示对阶 0 右移位 2,
对阶 1 右移位 3,对阶 2 右移位 0,对阶 3 右移位 2,对阶 4 右移位 1。经验总结
(可以防止产生数据溢出):对于 1024 点的基 4,Burst I/O 结构,Scale_SCH=[10
10 10 10 11];而对于 1024点的基 2结构,Scale_SCH=[01 01 01 01 01 01 01 01 01 10]。
对于流水线,Streaming I/O 结构,把临近的一对基 2 阶组在一起,即阶 0 和阶 1
为组 0,阶 2 和阶 3 为组 1,等等。Scale_SCH 的表示方法:对于每一组的压缩比
都由指定的一个 2bits 的数表示,零组的 2bits 数为最低位,具体形式为
[N4,N3,N2,N1,N0],每一个 2bits 数分别对应着相应组的压缩比,表示同组内的两
个基 2 阶有相同的压缩比。例:数据长度 N=1024,Scale_SCH=[10 10 00 01 11]
表示对组 0(阶 0 和阶 1)右移位 3,对组 1(阶 2 和阶 3)右移位 1,对组 2(阶
4 和阶 5)没有移位,对组 3(阶 6 和阶 7)右移位 2,对组 4(阶 8 和阶 9)右移
位 2。若变换长度 N 不是 4 的幂次方的时候,最后一组只包含一个基 2 阶,只能
用 00 或者 01 表示。经验总结(可以防止产生数据溢出):N=512 时,Scale_SCH=[01
10 10 10 11];N=1024 时,Scale_SCH=[1010 10 10 11]。压缩比例 Scale_SCH 的位
宽,对于流水线,Streaming I/O 结构和基 4,Burst I/O 结构,为 2*ceil(0.5*log2(N));
对于基 2,Burst I/O 结构和基 2 Lite Burst I/O 结构,为 2* log2(N),其中 N 为转
换数据长度。
在仿真过程中遇到的问题:
1. 数据的输入:datasheet 上写的是“two’s complement”,我看了许多的翻译,
多是翻译为 2 的补码的形式,我就想 2 的补码的形式是什么形式,后来百度后才
知道 two’s complement 就是补码的意思,而不是 2 的补码。
2. 还是输入的问题,数据输入分为实部和虚部,但是 AD 输出的信号为实数信
号,怎么将实数转换成复数输入,网上有个哥们提出了相同的问题。
有的人回答:将实数做正交转换,但是怎么转换呢????
还有的回答是:实部与虚部都输入 AD 的输出值,说 FFT 的速度会加快,没有
验证过。
我的理解应该:实部为 AD 的输出值,虚部为 0,验证正确。
3. 设置 Scale_SCH 时,不确定设置多大数值时,可以将 ovflo 引脚引出,看看
仿真的时候会不会溢出。
4. 数据的输入要在 rfd 为高时,才有效。
※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
FFT 的设置:
Transform length:16
R-2, Burest IO
data width 16, Phase factor16.
scaled,
convergent rounding,
natural order without cycle prefix insertion.
memory block ram,
complex multiplier- 3 multiplier structure,
butterfly arithmetic ,
use CLB logic
※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
仿真的程序:
`timescale 1ns / 1ps
///////////////////////////////////////////////////////////////////
/////////////
// Company:
// Engineer:calm_yi
//
// Create Date: 09:47:47 04/24/2012
// Design Name: fft
// Module Name: C:/Users/PC/Desktop/FFT/fft/fftt.v
// Project Name: fft
// Target Device:
// Tool versions:
// Description:
//
// Verilog Test Fixture created by ISE for module: fft
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
///////////////////////////////////////////////////////////////////
/////////////
modulefftt;
// Inputs
reg start;
regfwd_inv;
regclk;
regscale_sch_we;
regfwd_inv_we;
reg [7:0] scale_sch;
wirerfd;
wire [3:0] xn_index;
reg [15:0] xn_re;
reg [15:0] xn_im;
// Outputs
wire done;
wire busy;
wireedone;
wireovflo;
wire dv;
wire [3:0] xk_index;
wire [15:0] xk_im;
wire [15:0] xk_re;
// Instantiate the Unit Under Test (UUT)
fftuut (
.rfd(rfd),
.start(start),
.fwd_inv(fwd_inv),
.dv(dv),
.done(done),
.clk(clk),
.busy(busy),
.scale_sch_we(scale_sch_we),
.fwd_inv_we(fwd_inv_we),
.edone(edone),
.ovflo(ovflo),
.xn_re(xn_re),
.xk_im(xk_im),
.xn_index(xn_index),
.scale_sch(scale_sch),
.xk_re(xk_re),
.xn_im(xn_im),
.xk_index(xk_index)
);
initial begin
// Initialize Inputs
start = 1;
fwd_inv = 1;
clk = 0;
scale_sch_we =1;
scale_sch = 8'b01010101;
fwd_inv_we = 1;
xn_re = 0;
xn_im = 0;
num = 0;
// Wait 100 ns for global reset to finish
#100;
end
always
begin
#10 clk<= 1;
#10 clk<= 0;
end
reg [3:0]num;
always @(posedgeclk)
begin
if(rfd)
begin
num<= num + 1'b1;
case(num)
4'd0: xn_re<= 10000;
4'd1: xn_re<= 10000;
4'd2: xn_re<= 10000;
4'd3: xn_re<= 10000;
4'd4: xn_re<= 10000;
4'd5: xn_re<= 10000;
4'd6: xn_re<= 10000;
4'd7: xn_re<= 0;
4'd8: xn_re<= 0;
4'd9: xn_re<= 0;
4'd10: xn_re<= 0;
4'd11: xn_re<= 0;
4'd12: xn_re<= 0;
4'd13: xn_re<= 0;
4'd14: xn_re<= 0;
4'd15: xn_re<= 10000;
default: ;
endcase
end
end
endmodule
仿真的时序图:
◎输入为 16 个点:
。实数部分:10000、10000、10000、10000、10000、10000、10000、10000、
0、 0、 0、 0、 0、 0、 0、 0、
。虚实部分:0、0、0、0、0、0、0、0、0、0、0、0、0、0、0、0
其实就是一个幅值为 10000,占空比为 50%的方波在 rfd 为高电平时输入:
FFT 输出的数据:
dv 为高电平时输出转换的数据:
Num 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Re 5000 625 0 625 0 625 0 625 0 625 0 625 0 625 0 625
Im 0 -3142 0 -936 0 -418 0 -124 0 124 0 418 0 936 0 3142
为了验证仿真的结果,用 matlab 做了一下相同的十六个点的仿真,结果如下:
在结果中,我们发现,matlab 做出来的结果都是 FPGA 仿真的结果的两倍,
为什么,(待解决)
但是可以看出,应该是仿真结果是对的,因为 0Hz 时,也就是直流分量 FPGA
的仿真结果为 5000,而 matlab 的为 10000,所以应该是 matlab 的结果不知道那出
了问题!
◎下面是做的第二个仿真,FFT 的点数为 64,其他的参数与上面的一样。
。压缩比例:scale_sch = 12'b000000010101;
。输入的数据采样率为:64Hz/s
。采样时间为:1s
。采样的函数为:
1100*sin(5.3*2 * ) 500*sin(18*2 * ) 9000*sin(2.2*2 * )P t t t
输入的数据就是对 P 的采样后的 64 个数据。
。输入的细节:
。FFT 后输出的数据:
1100*sin(5.3*2 * ) 500*sin(18*2 * ) 9000*sin(2.2*2 * )P t t t
在图中,可以看到:22Hz、5Hz、18Hz 时的幅值是非常高的。证明包含的
频率分量是对的,但是幅值是不对的,可能跟压缩比例有关系。
以下是改变压缩比例后的结果:
压缩比例:scale_sch= 12'b010101010101;
两者之间有什么关系,需要进一步验证。
以下是 matlab 的输出结果