Skip to content

Commit

Permalink
scripts: build: gen_isr_tables: make bit masks configurable
Browse files Browse the repository at this point in the history
Some architectures such as RISC-v support more than 255 interrupts
per aggrigator. This diff adds the ability to forgo the aggrigator
pattern and use a configurable number of bits for multilevel
interruts.

Signed-off-by: Joshua Lilly <jgl@meta.com>
  • Loading branch information
jgl-meta committed Aug 8, 2023
1 parent 1f39e9e commit cc7acce
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 15 deletions.
17 changes: 17 additions & 0 deletions doc/kernel/services/interrupts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,23 @@ unique identifier which is then used to fetch the appropriate handler function
and parameter out of a table populated when the dynamic interrupt was
connected.

Going Beyond the Default Supported Number of Interrupts
-------------------------------------------------------

When generating interrupts in the multilevel configuration, 8-bits per level is the default
mask used when determining which level a given interrupt code belongs to. This can become
a problem when dealing with CPUs that support more than 255 interrupts per single
aggregator. In this case it may be desirable to override these defaults and use a custom
number of bits per level. Regardless of how many bits used for each level, the sum of
the total bits used between all levels must sum to be less than or equal to 32-bits,
fitting into a single 32-bit integer. To modify the bit total per level, override the
default 8 in `Kconfig.multilevel` by setting :kconfig:option:`CONFIG_1ST_LEVEL_INTERRUPT_BITS`
for the first level, :kconfig:option:`CONFIG_2ND_LEVEL_INTERRUPT_BITS` for the second tier and
:kconfig:option:`CONFIG_3RD_LEVEL_INTERRUPT_BITS` for the third tier. These masks control the
length of the bit masks and shift to apply when generating interrupt values, when checking the
interrupts level and converting interrupts to a different level. The logic controlling
this can be found in `irq.h`

Suggested Uses
**************

