除法指令div.divu

一.试商法思想

同数学除法计算,1.先将被除数的最高位数与除数比较,即两者做差判断。

2.若差大于等于0,则商的最高位为1,将差与被除数下一位组合,重复算商至被除数最后一位。

3.当差小于0时,就将被除数的这一位与下一最高位组合,重复算商至被除数的最后一位。

举个栗子

12÷5。你得先把1和5比较吧,1比5小商个零,这个咱通常都省略不写,再把2落下来和1组合除5,12比5大商2,12与10做差得余2

在二进制里也一样,比如101/11,被除数的最高位1比除数11小,就把第2位0和1组合与除数11比大小,10比11小就再把1与10组合和除数11比大小,再用被除数减除数,商1,所得差为余数。

6c7a331d3605417d8f987c64da269a5a.png

 注:32位除法,至少需要32个时钟周期得到结果

二.DIV模块添加

1.DIV模块主要部分是一个有4个状态的状态机

DivFree:除法模块空闲

DivByZero:除数为零

DivOn:除法在进行

DivEnd:除法结束

状态转换图ba8b7ad36e3e4a999261a525d5156b46.png

start_i是否开始运算

annul_i是否取消运算(0有效)

opdata1_i为被除数  opdata2_i为除数

cnt计数时钟周期

2.div模块实现

3e7ac85ad352403ea8bc01754ade9acb.png代码很长,建议一次观看完保证完整性୧⍤⃝🌷

module div(
    input wire clk,
    input wire rst,
    input wire signed_div_i,
    input wire[31:0] opdata1_i,//被除数
    input wire[31:0] opdata2_i,//除数
    input wire start_i,//是否开始
    input wire annul_i,//是否取消
    
    output reg[63:0] result_o,//高32位为余数HI,低32位为商LO
    output reg       ready_o //对前面连接的EX模块发是否结束了除法的信号
);
    wire[32:0] div_temp;//存对应位相减的差
    reg[5:0] cnt;//记时钟周期,32时结束除法
    reg[64:0] dividend;//低32位存商,高32位存余数,第32位空.
                       //低33-1位先存为被除数。作差,差小于零商给第0位0,被除数左移再做差;
                       //差大于零商给第0位1,被除数左移,差存入被除数少此位的左边。
    reg[1:0] state;//记四个状态
    reg[31:0] divsor;//除数(我也不理解为啥不直接用temp_op2 ?_?,
                    //或许是考虑除数temp_op2不为0时方便直接固定差的算法,可直接用差)
    reg[31:0] temp_op1;
    reg[31:0] temp_op2;//看是否取补码后的操作数
    assign div_temp={1'b0,divdend[63:32]}-{1'b0,divisor};//先定好差的固定算法
    always @ (posedge clk)begin
        if(rst==1'b1)begin  //复位初始化
            state<='DivFree;
            ready_o<='DivResultNotReady;
            result_o<={'Zeroword,'ZeroWord};
        end
        else begin
            case(state)
              'DivFree: 
                begin if(start=='DivStart&&annul_i==1'b0)  //除法开始且不取消
                        begin if(opdata2_i==32'h00000000) begin
                                state<='DivZero; end  //除数为0转'DivZero状态
                              else 
                              begin  state<='DivOn;  //除法正常进行状态。1.让计时器清零 2.负                                                              
                                                 //操作数取反码 3.设好被除数的某位与除数做差
                                     cnt<=6'b000000;
                                     if(signed_div_i==1'b1&&opdata1_i[31]==1'b1) 
                                        begin  temp_op1=~opdata1_i+1;     end
                                     else 
                                        begin temp_op1=opdata1_i;  end
                                     if(signed_div_i==1'b1&&opdata2_i[31]==1'b1) 
                                        begin temp_op1=~opdata2_i+1; end
                                     else 
                                        begin temp_op1=opdata2_i; end
                                     dividend<={32'h00000000,32'h00000000};
                                     dividend[32:1]<=temp_op1;//差为dividend[63:32]与除数divisor相减。                                            //将被除数先放入dividend[32:1]中,
                                           //那么首次相减时就可先使被除数的最高位与除数比较。
                                     divisor<=temp_op2; 
                              end
                         end
                      else  //除法未开始或被取消,不进行除法运算
                        begin  ready_o<='DivResultNotReady;
                               result_o<={32'h00000000,32'h00000000};
                        end
                end

            'DivByZero:      //除数为0
                begin  dividend<={32'h00000000,32'h00000000};//商0余0
                        //一般将商和余数都存在dividend中,在最后'DivEnd状态再将dividend赋给结果
                       state<='DivEnd;
                end

            'DivOn:
                //运算未取消且未结束,差大于0和小于等于0对应不同情况,让时钟加1
                //运算未取消但结束,看是否符号运算,看商和余数是否取补码,状态转'DivEnd结束,时钟清零
                //运算取消,状态转'DivFree
                begin  if(annul_i==1'b0)  
                          begin  if(cnt!=6'b100000) //cnt计数时钟不是32,除法未进行完
                                    begin  if(div_temp[32]==1'b1) //差为负,dividend被除数左移,未参与运算的最高位加入
                                            //商0,即在左移后的最后一位补0
                                              begin  dividend<={dividend[63:0],1'b0};  end
                                            else  //差为正,将差与未参与运算的最高位组合
                                              begin  dividend<={div_temp[31:0],dividend[31:0],1'b1}; end //差,除数,商 作为新的结果
                                            cnt<=cnt+1;
                                    end
                                 else //运算未取消但结束
                                    begin if((signed_div_i==1'b1)&&((opdata1_i[31]^opdata2_i[31]==1'b1))  //被除数与除数是否异号,异号商为负
                                            begin  dividend[31:0]<={~dividend[31:0]+1}; end
                                          if((signed_div_i==1'b1)&&((opdata1_i[31]^dividend[64])==1'b1)  //这个余数我不理解 -_-
                                            begin  dividend[64:33]<={~dividend[64:33]+1}; end
                                          state<='DivEnd;
                                          cnt<=6'b000000;
                                    end
                          end
                        else  
                           begin  state<='DivFree; end
                end
            'DivEnd:
                begin  result_o<={dividend[64:33],dividend[31:0]};  //结果赋值,高32位存余数,低32位存商
                       ready_o<='DivResultReady; //向EX模块输出运算结束信号
                       if(start_i=='DivStop)  //EX模块接收到上述运算结束信号时,送来运算停止信号。DIV模块到DivFree状态
                         begin  state<='DivFree;
                                ready_o<='DivResultNotReady;
                                result_o<={32'h00000000,32'h00000000};
                         end
                end
           endcase
       end
     end
endmodule

最后祝大家中秋快乐,祝老师教师节快乐! ~_<

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值