自己动手写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类指令,可以依据功能码确定是哪一种指令,确定指令的过程如图7-19所示。

其中涉及的宏定义如下,正是图7-15中各个指令的功能码。在本书附带光盘Code\Chapter7_3目录下的defines.v文件中可以找到这些定义。

`define EXE_DIV   6'b011010
`define EXE_DIVU  6'b011011

修改译码阶段的ID模块如下。完整代码位于本书附带光盘Code\Chapter7_3目录下的id.v文件。

module id(
  ......
);

  ......

  assign stallreq = `NoStop;

  always @ (*) begin
    if (rst == `RstEnable) begin
      ......
    end else begin
      aluop_o     <= `EXE_NOP_OP;
      alusel_o    <= `EXE_RES_NOP;
      wd_o        <= inst_i[15:11];          // 默认目的寄存器地址wd_o
      wreg_o      <= `WriteDisable;
      instvalid   <= `InstInvalid;
      reg1_read_o <= 1'b0;
      reg2_read_o <= 1'b0;
      reg1_addr_o <= inst_i[25:21];          // 默认的reg1_addr_o
      reg2_addr_o <= inst_i[20:16];          // 默认的reg2_addr_o
      imm         <= `ZeroWord;
      case (op)
         `EXE_SPECIAL_INST:		begin
            case (op2)
              5'b00000:			begin
                case (op3)
                  ......
                  `EXE_DIV: begin             //div指令
                     wreg_o      <= `WriteDisable;
                     aluop_o     <= `EXE_DIV_OP;
                     reg1_read_o <= 1'b1;
                     reg2_read_o <= 1'b1;
                     instvalid   <= `InstValid;
                   end
                  `EXE_DIVU: begin           //divu指令
                     wreg_o      <= `WriteDisable;
                     aluop_o     <= `EXE_DIVU_OP;
                     reg1_read_o <= 1'b1;
                     reg2_read_o <= 1'b1;
                     instvalid   <= `InstValid;
                   end
                   ......

这2条除法指令的译码过程都是相似的,简要说明如下。

(1)因为最终结果是写入HI、LO寄存器,不需要写通用寄存器,所以赋值wreg_o为WriteDisable。

(2)因为要读取两个通用寄存器的值,所以设置reg1_read_o、reg2_read_o为1‘b1,读取的是图7-15中地址为rs、rt的寄存器的值。

(3)alusel_o的值保持为默认值EXE_RES_NOP。

(4)设置aluop_o的值与具体指令对应。

7.12.3 修改执行阶段的EX模块

参考图7-17可知, EX模块需要增加部分接口,增加的接口如表7-6所示。

EX模块的代码主要修改如下。完整代码位于本书附带光盘Code\Chapter7_3目录下的ex.v文件。

