目录
第三章 FPGA实现数字信号处理基础
1、为什么要引入反码(补码)?
反码的作用就相当于数学中的负数。
对于小学生来说,会做的算术题是:5-3,但是不会做3-5。于是,我们上初中的时候,数学里就引进了一个新的概念:负数。引入负数之后,本来是减法的运算就可以变成加法来实现:
3-5=3+[-5]=[-2],中括号代表“负数”,“负数”就是我们人为给出的数学术语。
对于计算机来说,会做的算术题是:5+3,但是不会做3-5。于是,我们就在编码里引进了一个新的概念:反码。引入反码之后,本来是减法的运算就可以变成加法来实现:
3-5=3+[-5]=[-2],中括号代表“反码”,“反码”就是我们人为给出的计算机术语。
这里,你一定有一个疑问:为什么计算机只会做5+3,不会做3-5。这是因为在计算机的数字电路中只有加法器,没有所谓的“减法器”。不是说计算机厂商不会设计减法器,因为聪明的人既然发明了方法能够用加法来实现减法操作,那为什么还需要画蛇添足的弄一个减法器?
接着说:那么反码要怎么定义才能实现减法变加法的功能呢?聪明的人想的办法如下:
1.正数的反码保持原码不变:3=[0_0000011]
2.负数除最高位(正负符号位)外,全部取反(0变1,1变0):-5=1_0000101取反=[1_1111010]
于是3+[-5]=[-2]的计算过程为:
[0_0000011]+[1_1111010]=[1_1111101],将结果求其原码[1_0000010]
这样,这种反码方法就成功实现了目标!至于为什么,我想只有数学家能给出解释了。
补充:
反码:正数的反码还是等于原码
负数的反码就是他的原码除符号位外,按位取反。
补码:正数的补码等于他的原码
负数的补码等于反码+1。
(这只是一种算补码的方式,多数书对于补码就是这句话)
2、定点数
I、定点数
所谓定点格式,即约定机器中所有数据的小数点位置是固定不变的。通常将定点数据表示成纯小数或纯整数,为了将数表示成纯小数,通常把小数点固定在数值部分的最高位之前;而为了将数表示成纯整数,则把小数点固定在数值部分的最后面,如下图所示:
图中所标示的小数点在机器中是不表示出来的,而是事先约定在固定的位置。对于一台计算机,一旦确定了小数点的位置,就不再改变。
假设用n位来表示一个定点数x=x0x1x2...x(n-1),其中x0用来表示数的符号位,通常放在最左位置,并用数值0和1分别表示正号和负号,其余位数表示它的量值。如果定点数x表示纯整数,则小数点位于最低位x(n-1)的右边,数值范围是0<=|x|<=2^(n-1)-1,且,例如1111表示-7;如果定点数x表示纯小数,则小数点位于x0和x1之间,数值范围是0<=|x|<=1-2^(-(n-1)),且
,例如1111表示-0.875.
II.定点数加减运算
不论操作数是正还是负,在做补码加减法时,只需将符号位和数值部分一起参与运算,并且将符号位产生的进位丢掉即可。如:
short A=-9, B=-5;
cout<<A+B<<endl; //-14
推导过程如下:
A的原码为:1000 0000 0000 1001,因此补码为:1111 1111 1111 0111
B的原码为:1000 0000 0000 0101,因此补码位:1111 1111 1111 1011
A+B的补码为:1 1111 1111 1111 0010,将符号位产生的进位丢掉,因此最终结果为:
1111 1111 1111 0010,结果的原码为:1000 0000 0000 1110,即-14。
III.定点数加减运算的溢出判断
1)用一位符号位判断溢出
对于加法,只有在正数加正数和负数加负数两种情况下才可能出现溢出,符号不同的两个数相加是不会溢出的。
对于减法,只有在正数减负数和负数减正数两种情况下才可能出现溢出,符号相同的两个数相减是不会溢出的。
由于减法运算在机器中是用加法器实现的,因此:不论是作加法还是减法,只要实际操作数(减法时即为被减数和“求补”之后的减数)的补码符号位相同,而结果的符号位又与操作数补码符号位不同,即为溢出。如:
在4位机中,A=5,B=-4,则A-B溢出,推导过程如下:
A的原码为0101,补码为0101;-B的原码为0100,反码为0011,补码为0100;
A-B的补码为0101+0100=1001,结果的符号位为1,而实际操作数的符号位为0,因此溢出。
2)用两位符号位判断溢出
此时判断溢出的原则是:当2位符号位不同时,表示溢出;否则无溢出。不论是否发生溢出,高位符号位永远代表真正的符号。如:
x=-0.1011,y=-0.0111,则x+y溢出,推导过程如下:
x的原码为11.1011,反码为11.0100,补码为11.0101;y的原码为11.0111,反码为11.1000,补码为11.1001,
因此x+y的补码为11.0101 + 11.1001 = 1 10.1110,将符号位产生的进位丢掉,则结果为10.1110,因此溢出。
注:约定整数的符号位与数值位之间用逗号隔开,小数的符号位与数值位之间用小数点隔开。
一、FPGA中数的运算
1、加减运算
module SymbExam (
d1,d2,
signed_out,unsigned_out);
input [3:0]d1; //输入加数1
input [3:0]d2; //输入加数2
output [3:0] unsigned_out; //无符号加法输出
output signed [3:0] signed_out; //有符号加法输出
//无符号加法运算
assign unsigned_out = d1 + d2;
//有符号加法运算
wire signed [3:0] s_d1;
wire signed [3:0] s_d2;
assign s_d1 = d1;
assign s_d2 = d2;
assign signed_out = s_d1 + s_d2;
endmodule
从RTL 视图看出,两者的输出结果完全相同:
原因是:对于加法、减法,无论是否为符号数运算,其结果均完全相同,因为二进制的运算规则完全相同。如果将二进制数据转换成十进制数据,就可以看出两者的差别了。
由上表我们可以得出一些结论:
2、乘法运算
3、除法运算
二、有限字长效应
1、滤波器系数的字长效应
%E3_2_QuantCoeff.m
B=8; %量化位数
Fs=1000; %采样频率
a=[1 1.7 0.745]; %系统函数
b=[0.05];
Ps=roots(a);
[Ph,Pf]=freqz(b,a,1024,Fs); %求频率响应
%归一化处理
c=[a b];
Max=max(abs(c));
a=a/Max;
b=b/Max;
%截尾处理
Ra=floor(a*(2^(B-1)-1));
Rb=floor(b*(2^(B-1)-1));
PRs=roots(Ra)
[PRh,PRf]=freqz(Rb,Ra,2048,Fs); %求频率响应
%PRmag=abs(PRh); %幅度转换成dB单位
%Pmag=abs(Ph); %幅度转换成dB单位
PRmag=20*log(abs(PRh))/log(10); %幅度转换成dB单位
Pmag=20*log(abs(Ph))/log(10); %幅度转换成dB单位
plot(Pf,Pmag,'-',PRf,PRmag,'--');
legend('原系统响应','8bit量化后的响应')
xlabel('频率(Hz)'); ylabel('幅度(dB)');
grid;
%zplane(Rb,Ra);
2、滤波器运算中的字长效应
%E3_3_QuantArith.m
x=[7/8 zeros(1,15)];
y=zeros(1,length(x)); %存放原始运算结果
B=2; %量化位数
Qy=zeros(1,length(x)); %存放量化运算结果
Qy2=zeros(1,length(x)); %存放量化运算结果
Qy4=zeros(1,length(x)); %存放量化运算结果
Qy6=zeros(1,length(x)); %存放量化运算结果
%系统系数
A=0.5;
b=[1];
a=[1,A];
%未经过量化处理的运算
for i=1:length(x);
if i==1
y(i)=x(i);
else
y(i)=-A*y(i-1)+x(i);
end
end
%经过量化处理的运算
for i=1:length(x);
if i==1
Qy(i)=x(i);
Qy(i)=round(Qy(i)*(2^(B-1)))/2^(B-1);
else
Qy(i)=-A*Qy(i-1)+x(i);
Qy(i)=round(Qy(i)*(2^(B-1)))/2^(B-1);
end
end
Qy2=Qy;
B=4;
%经过量化处理的运算
for i=1:length(x);
if i==1
Qy(i)=x(i);
Qy(i)=round(Qy(i)*(2^(B-1)))/2^(B-1);
else
Qy(i)=-A*Qy(i-1)+x(i);
Qy(i)=round(Qy(i)*(2^(B-1)))/2^(B-1);
end
end
Qy4=Qy;
B=6;
%经过量化处理的运算
for i=1:length(x);
if i==1
Qy(i)=x(i);
Qy(i)=round(Qy(i)*(2^(B-1)))/2^(B-1);
else
Qy(i)=-A*Qy(i-1)+x(i);
Qy(i)=round(Qy(i)*(2^(B-1)))/2^(B-1);
end
end
Qy6=Qy;
xa=0:1:length(x)-1;
plot(xa,y,'-',xa,Qy2,'--',xa,Qy4,'O',xa,Qy6,'+');
legend('原系统运算结果','2bit量化运算结果','4bit量化运算结果','6bit量化运算结果')
xlabel('运算次数');ylabel('滤波结果');