第 7 章 指令系统
第一讲 概述与指令系统设计
指令系统概述
指令系统处在软/硬件交界面,同时被硬件设计者和系统程序员看到
- 硬件设计者角度:指令系统为 CPU 提供功能需求,要求易于硬件设计
- 系统程序员角度:通过指令系统来使用硬件,要求易于编写编译器 指令系统设计的好坏还决定了:计算机的性能和成本 冯·诺依曼结构机器对指令的规定:
- 用二进制表示,和数据一起存放在主存中
- 由两部分组成:操作码和操作数(或其地址码)
指令系统设计
概述
- Instruction Fetch:从存储器读取指令
- 指令地址、指令长度(定长/边长)
- Instruction Decode:对指令译码,以确定将要做什么操作
- 指令格式、操作码编码、操作数类型
- Operand Fetch:计算操作数地址并取操作数
- 地址码、寻址方式、操作数格式和存放
- Execute:进行相应计算,并得到标志位
- 操作类型、标志或条件码
- Result Store:将计算结果保存到目的地
- 结果数据位置(目的操作数)
- Next Instruction:计算下条指令地址(通常和取指令同时进行)
- 下条指令地址(顺序/转移) 指令执行的每一步都可能发生异常或中断,因此,指令集系统架构(ISA)还需要考虑异常和中断机制。 操作功能:用操作码表示,指定操作类型 源操作数参照:一个或多个源操作数所在的地址
指令
- 零地址指令
- 无需操作数,如空操作、停机等
- 所需操作数为默认的,如堆栈、累加器等
- 一地址指令
- 单目运算:如取反、取负等
- 双目运算:另一操作数为默认的,如累加器等
- 二地址指令(最常用)
- 三地址指令
- 多地址指令
指令格式的设计
- 应尽量短
- 要有足够的操作码位数
- 指令编码必须有唯一的解释,否则是不合法的指令
- 指令字长应是字节的整数倍
- 合理地选择地址字段的个数
- 指令尽量规整
Note
一般通过对操作码进行不同的编码来定义不同的含义,操作码相同时,再由功能码定义不同的含义。
- 操作码的全部组成:操作码个数、种类、复杂度 LD/ST/INC/BRN 四种指令已经足够编制任何可计算程序,但程序会很长
- 数据类型:对哪几种数据类型完成操作
- 指令格式:指令长度、地址码个数、各字段长度
- 通用寄存器:个数、功能、长度
- 寻址方式:操作数地址的指定方式
- 下条指令的地址如何确定:顺序,PC+1,条件转移,无条件转移……
- 异常和中断机制,包括存储保护方式等
数据类型
- 地址(指针):被看成无符号整数,用来参加运算以确定主(虚)存地址
- 数值数据
- 定点数(整数):一般用二进制补码表示
- 浮点数(实数):大多机器采用 IEEE754 标准
- 十进制数:用 NBCD 码表示,压缩/非压缩(汇编程序设计时使用)
- 位、位串、字符和字符串:用来表示文本、声音和图像等
- 4bits = nibble, 8bits = byte
- 16bits = half-word, 32bits = word
- 逻辑(布尔)数据:按位操作(0-假/1-真)
Note
操作数存放在寄存器或内存单元中,也可以以立即数的形式存在
操作数及其寻址方式
Addressing Modes(寻址方式)
- 寻址方式:指令或操作数地址的指定方式
- 地址码编码由操作数的寻址方式决定
- 地址码编码原则
- 指令地址码尽量短→目标代码短,省空间
- 操作数存放位置灵活,空间应尽量大→利于编译器优化产生高效代码
- 地址计算过程尽量简单→指令执行快(可变长地址寻址需要计算)
- 寻找方式的确定
- 没有专门的寻址方式位(由操作码确定寻址方式)
- 有专门的寻址方式位
- 有效地址:操作数所在存储单元的地址(逻辑或物理),可通过指令的寻址方式和地址码计算得到
- 基本寻址方式:立即、直接、间接 假设:A = 字段值,R = 寄存器编号,EA = 有效地址,(X) = X 中的内容
方式 | 算法 | 优点 | 缺点 | 操作数存储位置 |
---|---|---|---|---|
立即数 | 操作数 = A | 指令执行速度快 | 操作数幅值有限 | 指令寄存器 |
直接 | EA = A | 有效地址计算简单 | 地址范围有限 | 存储器 |
间接 | EA = (A) | 有效地址范围大 | 多次存储器访问 | 存储器 |
寄存器(直接) | 操作数 = (R) | 指令执行快,指令短 | 地址范围有限 | 寄存器 |
寄存器间接 | EA = (R) | 地址范围大 | 额外存储器访问 | 存储器 |
偏移 | EA = A + (R) | 灵活 | 复杂 | 存储器 |
栈 | EA = 栈顶 | 指令短 | 应用有限 | 存储器 |
偏移寻址
指令组成:OP R A 指令中给出的地址码 A 成为形式地址。EA=A+(R),R 可以明显给出,也可以隐含给出。R 可以为 PC、基址寄存器 B、变址寄存器 I
- 相对寻址:EA=A+(PC),相对于当前指令所处位移量为 A 的单元
- 实现公共子程序、动态链接库(类似规定某条指令的数据永远来自其的下面几行,然后此指令在代码中多处出现)
- 基址寻址:EA=A+(B),相对基址 B 所处位移量为 A 的单元
- 多段程序转移(静态转移,将多段程序合在一起)
- 变址寻址:EA=A+(I),相对于首地址 A 所处位移量为 I 的单元
- 实现数组(每次会自动加/减数组元素的长度 x,即 I=(I)+x)
操作类型和操作码编码
操作类型
- 算术逻辑运算指令
- 加减乘除、比较、与或非、取反、取负、异或等
- 算术运算包含整数和浮点数操作
- 移位指令
- 算术移位、逻辑移位、循环移位、半字交换
- 数据传送指令
- 寄存器间、内存-寄存器、内存内换
- 顺序控制指令
- 条件转移、无条件转移
- 调用、返回
- 系统控制指令
- 停机、开中断、关中断、系统模式切换等
- 输入/输出指令
Instruction Format(指令格式)
- Fixed Length Opcodes(定长操作码法)
- Expanding Opcodes(扩展操作码编法) 指令长度:
- 代码长度更重要时:采用变长指令字、变长操作码
- 性能更重要时:采用定长指令字、定长操作码 变长更紧凑,定长方便快速访问和译码。操作码长度和指令字长度没有绝对的关系。
定长操作码编码
- 基本思想:指令的操作码部分采用固定长度的编码。
- 特点:译码方便,但有信息冗余
扩展(变长)操作码编码
- 基本思想: 将操作码的编码长度分成几种固定长的格式。被大多指令集采用。
- 种类:等长扩展法:4-8-12;3-6-9;……/不等长扩展法
标志信息的生成与使用
条件测试方式
条件转移指令通常根据 Condition Code 转移,通过执行算数指令或显示地由比较和测试指令来设置 CC 常用的标志(条件码)有四种:
- SF:negative
- OF:overflow
- CF:进位/借位
- ZF:zero 标志可存于 FLAGs(称为标志寄存器/条件码寄存器/状态寄存器/程序状态字寄存器),也可由指定的通用寄存器来存放状态位
条件转移指令
- 根据单个标志的值转移
- 按无符号整数比较转移
- 按带符号整数比较转移
转移指令并不是必须的
知道了 SF、OF、CF、ZF 即可判断并转移,其他判断则可以在程序设计阶段实现
指令设计风格
按照数据存储位置分类
累加器型 (Accumulator)
特点:其中一个操作数和目的操作数总在累加器中
- 相当于 ALU 的其中一端始终是累加器 ACC,结果也存储在 ACC 中。 电路更加简单,但效率更低。
栈型 (Stack)
特点:总是将栈顶的两个操作数进行运算,指令无需指定操作数地址
通用寄存器型 (General Purpose Register)
特点:操作数可以是寄存器或存储器数据(即 A、B 和 C 可以是寄存器或存储单元)
装入/存储型 (Load/Store)
特点:运算操作只能是寄存器数据,只有 load/store 指令能访问存储器 访问寄存器的速度比访问主存快,可以加快计算速度。
指令风格比较
- 指令字长
- 所需指令数量
- 指令执行周期
栈型 | 累加器型 | 寄存器型 (寄存器存储器通用型) | 寄存器型 (装入存储型) | |
---|---|---|---|---|
指令字长 | 短 | 短 | 长 | 长 |
所需指令数量 | 多 | 较少 | 较少 | 多 |
指令执行周期 |
说明
- 累加器型所需指令数量较少,但因为所有运算都要使用累加器,表达式比较复杂时,会出现多次移入/移出累加器的指令,从而使累加器型所需指令数量迅速增加
寄存器型占据主导地位,优势:
- 寄存器速度快,使用大量通用寄存器可减少访存操作
- 表达式编译时与顺序无关(相对于 Stack 型)

