刚开始学时先看 RV32 还是 RV64?
先看 RV32 更容易建立基础心智模型,因为寄存器宽度、load/store 和立即数讨论都更直接。等你开始碰指针、系统调用、ABI 和地址宽度,再补 RV64 差异会更稳。
从 RISC-V 架构和内核开发初学者的视角,集中回答常见的概念误区和实践困惑。帮助你建立正确的排查思路。
先看 RV32 更容易建立基础心智模型,因为寄存器宽度、load/store 和立即数讨论都更直接。等你开始碰指针、系统调用、ABI 和地址宽度,再补 RV64 差异会更稳。
先问自己读出来的是 32 位数据,还是一个地址/指针。读 32 位数据时常见 lw;读 64 位地址或 64 位值时,RV64 更常见 ld。另外注意 lw 在 RV64 里会自动符号扩展结果到 64 位,而 ld 直接加载完整 64 位。
因为 RV64 里普通 add 按 XLEN 位结果语义工作,而 addw 专门保留 32 位 word 结果语义,再把结果符号扩展回 64 位。这在处理 C 语言 32 位 int 结果或模拟 32 位程序行为时很关键;普通 64 位指针地址计算应使用 XLEN 位运算,不要用 addw 截断。
中断来自异步外部事件(定时器、外部设备),异常由当前指令同步触发(非法指令、地址未对齐、缺页、ecall)。硬件通过 mcause / scause 寄存器的最高位区分:bit[XLEN-1]=1 为中断,0 为异常。低几位存具体编号,比如中断 7 是机器定时器中断(MTI),异常 2 是非法指令。
ecall 会产生环境调用异常,随后按当前特权级、委托配置和 trap 目标进入对应的 trap 流程。硬件会写入对应级别的 epc/cause/status 状态,并按 xstatus 中的前一中断使能和前一特权状态字段更新状态。epc 保存的是触发异常的那条 ecall 本身,而不是下一条指令。进入 M 模式时看 mepc/mcause/mtvec/mstatus;进入 S 模式时看 sepc/scause/stvec/sstatus。
mret 从 M 模式陷阱返回,sret 从 S 模式陷阱返回。如果 M 模式直接处理了来自 U/S 的异常就用 mret。如果 M 模式把异常委托给 S 模式处理,S 模式 handler 用 sret。mret 把 PC 恢复到 mepc,并按规范把 MPP/MPIE 更新到返回后的状态;sret 同理使用 sepc/SPP/SPIE。
CSR 位于独立的 12 位 CSR 地址空间(编号 0–4095),不属于普通内存地址空间,所以 load/store 不能访问。软件必须用 csrrw、csrrs、csrrc 及其立即数形式访问 CSR;这些指令提供读写、置位、清位等原子读改写语义。
MIE(bit 3)是在 hart 当前运行于 M-mode 时控制机器级中断全局使能的位;低特权级运行时还要结合特权级、委托和各级中断使能规则判断。M-mode trap 进入时,硬件把 MIE 保存到 MPIE(bit 7)并清 MIE,MPP(bits 12:11)保存 trap 前特权级,mret 再按这些字段恢复。S-mode 对应使用 SIE/SPIE/SPP。
LA 是通用“加载地址”伪指令,具体展开会受代码模型、PIC/非 PIC、重定位和符号可见性影响。LLA 表达 link-time-local 地址计算,常见展开是 PC 相对的 auipc+addi 序列,不通过 GOT 访问外部符号。写裸机或内核汇编时,应按符号是否本地、是否需要 PIC/GOT 选择 LA 或 LLA。
RISC-V 每条 32 位指令无法携带任意宽地址。AUIPC 把 20 位 U-immediate 左移 12 位后加到当前 PC,并写入 rd;再配合 ADDI 补低 12 位可构造 PC 相对数据地址,配合 JALR 可构造 PC 相对调用/跳转目标。绝对地址、GOT 和 PIC 展开还受代码模型与重定位规则影响,不能简单等同于 AUIPC+ADDI/JALR。
AMO(如 amoadd、amoswap)是单条指令的原子读-改-写,操作固定但简单可靠。LR/SC 是分步方案:LR 读内存并设 reservation,SC 只在 reservation 有效时才写入,否则失败。LR/SC 更灵活——可在 LR 和 SC 之间做任意计算,实现 compare-and-swap 等复杂原语,但可能虚假失败需要重试循环。两个机制互补,AMO 适合简单算术原子操作,LR/SC 适合通用 lock-free 数据结构。
本页参考以下官方 RISC-V 文档组织架构、ABI、CSR 和伪指令说明;平台或操作系统 ABI 差异仍需按对应规范核验。