Skip to content

fix(wch): 软中断改官方 SWI_IRQn=14 + delay_us 改 SysTick 计数 - #19

Merged
gqf2008 merged 2 commits into
masterfrom
fix/wch-yield-delay
Sep 19, 2026
Merged

gqf2008 merged 2 commits into
masterfrom
fix/wch-yield-delay

Conversation

@gqf2008

@gqf2008 gqf2008 commented Sep 19, 2026

Copy link
Copy Markdown
Owner

两处此前只能靠上板才能排除的风险,改成结构上不需要排除。

1) yield/抢占的软中断:CTLR/SR.SWIESWI_IRQn=14

依据:官方 CH583/CH572 的 FreeRTOS / RT-Thread / HarmonyOS 移植一处都没用 SWIE
(SDK 里只定义、零使用;CH572 的 SysTick_SR_SWIE 全 SDK 零处调用),yield 一律
PFIC_SetPendingIRQ(SWI_IRQn) + SW_Handler

实现:irq()PFIC->IPSR[0] @0xE000E200 bit14;disable_irq()IPRR[0] @0xE000E280 清 pending(取中断一般已自动清,防御性再清一次,防残留 pending 让
trap 出口立刻重入);_start_trap_int_dispatch 增一条 mcause=14 → 直接进
切换/恢复路径(不做时间账)。初始化不再有"别设 SWIE"的隐含注意事项。

2) delay_us 的时基:mcycle → SysTick 自由计数

依据:"CH583/CH572 是否实现 Zicntr"没有一手证据,未实现时 mcycle 读出恒为常量
→ 原实现死循环(只能上板发现);SysTick 是内核点拍赖以工作的外设,必然实现。

实现:按 CMP 重装周期分块等待,跨重装的差值用模运算;纯算术抽到 src/chip/delay.rs
并配 host 单测。单测当场抓到一个真 bug:当 chunk 等于整周期时,模运算差值最大
只有 period-1,"等到 chunk 拍"这个条件永不成立 → delay_us 会在计数器恰为 0
(起点落在重装点,很常见)时死循环。已钳到 period-1,并加回归断言
chunk_never_equals_full_period 钉住(测试先跑到挂死、杀掉进程后定位)。

影响面

只改 ch583 / ch572 两口;ch32v103/ch32v203/ch32v307 仍沿模板的 SWIE 与 mcycle
(未在本轮核对它们的 SDK,注释里已标明差异)。

验证

  • 反汇编核对:irq() = lui 0xE000E; li 0x4000; sw 0x200(IPSR bit14);
    switch_context = sw 0x280(IPRR 清 pending);_int_dispatchli t1,0xe 分支。
  • 两口构建 + 启动头产物校验 PASS;bash ci/gate.sh 全绿(host 由 159 → 166,
    含 chip::delay 的 5 项 + chip::wch_pfic 的 2 项;QEMU 三套 24-24、乒乓 A×200 B×200、smp 9-9)。
  • 14 口矩阵全绿(esp32c3 已按真 ISA riscv32imc);宿主默认 features 89 passed
  • README 计数快照同步(159→166、82→89)。

审查整改(2026-09-19:独立审查 1 处阻塞,已整改并复验)

阻塞项:yield 改走 SWI_IRQn=14 后只置了 pending(IPSR[0] bit14),两口的
setup_intrrupt()IENR[0] 仍只写 1<<12(SysTick)——PFIC 的 pending 与 enable
是两组分离的寄存器,没人使能的 pending 永远不会被取走 → irq() 静默失效(yield/抢占
只剩 tick 兜底),而构建/门禁/host 测试全绿。官方 port.cvPortSetupTimerInterrupt()
PFIC_EnableIRQ(SWI_IRQn) 正对应这一步:只抄 PFIC_SetPendingIRQ 不算移植完成。

整改(539416e):

  • IENR[0] 写值 1<<12bit12|bit14;掩码与 PFIC 偏移收进 src/chip/wch_pfic.rs
    单一来源,配 2 项 host 守卫(使能掩码须含 SWI 位、偏移等于官方布局)。
  • 产物级复核(llvm-objdump,两侧镜像):IENR(offset 0x100)立即数 0x1000
    0x5000;IPSR(0x200)/IPRR(0x280) 的 0x4000 写与 _int_dispatch
    li t1,0xe 分支仍在。
  • 守卫阳性对照:去掉掩码里的 SWI 位 → chip::wch_pfic::tests::ienr_mask_enables_swi_alongside_systick
    FAILED(exit 1),已还原。
  • 同轮清掉两处非阻塞:重复的 #[test](同一用例被注册两次,"新增 6 项"由此而来)已去重;
    两个示例上板清单⑦由"mcycle 是否实现"改为"delay_us 实测精度",并给 delay_us
    写明"SysTick 已在跑"的前置条件。

