Three fresh community threads this week converge on one theme - interrupts on WCH RISC-V MCUs. We collected the five most common pitfalls, the CH32V307 nesting workflow, and added our own note on where PFIC, PLIC and CLIC terminology actually applies.
Interrupts have been the dominant topic on Chinese-language RISC-V MCU forums this week, with three new threads in four days all circling the same migration pain: engineers moving from ARM Cortex-M to WCH's QingKe (QingKe) cores hit a different interrupt model and predictable failure modes. Here is the distilled checklist.
An engineer who recently moved from ARM to WCH RISC-V MCUs (QingKe V3A/V4A-class cores) listed five recurring mistakes, all producing the same symptoms - an interrupt that fires exactly once, or a system that hangs:
Pitfall Consequence Fix No interrupt attribute No context save, no mret __attribute__((interrupt(...))) Fast-interrupt under FreeRTOS Scheduler hangs, broken ticks Use normal interrupt attribute Pending flag never cleared ISR re-enters forever ClearITPendingBit / write SR=0 Shared global without volatile Compiler optimizes checks away Mark shared variables volatile Heavy work inside the ISR Missed events, jittery system Set flags, defer to main loopThe attribute detail is the one that bites hardest. A plain C function cannot save context correctly or return with mret, so every ISR needs an explicit declaration. Two flavors exist:
void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); void USART1_IRQHandler(void) __attribute__((interrupt()));The fast variant has lower entry/exit overhead (the author uses it for a systick heartbeat in bare-metal code) and relies on the hardware to push a partial context. That is exactly why it breaks under FreeRTOS: the RTOS expects a complete software-saved frame, and the incomplete hardware-stacked context corrupts scheduling. Under FreeRTOS, always use the plain interrupt attribute.
The flag-clearing pitfall deserves its own emphasis, because the failure looks like a hang rather than an interrupt problem. Peripheral-level interrupt request flags are not auto-cleared, so the controller sees the IRQ as still pending and re-enters the ISR immediately after mret. A minimal systick handler shows the pattern: void SysTick_Handler(void) { if (SysTick->SR == 1) { SysTick->SR = 0; // clear state flag if (time_tick > 0) { time_tick--; } } }The same thread recommends hardware nesting via PFIC_SetPriority() - the QingKe V3/V4 cores support automatic hardware nesting - and notes that fast-interrupt entry suits high-frequency sources.
A second thread (2026-10-28) walks through interrupt bring-up on the CH32V307 in migration terms. The workflow it describes maps cleanly onto any WCH RISC-V part:
Nesting notes from the same thread: high-priority interrupts can preempt lower ones, but stack budget must account for worst-case nesting depth; avoid long stretches with global interrupts disabled inside ISRs; implement critical sections as save-and-restore of interrupt state rather than blanket disable, so nesting stays consistent; and coordinate the order of clearing peripheral flags with the controller completion step to avoid the flag-cleared-but-still-pending re-entry case.
Its debugging table is short and practical: interrupt never fires - check controller enable bits and source enables first, then vector table registration; handlers run in the wrong order - verify priorities and preemption thresholds; config errors - inspect CSR values (mie, mstatus) live with a debugger.A third thread (2026-10-27) discusses CLIC-style configuration and reports measured GPIO external-interrupt latency of roughly 30-40 clock cycles (about 0.25 microseconds) at 144 MHz on a CH32V307 when using hardware vector mode with automatic register stacking, versus noticeably higher software-table-dispatch latency. The qualitative direction is right: vector mode plus hardware stacking is how you get tens-of-cycles ISRs, and priority granularity on modern controllers is far finer than the four NVIC levels Cortex-M developers are used to.
One caution from us before you copy that thread onto WCH silicon: terminology in these community posts is loose, and it matters when you are writing to registers. Strictly speaking:The practical takeaways survive the terminology fuzziness: use the correct interrupt attribute, clear pending flags, complete the controller transaction, keep ISRs minimal, and let hardware nesting with sensible priorities do the rest. For a 0.1-class part like the CH32V003 the same five pitfalls apply verbatim - only the register addresses change.
If you are choosing a WCH RISC-V MCU around interrupt-heavy designs: CH32V003 (US$0.1, RV32EC) for cost-sensitive simple ISRs, CH32V203 for USB-device work, CH32V307 with its Ethernet MAC+PHY for networked nodes, CH32V317 for USB 480Mbps, and CH32H417 when USB 3.0 enters the picture. All share the same interrupt programming model described above, so the checklist transfers across the family. Inquiry links are on our product pages.