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:
parent
02f4aeb93d
commit
cce530cae4
6 changed files with 207 additions and 20 deletions
|
@ -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
|
||||
**************
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue