__ _ __ _ __
/ / (_) /____ | |/_/
/ /__/ / __/ -_)> <
/____/_/\__/\__/_/|_|
Build your hardware, easily!
(c) Copyright 2012-2023 Enjoy-Digital
(c) Copyright 2007-2015 M-Labs
BIOS built on Sep 3 2023 19:17:53
BIOS CRC passed (4681dd6a)
--=============== SoC ==================--
CPU: PicoRV32 @ 24MHz
BUS: WISHBONE 32-bit @ 4GiB
CSR: 32-bit data
ROM: 32.0KiB
SRAM: 64.0KiB
FLASH: 8.0MiB
--============= Console ================--
litex>
\ | /
- RT - Thread Operating System
/ | \ 3.1.3 build Sep 3 2023
2006 - 2019 Copyright by rt-thread team
Hello picorv32 World
在用了各种 MCU 之后,一直都想用 FPGA 自己造一个 SoC,再给它移植操作系统。
两年前,我在 Lichee Tang (EG4S20) 国产 FPGA 上移植了 Picorv32 和 蜂鸟 HBird E203,这两个都是 RISC-V 的软核,并且移植了 RT-Thread Nano 实时系统,但是却一直不是很满意。
因为这两个 SoC 都不是很灵活,虽然实现了基本的功能 (RISC-V + UART + GPIO + Timer),但是如果想继续扩展其他模块 (Eth, SDCard, DRAM),直到希望能运行 Linux,就不是很方便了。
半年前,我接触到了 LiteX,可以用 Python 在不同型号的 FPGA 上配置一个灵活的 SoC,搭配各种 CPU (VexRISCV, PicoRV32, NEORV32, LM32) 和 外设,甚至支持 Zephyr 和 Linux。
于是我就用 LiteX 在 ICESugar 开发板上,定制了一个 VexRiscv (RV32I) 的 SoC,还顺便用 Rust 跑了个定时器点灯的小程序。
但是,我却没有进一步移植 RT-Thread,趁着最近休假前,感觉可以再更进一步。
我打算继续使用 RISC-V,不过在移植 RTOS 前得选定 RISC-V 的扩展指令集 (IMAC)。
于是我查了一下 Zephyr RTOS 支持的 RISC-V CPU,有 VexRiscv (RV32IM) [1] 和 NEOV32 (RV32IMC) [2]。因此,为了移植 RT-Thread Nano,至少得用个 RV32IM 的 RISC-V CPU。
这里顺便一提,之前使用 Rust 的时候,虽然我用 rustc 看到了很多 riscv32 的支持列表,但是这只是编译器支持列表,要想运行程序还需要 riscv-rt 等最小环境。最后,我只亲自测试并确认了 Rust 支持 RV32I (VexRiscv) 和 RV32IMAC (GD32VF103),并顺利运行了固件。
$ rustc --print target-list | grep riscv32
riscv32gc-unknown-linux-gnu
riscv32gc-unknown-linux-musl
riscv32i-unknown-none-elf
riscv32im-unknown-none-elf
riscv32imac-esp-espidf
riscv32imac-unknown-none-elf
riscv32imac-unknown-xous-elf
riscv32imc-esp-espidf
riscv32imc-unknown-none-elf
这次我打算使用 Picorv32 (RV32IM),虽然我尝试过了,但是很遗憾,还没能在 RV32IM 的 CPU 上跑起来 Rust。
在选定了 Picorv32 (RV32IM) 之后,还需要找到 LiteX 的对应配置。因为 LiteX 非常灵活,针对同一款 CPU,我们也可以配置支持不同的指令集扩展。
比如在 picorv32 的 core.py 里面,我们可以看到 LiteX 支持 minimal (RV32I) 和 standard (RV32IM) 两种配置。
https://github.com/enjoy-digital/litex/blob/master/litex/soc/cores/cpu/picorv32/core.py
CPU_VARIANTS = ["minimal", "standard"]
# GCC Flags ----------------------------------------------------------------------------------------
GCC_FLAGS = {
# /------------ Base ISA
# | /------- Hardware Multiply + Divide
# | |/----- Atomics
# | ||/---- Compressed ISA
# | |||/--- Single-Precision Floating-Point
# | ||||/-- Double-Precision Floating-Point
# i macfd
"minimal": "-march=rv32i2p0 -mabi=ilp32 ",
"standard": "-march=rv32i2p0_m -mabi=ilp32 ",
}
而 VexRiscv 则支持更多的配置,从 RV32I,RV32IM,RV32IMA,到 RV32IMAC 应有尽有。不过很遗憾,我用的 ICESugar FPGA 开发板资源有限,塞不下 VexRiscv 的 RV32IM 配置,所以我最后选择了 picorv32 standard 。
GCC_FLAGS = {
# /---------- Base ISA
# | /----- Hardware Multiply + Divide
# | |/---- Atomics
# | ||/--- Compressed ISA
# | |||/-- Single-Precision Floating-Point
# | ||||/- Double-Precision Floating-Point
# i macfd
"minimal": "-march=rv32i2p0 -mabi=ilp32",
"minimal+debug": "-march=rv32i2p0 -mabi=ilp32",
"minimal+debug+hwbp": "-march=rv32i2p0 -mabi=ilp32",
"lite": "-march=rv32i2p0_m -mabi=ilp32",
"lite+debug": "-march=rv32i2p0_m -mabi=ilp32",
"lite+debug+hwbp": "-march=rv32i2p0_m -mabi=ilp32",
"standard": "-march=rv32i2p0_m -mabi=ilp32",
"standard+debug": "-march=rv32i2p0_m -mabi=ilp32",
"imac": "-march=rv32i2p0_mac -mabi=ilp32",
"imac+debug": "-march=rv32i2p0_mac -mabi=ilp32",
"full": "-march=rv32i2p0_m -mabi=ilp32",
"full+cfu": "-march=rv32i2p0_m -mabi=ilp32",
"full+debug": "-march=rv32i2p0_m -mabi=ilp32",
"full+cfu+debug": "-march=rv32i2p0_m -mabi=ilp32",
"linux": "-march=rv32i2p0_ma -mabi=ilp32",
"linux+debug": "-march=rv32i2p0_ma -mabi=ilp32",
"linux+no-dsp": "-march=rv32i2p0_ma -mabi=ilp32",
"secure": "-march=rv32i2p0_ma -mabi=ilp32",
"secure+debug": "-march=rv32i2p0_ma -mabi=ilp32",
}
关于 LiteX 的介绍,可以参照我之前的一篇文章,这里就不重复介绍了。
之前提过,LiteX 生成 SoC 后,还会生成对应的 芯片文档 和 CSR 寄存器头函数,以及最基本的 C Runtime。所以我们可以对照生成的 CSR 寄存器布局,来完成移植工作。
比如下面的串口输出,CSR_UART_BASE 是 LiteX 自动生成的,它的定义可以在 csr.h 头文件里面找到。
#include <generated/csr.h>
#define reg_uart_data (*(volatile rt_uint32_t*)CSR_UART_BASE)
void rt_hw_console_output(const char *str)
{
int i=0;
for(i=0;'\0' != str[i];i++)
{
if(str[i] == '\n')
{
reg_uart_data = '\r';
}
reg_uart_data = str[i];
}
}
char rt_hw_console_getchar(void)
{
return (char)((*(volatile int*)CSR_UART_BASE)&0xFF);
}
当然,根据芯片的 Memory Layout,我们也需要修改链接脚本。我定制的 SoC 使用了 64KM 的 SRAM 和 32KB 的 Flash。
MEMORY {
sram : ORIGIN = 0x00000000, LENGTH = 0x00010000
rom : ORIGIN = 0x00840000, LENGTH = 0x00008000
csr : ORIGIN = 0x82000000, LENGTH = 0x00010000
}
然而,实不相瞒,在 RT-Thread 上的移植,还没有完全完成。
现在的状况是 CPU 能正常初始化,开机打印 RTT 的 Logo,并且可以进入 main 函数打印 Hello World,但是卡死在 了 MSH 控制台。
因为我只移植了串口和 Context Switch,可能是定时器和中断可能还有些问题,只能等以后有时间再更新了。
这里留白给未来的我更新 。。。。。。
项目源码:
https://github.com/wuhanstudio/litex-soc-icesugar-rust
当前进度:
VexRiscv (RV32I),支持 Rust,不支持 RT-Thread
项目源码:
https://github.com/wuhanstudio/litex-picorv32-rtthread
当前进度:
Picorv32 (RV32IM),不支持 Rust,支持 RT-Thread
总体对 LiteX 的体验还是非常不错的。
现在定制的 SoC,运行了基本的 C 程序,跑起来了 Rust,距离 RT-Thread 的支持也只差最后一步了。未来还可以考虑挂上硬件加速器,例如 TRIVIUM 和 PRESENT,矩阵运算加速等。
虽然这些 Side Project 跟自己的科研 (CARLA Autonomous Driving & Adversarial Attack) 关系不大,但是现在自己对深度学习模型的训练、部署,以及到操作系统的移植,和 CPU 指令集的硬件加速,都有了比较清晰的认知。在科研发文章之余,偶尔兴趣使然地给开发板清清灰,感觉自己对科研的热情和好奇心又满上了。