Chp3 Lab: Analysis of Instruction Execution Pipelining
Using the following code fragment:
LOOP: LW R1,0(R2) ; load R1 from address 0+R2 ADDI R1,R1,#1 ; R1=R1+1 SW 0(R2),R1; store R1 at address 0+R2 ADDI R2,R2,#4; R2=R2+4 SUB R4,R3,R2; R4=R3-R2 BNEZ R4,Loop ; branch to loop if R4!=0 Assume that the initial value of R3 is R2+396.
Analyze (a) by showing the timing of the first two iterations and calculate the total cycles needed; implement (b) and © in WinMIPS64. Execute the code in MARS, describe the difference between WinMIPS64 and MARS.
分析(a)通过显示前两个迭代的时间,并计算所需的总周期;实现(b)和©在WinMIPS64。在MARS中执行代码,描述WinMIPS64和MARS的区别。
a.
(a) Show the timing of this instruction sequence for the RISC pipeline without any forwarding or bypassing hardware but assuming a register read and a write in the same clock cycle “forwards” through the register file. Assume that the branch is handled by flushing the pipeline. If all memory references take 1 cycle, how many cycles does this loop take to execute?
在没有任何转发或绕过硬件的情况下,显示这个指令序列在RISC流水线上的计时,但假设在相同的时钟周期内,通过寄存器文件“转发”读和写寄存器。假设通过冲洗管道来处理分支。如果所有内存引用都需要一个周期,那么执行这个循环需要多少个周期?
在上述的代码中由于数据的依赖关系会发生Data hazards。首先列出存在依赖关系的指令(分别表示源指令和目标指令)和寄存器。
- 第一条
ADDI
指令必须等到上一条LW
指令执行完写回的阶段才能够得到正确的R1
的值。 SW
指令必须等到第一条ADDI
指令计算完R1
的值执行完写回阶段时才能开始执行。SUB
指令要等到第二条ADDI
指令计算完R2
的值到达写回阶段时才能开始执行。BNEZ
必须要等到SUB
指令计算完R4
的值到达写回阶段时才能开始执行。LD
指令在分支指令后需要重新的取值(当采用了分支跳转)
由于采用了通过寄存器文件“转发”读和写寄存器、冲洗管道来处理分支,目标和分支结果直到写回阶段结束后才知道。
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
LW R1,0(R2) | F | D | X | M | W | |||||||||||||||
ADDI R1,R1,#1 | F | D | - | - | X | M | W | |||||||||||||
SW 0(R2),R1 | F | - | - | D | - | - | X | M | W | |||||||||||
ADDI R2,R2,#4 | F | - | - | D | X | M | W | |||||||||||||
SUB R4,R3,R2 | F | D | - | - | X | M | W | |||||||||||||
BNEZ R4,Loop | F | - | - | D | - | - | X | M | W | |||||||||||
LW R1,0(R2) | F | - | - | F | D | X | M | W |
由于R3的初始值为R2+396, 并且在每次的循环过程中R2都会加上4。循环的条件是R4不等于0。由于R4=R3-R2, 即当R2=R3时结束循环。所以循环的总次数为396/4=99
。。
在每一次循环的过程中,由于Read and Write hazards 以及 分支跳转指令会损失8个周期。其中分支指令会损失两个时间周期由于instruction flushing。
如上表所示,第i
次循环中第一条指令的开始取指令的时钟周期为1+15*i
, 而最后一次循环过程中需要18
个时钟周期完成。因此总共需要的时钟周期为98*15+18=1488
b.
(b) Show the timing of this instruction sequence for the RISC pipeline with normal forwarding and bypassing hardware. Assume that the branch is handled by predicting it as not taken. If all memory references take 1 cycle, how many cycles does this loop take to execute?
显示这个指令序列的时间为RISC管道正常转发和旁路硬件。假设该分支是通过预测其未被采用来处理的。如果所有内存引用都需要一个周期,那么执行这个循环需要多少个周期?
在WinMIPS64
上进行测试,在执行完第一次循环后statistics
会出现2
次RAW stall 和 1
次branch taken stall。
由于我们现在使用了forwarding 和 bypassing hardware,分支结果和目标值在译码阶段结束后就可以知道了。
- 第一个
ADDI
仍然必须等到LW
到达WB
阶段才能得到R1
的值,但现在stall
通过联锁机制在EX阶段实现,值直接转发到EX阶段。 SW
会等待直到第一个ADDI
计算出R1的值并从EX
转发该值到MEM
SUB
获得由第二个ADDI
转发的R2值,而不需要等待。BNEZ
现在得到sub
转发的R4的值,但需要等待sub到EXE阶段。- 当分支被解析为错误预测后,将重新获取下一次迭代的
LW
。
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
LW R1,0(R2) | F | D | X | M | W | |||||||||
ADDI R1,R1,#1 | F | D | - | X | M | W | ||||||||
SW 0(R2),R1 | F | - | D | X | M | W | ||||||||
ADDI R2,R2,#4 | F | D | X | M | W | |||||||||
SUB R4,R3,R2 | F | D | X | M | W | |||||||||
BNEZ R4,Loop | F | D | - | X | M | W | ||||||||
LW R1,0(R2) | F | - | F | D | X | M | W |
如上表所示,第i
次循环中第一条指令的开始取指令的时钟周期为1+9*i
, 而最后一次循环过程中需要12
个时钟周期完成。因此总共需要的时钟周期为98*9+12=894
- Mars模拟器和WinMIPS64模拟器的区别
然后在mars上编写具有相同功能的代码如下:
.text
main:
addi $t2,$zero, 0x10010000
addi $t3,$t2,396
Loop:
lw $t1,0($t2)
addi $t1,$t1,1
sw $t1,0($t2)
addi $t2,$t2,4
sub $t4,$t3,$t2
bnez $t4,Loop
Finish:
li $v0, 10
syscall
-
功能侧重不同
首先Mars模拟器上的窗口是会显示代码区,调试区,数据段,寄存器段。但是
WinMIPS64
的窗口有Cycles
显示指令的执行的5段式流水线,pipeline
显示的是5段流水线的结构示意图。Registers
窗口显示的是寄存器以及具体的数值。Data
窗口显示的是数据段的内容和值。code
显示代码区,可以显示目前执行到哪一条语句。statistics
用来显示执行的指令个数、占用的指令周期数、不同原因导致stall的次数、以及代码的大小。Mars主要侧重于较为宏观的指令执行和调试,侧重于程序功能的实现;而WinMIP64把每一条指令的执行拆开了显示,会展示每条指令执行过程中的取指令、译码、执行指令、访存和写回5部分。侧重于分析指令的执行速度,而这些在Mars中对程序员是透明的。 -
寄存器不同
WinMIP64是采用的寄存器有
R0~R31
以及F0~F31
, 而Mars上的寄存器标号有$a0~$a3
、$t0~$t1
、s0~s7
、t8~t9
等,因此代码的编写方面有所不同。 -
指令执行速度
WinMIP64给出了CPI, 即每条指令占用的平均时钟周期,理论上指导了CPU的时钟周期后就可以计算出整个程序代码的执行时间。Mars模拟器上可以选择每秒钟执行的指令数,或者也可以选择以最大化的速度来运行,至于是如何调节执行的速度对于程序员来说是透明的。总的来说就是,mars可以执行速度的结果为导向倒推出不同的执行方式。而WinMIP64则是由不同的执行方式得到不同的执行速度,是一个正向推导的过程。
-
执行的方式
在WinMIP64上可以选择不同的执行选项,如
Enable Forwarding
、Enable Branch Target Buffer
、Enable Delay Slot
这些选项,用来控制不同的执行方式。而在Mars上没有这些选择。
c.
© Assume the RISC pipeline with a single-cycle delayed branch and normal forwarding and bypassing hardware. Schedule the instructions in the loop including the branch delay slot. You may reorder instructions and modify the individual instruction operands, but do not undertake other loop transformations that change the number or opcode of the instructions in the loop. Show a pipeline timing diagram and compute the number of cycles needed to execute the entire loop.
假设RISC流水线具有单周期延迟分支和正常转发和旁路硬件。调度循环中的指令,包括分支延迟槽。您可以重新排序指令并修改单个指令操作数,但不进行其他改变循环中指令的数目或操作码的循环转换。显示管道计时图并计算执行整个循环所需的周期数。
优化方法:
- 将第二个
addi
指令移动到加载延时的时隙中 - 将
sub
指令向前移动到第二个addi之后 - 将
sw
移动到分支等待的时隙,并调整偏移地址为(SW-4(R2),R1)
修改后的mips2.s代码如下所示:
.text
loop: LD R1,0(R2)
DADDI R2,R2,4
DSUB R4,R3,R2
DADDI R1,R1,1
BNEZ R4,loop
SD -4(R2),R1
然后在WinMIPS64
上进行测试,指令的执行流水线结果如下图所示:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | |
---|---|---|---|---|---|---|---|---|---|---|---|
LW R1,0(R2) | F | D | X | M | W | ||||||
ADDI R2,R2,#4 | F | D | X | M | W | ||||||
SUB R4,R3,R2 | F | D | X | M | W | ||||||
ADDI R1,R1,#1 | F | D | X | M | W | ||||||
BNEZ R4,Loop | F | D | X | M | W | ||||||
SW -4(R2),R1 | F | D | X | M | W | ||||||
LW R1,0(R2) | F | D | X | M | W |
- 修改完成之后,首先指令
ADDI R1,R1,#1
不需要额外等待,因为与第一个LW指令之间间隔了两个指令,刚好多占用了两个时钟周期 - 其次
BNEZ R4,Loop
指令也不需要额外的等待,因为它与sub
指令也间隔了一条指令,即不需要多占用一个时钟周期。 - 最后,
SW
指令占用了分支等待的时隙。使得第二次循环的第一条LW
指令也不需要额外的等待时间。
如上表所示,第i
次循环中第一条指令的开始取指令的时钟周期为1+6*i
, 而最后一次循环过程中需要10
个时钟周期完成。因此总共需要的时钟周期为6*98 +10=598