Generate rust code

master
pca006132 2020-07-29 14:28:02 +08:00
parent 08f7f8faae
commit c862650e0d
2 changed files with 129 additions and 53 deletions

33
README
View File

@ -1,5 +1,34 @@
A simple script for parsing TRM register definition... A simple script for parsing TRM register definition...
Experimenting Pipe the `pdftotext -layout` output to the python script stdin, and specify the
starting and ending address (absolute) of the registers.
pdftotext -f 1436 -l 1438 -layout ug585-Zynq-7000-TRM.pdf - | python main.py This script can handle fields generation and description. This script is very
hacky, have a lot of assumptions on the format of the PDF, so use with care,
better check the output before you trust it.
Example:
pdftotext -f 1435 -l 1437 -layout ug585-Zynq-7000-TRM.pdf - | python main.py 0xF8F00000 0xF8F01FFF
#[repr(C)]
pub struct RegisterBlock {
/// SCU Control Register
pub scu_control_register: ScuControlRegister,
/// SCU Configuration Register
pub scu_configuration_register: ScuConfigurationRegister,
}
register!(scu_control_register, ScuControlRegister, RW, u32);
register_bit!(scu_control_register, ic_standby_enable, 6);
register_bit!(scu_control_register, scu_standby_enable, 5);
register_bit!(scu_control_register, force_all_device_to_po, 4);
register_bit!(scu_control_register, scu_speculative_linefil, 3);
register_bit!(scu_control_register, scu_rams_parity_enab, 2);
register_bit!(scu_control_register, address_filtering_enabl, 1);
register_bit!(scu_control_register, scu_enable, 0);
register!(scu_configuration_register, ScuConfigurationRegister, RO, u32);
register_bits!(scu_configuration_register, tag_ram_sizes,u8, 8, 15);
register_bits!(scu_configuration_register, cpus_smp,u8, 4, 7);
register_bits!(scu_configuration_register, cpu_number,u8, 0, 1);

149
main.py
View File

