201 lines
8.0 KiB
Python
201 lines
8.0 KiB
Python
|
#!/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()
|
||
|
|