傅里叶级数与傅里叶变换
这个学过信号与系统应该都挺了解的
傅里叶级数
离散傅里叶变换
DFT将一个采样函数从一个域转换到频域。
离散信号和周期信号最重要的关联在于它可以用一组有限的数字表示。因此,可以使用数字系统来实现DFT。
X ( k ) = ∑ n = 0 N − 1 x ( n ) e − j 2 π k n / N X(k) = \sum\limits_{n=0}^{N-1}x(n)e^{-j2\pi kn/N} X(k)=n=0∑N−1x(n)e−j2πkn/N
这个求和的结果恰好落在实值轴上或虚轴上。
离散信号
如果有一个N点的时域信号
在使用DFT进行分析的时候,会得出各个品类的正弦及余弦幅度,其中余弦幅度可以看作是复数的实数部分,而正弦幅度可以看作是复数的虚数部分。
在复频域中,有N/2+1个正弦和N/2+1个余弦。
(频域样本数量为N/2+1是因为时域信号是纯实数的,复数时域信号经过DFT后有N个样本的频域信号)
具有N点的DFT可以通过NxN矩阵成一个大小为N的矢量来缺点
S = [ 1 1 1 . . . 1 1 s s 2 . . . s N − 1 1 s 2 s 4 . . . s 2 ( N − 1 ) 1 s 3 s 6 . . . s 3 ( N − 1 ) . . . . . . . . . . . . . . . 1 s N − 1 s 2 ( N − 1 ) . . . s ( N − 1 ) ( N − 1 ) ] S =\begin{bmatrix} 1&1&1&...&1\\ 1&s&s^2&...&s^{N-1}\\ 1&s^2&s^4&...&s^{2(N-1)}\\ 1&s^3&s^6&...&s^{3(N-1)}\\ ...&...&...&...&...\\ 1&s^{N-1}&s^{2(N-1)}&...&s^{(N-1)(N-1)}\\ \end{bmatrix} S=⎣⎢⎢⎢⎢⎢⎢⎡1111...11ss2s3...sN−11s2s4s6...s2(N−1)..................1sN−1s2(N−1)s3(N−1)...s(N−1)(N−1)⎦⎥⎥⎥⎥⎥⎥⎤
G = S ⋅ g , s = e − i 2 π N G = S\cdot g,s = e^{\frac{-i2\pi}{N}} G=S⋅g,s=eN−i2π
则有频域样本
G ( t ) = ∑ n = 0 N − 1 g ( n ) s k n f o r k = 0 , . . . , N − 1 G(t) = \sum_{n=0}^{N-1}g(n)s^{kn} for \ \ \ \ k = 0,...,N-1 G(t)=n=0∑N−1g(n)sknfor k=0,...,N−1
S是对角对称矩阵,且第k行和第N-k行共轭(从第0行开始数),因此N个采样点的实值输入信号仅具有N/2+1个余弦和正弦,盛于的N/2频域值提供了冗余信息。
矩阵向量乘法
示例代码
#define SIZE 8
typedef int BaseType;
void matrix_vector(BaseType M[SIZE][SIZE], BaseType V_In[SIZE], BaseType V_Out[SIZE]) {
BaseType i, j;
data_loop:
for (i = 0; i < SIZE; i++) {
BaseType sum = 0;
dot_product_loop:
for (j = 0; j < SIZE; j++) {
sum += V_In[j] * M[i][j];
}
V_Out[i] = sum;
}
}
matrix_vector 功能共有三个参数,我们对前两个参数进行乘法计算,输入矩阵和向量分别是BaseType M[SIZE][SIZE] 和 BaseType V In[SIZE]。第三个参数 BaseType V_Out[SIZE] 是合成向量。
pipeline和并行运行
这个基本算法就是循环嵌套,但是可以考虑能不能用并行思想来加速。
比如先考虑优化sum的累加
#define SIZE 8
typedef int BaseType;
void matrix_vector(BaseType M[SIZE][SIZE], BaseType V_In[SIZE], BaseType V_Out[SIZE]) {
BaseType i, j;
data_loop:
for (i = 0; i < SIZE; i++) {
BaseType sum = 0;
V_Out[i] = V_In[0] * M[i][0] + V_In[1] * M[i][1] + V_In[2] * M[i][2] +
V_In[3] * M[i][3] + V_In[4] * M[i][4] + V_In[5] * M[i][5] +
V_In[6] * M[i][6] + V_In[7] * M[i][7];
}
}
如果每个乘法同时执行,可以用加法器树求和就很好了。
在FPGA上加法器无法共享,因为加法器和多路复用器需要相同数量的资源
使用更少的乘法器将需要更多的时间周期。
在此基础上还可以进一步优化
还有更牛逼的优化
通过添加几个指令就能实现高度并行
#define SIZE 8
typedef int BaseType;
void matrix_vector(BaseType M[SIZE][SIZE], BaseType V_In[SIZE], BaseType V_Out[SIZE]) {
#pragma HLS array_partition variable=M dim=2 complete
#pragma HLS array_partition variable=V_In complete
BaseType i, j;
data_loop:
for (i = 0; i < SIZE; i++) {
#pragma HLS pipeline II=1
BaseType sum = 0;
dot_product_loop:
for (j = 0; j < SIZE; j++) {
sum += V_In[j] * M[i][j];
}
V_Out[i] = sum;
}
}
内部j循环由Vivado HLS 自动展开,因此j在每次使用的时候都被替换为常量。