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 7, 2023
1 parent 1f39e9e commit 1d584aa
Show file tree
Hide file tree
Showing 6 changed files with 191 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
24 changes: 24 additions & 0 deletions drivers/interrupt_controller/Kconfig.multilevel
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ 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
Expand Down Expand Up @@ -52,6 +60,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 +114,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
38 changes: 25 additions & 13 deletions include/zephyr/irq.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

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

#ifndef _ASMLANGUAGE
#include <zephyr/toolchain.h>
Expand All @@ -22,6 +24,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 @@ -255,6 +262,7 @@ void z_smp_global_unlock(unsigned int key);
#define irq_unlock(key) arch_irq_unlock(key)
#endif

#if defined(CONFIG_2ND_LEVEL_INTERRUPTS)
/**
* @brief Return IRQ level
* This routine returns the interrupt level number of the provided interrupt.
Expand All @@ -266,22 +274,24 @@ 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
/**
* @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 +301,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_2ND_LEVEL_INTERRUPT_BITS) &
BIT_MASK(CONFIG_1ST_LEVEL_INTERRUPT_BITS)) - 1;
#else
return (irq >> 8) - 1;
return (irq >> CONFIG_2ND_LEVEL_INTERRUPT_BITS) - 1;
#endif
}

Expand All @@ -312,7 +323,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_2ND_LEVEL_INTERRUPT_BITS;
}

/**
Expand All @@ -327,7 +338,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 +356,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 +373,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 +388,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_2ND_LEVEL_INTERRUPT_BITS) &
BIT_MASK(CONFIG_1ST_LEVEL_INTERRUPT_BITS);
}
#endif

Expand Down
34 changes: 32 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,13 +260,18 @@ 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"]

if "CONFIG_2ND_LEVEL_INTERRUPTS" in syms:
num_aggregators = syms["CONFIG_NUM_2ND_LEVEL_AGGREGATORS"]
irq2_baseoffset = syms["CONFIG_2ND_LVL_ISR_TBL_OFFSET"]
list_2nd_lvl_offsets = [syms['CONFIG_2ND_LVL_INTR_{}_OFFSET'.
format(str(i).zfill(2))] for i in
range(num_aggregators)]


INTERRUPT_BITS[1] = syms["CONFIG_2ND_LEVEL_INTERRUPT_BITS"]

debug('2nd level offsets: {}'.format(list_2nd_lvl_offsets))

if "CONFIG_3RD_LEVEL_INTERRUPTS" in syms:
Expand All @@ -256,8 +281,12 @@ def main():
format(str(i).zfill(2))] for i in
range(num_aggregators)]


INTERRUPT_BITS[2] = syms["CONFIG_3RD_LEVEL_INTERRUPT_BITS"]

debug('3rd level offsets: {}'.format(list_3rd_lvl_offsets))

update_masks()
intlist = read_intlist(args.intlist, syms)
nvec = intlist["num_vectors"]
offset = intlist["offset"]
Expand All @@ -266,6 +295,7 @@ def main():
raise ValueError('nvec is too large, check endianness.')

swt_spurious_handler = "((uintptr_t)&z_irq_spurious)"

vt_spurious_handler = "z_irq_spurious"
vt_irq_handler = "_isr_wrapper"

Expand Down Expand Up @@ -311,8 +341,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
77 changes: 77 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,81 @@ 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

unsigned int irq = 0x400;
unsigned int level = 0;
unsigned int ret_irq = 0;

level = irq_get_level(irq);

zassert_equal(2, level);

irq = 0x40;
level = irq_get_level(irq);
zassert_equal(1, level);

irq = 0x800;
ret_irq = irq_from_level_2(irq);
zassert_equal(1, ret_irq);

irq = 0x1;
ret_irq = irq_to_level_2(irq);
zassert_equal(0x800, ret_irq);

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
unsigned int irq = 0x400;
unsigned int level = 0;
unsigned int ret_irq = 0;

level = irq_get_level(irq);

zassert_equal(2, level);

irq = 0x40;
level = irq_get_level(irq);
zassert_equal(1, level);

irq = 0x800;
ret_irq = irq_from_level_2(irq);
zassert_equal(1, ret_irq);

irq = 0x1;
ret_irq = irq_to_level_2(irq);
zassert_equal(0x800, ret_irq);

irq = 0x401;
ret_irq = irq_parent_level_2(irq);
zassert_equal(1, ret_irq);

irq = 0x200000;
ret_irq = irq_from_level_3(irq);
zassert_equal(1, ret_irq);

irq = 0x0;
ret_irq = irq_to_level_3(irq);
zassert_equal(0x100000, ret_irq);

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 1d584aa

Please sign in to comment.