塞昉星光2

在昉·星光 2的hart0小核上运行embassy
tags Q TODO

编译rustsbi+uboot

ref https://github.com/rustsbi/rustsbi/blob/main/prototyper/docs/booting-in-visionfive2-using-uboot-and-rustsbi.md#%E7%83%A7%E5%BD%95-payload-%E9%95%9C%E5%83%8F
steps
mkdir workshop
cd workshop
git clone https://github.com/starfive-tech/VisionFive2.git
cd VisionFive2
git checkout JH7110_VisionFive2_devel
git submodule update --init --recursive
这一步会卡住,因为里面有linux内核嗯很大
建议看.gitmodules分别手动clone然后checkout到对应分支
其中soft_3rdpart clone还会2次失败
因为一个60MB的img-gpu-powervr-bin-1.1.5...无法downloading
但目前没用到gpu,没遇到什么问题
cd workshop/VisionFive2
git clone https://github.com/rustsbi/rustsbi 需要switch到v0.0.0-oscomp-2020的tag上
cd workshop/VisionFive2
添加workshop/VisionFive2/payload_image.its
/dts-v1/;
/ {
description = "U-boot-spl FIT image for JH7110 VisionFive2";
#address-cells = <2>;
images {
firmware {
description = "u-boot";
data = /incbin/("./rustsbi/target/riscv64imac-unknown-none-elf/release/rustsbi-prototyper-payload.bin");
type = "firmware";
arch = "riscv";
os = "u-boot";
load = <0x0 0x40000000>;
entry = <0x0 0x40000000>;
compression = "none";
};
};
configurations {
default = "config-1";
config-1 {
description = "U-boot-spl FIT config for JH7110 VisionFive2";
firmware = "firmware";
};
};
};
实际编译sdk,包含很多内容,linux内核,buildroot,非常耗时
bash make -j$(nproc)
nushell make -j (sys cpu | get name | length)
然鹅
可以直接make uboot
因为只用到了uboot
开始报错
/usr/bin/file么有
我的nix的锅
flake.nix添加file
将对应的文件里的/usr/bin/file替换成了file
没有bc 在flake.nix里添加bc
继续报错 nix路线舍弃
改用docker环境
dockerfile见仓库
一个bug
https://github.com/starfive-tech/VisionFive2/issues/111
没有后文,但对我没有影响
实际编译rustsbi
cd workshop/VisionFive2/rustsbi
cargo prototyper --payload ../work/u-boot/u-boot.bin --fdt ../work/u-boot/arch/riscv/dts/starfive_visionfive2.dtb
这部分nix下可以搞定
实际生成img,用于烧录
cd workshop/VisionFive2
apt install u-boot-tools
mkimage -f payload_image.its -A riscv -O u-boot -T firmware visionfive2_fw_payload.img
这部分在docker内部编译

硬件管脚图

uart和gpio都用得到

烧录

鉴于直接修改的rustsbi经常就因为实现错误卡住没uboot什么事
只能串口烧录
烧录要用到的管脚
直接参考 https://doc.rvspace.org/VisionFive2/SDK_Quick_Start_Guide/VisionFive2_SDK_QSG/recovering_bootloader%20-%20vf2.html
传输软件
windows下使用tera
https://github.com/TeraTermProject/teraterm/releases/tag/v5.4.0
因为支持XMODEM传输
需要设置波特率为 115200
然后每次选择 文件->传输->XMODEM->发送
拨码
关键步骤
插好对应的串口管脚
买一个uart转usb的串口转换小设备即可
下电
先拨拨码,进入uart模式
俩码都拨到上图对面 拨码
上电
看到CCCC是正确的
不然就是波特率设置错了
烧录官方提供的
jh7110-recovery-<Version>.bin
地址 https://github.com/starfive-tech/Tools/tree/master/recovery
选2
烧录刚才编译出的 visionfive2_fw_payload.img
下电
拨码,回到flash模式
上图就是 拨码
上电后应该有打印

参考文档

JH7110_Datasheet 硬件特性,用处没有那么大
JH7110_TRM_StarFive_Preliminary_V2 一些硬件的编程手册,稍微有点用处
https://doc-en.rvspace.org/JH7110/TRM/JH7110_TRM/sys_crg.html?hl=stg%2Capb 在线版本的TRM手册,用处见后面
sifive-interrupt-cookbook-v1p2.pdf
中断书,有CLINT的细节
Both the CLINT and CLIC integrate registers mtime and mtimecmp to configure timer interrupts,and msip to trigger software interrupts. Additionally, both the CLINT and the CLIC run at the core clock frequency.
提到了CLINT跑在core clock frequency
没有更多了
https://five-embeddev.com/baremetal/timer/
嵌入式timer讲解的blog
https://github.com/riscvarchive/riscv-aclint/blob/main/riscv-aclint.adoc
riscv-aclint的规范
https://docs.u-boot.org/en/latest/board/starfive/visionfive2.html 最有用的是u-boot的已有驱动和dts

