voodoo/gen/voodoo2.py

218 lines
5.8 KiB
Python

import sys
from dataclasses import dataclass
import enum
from generate import renderer
def calculate_line_columns(line):
columns = []
span_start = 0
span_end = 0
last_c = None
for i, c in enumerate(line):
if c == ' ':
if last_c != ' ':
span_start = i
span_end = i
elif last_c == ' ':
assert span_end >= span_start
if span_end > span_start:
columns.append(span_end)
last_c = c
print(columns)
def debug_columns(lines):
for line in lines:
calculate_line_columns(line)
def columnize(line):
_columns = [18, 45, 51, 65, 70, 82]
ix = 0
for col in _columns:
if ix > len(line):
yield ""
else:
yield line[ix:col+1]
ix = col+1
if ix > len(line):
yield ""
else:
yield line[ix:]
def columnize_lines(lines):
for line in lines:
if not line.strip():
continue
columns = list(columnize(line))
yield columns
@dataclass
class RegisterLine:
name: str
start_address: int
end_address: int
bits: tuple[int, int]
chips: set[str]
r_w: set[str]
pipelined: bool
fifo: bool
description: str
def parse_address(s):
assert s.startswith("0x")
s = s.removeprefix("0x")
assert s.endswith(")")
s = s.removesuffix(")")
addr16s, addr10s = s.split("(")
addr16 = int(addr16s, 16)
addr10 = int(addr10s, 10)
assert addr16 == addr10
return addr16
def parse_address_range(s):
if " to " in s:
start, end = s.split(" to ")
return parse_address(start), parse_address(end)
else:
address = parse_address(s)
return address, address
def parse_bits(s):
if s == "n/a":
return 31, 0
elif ":" in s:
high, low = s.split(":")
return int(high, 10), int(low, 10)
else:
v = int(s, 10)
return v, v
def parse_chips(s):
valid_chips = {"Chuck", "Bruce*", "Bruce%", "Bruce-1"}
for chip in s.split("+"):
assert chip in valid_chips, s
if chip == "Bruce-1":
chip = "Bruce1"
yield chip
def parse_r_w(s):
valid_r_w = {"R", "R/W", "W"}
assert s in valid_r_w, s
return set(map(str.lower, s.split("/")))
def parse_pipelined_fifo(s):
if s == "(n/a) / No":
return False, False
elif s == "No / Yes":
return False, True
elif s == "Yes / No":
return True, False
elif s == "Yes / Yes":
return True, True
elif s == "Yes / n/a":
return True, False
elif s == "n/a":
return False, False
else:
assert False, s
def parse(lines):
next_address = 0
for columns in columnize_lines(lines):
name, address_s, *rest = map(str.strip, columns)
start_address, end_address = parse_address_range(address_s)
assert next_address == start_address
next_address = end_address + 4
if name == 'reserved':
continue
#print(columns)
bits_s, chips_s, r_w_s, pipelined_fifo_s, description = rest
bits = parse_bits(bits_s)
chips = set(parse_chips(chips_s))
r_w = parse_r_w(r_w_s)
pipelined, fifo = parse_pipelined_fifo(pipelined_fifo_s)
yield RegisterLine(
name=name,
start_address=start_address,
end_address=end_address,
bits=bits,
chips=chips,
r_w=r_w,
pipelined=pipelined,
fifo=fifo,
description=description,
)
def format_rw(r_w):
if r_w == {"r", "w"}:
return "rw"
if r_w == {"w"}:
return " w"
if r_w == {"r"}:
return "r "
return ','.join(sorted(r_w))
def sanitize(name):
return name.replace("/", "_")
def format_register_line(rl, max_length):
if rl.start_address == rl.end_address:
space = ' ' * (max_length - len(rl.name))
return f"reg32 {sanitize(rl.name)};{space} // ({format_rw(rl.r_w)}) {rl.description}"
else:
length = (rl.end_address - rl.start_address) // 4 + 1
length_s = f"[{length}]"
space = ' ' * (max_length - (len(rl.name) + len(length_s)))
return f"reg32 {sanitize(rl.name)}{length_s};{space} // ({format_rw(rl.r_w)}) {rl.description}"
def render_register_struct(rls, max_length):
last_address = -4
reserved_ix = 0
yield "struct voodoo2_reg {"
for rl in rls:
assert rl.start_address >= last_address + 4
if rl.start_address != last_address + 4:
padding = rl.start_address - (last_address + 4)
yield f"reg32 _reserved{reserved_ix}[{padding // 4}];"
reserved_ix += 1
yield format_register_line(rl, max_length)
last_address = rl.end_address
yield "};"
def render_static_assert(rls, max_length):
for rl in rls:
if rl.start_address == rl.end_address:
space = ' ' * (max_length - len(rl.name))
yield f"static_assert((offsetof (struct voodoo2_reg, {sanitize(rl.name)})){space} == 0x{rl.start_address:03x});"
else:
space = ' ' * (max_length - (len(rl.name) + 3))
yield f"static_assert((offsetof (struct voodoo2_reg, {sanitize(rl.name)}[0])){space} == 0x{rl.start_address:03x});"
def render_all(rls):
max_length = max(len(rl.name) for rl in rls)
yield "#pragma once"
yield ""
yield "#include <stddef.h>"
yield "#include <stdint.h>"
yield ""
yield '#include "reg.h"'
yield ""
yield from render_register_struct(rls, max_length)
yield from render_static_assert(rls, max_length)
yield ""
yield "typedef struct voodoo2_reg voodoo2_reg;"
with open(sys.argv[1], 'r') as f:
buf = f.read()
lines = buf.split('\n')
rls = list(parse(lines))
render, out = renderer()
render(render_all(rls))
sys.stdout.write(out.getvalue())