|
|
|
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
|
|
mkimage -f payload_image.its -A riscv -O u-boot -T firmware visionfive2_fw_payload.img
|
|
这部分在docker内部编译
|
|
|
|
|
|
鉴于直接修改的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模式
|
|
上电
|
|
烧录官方提供的
|
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/
|
|
https://github.com/riscvarchive/riscv-aclint/blob/main/riscv-aclint.adoc
|
|
https://docs.u-boot.org/en/latest/board/starfive/visionfive2.html
|
最有用的是u-boot的已有驱动和dts
|
|
|
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
|
|
还需要注册一个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吧
|
|
|
|
理由
|
鉴于在启动第二个核的时候,
|
|
第一个核进入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里
|
|
|
|
gpio驱动实现
|
drivers/gpio/starfive-gpio.c
|
gpio的相关关键信息定义
|
u-boot/arch/riscv/include/asm/arch-jh7110/gpio.h
|
|
做了rust的最简单实现
|
没有实现embassy里的异步流程
|
|
|
|
|
在hart2上运行embassy
|
hart1上运行u-boot
|
|
|
一开始
|
使用hsm进行同步
|
预计hart2等待hart1 remote_hsm后开启embassy
|
|
后来
|
发现没有必要,在PLATFORM.ready()就做了某种程度上的同步
|
因而直接在hart2上启动embassy没有遇到更多问题
|
比实现gpio还要简单一些
|
|
TODO
|
hart2上跑的uart打印都看不见
|
只能看到uboot的打印
|
hart2上的embassy运行的task的呼吸灯正常运行
|
但hart0的行为不一样,或许需要解释一下
|
|
|
在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
|
|
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
|
|
hart 0 misa
|
|
|
因而
|
理论上hart 0支持A扩展
|
支持IMAC
|
I(Integer):支持整数基本指令集
|
M(Mul/Div):支持整数乘除法
|
A(Atomic):说明理论上支持原子指令
|
C(Compressed):支持压缩指令
|
|
不支持的是
|
|
|
实际上在time_driver使用了Atomic64
|
load/store可用
|
经过asm直接验证
|
使用lw/sw不会卡死
|
使用lr.w和sc.w确实会卡死
|
|
|
认为A扩展本身可能有问题
|
|
|
|
Q
|
embassy要如何修改
|
A
|
先实现了一个state的没有Atomic的版本
|
发现没有必要
|
|
embassy考虑了没有各种Atomic的情况,因而有多个版本的
|
但是根据target_has_atomic配置
|
不想修改编译的情况下
|
直接添加了一个feature用于强行使用state和run_queue的非atomic的版本
|
使用的是critical_section的版本
|
|
其中raw::executor也使用了AtomicPtr
|
但只使用了load,store所以道理上不用修改
|
也实际上没有使用target_has_atomic做区分
|
|
|
|
编译极简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跳转
|
|
|
然后可以了,看到了亮灯
|
|
|
在新的bin内添加embassy逻辑
|
现在叫embassy_app
|
|
|
Q
|
|
A
|
之前使用的rustsbi的ipi相关的功能
|
现在需要直接使用底层的sifive的一个包来做类似的操作
|
需要重新传入相关clint的地址
|
查看dts是0x20000000
|
|
本想使用embassy提供的StaticCell包装,但只能产生一个可以move的&static mut
|
不方便在time_driver的DRIVER里直接使用
|
最后直接用的static mut
|
|
|
Q
|
串口驱动需要重新实现
|
A
|
直接把rustsbi的相关串口驱动,更底层一些的uart,忽略了上层的抽象
|
搬了过来
|
|
Q
|
|
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段了
|
|
|
|
修改编译条件
|
在不用给embassy添加新feature的条件下
|
可以直接使用embassy
|
即,同时满足not(target_has_atomic = "ptr")
|
和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
|
|
|
参考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
|
运行在板子上没有发现与之前的行为有什么差异
|
|
|
|
|