指令系统
概述与指令系统设计指令系统概述指令系统设计指令设计风格异常和中断处理机制指令系统实例:RISC-VRISC-V系统概述RISC-V指令参考卡和指令格式RISC-V基础整数指令集RISC-V可选的扩展指令集
概述与指令系统设计
指令系统概述
- 指令系统在软/硬件交界,同时被硬件设计者和软件设计者看到
- 硬件设计者角度:指令系统为CPU提供功能需求,要求易于硬件设计
- 软件设计者角度:通过指令系统来使用硬件,要求易于编写编译器
- 冯诺依曼机构机器对指令系统要求
- 二进制表示,和数据一起放在主存
- 两部分组成,操作数和操作码
指令系统设计
- 从指令执行周期看指令设计涉及的问题
- 一条指令涉及的信息
- 操作功能:用操作码表示,指定操作类型
- 源操作数参照:一个或多个源操作数所在的地址
- 结果值参照:产生的结果存放何处(目的操作数)
- 下一条指令地址:下条指令存放何处
- 一个指令中应该有几个地址码字段
- 零地址指令
- 无需操作数:停机,空操作
- 操作数为默认的,如:堆栈/累加器
- 一地址指令
- 单目运算
- 双目运算:另一操作数默认,如累加器
- 二地址指令(最常用) 分别存放双目运算中两个操作数,并将其中一个地址作为结果的地址。 形式:OPA1A2
- 三地址指令 分别作为双目运算中两个源操作数的地址和一个结果的地址。 形式:OP+A1A2A3
- 多地址指令 用于成批数据处理的指令,如:向量 /矩阵等运算的SIMD指令。
- 指令格式的设计
- 指令格式的选择应遵循的几条基本原则
- 应尽量短
- 要有足够的操作码位数
- 指令编码必须有唯一的解释,否则是不合法的指令
- 指令字长应是字节的整数倍(常见8的倍数)
- 合理地选择地址字段的个数
- 指令尽量规整
- 与指令集设计相关的重要方面
- 操作码的全部组成:操作码个数 / 种类 / 复杂度 LD/ST/INC/BRN 四种指令已足够编制任何可计算程序,但程序会很长
- 数据类型:对哪几种数据类型完成操作
- 指令格式:指令长度 / 地址码个数 / 各字段长度
- 通用寄存器:个数 / 功能 / 长度
- 寻址方式:操作数地址的指定方式
- 下条指令的地址如何确定:顺序,PC+1;条件转移;无条件转移;……
- 异常和中断机制,包括存储保护方式等
- 操作数类型和存储方式
- 地址(指针)
- 数值数据
- 位、位串、字符和字符串
指令执行的每一步都可能发生异常或中断,因此 , 指令集系统架构(ISA)还需要考虑异常和中断机制
(操作码长度:固定/可变)
(操作数来源:主(虚)存/寄存器/I/O端口/指令本身)
(结果地址:主(虚)存/寄存器/I/O端口)
(下条指令地址 :主(虚)存)
(正常情况隐含在PC中,改变顺序时由指令给出)
形式:OP
其地址即使操作数地址也是结果地址
形式:OP+A1
操作数是指令处理的对象,与高级语言数据类型对应,基本类型有哪些?
被看成无符号整数,用来参加运算以确定主(虚)存地址
定点数(整数):一般用二进制补码表示
浮点数(实数):大多数机器采用IEEE754标准
十进制数:用NBCD码表示,压缩/非压缩(汇编程序设计时用)
用来表示文本、声音和图像等
4 bits is a nibble(一个十六进制数字)
8 bits is a byte
16 bits is a half-word
32 bits is a word
IA-32 & RISC-V Data Type

- 操作数及其寻址方式
- Addressing Modes(寻址方式)
- 偏移寻址
- 相对寻址: EA=A+(PC) 相对于当前指令处位移量为A的单元→实现公共子程序
- 基址寻址: EA=A+(B) 相对于基址(B)处位移量为A的单元→多程序段偏移
- 变址寻址: EA=A+(I) 相对于首址A处位移量为(I)的单元→实现数组
- 存储器操作数的寻址方式



