(完整word版)6.4-简单RISC处理器设计_第1页
(完整word版)6.4-简单RISC处理器设计_第2页
(完整word版)6.4-简单RISC处理器设计_第3页
(完整word版)6.4-简单RISC处理器设计_第4页
(完整word版)6.4-简单RISC处理器设计_第5页
已阅读5页,还剩50页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

现代电子系统设计(课堂讲义)-PAGE1-6.4简单RISC处理器设计前面我们已经学习了现代电子系统设计的基本方法,学习了使用VerilogHDL语言设计简单组合逻辑和简单时序逻辑模块,学习了自顶向下的设计方法,同时还学习了状态机的设计。在这里,将综合运用以上所学的知识,设计一个简化的处理器。“麻雀虽小,五脏俱全”,这里设计的处理器虽然是经过简化的,但是能对其进行综合、适配,最终可以在硬件上运行。这里介绍的处理器由于主要用于教学,只是一个简单的教学模型,设计不一定合理,只是从原理上说明了一个简单的处理器是如何构成的。6.4.1RISC处理器简介中央处理器(CPU,CentralProcessingUnit)是计算机的核心部件。计算机进行信息处理可分为两个步骤:(1)将数据和程序(即指令序列)输入到计算机的存储器中;(2)从第一条指令的地址起开始执行该程序,得到所需结果,结束运行。CPU的作用是协调并控制计算机的各个部件并执行程序的指令序列,使其有条不紊地进行。因此它必须具有以下基本功能:◆取指令——当程序已在存储器中时,首先根据程序入口地址取出一条程序,为此要发出指令地址和控制信号。◆分析指令——即指令译码,这是对当前取得的指令进行分析,指出它要求什么操作,并产生相应的操作控制命令。◆执行指令——根据分析指令时产生的“操作命令”形成相应的操作控制信号序列,通过运算器、存储器及输入/输出设备的执行,实现每条指令的功能,其中包括对运算结果的处理及下条指令地址的形成。将CPU的功能进一步细化,可概括如下:(1)能对指令进行译码并执行规定的动作;(2)可以进行算术和逻辑运算;(3)能与存储器和外设交换数据;(4)提供整个系统所需要的控制。尽管各种CPU的性能指标和结构细节各不相同,但它们所能完成的基本功能相同。由功能分析可知,任何一种CPU内部结构至少应包含下面这些部件:(1)算术逻辑运算部件(ALU);(2)累加器或寄存器;(3)程序计数器;(4)指令寄存器和译码器;(5)时序和控制部件。精简指令计算机(RISC,ReducedInstructionSetComputer)是一种20世纪80年代才出现的CPU,它与一般CPU相比不仅只是简化了指令系统,而且还通过简化指令系统使计算机的结构更加简单合理,从而提高了运算速度。从实现的途径看,RISC处理器与一般的CPU的不同之处在于:它的时序控制信号形成部件是用硬布线逻辑实现的而不是采用微程序控制的方式。所谓硬布线逻辑也就是用触发器和逻辑门直接连线所构成的状态机和相应的组合逻辑,故产生控制序列的速度比用微程序控制方式快得多,因为这样做省去了读取微程序指令的时间。下面就详细介绍一个简化的、用于教学目的的RISC处理器SimpleRISC,它是采用硬布线逻辑实现的控制,是一个可综合的CPU模型。6.4.2SimpleRISC处理器结构介绍一、处理器结构这里首先对我们即将要进行设计的SimpleRISC处理器硬件结构作一个简要介绍。这里所介绍的处理器结构只是SimpleRISC芯片的内部结构,或者说只是SimpleRISC的CPU核,所以不包括存储器单元和外设等。SimpleRISC处理器的内核结构如图6-54所示。图6-54SimpleRISC处理器内核结构从图6-54中可以看出,SimpleRISC处理器内核包括以下功能单元:控制器、程序计数器、通用寄存器、算术逻辑单元(ALU,ArithmeticLogicUnit)和总线接口单元(BIU,BusInterfaceUnit)。以下的内容将依次讨论这些模块。二、处理器内部子模块1、控制器控制器用于进行指令译码、产生ALU运算控制信号、产生通用寄存器读写控制信号以及协调处理器工作时序工作。2、程序计数器程序计数器用于指向下一条将要执行的指令的地址,它可以自动增值或是通过分支指令来设置其内容。3、通用寄存器SimpleRISC处理器内部有32个32位通用寄存器,ALU运算的源操作数、目的操作数大都来自这个通用寄存器堆。4、算术逻辑单元算术逻辑单元用于进行算术运算、移位操作、比较设置操作以及逻辑运算。对于其他未能实现的运算,例如乘法、浮点运算等,可以使用软件来实现。5、总线接口单元总线接口单元用于SimpleRISC处理器与外部存储器和I/O口的连接,为了简单起见,SimpleRISC系统的存储器和I/O采用统一编址的方式进行组织,并且采用程序存储器和数据存储器分开的哈佛结构。从图6-54可以看出,SimpleRISC处理器并没有实现中断处理以及Cache等功能,虽然如此,但是SimpleRISC处理器已经能进行一些简单的工作了。下面将继续讨论有关SimpleRISC的一些细节。6.4.3SimpleRISC处理器的指令集和寻址方式一、SimpleRISC处理器的指令集SimpleRISC处理器的指令集中包括以下指令:(1)算术运算指令算术运算指令包括加法(add)和减法(sub)指令。(2)比较指令比较指令只有小于则设置(slt)指令。(3)移位指令移位指令包括左移(ls)和右移(rs)指令。(4)逻辑运算指令逻辑运算指令包括与(and)、或(or)和非(not)指令。这里的逻辑运算都是按位进行的。(5)数据存储器访问指令数据存储器访问指令包括存储器数据加载(load)和数据存储(store)两条指令。数据存储器的访问都必须通过这两条指令来进行。(6)分支指令分支指令分为无条件分支和条件分支两类。无条件分支指令只有一条:branch;而条件分支只实现相等则分支(beq)指令。二、SimpleRISC处理器的寻址方式CPU的寻址方式指的是CPU在进行操作时如何找到操作数的方法。一般来说,操作数可以存储在CPU内部寄存器中,也可以由指令提供,还可以存储在数据存储器中。对于RISC处理器来说,操作数一般都由指令提供或存储在CPU内部寄存器中,而RISC处理器一般不直接使用数据存储器中的数据作为操作数。这里的SimpleRISC处理器,其操作数也由指令提供或存储在CPU内部寄存器中,相应的寻址方式为立即寻址(操作数由指令提供)和寄存器寻址(操作数存储在CPU内部寄存器中)。6.4.4SimpleRISC处理器的指令格式与编码一、指令格式这里假设设计的SimpleRISC处理器是32位的,由于它有32个32位寄存器,所以每个寄存器需要使用5位编码来寻址。将以上讨论的指令分为三种类型的指令:R-类型指令、I-类型指令和J-类型指令。R-类型指令指的是操作数全部来自通用寄存器,包括寄存器操作数的算术运算指令、比较指令和逻辑运算指令。由于这些指令的操作数都来自于通用寄存器,而一般的操作都有三个操作数:两个源操作数和一个目的操作数,所以R-类型指令中需要使用5×3=15位来对寄存器进行编址,对于32位指令,使用了15位以后,还剩余17位,其余的17位都可用于指令的编码,所以一共可以有217=条R-类型的指令。I-类型指令指的是操作数中有部分来自于通用寄存器,而另一部分是来自于指令本身的立即数,包括带有立即数的算术运算指令、比较指令、逻辑运算指令、移位指令、数据存储器访问指令和条件分支指令。除移位指令外,立即数都是16位的,而另外还需要两个寄存器操作数,所以共需要16+5×2=26位编码,还剩余6位编码,所以可以有26=64条I-类型的指令。对于移位指令,由于操作数是32位的,所以移位位数最多为32位,则只需要5位立即数即可,因此共需要5+5×2=15位编码,剩余17位可用于指令编码。J-类型指令只有一条:无条件分支指令Branch,它采用6位指令编码(为了与I-类型指令格式对齐),剩余的26位用于指明分支的目的地址到当前程序计数器的偏移量。假设采用补码表示法,则最大偏移量在-225~+(225-1)之间,即-0x~+0x01FFFFFF之间。而前面讨论的条件分支使用16位偏移量,所以最大偏移量在-215~+(215-1)之间,即-0x~+0x00007FFF之间。通过以上讨论,可以定义出以下指令格式。对于R-类型指令,指令中包括三个5位的通用寄存器地址域和一个17位的指令编码域,为了与I-类型及J-类型指令保持格式一致,将指令编码域分成了11位和6位两个部分,参见图6-55。图6-55R-类型指令格式图中的A、B、C为通用寄存器地址,OPX位11位指令编码,OP位6位指令编码。对于I-类型指令,指令中包括四个域:两个5位的通用寄存器地址、6位指令编码和16位地址偏移量,可以表示为图6-56所示的格式。图6-56I-类型指令格式图中的A、B为通用寄存器地址,IMM16为16位立即数(对于移位指令,只用最低5位),OP为指令编码。对于J-类型指令,指令中包括两个域:6位指令编码和26位地址偏移量,可以表示为图6-57所示的格式。图6-57J-类型指令格式图中的OP部分即为指令编码,而IMMED26是地址偏移量。从以上三种类型指令的编码格式可以看出,SimpleRISC处理器的格式比较统一,设计起来也就会比较简单。实际上,几乎所有的RISC处理器的指令格式都采用这里的设计思想,使得设计简单,译码逻辑也简单,从而提高处理器性能。更重要的是,这种简单的设计有利于使用流水线来实现,能更进一步提高处理器性能。二、SimpleRISC处理器的指令编码1、加法这里实现的是有符号数的加法,不考虑溢出。(1)寄存器操作数相加汇编语言表示形式:addrt,rs1,rs2操作说明:rt=rs1+rs2编码:(2)寄存器操作数与立即数相加汇编语言表示形式:addirt,rs,imm16操作说明:rt=rs+imm16,对imm16进行符号扩展编码:2、减法这里实现的是有符号数的减法,不考虑溢出。(1)寄存器操作数相减汇编语言表示形式:subrt,rs1,rs2操作说明:rt=rs1-rs2编码:(2)寄存器操作数与立即数相减汇编语言表示形式:subirt,rs,imm16操作说明:rt=rs–imm16,对imm16进行符号扩展编码:3、小于则置一这里实现的是无符号数的比较。(1)寄存器操作数比较汇编语言表示形式:sltrt,rs1,rs2操作说明:rt=(rs1<rs2)编码:(2)寄存器操作数与立即数比较汇编语言表示形式:sltirt,rs,imm16操作说明:rt=(rs<imm16),对imm16进行零扩展编码:4、左移汇编语言表示形式:slrt,rs,imm5操作说明:rt=rs<<imm5编码:5、右移汇编语言表示形式:rlrt,rs,imm5操作说明:rt=rs>>imm5编码:6、逻辑与这里实现的是无符号数的逻辑与。(1)寄存器操作数的逻辑与汇编语言表示形式:andlrt,rs1,rs2操作说明:rt=rs1&rs2编码:(2)寄存器操作数与立即数的逻辑与汇编语言表示形式:andlirt,rs,imm16操作说明:rt=rs&imm16,对imm16进行零扩展编码:7、逻辑或这里实现的是无符号数的逻辑或。(1)寄存器操作数的逻辑或汇编语言表示形式:orlrt,rs1,rs2操作说明:rt=rs1|rs2编码:(2)寄存器操作数与立即数的逻辑或汇编语言表示形式:orlirt,rs,imm16操作说明:rt=rs|imm16,对imm16进行零扩展编码:8、逻辑非这里只实现了寄存器操作数的逻辑非。汇编语言表示形式:notlrt,rs操作说明:rt=~rs编码:9、从存储器取数据汇编语言表示形式:loadrt,rs,imm16操作说明:rt=memory[rs+imm16],对imm16进行符号扩展编码:10、存储数据到存储器汇编语言表示形式:storers1,rs2,imm16操作说明:memory[rs2+imm16]=rs1,对imm16进行符号扩展编码:11、无条件分支汇编语言表示形式:branchimm26操作说明:PC=PC+imm26,对imm26进行符号扩展编码:12、相等则分支汇编语言表示形式:beqrs1,rs2,imm16操作说明:if(rs1==rs2)PC=PC+imm16;elsePC=PC+1;对imm16进行符号扩展编码:6.4.5数据通路的建立前面我们讨论了将要设计的SimpleRISC处理器的指令集和编码,包括算术逻辑运算指令、存储访问指令和分支指令。这些指令的实现过程大致相同,而与具体的指令类型无关。实现每条指令的前两步是一样的:(1)程序计数器(PC)指向指令所在的存储单元,并从中取出指令;(2)通过指令字段的内容,选择读取一个或两个寄存器。对于取字(load)指令,只需读取一个寄存器,而其他大多数指令要求读取两个。这两步之后,为完成指令而进行的步骤则取决于具体的指令类型。然而,对三种指令类型(存储访问、算术逻辑和分支)的每一种而言,其动作大致相同,与具体操作码无关。即使是不同类型的指令,也有一定的共性。例如,所有类型的指令在读取寄存器后,都要使用算术逻辑单元(ALU)。存储访问指令用ALU计算地址,算术逻辑指令用来执行运算,分支指令用ALU进行比较。可以看出,指令的简洁和规整使许多指令的执行很相似,因而简化了实现过程。使用ALU计算完成之后,不同类型指令需要进行不同的操作。存储访问指令需要对存储单元进行读出或写入;算术逻辑指令需要将ALU产生的数据写回寄存器中;而分支指令会根据比较的结果,决定是否需要更改下条指令的地址。图6-58概括地描述了SimpleRISC的实现方式。以下将介绍其细节。这里需要加入更多的功能部件,以及功能部件间的连接,当然还要有控制单元以控制不同类型指令需要执行的操作。图6-58SimpleRISC实现,包括主要的功能部件和它们之间的主要联系开始设计数据通路比较合理的方法是先看看每种SimpleRISC处理器指令执行时所需的主要部件。先来看看每条指令需要什么数据通路部件,再用这些部件为每种指令类型建立其数据通路。在指出数据通路部件的同时,我们也会指出它们的控制信号。首先需要的部件是一个存储程序指令的地方。一个存储单元是一个状态单元,它用来存储指令,并根据所给地址提供指令,如图6-59所示。指令的地址也必须存放在一个状态单元中,我们称之为程序计数器(PC),也在图6-59中示出。最后,需要一个加法器增加PC的值以指向下条指令的地址。图6-59存取指令需要两个状态单元,计算下条指令需要一个加法器要执行任何一条指令,首先要从存储单元中将指令取出。为准备执行下一条指令,也必须把程序计数器加到指向下条指令,即向后移动4个字节。此时的数据通路,如图6-60所示,使用了图6-59中的3个部件。图6-60用于取指和程序计数器增值的数据通路的一部分现在讨论R型指令(参见图6-55)。这种指令读两个寄存器,对它们的内容进行ALU操作,再写出结果。我们将这类指令称为R型指令或算术逻辑指令(因为它们进行数学或逻辑运算)。这个指令集合包括前面介绍的add、sub、slt、and、or和not指令。这类指令的典型形式是addrt,rs1,rs2,它将读取rs1和rs2,并将结果写回到rt。处理器的32个寄存器位于一个叫做寄存器堆(registerfile)的结构中。寄存器堆即是寄存器集合,其中的寄存器都可通过指定相应的寄存器号来进行读写。寄存器堆包含了计算机的寄存器状态。另外,还需要一个ALU来对从寄存器读出的数值进行运算。由于R型指令有3个寄存器操作数,对每条指令,都要从寄存器堆读出两个数据字,再写入一个。为读出一个数据字,寄存器堆需要一个输入信号指定要读的寄存器号和输出信号指示从寄存器堆读出的结果。为写入一个数据字,寄存器堆要有两个输入:一个指定要写的寄存器号,另一个提供要写的数据。寄存器堆总是根据输入的寄存器号输出相应的寄存器内容,而写操作由写控制信号控制,在写操作发生的时钟边沿,写控制信号必须是有效的。这样,我们一共需要4个输入(3个寄存器号和1个数据)和两个输出(两个数据),如图6-61所示。输入的寄存器号为5位,可指示32个寄存器中的某一个(25=32),一条输入总线和两条输出总线宽度均为32位。图6-61中的ALU由3位控制信号控制,该ALU有两个32位输入,一个32位输出。图6-61实现R型指令的ALU操作所需的两个状态单元:寄存器堆和ALU使用寄存器堆和图6-61中的ALU的R型指令的数据通路如图6-62所示。因为寄存器号来自指令字段,我们将图6-60输出的指令与寄存器堆的寄存器号输入连在一起。图6-62R型指令的数据通路下面考虑SimpleRISC取数据指令和存储数据指令,其一般形式为:loadrt,rs,imm16或storers1,rs2,imm16在这类指令中,通过将基址寄存器rs或rs2的内容与指令中的16位带符号偏移地址相加,得到存储器地址。如果是存储数据指令,要从寄存器rs1中读出要存储的数据;如果是取数据指令,则要将从存储器中读出的数存入指定的寄存器rt中。所以,图6-61中的寄存器堆和ALU都将被用到。另外,还需要一个单元将16位的偏移地址符号扩展为32位的带符号值,以及一个存储读出和写入数据的单元。存储单元在存储指令时被写入,所以它有读、写控制信号,地址输入和要被写入存储器的数据输入。图6-63给出了这两个单元。图6-63取数据指令和存储数据指令所需的两个单元:数据存储器和符号扩展单元图6-64显示了取指操作之后,所有上述单元如何构成取数据和存储数据指令的数据通路。寄存器堆的寄存器号输入由指令提供,偏移量也是这样。经过符号扩展,偏移量成为ALU的另一个输入。图6-64加入取数据和存储数据指令之后的数据通路beq指令有2个操作数,其中两个为寄存器,用于比较是否相等,另一个是16位偏移量,用以计算相对于分支指令所在地址的分支目标地址。除了计算分支目标地址,还必须确定是顺序执行下一条指令,还是去执行分支目标地址处的指令。当分支条件为真(即操作数相等)时,分支目标地址成为新的PC,我们就说实现了分支;若操作数不等,增值后的PC将取代当前PC(就像其他一般指令一样),这时就说没有实现分支。所以,分支数据通路需要进行两个操作:计算分支目标地址和比较操作数。图6-65所示为分支数据通路。图6-65分支指令的数据通路为计算分支目标地址,分值目标通路包含了一个如图6-63所示的符号扩展单元和一个加法器。为进行比较操作,要由图6-61的寄存器堆提供两个寄存器操作数(但不需要向寄存器堆写入数据)。另外,比较操作由ALU完成。无条件跳转指令将偏移地址的低26位左移两位后,以之代替PC的低28位(由于这里讨论的指令地址是字节地址,而实际的一条指令有4个字节,所以低两位指令地址无效)。到此为止,我们已经讨论过单独的每种类型指令所需的数据通路,可以将它们合并成一个简单的数据通路,并且加上控制部分,以达到完整的实现。6.4.6完整的数据通路两个不同类型指令共享一个数据通路部件时,需要给部件连接多个输入,并设置控制信号以便在输入中进行选择。这种选择通常由数据选择器来实现。首先将图6-62所示的算术逻辑指令数据通路和图6-64所示的存储器指令的数据通路进行合并,得到如图6-66所示的数据通路。图6-66算术逻辑指令和存储器指令的数据通路合并图中,加入了一些数据选择器及相应的控制信号。图6-67所示的是加入的取指部分的数据通路。图6-67加入取指部分的数据通路现在,加上图6-65所示的分支指令的数据通路,我们可以把所有部件加在一起建立起SimpleRISC处理器的一个简单数据通路。图6-68给出了把独立部件连在一起之后得到的数据通路。图6-68SimpleRISC处理器的数据通路图中的指令和数据存储器使用总线接口单元BIU来进行连接。在完成这个数据通路之后,可以加上控制单元。控制单元接受指令输入,并产生每个状态单元的同步信号,每个数据选择器和ALU的控制信号。加入控制单元后的数据通路如图6-69所示。图6-69有控制单元的完整数据通路6.4.7SimpleRISC处理器的实现通过前面的介绍,我们知道SimpleRISC处理器需要设计如下子模块:(1)算术逻辑单元;(2)寄存器堆;(3)程序计数器;(4)总线接口单元;(5)控制器。以下分别介绍各个模块的设计。一、算术运算单元算术逻辑单元根据输入的操作码,对两个源操作数进行算术逻辑运算,并产生相应的输出。程序6-6是算术逻辑单元ALU的设计。【程序6-6】`timescale1ns/1nsmoduleArithmeticLogicUnit(nReset,Clock,OperationCode,Source1,Source2,Destination);inputnReset,Clock;input[3:0]OperationCode;input[31:0]Source1,Source2;output[31:0]Destination;parameterADD=4'd0,SUB=4'd1,SLT=4'd2,SL=4'd3,SR=4'd4,AND=4'd5,OR=4'd6,NOT=4'd7,CMP=4'd8;reg[31:0]Destination;always@(negedgenResetorposedgeClock)beginif(!nReset)Destination<=32'd0;elsebegincase(OperationCode)ADD:Destination<=Source1+Source2;SUB:Destination<=Source1-Source2;SLT:Destination<=(Source1<Source2)?32'd1:32'd0;SL:Destination<=Source1<<Source2[4:0];SR:Destination<=Source1>>Source2[4:0];AND:Destination<=Source1&Source2;OR:Destination<=Source1|Source2;NOT:Destination<=~Source1;CMP:Destination<=(Source1==Source2)?32'd1:32'd0;default:Destination<=32'hxxxxxxxx;endcaseendendendmodule二、寄存器堆寄存器堆具有三个地址输入端口,一个数据总线输入和两个数据总线输出端口。内部结构可以用图6-70和图6-71,其中,图6-70表示寄存器堆的读端口,而图6-71表示写端口。图6-70寄存器堆读端口图6-71寄存器堆写端口由此可以得到寄存器堆的VerilogHDL描述,参见程序6-7。寄存器堆设计中,假定0号寄存器总是0值,且不能写入。【程序6-7】`timescale1ns/1nsmoduleGeneralPurposeRegister(nReset,Write,WriteAddress,WriteData,ReadAddress1,ReadAddress2,ReadData1,ReadData2);inputnReset,Write;input[4:0]WriteAddress,ReadAddress1,ReadAddress2;input[31:0]WriteData;output[31:0]ReadData1,ReadData2;reg[31:0]GPRs[31:1];integeri;always@(negedgenResetorposedgeWrite)beginif(!nReset)for(i=1;i<=31;i=i+1)GPRs[i]<=32'd0;elseif(WriteAddress!=5'd0)GPRs[WriteAddress]<=WriteData;endassignReadData1=(|ReadAddress1)?GPRs[ReadAddress1]:32'd0;assignReadData2=(|ReadAddress2)?GPRs[ReadAddress2]:32'd0;endmodule三、程序计数器程序计数器输入一个Branch用于判断是否发生跳转,如果发生跳转时,将程序计数器当前值加上输入的偏移量PCBias作为下一条指令的地址;而如果没有跳转发生时,直接将程序计数器加1。这里需要注意,这里的程序计数器设计时考虑采用的是存储器的字地址,所以增值是1,而不是前面图6-65中的4。程序计数器的Verilog程序参见程序6-8。【程序6-8】`timescale1ns/1nsmoduleProgramCounter(nReset,Clock,Branch,PCBias,PCValue);inputnReset,Clock,Branch;input[31:0]PCBias;output[31:0]PCValue;reg[31:0]PCValue;always@(negedgenResetorposedgeClock)beginif(!nReset)PCValue<=32'd0;elseif(Branch)PCValue<=PCValue+PCBias;elsePCValue<=PCValue+1;endendmodule四、总线接口单元总线接口单元只是一些三态缓冲器,所以直接在SimpleRISC处理器的顶层设计中实现,而这里就不专门进行设计。五、控制器控制器需要根据不同的指令,产生不同的控制信号,以控制数据通路中的部件能同步工作,并且使得数据通路中相应的数据选择器作出正确的选择。这里的控制器比较复杂,使用一个有限状态机来实现,图6-72给出了控制器的状态转移图。图6-72控制器状态转移图从图中可以看出,SimpleRISC处理器的控制器具有5个状态。起始状态是取指状态Fetch,Fetch状态使得指令存储器的读控制信号有效,从而取到要执行的指令。取到指令之后,要对其进行译码,所以进入了第二个指令译码状态Decode,Decode根据前一状态取到的指令进行译码操作,产生相应的数据选择器选择信号等控制信号。指令译码之后,进入指令执行状态Execute,这一步大多数指令使用ALU进行运算,然后根据指令类型选择是进行存储器操作还是直接将运算结果写回寄存器堆。对于存储器访问指令,执行状态之后计算出进行存储器访问的地址,之后进入存储器访问状态Memory进行相应的存储器操作。对于其他不需要进行存储器访问的指令,直接进入寄存器回写状态Write,将计算结果写回寄存器。程序6-9是控制器的Verilog实现。【程序6-9】`timescale1ns/1nsmoduleController(nReset,Clock,Instruction,ALUClock,GPRClock,PCClock,ALUCode,ALUSourceSelect,MemToReg,RegToMem,Branch,UnconditionalBranch,RegSrc,RegDst,i_Read,d_Read,d_Write);inputnReset,Clock;input[16:0]Instruction;outputALUClock,GPRClock,PCClock;output[3:0]ALUCode;output[1:0]ALUSourceSelect;outputMemToReg,RegToMem;outputBranch,UnconditionalBranch;outputRegSrc,RegDst;outputi_Read,d_Read,d_Write;regALUClock,GPRClock,PCClock;reg[3:0]ALUCode;reg[1:0]ALUSourceSelect;regMemToReg,RegToMem;regBranch,UnconditionalBranch;regRegSrc,RegDst;regi_Read,d_Read,d_Write;parameterADD=4'd0,SUB=4'd1,SLT=4'd2,SL=4'd3,SR=4'd4,AND=4'd5,OR=4'd6,NOT=4'd7,CMP=4'd8;parameterFetch=5'b00001,Decode=5'b00010,Execute=5'b00100,Memory=5'b01000,Write=5'b10000;reg[5:0]OperationCode;reg[4:0]State;always@(negedgenResetorposedgeClock)beginif(!nReset)beginALUClock<=1'b0;GPRClock<=1'b0;PCClock<=1'b0;ALUCode<=4'b0000;ALUSourceSelect<=2'b00;MemToReg<=1'b0;RegToMem<=1'b0;Branch<=1'b0;UnconditionalBranch<=1'b0;RegSrc<=1'b0;RegDst<=1'b0;i_Read<=1'b0;d_Read<=1'b0;d_Write<=1'b0;OperationCode<=6'b;State<=Fetch;endelsebegincase(State)Fetch:beginALUClock<=1'b0;GPRClock<=1'b0;PCClock<=1'b0;ALUCode<=4'b0000;ALUSourceSelect<=2'b00;MemToReg<=1'b0;RegToMem<=1'b0;Branch<=1'b0;UnconditionalBranch<=1'b0;RegSrc<=1'b0;RegDst<=1'b0;i_Read<=1'b1;d_Read<=1'b0;d_Write<=1'b0;OperationCode<=6'b;State<=Decode;endDecode:beginif(Instruction[5:0]==6'b)//R-Typebeginif(Instruction[16:6]==11'h001)//addbeginALUCode<=ADD;ALUSourceSelect<=2'b00;RegDst<=1'b1;endelseif(Instruction[16:6]==11'h002)//subbeginALUCode<=SUB;ALUSourceSelect<=2'b00;RegDst<=1'b1;endelseif(Instruction[16:6]==11'h003)//sltbeginALUCode<=SLT;ALUSourceSelect<=2'b00;RegDst<=1'b1;endelseif(Instruction[16:6]==11'h004)//andbeginALUCode<=AND;ALUSourceSelect<=2'b00;RegDst<=1'b1;endelseif(Instruction[16:6]==11'h005)//orbeginALUCode<=OR;ALUSourceSelect<=2'b00;RegDst<=1'b1;endelseif(Instruction[16:6]==11'h006)//notbeginALUCode<=NOT;ALUSourceSelect<=2'b11;RegDst<=1'b1;endelse//unimplementbeginALUCode<=ADD;ALUSourceSelect<=2'b11;RegDst<=1'b0;endMemToReg<=1'b0;RegToMem<=1'b0;Branch<=1'b0;UnconditionalBranch<=1'b0;RegSrc<=1'b0;endelsebeginif(Instruction[5:0]==6'b)//addibeginALUCode<=ADD;ALUSourceSelect<=2'b10;MemToReg<=1'b0;RegToMem<=1'b0;Branch<=1'b0;UnconditionalBranch<=1'b0;RegSrc<=1'b0;RegDst<=1'b1;endelseif(Instruction[5:0]==6'b)//subibeginALUCode<=SUB;ALUSourceSelect<=2'b10;MemToReg<=1'b0;RegToMem<=1'b0;Branch<=1'b0;UnconditionalBranch<=1'b0;RegSrc<=1'b0;RegDst<=1'b1;endelseif(Instruction[5:0]==6'b)//sltibeginALUCode<=SLT;ALUSourceSelect<=2'b01;MemToReg<=1'b0;RegToMem<=1'b0;Branch<=1'b0;UnconditionalBranch<=1'b0;RegSrc<=1'b0;RegDst<=1'b1;endelseif(Instruction[5:0]==6'b)//andibeginALUCode<=AND;ALUSourceSelect<=2'b01;MemToReg<=1'b0;RegToMem<=1'b0;Branch<=1'b0;UnconditionalBranch<=1'b0;RegSrc<=1'b0;RegDst<=1'b1;endelseif(Instruction[5:0]==6'b)//oribeginALUCode<=OR;ALUSourceSelect<=2'b01;MemToReg<=1'b0;RegToMem<=1'b0;Branch<=1'b0;UnconditionalBranch<=1'b0;RegSrc<=1'b0;RegDst<=1'b1;endelseif(Instruction[5:0]==6'b)//loadbeginALUCode<=ADD;ALUSourceSelect<=2'b10;MemToReg<=1'b1;RegToMem<=1'b0;Branch<=1'b0;UnconditionalBranch<=1'b0;RegSrc<=1'b0;RegDst<=1'b1;endelseif(Instruction[5:0]==6'b)//storebeginALUCode<=ADD;ALUSourceSelect<=2'b10;MemToReg<=1'b0;RegToMem<=1'b1;Branch<=1'b0;UnconditionalBranch<=1'b0;RegSrc<=1'b1;RegDst<=1'b0;endelseif(Instruction[5:0]==6'b)//branchbeginALUCode<=ADD;ALUSourceSelect<=2'b11;MemToReg<=1'b0;RegToMem<=1'b0;Branch<=1'b1;UnconditionalBranch<=1'b1;RegSrc<=1'b0;RegDst<=1'b0;endelseif(Instruction[5:0]==6'b)//beqbeginALUCode<=CMP;ALUSourceSelect<=2'b00;MemToReg<=1'b0;RegToMem<=1'b0;Branch<=1'b1;UnconditionalBranch<=1'b0;RegSrc<=1'b1;RegDst<=1'b0;endelseif(Instruction[5:0]==6'b)//slbeginALUCode<=SL;ALUSourceSelect<=2'b01;MemToReg<=1'b0;RegToMem<=1'b0;Branch<=1'b0;UnconditionalBranch<=1'b0;RegSrc<=1'b0;RegDst<=1'b1;endelseif(Instruction[5:0]==6'b)//srbeginALUCode<=SR;ALUSourceSelect<=2'b01;MemToReg<=1'b0;RegToMem<=1'b0;Branch<=1'b0;UnconditionalBranch<=1'b0;RegSrc<=1'b0;RegDst<=1'b1;endelse//unimplementbeginALUCode<=ADD;ALUSourceSelect<=2'b11;MemToReg<=1'b0;RegToMem<=1'b0;Branch<=1'b0;UnconditionalBranch<=1'b0;RegSrc<=1'b0;RegDst<=1'b0;endendi_Read<=1'b0;OperationCode<=Instruction[5:0];State<=Execute;endExecute:beginALUClock<=1'b1;if(OperationCode==6'b)//loadState<=Memory;elseif(OperationCode==6'b)//storeState<=Memory;else//othersState<=Write;endMemory:beginALUClock<=1'b0;if(OperationCode==6'b)//loadd_Read<=1'b1;elseif(OperationCode==6'b)//stored_Write<=1'b1;State<=Write;endWrite:beginPCClock<=1'b1;GPRClock<=1'b1;d_Write<=1'b0;State<=Fetch;enddefault:beginALUClock<=1'b0;GPRClock<=1'b0;PCClock<=1'b0;ALUCode<=3'd0;ALUSourceSelect<=2'd0;MemToReg<=1'b0;RegToMem<=1'b0;Branch<=1'b0;UnconditionalBranch<=1'b0;RegSrc<=1'b0;RegDst<=1'b0;i_Read<=1'b0;d_Read<=1'b0;d_Write<=1'b0;OperationCode<=6'd0;State<=Fetch;endendcaseendendendmodule下面针对控制器的Verilog程序,详细介绍各个状态的工作过程。在介绍控制器各个状态之前,首先需要了解控制器程序外部信号接口和内部信号。输入端口:☆nReset——复位输入端,连接到系统复位上,低电平有效。☆Clock——时钟输入端,连接到系统时钟上,上升沿有效。☆Instruction——指令输入端,只连接到指令输入的低16位,因为根据指令编码格式,只有指令的低16位参与控制,而高16位是作为寄存器地址或立即数等,并没有参与控制。输出端口:☆ALUClock——ALU操作控制信号,当产生ALUClock的上升沿时,ALU将计算结果输出。☆GPRClock——寄存器堆写控制信号。☆PCClock——程序计数器增值控制信号。☆ALUCode——ALU操作码,ALU根据这个操作码执行相应的操作。☆ALUSourceSelect——ALU的第二个源操作数选择控制信号。☆MemToReg——数据存储器到寄存器加载控制信号。☆RegToMem——寄存器到数据存储器总线控制信号。☆Branch——指示是否为分支指令,包括无条件分支指令和相等则分支指令都会使Branch信号有效。☆UnconditionalBranch——指示是否为无条件分支指令。☆RegSrc——寄存器堆读端口地址的选择。☆RegDst——寄存器堆写端口地址的选择。☆i_Read——指令存储器读控制信号。☆d_Read——数据存储器读控制信号。☆d_Write——数据存储器写控制信号。内部信号:☆与输出端口相应的reg型信号分别对应相应的输出端口。☆OperationCode——指令的最低六位,对于所有指令,这六位都作为有效的控制信号。☆State——状态信号,用于保存控制器的当前操作状态。1、复位操作复位信号nReset有效时,内部各寄存器都进行复位,同时将控制器的状态设置为第一个状态——取指令Fetch。这样,复位结束后,控制器就开始取出指令,然后进行后面的操作。2、取指令Fetch控制器首先进行的第一步操作就是取指令操作。取指令这一步只需要将指令寄存器读控制信号i_Read设置为有效即可,这时读出的指令是当前PC所指地址处的指令。取指令结束后,就可以开始进行指令译码操作,所以状态转移到Decode状态。3、指令译码Decode在指令译码状态,控制器状态机根据前面取到的指令,对其进行译码。译码的结果用于产生寄存器堆的读、写地址的选择,ALU的源操作数选择和操作代码,存储器端口控制以及分支控制。对于不同的指令,这些控制信号各不相同,下面分别进行说明。★RegSrc:用于寄存器堆读端口2地址的选择。根据指令编码格式,只有store指令和beq指令使用指令的第27位到第31位作为寄存器堆读端口2的地址,而其余的指令都使用指令的第17位到第21位作为寄存器堆读端口2的地址。★RegDst:用于寄存器堆写端口地址的选择。同样,根据指令编码格式,只有store指令、branch指令和beq指令不需要写回,这时设置其寄存器堆写端口地址为零;而且对所有的未定义指令,为了避免产生误操作,也将其寄存器堆写端口地址设置为零;其余指令的寄存器堆写端口地址使用指令的第27位到第31位。★MemToReg:数据存储器到寄存器加载控制信号,用于选择寄存器堆的写数据来源,只有load指令选择来自存储器的数据,而其余指令都选择来自ALU的输出。★ALUCode:这个信号用于控制ALU进行操作,对于加法指令、取数据指令、存储数据指令、无条件分支指令以及其他指令集外的未定义指令,ALUCode都设置为进行加法操作。而对于这些指令以外的指令集中的指令,则设置为相应的操作,需要注意,对于相等则分支指令ALUCode需要设置为CMP,以进行相等比较。★ALUSourceSelect:ALU的第二个源操作数选择控制信号,ALU的第二个源操作数可以有四种选择,分别是寄存器堆的数据、指令中16位立即数符号扩展的结果、指令中16位立即数或5位立即数无符号扩展的结果或是全零。除了not指令以外的寄存器指令和beq指令都使用寄存器堆的数据;addi指令、subi指令、load指令和store指令都使用符号扩展的结果;slti指令、andi指令、ori指令、sl指令和sr指令使用的是无符号扩展的结果;not指令、branch指令以及所有的未定义指令都使用零作为ALU的第二个源操作数。★Branch:无条件分支及条件分支指令的Branch都输出有效,而其余指令输出Branch无效。★UnconditionalBranch:只有无条件分支指令的UnconditionalBranch输出有效,而其余指令输出UnconditionalBranch无效。除了以上控制信号外,还需要将指令存储器的读控制信号i_Read撤销,同时要将指令的低6位保存起来,下面的状态还会用到。指令译码之后,就进入到执行状态。4、执行指令Execute由于指令译码时已经做好了大部分的准备工作,所以执行指令变得很简单,直接产生一个ALU的输出时钟即可。实际上,在刚才译码的过程中,ALU已经将运算结果计算出来了,只是没有输出到ALU的输出端口。在执行阶段,还将根据译码状态保存的操作码(指令的低6位)来决定下一步是进行存储器操作还是直接进行写回操作。只有load和store指令需要进行存储器操作,而其余指令直接进入写回操作状态。需要注意,下面将会看到,写回操作还涉及到PC的修改,所以即使是分支指令(不需要写寄存器堆)也需要进行写回操作。5、存储器操作Memory存储器操作状态根据是load指令还是store指令,来决定是让数据存储器的读控制信号d_Read还是让数据存储器的写控制信号d_Write有效。存储器操作后,不论是load指令还是store指令都进入写回状态。6、寄存器写回Write写回状态主要产生寄存器堆写控制信号和程序计数器的写控制信号。设计好前面这些功能模块后,就可以根据SimpleRISC处理器的数据通路,将这些部件连接起来,实现SimpleRISC处理器。程序6-10是SimpleRISC处理器的顶层设计模块。【程序6-10】`timescale1ns/1nsmoduleSimpleRISC(nReset,Clock,i_Address,i_Data,i_Read,d_Address,d_Data,d_Read,d_Write);inputnReset,Clock;input[31:0]i_Data;output[31:0]i_Address,d_Address;outputi_Read,d_Read,d_Write;inout[31:0]d_Data;wire[31:0]instruction;wire[15:0]imm16;wire[25:0]imm26;assigninstruction=i_Data;assignimm16=instruction[21:6];assignimm26=instruction[31:6];wirePCClock,Branch;wire[31:0]PCBias;ProgramCounterPC(.nReset(nReset),.Clock(PCClock),.Branch(Branch),.PCBias(PCBias),.PCValue(i_Address));wireGPRClock;wire[4:0]GPRWriteAddr,GPRReadAddr1,GPRReadAddr2;wire[31:0]GPRWriteData,GPRReadData1,GPRReadData2;assignGPRReadAddr1=instruction[26:22];GeneralPurposeRegisterGPR(.nReset(nReset),.Write(GPRClock),.WriteAddress(GPRWriteAddr),.WriteData(GPRWriteData),.ReadAddress1(GPRReadAddr1),.ReadAddress2(GPRReadAddr2),.ReadData1(GPRReadData1),.ReadData2(GPRReadData2));wireALUClock;wire[3:0]ALUCode;wire[1:0]ALUSourceSelect;wire[31:0]ALUSource2;wire[31:0]ALUOut;ArithmeticLogicUnitALU(.nReset(nReset),.Clock(ALUClock),.OperationCode(ALUCode),.Source1(GPRReadData1),.Source2(ALUSource2),.Destination(ALUOut));wire[31:0]UnsignedImm32;UnsignExtendU1(.Din(imm16),.Dout(UnsignedImm32));wire[31:0]SignedImm32;SignExtend#(16)U2(.Din(imm16),.Dout(SignedImm32));assignALUSource2=(ALUSourceSelect==2'b00)?GPRReadData2:((ALUSourceSelect==2'b01)?UnsignedImm32:((ALUSourceSelect==2'b10)?SignedImm32:32'd0));wire[31:0]PCBias32;SignExtend#(26)U3(.Din(imm26),.Dout(PCBias32));wireMemToReg;wireRegToMem;assignd_Data=RegToMem?GPRReadData2:32'hzzzzzzzz;assignGPRWriteData=MemToReg?d_Data:ALUOut;assignd_Address=ALUOut;wireRegSrc,RegDst;wireUnconditionalBranch;wireBranchEnable;ControllerCU(.nReset(nReset),.Clock(Clock),.Instruction(instruction[16:0]),.ALUCode(ALUCode),.PCClock(PCClock),.MemToReg(MemToReg),.RegToMem(RegToMem),.Branch(BranchEnable),.UnconditionalBranch(UnconditionalBranch),.ALUClock(ALUClock),.ALUSourceSelect(ALUSourceSelect),.GPRClock(GPRClock),.RegSrc(RegSrc),.RegDst(RegDst),.i_Read(i_Read),.d_Read(d_Read),.d_Write(d_Write));assignPCBias=UnconditionalBranch?PCBias32:SignedImm32;assignBranch=(UnconditionalBranch||(BranchEnable&&ALUOut[0]));assignGPRReadAddr2=RegSrc?instruction[31:27]:instruction[21:17];assignGPRWriteAddr=RegDst?instruction[31:27]:5'd0;endmodule6.4.8SimpleRISC应用系统前面已经设计好了SimpleRISC处理器,这里需要使用SimpleRISC处理器来设计一个处理器系统。一个完整的处理器系统应该包括至少中央处理器和存储器,这里的中央处理器就是我们刚才设计的SimpleRISC处理器,而还需要设计存储器,包括程序存储器和数据存储器;在我们设计的处理器系统中,还包含一个用于输出的端口,SimpleRISC处理器把这个输出端口作为数据存储器来访问。在设计我们的SimpleRISC处理器系统之前,首先应该确保前面设计的SimpleRISC处理器能正常工作,所以下面要做的第一项工作就是测试我们刚设计的SimpleRISC处理器。一、SimpleRISC处理器测试SimpleRISC处理器测试应该包括所有指令的测试,这里选取几条指令进行测试。首先让SimpleRISC处理器执行以下指令序列:【SimpleRISC指令序列1】①addir1,r0,0x4000②addir2,r0,0x2000③addr3,r1,r2here:④branchhere以上指令序列中,第①条指令是将立即数0x4000的值保存在1号寄存器(r1)中;同样,第②条令是将立即数0x2000的值保存在2号寄存器(r2)中;而第③条指令是将1号寄存器的值和2号寄存器的值相加,和存放在3号寄存器(r3)中;第④条指令相当于让处理器停机(当前位置跳转)。根据指令的编码格式及寄存器编号的原则,上述指令序列可以用以下的十六进制代码表示,这些十六进制代码即是以上指令序列的机器码。①0x,②0x,③0x,④0x将这些机器码作为指令输入,并将上面设计的SimpleRISC处理器进行综合、适配,然后在QuartusII中进行仿真,得到如图6-73所示的仿真结果。图6-73SimpleRISC指令序列1仿真结果图中的最后三行的信号分别是寄存器堆中的r1、r2和r3。上面的指令序列主要测试了寄存器指令和立即数指令的操作,以下再对移位指令、逻辑运算指令和存储器访问指令进行测试。为此,让SimpleRISC处理器执行以下指令序列:【SimpleRISC指令序列2】①orir1,r0,0x2000②storer1,r0,0x80③slr2,r1,8④loadr3,r1,0⑤andr1,r2,r3here:⑥branchhere以上指令序列中,第①条指令使用立即数或操作给寄存器r1设置初始值0x2000

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论