第四章 处理器体系结构

第四章 处理器体系结构

用AVX2为浮点数上的操作产生的机器代码风格类似于为整数上的操作产生的代码风格。它们都使用一组寄存器来保存和操作数据值,也都使用这些寄存器来传递函数参数。

4.1 Y86-64 指令集体系结构中

4.1.1 程序员可见的状态

4.1.2 Y86-64指令

Y86-64(Y86-64是假设的指令集)程序中的每条指令都会读取或修改处理器状态的某些部分。这称为程序员可见状态。这里的“程序员”指的是用汇编代码写程序的人或产生机器级代码的编译器。

下面是Y86-64指令的一些细节:

  • x86-64的movq指令分成了4个不同的指令:irmovq、rrmovq、mrmovq和rmmovq,分别指明源和目的的格式。源可以是立即数(i)、寄存器(r)或内存(m)。
  • 有4个整数操作符,addq、subq、andq和xorq。
  • 7个跳转指令。
  • 有6个条件传送指令:cmovle、cmovl、cmove、cmovne、cmovge和cmovg。只有当条件码满足所需要的约束时,才会更新目的寄存器的值。
  • call指令将返回地址入栈,然后跳到目的地址。ret指令从这样的调用中返回。
  • pushq和popq指令实现了入栈和出栈。
  • halt指令停止指令的执行。x86-64的应用程序不允许使用这条指令,因为它会导致整个系统暂停运行。

4.1.3 指令编码

图4-2给出了指令的字节级编码。每条指令需要1~10个字节不等。每条指令的第一个字节表明指令的类型。这个字节分为两个部分,每部分4位:高4位是代码(code)部分,低4位是功能(function)部分。

4.1.4 Y86-64 异常

对Y86-64来说,程序员可见的状态包括状态码Stat,它描述程序执行的总体状态。当遇到这些异常的时候,我们就简单地让处理器停止执行指令。在更完整的设计中,处理器通常会调用一个异常处理程序(exception handler),这个过程被指定用来处理遇到的某种类型的异常。

4.2 逻辑设计和硬件控制语言 HCL

在硬件设计中,用电子电路来计算对位进行运算的函数,以及在各种存储器单元中存储位。大多数现代电路技术都是用信号线上的高电压或低电压来表示不同的位值。要实现一个数字系统需要三个主要的组成部分:计算对位进行操作的函数的组合逻辑、存储位的存储器单元,以及控制存储器单元更新的时钟信号。

现代逻辑设计

曾经的硬件设计者通过描绘示意性的逻辑电路图来进行电路设计(最早是纸和笔,后来是计算机图形终端)。现在,大多数设计都是用硬件描述语言(Hardware Description Language,HDL)来表达。HDL是一种文本表示,看上去和编程语言类似,但是它是用来描述硬件结构而不是程序行为的。

20世纪80年代,研究者开发出了逻辑合成程序,它可以根据HDL的描述来生成有效的电路设计。从手工设计电路到合成生成的转变就好像从写汇编程序到写高级语言程序,再用编译器来产生机器代码的转变一样。

HCL(Hardware Control Language)语言只表达硬件设计的控制部分,只有有限的操作集合,也没有模块化。

4.2.1 逻辑门

逻辑门是数字电路的基本计算单元。它们产生的输出,等于它们输入位值的某个布尔函数。C语言中运算符的逻辑门下面是对应的HCL表达式:AND用&&表示,OR用||表示,而NOT用!表示。

逻辑门总是活动的(active)。一旦一个门的输入变化了,在很短的时间内,输出就会相应地变化。

4.2.2 组合电路和HCL布尔表达式

将很多的逻辑门组合成一个网,就能构建计算块(computational block),称为组合电路(combinational circuits)。构建这些网友几个限制:

  • 每个逻辑门的输入必须连接到下述选项之一:1)一个系统输入(称为主输入),2)某个存储器单元的输出,3)某个逻辑门的输出。
  • 两个或多个逻辑门的输出不能连接在一起。否则它们可能会使线上的信号矛盾,可能会导致一个不合法的电压或电路故障。
  • 这个网必须是无环的。也就是网中不能有路径经过一系列的门形成一个回路,这样的回路会导致该网络计算的函数有歧义。

