Skip to content

Commit

Permalink
tests: kernel: interrupt: Add testcase for shared interrupts
Browse files Browse the repository at this point in the history
This commit introduces a new testcase for shared interrupts.

Signed-off-by: Laurentiu Mihalcea <[email protected]>
  • Loading branch information
LaurentiuM1234 committed Sep 6, 2023
1 parent edd7097 commit 2ce3477
Show file tree
Hide file tree
Showing 5 changed files with 410 additions and 0 deletions.
5 changes: 5 additions & 0 deletions tests/kernel/interrupt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ target_sources(app PRIVATE

target_sources_ifdef(CONFIG_DYNAMIC_INTERRUPTS app PRIVATE src/dynamic_isr.c)
target_sources_ifdef(CONFIG_X86 app PRIVATE src/regular_isr.c)
target_sources_ifdef(CONFIG_SHARED_INTERRUPTS app PRIVATE src/static_shared_irq.c)

if (CONFIG_SHARED_INTERRUPTS)
target_sources_ifdef(CONFIG_DYNAMIC_INTERRUPTS app PRIVATE src/dynamic_shared_irq.c)
endif()
227 changes: 227 additions & 0 deletions tests/kernel/interrupt/src/dynamic_shared_irq.c
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);
96 changes: 96 additions & 0 deletions tests/kernel/interrupt/src/static_shared_irq.c
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 */
}
Loading

0 comments on commit 2ce3477

Please sign in to comment.