Skip to content

Commit 6270361

Browse files
committed
Run FreeRTOS ISRs on a dedicated stack
1 parent 2efe590 commit 6270361

11 files changed

Lines changed: 166 additions & 24 deletions

File tree

ch32v305/README.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,22 @@ wchisp flash ch32v305_sdr.elf
5454

5555
| Task | Priority | Stack |
5656
| --- | ---: | ---: |
57-
| I2S processing | 3 | 256 words / 1024 bytes |
58-
| TinyUSB device | 2 | 384 words / 1536 bytes |
59-
| Application/UI | 1 | 512 words / 2048 bytes |
57+
| I2S processing | 3 | 192 words / 768 bytes |
58+
| TinyUSB device | 2 | 256 words / 1024 bytes |
59+
| Application/UI | 1 | 384 words / 1536 bytes |
6060
| FreeRTOS idle | 0 | 192 words / 768 bytes |
6161

6262
- The firmware and NMSIS DSP library are built with `-fstack-usage`. With
6363
release LTO, the emitted linked call paths measured 320 bytes for I2S, 576
6464
bytes for USB, 1040 bytes for the application, and 128 bytes for idle. The
65-
allocations include the largest
66-
compiler-reported nested interrupt path, the 256-byte RISC-V integer/FPU
67-
switch frame, and extra margin for prebuilt newlib calls not represented in
68-
this target's `.su` records.
65+
allocations include the 256-byte RISC-V integer/FPU switch frame and extra
66+
margin for prebuilt newlib calls not represented in this target's `.su`
67+
records. Returning maskable ISRs use the 2 KiB startup stack as a dedicated
68+
post-scheduler interrupt stack; their assembly wrappers preserve the
69+
caller-saved FPU state before calling ordinary C bodies. The release/LTO
70+
compiler call graph reports 608 bytes for the worst priority-compatible
71+
three-active-interrupt path including all wrapper frames, leaving 1440 bytes
72+
of static margin in that stack.
6973
- The kernel tick is 1 kHz, derived from a free-running 144 MHz CH32 SysTick.
7074
Tickless idle installs an absolute compare but intentionally busy-waits
7175
instead of executing `WFI`. The wait tests `CNT >= deadline` as well as

ch32v305/lib/Ld/Link.ld

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,15 @@ SECTIONS
168168
PROVIDE( _heap_end = ORIGIN(RAM) + LENGTH(RAM) - __stack_size );
169169
PROVIDE( _susrstack = _heap_end );
170170
PROVIDE( _eusrstack = ORIGIN(RAM) + LENGTH(RAM) );
171+
PROVIDE( __freertos_irq_stack_bottom = _susrstack );
171172
PROVIDE( __freertos_irq_stack_top = _eusrstack );
172173

173174
ASSERT( _end <= _heap_end, "RAM overflow: .data/.bss/heap overlap stack" )
175+
ASSERT( (__freertos_irq_stack_bottom & 15) == 0,
176+
"FreeRTOS IRQ stack bottom must be 16-byte aligned" )
177+
ASSERT( (__freertos_irq_stack_top & 15) == 0,
178+
"FreeRTOS IRQ stack top must be 16-byte aligned" )
179+
ASSERT( (__freertos_irq_stack_top - __freertos_irq_stack_bottom) == __stack_size,
180+
"FreeRTOS IRQ stack bounds must cover the startup stack" )
174181

175182
}

ch32v305/src/ch32v30x_it.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
1111
*******************************************************************************/
1212
#include "ch32v30x_it.h"
13+
#include "freertos/port_isr.h"
1314
#include "tusb.h"
1415

1516
void NMI_Handler(void) __attribute__((interrupt));
@@ -44,6 +45,7 @@ void HardFault_Handler(void)
4445
}
4546
}
4647

