From d11d14dadd0af96de1a98352541c83836735128d Mon Sep 17 00:00:00 2001 From: RuoyuZhou Date: Fri, 6 Mar 2026 16:38:34 +0800 Subject: [PATCH] Block IFU through macro commit handoff --- src/bcc/backend/code_template_unit.py | 5 +- tests/pyc/tb_code_template_unit_wait_block.py | 54 +++++++++++++++++++ tests/test_ctu_handoff_guard.sh | 49 +++++++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 tests/pyc/tb_code_template_unit_wait_block.py create mode 100644 tests/test_ctu_handoff_guard.sh diff --git a/src/bcc/backend/code_template_unit.py b/src/bcc/backend/code_template_unit.py index d3c9152..59a2c46 100644 --- a/src/bcc/backend/code_template_unit.py +++ b/src/bcc/backend/code_template_unit.py @@ -40,7 +40,10 @@ def build_code_template_unit(m: Circuit) -> None: & (~head_skip) & head_ready ) - block_ifu = macro_active_i | start_fire + # Keep IFU blocked until the parent macro commit retires and hands off its + # redirect/next-PC state. Dropping the block as soon as template uops finish + # lets the IFU run ahead on stale PCs during the macro_wait_commit window. + block_ifu = macro_active_i | macro_wait_commit_i | start_fire ph_init = c(0, width=2) ph_mem = c(1, width=2) diff --git a/tests/pyc/tb_code_template_unit_wait_block.py b/tests/pyc/tb_code_template_unit_wait_block.py new file mode 100644 index 0000000..1b51f6b --- /dev/null +++ b/tests/pyc/tb_code_template_unit_wait_block.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +from pycircuit import Tb, testbench + +from bcc.backend.code_template_unit import build_code_template_unit as build # noqa: E402 + + +@testbench +def tb(t: Tb) -> None: + t.timeout(16) + + for cyc in range(8): + t.drive("base_can_run", 0, at=cyc) + t.drive("head_is_macro", 0, at=cyc) + t.drive("head_skip", 0, at=cyc) + t.drive("head_valid", 0, at=cyc) + t.drive("head_done", 0, at=cyc) + t.drive("macro_active_i", 0, at=cyc) + t.drive("macro_wait_commit_i", 0, at=cyc) + t.drive("macro_phase_i", 0, at=cyc) + t.drive("macro_op_i", 0, at=cyc) + t.drive("macro_end_i", 0, at=cyc) + t.drive("macro_stacksize_i", 0, at=cyc) + t.drive("macro_reg_i", 0, at=cyc) + t.drive("macro_i_i", 0, at=cyc) + t.drive("macro_sp_base_i", 0, at=cyc) + t.drive("macro_uop_uid_i", 0, at=cyc) + t.drive("macro_uop_parent_uid_i", 0, at=cyc) + + # Handoff window: template uops are done, but the parent macro commit has + # not retired yet. IFU must stay blocked and no new start may fire. + t.drive("base_can_run", 1, at=0) + t.drive("head_is_macro", 1, at=0) + t.drive("head_valid", 1, at=0) + t.drive("head_done", 1, at=0) + t.drive("macro_wait_commit_i", 1, at=0) + t.expect("start_fire", 0, at=0) + t.expect("block_ifu", 1, at=0) + + # Active template execution also blocks IFU. + t.drive("macro_wait_commit_i", 0, at=1) + t.drive("macro_active_i", 1, at=1) + t.expect("start_fire", 0, at=1) + t.expect("block_ifu", 1, at=1) + + # Once both the active body and the handoff wait clear, IFU may run again. + t.drive("macro_active_i", 0, at=2) + t.drive("head_is_macro", 0, at=2) + t.drive("head_valid", 0, at=2) + t.drive("head_done", 0, at=2) + t.expect("start_fire", 0, at=2) + t.expect("block_ifu", 0, at=2) + + t.finish(at=3) diff --git a/tests/test_ctu_handoff_guard.sh b/tests/test_ctu_handoff_guard.sh new file mode 100644 index 0000000..fee87c5 --- /dev/null +++ b/tests/test_ctu_handoff_guard.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +source "${ROOT_DIR}/tools/lib/workspace_paths.sh" + +PYC_ROOT_DIR="$(linxcore_resolve_pyc_root "${ROOT_DIR}" || true)" +if [[ -z "${PYC_ROOT_DIR}" || ! -d "${PYC_ROOT_DIR}" ]]; then + echo "error: cannot locate pyCircuit; set PYC_ROOT=..." >&2 + exit 2 +fi + +# shellcheck disable=SC1090 +source "${PYC_ROOT_DIR}/flows/scripts/lib.sh" +pyc_find_pycc + +OUT_DIR="${ROOT_DIR}/out/pyc/code_template_unit_wait_block" +rm -rf "${OUT_DIR}" >/dev/null 2>&1 || true +mkdir -p "${OUT_DIR}" + +PYTHONPATH="$(pyc_pythonpath):${ROOT_DIR}/src" PYTHONDONTWRITEBYTECODE=1 PYCC="${PYCC}" \ + python3 -m pycircuit.cli build \ + "${ROOT_DIR}/tests/pyc/tb_code_template_unit_wait_block.py" \ + --out-dir "${OUT_DIR}" \ + --target both \ + --jobs "${PYC_SIM_JOBS:-4}" \ + --logic-depth "${PYC_SIM_LOGIC_DEPTH:-128}" \ + --run-verilator + +CPP_BIN="$( + python3 - "${OUT_DIR}/project_manifest.json" <<'PY' +import json +import sys +from pathlib import Path + +p = Path(sys.argv[1]) +data = json.loads(p.read_text(encoding="utf-8")) +print(data.get("cpp_executable", "")) +PY +)" + +if [[ -z "${CPP_BIN}" || ! -x "${CPP_BIN}" ]]; then + echo "error: missing cpp_executable for CTU handoff guard: ${CPP_BIN}" >&2 + exit 3 +fi + +(cd "${OUT_DIR}" && "${CPP_BIN}") + +echo "ctu handoff guard: ok"