Skip to content

Commit 4cee975

Browse files
authored
Merge pull request #4980 from willmmiles/0_15_x_rmthi
(0.15) RMT High-priority Interrupt driver backport
2 parents a50f12d + 3e80c2c commit 4cee975

File tree

6 files changed

+1271
-13
lines changed

6 files changed

+1271
-13
lines changed

lib/NeoESP32RmtHI/include/NeoEsp32RmtHIMethod.h

Lines changed: 469 additions & 0 deletions
Large diffs are not rendered by default.

lib/NeoESP32RmtHI/library.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "NeoESP32RmtHI",
3+
"build": { "libArchive": false },
4+
"platforms": ["espressif32"],
5+
"dependencies": [
6+
{
7+
"owner": "makuna",
8+
"name": "NeoPixelBus",
9+
"version": "^2.8.3"
10+
}
11+
]
12+
}
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
/* RMT ISR shim
2+
* Bridges from a high-level interrupt to the C++ code.
3+
*
4+
* This code is largely derived from Espressif's 'hli_vector.S' Bluetooth ISR.
5+
*
6+
*/
7+
8+
#if defined(__XTENSA__) && defined(ESP32) && !defined(CONFIG_BTDM_CTRL_HLI)
9+
10+
#include <freertos/xtensa_context.h>
11+
#include "sdkconfig.h"
12+
#include "soc/soc.h"
13+
14+
/* If the Bluetooth driver has hooked the high-priority interrupt, we piggyback on it and don't need this. */
15+
#ifndef CONFIG_BTDM_CTRL_HLI
16+
17+
/*
18+
Select interrupt based on system check level
19+
- Base ESP32: could be 4 or 5, depends on platform config
20+
- S2: 5
21+
- S3: 5
22+
*/
23+
24+
#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
25+
/* Use level 4 */
26+
#define RFI_X 4
27+
#define xt_highintx xt_highint4
28+
#else /* !CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
29+
/* Use level 5 */
30+
#define RFI_X 5
31+
#define xt_highintx xt_highint5
32+
#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
33+
34+
// Register map, based on interrupt level
35+
#define EPC_X (EPC + RFI_X)
36+
#define EXCSAVE_X (EXCSAVE + RFI_X)
37+
38+
// The sp mnemonic is used all over in ESP's assembly, though I'm not sure where it's expected to be defined?
39+
#define sp a1
40+
41+
/* Interrupt stack size, for C code. */
42+
#define RMT_INTR_STACK_SIZE 512
43+
44+
/* Save area for the CPU state:
45+
* - 64 words for the general purpose registers
46+
* - 7 words for some of the special registers:
47+
* - WINDOWBASE, WINDOWSTART — only WINDOWSTART is truly needed
48+
* - SAR, LBEG, LEND, LCOUNT — since the C code might use these
49+
* - EPC1 — since the C code might cause window overflow exceptions
50+
* This is not laid out as standard exception frame structure
51+
* for simplicity of the save/restore code.
52+
*/
53+
#define REG_FILE_SIZE (64 * 4)
54+
#define SPECREG_OFFSET REG_FILE_SIZE
55+
#define SPECREG_SIZE (7 * 4)
56+
#define REG_SAVE_AREA_SIZE (SPECREG_OFFSET + SPECREG_SIZE)
57+
58+
.data
59+
_rmt_intr_stack:
60+
.space RMT_INTR_STACK_SIZE
61+
_rmt_save_ctx:
62+
.space REG_SAVE_AREA_SIZE
63+
64+
.section .iram1,"ax"
65+
.global xt_highintx
66+
.type xt_highintx,@function
67+
.align 4
68+
69+
xt_highintx:
70+
71+
movi a0, _rmt_save_ctx
72+
/* save 4 lower registers */
73+
s32i a1, a0, 4
74+
s32i a2, a0, 8
75+
s32i a3, a0, 12
76+
rsr a2, EXCSAVE_X /* holds the value of a0 */
77+
s32i a2, a0, 0
78+
79+
/* Save special registers */
80+
addi a0, a0, SPECREG_OFFSET
81+
rsr a2, WINDOWBASE
82+
s32i a2, a0, 0
83+
rsr a2, WINDOWSTART
84+
s32i a2, a0, 4
85+
rsr a2, SAR
86+
s32i a2, a0, 8
87+
#if XCHAL_HAVE_LOOPS
88+
rsr a2, LBEG
89+
s32i a2, a0, 12
90+
rsr a2, LEND
91+
s32i a2, a0, 16
92+
rsr a2, LCOUNT
93+
s32i a2, a0, 20
94+
#endif
95+
rsr a2, EPC1
96+
s32i a2, a0, 24
97+
98+
/* disable exception mode, window overflow */
99+
movi a0, PS_INTLEVEL(RFI_X+1) | PS_EXCM
100+
wsr a0, PS
101+
rsync
102+
103+
/* Save the remaining physical registers.
104+
* 4 registers are already saved, which leaves 60 registers to save.
105+
* (FIXME: consider the case when the CPU is configured with physical 32 registers)
106+
* These 60 registers are saved in 5 iterations, 12 registers at a time.
107+
*/
108+
movi a1, 5
109+
movi a3, _rmt_save_ctx + 4 * 4
110+
111+
/* This is repeated 5 times, each time the window is shifted by 12 registers.
112+
* We come here with a1 = downcounter, a3 = save pointer, a2 and a0 unused.
113+
*/
114+
1:
115+
s32i a4, a3, 0
116+
s32i a5, a3, 4
117+
s32i a6, a3, 8
118+
s32i a7, a3, 12
119+
s32i a8, a3, 16
120+
s32i a9, a3, 20
121+
s32i a10, a3, 24
122+
s32i a11, a3, 28
123+
s32i a12, a3, 32
124+
s32i a13, a3, 36
125+
s32i a14, a3, 40
126+
s32i a15, a3, 44
127+
128+
/* We are about to rotate the window, so that a12-a15 will become the new a0-a3.
129+
* Copy a0-a3 to a12-15 to still have access to these values.
130+
* At the same time we can decrement the counter and adjust the save area pointer
131+
*/
132+
133+
/* a0 is constant (_rmt_save_ctx), no need to copy */
134+
addi a13, a1, -1 /* copy and decrement the downcounter */
135+
/* a2 is scratch so no need to copy */
136+
addi a15, a3, 48 /* copy and adjust the save area pointer */
137+
beqz a13, 2f /* have saved all registers ? */
138+
rotw 3 /* rotate the window and go back */
139+
j 1b
140+
141+
/* the loop is complete */
142+
2:
143+
rotw 4 /* this brings us back to the original window */
144+
/* a0 still points to _rmt_save_ctx */
145+
146+
/* Can clear WINDOWSTART now, all registers are saved */
147+
rsr a2, WINDOWBASE
148+
/* WINDOWSTART = (1 << WINDOWBASE) */
149+
movi a3, 1
150+
ssl a2
151+
sll a3, a3
152+
wsr a3, WINDOWSTART
153+
154+
_highint_stack_switch:
155+
movi a0, 0
156+
movi sp, _rmt_intr_stack + RMT_INTR_STACK_SIZE - 16
157+
s32e a0, sp, -12 /* For GDB: set null SP */
158+
s32e a0, sp, -16 /* For GDB: set null PC */
159+
movi a0, _highint_stack_switch /* For GDB: cosmetics, for the frame where stack switch happened */
160+
161+
/* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */
162+
movi a6, PS_INTLEVEL(RFI_X) | PS_UM | PS_WOE
163+
wsr a6, PS
164+
rsync
165+
166+
/* Call C handler */
167+
mov a6, sp
168+
call4 NeoEsp32RmtMethodIsr
169+
170+
l32e sp, sp, -12 /* switch back to the original stack */
171+
172+
/* Done with C handler; re-enable exception mode, disabling window overflow */
173+
movi a2, PS_INTLEVEL(RFI_X+1) | PS_EXCM /* TOCHECK */
174+
wsr a2, PS
175+
rsync
176+
177+
/* Restore the special registers.
178+
* WINDOWSTART will be restored near the end.
179+
*/
180+
movi a0, _rmt_save_ctx + SPECREG_OFFSET
181+
l32i a2, a0, 8
182+
wsr a2, SAR
183+
#if XCHAL_HAVE_LOOPS
184+
l32i a2, a0, 12
185+
wsr a2, LBEG
186+
l32i a2, a0, 16
187+
wsr a2, LEND
188+
l32i a2, a0, 20
189+
wsr a2, LCOUNT
190+
#endif
191+
l32i a2, a0, 24
192+
wsr a2, EPC1
193+
194+
/* Restoring the physical registers.
195+
* This is the reverse to the saving process above.
196+
*/
197+
198+
/* Rotate back to the final window, then start loading 12 registers at a time,
199+
* in 5 iterations.
200+
* Again, a1 is the downcounter and a3 is the save area pointer.
201+
* After each rotation, a1 and a3 are copied from a13 and a15.
202+
* To simplify the loop, we put the initial values into a13 and a15.
203+
*/
204+
rotw -4
205+
movi a15, _rmt_save_ctx + 64 * 4 /* point to the end of the save area */
206+
movi a13, 5
207+
208+
1:
209+
/* Copy a1 and a3 from their previous location,
210+
* at the same time decrementing and adjusting the save area pointer.
211+
*/
212+
addi a1, a13, -1
213+
addi a3, a15, -48
214+
215+
/* Load 12 registers */
216+
l32i a4, a3, 0
217+
l32i a5, a3, 4
218+
l32i a6, a3, 8
219+
l32i a7, a3, 12
220+
l32i a8, a3, 16
221+
l32i a9, a3, 20
222+
l32i a10, a3, 24
223+
l32i a11, a3, 28 /* ensure PS and EPC written */
224+
l32i a12, a3, 32
225+
l32i a13, a3, 36
226+
l32i a14, a3, 40
227+
l32i a15, a3, 44
228+
229+
/* Done with the loop? */
230+
beqz a1, 2f
231+
/* If no, rotate the window and repeat */
232+
rotw -3
233+
j 1b
234+
235+
2:
236+
/* Done with the loop. Only 4 registers (a0-a3 in the original window) remain
237+
* to be restored. Also need to restore WINDOWSTART, since all the general
238+
* registers are now in place.
239+
*/
240+
movi a0, _rmt_save_ctx
241+
242+
l32i a2, a0, SPECREG_OFFSET + 4
243+
wsr a2, WINDOWSTART
244+
245+
l32i a1, a0, 4
246+
l32i a2, a0, 8
247+
l32i a3, a0, 12
248+
rsr a0, EXCSAVE_X /* holds the value of a0 before the interrupt handler */
249+
250+
/* Return from the interrupt, restoring PS from EPS_X */
251+
rfi RFI_X
252+
253+
254+
/* The linker has no reason to link in this file; all symbols it exports are already defined
255+
(weakly!) in the default int handler. Define a symbol here so we can use it to have the
256+
linker inspect this anyway. */
257+
258+
.global ld_include_hli_vectors_rmt
259+
ld_include_hli_vectors_rmt:
260+
261+
262+
#endif // CONFIG_BTDM_CTRL_HLI
263+
#endif // XTensa

0 commit comments

Comments
 (0)