Expand Down
25 changes: 25 additions & 0 deletions drivers/interrupt_controller/Kconfig.multilevel
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,19 @@ config MULTI_LEVEL_INTERRUPTS
by the hardware. (The term "aggregator" here means "interrupt
controller".)

config 1ST_LEVEL_INTERRUPT_BITS
int "Total number of first level interrupt bits"
range 1 32
default 8
help
The number of bits to use of the 32 bit interrupt mask for first
tier interrupts.

config MAX_IRQ_PER_AGGREGATOR
int "Max IRQs per interrupt aggregator"
default 0
depends on MULTI_LEVEL_INTERRUPTS

help
The maximum number of interrupt inputs to any aggregator in the
system.
Expand Down Expand Up @@ -52,6 +61,14 @@ config NUM_2ND_LEVEL_AGGREGATORS
aggregator can manage at most MAX_IRQ_PER_AGGREGATOR level 2
interrupts.

config 2ND_LEVEL_INTERRUPT_BITS
int "Total number of second level interrupt bits"
range 1 31
default 8
help
The number of bits to use of the 32 bit interrupt mask for second
tier interrupts.

prev-level-num = 1
cur-level-num = 2
cur-level = 2ND
Expand Down Expand Up @@ -98,6 +115,14 @@ config 3RD_LVL_ISR_TBL_OFFSET
where storage for 3rd level interrupt ISRs begins. This is
typically allocated after ISRs for level 2 interrupts.

config 3RD_LEVEL_INTERRUPT_BITS
int "Total number of third level interrupt bits"
range 1 30
default 8
help
The number of bits to use of the 32 bit interrupt mask for third
tier interrupts.

prev-level-num = 2
cur-level-num = 3
cur-level = 3RD
Expand Down
37 changes: 24 additions & 13 deletions include/zephyr/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

/* Pull in the arch-specific implementations */
#include <zephyr/arch/cpu.h>
#include <zephyr/sys/util.h>

#ifndef _ASMLANGUAGE
#include <zephyr/toolchain.h>
Expand All @@ -22,6 +23,11 @@
extern "C" {
#endif

#if defined(CONFIG_MULTI_LEVEL_INTERRUPTS) && CONFIG_MAX_IRQ_PER_AGGREGATOR > 0
BUILD_ASSERT((LOG2(CONFIG_MAX_IRQ_PER_AGGREGATOR) + 1) <= CONFIG_1ST_LEVEL_INTERRUPT_BITS,
"CONFIG_MAX_IRQ_PER_AGGREGATOR is too large");
#endif

/**
* @defgroup isr_apis Interrupt Service Routine APIs
* @ingroup kernel_apis
Expand Down Expand Up @@ -266,22 +272,25 @@ void z_smp_global_unlock(unsigned int key);
static inline unsigned int irq_get_level(unsigned int irq)
{
#if defined(CONFIG_3RD_LEVEL_INTERRUPTS)
return ((irq >> 16) & 0xFF) != 0 ? 3 :
(((irq >> 8) & 0xFF) == 0 ? 1 : 2);
return ((irq >> (CONFIG_1ST_LEVEL_INTERRUPT_BITS + CONFIG_2ND_LEVEL_INTERRUPT_BITS))
& BIT_MASK(CONFIG_3RD_LEVEL_INTERRUPT_BITS)) != 0 ?
3 : (((irq >> CONFIG_2ND_LEVEL_INTERRUPT_BITS)
& BIT_MASK(CONFIG_2ND_LEVEL_INTERRUPT_BITS)) == 0 ? 1 : 2);
#elif defined(CONFIG_2ND_LEVEL_INTERRUPTS)
return ((irq >> 8) & 0xFF) == 0 ? 1 : 2;
return ((irq >> CONFIG_2ND_LEVEL_INTERRUPT_BITS) &
BIT_MASK(CONFIG_2ND_LEVEL_INTERRUPT_BITS)) == 0 ?
1 : 2;
#else
ARG_UNUSED(irq);

return 1;
#endif
}

#ifdef CONFIG_2ND_LEVEL_INTERRUPTS
#if defined(CONFIG_2ND_LEVEL_INTERRUPTS)
/**
* @brief Return the 2nd level interrupt number
*
*
* This routine returns the second level irq number of the zephyr irq
* number passed in
*
Expand All @@ -291,10 +300,11 @@ static inline unsigned int irq_get_level(unsigned int irq)
*/
static inline unsigned int irq_from_level_2(unsigned int irq)
{
#ifdef CONFIG_3RD_LEVEL_INTERRUPTS
return ((irq >> 8) & 0xFF) - 1;
#if defined(CONFIG_3RD_LEVEL_INTERRUPTS)
return ((irq >> CONFIG_1ST_LEVEL_INTERRUPT_BITS) &
BIT_MASK(CONFIG_2ND_LEVEL_INTERRUPT_BITS)) - 1;
#else
return (irq >> 8) - 1;
return (irq >> CONFIG_1ST_LEVEL_INTERRUPT_BITS) - 1;
#endif
}

Expand All @@ -312,7 +322,7 @@ static inline unsigned int irq_from_level_2(unsigned int irq)
*/
static inline unsigned int irq_to_level_2(unsigned int irq)
{
return (irq + 1) << 8;
return (irq + 1) << CONFIG_1ST_LEVEL_INTERRUPT_BITS;
}

/**
Expand All @@ -327,7 +337,7 @@ static inline unsigned int irq_to_level_2(unsigned int irq)
*/
static inline unsigned int irq_parent_level_2(unsigned int irq)
{
return irq & 0xFF;
return irq & BIT_MASK(CONFIG_1ST_LEVEL_INTERRUPT_BITS);
}
#endif

Expand All @@ -345,7 +355,7 @@ static inline unsigned int irq_parent_level_2(unsigned int irq)
*/
static inline unsigned int irq_from_level_3(unsigned int irq)
{
return (irq >> 16) - 1;
return (irq >> (CONFIG_1ST_LEVEL_INTERRUPT_BITS + CONFIG_2ND_LEVEL_INTERRUPT_BITS)) - 1;
}

/**
Expand All @@ -362,7 +372,7 @@ static inline unsigned int irq_from_level_3(unsigned int irq)
*/
static inline unsigned int irq_to_level_3(unsigned int irq)
{
return (irq + 1) << 16;
return (irq + 1) << (CONFIG_1ST_LEVEL_INTERRUPT_BITS + CONFIG_2ND_LEVEL_INTERRUPT_BITS);
}

/**
Expand All @@ -377,7 +387,8 @@ static inline unsigned int irq_to_level_3(unsigned int irq)
*/
static inline unsigned int irq_parent_level_3(unsigned int irq)
{
return (irq >> 8) & 0xFF;
return (irq >> CONFIG_1ST_LEVEL_INTERRUPT_BITS) &
BIT_MASK(CONFIG_2ND_LEVEL_INTERRUPT_BITS);
}
#endif

Expand Down
29 changes: 27 additions & 2 deletions scripts/build/gen_isr_tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
SECND_LVL_INTERRUPTS = 0x0000FF00
THIRD_LVL_INTERRUPTS = 0x00FF0000

INTERRUPT_BITS = [8, 8, 8]

def debug(text):
if args.debug:
sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n")
Expand Down Expand Up @@ -230,6 +232,24 @@ def getindex(irq, irq_aggregator_pos):
format(irq, irq_aggregator_pos) +
" Recheck interrupt configuration.")

def bit_mask(bits):
mask = 0
for _ in range(0, bits):
mask = (mask << 1) | 1
return mask

def update_masks():
global FIRST_LVL_INTERRUPTS
global SECND_LVL_INTERRUPTS
global THIRD_LVL_INTERRUPTS

if sum(INTERRUPT_BITS) > 32:
raise ValueError("Too many interrupt bits")

FIRST_LVL_INTERRUPTS = bit_mask(INTERRUPT_BITS[0])
SECND_LVL_INTERRUPTS = bit_mask(INTERRUPT_BITS[1]) << INTERRUPT_BITS[1]
THIRD_LVL_INTERRUPTS = bit_mask(INTERRUPT_BITS[2]) << INTERRUPT_BITS[0] + INTERRUPT_BITS[2]

def main():
parse_args()

Expand All @@ -240,6 +260,11 @@ def main():
if "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms:
max_irq_per = syms["CONFIG_MAX_IRQ_PER_AGGREGATOR"]

INTERRUPT_BITS[0] = syms["CONFIG_1ST_LEVEL_INTERRUPT_BITS"]
INTERRUPT_BITS[1] = syms["CONFIG_2ND_LEVEL_INTERRUPT_BITS"]
INTERRUPT_BITS[2] = syms["CONFIG_3RD_LEVEL_INTERRUPT_BITS"]
update_masks()

if "CONFIG_2ND_LEVEL_INTERRUPTS" in syms:
num_aggregators = syms["CONFIG_NUM_2ND_LEVEL_AGGREGATORS"]
irq2_baseoffset = syms["CONFIG_2ND_LVL_ISR_TBL_OFFSET"]
Expand Down Expand Up @@ -311,8 +336,8 @@ def main():
else:
# Figure out third level interrupt position
debug('IRQ = ' + hex(irq))
irq3 = (irq & THIRD_LVL_INTERRUPTS) >> 16
irq2 = (irq & SECND_LVL_INTERRUPTS) >> 8
irq3 = (irq & THIRD_LVL_INTERRUPTS) >> INTERRUPT_BITS[0] + INTERRUPT_BITS[1]
irq2 = (irq & SECND_LVL_INTERRUPTS) >> INTERRUPT_BITS[1]
irq1 = irq & FIRST_LVL_INTERRUPTS

if irq3:
Expand Down
93 changes: 93 additions & 0 deletions tests/kernel/gen_isr_table/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -381,4 +381,97 @@ static void *gen_isr_table_setup(void)
return NULL;
}

