1 /**
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <stdio.h>
8 #include <stdarg.h>
9 #include "pico/stdlib.h"
10 #include "pico/test.h"
11 #include "pico/time.h"
12 #include "hardware/irq.h"
13 #include "hardware/dma.h"
14 #include "hardware/structs/scb.h"
15
16 PICOTEST_MODULE_NAME("IRQ", "IRQ Handler test");
17
18 extern void __unhandled_user_irq(void);
19
20 static bool remove_handler1_in_dma;
21 static bool remove_handler2_in_dma;
22 static bool remove_handler3_in_dma;
23 static dma_channel_config config;
24
25 #define MAX_FIRE_COUNT 4
26 static int fire_count;
27 static int fired[MAX_FIRE_COUNT];
28
29 static uint32_t dma_to = 0;
30 static uint32_t dma_from = 0xaaaa5555;
31
record_fire(int which)32 int record_fire(int which) {
33 PICOTEST_CHECK(fire_count < MAX_FIRE_COUNT, "too many firings");
34 fired[fire_count++] = which;
35 return 0;
36 }
37
handler1(void)38 void __isr handler1(void) {
39 record_fire(1);
40 dma_channel_acknowledge_irq0(0);
41 if (remove_handler1_in_dma) {
42 irq_remove_handler(DMA_IRQ_0, handler1);
43 remove_handler1_in_dma = false;
44 }
45 }
46
handler2(void)47 void __isr handler2(void) {
48 record_fire(2);
49 dma_channel_acknowledge_irq0(0);
50 if (remove_handler2_in_dma) {
51 irq_remove_handler(DMA_IRQ_0, handler2);
52 remove_handler2_in_dma = false;
53 }
54 }
55
handler3(void)56 void __isr handler3(void) {
57 record_fire(3);
58 dma_channel_acknowledge_irq0(0);
59 if (remove_handler3_in_dma) {
60 irq_remove_handler(DMA_IRQ_0, handler3);
61 remove_handler3_in_dma = false;
62 }
63 }
64
get_vtable(void)65 static inline irq_handler_t *get_vtable(void) {
66 return (irq_handler_t *) scb_hw->vtor;
67 }
68
dma_check(int expected,...)69 int dma_check(int expected, ...) {
70 if (expected == 0) {
71 // doing the DMA if there are no IRQ handlers will cause a hard fault, so we just check we are pointing at the handler which does this.
72 PICOTEST_CHECK_AND_ABORT(get_vtable()[16 + DMA_IRQ_0] == __unhandled_user_irq, "Expected there to be no IRQ handlers");
73 return 0;
74 }
75 fire_count = 0;
76 dma_channel_configure(0, &config, &dma_to, &dma_from, 1, true);
77 sleep_ms(100);
78 va_list args;
79 va_start(args, expected);
80 bool ok = expected == fire_count;
81 for(int i=0;ok && i<expected;i++) {
82 if (fired[i] != va_arg(args, int)) {
83 ok = false;
84 break;
85 }
86 }
87 va_end(args);
88 if (!ok) {
89 PICOTEST_CHECK(ok, "DMA handlers were not called in the order expected");
90 printf(" EXPECTED handlers: ");
91 va_start(args, expected);
92 for(int i=0;i<expected;i++) {
93 if (i) printf(", ");
94 printf("%d", va_arg(args, int));
95 }
96 printf("\n");
97 va_end(args);
98 printf(" CALLED handlers: ");
99 for(int i=0;i<fire_count;i++) {
100 if (i) printf(", ");
101 printf("%d", fired[i]);
102 }
103 printf("\n");
104 return -1;
105 }
106 return 0;
107 }
108
main()109 int main() {
110 stdio_init_all();
111
112 PICOTEST_START();
113 config = dma_channel_get_default_config(0);
114 dma_channel_set_irq0_enabled(0, true);
115 irq_set_enabled(DMA_IRQ_0, true);
116
117 // part I, just add/remove two handlers
118 PICOTEST_START_SECTION("I: Add handler1 default priority");
119 irq_add_shared_handler(DMA_IRQ_0, handler1, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
120 dma_check(1, 1);
121 PICOTEST_END_SECTION();
122
123 PICOTEST_START_SECTION("I: Add handler2 default priority");
124 irq_add_shared_handler(DMA_IRQ_0, handler2, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
125 dma_check(2, 2, 1);
126 PICOTEST_END_SECTION();
127
128 PICOTEST_START_SECTION("I: Remove handler1");
129 irq_remove_handler(DMA_IRQ_0, handler1);
130 dma_check(1, 2);
131 PICOTEST_END_SECTION();
132
133 PICOTEST_START_SECTION("I: Remove handler2");
134 irq_remove_handler(DMA_IRQ_0, handler2);
135 dma_check(0);
136 PICOTEST_END_SECTION();
137
138 // part II, add/remove three handlers including one in the middle
139
140 PICOTEST_START_SECTION("II: Add handler3 default priority");
141 irq_add_shared_handler(DMA_IRQ_0, handler3, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
142 dma_check(1, 3);
143 PICOTEST_END_SECTION();
144
145 PICOTEST_START_SECTION("II: Add handler2 default priority");
146 irq_add_shared_handler(DMA_IRQ_0, handler2, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
147 dma_check(2, 2, 3);
148 PICOTEST_END_SECTION();
149
150 PICOTEST_START_SECTION("II: Add handler1 default priority");
151 irq_add_shared_handler(DMA_IRQ_0, handler1, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
152 dma_check(3, 1, 2, 3);
153 PICOTEST_END_SECTION();
154
155 PICOTEST_START_SECTION("II: Remove handler2");
156 irq_remove_handler(DMA_IRQ_0, handler2);
157 dma_check(2, 1, 3);
158 PICOTEST_END_SECTION();
159
160 PICOTEST_START_SECTION("II: Remove handler3");
161 irq_remove_handler(DMA_IRQ_0, handler3);
162 dma_check(1, 1);
163 PICOTEST_END_SECTION();
164
165 PICOTEST_START_SECTION("II: Remove handler1");
166 irq_remove_handler(DMA_IRQ_0, handler1);
167 dma_check(0);
168 PICOTEST_END_SECTION();
169
170 // part III, the same as part II, but removing the handlers during the IRQ
171
172 PICOTEST_START_SECTION("III: Add handler3 default priority");
173 irq_add_shared_handler(DMA_IRQ_0, handler3, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
174 dma_check(1, 3);
175 PICOTEST_END_SECTION();
176
177 PICOTEST_START_SECTION("III: Add handler2 default priority");
178 irq_add_shared_handler(DMA_IRQ_0, handler2, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
179 dma_check(2, 2, 3);
180 PICOTEST_END_SECTION();
181
182 PICOTEST_START_SECTION("III: Add handler1 default priority");
183 irq_add_shared_handler(DMA_IRQ_0, handler1, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
184 dma_check(3, 1, 2, 3);
185 PICOTEST_END_SECTION();
186
187 PICOTEST_START_SECTION("III: Remove handler2");
188 remove_handler2_in_dma = true;
189 // note that this is the defined behavior, that any handlers after the removed handler are not called. the
190 // reasoning is that the same IRQ will immediately be re-entered, as we have missed clearing an interrupt (in whichever
191 // handlers were not called), and we will call any remaining handlers then. Because each IRQ handler is clearing the same
192 // interrupt handler in our case, this does not happen.
193 dma_check(2, 1, 2);
194 // but we call again to check
195 dma_check(2, 1, 3);
196 PICOTEST_END_SECTION();
197
198 PICOTEST_START_SECTION("III: Remove handler3");
199 remove_handler3_in_dma = true;
200 // note that this is the defined behavior, that any handlers after the removed handler are not called. the
201 // reasoning is that the same IRQ will immediately be re-entered, as we have missed clearing an interrupt (in whichever
202 // handlers were not called), and we will call any remaining handlers then. Because each IRQ handler is clearing the same
203 // interrupt handler in our case, this does not happen.
204 dma_check(2, 1, 3);
205 // but we call again to check
206 dma_check(1, 1);
207 PICOTEST_END_SECTION();
208
209 PICOTEST_START_SECTION("III: Remove handler3");
210 remove_handler1_in_dma = true;
211 // note that this is the defined behavior, that any handlers after the removed handler are not called. the
212 // reasoning is that the same IRQ will immediately be re-entered, as we have missed clearing an interrupt (in whichever
213 // handlers were not called), and we will call any remaining handlers then. Because each IRQ handler is clearing the same
214 // interrupt handler in our case, this does not happen.
215 dma_check(1, 1);
216 // but we call again to check
217 dma_check(0);
218 PICOTEST_END_SECTION();
219
220 // part IV, checking priorities
221 PICOTEST_START_SECTION("IV: Add handler1 high priority");
222 irq_add_shared_handler(DMA_IRQ_0, handler1, PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY);
223 dma_check(1, 1);
224 PICOTEST_END_SECTION();
225
226 PICOTEST_START_SECTION("IV: Add handler2 normal priority");
227 irq_add_shared_handler(DMA_IRQ_0, handler2, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
228 dma_check(2, 1, 2);
229 PICOTEST_END_SECTION();
230
231 PICOTEST_START_SECTION("IV: Add handler3 lowest priority");
232 irq_add_shared_handler(DMA_IRQ_0, handler3, PICO_SHARED_IRQ_HANDLER_LOWEST_ORDER_PRIORITY);
233 dma_check(3, 1, 2, 3);
234 PICOTEST_END_SECTION();
235
236 PICOTEST_START_SECTION("IV: Remove handler3");
237 irq_remove_handler(DMA_IRQ_0, handler3);
238 dma_check(2, 1, 2);
239 PICOTEST_END_SECTION();
240
241 PICOTEST_START_SECTION("IV: Add handler3 normal priority");
242 irq_add_shared_handler(DMA_IRQ_0, handler3, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
243 dma_check(3, 1, 3, 2);
244 PICOTEST_END_SECTION();
245
246 PICOTEST_START_SECTION("IV: Remove handler2");
247 irq_remove_handler(DMA_IRQ_0, handler2);
248 dma_check(2, 1, 3);
249 PICOTEST_END_SECTION();
250
251 PICOTEST_START_SECTION("IV: Add handler2 normal priority - 2");
252 irq_add_shared_handler(DMA_IRQ_0, handler2, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY - 2);
253 dma_check(3, 1, 3, 2);
254 PICOTEST_END_SECTION();
255
256 PICOTEST_START_SECTION("IV: Remove handler1");
257 irq_remove_handler(DMA_IRQ_0, handler1);
258 dma_check(2, 3, 2);
259 PICOTEST_END_SECTION();
260
261 PICOTEST_START_SECTION("IV: Add handler1 normal priority - 1");
262 irq_add_shared_handler(DMA_IRQ_0, handler1, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY - 1);
263 dma_check(3, 3, 1, 2);
264 PICOTEST_END_SECTION();
265
266 PICOTEST_START_SECTION("IV: Remove handler2 again");
267 irq_remove_handler(DMA_IRQ_0, handler2);
268 dma_check(2, 3, 1);
269
270 PICOTEST_END_SECTION();
271 PICOTEST_START_SECTION("IV: Remove handler3 again");
272 irq_remove_handler(DMA_IRQ_0, handler3);
273 dma_check(1, 1);
274 PICOTEST_END_SECTION();
275
276 PICOTEST_START_SECTION("IV: Remove handler1 again");
277 irq_remove_handler(DMA_IRQ_0, handler1);
278 dma_check(0);
279 PICOTEST_END_SECTION();
280 PICOTEST_END_TEST();
281 }
282
283