① WB级指令是写操作,即RegWrite_wb=1;
② WB级指令写回的目标寄存器不是$0,即RegWriteAddr_wb≠0;
③ WB级指令写回的目标寄存器与在ID级指令的源寄存器是同一寄存器,即
RegWriteAddr_wb=RsAddr_id 或 RegWriteAddr_wb=RtAddr_id。
2.2)数据冒险与阻塞
当一条指令试图读取一个寄存器,而它前一条指令是lw指令,并且该lw指令写入的是同一个寄存器时,定向转发的方法就无法解决问题。如图3.7所示
注意到lw指令只能在第 四时钟周期从内存中读 出数据,因此它和紧随其 后的and指令之间的依 赖关系与时序方向是相 反的,这种冒险是无法通 过转发来实现的。 图3.7 数据冒险与阻塞实例图 这类冒险不同于数据相关冒险,需要单独一个“冒险检测单元(Hazard Detector)”,它在ID级完成。
冒险成立的条件为:
① 上一条指令是lw指令,即MemRead_ex=1;
② 在EX级的lw指令与在ID级的指令读写的是同一个寄存器,即
RegWriteAddr_ex=RsAddr_id 或 RegWriteAddr_ex=RtAddr_id。
冒险的解决:
为解决数据冒险,我们引入流水线阻塞。当Hazard Detector检测到冒险条件成立时,在lw指令和下一条指令之间插入阻塞,即流水线气泡(bubble),使后一条指令延迟一个时钟周期执行,这样就将该冒险转化为二阶数据相关,可用转发解决。如图3.8所示。
6
图3.8 流水线气泡的引入
需要注意的是,如果处于ID级的指令被阻塞,那么处于IF级的指令也必须阻塞,否则,处于ID级的指令就会丢失。防止这两条指令继续执行的方法是:保持PC寄存器和IF/ID流水线寄存器不变,同时插入一个流水线气泡。
具体实现方法如下:
在ID级检测到冒险条件时, HazardDetector输出两个信号:Stall与PC_IFWrite。
Stall信号将ID/EX流水线寄存器中的EX、MEM和WB级控制信号全部清零。这些信号传递到流水线后面的各级,由于控制信号均为零,所以不会对任何寄存器和存储器进行写操作,高电平有效。
PC_IFWrite信号禁止PC寄存器和IF/ID流水线寄存器接收新数据,低电平有效。
2.3)分支冒险
还有一类冒险是包含分支的流水线冒险,下图。
7
图3.10 分支冒险实例
流水线每个时钟周期都得取指令才能维持运行,但分支指令必须等到MEM级才能确定是否执行分支。这种为了确定预取正确的指令而导致的延迟叫做控制冒险或分支冒险。
一种比较普遍的提高分支阻塞速度的方法是假设分支不发生,并继续执行顺序的指令流。如果分支发生的话,就丢弃已经预取并译码的指令,指令的执行沿着分支目标继续。由于分支指令直到MEM级才能确定下一条指令的PC,这就意味着为了丢弃指令必须将流水线中的IF、ID和EX级的指令都清除掉(flush)。这种优化方法的代价较大,效率较低。
如果我们能在流水线中提前分支指令的执行过程,那么就能减少需要清除的指令数。这是一种提高分支效率的方法,降低了执行分支的代价。
因此我们采用提前分支指令的方法解决分支冒险。 提前分支指令需要提前完成两个操作: ① 计算分支的目的地址:
由于已经有了PC值和IF/ID流水线寄存器中的指令值,所以可以很方便地将EX级的分支地址计算电路移到ID级。我们针对所有指令都执行分支地址的计算过程,但只有在需要它的时候才会用到。
② 判断分支指令的跳转条件:
我们将用于判断分支指令成立的Zero信号检测电路(Z test )从ALU中独立出来,并将它从EX级提前至ID级。具体的设计将在ID级设计中介绍。
在提前完成以上两个操作之外,我们还需丢弃IF级的指令。具体做法是:加入一个控制信号IF_flush,做为IF/ID流水线寄存器的清零信号。当分支冒险成立,即Z=1,则IF_flush=1,否则IF_flush=0,故IF_flush = Z。
考虑到本系统还要实现的无条件跳转指令:J和JR,在执行这两个指令时也必须要对IF/ID流水线寄存器进行清空,因此, IF_flush的表达式应表示为:
IF_flush = Z || J || JR。
8
2. MIPS指令格式
1) R型指令格式
op、funct:共同决定指令名称,都为6位; rs :指定第一操作数的寄存器地址,为5位; rt :指定第二操作数的寄存器地址,为5位; rd :指定目标寄存器地址,为5位; sa :位移运算的移动位数,为5位。
本实验要实现的R型指令有:
① 算术逻辑运算指令:ADD、ADDU、SUB、SUBU、AND、OR、NOR、XOR 、SLT、SLTU ② 移位指令:SLLV、SRLV、SRAV、SLL、SRL、SRA ③ 寄存器跳转指令:JR
2) I型指令格式
op :决定指令名称,为6位;
rs :指定第一操作数的寄存器地址,为5位; rt :储存结果的寄存器地址,为5位; Imm:立即数,为16位。
本实验要实现的I型指令有: ① 存储器访问指令:LW、SW
② 立即数算术逻辑运算指令:ADDI、ADDIU、ANDI、ORI、XORI、SLTI、SLTIU ③ 分支指令:BEQ、BNE、BGEZ、BGTZ、BLEZ、BLTZ 注:
① I型指令中立即数算术逻辑运算指令对立即数(Imm)的处理应分为两类情况考虑: ??当指令为ADDI、ADDIU、SLTI、SLTIU时,指令中的16位立即数(Imm)应做符号扩展为32位:sign-extend(Imm)。此符号扩展电路在ID级完成。
??当指令为ANDI、ORI、XORI时,Imm应做“0”扩展为32位。考虑到资源的限制,在执行ANDI、ORI、XORI指令时,“0”扩展功能放在ALU内部(即EX级)完成。
② 对于条件分支指令:不执行分支语句时,跳转地址应为下一条地址;执行分支语句时,跳转地址应为下一条地址加上跳转指令数,即为立即数。由于跳转方向有两个:向前与向后,故立即数存在正负性,应该有符号扩展为32位:sign-extend(Imm)。
如果能够将立即数的位数扩充,跳转指令的范围将大大增加。
由于CPU中指令的起始地址都是4的倍数,因此它们地址的后两位都是0,那么跳过的指令数后两位也应为2’b00,故可将16为立即数左移两位扩充为18位,寻址地址范围也
9
扩大4倍。因此分支地址可表示为{PC+4+sign-extend(Imm)<<2}.
③ 对于取字指令LW操作为:rt<=Mem[rs+sign_extend(Imm)]
对于存字指令SW操作为:Mem[rs+sign_extend(Imm)]<=rt
由于地址的变化是在内存中进行,故立即数只需进行有符号位扩展,不能位移。
3) J型指令格式
op :决定指令名称,为6位; address:跳转的地址,为26位。
本实验要实现的I型指令有: 无条件跳转指令:J
注:指令都是32位,因此我们要把26位跳转地址扩展为32位。由于CPU中指令的起始地址都是4的倍数,因此它们地址的后两位都是0,可以把26位的address扩展为28位:{26-bits address,2’b00};剩下的最高4位则直接从PC中取,即跳转地址扩展为: {PC[31:28],26-bits address,2’b00}。
3. 指令译码模块ID的设计
10