verilog Booth算法乘法器的实现(有无符号)

一、算法说明

1、具体的Booth算法原理,可以自行搜索,这里提供两篇文章,仅供参考。

2、算法实现说明:

  • ①假设有被乘数A乘数B,宽度都为n=8
  • ②建立P空间,P空间的宽度为2*n+1,P的值具体由操作数AB决定,在下面的示例中可以看出
  • ③根据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

posted @ 2020-10-15 17:35  耐心的小黑  阅读(378)  评论(0编辑  收藏  举报