forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
arch: arm: Add support for multiple zero-latency irq priorities
Add the ability to have multiple irq priority levels which are not masked by irq_lock() by adding CONFIG_ZERO_LATENCY_LEVELS. If CONFIG_ZERO_LATENCY_LEVELS is set to a value > 1 then multiple zero latency irqs are reserved by the kernel (and not only one). The priority of the zero-latency interrupt can be configured by IRQ_CONNECT. To be backwards compatible the prio argument in IRQ_CONNECT is still ignored and the target prio set to zero if CONFIG_ZERO_LATENCY_LEVELS is 1 (default). Implements zephyrproject-rtos#45276 Signed-off-by: Christoph Coenen <[email protected]>
- Loading branch information
1 parent
993cad1
commit b3dfc24
Showing
8 changed files
with
265 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
cmake_minimum_required(VERSION 3.20.0) | ||
|
||
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) | ||
project(arm_irq_zero_latency_levels) | ||
|
||
target_sources(app PRIVATE | ||
src/main.c | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
CONFIG_ZTEST=y | ||
CONFIG_DYNAMIC_INTERRUPTS=y | ||
CONFIG_DYNAMIC_DIRECT_INTERRUPTS=y | ||
CONFIG_ZERO_LATENCY_IRQS=y | ||
CONFIG_ZERO_LATENCY_LEVELS=2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
/* | ||
* Copyright (c) 2022 Baumer (www.baumer.com) | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include <ztest.h> | ||
#include <arch/cpu.h> | ||
#include <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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
common: | ||
filter: (CONFIG_ARMV6_M_ARMV8_M_BASELINE or CONFIG_ARMV7_M_ARMV8_M_MAINLINE) and not CONFIG_SOC_FAMILY_NRF | ||
tags: arm interrupt | ||
arch_allow: arm | ||
tests: | ||
arch.arm.irq_zero_latency_levels: | ||
filter: not CONFIG_TRUSTED_EXECUTION_NONSECURE | ||
arch.arm.irq_zero_latency_levels.secure_fw: | ||
filter: CONFIG_TRUSTED_EXECUTION_SECURE |