@ -167,18 +167,39 @@ def interpret(reg):
assert len(expected) == 0 assert len(expected) == 0
return result return result
def snake_to_camel(name: str): def to_camel(name: str):
result = [] result = []
start = True parts = name.split('_')
for c in name: for p in parts:
if c == '_': if len(p) == 0:
start = True continue
elif start: p = p[0].upper() + p[1:]
start = False result.append(p)
result.append(c.upper()) return ''.join(result)
def to_snake(text: str):
if len(text) == 0:
return ''
if '_' in text:
return text.lower()
prev_upper = text[-1].isupper()
result = []
for c in reversed(text):
current_upper = c.isupper()
if prev_upper and not current_upper:
if len(result) > 0 and result[-1] != '_':
result.append('_')
result.append(c.lower())
elif not prev_upper and current_upper:
result.append(c.lower())
result.append('_')
else: else:
result.append(c.lower()) result.append(c.lower())
return ''.join(result) prev_upper = current_upper
if result[-1] == '_':
result.pop()
return ''.join(reversed(result))
def access_to_type(access: str): def access_to_type(access: str):
access = access.upper() access = access.upper()
@ -192,14 +213,15 @@ def fields_to_rust(reg):
fields = reg['fields'] fields = reg['fields']
name_pattern = re.compile(r'(.+\w)\d+') name_pattern = re.compile(r'(.+\w)\d+')
access = access_to_type(reg['access']) access = access_to_type(reg['access'])
assert reg['width'] in [8, 16, 32]
if fields == []: if fields == []:
return (f'{access}<u{reg["width"]}>', []) return (f'{access}<u{reg["width"]}>', [])
if len(fields) == 1: if len(fields) == 1:
bits = fields[0]['bits'] bits = fields[0]['bits']
if bits[1] - bits[0] + 1 == reg['width']: if bits[1] - bits[0] + 1 == reg['width']:
return (f'{access_to_type(reg["access"])}<u{reg["width"]}>', []) return (f'{access_to_type(reg["access"])}<u{reg["width"]}>', [])
namespace = reg['name'].lower() namespace = to_snake(reg['name'])
name = snake_to_camel(reg['name']) name = to_camel(reg['name'])
if 'similar' in reg: if 'similar' in reg:
# remove the trailing digits # remove the trailing digits
name = name_pattern.fullmatch(name).group(1) name = name_pattern.fullmatch(name).group(1)
@ -208,13 +230,16 @@ def fields_to_rust(reg):
has_wtc = False has_wtc = False
lines = [] lines = []
for f in fields: for f in fields:
field_name = f['name'].lower() field_name = to_snake(f['name'])
if field_name in ['na', 'reserved']:
continue
field_access = f['access'].upper() field_access = f['access'].upper()
[low, high] = f['bits'] [low, high] = f['bits']
assert low <= high assert low <= high
if field_access == 'WTC': if field_access == 'WTC':
has_wtc = True has_wtc = True
else: else:
assert field_access in ['RO', 'RW', 'WO']
for i in range(high - low + 1): for i in range(high - low + 1):
bitmask |= 1 << (i + low) bitmask |= 1 << (i + low)
if low == high: if low == high:
@ -232,7 +257,7 @@ def fields_to_rust(reg):
# we did not implement WTC for multiple bits, but could be done # we did not implement WTC for multiple bits, but could be done
raise ValueError() raise ValueError()
lines.append(f'register_bits!({namespace}, {field_name},' lines.append(f'register_bits!({namespace}, {field_name},'
f'{value_range}, {low}, {high});') f' {value_range}, {low}, {high});')
if has_wtc: if has_wtc:
lines.insert(0, f'register!({namespace}, {name}, {access},' lines.insert(0, f'register!({namespace}, {name}, {access},'
f' u{reg["width"]}, {bitmask});') f' u{reg["width"]}, {bitmask});')
@ -245,49 +270,71 @@ def emit_rust(base_addr, ending_addr, registers):
current_addr = base_addr current_addr = base_addr
reserved_id = 0 reserved_id = 0
code = [] code = []
for reg in registers: fields = []
addr = int(reg['address'], 16) def advance_to(addr, width):
if addr > ending_addr: nonlocal current_addr, reserved_id, code
break
if addr < base_addr:
continue
padding = addr - current_addr padding = addr - current_addr
assert padding >= 0
if padding > 0: if padding > 0:
if padding % 4 == 0: if padding % 4 == 0:
code.append(f'unused{reserved_id}: [RO<u32>; {padding // 4}],') code.append(f' unused{reserved_id}: [u32; {padding // 4}],')
else: else:
code.append(f'unused{reserved_id}: [RO<u8>; {padding}],') code.append(f' unused{reserved_id}: [u8; {padding}],')
reserved_id += 1 reserved_id += 1
access = '' assert width in [8, 16, 32]
unknown = False current_addr += padding + width // 8
if reg['type'] == 'ro':
access = 'RO' for reg in registers:
elif reg['type'] == 'wo': reg = interpret(reg)
access = 'WO' (typename, lines) = fields_to_rust(reg)
elif reg['type'] in ['rw', 'mixed']: description = reg['description']
access = 'RW' addr = reg['abs']
if addr > ending_addr:
break
if 'similar' in reg:
for r in reg['similar']:
addr = r['abs']
if addr > ending_addr:
break
if addr < base_addr:
continue
advance_to(addr, reg['width'])
# add description for the first one
if description is not None:
code.append(f' /// {description}')
description = None
if len(lines) > 0:
fields.append('')
fields += lines
lines = []
code.append(f' pub {reg["name"].lower()}: {typename},')
else: else:
access = reg['type'] addr = reg['abs']
unknown = True if addr > ending_addr:
size = int(reg['size']) break
if size not in [8, 16, 32]: if addr < base_addr:
unknown = True continue
current_addr += padding + size // 8 advance_to(addr, reg['width'])
line = f"pub {reg['id'].lower()}: {access}<u{size}>," code.append(f' /// {description}')
if unknown: code.append(f' pub {reg["name"].lower()}: {typename},')
line = '// FIXME: ' + line if len(lines) > 0:
code.append(f"/// {reg['description']}") fields.append('')
code.append(line) fields += lines
code.insert(0, '#[repr(C)]')
code.insert(1, 'pub struct RegisterBlock {')
code.append('}')
code += fields
return code return code
parser = parse_registers() if __name__ == '__main__':
for line in sys.stdin: if len(sys.argv) != 3:
parser.send(line) print("Please read the README")
v = end_iterator(parser) exit(0)
for reg in v:
reg = interpret(reg) parser = parse_registers()
(name, lines) = fields_to_rust(reg) for line in sys.stdin:
print(reg) parser.send(line)
print(name) v = end_iterator(parser)
print(lines) for line in emit_rust(int(sys.argv[1], 0), int(sys.argv[2], 0), v):
print('----') print(line)