47-
__attribute__((interrupt)) void USBHS_IRQHandler(void) {
48+
PORT_ISR_BODY(USBHS_IRQHandler)
49+
{
4850
tud_int_handler(0);
4951
}

ch32v305/src/freertos/port_ch32v30x.S

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,111 @@
88

99
.extern vTaskSwitchContext
1010

11+
/*
12+
* A normal C interrupt body is called through one of these wrappers. QingKe
13+
* HWSTK preserves the caller-saved integer registers and interrupt return
14+
* state. The C ABI preserves the callee-saved integer/FPU registers. Save
15+
* the remaining caller-saved FPU state here.
16+
*
17+
* The 96-byte frame is 16-byte aligned and contains:
18+
* 0..76 f0-f7, f10-f17, f28-f31
19+
* 80 fcsr
20+
* 84 interrupted SP when switching from a task stack, otherwise zero
21+
* 88..95 alignment padding
22+
*
23+
* Test the actual SP against the IRQ-stack bounds instead of relying on PFIC
24+
* nesting state. This also handles a higher-priority interrupt arriving at
25+
* any instruction in another wrapper's stack-switch or restore sequence.
26+
* No SP-relative store is performed until the stack choice is complete.
27+
*/
28+
.macro portIRQ_WRAPPER vector, body
29+
.extern \body
30+
.global \vector
31+
.type \vector, @function
32+
.section .text.\vector, "ax", @progbits
33+
.p2align 2
34+
\vector:
35+
la t0, __freertos_irq_stack_bottom
36+
la t1, __freertos_irq_stack_top
37+
mv t2, sp
38+
bltu sp, t0, .Lirq_use_top_\@
39+
bgeu sp, t1, .Lirq_use_top_\@
40+
li t2, 0
41+
j .Lirq_frame_\@
42+
43+
.Lirq_use_top_\@:
44+
mv sp, t1
45+
46+
.Lirq_frame_\@:
47+
addi sp, sp, -96
48+
sw t2, 84(sp)
49+
50+
fsw f0, 0(sp)
51+
fsw f1, 4(sp)
52+
fsw f2, 8(sp)
53+
fsw f3, 12(sp)
54+
fsw f4, 16(sp)
55+
fsw f5, 20(sp)
56+
fsw f6, 24(sp)
57+
fsw f7, 28(sp)
58+
fsw f10, 32(sp)
59+
fsw f11, 36(sp)
60+
fsw f12, 40(sp)
61+
fsw f13, 44(sp)
62+
fsw f14, 48(sp)
63+
fsw f15, 52(sp)
64+
fsw f16, 56(sp)
65+
fsw f17, 60(sp)
66+
fsw f28, 64(sp)
67+
fsw f29, 68(sp)
68+
fsw f30, 72(sp)
69+
fsw f31, 76(sp)
70+
csrr t0, fcsr
71+
sw t0, 80(sp)
72+
73+
call \body
74+
75+
flw f0, 0(sp)
76+
flw f1, 4(sp)
77+
flw f2, 8(sp)
78+
flw f3, 12(sp)
79+
flw f4, 16(sp)
80+
flw f5, 20(sp)
81+
flw f6, 24(sp)
82+
flw f7, 28(sp)
83+
flw f10, 32(sp)
84+
flw f11, 36(sp)
85+
flw f12, 40(sp)
86+
flw f13, 44(sp)
87+
flw f14, 48(sp)
88+
flw f15, 52(sp)
89+
flw f16, 56(sp)
90+
flw f17, 60(sp)
91+
flw f28, 64(sp)
92+
flw f29, 68(sp)
93+
flw f30, 72(sp)
94+
flw f31, 76(sp)
95+
lw t0, 80(sp)
96+
csrw fcsr, t0
97+
98+
lw t2, 84(sp)
99+
addi sp, sp, 96
100+
beqz t2, .Lirq_return_\@
101+
mv sp, t2
102+
103+
.Lirq_return_\@:
104+
mret
105+
.size \vector, .-\vector
106+
.endm
107+
108+
portIRQ_WRAPPER SysTick_Handler, SysTick_Handler_Body
109+
portIRQ_WRAPPER USBHS_IRQHandler, USBHS_IRQHandler_Body
110+
portIRQ_WRAPPER DMA1_Channel4_IRQHandler, DMA1_Channel4_IRQHandler_Body
111+
portIRQ_WRAPPER DMA2_Channel2_IRQHandler, DMA2_Channel2_IRQHandler_Body
112+
portIRQ_WRAPPER EXTI15_10_IRQHandler, EXTI15_10_IRQHandler_Body
113+
portIRQ_WRAPPER TIM10_CC_IRQHandler, TIM10_CC_IRQHandler_Body
114+
portIRQ_WRAPPER TIM6_IRQHandler, TIM6_IRQHandler_Body
115+
11116
.global SW_Handler
12117
.type SW_Handler, @function
13118
.section .text.SW_Handler, "ax", @progbits

ch32v305/src/freertos/port_ch32v30x.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "task.h"
33

44
#include "debug.h"
5+
#include "port_isr.h"
56

67
#include <stdint.h>
78

@@ -59,6 +60,8 @@ static BaseType_t pfic_has_enabled_pending_irq(void)
5960
void vPortYield(void)
6061
{
6162
NVIC_SetPendingIRQ(Software_IRQn);
63+
/* Publish the PFIC MMIO write before execution can continue past a yield. */
64+
__asm volatile("fence iorw, iorw" ::: "memory");
6265
}
6366

6467
void vPortSetupTimerInterrupt(void)
@@ -85,8 +88,7 @@ void vPortSetupTimerInterrupt(void)
8588
CH32_SYSTICK_CTLR_STCLK;
8689
}
8790

