1
0
Fork 0
mirror of https://git.sr.ht/~kivikakk/niar synced 2024-12-23 04:32:25 +00:00
niar/niar/cxxrtl.py
2024-06-13 22:20:45 +03:00

201 lines
5.2 KiB
Python

import json
import logging
import subprocess
from enum import Enum
from functools import partial
from pathlib import Path
from typing import Any
from amaranth import Elaboratable
from amaranth._toolchain.yosys import YosysBinary, find_yosys
from amaranth.back import rtlil
from .build import construct_top
from .cxxrtl_platform import CxxrtlPlatform
from .logger import logger, logtime
from .project import Project
__all__ = ["add_arguments"]
CXXFLAGS = [
"-std=c++17",
"-g",
"-pedantic",
"-Wall",
"-Wextra",
"-Wno-zero-length-array",
"-Wno-unused-parameter",
]
class _Optimize(Enum):
none = "none"
rtl = "rtl"
def __str__(self):
return self.value
@property
def opt_rtl(self) -> bool:
return self in (self.rtl,)
def add_arguments(np: Project, parser):
parser.set_defaults(func=partial(main, np))
match sorted(t.__name__ for t in np.cxxrtl_targets or []):
case []:
raise RuntimeError("no cxxrtl targets defined")
case [first, *rest]:
parser.add_argument(
"-t",
"--target",
choices=[first, *rest],
help="which CXXRTL target to build",
required=bool(rest),
**({"default": first} if not rest else {}),
)
parser.add_argument(
"-c",
"--compile",
action="store_true",
help="compile only; don't run",
)
parser.add_argument(
"-O",
"--optimize",
type=_Optimize,
choices=_Optimize,
help="build with optimizations (default: rtl)",
default=_Optimize.rtl,
)
parser.add_argument(
"-d",
"--debug",
action="store_true",
help="generate source-level debug information",
)
parser.add_argument(
"-v",
"--vcd",
action="store",
type=str,
help="output a VCD file",
)
def main(np: Project, args):
yosys = find_yosys(lambda ver: ver >= (0, 10))
platform = np.cxxrtl_target_by_name(args.target)
design = construct_top(np, platform)
cxxrtl_cc_path = np.path.build(f"{np.name}.cc")
with logtime(logging.DEBUG, "elaboration"):
_cxxrtl_convert_with_header(
yosys,
cxxrtl_cc_path,
design,
np.name,
platform,
black_boxes={},
)
# TODO: bring in Chryse's CompilationUnit stuff to reduce rework.
cc_o_paths = {
cxxrtl_cc_path: np.path.build(f"{np.name}.o"),
}
for path in np.path("cxxrtl").glob("*.cc"):
cc_o_paths[path] = np.path.build(f"{path.stem}.o")
cxxflags = CXXFLAGS + [
f"-DCLOCK_HZ={int(platform.default_clk_frequency)}",
*(["-O3"] if args.optimize.opt_rtl else ["-O0"]),
*(["-g"] if args.debug else []),
]
procs = []
compile_commands = {}
for cc_path, o_path in cc_o_paths.items():
cmd = [
"c++",
*cxxflags,
"-I" + str(np.path("build")),
"-I"
+ str(yosys.data_dir() / "include" / "backends" / "cxxrtl" / "runtime"),
"-c",
str(cc_path),
"-o",
str(o_path),
]
compile_commands[o_path] = cmd
logger.debug(" ".join(str(e) for e in cmd))
procs.append((cc_path, subprocess.Popen(cmd)))
with open(np.path.build("compile_commands.json"), "w") as f:
json.dump(
[
{
"directory": str(np.path()),
"file": str(file),
"arguments": arguments,
}
for file, arguments in compile_commands.items()
],
f,
)
failed = []
for cc_path, p in procs:
if p.wait() != 0:
failed.append(cc_path)
if failed:
logger.error("Failed to build paths:")
for p in failed:
logger.error(f"- {p}")
raise RuntimeError("failed compile step")
exe_o_path = np.path.build("cxxrtl")
cmd = [
"c++",
*cxxflags,
*cc_o_paths.values(),
"-o",
exe_o_path,
]
logger.debug(" ".join(str(e) for e in cmd))
subprocess.run(cmd, check=True)
if not args.compile:
cmd = [exe_o_path]
if args.vcd:
cmd += ["--vcd", args.vcd]
logger.debug(" ".join(str(e) for e in cmd))
subprocess.run(cmd, check=True)
def _cxxrtl_convert_with_header(
yosys: YosysBinary,
cc_out: Path,
design: Elaboratable,
name: str,
platform: CxxrtlPlatform,
*,
black_boxes: dict[Any, str],
):
if cc_out.is_absolute():
try:
cc_out = cc_out.relative_to(Path.cwd())
except ValueError:
raise AssertionError(
"cc_out must be relative to cwd for builtin-yosys to write to it"
)
rtlil_text = rtlil.convert(design, name=name, platform=platform)
script = []
for box_source in black_boxes.values():
script.append(f"read_rtlil <<rtlil\n{box_source}\nrtlil")
script.append(f"read_rtlil <<rtlil\n{rtlil_text}\nrtlil")
script.append(f"write_cxxrtl -header {cc_out}")
yosys.run(["-q", "-"], "\n".join(script))