自己动手写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模块嵌入到流水线中,以最终实现除法指令,敬请关注!

时间: 2024-10-07 05:37:16

自己动手写CPU之第七阶段(10)——除法指令实现过程1的相关文章

自己动手写CPU之第七阶段(3)——简单算术操作指令实现过程(续)

将陆续上传本人写的新书<自己动手写CPU>,今天是第26篇,我尽量每周四篇 China-pub的预售地址如下(有目录.内容简介.前言): http://product.china-pub.com/3804025 亚马逊的预售地址如下,欢迎大家围观呵! http://www.amazon.cn/dp/b00mqkrlg8/ref=cm_sw_r_si_dp_5kq8tb1gyhja4 为了实现简单算术指令,需要修改译码阶段的ID模块.执行阶段的EX模块,上一篇博文中已经介绍了对译码阶段ID模块的

自己动手写CPU之第九阶段(9)——修改OpenMIPS以实现ll、sc指令

将陆续上传新书<自己动手写CPU>,今天是第48篇. 9.8 修改OpenMIPS以实现ll.sc指令 9.8.1 LLbit寄存器的实现 LLbit寄存器在LLbit模块中实现,模块接口如图9-30所示,各接口描述如表9-8所示. LLbit寄存器的代码如下,源文件是本书光盘Code\Chapter9_2目录下的LLbit_reg.v文件. module LLbit_reg( input wire clk, input wire rst, // 异常是否发生,为1表示异常发生,为0表示没有异

自己动手写CPU之第九阶段(6)——修改最小SOPC

将陆续上传新书<自己动手写CPU>,今天是第45篇. 这几天事情多,好久没更新了 前几篇实现了加载存储指令,今天将修改最小SOPC,用以测试加载存储指令是否实现正确.闲话少说,进入正题. 9.4 修改最小SOPC 为了验证上一节添加的加载存储指令是否实现正确,需要修改在第4章中设计的最小SOPC,为其添加数据存储器RAM. 9.4.1 添加数据存储器RAM 数据存储器RAM的接口如图9-24所示,还是采用左边是输入接口,右边是输出接口的方式绘制,这样便于理解.接口含义如表9-7所示. 数据存储

自己动手写CPU之第四阶段(3)——MIPS编译环境的建立

将陆续上传本人写的新书<自己动手写CPU>(尚未出版).今天是第13篇.我尽量每周四篇 4.4 MIPS编译环境的建立 OpenMIPS处理器在设计的时候就计划与MIPS32指令集架构兼容,所以能够使用MIPS32架构下已有的GNU开发工具链.本节将说明怎样安装使用GNU开发工具链以及怎样制作Makefile文件.从而以更加方便.快捷.自己主动的方式对測试程序进行编译.并得到指令存储器ROM的初始化文件inst_rom.data. 4.4.1 VisualBox的安装与设置 GNU工具链要安装

《自己动手写cpu》读书笔记

本文来自<自己动手写cpu>一书的总结.原来自己看过原作者的<步步惊芯--软核处理器分析>以及其他关于or1200的书.本次粗略浏览了该书,就某些感兴趣的部分详细分析,并总结成此文. 关于5级流水的架构,可以自己去参考<计算机接口>一书.本文重点不在此. 1.如何从rom里面取地址 简化版的最基本的sopc的框图如下: module openmips( input wire clk, input wire rst, input wire[`RegBus] rom_dat

自己动手写CPU之第七阶段(7)——乘累加指令的实现

将陆续上传本人写的新书<自己动手写CPU>,今天是第30篇,我尽量每周四篇 亚马逊的销售地址如下,欢迎大家围观呵! http://www.amazon.cn/dp/b00mqkrlg8/ref=cm_sw_r_si_dp_5kq8tb1gyhja4 China-pub的销售地址如下: http://product.china-pub.com/3804025 北发的销售地址如下: http://book.beifabook.com/Product/BookDetail.aspx?Plucode=

自己动手写CPU之第七阶段(4)——验证简单算术操作指令实现效果

将陆续上传本人写的新书<自己动手写CPU>,今天是第27篇,我尽量每周四篇 China-pub的预售地址如下(有目录.内容简介.前言): http://product.china-pub.com/3804025 亚马逊的预售地址如下,欢迎大家围观呵! http://www.amazon.cn/dp/b00mqkrlg8/ref=cm_sw_r_si_dp_5kq8tb1gyhja4 7.4 测试简单算术操作指令实现效果 本节通过实验来检验我们修改后的代码是否实现了简单算术操作指令,测试程序如下

自己动手写CPU之第七阶段(11)——除法指令实现过程2

将陆续上传本人写的新书<自己动手写CPU>,今天是第33篇,我尽量每周四篇 亚马逊的销售地址如下,欢迎大家围观呵! http://www.amazon.cn/dp/b00mqkrlg8/ref=cm_sw_r_si_dp_5kq8tb1gyhja4 在当当.京东.互动.北发等网上书店均有! 除法指令的实现过程有点长,分两篇博文介绍,今天是第二篇. 7.12.2 修改译码阶段的ID模块 译码阶段的ID模块要增加对除法指令的分析,根据图7-15给出的指令格式可知,除法指令都是SPECIAL类指令,

自己动手写CPU之第四阶段(2)——验证第一条指令ori的实现效果

将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第12篇,我尽量每周四篇 书名又之前的<自己动手写处理器>改为<自己动手写CPU> 4.3 验证OpenMIPS实现效果 4.3.1指令存储器ROM的实现 本节将验证我们的OpenMIPS是否实现正确,包含:流水线是否正确.ori指令是否实现正确.在验证之前,需要首先实现指令存储器,以便OpenMIPS从中读取指令. 指令存储器模块是只读的,其接口如图4-7所示,还是采用左边是输入接口,右边是输出接口的方式绘

自己动手写CPU之第六阶段(3)——移动操作指令的实现

将陆续上传本人写的新书<自己动手写CPU>(尚未出版),今天是第22篇,我尽量每周四篇 6.3 修改OpenMIPS以实现移动操作指令 6.3.1 HI.LO寄存器的实现 在HILO模块中实现HI.LO寄存器,HILO模块的接口描述如表6-1所示. HILO模块的代码如下,源文件是本书附带光盘Code\Chapter6目录下的hilo_reg.v.整个代码很简单:在时钟上升沿,如果复位信号无效,那么就判断输入的写使能信号we是否为WriteEnable,如果是WriteEnable,那么就将输