A community porting note explains how the CH32V307 V3A core handles interrupt nesting, why FreeRTOS critical sections need care, and the one-line assembly fix to enable nested interrupts.
FreeRTOS Porting on WCH CH32V307: RISC-V Interrupt Nesting and Critical-Section Notes
The WCH CH32V307 is one of the most cost-effective RISC-V MCUs with built-in gigabit Ethernet MAC, USB OTG, and CAN. When porting FreeRTOS to its QingKe V3A core, developers often hit a subtle difference from ARM Cortex-M: the V3A core does
not enable hardware interrupt nesting by default.
This post summarizes a practical porting note from the 21ic community, plus the follow-up discussion, so you can avoid the same pitfall.
The Trap: No Automatic Interrupt Nesting
On ARM Cortex-M, the NVIC automatically supports preemption based on priority levels. On the CH32V307 V3A core, RISC-V trap entry clears the global interrupt-enable bit in mstatus. That means once you enter an interrupt handler, higher-priority interrupts are blocked unless you re-enable them explicitly.
The one-line fix used by the author is to set the MIE bit inside the handler:
__asm volatile("csrrsi x0, mstatus, 8"); /
enable MIE for nested interrupts /
What this does:
- csrrsi is the RISC-V CSR set-immediate instruction.
- mstatus is the machine status register.
- Immediate value 8 corresponds to bit 3, the MIE (Machine Interrupt Enable) bit.
- Setting MIE re-opens global interrupts, allowing nested preemption.
Note: only do this when your ISR is re-entrant and your stack is sized for worst-case nesting.
FreeRTOS Porting Checklist
When adapting the official WCH FreeRTOS port to your own board, watch these points:
- Critical-section implementation
- The default port uses interrupt masking. Make sure your critical-section macros match the chosen nesting model.
- If you enable MIE inside ISRs, consider using a nesting-aware critical-section implementation.
- PendSV equivalent
- RISC-V has no hardware PendSV. The port uses a software exception / supervisor-call style entry to trigger context switch.
- Verify the trap handler saves all caller-saved registers and mepc.
- Clock and SYSCFG initialization
- Initialize the system clock and SYSCFG exactly as shown in the official EVT examples before starting the scheduler.
- Mismatched clock settings are a common cause of tick drift or hard faults after vTaskStartScheduler().
- Priority registers
- The QingKe interrupt controller has its own priority/threshold registers. Align the port layer with the actual IP/core configuration.
- Hardware stack push
- Enabling hardware automatic stack push/pop can noticeably reduce interrupt latency, which is important for fast nested interrupts.
Community Additions
Other developers in the thread added two important reminders:
- Pay attention to CSR saving (mstatus, mepc, mcause) in nested scenarios; the hardware saves some, the software must save the rest.
- Configure the priority registers correctly; the port file must match the core's interrupt-controller layout.
Why This Matters for the WCH Portfolio
The CH32V307 sits in the sweet spot for networked RISC-V applications: ~$3 price point, 144 MHz, USB + Ethernet MAC, hardware multiplication/division. If you are moving from ARM to this family:
- CH32V003 — $0.1 entry-level RV32EC, perfect for simple GPIO/PWM projects.
- CH32V208 — BLE 5.3 + USB, good for wireless peripherals.
- CH32V307 — gigabit Ethernet + USB, the go-to choice for IoT gateways.
- CH32V317 — high-speed USB 480 Mbps for more demanding USB device/host work.
- CH32V407 — adds the RISC-V V vector extension and dual high-speed USB, useful for DSP-like workloads.
Understanding the interrupt model is the first step to getting reliable FreeRTOS performance on any of these parts.
Source: 沁恒 CH32V307 的 RISC-V 中断嵌套与 FreeRTOS 移植的 C 实操记录 — 21ic Electronic Technology Forum