hardware_irq_test.c 9.9 KB

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