Skip to content

指令系统设计

  • 指令集体系结构(ISA)核心部分是指令系统,还包含数据类型和数据格式定义、寄存器设计、I/O空间的编址和数据传输方式等等
  • 指令=操作码+地址码(源数据+结果数据+[下一条指令地址])(对什么数据做什么事)
  • image-20231129105817590

  • 指令的执行过程

  • 存储器取指令:指令地址、指令长度
  • 对指令译码:指令格式、操作码编码、操作数类型
  • 计算操作数地址并取操作数:地址码、寻址方式、操作数格式和存放
  • 进行相应计算,并得到标志位:操作类型、标志或条件码
  • 保存计算结果:结果数据位置(目的操作数)
  • 计算下条指令地址:下条指令地址(顺序/转移)

  • 每次取一个指令之后就要pc+1,对于跳转距离要小心

  • 比如一个两字节跳转指令在258,那么在计算偏移距离时应该使用260

  • 指令格式设计原则

  • 尽量短
  • 足够的操作码位数
  • 合理的地址字段个数
  • 指令编必须有唯一的解释
  • 指令字长应是字节的整数倍

  • 指令格式的设计一般通过对操作码进行不同的编码来定义不同的含义,操作码相同时,再由功能码定义不同的含义

操作数及其寻址方式

  • 操作数的基本类型
  • 地址(无符号整数)
  • 数值数据:定点整数,浮点数,十进制(NBCD)
  • 串、字符:文本声音图像等
  • 布尔数据类型
  • image-20231129110630866

寻址方式

  • 地址码编码原则:
  • 指令代码尽量短
  • 操作数存放位置灵活,空间尽量大:利于编译器优化产生高效代码
  • 地址计算过程尽量简单
  • 寻址方式的确定:
  • 没有专门的寻址方式位(只要知道是什么指令,就知道去哪里找操作数。)
  • 有专门的寻址方式位 (指令中可以看出有多个操作数,但每个操作数去哪里找,还需要指令中再专门记录有它们的寻址方式位。)
  • 有效地址:操作数所在存储单元的地址(存放在内存中时才有), 通过寻址方式和地址吗计算得到

  • 指令的寻址较为简单:PC正常递增;jmp跳转(同操作数)

  • 因为数据结构和操作数来源多样,操作数的寻址较为复杂
  • 基本寻址方式:立即 / 直接 / 间接 / 寄存器 / 寄存器间接 / 偏移 / 栈
  • image-20231129112455539
  • image-20231129113403490
  • image-20231129113436153
  • image-20231129113629776

  • 偏移寻址方式

  • 相对寻址:指令地址码给出一个偏移量(带符号数),基准地址 R 隐含由PC给出。
    • EA=(PC)+A
    • 实现jmp转移指令
  • 基址寻址:指令地址码给出一个偏移量,基准地址R明显或隐含由基址寄存器B给出
    • EA=(B)+A
    • 实现多道程序重定位 或 过程调用中参数的访问
  • 变址寻址:指令地址码给出一个基准地址,而偏移量(无符号数)R明显或隐含由变址寄存器
    • EA=(I)+A
    • 可为循环重复操作提供一种高效机制,如实现对线性表的方便操作(指令中的地址码A给定数组首址,变址器I每次自动加/减数组元素的长度x。)
    • EA=( I )+A I=( I ) ± x
    • image-20231129114613565

操作类型和操作码编码

  • 操作码编码:
  • 定长操作码法
  • 扩展操作码编法
  • 代码长度更重要时:采用变长指令字(指整个指令的长度)、变长操作码;性能更重要时:采用定长指令字、定长操作码
    • 变长指令字使得机器码更加紧凑,定长便于快速访问和译码。
  • 定长操作码,也可以是变长指令字;但变长操作码,一般不会是定长指令字(操作码只是指令的一部分)

  • 定长操作码编码

  • 译码方便但是有信息冗余
  • IBM360/370(定长操作码、变长指令字)采用:8位定长操作码,最多可有256条指令只提供了183条指令,有73种编码为冗余信息
    • IBM360有16个32位通用寄存器,使用4位地址表示寄存器,基址器B和变址器X可用其中任意一个
  • image-20231129131657863
    • RX:X 为索引寄存器(其内容会被加到最终的内存地址中,用于变址);B 为基址寄存器;D 为附加偏移量的立即数

