乘法算是基本运算之一,广泛应用在数字信号处理中,滤波器中乘法运算必不可少,实现乘法器的方法很多,各有各的优缺点,常见的有移位相加法,加法树法,查表法,混合法……
在我们用语言设计电路时,初学时在实现乘法运算时通常很简单的用*号操作,但是这种方法谈不上设计乘法器,其最终的硬件实现要根据综合器综合的结果,好的综合器可以综合出想要的结果,但是实际上这种粗放的设计通常得到的都是劣等的乘法运算,无法满足对乘法速率的要求,在滤波电路中要求数据串行进入接着进行大量的乘法运算,当所设计的乘法器其的速度小于数据进入的速度的时候就会导致结果错……
1 移位相加乘法器,对两个二进制数进行相乘运算,运用列式求法我们可以得知,乘法最终就是由加法和移位运算构成的,由此可以用高速度的加法和移位实现乘法操作,具体代码如下:
begin
dout=0;
for(i=0;i<WIGTH;i=i+1)
dout=dout+((din_a_buf[i]==1)?(din_b_buf<<i):0);//移位相加逻辑
end
2 上面的设计中,由于产生了大量的组合逻辑,这就带来了大量的延迟从而使乘法器的速率受到限制,为了提高速度,可以采用流水线的方法,将组合逻辑分割成一个一个小的组合逻辑,中间加上触发器用来锁存数据,这样就可以大大提高频率,引入触发器仅仅是带来了延迟而已,具体代码实现如下 :
begin//流水线实现
din_a_buf<=din_a;
din_b_buf<=din_b;
buf0<=din_b_buf[0]?din_a_buf:0;
buf1<=din_b_buf[1]?din_a_buf<<1:0;
buf2<=din_b_buf[2]?din_a_buf<<2:0;
buf3<=din_b_buf[3]?din_a_buf<<3:0;
buf4<=din_b_buf[4]?din_a_buf<<4:0;
buf5<=din_b_buf[5]?din_a_buf<<5:0;
buf6<=din_b_buf[6]?din_a_buf<<6:0;
buf7<=din_b_buf[7]?din_a_buf<<7:0;
buf01<=buf0+buf1;
buf23<=buf2+buf3;
buf45<=buf4+buf5;
buf67<=buf6+buf7;
buf02<=buf01+buf23;
buf46<=buf45+buf67;
dout<=buf02+buf46;
end
此种乘法器叫做加法树式乘法器,此方法被广泛使用
3 查表法,就是建一个表,里面存放了所有的乘法结果,乘数和被乘数用来作为地址去里面的乘积,此种方法可以大大提高乘法的速率,但是当乘法位数很大时会要求产生很大的表格,所以此种方法适合位数较小的乘法,特别适合有一个乘数为固定的乘法,如滤波器中的乘法就可以采用此种方法设计
4 混合,顾名思义就是结合以上各种方法的乘法器
举个简单的例子,比如有两个32bit的数据X[31:0]与Y[31:0]相乘。当然,无论Altera还是Xilinx都有现成的乘法器IP核可以调用,这也是最简单的方法,但是两个32bit的乘法器将耗费大量的资源。那么有没有节省资源,又不太复杂的方式来实现呢?我们可以稍做修改:
将X[31:0]拆成两部分X1[15:0]和X2[15:0],令X1[15:0]=X[31:16],X2[15:0]=X[15:0],则X1左移16位后与X2相加可以得到X;同样将Y[31:0]拆成两部分Y1[15:0]和Y2[15:0],令 Y1[15:0]=Y[31:16],Y2[15:0]=Y[15:0],则Y1左移16位后与Y2相加可以得到Y;则X与Y的相乘可以转化为X1和X2 分别与Y1和Y2相乘,这样一个32bit*32bit的乘法运算转换成了四个16bit*16bit的乘法运算和三个32bit的加法运算。转换后的占用资源将会减少很多
带符号数的乘法器设计
module signed_mult17b_addtree (
mul_a,
mul_b,
mul_out,
clk,
rst_n,
);
parameter MUL_WIDTH = 17;
parameter MUL_RESULT = 33;//16+16+1
input [MUL_WIDTH-1:0] mul_a;
input [MUL_WIDTH-1:0] mul_b;
input clk;
input rst_n;
output [MUL_RESULT-1:0] mul_out;
reg [MUL_RESULT-1:0] mul_out;
reg [MUL_RESULT-1:0] mul_out_reg;
reg msb;
reg msb_reg_0;
reg msb_reg_1;
reg msb_reg_2;
reg msb_reg_3;
reg [MUL_WIDTH-1:0] mul_a_reg;
reg [MUL_WIDTH-1:0] mul_b_reg;
reg [MUL_RESULT-2:0] stored0;
reg [MUL_RESULT-2:0] stored1;
reg [MUL_RESULT-2:0] stored2;
reg [MUL_RESULT-2:0] stored3;
reg [MUL_RESULT-2:0] stored4;
reg [MUL_RESULT-2:0] stored5;
reg [MUL_RESULT-2:0] stored6;
reg [MUL_RESULT-2:0] stored7;
reg [MUL_RESULT-2:0] stored8;
reg [MUL_RESULT-2:0] stored9;
reg [MUL_RESULT-2:0] stored10;
reg [MUL_RESULT-2:0] stored11;
reg [MUL_RESULT-2:0] stored12;
reg [MUL_RESULT-2:0] stored13;
reg [MUL_RESULT-2:0] stored14;
reg [MUL_RESULT-2:0] stored15;
reg [MUL_RESULT-2:0] add0_0;
reg [MUL_RESULT-2:0] add0_1;
reg [MUL_RESULT-2:0] add0_2;
reg [MUL_RESULT-2:0] add0_3;
reg [MUL_RESULT-2:0] add0_4;
reg [MUL_RESULT-2:0] add0_5;
reg [MUL_RESULT-2:0] add0_6;
reg [MUL_RESULT-2:0] add0_7;
reg [MUL_RESULT-2:0] add1_0;
reg [MUL_RESULT-2:0] add1_1;
reg [MUL_RESULT-2:0] add1_2;
reg [MUL_RESULT-2:0] add1_3;
reg [MUL_RESULT-2:0] add2_0;
reg [MUL_RESULT-2:0] add2_1;
reg [MUL_RESULT-1:0] add3_0;
always @ ( posedge clk or negedge rst_n )
begin
if ( !rst_n )
begin
mul_a_reg <= 17'b0;
mul_b_reg <= 17'b0;
stored0 <= 32'b0;
stored1 <= 32'b0;
stored2 <= 32'b0;
stored3 <= 32'b0;
stored4 <= 32'b0;
stored5 <= 32'b0;
stored6 <= 32'b0;
stored7 <= 32'b0;
stored8 <= 32'b0;
stored9 <= 32'b0;
stored10 <= 32'b0;
stored11 <= 32'b0;
stored12 <= 32'b0;
stored13 <= 32'b0;
stored14 <= 32'b0;
stored15 <= 32'b0;
add0_0 <= 32'b0;
add0_1 <= 32'b0;
add0_2 <= 32'b0;
add0_3 <= 32'b0;
add0_4 <= 32'b0;
add0_5 <= 32'b0;
add0_6 <= 32'b0;
add0_7 <= 32'b0;
add1_0 <= 32'b0;
add1_1 <= 32'b0;
add1_2 <= 32'b0;
add1_3 <= 32'b0;
add2_0 <= 32'b0;
add2_1 <= 32'b0;
add3_0 <= 32'b0;
msb <= 1'b0;
msb_reg_0 <= 1'b0;
msb_reg_1 <= 1'b0;
msb_reg_2 <= 1'b0;
msb_reg_3 <= 1'b0;
mul_out_reg <= 33'b0;
mul_out <= 33'b0;
end
else
begin
mul_a_reg <= (mul_a[16]==0)? mul_a : {mul_a[16],~mul_a[15:0]+1'b1};
mul_b_reg <= (mul_b[16]==0)? mul_b : {mul_b[16],~mul_b[15:0]+1'b1};
msb_reg_0 <= mul_a_reg[16] ^ mul_b_reg[16];//符号位
msb_reg_1 <= msb_reg_0;
msb_reg_2 <= msb_reg_1;
msb_reg_3 <= msb_reg_2;
msb <= msb_reg_3;//符号位打4拍
stored0 <= mul_b_reg[0] ? {16'b0,mul_a_reg[15:0]} : 32'b0;//移位16次
stored1 <= mul_b_reg[1] ? {15'b0,mul_a_reg[15:0],1'b0} : 32'b0;//移位的结果都变为32位
stored2 <= mul_b_reg[2] ? {14'b0,mul_a_reg[15:0],2'b0} : 32'b0;
stored3 <= mul_b_reg[3] ? {13'b0,mul_a_reg[15:0],3'b0} : 32'b0;
stored4 <= mul_b_reg[4] ? {12'b0,mul_a_reg[15:0],4'b0} : 32'b0;
stored5 <= mul_b_reg[5] ? {11'b0,mul_a_reg[15:0],5'b0} : 32'b0;
stored6 <= mul_b_reg[6] ? {10'b0,mul_a_reg[15:0],6'b0} : 32'b0;
stored7 <= mul_b_reg[7] ? {9'b0,mul_a_reg[15:0],7'b0} : 32'b0;
stored8 <= mul_b_reg[8] ? {8'b0,mul_a_reg[15:0],8'b0} : 32'b0;
stored9 <= mul_b_reg[9] ? {7'b0,mul_a_reg[15:0],9'b0} : 32'b0;
stored10 <= mul_b_reg[10] ? {6'b0,mul_a_reg[15:0],10'b0} : 32'b0;
stored11 <= mul_b_reg[11] ? {5'b0,mul_a_reg[15:0],11'b0} : 32'b0;
stored12 <= mul_b_reg[12] ? {4'b0,mul_a_reg[15:0],12'b0} : 32'b0;
stored13 <= mul_b_reg[13] ? {3'b0,mul_a_reg[15:0],13'b0} : 32'b0;
stored14 <= mul_b_reg[14] ? {2'b0,mul_a_reg[15:0],14'b0} : 32'b0;
stored15 <= mul_b_reg[15] ? {1'b0,mul_a_reg[15:0],15'b0} : 32'b0;
add0_0 <= stored0 + stored1;
add0_1 <= stored2 + stored3;
add0_2 <= stored4 + stored5;
add0_3 <= stored6 + stored7;
add0_4 <= stored8 + stored9;
add0_5 <= stored10 + stored11;
add0_6 <= stored12 + stored13;
add0_7 <= stored14 + stored15;
add1_0 <= add0_0 + add0_1;
add1_1 <= add0_2 + add0_3;
add1_2 <= add0_4 + add0_5;
add1_3 <= add0_6 + add0_7;
add2_0 <= add1_0 + add1_1;
add2_1 <= add1_2 + add1_3;
add3_0 <= add2_0 + add2_1;
mul_out_reg <= {msb,add3_0[31:0]};
mul_out <= (mul_out_reg[32]==0)? mul_out_reg : {mul_out_reg[32],~mul_out_reg[31:0]+1'b1};
end
end
endmodule
改正一处:
原来:
mul_out_reg <= {msb,add3_0[31:0]};
mul_out <= (mul_out_reg[32]==0)? mul_out_reg : {mul_out_reg[32],~mul_out_reg[31:0]+1'b1};
更改为:
mul_out_reg <= (add3_0==0)? 33'b0 : {msb,add3_0[31:0]};
mul_out <= (mul_out_reg==0)? 33'b0 : (mul_out_reg[32]==0)? mul_out_reg :
{mul_out_reg[32],~mul_out_reg[31:0]+1'b1};//若为负数,则需要取反
更改原因:当输入的两个数,一个为0,另一个为负数时,按原来的代码运算乘积并不是0,而是-1,所以需要改正,改正后不会出现这样的错误;