blob: af4f1107108baa39c7e82419b67cb2555aa2b184 [file] [log] [blame]
/*
* Copyright (c) 2022 Baumer (www.baumer.com)
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/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;
}
ZTEST(arm_irq_zero_latency_levels, test_arm_zero_latency_levels)
{
/*
* 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 */
unsigned 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);
}
ZTEST_SUITE(arm_irq_zero_latency_levels, NULL, NULL, NULL, NULL, NULL);