文章目录
一、原理介绍
过程类似于十进制除法。
对于32位的除法,至少需要32个时钟周期,才能得到除法的结果。
设被除数是m, 除数是n, 商保存在s中, 被除数的位数是k, 计算步骤如下:
- 取出被除数的最高位m[k], 使用被除数的最高位减去除数n ,如果结果大于等于0, 则商的s[k] 为1,反之为0
- 如果上一步得到的结果为0,表示当前被减数小于除数,则取出被除数剩下的值的最高位m[k-1],与当前被减数组合作为下一轮被减数。如果上一轮得到的结果是1,表示当前被减数大于除数,则利用上一轮中减法的结果与被除数剩下的值的最高位m[k-1]组合作为下一轮的被减数。然后,设置k等于k-1。
- 新的被减数减去除数,如果结果大于等于0,则商的s[k]为1,否则s[k]为0,后面的步骤依次重复,直到k等于0。
二、状态机实现
2.1 状态转移图
部分状态介绍:
- idle
- divbyzero: 除数为0。
- divinit: 进行有符号相关的处理,以及相关信号初始化赋值。
- divon: 进行试商法除法。
- divend: 除法运算结束。
2.2 代码
2.2.1 div.sv
/**************************************
@ filename : div.sv
@ author : yyrwkk
@ create time : 2024/09/09 11:18:46
@ version : v1.0.0
**************************************/
module div # (
parameter N_WIDTH = 32
)(
input logic i_clk ,
input logic i_rst_n ,
input logic i_divsigned ,
input logic i_divstart ,
input logic [N_WIDTH-1:0] i_dividend ,
input logic [N_WIDTH-1:0] i_divisor ,
output logic [N_WIDTH-1:0] o_quotient ,
output logic [N_WIDTH-1:0] o_remainder ,
output logic o_done_vld ,
output logic o_ready
);
localparam s_idle = 3'd0;
localparam s_divinit = 3'd1;
localparam s_divbyzero = 3'd2;
localparam s_divon = 3'd3;
localparam s_divend = 3'd4;
logic [2:0] curr_state ;
logic [2:0] next_state ;
logic [4:0] cnt ;
logic [31:0] dividend_temp ;
logic [31:0] divisor_temp ;
logic [31:0] dividend ;
logic [31:0] divisor ;
logic div_signed ;
logic dividend_signed ;
logic divisor_signed ;
logic [31:0] div_temp ;
logic [32:0] div_sub ;
logic [31:0] quotient ;
logic [31:0] remainder ;
assign dividend_temp = (i_divsigned & i_dividend[31]) ? (~i_dividend + 1'b1) : i_dividend;
assign divisor_temp = (i_divsigned & i_divisor[31]) ? (~i_divisor + 1'b1) : i_divisor;
always_ff @( posedge i_clk or negedge i_rst_n ) begin
if(!i_rst_n ) begin
dividend <= 'b0;
divisor <= 'b0;
div_signed <= 'b0;
end else begin
if( (o_ready == 1'b1 ) && (i_divstart == 1'b1 ) ) begin
dividend <= dividend_temp;
divisor <= divisor_temp;
div_signed <= i_divsigned;
dividend_signed <= i_dividend[31];
divisor_signed <= i_divisor[31];
end else begin
dividend <= dividend ;
divisor <= divisor ;
div_signed <= div_signed ;
dividend_signed <= dividend_signed;
divisor_signed <= divisor_signed ;
end
end
end
always_ff@(posedge i_clk or negedge i_rst_n ) begin
if( !i_rst_n ) begin
curr_state <= s_idle;
end else begin
curr_state <= next_state;
end
end
always_comb begin
case(curr_state )
s_idle : begin
if( (o_ready == 1'b1 ) && (i_divstart == 1'b1 )) begin
if( i_divisor == 'b0 ) begin
next_state = s_divbyzero;
end else begin
next_state = s_divinit;
end
end else begin
next_state = s_idle;