全球最实用的IT互联网信息网站!

AI人工智能P2P分享&下载搜索网页发布信息网站地图

当前位置:诺佳网 > 电子/半导体 > 嵌入式技术 >

开发者分享 | 适用于HPM的RustSBI实现

时间:2025-02-08 13:44

人气:

作者:admin

标签: HPM  Linux  mcu 

导读:HPMicro的MCU一直以高性能著称,之前也一直有想在HPM的MCU上运行Linux的想法。直到看见Linux6.10中支持了RISC-V架构在S-mode中运行nommu内核*,才下定决心开始在HPM6360上折腾nommuLin...

HPMicro 的 MCU 一直以高性能著称,之前也一直有想在 HPM 的 MCU 上运行 Linux 的想法。直到看见 Linux 6.10 中支持了 RISC-V 架构在 S-mode 中运行 nommu 内核*,才下定决心开始在 HPM6360 上折腾 nommu Linux。

划线部分链接为:

RISC-V 上的 Linux 启动流程

ARM 架构中,通常 Linux 的启动流程为:

bf5789a8-e5df-11ef-9434-92fbcf53809c.png

而在具有 S 态的 RISC-V 架构中,通常的启动流程为:

bf84182e-e5df-11ef-9434-92fbcf53809c.jpg

其中 BootROM、Loader 和 SBI Runtime 运行 M-mode(机器模式)下,具体的引导程序和 Linux 等操作系统内核运行在 S-mode(监管者模式)下,而用户进程运行在 U-mode(用户模式)中。我们看到 RISC-V 的启动流程中相比 ARM 多了一个 SBI Runtime,那么什么是 SBI?

SBI (Supervisor Binary Interface)

RISC-V 架构中,存在着定义于操作系统之下的运行环境(Runtime)。这个运行环境不仅将引导启动 RISC-V 下的操作系统, 还将常驻后台,为操作系统提供一系列二进制接口,以便其获取和操作硬件信息。RISC-V 给出了此类环境和二进制接口的规范,称为“监管者二进制接口”,即 “SBI”。

SBI 有多种实现,如 Berkeley Boot Loader (BBL)、OpenSBI。而本次项目中使用的 SBI 实现为 RustSBI

RustSBI项目源于2020年清华操作系统夏令营,旨在使用 Rust 语言编写 RISC-V 指令集中的 SBI 实现,支撑上层系统软件比如操作系统的运行。在国际 SBI 实现列表中获得 编号四。具有以下功能:

· 多功能且可拓展的操作系统运行时

·为物理机、虚拟机、模拟器提供支持和兼容性

·支持 RISC-V SBI 规范 v2.0

·使用 Rust 编写,使用稳定版本的 Rust 工具链构建

由于 HPM 系列芯片的启动设备较为单一(XPI),且 XPI 的初始化工作已经在 BootROM 中完成并映射到地址空间中,同时为了加快启动速度,本项目最终没有移植 U-Boot;而是基于 RustSBI 库,编写操作系统的 Bootloader,实现 SDRAM 初始化、固件加载、设备树传递和内核跳转等功能,以及为操作系统提供监管者二进制接口(SBI)。

最终在 HPM6360 芯片上的启动流程如下:

bfc7b9d0-e5df-11ef-9434-92fbcf53809c.png

启动镜像布局

c000d170-e5df-11ef-9434-92fbcf53809c.png

部分实现细节

Zicntr 指令集拓展支持

Linux 内核中,使用了 TIME TIMEH 这两个 CSR 寄存器来获取系统时钟用于调度。但 HPM6360 使用的 Andes D45 核心并没有实现这两个 CSR 寄存器,在执行 csrrs rd, time, rs1 csrrs rd, timeh, rs1 指令时会产生非法指令异常(Illegal Instruction)。这就需要软件在异常处理函数中模拟这两条指令的执行。

static inline cycles_t get_cycles(void)
{
return csr_read(CSR_TIME);
}
#define get_cycles get_cycles

static inline u32 get_cycles_hi(void)
{
return csr_read(CSR_TIMEH);
}
#define get_cycles_hi get_cycles_hi

#endif /* !CONFIG_RISCV_M_MODE */

