207 lines
6.8 KiB
Python
Executable File
207 lines
6.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# This file is part of the libopencm3 project.
|
|
#
|
|
# Copyright (C) 2012 chrysn <chrysn@fsfe.org>
|
|
#
|
|
# This library is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Lesser General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This library is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
"""Generate an nvic.h header from a small JSON file describing the interrupt
|
|
numbers.
|
|
|
|
Code generation is chosen here because the resulting C code needs to be very
|
|
repetetive (definition of the IRQ numbers, function prototypes, weak fallback
|
|
definition and vector table definition), all being very repetitive. No portable
|
|
method to achieve the same thing with C preprocessor is known to the author.
|
|
(Neither is any non-portable method, for that matter.)"""
|
|
|
|
import sys
|
|
import os
|
|
import os.path
|
|
import json
|
|
from pathlib import Path
|
|
from typing import List
|
|
|
|
template_nvic_h = '''\
|
|
/* This file is part of the libopencm3 project.
|
|
*
|
|
* It was generated by the irq2nvic_h script from {sourcefile}
|
|
*/
|
|
|
|
#ifndef {includeguard}
|
|
#define {includeguard}
|
|
|
|
#include <libopencm3/cm3/nvic.h>
|
|
|
|
/** @defgroup CM3_nvic_defines_irqs User interrupts for {partname_humanreadable}
|
|
@ingroup CM3_nvic_defines
|
|
|
|
@{{*/
|
|
|
|
{irqdefinitions}
|
|
|
|
#define NVIC_IRQ_COUNT {irqcount}
|
|
|
|
/**@}}*/
|
|
|
|
/** @defgroup CM3_nvic_isrprototypes_{partname_doxygen} User interrupt service routines (ISR) prototypes for {partname_humanreadable}
|
|
@ingroup CM3_nvic_isrprototypes
|
|
|
|
@{{*/
|
|
|
|
BEGIN_DECLS
|
|
|
|
{isrprototypes}
|
|
|
|
END_DECLS
|
|
|
|
/**@}}*/
|
|
|
|
#endif /* {includeguard} */
|
|
'''
|
|
|
|
template_vector_nvic_c = '''\
|
|
/* This file is part of the libopencm3 project.
|
|
*
|
|
* It was generated by the irq2nvic_h script.
|
|
*
|
|
* This part needs to get included in the compilation unit where
|
|
* blocking_handler gets defined due to the way #pragma works.
|
|
*/
|
|
|
|
/** @defgroup CM3_nvic_isrdecls_{partname_doxygen} User interrupt service routines (ISR) defaults for {partname_humanreadable}
|
|
@ingroup CM3_nvic_isrdecls
|
|
|
|
@{{*/
|
|
|
|
{isrdecls}
|
|
|
|
/**@}}*/
|
|
|
|
/* Initialization template for the interrupt vector table. This definition is
|
|
* used by the startup code generator (vector.c) to set the initial values for
|
|
* the interrupt handling routines to the chip family specific _isr weak
|
|
* symbols. */
|
|
|
|
#define IRQ_HANDLERS \\
|
|
{vectortableinitialization}
|
|
'''
|
|
|
|
template_cmsis_h = '''\
|
|
/* This file is part of the libopencm3 project.
|
|
*
|
|
* It was generated by the irq2nvic_h script.
|
|
*
|
|
* These definitions bend every interrupt handler that is defined CMSIS style
|
|
* to the weak symbol exported by libopencm3.
|
|
*/
|
|
|
|
{cmsisbends}
|
|
'''
|
|
|
|
def convert(infile, outfile_nvic, outfile_vectornvic, outfile_cmsis):
|
|
data = json.load(infile)
|
|
|
|
irq2name = list(enumerate(data['irqs']) if isinstance(data['irqs'], list) else data['irqs'].items())
|
|
irqnames = [v for (k,v) in irq2name]
|
|
|
|
if isinstance(data['irqs'], list):
|
|
data['irqcount'] = len(irq2name)
|
|
else:
|
|
data['irqcount'] = max([int(x) for x in data['irqs'].keys()]) + 1
|
|
|
|
data['irqdefinitions'] = "\n".join('#define NVIC_%s_IRQ %d'%(v.upper(),int(k)) for (k,v) in irq2name)
|
|
data['isrprototypes'] = "\n".join('void %s_isr(void);'%name.lower() for name in irqnames)
|
|
data['isrdecls'] = "\n".join('void %s_isr(void) __attribute__((weak, alias("blocking_handler")));'%name.lower() for name in irqnames)
|
|
data['vectortableinitialization'] = ', \\\n '.join('[NVIC_%s_IRQ] = %s_isr'%(name.upper(), name.lower()) for name in irqnames)
|
|
data['cmsisbends'] = "\n".join("#define %s_IRQHandler %s_isr"%(name.upper(), name.lower()) for name in irqnames)
|
|
data['sourcefile'] = infile.name
|
|
|
|
outfile_nvic.write(template_nvic_h.format(**data))
|
|
outfile_vectornvic.write(template_vector_nvic_c.format(**data))
|
|
outfile_cmsis.write(template_cmsis_h.format(**data))
|
|
|
|
def makeparentdir(filename: Path):
|
|
try:
|
|
filename.parent.mkdir(parents = True)
|
|
except OSError:
|
|
# where is my 'mkdir -p'?
|
|
pass
|
|
|
|
def needs_update(infiles: List[Path], outfiles: List[Path]):
|
|
timestamp = lambda filename: filename.stat().st_mtime
|
|
return any(not o.exists() for o in outfiles) or max(map(timestamp, infiles)) > min(map(timestamp, outfiles))
|
|
|
|
def main():
|
|
# If the tool's been invoked in remove mode, set up for that
|
|
if sys.argv[1] == '--remove':
|
|
remove = True
|
|
del sys.argv[1]
|
|
else:
|
|
remove = False
|
|
|
|
# If the tool's been invoked with the output directory explicitly noted, use that for cwd
|
|
if sys.argv[1] == '--builddir':
|
|
cwd = Path(sys.argv[2])
|
|
del sys.argv[1:3]
|
|
else:
|
|
cwd = Path.cwd()
|
|
|
|
# Check to make sure infile refers to a valid irq.json in the tree
|
|
libopencm3_root = Path(__file__).parent.parent.resolve()
|
|
infile = Path(sys.argv[1]).resolve()
|
|
root_path = libopencm3_root.parts
|
|
infile_path = infile.parts[:len(root_path)]
|
|
# Check that the file is a irq.json and it exists
|
|
if root_path != infile_path or infile.name != 'irq.json' or not infile.exists():
|
|
raise ValueError("Argument must match ./include/libopencm3/**/irq.json")
|
|
# Now construct the paths to the new files
|
|
# Start by chopping off the 'irq.json' component
|
|
path_parts = infile.parts[:-1]
|
|
# Now iterate backwards till we find the index of 'include' before that
|
|
include_index = len(path_parts) - 1
|
|
while include_index > 0 and path_parts[include_index] != 'include':
|
|
include_index -= 1
|
|
# Having found the right start index, chop out the target name from the parts and construct a path segment
|
|
target = Path(*path_parts[include_index + 2:])
|
|
# Build the paths for all the output files
|
|
nvic_h = cwd / 'include' / 'libopencm3' / target / 'nvic.h'
|
|
vector_nvic_c = cwd / 'lib' / target / 'vector_nvic.c'
|
|
cmsis = cwd / 'include' / 'libopencmsis' / target / 'irqhandlers.h'
|
|
|
|
if remove:
|
|
if nvic_h.exists():
|
|
nvic_h.unlink()
|
|
if vector_nvic_c.exists():
|
|
vector_nvic_c.unlink()
|
|
if cmsis.exists():
|
|
cmsis.unlink()
|
|
sys.exit(0)
|
|
|
|
if not needs_update([Path(__file__), infile], [nvic_h, vector_nvic_c]):
|
|
sys.exit(0)
|
|
|
|
makeparentdir(nvic_h)
|
|
makeparentdir(vector_nvic_c)
|
|
makeparentdir(cmsis)
|
|
|
|
convert(infile.open('r'), nvic_h.open('w'), vector_nvic_c.open('w'), cmsis.open('w'))
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except ValueError as error:
|
|
print(error, file = sys.stderr)
|
|
sys.exit(1)
|