微信公众号获取更多FPGA相关源码:
1.前言
上一篇博客实现了无符号移位相加乘法器,有符号乘法器和无符号的原理是一样的,只不过需要进行符号位的处理。
2.原理
一些资料中,对于有符号数乘法,是这么处理的:以(-3)x(-6)为例,Verilog中有符号数用补码表示,正数的补码是他本身,负数的补码是符号位不变,其他位的反码加1。(-3)的补码是 1101,(-6)的补码是 1010,为了保证结果的正确,首先要将两个数进行符号位扩展,扩展为8bits。有符号位扩展,高位补1,所以(-3)就变为 11111101 ,(-6)就变为 11111010 。然后其计算步骤与无符号移位相加乘法器计算步骤一致。
而我想的是,能不能不进行符号位扩展,先抛开符号位,将负数的补码转换为原码,然后以无符号数乘法的计算方式进行计算。最后再来处理符号位,所谓“同号为正,异号为负”。
3.实现
下面展示了以我的思路设计的有符号移位相加乘法器,是并行计算的,只需要一个周期就能出结果,并且进行了参数化。
module sign_Mult
#(parameter WIDTH_A = 8,
WIDTH_B = 8)
(
input clk ,
input signed [WIDTH_A-1:0] a,
input signed [WIDTH_B-1:0] b,
output reg signed [WIDTH_A+WIDTH_B:0] out
);
integer i ;
reg signed [WIDTH_A+WIDTH_B:0] out_r;//考虑结果会有溢出
wire signed [WIDTH_A:0] a_r ;
wire signed [WIDTH_B:0] b_r ;
always @(a or b) begin
out_r = 0;
for(i=0; i<(WIDTH_B); i=i+1)
if(b_r[i])
out_r = out_r + (a_r << i);
end
assign a_r = a[WIDTH_A-1] ? {1'b0,~a + 1'b1} : {1'b0,a};//负数取反加一转化为补码
assign b_r = b[WIDTH_B-1] ? {1'b0,~b + 1'b1} : {1'b0,b};//正数保持不变
always @(posedge clk)
if(a[WIDTH_A-1] == b[WIDTH_B-1])
out <= out_r;
else
out <= ~out_r + 1'b1;
endmodule
测试激励代码:
`timescale 1ns / 1ps
module sign_Mult_tb;
reg signed [11:0] a ;
reg signed [15:0] b ;
wire signed [28:0] out ;
reg clk ;
sign_Mult
#(.WIDTH_A(12),
.WIDTH_B(16))
u1(
.clk(clk),
.a (a) ,
.b (b) ,
.out(out)
);
always #10 clk = ~clk;
initial begin
clk = 1'b0;
a = -12'd2;
b = -16'd3;//计算结果应为6
#200
a = -12'd2048;//12bit最小值-2048
b = 16'd32767;//16bit有符号数最大值//计算结果应为-67_106_816
#200
a = 12'd2;
b = -16'd3;//计算结果应为-6
#200
a = 12'd2047;//12bit最大值2047
b = -16'd32768;//16bit最小值-32768//计算结果应为-67_076_096
end
endmodule
举了几个比较典型的数进行验证,貌似结果是对的,没有多试,因为都是以前上课的时候做的作业,现在分享出来,自己也懒得去再验证了,有兴趣的朋友可以多去测试。
注意事项:
-
无论是有符号数相乘还是无符号数相乘,其乘积的位宽必定位a+b;
-
如果被乘数和乘数均为有符号数,那么相乘之前首先要进行符号位扩展,将被乘数和乘数均扩展位a+b位。
-
有符号数乘法的最终结果也是补码形式。
微信公众号获取更多FPGA相关源码: