#!/usr/bin/env python2 # Script to handle build time requests embedded in C code. # # Copyright (C) 2016-2021 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 "board/irq.h" #include "board/pgm.h" #include "compiler.h" #include "initial_pins.h" """ def error(msg): sys.stderr.write(msg + "\n") sys.exit(-1) Handlers = [] ###################################################################### # C call list generation ###################################################################### # Create dynamic C functions that call a list of other C functions class HandleCallList: def __init__(self): self.call_lists = {'ctr_run_initfuncs': []} self.ctr_dispatch = { '_DECL_CALLLIST': self.decl_calllist } def decl_calllist(self, req): funcname, callname = req.split()[1:] self.call_lists.setdefault(funcname, []).append(callname) def update_data_dictionary(self, data): pass def generate_code(self, options): code = [] for funcname, funcs in self.call_lists.items(): func_code = [' extern void %s(void);\n %s();' % (f, f) for f in funcs] if funcname == 'ctr_run_taskfuncs': add_poll = ' irq_poll();\n' func_code = [add_poll + fc for fc in func_code] func_code.append(add_poll) fmt = """ void %s(void) { %s } """ code.append(fmt % (funcname, "\n".join(func_code).strip())) return "".join(code) Handlers.append(HandleCallList()) ###################################################################### # 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 lookup_pin(self, pin): avail_pins = HandlerEnumerations.get_available_pins() gpio = avail_pins.get(pin) if gpio is None: error("Pin %s is not available for this build" % (pin,)) reserved_pins = self.get_reserved_pins() if pin in reserved_pins: error("Pin %s is reserved by an active MCU peripheral" % (pin,)) return gpio def generate_code(self, options): return "" HandlerConstants = HandleConstants() Handlers.append(HandlerConstants) ###################################################################### # Initial pins ###################################################################### class HandleInitialPins: def __init__(self): self.initial_pins = [] self.ctr_dispatch = { 'DECL_INITIAL_PINS': self.decl_initial_pins } def decl_initial_pins(self, req): pins = req.split(None, 1)[1].strip() if pins.startswith('"') and pins.endswith('"'): pins = pins[1:-1] if pins: self.initial_pins = [p.strip() for p in pins.split(',')] HandlerConstants.decl_constant_str( "_DECL_CONSTANT_STR INITIAL_PINS " + ','.join(self.initial_pins)) def map_pins(self): if not self.initial_pins: return [] out = [] for p in self.initial_pins: flag = "IP_OUT_HIGH" if p.startswith('!'): flag = "0" p = p[1:].strip() gpio = HandlerConstants.lookup_pin(p) out.append("\n {%d, %s}, // %s" % (gpio, flag, p)) return out def generate_code(self, options): out = self.map_pins() fmt = """ const struct initial_pin_s initial_pins[] PROGMEM = {%s }; const int initial_pins_size PROGMEM = ARRAY_SIZE(initial_pins); """ return fmt % (''.join(out),) Handlers.append(HandleInitialPins()) ###################################################################### # 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.pin = None self.ctr_dispatch = { 'DECL_LED_PIN': self.decl_led_pin } def decl_led_pin(self, req): pin = req.split(None, 1)[1].strip() if pin.startswith('"') and pin.endswith('"'): pin = pin[1:-1].strip() self.pin = pin def generate_code(self, options): led_gpio = led_gpio_high = 0 pin = self.pin if pin: led_gpio_high = 1 if pin[0] == "!": led_gpio_high = 0 pin = pin[1:].strip() led_gpio = HandlerConstants.lookup_pin(pin) fmt = """ uint32_t led_gpio = %d, led_gpio_high = %d; // "%s" """ return fmt % (led_gpio, led_gpio_high, self.pin) Handlers.append(HandleStatusLED()) ###################################################################### # Button entry functionality ###################################################################### class HandleButton: def __init__(self): self.pin = None self.ctr_dispatch = { 'DECL_BUTTON': self.decl_button } def decl_button(self, req): pin = req.split(None, 1)[1].strip() if pin.startswith('"') and pin.endswith('"'): pin = pin[1:-1].strip() self.pin = pin def generate_code(self, options): button_gpio = button_high = button_pullup = 0 pin = self.pin if pin: if pin[0] in "^~": button_pullup = 1 if pin[0] == "~": button_pullup = -1 pin = pin[1:].strip() button_high = 1 if pin[0] == "!": button_high = 0 pin = pin[1:].strip() button_gpio = HandlerConstants.lookup_pin(pin) fmt = """ int32_t button_gpio = %d, button_high = %d, button_pullup = %d; // "%s" """ return fmt % (button_gpio, button_high, button_pullup, self.pin) Handlers.append(HandleButton()) ###################################################################### # Main code ###################################################################### def main(): usage = "%prog [options] " opts = optparse.OptionParser(usage) 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, 'r') 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, 'w') f.write(code) f.close() if __name__ == '__main__': main()