From 161132cdcb76d9335823cf89f613085bc5c48060 Mon Sep 17 00:00:00 2001 From: Yangwook Kang Date: Wed, 8 Apr 2026 23:21:40 -0700 Subject: [PATCH] =?UTF-8?q?ADR-0021:=20PE=20pipeline=20refactor=20?= =?UTF-8?q?=E2=80=94=20component=20separation=20+=20token=20self-routing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Design for refactoring pe_accel monolith into independent builtin components: - D1: 6 independent components (scheduler, DMA, fetch_store, GEMM, MATH, TCM) - D2: Token self-routing — scheduler only dispatches + tracks completion - D3: done signal = simpy.Event (HW wire), data = message (queue) - D4: Async pipeline with single FIFO feeder, command-level ordering - D5: PE_FETCH_STORE separates TCM↔register from compute - D6: Compute components implement _process() only, chaining in base - D7: Topology adds pe_fetch_store + chaining edges - D8: Existing builtin/pe_accel → builtin_legacy backup, new builtin - D9: TileToken with plan + stage_idx for self-routing Key decisions from review: - No PipelineManager object — scheduler + existing ports sufficient - PipelineContext with exactly-once completion contract - _feed_loop singleton per scheduler, FIFO command ordering - Intra-PE chaining: no explicit latency model - Latency models ported from pe_accel current implementation Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/adr/ADR-0021-pe-pipeline-refactor.md | 528 ++++++++++++++++++++++ 1 file changed, 528 insertions(+) create mode 100644 docs/adr/ADR-0021-pe-pipeline-refactor.md diff --git a/docs/adr/ADR-0021-pe-pipeline-refactor.md b/docs/adr/ADR-0021-pe-pipeline-refactor.md new file mode 100644 index 0000000..982e1b4 --- /dev/null +++ b/docs/adr/ADR-0021-pe-pipeline-refactor.md @@ -0,0 +1,528 @@ +# ADR-0021: PE 파이프라인 리팩토링 — 컴포넌트 분리 + Scheduler 기반 라우팅 + +## Status + +Proposed + +## Context + +### 현재 구조의 문제 + +pe_accel (SchedulerV2Component)은 5개 하드웨어 블록(DmaIn, DmaWb, Gemm, Math, Tcm)을 +**단일 컴포넌트 내부**에 숨기고 있다. + +``` +SchedulerV2Component (단일 topology 노드) +├── DmaInBlock ← 내부 SimPy Store로 직접 연결 +├── DmaWbBlock ← topology에 안 보임 +├── GemmBlock ← 교체 불가 +├── MathBlock ← 교체 불가 +└── TcmBlock ← 교체 불가 +``` + +문제점: +- 블록이 다음 블록을 `desc.next_block`으로 직접 참조 — 하드코딩된 라우팅 +- 개별 블록 교체 불가 (ADR-0015 컴포넌트 교체 원칙 위배) +- topology에서 PE 내부 구조가 보이지 않음 +- GemmBlock과 MathBlock이 TCM load/store 로직을 각각 중복 구현 + +### 실제 하드웨어 구조 + +``` +HBM ←(DMA)→ TCM ←(Fetch/Store Unit)→ Register File ←→ GEMM/MATH Engine +``` + +- DMA: HBM ↔ TCM 전송 (fabric 경유, 수십~수백 ns) +- Fetch/Store Unit: TCM ↔ Register File 전송 (BW 기반, 수 ns) +- GEMM/MATH Engine: Register File 간 연산 (cycle-accurate) +- 완료 신호: PE 내부 1-cycle wire signal (done pin assert) + +--- + +## Decision + +### D1. 각 블록을 독립 컴포넌트로 분리 + +pe_accel의 내부 블록을 **독립 PeEngineBase 컴포넌트**로 분리한다. +기존 5개 + Fetch/Store Unit 1개 = 6개 컴포넌트. + +| 컴포넌트 | 역할 | HW 대응 | +|----------|------|---------| +| PE_SCHEDULER | plan 생성, tile 상태 관리, stage 라우팅 | Scheduler/Sequencer | +| PE_DMA | HBM ↔ TCM (fabric 경유) | DMA Engine | +| PE_FETCH_STORE | TCM ↔ Register File | Load/Store Unit | +| PE_GEMM | MAC compute (register only) | MAC Array | +| PE_MATH | element-wise/reduction (register only) | SIMD/Vector Unit | +| PE_TCM | BW-serialized scratchpad | SRAM Bank | + +각 컴포넌트는 topology 노드로 존재하며, port/wire로 연결된다. +`impl`을 교체하면 개별 블록의 타이밍 모델을 변경할 수 있다. + +### D2. Token Self-Routing — Scheduler는 dispatch + completion만 + +**컴포넌트가 매 stage마다 scheduler를 경유하지 않는다.** +Token이 plan을 가지고 있어 컴포넌트가 직접 다음 stage로 체이닝한다. + +``` +Scheduler → DMA → Fetch → GEMM → Math → Store → DMA_WB → (done) → Scheduler + ↑ 체이닝: scheduler 안 거침 completion만 +``` + +이는 실제 HW에서 각 블록의 done signal이 다음 블록에 직접 wire로 연결되어 +있는 구조와 일치한다. Scheduler는 **초기 dispatch + completion aggregation만** 담당. + +#### Stage 정의 + +```python +class StageType(Enum): + DMA_READ = 0 + FETCH = 1 + GEMM = 2 + MATH = 3 + STORE = 4 + DMA_WRITE = 5 +``` + +#### Plan 구조 + +Scheduler가 CompositeCmd를 받으면 **tile 단위 실행 plan**을 생성한다. +Plan은 각 tile의 **stage sequence**를 정의한다: + +```python +@dataclass +class Stage: + stage_type: StageType + component: str # topology 노드 ID (e.g. "sip0.cube0.pe0.pe_dma") + params: dict # stage별 파라미터 (dynamic) + +@dataclass(frozen=True) +class TilePlan: + tile_id: int + stages: tuple[Stage, ...] # 순서대로 실행할 stage 목록 (immutable) +``` + +Plan에 따라 stage sequence가 달라진다: + +```python +# 일반 GEMM: HBM → TCM → Register → Compute → Register → TCM → HBM +stages = (DMA_READ, FETCH, GEMM, STORE, DMA_WRITE) + +# TCM 데이터로 바로 GEMM (DMA read 생략): +stages = (FETCH, GEMM, STORE, DMA_WRITE) + +# MATH element-wise: +stages = (DMA_READ, FETCH, MATH, STORE, DMA_WRITE) + +# GEMM + accumulation (중간 K-tile, writeback 생략): +stages = (DMA_READ, FETCH, GEMM, STORE) # store to TCM only +``` + +**컴포넌트는 다음 컴포넌트를 하드코딩하지 않는다.** +Token의 plan에서 다음 stage를 읽고, out_port로 직접 전달한다. +네트워크 패킷이 라우팅 헤더를 가지고 있는 것과 같은 패턴이다. + +#### Pipeline Context + +```python +@dataclass +class PipelineContext: + id: str + total_tiles: int + completed_tiles: int = 0 + done_event: simpy.Event = None # 모든 tile 완료 시 succeed + + def complete_tile(self) -> None: + self.completed_tiles += 1 + if self.completed_tiles == self.total_tiles: + self.done_event.succeed() +``` + +**Completion은 exactly-once contract**: 각 tile의 마지막 stage는 정확히 한 번만 +`complete_tile()`을 호출해야 한다. 중복 호출은 버그이며, `done_event`는 +단 한 번만 succeed되어야 한다 (SimPy Event 제약). + +#### Scheduler 역할 (축소됨) + +Scheduler는 CompositeCmd를 받으면 plan과 PipelineContext를 생성한 뒤, +이를 scheduler 내부의 `_pending_feeds` FIFO에 enqueue하고 즉시 리턴한다. + +실제 tile 투입은 **단일 feeder process** (`_feed_loop`)가 담당한다. +이 feeder는 `_pending_feeds`를 FIFO 순서로 소비하며, +**composite command 간 tile feed interleaving은 허용하지 않는다.** +즉, 한 command의 모든 tile이 첫 stage queue에 투입된 후에만 +다음 command의 feed가 시작된다. + +Scheduler당 `_feed_loop`는 **정확히 하나만** 존재하며, +composite command의 tile feed는 이 단일 process를 통해서만 수행된다. +Command issue order는 **PE_SCHEDULER가 PeInternalTxn을 수신한 순서**를 의미한다. + +이 구조는 command issue order를 유지하면서도, 첫 stage queue full 시 +feeder process만 block되고 scheduler worker의 inbox 처리 자체는 멈추지 않도록 한다. + +```python +class PeSchedulerV2(PeEngineBase): + _pipelines: dict[str, PipelineContext] + _pending_feeds: simpy.Store # FIFO of (plan, ctx) + + def start(self, env): + super().start(env) + self._pending_feeds = simpy.Store(env) + env.process(self._feed_loop(env)) + + def _dispatch_composite(self, env, pe_txn, cmd): + plan = generate_plan(cmd) + ctx = PipelineContext( + id=next_id(), + total_tiles=len(plan.tiles), + done_event=pe_txn.done, + ) + self._pipelines[ctx.id] = ctx + + # feeder queue에 등록만 하고 즉시 리턴 + yield self._pending_feeds.put((plan, ctx)) + + def _feed_loop(self, env): + """단일 feeder process: composite command를 FIFO 순서로 feed. + + Composite command 간 tile feed interleaving은 허용하지 않는다. + 한 command의 모든 tile이 첫 stage queue에 투입된 후에만 + 다음 command의 feed가 시작된다. + + 첫 stage queue full 시 이 feeder만 block되며, + scheduler worker의 inbox 처리는 멈추지 않는다. + """ + while True: + plan, ctx = yield self._pending_feeds.get() + for tile in plan.tiles: + token = TileToken( + tile_id=tile.tile_id, + pipeline_ctx=ctx, + plan=tile, + stage_idx=0, + params=tile.stages[0].params, + ) + yield self.out_ports[tile.stages[0].component].put(token) + # queue capacity = HW queue depth → full이면 feeder만 block +``` + +본 ADR에서 scheduler는 여러 composite command를 수용할 수 있으나, +tile submission order는 command 단위 FIFO를 따른다. +Command 내부에서는 tile-level pipeline overlap을 허용하지만, +command 간 tile feed interleaving은 허용하지 않는다. + +### D3. 데이터 전달 vs 완료 신호 — HW 모델링 기준 + +| 통신 유형 | 방식 | HW 대응 | +|----------|------|---------| +| tile token (작업 지시) | message via out_port | command queue에 enqueue | +| stage 완료 → 다음 stage | 컴포넌트가 직접 out_port.put | done-triggered local enqueue | +| pipeline 완료 → scheduler | PipelineContext.complete_tile() | completion interrupt | + +**Tile token**: out_port.put() 사용. SimPy Store capacity = HW queue depth. + +**Intra-PE chaining latency**: 본 ADR 범위에서는 intra-PE stage trigger에 +explicit latency model을 두지 않는다. 컴포넌트 간 체이닝은 PE 내부 wire에 해당하며, +scheduler 왕복이 없으므로 artificial hop cost가 발생하지 않는다. + +**Pipeline 완료**: 마지막 stage의 컴포넌트가 `pipeline_ctx.complete_tile()` 호출. +모든 tile 완료 시 PipelineContext가 done_event.succeed(). + +### D4. 비동기 파이프라인 — 자연스러운 overlap + +Scheduler는 CompositeCmd를 **비동기로** 처리한다. +다만 tile feed는 command마다 독립 process를 만들지 않고, +scheduler 내부의 **단일 feeder process**가 FIFO 순서로 수행한다. +따라서 scheduler는 다음 command를 계속 받을 수 있지만, +첫-stage tile 투입 순서는 command 단위로 보장된다. + +**SimPy Store capacity = HW queue depth**이므로: +- queue가 차면 put()이 자연스럽게 block (backpressure) +- DMA가 tile 0을 처리하는 동안 GEMM은 이미 완료된 tile의 fetch를 시작 +- 두 번째 CompositeCmd가 들어오면 DMA queue에 바로 이어서 투입 + +``` +First-stage feed order (feeder → DMA queue): + [cmd1:t0][cmd1:t1][cmd1:t2]...[cmd1:tN] | [cmd2:t0][cmd2:t1]... + ↑ cmd1 feed 완료 후 cmd2 시작 + +Runtime pipeline (downstream overlap): + PE_DMA: [cmd1:t0][cmd1:t1][cmd1:t2]...[cmd1:tN][cmd2:t0][cmd2:t1]... + PE_FETCH: [cmd1:t0][cmd1:t1]... + PE_GEMM: [cmd1:t0][cmd1:t1]... + ↑ 같은 cmd 내부에서 pipeline overlap +``` + +이때 overlap은 서로 다른 command의 tile feed interleaving에서 오는 것이 아니라, +먼저 투입된 command의 tile들이 downstream stage로 진행되는 동안 feeder가 +다음 tile들을 계속 투입하면서 자연스럽게 발생한다. + +예를 들어 cmd1의 모든 tile이 첫 stage queue에 투입되기 전에는 +cmd2의 tile feed는 시작되지 않는다. 그러나 cmd1.tile0이 이미 GEMM으로 +진행한 상태에서 cmd1.tile1, cmd1.tile2가 DMA/FETCH에 남아 있을 수 있으므로, +**같은 command 내부에서는 pipeline overlap이 자연스럽게 발생**한다. + +#### 컴포넌트 체이닝 패턴 + +모든 컴포넌트가 동일한 패턴을 따른다: + +```python +def _pipeline_worker(self, env): + while True: + token = yield self._inbox.get() + + # 자기 stage 처리 + yield from self._process(env, token) + + # 다음 stage로 체이닝 (plan에서 읽음) + next_idx = token.stage_idx + 1 + if next_idx < len(token.plan.stages): + next_stage = token.plan.stages[next_idx] + token.stage_idx = next_idx + token.params = next_stage.params + yield self.out_ports[next_stage.component].put(token) + else: + # 마지막 stage — pipeline completion + token.pipeline_ctx.complete_tile() +``` + +### D5. PE_FETCH_STORE — TCM ↔ Register File 전담 + +기존에 GemmBlock과 MathBlock이 각각 TCM read/write를 구현했으나, +이를 **PE_FETCH_STORE 컴포넌트**로 분리한다. + +```python +# PE_FETCH_STORE._process() +def _process(self, env, token): + yield self.out_ports[tcm_id].put(TcmRequest(token.params["direction"], ...)) + yield tcm_done + # 체이닝은 base class가 처리 (D4 패턴) +``` + +장점: +- GEMM/MATH는 **순수 compute만** — TCM 접근 로직 없음 +- fetch/store BW 경합이 자연스럽게 모델링됨 (PE_TCM의 resource로 serialization) +- prefetch 전략 등 fetch unit 단독 교체로 실험 가능 + +### D6. 각 Compute 컴포넌트의 단순화 + +GEMM/MATH는 register 데이터가 이미 준비된 상태에서 compute만 수행. +**체이닝은 공통 패턴(D4)을 따르므로, _process()만 구현하면 된다:** + +```python +# PE_GEMM._process() +def _process(self, env, token): + yield env.timeout(self._mac_latency(token.params)) + +# PE_MATH._process() +def _process(self, env, token): + yield env.timeout(self._simd_latency(token.params)) + +# PE_FETCH_STORE._process() +def _process(self, env, token): + yield self.out_ports[tcm_id].put(TcmRequest(token.params["direction"], ...)) + yield tcm_done + +# PE_DMA._process() +def _process(self, env, token): + yield from self._do_fabric_dma(token.params) +``` + +타이밍 모델만 교체하면 cycle-accurate든 analytical든 자유롭게 변경 가능. +체이닝 로직은 base class에 있으므로 각 컴포넌트는 순수 stage 로직만 구현. + +### D7. Topology 변경 + +PE template에 PE_FETCH_STORE 추가: + +```yaml +pe_template: + components: + pe_cpu: { kind: pe_cpu, impl: pe_cpu_v1, ... } + pe_scheduler: { kind: pe_scheduler, impl: pe_scheduler_v2, ... } + pe_dma: { kind: pe_dma, impl: pe_dma_v1, ... } + pe_fetch_store: { kind: pe_fetch_store, impl: pe_fetch_store_v1, ... } + pe_gemm: { kind: pe_gemm, impl: pe_gemm_v1, ... } + pe_math: { kind: pe_math, impl: pe_math_v1, ... } + pe_mmu: { kind: pe_mmu, impl: pe_mmu_v1, ... } + pe_tcm: { kind: pe_tcm, impl: pe_tcm_v1, ... } + links: + # 기존 links... + fetch_store_to_tcm_bw_gbs: 512.0 + fetch_store_to_tcm_mm: 0.0 +``` + +PE 내부 edge 연결: +``` +PE_SCHEDULER → PE_DMA (초기 dispatch) +PE_SCHEDULER → PE_FETCH_STORE (초기 dispatch) +PE_SCHEDULER → PE_GEMM (초기 dispatch) +PE_SCHEDULER → PE_MATH (초기 dispatch) +PE_DMA → PE_FETCH_STORE (체이닝) +PE_FETCH_STORE → PE_GEMM (체이닝) +PE_FETCH_STORE → PE_MATH (체이닝) +PE_GEMM → PE_FETCH_STORE (store 체이닝) +PE_MATH → PE_FETCH_STORE (store 체이닝) +PE_FETCH_STORE → PE_DMA (writeback 체이닝) +PE_FETCH_STORE → PE_TCM (BW 요청) +``` + +Topology edge는 **control/dispatch visibility + runtime chaining** 양쪽을 포함한다. +Scheduler → 하위 컴포넌트 edge는 초기 dispatch 경로이며, +컴포넌트 간 edge는 token self-routing에 의한 runtime chaining 경로이다. + +### D8. 기존 코드 마이그레이션 — builtin 통합 + +기존 builtin v1 컴포넌트와 pe_accel을 **새 builtin으로 교체**한다. + +#### 마이그레이션 전략 + +1. 기존 `components/builtin/` → `components/builtin_legacy/`로 백업 (수정 없이 보관) +2. 기존 `components/custom/pe_accel/` → 동일하게 백업 +3. 새 `components/builtin/`에 ADR-0021 아키텍처로 재구현 +4. topology.yaml은 **하나만 유지** (pe_fetch_store 포함) +5. components.yaml은 새 builtin을 가리킴 + +```yaml +# components.yaml — 새 builtin +pe_scheduler_v1: kernbench.components.builtin.pe_scheduler:PeSchedulerComponent +pe_gemm_v1: kernbench.components.builtin.pe_gemm:PeGemmComponent +pe_math_v1: kernbench.components.builtin.pe_math:PeMathComponent +pe_dma_v1: kernbench.components.builtin.pe_dma:PeDmaComponent +pe_fetch_store_v1: kernbench.components.builtin.pe_fetch_store:PeFetchStoreComponent +pe_tcm_v1: kernbench.components.builtin.pe_tcm:PeTcmComponent +``` + +impl 이름(pe_gemm_v1 등)은 유지하되, **구현이 ADR-0021 아키텍처로 교체**된다. +기존 벤치마크와 테스트의 topology.yaml 참조는 변경 없이 동작한다. + +#### 레이턴시 모델 계승 + +새 builtin 컴포넌트의 레이턴시 모델링(MAC cycle 계산, SIMD latency, +TCM BW serialization, DMA fabric latency 등)은 **pe_accel 현재 버전의 구현을 바탕으로** 한다. +tiling.py의 tile schedule 생성 로직도 그대로 가져온다. +아키텍처(컴포넌트 분리, self-routing)만 변경하고, 타이밍 정확도는 유지한다. + +#### 테스트 전략 + +#### 테스트 계획 + +**1. 기존 테스트 통과** (regression): +마이그레이션 완료 후 기존 테스트(366개)가 전부 통과해야 한다. + +**2. 레이턴시 regression**: +pe_accel과 동일한 입력에 대해 새 builtin이 동일 레이턴시를 산출하는지 검증. + +**3. Phase 1 → Phase 2 end-to-end**: +SimPy 시뮬레이션(Phase 1)에서 op_log 생성 → DataExecutor(Phase 2)로 +실제 numpy 연산 → 결과 정합성 검증까지 통합 테스트. +- GEMM: tl.composite(gemm) → op_log → Phase 2 matmul → allclose 검증 +- MATH: tl.exp / tl.add 등 → op_log → Phase 2 numpy op → allclose 검증 +- 체이닝: GEMM 출력 → MATH 입력 → 최종 결과 end-to-end 검증 + +**4. TileToken self-routing**: +- tile이 plan의 stage sequence를 따라 체이닝되는지 검증 +- 마지막 stage에서 PipelineContext.complete_tile() exactly-once 검증 +- queue backpressure: DMA queue capacity 초과 시 feeder만 block 검증 + +**5. 비동기 pipeline overlap**: +- 동일 command 내 tile 간 stage overlap 발생 검증 (tile0 GEMM 중 tile1 DMA) +- 다중 command: cmd1 feed 완료 후 cmd2 feed 시작 (FIFO 순서) 검증 + +### D9. TileToken 메시지 정의 + +컴포넌트 간 tile 작업 전달에 사용하는 메시지. +Token이 plan과 stage index를 가지고 있어 self-routing이 가능하다. + +```python +@dataclass +class TileToken: + tile_id: int + pipeline_ctx: PipelineContext # completion 추적 + plan: TilePlan # 이 tile의 전체 stage sequence (immutable) + stage_idx: int # 현재 stage index in plan.stages + params: dict # current stage 파라미터 캐시 (canonical: plan.stages[stage_idx].params) + data_op: bool = True # op_log 기록 대상 (ADR-0020) +``` + +TileToken은 한 시점에 **하나의 컴포넌트에 의해서만 소유**되며, +동시에 여러 컴포넌트에 의해 참조되지 않는다 (single-owner). + +Token lifecycle: +1. Scheduler가 stage_idx=0으로 생성, 첫 stage 컴포넌트에 put +2. 컴포넌트가 _process() 실행 후 stage_idx 증가, 다음 컴포넌트에 put +3. 마지막 stage 컴포넌트가 pipeline_ctx.complete_tile() 호출 +4. 모든 tile 완료 시 PipelineContext가 done_event.succeed() + +기존 PeInternalTxn과의 관계: +- PeInternalTxn: PE_CPU → PE_SCHEDULER 간 command 전달 (기존 유지) +- TileToken: PE_SCHEDULER → 하위 컴포넌트 간 tile 단위 작업 전달 (신규, self-routing) + +--- + +## Non-goals + +- **PE_CPU 변경**: PE_CPU → PE_SCHEDULER 인터페이스는 변경하지 않음 + (PeInternalTxn 기반, ADR-0014 유지) +- **다중 pipeline 간 자원 경합 모델**: 현재 범위에서는 단일 pipeline의 + 정확한 모델링에 집중. 다중 pipeline 간 TCM bank conflict 등은 future work. +- **builtin_legacy 유지보수**: 백업 목적이며, 버그 수정이나 기능 추가 대상이 아님. + +## Open Questions + +- **Register File 용량 모델**: fetch unit이 register에 로드할 때 용량 제한을 + 모델링할지. 용량은 바이트 단위(register_file_bytes)로 표현하며, + 동시에 보유 가능한 tile 수는 tile 크기에 따라 결정된다. + 용량 초과 시 fetch가 stall되어 자연스러운 backpressure가 발생한다. +- **Prefetch 전략**: 본 ADR에서는 composite command 간 tile feed interleaving을 + 허용하지 않는다. 따라서 overlap은 command 간 선행 투입이 아니라, + 같은 command 내부 tile들의 pipeline progression에서 자연스럽게 발생한다. + 추가적인 prefetch가 필요하면 command 간 투입이 아니라, 같은 command 내부에서의 + tile ordering 또는 fetch/store unit policy 차원에서 검토한다. +- **PE_DMA coalescing**: tile 단위 DMA는 fragmentation 발생 가능. + DMA 내부에서 merge/coalesce하되 scheduler는 관여하지 않는 방향. +- **동기 실행 모드**: 본 ADR에서는 비동기 pipeline을 기본/유일 execution model로 + 채택한다. 디버그 또는 validation 목적의 sync mode가 필요하면 future ADR에서 검토. +- **다중 pipeline 간 TCM bank conflict**: 현재 단일 pipeline 기준. + 다중 pipeline이 동시에 TCM에 접근할 때의 bank conflict 모델은 future work. + +--- + +## Consequences + +### 긍정적 + +- 각 블록이 독립 컴포넌트 — 개별 교체 가능 (ADR-0015 준수) +- topology에서 PE 내부 구조 가시화 +- 컴포넌트가 다음 컴포넌트를 모름 — plan 기반 라우팅으로 유연성 확보 +- DMA와 compute의 자연스러운 파이프라인 overlap (SimPy Store backpressure) +- HW 모델링 정확도 향상 (done signal = Event, data transfer = message) +- fetch/store 분리로 TCM BW 경합 정확히 모델링 + +### 부정적 + +- PE 내부 컴포넌트 수 증가 (5 → 6) — topology 노드/edge 증가 +- 컴포넌트 분리로 인해 intra-PE token forwarding이 이전 대비 더 명시적으로 드러남 +- 기존 builtin/pe_accel과의 breaking change — 마이그레이션 필요 + +--- + +## 영향받는 파일 + +| 파일 | 변경 | +|------|------| +| `topology.yaml` | pe_fetch_store 컴포넌트 추가, 체이닝 edge 추가 | +| `components.yaml` | 새 builtin 컴포넌트 등록 | +| `src/kernbench/topology/builder.py` | PE 내부 edge에 fetch_store + 체이닝 edge 추가 | +| `src/kernbench/common/pe_commands.py` | TileToken 정의 추가 | +| `src/kernbench/components/builtin/pe_scheduler.py` | 재구현 (feeder + plan 기반 dispatch) | +| `src/kernbench/components/builtin/pe_gemm.py` | 재구현 (TileToken, _process 패턴) | +| `src/kernbench/components/builtin/pe_math.py` | 재구현 (TileToken, _process 패턴) | +| `src/kernbench/components/builtin/pe_dma.py` | 재구현 (TileToken, _process 패턴) | +| `src/kernbench/components/builtin/pe_fetch_store.py` | 신규 | +| `src/kernbench/components/builtin/pe_tcm.py` | 재구현 (TcmRequest 서비스) | +| `src/kernbench/components/builtin/types.py` | 신규: TilePlan, Stage, StageType, PipelineContext, TileToken | +| `src/kernbench/components/builtin/tiling.py` | pe_accel에서 이식: plan 생성 로직 | + +백업: +| `src/kernbench/components/builtin_legacy/` | 기존 builtin 전체 백업 (수정 없이 보관) | +| `src/kernbench/components/custom/pe_accel/` | 기존 pe_accel 백업 (수정 없이 보관) |