上图的电路用HCL表示:bool eq = (a && b) || (!a && !b);

上图的组合电路称为多路复用器(multiplexor)。多路复用器根据输入控制信号的值,从一组不同的数据信号中选出一个。控制信号s决定输出a或b,HCL表达式为:bool out = (s && a) || (!s && b);

HCL表达式表明了组合逻辑电路和C语言中逻辑表达式的对应之处。它们都是用布尔操作来对输入进行计算的函数。这两种表达计算的方法之间有以下区别:

  • 组合电路由一系列逻辑门组成,它的属性是输出会持续地响应输入的变化。而C表达式只会在程序执行过程中被遇到时才进行求值。
  • C的逻辑表达式允许参数的任意整数,0表示FASLE,其他任何值都表示TRUE。而逻辑门只对位值0和1进行操作。
  • C的逻辑表达式可能只被部分求值。而组合逻辑没有部分求值这条规则,逻辑门只是简答地响应输入的变化。

4.2.3 字级的组合电路和 HCL.整数表达式

通过将逻辑门组合成大的网,可以构造出能计算更加复杂函数的组合电路。通常,我们设计能对数据*字(word)*进行操作的电路。有一些位级信号,代表一个整数或一些控制模式。

执行字级计算的组合电路根据输入字的各个位,用逻辑门来计算输出字的各个位。例如,上图中,它测试两个64位字A和B是否相等。当且仅当A的每一位和B的相应位相等时,输出才为1。这个电路是用64个图4-10中所示的单个位相等电路实现的。在HCL中,我们将所有字级的信号声明为int,不指定字的大小(为了简单)。字级表达式为:bool Eq = (A == B);。这里的参数A和B是int型的。

上图是字级的多路复用器电路。这个电路根据控制输入为s,产生一个64位的字Out,等于两个输入字A或B的一个。处理器中会用到很多种多路复用器,使得我们能根据某些控制条件,从许多源中选出一个字。

上图用HCL来表示这个电路:

word Out4 = [
	!s1 && !s0 : A; # 00
	!s1 : B; # 01
	!s0 : C; # 10
	1 : D; # 11
];

算术/逻辑单元(ALU)是一种很重要的组合电路。这个电路有三个输入:标号为A和B的两个数据输入,以及一个控制输入。根据控制输入的设置,电路会对输入执行不同的算术或逻辑操作。

4.2.4 集合关系

在处理器设计中,很多时候都需要将一个信号与许多可能匹配的信号做比较,以此检测正在处理的某个指令代码是否属于某一类指令代码。

在上图这个电路中,两位的信号code可以用来控制对4个数据字做选择。

4.2.5 存储器和时钟

组合电路从本质上讲,不存储任何信息。相反,它们只是简单地响应输入信号,产生等于输入的某个函数的输出。按位存储信息的设备可以产生时序电路(sequential circuit),也就是是有状态并且在这个状态上进行计算的系统。存储设备都是由同一个时钟控制的,时钟是一个周期性信号,决定什么时候要把新值加载到设备中。有两类存储器设备:

  • 时钟寄存器(简称寄存器)存储单个位或字。时钟信号控制寄存器加载输入值。
  • 随机访问存储器(简称内存)存储多个字,用地址来选择该读或该写哪个字。随机访问存储器的例子包括:1)处理器的虚拟内存系统,硬件和操作系统软件结合起来使处理器可以在一个很大的地址空间内访问任意的字;2)寄存器文件,在此,寄存器标识符作为地址。在IA32或Y86-64处理器中,寄存器文件有15个程序寄存器(%rax~%r14)。

