#!/usr/bin/env python2 # Script to handle build time requests embedded in C code. # # Copyright (C) 2016-2018 Kevin O'Connor # # 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 #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] " 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()