diff --git a/api/steps/dc/01_run_api.step.py b/api/steps/dc/01_run_api.step.py new file mode 100644 index 0000000..b81145c --- /dev/null +++ b/api/steps/dc/01_run_api.step.py @@ -0,0 +1,39 @@ +from motia import ApiRequest, ApiResponse, FlowContext, api + +from utils.path import get_buckyball_path + +config = { + "name": "dc-run-api", + "description": "run Design Compiler synthesis script", + "flows": ["dc"], + "triggers": [api("POST", "/dc/run")], + "enqueues": ["dc.run"], +} + + +async def handler(request: ApiRequest, ctx: FlowContext) -> ApiResponse: + bbdir = get_buckyball_path() + body = request.body or {} + + srcdir = body.get("srcdir") + if not srcdir: + return ApiResponse( + status=400, + body={ + "status": "error", + "message": "Missing required parameter: --srcdir", + "example": 'bbdev dc --srcdir arch/ReluBall_1 --top ReluBall', + }, + ) + + data = { + "srcdir": srcdir, + "top": body.get("top"), + "keep_hierarchy": bool(body.get("keep_hierarchy", False)), + "balltype": body.get("balltype"), + "config": body.get("config", "sims.verilator.BuckyballToyVerilatorConfig"), + "output_dir": body.get("output_dir"), + "report_dir": f"{bbdir}/bb-tests/output/dc/reports", + } + await ctx.enqueue({"topic": "dc.run", "data": {**data, "_trace_id": ctx.trace_id}}) + return ApiResponse(status=202, body={"trace_id": ctx.trace_id}) diff --git a/api/steps/dc/01_run_event.step.py b/api/steps/dc/01_run_event.step.py new file mode 100644 index 0000000..e245d84 --- /dev/null +++ b/api/steps/dc/01_run_event.step.py @@ -0,0 +1,133 @@ +import os +import shlex +import sys + +from motia import FlowContext, queue + +# Add the utils directory to the Python path +utils_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) +if utils_path not in sys.path: + sys.path.insert(0, utils_path) + +from utils.event_common import check_result, get_origin_trace_id +from utils.path import get_buckyball_path +from utils.stream_run import stream_run_logger + +config = { + "name": "dc-run", + "description": "run Design Compiler synthesis script", + "flows": ["dc"], + "triggers": [queue("dc.run")], + "enqueues": [], +} + + +async def handler(input_data: dict, ctx: FlowContext) -> None: + origin_tid = get_origin_trace_id(input_data, ctx) + bbdir = get_buckyball_path() + script_path = f"{bbdir}/evals/run-dc.sh" + + srcdir = input_data.get("srcdir") + top = input_data.get("top") + keep_hierarchy = bool(input_data.get("keep_hierarchy", False)) + balltype = input_data.get("balltype") + config_name = input_data.get("config", "sims.verilator.BuckyballToyVerilatorConfig") + output_dir = input_data.get("output_dir") + + if not srcdir: + success_result, failure_result = await check_result( + ctx, + 1, + continue_run=False, + extra_fields={ + "task": "dc", + "error": "Missing required parameter: srcdir", + "example": "bbdev dc --srcdir arch/ReluBall_1 --top ReluBall", + }, + trace_id=origin_tid, + ) + return failure_result + + if not os.path.exists(script_path): + success_result, failure_result = await check_result( + ctx, + 1, + continue_run=False, + extra_fields={ + "task": "dc", + "error": f"DC script not found: {script_path}", + }, + trace_id=origin_tid, + ) + return failure_result + + # Keep compatibility with old manual flow: ensure run-dc.sh is executable. + os.chmod(script_path, os.stat(script_path).st_mode | 0o111) + + # Optional integrated pre-step: generate Ball verilog first. + if balltype: + final_output_dir = output_dir or f"{balltype}_1" + verilog_dir = f"{bbdir}/arch/{final_output_dir}" + verilog_cmd = ( + f"mill -i __.test.runMain sims.verify.BallTopMain {shlex.quote(str(balltype))} " + "--disable-annotation-unknown --strip-debug-info -O=debug " + f"--split-verilog -o={shlex.quote(verilog_dir)}" + ) + + verilog_result = stream_run_logger( + cmd=verilog_cmd, + logger=ctx.logger, + cwd=f"{bbdir}/arch", + stdout_prefix="dc verilog", + stderr_prefix="dc verilog", + ) + + if verilog_result.returncode != 0: + success_result, failure_result = await check_result( + ctx, + verilog_result.returncode, + continue_run=False, + extra_fields={ + "task": "dc", + "error": "Failed to generate pre-DC verilog", + "balltype": balltype, + "config": config_name, + }, + trace_id=origin_tid, + ) + return failure_result + + # If user keeps default-like srcdir, force it to generated dir for consistency. + srcdir = f"arch/{final_output_dir}" + + command_parts = ["bash", shlex.quote(script_path), "--srcdir", shlex.quote(str(srcdir))] + if top: + command_parts.extend(["--top", shlex.quote(str(top))]) + if keep_hierarchy: + command_parts.append("--keep-hierarchy") + + command = " ".join(command_parts) + + result = stream_run_logger( + cmd=command, + logger=ctx.logger, + cwd=bbdir, + executable="bash", + stdout_prefix="dc run", + stderr_prefix="dc run", + ) + + success_result, failure_result = await check_result( + ctx, + result.returncode, + continue_run=False, + extra_fields={ + "task": "dc", + "report_dir": input_data.get("report_dir", f"{bbdir}/bb-tests/output/dc/reports"), + "srcdir": srcdir, + "top": top, + }, + trace_id=origin_tid, + ) + + return diff --git a/bbdev b/bbdev index 9e8be93..03790fd 100755 --- a/bbdev +++ b/bbdev @@ -77,6 +77,9 @@ WORKFLOW_COMMANDS = { "flashbitstream": 'Flash bitstream onto AU280. Args: "[--bitstream ] [--serial ]"', "runworkload": 'Run workload on AU280. Args: "[--workload ] [--board ] [--timeout ] [--uart ]"', }, + "dc": { + "run": 'Run Design Compiler synthesis. Args: "--srcdir [--top ] [--keep-hierarchy] [--balltype ] [--config ] [--output-dir ]"', + }, } ALL_COMMANDS = ["start", "stop"] + list(WORKFLOW_COMMANDS) @@ -169,8 +172,21 @@ def parse_args(argv: list[str]) -> argparse.Namespace: stop_parser = subparsers.add_parser("stop", parents=[common], help="Stop dev server") stop_parser.add_argument("--all", action="store_true", help="Stop all servers") + # dc direct mode (no --run wrapper): + # bbdev dc --srcdir ... [--top ...] [--keep-hierarchy] + # [--balltype ...] [--config ...] [--output-dir ...] + dc_parser = subparsers.add_parser("dc", parents=[common], help="Run DC synthesis") + dc_parser.add_argument("--srcdir", type=str, required=True, help="Design source directory relative to repo root") + dc_parser.add_argument("--top", type=str, default=None, help="Top module name") + dc_parser.add_argument("--keep-hierarchy", action="store_true", help="Keep hierarchy during compile") + dc_parser.add_argument("--balltype", type=str, default=None, help="Optional Ball type for pre-verilog generation, e.g. ReluBall") + dc_parser.add_argument("--config", type=str, default="sims.verilator.BuckyballToyVerilatorConfig", help="Verilog generation config when --balltype is set") + dc_parser.add_argument("--output-dir", type=str, default=None, help="Verilog output dir under arch/, e.g. ReluBall_1") + # Build workflow subcommands from WORKFLOW_COMMANDS table for cmd_name, operations in WORKFLOW_COMMANDS.items(): + if cmd_name == "dc": + continue cmd_parser = subparsers.add_parser( cmd_name, parents=[common], help=f"{cmd_name} operations" ) @@ -193,6 +209,20 @@ def parse_args(argv: list[str]) -> argparse.Namespace: def extract_command_info(args: argparse.Namespace) -> dict: """Extract command, operation, and parsed sub-arguments from argparse Namespace.""" + if args.command == "dc": + return { + "command": "dc", + "operation": "run", + "args": { + "srcdir": args.srcdir, + "top": args.top, + "keep_hierarchy": bool(args.keep_hierarchy), + "balltype": args.balltype, + "config": args.config, + "output_dir": args.output_dir, + }, + } + result = {"command": args.command, "operation": None, "args": {}} for attr_name, attr_value in vars(args).items():