#ifdef CONFIG_64BIT
static inline u64 get_cycles64(void)
{
return get_cycles();
}
#else /* CONFIG_64BIT */
static inline u64 get_cycles64(void)
{
u32 hi, lo;

do {
hi = get_cycles_hi();
lo = get_cycles();
} while (hi != get_cycles_hi());

return ((u64)hi << 32) | lo;
}
#endif /* CONFIG_64BIT */

arch/riscv/include/asm/timex.h:51

c02fbbc0-e5df-11ef-9434-92fbcf53809c.png

这里我使用了 riscv-decode 这个库对机器码进行解码。

1、触发非法指令异常后从 mtval 寄存器中读取到非法指令;

2、将其作为参数调用 riscv_decode::decode() 函数进行解码,获取到 CSR 以及 rd 的值,判断是否为 TIME:0xc01 TIMEH:0xc81 这两个 CSR 寄存器;

3、如果是,则将 MCHTMR 外设中 MTIME 寄存器的值保存至 rd 寄存器中。并恢复现场并从异常指令的下一条指令开始执行;

4、如果不是,则将异常委托给 Linux 内核处理。模拟异常发生时的硬件行为,填充对应寄存器并更新 mstatus:MPP ,使异常返回后的特权级别为 S-mode,最后将 mepc 寄存器覆盖为 stvec 的值,异常返回后将从 Linux 内核的异常处理函数入口开始执行。具体实现细节请参考:

https://github.com/rustsbi/rustsbi-hpm/blob/194d9cc7899fef320ac9e4b8e2c57ffca3eafe34/src/trap.rs#L51-L67

请手动跳转

SDRAM 区域原子指令的支持

在调试过程中发现,HPM6360 无法在 SDRAM 的地址范围中执行原子指令,会产生存储/原子指令访问错误异常(Store/AMO Access Fault)。而 Linux 内核中有部分操作调用了原子指令(例如加锁等同步操作),在 CPU 不支持原子指令的情况下 Linux 内核无法运行。这里同样可以基于异常对原子指令进行模拟。

1、AMO 指令

在执行 AMO 指令时,会陷入存储/原子指令访问错误异常。同样的,我们需要从 mepc 指向的指令地址获取出错的原子指令,并进行模拟。

2、lr / sc

对于 lr (Load Reserved) 和 sc (Store Conditional) 指令的情况则有一些特殊。 lr 指令执行时会触发加载指令访问错误异常(Load Access Fault),但 sc 指令在执行时不会触发任何异常,rd 寄存器的结果直接返回 1 ,则就导致无法直接模拟该指令的执行。

这里我选择的解决办法是:在执行到 lr 指令时,先对 lr 指令进行模拟,然后查找后续内存地址中的 sc 指令,并将其替换为非法指令(实际使用的是 csrrw zero, time, zero)。这样在执行到原先 sc 指令的位置时就会触发非法指令异常。在异常处理函数中,判断是否是原先替换指令的地址,如果是,则还原被替换的 sc 指令,并模拟指令执行,随后异常退出至下一条指令继续执行;如果不是,则将异常委托给内核处理。

设备树

/dts-v1/;

/ {
#address-cells = <0x01>;
#size-cells = <0x01>;
compatible = "hpmicro,hpm6360";
model = "HPMicro HPM6360 Evaluate Kit";

aliases {
serial0 = &uart0;
};

chosen {
bootargs = "earlycon=sbi console=hvc0 ignore_loglevel rootwait root=/dev/mtdblock0";
stdout-path = "hvc0";
};

memory@40000000 {
device_type = "memory";
reg = <0x40000000 0x02000000>;
};

cpus {
#address-cells = <0x01>;
#size-cells = <0x00>;
timebase-frequency = <1000000>;

cpu@0 {
phandle = <0x01>;
device_type = "cpu";
reg = <0x00>;
status = "okay";
compatible = "riscv";
riscv,isa = "rv32imafdcp";
riscv,isa-base = "rv32i";
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicsr",
"zifencei", "zihpm";
mmu-type = "riscv,none";

interrupt-controller {
#interrupt-cells = <0x01>;
interrupt-controller;
compatible = "riscv,cpu-intc";
};
};
};

soc {
#address-cells = <0x01>;
#size-cells = <0x01>;
compatible = "simple-bus";
ranges;

rom@80400000 {
compatible = "mtd-rom";
reg = <0x80400000 0xC00000>;
bank-width = <1>;
};

uart0: uart0@f0040000 {
compatible = "hpmicro,hpm6360-uart";
reg = <0xf0040000 0x40>;
clock-frequency = <24000000>;
status = "okay";
};
};
};

