scripts: build: gen_isr_tables: Implement local ISR generation
This commit moves all the functionality related to the current
interrupt parser into gen_isr_tables_parser_carrays.py file.
The new parser file gen_isr_tables_parser_local.py file is
implemented with the new parser that.
Additional information added to the generated interrupt header
that contains data required by the new parser.
Signed-off-by: Radosław Koppel <radoslaw.koppel@nordicsemi.no>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ce9121b..a187601 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1228,10 +1228,11 @@
# isr_tables.c is generated from ${ZEPHYR_LINK_STAGE_EXECUTABLE} by
# gen_isr_tables.py
add_custom_command(
- OUTPUT isr_tables.c
+ OUTPUT isr_tables.c isr_tables_vt.ld isr_tables_swi.ld
COMMAND ${PYTHON_EXECUTABLE}
${ZEPHYR_BASE}/scripts/build/gen_isr_tables.py
--output-source isr_tables.c
+ --linker-output-files isr_tables_vt.ld isr_tables_swi.ld
--kernel $<TARGET_FILE:${ZEPHYR_LINK_STAGE_EXECUTABLE}>
--intlist-section .intList
--intlist-section intList
diff --git a/arch/common/isr_tables.c b/arch/common/isr_tables.c
index 9677c92..050597b 100644
--- a/arch/common/isr_tables.c
+++ b/arch/common/isr_tables.c
@@ -15,6 +15,11 @@
struct int_list_header {
uint32_t table_size;
uint32_t offset;
+#if IS_ENABLED(CONFIG_ISR_TABLES_LOCAL_DECLARATION)
+ uint32_t swi_table_entry_size;
+ uint32_t shared_isr_table_entry_size;
+ uint32_t shared_isr_client_num_offset;
+#endif /* IS_ENABLED(CONFIG_ISR_TABLES_LOCAL_DECLARATION) */
};
/* These values are not included in the resulting binary, but instead form the
@@ -24,6 +29,13 @@
Z_GENERIC_SECTION(.irq_info) __used struct int_list_header _iheader = {
.table_size = IRQ_TABLE_SIZE,
.offset = CONFIG_GEN_IRQ_START_VECTOR,
+#if IS_ENABLED(CONFIG_ISR_TABLES_LOCAL_DECLARATION)
+ .swi_table_entry_size = sizeof(struct _isr_table_entry),
+#if IS_ENABLED(CONFIG_SHARED_INTERRUPTS)
+ .shared_isr_table_entry_size = sizeof(struct z_shared_isr_table_entry),
+ .shared_isr_client_num_offset = offsetof(struct z_shared_isr_table_entry, client_num),
+#endif /* IS_ENABLED(CONFIG_SHARED_INTERRUPTS) */
+#endif /* IS_ENABLED(CONFIG_ISR_TABLES_LOCAL_DECLARATION) */
};
/* These are placeholder tables. They will be replaced by the real tables
diff --git a/scripts/build/gen_isr_tables.py b/scripts/build/gen_isr_tables.py
index 3032d1e..84812d7 100755
--- a/scripts/build/gen_isr_tables.py
+++ b/scripts/build/gen_isr_tables.py
@@ -8,9 +8,9 @@
#
import argparse
-import struct
import sys
import os
+import importlib
from elftools.elf.elffile import ELFFile
from elftools.elf.sections import SymbolTableSection
@@ -44,10 +44,13 @@
"""
# Constants
__ISR_FLAG_DIRECT = 1 << 0
- __swt_spurious_handler = "((uintptr_t)&z_irq_spurious)"
- __swt_shared_handler = "((uintptr_t)&z_shared_isr)"
+ __swt_spurious_handler = "z_irq_spurious"
+ __swt_shared_handler = "z_shared_isr"
__vt_spurious_handler = "z_irq_spurious"
__vt_irq_handler = "_isr_wrapper"
+ __shared_array_name = "z_shared_sw_isr_table"
+ __sw_isr_array_name = "_sw_isr_table"
+ __irq_vector_array_name = "_irq_vector_table"
@staticmethod
def __bm(bits):
@@ -141,6 +144,18 @@
return self.__vt_default_handler
@property
+ def shared_array_name(self):
+ return self.__shared_array_name
+
+ @property
+ def sw_isr_array_name(self):
+ return self.__sw_isr_array_name
+
+ @property
+ def irq_vector_array_name(self):
+ return self.__irq_vector_array_name
+
+ @property
def int_bits(self):
return self.__int_bits
@@ -233,289 +248,6 @@
return self.check_sym("CONFIG_64BIT")
-class gen_isr_parser:
- source_header = """
-/* AUTO-GENERATED by gen_isr_tables.py, do not edit! */
-
-#include <zephyr/toolchain.h>
-#include <zephyr/linker/sections.h>
-#include <zephyr/sw_isr_table.h>
-#include <zephyr/arch/cpu.h>
-
-typedef void (* ISR)(const void *);
-"""
-
- source_assembly_header = """
-#ifndef ARCH_IRQ_VECTOR_JUMP_CODE
-#error "ARCH_IRQ_VECTOR_JUMP_CODE not defined"
-#endif
-"""
-
- def __init__(self, intlist_data, config, log):
- """Initialize the parser.
-
- The function prepares parser to work.
- Parameters:
- - intlist_data: The binnary data from intlist section
- - config: The configuration object
- - log: The logging object, has to have error and debug methods
- """
- self.__config = config
- self.__log = log
- intlist = self.__read_intlist(intlist_data)
- self.__vt, self.__swt, self.__nv = self.__parse_intlist(intlist)
-
- def __read_intlist(self, intlist_data):
- """read a binary file containing the contents of the kernel's .intList
- section. This is an instance of a header created by
- include/zephyr/linker/intlist.ld:
-
- struct {
- uint32_t num_vectors; <- typically CONFIG_NUM_IRQS
- struct _isr_list isrs[]; <- Usually of smaller size than num_vectors
- }
-
- Followed by instances of struct _isr_list created by IRQ_CONNECT()
- calls:
-
- struct _isr_list {
- /** IRQ line number */
- int32_t irq;
- /** Flags for this IRQ, see ISR_FLAG_* definitions */
- int32_t flags;
- /** ISR to call */
- void *func;
- /** Parameter for non-direct IRQs */
- const void *param;
- };
- """
- intlist = {}
- prefix = self.__config.endian_prefix()
-
- # Extract header and the rest of the data
- intlist_header_fmt = prefix + "II"
- header_sz = struct.calcsize(intlist_header_fmt)
- header_raw = struct.unpack_from(intlist_header_fmt, intlist_data, 0)
- self.__log.debug(str(header_raw))
-
- intlist["num_vectors"] = header_raw[0]
- intlist["offset"] = header_raw[1]
- intdata = intlist_data[header_sz:]
-
- # Extract information about interrupts
- if self.__config.check_64b():
- intlist_entry_fmt = prefix + "iiQQ"
- else:
- intlist_entry_fmt = prefix + "iiII"
-
- intlist["interrupts"] = [i for i in
- struct.iter_unpack(intlist_entry_fmt, intdata)]
-
- self.__log.debug("Configured interrupt routing")
- self.__log.debug("handler irq flags param")
- self.__log.debug("--------------------------")
-
- for irq in intlist["interrupts"]:
- self.__log.debug("{0:<10} {1:<3} {2:<3} {3}".format(
- hex(irq[2]), irq[0], irq[1], hex(irq[3])))
-
- return intlist
-
- def __parse_intlist(self, intlist):
- """All the intlist data are parsed into swt and vt arrays.
-
- The vt array is prepared for hardware interrupt table.
- Every entry in the selected position would contain None or the name of the function pointer
- (address or string).
-
- The swt is a little more complex. At every position it would contain an array of parameter and
- function pointer pairs. If CONFIG_SHARED_INTERRUPTS is enabled there may be more than 1 entry.
- If empty array is placed on selected position - it means that the application does not implement
- this interrupt.
-
- Parameters:
- - intlist: The preprocessed list of intlist section content (see read_intlist)
-
- Return:
- vt, swt - parsed vt and swt arrays (see function description above)
- """
- nvec = intlist["num_vectors"]
- offset = intlist["offset"]
-
- if nvec > pow(2, 15):
- raise ValueError('nvec is too large, check endianness.')
-
- self.__log.debug('offset is ' + str(offset))
- self.__log.debug('num_vectors is ' + str(nvec))
-
- # Set default entries in both tables
- if not(self.__config.args.sw_isr_table or self.__config.args.vector_table):
- self.__log.error("one or both of -s or -V needs to be specified on command line")
- if self.__config.args.vector_table:
- vt = [None for i in range(nvec)]
- else:
- vt = None
- if self.__config.args.sw_isr_table:
- swt = [[] for i in range(nvec)]
- else:
- swt = None
-
- # Process intlist and write to the tables created
- for irq, flags, func, param in intlist["interrupts"]:
- if not vt:
- error("Direct Interrupt %d declared with parameter 0x%x "
- "but no vector table in use"
- % (irq, param))
- if self.__config.test_isr_direct(flags):
- if param != 0:
- self.__log.error("Direct irq %d declared, but has non-NULL parameter"
- % irq)
- if not 0 <= irq - offset < len(vt):
- self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d" %
- (irq - offset, offset, len(vt) - 1))
- vt[irq - offset] = func
- else:
- # Regular interrupt
- if not swt:
- self.__log.error("Regular Interrupt %d declared with parameter 0x%x "
- "but no SW ISR_TABLE in use"
- % (irq, param))
-
- table_index = self.__config.get_swt_table_index(offset, irq)
-
- if not 0 <= table_index < len(swt):
- self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d" %
- (table_index, offset, len(swt) - 1))
- if self.__config.check_shared_interrupts():
- lst = swt[table_index]
- if (param, func) in lst:
- self.__log.error("Attempting to register the same ISR/arg pair twice.")
- if len(lst) >= self.__config.get_sym("CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS"):
- self.__log.error(f"Reached shared interrupt client limit. Maybe increase"
- + f" CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS?")
- else:
- if len(swt[table_index]) > 0:
- self.__log.error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})"
- + f"\nExisting handler 0x{swt[table_index][0][1]:x}, new handler 0x{func:x}"
- + "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?"
- )
- swt[table_index].append((param, func))
-
- return vt, swt, nvec
-
- def __write_code_irq_vector_table(self, fp):
- fp.write(self.source_assembly_header)
-
- fp.write("void __irq_vector_table __attribute__((naked)) _irq_vector_table(void) {\n")
- for i in range(self.__nv):
- func = self.__vt[i]
-
- if func is None:
- func = self.__config.vt_default_handler
-
- if isinstance(func, int):
- func_as_string = self.__config.get_sym_from_addr(func)
- else:
- func_as_string = func
-
- fp.write("\t__asm(ARCH_IRQ_VECTOR_JUMP_CODE({}));\n".format(func_as_string))
- fp.write("}\n")
-
- def __write_address_irq_vector_table(self, fp):
- fp.write("uintptr_t __irq_vector_table _irq_vector_table[%d] = {\n" % self.__nv)
- for i in range(self.__nv):
- func = self.__vt[i]
-
- if func is None:
- func = self.__config.vt_default_handler
-
- if isinstance(func, int):
- fp.write("\t{},\n".format(func))
- else:
- fp.write("\t((uintptr_t)&{}),\n".format(func))
-
- fp.write("};\n")
-
- def __write_shared_table(self, fp):
- fp.write("struct z_shared_isr_table_entry __shared_sw_isr_table"
- " z_shared_sw_isr_table[%d] = {\n" % self.__nv)
-
- for i in range(self.__nv):
- if self.__swt[i] is None:
- client_num = 0
- client_list = None
- else:
- client_num = len(self.__swt[i])
- client_list = self.__swt[i]
-
- if client_num <= 1:
- fp.write("\t{ },\n")
- else:
- fp.write(f"\t{{ .client_num = {client_num}, .clients = {{ ")
- for j in range(0, client_num):
- routine = client_list[j][1]
- arg = client_list[j][0]
-
- fp.write(f"{{ .isr = (ISR){ hex(routine) if isinstance(routine, int) else routine }, "
- f".arg = (const void *){hex(arg)} }},")
-
- fp.write(" },\n},\n")
-
- fp.write("};\n")
-
- def write_source(self, fp):
- fp.write(self.source_header)
-
- if self.__config.check_shared_interrupts():
- self.__write_shared_table(fp)
-
- if self.__vt:
- if self.__config.check_sym("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS"):
- self.__write_address_irq_vector_table(fp)
- elif self.__config.check_sym("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE"):
- self.__write_code_irq_vector_table(fp)
- else:
- self.__log.error("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_{ADDRESS,CODE} not set")
-
- if not self.__swt:
- return
-
- fp.write("struct _isr_table_entry __sw_isr_table _sw_isr_table[%d] = {\n"
- % self.__nv)
-
- level2_offset = self.__config.get_irq_baseoffset(2)
- level3_offset = self.__config.get_irq_baseoffset(3)
-
- for i in range(self.__nv):
- if len(self.__swt[i]) == 0:
- # Not used interrupt
- param = "0x0"
- func = self.__config.swt_spurious_handler
- elif len(self.__swt[i]) == 1:
- # Single interrupt
- param = "{0:#x}".format(self.__swt[i][0][0])
- func = self.__swt[i][0][1]
- else:
- # Shared interrupt
- param = "&z_shared_sw_isr_table[{0}]".format(i)
- func = self.__config.swt_shared_handler
-
- if isinstance(func, int):
- func_as_string = "{0:#x}".format(func)
- else:
- func_as_string = func
-
- if level2_offset is not None and i == level2_offset:
- fp.write("\t/* Level 2 interrupts start here (offset: {}) */\n".
- format(level2_offset))
- if level3_offset is not None and i == level3_offset:
- fp.write("\t/* Level 3 interrupts start here (offset: {}) */\n".
- format(level3_offset))
-
- fp.write("\t{{(const void *){0}, (ISR){1}}}, /* {2} */\n".format(param, func_as_string, i))
- fp.write("};\n")
-
-
def get_symbols(obj):
for section in obj.iter_sections():
if isinstance(section, SymbolTableSection):
@@ -554,6 +286,12 @@
help="Print additional debugging information")
parser.add_argument("-o", "--output-source", required=True,
help="Output source file")
+ parser.add_argument("-l", "--linker-output-files",
+ nargs=2,
+ metavar=("vector_table_link", "software_interrupt_link"),
+ help="Output linker files. "
+ "Used only if CONFIG_ISR_TABLES_LOCAL_DECLARATION is enabled. "
+ "In other case empty file would be generated.")
parser.add_argument("-k", "--kernel", required=True,
help="Zephyr kernel image")
parser.add_argument("-s", "--sw-isr-table", action="store_true",
@@ -576,11 +314,31 @@
config = gen_isr_config(args, get_symbols(kernel), log)
intlist_data = read_intList_sect(kernel, config.get_intlist_snames())
- parser = gen_isr_parser(intlist_data, config, log)
+ if config.check_sym("CONFIG_ISR_TABLES_LOCAL_DECLARATION"):
+ sys.stdout.write(
+ "Warning: The EXPERIMENTAL ISR_TABLES_LOCAL_DECLARATION feature selected\n")
+ parser_module = importlib.import_module('gen_isr_tables_parser_local')
+ parser = parser_module.gen_isr_parser(intlist_data, config, log)
+ else:
+ parser_module = importlib.import_module('gen_isr_tables_parser_carrays')
+ parser = parser_module.gen_isr_parser(intlist_data, config, log)
with open(args.output_source, "w") as fp:
parser.write_source(fp)
+ if args.linker_output_files is not None:
+ with open(args.linker_output_files[0], "w") as fp_vt, \
+ open(args.linker_output_files[1], "w") as fp_swi:
+ if hasattr(parser, 'write_linker_vt'):
+ parser.write_linker_vt(fp_vt)
+ else:
+ log.debug("Chosen parser does not support vector table linker file")
+ fp_vt.write('/* Empty */\n')
+ if hasattr(parser, 'write_linker_swi'):
+ parser.write_linker_swi(fp_swi)
+ else:
+ log.debug("Chosen parser does not support software interrupt linker file")
+ fp_swi.write('/* Empty */\n')
if __name__ == "__main__":
main()
diff --git a/scripts/build/gen_isr_tables_parser_carrays.py b/scripts/build/gen_isr_tables_parser_carrays.py
new file mode 100644
index 0000000..e13ef19
--- /dev/null
+++ b/scripts/build/gen_isr_tables_parser_carrays.py
@@ -0,0 +1,292 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2017 Intel Corporation
+# Copyright (c) 2018 Foundries.io
+# Copyright (c) 2023 Nordic Semiconductor NA
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+import struct
+
+class gen_isr_parser:
+ source_header = """
+/* AUTO-GENERATED by gen_isr_tables.py, do not edit! */
+
+#include <zephyr/toolchain.h>
+#include <zephyr/linker/sections.h>
+#include <zephyr/sw_isr_table.h>
+#include <zephyr/arch/cpu.h>
+
+typedef void (* ISR)(const void *);
+"""
+
+ source_assembly_header = """
+#ifndef ARCH_IRQ_VECTOR_JUMP_CODE
+#error "ARCH_IRQ_VECTOR_JUMP_CODE not defined"
+#endif
+"""
+
+ def __init__(self, intlist_data, config, log):
+ """Initialize the parser.
+
+ The function prepares parser to work.
+ Parameters:
+ - intlist_data: The binnary data from intlist section
+ - config: The configuration object
+ - log: The logging object, has to have error and debug methods
+ """
+ self.__config = config
+ self.__log = log
+ intlist = self.__read_intlist(intlist_data)
+ self.__vt, self.__swt, self.__nv = self.__parse_intlist(intlist)
+
+ def __read_intlist(self, intlist_data):
+ """read a binary file containing the contents of the kernel's .intList
+ section. This is an instance of a header created by
+ include/zephyr/linker/intlist.ld:
+
+ struct {
+ uint32_t num_vectors; <- typically CONFIG_NUM_IRQS
+ struct _isr_list isrs[]; <- Usually of smaller size than num_vectors
+ }
+
+ Followed by instances of struct _isr_list created by IRQ_CONNECT()
+ calls:
+
+ struct _isr_list {
+ /** IRQ line number */
+ int32_t irq;
+ /** Flags for this IRQ, see ISR_FLAG_* definitions */
+ int32_t flags;
+ /** ISR to call */
+ void *func;
+ /** Parameter for non-direct IRQs */
+ const void *param;
+ };
+ """
+ intlist = {}
+ prefix = self.__config.endian_prefix()
+
+ # Extract header and the rest of the data
+ intlist_header_fmt = prefix + "II"
+ header_sz = struct.calcsize(intlist_header_fmt)
+ header_raw = struct.unpack_from(intlist_header_fmt, intlist_data, 0)
+ self.__log.debug(str(header_raw))
+
+ intlist["num_vectors"] = header_raw[0]
+ intlist["offset"] = header_raw[1]
+ intdata = intlist_data[header_sz:]
+
+ # Extract information about interrupts
+ if self.__config.check_64b():
+ intlist_entry_fmt = prefix + "iiQQ"
+ else:
+ intlist_entry_fmt = prefix + "iiII"
+
+ intlist["interrupts"] = [i for i in
+ struct.iter_unpack(intlist_entry_fmt, intdata)]
+
+ self.__log.debug("Configured interrupt routing")
+ self.__log.debug("handler irq flags param")
+ self.__log.debug("--------------------------")
+
+ for irq in intlist["interrupts"]:
+ self.__log.debug("{0:<10} {1:<3} {2:<3} {3}".format(
+ hex(irq[2]), irq[0], irq[1], hex(irq[3])))
+
+ return intlist
+
+ def __parse_intlist(self, intlist):
+ """All the intlist data are parsed into swt and vt arrays.
+
+ The vt array is prepared for hardware interrupt table.
+ Every entry in the selected position would contain None or the name of the function pointer
+ (address or string).
+
+ The swt is a little more complex. At every position it would contain an array of parameter and
+ function pointer pairs. If CONFIG_SHARED_INTERRUPTS is enabled there may be more than 1 entry.
+ If empty array is placed on selected position - it means that the application does not implement
+ this interrupt.
+
+ Parameters:
+ - intlist: The preprocessed list of intlist section content (see read_intlist)
+
+ Return:
+ vt, swt - parsed vt and swt arrays (see function description above)
+ """
+ nvec = intlist["num_vectors"]
+ offset = intlist["offset"]
+
+ if nvec > pow(2, 15):
+ raise ValueError('nvec is too large, check endianness.')
+
+ self.__log.debug('offset is ' + str(offset))
+ self.__log.debug('num_vectors is ' + str(nvec))
+
+ # Set default entries in both tables
+ if not(self.__config.args.sw_isr_table or self.__config.args.vector_table):
+ self.__log.error("one or both of -s or -V needs to be specified on command line")
+ if self.__config.args.vector_table:
+ vt = [None for i in range(nvec)]
+ else:
+ vt = None
+ if self.__config.args.sw_isr_table:
+ swt = [[] for i in range(nvec)]
+ else:
+ swt = None
+
+ # Process intlist and write to the tables created
+ for irq, flags, func, param in intlist["interrupts"]:
+ if self.__config.test_isr_direct(flags):
+ if not vt:
+ self.__log.error("Direct Interrupt %d declared with parameter 0x%x "
+ "but no vector table in use"
+ % (irq, param))
+ if param != 0:
+ self.__log.error("Direct irq %d declared, but has non-NULL parameter"
+ % irq)
+ if not 0 <= irq - offset < len(vt):
+ self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d"
+ % (irq - offset, offset, len(vt) - 1))
+ vt[irq - offset] = func
+ else:
+ # Regular interrupt
+ if not swt:
+ self.__log.error("Regular Interrupt %d declared with parameter 0x%x "
+ "but no SW ISR_TABLE in use"
+ % (irq, param))
+
+ table_index = self.__config.get_swt_table_index(offset, irq)
+
+ if not 0 <= table_index < len(swt):
+ self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d" %
+ (table_index, offset, len(swt) - 1))
+ if self.__config.check_shared_interrupts():
+ lst = swt[table_index]
+ if (param, func) in lst:
+ self.__log.error("Attempting to register the same ISR/arg pair twice.")
+ if len(lst) >= self.__config.get_sym("CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS"):
+ self.__log.error(f"Reached shared interrupt client limit. Maybe increase"
+ + f" CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS?")
+ else:
+ if len(swt[table_index]) > 0:
+ self.__log.error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})"
+ + f"\nExisting handler 0x{swt[table_index][0][1]:x}, new handler 0x{func:x}"
+ + "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?"
+ )
+ swt[table_index].append((param, func))
+
+ return vt, swt, nvec
+
+ def __write_code_irq_vector_table(self, fp):
+ fp.write(self.source_assembly_header)
+
+ fp.write("void __irq_vector_table __attribute__((naked)) _irq_vector_table(void) {\n")
+ for i in range(self.__nv):
+ func = self.__vt[i]
+
+ if func is None:
+ func = self.__config.vt_default_handler
+
+ if isinstance(func, int):
+ func_as_string = self.__config.get_sym_from_addr(func)
+ else:
+ func_as_string = func
+
+ fp.write("\t__asm(ARCH_IRQ_VECTOR_JUMP_CODE({}));\n".format(func_as_string))
+ fp.write("}\n")
+
+ def __write_address_irq_vector_table(self, fp):
+ fp.write("uintptr_t __irq_vector_table _irq_vector_table[%d] = {\n" % self.__nv)
+ for i in range(self.__nv):
+ func = self.__vt[i]
+
+ if func is None:
+ func = self.__config.vt_default_handler
+
+ if isinstance(func, int):
+ fp.write("\t{},\n".format(func))
+ else:
+ fp.write("\t((uintptr_t)&{}),\n".format(func))
+
+ fp.write("};\n")
+
+ def __write_shared_table(self, fp):
+ fp.write("struct z_shared_isr_table_entry __shared_sw_isr_table"
+ " z_shared_sw_isr_table[%d] = {\n" % self.__nv)
+
+ for i in range(self.__nv):
+ if self.__swt[i] is None:
+ client_num = 0
+ client_list = None
+ else:
+ client_num = len(self.__swt[i])
+ client_list = self.__swt[i]
+
+ if client_num <= 1:
+ fp.write("\t{ },\n")
+ else:
+ fp.write(f"\t{{ .client_num = {client_num}, .clients = {{ ")
+ for j in range(0, client_num):
+ routine = client_list[j][1]
+ arg = client_list[j][0]
+
+ fp.write(f"{{ .isr = (ISR){ hex(routine) if isinstance(routine, int) else routine }, "
+ f".arg = (const void *){hex(arg)} }},")
+
+ fp.write(" },\n},\n")
+
+ fp.write("};\n")
+
+ def write_source(self, fp):
+ fp.write(self.source_header)
+
+ if self.__config.check_shared_interrupts():
+ self.__write_shared_table(fp)
+
+ if self.__vt:
+ if self.__config.check_sym("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_ADDRESS"):
+ self.__write_address_irq_vector_table(fp)
+ elif self.__config.check_sym("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_CODE"):
+ self.__write_code_irq_vector_table(fp)
+ else:
+ self.__log.error("CONFIG_IRQ_VECTOR_TABLE_JUMP_BY_{ADDRESS,CODE} not set")
+
+ if not self.__swt:
+ return
+
+ fp.write("struct _isr_table_entry __sw_isr_table _sw_isr_table[%d] = {\n"
+ % self.__nv)
+
+ level2_offset = self.__config.get_irq_baseoffset(2)
+ level3_offset = self.__config.get_irq_baseoffset(3)
+
+ for i in range(self.__nv):
+ if len(self.__swt[i]) == 0:
+ # Not used interrupt
+ param = "0x0"
+ func = self.__config.swt_spurious_handler
+ elif len(self.__swt[i]) == 1:
+ # Single interrupt
+ param = "{0:#x}".format(self.__swt[i][0][0])
+ func = self.__swt[i][0][1]
+ else:
+ # Shared interrupt
+ param = "&z_shared_sw_isr_table[{0}]".format(i)
+ func = self.__config.swt_shared_handler
+
+ if isinstance(func, int):
+ func_as_string = "{0:#x}".format(func)
+ else:
+ func_as_string = func
+
+ if level2_offset is not None and i == level2_offset:
+ fp.write("\t/* Level 2 interrupts start here (offset: {}) */\n".
+ format(level2_offset))
+ if level3_offset is not None and i == level3_offset:
+ fp.write("\t/* Level 3 interrupts start here (offset: {}) */\n".
+ format(level3_offset))
+
+ fp.write("\t{{(const void *){0}, (ISR){1}}}, /* {2} */\n".format(param, func_as_string, i))
+ fp.write("};\n")
diff --git a/scripts/build/gen_isr_tables_parser_local.py b/scripts/build/gen_isr_tables_parser_local.py
new file mode 100644
index 0000000..91bd4a6
--- /dev/null
+++ b/scripts/build/gen_isr_tables_parser_local.py
@@ -0,0 +1,375 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2017 Intel Corporation
+# Copyright (c) 2018 Foundries.io
+# Copyright (c) 2023 Nordic Semiconductor NA
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+import struct
+
+class gen_isr_parser:
+ source_header = """
+/* AUTO-GENERATED by gen_isr_tables.py, do not edit! */
+
+#include <zephyr/toolchain.h>
+#include <zephyr/linker/sections.h>
+#include <zephyr/sw_isr_table.h>
+#include <zephyr/arch/cpu.h>
+
+"""
+
+ shared_isr_table_header = """
+
+/* For this parser to work, we have to be sure that shared interrupts table entry
+ * and the normal isr table entry have exactly the same layout
+ */
+BUILD_ASSERT(sizeof(struct _isr_table_entry)
+ ==
+ sizeof(struct z_shared_isr_table_entry),
+ "Shared ISR and ISR table entries layout do not match");
+BUILD_ASSERT(offsetof(struct _isr_table_entry, arg)
+ ==
+ offsetof(struct z_shared_isr_table_entry, arg),
+ "Shared ISR and ISR table entries layout do not match");
+BUILD_ASSERT(offsetof(struct _isr_table_entry, isr)
+ ==
+ offsetof(struct z_shared_isr_table_entry, isr),
+ "Shared ISR and ISR table entries layout do not match");
+
+"""
+
+ def __init__(self, intlist_data, config, log):
+ """Initialize the parser.
+
+ The function prepares parser to work.
+ Parameters:
+ - intlist_data: The binnary data from intlist section
+ - config: The configuration object
+ - log: The logging object, has to have error and debug methods
+ """
+ self.__config = config
+ self.__log = log
+ intlist = self.__read_intlist(intlist_data)
+ self.__vt, self.__swt, self.__nv, header = self.__parse_intlist(intlist)
+ self.__swi_table_entry_size = header["swi_table_entry_size"]
+ self.__shared_isr_table_entry_size = header["shared_isr_table_entry_size"]
+ self.__shared_isr_client_num_offset = header["shared_isr_client_num_offset"]
+
+ def __read_intlist(self, intlist_data):
+ """read an intList section from the elf file.
+ This is version 2 of a header created by include/zephyr/linker/intlist.ld:
+
+ struct {
+ uint32_t num_vectors; <- typically CONFIG_NUM_IRQS
+ uint8_t stream[]; <- the stream with the interrupt data
+ };
+
+ The stream is contained from variable length records in a form:
+
+ struct _isr_list_sname {
+ /** IRQ line number */
+ int32_t irq;
+ /** Flags for this IRQ, see ISR_FLAG_* definitions */
+ int32_t flags;
+ /** The section name */
+ const char sname[];
+ };
+
+ The flexible array member here (sname) contains the name of the section where the structure
+ with interrupt data is located.
+ It is always Null-terminated string thus we have to search through the input data for the
+ structure end.
+
+ """
+ intlist = {}
+ prefix = self.__config.endian_prefix()
+
+ # Extract header and the rest of the data
+ intlist_header_fmt = prefix + "IIIII"
+ header_sz = struct.calcsize(intlist_header_fmt)
+ header_raw = struct.unpack_from(intlist_header_fmt, intlist_data, 0)
+ self.__log.debug(str(header_raw))
+
+ intlist["num_vectors"] = header_raw[0]
+ intlist["offset"] = header_raw[1]
+ intlist["swi_table_entry_size"] = header_raw[2]
+ intlist["shared_isr_table_entry_size"] = header_raw[3]
+ intlist["shared_isr_client_num_offset"] = header_raw[4]
+
+ intdata = intlist_data[header_sz:]
+
+ # Extract information about interrupts
+ intlist_entry_fmt = prefix + "ii"
+ entry_sz = struct.calcsize(intlist_entry_fmt)
+ intlist["interrupts"] = []
+
+ while len(intdata) > entry_sz:
+ entry_raw = struct.unpack_from(intlist_entry_fmt, intdata, 0)
+ intdata = intdata[entry_sz:]
+ null_idx = intdata.find(0)
+ if null_idx < 0:
+ self.__log.error("Cannot find sname null termination at IRQ{}".format(entry_raw[0]))
+ bname = intdata[:null_idx]
+ # Next structure starts with 4B alignment
+ next_idx = null_idx + 1
+ next_idx = (next_idx + 3) & ~3
+ intdata = intdata[next_idx:]
+ sname = bname.decode()
+ intlist["interrupts"].append([entry_raw[0], entry_raw[1], sname])
+ self.__log.debug("Unpacked IRQ{}, flags: {}, sname: \"{}\"\n".format(
+ entry_raw[0], entry_raw[1], sname))
+
+ # If any data left at the end - it has to be all the way 0 - this is just a check
+ if (len(intdata) and not all([d == 0 for d in intdata])):
+ self.__log.error("Non-zero data found at the end of the intList data.\n")
+
+ self.__log.debug("Configured interrupt routing with linker")
+ self.__log.debug("irq flags sname")
+ self.__log.debug("--------------------------")
+
+ for irq in intlist["interrupts"]:
+ self.__log.debug("{0:<3} {1:<5} {2}".format(
+ hex(irq[0]), irq[1], irq[2]))
+
+ return intlist
+
+ def __parse_intlist(self, intlist):
+ """All the intlist data are parsed into swt and vt arrays.
+
+ The vt array is prepared for hardware interrupt table.
+ Every entry in the selected position would contain None or the name of the function pointer
+ (address or string).
+
+ The swt is a little more complex. At every position it would contain an array of parameter and
+ function pointer pairs. If CONFIG_SHARED_INTERRUPTS is enabled there may be more than 1 entry.
+ If empty array is placed on selected position - it means that the application does not implement
+ this interrupt.
+
+ Parameters:
+ - intlist: The preprocessed list of intlist section content (see read_intlist)
+
+ Return:
+ vt, swt - parsed vt and swt arrays (see function description above)
+ """
+ nvec = intlist["num_vectors"]
+ offset = intlist["offset"]
+ header = {
+ "swi_table_entry_size": intlist["swi_table_entry_size"],
+ "shared_isr_table_entry_size": intlist["shared_isr_table_entry_size"],
+ "shared_isr_client_num_offset": intlist["shared_isr_client_num_offset"]
+ }
+
+ if nvec > pow(2, 15):
+ raise ValueError('nvec is too large, check endianness.')
+
+ self.__log.debug('offset is ' + str(offset))
+ self.__log.debug('num_vectors is ' + str(nvec))
+
+ # Set default entries in both tables
+ if not(self.__config.args.sw_isr_table or self.__config.args.vector_table):
+ self.__log.error("one or both of -s or -V needs to be specified on command line")
+ if self.__config.args.vector_table:
+ vt = [None for i in range(nvec)]
+ else:
+ vt = None
+ if self.__config.args.sw_isr_table:
+ swt = [[] for i in range(nvec)]
+ else:
+ swt = None
+
+ # Process intlist and write to the tables created
+ for irq, flags, sname in intlist["interrupts"]:
+ if self.__config.test_isr_direct(flags):
+ if not 0 <= irq - offset < len(vt):
+ self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d" %
+ (irq - offset, offset, len(vt) - 1))
+ vt[irq - offset] = sname
+ else:
+ # Regular interrupt
+ if not swt:
+ self.__log.error("Regular Interrupt %d declared with section name %s "
+ "but no SW ISR_TABLE in use"
+ % (irq, sname))
+
+ table_index = self.__config.get_swt_table_index(offset, irq)
+
+ if not 0 <= table_index < len(swt):
+ self.__log.error("IRQ %d (offset=%d) exceeds the maximum of %d" %
+ (table_index, offset, len(swt) - 1))
+ # Check if the given section name does not repeat outside of current interrupt
+ for i in range(nvec):
+ if i == irq:
+ continue
+ if sname in swt[i]:
+ self.__log.error(("Attempting to register the same section name \"{}\"for" +
+ "different interrupts: {} and {}").format(sname, i, irq))
+ if self.__config.check_shared_interrupts():
+ lst = swt[table_index]
+ if len(lst) >= self.__config.get_sym("CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS"):
+ self.__log.error(f"Reached shared interrupt client limit. Maybe increase"
+ + f" CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS?")
+ else:
+ if len(swt[table_index]) > 0:
+ self.__log.error(f"multiple registrations at table_index {table_index} for irq {irq} (0x{irq:x})"
+ + f"\nExisting section {swt[table_index]}, new section {sname}"
+ + "\nHas IRQ_CONNECT or IRQ_DIRECT_CONNECT accidentally been invoked on the same irq multiple times?"
+ )
+ swt[table_index].append(sname)
+
+ return vt, swt, nvec, header
+
+ @staticmethod
+ def __irq_spurious_section(irq):
+ return '.irq_spurious.0x{:x}'.format(irq)
+
+ @staticmethod
+ def __isr_generated_section(irq):
+ return '.isr_generated.0x{:x}'.format(irq)
+
+ @staticmethod
+ def __shared_entry_section(irq, ent):
+ return '.isr_shared.0x{:x}_0x{:x}'.format(irq, ent)
+
+ @staticmethod
+ def __shared_client_num_section(irq):
+ return '.isr_shared.0x{:x}_client_num'.format(irq)
+
+ def __isr_spurious_entry(self, irq):
+ return '_Z_ISR_TABLE_ENTRY({irq}, {func}, NULL, "{sect}");'.format(
+ irq = irq,
+ func = self.__config.swt_spurious_handler,
+ sect = self.__isr_generated_section(irq)
+ )
+
+ def __isr_shared_entry(self, irq):
+ return '_Z_ISR_TABLE_ENTRY({irq}, {func}, {arg}, "{sect}");'.format(
+ irq = irq,
+ arg = '&{}[{}]'.format(self.__config.shared_array_name, irq),
+ func = self.__config.swt_shared_handler,
+ sect = self.__isr_generated_section(irq)
+ )
+
+ def __irq_spurious_entry(self, irq):
+ return '_Z_ISR_DIRECT_TABLE_ENTRY({irq}, {func}, "{sect}");'.format(
+ irq = irq,
+ func = self.__config.vt_default_handler,
+ sect = self.__irq_spurious_section(irq)
+ )
+
+ def __write_isr_handlers(self, fp):
+ for i in range(self.__nv):
+ if len(self.__swt[i]) <= 0:
+ fp.write(self.__isr_spurious_entry(i) + '\n')
+ elif len(self.__swt[i]) > 1:
+ # Connect to shared handlers
+ fp.write(self.__isr_shared_entry(i) + '\n')
+ else:
+ fp.write('/* ISR: {} implemented in app in "{}" section. */\n'.format(
+ i, self.__swt[i][0]))
+
+ def __write_irq_handlers(self, fp):
+ for i in range(self.__nv):
+ if self.__vt[i] is None:
+ fp.write(self.__irq_spurious_entry(i) + '\n')
+ else:
+ fp.write('/* ISR: {} implemented in app. */\n'.format(i))
+
+ def __write_shared_handlers(self, fp):
+ fp.write("extern struct z_shared_isr_table_entry "
+ "{}[{}];\n".format(self.__config.shared_array_name, self.__nv))
+
+ shared_cnt = self.__config.get_sym('CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS')
+ for i in range(self.__nv):
+ swt_len = len(self.__swt[i])
+ for j in range(shared_cnt):
+ if (swt_len <= 1) or (swt_len <= j):
+ # Add all unused entry
+ fp.write('static Z_DECL_ALIGN(struct _isr_table_entry)\n' +
+ '\tZ_GENERIC_SECTION({})\n'.format(self.__shared_entry_section(i, j)) +
+ '\t__used isr_shared_empty_entry_0x{:x}_0x{:x} = {{\n'.format(i, j) +
+ '\t\t.arg = (const void *)NULL,\n' +
+ '\t\t.isr = (void (*)(const void *))(void *)0\n' +
+ '};\n'
+ )
+ else:
+ # Add information about entry implemented by application
+ fp.write('/* Shared isr {} entry {} implemented in "{}" section*/\n'.format(
+ i, j, self.__swt[i][j]))
+
+ # Add information about clients count
+ fp.write(('static size_t Z_GENERIC_SECTION({}) __used\n' +
+ 'isr_shared_client_num_0x{:x} = {};\n\n').format(
+ self.__shared_client_num_section(i),
+ i,
+ 0 if swt_len < 2 else swt_len)
+ )
+
+ def write_source(self, fp):
+ fp.write(self.source_header)
+
+ if self.__vt:
+ self.__write_irq_handlers(fp)
+
+ if not self.__swt:
+ return
+
+ if self.__config.check_shared_interrupts():
+ self.__write_shared_handlers(fp)
+
+ self.__write_isr_handlers(fp)
+
+ def __write_linker_irq(self, fp):
+ fp.write('{} = .;\n'.format(self.__config.irq_vector_array_name))
+ for i in range(self.__nv):
+ if self.__vt[i] is None:
+ sname = self.__irq_spurious_section(i)
+ else:
+ sname = self.__vt[i]
+ fp.write('KEEP(*("{}"))\n'.format(sname))
+
+ def __write_linker_shared(self, fp):
+ fp.write(". = ALIGN({});\n".format(self.__shared_isr_table_entry_size))
+ fp.write('{} = .;\n'.format(self.__config.shared_array_name))
+ shared_cnt = self.__config.get_sym('CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS')
+ client_num_pads = self.__shared_isr_client_num_offset - \
+ shared_cnt * self.__swi_table_entry_size
+ if client_num_pads < 0:
+ self.__log.error("Invalid __shared_isr_client_num_offset header value")
+ for i in range(self.__nv):
+ swt_len = len(self.__swt[i])
+ # Add all entries
+ for j in range(shared_cnt):
+ if (swt_len <= 1) or (swt_len <= j):
+ fp.write('KEEP(*("{}"))\n'.format(self.__shared_entry_section(i, j)))
+ else:
+ sname = self.__swt[i][j]
+ if (j != 0) and (sname in self.__swt[i][0:j]):
+ fp.write('/* Repetition of "{}" section */\n'.format(sname))
+ else:
+ fp.write('KEEP(*("{}"))\n'.format(sname))
+ fp.write('. = . + {};\n'.format(client_num_pads))
+ fp.write('KEEP(*("{}"))\n'.format(self.__shared_client_num_section(i)))
+ fp.write(". = ALIGN({});\n".format(self.__shared_isr_table_entry_size))
+
+ def __write_linker_isr(self, fp):
+ fp.write(". = ALIGN({});\n".format(self.__swi_table_entry_size))
+ fp.write('{} = .;\n'.format(self.__config.sw_isr_array_name))
+ for i in range(self.__nv):
+ if (len(self.__swt[i])) == 1:
+ sname = self.__swt[i][0]
+ else:
+ sname = self.__isr_generated_section(i)
+ fp.write('KEEP(*("{}"))\n'.format(sname))
+
+ def write_linker_vt(self, fp):
+ if self.__vt:
+ self.__write_linker_irq(fp)
+
+ def write_linker_swi(self, fp):
+ if self.__swt:
+ self.__write_linker_isr(fp)
+
+ if self.__config.check_shared_interrupts():
+ self.__write_linker_shared(fp)