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>
This commit is contained in:
Radoslaw Koppel 2023-12-02 23:09:11 +01:00 committed by Carles Cufí
parent b0c83f328c
commit 0ae48ecb58
5 changed files with 726 additions and 288 deletions

View file

@ -1228,10 +1228,11 @@ if(CONFIG_GEN_ISR_TABLES)
# 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

View file

@ -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 @@ struct int_list_header {
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

View file

@ -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 @@ class gen_isr_config:
"""
# 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):
@ -140,6 +143,18 @@ class gen_isr_config:
def vt_default_handler(self):
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 @@ class gen_isr_config:
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 @@ def parse_args():
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 @@ def main():
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()

View file

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

View file

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