feat(chip): 新增 WCH CH583(QingKe V4A) 移植骨架(构建级) - #15
Conversation
src/port.rs 的芯片选择是「每口一条 pub use ...::XxxPorting as Porting」+ 「兜底 DefaultPorting 的 not(any(...)) 名单」两处成对维护;上一提交新增 ch583 特征时两处都漏了,导致开 ch583 特征时 Porting 落到 DefaultPorting (方法全是 unimplemented!())——内核 xtask::start() 走 Porting::start_scheduler, 即 CH583 例程能编过(port.S 的 global_asm 与 #[no_mangle] 符号仍在,构建门禁 是绿的)但实际未接上本口。 - 补 #[cfg(all(feature = "ch583", not(test)))] pub use Ch583Porting as Porting; - 兜底 DefaultPorting 的 any() 名单补 feature = "ch583" 取证(构建产物反汇编):修前 mscratch 在整份 disasm 里只出现一次 (_start_trap 的 csrr 读),restore_ctx.S 的 csrw mscratch 写缺失、且无 Ch583Porting 符号;修后 _...ch583..Ch583Porting..Portable..start_scheduler 出现,csrw mscratch 出现。本次改动仅补 cfg 接线,不动 ch583 口本身。
为什么改:正选别名与兜底桩名单原本两份手写,新增芯片只改一份时 Porting 静默落到 DefaultPorting(unimplemented!),构建与门禁全绿、运行期 xtask::start() 才 panic(ch583 骨架 PR 实测踩过,见 issue #14/PR #15)。 改法:chip_portings! 清单一次登记同时生成 正选别名/兜底 not(any(...))/RealPorting 标记实现+编译期断言;build.rs 再按 src/chip/<名字> 目录 ⇄ 启用 feature ⇄ 登记表 三方交叉,把漏登记变成构建期失败。 验证:删掉 ch583 登记行 → build.rs 报红(阳性对照);13 个移植口 --lib 全绿(riscv32imac 8 口/thumbv7em 2 口/thumbv7m/thumbv6m/armv7r)。
为什么改(按官方 openwch/ch583 EVT 源码核对,替换此前的推断): 1) ROM 启动头缺失:官方 Startup/startup_CH583.S 的 .vector 段第 5 字固定 0xF3F9BDA9(注释 boot option, can't modify),官方 Ld/Link.ld 把 .vector 紧跟在 .init 的 j handle_reset 之后 → 魔数落在 flash 0x14。此前本口 flash 0x14 是 riscv-rt 的启动代码,出厂(BootLoader 默认开)芯片不会进用户程序。现加 .bootvec 段复刻同一布局:0x00 入口跳转 + 向量表前 5 字,memory.x 用 SECTIONS 摆位并把 _stext 顶到 0x18,链接期 ASSERT 钉住 0x0/0x14。 2) SysTick CTLR:官方 RVMSIS/core_riscv.h 的 SysTick_Config() 写 INIT|STRE|STCLK|STIE|STE = 0x2F,不带 SWIE。原 0x8000_002F 的 SWIE 会让第一个任务 mret 后立刻多进一次 trap,且该 trap 早于 restore_ctx.S 写 mscratch(隐含依赖 MIE=0);去掉即无此依赖。 3) csrw 0xbc0,0x1f 与官方 reset 序列逐字一致(已按源码核对),保留并把注释换成出处引用。 验证:构建过;产物级检查 ci/check_ch583_boot.py PASS(入口跳转 + boot option@0x14);阳性对照——魔数改 0xF3F9BDAA → 脚本报红;.bootvec 挪到 0x4 → 链接期 ASSERT 报红。
本示例(ch583,+timer)要起 15 个默认任务(256 字栈 ≈1.03K/个)+ 软件定时器任务(1024 字 ≈4.1K),加 Task 结构与同步对象 ≈21.5K,全部走堆;原 16K 必 OOM。32K SRAM 里 _sheap(0x20001188) 到 _stack_start(0x20008000) 有 ~27.6K,取 24K 后堆顶 0x20007188,与中断栈(_stack_start-512)仍留 1.6K 余量。
ch583(QingKe V4A)没有 QEMU 机器可执行,门禁补两件事:①cargo build --example multitask_ch583 --features ch583,timer;②新增 ci/check_ch583_boot.py 读 ELF 校验 .bootvec 回到 flash 0x0..0x17、入口是 4 字节跳转、boot option 0xF3F9BDA9 落在 0x14。缺了它,ROM 不引导而构建/门禁全绿(假绿)。
README 芯片清单补 CH58x: ch583(构建级验证 2026-09-19,真机待验,注明启动头已复刻);docs/README.md 补 ch583 的官方 EVT 素材来源(Startup/Ld/RVMSIS/StdPeriphDriver),把审查提的'寄存器常量依据不可复核'落成可追溯链接;CLAUDE.md 的 chip feature 清单补 ch583;README 默认 features 计数按今日实测 82 修正(旧记 80)。
审查整改(2026-09-19):5 个提交,已按官方 openwch/ch583 EVT 源码核对先把审查依据补上:issue 里引用的
提交
验证本地全门禁( 阳性对照(新守卫不是摆设):①删 移植口回归:13 个口
未处理(留给后续):CH572 口(32 位 SysTick、SWIE 在 |
README 验证体系补一条'产物级':ci/gate.sh 除 host 回归与 QEMU 执行级外,还校验 ch583 的 ROM 启动头(flash 0x00..0x17,向量表第 5 字 boot option 0xF3F9BDA9 @0x14),并说明该口没有 QEMU 机器可跑;一键复现的命令注释同步。
复审实测(QEMU 探针:同一 32 位分配器 / 同一 Task 布局;WCH 侧无法仿真,只能这样取数): 15 个示例任务 = 17768 B + 软件定时器任务(1024 字) + idle(512 字) + 计表任务 ≈ 24900 B(计表任务占 1.2K) 即原 15 任务组合启动就需 ~24.4K 堆;24K 会在 start() 里 panic(memory out)(已实测复现), 而 32K SRAM 里 _sheap 到 _stack_start 只有 28.3K,给不出 26K+ 堆。 故把示例任务 15 → 12(仅 example_task 的 5 个纯打印任务留 2 个;semaphore/queue 各 5 个不动, 同优先级时间片与 IPC 语义不变):12 任务 + timer + idle ≈ 20.9K,24K 堆余量 ≈3.6K, 堆顶 0x20007188 距中断栈基 0x20007E00 仍有 3.2K。注释写清实测数与不要再加任务的告警。
复审发现两处: 1) 用 assert 做文件类型判断,-O 下会被关掉(校验不能依赖 assert);改为显式判断并补 ELF32/小端检查——否则指向 64 位 ELF 会以解析异常结束,而不是给出清晰 FAIL。 2) 只认 4 字节 JAL,而 2 字节 c.j 同样合法;真正钉死布局的是 .bootvec 段地址/长度与 魔数偏移,故放宽为 JAL/c.j 都接受,避免误报。 两项均实测:合成 ELF64 → 期望 32 位小端 ELF 报红;整改前 ch583 ELF → 没有 .bootvec 段报红。
复审 5 遍的结果(新增 2 个提交)复审按 5 个角度各跑一遍:①依据核对(回官方 SDK) ②链接布局与产物 ③Rust 逻辑与 cfg ④守卫有效性与可达性 ⑤端到端回归。查出 2 个真问题,已修并留证(详见 PR 描述新增的「复审(5 遍)修正与证据」一节):
顺带把最关键的"魔数在 flash 0x14"从推演升级成实测:用官方 验证:门禁全绿(host 159 / ch583 启动头 / 乒乓 A×200 B×200 / kernel_tests 24-24 ×3 / smp 9-9)、13 口矩阵全绿、4 项阳性对照全部按预期报红(验后复原)。唯一异常是首次门禁在 tlsf 变体偶发红一次(输出停在 |
独立审查(全新会话,只给任务书与一手资料)结论:需修改(2 处文档级)+ 1 条设计观察,逐条处理: 1) examples/multitask_ch583.rs 头部的上板核对清单第③条仍是旧值 CTLR=0x8000_002F, 而 7385bf4 已把代码改成 0x2F —— 照它验会验到固件根本没写的值。改为 0x2F,并把 第④项升级为"官方三套移植走 SWI_IRQn=14,本口 SWIE 路线待上板确认,备选改 IRQ 14"。 2) src/chip/ch583/mod.rs 模块文档仍写"无 A 扩展退路 = 换 riscv32imc target",与实测 (该 target 无 target_has_atomic,内核 fetch_add/swap 编不过)自相矛盾。改为 "需先做关中断/自旋锁原子垫片"。 3) 硬化:irq()/disable_irq() 对 CTLR 的读改写显式掩掉 INIT(bit5)。INIT 是否自清没有 一手证据(官方 SysTick_Config 只在初始化写一次 CTLR、之后从不读改写);若它是电平位, 回写会让每次 yield 重新装载 64 位计数器 → tick 账漂移。掩掉后两种情况都安全。 反汇编核对:irq() = lw / or SWIE / andi -0x21 / sw;disable_irq() = lw / and 0x7FFFFFDF / sw。 审查同时复现(未被推翻、无需改代码):官方常量与 flash 0x14 布局、产物级启动头、 三条守卫的阳性对照、门禁全绿(host 159 / ch583 启动头 / 乒乓 / kernel 24-24×3 / smp 9-9)、 13 口矩阵、宿主默认 82 passed。
独立审查(第三轮)结果:需修改 → 已修
|
为什么改:正选别名与兜底桩名单原本两份手写,新增芯片只改一份时 Porting 静默落到 DefaultPorting(unimplemented!),构建与门禁全绿、运行期 xtask::start() 才 panic(ch583 骨架 PR 实测踩过,见 issue #14/PR #15)。 改法:chip_portings! 清单一次登记同时生成 正选别名/兜底 not(any(...))/RealPorting 标记实现+编译期断言;build.rs 再按 src/chip/<名字> 目录 ⇄ 启用 feature ⇄ 登记表 三方交叉,把漏登记变成构建期失败。 验证:删掉 ch583 登记行 → build.rs 报红(阳性对照);13 个移植口 --lib 全绿(riscv32imac 8 口/thumbv7em 2 口/thumbv7m/thumbv6m/armv7r)。
合并记录(2026-09-19)
|
关联
Refs #14(不写 Closes——真机未验证,不关 issue)
变更概述
src/chip/ch583/{mod.rs, port.rs, port.S, restore_ctx.S, memory.x},以src/chip/ch32v103/(V3A,同为 PFIC + WCH 自有 SysTick + 纯软件全保存)为模板。examples/multitask_ch583.rs(构建级示例,与examples/multitask_ch32v103.rs同构)。Cargo.toml增ch583 = ["riscv-rt"](CH58x 无现成 ch32-rs PAC,零 PAC 直址访问);build.rs、src/chip/mod.rs注册;src/chip/env.rs增时钟常量。.bootvec(见下「启动头」)。src/port.rs芯片接线收成单一来源清单 +build.rs接线守卫(见下「接线守卫」)。原始寄存器常量与布局已按官方
openwch/ch583EVT 源码逐条核对(Startup/startup_CH583.S、Ld/Link.ld、RVMSIS/core_riscv.h、StdPeriphDriver/inc/CH583SFR.h),出处写在源码注释里。与既有口的 4 个关键差异
0x00000000(非0x08000000):memory.xFLASH ORIGIN = 0x0, LENGTH = 448K(官方Link.ld只给 448K,尾部 64K 留给 BootLoader/ISP/配置区);RAM ORIGIN = 0x20000000, LENGTH = 32K。前 0x18 字节留给启动头,.text顶到 0x18。0xbc0 = 0x1f:_setup_interrupts在关 HPE 之前多写一条——与官方startup_CH583.Sreset 序列逐字一致(CH572 是0xbc0=0x25且另有0xbc1=1)。mtvec+0x804=0(关 HPE,纯软件全保存):mtvec = _start_trap & ~3(低 2 位 0,不是官方向量模式的| 3);intsyscr(0x804) = 0关硬件压栈/嵌套(官方 startup 写0x3开 HWSTKEN+嵌套,本口刻意走 36 字全保存帧),退回标准 RV32 机器模式。CTLR.SWIE当 PendSV:SysTick@0xE000_F000;初始化CTLR = INIT(5)|STRE(3)|STCLK(2)|STIE(1)|STE(0) = 0x2F(与官方SysTick_Config()逐位一致,不带 SWIE;SWIE 只在运行期irq()里置位请求切换——初始化就置 SWIE 会让第一个任务mret后立刻多进一次 trap,且早于restore_ctx.S写 mscratch);SR.CNTIF写 0 清;systick()读 64 位 CNT(高:低:高重读,非 CH572 的 32 位读法);PFICIENR[0] @0xE000E100bit12 使能核中断 12。启动头(ROM boot option)
官方
EVT/EXAM/SRC/Startup/startup_CH583.S的.vector段第 5 个字固定0xF3F9BDA9(注释boot option, can't modify);官方Ld/Link.ld把.vector的 flash LMA 摆在.init的j handle_reset之后 → 该字落在 flash0x14。本口新增
.bootvec复刻同一布局:摆放:
memory.x的SECTIONS把.bootvec钉到 flash 0x0,同时先定义_stext = 0x18把.text顶到启动头之后(riscv-rt 的PROVIDE(_stext=...)不覆盖已定义符号);链接期ASSERT(_bootvec==0x0 / _boot_magic==0x14)钉地址,产物级由ci/check_ch583_boot.py读 ELF 钉段地址、入口跳转与魔数字节。接线守卫(防"构建绿、运行期 panic")
src/port.rs的芯片选择原本是两份手写名单(每口一条正选pub use ... as Porting+ 兜底not(any(...))的 feature 名单)。上一提交新增 ch583 时两处都漏 →Porting落到DefaultPorting(各方法unimplemented!()),构建门禁全绿、运行期进xtask::start()才 panic(修前取证:整份 disasm 里mscratch只出现一次、无Ch583Porting符号)。现改为
chip_portings!单一来源清单,一次生成:正选别名 / 兜底名单 /RealPorting标记实现 + 编译期断言;build.rs再按src/chip/<名字>目录 ⇄ 启用 feature ⇄ 登记表三方交叉。漏登记 = 构建期失败。接线修复保留在独立提交1554aef(未 amend 骨架提交,保留可追溯性)。验收证据
宿主
x86_64-pc-windows-msvc,rustc 1.91.0-nightly(rust-toolchain钉的 nightly-2025-08-08)。本地全门禁bash ci/gate.sh:阳性对照(证明新守卫真会红,验后已复原):①删
src/port.rs的"ch583" =>登记行 →build.rs报红;②魔数改0xF3F9BDAA→ci/check_ch583_boot.py报红;③.bootvec挪到 0x4 → 链接期ASSERT报红。移植口回归:13 口
cargo build --lib全绿(riscv32imac 8 口 / thumbv7em 2 口 / thumbv7m / thumbv6m / armv7r);默认 featurescargo test --lib= 82 passed(README 旧记 80 已在本 PR 修正)。复审(5 遍)修正与证据
复审(依据核对 / 链接布局与产物 / Rust 逻辑与 cfg / 守卫有效性 / 端到端回归)查出两个真问题,已修:
start()里panic("memory out")(已实测复现);而 32K SRAM 里_sheap→_stack_start只有 28.3K,给不出 26K+ 的堆。example_task的 5 个纯打印任务留 2 个;semaphore/queue 各 5 个不动,同优先级时间片与 IPC 语义不变)→ 12 任务 + timer + idle ≈ 20.9K,24K 堆余量 ≈3.6K(堆顶 0x20007188 距中断栈基 0x20007E00 仍 3.2K)。探针实测:heap=24576B时含 timer+idle 已用=22104B 空闲=2472B且打出METER-DONE。assert判文件类型,-O下会被关掉 → 改显式判断并补 ELF32/小端校验;②入口只认 4 字节 JAL → 放宽为 JAL/c.j(真正钉死布局的是段地址与魔数偏移,严编码只会引入误报)。复核结论(证据):
Ld/Link.ld+ 官方.init/.vector段内容链接出的 flash 镜像,0x14处正是0xF3F9BDA9—— "魔数在 flash 0x14"由推演升级为实测(顺带确认:官方把.vector放在.highcode里>RAM AT>FLASH,LMA 起点 0x04)。PFIC->IENR[0] @0x100bit12 与官方PFIC_EnableIRQ(SysTick_IRQn=12)逐字等价;start_scheduler反汇编确认CMPLR=59999、CTLR=0x2F(无 SWIE)、IENR bit12;STK基址与SysTick_Type{CTLR,SR,u64 CNT,u64 CMP}字偏移一致;SR.CNTIF写 0 清。objcopy -O binary的实际烧录镜像前 0x18 字节 =j _start+ 0/0 +_start_trap×2 +F3F9BDA9;_stext=0x18、_stack_start/_sheap与整改前一致(无布局回归)。"ch583" =>登记行 → build.rs 报红;魔数改0xF3F9BDAA→ 脚本报红;.bootvec挪到 0x4 → 链接期 ASSERT 报红;RealPorting实现 cfg 掉 → E0277。脚本对整改前 ch583 ELF 报"没有 .bootvec 段"、对合成 ELF64 报"期望 32 位小端 ELF"。bash ci/gate.sh全绿(host 159 / ISR 向量 / ch583 启动头 /qemu_pingpongA×200 B×200 /qemu_kernel_tests24-24 与 -smp2、tlsf 三跑 /qemu_smp9-9 / 全部通过);13 口cargo build --lib全绿;宿主默认 features 82 passed。suite starting),单独复跑 24/24、整门禁复跑亦全绿 —— 判为本机偶发,非回归。第三轮:独立审查(全新会话,仅给任务书与一手资料)
结论:需修改(2 处文档级旧值 + 1 条设计观察),已全部处理(
6c3e984):examples/multitask_ch583.rs上板核对清单第 ③ 条仍写CTLR=0x8000_002F(代码已改成0x2F)→ 已改正;第 ④ 项升级为"官方 CH583 三套移植都不走CTLR.SWIE,一律SWI_IRQn=14+SW_Handler,本口 SWIE 路线待上板确认,备选即改 IRQ 14"。src/chip/ch583/mod.rs模块文档仍写"无 A 扩展 = 换riscv32imctarget"→ 已改为"须先做关中断/自旋锁原子垫片"(实测该 target 无target_has_atomic)。irq()/disable_irq()对CTLR的读改写显式掩掉INIT(bit5)——它是否自清无一手证据;若为电平位,每次 yield 会重装 64 位计数器、tick 账漂移。反汇编已核(andi -0x21/and 0x7FFFFFDF)。审查独立复现并确认(未被推翻):官方常量与 flash
0x14布局、产物级启动头、三条守卫阳性对照、bash ci/gate.sh全绿(host 159 / ch583 启动头 / 乒乓 A×200 B×200 / kernel 24-24×3 / smp 9-9)、13 口矩阵、宿主默认 82 passed。审查提出的开放项(进上板清单,不在本 PR 内改设计):本口沿用 ch32v103 模板的
STK_CTLR.SWIE软中断,而官方 CH583 三套 RTOS 移植都走 QingKe 专用SWI_IRQn=14,SWIE在官方 SDK 里只定义、零使用。上板若发现 SWIE 无效,改走 IRQ 14 即可(_int_dispatch已按中断号分发,改动量小)。另:24K 堆余量按不同量法为 2.4–3.6K(审查方量到已用 22.1K),够启动但偏薄,上板若 OOM 优先再减示例任务。遗留
0x0是否可写 / 是否存在0x08000000别名;复位默认主频(官方FREQ_SYS=60MHz是配置值,本口未做 SAM 解锁/PLL 初始化,SYSTICK_CLOCK_HZ=60M待实测校正);0x804=0后是否确实不再硬件压栈(36 字帧前提);CTLR.SWIE置位后是否真进 SysTick 入口、SR.CNTIF是否仍为 0;BootLoader 是否放行(启动头已复刻,仍需实板确认)。.highcode(RAM 执行段)本版未启用:代码全留 FLASH(riscv-rt 的link.x不做 LMA→VMA 搬运)。delay_us仍用mcycle:CH57x 是否实现mcycle/Zicntr 待上板;未实现则读出恒 0 → 死循环,须改 SysTick/TMR 计时(CH57x 无 DWT)。SWIE在SR、SysTick 32 位、PFIC 无CFGR,需另一组差异常量)。riscv32imc退路不成立:实测该 target 只有target_has_atomic_load_store、没有target_has_atomic,内核里fetch_add/swap这类 RMW 编不过——无 A 扩展(CH572/青稞无 A 的档位)需要关中断/自旋锁原子垫片,不是换个 target 的事。