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