-
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
12138ae
commit fd9719d
Showing
5 changed files
with
395 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,75 @@ | ||
/* | ||
* 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) | ||
{ | ||
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 | ||
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 */ | ||
} |
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,66 @@ | ||
/* | ||
* Copyright 2023 NXP | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#ifndef __TEST_SHARED_IRQ_H__ | ||
#define __TEST_SHARED_IRQ_H__ | ||
|
||
#include <zephyr/ztest.h> | ||
#include <zephyr/interrupt_util.h> | ||
|
||
#define IRQ_PRIORITY 1 | ||
#define TEST_VECTOR_SIZE 10 | ||
#define TEST_INVALID_IDX 0xcafebabe | ||
#define TEST_DUMMY_ISR_VAL 0xdeadbeef | ||
#define TEST_INVALID_IRQ 0xcafebabe | ||
|
||
#define ISR_DEFINE(name) \ | ||
static inline void name(const void *data) \ | ||
{ \ | ||
int idx = POINTER_TO_INT(data); \ | ||
test_vector[idx] = result_vector[idx]; \ | ||
} \ | ||
|
||
static uint32_t test_vector[TEST_VECTOR_SIZE] = { | ||
}; | ||
|
||
static uint32_t result_vector[TEST_VECTOR_SIZE] = { | ||
0xdeadbeef, | ||
0xcafebabe, | ||
0x1234cafe, | ||
}; | ||
|
||
ISR_DEFINE(test_isr_0); | ||
ISR_DEFINE(test_isr_1); | ||
ISR_DEFINE(test_isr_2); | ||
|
||
static inline bool client_exists_at_index(void (*routine)(const void *arg), | ||
void *arg, int irq, size_t idx) | ||
{ | ||
size_t i; | ||
struct shared_irq_data *irq_data; | ||
|
||
if (idx == TEST_INVALID_IDX) { | ||
irq_data = &_shared_sw_isr_table[irq]; | ||
|
||
for (i = 0; i < irq_data->client_num; i++) { | ||
if (irq_data->clients[i].isr == routine && | ||
irq_data->clients[i].arg == arg) { | ||
return true; | ||
} | ||
} | ||
} else { | ||
if (_shared_sw_isr_table[irq].client_num <= idx) { | ||
return false; | ||
} | ||
|
||
return _shared_sw_isr_table[irq].clients[idx].isr == routine && | ||
_shared_sw_isr_table[irq].clients[idx].arg == arg; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
#endif /* __TEST_SHARED_IRQ_H__ */ |
Oops, something went wrong.