微信公众号获取更多FPGA相关源码:
1.前言
Matlab里面计算通常用的是浮点数,而FPGA在做数字信号处理时,为了节约资源,常常使用的是定点数。在实践中,我们经常需要将Matlab实现中的算法,用FPGA进行实现。
那么,Matlab里面的是浮点数怎么转换为定点数到FPGA里面进行使用,以及FPGA里面计算的定点数,怎么又在Matlab里面又转换为浮点数进行验证呢?
2.模拟信号——>数字信号
模拟信号是需要通过采样、量化、编码才能转化为数字信号进行处理。
通常在Matlab里面仿真算法的时候,只是将模拟信号进行了采样,即时间域上做了离散处理,使模拟信号变成数字序列,但是并没有将幅度值进行量化和编码,是不能够直接用于FPGA处理的。
2.1采样过程
采样过程是进行模拟信号数字化的第一步,模拟信号被采样后,称为采样信号,采样信号在时间上是离散的,但在取值上仍然是连续的,因此仍然是模拟信号。在采样时就不得不提及采样定理:
若想能够从采样后信号中恢复出原始信号,则采样信号的频率至少为原始信号最大频率的2倍,该频率又被称为奈奎斯特频率。
在理想情况下可以看作是用一连串的冲激函数与原始信号进行相乘,若要对采样信号求频谱可以先求冲击串的傅里叶级数,再用傅里叶级数来表示其傅里叶变换,即可以较少的计算量求出采样信号的傅里叶变换。
但在matlab中则不常采用这种方式,常采用的方式为,规定好时间间隔和起始时间后,对原始信号的每个规定时间间隔后的点进行取样,即可得到采样函数。
2.2量化
这里只讨论均匀量化:设模拟抽样信号的取值范围在a和b之间,量化电平数为M,则在均匀量化时的量化间隔为:
Δ
v
=
b
−
a
M
Δ v =\frac{b-a}{M}
Δv=Mb−a
且量化区间的端点为:
m
i
=
a
+
i
Δ
v
m i =a+i \Delta v
mi=a+iΔv
设对模拟信号的抽样值为:
m
(
k
T
s
)
m(kTs)
m(kTs) 所选均匀量化的量化区间端点为:
m
1
,
m
2
,
m
3
.
.
.
.
.
.
m
n
m_1,m_2,m_3......m_n
m1,m2,m3......mn
,则根据公式:
m
q
(
k
T
s
)
=
q
i
,(
m
i
−
1
⩽
m
(
k
T
s
)
⩽
m
i
)
m_q\left( kT_s \right) =q_i\text{,(}m_{i-1}\leqslant m\left( kT_s \right) \leqslant m_i\text{)}
mq(kTs)=qi,(mi−1⩽m(kTs)⩽mi)
就把模拟抽样信号 m ( k T s ) m(kTs) m(kTs)变换成了量化后的离散抽样信号,即量化信号。
非均匀量化读者感兴趣可以自行查阅相关资料。
2.3编码
前面的几篇文章中,我们详细讲解了计算机算法中的数字表示法。在使用FPGA进行信号处理时,常采用有符号的二进制补码进行存储计算。
以 8 位为例,我们约定从高位开始,1位符号位,4位整数位,后3位表示小数部分。
对于数字 -1.5 用定点数表示就是这样:
首先表示数字正的1.5,即原码:
1.
5
(
D
)
=
0000110
0
(
B
)
1.5_{(D)} = 00001 100_{(B)}
1.5(D)=00001100(B)
符号位不变,其余各位取反,+1得:
0111001
1
(
B
)
+
1
=
0111010
0
(
B
)
01110 011_{(B)} + 1 = 01110 100_{(B)}
01110011(B)+1=01110100(B)
然后对于负数,符号位填充为"1"得数字 -1.5 用定点数表示:
−
1.
5
(
D
)
=
1111010
0
(
B
)
-1.5_{(D)} = 11110 100_{(B)}
−1.5(D)=11110100(B)
表示步骤如下:
1.在有限的 bit 宽度下,先约定小数点的位置,一般取高位为符号位
2.先不管符号,整数部分和小数部分,分别转换为二进制表示
3.对于正数,两部分二进制组合起来,即是结果
4.对于负数,除符号位进行取反,然后+1,符号位填充"1"
3.举例说明
比如前面在进行OFDM调制映射时,复数以8位定点数形式进行输出,格式为:1位符号位,一位整数位,6位小数位,负数以补码形式表示。
16QAM调制的星座图映射如下表:
Input bits (b0 b1) | I-out | Input bits (b2 b3) | Q-out |
---|---|---|---|
00 | –3 | 00 | –3 |
01 | –1 | 01 | –1 |
11 | 1 | 11 | 1 |
10 | 3 | 10 | 3 |
映射过后,为了使平均功率一致,还需要乘上归一化因子,这就使得调制后的输出是复数形式的小数。
当输入为0 1 1 0时,映射后实部和虚部输出分别是-1,3。进行归一化后为 − 0.3125 + 0.9531 i -0.3125 + 0.9531i −0.3125+0.9531i。编码过后是11000011 + 00111101i。
这一步是怎么做到的呢?在Matlab中我是这样做的,当然也有其他办法,答案不唯一。
3.1 Quantizer函数:按给定间隔将输入离散化
在Matlab中,Quantizer 模块使用量化算法离散化输入信号。该模块使用舍入到最邻近整数方法将信号值映射到由量化区间定义的输出端的量化值。平滑的输入信号在量化后可能会呈现阶梯形状。
可以去看官方文档:https://ww2.mathworks.cn/help/simulink/slref/quantizer.html
以下方程用数学方法说明舍入到最邻近整数方法:
y
=
q
∗
r
o
u
n
d
(
u
/
q
)
y = q * round(u/q)
y=q∗round(u/q)
其中,y 是量化输出,u 是输入,q 是量化区间。
在matlab命令行窗口输入:
q= quantizer()
如下图:
可以看到量化格式q包含着四个参数‘datamode’,‘roundmode’,‘overflowmode’,‘format’,默认分别是’fixed’,‘floor’,‘saturate’,[16,15]。
quantizer函数的4个输入参数,不限制输入顺序,不限制输入个数。但还是按顺序分别输入好。
四个参数的意思和可选择的模式如下:
1)DataMode数据类型,默认是’fixed’。除默认还有4种。
‘fixed’— 有符号定点模式。
‘ufixed’— 无符号定点模式。
‘float’— 自定义精度浮点模式。
‘single’— 单精度模式。此模式将覆盖所有其他属性设置。
‘double’— 双精度模式。此模式将覆盖所有其他属性设置。
2)RoundMode取整模式,默认是‘floor’。除默认还有5种
‘floor’— 向下舍入到下一个允许的量化值。
‘convergent’— 舍入到最接近的允许量化值。仅当舍入后的最低有效位设置为 0 时,恰好位于两个最接近的允许量化值之间的数字才会向上舍入。
‘fix’— 向上舍入负数,将正数向下舍入到下一个允许的量化值。
‘ceil’— 向上舍入到下一个允许的量化值。
‘nearest’— 舍入到最接近的允许量化值。介于两个最接近的允许量化值之间的数字将向上舍入。
‘round’— 舍入到最接近的允许量化值。介于两个最接近的允许量化值之间的数字将以绝对值向上舍入。
3)OverFlowMode溢出如何处理,默认’saturate’,针对定点类型的参数,如果对浮点型设置了这个参数会没用,不会报错。
‘saturate’— 溢出饱和。
当要量化的数据值位于数据格式属性指定的最大和最小可表示数字的范围之外时,这些值将量化为最大或最小可表示值的值,具体取决于哪个值最接近。
‘wrap’— 溢出换行到可表示值的范围。
更换范围。超出动态范围的浮点数溢出到 ±Inf
4)Format数据的格式,默认[16,15]。
对于fixed和ufixed是【数据全长,小数位长】
例如fixed,设置[8,6],数据全长8,小数位长6,符号位占1位,整数位占1位,所以能表示的数据范围是[-2,1.984375]。对应二进制’10000000’ ‘01111111’。
以上四种参数按需求设置即可。
3.2 num2bin函数:使用 quantizer 对象将数字转换为二进制表示
语法:
y = num2bin(q,x);
说明:
y = num2bin(q,x) 使用 quantizer 对象 q 指定的数据类型属性,将数值数组 x 转换为在 y 中返回的二进制字符向量。
如果 x 是包含数值矩阵的元胞数组,则 y 将是包含二进制字符串的相同维数的元胞数组。如果 x 是结构体,则 x 的每个数值字段都会转换为二进制。
[y1,y2,…] = num2bin(q,x1,x2,…) 将数值矩阵 (x1, x2 …) 转换为二进制字符串 (y1, y2 …)。
更详细的,可以去官网查看:https://ww2.mathworks.cn/help/fixedpoint/ref/num2bin.html
4.Matlab代码
下面是一个16-QAM调制映射,复数以8位定点数形式进行输出,格式为:1位符号位,一位整数位,6位小数位,负数以补码形式表示的Matlab代码。
%function [map_data]=maping(data_in)
data_in = [0 1 1 0];
%16QAM调制,符合802.11a标准
%%data_in为输入数据
%data_outI,data_outQ为映射后的星座数据
% Input bits (b0 b1) I-out Input bits (b2 b3) Q-out
% 00 –3 00 –3
% 01 –1 01 –1
% 11 1 11 1
% 10 3 10 3
Kmod=sqrt(10);%归一化量
L=length(data_in)/4;%I,Q支路输出的长度
%IQ初始化
data_outI=zeros(1,L);
data_outQ=zeros(1,L);
%星座映射
for k=1:L
switch (data_in(4*k-3))*2+data_in(4*k-2) %data_outI
case 0 %00
data_outI(k)=-3;
case 1 %01
data_outI(k)=-1;
case 3 %11
data_outI(k)=1;
case 2 %10
data_outI(k)=3;
otherwise
end
switch (data_in(4*k-1))*2+data_in(4*k) %data_outQ
case 0 %00
data_outQ(k)=-3;
case 1 %01
data_outQ(k)=-1;
case 3 %11
data_outQ(k)=1;
case 2 %10
data_outQ(k)=3;
otherwise
end
end
%归一化
data_outI=data_outI/Kmod;
data_outQ=data_outQ/Kmod;
map_data = data_outI + data_outQ*1j;
q = quantizer('fixed','round','saturate',[8,6]);
map_data = num2bin(q,map_data);
%map_data = bin2num(q,map_data)';
取消注释第一行,将第二行注释,可以当成函数调用。
微信公众号获取更多FPGA相关源码: