risc-v 实验简介 uboot + gem5-riscv

在 gem5 中只运行 U-Boot(不加载 Linux 内核)实际上是一个非常灵活且强大的裸机开发/调试平台。我们可以做很多有意思的事情,从简单的命令行操作到深入的固件级开发研究。

以下是几个不同层次的有趣工作:


第一层:U-Boot 命令行直接操作(无需改代码)

一旦 U-Boot 在 gem5 中启动并显示命令提示符 =>,我们可以直接与它交互:

1. 内存探索与测试

bash 复制代码
=> bdinfo              # 查看板级信息(内存布局、时钟等)
=> printenv            # 查看所有环境变量
=> md 0x80000000 64    # 查看物理内存内容
=> mw 0x80100000 0xDEADBEEF  # 向内存写入数据
=> mtest 0x80000000 0x81000000  # 内存压力测试
=> crc32 0x80000000 0x100000   # 计算 CRC 校验

2. 研究设备树 (DTB)

bash 复制代码
=> fdt addr 0x87E00000    # 告诉 U-Boot DTB 在哪里(gem5 生成的 device.dtb)
=> fdt print /cpus         # 查看 CPU 节点
=> fdt print /memory       # 查看内存节点
=> fdt print /soc          # 查看 SoC 设备

这能让我们直观理解 gem5 模拟的 HiFive Unmatched 板子的硬件拓扑。

3. 利用 U-Boot 作为调试跳板

U-Boot 可以加载并执行我们自己的裸机程序:

bash 复制代码
# 在 gem5 运行后,通过串口将裸机程序加载到内存
=> loadb 0x80200000       # 通过串口加载二进制文件到指定地址
# 然后用终端工具发送文件

=> go 0x80200000          # 跳转到该地址执行

第二层:修改 U-Boot 源码做开发

这是最实用的部分。我们可以直接在 U-Boot 源码中添加功能,然后重新编译在 gem5 中测试。

示例:添加一个自定义命令

编辑 u-boot/common/cmd_custom.c(新建文件):

c 复制代码
#include <common.h>
#include <command.h>

static int do_hello(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    printf("Hello from U-Boot on gem5 RISC-V!\n");
    
    // 读取 mhartid CSR 显示当前核心 ID
    unsigned long hartid;
    __asm__ volatile ("csrr %0, mhartid" : "=r"(hartid));
    printf("Current hart ID: %lu\n", hartid);
    
    // 读取 misa CSR 显示 ISA 信息
    unsigned long misa;
    __asm__ volatile ("csrr %0, misa" : "=r"(misa));
    printf("MISA: 0x%lx (RV64%s)\n", misa, (misa & (1L << 8)) ? "I" : "?");
    
    return CMD_RET_SUCCESS;
}

U_BOOT_CMD(
    hello, 1, 0, do_hello,
    "Print hello message and CPU info",
    ""
);

然后在 u-boot/common/Makefile 中加上:

make 复制代码
obj-y += cmd_custom.o

重新编译后在 gem5 中运行:

bash 复制代码
=> hello
Hello from U-Boot on gem5 RISC-V!
Current hart ID: 0
MISA: 0x8000000000101105 (RV64I)

示例:开发 RISC-V 汇编测试代码

在 U-Boot 中运行内联汇编来研究 RISC-V 特权架构:

c 复制代码
static int do_csrtest(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    unsigned long mstatus, mepc, mscratch, mtvec;
    
    __asm__ volatile ("csrr %0, mstatus" : "=r"(mstatus));
    __asm__ volatile ("csrr %0, mepc"    : "=r"(mepc));
    __asm__ volatile ("csrr %0, mscratch": "=r"(mscratch));
    __asm__ volatile ("csrr %0, mtvec"   : "=r"(mtvec));
    
    printf("mstatus = 0x%016lx\n", mstatus);
    printf("mepc    = 0x%016lx\n", mepc);
    printf("mscratch= 0x%016lx\n", mscratch);
    printf("mtvec   = 0x%016lx\n", mtvec);
    
    // 解析 mstatus 的 MPP 字段 (Machine Previous Privilege)
    int mpp = (mstatus >> 11) & 0x3;
    printf("MPP (previous privilege) = %d (%s)\n", mpp, 
           mpp == 3 ? "M-mode" : mpp == 1 ? "S-mode" : "U-mode");
    
    return CMD_RET_SUCCESS;
}
U_BOOT_CMD(csrtest, 1, 0, do_csrtest, "Read RISC-V CSRs", "");

第三层:结合 gem5 的强大功能做研究

这才是 gem5 + U-Boot 组合的真正威力所在:

1. 指令级跟踪分析

bash 复制代码
./build/RISCV/gem5.opt --debug-flags=Exec,RiscvMisc \
    --debug-start=0 --debug-file=trace.txt \
    run_uboot_riscv.py --kernel=./u-boot/u-boot

# 查看执行轨迹
head -100 m5out/trace.txt

我们会看到每一条 RISC-V 指令的执行细节(PC、指令编码、寄存器变化)。

2. 启动性能分析

bash 复制代码
# 加上统计输出
./build/RISCV/gem5.opt --stats-file=stats.txt \
    run_uboot_riscv.py --kernel=./u-boot/u-boot

cat m5out/stats.txt | head -50

可以看到:

  • 模拟的指令总数
  • 各类指令的比例
  • 缓存命中/未命中统计
  • 分支预测准确率
  • 每条指令的平均 CPI

3. 研究 U-Boot → Linux 的交接过程

如果我们让 U-Boot 加载 Linux 内核,可以完整观察:

  • U-Boot 如何解压内核镜像
  • 如何设置设备树(FDT)传递给内核
  • mstatus.MPP 如何从 M-mode 切换到 S-mode
  • sret 指令的执行瞬间

4. 内存一致性研究

利用 gem5 的缓存模型,研究 U-Boot 中的 DMA 操作、缓存刷新指令的效果。


第四层:完整实战场景

场景 A:裸机 RISC-V 教学实验

编写一个极简的 "Hello World" 裸机程序,通过 U-Boot 加载执行:

bash 复制代码
# 编写 baremetal.S
.section .text
.globl _start
_start:
    li t0, 0x10000000   # UART 基地址 (gem5 HiFive 的 UART0)
    la t1, msg
loop:
    lb t2, 0(t1)
    beqz t2, done
    sb t2, 0(t0)        # 写入 UART
    addi t1, t1, 1
    j loop
done:
    j done              # 死循环

.section .rodata
msg:
    .string "Hello baremetal RISC-V!\n"

编译成裸机二进制,通过 U-Boot 的 go 命令执行------完全不依赖操作系统

场景 B:安全启动研究

研究 U-Boot 的:

  • SPL (Secondary Program Loader) 阶段
  • FIT (Flattened Image Tree) 镜像验证
  • Secure Boot 签名链验证
  • PMP (Physical Memory Protection) 配置

场景 C:网络协议栈极简测试

U-Boot 内置了极简的 TCP/IP 栈(如果编译时启用了网络支持),可以在 gem5 中测试:

bash 复制代码
=> setenv ipaddr 10.0.0.2
=> setenv serverip 10.0.0.1
=> ping ${serverip}
=> tftpboot 0x80200000 baremetal.bin

推荐起步路线

如果这是第一次尝试,建议按这个顺序:

步骤 操作 收获
1 运行 U-Boot,用 bdinfoprintenvmd 探索 熟悉裸机环境
2 fdt print 查看设备树 理解 gem5 模拟的硬件
3 添加一个 hello 命令,打印 CSR 信息 学会修改/编译/测试循环
4 写一个简单的裸机汇编程序,用 go 执行 理解 RISC-V 裸机编程
5 --debug-flags=Exec 跟踪指令执行 深入理解硬件行为
相关推荐
Eloudy3 小时前
gem5 运行 risc-v 64bit 的 U-Boot
risc-v
木由里予2 天前
RISC-V 32 位基础指令集(RV32I)完整参考手册
risc-v
一枝小雨3 天前
RISC-V架构sp寄存器 & RISC-V架构下FreeRTOS任务上下文保存与恢复
单片机·架构·嵌入式·risc-v·rtos·内核原理
一枝小雨3 天前
RISC-V架构的中断与异常处理机制学习笔记
单片机·架构·嵌入式·risc-v·内核原理·中断与异常
一枝小雨3 天前
什么是标准C函数:以RISC-V架构下的C函数为例
c语言·risc-v·内核原理
咕咚.萌西4 天前
RISCV AS汇编器
risc-v
嵌入式小企鹅5 天前
UiPath推出AI编程“总指挥台”,SiFive发布RISC-V第三代猛兽
人工智能·学习·google·程序员·ai编程·risc-v·开源工具
阿祖_in_coding7 天前
RISC-V ACT测试
risc-v
yusur7 天前
开芯院院长唐丹一行来访中科驭数 共探RISC-V与DPU算力协同创新之路
risc-v