跳转至

指令集体系结构

Abstract

计算机系统 Ⅰ 第十一~十二周课程内容

参考:

  • 逻辑与计算机设计基础(第五版) 第九章、指令集体系结构

ISA 概念

指令

指令(instruction)= 操作符(opcode)+ 操作数(operand)

指令格式包含长度、操作数个数、寄存器个数、寻址内存大小、寻址模式等

指令集

CPU 可以“理解”的一系列指令,以二进制机器码(machine code)的形式表现

指令集体系结构

规定了所有硬件实现的指令、规定了指令符号名称、二进制编码格式,提供了每条指令精确定义的”手册“即指令集体系结构(ISA,Instruction Set Architecture)

计算机根据 ISA 实现,ISA 可以有多种实现、ISA 使软件可以操纵硬件、ISA 定义了机器语言,ISA 规定了微处理器(microprocessor)的设计(ISA 定义了 CPU 或者一类 CPU,它包含 CPU 的内存视图、寄存器个数等信息,而不只是一系列指令的集合)

注:体系结构覆盖整个计算机,包括 ISA、组成和硬件,而不仅仅是 ISA。ISA 也不是 CPU 的体系结构(同一 ISA 有不同种 CPU)

ISA 设计

设计原则

指令格式设计原则

  • 指令长度要短
  • 为新的 opcode 留出足够空间
  • 有区分性的编码
  • 设计好操作数的个数
  • 设计好指令的对齐模式
  • 保持规范化

ISA 设计原则

  • Simplicity favors regularity
  • Make the common case fast
  • Smaller is faster
  • Good design demands good compromises

操作数

操作数个数

一般没有 4 个或以上操作数的指令,这样每个操作数不一定都会用上,而且会增加 CPU 的复杂度,并且使指令变长

大部分指令集均有 0/1/2/3 个操作数。下面以计算 Y = (A - B) / (C + (D * E)) 为例子

三操作数指令

可以有两个源操作数和一个目的操作数

SUB R1, A, B
MUL R2, D, E
ADD R2, R2, C
DIV R1, R1, R2
三操作数的指令优点是可以很灵活地指定结果存放在哪里,计算表达式的程序很短,缺点是二进制编码指令时需要用较多位数来指定三个地址

二操作数指令

压缩目的操作数到源操作数,也就是用一个源操作数同时代表目的

SUB A, B    ; A <- A - B
MUL D, E    ; D <- D * E
ADD D, C    ; D <- D + C
DIV A, D    ; A <- A / D
但是这样会更改 ABCDE 原来的值,所以需要通过 MOV 类指令来暂存用做计算:
MOV R1, A
MOV R2, D
SUB R1, B
MUL R2, E
ADD R2, C
DIV R1, R2
所以它会比三操作数指令的指令条数更多

一操作数指令

可以通过继续隐藏一个操作数的方法来构造出一操作数的指令,比如规定运算中的一个操作数和目的都为 Acc(累加器)

LDA D   ; Acc <- D
MUL E   ; Acc <- Acc * E
ADD C   ; Acc <- Acc + C
STO R1  ; R1 <- Acc
LDA A   ; Acc <- A
SUB B   ; Acc <- Acc - B
DIV R1  ; Acc <- Acc / R1
这样指令条数会更多,但是 CPU 却更容易设计

零操作数指令

因为是零操作数,所以指令运算中的三个地址都是隐式的,可以使用栈来操作,即每次运算取出栈顶两个元素然后运算后将结果压回栈中

PUSH B  ; B
PUSH A  ; B, A
SUB     ; A-B
PUSH E  ; A-B, E
PUSH D  ; A-B, E, D
MUL     ; A-B, D*E
PUSH C  ; A-B, D*E, C
ADD     ; A-B, C+D*E
DIV     ; (A-B)/(C+D*E)

可见操作数越多,指令越复杂但程序包含的指令条数少;操作数越多,指令越简单,指令执行越快,但程序包含的指令条数也会越多

寻址模式

在指令执行的过程中,如何获取操作数取决于指令的寻址模式。寻址模式指定了一个在实际访问操作数之前,解释或调整指令地址字段的规则,用这个规则可以生成操作数的有效地址(effective address)。

寻址模式可以指定到常数、寄存器和内存地址。高效的寻址模式设计可以减少指令的长度。下面是一些常见的寻址模式

立即数寻址

立即数寻址(immediate addressing)即操作数就在指令当中,也就是指令中有一个操作数字段就代表了这个常数本身而不是地址。

直接寻址

直接寻址(direct addressing)即操作数作为内存地址,直接读取该地址处的值进行操作,也就是指令中有 ADRS,但实际用的是 M[ADRS]

这就有了一个问题,如果是 32 位的地址,指令也是 32 位的,不能放下完整的地址,所以一般使用段地址和偏移地址配合,隐含一个段地址,然后 ADRS 仅使用偏移地址。但是直接寻址需要在汇编的时候就知道准确的地址位置

直接寻址的地址也可以代表一个寄存器

间接寻址

间接寻址(indire addressing)即操作数中包含内存地址,这个内存地址处的值还是一个地址,这个地址所指向的位置是实际要用的数。也就是指令中有 ADRS,但实际用的是 M[M[ADRS]]

ADRS 必须在汇编的时候固定,但是 ADRS 所指向的位置可以是变化的,这样可以使访问数组这样的操作变得更方便

ADRS 也可以是一个寄存器

变址寻址与基址寻址

