3.3 Y86-64的顺序实现

发布时间 2023-05-28 16:00:35作者: C~A

将处理组织成阶段

为了实现流水线处理机制,要将指令组织成某个特殊的阶段序列,所有的指令遵循统一的序列,不同阶段放在不同硬件上进行处理。下面是对各阶段的简述。

取指(fetch):取指阶段从内存读取指令字节,地址为程序计数器(PC)的值。从指令中抽取出指令指示符字节的两个四位部分,称为icode(指令代码)和ifun(指令功能)。它可能取出一个寄存器指示符字节,指明一个或两个寄存器操作数指示符rA和rB。它还可能取出一个四字节常数字va1C。它按顺序方式计算当前指令的下一条指令的地址va1P。也就是说,valP等于PC的值加上已取出指令的长度。

译码(decode):译码阶段从寄存器文件读入最多两个操作数,得到值valA和/或valB。通常,它读入指令rA和rB字段指明的寄存器,不过有些指令是读寄存器rsp的。

执行(execute):在执行阶段,算术/逻辑单元(ALU)要么执行指令指明的操作(根据ifun的值),计算内存引用的有效地址,要么增加或减少栈指针。得到的值我们称为valE。在此,也可能设置条件码。对一条条件传送指令来说,这个阶段会检验条件码和传送条件(由ifun给出),如果条件成立,则更新目标寄存器。同样,对一条跳转指令来说,这个阶段会决定是不是应该选择分支。

访存(memory):访存阶段可以将数据写入内存,或者从内存读出数据。读出的值为valM。

写回(write back):写回阶段最多可以写两个结果到寄存器文件。

更新PC(PC update):将PC设置成下一条指令的地址。

 SEQ硬件结构

取指:将程序计数器寄存器作为地址,指令内存读取指令的字节。PC增加器(PC incre-menter)计算valP,即增加了的程序计数器。

译码:寄存器文件有两个读端口A和B,从这两个端口同时读寄存器值valA和valB。

执行:执行阶段会根据指令的类型,将算术/逻辑单元(ALU)用于不同的目的。对整数操作,它要执行指令所指定的运算。对其他指令,它会作为一个加法器来计算增加或减少栈指针,或者计算有效地址,或者只是简单地加0,将一个输入传递到输出。条件码寄存器(CC)有三个条件码位。ALU负责计算条件码的新值。当执行条件传送指令时,根据条件码和传送条件来计算决定是否更新目标寄存器。同样,当执行一条跳转指令时,会根据条件码和跳转类型来计算分支信号Cnd。

访存:在执行访存操作时,数据内存读出或写入一个内存字。指令和数据内存访问的是相同的内存位置,但是用于不同的目的。

写回:寄存器文件有两个写端口。端口E用来写ALU计算出来的值,而端口M用来写从数据内存中读出的值。

PC更新:程序计数器的新值选择自:valP,下一条指令的地址;va1C,调用指令或跳转指令指定的目标地址;va1M,从内存读取的返回地址。

 SEQ的时序

SEQ的实现包括组合逻辑和两种存储器设备:时钟寄存器(程序计数器和条件码寄存器),随机访问存储器(寄存器文件、指令内存和数据内存)。组合逻辑不需要任何时序或控制——只要输入变化了,值就通过逻辑门网络传播。正如提到过的那样,我们也将读随机访问存储器看成和组合逻辑一样的操作,根据地址输入产生输出字。对于较小的存储器来说(例如寄存器文件),这是一个合理的假设,而对于较大的电路来说,可以用特殊的时钟电路来模拟这个效果。由于指令内存只用来读指令,因此我们可以将这个单元看成是组合逻辑。

现在还剩四个硬件单元需要对它们的时序进行明确的控制——程序计数器、条件码寄存器、数据内存和寄存器文件。这些单元通过一个时钟信号来控制,它触发将新值装载到寄存器以及将值写到随机访问存储器。每个时钟周期,程序计数器都会装载新的指令地址。只有在执行整数运算指令时,才会装载条件码寄存器。只有在执行rmmovq、 pushq或cal1指令时,才会写数据内存。寄存器文件的两个写端口允许每个时钟周期更新两个程序寄存器,不过我们可以用特殊的寄存器IDOxF作为端口地址,来表明在此端口不应该执行写操作。

 SEQ的实现

取指阶段:

取指阶段包括指令内存硬件单元。以PC作为第一个字节(字节0)的地址,这个单元一次从内存读出10个字节。第一个字节被解释成指令字节,(标号为“Split”的单元)分为两个4位的数。然后,标号为“icode”和“ifun”的控制逻辑块计算指令和功能码,或者使之等于从内存读出的值,或者当指令地址不合法时(由信号imem error指明),使这些值对应于nop指令。根据icode的值,我们可以计算三个一位的信号(用虚线表示)。从指令内存中读出的剩下9个字节是寄存器指示符字节和常数字的组合编码。标号为“Align”的硬件单元会处理这些字节,将它们放入寄存器字段和常数字中。当被计算出的信号need_regids为1时,字节1被分开装入寄存器指示符rA和rB中。否则,这两个字段会被设为OxF(RNONE),表明这条指令没有指明寄存器。任何只有一个寄存器操作数的指令,寄存器指示值字节的另一个字段都设为OxF(RNONE)。因此,可以将信号rA和rB看成,要么放着我们想要访问的寄存器,要么表明不需要访问任何寄存器。这个标号为“Align”的单元还产生常数字valC。根据信号need_regids的值,要么根据字节1~8来产生va1C,要么根据字节2~9来产生。

PC增加器硬件单元根据当前的PC以及两个信号need_regids和need_valc的值,产生信号valP。对于PC值p、need_regids值r以及need_va1c值i,增加器产生值p+1+r+8i。

 

译码和写回阶段:

寄存器文件有四个端口。它支持同时进行两个读(在端口AB上)和两个写(在端口E和M上)。每个端口都有一个地址连接和一个数据连接,地址连接是一个寄存器ID,而数据连接是一组64根线路,既可以作为寄存器文件的输出字(对读端口来说),也可以作为它的输入字(对写端口来说)。两个读端口的地址输入为srcA和srcB,而两个写端口的地址输入为dstE和dstM。如果某个地址端口上的值为特殊标识符OxF(RNONE),则表明不需要访问寄存器。

根据指令代码icode以及寄存器指示值rA和rB,可能还会根据执行阶段计算出的Cnd条件信号。

执行阶段:

 执行阶段包括算术/逻辑单元(ALU)。这个单元根据alufun信号的设置,对输入aluA和aluB执行ADD、SUBTRACT、AND或EXCLUSIVE-OR运算。如图4-29所示,这些数据和控制信号是由三个控制块产生的。ALU的输出就是valE信号。执行阶段的第一步就是每条指令的ALU计算。列出的操作数 aluB在前面,后面是aluA,这样是为了保证subq指令是va1B减去valA。可以看到,根据指令的类型,alua的值可以是valA、valC,或者是一8或+8。

 访存阶段:

访存阶段的任务就是读或者写程序数据。如图所示,两个控制块产生内存地址和内存输入数据(为写操作)的值。另外两个块产生表明应该执行读操作还是写操作的控制信号。当执行读操作时,数据内存产生值valM。

访存阶段最后的功能是根据取值阶段产生的icode、imem_error、instr_valid值以及数据内存产生的dmem error信号,从指令执行的结果来计算状态码Stat。

 

更新PC阶段:

SEQ中最后一个阶段会产生程序计数器的新值。如图 最后步骤所示,依据指令的类型和是否要选择分支,新的PC可能是valC、valM或valP。