按照指令设计的复杂度分类
复杂指令集计算机(CISC:Complex Instruction Set Computer) 精简指令集计算机(RISC:Reduced Instruction Set Computer)
CISC 的特点
- 指令系统复杂:变长操作码、变长指令字、指令多、寻址方式多、指令格式多
- 指令周期长:绝大多数指令需要多个时钟周期才能完成
- 各种指令都能访问存储器:除了专门的存储器读写指令外,运算指令也能访问存储器
- 采用微程序控制
- 有专用寄存器
- 难以进行编译优化来生成高效目标代码
CISC 的缺陷
- 研制周期变长、难以保证设计正确性、难以调试和维护、降低系统性能
- 指令出现的频率悬殊很大,常用的简单指令在程序指令中占大多数
RISC 的特点
- 简化的指令系统:指令少、寻址方式少、指令格式少、指令长度一致
- 以 RR 的方式工作:除 Load/Store 指令可访问存储器外,其余指令都只访问寄存器
- 指令周期短:以流水线方式工作,除了 Load/Store 指令外,其他指令都需要一个或者不到一个时钟周期即可完成
- 采用大量通用寄存器,以减少访存次数
- 采用组合逻辑电路控制,不用或少用微程序控制
- 采用优化的编译系统,力求有效地支持高级语言程序
异常和中断处理机制
- 程序执行过程中,CPU 会遇到一些特殊情况,使正在执行的程序被“中断”
- CPU 终止原来正在执行的程序,转到处理异常情况或特殊事件的程序去执行,结束后再回到原来终止的程序处(断点)继续执行
- 程序执行被“中断”的事件有两类
- 内部“异常”:在 CPU 内部发生的意外事件或特殊事件
- 硬故障中断:如电源掉电、硬件线路故障等
- 程序性中断:Exception 事件,如溢出、缺页、越界、越权、越级、非法指令、除数为 0、堆/栈溢出、访问超时、断点设置、单步、系统调用等。
- 外部“中断”:在 CPU 外部发生的特殊事件,通过“中断请求”信号向 CPU 请求处理。如时钟、控制台、打印机缺纸、外设准备好、采样计时到、DMA 传输结束等。
- 内部“异常”:在 CPU 内部发生的意外事件或特殊事件
- 发生异常(Exception)和中断(Interrupt)事件后,系统将进入 OS 内核态对相应事件进行处理,即改变处理器状态(用户态→内核态)

