1.在我上一个文章里,记录了我对于基于Booth编码乘法器的学习
这篇文章再简要介绍一下自己所想的一个通用除法器的设计
2.先从原理入手,以 8(1000) / 3(0011) 为例,讲解除法的原理,可以概括除法分为俩个阶段,第一个阶段是求解商的整数部分,第二个阶段是求解商的小数部分
在这里假设 被除数为 y , 除数为 x , 商的整数部分为 q, 小数部分为 n, 初始化q,n均为0
2.1 求解整数部分:
这一阶段很简单,就拿被除数不停地减除数,直到被除数小于除数为止,以 y=8,x=3 y÷x为例
- 判断 y ≥ x ? 是 y=y-x=5 , q=q+1=1 ,否则 y=y , 进入第二阶段
- 判断 y ≥ x ? 是 y=y-x=2 , q=q+1=2 , 否则 y=y , 进入第二阶段
- 判断 y ≥ x ? 是 y=y-x , q=q+1 , 否则 y=y , 进入第二阶段 (这一步进入到阶段2)
2.2 求解小数部分:
把0010,进行小数位扩充,如果够减则商补1,被除数变为2者的差,如果不够减则商补0,被除数也同样补0,图中的101分别对应小数后边的第1、2、3位,后面的位数也依次类推
3.通用除法器代码:被除数和除数的位宽均取8位,商的整数也取8位,小数取8位有效位
//file name : divider.v
//author : Kong
module divider#(
parameter DAT_WID = 8,
parameter DEC_WID = 8,
parameter INT_WID = 8
)
(
clk,
rst_n,
vld_i,
dividend,
divisor,
quotient,
decimals,
done
);
//------------clob2function-----------------//
function integer logb2;
input integer depth;
for(logb2 = 0 ;depth > 1; logb2 = logb2 + 1) begin
depth = depth >> 1;
end
endfunction
//----------------parameter-----------------//
localparam STD_WID = 2 ;
localparam IDLE = 2'd0 ;
localparam INT = 2'd1 ;
localparam DEC = 2'd2 ;
localparam DONE = 2'd3 ;
//-----------------in/out-------------------//
input clk ;
input rst_n ;
input vld_i ;
input [ DAT_WID - 1 : 0] dividend ;
input [ DAT_WID - 1 : 0] divisor ;
output reg [ INT_WID - 1 : 0] quotient ;
output reg [ DEC_WID - 1 : 0] decimals ;
output reg done ;
//----------------reg/wire------------------//
reg [ DAT_WID - 1 : 0] dividend_r ;
reg [ DAT_WID - 1 : 0] divisor_r ;
reg [ DAT_WID - 1 : 0] quotient_r ;
reg [ DAT_WID - 1 : 0] decimals_r ;
reg div_done ;
reg [ STD_WID - 1 : 0] cur_state ;
reg [ STD_WID - 1 : 0] nxt_state ;
reg [ logb2(DAT_WID - 1) :0] cnt ;
wire [ DAT_WID - 1 : 0] divend_sub_divsor ;
//-----------------main---------------------//
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cur_state <= IDLE ;
end
else begin
cur_state <= nxt_state ;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
done <= 1'b0 ;
end
else begin
done <= div_done ;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt <= 'b0 ;
end
else if(cur_state == DEC)begin
cnt <= cnt + 1'b1 ;
end
else begin
cnt <= 'b0;
end
end
always@(*)begin
nxt_state = IDLE;
case(cur_state)
IDLE: begin
if(vld_i)
nxt_state = INT ;
else
nxt_state = IDLE ;
end
INT: begin
if(divend_sub_divsor == 'b0) begin
nxt_state = DONE;
end
else if(dividend_r < divisor_r) begin
nxt_state = DEC ;
end
else begin
nxt_state = INT ;
end
end
DEC: begin
if(cnt == DEC_WID - 1 )begin
nxt_state = DONE ;
end
else begin
nxt_state = DEC ;
end
end
DONE: begin
nxt_state = IDLE ;
end
endcase
end
assign divend_sub_divsor = dividend_r - divisor_r ;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
dividend_r <= 'b0;
divisor_r <= 'b0;
quotient_r <= 'b0;
div_done <= 'b0;
decimals_r <= 'b0;
end
else begin
case(cur_state)
IDLE : begin
dividend_r <= 'b0;
divisor_r <= 'b0;
quotient_r <= 'b0;
div_done <= 'b0;
decimals_r <= 'b0;
if(vld_i) begin
dividend_r <= dividend;
divisor_r <= divisor ;
end
end
INT : begin
if(dividend_r >= divisor_r) begin
dividend_r <= divend_sub_divsor ;
quotient_r <= quotient_r + 1'b1 ;
end
else begin
dividend_r <= dividend_r << 1;
end
end
DEC : begin
if(dividend_r >= divisor_r) begin
decimals_r <= {decimals_r[DEC_WID-2 :0],1'b1} ;
dividend_r <= divend_sub_divsor << 1 ;
end
else begin
decimals_r <= {decimals_r[DEC_WID-2 :0],1'b0} ;
dividend_r <= dividend_r << 1 ;
end
end
DONE : begin
div_done <= 1'b1 ;
end
endcase
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
quotient <= 'b0 ;
decimals <= 'b0 ;
end
else if(div_done) begin
quotient <= quotient_r ;
decimals <= decimals_r ;
end
end
endmodule
4.tb
//file name: divider_tb.v
//author : Kong
module divider_tb();
// divider Parameters
parameter DAT_WID = 8;
parameter DEC_WID = 8;
parameter INT_WID = 8;
// divider Inputs
reg clk;
reg rst_n;
reg vld_i;
reg [ DAT_WID - 1 : 0] dividend ;
reg [ DAT_WID - 1 : 0] divisor ;
// divider Outputs
wire [ INT_WID - 1 : 0] quotient;
wire [ DEC_WID - 1 : 0] decimals;
wire done;
initial begin
dividend = 8'd 8 ;
divisor = 8'd 2 ;
end
localparam max = 8'hff;
always @(posedge clk) begin
if(done)begin
vld_i <= 1'b1;
dat_gen(dividend,divisor);
end
else if(vld_i == 1'b1) begin
vld_i <= 1'b0;
end
end
task dat_gen;
output [DAT_WID - 1 : 0] a ;
output [DAT_WID - 1 : 0] b ;
begin
a = $random%{max} ;
b = $random%{max} ;
end
endtask
initial begin
rst_n = 0; # 13 rst_n = 1'b1;
vld_i = 0; #20 vld_i = 1'b1 ;#10 vld_i = 1'b0;
end
initial begin
clk = 0 ;
end
always #5 clk = !clk;
divider #(
.DAT_WID ( DAT_WID ),
.DEC_WID ( DEC_WID ),
.INT_WID ( DEC_WID ))
u_divider (
.clk ( clk ),
.rst_n ( rst_n ),
.vld_i ( vld_i ),
.dividend ( dividend ),
.divisor ( divisor ),
.quotient ( quotient ),
.decimals ( decimals ),
.done ( done )
);
endmodule
5.wave:
可以看到当被除数和除数分别为128和42时,128/42=3.0476
商的整数部分为3,小数部分为12÷256=0.469,达到了较高的精度需求