体系架构 (Architecture) 导论 与 指令集体系结构 (ISA)

⚠ 转载请注明出处:作者:ZobinHuang,更新日期:Sept.22 2021


知识共享许可协议

    本作品ZobinHuang 采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可,在进行使用或分享前请查看权限要求。若发现侵权行为,会采取法律手段维护作者正当合法权益,谢谢配合。


目录

有特定需要的内容直接跳转到相关章节查看即可。

    Section 1. 计算机体系架构的定义:给出了计算机体系架构的地图;

    Section 2. 指令集架构:从多个方面讨论了在关注一种 ISA 的时候需要关注的内容;
        2.1 ISA 模型
        2.2 存储器寻址
        2.3 操作数的大小与类型
        2.4 操作指令
        2.5 指令系统的编码

    Section 3. 微体系架构:讨论了支撑 ISA 的微体系架构的基本概念;

    Section 4. 硬件:讨论了支撑微体系架构的硬件的基本概念

1. 计算机体系架构的定义

    当我们在讨论计算机体系架构的时候,我们必须对我们所讨论的东西所处的位置有一个明确的理解。我们将以上图为地图来进行相关的说明。

2. 指令集架构

    我们的操作系统面对的底下一层是 指令集架构 (Instruction Set Architecture, ISA)。ISA 是处理器的语言,是软件和硬件之间的最后一道界限。我们可以从以下七个方面展开对一个 ISA 的分析。注意,为了区分不同的知识领域,我把关于指令集架构的更多内容放到了 指令集架构 & Linux内核 专栏中,在本文我们仅会简单回顾和总结指令集架构的一些特点。

    如上图所示,ISA 的模型可以分为 4 类。现代 ISA 基本都属于后面的两种 —— register-memoryregister-register / load-store 架构,这两种架构归属于 通用寄存器架构。对于前者,一般的指令都可以用于访问内存,指令的操作数既可以在寄存器中,也可以在存储器中;而对于后者,必须使用专门的指令 (e.g. load, store, etc) 来访问内存,在执行指令的时候需要把操作数从存储器中读取到寄存器后,才能够进行计算。使用通用寄存器而不是仅使用存储器 (i.e. 堆栈/累加器结构) 进行计算的 Motivation 如下:

  • 寄存器比存储器快,且使用寄存器用来存放变量,减少了数据流量,加速程序运行(寄存器比存储器快);
  • 改善代码密度(寄存器地址比存储器地址的位数少);
  • 编译器使用寄存器很方便,比使用其他存储形式效率更高。如:(A*B) – (C*D) –(E*F) 在寄存器系统结构的计算机上,可以按任意顺序来执行三个乘法,但是在堆栈计算机上则只有一种计算顺序,因为操作数隐含在堆栈中,且必须多次载入

    对于 load-store 架构的 ISA 来说,ALU 指令中一般会有三个操作数,包含一个结果(目的操作数)和两个源操作数 (e.g. add r3, r1, r2);对于 register-memory 架构的 ISA 来说,ALU 指令一般会有两个操作数,其中有一个操作数既是结果操作数也是源操作数 (e.g. add r2, r1)。总结起来,它们有如下的特征:

