3. 配置和读取CSR

配置和读取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

验证方法与判定标准

  1. 预期输出(由代码逻辑直接推出):
    mstatus:0x8000000000001800(MPP=3, MPIE=1, MIE=0)
    mepc:0x80000000
    mcause:0x3
    PASS
  2. 判定通过条件:串口输出包含上述四行,且最后一行是PASS。若输出FAIL,说明CSR写入未生效或位操作有误。
  3. 特权级机制验证: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处理。