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