DLX Lab
Lab1 WinDLX使用及指令流水线冲突性分析
- 首先载入
GCM.S
和INPUT.S
两个汇编程序(GCM.S中需要用到INPUT.S程序代码),它们会被重定向到同一个目标文件中。 - 通过观察
WinDLX
的Code
窗口、Clock Cycle Diagram
窗口以及Memory/Symbols
菜单栏,可以发现Data
段是由0x1000
开始的,而且0x1000
存放的同时也是Prompt1
标号的起始地址。 GCM.S
中,首先将Prompt1
提示符保存到R1
寄存器中之后然后jal指令
无条件跳转到InputUnsigned
标号处(在Text
段中,地址为0x144
)。这里会出现一次控制冲突,紧跟在jal指令
后面的add指令
会被清空(注意跳转发生在第二个stage,因此只清空后面紧跟着的一条指令)。- 进入
InputUnsigned
的内部先完成Callee save
,将R2
到R5
寄存器保存到SaveR2
到SaveR5
(分别用space.
指令以4字节填充地址单元的地址) - 无论何时遇到一条trap指令时,DLX处理器的流水线将被清空(即
trap
一直在IF
阶段停顿直到当前流水线中的指令执行完毕) .word
指令表示就将后面的内容以字为单位连续存储到内存中。trap 5
将r14
中的字符串重定向到标准输出(i.e.显示提示符)trap 3
将标准输入重定向到r14
中(i.e.从控制台中读取用户输入的数字,以回车作为结束)- 在循环处理前将
r1
置为0用于保存最后的结果。 - 循环读取输到
ReadBuffer
中的数据直到读取到Line Feed
回车键,通过seqi
指令将r3
寄存器与回车键的ASCII
值进行比较对r5
寄存器进行赋值。然后bnez
根据r5
寄存器的值来选择是否跳转到Finish
标号。再将当前数值减去48(0x30)将ASCII数字字符转换为二进制数。将r1
乘10之后再加上从ReadBuffer
中读取的数字(目的是为了将控制台中输入的数字转换成二进制数),最后再移动ReadBuffer
指针以取下一个字符。最后完成restore
回到GCM.S
中。 - 观察可以发现
jar
跳转到label前会把当前指令的下一条指令的地址放入r31
中以便返回。 lbu r3, 0x0(r2)
和seqi r5, r3, 0xa
之前存在r3寄存器RAW数据相关导致的数据冲突。因为lbu指令需要访问存储器,最快取出数据也是在MEM阶段末,但在此时seqi指令已经处于第三个阶段并已经经过ALU了。为了解决这种数据冲突,只通过转发是不能解决的(时光不能倒流),还需要通过在两条指令之间插入nop指令,插入之后lbu处于MEM阶段,seqi处于ID阶段,seqi完全可以在进行ALU运算之前使用到通过转发部件(由MEM阶段末转发到ID/EX寄存器)传送的lbu取出的结果,就可以解决数据冲突。seqi r5, r3, 0xa
和bnez r5, Finish
之间存在r5寄存器RAW数据相关导致的数据冲突。之前老师上课专门有让大家讨论过Branch放到第二个阶段会带来什么问题。因为seqi指令在EX阶段的时钟周期末尾才更新r5的值,但bnez在ID阶段的时钟周期初就需要R5的值来决定是否要发生跳转(时光不能倒流)。同样也不能只用转发部件来转发,还需要在两条指令之间插个气泡(bnez已经随着seqi因为上一条指令的数据相关停顿过一次了)。此时再从EX阶段末得到的值转发到IF/ID寄存器中。multu r1, r1, r4
和add r1, r1, r3
之间存在r1寄存器RAW的数据相关导致的数据冲突。因为multu指令是完成浮点数乘法,实验开始时就要求将浮点的所有运算设定为4个时钟周期。因此add指令也需要停顿四个时钟周期等待multu浮点运算完成后更新r1寄存器才进行转发(由EX阶段末转发到ID/EX寄存器),这样才能解决数据冲突。subi r3, r3, 0x30
和add r1, r1, r3
之前存在r3寄存器RAW的数据相关导致的数据冲突。直接通过转发(而不是等到WB阶段)就能解决。j Loop
指令会导致控制冲突,因此紧跟在jump指令之后的lw指令就被清空了。j r31
后会插入一个nop
指令到延迟槽中。seq r3, r1, r2
和bnez r3, Result
之间存在r3寄存器RAW的数据相关导致的数据冲突。需要插入一个气泡,再将EX阶段末得到的r3更新后的数据转发到ID/EX寄存器,从而解决控制冲突。同理,sgt r3, r1, r2
和bnez r3, Result
也是类似的。- 最后再来分析一下使用转发部件解决冲突和不使用转发部件的性能差异。由图可以看出,使用转发部件后,停顿减少了13次。这13次都是RAW Stall,结合所学知识,其他三类LD指令、分支指令以及浮点指令导致的冲突都是不能单靠转发部件能解决的。同时注意到增加转发部件执行程序的时钟周期也从146降到了129,由CPU性能方程可知,提高了整体的性能。
Lab2 DLX处理器程序设计
- 浮点数除法DLX汇编代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46;fdiv.s
.data
;data for input
Scanf0: .asciiz "\nEnter the 1st number:"
Scanf1: .asciiz "\nEnter the 2nd number:"
;data for print
Printf0:.asciiz "\n(1st number) %g divided by (2nd number) %g = %g"
Printf1:.asciiz "\n divide by 0!!"
.align 2
Printf: .word Printf0
Printf3: .word Printf1
PrintfValue1:.space 8
PrintfValue2:.space 8
PrintfValue3:.space 8
.text
.global main
main:
addi r1,r0,Scanf0 ;input dividend
jal InputUnsigned ;This function convert ASCII to Binary
add r2,r1,r0 ;r2=dividend
addi r1,r0,Scanf1 ;input divisor
jal InputUnsigned
bnez r1,num2notzero ;(r1!=0) PC=num2notzero. This instruction to guarantee divisor is not zero
addi r14,r0,Printf3 ;when num2=0 print the prompt message
trap 5 ;print this message to standard output
j Result ;jump to the end and interrupt the running program
num2notzero:
;convert interger num1 and num2 to double floating-point
movi2fp f1,r1 ;Copies one word from integer register r1 to fp register f1.
movi2fp f2,r2
cvti2d f0,f1 ;Converts a signed integer to double precision float.
cvti2d f2,f2
sd PrintfValue1,f2
sd PrintfValue2,f0
divd f8,f2,f0 ;divide for double precision floats
;print the quotient
sd PrintfValue3,f8
addi r14,r0,Printf ;r14 is specialized for trap instruction
trap 5
Result:
trap 0 - 程序运行结果图
- 首先查看DLX指令编码表,通过Chrome浏览器中的vim插件进行模糊查找定位到双精度浮点除法指令divd。因为除法程序需要用到input.s文件将从命令行输入的ASCII字符转换成二进制整型数字(因此先不采用浮点寄存器,最后再进行整型到浮点的转换)。分析InputUnsigned可以发现,它会将转换后的结果放入到R1寄存器中。
- 注意trap 5需要的r14存放的是字符串起始地址的间接地址,否则会出现“address wrong”的报错。
- 在实现最后打印语句
printf("\n(1st number) %g divided by (2nd number) %g = %g", (double)num1, (double)num2, num3 );
时,需要将后面的参数的地址连续存放在提示串的后面才能正确显示,同时也需要注意参数的大小,双精度浮点数参数要分配8个地址单元(DLX按字节编址寻址,且双精度浮点数为64-bit)。 - movi2fp-cvi2d指令存在浮点寄存器f1的数据相关导致的数据冲突。这两条指令的f2也有同样的情况。解决这种控制冲突可以通过将movi2fp在MEM阶段末的值转发给cvi2d的EX阶段使用。
- cvi2d-sd指令之间存在浮点寄存器f2之间的数据相关导致的数据冲突。解决这种数据冲突可以通过在cvi2d指令的EX阶段或者是在MEM阶段进行转发,转发到sd指令的MEM阶段来解决数据冲突。
- divd-sd指令之间存在浮点寄存器f8的数据相关导致的数据冲突。因为浮点除法指令的在EX阶段需要执行的周期为4个周期,因此只使用转发也不能解决冲突,还得需要加以停顿。
- 分析使用转发和不使用转发的性能差异。可以发现在使用转发部件后程序的执行周期由153降到了132,RAW停顿由43下降到了27,由CPU性能方程可知,程序整体性能得到提升。
- 观察指令
bnez r1,num2notzero
。如果不分支发生,程序数据分析就如上图所示,即进行浮点的除法运算。如果分支发生(因为除数为0)则直接输出提示信息并跳转到结束处,数据分析如下图所示。对比可以发现,在使用转发部件的情况下,分支不发生程序执行所需的时钟周期是132,而分支发生所需的时钟周期是121;在不使用转发部件的情况下,分支不发生程序执行所需的时钟周期是153,分支发生所需的时钟周期是137。可以得出结论,在分支发生时程序所使用的时钟周期更短,程序整体性能更高。