regs-parser/main.py

229 lines
6.7 KiB
Python
Raw Normal View History

2020-07-28 16:09:24 +08:00
import re
import sys
2020-07-29 10:15:58 +08:00
import json
2020-07-28 16:09:24 +08:00
ADDRESS_PATTERN = r'0x[0-9A-F]+'
2020-07-28 17:33:59 +08:00
def parse_table_entries(fields):
2020-07-28 16:34:06 +08:00
"""Parse aligned table entries"""
2020-07-28 17:33:59 +08:00
pattern = r'\s+'.join([f"(?P<{v[:v.find(':')]}>{v[v.find(':') + 1:].strip()})"
for v in fields])
pattern = re.compile(pattern)
fields = [v[:v.find(':')] for v in fields]
prev = None
2020-07-28 16:34:06 +08:00
entries = []
def flush():
nonlocal prev
if prev is not None:
entries.append(prev['data'])
prev = None
2020-07-28 17:33:59 +08:00
while True:
line = yield
if line == None:
break
2020-07-29 10:15:58 +08:00
line = line.rstrip()
2020-07-28 16:09:24 +08:00
if len(line.strip()) == 0:
2020-07-28 16:34:06 +08:00
flush()
2020-07-28 16:09:24 +08:00
continue
2020-07-29 10:15:58 +08:00
match = pattern.fullmatch(line)
2020-07-28 16:09:24 +08:00
if match is not None:
2020-07-28 16:34:06 +08:00
flush()
2020-07-28 17:33:59 +08:00
starts = [match.span(v)[0] for v in fields]
2020-07-28 16:09:24 +08:00
prev = {
2020-07-28 17:33:59 +08:00
'data': {v: [match.group(v)] for v in fields},
2020-07-28 16:09:24 +08:00
'starts': starts
}
elif prev is not None:
for i, field in enumerate(fields):
2020-07-28 17:33:59 +08:00
if i + 1 == len(fields):
v = line[prev['starts'][i]:].strip()
else:
v = line[prev['starts'][i]:prev['starts'][i + 1]].strip()
2020-07-28 16:09:24 +08:00
if len(v) == 0:
continue
2020-07-28 17:33:59 +08:00
prev['data'][field].append(v)
2020-07-28 16:34:06 +08:00
flush()
return entries
2020-07-28 16:09:24 +08:00
2020-07-28 17:33:59 +08:00
def parse_register_fields():
fields = [
r'name: \w+',
r'bits: \d+(:\d+)?',
r'type: \w+',
f'reset: {ADDRESS_PATTERN}',
2020-07-28 17:33:59 +08:00
r'description: .+'
]
it = parse_table_entries(fields)
next(it)
return it
2020-07-28 16:34:06 +08:00
2020-07-28 17:33:59 +08:00
def end_iterator(it):
try:
it.send(None)
except StopIteration as e:
return e.value
def parse_registers():
2020-07-29 10:15:58 +08:00
def two_column(width):
# only require the leftmost column starts with a \w, and the a space
# separator between the two columns...
2020-07-29 10:15:58 +08:00
fields = [
f'key: \\w.{{1,{width-2}}}\\s',
r'value: \w.+'
2020-07-29 10:15:58 +08:00
]
it = parse_table_entries(fields)
next(it)
return it
2020-07-28 17:33:59 +08:00
def inner():
2020-07-29 10:15:58 +08:00
def_start = re.compile(r'Name\s+(\w+)\s+')
field_start = re.compile(r'\s+Field Name\s+Bits\s+Type\s+Reset '
r'Value\s+Description\s+')
2020-07-28 17:33:59 +08:00
state = 0
it = None
2020-07-29 10:15:58 +08:00
results = []
2020-07-28 17:33:59 +08:00
while True:
line = yield
if line == None:
break
2020-07-29 10:15:58 +08:00
line = line
if state == 0:
m = def_start.fullmatch(line)
if m is not None:
if it is not None:
results[-1]['fields']= end_iterator(it)
it = two_column(m.span(1)[0])
2020-07-28 17:33:59 +08:00
it.send(line)
2020-07-29 10:15:58 +08:00
state = 1
else:
if it is not None:
it.send(line)
elif state == 1:
m = field_start.fullmatch(line)
if m is not None:
if it is not None:
results.append({'def': end_iterator(it)})
it = parse_register_fields()
state = 0
else:
if it is not None:
it.send(line)
2020-07-28 17:33:59 +08:00
if it is not None:
2020-07-29 10:15:58 +08:00
if state == 0:
results[-1]['fields']= end_iterator(it)
2020-07-28 17:33:59 +08:00
return results
it = inner()
next(it)
return it
2020-07-28 16:09:24 +08:00
2020-07-29 11:27:11 +08:00
def interpret_field(field):
bit_pattern = re.compile(r'(\d+)(:(\d+))?')
m = bit_pattern.fullmatch(field['bits'][0])
if m is None:
raise ValueError(field['bits'])
high = int(m.group(1))
low = high
if m.group(3) is not None:
low = int(m.group(3))
result = {}
result['name'] = field['name'][0]
result['bits'] = [low, high]
result['type'] = field['type'][0]
return result
def interpret(reg):
pattern = re.compile(r'(.+\w)\d+')
2020-07-29 11:27:11 +08:00
width_pattern = re.compile(r'(\d+)\s?bits')
result = {}
name_pattern = None
2020-07-29 11:27:11 +08:00
expected = [
['Name', 'name', lambda v: ''.join(v)],
['Relative Address', 'rel', lambda v: int(v[0], 16)],
['Absolute Address', 'abs', lambda v: int(v[0], 16)],
['Width', 'width', lambda v: int(width_pattern.fullmatch(v[0]).group(1))],
['Access Type', 'access', lambda v: v[0]],
['Reset Value', 'reset', lambda v: v[0]],
['Description', 'description', lambda v: ' '.join(v)]
]
for v in reg['def']:
a = v['key']
b = v['value']
key = a[0].strip()
2020-07-29 11:27:11 +08:00
if len(expected) > 0 and key == expected[0][0]:
result[expected[0][1]] = expected[0][2](b)
expected = expected[1:]
if key == 'Name':
m = pattern.fullmatch(b[0].strip())
if m is not None:
name_pattern = m.group(1)
elif name_pattern != None and key.startswith(name_pattern):
if 'similar' not in result:
result['similar'] = []
2020-07-29 11:27:11 +08:00
result['similar'].append({'name': key, 'abs': int(b[0], 16)})
result['fields'] = [interpret_field(f) for f in reg['fields']]
assert len(expected) == 0
return result
2020-07-29 11:27:11 +08:00
def snake_to_camel(name: str):
result = []
start = True
for c in name:
if c == '_':
start = True
elif start:
start = False
result.append(c.upper())
else:
result.append(c.lower())
return ''.join(result)
2020-07-28 16:09:24 +08:00
def emit_rust(base_addr, ending_addr, registers):
current_addr = base_addr
reserved_id = 0
code = []
for reg in registers:
addr = int(reg['address'], 16)
if addr > ending_addr:
break
if addr < base_addr:
continue
padding = addr - current_addr
if padding > 0:
if padding % 4 == 0:
code.append(f'unused{reserved_id}: [RO<u32>; {padding // 4}],')
else:
code.append(f'unused{reserved_id}: [RO<u8>; {padding}],')
reserved_id += 1
access = ''
unknown = False
if reg['type'] == 'ro':
access = 'RO'
elif reg['type'] == 'wo':
access = 'WO'
elif reg['type'] in ['rw', 'mixed']:
access = 'RW'
else:
access = reg['type']
unknown = True
size = int(reg['size'])
if size not in [8, 16, 32]:
unknown = True
current_addr += padding + size // 8
line = f"pub {reg['id'].lower()}: {access}<u{size}>,"
if unknown:
line = '// FIXME: ' + line
code.append(f"/// {reg['description']}")
code.append(line)
return code
2020-07-28 17:33:59 +08:00
parser = parse_registers()
for line in sys.stdin:
parser.send(line)
2020-07-29 10:15:58 +08:00
v = end_iterator(parser)
for reg in v:
reg = interpret(reg)
2020-07-29 11:27:11 +08:00
print(reg)