计算机组成设计-软件硬件接口-RISC-V版笔记

rCoreCamp 2022过程里看的书的笔记

chap 1. 计算机抽象及相关技术

八个伟大思想:

  1. 面向摩尔定律的设计
    设计芯片的时候要考虑未来工艺,需要超前点
  2. 使用抽象简化设计
  3. 加速经常性事件
  4. 通过并行提高性能
  5. 通过流水线提高性能
  6. 通过预测提高性能
  7. 存储层次
    Memory Hierarchy
  8. 通过冗余提高可靠性

chap2 指令

RV字段

R型(用于寄存器)

funct7 rs2 rs1 funct3 rd opcode 总共
7位 5位 5位 3位 5位 7位 32位
  • opcode:指令基本操作
  • rd:目的操作数寄存器,存放结果
  • funct3:一个另外的操作码字段
  • rs1:第一个源操作数寄存器
  • rs2:第二个源操作数寄存器
  • funct7:一个另外的操作码字段

funct3和funct7用来表示opcode的附加字段(比如add和sub的opcode都是0110011,而add的funct7是0000000,sub的是0100000

I型(用于带常数的算数指令和加载指令)

immediate rs1 funct3 rd opcode 总共
7位 5位 3位 5位 7位 32位
  • immediate为补码,可以表示-2^11^ 到2^11^ - 1之间的整数,当I型格式用于加载指令时,immediate表示一个偏移量
  • ld指令的funct3为3

S型(用于Store)

immediate[11:5] rs2 rs1 funct3 immediate[4:0] opcode 总共
7位 5位 5位 3位 5位 7位 32位
  • immediate设计成两个字段是因为想把rs2和rs1跟上面的处于同一位置,降低硬件复杂性,funct3和opcode也是同一位置

逻辑操作

  • RV里没有!的逻辑操作,可以使用xxx XOR 全FF实现

  • 移位操作是I型格式,因为只用到immediate的低6位字母(不能移超过63次),所以高6位被当做另外的操作码字段,即funct6

调用约定

  • x10~x17:八个,用于传参/返回值

  • x0:一定是常量0,如果试图更改会被直接丢弃

  • x1:返回地址寄存器,用于返回到起始点

  • 跳转-链接指令(jal):跳转到某个地址的同时将下一条指令的地址保存到目标寄存器rd

    • jal x1, ProcedureAddress:跳转到ProcedureAddress并把返回地址写入x1
  • RV中x2是栈指针(stack pointer/ sp)

  • RV把19个寄存器分成两组:

    • x5~x7以及x28~x31:临时寄存器,过程调用里不被【被调用者(callee)】保存
    • x8~x9以及x18~x27:保存寄存器,在过程调用里必须被保存
  • RV使用帧指针(frame pointer)来表示过程帧的第一个双字(类似x64的rbp)

  • 超过8个参数怎么办?跟x86的处理方式一样,额外的放stack上

RV寄存器约定

大立即数处理

lui指令(load upper immediate)

取立即数高位,将20位常数加载到寄存器的31到12位,并把31位复制填充到最左边32位,最右边12位用0填充。

这条指令允许使用两条指令创建32位常量。lui的指令格式为U型,因为其他的不支持大常量。
加载一个大常量的过程如lui x19, 12到31位的数; addi x19, x19, 低12位的数

分支寻址

分支指令使用SB型指令,可以表示-4096到4094的分支地址,以2的倍数表示。

SB型

比如bne x10, x11, 2000 // if x10 != x11, goto location 2000
表示为

imm[12] imm[10:5] rs2 rs1 funct3 imm[4:1] imm[11] opcode
0 111110 01011 01010 001 1000 0 1100111

opcode=1100111是条件分支的操作码,bne的funct3是001

唯一使用UJ型的jal指令(无条件跳转-链接)

比如jal x0, 2000 // goto location 2000 = 0111 1101 0000

imm[20] imm[10:1] imm[11] imm[19:12] rd opcode
0 11 1110 1000 0 0000 0000 0 0000 110 1111

但是20位的地址空间太小了,另一种方法使用寄存器+偏移的方式,能够扩大到2^64^

使用哪个寄存器?

SPEC基准测试里有一半的条件分支跳到小于16距离的位置

因此如果用PC来当基址,那就很理想,这种叫PC相对寻址,RV对条件分支和无条件跳转使用PC相对寻址。在RV里PC相对偏倚表示分支和目标指令之间的半字数(2 bytes),jal里20位可以编码PC±2^19^个半字/ ±1MB的距离

同步

atomic exchange原语

jyy讲过,拿1去对🔐变量不断交换,交换出来0说明拿到锁了,交换出来1说明没拿到(被占用)

指令对

第二条指令返回一个值,表示该指令对是否被原子执行。

这对指令指保留加载(load-reserved)双字的特殊加载指令(称为lr.d)和一个条件存储(sotre-conditional)双字的特殊存储指令(称为sc.d),如果加载的内容跟存储前探测到的内容不同,条件存储就会失败,不会写入内存。成功就会把另一个寄存器的值更改为0,如果失败就改成非0。
sc.d指定了三个寄存器:保存地址、指示原子操作失败成功、如果成功就把指写入内存。

双指令实现原子交换

上面的过程实现了在x20指定的内存上的原子交换,即atomic_exchange(x23, mem[x20]

链接

三个工作步骤:

  1. 将代码和数据模块按符号特征放入内存
  2. 决定数据和指令标签的地址
  3. 修正内部和外部引用

链接需求

MIPS vs RISC-V

相同

相同点

不同

  • 除了eq、neq外的条件分支。RV只提供分支指令来比较两个寄存器,而MIPS提供比较指令,把比较结果写入一个寄存器(0 or 1)
  • MIPS只执行小于比较,存在有符号和无符号版本(slt和sltu)
  • 完整的MIPS指令系统 » RSIC-V

总结

  • 讲了指令系统体系结构ISA(Instruction Set Architecture)的历史
  • RV的指令结构
  • 实现同步
  • RV与x86、MIPS的比较
Licensed under CC BY-NC-SA 4.0