偏移寻址:EA=A+(R) R可以明显给出,也可以隐含给出
R可以为PC、基址寄存器B、变址寄存器 I
变址寻址实现线性列表元素的存取
自动变址
指令中的地址码A给定数组首址,变址器 I 每次自动加/减数组元
素的长度x。
EA=A+( I )
I=( I ) ± x
对于“for (i=0;i<N;i++) ….”,即地址从低→高变化:加
对于“for (i=N-1;i>=0;i--) ….”,即地址从高→低变化:减
一般RISC机器不提供自动变址寻址,并将变址和基址寻址统一成一种偏移寻址方式

- 操作类型和操作码编码
- 几种指令
- 算术和逻辑运算指令
- 移位指令
- 数据传送指令
- 顺序控制指令
- 系统控制指令
- 输入/输出指令
- 指令格式
- Fixed Length Opcodes (定长操作码法)
- Expanding Opcodes (扩展操作码编法)
- 代码长度更重要时:采用变长指令字、变长操作码
- 性能更重要时:采用定长指令字、定长操作码
- 定长操作码编码 Fixed Length Opcodes
- 基本思想
- 指令的操作码部分采用固定长度的编码
- 如:假设操作码固定为6位,则系统最多可表示64种指令
- 特点
- 译码方便,但有信息冗余
- 举例
- IBM360/370采用:
- 8位定长操作码,最多可有256条指令
- 只提供了183条指令,有73种编码为冗余信息
- 机器字长32位,按字节编址
- 有16个32位通用寄存器,基址器B和变址器X可用其中任意一个
- 问题:通用寄存器编号有几位?B和X的编号占几位? 都是4位
- 扩展(变长)操作码编码 Expanding Opcodes
- 基本思想
- 将操作码的编码长度分成几种固定长的格式。被大多数指令集采用。PDP-11是典型的变长操作码机器。
- 种类
- 等长扩展法:4-8-12;3-6-9;…... / 不等长扩展法
- 举例说明如何扩展
- 设某指令系统指令字是16位,每个地址码为6位。若二地址指令15条,一地址指令34条,则剩下零地址指令最多有多少条?
加、减、乘、除、比较、与、或、非、取反、取负、异或等
加算术运算包含整数和浮点数操作
算术移位、逻辑移位、循环移位、半字交换
寄存器间、内存-寄存器、内存内换
条件转移、无条件转移
调用、返回
停机、开中断、关中断、系统模式切换等
操作码的编码有两种方式
instructions size
变长指令字和变长操作码使机器代码更紧凑;定长指令字和定长操作码便于快速访问和译码。学了CPU设计就更明白了
问题:是否可以有定长指令字、变长操作码?定长操作码、变长指令字呢?
指令长度是否可变与操作码长度是否可变没有绝对关系,但通常是 “定长操作码不一定是定长指令字”、 “变长操作码一般是变长指令字”。

- 标志信息的生成与使用
- 条件测试方式
- 条件转移指令通常根据Condition Codes (条件码 CC/ 状态位 / 标志位)转移 通过执行算术指令或显式地由比较和测试指令来设置
- 常用的标志(条件码)有四种
- 标志可存于标志寄存器/条件码寄存器/状态寄存器/程序状态字寄存器 也可由指定的通用寄存器来存放状态位
SF – negative OF – overflow
CF – 进位/借位 ZF – zero

指令设计风格
- Accumulator: (earliest machines) 累加器型 特点:其中一个操作数(源操作数1)和目的操作数总在累加器中
- Stack: (e.g. HP calculator, Java virtual machines) 栈型 特点:总是将栈顶两个操作数进行运算,指令无需指定操作数地址
- General Purpose Register: (e.g. IA-32, Motorola 68xxx) 通用寄存器型 特点:操作数可以是寄存器或存储器数据(即A、B和C可以是寄存器或存储单元)
- Load/Store: (e.g. SPARC, MIPS, RISC-V) 装入/存储型 特点:运算操作数只能是寄存器数据,只有load/store能访问存储器
- 指令风格比较

- 寄存器使用方式的举例


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

指令系统实例:RISC-V
RISC-V系统概述
- 设计目的
- 广泛的适应性:从最袖珍的嵌入式微控制器,到最快的高性能计算机
- 支持各种异构处理架构,成为定制加速器的基础
- 稳定的基础指令集架构,并能灵活扩展,且扩展时不影响基础部分
- 开源理念和设计原则
- 本着“指令集应自由(Instruction Set Want to be Free)”的理念,指令集完全公开,且无需为指令集付费
- 由一个非盈利性质的基金会管理,以保持指令集的稳定性,并加快生态建设
- 与以前的增量ISA不同,遵循“大道至简”的设计哲学,采用模块化设计,既保持基础指令集的稳定,也保证扩展指令集的灵活配置
- 特点:具有模块化结构、稳定性和可扩展性好,在简洁性、实现成本、功耗、性能和程序代码量等各方面具有显著优势
- 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指令参考卡和指令格式
- 指令参考卡1
- 核心指令集:基础整数指令集 RV32I 和 RV64I
- 特权指令:陷阱指令对应的返回指令、wfi等待中断指令、sfence.vma虚拟存储器的同步操作
- 伪指令举例
- 压缩指令集:RV32C和RV64C

- 指令参考卡2
- 扩展指令集 乘除运算指令集RVM、原子操作指令RVA、浮点运算指令集RVF和RVD、向量操作指令集RVV
- 通用寄存器的调用约定 32个定点通用寄存器x0~x31;32个浮点寄存器f0~f31;通用寄存器x0中恒0;x1中返回地址;x2、x3和x4分别为栈指针、全局指针和线程指针

- RV32I寄存器的功能定义和两种汇编表示

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

- 16位RISC-V压缩指令格式
- 共有8种指令格式。与32位指令相比,16位指令中的一部分寄存器编号还是占5位。指令变 短了,但还是32位架构,处理的还是32位数据,还是有32个通用寄存器。
- 为了缩短指令长度,操作码op、功能码funct、立即数imm和另一部分寄存器编号的位数都 减少了。
- 每条16位指令都有功能完全相同的32位指令,在执行时由硬件先转换为32位指令再执行。 目的是:缩短程序代码量,用少量时间换空间!