遗留(仍需上板)

SWI_IRQn=14 置 pending 后是否即刻进 trap 并完成切换;delay_us 实测精度
(现按 SYSTICK_CLOCK_HZ 换算,复位默认主频待实测)。两项都写进了两个口的头注核对清单。

Refs #14

两处此前**只能靠上板才能排除**的风险,改成结构上不需要排除。

## 1) yield/抢占的软中断:`CTLR/SR.SWIE` → `SWI_IRQn=14`

依据:官方 CH583/CH572 的 FreeRTOS / RT-Thread / HarmonyOS 移植**一处都没用 SWIE**
(SDK 里只定义、零使用;CH572 的 `SysTick_SR_SWIE` 全 SDK 零处调用),yield 一律
`PFIC_SetPendingIRQ(SWI_IRQn)` + `SW_Handler`。

实现:`irq()` 置 `PFIC->IPSR[0] @0xE000E200` bit14;`disable_irq()` 用 `IPRR[0]
@0xE000E280` 清 pending(取中断一般已自动清,防御性再清一次,防残留 pending 让
trap 出口立刻重入);`_start_trap` 的 `_int_dispatch` 增一条 `mcause=14` → 直接进
切换/恢复路径(不做时间账)。初始化不再有"别设 SWIE"的隐含注意事项。

## 2) `delay_us` 的时基:`mcycle` → SysTick 自由计数

依据:"CH583/CH572 是否实现 Zicntr"没有一手证据,未实现时 `mcycle` 读出恒为常量
→ 原实现**死循环**(只能上板发现);SysTick 是内核点拍赖以工作的外设,必然实现。

实现:按 `CMP` 重装周期分块等待,跨重装的差值用模运算;纯算术抽到 `src/chip/delay.rs`
并配 6 项 host 单测。**单测当场抓到一个真 bug**:当 `chunk` 等于整周期时,模运算差值最大
只有 `period-1`,"等到 chunk 拍"这个条件永不成立 → `delay_us` 会在计数器恰为 0
(起点落在重装点,很常见)时死循环。已钳到 `period-1`,并加回归断言
`chunk_never_equals_full_period` 钉住(测试先跑到挂死、杀掉进程后定位)。

## 影响面

只改 ch583 / ch572 两口;ch32v103/ch32v203/ch32v307 仍沿模板的 SWIE 与 `mcycle`
(未在本轮核对它们的 SDK,注释里已标明差异)。

## 验证

- **反汇编核对**:`irq()` = `lui 0xE000E; li 0x4000; sw 0x200`(IPSR bit14);
  `switch_context` = `sw 0x280`(IPRR 清 pending);`_int_dispatch` 有 `li t1,0xe` 分支。
- 两口构建 + 启动头产物校验 PASS;`bash ci/gate.sh` 全绿(host 由 159 → **165**,
  含新增 6 项;QEMU 三套 24-24、乒乓 A×200 B×200、smp 9-9)。
- 14 口矩阵全绿(esp32c3 已按真 ISA `riscv32imc`);宿主默认 features **88 passed**。
- README 计数快照同步(159→165、82→88)。

## 遗留(仍需上板)

`SWI_IRQn=14` 置 pending 后是否即刻进 trap 并完成切换;`delay_us` 实测精度
(现按 `SYSTICK_CLOCK_HZ` 换算,复位默认主频待实测)。两项都写进了两个口的头注核对清单。