#ifdef CONFIG_MULTI_LEVEL_INTERRUPTS
ZTEST(gen_isr_table, test_multi_level_bit_masks_sec)
{
#if CONFIG_1ST_LEVEL_INTERRUPT_BITS < 10 && CONFIG_2ND_LEVEL_INTERRUPT_BITS < 10
ztest_test_skip();
#endif
/* 0x400 is an l2 interrupt */
unsigned int irq = 0x400;
unsigned int level = 0;
unsigned int ret_irq = 0;

level = irq_get_level(irq);

zassert_equal(2, level);

/* 0x40 is l1 interrupt since it is less than 10 bits */
irq = 0x40;
level = irq_get_level(irq);
zassert_equal(1, level);

/* this is an l2 interrupt since it is more than 10 bits */
irq = 0x800;
ret_irq = irq_from_level_2(irq);
zassert_equal(1, ret_irq);

/* convert l1 interrupt to l2 */
irq = 0x1;
ret_irq = irq_to_level_2(irq);
zassert_equal(0x800, ret_irq);

/* get the parent of this l2 interrupt */
irq = 0x401;
ret_irq = irq_parent_level_2(irq);
zassert_equal(1, ret_irq);
}
#endif

#ifdef CONFIG_3RD_LEVEL_INTERRUPTS
ZTEST(gen_isr_table, test_multi_level_bit_masks_thr)
{
#if CONFIG_2ND_LEVEL_INTERRUPT_BITS < 10 && CONFIG_3RD_LEVEL_INTERRUPT_BITS < 9
ztest_test_skip();
# endif
/* 0x400 is an l2 interrupt */
unsigned int irq = 0x400;
unsigned int level = 0;
unsigned int ret_irq = 0;


/* note the first part of this test is the same as the above
* test this is to ensure the values are true after enabling l3 interrupts
*/
level = irq_get_level(irq);

zassert_equal(2, level);

/* this irq is within 10 bits so it is a l1 interrupt */
irq = 0x40;
level = irq_get_level(irq);
zassert_equal(1, level);

/* this irq is in the second 10 bits so it is a l2 interrupt */
irq = 0x800;
ret_irq = irq_from_level_2(irq);
zassert_equal(1, ret_irq);

/* convert a l1 interrupt to an l2 0x1 is less than 10 bits so it is l1 */
irq = 0x1;
ret_irq = irq_to_level_2(irq);
zassert_equal(0x800, ret_irq);

/* get the parent of an l2 interrupt 0x401 is an l2 interrupt with parent 1 */
irq = 0x401;
ret_irq = irq_parent_level_2(irq);
zassert_equal(1, ret_irq);

/* get the irq from level 3 this value is an l3 interrupt */
irq = 0x200000;
ret_irq = irq_from_level_3(irq);
zassert_equal(1, ret_irq);

/* convert the zero interrupt to l3 */
irq = 0x0;
ret_irq = irq_to_level_3(irq);
zassert_equal(0x100000, ret_irq);

/* parent of the l3 interrupt */
irq = 0x101000;
ret_irq = irq_parent_level_3(irq);
zassert_equal(0x4, ret_irq);
}
#endif

