894 lines
37 KiB
Python
894 lines
37 KiB
Python
#!/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 <debug.h>"
|
|
print "#include <drivers/apple/gpio.h>"
|
|
print "#include <platform.h>"
|
|
print "#include <platform/soc/hwregbase.h>"
|
|
print "#include <stdint.h>"
|
|
if len(targets) > 1:
|
|
print "#include <target/boardid.h>"
|
|
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 '<filename>'
|
|
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()
|