首页 > 代码库 > 自己动手写CPU之第七阶段(10)——除法指令实现过程1
自己动手写CPU之第七阶段(10)——除法指令实现过程1
将陆续上传本人写的新书《自己动手写CPU》,今天是第33篇,我尽量每周四篇
亚马逊的销售地址如下,欢迎大家围观呵!
http://www.amazon.cn/dp/b00mqkrlg8/ref=cm_sw_r_si_dp_5kq8tb1gyhja4
China-pub的销售地址如下:
http://product.china-pub.com/3804025
在京东、北发等网上书店均有!
除法指令的实现过程有点长,分两篇博文介绍,今天是第一篇。
7.12 修改OpenMIPS以实现除法指令
7.12.1 增加DIV模块
DIV模块的接口如图7-17所示,各接口的含义如表7-5所示。
DIV模块的主要部分是一个状态机,共有四个状态,如下,状态转换如图7-18所示。
- DivFree:除法模块空闲
- DivByZero:除数是0
- DivOn:除法运算进行中
- DivEnd:除法运算结束
复位的时候,DIV模块处于DivFree状态,当输入信号start_i为DivStart,且输入信号annul_i为0时,表示除法操作开始。
- 如果除数opdata2_i为0,那么进入DivByZero状态,直接给出除法结果,这里设置为0,余数也为0,然后进入DivEnd状态,并通知EX模块除法运算结果得到,后者会设置DIV模块的输入信号start_i为DivStop,除法运算结束。
- 如果除数opdata2_i不为0,那么进入DivOn状态,使用试商法,经过32个时钟周期,得出除法结果,然后进入DivEnd状态,并通知EX模块除法运算结果得到,后者会设置DIV模块的输入信号start_i为DivStop,除法运算结束。
DIV模块的代码如下,源文件是本书附带光盘Code\Chapter7_3目录下的div.v。
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, output reg ready_o ); wire[32:0] div_temp; reg[5:0] cnt; //记录试商法进行了几轮,当等于32时,表示试商法结束 reg[64:0] dividend; reg[1:0] state; reg[31:0] divisor; reg[31:0] temp_op1; reg[31:0] temp_op2; //dividend的低32位保存的是被除数、中间结果,第k次迭代结束的时候dividend[k:0] //保存的就是当前得到的中间结果,dividend[31:k+1]保存的就是被除数中还没有参与运算 //的数据,dividend高32位是每次迭代时的被减数,所以dividend[63:32]就是图7-16 //中的minuend,divisor就是图7-16中的除数n,此处进行的就是minuend-n运算,结 //果保存在div_temp中 assign div_temp = {1'b0,dividend[63:32]} - {1'b0,divisor}; always @ (posedge clk) begin if (rst == `RstEnable) begin state <= `DivFree; ready_o <= `DivResultNotReady; result_o <= {`ZeroWord,`ZeroWord}; end else begin case (state) //******************* DivFree状态 *********************** //分三种情况: //(1)开始除法运算,但除数为0,那么进入DivByZero状态 //(2)开始除法运算,且除数不为0,那么进入DivOn状态,初始化cnt为0,如 // 果是有符号除法,且被除数或者除数为负,那么对被除数或者除数取补码。 // 除数保存到divisor中,将被除数的最高位保存到dividend的第32位, // 准备进行第一次迭代 //(3)没有开始除法运算,保持ready_o为DivResultNotReady,保持 // result_o为0 //*********************************************************** `DivFree: begin // DivFree状态 if(start_i == `DivStart && annul_i == 1'b0) begin if(opdata2_i == `ZeroWord) begin state <= `DivByZero; // 除数为0 end else begin state <= `DivOn; // 除数不为0 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_op2 = ~opdata2_i + 1; // 除数取补码 end else begin temp_op2 = opdata2_i; end dividend <= {`ZeroWord,`ZeroWord}; dividend[32:1] <= temp_op1; divisor <= temp_op2; end end else begin // 没有开始除法运算 ready_o <= `DivResultNotReady; result_o <= {`ZeroWord,`ZeroWord}; end end //******************* DivByZero状态 ******************** //如果进入DivByZero状态,那么直接进入DivEnd状态,除法结束,且结果为0 //*********************************************************** `DivByZero: begin //DivByZero状态 dividend <= {`ZeroWord,`ZeroWord}; state <= `DivEnd; end //******************* DivOn状态 *********************** //分三种情况: //(1)如果输入信号annul_i为1,表示处理器取消除法运算,那么DIV模块直 // 接回到DivFree状态。 //(2)如果annul_i为0,且cnt不为32,那么表示试商法还没有结束,此时 // 如果减法结果div_temp为负,那么此次迭代结果是0,参考图7-16;如 // 果减法结果div_temp为正,那么此次迭代结果是1,参考图7-16,dividend // 的最低位保存每次的迭代结果。同时保持DivOn状态,cnt加1。 //(3)如果annul_i为0,且cnt为32,那么表示试商法结束,如果是有符号 // 除法,且被除数、除数一正一负,那么将试商法的结果取补码,得到最终的 // 结果,此处的商、余数都要取补码。商保存在dividend的低32位,余数 // 保存在dividend的高32位。同时进入DivEnd状态。 //*********************************************************** `DivOn: begin //DivOn状态 if(annul_i == 1'b0) begin if(cnt != 6'b100000) begin //cnt不为32,表示试商法还没有结束 if(div_temp[32] == 1'b1) begin //如果div_temp[32]为1,表示(minuend-n)结果小于0, //将dividend向左移一位,这样就将被除数还没有参与运算的 //最高位加入到下一次迭代的被减数中,同时将0追加到中间结果 dividend <= {dividend[63:0] , 1'b0}; end else begin //如果div_temp[32]为0,表示(minuend-n)结果大于等 //于0,将减法的结果与被除数还没有参运算的最高位加入到下 //一次迭代的被减数中,同时将1追加到中间结果 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; //进入DivEnd状态 cnt <= 6'b000000; //cnt清零 end end else begin state <= `DivFree; //如果annul_i为1,那么直接回到DivFree状态 end end //******************* DivEnd状态 *********************** //除法运算结束,result_o的宽度是64位,其高32位存储余数,低32位存储商, //设置输出信号ready_o为DivResultReady,表示除法结束,然后等待EX模块 //送来DivStop信号,当EX模块送来DivStop信号时,DIV模块回到DivFree //状态 //********************************************************** `DivEnd: begin //DivEnd状态 result_o <= {dividend[64:33], dividend[31:0]}; ready_o <= `DivResultReady; if(start_i == `DivStop) begin state <= `DivFree; ready_o <= `DivResultNotReady; result_o <= {`ZeroWord,`ZeroWord}; end end endcase end end endmodule DIV模块中涉及的宏定义,在defines.v中定义,如下: `define DivFree 2'b00 `define DivByZero 2'b01 `define DivOn 2'b10 `define DivEnd 2'b11 `define DivResultReady 1'b1 `define DivResultNotReady 1'b0 `define DivStart 1'b1 `define DivStop 1'b0
本篇实现了除法模块DIV,下一篇将把DIV模块嵌入到流水线中,以最终实现除法指令,敬请关注!
自己动手写CPU之第七阶段(10)——除法指令实现过程1
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。