2. RISC-V指令集基础
玄铁C906/C910均基于RISC-V开放指令集架构,C906面向RV64GC,C910面向RV64GC并支持更高性能的乱序执行与多核扩展。理解RISC-V的指令格式、寄存器组织、特权模式、CSR寄存器与异常处理机制,是后续编写裸机启动代码、移植操作系统、调试异常与中断的基础。本节从指令编码出发,逐步过渡到特权架构与异常处理,帮助建立完整的底层认知框架。
一、RV32/RV64指令格式:定长编码与立即数拼接
RISC-V采用定长32位指令编码(RV32I/RV64I基础指令),并可通过扩展指令集支持16位压缩指令(C扩展)。定长编码简化了取指与译码逻辑,使流水线设计更规整。基础指令格式分为R型、I型、S型、B型、U型、J型六种,核心区别在于立即数字段的位置与拼接方式。
- R型:用于寄存器-寄存器运算,如add、sub、and、or。字段为funct7[31:25]、rs2[24:20]、rs1[19:15]、funct3[14:12]、rd[11:7]、opcode[6:0]。
- I型:用于立即数运算与加载,如addi、lw、jalr。立即数为12位有符号数,位于[31:20]。
- S型:用于存储,如sw、sh、sb。立即数被拆分为[31:25]与[11:7]两段,需拼接后符号扩展。
- B型:用于条件分支,如beq、bne。立即数为13位,最低位恒为0,编码时分散在多个字段中。
- U型:用于高位立即数加载,如lui、auipc。立即数位于[31:12],低12位补0。
- J型:用于无条件跳转,如jal。立即数为21位,最低位恒为0,编码同样分散。
RV64与RV32的主要差异在于:寄存器宽度由32位扩展为64位,新增了字操作指令(如addw、subw、ld、sd),并调整了部分立即数符号扩展规则。例如,RV32中addi的12位立即数符号扩展至32位,而RV64中需符号扩展至64位。此外,RV64的移位指令使用6位shamt字段(RV32为5位),以支持64位宽度的移位。
二、寄存器组织:32个通用寄存器与ABI约定
RISC-V定义了32个通用寄存器(x0~x31),其中x0恒为0,写入无效,读取始终返回0。RV64下每个寄存器宽度为64位。为便于汇编编程与函数调用,RISC-V定义了ABI名称与调用约定,如下表所示。
| 寄存器 | ABI名称 | 用途 | 调用约定 |
|---|---|---|---|
| x0 | zero | 常量0 | 只读,恒为0 |
| x1 | ra | 返回地址 | 调用者保存 |
| x2 | sp | 栈指针 | 被调用者保存 |
| x3 | gp | 全局指针 | — |
| x4 | tp | 线程指针 | — |
| x5-x7 | t0-t2 | 临时寄存器 | 调用者保存 |
| x8 | s0/fp | 保存寄存器/帧指针 | 被调用者保存 |
| x9 | s1 | 保存寄存器 | 被调用者保存 |
| x10-x11 | a0-a1 | 函数参数/返回值 | 调用者保存 |
| x12-x17 | a2-a7 | 函数参数 | 调用者保存 |
| x18-x27 | s2-s11 | 保存寄存器 | 被调用者保存 |
| x28-x31 | t3-t6 | 临时寄存器 | 调用者保存 |
此外,RISC-V还包含程序计数器pc,以及浮点寄存器f0~f31(F/D扩展)。在玄铁C906/C910中,浮点单元与向量单元均通过扩展寄存器进行访问,需在CSR中使能相应状态。
三、特权模式:M/S/U三级与玄铁实现
RISC-V特权架构定义了三个特权级别:机器模式(M-mode)、监督模式(S-mode)和用户模式(U-mode)。M-mode是最高特权级,负责启动、异常向量初始化与CSR访问控制;S-mode用于运行操作系统内核;U-mode用于运行用户应用程序。玄铁C906/C910均支持M/S/U三级模式,并可通过misa寄存器查询支持的扩展指令集。
- M-mode:复位后默认进入,可访问所有CSR,处理最高优先级异常与中断。
- S-mode:由M-mode通过mstatus.MPP配置后,执行mret进入,可访问S级CSR,处理系统调用与页错误。
- U-mode:由S-mode通过sstatus.SPP配置后,执行sret进入,权限最低,无法直接访问CSR。
四、CSR寄存器与异常处理机制
CSR(Control and Status Register)是RISC-V特权架构的核心,用于配置特权状态、异常向量、中断使能、性能计数等。CSR地址为12位,通过csrrw、csrrs、csrrc等指令读写。常用CSR包括:
- mstatus:全局状态寄存器,包含MIE、MPIE、MPP等字段,控制中断使能与特权切换。
- mtvec:机器模式异常向量基址,低2位决定向量模式(直接/向量)。
- mepc:机器模式异常程序计数器,保存异常发生时的pc。
- mcause:异常原因寄存器,高位置1表示中断,低位表示异常码。
- mie/mip:中断使能与挂起寄存器,控制定时器、外部、软件中断。
- satp:S-mode地址转换与保护,用于开启分页。
异常处理流程如下:当异常或中断发生时,硬件自动将当前pc保存到mepc,将原因写入mcause,将mstatus.MIE保存到MPIE并清除MIE,然后跳转到mtvec指定的入口。异常处理程序需保存现场、读取mcause判断原因、处理完毕后执行mret返回。mret会恢复mstatus.MIE,并将pc设置为mepc的值。
# 异常向量初始化示例(M-mode)
la t0, trap_entry
csrw mtvec, t0
# 使能定时器中断
li t0, 0x80
csrs mie, t0
csrs mstatus, 0x8
# 异常入口保存现场
trap_entry:
addi sp, sp, -128
sd ra, 0(sp)
sd t0, 8(sp)
csrr t0, mcause
# 根据mcause分发处理
...
ld ra, 0(sp)
addi sp, sp, 128
mret
五、案例与复盘:从复位到异常处理的完整链路
以玄铁C906启动为例:复位后CPU进入M-mode,pc指向复位向量。启动代码首先初始化栈指针sp,设置mtvec指向异常入口,配置mie使能定时器中断,然后通过mret切换到S-mode运行操作系统。当定时器中断发生时,硬件跳转到mtvec,保存mepc与mcause,软件读取mcause判断为中断后,清除mip中的定时器挂起位,执行调度,最后mret返回。若发生非法指令异常,mcause为2,处理程序可打印mepc与指令编码,辅助定位问题。
复盘要点:一是CSR读写必须使用csrrw/csrrs/csrrc指令,不可用普通load/store;二是异常入口需保存所有可能被破坏的寄存器,尤其是ra与t0-t6;三是mret与sret的返回地址分别来自mepc与sepc,不可混淆;四是玄铁C910支持中断嵌套,需在异常处理中谨慎管理mstatus.MIE与MPIE,避免中断丢失或重复进入。