katapult/scripts/buildcommands.py
Arksine 2762299895 canboot: initial source commit
Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
2021-02-06 20:04:47 -05:00

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