Y86-64的顺序实现
将处理组织成阶段
- 各个阶段以及阶段内执行操作的简略描述:
- 取指(fetch):
- 取指阶段从内存读取指令字节,地址为程序PC的值
- 从指令中取出指令指示符字节的两个四位部分:
- icode指令代码
- ifun指令功能
- 它可能取出一个寄存器指示符字节,指明一个或两个寄存器操作数指示符rA和rB
- 还可能取出一个四字节常数字valC
- 并且,按照顺序方式计算当前指令的下一条指令地址valP
- 译码(decode):
- 译码阶段从寄存器文件读入最多两个操作数,得到值valA和/或valB
- 通常,它读入指令rA和rB字段指明的寄存器,不过有些指令是读取寄存器%rsp的
- 执行(execute):
- 算数/逻辑单元(ALU)根据指令指明(ifun)的操作,执行指令指明的操作,计算内存引用的有效地址
- 或增加或减少栈指针
- 得到的值被称为valE(xecute)
- 同时,可能设置条件码
- 访存(memory):访存阶段可以将数据写入内存,或者从内存中读取出数据,读取出的值为valM(emory)
- 写回(write back):写回阶段最多可以写两个结果到寄存器文件
- 更新PC(PC update):将PC设置成下一条指令的地址
- 取指(fetch):
- 在简化的实现中,发生任何异常时,处理器就回停止:执行halt指令或非法指令,或它试图读写非法的地址
- 下面的图中描述了不同指令在各个阶段是如何处理的,表中的每一行都描述了一个信号或存储状态的分配:
- 对指令的跟踪执行
SEQ硬件结构
- 硬件单元与各个处理阶段的相关性: - 取指: - 将程序计数器存储器作为地址,指令内存读取指令的字节,PC增加器计算valP - 译码: - 寄存器文件有两个读端口A和B,从这两个端口同时读取寄存器值valA和valB - 执行: - 执行阶段根据指令的类型,将ALU用于不同的目的 - 对于整数操作,它要执行指令所制定的运算 - 对于其他指令,他会作为一个加法器来计算增加或减少栈指针,或计算有效地址 - 条件码寄存器有三个条件码位,ALU负责计算条件码的新值 - 当执行条件传送指令时,根据条件码和传送条件来计算决定是否更新目标寄存器 - 同样,当执行一条跳转指令时,会根据条件码和跳转类型来计算分值信号Cnd - 访存: - 数据内存读出或写入一个内存字 - 指令和数据内存访问的是内存的相同位置,但是用于不同的目的 - 写回: - 寄存器文件有两个写端口: - 端口E用来写ALU计算出来的值 - 端口M用来写从数据内存中读出的值 - PC更新: - valP:下一条指令的地址 - valC:调用指令或跳转指令指定的目标地址 - valM:从内存读取的返回地址 - 画图惯例: - 白色方框表示时钟寄存器 - 程序计数器PC是SEQ中惟一的时钟寄存器 - 浅蓝色方框表示硬件单元 - 包括内存、ALU等等 - 控制逻辑块使用灰色圆角矩形表示 - 这些块用一组信号源中进行选择,或者用来计算一些布尔函数 - 是后续分析的重点 - 线路的名字在白蒜圆圈中说明 - 宽度为字长的数据连接用中等粗细的线表示 - 宽度为字节或更窄的数据连接用细线表示 - 单个位的连接用虚线来表示
SEQ的时序
- SEQ的实现包括组合逻辑和两种存储器设备:时钟寄存器(程序计数器和条件码寄存器)、随机访问存储器(寄存器文件、指令内存和数据内存)
组合逻辑的变化:只要输入变化,值就通过逻辑门网络传播
- 我们需要通过时钟信号来控制以下四个硬件单元:
- 程序计数器
- 条件码寄存器
- 数据内存
- 寄存器文件
- 原则:从不回读
- 处理器从来不需要为了完成一条指令的执行而去读由该指令更新了的状态
- 依照这一条原则,就可以很好地理解
pop %rsp
和push %rsp
了
SEQ阶段的实现
这一节聚焦于实现SEQ所需要的控制逻辑块的HCL描述 - HCL描述中使用的常数值:
取指阶段
- 一次从内存中读取出10个字节,并分析前两个4位的数
- 根据icode
值,我们可以计算三个一位的信号(虚线)
- instr_valid
:查看当前的指令是否合法
- need_regids
:查看当前指令是否包括一个寄存器指示符字节
need_valC
:查看当前指令是否包括一个常数
- 接下来根据从指令内存中读出的剩余9个字节是寄存器指示符字节和成熟字节的组合编码
- 标号为Align
的硬件单元会处理这些字节,并将其放入寄存器字段和常数字中
- need_regids
为1时:字节1倍分开装入寄存器指示符rA
和rB
中
- 任何只有一个寄存器的操作数指令,寄存器指示值字节的另一个字段都被设为了0xF
- 根据need_valC
来产生一个常数valC
- PC增加器根据当前的PC以及两个信号need_regids
和need_valC
的值来产生信号valP
- 增加器产生值为:pc+1 + need_regids + 8 * need_valC
译码和写回阶段
- 寄存器有四个端口:
- 支持同时进行两个读和两个写
- 每个端口都有一个地址连接和一个数据连接
- 地址连接是一个寄存器ID
- 数据连接是一组64根线路
- 既可以作为寄存器文件的输出字也可以作为输入字
- 两个读端口的地址:srcA
和srcB
- 两个写端口的地址:dstE
和dstM
- 根据指令代码icode
以及寄存器指示值rA
和rB
,可能会根据执行阶段计算出的Cnd条件信号,产生出四个不同的寄存器文件的寄存器ID
- 寄存器ID srcA
表明应该读那个寄存器以产生valA
word srcA = [
icode in {IRRMOVQ, IRMMOVQ, IOPQ, IPUSH} : rA;
icode in {IOPQ, IRET} : RRSP;
1 : RNONE; // Don't need register
]
word srcB = [
icode in {IOPQ, IRMMOVQ, IMRMOVQ} : rB;
icode in {IPUSHQ, IPOPQ, ICALL, IRET} : RRSP;
1 : RNONE;
]
dstE
表明写端口E的目标寄存器,计算出来的值valE
将放在哪里
- 寄存器ID dstM
表明写端口M的目标寄存器,从内存中读取出来的值valM
将放在这里
word dstE = [
icode in {IRRMOVQ} : rB;
icode in {IIRMOVQ, IOPQ} : rB;
icode in {IPUSHQ, IPOPQ, ICALL, IRET} : RRSP;
1 : RNONE;
]
word dstM = [
icode in {IMRMOVQ, IPOPQ} : rA;
1 : RNONE;
]
执行阶段
- ALU:这个单元根据alufun
信号的设置,对输入aluA
和aluB
执行对应的操作
- set_cc
来控制是否该更新条件码寄存器
word alufun = [
icode == IOPQ : ifun;
1 : ALUADD;
]
word aluA = [
icode in {IRRMOVQ, IOPQ} : valA;
icode in {IIRMOVQ, IRMMOVQ, IMRMOVQ} : valC;
icode in {ICALL, IPUSH} : -8;
icode in {IRET, IPOP} : 8;
// Other instructions don't need ALU
]
word aluB = [
icode in {IOPQ, IRMMOVQ, IMRMOVQ, IPOPQ, IPUSHQ, ICALL, IRET} : valB;
icode in {IRRMOVQ, IIRMOVQ} : 0; //这两种情况需要使用0来处理valA或valC + 0
]
word set_cc = [
icode in {IOPQ};
]
访存阶段
- 访存阶段的任务就是读或者写程序数据
word mem_addr = [
icode in {IRMMOVQ, IMRMOVQ, IPOPQ, ICALL} : valE;//需要计算后得到的内存地址
icode in {IPOPQ, IRET} : valA;
]
word mem_data = [
icode in {IRMMOVQ, IPUSHQ} : valA;
icode == ICALL : valP;
]
bool mem_read = icode in {IMRMOVQ, IPOPQ, IRET};
bool mem_write = icode in {IPUSH, ICALL, IRMMOVQ};
word Stat = [
imem_error || dmem_error : SADR;
!instr_valid : SINS;
icode == IHALT : SHLT;
1 : SAOK;
]
更新PC阶段