第二讲 指令系统实例:RISC-V 架构
RISC-V 指令系统概述
- 设计目标
- 广泛的适应性:从最袖珍的嵌入式微控制器,到最快的高性能计算机
- 支持各种异构处理架构,成为定制加速器的基础
- 稳定的基础指令架构,并能灵活扩展,且扩展时不影响基础部分
- 开源理念和实际原则
- 指令集完全公开,且无需为指令集付费
- 采用模块化设计,既保持基础指令集的稳定,也保证扩展指令集的灵活配置
- 特点:具有模块化结构、稳定性和扩展性好,在简洁性、实现成本、功耗、性能和程序代码量等方面具有显著优势
- 模块化结构
- 核心(RV32I) + 标准扩展集(RV32M、RV32F、RV32D、RV32A) = RV32G
- 32 位架构 RV32G = RV32IMAFD,其压缩指令集为 RV32C(指令长度为 16 位)
- 64 位架构 RV64G = RV64IMAFD,其压缩指令集为 RV64C(指令长度为 16 位)
- 向量计算 RV32V 和 RV64V;嵌入式 RV32E(RV32I 的自己,16 个通用寄存器)
RISC-V 指令参考卡和指令格式
指令卡
- 核心指令集:基础整数指令集 RV32I 和 RV64I
- 特权指令:陷阱指令对应的返回指令、wfi等待中断指令、sfence.vma 虚拟存储器的同步操作
- 伪指令(一共有 60 个):
- 如 MoVe rd, rs 指令实际执行了 ADDI rd, rs, 0 指令
- 压缩指令集:RV32C 和 RV64C
- 压缩指令的底层实现是通过电路转化为标准指令,然后用标准指令的电路进行实现
- 扩展指令集:
- 乘除运算指令集 RVM、原子操作指令 RVA、浮点运算指令集 RVF 和 RVD、向量操作指令集 RVV
- 通用寄存器的调用约定:
- 32 个定点通用寄存器 x0~x31;
- 32 个浮点寄存器 f0~f31;
- 通用寄存器 x0 中恒 0 ;x1 中返回地址;x2、x3 和 x4 分别为栈指针、全局指针和线程指针

