scripts: build: gen_isr_tables: make bit masks configurable

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>
This commit is contained in:
Joshua Lilly 2023-08-01 14:44:51 -07:00 committed by Chris Friedt
commit cce530cae4
6 changed files with 207 additions and 20 deletions

View file

@ -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
**************

View file

@ -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.
@ -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 0 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
@ -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 0 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

View file

@ -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>
@ -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
@ -265,23 +271,26 @@ 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);
#elif defined(CONFIG_2ND_LEVEL_INTERRUPTS)
return ((irq >> 8) & 0xFF) == 0 ? 1 : 2;
#else
ARG_UNUSED(irq);
const uint32_t mask2 = BIT_MASK(CONFIG_2ND_LEVEL_INTERRUPT_BITS) <<
CONFIG_1ST_LEVEL_INTERRUPT_BITS;
const uint32_t mask3 = BIT_MASK(CONFIG_3RD_LEVEL_INTERRUPT_BITS) <<
(CONFIG_1ST_LEVEL_INTERRUPT_BITS + CONFIG_2ND_LEVEL_INTERRUPT_BITS);
if (IS_ENABLED(CONFIG_3RD_LEVEL_INTERRUPTS) && (irq & mask3) != 0) {
return 3;
}
if (IS_ENABLED(CONFIG_2ND_LEVEL_INTERRUPTS) && (irq & mask2) != 0) {
return 2;
}
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
*
@ -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
}
@ -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;
}
/**
@ -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
@ -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;
}
/**
@ -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);
}
/**
@ -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

View file

@ -27,6 +27,8 @@ FIRST_LVL_INTERRUPTS = 0x000000FF
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")
@ -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[0]
THIRD_LVL_INTERRUPTS = bit_mask(INTERRUPT_BITS[2]) << INTERRUPT_BITS[0] + INTERRUPT_BITS[2]
def main():
parse_args()
@ -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"]
@ -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[0]
irq1 = irq & FIRST_LVL_INTERRUPTS
if irq3:

View file

@ -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);

View file

@ -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