RISC-V

RISC 哲学:

  • 保持指令集小而简单,让其能更容易构建快速的硬件
  • 通过将简单的组合在一起让软件实现复杂的操作

RISC-V 中有 32 个寄存器

  • 序号为 x0-31
  • x0 的值总是为 0
  • 可以使用序号引用,也可以使用名字

注释 #,只支持单行注释

加减的基本格式:sub x3, x4, x5 等价于 x3 = x4 - x5

立即数有不同的指令:addi x3, x4, 10,即最后一个数必须为立即数。注意到没有对应的减法的版本,因为可以通过加法实现

因为 0 非常常用,所以 x0 寄存器在硬件上设置为 0,即例如 add x0, x3, x4 的指令无效

从内存中读取数据:lw x10, 12(x15),其中 x15 指向 int 数组 A,则 x10 = A[3],即 12 表示字节的偏移

将数据存到内存中:sw x10, 40(x15)

除了按 word 转移数据,也支持按字节 byte 转移数据,如 lbsb,注意 lb x10, 3(x11) 将内存中的数据复制到 x10 的低位中,使用符号扩展前面的内容

也有 unsigned 版本 lbu,即使用 0 填充高位

分支指令:beq reg1, reg2, L1 表示如果 reg1 中的值 == reg2 中的值,则跳转到 L1 标签处,否则继续执行下一条指令

当然条件跳转还有其他版本,无条件跳转的指令为 j label

逻辑操作包括:andorxorsllsrl,注意没有逻辑 NOT,对 11111… 使用 xor 即可

一些常用指令的简写:

  • mv rd, rs = addi rd, rs, 0
  • li rd, 13 = addi rd, x0, 13
  • nop = addi x0, x0, 0

函数调用:

  • a0-a7(x10-17) 用于传参,a0-a1 用于返回值
  • ra(x1) 存储返回地址
  • s0-s1(x8-x9) 和 s2-s11(x18-x27) 保存寄存器
  • sp(x2) 是栈指针

被调函数返回使用 jr ra 之所以多了 r 是因为目标地址是一个变量

跳转并保存返回地址:jal 函数标签(jump and link)

  • 在函数调用中保持不变的变量保存在 s0-s11 中,用 callee 保存
  • 会变的,如 a0-a7, t0-t6 由 caller 保存
寄存器 ABI 名字 描述 保存者
x0 zero 硬线的零
x1 ra 返回地址 caller
x2 sp 栈指针 callee
x3 gp 全局指针
x4 tp 线程指针
x5 t0 临时/交替链接寄存器 caller
x6-7 t1-2 临时 caller
x8 s0/fp 保存寄存器/栈帧指针 callee
x9 s1 保存寄存器 callee
x10-11 a0-a1 函数参数/返回值 caller
x12-17 a2-a7 函数参数 caller
x18-27 s2-11 保存的寄存器 callee
x28-31 t3-6 临时 caller
算术/逻辑 立即数 分支/跳转 载入/保存
add rd, rs1, rs2 addi rd, rs1, rs2 beq rs1, rs2, Label lw rd, rs1, imm
sub rd, rs1, rs2 subi rd, rs1, rs2 bne rs1, rs2, Label lb rd, rs1, imm
and rd, rs1, rs2 andi rd, rs1, rs2 bge rs1, rs2, Label lbu rd, rs1, imm
or rd, rs1, rs2 ori rd, rs1, rs2 blt rs1, rs2, Label sw rd, rs1, imm
xor rd, rs1, rs2 xori rd, rs1, rs2 bgeu rs1, rs2, Label sb rd, rs1, imm
sll rd, rs1, rs2 slli rd, rs1, rs2 bltu rs1, rs2, Label
srl rd, rs1, rs2 srli rd, rs1, rs2 jal rd, Label
sra rd, rs1, rs2 srai rd, rs1, rs2 jalr rd, rs, imm

RISC-V 指令格式

每条指令都由 32 bit 组成

**R-Format:**寄存器之间的算术操作

R-Format

  • 其中 opcode 指明是哪一类型的指令,如 0110011 表示 R-Format 指令
  • funct7 + funct3:和 opcode 共同决定是具体是哪个操作
  • rs1, rs2, rd:表示具体哪个寄存器(x0-x31)

所有R-format

**I-Format:**寄存器和立即数之间的算术操作和装载

I-format

和 R-format 很像,只有 funct7 + rs2 合并成了立即数,注意到其只能表达 [-2048, 2047] 之间的数,同时立即数在算术操作中总是符号扩展到 32 位

所有I-format

注意到位移指令只使用了低 5 位,即只能移动 0-31 bit

**S-Format:**存储

S-format

所有S-format

**B-format:**分支

跳转时使用 PC 相关地址,同时为了兼容 16 bit 的压缩 RISC-V 指令,每次调整不是 4 字节,而是 2 字节,即 PC = PC + immediate*2

B-format

**U-format:**立即数指令的高 20 bit

U-format

  • lui:装载立即数高位
  • auipc:把立即数高位加到 PC 上

注意加法是符号扩展的,所以有时需要高位 +1

**J-format:**跳转

J-format

JAL 在 rd 寄存器中保存返回地址

JALR

jalr rd, rs, immediate 设置 PC = rs + immediate,注意乘 2 字节

更多详见RISC-V Green Card (from P&H Book)