diff --git a/README b/README index 10ba6f2..4446820 100644 --- a/README +++ b/README @@ -1,5 +1,34 @@ 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); diff --git a/main.py b/main.py index c49ffb7..d5efec8 100644 --- a/main.py +++ b/main.py @@ -167,18 +167,39 @@ def interpret(reg): assert len(expected) == 0 return result -def snake_to_camel(name: str): +def to_camel(name: str): result = [] - start = True - for c in name: - if c == '_': - start = True - elif start: - start = False - result.append(c.upper()) + parts = name.split('_') + for p in parts: + if len(p) == 0: + continue + p = p[0].upper() + p[1:] + result.append(p) + 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: 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): access = access.upper() @@ -192,14 +213,15 @@ def fields_to_rust(reg): fields = reg['fields'] name_pattern = re.compile(r'(.+\w)\d+') access = access_to_type(reg['access']) + assert reg['width'] in [8, 16, 32] if fields == []: return (f'{access}', []) if len(fields) == 1: bits = fields[0]['bits'] if bits[1] - bits[0] + 1 == reg['width']: return (f'{access_to_type(reg["access"])}', []) - namespace = reg['name'].lower() - name = snake_to_camel(reg['name']) + namespace = to_snake(reg['name']) + name = to_camel(reg['name']) if 'similar' in reg: # remove the trailing digits name = name_pattern.fullmatch(name).group(1) @@ -208,13 +230,16 @@ def fields_to_rust(reg): has_wtc = False lines = [] 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() [low, high] = f['bits'] assert low <= high if field_access == 'WTC': has_wtc = True else: + assert field_access in ['RO', 'RW', 'WO'] for i in range(high - low + 1): bitmask |= 1 << (i + low) if low == high: @@ -232,7 +257,7 @@ def fields_to_rust(reg): # we did not implement WTC for multiple bits, but could be done raise ValueError() lines.append(f'register_bits!({namespace}, {field_name},' - f'{value_range}, {low}, {high});') + f' {value_range}, {low}, {high});') if has_wtc: lines.insert(0, f'register!({namespace}, {name}, {access},' f' u{reg["width"]}, {bitmask});') @@ -245,49 +270,71 @@ 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 + 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}: [RO; {padding // 4}],') + code.append(f' unused{reserved_id}: [u32; {padding // 4}],') else: - code.append(f'unused{reserved_id}: [RO; {padding}],') + code.append(f' unused{reserved_id}: [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' + assert width in [8, 16, 32] + current_addr += padding + width // 8 + + for reg in registers: + 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: + 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: - 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}," - if unknown: - line = '// FIXME: ' + line - code.append(f"/// {reg['description']}") - code.append(line) + addr = reg['abs'] + if addr > ending_addr: + break + if addr < base_addr: + continue + advance_to(addr, reg['width']) + code.append(f' /// {description}') + code.append(f' pub {reg["name"].lower()}: {typename},') + if len(lines) > 0: + fields.append('') + fields += lines + code.insert(0, '#[repr(C)]') + code.insert(1, 'pub struct RegisterBlock {') + code.append('}') + code += fields return code -parser = parse_registers() -for line in sys.stdin: - parser.send(line) -v = end_iterator(parser) -for reg in v: - reg = interpret(reg) - (name, lines) = fields_to_rust(reg) - print(reg) - print(name) - print(lines) - print('----') +if __name__ == '__main__': + if len(sys.argv) != 3: + print("Please read the README") + exit(0) + + parser = parse_registers() + for line in sys.stdin: + parser.send(line) + v = end_iterator(parser) + for line in emit_rust(int(sys.argv[1], 0), int(sys.argv[2], 0), v): + print(line) +