在硬件和机器级编程中,“寄存器”这个词有细微差别。在硬件中,寄存器直接将它的输入和输出连接到电路的其他部分。在机器级编程中,寄存器代表的是CPU中为数不多的可寻址的字,这里的地址是寄存器ID。这些字通常都存在寄存器文件中。

上图显示一个硬件寄存器以及它是如何工作的。大多数时候,寄存器都保持在稳定状态(用x表示),产生的输出等于它的当前状态。信号沿着寄存器前面的组合逻辑传播,这时,产生了一个新的寄存器输入(用y表示),但只要时钟是低电位的,寄存器的输出就仍然保持不变。当时钟变成高电位时,输入信号就加载到寄存器中,成为下一个状态y,直到下一个时钟上升沿,这个状态就一直是寄存器的新输出。我们的Y86-64寄存器会用时钟寄存器保存程序计数器(PC)、条件代码(CC)和程序状态(Stat)。

下面的图展示了一个典型的寄存器文件:

上图的寄存器文件有两个读端口(A和B)和一个写端口(W)。这样一个多端口随机访问存储器允许同时进行多个读和写操作。电路可以读两个程序寄存器的值,同时更新第三个寄存器的状态。每个端口都有一个地址输入,表明该选择哪个程序寄存器,另外还有一个数据输出或对应该程序寄存器的输入值。读端口有地址输入和数据输出。写端口有地址输入和数据输入。

向寄存器文件写入字是由时钟信号控制的,控制方式类似于将值加载到时钟寄存器。每次时钟上升时,输入valW上的值会被写入输入dstW上的寄存器ID指示的程序寄存器。

处理器有一个随机访问存储器来存储程序数据,如下图所示:

上图是一个随机访问存储器。这个内存有一个地址输入、一个写的数据输入和一个读的数据输出。如果地址超出范围,error信号会设置为1,否则就设置为0。写内存是由时钟控制的。

处理器还包括一个只读存储器,用来读指令。在大多数实际系统中,这两个存储器被合并为一个具有双端口的存储器:一个用来读指令,另一个用来读或者写数据。

4.3 Y86-64的顺序实现

现在我们已有实现Y86-64处理器所需要的部件。每个时钟周期上,SEQ(se-quential顺序的处理器)执行处理一条完整指令所需的所有步骤。不过,这需要一个很长的时钟周期时间,因此时钟周期频率会低到不可接受。我们最终目的是实现一个高效的、流水线化的处理器。

se-quential顺序处理器是一种类型的处理器,其中指令按照顺序执行,一次执行一条指令。这种处理器通常使用单独的控制单元来管理指令流程,以确保指令按预定顺序执行。在顺序处理器中,如果某个指令需要花费很长时间才能执行完毕,则整个处理器将长时间停顿,等待该指令执行完毕。然而,顺序处理器是一种简单的,易于理解和设计的处理器类型,因此仍然在许多系统中使用。

流水线是计算机系统设计的一种重要思想。它的基本原理是把一个复杂的任务分解成若干个相对独立的子任务,并对每个子任务分别进行处理,然后把它们的处理结果依次组装起来,形成最终的处理结果。它利用了并行计算的思想,通过同时处理多个任务,实现对整个系统的加速。这样可以有效利用计算机的资源,提高处理效率。

4.3.1 将处理组织成阶段

通常,处理一条指令包括很多操作。下面是关于各个阶段以及各阶段内执行操作的简略描述:

  • 取指(fetch):取指阶段从内存读取指令字节,地址为程序计数器(PC)的值。从指令中抽取出指令指示符字节的两个四位部分,称为icode(指令代码)和ifun(指令功能)。它可能取出一个寄存器指示符字节,指明一个或两个寄存器操作数指示符rA和rB。它还可能取出一个8字节常数字valC。它按顺序方式计算当前指令的下一条指令valP(等于PC的值加上已取出指令的长度)。
  • 译码(decode):译码阶段从寄存器文件读入最多两个操作数,得到值valA和/或valB。
  • 执行(execute):在执行阶段,算术/逻辑单元(ALU)要么执行指令指明的操作(根据ifun的值),计算内存引用的有效地址,要么增加或减少栈指针。得到的值我们称为valE。在此,也可能设置条件码。
  • 访存(memory):访存阶段可以将数据写入内存,或者从内存读出数据。读出的值为valM。
  • 写回(write back):写回阶段最多可以写两个结果到寄存器文件。
  • 更新PC(PC update):将PC设置成下一条指令的地址。