ZTEST_SUITE(gen_isr_table, NULL, gen_isr_table_setup, NULL, NULL, NULL);
16 changes: 16 additions & 0 deletions tests/kernel/gen_isr_table/testcase.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,19 @@ tests:
filter: CONFIG_SOC_FAMILY_RISCV_PRIVILEGED
extra_configs:
- CONFIG_GEN_IRQ_VECTOR_TABLE=n
arch.interrupt.gen_isr_table.bit_shift_2nd_level:
platform_allow: qemu_riscv32
filter: CONFIG_SOC_FAMILY_RISCV_PRIVILEGED
extra_configs:
- CONFIG_1ST_LEVEL_INTERRUPT_BITS=10
- CONFIG_2ND_LEVEL_INTERRUPT_BITS=10
arch.interrupt.gen_isr_table.bit_shift_3rd_level:
platform_allow: qemu_riscv32
filter: CONFIG_SOC_FAMILY_RISCV_PRIVILEGED
extra_configs:
- CONFIG_MULTI_LEVEL_INTERRUPTS=y
- CONFIG_2ND_LEVEL_INTERRUPTS=y
- CONFIG_3RD_LEVEL_INTERRUPTS=y
- CONFIG_1ST_LEVEL_INTERRUPT_BITS=10
- CONFIG_2ND_LEVEL_INTERRUPT_BITS=10
- CONFIG_3RD_LEVEL_INTERRUPT_BITS=9

0 comments on commit cc7acce

Please sign in to comment.