跳转至

RISC-V

编译工具链

  • 工具链仓库

    # 仓库地址
    https://github.com/riscv-collab/riscv-gnu-toolchain
    
    git clone https://github.com/riscv-collab/riscv-gnu-toolchain.git
    

  • 显式添加默认的平台支持并附加特定的平台

    ./configure --prefix=/opt/riscv \
      --enable-multilib \
      --with-multilib-generator="rv64gc-lp64d--;rv64imac-lp64--;rv32gc-ilp32d--;rv32imac-ilp32--;rv32ima-ilp32--"
    

  • 验证支持的多库组合

    riscv64-unknown-elf-gcc --print-multi-lib
    

RISC-V主要内容

  1. ISA命名规范

    RV[###][abc...xyz]
    

    • RV用于标识RISC-V 。
    • [###]: {32, 64, 128} 用于标识处理器的字宽(寄存器的宽度)。
    • [abc...xyz]: 标识该处理器支持的指令集模块集合。
  2. 模块化的ISA

    • RISC-V ISA = 1个基本整数指令集 + 多个可选的扩展指令集。
  3. 通用寄存器

    • 定义了32个通用寄存器以及一个PC。
    • 寄存器宽度由ISA指定。
    • 每个寄存器具体编程时有特定的用途以及各自的别名。由RISC-V ABI定义。
  4. Hart

    • HARdware Thread(相当于一个基本的CPU单元)
  5. 特权级别

    • 机器模式(M Machine Mode):最高权限,用于启动、配置硬件和异常处理。
    • 监督模式(S Supervisor Mode):运行操作系统内核。
    • 用户模式(U User/Application Mode):运行应用程序,权限受限。 (可选H Mode:支持虚拟化扩展的超监模式。)
  6. Control and Status Registers(CSR)

    • 不同的特权级别下时分别对应各自的一套Register。
    • 高级别的特权级别下可以访问低级别的CSR。
    • RISC-V 定义了专门用于操作CSR的指令。
    • RISC-V 定义了特定的指令可以用于在不同特权级别之间进行切换。
  7. 内存管理和保护(物理内存保护、虚拟内存)

    • 物理内存保护(Physical Memory Protection, PMP)
    • 虚拟内存 RISC-V 的虚拟内存(Virtual Memory)既依赖硬件支持,也依赖软件实现。其 ISA(指令集架构)提供了明确的硬件机制,包括特权模式、控制状态寄存器(CSR)和地址转换机制,而操作系统(如 Linux)则负责页表管理、缺页处理等软件层面的实现。
  8. 异常和中断

    risc-v的大多数异常会中断返回后会再执行一次异常指令。而中断后本身返回后会执行下一条语句。

主机字节序

  • 二进制码: 左高右底
  • 内存布局: 上高下底
  • 大端序: 高位二进制码存入底地址
  • 小端序: 高位二进制码存入高地址
  • risc-v使用小端序
  • 注意:字节序是否反转以字节为单位,一个字节8位,8位内部不反转

risc-v 的6种指令格式

  • R-Type: 用于寄存器间的算术逻辑运算(如 add x1, x2, x3)。
  • I-Type: 用于立即数操作、加载指令和跳转(如 addi x1, x2, 5lw x1, 4(x2))。
  • S-Type: 专用于存储指令到内存(如 sw x1, 8(x2))。
  • B-Type: 用于条件分支跳转(如 beq x1, x2, label)。
  • U-Type: 用于将长立即数装入寄存器高位(如 lui x1, 0x12345)。
  • J-Type: 用于无条件长跳转(如 jal x1, far_away_label)。

汇编相关命令

  1. 编写汇编 nvim test.s
  2. 编译 [rgcc] -march=[rv32im] -mabi=ilp32 -c test.s -o test.o
  3. 链接 [rld] -m elf32lriscv -o test.elf test.o
  4. 查看段头信息 [rreadelf] -S test.elf
  5. 查看 .text 段原始内容 [rreadelf] -x .text test.elf
  6. 反汇编所有段 [robjdump] -D test.elf
  7. 反汇编代码段 [robjdump] -d test.elf
  8. 展开所有伪指令 [robjdump] -d -M no-aliases test.elf

模拟器运行与调试

  1. 模拟器运行程序。启动时暂停CPU等待GDB连接,默认1234端口。
    • qemu-system-riscv32 -nographic -smp 1 -machine virt -bios none -kernel kernel -S -s
    • -S 启动时暂停CPU,等待GDB的 continue 命令才开始执行
    • -s 是 -gdb tcp:127.0.0.1:1234 的缩写
  2. 运行gdb [rgdb] test.elf
  3. gdb连接远程1234端口 target remote : 1234
  4. monitor quit 让QEMU立即停止运行
  5. gdb运行参数 -q 安静模式,不显示介绍信息, -x <file> 执行指定文件中的 GDB 命令

示例代码

  • makefile

    CROSS_COMPILE = riscv64-unknown-elf-
    CFLAGS = -nostdlib -fno-builtin -march=rv32im -mabi=ilp32 -g -Wall
    
    CC = ${CROSS_COMPILE}gcc
    OBJDUMP = ${CROSS_COMPILE}objdump
    
    QEMU = qemu-system-riscv32
    QFLAGS = -nographic -smp 1 -machine virt -bios none
    
    GDBINIT = gdbinit
    GDB = ${CROSS_COMPILE}gdb
    
    EXEC = test
    SRC = ${EXEC}.s
    
    all:
        @${CC} ${CFLAGS} ${SRC} -Ttext=0x80000000 -o ${EXEC}.elf
    
    run: all
        @${QEMU} ${QFLAGS} -kernel ./${EXEC}.elf
    
    debug: all
        @${QEMU} ${QFLAGS} -kernel ${EXEC}.elf -s -S &
        @${GDB} ${EXEC}.elf -q -x ${GDBINIT}
    
    clean:
        rm -rf *.o *.bin *.elf
    

  • gdbinit

    # /z 十六进制显示寄存器中的值
    # 每一步都显示
    display/z $x5
    display/z $x6
    display/z $x7
    
    # 自动反汇编,每一步都反汇编
    set disassemble-next-line on
    b _start
    target remote : 1234
    c
    

RISC-V 常用指令和伪指令系统分类总结

算术运算指令

  • 基本算术运算
指令 格式 功能描述
add add rd, rs1, rs2 寄存器加法 (rd = rs1 + rs2)
sub sub rd, rs1, rs2 寄存器减法 (rd = rs1 - rs2)
addi addi rd, rs1, imm 立即数加法 (rd = rs1 + sign-extended imm)
mul mul rd, rs1, rs2 有符号乘法 (RV32M/RV64M)
div div rd, rs1, rs2 有符号除法 (RV32M/RV64M)
  • 逻辑运算
指令 格式 功能描述
and and rd, rs1, rs2 按位与
or or rd, rs1, rs2 按位或
xor xor rd, rs1, rs2 按位异或
andi andi rd, rs1, imm 立即数按位与
ori ori rd, rs1, imm 立即数按位或
xori xori rd, rs1, imm 立即数按位异或
  • 移位运算
指令 格式 功能描述
sll sll rd, rs1, rs2 逻辑左移 (rd = rs1 << rs2[4:0])
srl srl rd, rs1, rs2 逻辑右移
sra sra rd, rs1, rs2 算术右移
slli slli rd, rs1, shamt 立即数逻辑左移
srli srli rd, rs1, shamt 立即数逻辑右移
srai srai rd, rs1, shamt 立即数算术右移

数据传输指令

  • 加载指令
指令 格式 功能描述
lb lb rd, offset(rs1) 加载字节 (符号扩展)
lh lh rd, offset(rs1) 加载半字 (符号扩展)
lw lw rd, offset(rs1) 加载字 (32位)
ld ld rd, offset(rs1) 加载双字 (RV64)
lbu lbu rd, offset(rs1) 加载无符号字节
lhu lhu rd, offset(rs1) 加载无符号半字
lwu lwu rd, offset(rs1) 加载无符号字 (RV64)
  • 存储指令
指令 格式 功能描述
sb sb rs2, offset(rs1) 存储字节
sh sh rs2, offset(rs1) 存储半字
sw sw rs2, offset(rs1) 存储字
sd sd rs2, offset(rs1) 存储双字 (RV64)

控制流指令

  • 无条件跳转
指令 格式 功能描述
jal jal rd, offset 跳转并链接 (rd = pc+4)
jalr jalr rd, offset(rs1) 间接跳转并链接
j j offset 直接跳转 (伪指令)
  • 条件分支
指令 格式 功能描述
beq beq rs1, rs2, offset 相等时分支
bne bne rs1, rs2, offset 不等时分支
blt blt rs1, rs2, offset 有符号小于时分支
bge bge rs1, rs2, offset 有符号大于等于时分支
bltu bltu rs1, rs2, offset 无符号小于时分支
bgeu bgeu rs1, rs2, offset 无符号大于等于时分支

伪指令

  • 常用伪指令
伪指令 实际指令 功能描述
nop addi x0, x0, 0 空操作
mv rd, rs addi rd, rs, 0 寄存器复制
not rd, rs xori rd, rs, -1 按位取反
neg rd, rs sub rd, x0, rs 取负值
j offset jal x0, offset 无条件跳转
ret jalr x0, 0(x1) 从子程序返回
call offset auipc x1, offset[31:12]
jalr x1, offset[11:0](x1)
调用远过程
tail offset auipc x0, offset[31:12]
jalr x0, offset[11:0](x0)
尾调用
  • 地址加载伪指令
伪指令 实际指令 功能描述
la rd, symbol auipc rd, symbol[31:12]
addi rd, rd, symbol[11:0]
加载绝对地址
li rd, imm 根据立即数大小选择不同指令序列 加载立即数

系统指令

  • CSR操作
指令 格式 功能描述
csrrw csrrw rd, csr, rs1 CSR读后写
csrrs csrrs rd, csr, rs1 CSR读后置位
csrrc csrrc rd, csr, rs1 CSR读后清除
csrr csrr rd, csr CSR读 (伪指令 = csrrs rd, csr, x0)
csrw csrw csr, rs1 CSR写 (伪指令 = csrrw x0, csr, rs1)
  • 特权指令
指令 格式 功能描述
ecall ecall 环境调用
ebreak ebreak 断点
wfi wfi 等待中断
mret mret 从机器模式异常返回
sret sret 从监管模式异常返回

原子操作指令 (A扩展)

指令 格式 功能描述
lr.w lr.w rd, (rs1) 加载保留 (32位)
sc.w sc.w rd, rs2, (rs1) 条件存储 (32位)
amoswap.w amoswap.w rd, rs2, (rs1) 原子交换 (32位)
amoadd.w amoadd.w rd, rs2, (rs1) 原子加 (32位)
amoand.w amoand.w rd, rs2, (rs1) 原子与 (32位)

浮点指令 (F/D扩展)

  • 基本浮点运算
指令 格式 功能描述
fadd.s fadd.s rd, rs1, rs2 单精度浮点加
fsub.s fsub.s rd, rs1, rs2 单精度浮点减
fmul.s fmul.s rd, rs1, rs2 单精度浮点乘
fdiv.s fdiv.s rd, rs1, rs2 单精度浮点除
fsqrt.s fsqrt.s rd, rs1 单精度平方根
  • 浮点转换
指令 格式 功能描述
fcvt.w.s fcvt.w.s rd, rs1 单精度转有符号字
fcvt.s.w fcvt.s.w rd, rs1 有符号字转单精度

RISC-V 32位寄存器分类说明

基础寄存器(x0-x31)

寄存器 别名 用途 调用约定 说明
x0 zero 硬编码零 - 读取始终为0,写入无效
x1 ra 返回地址 Caller 存储函数返回地址
x2 sp 栈指针 Callee 当前栈指针
x3 gp 全局指针 - 指向全局数据区
x4 tp 线程指针 - 线程局部存储
x5 t0 临时寄存器 Caller 临时数据
x6 t1 临时寄存器 Caller 临时数据
x7 t2 临时寄存器 Caller 临时数据
x8 s0/fp 保存寄存器/帧指针 Callee 函数帧指针
x9 s1 保存寄存器 Callee 跨调用保存
x10 a0 函数参数/返回值 Caller 第一个参数/返回值
x11 a1 函数参数/返回值 Caller 第二个参数/返回值
x12 a2 函数参数 Caller 第三个参数
x13 a3 函数参数 Caller 第四个参数
x14 a4 函数参数 Caller 第五个参数
x15 a5 函数参数 Caller 第六个参数
x16 a6 函数参数 Caller 第七个参数
x17 a7 函数参数 Caller 第八个参数
x18 s2 保存寄存器 Callee 跨调用保存
x19 s3 保存寄存器 Callee 跨调用保存
x20 s4 保存寄存器 Callee 跨调用保存
x21 s5 保存寄存器 Callee 跨调用保存
x22 s6 保存寄存器 Callee 跨调用保存
x23 s7 保存寄存器 Callee 跨调用保存
x24 s8 保存寄存器 Callee 跨调用保存
x25 s9 保存寄存器 Callee 跨调用保存
x26 s10 保存寄存器 Callee 跨调用保存
x27 s11 保存寄存器 Callee 跨调用保存
x28 t3 临时寄存器 Caller 临时数据
x29 t4 临时寄存器 Caller 临时数据
x30 t5 临时寄存器 Caller 临时数据
x31 t6 临时寄存器 Caller 临时数据

按功能分类

1. 特殊功能寄存器

类别 寄存器 别名 主要用途
控制寄存器 x0 zero 常数零值
栈指针 x2 sp 栈操作
链接寄存器 x1 ra 函数返回
全局指针 x3 gp 全局数据访问
线程指针 x4 tp 线程局部存储

2. 参数传递寄存器

用途 寄存器 数量 说明
参数传递 a0-a7 (x10-x17) 8个 函数调用参数
返回值 a0-a1 (x10-x11) 2个 函数返回值

3. 临时寄存器(调用者保存)

类型 寄存器 数量 说明
临时寄存器 t0-t2 (x5-x7) 3个 短期临时数据
临时寄存器 t3-t6 (x28-x31) 4个 短期临时数据
小计 t0-t6 7个 调用者负责保存

4. 保存寄存器(被调用者保存)

类型 寄存器 数量 说明
帧指针 s0/fp (x8) 1个 栈帧指针
保存寄存器 s1-s11 (x9-x27) 11个 跨调用保持
小计 s0-s11 12个 被调用者负责保存

调用约定总结

Caller-Saved(调用者保存)

  • 临时寄存器: t0-t6 (x5-x7, x28-x31)
  • 参数寄存器: a0-a7 (x10-x17)
  • 返回地址: ra (x1)

Callee-Saved(被调用者保存)

  • 保存寄存器: s0-s11 (x8-x27)
  • 栈指针: sp (x2) - 通常由硬件维护

汇编代码中的使用示例

# 函数调用示例
func:
    addi sp, sp, -16     # 分配栈空间
    sw   ra, 12(sp)      # 保存返回地址 (Caller-saved, 但被调用者保存)
    sw   s0, 8(sp)       # 保存s0 (Callee-saved)

    mv   s0, a0          # 保存参数到s0
    li   t0, 100         # 使用临时寄存器t0

    # 函数体...

    lw   s0, 8(sp)       # 恢复s0
    lw   ra, 12(sp)      # 恢复返回地址
    addi sp, sp, 16      # 释放栈空间
    ret                  # 返回到ra地址

重要说明

  1. RV32与RV64寄存器:寄存器编号和别名相同,只是位宽不同(32位 vs 64位)
  2. 压缩指令:某些指令只能访问x8-x15寄存器(更短的编码)
  3. ABI名称:在汇编代码中建议使用ABI名称(如a0而不是x10)提高可读性

CSR(控制状态寄存器)分类表格

CSR 地址空间布局

地址范围 特权级 描述 示例
0x000-0x0FF 机器模式 机器信息寄存器 mhartid, mstatus
0x300-0x3FF 机器模式 机器模式控制和状态 mtvec, mepc
0x700-0x7FF 机器模式 机器模式计数器 mcycle, minstret
0x800-0x8FF 监管模式 监管模式CSR stvec, sepc
0xC00-0xCFF 用户模式 用户模式CSR cycle, time
0x1000-0x1FFF 自定义 厂商自定义 实现特定功能

重要的机器模式(M-mode)CSR

CSR名称 地址 读写权限 功能描述
mhartid 0xF14 RO 硬件线程ID(核心编号)
mstatus 0x300 RW 机器模式状态寄存器
misa 0x301 RO ISA信息(架构特性)
medeleg 0x302 RW 异常委托给监管模式
mideleg 0x303 RW 中断委托给监管模式
mie 0x304 RW 机器模式中断使能
mtvec 0x305 RW 陷阱向量基地址
mcounteren 0x306 RW 计数器使能
mscratch 0x340 RW 机器模式临时存储
mepc 0x341 RW 异常程序计数器
mcause 0x342 RW 异常原因
mtval 0x343 RW 陷阱值(错误地址等)
mip 0x344 RW 机器模式中断等待

计数器相关CSR

CSR名称 地址 特权级 功能描述
mcycle 0xB00 M 机器周期计数器(低32位)
mcycleh 0xB80 M 机器周期计数器(高32位)
minstret 0xB02 M 退休指令计数器(低32位)
minstreth 0xB82 M 退休指令计数器(高32位)
cycle 0xC00 U 用户模式周期计数器
time 0xC01 U 实时时钟计数器
instret 0xC02 U 用户模式指令计数器

CSR 访问指令

指令格式 功能 示例 描述
csrr rd, csr 读CSR csrr t0, mhartid 读取CSR到寄存器
csrw csr, rs 写CSR csrw mtvec, t0 将寄存器写入CSR
csrs csr, rs 置位CSR csrs mie, t0 将寄存器中为1的位在CSR中置位
csrc csr, rs 清零CSR csrc mie, t0 将寄存器中为1的位在CSR中清零
csrwi csr, imm 立即数写 csrwi mstatus, 0 用立即数写CSR
csrsi csr, imm 立即数置位 csrsi mie, 0x8 用立即数置位CSR
csrci csr, imm 立即数清零 csrci mie, 0x8 用立即数清零CSR

内存映射关系

RISC-V 系统内存视图:

+-------------------+ 高地址
|    内存映射I/O     |
|   (设备寄存器)     |
+-------------------+
|    主内存         |
|   (DRAM)          |
+-------------------+
|    CSR空间        |  ← 通过csr指令访问,不在常规内存中
|   (控制状态寄存器) |     独立于32个通用寄存器
+-------------------+ 低地址

重要特性总结

  1. 独立空间:CSR与32个通用寄存器(x0-x31)完全独立
  2. 特权分级:不同特权级别(M/S/U)可访问的CSR不同
  3. 硬件直接实现:由CPU硬件提供,不是内存映射
  4. 系统控制:用于中断、异常、内存管理、性能计数等
  5. 原子操作:CSR指令提供原子读-修改-写操作

使用示例

# 读取当前核心ID
csrr a0, mhartid

# 设置陷阱向量表
la t0, trap_handler
csrw mtvec, t0

# 启用机器模式中断
li t0, 0x8
csrs mie, t0

# 保存/恢复临时数据
csrw mscratch, sp  # 保存sp到mscratch
csrr sp, mscratch  # 从mscratch恢复sp

CSR是RISC-V架构中用于系统控制和状态监控的核心机制,对于操作系统和底层开发至关重要。