#!/usr/bin/python import argparse import collections import csv import pipes import sys try: import openpyxl except ImportError: openpyxl = None class ParseError(Exception): pass ''' Representation of one pin in the GPIO config ''' class BasePin(object): default_drive = 'X1' valid_pupdn = set(["PULL_UP", "PULL_DOWN", ""]) valid_drive_strength = set() def __init__(self, gpio_pin_number): self.gpio_pin_number = int(gpio_pin_number) self.bump_name = None self.net_name = None self.function = None self.drive_strength = 'X1' self.pupd = None self.bus_hold = None self.input_mode = None self.row_number = None self.slew_rate = '' self.rom = None def to_pinconfig_line(self): if None in (self.bump_name, self.net_name, self.function, self.drive_strength, self.pupd): raise ParseError, "Some attributes were unset for pin %d: %s" % (self.gpio_pin_number, (self.bump_name, self.net_name, self.function, self.drive_strength, self.pupd)) if self.drive_strength == self.default_drive: drive_strength = '' else: drive_strength = "DRIVE_{}".format(self.drive_strength) if self.slew_rate == '': slew_rate = '' else: slew_rate = "{}_SLEW".format(self.slew_rate) non_empty_props = [i for i in [self.function, self.pupd, drive_strength, slew_rate, self.bus_hold, self.input_mode] if i] args = " | ".join(non_empty_props) + "," return [args, "// {:>3} : {}".format(self.gpio_pin_number, self.bump_name), "-> " + self.net_name] def set_function(self, fn): cfg_map = { 'in': 'CFG_IN', 'input': 'CFG_IN', 'out0': 'CFG_OUT_0', 'out 0': 'CFG_OUT_0', 'out1': 'CFG_OUT_1', 'out 1': 'CFG_OUT_1', 'alt func 0': 'CFG_FUNC0', 'alt func 1': 'CFG_FUNC1', 'alt func 2': 'CFG_FUNC2', 'int rising': 'CFG_IN', 'int falling': 'CFG_IN', 'int any': 'CFG_IN', 'disable': 'CFG_DISABLED', 'disabled': 'CFG_DISABLED', # The numbering offset isn't a typo, these are legacy 'function 1': 'CFG_FUNC0', 'function 2': 'CFG_FUNC1', 'function 3': 'CFG_FUNC2', } self.function = cfg_map[fn.lower()] def set_net_name(self, name): self.net_name = name.strip() def set_bump_name(self, name): self.bump_name = name.strip() def set_drive_strength(self, strength): if strength == '': strength = self.default_drive else: strength = strength.upper() if strength.isdigit(): strength = self.drive_strength_prefix + strength if not strength in self.valid_drive_strength: sorted_drive_strengths = sorted(self.valid_drive_strength, key=lambda item: (int(item[1:]))) raise ParseError, "drive strength '{}' not valid (options are {})".format(strength.upper(), sorted_drive_strengths) self.drive_strength = strength def set_pupd(self, pupd): pupd_map = { 'pullup strong' : 'PULL_UP_STRONG', 'spu' : 'PULL_UP_STRONG', 'pullup': 'PULL_UP', 'pu': 'PULL_UP', 'pulldown': 'PULL_DOWN', 'pd': 'PULL_DOWN', 'disable': '', '': ''} if not pupd_map[pupd.lower()] in self.valid_pupdn: pretty_pupdn = filter(lambda x : pupd_map[x] in self.valid_pupdn , pupd_map.keys()) raise ParseError, "pu/pd value '{}' not valid (options are {})".format(pupd, pretty_pupdn) self.pupd = pupd_map[pupd.lower()] class PinV1(BasePin): valid_drive_strength = set(["X1", "X2", "X3", "X4"]) drive_strength_prefix = 'X' def set_bus_hold(self, hold): bus_hold_map = { '': '', '0': '', None: '', 'y': 'BUS_HOLD', 'yes': 'BUS_HOLD', '1': 'BUS_HOLD'} self.bus_hold = bus_hold_map[hold.lower()] class PinV2(BasePin): valid_drive_strength = set(["X1", "X2", "X4", "X6"]) drive_strength_prefix = 'X' def set_bus_hold(self, hold): bus_hold_map = { '': '', '0': '', None: '', 'y': 'BUS_HOLD', 'yes': 'BUS_HOLD', '1': 'BUS_HOLD'} self.bus_hold = bus_hold_map[hold.lower()] class PinV3(BasePin): valid_drive_strength = set(["X1", "X2", "X4", "X6"]) drive_strength_prefix = 'X' def set_slew_rate(self, slew): slew_map = { '': '', None: '', "fast": "FAST", "slow": "SLOW" } self.slew_rate = slew_map[slew.lower()] class PinV4(PinV3): def set_input_mode(self, mode): mode_map = { '': '', None: '', 'cmos': '', 'schmitt': 'INPUT_SCHMITT', 'Schmitt': 'INPUT_SCHMITT'} self.input_mode = mode_map[mode] class PinV5(PinV4): default_drive = 'S0' valid_drive_strength = set(["S0", "S1", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S10", "S11", "S12", "S13", "S14", "S15"]) drive_strength_prefix = 'S' valid_pupdn = set(["PULL_UP_STRONG", "PULL_UP", "PULL_DOWN", ""]) def set_slew_rate(self, slew): slew_map = { '': '', None: '', "fast": "FAST", "slow": "SLOW", "very fast": "VERY_FAST" } self.slew_rate = slew_map[slew.lower()] class ReservedPin(object): def to_pinconfig_line(self): return ["CFG_DISABLED,"] class GPIODomainInfo(object): def __init__(self, offset, count, reserved): self.id_offset = offset self.pin_count = count self.reserved_pins = reserved class GPIODomain(object): def __init__(self, info, pin_class): self.id_offset = info.id_offset self.pin_count = info.pin_count self.reserved_pins = info.reserved_pins # GPIO domains must start and end on 8-pin boundaries assert self.id_offset % 8 == 0 if self.pin_count % 8 != 0: new_pin_count = self.pin_count + 8 - self.pin_count % 8 self.reserved_pins |= set(range(self.id_offset + self.pin_count, self.id_offset + new_pin_count)) self.pin_count = new_pin_count self.pins = [None] * self.pin_count for i in range(self.pin_count): if not i + self.id_offset in self.reserved_pins: pin = pin_class(i) pin.set_bump_name("UNSPECIFIED") pin.set_net_name("UNSPECIFIED") pin.set_function("disable") pin.set_pupd("disable") pin.set_drive_strength('') else: pin = ReservedPin() self.pins[i] = pin def has_pin_id(self, pin_id): return pin_id >= self.id_offset and pin_id < self.id_offset + self.pin_count def get_pin(self, pin_id): if not self.has_pin_id(pin_id): raise IndexError return self.pins[pin_id - self.id_offset] class BasePinConfig(object): # Should be overridden in subclasses to a subclass of BasePin pin_class = None domain_info = None def __init__(self): self.domains = [GPIODomain(info, self.pin_class) for info in self.domain_info] def get_pin(self, pin_id): for d in self.domains: if d.has_pin_id(pin_id): return d.get_pin(pin_id) raise IndexError def array_declaration(self, array_type, name, domain_index): if domain_index == 0: count_label = '' else: count_label = '_{}'.format(domain_index) templ = 'static const {} {}_{}[GPIO{}_GROUP_COUNT * GPIOPADPINS] = {{' return templ.format(array_type, name, domain_index, count_label) def to_pinconfig_struct(self, name, array_type, max_domains): lines = [] for domain_idx in range(min(len(self.domains), max_domains)): domain = self.domains[domain_idx] pin_matrix = [domain.pins[i].to_pinconfig_line() for i in range(domain.pin_count)] pin_matrix = tab_align(pin_matrix, min_column_tabs={0: 8}) lines.append(self.array_declaration(array_type, name, domain_idx)) rownum = 0 for row in pin_matrix: if (rownum % 8) == 0: lines.append("\n/* Port {:2} */".format(rownum/8)) lines.append("\t"+"".join(row)) rownum += 1 lines.append("};") lines.append('') return "\n".join(lines) class BasePinConfigV1(BasePinConfig): array_type = 'uint32_t' pin_class = PinV1 class BasePinConfigV2(BasePinConfig): array_type = 'uint32_t' pin_class = PinV2 class BasePinConfigV3(BasePinConfig): array_type = 'uint32_t' pin_class = PinV3 class BasePinConfigV4(BasePinConfig): array_type = 'uint32_t' pin_class = PinV4 class BasePinConfigV5(BasePinConfig): array_type = 'uint32_t' pin_class = PinV5 class H4APinConfig(BasePinConfigV1): domain_info = [GPIODomainInfo(offset=0, count=208, reserved=set())] class H5PPinConfig(BasePinConfigV2): domain_info = [GPIODomainInfo(offset=0, count=232, reserved=set())] class H5GPinConfig(BasePinConfigV2): reserved_pins = set(range(14, 32) + range(140, 160) + range(212, 224) + range(241, 248)) domain_info = [GPIODomainInfo(offset=0, count=248, reserved=reserved_pins)] class AlcatrazPinConfig(BasePinConfigV3): reserved_pins = set(range(24, 32) + range(45, 64) + range(151, 160) + range(173, 192)) domain_info = [GPIODomainInfo(offset=0, count=195, reserved=reserved_pins)] class FijiPinConfig(BasePinConfigV4): reserved_pins = set(range(16, 32) + range(88, 96) + range(140, 160)) domain_info = [GPIODomainInfo(offset=0, count=204, reserved=reserved_pins)] class CapriPinConfig(BasePinConfigV4): reserved_pins = set(range(16, 32)) domain_info = [GPIODomainInfo(offset=0, count=184, reserved=reserved_pins)] class M7PinConfig(BasePinConfigV4): reserved_pins = set(range(10, 32)) domain_info = [GPIODomainInfo(offset=0, count=138, reserved=reserved_pins), GPIODomainInfo(offset=160, count=50, reserved=set())] class MauiPinConfig(BasePinConfigV4): reserved_pins = set(range(55, 64) + range(92, 96) + range(152, 160) + range(168, 192)) domain_info = [GPIODomainInfo(offset=0, count=199, reserved=reserved_pins), GPIODomainInfo(offset=288, count=28, reserved=set())] class ElbaPinConfig(BasePinConfigV5): reserved_pins = set(range(37, 64) + range(125, 128) + range(142, 160)) domain_info = [GPIODomainInfo(offset=0, count=220, reserved=reserved_pins), GPIODomainInfo(offset=288, count=28, reserved=set())] class CaymanPinConfig(BasePinConfigV5): ap_reserved_pins = set(range(55, 64) + range(92, 96) + range(104, 128) + range(155, 160) + range(188, 192)) aop_reserved_pins = set(range(318, 320) + range(330, 352)) domain_info = [GPIODomainInfo(offset=0, count=208, reserved=ap_reserved_pins), # AP GPIODomainInfo(offset=288, count=85, reserved=aop_reserved_pins), # AOP GPIODomainInfo(offset=256, count=7, reserved=set())] # SEP output_domains = 2 class M8PinConfig(BasePinConfigV5): ap_reserved_pins = set(range(138, 160)) aop_reserved_pins = set(range(264, 288)) domain_info = [GPIODomainInfo(offset=32, count=139, reserved=ap_reserved_pins), # AP GPIODomainInfo(offset=256, count=96, reserved=aop_reserved_pins), # AOP GPIODomainInfo(offset=0, count=7, reserved=set()), GPIODomainInfo(offset=192, count=96, reserved=set())] output_domains = 2 def excel_reader(filename, sheet): try: workbook = openpyxl.load_workbook(filename, data_only=True) except IOError: sys.stderr.write('Could not open workbook {}\n'.format(filename)) return None try: worksheet = workbook.get_sheet_by_name(sheet) except KeyError: sys.stderr.write('Could not open worksheet {}\n'.format(sheet)) return None def row_generator(ws): def cell_to_str(cell): value = cell.value if value is None: value = '' return unicode(value) for row in ws.rows: yield [cell_to_str(cell) for cell in row] return row_generator(worksheet) def try_parse_hex(i): try: return int(i, 16) except ValueError: return None def row_is_empty(row): for col in row: if not col is None and len(col.strip()) > 0: return False return True def row_is_footer(row): for col in row: if not col is None: stripped = col.strip() if len(stripped) > 0 and stripped != '--': return False return True class CSVParser(object): def __init__(self, pinconfig_class, per_target_headers): self.pinconfig_class = pinconfig_class self.per_target_headers = per_target_headers self.targets = per_target_headers.keys() # Map the standardized name for a property to a list of acceptable names self.header_name_map = { 'gpio_id': ['gpio id', 'id'], 'net_name': ['net name', 'net names'], 'bump_name': ['ball name', 'pad name'], 'configurable': ['gpio configurable'], 'drive_strength': ['drive strength', 'drive strength (ma)'], 'drive_strength_ap': ['drive strength form factor', 'drive strength ap'], 'drive_strength_dev': ['drive strength dev'], 'pupd': ['pu/pd', 'pullup/pulldown', 'pull-up/pull-down'], 'function': ['config', 'dir', 'SecureROM config'], 'rom': ['rom function']} if callable(getattr(self.pinconfig_class.pin_class, 'set_slew_rate', None)): self.header_name_map['slew_rate'] = ['slew rate', 'gpio_sr'] self.header_name_map['slew_rate_ap'] = ['slew rate form factor', 'gpio_sr ap'] self.header_name_map['slew_rate_dev'] = ['slew rate dev', 'gpio_sr dev'] if callable(getattr(self.pinconfig_class.pin_class, 'set_bus_hold', None)): self.header_name_map['bus_hold'] = ['bus hold', 'gpio_bh'] if callable(getattr(self.pinconfig_class.pin_class, 'set_input_mode', None)): self.header_name_map['input_mode'] = ['input mode', 'input select', 'gpio_is'] def create_pinconfig(self): return self.pinconfig_class() def parse_version(self, row): for col in row: lowercase_col = col.lower() if 'rev' in lowercase_col or 'version' in lowercase_col or 'ver' in lowercase_col: return lowercase_col return None def canonical_header(self, header): for canonical, options in self.header_name_map.iteritems(): if header.lower() in options: return canonical return None def find_headers(self, row, rom): lowercase_row = map(lambda x: x.lower(), row) columns_per_target = {} columns_per_config = collections.defaultdict(dict) per_config_columns = set() ignore_missing_keys = set() missing_keys = set() # create a mapping from canonical header names to column indices header_map = {} for idx in range(len(lowercase_row)): header = lowercase_row[idx] canonical = self.canonical_header(header) if canonical is None: continue header_map[canonical] = idx # some parameters can be specified per config (ap/dev). figure out # which if any are done that way for this spreadsheet config_names = ['ap', 'dev'] for key in ['drive_strength', 'slew_rate', 'net_name']: config_to_key_map = {x: '{}_{}'.format(key, x) for x in config_names} per_config_keys = set(config_to_key_map.values()) if per_config_keys <= set(header_map): assert True, 'error message' # XXX # used in the loop below to skip over columns that are per config per_config_columns.update(per_config_keys) # passed back to the caller for use in parsing rows for config in config_names: columns_per_config[config][key] = header_map[config_to_key_map[config]] ignore_missing_keys.add(key) elif not per_config_keys.isdisjoint(set(header_map)): # either all or none of per-config columns should be present found = ', '.join(sorted(set(header_map) & per_config_keys)) raise ParseError('All or none of the per-config {} columns must be specified (found {})'.format(key, found)) else: # this key isn't per config, so fall through to the logic below, but # let the missing column checker know the per-config columns won't be present ignore_missing_keys.update(per_config_keys) # If no per-config columns were set, then there's only one configuration. # Set up the key map accordingly if len(columns_per_config) == 0: for key in ['drive_strength', 'slew_rate']: columns_per_config['all'][key] = header_map[key] for target in self.targets: target_header_name_map = self.per_target_headers[target] columns_per_target[target] = {} for key in self.header_name_map.keys(): if key in per_config_columns: continue if key in target_header_name_map: header = target_header_name_map[key] # This value has a column per target, so find the per-target header in the row try: idx = lowercase_row.index(header.lower()) except ValueError: sys.stderr.write('Could not find target {}\'s "{}" column "{}"\n'.format(target, key, header)) missing_keys.add(key) columns_per_target[target][key] = idx else: # This value has one column for all targets, so find one of the standard headers in the row for header in self.header_name_map[key]: if header.lower() in lowercase_row: idx = lowercase_row.index(header.lower()) columns_per_target[target][key] = idx break else: missing_keys.add(key) if rom: for key, value in target_header_name_map.iteritems(): if key == 'function' and value.lower() != 'SecureROM Config'.lower(): sys.stderr.write('ROM pins should have SecureROM Config column defined\n') missing_keys.add(key) # Some columns are optional missing_keys -= set(['configurable', 'input_mode']) # ROM Function is optional if not --rom if not rom: missing_keys -= set(['rom']) # Marked as being unneeded by the per-config column logic missing_keys -= ignore_missing_keys if len(missing_keys) == 0: missing_keys = None return columns_per_target, columns_per_config, missing_keys def parse(self, filename, force=False, rom=False, sheet=None): if filename.endswith('.xlsx'): if openpyxl is None: sys.stderr.write('To import Excel sheets, install the openpyxl package\n') return None, None if sheet is None: sys.stderr.write('To import Excel sheets, specify the --sheet option\n') return None, None reader = excel_reader(filename, sheet) if reader is None: return None, None else: reader = csv.reader(open(filename, "rU")) cnt=0 row_number = 0 # top row has version number row0 = reader.next() row_number += 1 # next non-blank row has headers while True: row1 = reader.next() row_number += 1 if len(filter(lambda x: len(x.strip()) > 0, row1)) > 0: break version = self.parse_version(row0) if version is None: sys.stderr.write('Could not find version in 1st row\n') return None, None try: columns_per_target, columns_per_config, missing = self.find_headers(row1, rom) except ParseError as e: sys.stderr.write('{}\n'.format(e)) return None, None if not missing is None: missing_options = {"{} ({})".format(col, " or ".join(self.header_name_map[col])) for col in missing} sys.stderr.write('Missing columns:\n{}\n'.format('\n '.join(missing_options))) return None, None if len(columns_per_config) == 1: configs = ['all'] else: configs = ['ap', 'dev'] pinconfigs = {} for target in self.targets: pinconfigs[target] = {} for config in configs: pinconfigs[target][config] = self.create_pinconfig() have_error = False for row in reader: row_number += 1 if row_is_empty(row): continue if row_is_footer(row): break for target, target_columns in columns_per_target.iteritems(): for config, config_columns in columns_per_config.iteritems(): columns = {} columns.update(config_columns) columns.update(target_columns) try: parsed_props = {prop: row[column].strip() for prop, column in columns.iteritems()} # If the optional 'GPIO Configurable' column is present, make sure it says y or yes try: if not parsed_props['configurable'].lower() in ('y', 'yes'): continue # And then get rid of the column, because the code below can't do anything with it del parsed_props['configurable'] except KeyError: pass try: gpio_id = int(parsed_props['gpio_id']) except ValueError: if rom and parsed_props['gpio_id'] == '': continue else: raise ParseError('Invalid GPIO ID "{}"'.format(parsed_props['gpio_id'])) pinconfig = pinconfigs[target][config] try: pin = pinconfig.get_pin(gpio_id) except IndexError: raise ParseError('GPIO ID {} is out of range'.format(gpio_id)) if isinstance(pin, ReservedPin): raise ParseError('GPIO ID {} is reserved pin'.format(gpio_id)) elif not pin.row_number is None: raise ParseError('GPIO ID {} redefined (previously defined on row {})'.format(pin.gpio_id, pin.row_number)) pin.row_number = row_number # The setters for all of the columns are predictable based on their name for prop, value in parsed_props.iteritems(): if rom: if prop == 'function': if parsed_props['rom'].lower() in ('y', 'yes'): value = parsed_props['function'] else: value = 'disabled' if prop == 'net_name': if not parsed_props['rom'].lower() in ('y', 'yes'): value = '' if prop != 'gpio_id' and prop != 'rom': setter = getattr(pin, 'set_' + prop) try: setter(value) except KeyError: raise ParseError('Invalid value "{}" for key {} in column "{}"'.format(value, prop, row1[columns[prop]])) except ParseError as e: sys.stderr.write('Row {:3}, Config {:3}: {}\n'.format(row_number, config, e)) have_error = True if not have_error or force: return version, pinconfigs else: return None, None def tab_align(data_matrix, tabwidth=8, min_spacing_slack=1, min_column_tabs={}): def rounduptab(x): return int(((x + float(tabwidth-1))/tabwidth))*tabwidth def tab(x): if x % tabwidth: return rounduptab(x) else: return x+tabwidth num_cols = max(map(len, data_matrix)) # Compute max number of characters seen for each column max_char_counts = [0]*num_cols for column, min_width in min_column_tabs.iteritems(): max_char_counts[column] = min_width*tabwidth def grow_count(new_row): for i in range(len(new_row)): max_char_counts[i] = max(max_char_counts[i], rounduptab(len(new_row[i]) + min_spacing_slack)) for row in data_matrix: grow_count(row) def pad_tabs(item, required_length): length = len(item) tabcount = 0 while length < required_length: tabcount+=1 length=tab(length) return "%s%s" % (item, '\t'*tabcount) def format_row(row): return "".join([pad_tabs(a, b) for (a,b) in zip(row, max_char_counts)]).rstrip() return [ format_row(x) for x in data_matrix ] def print_pinconfigs(target_config_pinconfigs, pinconfig_class, version, radar, copyright, commandline, prefix, rom, header): array_type = pinconfig_class.array_type num_domains = len(pinconfig_class.domain_info) if hasattr(pinconfig_class, 'output_domains'): if pinconfig_class.output_domains < num_domains: num_domains = pinconfig_class.output_domains targets = sorted(target_config_pinconfigs.keys()) configs = sorted(target_config_pinconfigs[targets[0]].keys()) if header: pinconfig_prefix = 'gpio_default_cfg' else: pinconfig_prefix = 'pinconfig' if not prefix is None: pinconfig_prefix = "{}_{}".format(pinconfig_prefix, prefix) if rom: assert configs == ['all'] assert len(targets) == 1 assert len(configs) == 1 else: assert configs == ['ap', 'dev'] or configs == ['all'] print "/*" print " * Copyright (C) {} Apple Inc. All rights reserved.".format(copyright) print " *" print " * This document is the property of Apple Inc." print " * It is considered confidential and proprietary." print " *" print " * This document may not be reproduced or transmitted in any form," print " * in whole or in part, without the express written permission of" print " * Apple Inc." print " */" print "" print "/* THIS FILE IS AUTOMATICALLY GENERATED BY tools/csvtopinconfig.py. DO NOT EDIT!" print " I/O Spreadsheet version: {}".format(version) print " I/O Spreadsheet tracker: {}".format(radar) print print " Generated with oldskool_csvtopinconfig.py. For new projects, use the regular csvtopinconfig.py" print " Conversion command: {}".format(commandline) print "*/" print "" if header: print "#ifndef __PLATFORM_PINCONFIG_H" print "#define __PLATFORM_PINCONFIG_H" print "" else: print "#include " print "#include " print "#include " print "#include " print "#include " if len(targets) > 1: print "#include " print "" for target in targets: for config in configs: if len(targets) == 1: if len(configs) == 1: name = pinconfig_prefix else: name = '{}_{}'.format(pinconfig_prefix, config.lower()) else: if len(configs) == 1: name = '{}_{}'.format(pinconfig_prefix, target.lower()) else: name = '{}_{}{}'.format(pinconfig_prefix, target.lower(), config.lower()) print target_config_pinconfigs[target][config].to_pinconfig_struct(name, array_type, num_domains) if header: print "" print "#endif /* __PLATFORM_PINCONFIG_H */" else: print 'struct pinconfig_map {' print '\tuint32_t board_id;' print '\tuint32_t board_id_mask;' print '\tconst {} *pinconfigs[GPIOC_COUNT];'.format(array_type) print '};' print '' print 'static const struct pinconfig_map cfg_map[] = {' if len(targets) == 1: if len(configs) == 1: # just one target and one config, so make a map that spits out the same pinconfig # for all board IDs pinconfigs = ', '.join(map(lambda x: '{}_{}'.format(pinconfig_prefix, x), range(num_domains))) print '\t{{ 0, 0, {{ {} }} }},'.format(pinconfigs) elif len(configs) == 2: # One target, but ap/dev configs, so make a map that selects ap/dev based on the lsb # of the board ID pinconfigs = ', '.join(map(lambda x: '{}_ap_{}'.format(pinconfig_prefix, x), range(num_domains))) print '\t{{ 0, 1, {{ {} }} }},'.format(pinconfigs) pinconfigs = ', '.join(map(lambda x: '{}_dev_{}'.format(pinconfig_prefix, x), range(num_domains))) print '\t{{ 1, 1, {{ {} }} }},'.format(pinconfigs) else: assert False, 'Can\'t handle more than 2 configs per target' else: for target in targets: if len(configs) == 1: pinconfigs = ', '.join(map(lambda x: '{}_{}_{}'.format(pinconfig_prefix, target.lower(), x), range(num_domains))) print '\t{{ TARGET_BOARD_ID_{}AP, ~1, {{ {} }} }},'.format(target.upper(), pinconfigs) elif len(configs) == 2: pinconfigs = ', '.join(map(lambda x: '{}_{}ap_{}'.format(pinconfig_prefix, target.lower(), x), range(num_domains))) print '\t{{ TARGET_BOARD_ID_{}AP, ~0, {{ {} }} }},'.format(target.upper(), pinconfigs) pinconfigs = ', '.join(map(lambda x: '{}_{}dev_{}'.format(pinconfig_prefix, target.lower(), x), range(num_domains))) print '\t{{ TARGET_BOARD_ID_{}DEV, ~0, {{ {} }} }},'.format(target.upper(), pinconfigs) else: assert False, 'Can\'t handle more than 2 configs per target' print '};' print '' if prefix is None: print 'const {} * target_get_default_gpio_cfg(uint32_t gpioc)'.format(array_type) else: print 'const {} * target_get_{}_gpio_cfg(int gpioc)'.format(array_type, prefix) print '{' print '\tstatic const struct pinconfig_map *selected_map = NULL;' print '' print '\tif (selected_map == NULL) {'; print '\t\tuint32_t board_id = platform_get_board_id();' print '\t\tfor (unsigned i = 0; i < sizeof(cfg_map)/sizeof(cfg_map[0]); i++) {' print '\t\t\tif ((board_id & cfg_map[i].board_id_mask) == cfg_map[i].board_id) {' print '\t\t\t\tselected_map = &cfg_map[i];' print '\t\t\t\tbreak;' print '\t\t\t}' print '\t\t}' print '' print '\t\tif (selected_map == NULL)' print '\t\t\tpanic("no default pinconfig for board id %u", board_id);' print '\t}' print '' print '\tASSERT(gpioc < GPIOC_COUNT);' print '\treturn selected_map->pinconfigs[gpioc];' print '}' def main(): soc_classes = collections.OrderedDict() soc_classes['h4a'] = H4APinConfig soc_classes['h5p'] = H5PPinConfig soc_classes['h5g'] = H5GPinConfig soc_classes['alcatraz'] = AlcatrazPinConfig soc_classes['h6'] = AlcatrazPinConfig soc_classes['h6p'] = AlcatrazPinConfig soc_classes['fiji'] = FijiPinConfig soc_classes['h7p'] = FijiPinConfig soc_classes['capri'] = CapriPinConfig soc_classes['h7g'] = CapriPinConfig soc_classes['m7'] = M7PinConfig soc_classes['h8p'] = MauiPinConfig soc_classes['maui'] = MauiPinConfig soc_classes['h8g'] = ElbaPinConfig soc_classes['elba'] = ElbaPinConfig soc_classes['h9p'] = CaymanPinConfig soc_classes['cayman'] = CaymanPinConfig soc_classes['m8'] = M8PinConfig argparser = argparse.ArgumentParser(description='Converts CSV file to iBoot pinconfig.h format') argparser.add_argument('--soc', required=True, choices=soc_classes.keys()) argparser.add_argument('--config-column', type=str, action='append') argparser.add_argument('--pupd-column', type=str, action='append') argparser.add_argument('--prefix', type=str, help='prefix for auto-generated variable and function names') argparser.add_argument('--copyright', type=str, default='YEAR', help='Year(s) to put in copyright notice') argparser.add_argument('--netname-column', type=str, action='append') argparser.add_argument('--radar', type=str, help='Link to spreadsheet tracker radar') argparser.add_argument('--force', action="store_true", help='attempt ouput even when there are parsing errors') argparser.add_argument('--rom', action="store_true", help='all pins except ROM function pins are disabled') argparser.add_argument('--header', action="store_true", help='generate a C header file instead of a C source file') argparser.add_argument('--sheet', default=None, help='sheet to read from when reading Excel files') argparser.add_argument('filename') args = argparser.parse_args() pinconfig_class = soc_classes[args.soc] per_target_headers = collections.defaultdict(dict) if not args.config_column is None: for item in map(lambda x: x.split(":"), args.config_column): if len(item) == 1: target = None column = item[0] elif len(item) == 2: target = item[0].upper() column = item[1] else: sys.stderr.write("Invalid config-column parameter\n") sys.exit(1) if 'function' in per_target_headers[target]: sys.stderr.write("Duplicate target in --config-column: \"{}\"".format(target)) sys.exit(1) per_target_headers[target]['function'] = column if not args.pupd_column is None: for item in map(lambda x: x.split(":"), args.pupd_column): if len(item) == 1: target = None column = item[0] elif len(item) == 2: target = item[0].upper() column = item[1] else: sys.stderr.write("Invalid pupd-column parameter\n") sys.exit(1) if 'pupd' in per_target_headers[target]: sys.stderr.write("Duplicate target in --pupd-column: \"{}\"".format(target)) sys.exit(1) per_target_headers[target]['pupd'] = column if not args.netname_column is None: for item in map(lambda x: x.split(":"), args.netname_column): if len(item) == 1: target = None column = item[0] elif len(item) == 2: target = item[0].upper() column = item[1] else: sys.stderr.write("Invalid netname-column parameter\n") sys.exit(1) if 'net_name' in per_target_headers[target]: sys.stderr.write("Duplicate target in --netname-column: \"{}\"".format(target)) sys.exit(1) per_target_headers[target]['net_name'] = column if not per_target_headers: per_target_headers[None] = {} parser = CSVParser(pinconfig_class, per_target_headers) version, pinconfigs = parser.parse(args.filename, force=args.force, rom=args.rom, sheet=args.sheet) if pinconfigs is None: sys.exit(1) def pretty_arg(arg): if arg == args.filename: return '' else: return pipes.quote(arg) commandline = './tools/oldskool_csvtopinconfig.py {}'.format(' '.join(pretty_arg(a) for a in sys.argv[1:])) print_pinconfigs(pinconfigs, pinconfig_class=pinconfig_class, version=version, radar=args.radar, copyright=args.copyright, commandline=commandline, prefix=args.prefix, rom=args.rom, header=args.header) if __name__ == '__main__': main()