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位宽度的移位。

RISC-V 六种基础指令格式(32位) 字段位置固定,立即数按格式拼接后符号扩展 R型 funct7[31:25] rs2[24:20] rs1[19:15] funct3[14:12] rd[11:7] opcode[6:0] add / sub / and / or I型 imm[11:0] rs1[19:15] funct3[14:12] rd[11:7] opcode[6:0] addi / lw / jalr S型 imm[11:5] rs2[24:20] rs1[19:15] funct3[14:12] imm[4:0] opcode[6:0] sw / sh / sb B型 imm[12|10:5] rs2[24:20] rs1[19:15] funct3[14:12] imm[4:1|11] opcode[6:0] beq / bne / blt U型 imm[31:12] rd[11:7] opcode[6:0] lui / auipc J型 imm[20|10:1|11|19:12] rd[11:7] opcode[6:0] jal

二、寄存器组织:32个通用寄存器与ABI约定

RISC-V定义了32个通用寄存器(x0~x31),其中x0恒为0,写入无效,读取始终返回0。RV64下每个寄存器宽度为64位。为便于汇编编程与函数调用,RISC-V定义了ABI名称与调用约定,如下表所示。

寄存器ABI名称用途调用约定
x0zero常量0只读,恒为0
x1ra返回地址调用者保存
x2sp栈指针被调用者保存
x3gp全局指针—
x4tp线程指针—
x5-x7t0-t2临时寄存器调用者保存
x8s0/fp保存寄存器/帧指针被调用者保存
x9s1保存寄存器被调用者保存
x10-x11a0-a1函数参数/返回值调用者保存
x12-x17a2-a7函数参数调用者保存
x18-x27s2-s11保存寄存器被调用者保存
x28-x31t3-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。
玄铁C910支持多核与多hart,每个hart独立拥有自己的CSR与特权状态。在SMP系统中,需通过mhartid区分不同hart,并分别初始化各自的异常向量表。

四、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
常见误区:在M-mode下直接访问S-mode CSR(如sstatus)会触发非法指令异常。若需在M-mode中配置S-mode状态,应通过mstatus.MPP与mret切换,或使用mstatus的代理字段。此外,mtvec向量模式要求入口地址4字节对齐,直接模式则无此限制。

五、案例与复盘:从复位到异常处理的完整链路

以玄铁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,避免中断丢失或重复进入。