module ex(
   ......

   // 新增来自除法模块的输入
   input wire[`DoubleRegBus]     div_result_i,
   input wire                    div_ready_i,

   ......

   // 新增到除法模块的输出
   output reg[`RegBus]           div_opdata1_o,
   output reg[`RegBus]           div_opdata2_o,
   output reg                    div_start_o,
   output reg                    signed_div_o,

   output reg			      stallreq

);

   ......

   reg stallreq_for_div;         // 是否由于除法运算导致流水线暂停

   ......	

/****************************************************************
*******   第一段:输出DIV模块控制信息,获取DIV模块给出的结果   ******
*****************************************************************/

   always @ (*) begin
     if(rst == `RstEnable) begin
       stallreq_for_div <= `NoStop;
       div_opdata1_o    <= `ZeroWord;
       div_opdata2_o    <= `ZeroWord;
       div_start_o      <= `DivStop;
       signed_div_o     <= 1'b0;
     end else begin
       stallreq_for_div <= `NoStop;
       div_opdata1_o    <= `ZeroWord;
       div_opdata2_o    <= `ZeroWord;
       div_start_o      <= `DivStop;
       signed_div_o     <= 1'b0;
       case (aluop_i)
       `EXE_DIV_OP:		begin           //是div指令
          if(div_ready_i == `DivResultNotReady) begin
            div_opdata1_o    <= reg1_i;         //被除数
            div_opdata2_o    <= reg2_i;         //除数
            div_start_o      <= `DivStart;      //开始除法运算
            signed_div_o     <= 1'b1;           //有符号除法
            stallreq_for_div <= `Stop;          //请求流水线暂停
          end else if(div_ready_i == `DivResultReady) begin
            div_opdata1_o    <= reg1_i;
            div_opdata2_o    <= reg2_i;
            div_start_o      <= `DivStop;       //结束除法运算
            signed_div_o     <= 1'b1;
            stallreq_for_div <= `NoStop;        //不再请求流水线暂停
          end else begin
            div_opdata1_o    <= `ZeroWord;
            div_opdata2_o    <= `ZeroWord;
            div_start_o      <= `DivStop;
            signed_div_o     <= 1'b0;
            stallreq_for_div <= `NoStop;
          end
        end
       `EXE_DIVU_OP:		begin           //是divu指令
          if(div_ready_i == `DivResultNotReady) begin
            div_opdata1_o    <= reg1_i;
            div_opdata2_o    <= reg2_i;
            div_start_o      <= `DivStart;
            signed_div_o     <= 1'b0;           //无符号除法
            stallreq_for_div <= `Stop;
          end else if(div_ready_i == `DivResultReady) begin
            div_opdata1_o    <= reg1_i;
            div_opdata2_o    <= reg2_i;
            div_start_o      <= `DivStop;
            signed_div_o     <= 1'b0;
            stallreq_for_div <= `NoStop;
          end else begin
            div_opdata1_o    <= `ZeroWord;
            div_opdata2_o    <= `ZeroWord;
            div_start_o      <= `DivStop;
            signed_div_o     <= 1'b0;
            stallreq_for_div <= `NoStop;
          end
         end
     default: begin
     end
    endcase
  end
end	

/****************************************************************
**********                第二段:暂停流水线               *******
*****************************************************************/

   always @ (*) begin
      stallreq = stallreq_for_madd_msub || stallreq_for_div;
   end

......

/****************************************************************
**********            第三段:修改HI、LO寄存器写信息        *******
*****************************************************************/

  always @ (*) begin
    if(rst == `RstEnable) begin
       whilo_o <= `WriteDisable;
       hi_o    <= `ZeroWord;
       lo_o    <= `ZeroWord;
       ......
    end else if((aluop_i == `EXE_DIV_OP) || (aluop_i == `EXE_DIVU_OP)) begin
       whilo_o <= `WriteEnable;
       hi_o    <= div_result_i[63:32];
       lo_o    <= div_result_i[31:0];
       ......

上面的代码可以分为三段理解。

(1)第一段:如果是div指令,并且DIV模块没有声明除法结束(即div_ready_i等于DivResultNotReady),那么输出被除数、除数、除法开始信号、有符号除法等信息到DIV模块,设置div_start_o为DivStart,以指示DIV模块开始除法运算,同时,设置stallreq_for_div为Stop,表示由于除法运算请求流水线暂停。反之,如果DIV模块声明除法结束(即div_ready_i等于DivResultReady),那么设置div_start_o为DivStop,以指示DIV模块停止除法运算,同时,设置stallreq_for_div为NoStop,表示不是由于除法运算请求流水线暂停。

divu指令的执行过程与div指令类似。

(2)第二段:给出暂停流水线请求信号stallreq的值,目前已实现的乘累加、乘累减、除法指令都会请求流水线暂停,所以stallreq等于stallreq_for_madd_msub与stallreq_for_div进行逻辑“或”运算的结果。

(3)第三段:由于除法指令要将最终结果写入HI、LO寄存器,所以在第三段给出了对HI、LO寄存器的写信息。其中div_result_i就是DIV模块计算出来的除法结果,高32位存储的是余数,低32位存储的是商。

7.12.4 修改OpenMIPS模块

因为添加了DIV模块,并且修改了EX模块的接口,所以要修改OpenMIPS顶层模块,以将这些新增模块、接口连接起来,连接关系如图7-17所示。完整代码可以参考本书附带光盘Code\Chapter7_3目录下的openmips.v文件,书中只给出DIV模块的例化语句,如下。

div div0(
 .clk(clk),
 .rst(rst),

 .signed_div_i(signed_div),
 .opdata1_i(div_opdata1),
 .opdata2_i(div_opdata2),
 .start_i(div_start),
 .annul_i(1'b0),

 .result_o(div_result),
 .ready_o(div_ready)
);

这里需要说明一点,DIV模块的输入接口annul_i在目前固定为0,表示不会有取消除法指令的情况发生,但是在后续章节,当我们实现异常处理的时候,会重新确定DIV模块的输入接口annul_i的值。

下一次将测试除法指令的实现效果。

时间: 2024-10-12 19:14:22

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

自己动手写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之第四阶段(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,那么就将输