难点

  • 扩展(变长)操作码编码
  • 设某指令系统指令字是16位,每个地址码为6位。若二地址指令15条,一地址指令34条,则剩下零地址指令最多有多少条?
  • 不同的指令之间不能互为前缀和
  • 二地址指令中地址占据12位,故剩下4位作为操作码,即15种(实际使用0000~1110)
  • 一地址指令有10位可以作为操作码,以二地址不使用的1111作为前缀
    • 11110+(0000~11111)32种
    • 11111+(00000~00001)2种
  • 零地址,使用16位作为操作码
    • 11111+(00010~11111)+(000000~111111)
    • \(30*2^6\)

指令设计风格

  • 操作数位置指定风格来分
  • 累加器型:其中一个操作数和目的操作数总在累加器中
  • 栈型:总是将栈顶两个操作数进行运算,指令无需指定操作数地址
  • 通用寄存器型(IA32):操作数可以是寄存器或存储器数据
  • 装入/存储型(RISC-V):运算操作数只能是寄存器数据,只有load/store能访问存储器

  • image-20231129134257357

  • 寄存器型占主导地位:

    • 寄存器速度快,使用大量通用寄存器可减少访存操作
    • 表达式编译时与顺序无关(相对于Stack)
  • 按指令格式的复杂度来分

  • 复杂指令集计算机CISC

    • 指令系统复杂: 变长、寻址方式多、格式多
    • 指令周期长
    • 各种指令都能访问存储器
    • 采用微程序控制
    • 有专用寄存器
    • 难以进行编译优化来生成高效目标代码
    • 日趋庞大的指令系统不但使计算机的研制周期变长,而且难以保证设计的正确性,难以调试和维护,并且因指令操作复杂而增加机器周期,从而降低了系统性能
    • 在程序中各种指令出现的频率悬殊很大,最常使用的是一些简单指令,这些指令占程序的80%,但只占指令系统的20%。而且在微程序控制的计算机中,占指令总数20%的复杂指令占用了控制存储器容量的80%。
  • 精简指令集计算机 RISC

    • 简化的指令系统:指令少 / 寻址方式少 / 指令格式少 / 指令长度一致
    • 装入/存储方式工作:除了 loard/store 之外只访问寄存器
    • 采用大量通用寄存器,以减少访存次数
    • 采用组合逻辑电路控制,不用或少用微程序控制
    • 采用优化的编译系统,力求有效地支持高级语言程序
  • image-20231129141328026

  • MIPS32的指令格式(ld/st型、RISC型)

  • image.png|600

  • IA32

  • image-20231129142407395

RISC-V架构

  • 具有模块化结构,稳定性和可扩展性好,在简洁性、实现成本、功耗、性能和程序代码量等各方面具有显著优势
  • 模块化设计:对指令集根据功能的不同进行划分
  • image-20231129143207755

指令格式

  • 寄存器:
  • image-20231129143310699
  • x1寄存器通常存储返回地址
  • x0寄存器永远为0

  • image-20231129143402186

基础整数指令集RV32I

  • RTL 规定:
  • R[r]:通用寄存器 r 的内容
  • M[addr]:存储单元 addr 的内容
  • M[R[r]]:寄存器 r 的内容所指存储单元的内容
  • PC:PC的内容
  • M[PC]:PC 所指存储单元的内容
  • SEXT[imm]:对 imm 进行符号扩展
  • ZEXT[imm]:对 imm 进行零扩展
  • 传送方向用←表示,即传送源在右,传送目的在左

整数运算

  • image-20231129144540200

  • U型指令(仅一个寄存器)

  • lui rd, imm20:将立即数imm20存到rd寄存器20位,同时将低12位置为0
  • image-20231129150319864
  • addi rd, rs1, imm12:立即数加,赋值到rd的低12位
  • 与lui结合实现32位数的赋值操作
  • int x=-8191;
    • image-20231129150604323
  • auipc rd, imm20:将立即数imm20加到PC(32位)的高20位上,结果存rd

  • I型指令(寄存器与立即数运算):

  • image-20231129151318880
  • opcode 相同,使用 func3 指定功能,移位运算(黄色)还使用最高7位标识移位类型
  • imm[11:0]:12位立即数,符号扩展为32位,作为第2个源操作数,和 R[rs1](寄存器 rs1中的内容)进行运算,结果存 rd。
  • shamt:移位位数
  • 例子:由于符号扩展的性质,有时直接使用lui和addi的不到正确结果
  • image-20231129151845798
  • 因为 addi 是针对12为有符号立即数,因此数据范围仅为-2048~2047,因此应该用 lui 先装入一个距离目标常数小于2048的数,再通过 addi 进行加或减(imm12为负时)来调整
  • image-20231129152409753

  • R型指令(两个寄存器计算):

  • 操作码opcode:都是0110011,其功能由funct3指定,而当funct3=000、101时,再由funct7区分是加(add)还是减(sub)、逻辑右移(srl)还是算术右移(sra)。
  • 两个源操作数分别在rs1和rs2寄存器中,结果存rd。
  • 比较:带符号小于(slt、slti)、无符号小于(sltu、sltiu)
  • sltiu rd, rs1, imm12:将 rs1内容与 imm12 符号扩展结果按无符号整数比较,若小于,则1存入 rd 中;否则,0存入 rd 中。
  • image-20231129153014821
  • z=x+y的机器代码:
    • image-20231129153250187
    • image-20231129153427074
    • 使用sltu实现了从低位进位到高位