阶段1

在rustsbi的
boot hart 就是 hart 1
启动embassy
Q 如何确定启动hart
A
代码内打印当前hart
dts内有boot核的设置
u-boot/arch/riscv/dts/starfive_visionfive2.dts
boot-hart-id = <1>
Q 最小需要实现什么
A embassy的timer抽象层
参考
embassy-rp/src/time_driver.rs
这个实现就比较基本,方便参考
esp-hal-embassy/src/time_driver.rs
这个实现更加复杂
实现
ipi已经实现了基本的timer功能,可以直接使用 rustsbi/ipi
从platform可以获得ipi
主要需要实现embassy_time_driver::Driver
其中的 now
schedule_wake
还需要注册一个time handler
用于在每次触发硬件timer中断时
填充embassy-timer使用的queue
实现在prototyper/prototyper/src/sbi/trap/mod.rs
Trap::Interrupt(Interrupt::MachineTimer
Q FREQ的转换
A
embassy-time的feature定义embassy的ticks
从ipi读出的timer是ticks
但embassy需要的now()返回的是us
所以需要根据几个ticks做转换
embassy默认设置的ticks在TICK_HZ里
实际的FREQ_RATIO = CLINT_FREQ_HZ / TICK_HZ
然后根据读出的clint ticks * FREQ_RATIO就能得到us
Q CLINT的FREQ是多少
A
没有找到具体的描述
https://www.kernel.org/doc/Documentation/devicetree/bindings/timer/sifive%2Cclint.yaml
提到timebase-frequency设置(在dts)
使用的dts是starfive_visionfive2没有这个设置 u-boot/arch/riscv/dts/starfive_visionfive2.dts
但隔壁的dtsi里出现了配置是4m u-boot/arch/riscv/dts/jh7110-u-boot.dtsi
延用,目前看起来可行
Q CriticalSection需要实现
A
在直接使用embassy-executor的riscv+thread的feature的时候,会用到
实现就是直接开关中断,比较直接
Q
目前timer实现使用Mutex来包装Queue
而rp的实现里用了critical_section来包装
没有细想,critical_section主要在开关中断
但基本各种实现周围都加上了开关中断的逻辑 或许可以直接包一层critical_section吧

阶段2

实现gpio驱动
理由
鉴于在启动第二个核的时候,
第一个核进入uboot可能会接管uart 这件事现在有不同的现象
hart2启动embassy,hart1进入u-boot,只能看到hart1的打印
hart0启动embassy,hart1进入u-boot,这俩的打印都能看见 TODO
使用gpio来点个灯来确定embassy在正常运行
参考
https://doc.rvspace.org/VisionFive2/40_Pin_GPIO_Header_UG/Shared/c_legal_notice_OP.html
gpio相关文档
至少给我提供了管脚图=_=
其他没啥用,都是linux下使用`^
https://doc-en.rvspace.org/JH7110/TRM/JH7110_TRM/sys_crg.html?hl=stg%2Capb 在线版本的TRM手册
实现
上面的参考查询困难
没有找到GPIO_DOEN的相关数据
只有个代码参考在TRM里
但在u-boot内有驱动
有寄存器的准确定义
gpio驱动实现 drivers/gpio/starfive-gpio.c
gpio的相关关键信息定义 u-boot/arch/riscv/include/asm/arch-jh7110/gpio.h
做了rust的最简单实现
没有实现embassy里的异步流程

阶段3

在hart2上运行embassy
hart1上运行u-boot
一开始
使用hsm进行同步
预计hart2等待hart1 remote_hsm后开启embassy
后来
发现没有必要,在PLATFORM.ready()就做了某种程度上的同步
因而直接在hart2上启动embassy没有遇到更多问题
比实现gpio还要简单一些
TODO
hart2上跑的uart打印都看不见
只能看到uboot的打印
hart2上的embassy运行的task的呼吸灯正常运行
但hart0的行为不一样,或许需要解释一下

阶段4

在hart0小核上运行embassy
hart1上运行u-boot
Q 直接将current_hartid() 从2改到0发生了什么
A
程序卡在某个位置,后续没有任何事情发生
没有panic
Q 如何在embassy里使用log的日志功能
A
开启embassy的log feature log
由于log是项目中只允许一个全局的log::Log实现
因而embassy也会使用当前rustsbi的log::Log实现
在串口进行打印
Q 定位过程
A
通过加打印首先
卡在了spawn操作,spawn没有返回
spawn的task也会执行一次
没有task:run()里的打印,因而task也没有实际进入
executor.run(|spawner| {
info!("in executor run");
match spawner.spawn(run()) {
Ok(_) => info!("spawn ok"),
Err(e) => info!("spawn one err{}", e),
}
})
清空了spawn里的操作,没有变化
与spawn无关
是run先初始化返回Token的过程有问题
run这个task本身使用宏#[embassy_executor::task]标记
cargo expand展开宏
其中run会被pool_get._spawn_async_fn调用
经过一串打印_spawn_async_fn->spawn_impl->AvailableTask::claim->task.raw.state.spawn->compare_exchange
最后卡死在这里
Q 为什么会卡死在compare_exechange
A
异构参考
https://cloud.tencent.com/developer/article/2386842
这里是个更复杂的异构 arm+riscv
甚至不共享内存
里面主要提到的是通信
AMP+RPMsg
linux和uboot都有相关实现
rustsbi没到那个层级没有这方面支持
0阶段引导
零阶段引导程序.pdf
RustSBI_零阶段引导程序.pdf
https://github.com/rustsbi/bouffaloader?tab=readme-ov-file
https://github.com/YuzukiHD/SyterKit
riscv参考
https://five-embeddev.com/riscv-priv-isa-manual/Priv-v1.12/machine.html
一些riscv必要寄存器的讲解
https://kaifu6821.gitbook.io/os-on-hifive-unmatched/boot-s7#ying-jian-bei-jing
提到了S7
fdt print /cpus;fdt list /cpus:查看设备信息,如图;可以看到cpu相关信息,大核和小核的架构不同,
小核不支持页表,不支持浮点运算,且没有d-cache
由于S74核只支持IMAC指令集,因此本项目中采用-march=rv64imac -mabi=lp64 -mcmodel=medany的组合
I(Integer):支持整数基本指令集
M(Mul/Div):支持整数乘除法
A(Atomic):说明理论上支持原子指令
C(Compressed):支持压缩指令
关于A扩展
Atomic::load/store 实际上生成的是 普通的 lw/sw 指令(不是 lr.w / sc.w),
所以不依赖于 A 扩展,
在所有基本支持 I 指令集的核上都能正常运行。
compare_exchange / fetch_add / swap 等 需要“读-改-写”语义
会使用lr.w/sc.w
需要 A 扩展了。
AtomicPtr 只有在执行涉及“读-改-写”语义的操作时才会依赖 A 扩展(也就是 LR/SC 指令)。
方法
load(Ordering) ❌ 否
store(val, Ordering) ❌ 否
swap(val, Ordering) ✅ 是(使用 lr/sc)
compare_exchange(...) ✅ 是(使用 lr/sc)
fetch_update(...) ✅ 是
打印misa
misa
https://five-embeddev.com/riscv-priv-isa-manual/Priv-v1.12/machine.html
3.1.1 Machine ISA Register misa
hart 1 misa
0x80...094112f
hart 0 misa
0x80...0901107
相对上面,没了
单精度,双精度浮点
没了S态
因而
理论上hart 0支持A扩展
支持IMAC
I(Integer):支持整数基本指令集
M(Mul/Div):支持整数乘除法
A(Atomic):说明理论上支持原子指令
C(Compressed):支持压缩指令
不支持的是
单精度浮点
双精度浮点
S态
实际上在time_driver使用了Atomic64 load/store可用
经过asm直接验证
使用lw/sw不会卡死
使用lr.w和sc.w确实会卡死
认为A扩展本身可能有问题
Q embassy要如何修改
A
先实现了一个state的没有Atomic的版本
发现没有必要
embassy考虑了没有各种Atomic的情况,因而有多个版本的
state
run_queue
但是根据target_has_atomic配置
不想修改编译的情况下
直接添加了一个feature用于强行使用state和run_queue的非atomic的版本
使用的是critical_section的版本
其中raw::executor也使用了AtomicPtr
但只使用了load,store所以道理上不用修改
也实际上没有使用target_has_atomic做区分

阶段5

编译极简bin-只点个灯
然后像u-boot一样让rustsbi在0核启动
Q 首先一个极简的bin文件
A
只搬了gpio驱动过来
并只点亮了灯,保证rustsbi真的有跳转过来
Q 需要设计linker.ld文件
A
直接参考的rustsbi的,不过稍微复杂了点
所以又参考了rcore的应用那边的ld文件 只做了最简单的配置
地址参考了u-boot的位置 位于0x80200000
因而将embassy_app放在了 0x80400000
Q 需要让rustsbi加载新的bin
A
首先修改了xtask,让它可以接受新的bin
然后参考了u-boot的方法,在rustsbi/prototype/prototype/build.rs里添加了一个.text
然后没成功,进入单板直接死掉了,访问了非法的地址
经过各路AI瞎猜,最后感觉那个说u-boot-spl比较定制,处理不了这种链接的可能性较大
因而改成了在rustsbi/prototyper/prototyper/src/firmware/payload.rs
内,直接添加了include_bytes的硬编码,以及加载地址0x80400000的硬编码 很临时了
xtask的修改已经没有了意义
并在main.rs那边
使用core::ptr::copy_nonoverlapping来将bin copy到对应的地址
Q 然后需要让rustsbi在hart 0执行跳转
A
首先,再次,通过参考u-boot的跳转,使用了local_remote_hsm().start执行了地址跳转
卡了很久
始终没有成功
后来只能看了下hsm发现local_remote_hsm本身也有很多compare_exchange
再加上main的后续,主要针对要跳转的是S态做了很多工作
最后使用了更朴素的汇编来执行简单跳转
csrw mepc, a0 来将跳转地址设置到mepc
然后传递a1和a2 尽管没有使用
最后mret跳转
然后可以了,看到了亮灯

阶段6

在新的bin内添加embassy逻辑
现在叫embassy_app
Q
time_driver需要重新实现
A
之前使用的rustsbi的ipi相关的功能
现在需要直接使用底层的sifive的一个包来做类似的操作
需要重新传入相关clint的地址
查看dts是0x20000000
本想使用embassy提供的StaticCell包装,但只能产生一个可以move的&static mut
不方便在time_driver的DRIVER里直接使用
最后直接用的static mut
Q 串口驱动需要重新实现
A
直接把rustsbi的相关串口驱动,更底层一些的uart,忽略了上层的抽象
搬了过来
Q
log需要重新实现(失败了) TODO
A
由于不支持compare_exchange只能使用log::set_logger_racy(&Logger)
虽然这一条初始化成功了
但后续使用info!还是会卡住
info的宏展开没有发现什么其他的原子读改写类似操作,因而先放弃了
直接使用println!姑且也可以调试
println直接使用的串口驱动实现
Q
第一轮卡在了embassy的StaticCell的init操作
这里有AtomicBool的compare_exchange
怀疑是这里的问题
在rustsbi里直接运行embassy的时候使用了相同的StaticCell的init
也就是这很可能不是问题,也就是支持AtomicBool的compare_exchange
在下一个Q这个问题解决了
A 先只简单的改成了不用compare_exchange的代码后跳过这个问题
Q
第二轮卡在了spawn
返回的是SpawnError::Busy,说我的Poolsize大小不够,已经耗尽
重新展开宏
POOL是个静态变量,默认size是1
经过各种AI的瞎猜,最后认为是bss段有问题
于是将POOL强行放进了data段,成功运行了embassy
但POOL在,rustsbi直接运行embassy的时候也是放在bss段的,也就是全0静态
反编译了查看了一下
因而重新检查了linker.ld
发现写的有问题,*.bss段没有正常收集(大意没写,大概这样子)
旧的
.bss (NOLOAD) : ALIGN(0x1000) {
start_bss = .;
*(.bss.stack)
*(.sbss .sbss.*)
end_bss = .;
}
新的
.bss (NOLOAD) : ALIGN(0x1000) {
*(.bss.stack)
start_bss = .;
*(.bss .bss.*)
*(.sbss .sbss.*)
end_bss = .;
}
也就是一个bss.stack没必要放进需要清空的start_bss-end_bss
其次*(.bss .bss.*)不能省略,不然反编译后,.bss段分成了多个部分嗯
暂不太清楚怎么理解,反正不太正常,有多个section .bss TODO
经过修改后
StaticCell的问题也消失了,可以正常使用AtomicBool的compare_exchange
也不需要将POOL强行放入data段了

阶段7

修改编译条件
在不用给embassy添加新feature的条件下
可以直接使用embassy
即,同时满足not(target_has_atomic = "ptr")
ptr是当前平台的指针宽度,这里等同于64
和not(target_has_atomic = "8")
Q 首先,尝试使用riscv64imc来编译,也就是去掉a,不过riscv64没有imc只有riscv32有
A
尝试了riscv32imc,s7核是64bit的问题较多放弃
Q 尝试用riscv64imac生成json然后修改来变成不支持a的情况
A
首先,riscv64imac生成json rustc -Z unstable-options --print target-spec-json --target riscv64imac-unknown-none-elf | save riscv64imc.json
修改后的json见代码 就是去掉了a
然后这个东西现在需要重新编译core
修改了.cargo/config.toml
使用的是build-std和build-std-features
然后build target是riscv64imc.json
使用cargo build -Z build-std --release
出现了很多问题,因为现在连load/save操作也不支持了
embassy出现了大量的编译问题
首先spin_lock不行 修改成creatical_section
然后StaticCell不行 改成static mut
然后raw_waker不行
然后thread::executor不行
然后raw::SyncExecutor不行
修改量太大,没招了
Q
经过zjp同学和张同学提示,有软件模拟不支持的原子操作库
最后使用zjp同学给出的,还在活跃更新的crate https://github.com/taiki-e/portable-atomic
直接使用失败,经过了几个阶段,在下面的issues有记录
https://github.com/taiki-e/portable-atomic/issues/148
A
大概步骤
首先riscv64 json依然是-a处理,只有-a,embassy才能认定target没有atomic操作,但直接添加这个portable-atomic库会报错 错误就是上面issues里的错误
根据原作者提示,添加atomic-cas:false 后继续报错atomic_8没有对应的symbol
后重新生成了riscb32imc的json,发现了一个forced-atomics的feature
补充后成功编译
并且已经不需要依赖仓库了...
因为加上了-a条件,也关闭了cas
zjp同学补充的rust官方的issue
我发现了 Rust 官方里的 Issue: How should we expose atomic load/store on targets that don't support full atomics
* https://github.com/rust-lang/rust/issues/99668#issuecomment-1508757127
也就是如果要使用这个portable-atomic库
可能要使用作者提到的另一个思路
也见issue里提到的,但我没有成功执行

总结

由于上面已经对各阶段进行了总结因而在重新压缩后的几个节点变得更加简短
学习和分析了embassy+ariel-os输出了初版文档,但不太容易看
后续还要继续整理
初期老师给设定任务是在ariel-os上重现前面同学实现的embassy-preempt
但最后确定embassy-preempt不需要考虑相同级别的任务
因为ucosII没有相同级别的任务
因而跟ariel-os/embassy配合的设计思路有比较大的差异则任务被放弃
随后老师给设定的任务是在塞昉星光2上启动embassy,单独区分了几个阶段
1 启动单板并成功编译uboot+rustsbi并用串口烧录
2 在启动核上即hart 1上启动embassy
需要实现embassy的time_driver
需要实现critical_section,在里面开关中断
3
在非启动核/非hart0上即hart 2启动embassy
同时hart1上启动uboot,两者没有影响
为了更方便观察,因而实现了简单gpio驱动
之后直接在hart2上启动embassy没有太多困难见上面的阶段2-3
4
在hart0,特殊s7核上启动embassy
同时在hart1上启动uboot,两者没有影响
遇到比较多困难见阶段4
主要问题在于s7的文档上标注支持imac 带a扩展
但实际上对cas类的原子指令并不支持,会直接卡死
5
以上的embassy逻辑都直接跑在rustsbi内部
到这里embassy需要单独编译成bin文件由rustsbi进行加载
就跟加载uboot一样
遇到比较多困难见阶段5-6
这个阶段调试困难,因为出现错误基本什么都不会发生
ld文件编写,犯了简单错误
参考uboot的bin放在ld文件中失败 在更早的uboot-spl阶段报错访问非法地址
后续使用rustsbi代码内include_bytes 在跳转前copy到指定地址
从0核跳转到embassy_app的bin不能与uboot的逻辑一样
一个是hsm里面也有cas操作同样无法在hart 0里使用
另外跳转uboot是从M态到S态,embassy_app是从M态到M态
其实需要的步骤更简单
单独使用embassy的时候需要重新实现
time_driver,现在没有rustsbi的timer支持,直接读写starfive的crate
gpio驱动直接搬过来
uart驱动直接搬过来并在此基础上实现println!
6
以上的embassy为了规避cas问题,手动添加了feature来跳过相关的逻辑
这个阶段要求embassy不做修改,只修改编译条件来生成对应的bin
遇到比较多困难见阶段7
经过同学帮助,以及仓库作者帮助
成功通过了编译,并不需要再依赖用于软件实现atomic的portable-atomic
运行在板子上没有发现与之前的行为有什么差异