除法的实现
对于除法的实现,相对于加减乘要麻烦一些。当然目前除法主要支持无符号数除法,我们分为两类进行介绍,一类是被除数是变量,即a/b这种,一类是被除数是常量,即a/B这种。
1. 被除数常量,方法一:长除法,即根据二进制手算除法,每次将被除数左移一位,每个周期得到一位商
比如 11/4 = 2 于 3
1011 --->11
- 100 ---->4
------------------------------------
0011 101 > 100 商最高位1, 余数 0011,将0011左移一位
011
- 100 011 < 100 商次高位为0, 余数为011
最终结果上为2'b10, 余数为011
需要注意一点,如果除数的高位为0,则需要对被除数高位补0,比如1111/001 (15/1)
由于001的bit2和bit1为0,因此1111需要补位为001111当作被除数,进行运算
001111
- 001 商的bit3为1
------------------------------------
0001
- 001 商的bit2为1
-------------------------------------
0001
- 001 商的bit1为1
-------------------------------------
0001
- 001 商的bit0为1
---------------------------------------
000 余数为0
为了实现简便,我们对被除数的扩位进行归一化,统一扩位到被除数位宽+除数位宽,得到商取低位的被除数位宽即可。
根据这个思路,Verilog代码示意如下:
module SHIFT_DIV #(
parameter DIVIDEND_WIDTH = 16,
parameter DIVISOR_WIDTH = 8,
parameter QUOTIENT_WIDTH = DIVIDEND_WIDTH,
parameter REMAINDER_WIDTH = DIVISOR_WIDTH - 1
)
(
input clk_sys,
input rst_sys_n,
input div_strt,
input div_clr,
input [DIVIDEND_WIDTH-1 : 0] dividend,
input [DIVISOR_WIDTH-1 : 0] divisor,
output reg div_end,
output reg [QUOTIENT_WIDTH -1:0] quotient,
output reg [REMAINDER_WIDTH-1:0] remainder
);
localparam DIV_CNT_WIDTH = log2(QUOTIENT_WIDTH) + 1'b1;
localparam LSF_REG_WIDTH = DIVIDEND_WIDTH+DIVISOR_WIDTH;
/////////////////////////////////////////////////////////////////////////////////
reg div_cnt_en;
reg [DIV_CNT_WIDTH-1 : 0] div_cnt;
reg [LSF_REG_WIDTH-1 : 0] lsf_dividend;
reg [DIVISOR_WIDTH-1 + 1 : 0] sub_dividend_divsor;
/////////////////////////////////////////////////////////////////////////////////
wire [DIVISOR_WIDTH-1 : 0] divivend_cut;
////////////////////////////////////////////////////////////////////////////////////
//Generate counter to control calculation cycle
always @(posedge clk_sys or negedge rst_sys_n) begin
if (rst_sys_n == 1'b0) begin
div_cnt_en <= 1'b0;
end
else begin
if ((div_clr == 1'b1) ||
(div_cnt >= (QUOTIENT_WIDTH))) begin
div_cnt_en <= 1'b0;
end
else if (div_strt == 1'b1)begin
div_cnt_en <= 1'b1;
end
end
end
always @(posedge clk_sys or negedge rst_sys_n) begin
if (rst_sys_n == 1'b0) begin
div_cnt <= {DIV_CNT_WIDTH{1'b0}};
end
else begin
if ((div_clr == 1'b1) || (div_strt == 1'b1) ||
(div_cnt >= (QUOTIENT_WIDTH))) begin
div_cnt <= {DIV_CNT_WIDTH{1'b0}};
end
else if (div_cnt_en == 1'b1)begin
div_cnt <= div_cnt + 1'b1;
end
end
end
assign divivend_cut = lsf_divivend[DIVIDEND_WIDTH-1 -: DIVISOR_WIDTH];
always @(*) begin
sub_dividend_divsor = {1'b0,divivend_cut } - {1'b0, divisor};
end
always @(posedge clk_sys or negedge rst_sys_n) begin
if (rst_sys_n == 1'b0) begin
lsf_dividend <= {LSF_REG_WIDTH{1'b0}};
end
else if ((div_strt == 1'b1) || (div_clr == 1'b1))begin
lsf_dividend <= {{DIVISOR_WIDTH{1‘b0}},dividend};
end
else if (div_cnt_en == 1'b1) begin
if (sub_dividend_divsor[DIVISOR_WIDTH+1] == 1'b1 ) begin
lsf_dividend <= {lsf_dividend[LSF_REG_WIDTH-2:0],1'b0};
end
else begin
lsf_dividend <= {sub_dividend_divsor[DIVISOR_WIDTH-2:0],
lsf_dividend[LSF_REG_WIDTH-DIVIDEND_WIDTH-1:0],1'b0};
end
end
end
always @(posedge clk_sys or negedge rst_sys_n) begin
if (rst_sys_n == 1'b0) begin
quotient <= {QUOTIENT_WIDTH{1'b0}};
end
else if ((div_strt == 1'b1) || (div_clr == 1'b1))begin
quotient <= {QUOTIENT_WIDTH{1‘b0}};
end
else if (div_cnt_en == 1'b1) begin
if (sub_dividend_divsor[DIVISOR_WIDTH+1] == 1'b1 ) begin
quotient <= {quotient[QUOTIENT_WIDTH-2:0],1'b0};
end
else begin
quotient <= {quotient[QUOTIENT_WIDTH-2:0],1'b1};
end
end
end
always @(posedge clk_sys or negedge rst_sys_n) begin
if (rst_sys_n == 1'b0) begin
remainder <= {REMAINDER_WIDTH{1'b0}};
end
else if ((div_strt == 1'b1) || (div_clr == 1'b1))begin
remainder <= {REMAINDER_WIDTH{1'b0}};
end
else if (div_cnt_en == 1'b1 && (div_cnt >= (QUOTIENT_WIDTH)) begin
remainder <= sub_dividend_divsor[REMAINDER_WIDTH-1:0] ;
end
end
always @(posedge clk_sys or negedge rst_sys_n) begin
if (rst_sys_n == 1'b0) begin
div_end <= 1'b0;
end
else if ((div_strt == 1'b1) || (div_clr == 1'b1))begin
div_end <= 1'b0;
end
else if (div_cnt_en == 1'b1 && (div_cnt >= (QUOTIENT_WIDTH)) begin
div_end <= 1'b1 ;
end
else begin
div_end <= 1'b0;
end
end
endmodule
1. 被除数常量,方法一:长除法,即根据二进制手算除法,每次将被除数左移一位,每个周期得到一位商
比如 11/4 = 2 于 3
1011 --->11
- 100 ---->4
------------------------------------
0011 101 > 100 商最高位1, 余数 0011,将0011左移一位
011
- 100 011 < 100 商次高位为0, 余数为011
最终结果上为2'b10, 余数为011
需要注意一点,如果除数的高位为0,则需要对被除数高位补0,比如1111/001 (15/1)
由于001的bit2和bit1为0,因此1111需要补位为001111当作被除数,进行运算
001111
- 001 商的bit3为1
------------------------------------
0001
- 001 商的bit2为1
-------------------------------------
0001
- 001 商的bit1为1
-------------------------------------
0001
- 001 商的bit0为1
---------------------------------------
000 余数为0
为了实现简便,我们对被除数的扩位进行归一化,统一扩位到被除数位宽+除数位宽,得到商取低位的被除数位宽即可。
根据这个思路,Verilog代码示意如下:
module SHIFT_DIV #(
parameter DIVIDEND_WIDTH = 16,
parameter DIVISOR_WIDTH = 8,
parameter QUOTIENT_WIDTH = DIVIDEND_WIDTH,
parameter REMAINDER_WIDTH = DIVISOR_WIDTH - 1
)
(
input clk_sys,
input rst_sys_n,
input div_strt,
input div_clr,
input [DIVIDEND_WIDTH-1 : 0] dividend,
input [DIVISOR_WIDTH-1 : 0] divisor,
output reg div_end,
output reg [QUOTIENT_WIDTH -1:0] quotient,
output reg [REMAINDER_WIDTH-1:0] remainder
);
localparam DIV_CNT_WIDTH = log2(QUOTIENT_WIDTH) + 1'b1;
localparam LSF_REG_WIDTH = DIVIDEND_WIDTH+DIVISOR_WIDTH;
/////////////////////////////////////////////////////////////////////////////////
reg div_cnt_en;
reg [DIV_CNT_WIDTH-1 : 0] div_cnt;
reg [LSF_REG_WIDTH-1 : 0] lsf_dividend;
reg [DIVISOR_WIDTH-1 + 1 : 0] sub_dividend_divsor;
/////////////////////////////////////////////////////////////////////////////////
wire [DIVISOR_WIDTH-1 : 0] divivend_cut;
////////////////////////////////////////////////////////////////////////////////////
//Generate counter to control calculation cycle
always @(posedge clk_sys or negedge rst_sys_n) begin
if (rst_sys_n == 1'b0) begin
div_cnt_en <= 1'b0;
end
else begin
if ((div_clr == 1'b1) ||
(div_cnt >= (QUOTIENT_WIDTH))) begin
div_cnt_en <= 1'b0;
end
else if (div_strt == 1'b1)begin
div_cnt_en <= 1'b1;
end
end
end
always @(posedge clk_sys or negedge rst_sys_n) begin
if (rst_sys_n == 1'b0) begin
div_cnt <= {DIV_CNT_WIDTH{1'b0}};
end
else begin
if ((div_clr == 1'b1) || (div_strt == 1'b1) ||
(div_cnt >= (QUOTIENT_WIDTH))) begin
div_cnt <= {DIV_CNT_WIDTH{1'b0}};
end
else if (div_cnt_en == 1'b1)begin
div_cnt <= div_cnt + 1'b1;
end
end
end
assign divivend_cut = lsf_divivend[DIVIDEND_WIDTH-1 -: DIVISOR_WIDTH];
always @(*) begin
sub_dividend_divsor = {1'b0,divivend_cut } - {1'b0, divisor};
end
always @(posedge clk_sys or negedge rst_sys_n) begin
if (rst_sys_n == 1'b0) begin
lsf_dividend <= {LSF_REG_WIDTH{1'b0}};
end
else if ((div_strt == 1'b1) || (div_clr == 1'b1))begin
lsf_dividend <= {{DIVISOR_WIDTH{1‘b0}},dividend};
end
else if (div_cnt_en == 1'b1) begin
if (sub_dividend_divsor[DIVISOR_WIDTH+1] == 1'b1 ) begin
lsf_dividend <= {lsf_dividend[LSF_REG_WIDTH-2:0],1'b0};
end
else begin
lsf_dividend <= {sub_dividend_divsor[DIVISOR_WIDTH-2:0],
lsf_dividend[LSF_REG_WIDTH-DIVIDEND_WIDTH-1:0],1'b0};
end
end
end
always @(posedge clk_sys or negedge rst_sys_n) begin
if (rst_sys_n == 1'b0) begin
quotient <= {QUOTIENT_WIDTH{1'b0}};
end
else if ((div_strt == 1'b1) || (div_clr == 1'b1))begin
quotient <= {QUOTIENT_WIDTH{1‘b0}};
end
else if (div_cnt_en == 1'b1) begin
if (sub_dividend_divsor[DIVISOR_WIDTH+1] == 1'b1 ) begin
quotient <= {quotient[QUOTIENT_WIDTH-2:0],1'b0};
end
else begin
quotient <= {quotient[QUOTIENT_WIDTH-2:0],1'b1};
end
end
end
always @(posedge clk_sys or negedge rst_sys_n) begin
if (rst_sys_n == 1'b0) begin
remainder <= {REMAINDER_WIDTH{1'b0}};
end
else if ((div_strt == 1'b1) || (div_clr == 1'b1))begin
remainder <= {REMAINDER_WIDTH{1'b0}};
end
else if (div_cnt_en == 1'b1 && (div_cnt >= (QUOTIENT_WIDTH)) begin
remainder <= sub_dividend_divsor[REMAINDER_WIDTH-1:0] ;
end
end
always @(posedge clk_sys or negedge rst_sys_n) begin
if (rst_sys_n == 1'b0) begin
div_end <= 1'b0;
end
else if ((div_strt == 1'b1) || (div_clr == 1'b1))begin
div_end <= 1'b0;
end
else if (div_cnt_en == 1'b1 && (div_cnt >= (QUOTIENT_WIDTH)) begin
div_end <= 1'b1 ;
end
else begin
div_end <= 1'b0;
end
end
endmodule