4.1 Y86-64指令集体系结构
- 定义一个指令集体系结构包括定义各种状态单元、指令集和它们的编码、一组编程规范和异常事件的处理
4.1.1 程序员可见状态:
程序中的每条指令都会读取或修改处理器状态的某个部分
4.1.2 Y86-64指令
- 显式指明源和目的地格式。源可以是立即数(i)、寄存器(r)或内存(m)
- halt指令停止指令的执行,并将状态码设置为HLT
4.1.3 Y86-64 指令编码
每个指令1~10个字节不等,第一个字节高四位是代码部分,低四位是功能部分
(1)寄存器编码
Y86-64中,15个程序寄存器每个对应一个寄存器标识符(0 ~ 0xE)
- 附加的寄存器指示符字节,指定一个或两个寄存器(rA,rB)
- 附加的4字节常数字,能做为irmovq的立即数数据
- 当需要指明不应访问任何寄存器时,就用ID值0xF表示
- 字节编码必须有一个唯一的解释,(合法 或 不合法)
练习4.1 (指令转编码
练习4.2(编码转指令
(2)CISC和RISC的特性区别:
4.1.4 Y86-64异常
注意:
(1)任何访问超过最大的地址(限定值)的地址,都会引起ADR异常
(2)在Y86-64,遇到异常,处理器停止执行指令。更完整的设计,调用异常处理程序(用于处理某种异常)
4.1.5 Y86-64程序
(1)Y86和X86对比
分析:
(1)Y86-64将常数加载到寄存器(算术指令无法直接使用立即数)
(2)从内存读取一个数值并且将其与一个寄存器相加(X86用addq即可,Y86需要先取再加)
(3)Y86优势,subq同时设置条件码(不需要testq),但是必须在循环前设置条件码
(2)Y86程序
分析:
(1)" . "开头的词是 汇编器伪指令(告诉汇编器调整地址,以便在那生成代码或插入数据)
(2).pos 0 告诉汇编器应该从地址0处产生代码(这是所有Y86程序起点)
(3)irmovq stack, %rsp 初始化栈指针
(4).align 8 在8字节边界处对齐
(3)指令集模拟器(YIS)
功能:
模拟Y86-64机器代码程序的执行,而不模拟任何具体处理器实现的行为
运行代码后的结果:
分析:
(1)第一行总结执行以及PC和程序状态的结果值。
(2)模拟器只打印在模拟过程中被改变寄存器或内存中的字
(3)左边是原始值,右边是最终的值
(4)还能看到栈从地址0x200开始向下增长,导致内存0x1f0 ~ 0x1f8发生变化
(5)可执行代码的最大地址为0x090,故出入栈不会破环可执行代码
4.2 逻辑设计和硬件控制语言HCL
4.2.1 逻辑门
逻辑门总是活动的,一旦一个门的输入变化了,在很短的时间内,输出就会相应的变化
4.2.2 组合电路:
将很多的逻辑门组成一个网,就能构建计算块,称为组合电路。
构建组合电路限制:
4.2.3 字级组合电路的位级实现
在HCL中,我们将所有字级的信号都声明为int,不指定字的大小(为了简单)
HCL表达式:
4.2.4 集合关系
很多时候需要将一个信号与许多可能匹配的信号做比较,以此判断是否属于某一类指令代码
数据选择器:
4.2.5 存储器和时钟
时钟是一个周期性的信号,决定什么时候要把新值加载到设备中
- 时钟寄存器,简称寄存器(硬件寄存器)
- 存储单个位或字
- 时钟信号控制寄存器加载输入值
- 边缘触发锁存器的集合
- 随机访问存储器,简称内存(程序寄存器)
- 存储多个字
- 用地址来选择该读或该写哪个字
(1)硬件寄存器:
时钟上升沿加载输入
(2)寄存器文件
分析:
(1)两个读端口A和B,一个写端口W ,这样一个多端口随机访问存储器允许同时进行多个读和写的操作( 图中可以读两个程序寄存器的值,同时更新第三个寄存器的状态
(2)每个端口结构
一个地址输入(地址实际为寄存器标识符 eg. srcA = 3 %rbx)
一个数据输出(读端口)或者 对应该寄存器的输入值(写端口)
(3) 向寄存器文件写入字是由时钟信号控制(时钟上升,将值加载到时钟寄存器
(3)随机访问存储器
(处理器内部)
分析:
(1)读写方式和寄存器文件一致
(2)多少实际系统中,与只读存储器合并为一个具有双端口的存储器(一个端口读指令,一个读或写指令)
4.3 Y86-64的顺序实现
- SEQ处理器:每个时钟周期上,SEQ执行处理一条完整指令所需的所有步骤。
4.3.1 处理组织成阶段
(1)取指
(在地址PC处取出指令指示符字节的两个四位部分,icode(指令代码)或ifun(指令功能)
可能:
1.取出一个寄存器指示符字节(一个或两个寄存器操作数指示符rA和rB)
2.取出一个8字节常数字valC
计算下一条指令地址valP = PC的值 + 已取出指令的长度
(2)译码
从寄存器文件读入最多两个操作数 valA和valB
(通常读入rA,rB指明的寄存器,有些读入%rsp
(3) 执行
1.执行指令指明的操作(ifun)
2.增加或减少栈指针
得到valE,还可以设置条件码
(4) 访存
数据写入内存 or 内存读出数据
(5)写回
更新PC
指令执行具体步骤:
4.3.2 SEQ硬件结构
4.3.3 SEQ的时序
运行顺序:一个时钟变化引发一个组合逻辑的流
组合逻辑:只要输入变化,值就通过逻辑门网络传播
时序逻辑:通过一个时钟信号控制,触发新值装载到寄存器以及将值写入随机访问存储器
控制时序原则(从不回读):
处理器从不需要为了完成一条指令执行而去读该指令更新的状态
eg. push %rsp (rsp - 8) == push rsp的原始值
处理跳转 运算
- 读操作类似组合逻辑,写操作类似时序逻辑(eg. 下一个周期上升沿才更新寄存器,条件码等)
- 用时钟控制状态单元的更新,以及值通过组合逻辑传播
4.3.4 SEQ阶段的实现
SEQ 所需要的控制逻辑块的 HCL 描述中使用的名称和含义:
1.取指阶段:
指令结构(取10个字节):
第一个字节(split) 指令字节:
- icode: 区分不同指令(eg. call, jxx, movq...) (4位
- ifun: 区分具体功能(eg. OPq(add, sub....), JXX(JMP,)....)(4位
剩下的9个字节(Align):
need_regids :
- 1 (寄存器指示符): 分开装入rA, rB
- 2~9 (常数字): valC (constant)
Not_need_regids :
- 1~8 (常数字): valC (constant)
计算信号(根据icode):
instr_valid
: 这个字节是否是一个合法的 Y86-64 指令。need_regids
:这个 Y86-64 指令是否为一条带有寄存器指示值字节的指令。need_valC
:这个 Y86-64 指令是否包括一个常数字。HCL描述
PC增加器:
valP = p + 1 + r + 8i
- p : 当前PC值
- r :need_regids
- i :need_valC
2.译码和写回阶段
端口:
- 地址连接是一个寄存器ID
- 数据连接是一组64根线路
地址输入( 地址为0xf不需要访问):
- 读端口: srcA srcB
- 写端口: dstE dstM
数据连接:
- 读端口: valA valB
- 写端口: valE valM
srcA
的HCL描述:注意:
- (1)写回阶段在访存阶段之后
- (2)两个写端口可能会用到同一个地址,设置M优先级更高实现从不回读原则
3.执行阶段
算术/逻辑单元结构:
ALUA: valA valC -8 +8
ALUB: valB 0
ALU: ALUB - ALUA (ALUB 在前面 = valE
条件码设置:
只希望在执行
OPq
指令时才更新条件码寄存器:执行操作:
对于OPq,使用ifun编码操作
Cnd信号:
cond 硬件根据条件码和功能码确定
用于设置条件传送的dstE,或条件分支的下一个PC逻辑
练习题4.24(设置条件传送):
设置IRRMOVQ条件传送指令
4.访存阶段
访存结构:
读写控制信号(icode):
Mem read: 输出为valM Mem write:读写地址及数据:
- Mem addr: valE valA
- Mem data: valA valP(跳转的PC值)
产生信号:
dmem_error: 数据内存产生
## Determine instruction status word Stat = [ imem_error || dmem_error : SADR; !instr_valid: SINS; icode == IHALT : SHLT; 1 : SAOK; ];
5.更新PC阶段
结构:
SEQ 中最后一个阶段会产生 PC 的新值,依据指令的类型和是否选择分支,新的 PC 可能是
valC
、valM
或valP
。word new_pc [ # Call. Use instruction constant icode == ICALL : valC; # Taken branch. Use instruction constant icode == IJXX && Cnd : valC; # Completion of RET instruction. Use value from stack icode == IRET : valM; # Default: Use incremented PC 1 : valP; ];
4.4 流水线的通用原理
4.4.1 计算流水线
电路延迟单位: 皮秒 ps s
吞吐量单位: GIPS 每秒十亿条指令
300 为时钟周期 + 寄存器延迟20
流水线代价:增加硬件,延迟少量增加(流水线寄存器时间开销)
4.4.2 流水线操作的详细说明
操作说明:
- 减缓时钟不会影响流水线的行为
- 但是时钟过快会影响,值可能无法通过组合逻辑,时钟上升的时候值为非法输入
- A阶段计算的指令I2的值传播到第一个流水线寄存器的输入,但时钟上升才加载到流水线寄存器中,成为寄存器的输出,同时阶段A的输入被设置为发起指令I3的计算
4.4.3 流水线的局限性
1.不一致的划分
不同阶段可能延迟不同,但是运行时钟的速率是由最慢的阶段的延迟限制的
2.流水线过深,收益反而下降
设计处理器流水线时:
- 阶段尽可能多(有15级或更多的)
- 与寄存器交互的阶段尽可能少(减少寄存器延迟)
4.4.4 带反馈的流水线系统
相关指令:
- 数据相关:
- 控制相关:
4.5 Y86-64的流水线实现
4.5.1 SEQ+:重新安排计算阶段
- 更新PC阶段在一个时钟周期开始时执行
- 创建状态寄存器来保存在一条指令执行过程中计算出来的信号(
icode
、Cnd
等) - 在任意给定的周期,它们保存的都是前一个周期中产生的控制信号
4.5.2 插入流水线寄存器
PIPE-硬件结构:
- D保存关于最新取出的指令信息
- E保存关于最新译码的指令和从寄存器文件读出的值信息
- M保存最新执行的指令结果 和 用于处理条件转移及分支目标的信息
- W反馈路径将计算出的值提供寄存器文件写,ret指令后向PC选择逻辑提供返回地址
4.5.3 对信号进行重新排列和标号
信号命令:
m_stat 和 M_stat
流水线寄存器: 大写的前缀"D", "E", "M", "W"
流水线阶段: 小写的前缀"f", "d", "e", "m"
保证使用正确版本的信号:
保存处于一个流水线中指令的所有信息(每条指令关联的状态码与指令一起通过流水线)
4.4.5 预测下一个PC
- (非条件转移指令或非ret指令)均可在取值阶段确定下一条PC地址(valP)
- (条件转移指令)分支预测策略复杂,在设计中总是选择条件分支(valC)
- (ret指令)由于ret指令返回地址是栈顶的字,内容任意,故设计不做预测,暂停至ret通过写回阶段
4.5.5 流水线冒险
指令相关:
- 数据相关:下一条指令会用到这一条指令计算出的结果
- 控制相关:一条指令要确定下一条指令的地址
设计中的冒险
条件码寄存器
不会发生冒险
- 整数操作在执行阶段写这些寄存器
- 条件传送指令在执行阶段读,条件转移指令在访存阶段读(此时整数操作已经更新寄存器)
(1)数据冒险:
程序寄存器:
- 读写在不同阶段进行,不同指令发生不希望的相互作用
eg.第一条irmovq
指令中寄存器 %rdx
的写仍处在写回阶段
第二条irmovq
指令对寄存器 %rax
的写还处在访存阶段 => addq
指令会得到两个错误的操作数
内存:
- 对数据内存读写都在访存阶段,出现读后写冒险,写后读冒险,写后写冒险
(2)控制冒险:
- 定义: 处理器无法根据处于取指阶段的当前指令确定下一条指令的地址
程序计数器:
- 取指前错误预测PC值(更新和读取程序计数器之间的冲突)
避免数据冒险:
(1)暂停
- 定义: 让一组指令阻塞在它们所处的阶段(插入气泡),而允许其他指令继续通过流水线
气泡: 类似动态生成的NOP指令
触发条件:
- 暂停控制逻辑只要发现冒险,就插入一个气泡
缺点:
-
严重降低整体的吞吐量
(2)转发
- 定义: 将结果值直接从一个流水线阶段传到较早阶段的技术
具体应用:
- 周期6中译码阶段逻辑发现写回阶段有rax未进行的写,直接用这个值,而不是从寄存器文件读取
转发技术的硬件支持
- 五个不同的转发源:
运算单元ALU计算产生的值
e_valE
访存阶段从流水线寄存器M中读出的值
M_valE
和 从内存中读出的值m_valM
、写回阶段将要写入寄存器文件中的的值
W_valE
和信号W_valM,
- 两个不同的转发目的(
valA
和valB
)。
(3)加载/使用数据冒险(加载互锁)
-
单纯使用转发的问题:内存读取较晚,存在读取阶段晚于使用该值的阶段(转发不可能转发至过去的时间)
-
加载互锁和转发结合足以处理所有可能类型的数据冒险
方法:转发和暂停结合
- 具体实现:mrmovq在执行阶段,流水线控制逻辑发现译码addq需要读取内存的结果,故将译码阶段暂停一个周期
避免控制冒险
ret指令
- ret沿流水线前进,插入三个气泡,直到ret到达写回阶段,PC选择逻辑设置PC为返回地址
跳转指令
- 分支预测错误,执行的指令恰巧未改变寄存器和内存,可以通过插入气泡取消指令,同时取出正确分支上指令
4.5.6 异常处理
产生来源:
- 由程序执行从内部产生
- 由某个外部信号从外部产生
异常类型:
- halt指令
- 非法指令和功能码组合的指令
- 取指或数据读写试图访问一个非法地址
异常细节问题:
- 有多个指令会引起异常
eg.取指阶段有halt指令,数据内存访存阶段指令数据地址越界
基本原则:由流水线中最深的指令引起的异常,优先级最高(故报告地址越界)
- 执行某个指令,导致一个异常,后因为分支预测错误,取消指令
跳转0xFF,报告非法指令异常,后因为预测错误取消该指令,但我们希望避免出现异常
- 处理器会在不同的阶段更新系统状态的不同部分
pushq绕回非法地址,导致地址异常,同周期下addq处于执行阶段,而addq会将条件码设置为新值(违反异常指令后,所有指令都不能影响系统状态的要求)
处理异常方法:
- 异常会禁止流水线中后面指令更新寄存器及内存,直至异常指令结束
- 取出过后被取消的指令,其引起的异常状态也会被取消
携带指令的异常状态以及所有其他信息通过流水线的简单原则是处理异常的简单和可靠的机制
4.5.7 PIPE各阶段
1.PC选择和取指阶段
PC选择:
- 一条预测错误的分支进入访存阶段,流水线寄存器M(M_valA)中读出valP
- ret指令进入写回阶段,流水线寄存器W(W_valW)中读出返回地址
- 其他情况使用存放在流水线寄存器F(F_predPC)的PC的预测值
PC预测:
- 函数调用或者跳转选择valC,否则选择valP
取指阶段:
指令状态:
将指令状态计算分为两个部分 :
- 测试指令地址越界引起的内存错误
- 发现非法指令或者halt指令
必须推迟到访存阶段才发现非法数据地址
2.译码和写回阶段
译码阶段
合并信号:
- 没有一个指令既需要valP(调用函数和跳转),又需要寄存器端口A读取的值,Sel+Fwd A块将valP和valA合并
- Sel+Fwd A实现valA的转发源逻辑:
转发源优先级:
流水线化的实现应该总是给处于最早流水线阶段中转发源以较高的优先级(eg.检测顺序:执行,访存,写回)
eg.练习题4.32
1 irmovq $5, %rdx 2 irmovq $0x100,%rsp 3 rmmovq %rdx,0(%rsp) 4 popq %rsp 5 rrmovq %rsp,%rax
情况:
- 4,5行出现加载/使用冒险,需要暂停一个周期,rrmovq译码阶段,此时popq为访存阶段
原始逻辑:
- M_dstE和M_dstM都是rsp
valA 的HCL转发从第三和第四种情况反过来:
- M_dstE为增加后的 rsp(rsp + 8) 和 M_dstM都是rsp, M_dstE优先
写回阶段:
状态码:
- 写回阶段有气泡也是正常操作
3.执行阶段:
更新条件码:
- Set_CC以m_stat和W_stat为输入(用于检查导致异常的指令通过流水线阶段)
4.访存阶段:
PIPE没有Data块(用于valP与valA的选择)
4.5.9 性能分析
量化整体性能:
CPI衡量方法
计算PIPE执行一条指令所需要的平均时钟周期数的估计值
- 衡量值是流水线平均吞吐量的倒数
- 单位:时钟周期
计算CPI:
- CPI等于1.0加上处罚项Cb/Ci
处罚项Cb/Ci分解:
- lp(加载处罚 ”load penalty“): 加载/使用冒险造成暂停时插入气泡的平均数
- mp(预测错误分支 “mispredicted branch penalty“): 预测错误取消指令时插入气泡的平均数
- rp(返回处罚 “return penalty”): 由于ret指令造成暂停时插入气泡的平均数
都是某种原因插入气泡总数(Cb的一部分) / 执行指令的总数Ci
4.5.10 增加的处理器特性
1.多周期指令:
不同单元的操作必须同步
简单扩展:
简单扩展执行阶段逻辑的功能,增加一些整数和浮点算术逻辑单元(一条指令会逗留多个时钟周期,导致暂停)
特殊单元:
采用独立于主流水线的特殊硬件功能单元处理较复杂的操作,这个特殊单元执行某个浮点运算或乘法,除法,特殊单元执行该操作,流水线会处理其他指令
2.与存储系统的接口:
- 翻译后备缓冲器(TLB)和高速缓存结合起来使用,大多数时间可以在一个时钟周期读指令或读写数据
- 高速缓存不命中,流水线暂停直至高速缓存能够执行读写操作
- 缺页异常处理:引用存储器位于磁盘存储器(缺页异常),发起磁盘到主存传送的操作,完成后返回原来的程序