verilog Booth算法乘法器的实现(有无符号)
一、算法说明
1、具体的Booth
算法原理,可以自行搜索,这里提供两篇文章,仅供参考。
2、算法实现说明:
- ①假设有
被乘数A
、乘数B
,宽度都为n=8 - ②建立
P
空间,P
空间的宽度为2*n+1,P的值具体由操作数A
和B
决定,在下面的示例中可以看出 - ③根据
P[0]、P[1]
的值确定接下来需要执行的操作;紧接着再根据P
的最高位P[16]
确定接下来的移位操作形式
P[0]、P[1]
的值和其对应的具体操作如下:
P[1:0] | 操作 |
---|---|
00 | 无操作 |
01 | +被乘数 |
10 | -被乘数 |
11 | 无操作 |
- ④对③执行n=8次循环,最终结果为
result=P[16:1]
下面通过一个两个4位操作数的计算实例来看一下具体的计算流程:
二、算法实现
1、RTL代码
`timescale 1ns / 1ps
module booth_multiply#(
parameter DATAWIDTH = 8
)
(
input CLK,
input RSTn,
input START,
input [ DATAWIDTH - 1 : 0 ] A,
input [ DATAWIDTH - 1 : 0 ] B,
output [ DATAWIDTH * 2 - 1 : 0 ] RESULT,
output Done
);
reg [ DATAWIDTH - 1 : 0 ] i;
reg [ DATAWIDTH * 2 : 0 ] P;
reg [ DATAWIDTH - 1 : 0 ] A_reg;
reg [ DATAWIDTH - 1 : 0 ] A_bm;
reg [ DATAWIDTH - 1 : 0 ] N;
reg [ DATAWIDTH * 2 : 0 ] result_reg;
reg isDone;
always @ ( posedge CLK or negedge RSTn )
begin
if (!RSTn)
begin
i <= 0;
P <= 0;
A_reg <= 0;
A_bm <= 0;
result_reg <= 0;
N <=0;
isDone <= 0;
end
else if (START)
begin
case (i)
0:
begin
A_reg <= A;
//complement code of A 计算A的补码,方便后面的+-操作
A_bm <= ~A + 1'b1;
P <= { 8'd0, B, 1'b0 }; //B add to last 8bit of P
i <= i + 1'b1;
N <= 0;
end
//operating 这一个状态用来判断最低两位,然后决定执行什么操作
1:
begin
if (N == DATAWIDTH)
begin
N <= 0;
i <= i + 2'b10;
end
else if (P[1:0] == 2'b00 | P[1:0] == 2'b11)
begin
P <= P;
i <= i + 1'b1;
end
else if (P[1:0] == 2'b01)
begin
P <= {P[16:9] + A_reg,P[8:0]};
i <= i + 1'b1;
end
else if (P[1:0] == 2'b10)
begin
P <= {P[16:9] + A_bm,P[8:0]};
i <= i + 1'b1;
end
end
//shift 这是无论最低两位是什么,最后都要执行的移位操作
2:
begin
P <= {P[16],P[16:1]};
N <= N + 1'b1;
i <= i - 1'b1;
end
3:
begin
isDone <= 1;
result_reg <= P;
i <= i + 1'b1;
end
4:
begin
isDone <= 0;
i <= 0;
end
endcase
end
end
assign Done = isDone;
assign RESULT = result_reg[16:1];
endmodule
2、仿真代码
`timescale 1ns / 1ps
module tb_booth_multiply;
// booth_multiply Parameters
parameter PERIOD = 10;
parameter DATAWIDTH = 8;
// booth_multiply Inputs
reg CLK = 0 ;
reg RSTn = 0 ;
reg START = 0 ;
reg [ DATAWIDTH - 1 : 0 ] A = 0 ;
reg [ DATAWIDTH - 1 : 0 ] B = 0 ;
// booth_multiply Outputs
wire [ DATAWIDTH * 2 - 1 : 0 ] RESULT ;
wire Done ;
initial
begin
forever #(PERIOD/2) CLK=~CLK;
end
initial
begin
#(PERIOD) RSTn = 1;START = 1;
end
booth_multiply #(
.DATAWIDTH ( DATAWIDTH ))
u_booth_multiply (
.CLK ( CLK ),
.RSTn ( RSTn ),
.START ( START ),
.A ( A [ DATAWIDTH - 1 : 0 ] ),
.B ( B [ DATAWIDTH - 1 : 0 ] ),
.RESULT ( RESULT [ DATAWIDTH * 2 - 1 : 0 ] ),
.Done ( Done )
);
initial
begin
A = 2;
B = 4;
#200
A = 3;
B = 5;
#200
A = -4;
B = 6;
#200
A = 123;
B = -56;
#200
A = 99;
B = 44;
#200
A = -34;
B = -66;
#200
A = 23;
B = 12;
#200
A = 111;
B = 100;
#400
$finish;
end
endmodule
3、仿真结果
需要注意的是:因为采用了时序电路,并且使用了状态机,所以每个计算过程实际上会消耗很多个时钟周期。在该代码中输出是比输入延迟了19个时钟周期。所以,在仿真的时候,我们控制两个相连输入之间延迟了20个时钟周期,否则你提供的输入就不会依次被计算。假如,第一个结果是第一组数据,但是第二个结果可能就是第10组数据,大概就是这个意思。
不过,你只需要知道输出比输入延迟了19个时钟周期即可,至于上述的两组仿真数据的之间的延迟设置都无所谓。
注意:特别需要知道的是,该算法同时适用于有符号与无符号数的计算,只是在计算有符号数的时候,同样位宽的情况下,有符号数的数值范围和无符号数不一样。
参考文章:
https://blog.csdn.net/weixin_42183170/article/details/100743362