类型 代表 ISA 允许的存储器操作数个数 最多操作数个数 优点 缺点
Register-Register Alpha, ARM, MIPS, PowerPC, SPARC, superH, TM32 0 3 简单、定长的指令编码;简单的代码生成模式;每条指令运行的时钟周期数相近 指令数比可以直接访问存储器的系统结构多;指令多和指令密度低使程序变得很大
Register-Memory IBM360/370, Inter80x86, Motorola 6800, T1 TMS320C54x 1 2 数据不需要专门的载入指令就可以直接访问;指令格式更加易于编码,代码密度高 由于源操作数在二元操作中被破坏了,所以操作数不是等价的;在一条指令中同时对存储器地址和寄存器号码进行编码会限制寄存器的数量;操作数位置不同使得每条指令执行所需的时钟周期不同

    我们讨论的所有 ISA 都是字节寻址的,都提供了字节(8位)、半字(16位)和字(32位)寻址,大多数的计算机还提供了双字(64位)寻址。

    当我们讨论 寻址方式 的时候,我们关注的是指令是如何定位到存储器的某一个具体位置的。下图列举了在指令中常见的存储器寻址方式。在工程实践中,立即数寻址位移量寻址 是最常见的寻址方式。

    另外,对于像 ARM 和 MIPS 等 ISA 来说,还要求处理器在访问内存的时候必须是 对齐 (align) 的。对齐指的是对于一个大小为 s 字节的数据对象来说,其在内存中的地址为 A,若 A mod s=0,则说该数据对象是对齐的。下图生动地说明了这一点。对齐访问内存的好处是简化了硬件的复杂性,并且加速了处理器对相应内存位置访问的速度 (p.s. 不对齐的内存访问将导致多次对齐的内存访问)。


    计算机二进制底层的精髓设计之一在于:不论程序员如何看待送入 ALU 的操作数是否是有符号的这件事情,ALU 总能输出正确的结果。因此底层管得着的就只有操作数宽度,而操作数具体的类型则是由程序员决定的。操作数是否带符号操作数宽度 这两个属性决定了操作数的类型,常见的操作数类型如下所示:

    基于丰富的操作数类型,大多数 ISA 支持的对操作数的操作如下所示:


    对于一个 ISA 来说,它常见的操作指令可以被分为:数据传输指令算术逻辑指令控制指令浮点指令。ISA 有一条共同的规律:使用最多的是一些简单指令,所以在处理器的设计上,简单指令的执行应该尽量快。

    其中,对于控制流指令,可以分为:条件转移无条件转移过程调用过程返回 指令。控制流指令的寻址方式可以是直接指明转移的目标地址 (p.s. 过程返回是例外,因为编译时不知道返回地址),也有 PC 相对寻址,即:即使用基于程序计数器(PC)的位移量来指定目标地址,其优点是在目标与当前指令离得不远的情况下,使用相对偏移地址可以缩减指令长度,并且使用相对寻址的程序可以载入到主存任何位置,称为位置无关,对在执行时才链接的程序可以减少工作量。

    对于控制流指令中的过程调用来说,针对寄存器现场保护来说,有两种可选的方案:调用者保存被调用者保存。前者指的是调用者调用其他过程时,必须保存在调用过程后还要使用的寄存器,被调用者则无须维护这些寄存器;而后者指的是被调用的过程必须保存它要使用的寄存器,调用者则不受这种限制。有时候,如果两个不同的过程都要访问相同的全局变量,则必须使用调用者保存方法。大多数实际使用的编译器会结合这两种方法。


    ISA 指的不仅仅是 readable 的汇编代码,更重要的还有这些汇编代码所对应的机器码的编码格式。汇编指令如何编码,将影响到高级语言编译后的最终程序大小,以及处理器的具体实现。在指令编码的设计上,我们需要着重考虑下面三点:

  • 指令编码要能尽可能多的表示各个寄存器和不同的寻址方式 (e.g. 立即数寻址、位移量寻址等)
  • 寄存器字段、寻址方式字段尽量少,以缩短指令长度。
  • 指令长度易于流水线处理

    举个例子来说,下面是 x86 的指令编码格式:

    按照长度对编码格式进行分类,可以分为以下几类:

    对于 Intel 80x86 和 VAX 来说,它们采用的是 变长编码 的形式,机器码的长度是不固定的,这种编码方式允许所有的操作使用所有的寻址方式,适合寻址方式和操作比较多的情形,并且编译后的程序通常相对较小。但是处理器在面对这种编码方式的时候会有译码复杂,不适合流水线的问题。

    对于 ARM 和 MIPS 来说,他们选择的是 定长编码 的形式。这种编码方式把操作和寻址方式组合在操作码里,通常所有的指令长度都相同。这种方式适合于寻址方式和操作比较少的情况。译码简单,适合流水线,相对变长编码来说代码量更大。

3. 微体系架构

    对于处理器来说,在 ISA 层面上相同并不意味着处理器底层如何运转相关指令的设计就是相同的:AMD Opteron 和 Intel Core i7 使用的是相同的 ISA,但是处理器底层的设计和实现上是完全不同的,因此在不同方面就具有不同的性能。我们把一种给定的 ISA 在处理器中执行的方法称为 微体系架构 (Microarchitecture)。微体系架构解决的是计算机存储器系统、存储器互联、处理器内部设计等的问题。

4. 硬件

    在微体系架构之下,我们把具体的数字逻辑设计和相关的封装技术称为 硬件 (Hardware) 层面。