将陆续上传本人写的新书《自己动手写处理器》(尚未出版),今天是第11篇,我尽量每周四篇
第4章 第一条指令ori的实现
前面几章介绍了很多预备知识,也描绘了即将要实现的OpenMIPS处理器的蓝图,各位读者是不是早已摩拳擦掌,迫切希望一展身手了,好吧,本章我们将实现OpenMIPS处理器的第一条指令ori,为什么选择这条指令作为我们实现的第一条指令呢?答案就两个字——简单,指令ori用来实现逻辑“或”运算,选择一条简单的指令有助于我们排除干扰,将注意力集中在流水线结构的实现上,当然也可以选择其它类似的指令,只要简单即可。通过这条简单指令的实现,本章在4.2节将初步建立OpenMIPS的五级流水线结构,当我们在后面章节中实现其余指令的时候,都是在这个初步建立的流水线结构上进行扩充。
在ori指令实现后,要验证其实现是否正确,所以在4.3节建立了最小SOPC,仅仅包含OpenMIPS、指令存储器,用于验证ori指令是否实现正确,后续章节验证其余指令的时候,都是在这个最小SOPC或者其改进模型上进行验证。
本章最后介绍了MIPS编译环境的建立。
4.1 ori指令说明
ori是进行逻辑“或”运算的指令,其指令格式如图4-1所示。
从指令格式中可以知道,这是一个I类型的指令,ori指令的指令码是6'b001101,所以当处理器发现正在处理的指令的高6bit是6'b001101时,就知道当前正在处理的是ori指令。
指令用法为:ori rs, rt, immediate,作用是将指令中的16位立即数immediate进行无符号扩展至32位,然后与索引为rs的通用寄存器的值进行逻辑“或”运算,运算结果保存到索引为rt的通用寄存器中。这里需要说明以下两点。
(1)无符号扩展
在MIPS32指令集架构中,经常会有指令需要将其中的立即数进行符号扩展,或者无符号扩展,一般都是将n位立即数扩展为32位,其中,符号扩展是将n位立即数的最高位复制到扩展后的32位数据的高(32-n)位,无符号扩展则是将扩展后的32位数据的高(32-n)位都置为0。以将指令中的16位立即数扩展为32位为例,表4-1给出了当16位立即数分别是0x8000、0x1000时的符号扩展、无符号扩展的结果。
(2)通用寄存器
在MIPS32指令集架构中定义了32个通用寄存器$0-$31,OpenMIPS实现了这32个通用寄存器,使用某一个通用寄存器只需要给出相应索引,这个索引占用5bit,ori指令中的rs、rt就是通用寄存器的索引,例如:当rs为5'b00011时,就表示通用寄存器$3。
4.2 流水线结构的建立
4.2.1 流水线的简单模型
数字电路有组合逻辑、时序逻辑之分,其中时序逻辑最基本的器件是寄存器,此处的寄存器不是在4.1节中提到的MIPS架构规定的通用寄存器$0-$31,后者是一个更高层面的概念,前者是类似于D触发器这种数字电路的基本器件。寄存器按照给定时间脉冲来进行时序同步操作,其使得时序逻辑电路具有记忆功能。而组合逻辑电路则由逻辑门组成,提供电路的所有逻辑功能。实际的数字电路一般是组合逻辑与时序逻辑的结合。如果寄存器的输出端和输入端存在环路,这样的电路称为“状态机”。如图4-2所示。如果寄存器之间有连接,而没有上述环路,这样的电路结构称为“流水线”。如果4-3所示。
在流水线结构中,信号在寄存器之间传递,每传递到一级都会引起相应的组合逻辑电路变化,对这种模型进行抽象描述就是寄存器传输级(RTL:Register Transfer Level)。本节接下来要实现的原始的OpenMIPS五级流水线结构就是图4-3的扩充。
4.2.2 原始的OpenMIPS五级流水线结构
扩充图4-3,可以得到OpenMIPS的原始数据流图如图4-4所示,这个数据流图还很不完整,在后续章节中会随着实现指令的增加而丰富,但这个原始的数据流图已经可以表达本节要实现的ori指令在流水线中的处理过程了。
图中深色部分对应的是图4-3中的D触发器,深色部分之间的部分对应的是图4-3中的组合逻辑。各个阶段完成的主要工作如下。
- 取指:取出指令存储器中的指令,PC值递增,准备取下一条指令。
- 译码:对指令进行译码,依据译码结果,从32个通用寄存器中取出源操作数,有的指令要求两个源操作数都是寄存器的值,比如or指令,有的指令要求其中一个源操作数是指令中立即数的扩展,比如ori指令,所以这里有两个复用器,用于依据指令要求,确定参与运算的操作数,最终确定的两个操作数会送到执行阶段。
- 执行阶段:依据译码阶段送入的源操作数、操作码,进行运算,对于ori指令而言,就是进行逻辑“或”运算,运算结果传递到访存阶段。
- 访存阶段:对于ori指令,在访存阶段没有任何操作,直接将运算结果向下传递到回写阶段。
- 回写阶段:将运算结果保存到目的寄存器。
图4-5是为实现上述数据流图而设计的OpenMIPS系统结构,图中显示了各个模块的接口、连接关系。每个模块上方是模块名,下方是对应的Verilog HDL程序文件名。本节接下来将分别实现图中各个模块。
4.2.3 一些宏定义
在正式开始介绍流水线结构实现之前,需要给出一些宏定义,因为在OpenMIPS的实现过程中,为了提高代码的可读性和易懂性,使用了较多的宏,全部的宏都在文件defines.v中定义。此处列举在本章中会使用到的一部分宏,后面随着OpenMIPS功能的不断完善,会有更多的宏添加进来,届时会对新增加的宏进行说明。
//******************* 全局的宏定义 ***************************
`define RstEnable 1'b1 //复位信号有效
`define RstDisable 1'b0 //复位信号无效
`define ZeroWord 32'h00000000 //32位的数值0
`define WriteEnable 1'b1 //使能写
`define WriteDisable 1'b0 //禁止写
`define ReadEnable 1'b1 //使能读
`define ReadDisable 1'b0 //禁止读
`define AluOpBus 7:0 //译码阶段的输出aluop_o的宽度
`define AluSelBus 2:0 //译码阶段的输出alusel_o的宽度
`define InstValid 1'b0 //指令有效
`define InstInvalid 1'b1 //指令无效
`define True_v 1'b1 //逻辑“真”
`define False_v 1'b0 //逻辑“假”
`define ChipEnable 1'b1 //芯片使能
`define ChipDisable 1'b0 //芯片禁止
//********************* 与具体指令有关的宏定义 *****************************
`define EXE_ORI 6'b001101 //指令ori的指令码
`define EXE_NOP 6'b000000
//AluOp
`define EXE_OR_OP 8'b00100101
`define EXE_NOP_OP 8'b00000000
//AluSel
`define EXE_RES_LOGIC 3'b001
`define EXE_RES_NOP 3'b000
//********************* 与指令存储器ROM有关的宏定义 **********************
`define InstAddrBus 31:0 //ROM的地址总线宽度
`define InstBus 31:0 //ROM的数据总线宽度
`define InstMemNum 131071 //ROM的实际大小为128KB
`define InstMemNumLog2 17 //ROM实际使用的地址线宽度
//********************* 与通用寄存器Regfile有关的宏定义 *******************
`define RegAddrBus 4:0 //Regfile模块的地址线宽度
`define RegBus 31:0 //Regfile模块的数据线宽度
`define RegWidth 32 //通用寄存器的宽度
`define DoubleRegWidth 64 //两倍的通用寄存器的宽度
`define DoubleRegBus 63:0 //两倍的通用寄存器的数据线宽度
`define RegNum 32 //通用寄存器的数量
`define RegNumLog2 5