因为OpenMIPS设计乘累加、乘累减、除法指令在流水线执行阶段占用多个时钟周期,因此需要暂停流水线,以等待这些多周期指令执行完毕。OpenMIPS采用的是一种改进的方法:假如位于流水线第n阶段的指令需要多个时钟周期,进而请求流水线暂停,那么需保持取指令地址PC的值不变,同时保持流水线第n阶段、第n阶段之前的各个阶段的寄存器不变,而第n阶段后面的指令继续运行。
为实现CPU的流水线暂停功能,我们设计添加了CTRL模块,其作用是接收各阶段传递过来的流水线暂停请求信号,从而控制流水线各阶段的运行。CTRL模块对译码和执行阶段的暂停请求信号进行判断,然后输出流水线暂停信号stall。下图是为了实现流水线暂停机制而对系统结构所做的修改。具体代码修改之处见文末的项目链接。
乘累加、乘累减指令共有4条,包括: madd、maddu、msub、msubu,各指令的格式如下图所示。从图中可知,这4条指令的指令码都是SPECIAL2,第6~15bit都为0,可以依据第0~5bit的功能码确定是哪一种指令。
实现思路
乘累加与乘累减指令计划在流水线阶段采用两个时钟周期完成运算,所以必须要保存两个信息:(1)、当前是第几个时钟周期;(2)、乘法结果。所以OpenMIPS通过在EX/MEM模块添加两个寄存器cnt、hilo,分别保存上述信息。修改系统结构框图如下所示:
1.修改译码阶段的ID模块
译码阶段的ID模块要添加对乘累加、乘累减指令的分析,根据给出的指令格式可知,这4条指令都是 SPECIAL2类指令,可以依据功能码确定是哪一种指令。确定指令的过程如下图所示。
涉及的宏定义如下(因为不涉及对通用寄存器的回写,所以无需“_OP”的宏定义)
`define EXE_MADD 6'b000000
`define EXE_MADDU 6'b000001
`define EXE_MSUB 6'b000100
`define EXE_MSUBU 6'b000101
这4条指令的译码过程都是相似的,简单说明如下。
2.修改执行阶段的EX模块
3.修改EX/MEM模块
增加两个输入接口hilo_i,cnt_i
,增加两个输出接口hilo_o,cnt_o
,在流水线执行阶段暂停的时候,将输入信号hilo_i通过输出接口hilo_o送出,输入信号cnt i通过输出接口cnt_o送出。其余时刻,hilo_o为0,cnt_o也为0。
采用"试商法"实现除法运算,对于32位的除法,至少需要32个时钟周期才能得到除法结果。
实现思路
新建一个模块 DIV,在其中实现采用试商法的32位除法运算。当流水线执行阶段的EX模块发现当前指令是除法指令时,首先暂停流水线,然后将被除数、除数等信息送到DIV模块,开始除法运算。DIV模块在除法运算结束后,通知EX模块,并将除法结果送到EX模块,后者依据除法结果设置HI、LO寄存器的写信息,同时取消暂停流水线。
修改后的系统部分框图如下
DIV模块的主要部分是一个状态机,共有四个状态
作者使用的是一段式的状态机,里面阻塞和非阻塞赋值交替使用,对于我这个硬件小白来说还是难度太高,我还是老老实实用三段式重新实现了,中间过程还算有点复杂,输出的逻辑既有组合逻辑也有时序逻辑,好在调试了一会之后总算通过了。
乘累加、乘累减测试指令
.org 0x0
.set noat
.global _start
_start:
ori $1,$0,0xffff
sll $1,$1,16
ori $1,$1,0xfffb # $1 = -5
ori $2,$0,6 # $2 = 6
mult $1,$2 # hi = 0xffffffff
# lo = 0xffffffe2
madd $1,$2 # hi = 0xffffffff
# lo = 0xffffffc4
maddu $1,$2 # hi = 0x5
# lo = 0xffffffa6
msub $1,$2 # hi = 0x5
# lo = 0xffffffc4
msubu $1,$2 # hi = 0xffffffff
# lo = 0xffffffe2
由波形图可见,乘累加、乘累减指令实现正确,同时观察到乘累加、乘累减指令都需要两个时钟周期来完成,且两个时钟周期内的PC值都保持不变。
除法模块测试指令
.org 0x0
.set noat
.global _start
_start:
ori $2,$0,0xffff
sll $2,$2,16
ori $2,$2,0xfff1 # $2 = -15
ori $3,$0,0x11 # $3 = 17
div $zero,$2,$3 # hi = 0xfffffff1,lo = 0x0
divu $zero,$2,$3 # hi = 0x00000003,lo = 0x0f0f0f0e
div $zero,$3,$2 # hi = 2,lo = 0xffffffff
执行阶段的EX模块对HI、LO寄存器的写操作为什么不引入cnt_i来进行判断呢?即cnt_i=2’b01(执行阶段的第二个周期)再加上功能码的定义来进行对HI、LO寄存器是否写入的决策呢?
联系前后模块发现,EX模块发出暂停流水线的请求后,EX_MEM模块收到来自EX模块的暂停流水线请求后就把HI、LO寄存器的写使能关闭了,也就是说我们在乘累加、乘累减的第一阶段的写指令是传不到HILO寄存器堆的,所以就不用加上cnt_i这个冗余指令的判断过程了。
三段式状态机还需多多训练,多注意时序上的问题,哪些变量在上升沿赋值,哪些变量是组合逻辑赋值。
乘累加、乘累减项目
除法运算项目