◆ 本章学习目标一台计算机的性能由三个关键因素决定:指令数目、时钟周期长度和每条指令所需时钟周期数。编译器和指令集决定了一个程序所需的指令数目。而处理器的实现方式则决定了时钟周期长度和 CPI。为 MIPS 指令集的两种不同实现方式分别建立数据通路和控制单元。本章包含了实现一个处理器所需的原理与技术知识。先从一个高度抽象和简化的概述开始,再建立数据通路并进一步构建一个简单的处理器以实现像 MIPS 这样的指令集。通过对本章的教学,掌握单周期处理机数据通路和控制单元的构成,掌握 MIPS 指令集的不同实现方式。掌握微程序的概念和设计的基本方法。
一、一个基本的 MIPS 实现将要设计的实现方式包含 MIPS 指令集的一个核心子集:·存储器访问指令:取字(lw)和存储字(sw)。·算术逻辑指令:加法(add)、减法(sub)、与运算(AND)、或运算(OR)和小于则设置(slt)。·分支指令:相等则分支(beq)和跳转(j),我们放到最后实现。这个子集没有包含所有的整数指令(如不包含乘、除指令和移位指令等),也没有包含任何浮点指令。然而,使用该子集可以说明在建立数据通路和控制通路时的关键原理,并可以在此基础之上实现其他指令。在学习此实现方式时,我们将看到指令集如何决定实现方式的多个方面,以及实现策略如何影响计算机的时钟速度和 CPI。用于实现 MIPS 子集的大多数概念与很多计算机的基本构造思想是一致的,包括从高性能服务器到通用微处理器、嵌入式处理器等各式各样的计算机。二、实现方式概述在前面章节中,我们学习了 MIPS 的核心指令,包括整数算术逻辑指令、存储访问指令及分支指令。这些指令的实现过程大致相同,而与具体的指令类型无关。实现每条指令的前两步是一样的:1)程序计数器(PC)指向指令所在的存储单元,并从中取出指令。2)通过指令字段内容,选择读取一个或两个寄存器。对于取字指令,只需读取,一个寄存器,而其他大多数指令要求读取两个寄存器。这两步之后,为完成指令而进行的步骤则取决于具体的指令类型。幸运的是,对三种指令类型(存储访问、算术逻辑、分支)的每一种而言,其动作大致相同,与具体指令无关。 MIPS 指令集的简洁和规整使许多指令的执行很相似,因而简化了实现过程。例如,除跳转指令外的所有指令在读取寄存器后,都要使用算术逻辑单元(ALU)。存储访问指令用 ALU 计算地址,算术逻辑指令用 ALU 执行运算,分支指令用 ALU 进行比较。在使用 ALU 之后,完成不同指令所需的动作就有所不同了。存储访问指令需要访问内存以便读取和存储数据。算术逻辑指令或装载指令将来自 ALU 或存储器的数据写入寄存器。对分支指今,我们需要基于比较的结果决定是否改变下一条指令地址;如果不修改下一条指令地址,则下一条指令地址默认是当前指令地址+4。
MIPS 子集实现的抽象视图,描述了主要功能单元及其连接。在图中的许多位置,某个单元的数据可能来自于两个不同的单元。例如,写入 PC 的值可能来自两个加法器中的一个,写入寄存器堆的数据可能来自 ALU 或数据存储器,ALU 的第二个输入可能来自寄存器或指令中的立即数字段。图中的许多单元的控制依赖于当前执行指令的类型。例如,存取指令读写数据存储器,装载指令和算术逻辑指令写入寄存器堆。很显然 ALU 根据不同的指令执行不同的操作,这些操作都由控制信号确定,而控制信号是由指令的某些字段所决定的。所有指令都开始于使用程序计数器获得的指令存储器中指令的地址。在取到指令后,指令所使用的寄存器操作数由指令中的对应字段决定。在取到寄存器操作数之后,可以用来计算存储器地址(对于存取类指令),或者计算算术运算结果(对于整数算术逻辑类指令)或者进行比较(对于分支类指令)。如果是算术逻辑类指令,ALU 的结果必须写回寄存器;如果是存取类指令,ALU 的结果可作为读写存储器的地址。ALU 或存储器的结果可写回寄存器堆。分支操作需要使用 ALU 的输出来决定下一个指令地址,下一个指令地址可能来自 ALU(在其中 PC 值与分支偏移量相加).也可能来自加法器(当前 PC 值加 4)。 最上面的多选器控制写入 PC 的值(PC+4 或分支目的地址),该多选器由一个门控制,该门将 ALU 的零输出与一个指示是否为分支指令的信号相“与”。中间输出到寄存器堆的多选器,用来选择将被写入寄存器堆中的是 ALU 的输出(算术逻辑指令时)还是数据存储器的输出(装载指令时)。最下面的多选器决定 ALU 的第二个输入是来自寄存器堆(算术逻辑指令或分支指令时)还是指令的偏移量字段(存取指令时)。
4.1.2 逻辑设计惯例MIPS 实现中的数据通路功能部件包括两种不同的逻辑单元:处理数据值的单元和存储状态的单元。处理数据值的单元都是组合单元,它们的输出只取决于当前的输入。当输入相同时,组合单元产生的输出也相同。前面详细论述的 ALU 就是组合单元。因为其没有内部存储功能,当给定一组输入时总是产生同样的输出。设计中的其他单元不是组合的,而是包含状态的。如果一个单元带有内部存储功能,它就包含状态,称之为状态单元。这些状态单元完全描述了计算机的状态。图 4.1.1 中的指令存储器、数据存储器和寄存器都是状态单元。一个状态单元至少有两个输入和一个输出。两个必要的输人为要写入单元的数据值和决定何时写入的时钟信号。状态单元的输出提供了在前一个时钟信号写入单元的数据值。例如,逻辑上最简单的一种状态单元是 D 触发器,它有两个输入(一个数据值和一个时钟)和一个输出。除了触发器,MIPS 的实现中还用了另外两种状态单元:存储器和寄存器,时钟用于决定状态单元何时被写入。状态单元随时可读。包含状态的逻辑部件又被称为时序逻辑电路,因为它们的输出由输入和内部状态共同决定。时钟方法规定了信号可以读出和写入的时间。我们假定采用边沿触发的时钟方法,即在时序逻辑单元中存储的所有值都只允许在时钟跳变的边沿时改变。因为只有状态单元能存储数据值,所有的组合逻辑都必须从状态单元集合接收输入,并将输出写入状态单元集合中。
4.1.2 逻辑设计惯例
图 4.1.2-1 描述了一个组合逻辑单元及与其相连的两个状态单元。组合逻辑单元的操作在一个时钟周期内完成:所有信号在一个时钟周期内从状态单元 1 经组合逻辑到达状态单元 2,信号到达状态单元 2 所需的时间决定了时钟周期的长度。为简单起见,若某状态单元在每个有效的时钟边沿都进行写入操作,则可忽略写控制信号。相反,若某状态单元不是每个周期都进行修改,那么它就需要一个写控制信号。写控制信号和时钟都是输入信号,只有当写控制信号有效并且时钟边沿到来时,状态单元才改变状态。使用如图 4.1.2-2 所示的一种边沿触发的方法可以在一个时钟周期内读出一个寄存器的值并使之经过一些组合逻辑,同时将别的值写入该寄存器。选择在时钟的上升沿还是下降沿进行写操作无关紧要,因为组合逻辑的输入只有在所规定的时钟边沿才可能发生变化。这种边沿触发时钟方法在一个时钟周期内不会出现反馈,
4.2.1 数据通路的建立一、数据通路部件设计数据通路合理的方法是首先分析执行每种 MIPS 指令时需要哪些主要部件。下面先来看看每条指令需要什么数据通路部件。数据通路部件是一个用来操作或保存处理嚣中数据的单元。在 MIPS 实现中,数据通路部件包括指令存储器、数据存储器、寄存器堆、ALU 和加法器。需要的第一个部件:一个存储单元,它用于存储程序的指令,并在给定地址时提供指令。需要的第二个部件:程序计数器(PC),用于保存当前指令的地址。需要的第三个部件:一个加法器增加 PC 的值以指向下条指令的地址。这个加法器是一个组合单元。两个状态单元分别是指令存储器和程序计数器。因为数据通路没有写指令,所以指令存储器只提供读访问。因为指令存储器是只读的,我们将它视为组合逻辑:任意时刻的输出都反映了输入地址处的内容,而不需要读控制信号。程序计数器是一个 32 位的寄存器,它在每个时钟周期末都会被写入,所以不需要写控制信号。加法器采用只进行加法的 ALU。它将输入的两个 32 位数相加,将结果输出。
在数据通路中的另一主要部件是寄存器堆。寄存器堆由许多寄存器组成,这些寄存器能通过寄存器号进行读写存取。利用对每个读写口进行译码,并由触发器组成寄存器阵列,就可以实现寄存器堆。因为读取寄存器不会更改其内容,故而只需提供寄存器号就可以读出该寄存器的内容。而对于往寄存器写数据,则需三个输入量:寄存器号、待写入数据以及控制写操作的时钟信号。它的结构请参照图 4.2.1-2 读端口采用一对多路复用器加以确定,其中每个复用器的带宽等于寄存器堆的位数。图 4.2.1-3 展示了字宽为 32 位,带有两个读端口的寄存器堆的逻辑图。由于我们只能改变目的寄存器的内容,所以实现读端口操作就显得略为复杂。对于写操作,通过译码器产生信号以决定选取往哪个寄存器写入,就算是实现了对寄存器的写操作。触发器机制下输出状态量的变化只能在时钟触发沿时刻才有效。
寄存器堆的读输出总是对应于读寄存器号,不需要其他的控制信号。但是写寄存器必须明确使能与控制信号。注意写操作是边沿触发的,所以所有的写操作的输入(要写的内容、寄存器号、写控制信号)必须在时钟边沿有效。因为寄存器堆的写入是边沿触发的,故可以在同一时钟周期内读出和写入同一寄存器。读操作将读出以前写入的内容,而写入的内容在下一时钟周期才可读。寄存器号的输入都是 5 位,数据线为 32 位。要执行任何一条指令,首先要从存储单元中将指令取出。为准备执行下一条指令,也必须增加程序计数器使其指向下一条指令,即向后移动 4 个字节。取指令数据通路部分
二、R 型指令R 型指令或算术逻辑指令(因为它们执行算术或逻辑运算)这类指令读两个寄存器,对它们的内容进行 ALU 操作,再写回结果。这个指令集合包括 add,sub,AND,OR 和 slt 指令。此类指令的典型形式是 add $t1,$t2,$t3,它将读取$t2 和$t3,并将结果写回$t1。由于 R 型指令有 3 个寄存器操作数,每条指令都要从寄存器堆读出两个数据字,再写入一个数据字。为读出一个数据字,寄存器堆需要输入一个要读的寄存器号和一个从寄存器堆读出结果的输出指示。为写入一个数据字,寄存器堆要有两个输入:一个提供要写的寄存器号,另一个提供要写的数据。寄存器堆总是根据输入的寄存器号输出相应的寄存器内容,而写操作由写控制信号控制,在写操作发生的时钟边沿,写控制信号必须是有效的。这样,我们一共需要 4 个输入(3 个寄存器号和 1 个数据)和两个输出(两个数据)。输入的寄存器号为 5 位,可指示 32 个寄存器中的某一个,而一条数据输入总线和两条数据输出总线宽度均为 32 位。
三、MIPS 的存取指令MIPS 的存取指令其一般形式为: lw $t1,offset_ value($t2) 或者 sw $t1,offset_ value($t2)在这类指令中,通过将基址寄存器$t2 的内容与指令中的 16 位带符号偏移地址相加,得到存储器地址。如果是存储指令,要从寄存器$t1 中读出要存储的数据;如果是装载指令,则要将从存储器中读出的数据存入指定的寄存器$t1 中。所以,寄存器堆和 ALU 都会用到。另外,还需要一个单元将 16 位的偏移地址符号扩展为 32 位的带符号值,以及一个保存读出或写入数据的存储单元。数据存储单元在存储指令时被写入,所以它有读、写控制信号,地址输入和写入存储器的数据输入。图 4.2.1-7 中给出了这两个单元。
数据存储器单元是一个状态单元,两个输入为地址和所写数据。一个输出为读出的数据。读、写控制信号都是独立的,尽管任意时钟只能激活其中一个。不像寄存器,存储器单元需要一个读控制信号,因为读一个无效地址可能会出问题,符号扩展单元有一个 16 位的输入,符号扩展为 32 位后输出。
四、分支指令beq 指令有 3 个操作数,其中两个为寄存器,用于比较是否相等,另一个是 16 位偏移量,用于计算相对于分支指令所在地址的分支目标地址。它的指令格式为: beq $t1,$t2,offset 为了实现该指令,我们必须将 PC 值与符号扩展后的指令偏移量字段相加以得到分支目标地址。分支指令的定义中有两个需要注意的地方:·指令集规定计算分支地址时使用的基地址,是分支指令的下一条指令的地址。原因是我们在取指通路中计算了 PC + 4(下一条指令的地址),用这个值作为计算分支目标地址时的基地址比较容易实现。·系统结构还规定偏移量左移 2 位以指示以字为单位的偏移量,这样偏移量的有效范围就扩大了 4 倍。为了处理后面这种情况,我们需要把偏移量左移 2 位。除了计算分支目标地址,还必须确定是顺序执行下一条指令,还是去执行分支目标地址处的指令。当分支条件为真(例如,操作数相等)时,分支目标地址成为新的 PC,我们就说分支发生了。若操作数不等,自增后的 PC 将取代当前 PC(就像其他普通指令一样),这时就说分支未发生。所以,分支数据通路需要进行两个操作:计算分支目标地址和比较操作数。图 4.2.1-8 为分支数据通路。为计箅分支目标地址,分支目标通路包含了一个符号扩展单元和一个加法器。为了进行比较,要由 ALU 完成。因为 ALU 提供一个指示结果是否为 0 的输出信号,故可以把两个寄存器数作为输入,并将 ALU 设置为减法。若 ALU 输出的零信号有效,则可知两操作数相等。尽管零输出信号始终指示结果是否为 0,但我们只用它来实现分支时的等值测试。
跳转指令将偏移地址的低 26 位左移两位后,以之代替 PC 的低 28 位。移位通过给偏移量后面加上两个 0 实现。标有“左移两位”的单元只是输入到输出之间一条简单的数据通路,它给符号扩展后的偏移量字段的低位加上两个 0(二进制)。因为“移动”的距离是固定的,所以并不需要真正的移位电路。
四、创建一个简单的数据通路我们已经讨论了不同指令类型所需要的数据通路单元,可以把它们连在一起并加上控制来完成一个最简单的 MIPS 子集实现方案。这个最简单的数据通路每个时钟周期执行一条指令。这意味着每条指令执行过程中任何数据通路单元都只能被用一次,如果需要使用多次则必须将该数据通路单元复制多份。所以我们除了需要一个指令存储器外,还需要一个数据存储器。尽管有的功能单元需要复制,但在执行不同指令时,很多功能单元也可以被共享。为了在两种不同类型的指令间共享数据通路单元,我们需要让功能单元有多个输入,而使用多选器和控制信号来从多个输入中进行选择。算术逻辑指令(或 R 型指令)的数据通路与存取指令的数据通路很相似。它们的主要区别为:·算术逻辑指令使用 ALU,并且其输入来自两个寄存器。存储指令也使用 ALU 来进行地址计算,但 ALU 的第二个输入是对指令中 16 位偏移地址进行符号扩展后的值。·存入目标寄存器中的值来自于 ALU(对 R 型指令而言)或者存储器(对装载操作而言)。为了只用一个 ALU 和一个寄存器堆来创建一个数据通路,ALU 的第二个输入和要存入寄存器堆的数据都需要两个不同的来源。所以,要在 ALU 的输入和寄存器堆的输入数据处各加入一个多选器。图 4.2.1-9 给出了合并后的数据通路。
现在,加上图 4.2.1-5 的取指数据通路、图 4.2.1-8 的分支数据通路、图 4.2.1-9 的 R 型指令和存储指令数据通路,我们可以把所有部件合并在一起建立一个简单的 MIPS 体系结构数据通路,如图 4.2.1-10 所示。由于分支指令用主 ALU 对寄存器操作数进行比较,所以还需要图 4.2.1-8 中的加法器完成分支目标地址的计算。此外还增加了一个多选器,用于选择是将顺序的指令地址(PC+4)还是分支目标地址写入 PC。在完成这个简单的数据通路后,可以加上控制单元。控制单元必须能够接收输入,能够产生每个状态单元的写信号、每个多选器的选择信号和 ALU 的控制信号。由于 ALU 的控制比较特殊,因此最好先设计 ALU,随后再设计控制单元的其他部分。
4.2.2 数据通路的操作我们用上一节的数据通路和增加一个简单的控制单元来实现一个 MIPS 体系结构。这一结构实现了取字(lw)、存储字(Sw)、相等则分支(beq)和算术逻辑指令加法(add)、减法(sub)、与运算(AND)、或运算(OR)和小于则设置( set on Less than)slt,后面我们还将实现跳转指令(j)。一、ALU 控制ALU 有 4 个控制输入,所以 16 种可能的输入组合中只有 6 种可能用于这个子集。表 4.2.2-1 中的 MIPS ALU 显示了下面 6 种组合:根据指令的不同,ALU 将实现前五种功能中的某一种。(MIPS 指令集中的其他部分需要或非。)对于取字和存字指令,ALU 进行加法运算获取存储地址。对于 R 型指令,根据指令低六位的功能字段(funct)。确定 ALU 执行五种操作中的一种(与、或、减、加、小于则置 1)。对相等分支指令,ALU 做减法操作。
使用一个小的控制单元即可生成 4 位的 ALL 控制信号,其输入为指令的 funct 字段和 2 位的 ALUOp 字段。 ALUOp 指明要进行的操作是存取指令需要的加法(00)、beq 需要的减法(01),还是由指令的 funct 字段决定(10)。该 ALU 控制单元输出 4 位信号,即前面介绍的 4 位控制信号,直接对 ALU 进行控制。
图 4.2.2-1 ALU 的控制信号操作码在第一列,决定了 ALUOp 的各位设置。所有的编码以二进制给出。注意当 ALUOp 码为 00 或 01 时,期望的 ALU 动作不依赖于功能码字段;这时,称我们“不关心”功能码的值,功能字段写为 xxxxxx。当 ALUOp 值为 10 时,功能码用于设置 ALU 的控制输入。这种多层解码的手法,即主控制单元生成 ALUOp 位作为 ALU 控制的输入,ALU 控制再生成真正控制 ALU 单元的信号,是一种常用的实现技术。使用多层控制可以减小主控制单元的规模。使用多个小控制单元还可能提高控制单元的速度。这种优化是很重要的,因为控制单元的性能通常很关键。把 2 位的 ALUOp 字段和 6 位的功能字段映射为 3 位的 ALU 操作拉制位,有多种不同方法。因为功能字段的 64 种可能取值中只有很小一部分有意义,并且仅当 ALUOp 位取值为 10 时才使用功能字段,可以用一个小逻辑单元去识别可能取的值,以生成正确的 ALU 控制位。为设计这个逻辑单元,有必要为 ALUQp 位和功能码字段的有意义的组合生成一张真值表,如图 4.2.2-2 所示;这个真值表说明了 3 位的 ALU 控制信号怎样根据这两个输入字段得到。由于完整的真值表很大(28= 256 项),并且对其中许多种输入组合的 ALU 控制的值都不关心,只列出了使 ALU 控制有确定取值的真值表项。
图 4.2.2-2 三个 ALU 控制位(称为 Operation)的真值表输人为 ALUOp 和功能码字段。在此只列出了 ALU 控制有效的项,也包括一些无关项:比如,ALUOp 不用 11 编码,则真值表可包含 1X 和 X1 项,而不是 10 和 01 项:同样,当使用功能字段时,指令的前两位(F4 和 F5)总是 10,所以它们是无关项,在真值表中以 xx 代替。由于在许多情况下对某些输入的取值无关,为了使真值表简练,也列出无关项。真值表中的无关项(通过在输入列以“X“表示)表明,输出与该列对应的输入取值无关。例如,ALUOp 位取 00 时,上图的第一行,无论功能码取何值,ALU 控制总是被设为 010。这时,真值表中此行的功能码就为无关项。真值表建好之后,可以进行优化并转化成门电路。这是一个完全机械的过程。如图 4.2.2-3
图 4.2.2-3 ALU 控制码的生成二、主控制单元的设计在设计完以功能码和 2 位信号作为控制输入的 ALU 后,现在来看看控制的其他部分。开始之前,首先看看一条指令的各个字段和如图 4.2.1-10 所示的数据通路所需的控制线路。为了理解怎样将指令的各个字段与数据通路相连,需要复习一下三种指令类型的格式:R-型指令、分支指令和存取指令。它们的格式如图 4.2.2-4 所示。
图 4.2.2-4 三种指令的指令格式遵循以下几条指令格式的主要规则:· op 字段,亦称操作码,总是为 31:26 位。用 Op[5:0]来表示。·对于 R-型指令、分支指令和存数指令,要读取的两个寄存器为 rs 和 rt 字段,分别为 25: 21 位和 20:16 位。·存取数指令的基址寄存器字段在 25:21 位(rs 字段)。·等值分支指令、存数/取数指令的 16 位偏移量在 15:0 位。·有两个地方存放目标寄存器。对存数指令为 20:16 位(rt 字段),对 R-型指令为 15:11 位 (rd 字段)。所以需要一个多路复用器,以指示要写的寄存器序号在指令的哪个字段中。根据这些信息,可以给简单的数据通路加上指令标记和一个额外的多路复用器(给寄存器堆的写寄存器号输入)。图 4.2.2-5 加上了这些,以及 ALU 控制模块、状态单元的写信号、数据存储器的读信号和多路复用器的控制信号。由于所有多路复用器都为双输入,它们各需要一根控制线。
图 4.2.2-5 数据通路上增加了所有必需的多选器并标识出了所有的控制信号 还增加了 ALU 控制单元。PC 不需要写控制,因为它在每个时钟周期末都被写入一次。分支控制逻辑决定给 PC 自增还是写入分支目标地址。图 4.2.2-5 给出了 7 个 1 位控制信号和 2 位 ALUOp 控制信号。我们已经说明了 ALUOp 控制信号如何工作,在继续说明指令执行过程中如何设置这些控制信号之前,最好非正式地定义一下其他 7 条控制信号如何工作。图 4.2.2-6 说明了这 7 个控制信号的功能。
图 4.2.2-6 7 个控制信号的功能了解了每个控制信号的功能之后,再来看看它们如何设置。除 PCSrc 控制信号外,所有控制信号都可由控制单元只根据指令的操作码来确定。而 PCSrc 信号有效的条件是指令为相等则分支(由控制单元确定),且用于等值比较的 ALU 的零输出有效。为生成 PCSrc 信号,需将一个来自控制单元称为“Branch”(分支)的信号与 ALU 的零输出信号相“与”。现在,这 9 位控制信号(图 4.2.2-6 的 7 位和 2 位 ALUOp)的状态可根据控制单元的 6 位输入信号(操作码位 31: 26)来设置。图 4.2.2-7 给出了包含控制单元和控制信号的数据通路。
图 4.2.2-7 有控制单元和控制信号的数据通路在设计控制单元之前,这里先非正式地定义一下控制功能。由于控制信号的状态仅由操作码决定,我们需要定义在每种操作码下每个控制信号的取值:0、1 或任意值 X。图 4.2.2-8 定义了对应于每种操作码的控制信号状态。
图 4.2.2-8 按指令操作码设置的控制信号图的第一行对应于 R 型指令(add、sub、AND、OR 和 slt):源寄存器字段都为 rs 和 rt,目的寄存器字段为 rd,这决定了 ALUSrc 和 RegDst 信号如何设置。并且,R 型指令写寄存器(RegWrite=1)但是不读写数据存储嚣。当 Branch 控制信号为 0 时,PC 无条件地由 PC+4 取代;反之,如果 ALU 的零输出也为高,则 PC 由分支目标地址取代。当 R 型指令的 ALUOp 为 10 时,ALU 的控制信号应由 funct 字段生成。第二行和第三行给出了 lw、sw 指令的控制信号设置:ALUSrc 翻 ALUOp 被设为进行地址计算;MemRead 和 MemWrite 被设为进行存储访问;最后.RegDst 和 RegWrite 被设为在装载指令中将结果存入寄存器 rt 中。分支指令与 R 型指令相似,因为它将寄存器 rs 和 rt 送入 ALU:分支指令的 ALUOp 字段被设为进行减法(ALUOp =01)以进行等值的测试。注意,RegWrite=0 时 MemtoReg 的设置无关紧要——因为寄存器没有被写入,寄存器写端口的数据值不被使用,所以最后两行 MemtoReg 的值由于不被关心而用 X 取代。RegWnte=0 时,RegDst 的值也可用 X 取代。这种无关项必须由设计者加入,因为它依赖于对数据通路工作原理的了解。三、数据通路的操作根据图 4.2.2-6 和图 4.2.2-8 包含的信息,可以设计出控制单元逻辑,但在此之前先分析一下每条指令是如何使用数据通路的。接下来的一些图说明了 3 种类型的指令在数据通路上的执行过程。在这些图中,有效的控制信号和数据通路部件已着重标出。需要注意的是,对于多选器其控制为 0 时,即使其控制信号没有着重标出,它也有相应的动作。对于多位信号,只要其中任何信号有效,就将其着重标出。图 4.2.2-9 给出了执行 R 型指令(如 add $t1,$t2,$t3)时的数据通路操作。尽管一切都发生在一个时钟周期内,但我们可以考虑分 4 步来执行指令,具体如下:1)从指令存储器中取出指令,PC 自增。2)从寄存器堆中读出寄存器$t2 和$t3。同时,主控制单元计算出各控制信号的状态。3) ALU 根据 funct 字段(指令的 5:0 位)确定 ALU 的功能,对从寄存器堆读出的数据进行操作。4)将 ALU 的结果写入寄存器堆,根据指令的 15: 11 位选择目标寄存器($t1)。
图 4.2.2-9 执行 R 型指令如 add $t1,$t2,$t3 时数据通路的操作(粗线标出活跃的控制线)我们可以用类似的方式描述装载指令如 lw $t1,offset($t2)的执行。图 4.2.2-10 给出了取数时有效的功能单元和控制信号。可以考虑将装载指令的执行分为 5 步(与将 R 型指令的执行分为 4 步类似):1)从指令存储器取指,PC 自增。2)从寄存器堆读出寄存器$t2 的值。3)ALU 将从寄存器堆读出的值与符号扩展后的指令低 16 位值(offset)相加。4)将 ALU 的结果作为数据存储器的地址。5)存储单元的数据写入寄存器堆,目标寄存器由指令的 20: 16 位($t1)指出。
图 4.2.2-10 执行装载指令如 lw $t1,offset($t2)时数据通路的操作(粗线标出活跃的控制线)最后,以同样方式说明相等则分支指令(如 beq $t1, $t2 ,offset)的执行过程。它的操作类似于 R 型指令,但 ALU 的零输出用于决定 PC 自增为 PC +4 还是置为分支目标地址。图 4.2.2-11 给出了执行的 4 步:1)从指令存储器中取指,PC 自增。2)从寄存器堆读出寄存器$t1 和$t2 的值。3) ALU 将从寄存器堆读出的两数相减。 PC+4 的值与符号扩展并左移 2 位后的指令低 16 位(offset)相加,结果即分支目标地址。4)根据 ALU 的零输出决定哪个加法器的结果存人 PC 中。
图 4.2.2-11 执行相等则分支指令时数据通路的操作(粗线标出活跃的控制线)
将跳转指令的低 26 位左移 2 位,其实是加入低两位 00 作为低位,再将 PC+4 的高 4 位作为高位连接起来,即得到 32 位的跳转目的地址。为了在 PC+4、分支目标 PC 和跳转目标 PC 中选择新 PC 值的来源,还加上了一个多路复用器。这个多路复用器需要一个控制信号,称为跳转(Jump)。只有当操作码为 2,即指令为跳转指令时,该控制信号才有效。
4.3.1 多周期处理机数据通路虽然单周期设计也可以正确地运作,但现代设计中并不采取这种方式,因为它的效率太低。究其原因,是在单周期设计中,时钟周期对所有指令等长,即 CPI 为 1。当然,时钟周期要由计算机中可能的最长路径决定。这条路径几乎肯定是一条取数指令,它顺序使用 5 个功能单元:指令存储器、寄存器堆、ALU、数据存储器、寄存器堆。虽然 CPI 为 1,单周期实现方式的总体性能并不见得很好,因为某些指令类型本来可在更短时钟周期内完成。使用固定时钟周期的单周期设计的代价是很大的,可是若要实现浮点单元和包含更复杂指令的指令集,这样的单周期设计根本不能胜任。对于所有指令用一个固定时钟周期的计算机,时钟周期长度将由最长的指令决定,因为必须假定时钟周期等于所有指令中最坏情况下的延迟,因此在不改进最坏时钟周期时间情况下,不能使用减少一般操作延迟的技术。所以,单周期的实现方法违反了关键的设计原则,即加速完成常用操作。另外,在单周期实现中,一个时钟周期内每个功能单元只能使用一次。因此,某些功能单元必须有副本,这就增加了实现的代价。从性能和硬件成本来说单周期设计都是低效的!可变时钟周期的实现方式可以加快指令的执行,遗憾的是,为每个指令类型实现一个可变的时钟周期非常困难,而且所导致的额外开销可能得不偿失。 为避免这些困难,可以使用具有更短时钟周期的实现技术——时钟周期由基本功能单元延时决定,并且每条指令需在多个时钟周期完成。它通过在更短的时钟周期做较少的工作,改变每种指令类型所用的时钟周期数。
一、多周期实现方案多周期实现,也称为多时钟周期实现,是指一条指令的执行要经过多个时钟周期。在前面的一个例子中,曾根据所需进行的功能单元的操作,将一条指令的执行分解为一系列步骤 c 可以用这些步骤来生成一个多周期实现方案。在这个方案中,指令的每一步将占用一个时钟周期。一个功能单元可以在一条指令的执行过程中使用多次,只要是在不同周期中。这种共享可减少所需硬件数量。允许指令的执行占用不同周期数和功能单元可在单指令执行过程中共享,是多周期设计的主要优点。图 4.3.1-1 给出了多周期数据通路的大致轮廓。和图 4.2.1-10 的单周期数据通路相比,可发现以下区别:●指令和数据使用同一个存储器单元。●只有一个 ALU,而不是一个 ALU 和两个加法器。●每个主要的功能单元都加上了一个或多个寄存器存储输出值,以便在后面的时钟周期中使用。
这张图画出了数据通路的关键部件:共享的存储单元、一个各指令共享的 ALU 和各共享单元之间的联系。共享功能单元的使用要求增加或扩展多路复用器,以及用于在同一条指令不同时钟周期间存储数据的临时寄存器。附加的寄存器有指令寄存器(IR)、存储器数据寄存器(MDR)、A、B 和 ALUOut。一个时钟周期结束时,在以后的周期中将要用到的所有数据都必须存储在状态单元中。以后时钟周期中随后的指令将要用到的数据被存人一个程序员可见的状态单元中(即寄存器堆、PC 或存储器)。相反,在以后的周期中同一指令要用到的数据必须存入这些附加寄存器中。这样,附加寄存器究竟放在哪里将由两个因素决定:哪些组合单元适合在单个周期中使用,以及哪些数据将在指令执行过程中后面的各个周期中使用。在这个多周期设计中,假设一个时钟周期内最多只能完成下列操作之一:一次访存、一次寄存堆访问(2 次读或 1 次写)或一个 ALU 操作。于是这三个功能单元的任何一个(存储器、寄存器堆或 ALU)产生的数据必须存储在一个临时寄存器中,以供后面的周期使用。如果没有被缓存,则可能出现周期竞争,导致使用不正确的值。为满足这些要求,加入下面的临时寄存器: ●指令寄存器(IR)和存储器数据寄存器(MDR)分别用于存储从存储器读取的指令和数据的输出。使用两个独立的寄存器,是因为在同一周期中两者的值都要被用到。●寄存器 A、B,用于存储从寄存器堆中读出的寄存器操作数。●寄存器 ALUOut,用于存储 ALU 的输出。除 IR 外,所有寄存器只在相邻两时钟周期之间存储数据,所以它们不需要写控制信号。IR 需保存指令至其执行结束,所以需要一个写控制信号。这个区别将在研究了每条指令的各个时钟周期后更加清楚。由于一些功能单元要为不同目的所共享,所以需要添加多路复用器,并扩展已有的多路复用器。比如,由于一个存储器既用于存储指令也用于存储数据,就需要一个多路复用器来为一个存储地址选择数据来源,即 PC 用于存取指令还是 ALUOut 用于存取数据。用一个 ALU 代替单周期数据通路中的三个 ALU。意味着这一个 ALU 必须能接收原来三个 ALU 的所有输入。处理这些输入使数据通路有以下变化:1)给 ALU 的第一个输入添加了一个多路复用器,它在寄存器 A 和 PC 间进行选择。2)ALU 第二个输入的多路复用器由 2 路改为 4 路,其新加的两个输入为常数 4(用于 PC 增值)和符号扩展及移位后的偏移量字段(用于计算分支地址)。图 4.3.1-2 详细画出了加上了这些多路复用器后的数据通路。通过引入一些寄存器和多路复用器得以将存储单元数目从 2 减为 1,并取消了 2 个加法器。由于寄存器和多路复用器都很小,这样做可以大大降低硬件成本。
图 4.3.1-2 MIPS 的多周期数据通路虽然这条数据通路支持 PC 的普通增值,为了分支和跳转指令还需要增加一些连接和一个多路复用器;比起单周期的数据通路,增加了一些寄存器(IR、MDR、A、B、ALUOut)。一个用于存储地址的多路复用器,一个用于 ALU 最高输入的多路复用器以及将 ALU 底部输入的多路复用器扩展为 4 个输入。这些小的增加可以去掉两个加法器和一个存储单元。图 4.3.1-2 的数据通路用多个时钟周期完成一条指令,所以需要一套不同的控制信号。程序员可见的状态单元(PC、存储器和寄存器)和 IR 需要写控制信号。存储器还需一个读信号。单周期数据通路的 ALU 控制单元也可在此用于控制 ALU。最后,每个 2 输入的多路复用器都需要一根控制线,4 输入的多路复用器需要 2 根控制线。图 4.3.1-3 给出了图 4.3.1-2 中的数据通路加上这些控制线后的情况。
图 4.3.1-3 图 4.3.1-2 的数据通路加上控制线路 ALUOp 和 ALUSrcB 信号为 2 位控制信号,其他控制信号为 1 位。寄存器 A 和 B 都不需要写控制信号,因为它们的内容只在写入的下一个周期读出。加入了存储数据寄存器,以便使取数时存储从存储器读出的数据。从存储器读出的数据不能直接写入寄存器堆,因为单时钟周期内不能够既访问存储器又写寄存嚣堆。MemRead 信号被移到存储器单元的顶部以简化该图。为支持分支和跳转指令,多周期数据通路还要添加部件;完成这些添加后,再看看指令的执行顺序如何确定,以及如何生成数据通路的控制。对于跳转和分支指令.PC 有三种可能来源:1) ALU 的输出,取指时为 PC+4。这个数应直接写入 PC。2)寄存器 ALUOut,存储的是计算出的分支目标地址。3)指令寄存器(IR)的低 26 位左移 2 位与 PC 增值后的高 4 位相连,这是跳转指令时 PC 的来源。在单周期控制的实现中可以看出,PC 的写入可以是有条件的或无条件的。在普通的增值和跳转时,PC 的写入是无条件的。对于条件分支指令,只有在两个指定寄存器相等时 ALUOut 的值才会取代增值的 PC。所以,控制部分需两条独立的 PC 写控制信号,称为“PCWrite”和“PCWrireCond。这两个控制信号需要与 PC 写控制相连。和单周期数据通路一样,我们将使用一些门电路,从 PCWrite、PCWriteCond 和用于检验 beq 的两个寄存器操作数是否相等的 ALU 零输出信号生成 PC 写控制信号。为了决定在条件分支中 PC 是否被写入,将 ALU 的零输出信号与 PCWriteCond 相与,然后将与门的输出结果与非条件的 PC 写信号(PCWriie)相或,或门的输出直接作为 PC 的写控制信号。图 4.3.1-4 为完整的多周期数据通路和控制单元,包括为实现更新 PC 而添加的控制信号和多路复用器。
图 4.3.1-4 多周期实现的完整的数据通路和必要的控制线路图 4.3.1-3 的控制线路被加到控制单元,并且包含了改变 PC 所需的控制和数据通路单元;相对于图 4.3.1-4 而盲,主要增加的包括一个用于选择新 PC 值来源的多路复用器(右上角);用于合并 PC 写信号(左上角)的两个门;和控制信号 PCSource、PCWrite、PCWrireCond。PCWriteCond 信号用来判定条件分支是否成立。这也包括对跳转指令的支特。在讨论各指令的执行步骤前,让我们先大致看看所有控制信号的作用。图 4.3.1-5 说明了各个控制信号被设置为有效或无效状态时所起的作用。1.位控制信号动作
图 4.3.1-5 图 4.3.1-4 的控制信号所产生的动作 2.位控制信号动作
图 4.3.1-6 图 4.3.1-4 的控制信号所产生的动作二、将指令的执行分到各个时钟周期图 4.3.1-4 给出了数据通路,现在应该看看多周期指令执行在每个时钟周期内的动作,因为这决定了可能需要附加什么控制信号,以及它们的设置。将指令的执行分解到各个时钟周期,目的是最大化性能。可以先把任意指令的执行分解为一系列步骤,每步一个时钟周期,基本上长度相当。比如,规定每一步最多只能包含一次 ALU 操作,或一次寄存器堆访问,或一次存储器访问。在此规定下,时钟周期应当至少长于这些操作中最长的操作所花费的时间。前面讲过,每个时钟周期结束时,要在后面的周期中用到的所有数据必须存入一个寄存器,可以是一个主状态单元(如 PC、寄存器堆或存储器),一个在每个时钟周期写入的临时寄存器(如 A、B、MDR 或 ALUOut)或一个有写控制的临时寄存器(如 IR)。并且,由于我们的设计基于边缘触发型,可以继续读取寄存器的当前值;新的值直到下一个时钟周期到来时才可能出现。在单周期数据通路中,每条指令的执行使用一套数据通路单元。许多数据单元顺序操作,以某个其他单元的输出作为自己的输入。一些数据通路单元并行操作;比如,PC 的增值和指令的读取同时进行。多周期数据通路的情况类似。同一步内列出的所有操作在一个时钟周期内并行完成,随后各步的操作在不同周期内顺序完成。而一次 ALU 操作、一次存储器访问和一次寄存堆访问的限制也决定了一步内最多可以完成的内容。需要把读写 PC 或一个独立的寄存器与读写寄存器堆区分开来。前者的读写只占一个时钟周期的一部分,而寄存器堆的读写额外需要一个周期。此区别的原因是相对于单独的寄存器而言,寄存器堆有额外的控制和访问开销。为了缩短时钟周期,访问寄存器堆需要多个时钟周期完成。可能的指令执行步骤及动作如下。每条 MIPS 指令需要其中 3 到 5 步:步骤 1:取指从存储器中取出指令并计算下条指令的地址:IR<=Memory[PC];PC<= PC+4;操作:将 PC 作为地址传送给存储器,进行读取,将指令写入并存储在指令寄存器(IR)。同时,将 PC 加 4。为实现这一步,需要将控制信号 MemRead 和 IRWrite 设为有效,将 IorD 置 0 以选择 PC 作为地址来源 c 为实现 PC 加 4,要将 ALUSrcA 信号置 0(将 PC 送往 ALU),ALUSrcB 信号置 01(将 4 送往 ALU),ALUOp 置 00(使 ALU 进行加法)。最后,还要将 PC 源设置为 00 并且设置 PCWrite,以把增值后的指令地址存入 PC。 PC 的增值和指令存储器的访问可以并行进行。新 PC 值直到下个时钟周期才可见。增值后的 PC 也要存入 ALUOut,但这个动作没有什么影响。 步骤 2:指令译码和读取寄存器在上一步和这一步,还不知道指令的具体内容是什么,所以只能进行那些针对所有指令都需要进行的操作(如第一步中的取指)或没有坏影响的操作。于是,在这一步中可以读取指令字段 rs 和 rt 指定的寄存器的内容,因为即使不必要这样傲也没有什么害处。从寄存器堆读出的数据值可能在后面的步骤中要用到,所以我们将它们从寄存器堆中读出并将它们存入临时寄存器 A 和 B 中。还要用 ALU 计算分支目标地址,这样做也没有什么害处。因为若发现指令不是分支指令则忽略计算结果即可。计算出的分支目标地址存于 ALUOut 中。尽早执行这些“良性”操作的好处是减少指令执行所用的时钟周期数。指令格式的规整便于这些“良性”操作的尽早完成。比如,若指令有两个寄存器输入,则它们总是位于 rs 和 rt 字段;若指令是分支指令,则其偏移量总是低 16 位: A<= Reg[IR[25:21]]; B<= Reg[IR[20:16]]; ALUOut<=PC+(sign-extend (IR[15-0])<<2);操作:访问寄存器堆,读取寄存器 rs 和 rt 的内容并将结果存人寄存器 A 和 B。由于寄存器 A 和 B 在每个时钟周期都被重新写入,所以可以在每个时钟周期读出寄存器堆的内容存入寄存器 A、B。这一步中还计算分支目标地址并存人 ALUOut,若指令为分支指令则下一个时钟周期将用到此 ALUOut。这个操作要求给 ALUSrcA 置 0(以将 PC 送入 ALU)。ALUSrcB 置 11(以将符号扩展并移位后的偏移量送入 ALU),ALUOp 置 00(以使 ALU 作加法)。读取寄存器堆和计算分支目标地址并行执行。在这个时钟周期以后,就可以根据指令内容来决定要进行的具体操作了。 步骤 3:指令的执行,存储地址的计算或分支的完成 这是数据通路操作中由指令类型决定的第一个时钟周期。在所有情况下,ALU 对前面准备好的操作数进行操作,根据指令类型执行四种功能之一种。根据指令的类型说明要进行的操作: 存储器访问: ALUOut<= A + sign-extend (IR[15-0]);操作:ALU 将操作数相加,得到存储地址。这要求将 ALUSrcA 置 1(使 ALU 的第一个输入为寄存器 A),ALUSrcB 置 10(使符号扩展单元的输出作为 ALU 的第二个输入)。需将 ALUOp 信号置为 00(以使 ALU 作加法运算)。 算术逻辑指令(R 型): ALUOut<=A op B; 操作:ALU 对前面周期中从寄存器堆读出的数据进行功能码所指定的操作。这要求将 ALUSrcA 置 1,ALUSrcB 置 00(使寄存器 A 和 B 作为 ALU 的输入)。 ALUOp 信号需置为 10(以由功能字段决定 ALU 控制信号的设置)。 分支: if (A==B)PC <= ALUOut; 操作:ALU 将前面步骤中从寄存器堆读出的两寄存器进行相等比较。ALU 的零信号输出用来决定是否分支。这要求将 ALUSrcA 置 1,ALUSrcB 置 00(使寄存器堆的输出成为 ALU 的输入)。ALUOp 信号需置为 01(使 ALU 做减法)以进行等值的测试。若 ALU 的零输出信号有效,则需将 PCWriteCond 信号置有效,以修改 PC。通过将 PCSource 置 01,写入 PC 的内容将来自 ALUOut。而 ALUOut 存储的正是前面周期中计算出来的分支目标地址。对于条件分支指令,实际上有两次写 PC:一次来自 ALU 的输出(在指令译码/读取寄存器时),一次来自 ALUOut(在分支完成时)。最后写入 PC 的内容用于下一次取指。 跳转: PC <={PC[31:28],(IR[25:0],2’b00)}; 操作:PC 由跳转目的地址取代。PCSource 设置为指示将跳转地址直接作为 PC 的输入,PCWrite 置为有效,以将跳转地址写入 PC。 步骤 4:存储的访问或 R 型指令的完成 在这一步中,存或取指令访问存储器,算术逻辑指令写结果。当数据值从存储器中取出后,被存入存储器的数据寄存器(MDR),它将用于下一个时钟周期。 存储访问指令: MDR <= Memory[ALUOut]; 或 Memory [ALUOut] <= B; 操作:若为取数指令,从存储器中读出一个数据字并写入 MDR。若为存数指令,则将数据写回存储器。在这两种情况下,所用的存储器地址都是在前面的步骤中计算出并存于 ALUOut 中的地址。对于存数指令,源操作数存于寄存器 B 中。(实际上 B 被读取了两次,一次在第 2 步,一次在第 3 步。幸好,两次读取的数据值相同,因为寄存器号——存于 IR 中,用于读取寄存器堆没有变。)信号 MemRead(用于取数)或 MemWrite(用于存数)将置为有效。另外,对于存取指令,信号 IorD 被置为 1,以强迫存储器地址来自 ALU 而非 PC。由于 MDR 在每个时钟周期被写入,不需设置另外的控制信号。 算术逻辑指令(R 型): Reg[IR[15:11]] <= ALUOut: 操作:将 ALUOut 的内容,即前面周期中的 ALU 的操作结果,写入结果寄存器。信号 RegDst 必须置为 1,以使 rd 字段(15: 11 位)在寄存器堆中用于选择要写入的寄存器。 RegWrite 必须有效,同时 MemtoReg 必须为 0,以使 ALU 的输出而非存储器的数据输出被写入。 步骤 5:读取存储嚣完成 在这一步中,写入来自存储器的值,完成取数指令。 取数指令: Reg[lR[20:16]] <= MDR; 操作:将在前面周期中存入 MDR 的数据写入寄存器堆,为此,将 MemtoReg 置 1(以写入来自存储器的结果)。使 RegWrite 有效(以引起写操作),将 RegDst 置 0 以选择 rt(20:16 位)字段为寄存器号。以上五步概括如图 4.3.1-7。这五步可以决定每个时钟周期里控制信号该做些什么。
图 4.3.1-7 所有类型指令执行步骤的总结指令分 3 到 5 步执行,前两步与指令类型无关。之后,指令根据类型不同,还需要 1-3 步完成,存储器访问或存储器读完成步骤为空的项说明有些指令需要较少的周期完成。在一个多周期实现中,当前指令一完成下一条指令就马上开始,所以这些周期不会空闲或浪费。如前所述,实际上每个周期都读寄存器堆,但是只要 IR 不变,从寄存器堆读出的内容就是一样的。特别地,对于分支或 R 型指令,在指令解码阶段读入寄存器 B 的值,与在执行阶段写入 B 的值,以及随后存字指令中存储器访问阶段使用的值都是一样的。
图 4.3.1-3 图 4.3.1-2 的数据通路加上控制线路 ALUOp 和 ALUSrcB 信号为 2 位控制信号,其他控制信号为 1 位。寄存器 A 和 B 都不需要写控制信号,因为它们的内容只在写入的下一个周期读出。加入了存储数据寄存器,以便使取数时存储从存储器读出的数据。从存储器读出的数据不能直接写入寄存器堆,因为单时钟周期内不能够既访问存储器又写寄存嚣堆。MemRead 信号被移到存储器单元的顶部以简化该图。为支持分支和跳转指令,多周期数据通路还要添加部件;完成这些添加后,再看看指令的执行顺序如何确定,以及如何生成数据通路的控制。对于跳转和分支指令.PC 有三种可能来源:1) ALU 的输出,取指时为 PC+4。这个数应直接写入 PC。2)寄存器 ALUOut,存储的是计算出的分支目标地址。3)指令寄存器(IR)的低 26 位左移 2 位与 PC 增值后的高 4 位相连,这是跳转指令时 PC 的来源。在单周期控制的实现中可以看出,PC 的写入可以是有条件的或无条件的。在普通的增值和跳转时,PC 的写入是无条件的。对于条件分支指令,只有在两个指定寄存器相等时 ALUOut 的值才会取代增值的 PC。所以,控制部分需两条独立的 PC 写控制信号,称为“PCWrite”和“PCWrireCond。这两个控制信号需要与 PC 写控制相连。和单周期数据通路一样,我们将使用一些门电路,从 PCWrite、PCWriteCond 和用于检验 beq 的两个寄存器操作数是否相等的 ALU 零输出信号生成 PC 写控制信号。为了决定在条件分支中 PC 是否被写入,将 ALU 的零输出信号与 PCWriteCond 相与,然后将与门的输出结果与非条件的 PC 写信号(PCWriie)相或,或门的输出直接作为 PC 的写控制信号。图 4.3.1-4 为完整的多周期数据通路和控制单元,包括为实现更新 PC 而添加的控制信号和多路复用器。
4.3.2 多周期处理机控制器实现现在已经确定了控制信号的内容及设置的时间,就可以实现控制单元了。为单周期数据通路设计控制单元,使用了一组真值表,根据指令类型确定控制信号的设置。而多周期数据通路的控制更为复杂,因为其指令是分一系列步骤执行的。多周期数据通路的控制部分必须确定每一步及下一步要设置的信号。描述多周期控制的第一种方法是有限状态机。一个有限状态机由一组状态及状态间的转换规则组成。转换规则由一个后继状态函数确定,它将现有状态和输入映射到一个新的状态。当用一个有限状态机描述控制时,每个状态还表示一组输出,当机器在此状态耐,这些输出有效。有限状态机的实现方式一般假设所有的输出都不会被明确地设为有效或无效。同样地,当一个信号没有被明确地设为有效时,就是无效状态,而非无关状态,这是数据通路正常运作的前提。比如,仅当寄存器堆中某一项要被写入时,RegWrite 信号才应是有效的;当它未被明确设为有效时,一定是无效的。多路复用器的控制稍有不同,因为它们选择一个为 0 或为 1 的输入。这样,在有限状态机中,经常要确定好所关心的多路复用器的控制。当逻辑实现有限状态机时,可将控制置 0 设为默认值,这样它们不需任何门电路。有限状态机的控制基本上对应于 4.3.1 的五个执行步骤;有限状态机的每一个状态占用一个时钟周期。有限状态机由几个部分组成。由于所有指令的前两个执行步骤都一样,所有指令的有限状态机的前两个状态也都一样。第 3-5 步不一样,具体由操作码决定。在某类型指令的最后一步执行完后,有限状态机将回到原始状态,开始取下一条指令。图 4.3.2-1 抽象地表示了一个有限状态机。为了进一步研究有限状态机的细节,首先要扩充取指令和解码都分,然后介绍不同指令类型各自对应的状态(和动作)c
图 4.3.2-1 有限状态机控制的高层视图前面的步骤与指令类型无关;然后进行一系列由指令的操作码决定的操作,以完成各种类型指令的执行。完成该类型指令所需操作后,控制转回取新的指令:这张图中的每一个方框表示一个或多个状态。标有开始的线指出要取第一条指令开始时的状态。图 4.3.2-2 以传统的图形方式表示了有限状态机的前两个状态。为便于说明,给各状态随机标号:0 状态对应第一步,是有限状态机的初始状态。
图 4.3.2-2 所有指令的取指和译码部分都是相同的这些状态对应于图 4.3.2-1 的概括的抽象有限状态机的上面那个方框。在第一个状态,激活两个信号(MemRead 和 IRWrite)使存储器读一条指令并将它写入指令寄存器,并设置 lorD 为 0 以选择 PC 为地址来源。设置信号 ALUSrcA、ALUSrcB、ALUOp、PCWrite、PCSource 以计算 PC+4 并存人 PC。(它也会存人 ALUOut.但不会从那里被使用)在下一状态,将 ALUSrcB 设为 11(使低 16 位符号扩展并移位后的 IR 送人 ALU)。ALUSrcA 设为 0,ALUOp 设为 00,以计算分支目标地址;将结果存于每个周期都写入的 ALUOut 寄存器中。根据在当前状态中得知的指令类型,在 4 个后续状态中进行选择。控制单元的输人称为 Op,用于决定选择哪条弧线,请记住,所有不是显式标明为有效的信号都是无效的;这点对于控制写操作的信号特别重要。对于多路复用器控制,没有特别设置的表示无关多路复用器的设置。各状态中有效的信号在表示该状态的圆圈中写出。状态之间的弧线指向下一个状态,当有多个可能的下一状态时,弧线上标注了选择此下一状态的条件。在状态 1 之后,有效的信号取决于指令类型。所以,有限状态机从状态 1 伸出 4 条弧线,对应于 4 个指令类型:存储访问、R 型、等值分支和跳转。这个根据指令向不同状态分支的过程称为解码,因为后继状态的选择,即下面的动作,由指令类型决定。●存储访问指令图 4.3.2-3 给出了有限状态机用于实现存储访问指令的部分。对于这种指令,取指或读取寄存器后的第一个状态计算存储地址(状态 2)。为计算存储地址,ALU 的输入多路复用器需设置为接受寄存器 A 为第一个输入,符号扩展后的偏移量字段为第二个输入;其结果写入 ALUOut 寄存器中。存储地址计算之后,应读/写存储器;这需要两个不同的状态。若指令的操作码为 lw,则状态 3(对应于存储访问步骤)进行存储器读(MemRead 被激活)。存储器的输出通常写入 MDR。若是 sw,状态 5 进行存储器写(MemWrite 被激活)。在状态 3 和 5 中,IorD 信号被设为 1 以保证存储器地址来自 ALU(存数指令并不需要这样,因为写地址使用存储器的不同输入)。写存储后,sw 指令执行完毕,后继状态为状态 0。若是取数指令,则需要另一状态(状态 4)将存储器的结果写入寄存器堆。设置多路复用器控制信号 MemtoReg=1 和 RegDsr=0 将使从 MDR 中取出的数值写入寄存器堆,并以 rt 作为寄存器号。这一步对应于存储器读完成步骤,其下一状恋为状态 0。
图 4.3.2-3 控制存储访问指令的有限状态机有四个状态信号 ALUSrcA、ALUSrcB 和 ALUOp 的设置是为了在状态 2 进行存储地址的计算。取指令需要多一个状态以将来自 MDR 的结果(结果在状态 3 时写人)写入寄存器堆。● R 型指令为实现 R 型指令,需要对应于步骤 3(执行)和步骤 4(R 型指令完成)的两个状态。图 5-34 给出了有限状态机的两个状态部分。状态 6 激活 ALUSrcA 并置 ALUSrcB 为 00;这使得从寄存器堆中读取的两个寄存器作为 ALU 的输入。置 ALUOp 为 10 使 ALU 控制单元根据指令的功能字段设置 ALU 的控制信号。在状态 7 中,RegWrite 被激活以写寄存器堆,RegDst 被激活以使 rd 字段作为目的寄存器号,MemtoReg 无效以选择 ALUOut 作为源值写入寄存器堆。
图 4.3.2-4 R 型指令的两个状态这些状态对应于图 4.3.2-1 中标有“R 型指令”的方框。第一个状态使 ALU 进行操作,第二个状态使 ALU 的结果(在 ALUOut 中)写入寄存器堆。在状态 7 激活的三个信号使 ALUOut 的内容写入寄存器堆中由指令寄存器的 rd 字段标识的寄存器中。● 分支指令对于分支指令,只需多加一个状态,因为指令执行在第 3 步就完成了。在这一状态中,必须设置控制信号使 ALU 对寄存器 A 和 B 的内容做比较,也要设置信号使 ALUOut 寄存器中的内容有条件地写入 PC。为进行比较,需激活 ALUSrcA 并置 ALUSrcB 为 00,置 ALUOp 为 01(强制进行减法)。(只使用 ALU 的零输出信号,而不用减法的结果。)为控制 PC 的写入,需激活 PCWrite-Cond 并置 PCSource=01,这样若 ALU 的零输出信号有效,ALUOut 寄存器的内容将写入 PC。
图 4.3.2-5 分支指令只需有一个状态被激活的前三个输出(ALUSrcA、ALUSrcB 和 ALUOp)使 ALU 对寄存嚣进行比较,若分支条件为真,PCSource 和 PCWnteCond 信号进行条件写。注意不使用写入 ALUOut 的值;而只用 ALU 的零输出信号。分支目标地址从 ALUOut 得到,它在第一个状态结束时存人 ALUOut 中。●跳转指令最后一类是跳转指令;同分支指令一样,它也只需一个状态完成其执行。在这一状态中,PCWrite 信号被激活以写 PC。通过置 PCSource 为 10,所写的内容将为指令寄存器的低 26 位加上最低两位 0 和高 4 位 PC 值。
图 4.3.2-6 跳转指令只需有一个状态现在,我们可以将有限状态机的这些部分合在一起,组成控制单元有限状态图,如图 4.3.2-7 所示。在每一状态中写出了被激活的信号。后继状态取决于指令的操作码位,所以弧线上标出相应的指令操作码。
图 4.3.2-7 控制单元有限状态图弧线上的标注是在确定后继状态时进行测试的条件;当后继状态是无条件的时候没有标注。结点中的标注为该状态中激活的输出信号;如果正确的操作要求,通常就要指明多路复用嚣控制信号的设置。这样,在某些状态中一个多路复用器的控制可能设为 0。一个有限状态机可由一个包含当前状态的临时寄存器和一个决定数据通路信号设置和下一状态的组合逻辑模块实现。组合逻辑的输出为下一状态号和在当前状态中应该激活的控制信号。组合逻辑的输入为当前状态和所有可能决定后继状态的输入信号,在当前情况下,输入即为指令寄存器的操作码位。注意在使用的有限状态机中,输出仅取决于当前状态,而不取决于输入信号。图 4.3.2-8 的有限状态机称为 Moore 机,其显著特点是其输出只依赖于当前状态。对一个 Moore 机,标有组合控制逻辑的模块可分为两部分。一部分有控制输出和只有状态输入,另一部分只有后继状态输出。另一种有限状态机称为 Mealy 机,Mealy 机允许输入和当前状态共同决定输出,Moore 机在速度和控制单元大小方面有潜在的实现优势。在用有限状态机实现控制电路之前,我们必须先给 10 个状态分别指定一个数字。任一状态都可以使用任何数字,但为简单起见,我们将使用连续的数字。我们需要 4 个二进制位来为 1010 个状态编码。称这些状态位分别为:S3,S2,S1,S0。如图 4.3.2-8 所示,当前状态号存在一个状态寄存器中。如果各个状态顺序编号,状态 i 可以用二进制数 i 来编码。控制单元的输出指定了下一状态。这个状态在时钟周期边缘写到状态寄存器中,在紧接活动周期边沿的下一时钟周期开始时成为新状态。我们称这些输出为 NS3,NS2,NS1,NS0。一旦确定了输入、状态和输出的数目,我们就可以得出如图 4.3.2-8 所示的控制单元的基本设计轮廓。
图 4.3.2-8 有限状态机的控制器图 4.3.2-8 中标有“控制逻辑”的部分是组合逻辑。我们可以将其视为一张大表,其功能是按照输入给出对应的输出。这一块逻辑实现了有限状态机的两个不同的部分。一部分是确定数据通路控制输出设置的逻辑,这仅取决于状态位。控制逻辑的另一部分是实现求下一状态的函数,这些方程式根据当前状态和其他输入(6 位操作码)确定下一状态的值。图 4.3.2-9 给出了逻辑方程:上半部分是输出,下半部分是下一状态函数。表中的值根据图 4.3.2-7 的状态图得出。当控制线在某个状态下被激活时,这一状态就被记入到表中该控制信号行的第二列。同样,当某个状态是另一个状态的下一状态时,就可得出表中的下一状态表项的值。
图 4.3.2-9 控制信号输出
图 4.3.2-10 下一状态函数在图 4.3.2-10 中,我们用缩写 stateN 表示当前状态 N。这样,stateN 被状态编号 N 的编码代替。我们用 NextStateN 表示输出的下一状态为 N。这个结果从下一状态输出(NS)得到。当 NextStateN 有效时,二进制位 NS[3-0]根据 N 的二进制码被设置。当然,因为一个给定的信号位可以在多个下一状态设为有效,所以每个状态位的方程是或的形式。控制函数分为两部分:下一状态输出,它们的值取决于所有输入;以及控制信号输出,它们的值仅取决于当前状态位。针对控制信号输出以及下一状态函数,我们很容易给出逻辑方程:
我们已经看到,控制函数可以表达成每个输出的逻辑方程。这些逻辑方程集可以用两种方法实现:依据完全的真值表,或者根据对真值表稀疏编码而形成的二级逻辑结构。
4.3.3 控制器 ROM 实现要实现控制函数,最简单的方法应当是将真值表编码存储在只读存储器 ROM 中。从图 4.3.2-8 可以看出,这个 ROM 有 10 个输入(6 位操作码加上 4 个状态位),也就是 210= 1024。控制单元的 10 位输入将作为控制逻辑块的 ROM 的地址线。由于共有 16 个数据通路控制输出和 4 个下一状态位,所以每个存储单元的宽度(或存储器的字)是 20 位。这意味着整个 ROM 的容量是 210×20=20Kbit。一、ROM 的实现方法ROM 中字的各位的设置取决于哪个输出在该字中有效。在看控制字之前,我们需要分别规定一下控制输入(地址)和输出字(内容)的位的顺序。我们用图 4.3.2-8 中的顺序给各位进行编号,下一状态的各位在控制字的最低位,而当前状态输入位作为输入地址的低位。这样一来,PCWrite 输出就是每个存储字的最高位(位 19),NS0 是最低位。地址的最高位是指令的最高位 Op5,地址的最低位是 s0。我们可以这样构造真值表来填充 ROM。真值表每行对应 2n 个惟一的输入组合,列说明哪些输出在此输入组合时有效。由于数据通路控制输出仅由当前状态决定,通过分开数据通路控制和下一状态的输出,我们可以显示所有的行。图 4.3.3-1 给出了数据通路控制输出的真值表。我们仅包括正在使用的状态输入的编码(也就是说,值 0-9 对应于状态机的 10 个状态)。
图 4.3.3-1 16 个数据通路控制输出的真值表尽管 4 位状态域有 16 个可能的值,这里只用到并显示了 10 个状态。这 10 个可能的值在顶部显示,每一列给出了对应第一行状态输入值的数据通路控制输出。例如,当状态输入是 0011(state3)时,有效的数据通路控制信号是 IorD 或 MemRead。这里列是输入,行是输出图 4.3.3-2 中的真值表给出了 ROM 中每个字的高 16 位的内容。4 位输入域给出了每个字的低 4 位地址位。每列则给出了该地址处所存储的内容。
图 4.3.3-2 仅取决于状态输入的 ROM 高 16 位的内容 PCWrite 在 state0 和 state9 时为 1;这对应低 4 位是 0000 或 1001 的地址。该位在存储字中的值和输入 Op[5-0]没有关系,所以该位为 1 的地址是 000000000,0000001001,0000010000,0000011001,….1111110000, 11111110010 一般的形式是 XXXXXX0000 或 XXXXXX1001,这里 XXXXXX 是 0、1 的任意组合,对应于不为输出所依赖的 6 位操作码。 图 4.3.3-3 给出了对应于下一状态输出的控制字的低 4 位。图中最后一列对应不符合给定的操作码的所有可能操作码的值。在 state0,下一状态总是 state1。因为指令还处在取指令阶段。在 state1 后,操作码域必须是合法的。通过标明非法的表项说明了这一点。
图 4.3.3-3 控制字的低 4 位(NS 输出),