控制转移

  • image-20231129144757066
  • Tip:[20|10:1|11|19:12] 标识立即数的组织顺序

  • J 型:jal 功能为: PC←PC+SEXT[imm[20:1]<<1] R[rd]←PC+4(存储当前的地址)

  • 过程调用:jal x1, immimm 是跳转目标地址相对于当前程序计数器(PC)的偏移量。并且将跳转地址存储在返回地址寄存器x1
  • 无条件跳转jal x0, imm,只进行跳转,不存储地址,因为x0不会存储写入
  • 由于立即数有20位,可跳转的范围相对较大

  • I 型:jalr 功能为: PC←R[rs1]+SEXT[imm[12]] R[rd]←PC+4

  • 调用返回:jalr x0, x1, 0,取出并返回到x1指向的地址
  • 由于使用寄存器,可以实现很远距离的跳转

  • B型:分支指令(条件转移)

  • bltu、bgeu、bne 分别为无符号数比较小于、大于等于、不等于时转移。
  • PC<-PC+SEXT[imm[12:1]<<1]

    • 由于立即数只有12位,这就导致了 B 型只能在附近跳转
  • <<1: 指令地址总是2的倍数(RV32G、RV32C 指令分别为4、2字节长)

  • 若int型变量x、y、z分别存放在寄存器x5、x6、x7中,写出C语句“z=x+y;”对应的RISC-V机器级代码,要求检测是否溢出。

  • image-20231129170726997
  • |575
    • 实际偏移量是立即数的二倍

系统控制*

  • image-20231129144835065
  • fence:RISC-V架构在不同硬件线程之间使用宽松一致性模型,fence和fence.i 两条屏障指令,用于保证一定的存储访问顺序。
  • ecall和ebreak:陷阱(trap)指令,也称自陷指令,主要用于从用户程序陷入到操作系统内核(ecall)或调试环境(ebreak)执行,因此也称为环境(Environment)类指令
  • csrxxx: 6条csr指令用于设置和读取相应的控制状态寄存器(CSR)

存储访问

  • image-20231129144809981
  • I型:取数指令R[rd]←M[R[rs1]+SEXT[imm[12]]
  • 如:lbu、lhu:分别为无符号字节、半字取,取出数据按0扩展为32位,装入rd
  • S型:存数指令M[R[rs1]+SEXT[imm[12]]←R[rs2]
  • 如:sb、sh: 分别将rs2寄存器中低8、低16位写入存储单元中。
  • lw rd, imm12(rs1), sw rs2, imm12(rs1)

可选的扩展指令集*

  • image-20231129171838492

乘除运算

  • 乘法指令: mul,mulh,mulhu,mulhsu
  • mul rd, rs 1, rs 2:将低 32位乘积存入结果寄存器 rd()有无符号不影响低 32位
  • Mulh:将两个乘数同时按带符号整数相乘,高 32 位乘积存入 rd 中
  • mulhu:将两个乘数同时按无符号整数相乘,高 32 位乘积存入 rd 中
  • mulhsu:将两个乘数分别作为带符和无符整数相乘,高 32 位乘积存入 rd
  • 得到 64 位乘积需要两条连续的指令,其中一定有一条是 mul 指令,硬件实际执行时其实只是执行了一条指令
  • 两种乘法指令都不检测溢出, 而是直接把结果写入结果寄存器。由软件根据结果寄存器的值自行判断和处理溢出

  • 实现乘法溢出的判断:已知入口参数 x、y 分别在寄存器 a 0、a 1 中,返回值在 a 0 中,写出实现 imul_overflow 函数功能的 RISC-V 汇编指令序列,并给出注解。(编译器中判断溢出的代码)

  • image-20231129172931759
  • 算术右移使得每一位都等于符号位

  • 除法指令: div ,divu,rem,remu

  • div / rem:按带符号整数做除法,得到商 / 余数
  • divu / remu:按无符号整数做除法,得到商 / 余数
  • RISC-V 指令不检测和发出异常(除 0),而是由系统软件自行处理

  • 除法结果的处理:

  • 除法错,不触发异常,而用特殊的商和余数来表示
  • image-20231129173311689