293 lines
11 KiB
Python
293 lines
11 KiB
Python
#!/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")
|