第4章 处理器体系结构
处理器执行一系列指令,指令被编码为由一个或多个字节序列组成的二进制格式。一个处理器支持的指令和指令的字节级编码称为它的指令集体系结构,ISA。
4.1 Y86指令集体系结构
定义一个指令集体系结构,包括定义各种状态元素、指令集和他们的编码、一组编程规范和异常事件处理。
4.1.1程序员可见状态
Y86程序中的每条指令都会读取或修改处理器状态的某些部分,称为程序员可见状态。
8个程序寄存器:%eax,%ecx,%edx,%ebx,%esi,%edi,%esp,%ebp
寄存器%esp被入栈,出栈,调用和返回指令作为栈指针
三个一位的条件码:ZF/SF/OF
程序计数器PC存放当前正在执行指令的地址
存储器,是一个很大的字节数组,保存着程序和数据
Y86程序用虚拟地址来引用存储器位置。硬件和操作系统软件联合起来将虚拟地址翻译成实际或物理地址
状态码Stat 表明程序执行的总体状态:正常运行/某种异常
4.1.2 Y86指令
这个指令集是处理器实现的目标,Y86指令集基本上是IA32指令集的一个子集,只包括四字节整数操作,寻址方式较少,操作也较少。
IA32的movl指令分成了四个不同的指令:irmovl,rrmovl,mrmovl,rmmovl 显示的指明源和目的。
源可以是立即数i,寄存器r,存储器m 目的可以是寄存器r,存储器m
不允许从存储器到存储器,也不允许将立即数传到存储器。
4个整数操作指令:addl,subl,andl,xorl 只对寄存器数据进行操作,会设置3个条件码
7个跳转指令:jmp,jle,jl,je,jne,jge,jg
6个条件传送指令:cmovle,comvl,cmove,cmovne,cmovge,cmovg
call指令将返回地址入栈,然后跳到目的地址。ret从这样的过程调用中返回
pushl,popl实现入栈和出栈
halt停止指令的执行,会导致处理器停止,并将状态码设置为HLT
4.1.3指令编码
每条指令的字节级代码需要1~6个字节不等
1、第一个字节表明指令的类型,分两部分:高四位代码部分,低四位功能部分。代码值为0~0xB,功能值只有在一组相关指令共用一个代码时才有用。
2、8个程序寄存器都有相应的寄存器标识符:
数字 寄存器名字
0 %eax
1 %ecx
2 %edx
3 %ebx
4 %esp
5 %ebp
6 %esi
7 %edi
F 无寄存器
程序寄存器存在CPU的一个寄存器文件中,当指令需要操作数时,会有附加的寄存器指示符字节(如上数字),可以指定一个或两个寄存器,这些寄存器字段为rA和rB,可以指定用于源和目的的寄存器或者是用来地址计算的基址寄存器。
只需要一个寄存器的将另一个寄存器指示符设为0xF。
3、有些指令需要附加4字节常数字
1 作为irmovl的立即数数据
2 rmmovl和mrmovl的地址指示符的偏移量
3 分支指令和调用指令的目的地址,这里是一个绝对地址,而不是像IA32中使用PC相对寻址方式。同时这里所有整数采用小端法编码
指令集的一个重要性质就是字节编码必须有唯一的解释。每条指令的第一个字节有唯一的代码和功能组合,可以决定其他附加字节的长度和含义。
4.1.4 Y86异常
程序员可见的状态包括状态码stat描述程序执行的总体状态。
代码值 名字 含义
1 AOK 正常操作
2 HLT 处理器执行halt指令
3 ADR 遇到非法地址
4 INS 遇到非法指令
当遇到这些异常时可以简单的让处理器停止执行指令,在更完整的设计中,通常会调用一个异常处理程序,可配置不同的结果如放弃程序或者调用一个用户自定义的信号处理程序。
4.1.5 Y86程序
一些小示例:
以“.”开头的是汇编器命令,告诉汇编器调用地址。
.pos0 告诉汇编器应该从0地址开始产生代码,这个地址是所有Y86程序的起点
irmovl Stack,%esp 初始化栈指针
irmovl Stack,%ebp 初始化帧指针
结尾申明标号Stack(Stack:),用.pos指明地址0x100(.pos 0x100),栈会从这里开始向低地址增长。
声明一个4个元素的数组,标号array表示是这个数组的起始
.align 4 ;表明在四字节边界处对齐
array: .long 0xd
.long 0xc0
.long 0xb00
.long 0xa000
YIS:指令集模拟器 目的是模拟Y86机器代码程序的执行,而不用试图去模拟任何具体的处理器实现的行为。
模拟输出的第一行总结了执行以及PC和程序状态的结果值,只打印出改变了的寄存器或存储器中的字,左边是原始值,右边是最终值。
4.1.6 一些Y86指令的详情
pushl指令会把栈指针减4并且将一个寄存器的值写入存储器中,因此执行 pushl %esp时处理器的行为是不正确的,因为要入栈的寄存器会被同一条指令修改。有两种约定:
1 压入%esp的原始值
2 压入减去4的%esp的值
同样popl %esp 可以将%esp设置为从存储器中读出的值,也可以设置为加上4后的栈指针。
4.2 逻辑设计和硬件控制语言HCL
逻辑1:高压 逻辑0:低压
实现一个数字系统需要三个组成部分:
计算对位进行操作的函数的组合逻辑
存储位的存储器元素
控制存储器元素更新的时钟信号
HCL语言:描述不同处理器设计的控制逻辑。
4.2.1 逻辑门
逻辑门是数字电路的基本计算元素,输出为输入位值的某个布尔函数。
HCL表达式:
AND: &&
OR: ||
NOT: !
逻辑门只对单个位的数进行操作,而不是整个字。
常见的n路操作表示,例:AND门,输入为a,b,c,HCL表示为a&&b&&c
逻辑门总是活动的,一旦输入变化了,输出会相应的变化。
4.2.2 组合电路和HCL布尔表达式
将很多的门组合成网构建计算块,称为组合电路
构建的两条限制:
两个或多个逻辑门的输出不能连接在一起,否则会使线上的信号矛盾
网必须是无环的,也就是不能形成一个回路
例:检测位相等的组合电路
bool eq = (a && b) || (!a && !b)
"="将一个信号名与一个表达式联系起来,和C不同,这不是执行了一次计算并将结果放入存储器中的某个位置,它只是用一个名字来称谓一个表达式。
例:多路复用器
单个位的多路复用器中,两个输入数据a、b,一个控制信号s。s=1输出为a,s=0输出为b
bool out = (s && a) || (!s && b)
组合逻辑电路和C表达式区别:
1 组合电路中输出会持续的响应输入的变化,C表达式只会在程序执行过程中被遇到时才进行求值。
2 C表达式允许参数是任意整数,0表示FALSE,其他都表示TRUE。逻辑门只对位值0和1操作。
3 C表达式可以只被部分求值,如果第一个参数求值就能确定不会求值第二个参数,而组合逻辑没有部分求值。
4.2.3 字级的组合电路和HCL整数表达式
执行字级计算的组合电路根据输入字的各个位,用逻辑门来计算输出字的各个位。
例:测试32位字A和B是否相等
用32个单个位相等电路的输出用一个AND连起来实现。
HCL中所有字级信号都声明为int,不指定字的大小,上述电路可表达成
bool Eq = (A == B)
A和B都是int型的,‘=’为赋值,‘==’是相等运算符。
例:字级的多路复用电路,控制位s,32位的Out为A/B
32个位级多路复用器组成,其中只产生一次!s,重复使用。
HCL中,多路复用函数是用情况表达式描述的,通用格式如下:
[
select_1 : expr_1
select_2 : expr_2
:
select_k : expr_k
]
布尔表达式和整数表达式,前者表明什么时候该选择,后者指明得到的值
同switch语句不同,这里不要求不同的选择表达式之间互斥,顺序求值,第一个求值为1的情况会被选中。
字级多路器HCL表示:
int Out = [
s: A;
1: B;
];
第二个选择表达式为1,如果前面的没被选中就选这种情况,这是HCL中指定默认情况的方法,几乎所有的都是依次结尾。
选择表达式可以是任意的布尔表达式,可以有任意多的情况,书上还有四路复用器和找最小值的例子。
注:任何以#开头到行尾结束的文字都是注释。
组合逻辑电路可以设计成在字级数据上执行许多不同类型的操作。
例:算术/逻辑单元(ALU)
三个输入:A、B两个数据输入和一个控制输入,根据控制输入可执行不同的算术或逻辑操作,控制值和Y86指令操作的功能码相对应。
0 addl
1 subl
2 andl
3 xorl
4.2.4 集合关系
例:从两位信号code中选择高位和低位
可以用相等测试来表示:
bool s1 = code == 2 || code == 3;
bool s0 = code == 1 || code == 3;
用更简洁的集合关系表示:
bool s1 = code in {2,3};
bool s0 = code in {1,3};
判断集合关系的通用格式是:
iexpr in {iexpr1,iexpr2,...,iexprk}
被测试值 待匹配值 都是整数表达式
4.2.5 存储器和时钟
为了产生时序电路,引入按位存储信息的设备,存储设备由一个时钟控制,时钟是一个周期性信号,决定什么时候要把新值加载到设备中。两类存储器设备:
时钟寄存器:存储单个位或字,时钟信号控制寄存器加载输入值
随机访问存储器:存储多个字,用地址来选择该读或该写哪个字
在硬件中,寄存器直接将他的输入和输出线连接到电路的其它部分。在机器级编程中,寄存器代表的是CPU中为数不多的可寻址的字,这里的地址是寄存器ID,这两类称为“硬件寄存器”和”程序寄存器“
当时钟到达上升沿时,值才会从寄存器的输入传送到输出,Y86处理器会用时钟寄存器来保存程序计数器PC、条件代码CC和程序状态Stat。
例:寄存器文件
两个读端口A/B,一个写端口W。电路可以读两个程序寄存器的值同时更新第三个寄存器的状态。每个端口都有一个地址输入,表明该选择哪个程序寄存器,地址是寄存器标识符。读端口有地址输入srcA和srcB和数据输出valA和valB,写端口有地址输入dstW和数据输入valW。
读数据:以地址为输入,以数据为输出,当srcA或srcB被设成某个寄存器的ID,存储在寄存器中的值会出现在valA或valB上。
写入字:由时钟信号控制,时钟上升时,输入valW上的值会被写入输入dstW上的寄存器ID对应的程序寄存器中。ID为0xF时,不会写入任何程序寄存器中。
例:随机访问存储器
一个地址输入,一个写的数据输入,一个读的数据输出。
读:输入address上提供地址,write控制信号设为0,存储在地址上的值会出现在输出data上。
写:将address设置为期望的地址,data in设置为期望的值,write设置为1,时钟上升沿时会更新存储器中指定的位置。
如果地址不合法,error会被设置为1。
4.3 Y86的顺序实现
SEQ处理器顺序处理,每个时钟周期上,SEQ会执行一条完整指令,这需要很长的时钟周期,因此时钟周期频率会很低,最终目标是事项一个高效的、流水线化的处理器。
4.3.1 将处理组织成阶段
把每条指令组织成特殊的阶段序列,各个阶段通用框架:
取指:从存储器读取指令字节,地址为程序计数器PC的值。
指令指示符字节的两个4位为icode指令代码和ifun指令功能。还可能有寄存器指示符和四字节常数字。
按顺序方式计算下一条指令的地址valp=PC+已取出指令的长度。
译码:从寄存器文件读入最多两个操作数读入指令rA、rB指明的寄存器或%esp。
执行:算术逻辑单元ALU执行操作或计算存储器引用的有效地址,增加或减少栈指针,也可能设置条件码。
访存:将数据写入存储器或者从存储器读数据。
写回:最多可以写两个结果到寄存器文件。
更新PC:将PC设置成下一条指令的地址。
处理器无限循环执行这些指令。异常:执行halt或非法指令,非法地址。完整的设计中有异常处理模式。
具体示例:
1、OPl整数和逻辑运算、rrmovl寄存器-寄存器传送、irmovl立即数-寄存器传送
2、存储器读写指令rmmovl/mrmovl
3、入栈出栈指令pushl/popl
4、三类控制转移指令:各种跳转、call和ret
4.3.2 SEQ硬件结构
在硬件结构的抽象表示中信息流从最低端先向上再向右,同各个阶段相关的硬件单元负责执行处理。在右边,反馈线路向下,包括要写到寄存器文件的更新值,以及更新的程序计数器的值。
硬件单元与各个处理阶段相关联:
取指
译码
执行
访存
写回
硬件图的画图惯例:
浅灰色方框表示硬件单元
控制逻辑块是用灰色圆角矩形表示的
线路的名字在白色椭圆中说明
宽度为字长的数据连接用中等粗度的线表示
宽度为字节或更窄的数据连接有细线表示
单个位的连接用虚线来表示
4.3.3 SEQ的时序
一个时钟变化会引发一个经过组合逻辑的流来执行整个指令。
SEQ的实现包括组合逻辑和两种存储器设备:
时钟寄存器:程序计数器和条件码寄存器
随机访问存储器:寄存器文件、指令存储器和数据存储器
组合逻辑不需要时序或控制。
四个硬件单元需要时序进行明确控制:程序计数器、条件码寄存器、数据存储器和寄存器文件,通过一个时钟信号来控制,它触发将新值装载到寄存器以及将值写到随机访问存储器。即要控制处理器中的时序,只需要寄存器和存储器的时钟控制。
一条重要原则:
处理器从来不需要为了完成一条指令的执行而去读由该指令更新了的状态。
课本上还有具体的示例来说明这条原则。
4.3.4 SEQ阶段的实现
在控制逻辑中必须被显式引用的常数:(常数值都是大写的)
nop指令只是简单的经过各个阶段,除了将PC加1,不进行任何处理。halt指令是处理器状态被设置为HLT,导致处理器停止运行。
1、取指阶段
以PC作为起始地址,从指令存储器中读出六个字节。
根据这些字节,产生出各个指令字段。PC增加模块计算信号valP。
2、译码和写回阶段
指令字段译码,产生寄存器文件使用的四个地址(两个读和两个写)的寄存器标识符。
从寄存器文件中读出的值成为信号valA和valB。两个写回值valE和valM作为写操作的数据。
3、执行阶段
ALU要么为整数运算指令执行操作,要么作为加法器。
根据ALU的值,设置条件码寄存器。检测条件码的值,判断是否该选择分支。
4、访存阶段
数据存储器既可以写,也可以读存储器的值。从存储器读出的值就形成了信号valM。
5、更新PC阶段
根据指令代码和分支标志,从信号valC、valM和valP中选出下一个PC的值。
实验
构建YIS环境
【问题】make报错
书上240页汇编器的输出
学习总结
书中抽象概念部分理解起来比较困难,但是书中很多地方均有具体的示例帮助我们去理解,再者通过每节练习题作为动力去解释概念,也能起到很好的巩固作用。
看完这几节的内容后通过blog再从头到尾梳理一遍的时候,会发现前面的内容为后面的做了铺垫,彼此之间的联系特别紧密,知识都能串起来了。
参考资料
课本,实验楼,其中截图均来自《深入理解计算机系统》pdf版。