实现效果

目前已经成功实现 Linux 6.10 内核启动引导,并传递设备树给内核。

c08b80ae-e5df-11ef-9434-92fbcf53809c.gif

coremark 跑分结果:

~ # coremark
2K performance run parameters for coremark.
CoreMark Size : 666
Total ticks : 13913
Total time (secs): 13.913000
Iterations/Sec : 1437.504492
Iterations : 20000
Compiler version : GCC13.3.0
Compiler flags : -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -O2 -g0 -fPIC -Wl,-elf2flt=-r -static -lrt
Memory location : Please put data memory location here
(e.g. code in flash, data on heap etc)
seedcrc : 0xe9f5
[0]crclist : 0xe714
[0]crcmatrix : 0x1fd7
[0]crcstate : 0x8e3a
[0]crcfinal : 0x382f
Correct operation validated. See readme.txt for run and reporting rules.
CoreMark 1.0 : 1437.504492 / GCC13.3.0 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -O2 -g0 -fPIC -Wl,-elf2flt=-r -static -lrt / Heap

ramspeed 测试结果,可以看出缓内的读取速度是非常快的,超出缓存范围后读取速度受限于 SDARM 的速度:

~ # ramspeed -b 2 -g 1 -m 1 -r
RAMspeed (GENERIC) v2.6.0 by Rhett M. Hollander and Paul V. Bolotoff, 2002-09

1Gb per pass mode

INTEGER & READING 1 Kb block: 1460.57 Mb/s
INTEGER & READING 2 Kb block: 1487.33 Mb/s
INTEGER & READING 4 Kb block: 1498.92 Mb/s
INTEGER & READING 8 Kb block: 1505.16 Mb/s
INTEGER & READING 16 Kb block: 1507.90 Mb/s
INTEGER & READING 32 Kb block: 1301.73 Mb/s
INTEGER & READING 64 Kb block: 116.71 Mb/s
INTEGER & READING 128 Kb block: 116.79 Mb/s
INTEGER & READING 256 Kb block: 116.81 Mb/s
INTEGER & READING 512 Kb block: 116.82 Mb/s
INTEGER & READING 1024 Kb block: 116.74 Mb/s

最后附上仓库地址,同时也提供了 pre-built 的系统镜像,欢迎各位开发者下载体验。

1、rustsbi-hpm:

https://github.com/rustsbi/rustsbi-hpm

2、linux:

https://github.com/hpm-rs/linux

3、buildroot:

https://github.com/hpm-rs/buildroot

HPMICRO

鸣谢

感谢华中科技大学洛佳同学 (https://github.com/luojia65)在本项目开发过程中提供的建议和支持!同时他也是 RustSBI 的作者。

感谢华中科技大学王振辰同学(https://github.com/Plucky923)完善了 riscv-decode 库对 RVA 指令集解码的支持!(https://github.com/fintelia/riscv-decode/pull/6)

参考

https://riscv.org/wp-content/uploads/2019/06/13.30-RISCV_OpenSBI_Deep_Dive_v5.pdf


https://github.com/rustsbi/rustsbi


https://github.com/fintelia/riscv-decode

以上内容来自先楫开发者的原创分享。

我们始终相信开发者共创的力量。先楫社区坚持开源共享、互惠互利,贴近每一个开发者,一步一个脚印,一点一滴积累,为成为更好的我们而不断努力。

心之所向,锐意进取,星辰大海,恣意成长。

温馨提示:以上内容整理于网络,仅供参考,如果对您有帮助,留下您的阅读感言吧!
相关阅读
本类排行
相关标签
本类推荐

CPU | 内存 | 硬盘 | 显卡 | 显示器 | 主板 | 电源 | 键鼠 | 网站地图

Copyright © 2025-2035 诺佳网 版权所有 备案号:赣ICP备2025066733号
本站资料均来源互联网收集整理,作品版权归作者所有,如果侵犯了您的版权,请跟我们联系。

关注微信