深入理解计算机系统
Y86
Y86介绍
电路逻辑
可以把上图长方形看成寄存器的一个单元,Y看成是数据输入,X是输出,时钟信号控制读写。当时钟信号是上升沿时,“写”有效,否则即为“读”
上图为一个寄存器文件,valA和valB相当于X,valW相当于Y,时钟控制读写。
src和dst表示地址,分别决定读和写的目标单元。
显然,读无需Y,写无需X。当对同一单元同时读写时,X是会从旧值变新值。
SEQ
Sequential (顺序执行)
SEQ的实现包括
- 组合逻辑
- 时钟寄存器:PC,CC
- 随机访问存储器:寄存器文件(esp,eax等),指令存储器,数据存储器
加粗表示需要时钟信号,如上面电路逻辑中提到的寄存器文件
下面以push为例说明SEQ
Fetch 指令存储器只读,取指无需一个时钟周期
Decode 译码没涉及到寄存器文件的写,故无需一个时钟周期
Execute 执行减4也无需一个时钟周期,但进行整数运算会涉及CC的写,这需要一个时钟周期。
Memory 访存数据存储器需要一个时钟周期
Write back写回寄存器文件需要一个时钟周期,可与Memory共用一个时钟周期
PC update 更新PC要一个时钟周期
push先算读出esp并计算新值,但未马上更新esp,而是先把写入内存再更新esp(二者可并行)
pop同理。
为什么valE只给address不给data?我觉得地址偏移用的比较多,而把运算结果写入内存用的更少。
而更新后不读,我想是为了指令流水。
取指
instr_valid = icode in {} #指令合法
need_regids = icode in {} #需要寄存器
need_valC = icode in {} #包含常数
valP是根据need_regids和need_valC来对决定对PC+1,+2,+5,+6
+1 都不要
+2 只需寄存器
+5 只需常数
+6 全要
译码
两读两写
push时要提供address和data,故AB都得用上,
pop是只要address,但为了保持队列,A也给address
E是ALU后write back时的目标,比如esp+4
M是访存后write back时的目标
可以看出寄存器间的mov是通过ALU来来实现的
pop %esp需要E和M,且译码时就定了。因为访存为组合逻辑,不需时钟周期,
故valE和valM可看成同时到位,故M优先级应高于E
执行
访存
更新PC
常用指令
可以看出rrmovl是opl(rB=0)的特例
下图是上图特例
可以看出,数据存储器的地址关联BE组合,读关联M,写关联A
流水线
SEQ+
为了实现流水线,将SEQ的更新PC去掉,在取指前根据前一条指令的输出更新PC,逻辑不变。
PIPE
译码时可以通过暂停和转发来解决数据冒险
在第7周期,发现第5条指令执行阶段的目的寄存器(%eax)与第6条指令译码阶段的(%eax)冲突,
故在硬件上,第8周期FD保持上一周期状态,E空白,MW照常。
PC选择和取指
如果是JXX先不跳,而是先执行下条,等预测错了再跳,那么代码变动如下
从PIPE完整图中可以看出传播轨迹
跳转地址 -> D_valC -> E_valC -> M_valE
下条地址 -> D_valP -> E_valA -> M_valA
显然,JXX包含jmp,jmp是无条件直接跳。
如果像课后作业4.54所述,jmp直接跳,其他JXX先执行下条。代码变动如下
jmp不涉及预测分支错误,因为它不连预测的机会都没有。
应该在流水线控制逻辑中从JXX踢除jmp
即用IJXX && f_ifun != 0 替换 IJXX,像f_pc中那样。需要注意的是e_Cnd是指示的是否满足跳转条件,而非预测的正确性
译码和写回
- 在SEQ中,寄存器文件 中的dstE在D时就是等于d_dstE,就是说译码时就知道要写回到哪个寄存器,
而在PIPE中d_dstE是先往上传,传到W时回传寄存器文件dstE,这样dstE只有在W阶段才占有值, W前这段时间可以给其他指令,有利于流水。 - 在下面的代码中从d_valAM取值优先级可以看出M段M_dstM优先级大于M_valE,呼应了前面所说的pop %esp
- 发现在SEQ中D_valP直接给Data,而在PIPE被整合到A中, 因为A和P不会产生冲突。call需要在M时往数据数据俱器中写入P,跳转指令在M时需要回传M_valA,二者都用不到d_valA。
这样D后的流水寄存器就不用给P留位置
执行
可以看出执行阶段转发给译码阶段的并不是E_dstE,而是e_distE。 因为条件转送要根据e_con和icode来判断是否进行传送
直接转发E_dstE的话,下面结果为0x444
访存
写回
异常
某条指令发生异常后,后面的指令并不会马上停止,只是不能更新CC,受m_stat和W_stat控制。见PIPE的执行阶段图
并且在M阶段插入bubble防止写存,直到异常指令到达最后阶段,
程序停止执行,程序状态为异常指令W寄存器中的stat,后面应该转而执行异常处理程序。
如果指令执行一半发现取错了(分支预测错误),该指令所出现的异常信息会被清空
控制逻辑
RET
执行ret时,后面的指令可以取出,但只取不译,直到访存结束
分支预测
执行jXX时,后面的指令可以取出并运行,但在第5周期要视情况是否继续,这由分支逻辑决定是否插入bubble。
若是执行另一分支,转发逻辑会使用PC取M_valA,这在PIPE的译码阶段定义过f_pc。
跳转指令可以有不同跳转方式,但大同小异。
加载/使用 冒险
执行pop或mrmov时,会使下条指令暂停一周期,这在PIPE提到过。
总结
jXX+ret依ret。因为预测正确的话也要依ret规则,不正确的话就随意,反正最后也要换另一分支
pop+ret依pop。因为假如依ret把D给bubble,则ret自己经过D时就会被buble,故推迟1周期等到pop冒险结束