From the GCC manual. > GCC permits a C structure to have no members: struct empty { }; > The structure has size zero. In C++, empty structures are part of the > language. G++ treats empty structures as if they had a single member of type > char. I was not aware of the different behavior in C++. This fixes every maple example--most were broken for multiple reasons, including this one. This also enables SH4 caching. This includes linking code/data into the P1 area (previously this was not the case). The maple examples (which indeed involve much use of DMA) require much work to successfully work with the operand and copyback caches. The vibration example currently is the most complete, though I should consider more on how I want to structure maple response operand cache invalidation more generally.
177 lines
5.1 KiB
Python
177 lines
5.1 KiB
Python
from dataclasses import dataclass
|
|
from typing import Union
|
|
import sys
|
|
|
|
from sh7091 import read_input
|
|
from generate import renderer
|
|
|
|
@dataclass
|
|
class CommandNamespace:
|
|
name: str
|
|
issuing_right: set[str]
|
|
command_code: int
|
|
data_size: int
|
|
|
|
def command_namespace(namespace: CommandNamespace,
|
|
data_fields: list[tuple[str, tuple[int, str]]]):
|
|
length, variable = namespace.data_size
|
|
if variable is not None:
|
|
assert variable.lower() == "n"
|
|
yield "template <typename T>"
|
|
|
|
yield f"struct {namespace.name} {{"
|
|
yield f"static constexpr uint32_t command_code = {hex(namespace.command_code)};"
|
|
yield ""
|
|
|
|
if namespace.data_size == (0, None):
|
|
assert data_fields == []
|
|
yield "struct data_fields {"
|
|
yield "uint8_t _empty[0];"
|
|
yield "};"
|
|
else:
|
|
yield "struct data_fields {"
|
|
|
|
for field_name, field_size in data_fields:
|
|
const, var = field_size
|
|
if var is None:
|
|
if const in {1, 2, 4}:
|
|
bits = const * 8
|
|
yield f"uint{bits}_t {field_name};"
|
|
elif field_name == "device_id":
|
|
yield f"struct device_id {field_name};"
|
|
else:
|
|
yield f"uint8_t {field_name}[{const}];"
|
|
elif const == 0:
|
|
assert var == "n"
|
|
yield f"T {field_name};"
|
|
else:
|
|
yield f"uint8_t {field_name}[{const} + {var.upper()}];"
|
|
|
|
yield "};"
|
|
|
|
yield ""
|
|
|
|
yield "};"
|
|
if variable is not None:
|
|
assert variable == "n"
|
|
yield f"static_assert((sizeof (struct {namespace.name}<uint8_t[0]>::data_fields)) == {length});"
|
|
else:
|
|
yield f"static_assert((sizeof (struct {namespace.name}::data_fields)) == {length});"
|
|
|
|
yield ""
|
|
|
|
def parse_data_size(data_size, base, multiple) -> tuple[int, str]:
|
|
def parse_term(s):
|
|
try:
|
|
return int(s, base) * multiple
|
|
except ValueError:
|
|
div = s.split("/")
|
|
if len(div) == 1:
|
|
# this must be a variable
|
|
assert multiple == 1, s
|
|
a, = div
|
|
return a
|
|
elif len(div) == 2:
|
|
# this must be a variable divided by a number
|
|
a, b = div
|
|
b = int(b, 10)
|
|
assert b == multiple
|
|
return a
|
|
else:
|
|
assert False, div
|
|
|
|
_terms = data_size.split("+")
|
|
terms = tuple(parse_term(term) for term in _terms)
|
|
if len(terms) == 1:
|
|
term, = terms
|
|
if type(term) == str:
|
|
return (0, term)
|
|
elif type(term) == int:
|
|
return (term, None)
|
|
else:
|
|
assert False, (_terms, terms)
|
|
elif len(terms) == 2:
|
|
assert type(terms[0]) == int
|
|
assert type(terms[1]) == str
|
|
return terms
|
|
else:
|
|
assert False, (_terms, terms)
|
|
|
|
def new_aggregator():
|
|
last_name = None
|
|
namespace = None
|
|
data_fields = []
|
|
all_names = set()
|
|
|
|
def process_row(row):
|
|
nonlocal last_name
|
|
nonlocal namespace
|
|
nonlocal data_fields
|
|
|
|
if row["name"] == "":
|
|
assert all(v == "" for v in row.values()), row
|
|
return
|
|
|
|
if row["name"] != last_name:
|
|
if namespace is not None:
|
|
yield namespace, data_fields
|
|
else:
|
|
assert data_fields == []
|
|
assert row["name"] != ""
|
|
last_name = row["name"]
|
|
issuing_right = set(row["issuing_right"].split(", "))
|
|
assert all(s in {"host", "peripheral"} for s in issuing_right), row
|
|
assert issuing_right != set(), issuing_right
|
|
data_size = parse_data_size(row["data_size"], 16, 4)
|
|
|
|
namespace = CommandNamespace(
|
|
name = row["name"],
|
|
issuing_right = issuing_right,
|
|
command_code = int(row["command_code"], 16),
|
|
data_size = data_size,
|
|
)
|
|
data_fields = []
|
|
# fall through
|
|
|
|
assert last_name is None or row["name"] == last_name, (row["name"], last_name)
|
|
if row["data_field"] != "":
|
|
assert row["data_field_size"] != ""
|
|
data_fields.append((
|
|
row["data_field"],
|
|
parse_data_size(row["data_field_size"], 10, 1)
|
|
))
|
|
|
|
def terminate():
|
|
nonlocal namespace
|
|
nonlocal data_fields
|
|
if namespace is not None:
|
|
yield namespace, data_fields
|
|
|
|
def process(rows):
|
|
for row in rows:
|
|
yield from process_row(row)
|
|
yield from terminate()
|
|
|
|
return process
|
|
|
|
def headers():
|
|
yield "#pragma once"
|
|
yield ""
|
|
yield "#include <cstdint>"
|
|
yield ""
|
|
yield "struct device_id {"
|
|
yield "uint32_t ft;"
|
|
yield "uint32_t fd[3];"
|
|
yield "};"
|
|
yield "static_assert((sizeof (struct device_id)) == 16);"
|
|
|
|
input_file = sys.argv[1]
|
|
rows = read_input(input_file)
|
|
process = new_aggregator()
|
|
|
|
render, out = renderer()
|
|
render(headers())
|
|
for namespace, data_fields in process(rows):
|
|
render(command_namespace(namespace, data_fields))
|
|
sys.stdout.write(out.getvalue())
|