本文首发于个人博客
描述
Booth乘法器是一种使用移位实现的乘法器,实现过程如下,对于乘法:
扩展A的位数为n+1位,添加,则A变为:
从i=0开始,到i=n-1结束,依次考察的值,做如下操作:
- 若,不进行操作
- 若,
- 若,
最后,舍弃R的最右端1位,即获得
原理
其原理比较容易理解,对于以上乘法,可以分解为:
以上是位移乘法器的原理,那么对于booth乘法器,添加了一条:
即有:
将移位乘法器原理式中连续为1的部分使用两个减法代替,即形成booth乘法器
代码实现
这次实现了一个基于P2P接口的booth乘法器,位宽可配置。
module booth_mul #(
parameter DIN_WIDTH_LOG = 3
)(
input clk, // Clock
input rst_n, // Asynchronous reset active low
input din_valid,
output din_busy,
input [2 ** DIN_WIDTH_LOG-1:0] din_data_a,
input [2 ** DIN_WIDTH_LOG-1:0] din_data_b,
output reg dout_valid,
input dout_busy,
output [2 ** (DIN_WIDTH_LOG + 1) - 1:0]dout_data
);
首先定义控制流,控制流为一个状态机,分别为:
-
INIT
:静默状态,等待输入,获得输入时,转向WORK
状态 -
WORK
:工作状态,进行booth乘法,过程中din_busy
信号被拉高,当运算完成后,转向TRAN
-
TRAN
:传输状态,进行P2P输出,输出完成后转向INIT
状态
parameter INIT = 2'b00;
parameter WORK = 2'b01;
parameter TRAN = 2'b11;
reg [DIN_WIDTH_LOG-1:0]shifter_counter;
reg [1:0] status,next_status;
always @(posedge clk or negedge rst_n) begin : proc_status
if(~rst_n) begin
status <= 'b0;
end else begin
status <= next_status;
end
end
wire is_computed = (shifter_counter == 2 ** DIN_WIDTH_LOG - 1);
wire is_traned = dout_valid && !dout_busy;
always @(*) begin
case (status)
INIT:begin
if(din_valid) begin
next_status = WORK;
end else begin
next_status = INIT;
end
end
WORK:begin
if(is_computed) begin
next_status = TRAN;
end else begin
next_status = WORK;
end
end
TRAN:begin
if(is_traned) begin
next_status = INIT;
end else begin
next_status = TRAN;
end
end
default : next_status = INIT;
endcase
end
assign din_busy = status[0];
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
shifter_counter <= 'b0;
end else if(status == WORK) begin
shifter_counter <= shifter_counter + 1'b1;
end else begin
shifter_counter <= 'b0;
end
end
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
dout_valid <= 'b0;
end else if(is_computed) begin
dout_valid <= 1'b1;
end else if(is_traned) begin
dout_valid <= 'b0;
end
end
下面是数据流的部分,该部分实现了上述的booth乘法操作
reg [2 ** DIN_WIDTH_LOG:0]a_data;
wire is_read = !din_busy && din_valid;
always @(posedge clk or negedge rst_n) begin : proc_a_data
if(~rst_n) begin
a_data <= 0;
end else if(is_read) begin
a_data <= {din_data_a,1'b0};
end else if(status == WORK) begin
a_data <= a_data >> 1;
end
end
reg [2 ** (DIN_WIDTH_LOG + 1) - 1:0]b_data;
always @(posedge clk or negedge rst_n) begin : proc_b_data
if(~rst_n) begin
b_data <= 0;
end else if(is_read)begin
b_data <= {(2 ** DIN_WIDTH_LOG)'(0),din_data_b};
end else if(status == WORK) begin
b_data <= b_data << 1;
end
end
reg [2 ** (DIN_WIDTH_LOG + 1):0]temp_data,result_data;
always @(*) begin
case (a_data[1:0])
2'b01:temp_data = dout_data + b_data;
2'b10:temp_data = dout_data - b_data;
default:temp_data = dout_data;
endcase
end
always @(posedge clk or negedge rst_n) begin : proc_dout_data
if(~rst_n) begin
result_data <= 0;
end else if(is_read) begin
result_data <= 'b0;
end else if(status == WORK) begin
result_data <= temp_data;
end
end
assign dout_data = result_data;
endmodule
网友评论