Skip to content

critical_section: use 0x88 mask on v4 to avoid INESTEN=1 wedge - #25

Merged
aq1018 merged 1 commit into
mainfrom
fix-v4-cs-mpie-corruption
Jun 26, 2026
Merged

critical_section: use 0x88 mask on v4 to avoid INESTEN=1 wedge#25
aq1018 merged 1 commit into
mainfrom
fix-v4-cs-mpie-corruption

Conversation

@aq1018

@aq1018 aq1018 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Summary

V4 cores (CH32V20x) can wedge with MIE stuck at 0 in user code when critical_section::with runs inside an ISR body while INTSYSCR.INESTEN=1. The standard 0x8-mask path touches MIE only, leaving an MIE=0 / MPIE=1 mismatch that V4's nested-interrupt machinery doesn't recover from — MIE=0 eventually propagates outward through nested mrets, all async IRQs end up masked indefinitely, and the system silently stalls (no sync exception, ISR enter/exit counts balanced).

This PR routes V4 through its own cfg arm using the 0x88 mask (MIE + MPIE together via gintenr), matching openwch's __disable_irq / __enable_irq pattern for V20x [1] and preserving the implicit "MIE and MPIE move together" invariant V4 expects inside an ISR body.

Why openwch's C SDK doesn't hit this

C code without an embassy-style task runtime essentially never invokes a critical_section-style acquire from inside an ISR body, so the bad path doesn't fire. openwch's __disable_irq / __enable_irq happen to use the 0x88 mask anyway — likely accidental robustness inherited from older WCH conventions, no comment explains it.

Verification

Reproduced on CH32V203C8T6 with embassy + nested PFIC (INTSYSCR=0b0111, HWSTKEN + INESTEN + PMTCFG=01): wedge fires within a few thousand IRQ-driven round-trips on the standard 0x8 mask; the 0x88 mask cleared tens of thousands without recurrence. PMTCFG=0 (no preempt classes possible) still wedges, so the trigger is the INESTEN bit enabling the nesting machinery, not actual L2-preempt firings.

References

  • V4 manual §2.3 (MIE not cleared on non-deepest trap entry) and §8.3 (gintenr is the bit-level alias of mstatus)
  • [1] openwch CH32V20X SDK core_riscv.h

V4 silicon (CH32V20x) wedges with MIE stuck at 0 in user code when
critical_section::with is invoked inside an ISR body with
INTSYSCR.INESTEN=1. The standard 0x8 mask creates an MIE=0/MPIE=1
mismatch that V4's nested-interrupt machinery handles incorrectly,
eventually propagating MIE=0 outward via nested mrets.

The 0x88 mask (MIE+MPIE together) matches the openwch SDK
__disable_irq/__enable_irq pattern for V20x [1] and preserves the
implicit "MIE and MPIE move together" invariant V4 assumes. Uses
gintenr (CSR 0x800, bit-level alias of mstatus per V4 manual §8.3)
to match the openwch site exactly.

Repro: dxl-pirate (embassy on CH32V203C8T6) at INTSYSCR=0b0111 wedges
within ~3000 USART3 pings on the 0x8 mask; 0x88 cleared 40K+.

[1] https://github.com/openwch/ch32v20x/blob/main/EVT/EXAM/SRC/Core/core_riscv.h
@aq1018
aq1018 merged commit 284240e into main Jun 26, 2026
1 check passed
@aq1018
aq1018 deleted the fix-v4-cs-mpie-corruption branch June 26, 2026 02:54
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