CS:APP2e Y86处理器模拟器∗指南
Randal E.Bryant
David R. O’Hallaron
2013年7月29日
本文档描述了处理器模拟器,伴随的表示在第4章Y86处理器架构的计算机系统:一个程序员的角度来看,第二版。这些模拟器模型的三种不同的处理器设计:SEQ,SEQ +和PIPE。
- 安装
模拟器的代码通常是分布于一个名叫sim.tar的文件之中,tar格式文件。你可以从CS:APP2e Web site (csapp.cs.cmu.edu)这个网站获取一份拷贝的文件。在tar文件的目录中安装代码,执行以下步骤:
unix> tar xf sim.tar
unix> cd
sim unix> make clean
unix> make
默认情况下,这个生成GUI(图形用户界面graphic user interface)版本的模拟器要求你有在系统中安装Tcl/Tk。若你没安装,那么你可以选择安装TTY-only版本,TTY-only的标准输出是ASCII文本。参见README文件如何生成GUI和TTY版本。
Sim目录包含以下子目录:
Misc:源代码文件中的实用工具,例如:YAS(Y86j集合),YIS(Y86指令集模拟器)和HCL2C(HCL翻译到C)。它还包括isa,c源文件所使用的所有处理器模拟器。
Seq:SEQ和SEQ+模拟器的源代码。4.49和4.50包含HCL文件作业问题。参见README对不同版本的模拟器编译的说明。
Pipe:pipe模拟器的源代码。4.52-4.57包括HCL文件问题。参见README对不同版本的模拟器编译的说明。
Y86-code:Y86汇编代码的那一张中所示的实例程序。你可以在这些基础程序上测试你修改的模拟器。参见README文件对如何运行这些测试进行说明。作为一个运行的实例,我们将使用在这个子目录中的asum.ys。这个程序在CS:APP2e的图4.8中说明。编译后的版本如图一所示。
Ptest:生成在不同的指令,不同的可能性和不同的风险下系统回归测试的脚本。这些脚本能够很好的帮助你在你的工程中发现bug。
1 | # Execution begins at address 0
2 0x000: | .pos 0
3 0x000: 30f400010000 | init: irmovl Stack, %esp # Set up stack pointer
4 0x006: 30f500010000 | irmovl Stack, %ebp # Set up base pointer
5 0x00c: 8024000000 | call Main # Execute main program
6 0x011: 00 | halt # Terminate program
7 |
8 | # Array of 4 elements
9 0x014: | .align 4
10 0x014: 0d000000 | array: .long 0xd
11 0x018: c0000000 | .long 0xc0
12 0x01c: 000b0000 | .long 0xb00
13 0x020: 00a00000 | .long 0xa000
14 |
15 0x024: a05f | Main: pushl %ebp
16 0x026: 2045 | rrmovl %esp,%ebp
17 0x028: 30f004000000 | irmovl $4,%eax
18 0x02e: a00f | pushl %eax # Push 4
19 0x030: 30f214000000 | irmovl array,%edx
20 0x036: a02f | pushl %edx # Push array
21 0x038: 8042000000 | call Sum # Sum(array, 4)
22 0x03d: 2054 | rrmovl %ebp,%esp
23 0x03f: b05f | popl %ebp
24 0x041: 90 | ret
25 |
26 | # int Sum(int *Start, int Count)
27 0x042: a05f | Sum: pushl %ebp
28 0x044: 2045 | rrmovl %esp,%ebp
29 0x046: 501508000000 | mrmovl 8(%ebp),%ecx # ecx = Start
30 0x04c: 50250c000000 | mrmovl 12(%ebp),%edx # edx = Count
31 0x052: 6300 | xorl %eax,%eax # sum = 0
32 0x054: 6222 | andl %edx,%edx # Set condition codes
33 0x056: 7378000000 | je End
34 0x05b: 506100000000 | Loop: mrmovl (%ecx),%esi # get *Start
35 0x061: 6060 | addl %esi,%eax # add to sum
36 0x063: 30f304000000 | irmovl $4,%ebx #
37 0x069: 6031 | addl %ebx,%ecx # Start++
38 0x06b: 30f3ffffffff | irmovl $-1,%ebx #
39 0x071: 6032 | addl %ebx,%edx # Count-
40 0x073: 745b000000 | jne Loop # Stop when 0
41 0x078: 2054 | End: rrmovl %ebp,%esp
42 0x07a: b05f | popl %ebp
43 0x07c: 90 | ret
44 |
45 | # The stack starts here and grows to lower addresses
46 0x100: | .pos 0x100
47 0x100: | Stack:
图1:代码实例。这段代码在y86-code子目录中的asum.yo文件中。
2、 实用程序
安装完成后,misc目录中包含两种有用的代码。
YAS:Y86汇编程序。这需要一个有.ys扩展名的Y86文件,用.yo文件生成一个.ys文件。生成的文件包括一个ASCII版本的代码,像图一所示(相同的程序所示在CS:APP2e的图4.8中)最简单的方法来调用一个汇编程序,可以以使用或者建立一个汇编到y-86 code这个子目录文件中。例如,想要汇编这个目录中的asum.ys文件,我们可以使用这样的命令:
unix> make asum.yo
YIS:Y86指令模拟器。这个程序在Y86机器级执行指令根据指令集定义。例如,假设你想运行在y86代码目录下的程序asum.yo。简单的运行:
unix> ../misc/yis asum.yo
YIS模拟执行程序然后打印所有改变的寄存器或者是主存在终端上。就像CS:APP2e 4.1节所述。
3、处理器模拟器
对于SEQ,SEQ+和PIPE三个处理器,我们分别提供了SSIM,SSIM+和PSIM。每一个模拟器可以在TTY和GUI模式下运行。
TTY模式 使用一个简单的,面对终端的接口。打印所有在终端输出上。虽然调试不是很方便,但可以安装在任何系统,可用于自动化测试。默认对所有模拟器。
GUI模式 有一个图形界面,不久将被描述,对于观察处理机活动和调试修改版本很有用。然而,它需要在你的系统上安装Tcl/Tk。使用-g调用命令行选项。
3.1 命令行选项
你可以从命令行使用下列选项:
-h 打印所有命令行选项的摘要
-g 在GUI模式下运行模拟器(默认TTY模式)
-t 运行处理器和ISA模拟器,比较内存的结果的值,寄存器文件和条件代码。如果没有发现差异,就打印“ISA检查成功”。否则,打印关于寄存器文件或者内存不同的信息。这个功能对于测试处理器设计非常有用。
-l m设置指令限制,在停止之前最多执行m条指令(默认10000指令)
-v n信息显示级别设置为n,这个值默认是2,在0-2之间。
模拟器中在GUI模式上运行必须调用一个文件对象的名称在命令行上。在TTY模式下文件的名称是可选的,默认情况下来自stdin。
下面是一些调用模拟器的典型(从y86-code子目录):
unix> ../seq/ssim -h
unix> ../seq/ssim -t < asum.yo
unix> ../pipe/psim -t -g asum.yo
第一种情况打印了SSIM在命令行选项的摘要。第二种选项在TTY下运行SEQ模拟器,从stdin中读取目标文件asum.yo。第三种情况在GUI模式下运行PIPE模拟器,执行指令对象文件asum.yo。在第二中和第三种情况之中,将结果与更高级ISA模拟器的结果相对比。
3.2 SEQ和SEQ+模拟器
SEQ处理器的GUI版本在命令行中被对象文件名调用:
unix> ../seq/ssim -g asum.yo &
在最后有“&”的命令允许模拟器在后台模式运行。启动模拟器程序并创建三个窗口,如图2-4.
第一个窗口(图二)是主要的控制面板。如果HCL被HCL2C用-n 选项编译了,主控制面板的标题将显示“Y86处理器:名称”。否则只显示“Y86处理器”
主控制窗口包含处理器控制按钮以及处理器的信息处理状态。窗口不同的部分在途中有所标记:
控制:位于顶部的按钮控制模拟器。点击退出按钮退出模拟器。单机Go按钮可以使模拟器开始运行。单机停止按钮使模拟器暂时停止。单机步骤按钮可以使模拟器执行一条指令然后停止。单机重置按钮使模拟器回到初始状态,程序计数器的地址0,寄存器设置变为0,内存除了程序清零,设定条件规范ZF=1,CF=0,OF=0。程序状态设定为AOK。
图二:SEQ模拟器主控制面板
图三:SEQ模拟器代码显示窗口
图四:SEQ模拟器内存显示窗口
当模拟器运行时,按钮下面的滑块可以控制模拟器的速度。向右移动可以使模拟器运行速度更快。
阶段信号:这一部分显示不同处理器信号在当前的指令求值期间的值。这些信号几乎是相同的,如CSAPP2e中的图4.23所示。主要的区别在于模拟器显示在Instr中指令的名称,而不是icode和ifun的数值。同样所有寄存器标识符显示他们的名字,而不是他们的数值,用”----”表示不需要注册访问。
寄存器文件:这部分显示了8个程序寄存器的值。已经更新的了最新的寄存器会浅蓝色显示。寄存器的内容不会显示,指导第一次设置为非零的值。 注意当一条指令写入程序寄存器时,指到下一个时钟周期的开始,寄存器文件不会更新。这意味着你必须一步一步的看到寄存器更新。
状态:显示当前正在执行指令的状态。这些值可能是:
AOK:没有问题
ADR:寻址错误,试图读取一个指令或者试图去读写一个数据。地址不能超过0X0fff。
INS:遇到非法指令。
HLT:遇到停止指令。
状态代码:显示三个状态标志:ZF,SF和OF。
注意:当一个指令改编状态代码,直到下一个时钟周期开始,状态代码寄存器不更新。这意味着你要一步一步看到更新。
处理器asum第38行第二线程执行状态如图2所示。程序yo如图一所示,我们可以看见程序计数器在0X06b,它处理指令将%ebx,%ecx相加,寄存器%eax存储0xcd,前两个数组的和。%edx保存3,计数递减。寄存器%ecx保存0x1c,第三个数组的地址。寄存器%ebx存储4(从第36行)但有一个0xFFFFFFFF写入这个寄存器(由于dstE存放在%ebx中,数值是0xFFFFFFFF)。写入将在下个始终周期开始。
窗口(图三所示)描述了目标代码已经被模拟器执行了。编辑框标识被执行的程序的文件名。你可以编辑文件名在这个窗口,点击登陆按钮登陆新的程序。左边显示已经被执行的对象代码,右边显示了汇编代码的文本文件。中心有一个星号表示指令目前正在被模拟。这相对应于图一所示asum.yo的第38行。
图四所示关于内存的内容。它只显示当程序开始执行时最大值和最小值地址的位置。每一行显示内存的数据。因此每一行显示内存16字节的内存,这些字节地址只有最低有效位16字节。左边的内容是根地址,最低有效位的位置显示”-”。每一列之后对应最低地址位数0x0,0x4,0x8,0xc。图四所示的例子中有箭头表明存储器地址0x00e8,0x00e4,0x00e8,0x00ec。
图中描述了关于求和程序在运行时,asum.yo程序在存储器中堆栈的内容。我们可以看到到目前为止堆栈的运行情况,我们能够看到%esp和%ebp被初始化为0x100(3行和4行所示)。在第五行推出调用返回指针0x011,被写入地址0x00fc。程序通过推出%ebp开始,将0x100写入0x00f8。然后退出%eax的值(18行),将0x4写入0x00f4和%edx(20行)。将0x14(数组)写入0x00f0。在第21行调用Sum导致返回指针0x3d被写入地址0x00ec。在运行sum时,存储器先推出%ebp的值导致0xf8被写入地址0x00e8。这归因于将数值在存储器和堆栈指针被设定为0xe8.
图5显示了在执行相同代码文件和有相同点的程序的SEQ+模拟器的控制面板窗口。我们可以看出唯一不同的就是排序不同的流程和不同的信号列表。这些信号在CSAPP2e图4.40中有对应。SEQ+模拟器还声称代码和存储器窗口。在SEQ模拟器中他们的格式相同。
3.3 PIPE模拟器
PIPE模拟器也生成三种窗口。图6显示了控制面板。它的控制有相同的设定,寄存器文件和条件代码有相同的显示。中间的部分显示了寄存器的状态。不同的字段在CSAPP2e的图4.52中有相应的对应。在面板的底部显示数据的周期模拟(不包括最初的周期),完成数据的指令,结果CPI。
见图7的观察图,每个pipeline寄存器显示了两个部分。上面白色框中的数值显示了最近pipeline寄存器中的数值。较低的灰色背景中的数值显示了pipeline中的输出。除非寄存器。。。。。。,这些在下一个时钟周期被寄存器载入。
通过PIPE模拟器的流量和在SEQ和SEQ+模拟器中的不一样。在SEQ和SEQ+中,控制面板显示执行单一命令的数值结果。每一步执行模拟器处理一条指令。在PIPE中,控制面板显示多种通过pipeline的命令流。每一步执行模拟器执行这一阶段每一条命令的数值计算。
图八显示了PIPE模拟器的代码显示。格式类似于SEQ和SEQ+,除了执行命令时单一的标记指示,用字符F、D、E、M和W来显示pipeline每一阶段的取回、解码、执行、内存、重写阶段。
图5:SEQ+模拟器的主控制界面
图6:PIPE模拟器的主控制界面
图7:PIPE模拟器主控板查看单一PIPE寄存器
图8:PIPE模拟器代码显示窗口
PIPE模拟器同样生成一个窗口来显示存储目录,SEQ模拟器有相同的格式。(图4)
图6和图8的例子显示了当执行图1,34-40行的循环时显示pipeline的状态。我们可以看见模拟器开始第二轮迭代,每个阶段的状态如下:
重写:loop-closing指令(40行)完成。
存储:mrmov指令(24行)从地址0x018读出。我们可以看见在pipeline寄存器M中valE的地址,和pipeline寄存器W中作为valM的输入,从存储器中读取的数值。
执行:这一阶段包含一个气泡。这个气泡的产生是由于mrmovl命令(34行)和addl命令(35行)之间的以来关系。这个泡沫就像是nop指令。它解释了为什么没有指令在图8中标记”E”。
解码:addl指令(第35行)刚刚从寄存器%eax读取0x00D。也从寄存器%esi读到数据0x00D。但是我们可以看到转发逻辑并没有使用刚刚从内存(在pipeline寄存器W中视为对valM的输入)中读取到的数据0x0C0作为valA的新值(在pipeline寄存器E中视为对valA的输入)
读取:irmovl指令(38行)刚从地址0x063获取到,电脑的新值预计是0x069。
每个阶段用他们的状态字段统计联系起来。这一字段显示了指令在pipeline中那一阶段的状态。AOK状态意味着没有遇到任何异常。状态BUB表明了在次阶段中有一个泡沫 ,并不是一个标准的指令。另一种一些可能的状态是:ADR:当一个无效的内存地址被引用时;INS:遇到一个不合法的指令代码;PIP:当在pipeline中遇到问题时(这常常发生在停止和某些被设置为1的pipeline寄存器出现泡沫信号);HTL:与带了一个停止命令。当遇到上述的四种情况,模拟器会停止。
一些建议:
以下是一些其它的技巧,我们从学习使用这些模拟器的经验。
熟悉这些模拟器的操作。尝试去运行一些在y86-code目录中的示例代码。确保你理解每条指令是如何处理一些小的例子。看一些有趣的情况,例如:如在驱动的装置,程序的返回值。
你需要寻找信息。看到数据转发的影响时尤其棘手。有七个可能的源信号valA流水线寄存器E,和六个可能的源信号valB。选择哪一个,你需要比较这些流水线寄存器输入字段值的可能来源。可能的来源有:
R[d srcA] 源寄存器被输入在流水线寄存器srcA E中。寄存器内容显示在底部。
R(d srcB) 源寄存器被输入在流水线寄存器srcB E中。寄存器内容显示在底部。
D valP 这个值是流水线寄存器状态的一部分。
e valE 这个值是valE的输入字段在流水线寄存器M中。
M valE 这个值是M流水线寄存器的状态的一部分。
m valM 这个值是valE的输入字段在流水线寄存器W中。
W valE 这个值是W流水线寄存器的状态的一部分。
W valM 这个值是M流水线寄存器的状态的一部分。
你不要写重复的代码。由于数据和代码共享相同的地址空间,很容易有一个项目覆盖一些代码,在它试图再次执行指令时导致完全混乱,。重要的是设置距代码足够远的堆栈来避免这种情况。
避免大的地址值。模拟器不允许任何大于 0x0FFF的地址。此外,如果你修改内存位置时地址较大内存显示会慢。
注意一些GUI模式模拟器的“特性”(SSIM SSIM +,PSIM)
- 你必须从他们的主目录执行程序。换句话说,去运行SSIM或SSIM+,你一定要处于seq目录中,然而你运行PSIM子目录必须在管道中。这个需求出现是由于Tcl解释器定位模拟器配置文件的方式。
-
如果你是在Unix机器上运行GUI模式,记得要初始化显示环境变量:
unix> setenv DISPLAY myhost.edu:0
在一些Unix X窗口管理器中,把“程序代码”窗口开始运行作为一个封闭的图标。如果您没有看到这个窗口当模拟器启动时,你需要手动点击来扩大它。
- 一些微软的windows X服务器,当存储内容更改时存储内容的窗口不会自动调整自己的大小,在这种情况下,你需要自己去手动调整来查看内存的值
- 如果你要求他们执行一个不是有限的Y86文件,模拟器将因错误而终止。