FPGA series # 基於SDx的fft函數加速

最近比較喪,也不知道是擔心未來還是擔心這樣的自己
SDx建工程,new—>SDx project,展開,src右鍵—>import(或者在文件夾內添加相應的.c文件)。
main.c:

#include <stdio.h>
#include <math.h>
#include "FFT.h"
#define N 256

extern complex x[s];
extern complex *W;

int main()  
{
    unsigned int i; 
    printf("start!\n");
   //輸入序列的實部和虛部s
    for(i = 0; i < N; i++)  
    {  
    	x[i].real = i;
    	x[i].img = i+1;
    }  
    FFT(x, W, N);
    for(i=0;i<N;i++)
    {
        printf("%.4f",x[i].real);
        if(x[i].img>=0.0001)
         printf("+%.4fj\n",x[i].img);
        else if(fabs(x[i].img)<0.0001)
         printf("\n");
        else
         printf("%.4fj\n",x[i].img);
     }
    return 0;  
}

FFT.c:

#include <stdio.h>
#include <math.h> 
#include <stdlib.h>
#include "FFT.h"

#define PI 3.1415926
#define s 1000

complex x[s];
complex *W;

void mul(complex a,complex b,complex *c);
void add(complex a,complex b,complex *c);
void sub(complex a,complex b,complex *c);

void FFT(complex *x, complex *W, unsigned int N)
{  
	unsigned int i, j, tw, a, b, temp_a;
	unsigned int level_num, sample_num;
    complex temp, top,bottom,xw;
    /*旋轉因子*/
    W = (complex *)malloc(sizeof(complex) * N);  //生成變換核
    for(tw = 0; tw < N; tw++)
    {
      W[tw].real = cos(2 * PI / N * tw);   //計算旋轉因子
      W[tw].img = -1 * sin(2 * PI / N * tw);
    }
    /*倒序*/
    for(a = 0; a < N; a++)
    {
    	temp_a = a;
    	b = 0;
    	for(temp_a = 0; temp_a < log(N)/log(2); temp_a++)
    	{
    		b <<= 1;
    		b |= (temp_a&1);
    		temp_a >>= 1;
    	}
    	if(b > a)
    	{
    		temp = x[a];
    		x[a] = x[b];
    		x[b] = temp;
    	}
    }
    /*fft運算*/
    for(level_num = 0; level_num < log(N)/log(2); level_num++)        //級  
    {     
        //個數、蝶“距離”
        sample_num = 1 << level_num;  
        for(i = 0; i < N; i += 2*sample_num )     //組  
        {              
            for(j = 0; j < sample_num; j++)        //個  
            {         
                mul(x[i+j+sample_num],W[N*j/2/sample_num],&xw);  
                add(x[i+j],xw,&top);  
                sub(x[i+j],xw,&bottom);  
                x[i+j]=top;  
                x[i+j+sample_num]=bottom;  
            }  
        }  
    }  
}

void mul(complex a,complex b,complex *c)  //複數乘法的定義
{
    c->real=a.real*b.real-a.img*b.img;
    c->img=a.real*b.img+a.img*b.real;
}
void add(complex a,complex b,complex *c)  //複數減法的定義
{
    c->real=a.real+b.real;
    c->img=a.img+b.img;
}
void sub(complex a,complex b,complex *c)  //複數減法的定義
{
    c->real=a.real-b.real;
    c->img=a.img-b.img;
}

FFT.h:

#ifndef FFT__H_
#define FFT__H_

#define s 1000

typedef struct{
		double real;
		double img;
	}complex;
complex x[s];
complex *W;

#pragma SDS data mem_attribute(a:PHYSICAL_CONTIGUOUS)
#pragma SDS data copy(a[0:s])
#pragma SDS data access_pattern(a:SEQUENTIAL)
#pragma SDS data sys_port(a:AFI)

#pragma SDS data mem_attribute(b:PHYSICAL_CONTIGUOUS)
#pragma SDS data copy(b[0:s])
#pragma SDS data access_pattern(b:SEQUENTIAL)
#pragma SDS data sys_port(b:AFI)

