#!/usr/bin/env python3 import argparse, sys from typing import * import dvfparse from dvfparse import Dvf, DvfMI, DvfVN, DvfSN def comment_lines(t: str, lines: Sequence[str]) -> Sequence[str]: if t in ('c', 'gas-with-cpp', 'gas', 'ld-script'): return ["/* ", *[" * " + l for l in lines], " */"] else: return ["; " + l for l in lines] def format_define(t: str, pfix: str, name: str, addr: str, width: int, deref: bool) -> str: name = name.upper() if t == 'c': return "#define %s%s (%s(volatile uint%d_t*)0x%04x)" % \ (pfix, name, "*" if deref else "", width, addr) elif t in ('gas-with-cpp', 'gas', 'nasm'): PFIX = { 'gas-with-cpp': '#', 'gas': '.', 'nasm': '%' } return "%cdefine %s%s 0x%04x" % (PFIX[t], pfix, name, addr) elif t in ('nasm-equ', 'asxxxx', 'elflift'): EQU = { 'nasm-equ': " equ ", 'asxxxx': " = ", 'elflift': "@", } return "%s%s%s0x%04x" % (pfix, name, EQU[t], addr) else: assert False, ("bad format type %s" % t) def format_defines(t: str, pfixr: str, pfixv: str, regs: List[DvfSN], vt: List[DvfVN]) -> str: return '\n'.join(format_define(t, pfixr, reg.name, reg.address, (reg.flg2 & 0xf) * 2, True) for reg in regs) \ + '\n'.join( format_define(t, pfixv, ev.name, ev.address, 16, False) for ev in vt) + '\n' def format_mem_line(mi: DvfMI) -> str: # FIXME: handle multiple of each existing NAMES = { DvfMI.MREG_FLASH: "flash", DvfMI.MREG_RAM: "ram", DvfMI.MREG_VECTOR: "vectors", DvfMI.MREG_CALLT: "callt", DvfMI.MREG_CALLF: "callf" } return "\t%s (%s%s%s) : ORIGIN = 0x%04x, LENGTH = 0x%04x" % \ (NAMES.get(mi.mreg_type, "//unk%02x"%mi.mreg_type), "r" if (mi.access & 1) != 0 else "", "w" if (mi.access & 2) != 0 else "", "x" if (mi.access & 4) != 0 else "", mi.addr_start, mi.addr_end - mi.addr_start + 1) def format_linker_script(mi: List[DvfMI]) -> str: useful_types = { DvfMI.MREG_FLASH, DvfMI.MREG_RAM, DvfMI.MREG_VECTOR, DvfMI.MREG_CALLT, DvfMI.MREG_CALLF, } #mi = [m for m in mi if m.mreg_type in useful_types] flashreg = [m for m in mi if m.mreg_type == DvfMI.MREG_FLASH] flashreg.sort(key=lambda m: m.addr_start) assert len(flashreg) > 0 flashreg = flashreg[0] if not any(m.mreg_type == DvfMI.MREG_VECTOR for m in mi): # let's guess something lowest_next = [m for m in mi if m.addr_start > flashreg.addr_start] lowest_next.sort(key=lambda m: m.addr_start) assert len(lowest_next) > 0 if lowest_next[0].addr_start < flashreg.addr_end: # we need a subregion of flash so this should be usable mi.append(DvfMI(0, lowest_next[0].addr_start-1, DvfMI.MREG_VECTOR, 1)) # now modify the flash so that it only starts AFTER the lowest_next i = 0 oldorg = flashreg.addr_start while any(m.addr_start == flashreg.addr_start and m.mreg_type != DvfMI.MREG_FLASH for m in mi): flashreg = flashreg._replace(addr_start = lowest_next[i].addr_end + 1) i = i = 1 for i in range(len(mi)): if mi[i].addr_start == oldorg and mi[i].mreg_type == DvfMI.MREG_FLASH: mi[i] = flashreg break return "MEMORY {\n" + '\n'.join(format_mem_line(m) for m in mi) + "\n}\n" if __name__ == '__main__': parser = argparse.ArgumentParser(prog="dvf2h", description="Convert NEC/Renesas Device "+\ "Files to C/asm defines and linker scripts") parser.add_argument('-p', '--use-reg-prefix', dest='reg_prefix', default=None, action='store_true', help="Use a REG_ prefix in the names for registers. "+\ "Defaults to on for the 'c' output format, but off otherwise") parser.add_argument('-P', '--no-use-reg-prefix', dest='reg_prefix', default=None, action='store_false', help="Don't use a REG_ prefix in the names for registers. "+\ "See the help for `--use-reg-prefix' for more info.") parser.add_argument('-f', '--out-format', default="c", choices=["c", "gas-with-cpp", "gas", "nasm-def", "nasm-equ", "asxxxx", "elflift", "ld-script"], help="Output format. 'c' is a C/C++ header (default), "+\ "'gas-with-cpp' is a GNU as-compatible header, "+\ "'gas' uses GNU as .define directives, "+\ "'nasm-def' outputs NASM %%define directives, "+\ "'nasm-equ' outputs defines using the EQU syntax, "+\ "'asxxxx' outputs ASxxxx-compatible include files, "+\ "'elflift' outputs an elflift-compatible symbol table, "+\ "'ld-script' outputs the MEMORY part of a GNU ld script.") parser.add_argument('-o', '--output', type=str, default=None) parser.add_argument('input', type=argparse.FileType('rb')) args = parser.parse_args() reg_prefix = args.reg_prefix or (args.out_format == 'c') output = sys.stdout if args.output is None else open(args.output, 'w') try: dvf = Dvf.parse(args.input.read()) md = dvf.sections[dvfparse.name2int('MD')] mi = dvf.sections[dvfparse.name2int('MI')] sn = dvf.sections[dvfparse.name2int('SN')] es = [v for k, v in dvf.sections.items() if k == dvfparse.name2int('ES')] vn = dvf.sections[dvfparse.name2int('VN')] assert len(md) == 1 md = md[0] assert len(es) == 0 or len(es) == 1 es = es[0] if len(es) != 0 else None # FIXME: do something about bitflags # these are flags of another listed register, you have to find # the non-1bit SN entry with the same address as this one to # figure out the name of the register it belogs to sn = [DvfSN(s.name, s.flg0, s.flg1, s.flg2, s.address, s.flg3, s.flg4) for s in sn if (s.flg2 & 0xf) != 1] if es is not None: es = [DvfSN(s.name, s.flg0, s.flg1, s.flg2, s.address, s.flg3, s.flg4) for s in es if (s.flg2 & 0xf) != 1] if (dvf.mcutype & 0xF0) == Dvf.MCU_78K0R: # FIXME: ugly hack sn = [DvfSN(s.name, s.flg0, s.flg1, s.flg2, s.address | 0xf0000, s.flg3, s.flg4) for s in sn] if es is not None: es = [DvfSN(s.name, s.flg0, s.flg1, s.flg2, s.address | 0xf0000, s.flg3, s.flg4) for s in es] output.write('\n'.join(comment_lines(args.out_format, [ "%s automatically generated by dvf2h.py" % \ ("Linker script" if args.out_format == 'ld-script' else "Register include file"), "For the %s (%s)" % (dvf.chipname_sym, dvf.chipname_disp), "", "Original: DVF version %s from %s" % (md.version, md.timestamp), dvf.copyright_notice ]))) output.write("\n\n") if args.out_format == 'ld-script': output.write(format_linker_script(mi)) else: rpf = "REG_" if reg_prefix else "" vpf = "VEC_" if reg_prefix else "" output.write(format_defines(args.out_format, rpf, vpf, sn, [])) output.write("\n\n\n") if es is not None: output.write(format_defines(args.out_format, rpf, vpf, es, [])) output.write("\n\n\n") output.write(format_defines(args.out_format, rpf, vpf, [], vn)) output.write("\n\n\n") finally: if args.output is not None: output.close()