Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | /* * Copyright (c) 2022 Baumer (www.baumer.com) * * SPDX-License-Identifier: Apache-2.0 */ #include <ztest.h> #include <zephyr/arch/cpu.h> #include <zephyr/arch/arm/aarch32/cortex_m/cmsis.h> #define EXECUTION_TRACE_LENGTH 6 #define IRQ_A_PRIO 1 /* lower priority */ #define IRQ_B_PRIO 0 /* higher priority */ #define CHECK_STEP(pos, val) zassert_equal( \ execution_trace[pos], \ val, \ "Expected %s for step %d but got %s", \ execution_step_str(val), \ pos, \ execution_step_str(execution_trace[pos])) enum execution_step { STEP_MAIN_BEGIN, STEP_MAIN_END, STEP_ISR_A_BEGIN, STEP_ISR_A_END, STEP_ISR_B_BEGIN, STEP_ISR_B_END, }; static volatile enum execution_step execution_trace[EXECUTION_TRACE_LENGTH]; static volatile int execution_trace_pos; static int irq_a; static int irq_b; static const char *execution_step_str(enum execution_step s) { const char *res = "invalid"; switch (s) { case STEP_MAIN_BEGIN: res = "STEP_MAIN_BEGIN"; break; case STEP_MAIN_END: res = "STEP_MAIN_END"; break; case STEP_ISR_A_BEGIN: res = "STEP_ISR_A_BEGIN"; break; case STEP_ISR_A_END: res = "STEP_ISR_A_END"; break; case STEP_ISR_B_BEGIN: res = "STEP_ISR_B_BEGIN"; break; case STEP_ISR_B_END: res = "STEP_ISR_B_END"; break; default: break; } return res; } static void execution_trace_add(enum execution_step s) { __ASSERT(execution_trace_pos < EXECUTION_TRACE_LENGTH, "Execution trace overflow"); execution_trace[execution_trace_pos] = s; execution_trace_pos++; } void isr_a_handler(const void *args) { ARG_UNUSED(args); execution_trace_add(STEP_ISR_A_BEGIN); /* Set higher prior irq b pending */ NVIC_SetPendingIRQ(irq_b); __DSB(); __ISB(); execution_trace_add(STEP_ISR_A_END); } void isr_b_handler(const void *args) { ARG_UNUSED(args); execution_trace_add(STEP_ISR_B_BEGIN); execution_trace_add(STEP_ISR_B_END); } static int find_unused_irq(int start) { int i; for (i = start - 1; i >= 0; i--) { if (NVIC_GetEnableIRQ(i) == 0) { /* * Interrupts configured statically with IRQ_CONNECT(.) * are automatically enabled. NVIC_GetEnableIRQ() * returning false, here, implies that the IRQ line is * either not implemented or it is not enabled, thus, * currently not in use by Zephyr. */ /* Set the NVIC line to pending. */ NVIC_SetPendingIRQ(i); if (NVIC_GetPendingIRQ(i)) { /* * If the NVIC line is pending, it is * guaranteed that it is implemented; clear the * line. */ NVIC_ClearPendingIRQ(i); if (!NVIC_GetPendingIRQ(i)) { /* * If the NVIC line can be successfully * un-pended, it is guaranteed that it * can be used for software interrupt * triggering. Return the NVIC line * number. */ break; } } } } zassert_true(i >= 0, "No available IRQ line to configure as zero-latency\n"); TC_PRINT("Available IRQ line: %u\n", i); return i; } void test_arm_zero_latency_levels(void) { /* * Confirm that a zero-latency interrupt with lower priority will be * interrupted by a zero-latency interrupt with higher priority. */ if (!IS_ENABLED(CONFIG_ZERO_LATENCY_IRQS)) { TC_PRINT("Skipped (Cortex-M Mainline only)\n"); return; } /* Determine two NVIC IRQ lines that are not currently in use. */ irq_a = find_unused_irq(CONFIG_NUM_IRQS); irq_b = find_unused_irq(irq_a); /* Configure IRQ A as zero-latency interrupt with prio 1 */ arch_irq_connect_dynamic(irq_a, IRQ_A_PRIO, isr_a_handler, NULL, IRQ_ZERO_LATENCY); NVIC_ClearPendingIRQ(irq_a); NVIC_EnableIRQ(irq_a); /* Configure irq_b as zero-latency interrupt with prio 0 */ arch_irq_connect_dynamic(irq_b, IRQ_B_PRIO, isr_b_handler, NULL, IRQ_ZERO_LATENCY); NVIC_ClearPendingIRQ(irq_b); NVIC_EnableIRQ(irq_b); /* Lock interrupts */ int key = irq_lock(); execution_trace_add(STEP_MAIN_BEGIN); /* Trigger irq_a */ NVIC_SetPendingIRQ(irq_a); __DSB(); __ISB(); execution_trace_add(STEP_MAIN_END); /* Confirm that irq_a interrupted main and irq_b interrupted irq_a */ CHECK_STEP(0, STEP_MAIN_BEGIN); CHECK_STEP(1, STEP_ISR_A_BEGIN); CHECK_STEP(2, STEP_ISR_B_BEGIN); CHECK_STEP(3, STEP_ISR_B_END); CHECK_STEP(4, STEP_ISR_A_END); CHECK_STEP(5, STEP_MAIN_END); /* Unlock interrupts */ irq_unlock(key); } void test_main(void) { ztest_test_suite(arm_irq_zero_latency_levels, ztest_unit_test(test_arm_zero_latency_levels)); ztest_run_test_suite(arm_irq_zero_latency_levels); } |