-
Notifications
You must be signed in to change notification settings - Fork 6.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tests: kernel: interrupt: Add testcase for shared interrupts
This commit introduces a new testcase for shared interrupts. Signed-off-by: Laurentiu Mihalcea <[email protected]>
- Loading branch information
1 parent
edd7097
commit 2ce3477
Showing
5 changed files
with
410 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
/* | ||
* Copyright 2023 NXP | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include "test_shared_irq.h" | ||
|
||
struct shared_irq_fixture { | ||
unsigned int irq1; | ||
unsigned int irq2; | ||
unsigned int irq1_table_idx; | ||
unsigned int irq2_table_idx; | ||
unsigned int irq_priority; | ||
}; | ||
|
||
static struct shared_irq_fixture fixture; | ||
|
||
static void reset_test_vector(void) | ||
{ | ||
int i; | ||
|
||
for (i = 0; i < TEST_VECTOR_SIZE; i++) { | ||
test_vector[i] = 0; | ||
} | ||
} | ||
|
||
static void dynamic_shared_irq_suite_after(void *data) | ||
{ | ||
ARG_UNUSED(data); | ||
|
||
/* note: no need to check the state of the SW ISR tables after | ||
* all these disconnect operations. If there's something wrong | ||
* it should be detected by dynamic_shared_irq_suite_before(). | ||
*/ | ||
arch_irq_disconnect_dynamic(fixture.irq1, fixture.irq_priority, | ||
test_isr_0, 0, 0); | ||
arch_irq_disconnect_dynamic(fixture.irq1, fixture.irq_priority, | ||
test_isr_1, (void *)1, 0); | ||
arch_irq_disconnect_dynamic(fixture.irq2, fixture.irq_priority, | ||
test_isr_2, (void *)2, 0); | ||
} | ||
|
||
static void dummy_isr(const void *data) | ||
{ | ||
ARG_UNUSED(data); | ||
|
||
test_vector[0] = TEST_DUMMY_ISR_VAL; | ||
} | ||
|
||
static unsigned int get_irq_slot(unsigned int start) | ||
{ | ||
unsigned int i, table_idx; | ||
|
||
for (i = start; i <= CONFIG_GEN_IRQ_START_VECTOR + CONFIG_NUM_IRQS - 1; i++) { | ||
table_idx = i - CONFIG_GEN_IRQ_START_VECTOR; | ||
|
||
if (_sw_isr_table[table_idx].isr == &z_irq_spurious) { | ||
test_vector[0] = 0; | ||
|
||
/* check to see if we can trigger this IRQ */ | ||
arch_irq_connect_dynamic(i, IRQ_PRIORITY, dummy_isr, | ||
NULL, 0); | ||
irq_enable(i); | ||
trigger_irq(i); | ||
|
||
/* wait a bit */ | ||
k_busy_wait(100); | ||
|
||
if (test_vector[0] == TEST_DUMMY_ISR_VAL) { | ||
/* found a valid INTID */ | ||
irq_disable(i); | ||
|
||
arch_irq_disconnect_dynamic(i, IRQ_PRIORITY, | ||
dummy_isr, NULL, 0); | ||
return i; | ||
} | ||
} | ||
} | ||
|
||
return TEST_INVALID_IRQ; | ||
} | ||
|
||
static void *dynamic_shared_irq_suite_setup(void) | ||
{ | ||
fixture.irq1 = get_irq_slot(CONFIG_GEN_IRQ_START_VECTOR); | ||
zassert_true(fixture.irq1 != TEST_INVALID_IRQ, | ||
"no suitable value found for irq1"); | ||
fixture.irq2 = get_irq_slot(fixture.irq1 + 1); | ||
zassert_true(fixture.irq2 != TEST_INVALID_IRQ, | ||
"no suitable value found for irq2"); | ||
fixture.irq_priority = IRQ_PRIORITY; | ||
|
||
fixture.irq1_table_idx = fixture.irq1 - CONFIG_GEN_IRQ_START_VECTOR; | ||
fixture.irq2_table_idx = fixture.irq2 - CONFIG_GEN_IRQ_START_VECTOR; | ||
|
||
return NULL; | ||
} | ||
|
||
static void dynamic_shared_irq_suite_before(void *data) | ||
{ | ||
ARG_UNUSED(data); | ||
|
||
arch_irq_connect_dynamic(fixture.irq1, fixture.irq_priority, | ||
test_isr_0, 0, 0); | ||
|
||
zassert_true(_sw_isr_table[fixture.irq1_table_idx].isr == test_isr_0, | ||
"wrong _sw_isr_table ISR at irq1"); | ||
zassert_true(!_sw_isr_table[fixture.irq1_table_idx].arg, | ||
"wrong _sw_isr_table argument at irq1"); | ||
zassert_true(!_shared_sw_isr_table[fixture.irq1_table_idx].client_num, | ||
"wrong client number at irq1"); | ||
|
||
arch_irq_connect_dynamic(fixture.irq1, fixture.irq_priority, | ||
test_isr_1, (void *)1, 0); | ||
|
||
zassert_true(_sw_isr_table[fixture.irq1_table_idx].isr == shared_isr, | ||
"wrong _sw_isr_table ISR at irq1"); | ||
zassert_true(_sw_isr_table[fixture.irq1_table_idx].arg == | ||
&_shared_sw_isr_table[fixture.irq1_table_idx], | ||
"wrong _sw_isr_table argument at irq1"); | ||
zassert_true(_shared_sw_isr_table[fixture.irq1_table_idx].client_num == 2, | ||
"wrong client number at irq1"); | ||
|
||
zassert_true(client_exists_at_index(test_isr_0, 0, fixture.irq1_table_idx, 0), | ||
"unexpected client data for irq1, index 0"); | ||
zassert_true(client_exists_at_index(test_isr_1, (void *)1, fixture.irq1_table_idx, 1), | ||
"unexpected client data for irq1, index 1"); | ||
|
||
arch_irq_connect_dynamic(fixture.irq2, fixture.irq_priority, | ||
test_isr_2, (void *)2, 0); | ||
|
||
zassert_true(_sw_isr_table[fixture.irq2_table_idx].isr == test_isr_2, | ||
"wrong _sw_isr_table ISR at irq2"); | ||
zassert_true(_sw_isr_table[fixture.irq2_table_idx].arg == (void *)2, | ||
"wrong _sw_isr_table argument at irq2"); | ||
zassert_true(!_shared_sw_isr_table[fixture.irq2_table_idx].client_num, | ||
"wrong client number at irq2"); | ||
|
||
reset_test_vector(); | ||
} | ||
|
||
/** | ||
* @brief Test writing to a vector with a shared interrupt | ||
* | ||
* @ingroup kernel_interrupt_tests | ||
* | ||
* @details This tests if interrupts are dynamically shared successfully | ||
* (i.e: multiple ISR/arg pairs are called whenever the interrupt | ||
* they were registered for is triggered). | ||
*/ | ||
ZTEST(shared_irq_feature, test_dynamic_shared_irq_write) | ||
{ | ||
int i; | ||
|
||
irq_enable(fixture.irq1); | ||
irq_enable(fixture.irq2); | ||
|
||
trigger_irq(fixture.irq1); | ||
trigger_irq(fixture.irq2); | ||
|
||
/* wait 5ms before checking the results */ | ||
k_busy_wait(5000); | ||
|
||
for (i = 0; i < TEST_VECTOR_SIZE; i++) { | ||
zassert_true(test_vector[i] == result_vector[i], | ||
"wrong test_vector value at %d: 0x%x vs 0x%x", | ||
i, test_vector[i], result_vector[i]); | ||
} | ||
|
||
irq_disable(fixture.irq1); | ||
irq_disable(fixture.irq2); | ||
} | ||
|
||
/** | ||
* @brief Test writing to a vector after an ISR/arg disconnect. | ||
* | ||
* @ingroup kernel_interrupt_tests | ||
* | ||
* @details This tests if ISR/arg pairs are disconnected successfully | ||
* and the interrupts are "unshared" whenever a single ISR/arg pair is | ||
* left. | ||
*/ | ||
ZTEST(shared_irq_feature, test_dynamic_shared_irq_disconnect_write) | ||
{ | ||
int i; | ||
|
||
/* remove test_isr_0/NULL pair. After this statement we expect | ||
* irq1 to be unshared. | ||
*/ | ||
arch_irq_disconnect_dynamic(fixture.irq1, fixture.irq_priority, | ||
test_isr_0, 0, 0); | ||
|
||
zassert_true(_sw_isr_table[fixture.irq1_table_idx].isr == test_isr_1, | ||
"wrong _sw_isr_table ISR at irq1"); | ||
zassert_true(_sw_isr_table[fixture.irq1_table_idx].arg == (void *)1, | ||
"wrong _sw_isr_table arg at irq1"); | ||
zassert_true(!_shared_sw_isr_table[fixture.irq1_table_idx].client_num, | ||
"wrong client number at irq1"); | ||
|
||
irq_enable(fixture.irq1); | ||
trigger_irq(fixture.irq1); | ||
|
||
/* wait 5ms before checking the results */ | ||
k_busy_wait(5000); | ||
|
||
for (i = 0; i < TEST_VECTOR_SIZE; i++) { | ||
if (i == 1) { | ||
zassert_true(test_vector[i] == result_vector[i], | ||
"wrong test_vector at %d: 0x%x vs 0x%x", | ||
i, test_vector[i], result_vector[i]); | ||
continue; | ||
} | ||
|
||
zassert_true(!test_vector[i], | ||
"wrong test_vector value at %d: 0x%x vs 0x%x", | ||
i, test_vector[i], result_vector[i]); | ||
} | ||
|
||
irq_disable(fixture.irq1); | ||
} | ||
|
||
ZTEST_SUITE(shared_irq_feature, NULL, | ||
dynamic_shared_irq_suite_setup, | ||
dynamic_shared_irq_suite_before, | ||
dynamic_shared_irq_suite_after, | ||
NULL); |
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,96 @@ | ||
/* | ||
* Copyright 2023 NXP | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include "test_shared_irq.h" | ||
|
||
#define GIC_IRQ1 10 | ||
#define GIC_IRQ2 11 | ||
|
||
/** | ||
* @brief Test writing to a vector using static shared interrupts. | ||
* | ||
* @ingroup kernel_interrupt_tests | ||
* | ||
* @details This tests if interrupts are statically shared successfully | ||
* (i.e: multiple ISR/arg pairs are called whenever the interrupt they | ||
* were registered for is triggered). | ||
*/ | ||
ZTEST(interrupt_feature, test_static_shared_irq_write) | ||
{ | ||
/* note: this test is very brittle since it requires that | ||
* the chosen interrupt lines be unused for all of the | ||
* testing platforms. Failing to meet this requirement | ||
* leads to build failures due to the number of clients | ||
* exceeding the limit. Still, it's important to test that | ||
* the static shared interrupts work properly. As such, | ||
* this test shall be restricted to a single platform, thus | ||
* decreasing the risk of build errors appearing due to the | ||
* chosen interrupts being used. | ||
*/ | ||
#ifndef CONFIG_BOARD_QEMU_CORTEX_A53 | ||
ztest_test_skip(); | ||
#else | ||
int i; | ||
|
||
IRQ_CONNECT(GIC_IRQ1, IRQ_PRIORITY, test_isr_0, 0, 0); | ||
IRQ_CONNECT(GIC_IRQ1, IRQ_PRIORITY, test_isr_1, (void *)1, 0); | ||
IRQ_CONNECT(GIC_IRQ2, IRQ_PRIORITY, test_isr_2, (void *)2, 0); | ||
|
||
zassert_true(_sw_isr_table[GIC_IRQ1].isr == shared_isr, | ||
"wrong _sw_isr_table ISR at GIC_IRQ1"); | ||
zassert_true(_sw_isr_table[GIC_IRQ2].isr == test_isr_2, | ||
"wrong _sw_isr_table ISR at GIC_IRQ1"); | ||
|
||
zassert_true(_sw_isr_table[GIC_IRQ1].arg == | ||
&_shared_sw_isr_table[GIC_IRQ1], | ||
"wrong _sw_isr_table arg at GIC_IRQ1"); | ||
zassert_true(_sw_isr_table[GIC_IRQ2].arg == (void *)2, | ||
"wrong _sw_isr_table arg at GIC_IRQ2"); | ||
|
||
zassert_true(_shared_sw_isr_table[GIC_IRQ1].client_num == 2, | ||
"wrong client number for GIC_IRQ1"); | ||
zassert_true(!_shared_sw_isr_table[GIC_IRQ2].client_num, | ||
"wrong client number for GIC_IRQ2"); | ||
|
||
zassert_true(client_exists_at_index(test_isr_0, 0, GIC_IRQ1, | ||
TEST_INVALID_IDX), | ||
"test_isr_0 not a client for GIC_IRQ1"); | ||
zassert_true(client_exists_at_index(test_isr_1, (void *)1, GIC_IRQ1, | ||
TEST_INVALID_IDX), | ||
"test_isr_1 not a client for GIC_IRQ1"); | ||
|
||
irq_enable(GIC_IRQ1); | ||
irq_enable(GIC_IRQ2); | ||
|
||
trigger_irq(GIC_IRQ1); | ||
trigger_irq(GIC_IRQ2); | ||
|
||
/* wait 5ms before checking the results */ | ||
k_busy_wait(5000); | ||
|
||
for (i = 0; i < TEST_VECTOR_SIZE; i++) { | ||
zassert_true(test_vector[i] == result_vector[i], | ||
"wrong test_vector value at %d: 0x%x vs 0x%x", | ||
i, test_vector[i], result_vector[i]); | ||
} | ||
|
||
irq_disable(GIC_IRQ1); | ||
irq_disable(GIC_IRQ2); | ||
|
||
#ifdef CONFIG_DYNAMIC_INTERRUPTS | ||
/* if dynamic interrupts are enabled this will restore the _sw_isr_table | ||
* entries for GIC_IRQ1 and GIC_IRQ2 to their default values (NULL, | ||
* z_irq_spurious). In turn, this will increase the probability of | ||
* dynamic_shared_irq.c's get_irq_slot() being able to find an available | ||
* slot. | ||
*/ | ||
arch_irq_disconnect_dynamic(GIC_IRQ1, IRQ_PRIORITY, test_isr_0, 0, 0); | ||
arch_irq_disconnect_dynamic(GIC_IRQ1, IRQ_PRIORITY, test_isr_1, (void *)1, 0); | ||
arch_irq_disconnect_dynamic(GIC_IRQ2, IRQ_PRIORITY, test_isr_2, (void *)2, 0); | ||
#endif /* CONFIG_DYNAMIC_INTERRUPTS */ | ||
|
||
#endif /* CONFIG_BOARD_QEMU_CORTEX_A53 */ | ||
} |
Oops, something went wrong.