3. 配置和读取CSR
任务目标
在RISC-V M-Mode下,通过内联汇编配置关键CSR寄存器(mstatus、mepc、mcause),并读取回显验证写入结果。本案例聚焦特权级状态保存与恢复机制中最核心的三个寄存器:mstatus(全局中断与特权级状态)、mepc(异常返回地址)、mcause(异常原因编码)。
环境与依赖
- RISC-V 64位工具链(
riscv64-unknown-elf-gcc)或带__riscv宏的交叉编译器 - QEMU模拟器(
qemu-system-riscv64)或真实RISC-V开发板(如SiFive HiFive1) - 无需操作系统,裸机程序直接运行在M-Mode
核心实现
以下代码在M-Mode下完成CSR的配置与读取。使用内联汇编的csrw/csrr指令直接操作寄存器,并通过UART输出(此处用内存映射地址模拟)打印结果。
// csr_demo.c - M-Mode CSR配置与读取
#include <stdint.h>
// 模拟UART输出(实际开发板替换为真实UART地址)
#define UART_BASE 0x10000000
#define UART_REG (*(volatile uint32_t *)UART_BASE)
static void put_char(char c) {
UART_REG = c;
}
static void print_hex(uint64_t val) {
const char hex[] = "0123456789abcdef";
put_char('0'); put_char('x');
for (int i = 60; i >= 0; i -= 4) {
put_char(hex[(val >> i) & 0xF]);
}
put_char('\n');
}
// 读取CSR
#define read_csr(reg) ({ uint64_t _v; asm volatile("csrr %0, " #reg : "=r"(_v)); _v; })
// 写入CSR
#define write_csr(reg, val) asm volatile("csrw " #reg ", %0" :: "r"((uint64_t)(val)))
int main(void) {
// 1. 配置mstatus: 设置MPP=11(M-Mode), MPIE=1(进入异常前中断使能)
uint64_t mstatus_val = read_csr(mstatus);
mstatus_val |= (3 << 11); // MPP = 11
mstatus_val |= (1 << 7); // MPIE = 1
mstatus_val &= ~(1 << 3); // MIE = 0 (当前关闭中断)
write_csr(mstatus, mstatus_val);
// 2. 配置mepc: 设置异常返回地址为当前PC+4 (模拟)
uint64_t mepc_val = 0x80000000; // 示例地址
write_csr(mepc, mepc_val);
// 3. 配置mcause: 设置异常原因 (例如: 3 = 断点)
write_csr(mcause, 3);
// 4. 读取并打印验证
put_char('m'); put_char('s'); put_char('t'); put_char('a'); put_char('t'); put_char('u'); put_char('s'); put_char(':');
print_hex(read_csr(mstatus));
put_char('m'); put_char('e'); put_char('p'); put_char('c'); put_char(':');
print_hex(read_csr(mepc));
put_char('m'); put_char('c'); put_char('a'); put_char('u'); put_char('s'); put_char('e'); put_char(':');
print_hex(read_csr(mcause));
// 5. 验证mstatus特定位
uint64_t check = read_csr(mstatus);
if ((check & (3 << 11)) == (3 << 11) && (check & (1 << 7)) && !(check & (1 << 3))) {
put_char('P'); put_char('A'); put_char('S'); put_char('S'); put_char('\n');
} else {
put_char('F'); put_char('A'); put_char('I'); put_char('L'); put_char('\n');
}
while (1); // 停机
}
编译与运行
# 编译为裸机ELF(使用QEMU的virt机器)
riscv64-unknown-elf-gcc -march=rv64im -mabi=lp64 -nostdlib -Ttext=0x80000000 -o csr_demo.elf csr_demo.c
# 用QEMU运行(无操作系统,直接执行)
qemu-system-riscv64 -machine virt -bios csr_demo.elf -nographic
验证方法与判定标准
- 预期输出(由代码逻辑直接推出):
mstatus:0x8000000000001800(MPP=3, MPIE=1, MIE=0)
mepc:0x80000000
mcause:0x3
PASS - 判定通过条件:串口输出包含上述四行,且最后一行是
PASS。若输出FAIL,说明CSR写入未生效或位操作有误。 - 特权级机制验证:
mstatus.MPP保存了发生异常前的特权级(此处设为M-Mode),mepc保存异常返回地址,mcause记录异常原因。当实际发生异常时,硬件会自动将当前状态写入这些寄存器,mret指令则用它们恢复现场——本案例通过手动配置模拟了这一保存过程。
常见问题
- 编译报错:确认工具链支持
rv64im架构;若用32位,改用-march=rv32im并调整寄存器宽度。 - QEMU无输出:确保
-bios参数指向ELF文件,且-nographic将串口重定向到终端。 - CSR写入无效:在M-Mode下所有CSR均可直接访问;若在S/U-Mode运行,需通过
csrrw陷入M-Mode处理。