diff --git a/CMakeLists.txt b/CMakeLists.txt index 2dcad1bd0a..ab81968941 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -630,6 +630,7 @@ if(CONFIG_GEN_ISR_TABLES) COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/arch/common/gen_isr_tables.py --output-source isr_tables.c + --kernel $ --intlist isrList.bin $<$:--debug> --sw-isr-table diff --git a/arch/common/gen_isr_tables.py b/arch/common/gen_isr_tables.py index a16c4ca4ad..286e43c2a7 100755 --- a/arch/common/gen_isr_tables.py +++ b/arch/common/gen_isr_tables.py @@ -8,9 +8,23 @@ import argparse import struct import sys import os +from elftools.elf.elffile import ELFFile +from elftools.elf.sections import SymbolTableSection ISR_FLAG_DIRECT = (1 << 0) +# The below few hardware independent magic numbers represent various +# levels of interrupts in a multi-level interrupt system. +# 0x000000FF - represents the 1st level (i.e. the interrupts +# that directly go to the processor). +# 0x0000FF00 - represents the 2nd level (i.e. the interrupts funnel +# into 1 line which then goes into the 1st level) +# 0x00FF0000 - represents the 3rd level (i.e. the interrupts funnel +# into 1 line which then goes into the 2nd level) +FIRST_LVL_INTERRUPTS = 0x000000FF +SECND_LVL_INTERRUPTS = 0x0000FF00 +THIRD_LVL_INTERRUPTS = 0x00FF0000 + def debug(text): if args.debug: sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n") @@ -104,6 +118,8 @@ def parse_args(): help="Print additional debugging information") parser.add_argument("-o", "--output-source", required=True, help="Output source file") + parser.add_argument("-k", "--kernel", required=True, + help="Zephyr kernel image") parser.add_argument("-s", "--sw-isr-table", action="store_true", help="Generate SW ISR table") parser.add_argument("-V", "--vector-table", action="store_true", @@ -143,13 +159,59 @@ def write_source_file(fp, vt, swt, intlist): fp.write("\t{(void *)0x%x, (void *)0x%x},\n" % (param, func)) fp.write("};\n") +def get_symbols(obj): + for section in obj.iter_sections(): + if isinstance(section, SymbolTableSection): + return {sym.name: sym.entry.st_value + for sym in section.iter_symbols()} + + error("Could not find symbol table") + +def getindex(irq, irq_aggregator_pos): + for indx, val in enumerate(irq_aggregator_pos): + if irq == irq_aggregator_pos[indx]: + return indx + error("The index %d has no match. Recheck interrupt configuration" % indx) + def main(): parse_args() + with open(args.kernel, "rb") as fp: + kernel = ELFFile(fp) + syms = get_symbols(kernel) + + if "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms: + if "CONFIG_2ND_LEVEL_INTERRUPTS" in syms: + if "CONFIG_NUM_2ND_LEVEL_AGGREGATORS" in syms: + num_aggregators = syms["CONFIG_NUM_2ND_LEVEL_AGGREGATORS"] + list_2nd_lvl_offsets = [] + for i in range(num_aggregators): + offset_str = 'CONFIG_2ND_LVL_INTR_' + str(i).zfill(2) + '_OFFSET' + if offset_str in syms: + list_2nd_lvl_offsets.append(syms[offset_str]) + + debug(str(list_2nd_lvl_offsets)) + + if syms["CONFIG_3RD_LEVEL_INTERRUPTS"]: + if "CONFIG_NUM_3RD_LEVEL_AGGREGATORS" in syms: + num_aggregators = syms["CONFIG_NUM_3RD_LEVEL_AGGREGATORS"] + list_3rd_lvl_offsets = [] + for i in range(num_aggregators): + offset_str = 'CONFIG_3RD_LVL_INTR_' + str(i).zfill(2) + '_OFFSET' + if offset_str in syms: + list_3rd_lvl_offsets.append(syms[offset_str]) + + debug(str(list_3rd_lvl_offsets)) + intlist = read_intlist(args.intlist) nvec = intlist["num_vectors"] offset = intlist["offset"] prefix = endian_prefix() + numisrs = intlist["num_isrs"] + + debug('offset is ' + str(offset)) + debug('num_vectors is ' + str(nvec)) + debug('num_isrs is ' + str(numisrs)) # Set default entries in both tables if args.sw_isr_table: @@ -181,7 +243,40 @@ def main(): error("Regular Interrupt %d declared with parameter 0x%x " "but no SW ISR_TABLE in use" % (irq, param)) - swt[irq - offset] = (param, func) + + if not "CONFIG_MULTI_LEVEL_INTERRUPTS" in syms: + swt[irq - offset] = (param, func) + else: + # Figure out third level interrupt position + debug('IRQ = ' + hex(irq)) + irq3 = (irq & THIRD_LVL_INTERRUPTS) >> 16 + if irq3: + irq_parent = (irq & SECND_LVL_INTERRUPTS) >> 8 + list_index = getindex(irq_parent, list_3rd_lvl_offsets) + irq3_baseoffset = syms["CONFIG_3RD_LVL_ISR_TBL_OFFSET"] + irq3_pos = irq3_baseoffset + 32*list_index + irq3 - 1 + debug('IRQ_Indx = ' + str(irq3)) + debug('IRQ_Pos = ' + str(irq3_pos)) + swt[irq3_pos - offset] = (param, func) + + # Figure out second level interrupt position + if not irq3: + irq2 = (irq & SECND_LVL_INTERRUPTS) >> 8 + if irq2: + irq_parent = (irq & FIRST_LVL_INTERRUPTS) + list_index = getindex(irq_parent, list_2nd_lvl_offsets) + irq2_baseoffset = syms["CONFIG_2ND_LVL_ISR_TBL_OFFSET"] + irq2_pos = irq2_baseoffset + 32*list_index + irq2 - 1 + debug('IRQ_Indx = ' + str(irq2)) + debug('IRQ_Pos = ' + str(irq2_pos)) + swt[irq2_pos - offset] = (param, func) + + # Figure out first level interrupt position + if not irq3 and not irq2: + irq1 = (irq & FIRST_LVL_INTERRUPTS) + debug('IRQ_Indx = ' + str(irq1)) + debug('IRQ_Pos = ' + str(irq1)) + swt[irq1 - offset] = (param, func) with open(args.output_source, "w") as fp: write_source_file(fp, vt, swt, intlist) diff --git a/doc/kernel/other/interrupts.rst b/doc/kernel/other/interrupts.rst index 4986fe3458..981262d343 100644 --- a/doc/kernel/other/interrupts.rst +++ b/doc/kernel/other/interrupts.rst @@ -57,6 +57,58 @@ nesting support is enabled. alter its behavior depending on whether it is executing as part of a thread or as part of an ISR. +Multi-level Interrupt handling +============================== + +A hardware platform can support more interrupt lines than natively-provided +through the use of one or more nested interrupt controllers. Sources of +hardware interrupts are combined into one line that is then routed to +the parent controller. + +If nested interrupt controllers are supported, :option:`CONFIG_MULTI_LEVEL_INTERRUPTS` +should be set to 1, and :option:`CONFIG_2ND_LEVEL_INTERRUPTS` and +:option:`CONFIG_3RD_LEVEL_INTERRUPTS` configured as well, based on the +hardware architecture. + +A unique 32-bit interrupt number is assigned with information +embedded in it to select and invoke the correct Interrupt +Service Routine (ISR). Each interrupt level is given a byte within this 32-bit +number, providing support for up to four interrupt levels using this arch, as +illustrated and explained below: + +.. code-block:: none + + 9 2 0 + _ _ _ _ _ _ _ _ _ _ _ _ _ (LEVEL 1) + 5 | A | + _ _ _ _ _ _ _ _ _ _ _ _ _ _ (LEVEL 2) + | C B + _ _ _ _ _ _ _ (LEVEL 3) + D + +There are three interrupt levels shown here. + +* LEVEL 1 has 12 interrupt lines, with two lines (2 and 9) connected + to nested controllers. +* One of the LEVEL 2 controllers has interrupt line 5 connected to + a LEVEL 3 nested controller. +* The other LEVEL 2 controller has no nested controllers. + +Here's how unique interrupt numbers are generated for each +hardware interrupt. Let's consider four interrupts shown above +as A, B, C, and D: + +.. code-block:: none + + A -> 0x00000004 + B -> 0x00000302 + C -> 0x00000409 + D -> 0x00030609 + +.. note:: + The bit positions for LEVEL 2 and onward are offset by 1, as 0 means that + interrupt number is not present for that level. + Preventing Interruptions ======================== diff --git a/drivers/interrupt_controller/Kconfig b/drivers/interrupt_controller/Kconfig index e38cb87382..2be10e0f7b 100644 --- a/drivers/interrupt_controller/Kconfig +++ b/drivers/interrupt_controller/Kconfig @@ -128,4 +128,6 @@ config PLIC_FE310 source "drivers/interrupt_controller/Kconfig.stm32" +source "drivers/interrupt_controller/Kconfig.multilevel" + endmenu diff --git a/drivers/interrupt_controller/Kconfig.multilevel b/drivers/interrupt_controller/Kconfig.multilevel new file mode 100644 index 0000000000..2e38d51173 --- /dev/null +++ b/drivers/interrupt_controller/Kconfig.multilevel @@ -0,0 +1,119 @@ +# Kconfig - Multilevel interrupt configuration +# +# Copyright (c) 2017 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +config MULTI_LEVEL_INTERRUPTS + bool "Multi-level Interrupts" + default n + depends on GEN_SW_ISR_TABLE + help + Multiple levels of interrupts are normally used to increase the + number of addressable interrupts in a system. For e.g., if 2 levels + of interrupts are used, the 2nd level controller would combine all + interrupts routed to it into one line which then gets routed to one + of the lines in 1st level interrupt controller. + +config MAX_IRQ_PER_AGGREGATOR + int "Max IRQs per interrupt aggregator" + default 0 + depends on MULTI_LEVEL_INTERRUPTS + help + This represents the max number of interrupts that an aggregator + can map. + +config 2ND_LEVEL_INTERRUPTS + bool "Second-level Interrupts" + default n + depends on MULTI_LEVEL_INTERRUPTS + help + Second level interrupts are used to increase the number of + addressable interrupts in a system. + +config 2ND_LVL_ISR_TBL_OFFSET + int "Offset in the SW ISR Table for 2nd level interrupt controller" + default 0 + depends on MULTI_LEVEL_INTERRUPTS + help + This is the offset in the SW ISR table beginning where the ISRs for + 2nd level interrupts are located. This typically is allocated after + allocating first level interrupts. + +config NUM_2ND_LEVEL_AGGREGATORS + hex "Total Number of Second level Interrupt Aggregators" + default 0 + depends on MULTI_LEVEL_INTERRUPTS + help + This is the number of second level interrupt aggregators present + in the system. Each of these interrupt aggregators can typically + map MAX_IRQ_PER_AGGREGATOR second level interrupts to one first + level interrupt. + +config 2ND_LVL_INTR_00_OFFSET + hex "Parent interrupt number to which controller_0 maps" + default 0x00 + depends on 2ND_LEVEL_INTERRUPTS + help + This is the interrupt number in the first level to which + controller_0 of second level maps. + +config 2ND_LVL_INTR_01_OFFSET + hex "Parent interrupt number to which controller_1 maps" + default 0x00 + depends on 2ND_LEVEL_INTERRUPTS + help + This is the interrupt number in the first level to which + controller_1 of second level maps. + +config 2ND_LVL_INTR_02_OFFSET + hex "Parent interrupt number to which controller_2 maps" + default 0x00 + depends on 2ND_LEVEL_INTERRUPTS + help + This is the interrupt number in the first level to which + controller_2 of second level maps. + +config 2ND_LVL_INTR_03_OFFSET + hex "Parent interrupt number to which controller_3 maps" + default 0x00 + depends on 2ND_LEVEL_INTERRUPTS + help + This is the interrupt number in the first level to which + controller_3 of second level maps. + +config 3RD_LEVEL_INTERRUPTS + bool "Third-level Interrupts" + default n + depends on 2ND_LEVEL_INTERRUPTS + help + Third level interrupts are used to increase the number of + addressable interrupts in a system. + +config NUM_3RD_LEVEL_AGGREGATORS + hex "Total Number of Third level Interrupt Aggregators" + default 0 + depends on 3RD_LEVEL_INTERRUPTS + help + These are the number of third level interrupt aggregators present + in the system. Each of these interrupt aggregators can typically + map MAX_IRQ_PER_AGGREGATOR third level interrupts to one second + level interrupt. + +config 3RD_LVL_ISR_TBL_OFFSET + int "Offset in the SW ISR Table for 3rd level interrupt controller" + default 0 + depends on 3RD_LEVEL_INTERRUPTS + help + This is the offset in the SW ISR table beginning where the ISRs for + 3rd level interrupts are located. This typically is allocated after + allocating first and second level interrupts. + +config 3RD_LVL_INTR_00_OFFSET + hex "Parent interrupt number to which controller_0 maps" + default 0x00 + depends on 3RD_LEVEL_INTERRUPTS + help + This is the interrupt number in the second level to which + controller_0 of third level maps.