Chapter7 | 指令系统
概述与指令系统设计
指令的执行过程

一条指令必须明显或隐含包含的信息:
操作码:指定操作类型
(操作码长度:固定/可变)源操作数参照:一个或多个源操作数所在的地址
(源操作数或其地址,由取址方式决定)
(操作数来源:主(虚)存/寄存器/I/O端口/指令本身)
结果值参照:产生的结果存放何处(目的操作数)
(结果地址:主(虚)存/寄存器/I/O端口)下一条指令地址:下条指令存放何处
(下条指令地址:主(虚)存)
(正常情况隐含在PC中,改变顺序时由指令给出)(PC会自动改变)
ISA的要素:指令集
指令的要素:操作码,地址码
CPU执行指令的步骤:读取指令、译码、取操作数、运算、存数、读取下一条指令。
操作码和译码、运算有关
指令按照地址码的字段个数可以分为0地址操作码,1地址操作码,等等。RISC-V的风格是三地址指令。OP A1 A2 A3
.
指令设计的要求:
- 一个编码对应唯一的操作码
- 长度是字节的整数倍
操作数类型和存储方式:
操作数类型:
- 地址
- 数值数据
- 位串等
- 布尔数据
操作数可以存放在:1. 寄存器 2. 内存单元 3. 立即数(和指令放在一起) 中
寻址方式
根据地址找到指或操作数的方法。
地址码编码原则: 编码地址尽量短(指令短->汇编代码短->机械码短),位置尽量灵活、空间尽量大(利于编译器优化),地址的计算尽量简单(指令执行速度快)。
指令的寻址相对简单:
- PC增加
- 特殊的跳转:JUMP,call,return等
下面主要讲操作数的寻址方式:
寻址方式确定:
- 无单独的寻址方式位,由操作码确定寻址方式
- 由单独的寻址方式
有效地址:
操作数所在存储单元的地址(可能是逻辑地址或物理地址),可通过指令的寻址方式和地址码计算得到
基本寻址方式的算法和优缺点:
A表示地址段值,R位寄存器编号,EA为有效地址,(X)为X存储的值

有效地址其实都是操作数在内存里面的位置!
总结:
直接寻址和寄存器直接寻址的操作数分别是在指令和寄存器中
直接寻址的操作数在内存里,只需要访问一次内存(存储器),得到的就是数据;间接寻址第一次得到的是有效地址EA
间接寻址是先找到存储有效地址EA的地址,然后在从EA地址中取出(EA)
寄存机间接寻址,寄存器中存储的是EA。
偏移寻址:EA=A+(R)。
image-20250414105327810 - 变址寻址可以实现通过I的改变,寻得不同的数据,而指令可以用同一个指令,可以用于实现类似for循环的功能。
即,相对寻址和基址寻址都是以寄存器中的为基准,偏移A个位置;而变址寻址是以A为基准,偏移(R)个位置。
指令格式
操作码的编码:
- 定长操作码
- 变长操作码 (减少空间消耗,性能开销大)
变长操作码一定是和变长指令字相配合;
定长编码:
操作码固定长度:
自己看ppt吧,这里没听懂;
如何找到下一位指令?
- 顺序执行:在PC加1后找。加1的含义:按顺序找到下一条指令;如果地址是按照字节编码的,所以要按照指令的长度上字节的长度,即如果指令的为4个字节,那么应该PC加4 。
- 改变顺序时,指令显式地给出下一条地址和条件转移指令。
条件测试方法
ppt都写的什么鬼。没有结构。。
指令风格介绍
从指令风格可以分为:
- stack型
- Accumulator型
- 通用寄存器型
- 装入、存储型;load store型,比如RISC-V
指令设计风格:
- CISC:复杂,用的多
- RISC: 精简,效率高
异常和中断
CPU停止正在执行的程序,转到处理异常情况的程序。
分为:
- 内部异常:
- 硬故障中断:
- 程序性中断:各种
Exception
的指令
- 外部中断:
- 在CPU外部发生的特殊事件,通过中断请求的信号,向CPU发送请求处理异常的信号。
异常的处理

这里还有几页ppt,但是没有讲,自己看。
指令系统实例:RISC-V架构
RISC-V 的模块化设计:
- 核心模块:RI32I
- 拓展模块:RV32M等
几种长度:
指令长度,RI32G 和RI64G的指令长度都是16位。
机器的架构长度32/64:机器字长=通用寄存器长度=定点运算器的长度,设计硬件的时候,将大量的GPR的长度设计为32/64位
处理数据的长度
一个字节=8bit (地址可以按字节编码)
以下是32位与64位机器、x86与x64架构的简要对比表格:
特性 | 32位机器(x86) | 64位机器(x64) |
---|---|---|
数据宽度 | 一次处理32位数据 | 一次处理64位数据 |
地址空间 | 32位地址空间 | 64位地址空间 |
指令集 | x86指令集 | x64指令集(兼容x86) |
- 32位 vs 64位:主要是数据处理能力和内存支持的区别。
- x86 vs x64:x86是32位架构,x64是64位扩展架构,且兼容x86。

