"""PE_TCM: tightly-coupled memory with BW-based access serialization (ADR-0014 D1). Models scratchpad memory inside the PE. Handles both legacy Transaction forwarding and TcmRequest from PE_FETCH_STORE for BW-serialized read/write access. Two channels (read/write) with independent serialization. Ported from pe_accel TcmBlock timing model. """ from __future__ import annotations from collections.abc import Generator from dataclasses import dataclass from typing import TYPE_CHECKING, Any import simpy from kernbench.components.base import ComponentBase if TYPE_CHECKING: from kernbench.components.context import ComponentContext from kernbench.topology.types import Node @dataclass class TcmRequest: """Request to read from or write to TCM (used by PE_FETCH_STORE).""" direction: str # "read" or "write" nbytes: int done: simpy.Event tag: str = "" class PeTcmComponent(ComponentBase): """PE_TCM: BW-serialized scratchpad memory (ADR-0014 D1). Dual-channel: read and write can proceed in parallel, but concurrent reads serialize, concurrent writes serialize. BW from topology attrs or pe_template links. """ def __init__(self, node: Node, ctx: ComponentContext | None = None) -> None: super().__init__(node, ctx) self._read_bw: float = float(node.attrs.get("read_bw_gbs", 512.0)) self._write_bw: float = float(node.attrs.get("write_bw_gbs", 512.0)) self._read_res: simpy.Resource | None = None self._write_res: simpy.Resource | None = None def start(self, env: simpy.Environment) -> None: self._read_res = simpy.Resource(env, capacity=1) self._write_res = simpy.Resource(env, capacity=1) super().start(env) def run(self, env: simpy.Environment, nbytes: int) -> Generator: overhead_ns = float(self.node.attrs.get("overhead_ns", 0.0)) yield env.timeout(overhead_ns) def _worker(self, env: simpy.Environment) -> Generator: """Dispatch TcmRequest (from fetch_store) and Transaction (fabric).""" while True: msg: Any = yield self._inbox.get() if isinstance(msg, TcmRequest): env.process(self._handle_tcm_request(env, msg)) else: env.process(self._forward_txn(env, msg)) def _handle_tcm_request(self, env: simpy.Environment, req: TcmRequest) -> Generator: """BW-serialized access: acquire channel, apply delay, signal done.""" if req.direction == "write": res = self._write_res bw = self._write_bw else: res = self._read_res bw = self._read_bw assert res is not None with res.request() as lock: yield lock if bw > 0 and req.nbytes > 0: delay_ns = req.nbytes / bw yield env.timeout(delay_ns) req.done.succeed()