| #!/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") |