342 lines
13 KiB
Python
Executable File
342 lines
13 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import sys
|
|
import xml.etree.ElementTree as ElementTree
|
|
|
|
boilerplate = '''\
|
|
/*
|
|
* Copyright (C) 2018 Marcus Comstedt
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
* this software and associated documentation files (the "Software"), to deal in
|
|
* the Software without restriction, including without limitation the rights to
|
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
'''
|
|
|
|
ppguard_prefix = 'RDB_'
|
|
|
|
# This register definition is mysteriously absent from the TRM, even though
|
|
# it refers to it from other places...
|
|
missing = [
|
|
('gctl', 'GCTL_CONTROL', 'Global control', '0xE0050000',
|
|
[('31', 'HARD_RESET_N'), ('30', 'CPU_RESET_N'), ('29', 'WARM_BOOT'),
|
|
('28', 'BOOTROM_EN'), ('26', 'MAIN_POWER_EN'), ('25', 'MAIN_CLOCK_EN'),
|
|
('24', 'FREEZE_IO'), ('22', 'USB_VBAT_EN'), ('21', 'USB_POWER_EN'),
|
|
('18', 'ANALOG_SWITCH'), ('17:16', 'WDT_PROTECT[1:0]'),
|
|
('15', 'NO_SBYWFI'), ('14', 'SYSMEM_BIST_EN'), ('12', 'WAKEUP_CPU_INT'),
|
|
('11', 'WAKEUP_AP_INT'), ('10', 'RAM_SLEEP'), ('9', 'DEBUG_MODE'),
|
|
('8', 'BOOT_COMPLETE'), ('4', 'WAKEUP_CLK'), ('3', 'WAKEUP_PWR'),
|
|
('2', 'WDT_RESET'), ('1', 'SW_RESET'), ('0', 'POR')]),
|
|
|
|
# More missing regs...
|
|
('uib', 'PROT_LMP_PORT_CAPABILITY_TIMER', 'Capability Timer Register', '0xE0033418', []),
|
|
('uib', 'PROT_LMP_PORT_CONFIGURATION_TIMER', 'Configuration Timer Register', '0xE003341C', []),
|
|
('uib', 'PROT_LMP_RECEIVED', 'Received Register', '0xE003345C', []),
|
|
('uib', 'PROT_STREAM_ERROR_DISABLE', 'Stream Error Disable Register', '0xE0033700', []),
|
|
('uib', 'PROT_STREAM_ERROR_STATUS', 'Stream Error Status Register', '0xE0033704', []),
|
|
('gpif', 'GPIF_SERIAL_IN_CONFIG', 'Serial Input Configuration Register', '0XE001401C', []),
|
|
('gpif', 'GPIF_SERIAL_OUT_CONFIG', 'Serial Output Configuration Register', '0XE0014020', []),
|
|
|
|
# TODO: add EFUSE, MMU stuff -sys64738
|
|
]
|
|
|
|
# Missing register fields
|
|
missing_fields = {
|
|
'LNK_LTSSM_STATE' : [('11:6', 'LTSSM_OVERRIDE_VALUE[5:0]'),
|
|
('12', 'LTSSM_OVERRIDE_EN'),
|
|
('13', 'LTSSM_OVERRIDE_GO'),
|
|
('14', 'LOOPBACK_MASTER'),
|
|
('15', 'DISABLE_SCRAMBLING'),
|
|
('16', 'LOOPBACK_ERROR'),
|
|
('17', 'LOOPBACK_GOOD'),
|
|
('31', 'LTSSM_FREEZE')],
|
|
}
|
|
|
|
# Fixes for typos in TRM
|
|
reg_name_fixups = {
|
|
'0xE0000BF0': ('I2C_ID', 'UART_ID'),
|
|
'0xE003151C': ('DEV_CTRL_INTR_MASK', 'DEV_CTRL_INTR'),
|
|
'0xE003205C': ('OHCI_RH_PORT_STATUS', 'EHCI_HCCPARAMS'),
|
|
'0xE0032060': ('OHCI_RH_PORT_STATUS', 'EHCI_USBCMD'),
|
|
'0x00': ('SCK_INTR', 'SCK_INTR0'),
|
|
}
|
|
reg_addr_fixups = {
|
|
'OHCI_REVISION': ('0xE0032010', '0xE0032024'),
|
|
}
|
|
field_name_fixups = {
|
|
'OTG_CTRL': {'11': ('B_SESS_VALID', 'B_END_SESS')},
|
|
}
|
|
field_bits_fixups = {
|
|
'GPIF_ADDR_COUNT_CONFIG': {'INCREMENT': ('7:0', '15:8')},
|
|
'GPIF_DATA_COUNT_CONFIG': {'INCREMENT': ('7:0', '15:8')},
|
|
}
|
|
|
|
class Register:
|
|
rdb_prefix='FX3_'
|
|
pad_size=34
|
|
pad_size_field=45
|
|
|
|
def __init__(self, group, name, description, address, fields):
|
|
try:
|
|
fixup = reg_name_fixups[address]
|
|
if name == fixup[0]:
|
|
name = fixup[1]
|
|
except KeyError:
|
|
pass
|
|
try:
|
|
fixup = reg_addr_fixups[name]
|
|
if address == fixup[0]:
|
|
address = fixup[1]
|
|
except KeyError:
|
|
pass
|
|
try:
|
|
fields += missing_fields[name]
|
|
except KeyError:
|
|
pass
|
|
|
|
self.group = group
|
|
self.name = name
|
|
self.description = description
|
|
self.address = address
|
|
self.fields = fields
|
|
|
|
def print_reg_def(self, file):
|
|
file.write('#define %s%s%s%s /* %s */\n' % (self.rdb_prefix, self.name, ' '*(self.pad_size - len(self.name)), self.address, self.description))
|
|
|
|
def print_field_defs(self, file):
|
|
if self.fields and (len(self.fields)>1 or (self.fields[0][0] != '0:31' and self.fields[0][0] != '31:0')):
|
|
file.write('\n')
|
|
for field in self.fields:
|
|
bits = field[0].split(':')
|
|
name = field[1].replace('{','[').split('[')
|
|
try:
|
|
fixup = field_name_fixups[self.name][field[0]]
|
|
if name[0] == fixup[0]:
|
|
name[0] = fixup[1]
|
|
except KeyError:
|
|
pass
|
|
try:
|
|
fixup = field_bits_fixups[self.name][name[0]]
|
|
if field[0] == fixup[0]:
|
|
bits = fixup[1].split(':')
|
|
except KeyError:
|
|
pass
|
|
if len(bits) == 1 and len(name) == 2 and (name[0] == 'EN_GPIO' or name[0] == 'EV_GPIO' or name[0] == 'POL_GPIO'):
|
|
name = [name[0]+'_'+name[1].split(']')[0]]
|
|
pad_size = self.pad_size_field
|
|
while pad_size <= len(self.name+'_'+name[0]+'_SHIFT'):
|
|
pad_size = pad_size + 1
|
|
if len(bits)>1:
|
|
lsb = int(bits[0])
|
|
msb = int(bits[1])
|
|
if lsb > msb:
|
|
(lsb, msb) = (msb, lsb)
|
|
if len(name) > 1:
|
|
name_range = name[1].split(']')[0].split(':')
|
|
nmsb = int(name_range[0])
|
|
nlsb = int(name_range[1])
|
|
if nmsb-nlsb != msb-lsb:
|
|
print('Field size mismatch! %s != %s (%s %s)'%(nmsb-nlsb+1, msb-lsb+1, self.name, name[0]))
|
|
if nmsb == msb and nlsb == 0:
|
|
lsb = 0
|
|
file.write('#define %s%s_%s_SHIFT%s%s\n' % (self.rdb_prefix, self.name, name[0], ' '*(pad_size - len(self.name+'_'+name[0]+'_SHIFT')), lsb))
|
|
file.write('#define %s%s_%s_BITS%s%s\n' % (self.rdb_prefix, self.name, name[0], ' '*(pad_size - len(self.name+'_'+name[0]+'_BITS')), msb-lsb+1))
|
|
file.write('#define %s%s_%s_MASK%s(%sUL << %s)\n' % (self.rdb_prefix, self.name, name[0], ' '*(pad_size - len(self.name+'_'+name[0]+'_MASK')), hex((1<<(msb-lsb+1))-1), lsb))
|
|
else:
|
|
file.write('#define %s%s_%s%s(1UL << %s)\n' % (self.rdb_prefix, self.name, name[0], ' '*(pad_size - len(self.name+'_'+name[0])), bits[0]))
|
|
|
|
class FontDatabase:
|
|
def __init__(self):
|
|
self.fonts = {}
|
|
|
|
def addfont(self, id, size, family, color, **extra):
|
|
if color == '#000000':
|
|
color = ''
|
|
self.fonts[id] = family+'/'+size+color
|
|
|
|
def getfont(self, id, bold, italic):
|
|
fontname = id and self.fonts[id]
|
|
if bold and fontname:
|
|
fontname = '*'+fontname
|
|
if italic and fontname:
|
|
fontname = '~'+fontname
|
|
return fontname
|
|
|
|
class RegDefParser:
|
|
reg_detail_font = '*Times/9'
|
|
reg_detail_alt_font = 'Times/9'
|
|
reg_detail_superscript_font = '*Times/7'
|
|
|
|
def __init__(self, group):
|
|
self.group = group
|
|
self.details = []
|
|
self.detail = []
|
|
self.info = []
|
|
self.combine = False
|
|
self.fontsel = False
|
|
|
|
def flush(self):
|
|
if self.detail:
|
|
if len(self.detail) == 1:
|
|
pass
|
|
elif self.info and len(self.detail) == 2 and self.detail[0] == self.info[0]:
|
|
pass
|
|
elif len(self.detail) == 2 and '0123456789'.find(self.detail[0][:1]) < 0:
|
|
pass
|
|
elif len(self.detail) == 3:
|
|
if self.info:
|
|
sys.exit('Multiple info candidates! %s %s'%(self.info, self.detail))
|
|
else:
|
|
self.info = self.detail
|
|
else:
|
|
self.details.append(self.detail)
|
|
self.detail = []
|
|
self.combine = False
|
|
|
|
def feedtext(self, font, text, leftcol):
|
|
if text.strip() == '':
|
|
pass
|
|
elif font == self.reg_detail_font or (leftcol and not self.fontsel and font == self.reg_detail_alt_font):
|
|
if self.combine:
|
|
self.detail[-1] = self.detail[-1] + text
|
|
self.combine = False
|
|
else:
|
|
self.detail.append(text)
|
|
if font == self.reg_detail_font:
|
|
self.fontsel = True
|
|
elif font == self.reg_detail_superscript_font and self.detail:
|
|
if text == '2':
|
|
text = '²'
|
|
else:
|
|
sys.exit('Unable to handle superscript text: %s' % text)
|
|
self.detail[-1] = self.detail[-1] + text
|
|
self.combine = True
|
|
else:
|
|
self.flush()
|
|
|
|
def finalize(self):
|
|
self.flush()
|
|
if not self.details and not self.info:
|
|
return None
|
|
if self.info:
|
|
return Register(self.group, *self.info, fields=self.details)
|
|
else:
|
|
sys.exit('Incomplete register details')
|
|
return None
|
|
|
|
class RegDefFinder:
|
|
chapter_font = 'Times/34#fbfbfb'
|
|
heading_font = '*Times/18'
|
|
register_chapter = '10'
|
|
register_sections = {
|
|
'3': 'vic',
|
|
'4': 'gctl',
|
|
'5': 'gctl',
|
|
'6': 'pib',
|
|
'7': 'gpif',
|
|
'8': 'pport',
|
|
'9': 'uib',
|
|
'10': 'uib',
|
|
'11': 'uib',
|
|
'12': 'uib',
|
|
'13': 'uib',
|
|
'14': 'uib',
|
|
'15': 'uib',
|
|
'16': 'uib',
|
|
'17': 'uibin',
|
|
'18': 'i2s',
|
|
'19': 'i2c',
|
|
'20': 'uart',
|
|
'21': 'spi',
|
|
'22': 'gpio',
|
|
'23': 'gpio',
|
|
'24': 'lpp',
|
|
'25': 'dma',
|
|
'26': 'dma'
|
|
}
|
|
|
|
def __init__(self):
|
|
self.chapter_number = None
|
|
self.section = None
|
|
self.register = None
|
|
self.registers = {group: [] for group in self.register_sections.values()}
|
|
|
|
def flush(self):
|
|
if self.register:
|
|
reg = self.register.finalize()
|
|
if reg:
|
|
self.registers[reg.group].append(reg)
|
|
self.register = None
|
|
|
|
def feedtext(self, font, text, left):
|
|
if font == self.chapter_font:
|
|
self.chapter_number = text.split('.')[0]
|
|
self.section = None
|
|
elif self.chapter_number != self.register_chapter:
|
|
pass
|
|
elif font == self.heading_font:
|
|
if text.startswith(self.register_chapter+'.'):
|
|
section = text.split()[0]
|
|
if section != self.section:
|
|
self.flush()
|
|
self.section = section
|
|
secnr = section.split('.')
|
|
if len(secnr) == 3:
|
|
self.register = RegDefParser(self.register_sections[secnr[1]])
|
|
elif self.register:
|
|
self.register.feedtext(font, text, left < 250)
|
|
|
|
def add_missing(self, group, name, *rest):
|
|
if next((r for r in self.registers[group] if r.name == name), False):
|
|
pass
|
|
else:
|
|
self.registers[group].append(Register(group, name, *rest))
|
|
|
|
fontdb = FontDatabase()
|
|
finder = RegDefFinder()
|
|
|
|
file = open('trm.xml', 'r')
|
|
|
|
for event, elem in ElementTree.iterparse(file):
|
|
if elem.tag == 'text':
|
|
finder.feedtext(fontdb.getfont(elem.get('font'), elem.find('b') != None, elem.find('i') != None),
|
|
''.join(elem.itertext()), int(elem.get('left')))
|
|
elif elem.tag == 'fontspec':
|
|
fontdb.addfont(**elem.attrib)
|
|
elif elem.tag == 'page':
|
|
elem.clear() # save memory
|
|
|
|
file.close()
|
|
finder.flush()
|
|
for m in missing:
|
|
finder.add_missing(*m)
|
|
|
|
for group, registers in finder.registers.items():
|
|
if group != 'dma':
|
|
registers = sorted(registers, key=lambda r: int(r.address.replace('?', 'F'), 16))
|
|
ppguard = '%s%s_H_' % (ppguard_prefix, group.upper())
|
|
file = open('%s.h' % group, 'w')
|
|
file.write(boilerplate)
|
|
file.write('#ifndef %s\n#define %s\n\n' % (ppguard, ppguard))
|
|
for reg in registers:
|
|
reg.print_reg_def(file)
|
|
for reg in registers:
|
|
reg.print_field_defs(file)
|
|
file.write('\n#endif /* %s */\n' % ppguard)
|
|
file.close()
|
|
|