Generate rust code
This commit is contained in:
parent
08f7f8faae
commit
c862650e0d
33
README
33
README
|
@ -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);
|
||||||
|
|
147
main.py
147
main.py
|
@ -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 = []
|
||||||
|
fields = []
|
||||||
|
def advance_to(addr, width):
|
||||||
|
nonlocal current_addr, reserved_id, code
|
||||||
|
padding = addr - current_addr
|
||||||
|
assert padding >= 0
|
||||||
|
if padding > 0:
|
||||||
|
if padding % 4 == 0:
|
||||||
|
code.append(f' unused{reserved_id}: [u32; {padding // 4}],')
|
||||||
|
else:
|
||||||
|
code.append(f' unused{reserved_id}: [u8; {padding}],')
|
||||||
|
reserved_id += 1
|
||||||
|
assert width in [8, 16, 32]
|
||||||
|
current_addr += padding + width // 8
|
||||||
|
|
||||||
for reg in registers:
|
for reg in registers:
|
||||||
addr = int(reg['address'], 16)
|
reg = interpret(reg)
|
||||||
|
(typename, lines) = fields_to_rust(reg)
|
||||||
|
description = reg['description']
|
||||||
|
addr = reg['abs']
|
||||||
|
if addr > ending_addr:
|
||||||
|
break
|
||||||
|
if 'similar' in reg:
|
||||||
|
for r in reg['similar']:
|
||||||
|
addr = r['abs']
|
||||||
if addr > ending_addr:
|
if addr > ending_addr:
|
||||||
break
|
break
|
||||||
if addr < base_addr:
|
if addr < base_addr:
|
||||||
continue
|
continue
|
||||||
padding = addr - current_addr
|
advance_to(addr, reg['width'])
|
||||||
if padding > 0:
|
# add description for the first one
|
||||||
if padding % 4 == 0:
|
if description is not None:
|
||||||
code.append(f'unused{reserved_id}: [RO<u32>; {padding // 4}],')
|
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:
|
||||||
code.append(f'unused{reserved_id}: [RO<u8>; {padding}],')
|
addr = reg['abs']
|
||||||
reserved_id += 1
|
if addr > ending_addr:
|
||||||
access = ''
|
break
|
||||||
unknown = False
|
if addr < base_addr:
|
||||||
if reg['type'] == 'ro':
|
continue
|
||||||
access = 'RO'
|
advance_to(addr, reg['width'])
|
||||||
elif reg['type'] == 'wo':
|
code.append(f' /// {description}')
|
||||||
access = 'WO'
|
code.append(f' pub {reg["name"].lower()}: {typename},')
|
||||||
elif reg['type'] in ['rw', 'mixed']:
|
if len(lines) > 0:
|
||||||
access = 'RW'
|
fields.append('')
|
||||||
else:
|
fields += lines
|
||||||
access = reg['type']
|
code.insert(0, '#[repr(C)]')
|
||||||
unknown = True
|
code.insert(1, 'pub struct RegisterBlock {')
|
||||||
size = int(reg['size'])
|
code.append('}')
|
||||||
if size not in [8, 16, 32]:
|
code += fields
|
||||||
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
|
return code
|
||||||
|
|
||||||
parser = parse_registers()
|
if __name__ == '__main__':
|
||||||
for line in sys.stdin:
|
if len(sys.argv) != 3:
|
||||||
|
print("Please read the README")
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
parser = parse_registers()
|
||||||
|
for line in sys.stdin:
|
||||||
parser.send(line)
|
parser.send(line)
|
||||||
v = end_iterator(parser)
|
v = end_iterator(parser)
|
||||||
for reg in v:
|
for line in emit_rust(int(sys.argv[1], 0), int(sys.argv[2], 0), v):
|
||||||
reg = interpret(reg)
|
print(line)
|
||||||
(name, lines) = fields_to_rust(reg)
|
|
||||||
print(reg)
|
|
||||||
print(name)
|
|
||||||
print(lines)
|
|
||||||
print('----')
|
|
||||||
|
|
Loading…
Reference in New Issue