在这里,X0代表的是固定的32个0(硬编码为0)。ABI是Aplication Binnary Interface
32位的RISC-V 指令风格
六种格式:
R-型为寄存器操作数指令,7+5+5+3+5+7;
I-型为短立即数或装入(Load)指令
S-型为存储(Store)指令
B-型为条件跳转指令 【B即branch,条件跳转】
U-型为长立即数操作指令 :20位立即数
J-型为无条件跳转指令:Jump


这里的imm的位置如此地混乱是为了提高效率
几个常见的缩写:
- opcode:最末尾的7位!!
- rd : rigister destination目标寄存器 【需要5位,因为有32个通用寄存器】
- rs1 rs2 : rigister source源操作数的寄存器地址、【也是5位】
- imm: immediate 立即数
- funtc3和funct7,表示3为和7位功能码,和opcode一起定义指令的操作功能。
在这里,注意: 通用寄存器的地址都要对齐,比如rs2,rs1,rd都是对齐的5为位;
16为RISC-V的设计风格
共有8种指令格式。与32位指令相比,16位指令中的一部分寄存器编号还是占5位。指令变短了,但还是32位架构,处理的还是32位数据,还是有32个通用寄存器。
为了缩短指令长度,操作码op、功能码funct、立即数imm和另一部分,寄存器编号的位数都减少了。每条16位指令都有功能完全相同的32位指令,在执行时由硬件先转换为32位指令再执行。目的是:缩短程序代码量,用少量时间换空间!
只能压缩:op,imm,和寄存器的个数;(寄存器编码的长度如果使用低于5位,肯定有解压缩的过程)
- 16位的exe文件变短了,但是因为16位指令还是要转换成32位,因为最后的执行时间其实比32位更短。
基础整数指令集 RV32I
RTL(Rigister Transfer Language)
- R[r]:取出寄存器r中的内容。
- M[addr]: 取出内存中addr的内容。
- PC: PC地址指向的内容。
- M[PC]:读取PC所指的地址的内容。
- SEXT[imm]:Sign Extension,符号位扩展。
- ZEXT[imm]:Zero Extension, 0扩展。

一个例子:

在这里的I类指令,移位类指令,前6位用于区分是什么移位(左移,逻辑右移,算数右移),shamt是5位,因为,每一个寄存器就是32位,
只需要5位shamt就能实现。
几个I型指令
一个例子: int a= -8191

因为 lui (load upper immediate)指令只有20位的立即数,因而只能将高20位送入(进行符号扩展SEXT),一个寄存器,之后再使用addi指令将低12位加入。
在ISA中,imm都是进行符号位扩展!
几个I型指令:

R型指令
- 操作码opcode都是0110011,功能由funct3决定;
4条比较指令:带符号小于(slt、slti)、无符号小于(sltu、sltiu):
- sltiu rd, rs1 imm12 按顺序,rs1和imm12比较,如果rs1小于imm12那么,rd中存入1,否则为0; (进行无符号比较)
- 无论是有符号比较还是无符号比较,都是要将imm12扩展为32位,才能进行比较。
对于指令的要求:
- 能够看懂每一个指令的意义,谁和谁做运算,怎么做运算
- 能看将指令和汇编进行一一对应
- 区分谁是rd和rs
一个例子:

为什么是这样的呢? bne(branch not equal),将立即数40按照要求B类型指令的要求,将其组合上去。
内存访问指令
load指令:R[rd]<-M[R[rs1]+SEXT[imm[12]]
store指令:
乘法和除法
乘法指令: mul,mulh,mulhu,mulhsu
– mul rd, rs1, rs2:将低32位乘积存入结果寄存器rd
– mulh、mulhu:将两个乘数同时按带符号整数(mulh)、同时按无符号
整数(mulhu)相乘,高32位乘积存入rd中
– mulhsu:将两个乘数分别作为带符号整数和无符号整数相乘后得到的高
32位乘积存入rd中
– 得到64位乘积需要两条连续的指令,其中一定有一条是mul指令,实际执
行时只有一条指令
– 两种乘法指令都不检测溢出, 而是直接把结果写入结果寄存器。由软件根据
结果寄存器的值自行判断和处理溢出
除法指令: div ,divu,rem,remu
– div / rem:按带符号整数做除法,得到商 / 余数
– divu / remu:按无符号整数做除法,得到商 / 余数
RISC-V指令不检测和发出异常,而是由系统软件自行处理