二者很相似,都是将寄存器值与地址加起来的和作为实际要访问的地址

  • 变址寻址(indexed addressing),寄存器保存的是偏移地址,指令中地址保存的是基地址
  • 基址寻址(based addressing),寄存器保存的是基地址,指令中地址是偏移地址

总结:

寻址模式 表示法 含义
立即数寻址 #K K
直接寻址 K M[K]
间接寻址 (K) M[M[K]]
寄存器寻址 (Rn) M[Rn]
寄存器变址寻址 (Rm+Rn) M[Rm+Rn]
寄存器基址寻址 (Rm+X) M[Rm+X]
寄存器基址变址寻址 (Rm+Rn+X) M[Rm+Rn+X]

操作类型及编码

操作类型

一般的指令集都包含下面这些种操作类型:

  • 算数运算、逻辑运算
  • 移位运算
  • 数据传送(MOV/LOAD/STORE 之类)
  • 字符串运算
  • 控制流变化(BRANCH/JMP/CALL/RET 等)
  • 系统指令(HALT/INT 等)
  • 输入输出
  • ...

指令编码

指令编码的长度有几种

  • 变长(Variable):每条指令的长度都不一定
  • 定长(Fixed):所有指令都是同一长度
  • 混合(Hybrid):有多种指令的长度

一般如果代码的大小最重要的话选择变长指令,如果是执行表现最重要的话选择定长指令。而一些为了兼容,可能会选择混合长度指令

RISC 与 CISC

CISC

CISC(Complex Instruction Set Computer)即复杂指令集计算机

  • 编程和执行之间的语义间隔(semantic gap)小
  • 程序机器码体积小
  • 编译过程简单

CISC 类型的指令集体系结构有 x86、Intel 432、IBM 360、DEC VAX 等

RISC

RISC(Reduced Instruction Set Computer)即精简指令集计算机

  • 由 IBM 发明
  • RISC 的指令很少,而且每个指令简单,长度固定
  • CISC 每增加一个指令都会使解码变慢,从而使整个 ISA 都会变慢,但 RISC 不会
  • 运算的操作数都是寄存器(reg-reg)
  • 编译会更复杂

RISC 类型的指令集体系结构有 RISC I、RISC II、MIPS、ARM、RISC V 等

CISC 与 RISC 区别:

CISC RISC
变长指令 定长指令
大量指令和寻址模式 指令和寻址模式都很少
编码长、复杂 编码简单
包含从内存到内存的操作 只能存/取
使用微指令 没有微指令,一切都直接从指令中解码出来
语义间隔小 需要更智能的编译器

CISC 和 RISC 在发展中也不应完全割裂

寻址结构

寻址结构有几种:

  • 累加器结构,Accumulator(1960 前,如 68HC11)
  • 栈结构,Stack(1960s ~ 1970s)
  • 存储器到存储器结构,Memory-Memory(1970s ~ 1980s)
  • 寄存器到存储器结构,Register-Memory(1970s 至今,如 x86)
  • 寄存器到寄存器结构,Register-Register(1960s 至今,如 MIPS),又称装载/存储结构(Load/Store)

栈结构

  • ALU 运算没有操作数,push/pop 有一个操作数
  • 优点
    • 指令短
    • 硬件要求低
    • 编译器好写
  • 缺点
    • 效率低
    • 栈空间有限
    • 并行或流水线能力弱
    • 编译器难优化
  • 例子:60 年代的 B5500/6500 HP3000/70,现在的 Java 虚拟机

累加器结构

  • 使用单个操作数(一个显式一个隐含)
  • 指令有 ALU 运算、加载到累加器、从累加器输出到存储器
  • 优点
    • 硬件要求极低
    • 易于设计、理解
  • 缺点
    • 累加器成为瓶颈
    • 并行/流水线能力弱
    • 需要的 load store 很多,内存读写频繁
  • 例子:早期 IBM 7090 等,现在的 DSP 结构

存储器到存储器结构

  • 所有的 ALU 运算都从存储器读写
  • 优点
    • 无需使用寄存器
    • 需要的指令数量少
    • 容易写编译器
  • 缺点
    • 指令长度变化大
    • 每条指令执行的时间浮动也大
    • 巨大的存储器读写导致效率低
  • 例子:VAX

寄存器到存储器结构

  • ALU 操作中有一个存储器
  • 一般指令有两个操作数
  • 优点
    • 指令数量少
    • 指令易于编解码
  • 缺点
    • 运算结果会覆盖原值
    • 指令长度变化大
    • 指令运行时间变化大
    • 可能会限制寄存器个数
  • 例子:IBM 360/370、VAX

装载/存储结构

  • ALU 指令中不需要存储器
  • 一般指令有三个操作数
  • 优点
    • 简单、定长的指令编码
    • 指令运行的时间变化小
    • 易于进行流水线
  • 缺点
    • 指令个数多
    • 并不是所有操作都有三个操作数
    • 需要更好的编译器
  • 例子:CDC6600、CRAY-1、大部分 RISC

寄存器的优缺点

  • 优点
    • 比存储器读写更快
    • 更明确
    • 需要更少比特就能定位使用哪个寄存器
    • 节省内存读写
  • 缺点
    • 在进行过程调用的时候需要保护寄存器(存储/恢复)
    • 不能取一个寄存器的地址
    • 寄存器可存储的长度固定
    • 编译器需要更好的管理寄存器
    • 寄存器个数有限

RISC-V ISA

RISC-V ISA


最后更新: 2022年6月15日 16:45:09
创建日期: 2022年5月29日 12:21:50
回到页面顶部