指令格式
RISC-V 采用定长指令,共有6种指令格式:
- R-型为寄存器操作数指令
- I-型为短立即数或装入(Load)指令
- S-型为存储(Store)指令
- B-型为条件跳转指令
- U-型为长立即数操作指令
- J-型为无条件跳转指令 图注:
- opcode:操作码字段
- rd、rs1和rs2:通用寄存器编号
- imm:立即数,其位数在括号[ ]中表示
- funct3和funct7:分别表示 3 位功能码和 7 位功能码,和 opcode 字段一起定义指令的操作功能

16 位 RISC-V 压缩指令格式

每条 16 位指令都有功能完全相同的 32 位指令,在执行时由硬件先转换为 32 位指令再执行。目的是:缩短程序代码量,用少量时间换空间。
RISC-V 基础整数指令集
- 整数运算类指令
- 移位
- 算术运算
- 逻辑运算
- 比较
- 控制转移类指令
- 分支
- 跳转链接
- 系统控制类指令
- 同步
- 环境
- 控制状态寄存器
- 存储访问类指令
- 取数
- 存数

符号扩展
- 若为正数,则零扩展和符号扩展是一样的
- 若为负数,则 SEXT[1101] = 1111 1101
统一起来,就是将最高位进行扩展
RISC-V 可选的扩展指令集
- 标准扩展指令集
- RV32I基础指令集之上,可标准扩展RV32M、RV32F/D、RV32A,以形成32位架构合集RV32IMAFD,也称为RV32G
- RV32G基础上,对每个指令集进行调整和添加,可形成64位架构RV64G,原先在RV32G中处理的数据将调整为64位。但为了支持32位数据操作,每个64位架构指令集中都会添加少量32位数据处理指令。
- RISC-V扩展集包括
- 针对64位架构需要,在47条RV32I指令基础上,增加12条整数指令(+RV64I),包括6条32位移位指令、3条32位加减运算指令、两条64位装入(Load)指令和1条64位存储(Store)指令,故RV64I共59条指令。
- 针对乘除运算需要,提供了32位架构乘除运算指令集RV32M中的8条指令,并在此基础上增加了4条RV64M专用指令(+RV64M)
- 针对浮点数运算的需要,提供了32位架构的单精度浮点处理指令集RV32F和双精度浮点处理指令集 RV32D,并在此基础上分别增加了RV64F和RV64D专用指令集(+RV64F)和(+RV64D)。
- 针对事务处理和操作原子性的需要,提供了32位架构原子操作指令集RV32A以及RV64A专用指令集 (+RV64A)。
- 向量处理指令集RVV、未来可选扩展指令集RVB、RVE、RVH、……