Source code for fabulous.fabulous_cli.cmd_compile_design

"""Compile design command implementation for the FABulous CLI.

This module provides a unified compile flow (synthesis -> PnR -> bitgen) for the
FABulous command-line interface. It delegates execution to a compile Taskfile, passing
all necessary variables for Yosys synthesis, nextpnr place-and-route, and bitstream
generation.
"""

import argparse
import subprocess
from pathlib import Path
from typing import TYPE_CHECKING

from cmd2 import Cmd, Cmd2ArgumentParser, with_argparser, with_category
from loguru import logger

from fabulous.fabulous_cli.helper import run_task
from fabulous.fabulous_settings import get_context

if TYPE_CHECKING:
    from fabulous.fabulous_cli.fabulous_cli import FABulous_CLI

[docs] CMD_USER_DESIGN_FLOW = "User Design Flow"
[docs] compile_design_parser = Cmd2ArgumentParser( description="Compile a user design through the full flow: synthesis, " "place-and-route, and bitstream generation. " "Commonly used synth_fabulous flags (pass via --synth-extra-args): " "-nofsm, -noflatten, -extra-plib <file>. " "Run with --yosys-synth-help or --nextpnr-help for full tool documentation." )
compile_design_parser.add_argument( "files", type=Path, help="Path to the target files.", completer=Cmd.path_complete, nargs="+", ) compile_design_parser.add_argument( "-top", type=str, help="Use the specified module as the top module (default='top_wrapper').", default="top_wrapper", ) compile_design_parser.add_argument( "-json", type=Path, help="Write the design to the specified JSON file. " "If not specified, defaults to <first_file_stem>.json.", completer=Cmd.path_complete, ) compile_design_parser.add_argument( "--synth-only", help="Only run synthesis.", action="store_true", ) compile_design_parser.add_argument( "--pnr-only", help="Only run place-and-route (JSON must already exist).", action="store_true", ) compile_design_parser.add_argument( "--bitgen-only", help="Only run bitstream generation (FASM must already exist).", action="store_true", ) compile_design_parser.add_argument( "--synth-extra-args", type=str, default="", help="Extra arguments appended to the synth_fabulous command " "(e.g. '-nofsm -extra-plib prims.v').", ) compile_design_parser.add_argument( "--yosys-extra-args", type=str, default="", help="Extra arguments passed to the Yosys CLI itself (before the -p flag).", ) compile_design_parser.add_argument( "--nextpnr-extra-args", type=str, default="", help="Extra arguments passed to the nextpnr CLI.", ) compile_design_parser.add_argument( "-log", type=Path, help="Set log output file path", completer=Cmd.path_complete, ) compile_design_parser.add_argument( "-fasm", type=Path, help="Set fasm output file path", completer=Cmd.path_complete, ) compile_design_parser.add_argument( "-bin", type=Path, help="Set bit file output file path", completer=Cmd.path_complete, ) compile_design_parser.add_argument( "--yosys-synth-help", help="Print the full synth_fabulous help from Yosys and exit.", action="store_true", ) compile_design_parser.add_argument( "--nextpnr-help", help="Print the full nextpnr help and exit.", action="store_true", ) def _print_tool_help(tool_path: Path | str, args: list[str], tool_name: str) -> None: """Run a tool with the given arguments to print its help output. Parameters ---------- tool_path : Path | str Path to the tool binary. args : list[str] Arguments to pass to the tool (e.g. ["-p", "help synth_fabulous"]). tool_name : str Human-readable tool name for error messages. """ try: subprocess.run( [str(tool_path), *args], check=False, ) except FileNotFoundError: logger.error( f"{tool_name} not found at '{tool_path}'. " "Ensure it is installed and on PATH." ) @with_category(CMD_USER_DESIGN_FLOW) @with_argparser(compile_design_parser)
[docs] def do_compile_design(self: "FABulous_CLI", args: argparse.Namespace) -> None: """Compile a user design through synthesis, PnR, and bitstream generation. This function orchestrates the full compile flow by delegating to a compile Taskfile. It resolves input file paths, builds the synthesis command, and invokes the appropriate task(s) depending on the selected mode (full compile, synth-only, pnr-only, or no-bitgen). """ if args.yosys_synth_help: ctx = get_context() _print_tool_help(ctx.yosys_path, ["-p", "help synth_fabulous"], "Yosys") return if args.nextpnr_help: ctx = get_context() _print_tool_help(ctx.nextpnr_path, ["--help"], "nextpnr") return logger.info(f"Compiling design with files {[str(i) for i in args.files]}") p: Path paths: list[Path] = [] for p in args.files: if not p.is_absolute(): p = self.projectDir / p resolvePath: Path = p.absolute() if resolvePath.exists(): paths.append(resolvePath) else: logger.error(f"{resolvePath} does not exist") return # Output paths must be absolute: the task runs with cwd=.FABulous/. json_file = args.json or paths[0].with_suffix(".json") if not json_file.is_absolute(): json_file = (self.projectDir / json_file).resolve() fasm_file = args.fasm or json_file.with_suffix(".fasm") if not fasm_file.is_absolute(): fasm_file = (self.projectDir / fasm_file).resolve() log_file = args.log or json_file.parent / ( json_file.with_suffix("").name + "_npnr_log.txt" ) if not log_file.is_absolute(): log_file = (self.projectDir / log_file).resolve() bin_file = args.bin or fasm_file.with_suffix(".bin") if not bin_file.is_absolute(): bin_file = (self.projectDir / bin_file).resolve() task_dir = self.projectDir / "Test" compile_taskfile = task_dir / "Taskfile.yml" if not compile_taskfile.exists(): raise FileNotFoundError( f"Compile Taskfile not found at {compile_taskfile}. " "Please ensure the project is set up correctly." ) ctx = get_context() task_vars: dict[str, str] = { "YOSYS_PATH": str(ctx.yosys_path), "NEXTPNR_PATH": str(ctx.nextpnr_path), "FAB_PROJ_ROOT": str(self.projectDir), "DESIGN": paths[0].stem, "TOP_WRAPPER": args.top, "DESIGN_FILES": " ".join(str(p) for p in paths), "TOP_WRAPPER_FILE": str(self.projectDir / "user_design" / "top_wrapper.v"), "JSON_FILE": str(json_file), "FASM_FILE": str(fasm_file), "BIN_FILE": str(bin_file), "LOG_FILE": str(log_file), "SYNTH_EXTRA_ARGS": args.synth_extra_args, "YOSYS_EXTRA_ARGS": args.yosys_extra_args, "NEXTPNR_EXTRA_ARGS": args.nextpnr_extra_args, "NEXTPNR_VERBOSE": "--verbose" if (self.verbose or self.debug) else "", } if args.synth_only: run_task("run-yosys", task_dir, task_vars) elif args.pnr_only: run_task("run-nextpnr", task_dir, task_vars) elif args.bitgen_only: run_task("run-bitgen", task_dir, task_vars) else: run_task("build-test-design", task_dir, task_vars) logger.info("Compile flow completed successfully.")