1、DIT-FFT至简设计实现法1、 DIT-FFT算法的基本原理有限长序列xn的N点DFT定义为:Xk=n=0N-1xnWNKn,式中WN=e-j2N。DFT在实际应用中很重要,但是如果直接按DFT变换进行计算,当序列长度N很大时,计算量会非常大,所需时间也很长,因此常用的是DFT的一种快速计算算法,简称FFT。最常用的FFT算法是基于时间抽取的基2-FFT算法和基于频率抽取的基2-FFT算法,这种算法的特点在于FFT会把一次大的DFT分割成几个小的DFT,这样递归式地细分下去,例如有8个采样点的FFT,首先会把最外层的8点运算分成两个4点FFT的奇偶组合,第二层FFT又分成四个两点FFT的奇偶
2、组合,并且由此计算出的频谱中很有趣的一点在于对于实数输出的数组,后面一半和前面一半正好对称相同,对于虚数输出的数组,后面一半是前面数组对称后乘上负1,因此,我们只需要算出FFT的一半即可求出全部。 本设计讨论的是基于至简设计法实现按时间抽选的基2-FFT算法(即DIF-FFT)实现过程,支持N由8到1024。 图 1按时间抽取的基2-FFT算法蝶形运算流图(N=8)2、 蝶形运算至简实现过程2、1 模块划分 图 2蝶形运算模块框图 本模块包括三个RAM模块(RAM1,RAM2,RAM3)与一个DFT模块,各模块功能如下:1) RAM1模块:在开始进行蝶形运算前,全部采样点(如图1所示的x(0)
3、、x(4)、x(2)、x(6)、x(1)、x(5)、x(3)、x(7))已经按照倒位序二进制的地址依次存储在RAM1模块中,即地址0保存了采样点x(0),地址1保存了采样点x(4)。选用双端口RAM1可以同时对两点采样数据(如图1的x(0)、x(4)进行读、写操作。2) RAM2模块:RAM2模块也是采用双端口输入输出,可同时对两点数据进行读、写操作。3) DFT模块:DFT模块用于对RAM1、RAM2输出的两点采样数据(如图1的x(0)、x(4))进行蝶形运算,它将运算结果输出至RAM1、RAM2模块进行保存。4) RAM3模块:RAM3模块是单输出模块,理论是应保存N(N为采样点个数)个运
4、算参数WNr,但由于每一次蝶形运算结果(如 x1k+WNkX2(k), x1k-WNkX2(k))具有对称性,因此RAM3只需要保存N/2个WNr即可。2、1、1 奇数轮蝶形运算 图3 第奇数轮蝶形运算流图如图3所示,RAM1首先根据计数器给出的两个点的地址(如地址0,地址1)进行数据读操作,然后将数据(如x1(k)和x2(k))送进DFT模块进行运算,最后RAM2将DFT模块输出的数据(如x1k+WNkX2(k), x1k-WNkX2(k))按照原来的地址顺序进行写操作,直到RAM1全部读完N个数据,并且RAM2全部写完N个数据后,则第一轮蝶形运算计算完毕。2、1、2 偶数轮蝶形运算 图4
5、第偶数轮蝶形运算流图偶数轮运算跟奇数轮运算相似,唯一的不同就是:读取RAM由RAM1改为RAM2,写RAM由RAM2改为RAM1。RAM1与RAM2按照这样的读写交替顺序,直至历遍完n轮蝶形运算(n为蝶形运算一共要计算的轮数)。2、2 计数器架构设计由于需要依次读取和写入RAM1和RAM2,并且还要经过N轮的运算,很明显需要运用到计数器。计数器架构,关乎到整个设计的可靠性和至简性,因此是重中之中的设计。按照至简设计法的建议,需要用到N轮运算,这需要一个计数器但每轮的计数器如何设计呢?由于这些计数器主要是用于产生读写地址的,所以我们需要仔细分析地址的规律。我们以8点的FFT为例进行分析。观察上图
6、,每一轮取址如表1所示:蝶形运算第几轮运算节点第一次蝶形运算第二次蝶形运算第三次蝶形运算第四次蝶形运算1X1(k)的地址0246X2(k)的地址13572X1k的地址0145X2k的地址23673X1(k)的地址0123X2(k)的地址4567表1 N为8的蝶形运算每一轮取址蝶形运算每一轮每一次的取地址满足什么关系呢,如何才能在FPGA设计中实现如表1的取地址运算,观察上表,我们可以发现如下规律:第几级蝶形运算X1(k)的地址第一次第二次第三次第四次第一级0=0+0*21 2=0+1*21 4=0+2*21 6=0+3*21 第二级0=0+0*22 1=1+0*22 4=0+1*22 5=1+
7、1*22 第三级0=0+0*23 1=1+0*23 2=2+0*23 3=3+0*23 第几级蝶形运算X2(k)的地址第一次第二次第三次第四次第一级1=20 +0+0*21 3=20 +0+1*21 5=20 +0+2*21 7=20 +0+3*21 第二级2=21 +0+0*22 3=21 +1+0*22 6=21 +0+1*22 7=21 +1+1*22 第三级4=22 +0+0*23 5=22 +1+0*23 6=22 +2+0*23 7=22 +3+0*23 表2 X1(k)的取址 表3 X2(k)的取址根据表2、表3,可得到X1k地址与X2k地址与数组a,b,c有关的表达式X1k地址
8、=a+b*2c;X2k地址=X1k地址+2c-1; (式1)通过上面的观察,按照明德扬的计数器架构建议,可设计三个计数器cnt0,cnt1,cnt2分别表示数组a,b,c,因此可将式1变为:X1k地址=cnt0+cnt1*2cnt2+1;X2k地址=X1k地址+2cnt2; (式2)各个计数器每一轮的结束条件为: cnt0=2cnt2-1; cnt1=2n-1-cnt2-1; cnt2=n-1; (式3)其中n为蝶形运算一共要计算的轮数,如采样点数N为8时,则一共要进行三轮运算。通过这三个简易的计数器设计,就能实现复杂的DIT-FFT蝶形运算取址操作。终上所述,无论是模块划分、计数器设计、还是
9、乒乓操作的读写处理,都始终基于“至简设计”的原则,用简易的代码结构就能实现复杂的DIT-FFT蝶形运算,代码设计风格极其简洁,详细可参考附录代码。本案例是FFT的串行实现,但根据同样的思路和资源换速度的思想,可以很方便地实现多个并行或者全并行的设计。3、至简设计代码实现(附录部分代码)1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
10、848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881
11、89190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272parameter N = 512;parameter LOGN = 9;/*/计数器架构,下面三
12、个计数器用于产生读地址*/always (posedge clk or negedge rst_n)begin if(!rst_n)begin cnt0 = 0; end else if(add_cnt0)begin if(end_cnt0) cnt0 = 0; else cnt0 = cnt0 + 1; endendassign add_cnt0 = flag;assign end_cnt0 = add_cnt0 & cnt0= (1cnt2)-1 ;always (posedge clk or negedge rst_n)begin if(!rst_n)begin cnt1 = 0; en
13、d else if(add_cnt1)begin if(end_cnt1) cnt1 = 0; else cnt1 (cnt2+1)-1 ;always (posedge clk or negedge rst_n)begin if(!rst_n)begin cnt2 = 0; end else if(add_cnt2)begin if(end_cnt2) cnt2 = 0; else cnt2 = cnt2 + 1; endendassign add_cnt2 = end_cnt1;assign end_cnt2 = add_cnt2 & cnt2=LOGN-1 ;/*/计数器架构,下面三个计
14、数器用于产生写地址*/always (posedge clk or negedge rst_n)begin if(!rst_n)begin wr_cnt0 = 0; end else if(add_wr_cnt0)begin if(end_wr_cnt0) wr_cnt0 = 0; else wr_cnt0 = wr_cnt0 + 1; endendassign add_wr_cnt0 = fft_dout_vld;assign end_wr_cnt0 = add_wr_cnt0 & wr_cnt0=(1wr_cnt2)-1 ;always (posedge clk or negedge rs
15、t_n)begin if(!rst_n)begin wr_cnt1 = 0; end else if(add_wr_cnt1)begin if(end_wr_cnt1) wr_cnt1 = 0; else wr_cnt1 (wr_cnt2+1)-1 ;always (posedge clk or negedge rst_n)begin if(!rst_n)begin wr_cnt2 = 0; end else if(add_wr_cnt2)begin if(end_wr_cnt2) wr_cnt2 = 0; else wr_cnt2 = wr_cnt2 + 1; endendassign ad
16、d_wr_cnt2 = end_wr_cnt1;assign end_wr_cnt2 = add_wr_cnt2 & wr_cnt2=LOGN-1 ;/*RAM1地址0的设计*/always (posedge clk or negedge rst_n)begin if(rst_n=1b0)begin addr_0 = 0; end else if(wr_cnt20=0) begin addr_0 = cnt0 + cnt1*(1(cnt2+1); end else begin addr_0 = wr_cnt0 + wr_cnt1*(1(wr_cnt2+1); endend/*RAM1写数据0的
17、设计*/always (posedge clk or negedge rst_n)begin if(rst_n=1b0)begin wdata_0 = 0; end else begin wdata_0 = fft_dout0; endend/*RAM1写请求0的设计*/always (posedge clk or negedge rst_n)begin if(rst_n=1b0)begin wrreq_0 = 0; end else if(wr_cnt20=1) begin wrreq_0 = fft_dout_vld; end else begin wrreq_0 = 0; endend/
18、*RAM1地址1的设计*/always (posedge clk or negedge rst_n)begin if(rst_n=1b0)begin addr_1 = 0; end else if(wr_cnt20=0) begin addr_1 = cnt0 + cnt1*(1(cnt2+1) + (1cnt2); end else begin addr_1 = wr_cnt0 + wr_cnt1*(1(wr_cnt2+1) + (1wr_cnt2); endend/*RAM1写数据1的设计*/always (posedge clk or negedge rst_n)begin if(rst
19、_n=1b0)begin wdata_1 = 0; end else begin wdata_1 = fft_dout1; endend/*RAM1写请求1的设计*/always (posedge clk or negedge rst_n)begin if(rst_n=1b0)begin wrreq_1 = 0; end else if(wr_cnt20=1)begin wrreq_1 = fft_dout_vld; end else begin wrreq_1 = 0; endend/*RAM2地址0的设计*/always (posedge clk or negedge rst_n)begi
20、n if(rst_n=1b0)begin addr_2 = 0; end else if(wr_cnt20=1) begin addr_2 = cnt0 + cnt1*(1(cnt2+1); end else begin addr_2 = wr_cnt0 + wr_cnt1*(1(wr_cnt2+1); endend/*RAM2写数据0的设计*/always (posedge clk or negedge rst_n)begin if(rst_n=1b0)begin wdata_2 = 0; end else begin wdata_2 = fft_dout0; endend/*RAM2写请求
21、0的设计*/always (posedge clk or negedge rst_n)begin if(rst_n=1b0)begin wrreq_2 = 0; end else if(wr_cnt20=0) begin wrreq_2 = fft_dout_vld; end else begin wrreq_2 = 0; endend/*RAM2地址1的设计*/always (posedge clk or negedge rst_n)begin if(rst_n=1b0)begin addr_3 = 0; end else if(wr_cnt20=1) begin addr_3 = cnt0
22、 + cnt1*(1(cnt2+1) + (1cnt2); end else begin addr_3 = wr_cnt0 + wr_cnt1*(1(wr_cnt2+1) + (1wr_cnt2); endend/*RAM2写数据1的设计*/always (posedge clk or negedge rst_n)begin if(rst_n=1b0)begin wdata_3 = 0; end else begin wdata_3 = fft_dout1; endend/*RAM2写请求1的设计*/always (posedge clk or negedge rst_n)begin if(rst_n=1b0)begin wrreq_3 = 0; end else if(wr_cnt20=0)begin wrreq_3 = fft_dout_vld; end else begin wrreq_3 = 0; endend