RISC-V基础整数指令集
- 包括了以下指令
- RTL规定
RTL规定
R[r]:通用寄存器r的内容
M[addr]:存储单元addr的内容
M[R[r]]:寄存器r的内容所指存储单元的内容
PC:PC的内容
M[PC]:PC所指存储单元的内容
SEXT[imm]:对imm进行符号扩展(算术位移)
ZEXT[imm]:对imm进行零扩展(逻辑位移)
传送方向用←表示,即传送源在右,传送目的在左
- 分类
- 整数类运算指令
- U型指令
lui rd, imm20:将立即数imm20存到rd寄存器高20位,低12位为0。该指令和“addi rd, rs1, imm12”结合,可以实现对一个32位变量赋初值。auipc rd, imm20:将立即数imm20加到PC的高20位上,结果存rd。可用指令“auipc x10, 0”获取当前PC的内容,存入寄存器x10中。lui x5, 0xFFFFE将高20位加载到寄存器x5中。addi x5, x5, 1将低12位的立即数1加到x5中。- I 型指令共9条,其中三条为用立即数指定所移位数的移位指令
- 操作码opcode:都是0010011,其功能由funct3指定,
- 当funct3=101时,再由高7位区分是算术右移(srai)还是逻辑右移(srli)。
- shamt:指出移位位数,因为最多移31位,故用5位即可。
- imm[11:0]:12位立即数,符号扩展为32位,作为第2个源操作数,和R[rs1](寄存器rs1中的内容)进行运算,结果存rd。
- R型指令
- R型指令共10条
- 操作码opcode:都是0110011,其功能由funct3指定,而当funct3=000、101时,再由funct7区分是加(add)还是减(sub)、逻辑右移(srl)还是算术右移(sra)
- rs1、rs2、rd:5位通用寄存编号,共32个;两个源操作数分别在rs1和rs2寄存器中,结果存rd
- sll:逻辑左移指令,无算术左移指令。因逻辑左移和算术左移结果完全相同!
- 四条比较指令
- 带符号小于(slt、slti)、无符号小于(sltu、sltiu)
- 例如,“sltiu rd, rs1, imm12”功能为:将rs1内容与imm12符号扩展结果按无符号整数比较,若小于,则1存入rd中;否则,0存入rd中。
- 控制转移类指令
- J型:jal 功能为: R[rd]←PC+4;PC←PC+SEXT[imm[20:1]<<1]
- I型:jalr功能为: R[rd]←PC+4;PC←R[rs1]+SEXT[imm[12]]
- B型:皆为分支指令,其中,bltu、bgeu分别为无符号数比较小于、大于等于转移。
- 存储访问指令
- I型:5条取数(Load)指令。功能: R[rd]←M[R[rs1]+SEXT[imm[12]]
- S型:3条存数(Store)指令。功能: M[R[rs1]+SEXT[imm[12]]←R[rs2]。
- 汇编形式:存储地址可写成imm12(rs1)。
- 系统控制类指令
- fence:RISC-V架构在不同硬件线程之间使用宽松一致性模型,fence和fence.i 两条屏障指令,用于保证一定的存储访问顺序。
- ecall和ebreak:陷阱(trap)指令,也称自陷指令,主要用于从用户程序陷入到操作系统内核(ecall)或调试环境(ebreak)执行,因此也称为环境(Environment)类指令。
- csrxxx: 6条csr指令用于设置和读取相应的控制状态寄存器(CSR)。

如何使用 RISC-V 指令来实现 第一步:使用
第二步:使用
int x = -8191; 这个 C 语句。首先,我们知道在 RISC-V 中,可以通过
lui 指令加载一个立即数的高20位到寄存器,并将低12位置为0。然后,我们可以使用 addi 指令将这个寄存器与一个12位的立即数相加,得到最终的结果。int x = -8191; 表示将整数 -8191 赋值给变量 x。在 RISC-V 中,整数 -8191 的 32 位二进制表示为:为了实现这个赋值,我们可以按照以下步骤进行:
第一步:使用 lui 指令加载高20位
首先,我们使用
lui 指令将立即数的高20位加载到寄存器中。在这个例子中,立即数的高20位是 1111 1111 1111 1111 1110,对应的十六进制是 FFFFE。这条指令将
FFFFE000H 加载到寄存器 x5 中。第二步:使用 addi 指令将剩余的低12位相加
接下来,我们需要将低12位的立即数
1 加到 x5 中:这条指令将
1 加到寄存器 x5 当前的值 FFFFE000H 中,使其变为 FFFFE001H。组合起来
结合上述两条指令,我们得到:
这两条指令共同实现了将 -8191 赋值给寄存器
x5。



rd指定为x1时可实现过程调用;指定为x0时,可实现无条件跳转。
指令“jalr x0,x1,0”可实现过程调用的返回。
转移目标地址=PC+SEXT[imm[12:1]<<1]
<<1: 指令地址总是2的倍数(RV32G、RV32C指令分别为4、2字节长)
lbu、lhu:分别为无符号字节、半字取,取出数据按0扩展为32位,装入rd
sb、sh: 分别将rs2寄存器中低8、16位写入存储单元中。
- 例子
若int型变量x、y、z分别存放在寄存器x5、x6、x7中,写出C语句“z=x+y;”对应的RISC-V机器级代码,要求检测是否溢出

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、......
乘除法