void mul(complex a,complex b,complex *c);
void add(complex a,complex b,complex *c);
void sub(complex a,complex b,complex *c);

void FFT(complex *x, complex *W, unsigned int N);

#endif

過程中一些語法和應用問題:

  • .c文件和.h文件在處理全局變量和結構體時的關係應用
    結構體是一種類型,定義一種類型最好是在.h定義,這樣其他地方想用這個結構體,只需包含此.h文件即可,但是定義結構體變量的話,最好在.c文件中定義(爲了防止重複定義,所以不建議在.c文件中定義變量),然後在.h裏面extern聲明,其他.c文件想用只需包含那個.h文件即可
//point.h
#ifndef POINT_H
#define POINT_H
struct POINT{
int x;
int y;
};
#endif

//1.c
#include "point.h"
struct POINT p1,p2,p3;

//1.h(#ifndef之類的略)
#include "point.h"
extern struct POINT p1,p2,p3;

2.c
#include "point.h"
#include "1.h"
//後面就可以用p1,p2,p3了。以後每個.c都像這樣用就可以了,變量在哪個.c裏定義的,就在與之對應的.h裏extern,以後要用到的每個頭文件都include它。

參考:c語言頭文件中定義全局變量的問題

畫重點!這裏引申出一個重要問題,不能在頭文件中定義全局變量!就是說頭文件中不可以放變量的定義!!!一般情況下頭文件中只放變量的聲明,因爲頭文件要被其他文件包含(即 #include),如果把定義放到頭文件的話,就不能避免多次定義變量。就會報錯爲:multiple definition of
根據百度的搜索,有網友說:
原因有二:
一、跟蹤難度大。如果工程小,跟蹤其變化沒有什麼難度,如果工程很大,包含這個頭文件的文件都有可能修改其值,出了問題不好排查。
二、c主要還是用於嵌入式,與硬件有關。許多嵌入式系統的內存不像電腦那麼大,如果在頭文件中聲明全局變量,那麼所有引用該頭文件的文件都將爲此變量非配內存,這樣降低了內存的利用率,有時幾K就是致命的。
也可參考:能不能在頭文件中定義全局變量?
且,定義和聲明並不是一個概念
extern的問題在於不知道這個關鍵詞出現的時候到底是聲明還是定義
1、函數的聲明extern關鍵詞是可有可無的,因爲函數本身不加修飾的話就是extern。但是引用的時候一樣需要聲明的。

2、全局變量在外部使用聲明時,extern關鍵字是必須的,如果變量沒有extern修飾且沒有顯式的初始化,同樣成爲變量的定義,因此此時必須加extern,而編譯器在此標記存儲空間在執行時加載內並初始化爲0。而局部變量的聲明不能有extern的修飾,且局部變量在運行時纔在堆棧部分分配內存。

3、全局變量或函數本質上講沒有區別,函數名是指向函數二進制塊開頭處的指針。而全局變量是在函數外部聲明的變量。函數名也在函數外,因此函數也是全局的。

4、謹記:聲明可以多次,定義只能一次。

5、

		extern int i; //聲明,不是定義
		int i; //聲明,也是定義
  • extern “C”的作用詳解C++中爲什麼有時要使用extern "C"

  • #pragma once與 #ifndef的區別

  • strlen和sizeof的區別
    sizeof是運算符,在頭文件中typedef爲unsigned int,其值在編譯時即計算好了,參數可以是數組、指針、類型、對象、函數等。
    它的功能是:獲得保證能容納實現所建立的最大對象的字節大小。具體而言,當參數分別如下時,sizeof返回的值表示的含義如下:數組——編譯時分配的數組空間大小;指針——存儲該指針所用的空間大小(存儲該指針的地址的長度,是長整型,應該爲4)。
    strlen是函數,要在運行時才能計算。參數必須是字符型指針。當數組名作爲參數傳入時,實際上數組就退化成指針了。
    它的功能是:返回字符串的長度。該字符串可能是自己定義的,也可能是內存中隨機的,該函數實際完成的功能是從代表該字符串的第一個地址開始遍歷,直到遇到結束符NULL。返回的長度大小不包括NULL。

  • struct和typedef struct區別
    1 、注意在C和C++裏不同
        在C中定義一個結構體類型要用typedef:
        typedef struct Student
        {
        int a;
        }Stu;
        於是在聲明變量的時候就可:Stu stu1;(如果沒有typedef就必須用struct Student stu1;來聲明)
        這裏的Stu實際上就是struct Student的別名。Stu==struct Student
        另外這裏也可以不寫Student(於是也不能struct Student stu1;了,必須是Stu stu1;)
        typedef struct
        {
        int a;
        }Stu;
        但在c++裏很簡單,直接
        struct Student
        {
        int a;
        };    
        於是就定義了結構體類型Student,聲明變量時直接Student stu2;
    2、在c++中如果用typedef的話,又會造成區別:
        struct Student
        {
        int a;
        }stu1;//stu1是一個變量
        
        typedef struct Student2
        {
        int a;
        }stu2;//stu2是一個結構體類型=struct Student
        
        使用時可以直接訪問stu1.a,但是stu2則必須先 stu2 s2; 然後 s2.a=10;

  • C語言頭文件和""的區別
    1.頭文件#include <> :表示引用標準庫頭文件,編譯器會從系統配置的庫環境中去尋找
    2.頭文件#include “”:一般表示用戶自己定義使用的頭文件,編譯器默認會從當前文件夾中尋找,如果找不到,則到系統默認庫環境中去尋找。

  • 包含指針的結構體作爲函數的參數
    參考:結構體作爲函數的參數
    後在build的時候發現報錯,不論是結構體數組做函數參數還是指向結構體變量的指針做函數參數,在調用該函數時,其變量前均可不加取地址符(即&)。
    書上是這麼說的:用指向結構體變量(或數組)的指針做實參是經常採用的一種辦法。形參指針和實參指針都指向同一存儲單元,形參值的改變會影響實參的值。這種特點爲函數返回多個數據提供了途徑。給出的代碼如下:

#include <stdio.h>

struct student
{
	int num;
	char *name;
	float score[3];
};

float average(struct student *pstu)
{
	float aver;
	float sum = 0;
	for(int i = 0; i < 3; i++)
	{
		sum += pstu -> score[i];
	}
	aver = sum/3;
	return aver;
}

void main()
{
	struct student stu;
	float aver;
	stu.num = 1001;
	stu.name = "Mary";
	stu.score[0] = 87.6f;
	stu.score[1] = 78.2f;
	stu.score[2] = 84.1f;
	aver = average(&stu);
	printf("num = %d name = %s average = %f\n",stu.num,stu.name,aver);
}

這裏的aver = average(&stu);中如果刪除&就會報錯如下:0
但如果是在SDx這個工程的main.c裏在調用FFT函數時在參數W(即指向結構體變量的指針)前加上這個&, build又會報錯:1
對於指針這個神奇的物種,有點迷但又很有趣,玩好了一定很有意思。這裏可以再從指針的初衷去琢磨。(題外話,也不知道是什麼時候開始,對編程的興趣沒以前那麼大了,可能是後來覺得寫什麼都寫不好,怎麼寫都有bug,雖然其中能學到很多以前不知道的。。。哎。。。不知道我這樣的人到底能做好什麼,大概也是我最近喪的很的原因。勉勵自己,希望不要那麼喪,抓緊時間和時機,找些有趣的函數或者算法去寫去上板實現。加油啊。)

在project.sdx內將system configuration設置爲standalone。2
在debug configuration(工程名右鍵—>debug as—>debug configuration)裏修改相應設置:3
還有將板子上的跳帽位置改爲JTAG模式,因爲要下elf文件。

順序大致就是build,連接開發板上電,debug。雖然指示燈看起來亮的正確,但SDx terminal裏沒有打印出任何信息,打開 secureCRT也顯示沒有串口信息,接的是UART串口,爲啥沒有打印出信息,擱置了幾天也沒有再研究。。。還是喪的很。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章