Phase 2c-1: wire chunkifies into Flits + reassembly compat layer

Wire decomposes Transactions into Flits per `_flit_bytes` but emits all
flits atomically at the same env.now — preserves single-msg timing as
infrastructure for Phase 2c-2 (per-flit timing + flit-aware routers).

Non-flit-aware components reassemble Flits in `_fan_in`; `_update_step`
sets txn.step to current component's path position so legacy
step-based routing continues working when upstream is flit-aware.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-14 22:03:59 -07:00
parent 5fdb6f8797
commit b31b3e8248
2 changed files with 67 additions and 11 deletions
+25 -9
View File
@@ -11,7 +11,7 @@ from kernbench.components.context import ComponentContext
from kernbench.policy.address.phyaddr import PhysAddr
from kernbench.policy.routing.router import AddressResolver, PathRouter
from kernbench.runtime_api.kernel import KernelLaunchMsg, MemoryReadMsg, MemoryWriteMsg, PeDmaMsg
from kernbench.sim_engine.transaction import Transaction
from kernbench.sim_engine.transaction import Flit, Transaction
from kernbench.topology.types import Edge, TopologyGraph
@@ -41,6 +41,14 @@ class GraphEngine:
for e in graph.edges:
self._edge_map[(e.src, e.dst)] = e
self._ns_per_mm: float = graph.spec.get("system", {}).get("ns_per_mm", 0.01)
# ADR-0033 Phase 2c-1: wire chunkifies into Flits (Phase 2c-2/3
# will graduate to per-flit timing + flit-aware components). At
# 2c-1 stage all flits of a Transaction are emitted atomically
# at the same env.now to preserve current single-msg timing —
# Flit transport is in place but behaviorally equivalent.
self._flit_bytes: int = int(
graph.spec.get("system", {}).get("flit_bytes", 256)
)
self._results: dict[str, tuple[Completion, Trace]] = {}
self._events: dict[str, simpy.Event] = {}
self._counter = 0
@@ -259,18 +267,26 @@ class GraphEngine:
available_at = 0.0
while True:
msg = yield out_port.get()
# ADR-0033 Phase 2c-1: chunkify Transactions into Flits but
# emit atomically (same env.now) to preserve current timing.
# Phase 2c-2 will graduate to per-flit timing.
if isinstance(msg, Transaction) and msg.nbytes > 0:
items = list(msg.into_flits(self._flit_bytes))
payload_nbytes = msg.nbytes
else:
items = [msg]
payload_nbytes = getattr(msg, "nbytes", 0) or 0
# BW occupancy: wait for link to become free, then mark busy
if bw_gbs > 0:
nbytes = getattr(msg, "nbytes", 0)
if nbytes > 0:
wait = available_at - self._env.now
if wait > 0:
yield self._env.timeout(wait)
available_at = self._env.now + (nbytes / bw_gbs)
if bw_gbs > 0 and payload_nbytes > 0:
wait = available_at - self._env.now
if wait > 0:
yield self._env.timeout(wait)
available_at = self._env.now + (payload_nbytes / bw_gbs)
# Propagation delay
if prop_ns > 0:
yield self._env.timeout(prop_ns)
yield in_port.put(msg)
for item in items:
yield in_port.put(item)
def _process(self, key: str, request: Any, done: simpy.Event):
if isinstance(request, PeDmaMsg):