88-
void SysTick_Handler(void) __attribute__((interrupt));
89-
void SysTick_Handler(void)
91+
PORT_ISR_BODY(SysTick_Handler)
9092
{
9193
UBaseType_t saved_mie = portSET_INTERRUPT_MASK_FROM_ISR();
9294
BaseType_t switch_required = pdFALSE;

ch32v305/src/freertos/port_isr.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#ifndef HFSDR_FREERTOS_PORT_ISR_H
2+
#define HFSDR_FREERTOS_PORT_ISR_H
3+
4+
/*
5+
* Returning maskable interrupt vectors are assembly wrappers in
6+
* port_ch32v30x.S. Their ordinary C bodies use the platform ABI and return
7+
* with ret; the wrapper owns the dedicated IRQ stack and the final mret.
8+
*
9+
* used and externally_visible keep the body available to the assembly-only
10+
* reference when link-time optimisation is enabled. noinline keeps the
11+
* compiler-emitted stack-usage record attributable to the ISR body.
12+
*/
13+
#define PORT_ISR_BODY(vector_name) \
14+
void vector_name##_Body(void) \
15+
__attribute__((used, noinline, externally_visible)); \
16+
void vector_name##_Body(void)
17+
18+
#endif

ch32v305/src/freertos/task_stacks.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
* Sizes are in 32-bit StackType_t words. The release/LTO -fstack-usage
66
* records give worst linked call paths of 1040 bytes for Application_Task,
77
* 576 bytes for usb_task, and 320 bytes for i2s_task. The allocations below
8-
* also cover the maximum compiler-reported nested interrupt path, the 256-byte
9-
* integer/FPU context-switch frame, and margin for prebuilt newlib routines
10-
* whose stack frames are not present in this target's .su files.
8+
* cover the 256-byte integer/FPU context-switch frame and retain conservative
9+
* margin for prebuilt newlib routines whose stack frames are not present in
10+
* this target's .su files. Returning maskable ISRs use the dedicated 2 KiB
11+
* IRQ stack instead of application task stacks. The remaining measured
12+
* margins are 240 bytes for Application_Task and 192 bytes each for usb_task
13+
* and i2s_task.
1114
*/
12-
#define APP_TASK_STACK_WORDS 512U
13-
#define USB_TASK_STACK_WORDS 384U
14-
#define I2S_TASK_STACK_WORDS 256U
15+
#define APP_TASK_STACK_WORDS 384U
16+
#define USB_TASK_STACK_WORDS 256U
17+
#define I2S_TASK_STACK_WORDS 192U
1518

1619
#endif

ch32v305/src/hw/encoder.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "encoder.h"
22

3+
#include "freertos/port_isr.h"
34
#include "hw/pinout.h"
45
#include "ui/ui.h"
56

@@ -61,7 +62,7 @@ static int16_t encoder_sync_delta(void)
6162
return delta;
6263
}
6364

64-
__attribute__((interrupt)) void EXTI15_10_IRQHandler(void)
65+
PORT_ISR_BODY(EXTI15_10_IRQHandler)
6566
{
6667
if(EXTI_GetITStatus(ENCODER_BUTTON_EXTI_LINE) == RESET)
6768
{
@@ -106,7 +107,7 @@ __attribute__((interrupt)) void EXTI15_10_IRQHandler(void)
106107
}
107108
}
108109

109-
__attribute__((interrupt)) void TIM10_CC_IRQHandler(void)
110+
PORT_ISR_BODY(TIM10_CC_IRQHandler)
110111
{
111112
uint8_t have_cc1 = (uint8_t)(TIM_GetITStatus(ENCODER_TIMER, TIM_IT_CC1) != RESET);
112113
uint8_t have_cc2 = (uint8_t)(TIM_GetITStatus(ENCODER_TIMER, TIM_IT_CC2) != RESET);

ch32v305/src/hw/i2s.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "debug.h"
1010
#include "demod/demod.h"
11+
#include "freertos/port_isr.h"
1112
#include "main.h"
1213
#include "pinout.h"
1314
#include "usb.h"
@@ -56,7 +57,6 @@ typedef enum
5657
int32_t i2s_fft_sample_arr[I2S_HW_COMPLEX_SAMPLE_COUNT * 2];
5758
static volatile uint32_t s_fft_sample_cnt = 0U;
5859

59-
void DMA1_Channel4_IRQHandler(void) __attribute__((interrupt));
6060
extern void audio_usb_mic_write(volatile uint16_t const *src_words, size_t word_count);
6161

6262
static_assert(sizeof(uintptr_t) <= sizeof(uint32_t),
@@ -455,7 +455,7 @@ void i2s_hw_enable(FunctionalState state)
455455
i2s_dma_rx_start();
456456
}
457457

458-
void DMA1_Channel4_IRQHandler(void)
458+
PORT_ISR_BODY(DMA1_Channel4_IRQHandler)
459459
{
460460
BaseType_t higher_priority_task_woken = pdFALSE;
461461

ch32v305/src/hw/si5351.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "si5351.h"
22

3+
#include "freertos/port_isr.h"
34
#include "hw/i2c.h"
45

56
#include "debug.h"
@@ -610,7 +611,7 @@ static void si5351_tim6_handle_finalize_wait(void)
610611
si5351_tim6_finalize_clk1();
611612
}
612613

613-
__attribute__((interrupt)) void TIM6_IRQHandler(void)
614+
PORT_ISR_BODY(TIM6_IRQHandler)
614615
{
615616
if(TIM_GetITStatus(TIM6, TIM_IT_Update) == RESET)
616617
{

0 commit comments

Comments
 (0)