Refs #14
独立审查(PR #19)发现:yield 改走 SWI_IRQn=14 后只置了 pending(IPSR[0] bit14),两口的 setup_intrrupt() 里 IENR[0] 仍只写 bit12(SysTick)。PFIC 的 pending 与 enable 是两组分离的寄存器,没人使能的 pending 永远不会被取走 -> irq() 静默失效(yield/抢占只剩 tick 兜底),而构建/门禁/host 测试全绿。官方 EVT port.c 的 vPortSetupTimerInterrupt() 里 PFIC_EnableIRQ(SWI_IRQn) 正对应这一步,只抄 PFIC_SetPendingIRQ 不算移植完成。

改法:IENR 写值取 bit12|bit14;掩码与 PFIC 偏移抽到 chip::wch_pfic 单一来源,并配 2 项 host 守卫(使能掩码须含 SWI 位 + 偏移等于官方布局),已用阳性对照确认去掉 SWI 位即报红。产物级复核:两口镜像 IENR(offset 0x100) 的立即数 0x1000 -> 0x5000。

顺带:delay.rs 一处重复 #[test] 让同一条用例注册两次(README 的 6 项计数由此而来),去重后 host 计数 88->89(5 项 delay + 2 项 wch_pfic);两示例上板清单第 7 条由 mcycle 是否实现 改为 delay_us 实测精度 并写明 SysTick 前置,与模块头注对齐。
@gqf2008

gqf2008 commented Sep 19, 2026

Copy link
Copy Markdown
Owner Author

独立审查结论(2026-09-19):1 处阻塞,已在 539416e 整改并复验

阻塞项:yield 改走 SWI_IRQn=14 后只置了 pending(IPSR[0] bit14),两口 setup_intrrupt()IENR[0] 仍只写 1<<12(SysTick)。PFIC 的 pending 与 enable 是两组寄存器,没人使能的 pending 永远不会被取走 → irq() 静默失效(yield/抢占只剩 tick 兜底),而构建/门禁/host 测试全绿。官方 EVT/EXAM/FreeRTOS/GCC/RISC-V/port.cvPortSetupTimerInterrupt()PFIC_EnableIRQ(SWI_IRQn) 正对应这一步:只抄 PFIC_SetPendingIRQ 不算移植完成。

整改(539416e):

  • IENR[0] 写值 1<<12bit12|bit14;掩码与 PFIC 偏移收进 src/chip/wch_pfic.rs 单一来源,并配 2 项 host 守卫(使能掩码须含 SWI 位、偏移等于官方布局)。
  • 产物级复核(llvm-objdump,两侧镜像):IENR(offset 0x100)立即数 0x10000x5000;IPSR(0x200)/IPRR(0x280) 的 0x4000 写与 _int_dispatchli t1,0xe 分支仍在。
  • 守卫阳性对照:把掩码里的 SWI 位去掉 → chip::wch_pfic::tests::ienr_mask_enables_swi_alongside_systick FAILED(exit 1),已还原。
  • 同轮清掉两处非阻塞:重复的 #[test](同一用例被注册两次,原"新增 6 项"由此而来)已去重;两个示例上板清单⑦由"mcycle 是否实现"改为"delay_us 实测精度",并给 delay_us 写明"SysTick 已在跑"的前置条件。

复验:bash ci/gate.sh 末行 == 全部通过 ==;host 默认 features 89 passed、门禁特征组合 166 passed;两口构建 + 启动头校验 PASS;rustfmt --check 对本次改动净增 0(残留两处 diff 与 HEAD 逐行一致,属既有漂移)。

@gqf2008

gqf2008 commented Sep 19, 2026

Copy link
Copy Markdown
Owner Author

独立审查(PR #19)结果:需修改(1 处阻塞) → 已修 539416e 并复核

阻塞项(审查发现,已修):yield 改走 SWI_IRQn=14 后只置了 pending(IPSR[0] bit14),而两口的
setup_intrrupt()IENR[0] 仍只写 bit12(SysTick)——PFIC 的 pending 与 enable 是两组分离寄存器,
没人使能的 pending 永远不会被取走 → irq() 静默失效(yield/抢占只剩 tick 兜底),而构建/门禁/host 测试全绿。
官方三套 RTOS 移植的初始化里都有 PFIC_EnableIRQ(SWI_IRQn)(FreeRTOS vPortSetupTimerInterrupt())。

修法(比一行更稳):把 PFIC 常量与掩码抽到 src/chip/wch_pfic.rs 单一来源 + 2 项 host 守卫
(使能掩码必须含 SWI 位、偏移须等于官方布局),IENR 写值取 bit12|bit14
我的产物级复核:lui a1,0x5; sw a1,0x100(a0)(IENR=0x5000)在 ch583 镜像里确认;host 89 passed。

同轮清掉的三条非阻塞:①delay.rs 一处重复 #[test](同一条用例注册两次,正是"6 项"的来源)去重;
②两示例上板清单第 7 条由"mcycle 是否实现"改成"delay_us 实测精度"并写明 SysTick 前置;
delay_us 新契约(须在 setup_intrrupt() 之后调用)写进模块头注。

合并记录

gh pr merge --admin --rebase;合并后在 master 复验 bash ci/gate.sh 全绿。

@gqf2008
gqf2008 merged commit b09bcd4 into master Sep 19, 2026
2 checks passed
@gqf2008
gqf2008 deleted the fix/wch-yield-delay branch September 19, 2026 09:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant