mirror of
https://github.com/andreili/katapult.git
synced 2025-08-23 19:34:06 +02:00
255 lines
8.0 KiB
Python
255 lines
8.0 KiB
Python
#!/usr/bin/env python2
|
|
# Script to handle build time requests embedded in C code.
|
|
#
|
|
# Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
|
|
#
|
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
|
import sys, optparse, logging
|
|
|
|
FILEHEADER = """
|
|
/* DO NOT EDIT! This is an autogenerated file. See scripts/buildcommands.py. */
|
|
|
|
#include <stdint.h>
|
|
#include "compiler.h"
|
|
#include "board/misc.h"
|
|
#include "board/gpio.h"
|
|
"""
|
|
|
|
def error(msg):
|
|
sys.stderr.write(msg + "\n")
|
|
sys.exit(-1)
|
|
|
|
Handlers = []
|
|
|
|
######################################################################
|
|
# Enumeration and static string generation
|
|
######################################################################
|
|
|
|
STATIC_STRING_MIN = 2
|
|
|
|
# Generate a dynamic string to integer mapping
|
|
class HandleEnumerations:
|
|
def __init__(self):
|
|
self.static_strings = []
|
|
self.enumerations = {}
|
|
self.ctr_dispatch = {
|
|
'DECL_ENUMERATION': self.decl_enumeration,
|
|
'DECL_ENUMERATION_RANGE': self.decl_enumeration_range
|
|
}
|
|
def add_enumeration(self, enum, name, value):
|
|
enums = self.enumerations.setdefault(enum, {})
|
|
if name in enums and enums[name] != value:
|
|
error("Conflicting definition for enumeration '%s %s'" % (
|
|
enum, name))
|
|
enums[name] = value
|
|
def decl_enumeration(self, req):
|
|
enum, name, value = req.split()[1:]
|
|
self.add_enumeration(enum, name, int(value, 0))
|
|
def decl_enumeration_range(self, req):
|
|
enum, name, value, count = req.split()[1:]
|
|
self.add_enumeration(enum, name, (int(value, 0), int(count, 0)))
|
|
def get_available_pins(self):
|
|
avail_pins = {}
|
|
pin_enums = self.enumerations.get('pin')
|
|
if pin_enums is None:
|
|
error("No pin enumeration available")
|
|
for port_id, (offset, count) in pin_enums.items():
|
|
for i in range(count):
|
|
pin_name = "%s%d" % (port_id[:-1], i)
|
|
pin_num = i + offset
|
|
avail_pins[pin_name] = pin_num
|
|
return avail_pins
|
|
def generate_code(self, options):
|
|
return ""
|
|
|
|
|
|
HandlerEnumerations = HandleEnumerations()
|
|
Handlers.append(HandlerEnumerations)
|
|
|
|
|
|
######################################################################
|
|
# Constants
|
|
######################################################################
|
|
|
|
# Allow adding build time constants to the data dictionary
|
|
class HandleConstants:
|
|
def __init__(self):
|
|
self.constants = {}
|
|
self.ctr_dispatch = {
|
|
'DECL_CONSTANT': self.decl_constant,
|
|
'DECL_CONSTANT_STR': self.decl_constant_str,
|
|
}
|
|
self.reserved_pins = []
|
|
def set_value(self, name, value):
|
|
if name in self.constants and self.constants[name] != value:
|
|
error("Conflicting definition for constant '%s'" % name)
|
|
self.constants[name] = value
|
|
def decl_constant(self, req):
|
|
name, value = req.split()[1:]
|
|
self.set_value(name, int(value, 0))
|
|
def decl_constant_str(self, req):
|
|
name, value = req.split(None, 2)[1:]
|
|
value = value.strip()
|
|
if value.startswith('"') and value.endswith('"'):
|
|
value = value[1:-1]
|
|
self.set_value(name, value)
|
|
def get_reserved_pins(self):
|
|
reserved_pins = []
|
|
for name, val in self.constants.items():
|
|
if name.upper().startswith("RESERVE_PINS"):
|
|
rpins = [v.strip() for v in val.split(',') if v.strip()]
|
|
reserved_pins.extend(rpins)
|
|
return reserved_pins
|
|
def generate_code(self, options):
|
|
return ""
|
|
|
|
|
|
HandlerConstants = HandleConstants()
|
|
Handlers.append(HandlerConstants)
|
|
|
|
######################################################################
|
|
# ARM IRQ vector table generation
|
|
######################################################################
|
|
|
|
# Create ARM IRQ vector table from interrupt handler declarations
|
|
class Handle_arm_irq:
|
|
def __init__(self):
|
|
self.irqs = {}
|
|
self.ctr_dispatch = { 'DECL_ARMCM_IRQ': self.decl_armcm_irq }
|
|
def decl_armcm_irq(self, req):
|
|
func, num = req.split()[1:]
|
|
num = int(num, 0)
|
|
if num in self.irqs and self.irqs[num] != func:
|
|
error("Conflicting IRQ definition %d (old %s new %s)"
|
|
% (num, self.irqs[num], func))
|
|
self.irqs[num] = func
|
|
def generate_code(self, options):
|
|
armcm_offset = 16
|
|
if 1 - armcm_offset not in self.irqs:
|
|
# The ResetHandler was not defined - don't build VectorTable
|
|
return ""
|
|
max_irq = max(self.irqs.keys())
|
|
table = [" DefaultHandler,\n"] * (max_irq + armcm_offset + 1)
|
|
defs = []
|
|
for num, func in self.irqs.items():
|
|
if num < 1 - armcm_offset:
|
|
error("Invalid IRQ %d (%s)" % (num, func))
|
|
defs.append("extern void %s(void);\n" % (func,))
|
|
table[num + armcm_offset] = " %s,\n" % (func,)
|
|
table[0] = " &_stack_end,\n"
|
|
fmt = """
|
|
extern void DefaultHandler(void);
|
|
extern uint32_t _stack_end;
|
|
%s
|
|
const void *VectorTable[] __visible __section(".vector_table") = {
|
|
%s};
|
|
"""
|
|
return fmt % (''.join(defs), ''.join(table))
|
|
|
|
|
|
Handlers.append(Handle_arm_irq())
|
|
|
|
######################################################################
|
|
# Status LED Functionality
|
|
######################################################################
|
|
|
|
class HandleStatusLED:
|
|
def __init__(self):
|
|
self.ctr_dispatch = {}
|
|
def generate_code(self, options):
|
|
pin = options.led_pin
|
|
if not pin:
|
|
led_def = led_init = led_toggle = led_on = led_off = ""
|
|
else:
|
|
led_def = "static struct gpio_out led;"
|
|
led_init = "led = gpio_out_setup(%d, %d);"
|
|
led_toggle = "gpio_out_toggle(led);"
|
|
led_write = "gpio_out_write(led, %d);"
|
|
write_on = 1
|
|
if pin[0] == "!":
|
|
write_on = 0
|
|
pin = pin[1:].upper()
|
|
avail_pins = HandlerEnumerations.get_available_pins()
|
|
reserved_pins = HandlerConstants.get_reserved_pins()
|
|
pin_num = avail_pins.get(pin)
|
|
if pin_num is None:
|
|
error("Pin %s is not available for this build" % pin)
|
|
if pin in reserved_pins:
|
|
error("Pin %s is reserved by an active MCU peripheral" % pin)
|
|
led_init = led_init % (pin_num, write_on)
|
|
led_on = led_write % (write_on)
|
|
led_off = led_write % (int(not write_on))
|
|
fmt = """
|
|
|
|
%s
|
|
|
|
void __always_inline
|
|
led_init(void)
|
|
{
|
|
%s
|
|
}
|
|
|
|
void __always_inline
|
|
led_toggle(void)
|
|
{
|
|
%s
|
|
}
|
|
|
|
void __always_inline
|
|
led_on(void)
|
|
{
|
|
%s
|
|
}
|
|
|
|
void __always_inline
|
|
led_off(void)
|
|
{
|
|
%s
|
|
}
|
|
"""
|
|
return fmt % (led_def, led_init, led_toggle, led_on, led_off)
|
|
|
|
|
|
Handlers.append(HandleStatusLED())
|
|
|
|
######################################################################
|
|
# Main code
|
|
######################################################################
|
|
|
|
def main():
|
|
usage = "%prog [options] <cmd section file> <output.c>"
|
|
opts = optparse.OptionParser(usage)
|
|
opts.add_option("-l", "--ledpin", dest="led_pin", default="",
|
|
help="LED Status Pin")
|
|
opts.add_option("-v", action="store_true", dest="verbose",
|
|
help="enable debug messages")
|
|
options, args = opts.parse_args()
|
|
if len(args) != 2:
|
|
opts.error("Incorrect arguments")
|
|
incmdfile, outcfile = args
|
|
if options.verbose:
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
|
|
# Parse request file
|
|
ctr_dispatch = { k: v for h in Handlers for k, v in h.ctr_dispatch.items() }
|
|
f = open(incmdfile, 'rb')
|
|
data = f.read()
|
|
f.close()
|
|
for req in data.split('\n'):
|
|
req = req.lstrip()
|
|
if not req:
|
|
continue
|
|
cmd = req.split()[0]
|
|
if cmd not in ctr_dispatch:
|
|
error("Unknown build time command '%s'" % cmd)
|
|
ctr_dispatch[cmd](req)
|
|
|
|
# Write output
|
|
code = "".join([FILEHEADER] + [h.generate_code(options) for h in Handlers])
|
|
f = open(outcfile, 'wb')
|
|
f.write(code)
|
|
f.close()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|