处理器无限循环,执行这些阶段。在我们简化的实现中,发生任何异常时,处理器就会停止:它执行halt指令或非法指令,或它试图读或者写非法地址。在更完整的设计中,处理器会进入异常处理模式,开始执行由异常的类型决定的特殊代码。

  • valA: 该寄存器中保存的值是从寄存器A读取的值。这个值通常用于指令执行的操作数之一。
  • valB: 该寄存器中保存的值是从寄存器B读取的值。这个值通常用于指令执行的操作数之一。
  • valC: 该寄存器中保存的值是从指令中读取的立即数。这个值通常用于指令执行的操作数之一。
  • valE: 该寄存器中保存的值是指令执行产生的结果。这个值通常用于更新寄存器或存储器。
  • valP: 该寄存器中保存的值是下一条指令的地址。这个值通常在指令执行结束后用于更新程序计数器(PC)。
  • valM: 该寄存器中保存的值是从内存中读取的值。这个值通常用于指令执行的操作数之一或用于更新寄存器。
  • dstE: 该寄存器指定了指令执行的结果应该被写入的目标寄存器的编号。
  • dstB: 该寄存器指定了指令执行的结果应该被写入的目标寄存器的编号。
  • srcA: 该寄存器指定了指令执行的第一个操作数的寄存器编号。
  • srcB: 该寄存器指定了指令执行的第二个操作数的寄存器编号。

4.3.2 SEQ硬件结构

程序计数器(PC)放在寄存器中。在SEQ中,所有硬件单元的处理都在一个时钟周期内完成。上面的图省略了一些小的组合逻辑块,还省略了所有用来操作各个硬件单元以及将相应的值路由到这些单元的控制逻辑。

硬件单元与各个处理阶段相关联:

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

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

  • **执行:**执行阶段会根据指令的类型,将算术/逻辑单元(ALU)用于不同的目的。对整数操作,它要执行指令所指定的运算。对其他指令,它会作为一个加法器来计算增加或减少栈指针,或者计算有效地址,或者只是简单地加0,将一个输入传递到输出。

    条件码寄存器(CC)有三个条件码位。ALU负责计算条件码的新值。当执行条件传送指令时,根据条件码和传送条件来计算决定是否更新目标寄存器。同样,当执行一条跳转指令时,会根据条件码和跳转类型来计算分支信号Cnd。

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

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

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

上图中的画图惯例如下:

  • 白色方框表示时钟寄存器。程序计数器PC是SEQ中唯一的时钟寄存器。
  • 浅蓝色方框表示硬件单元。这包括内存、ALU等等。
  • 控制逻辑块用灰色圆角矩形表示。这些块用来从一组信号源中进行选择,或者用来计算一些布尔函数。
  • 线路的名字在白色圆圈中说明。
  • 宽度为字长的数据连接用中等粗度的线表示。每条这样的线实际上都代表一簇64根线,并列地连在一起,将一个字从硬件的一个部分传送到另一部分。
  • 宽度为字节或更窄的数据连接用细线表示。根据线上要携带的值的类型,每条这样的线实际上都代表一簇4根或8根线。
  • 单个位的连接用虚线表示。这代表芯片上单元与块之间传递的控制值。

4.3.3 SEQ的时序

图4-23显示出,一个时钟变化会引发一个经过组合逻辑的流,来执行整个指令。

