1.1 复杂指令集与精简指令集概述
CISC的典型代表就是Intel和AMD的一系列CPU。因为早期Intel的系列CPU被命名为80x86,所以俗称x86系列[其他比较知名的CISC CPU包括IBM Z系列(z/Architecture)、DEC VAX780和Motorola 68000等],它的一个主要的设计思想是尽可能多地对指令流进行编码,这导致了指令长度和格式可变。预期指令流越紧凑,指令内存和缓存的利用率就越高,执行一段代码所需的指令字节就越少。CISC的设计旨在通过将复杂性转移到硬件中来降低内存读写的高成本(在CISC出现的20世纪70年代,计算机内存较小且非常昂贵)。强调代码密度,一些指令对一个变量依次执行多个操作,试图通过最小化CPU为执行给定任务而必须执行的指令数量来提高性能。这是通过在单个复杂指令中嵌入一些低级指令来完成的,也就是说复杂的ISA必须使用微码(Microcode)实现,因为直接用硬件实现会太复杂。微指令(Micro-instruction)是一种非常简单的指令形式,可以看作是一组同时执行的微操作(Micro-operations)。微操作是CPU可以实现的最基础的操作。一个复杂的指令的执行实际是通过执行一系列微指令/微操作来实现的。CISC的大体框架如图1-1所示。
从图1-1中可以看到一个特殊的单元:微程序控制单元,它可以使用存储在控制存储器中的一系列微操作并生成控制信号。创建控制信号的程序称为微程序,一个微程序有一组微操作,也称为控制字(Control Word)。每个微操作都有N-bit字并具有不同的位模式,根据控制字的位模式区分控制信号。控制单元访问微程序控制单元产生的控制信号并实现CPU硬件的功能,同时指令和数据通路从缓存和主存储器中获取指令的操作码(Operation Code)和操作数(Operand)。
· 图1-1 CISC架构示意
简而言之,CISC指令的执行流程可以大致描述为:
1)通过微操作地址生成器从指令缓存中提取指令。
2)对从指令缓存中提取的指令进行译码,检索执行指令中相应操作所需的微程序的起始地址并将该地址加载到微程序计数器。
3)读取与上述起始地址对应的控制字。随着执行的进行,微程序地址生成器将递增微程序计数器的值以读取微程序的连续控制字(控制类指令例外,不是顺序递增取指,本书后续章节会加以说明,此处不赘述)。
对于一个操作,CISC风格的指令原则上是最小化指令长度。例如,对于操作“A=B+C”,CISC指令翻译为:
在CISC中,Move指令用来访问内存操作数,指令的一般格式为:
因为CISC的指令是可变长度的,并且具有多个操作数,指令占用的内存不止一个字(Word),所以取操作数可能需要几个周期,为硬件设计增加了复杂度。CISC除了包括五种基本寻址方式(立即模式、直接/绝对模式、寄存器模式、间接模式和索引模式)之外,还有三种额外的寻址模式:自动递增模式、自动递减模式和相对模式。所谓自动递增模式,是指操作数的有效地址是寄存器的内容,当访问寄存器的内容后,它会自动递增以指向下一个操作数的内存位置;自动递减模式中操作数的有效地址也是寄存器的内容,但是,这种模式下最初是将寄存器的内容递减,然后将寄存器的内容用作操作数的有效地址。相对模式类似于变址寄存器模式,通过在通用寄存器的内容上添加一个常量来获得有效地址。在相对模式下,使用程序计数器而不是通用寄存器,这种寻址方式用于在内存中引用大范围的区域。
CISC的优点主要在于:
· 代码大小相对较短,从而最大限度地减少了内存需求。
· 单条指令的执行可以完成多个子任务。
· 复杂的寻址方式使内存访问更加灵活,并且指令可以直接访问内存位置。
CISC的缺点主要是硬件实现复杂,并且虽然编译出的代码量小,但执行一条指令需要多个时钟周期,可能降低CPU的整体性能。另外,CISC旨在当内存较小且成本较高时将内存需求降至最低,但是现在情况与CISC设计之初相比发生了变化,内存变得大而便宜。与此同时,即使引入微操作的设计,CISC也由于生态原因需要不断向后兼容,背上了沉重的“历史包袱”。
RISC的典型代表是ARM和MIPS架构的CPU(当然后续的龙芯和RISC-V也是)。相比于CISC,RISC具有简单的指令集和寻址模式,RISC风格的指令在内存中占用一个字(Word)而不是CISC的多个字,其指令的执行速度更快,一般每条指令只需要一个时钟周期(此处需要说明,整型类指令大部分一般一个时钟周期能执行完运算,浮点类指令只有极少数可以做到一个时钟周期能执行完成,大部分的执行都是几个时钟周期)。RISC的大体框架如图1-2所示。
· 图1-2 RISC架构示意
RISC强调使用寄存器而不是内存,因为寄存器是访问最快的可用资源。RISC中的通用寄存器放置在与运算单元和控制单元接近的位置上,指令对寄存器中存储的操作数进行操作。在RISC的架构示意图中可以看到,其中一个特殊的单元称为硬连线控制单元(Hardwired Control Unit),其产生调节RISC硬件工作的控制信号。该单元基于硬件创建,产生的控制信号用以控制指令在正确的时间以正确的顺序执行。
此处没有CISC架构中的微程序控制单元,因为RISC中的所有指令都很简单,是硬连线的,不需要控制存储。对于每个操作,给予对应的硬连线,即使用连接的电路使指令中的功能或操作固化。RISC中采用硬件控制以实现快速的指令译码,并采用较少的指令和简单的寻址模式,通过固定的指令格式来简化指令译码和硬连线控制逻辑。同时,设计访存指令Load(加载)和Store(存储)来执行内存操作,其他指令均对寄存器操作。
RISC指令的执行流程可以大致描述为:CPU前端提取当前执行的指令,并生成操作数和操作数寻址模式的操作码位。译码单元接收并解析指令的操作和寻址模式。每条指令都以步进的方式执行,都是基于取指、译码、(寄存器)取操作数、执行和内存操作这五个基本步骤,硬连线控制单元需要知道指令执行的当前阶段(流水线中的位置)。
对于上文提到的操作“A=B+C”,RISC指令翻译为:
可以看到两条CISC指令完成的操作需要四条RISC指令完成。然而,RISC指令大小恒定,格式规则。RISC通过将源程序代码直接编译成微码(即RISC指令)来消除通过微操作来解释指令的开销,从而将微代码直接交给给编译器优化。
RISC指令具有简单的寻址方式,其寻址模式有以下几种。
· 立即数寻址模式:这种寻址模式明确指定指令中的操作数。
· 寄存器寻址模式:这种寻址模式描述了保存操作数的寄存器。
· 绝对寻址模式:这种寻址模式描述了指令中内存位置的名称,它用于在程序中声明全局变量。
· 寄存器间接寻址方式:这种寻址方式描述了具有指令中实际操作数地址的寄存器。
· 索引寻址方式:这种寻址方式在指令中提供了一个寄存器,当向其中添加一个常量时就可以得到实际操作数的地址。
RISC的优点主要在于:
· 指令是简单的微码(机器指令),使用硬连线来加快执行速度。
· 寻址方式简单。
· 指令执行速度快,大部分指令都在CPU寄存器上运行,不需要为每条指令访问内存。
· 流水线设计容易,所有指令的长度都是固定的,同一类指令的指令码编码规则类似。
与CISC相比,虽然RISC指令的长度减小了,但需要更多的指令来执行操作,所以实际上程序的长度增加了。由于RISC中指令是硬连线的,因此任何指令的修改都是需要花费成本的。另外,RISC指令不允许直接内存到内存传输,需要加载和存储指令才能做到。
“CISC vs.RISC”一直是CPU设计领域讨论的热门话题,与其翻译成“CISC与RISC对比”似乎不如翻译成“CISC与RISC之争”更加贴切。争论持续存在,然而有两点基本达成共识:第一,CISC与RISC指的是ISA的复杂度而非硬件实现的复杂度,所谓的“精简(Reduced)”并非意味着硬件上的精简;第二,CISC与RISC之间的界限越来越模糊,并不一定完全遵循ISA的原始定义。
探讨CISC与RISC对比的观点大致分为两派:以ISA为中心和以硬件实现为中心。以ISA为中心的观点认为,RISC指令集的某些先天特征使该架构比CISC更加高效,包括使用固定长度指令和加载/存储设计。而以硬件实现为中心的观点认为微架构更具有决定性意义。相信不同的读者基于自身的经验和视角,也会有各自的看法。