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串口,为啥没有打印出信息,搁置了几天也没有再研究。。。还是丧的很。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值