SEQ的实现包括组合逻辑和两种存储器设备:时钟寄存器(程序计数器和条件码寄存器),随机访问存储器(寄存器文件、指令内存和数据内存)。组合逻辑不需要任何时序或控制——只要输入变化了,值就通过逻辑门网络传播。由于指令内存只是用来读指令,因此我们可以将这个单元看成是组合逻辑。

剩下4个硬件单元(程序计数器、条件码寄存器、数据内存和寄存器文件)需要对它们的时序进行明确的控制。这些单元通过一个时钟信号来控制,它触发将新值装载到寄存器以及将值写到随机访问存储器。每个时钟周期,程序计数器都会装载新的指令地址。只有在执行整数运算指令时,才会装载条件码寄存器。只有在执行rmmovq、pushq或call指令时,才会写数据内存。寄存器文件的两个写端口允许每个时钟周期更新两个程序寄存器。

要控制处理器中活动的时序,只需要寄存器和内存的时钟控制。即使所有的状态更新实际同时发生,且只在时钟上升开始下一个周期时。之所以能保持这样的等价性,是由于Y86-64指令集的本质。**原则:从不回读。**处理器从来不需要为了完成一条指令的执行而去读由该指令更新了的状态。

上图中不同颜色的代码表明电路信号是如何与正在被执行的不同指令相联系的。在时钟周期3开始的时候(点1),状态单元保持的是第二条irmovq指令更新过的状态,该指令用浅灰色表示。组合逻辑用白色表示,表示它还没来得及对变化了的状态做出反应。时钟周期开始时,地址0x014载入程序计数器中。这样就会取出和处理addq指令。值沿着组合逻辑流动,包括读随机访问存储器。在这个周期末尾(点2),组合逻辑为条件码产生了新的值(000),程序寄存器%rbx的更新值,以及程序计数器的新值(0x016)。在此时,组合逻辑已经根据addq指令被更新了,但是状态还是保持着第二条irmovq指令(用浅灰色表示)设置的值。

当时钟上升开始周期4时(点3),会更新程序计数器、寄存器文件和条件码寄存器,我们用蓝色表示,但是组合逻辑还没有对这些变化做出反应,所以用白色表示。在这个周期内,会取出并执行je指令,在图用深灰色表示,因为条件码ZF为0,所以不会选择分支。在这个周期末尾(点4),程序计数器已经产生了新值0x01f。组合逻辑已经根据je指令(用深灰色表示)被更新过了,但是直到下个周期开始之前,状态还是保持着addq指令(用蓝色表示)设置的值。

如此例所示,用时钟来控制状态单元的更新,以及值通过组合逻辑来传播,足够控制我们SEQ实现中每条指令执行的计算了。每次时钟由低变高时,处理器开始执行一条新指令。

4.3.4 SEQ阶段的实现

nop指令只是简单地经过各个阶段,除了要将PC加1,不进行任何处理。halt指令使得处理器状态被设置为HLT,导致处理器停止运行。

1.取指阶段

取值阶段包括指令内存硬件单元。以PC作为第一个字节(字节0)的地址,这个单元一次从内存读出10个字节。第一个字节被解释成指令字节,分为两个4位的数。然后,标号为“icode”和“ifun”的控制逻辑块计算指令和功能码,或者使之等于从内存读出的值,或者当指令地址不合法使(由信号imem_error指明),使这些值对应于nop指令。根据icode的值,我们可以计算三个一位的信号(用虚线表示):

instr_valid:这个字节对应于一个合法的Y86-64指令吗?
need_regids:这个指令包括一个寄存器指示符字节吗?
need_valC:这个指令包括一个常数字吗?
从指令内存中读出的剩下9个字节是寄存器指示符字节和常数字的组合编码。标号为“Align”的硬件单元会处理这些字节,将它们放入寄存器字段和常数字中。当need_regids为1时,字节1被分开转入寄存器指示符rA和rB中。否则这两个字段会被设置为0xF(RNODE),表明这条指令没有指明寄存器。“Align”单元还产生常数字valC,根据信号need_regids的值,要么根据字节18来产生valC,要么根据字节29来产生。

PC增加器硬件单元根据当前的PC以及两个信号need_regids和need_valC的值来产生valP。

2.译码和写回阶段

上图中,把译码和写回阶段联系在一起是因为它们都要访问寄存器文件。

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

3.执行阶段

执行阶段包括算术/逻辑单元(ALU)。这个单元根据alufun信号的设置,对输入aluA和aluB执行ADD、SUBTRACT、AND或EXCLUSIVE-OR运算。

标号为“cond”的硬件单元会根据条件码和功能码来确定是否进行条件分支或者条件数据传送。它产生信号Cnd,用于设置条件传送的dstE,也用在条件分支的下一个PC逻辑中。

4.访存阶段

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

5.更新PC阶段

SEQ中最后一个阶段会产生程序计数器的新值。

6.SEQ小结

SEQ唯一的问题就是它太慢了。时钟必须非常慢,以使信号能在一个周期内传播所有的阶段。

4.4 流水线的通用原理

在流水线化的系统中,待执行的任务被划分成了若干个独立的阶段。流水线化的一个重要特性是提高了系统的吞吐量(throughput),也就是单位时间内服务的顾客总数,不过它也会轻微地增加延迟(latency),也就是服务一个用户所需要的时间。

4.4.1 计算流水线

在现代逻辑设计中,电路延迟以微微秒或皮秒(picosecond,ps)为单位来计算。

上图是一个很简单的非流水线化的硬件系统的例子。这个实现中,在开始下一条指令之前必须完成前一个。

假设将系统执行的计算分为三个阶段,每个阶段需要100ps。然后在各个阶段之间放上流水线寄存器(pipeline register),这样每条指令都会按照三步经过这个系统,从头到尾需要三个完整的时钟周期。在稳定状态下,三个阶段都应该是活动的,每个时钟周期,一条指令离开系统,一条新的进入。这条流水线的吞吐量是非流水线化的2.67倍,代价是增加了一些硬件以及延迟的少量增加(流水线寄存器的时间开销)。

4.4.2 流水线操作的详细说明

如上图所示,流水线阶段之间的指令转移是由时钟信号来控制的。每个120ps,信号从0上升到1,开始下一组流水线阶段的计算。

上图是240~360之间的电路活动。随着时钟周而复始地上升和下降,不同的指令就会通过流水线的各个阶段,不会相互干扰。

4.4.3 流水线的局限性

1.不一致的划分

上图表明,每个时钟周期,阶段A都会空闲100ps,阶段C会空闲50ps。我们必须将时钟周期设为150+20=170ps。

2.流水线过深,收益反而下降

上图说明了流水线技术的另一个局限性。延迟成了流水线吞吐量的一个制约因素。

4.4.4 带反馈的流水线系统

到目前为止,我们只考虑一种系统,其中传过流水线的对象,相互都是完全独立的,但是,对于Y86-64这样执行机器程序的系统来说,相邻指令之间很可能是相关的,例如:

irmovq $50, %rax 
addq %rax, %rbx 
mrmovq 100(%rbx), %rdx 

在这包含三条指令的序列中,每对相邻的指令之间都有数据相关(data dependency)。

50+20=170ps。

2.流水线过深,收益反而下降

[外链图片转存中…(img-8q44OIfa-1722342163654)]

上图说明了流水线技术的另一个局限性。延迟成了流水线吞吐量的一个制约因素。

4.4.4 带反馈的流水线系统

到目前为止,我们只考虑一种系统,其中传过流水线的对象,相互都是完全独立的,但是,对于Y86-64这样执行机器程序的系统来说,相邻指令之间很可能是相关的,例如:

irmovq $50, %rax 
addq %rax, %rbx 
mrmovq 100(%rbx), %rdx 

在这包含三条指令的序列中,每对相邻的指令之间都有数据相关(data dependency)。

另一种相关是由于指令控制流造成的顺序相关。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值