Follow-up to the bilingual-structure commit: docs/adr-ko/ now holds only Korean versions (24 files translated from English placeholders), ADR-0013 slug uses kebab-case in both folders, and the verify tool allows translated parenthetical commentary in the Status block. - Translate 24 English files in docs/adr-ko/ to Korean. The previous bilingual-structure commit had left these as English copies because their source content was already English; this commit fulfills the policy that docs/adr-ko/ contains only Korean. - Rename ADR-0013 in both adr/ and adr-ko/ from ver-verification_strategy.md to ver-verification-strategy.md (kebab-case consistency with other ADRs). - CLAUDE.md (ADR Translation Discipline): clarify that only the Status lifecycle keyword (Accepted / Proposed / Stub / Draft / Superseded by ADR-NNNN / Merged into ADR-NNNN) must match across EN and KO; parenthetical commentary and trailing list items may be translated. - tools/verify_adr_lang_pairs.py: replace byte-equal Status check with normalize_status_keyword() which strips parenthetical commentary and takes only the first non-empty line. - tests/test_verify_adr_lang_pairs.py: update existing test names, add coverage for translated parenthetical, translated trailing list, and Superseded-by-NNNN keyword equality. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
16 KiB
ADR-0014: PE 파이프라인 실행 모델
Status
Accepted
Context
본 ADR은 PE 내부 커널 실행 모델을 정의한다:
- PE 내부 컴포넌트의 역할 분담
- 명령 디스패치 경로 (simple / composite / epilogue를 포함한 multi-op composite)
- TileToken 기반 자가-라우팅 파이프라인 (스케줄러는 디스패치와 완료 처리만 담당)
- 레지스터 파일을 매개로 한 TCM 중심 데이터플로우
- 엔진 자원 모델
- 관측 가능성 및 트레이스 계약
- 토폴로지 표현
PE 내부 구조 (본 ADR 범위 7개 컴포넌트 + 외부 참조 2개):
pe_cpu,pe_scheduler,pe_dma,pe_fetch_store,pe_gemm,pe_math,pe_tcm— 본 ADR에서 정의pe_mmu— VA 모델, ADR-0011 D-VA에서 정의pe_ipcq— 집합 통신, ADR-0023에서 정의
목표는 결정론적이고 트레이스 친화적인 실행 계약을 통해 각 블록이 독립적으로 교체 가능하도록 유지하는 것이다.
Decision
D1. PE 내부 컴포넌트의 역할
PE_CPU
- 커널 명령어 스트림 / 제어 로직을 실행한다.
- PE 명령을 생성하여
PE_SCHEDULER에 제출한다 (PeInternalTxn을 통해). - 엔진 큐에 직접 작업을 넣지 않는다.
PE_SCHEDULER
- PE 내부의 유일한 디스패처.
PE_CPU로부터 명령을 수신한다. 명령 타입별 디스패치:- Simple 명령 (
DmaReadCmd,DmaWriteCmd,GemmCmd,MathCmd) → 대상 엔진으로 직접 전달. CompositeCmd→TilePlan을 생성하고, 단일_feed_loop를 통해 파이프라인에 타일을 공급한다 (D6).
- Simple 명령 (
- composite 내부의 stage-to-stage 체이닝에는 관여하지 않는다; 이는 토큰 자가-라우팅(D6)으로 처리된다.
PE_DMA
- 큐브 NoC를 통해 TCM과 외부 메모리 도메인(HBM, 공유 SRAM, 큐브 간 UCIe) 사이의 메모리 전송을 처리한다.
- 두 개의 실행 채널:
DMA_READ(capacity = 1) 및DMA_WRITE(capacity = 1) — D4 참조.
- 추가 가상 채널:
vc_compute— GEMM/MATH 타일의 load/store/writeback 트래픽.vc_comm— IPCQ 집합 통신 송신 데이터 (ADR-0023 D8에서 정의).
PE_FETCH_STORE
- TCM ↔ 레지스터 파일 전송 유닛.
- 레지스터 파일 접근 시맨틱을 컴퓨트 엔진으로부터 격리하여 GEMM/MATH가 순수한 컴퓨트 컴포넌트로 유지되도록 한다.
- BW 기반 레이턴시 모델; TCM 접근 경합은
PE_TCM의 BW 자원을 통해 자연스럽게 직렬화된다.
PE_GEMM
- MAC 어레이. 레지스터 파일에서 피연산자를 읽고, 결과를 레지스터 파일에
쓴다.
PE_TCM에 직접 접근하지 않는다.
PE_MATH
- 원소별 / 리덕션 / SIMD 유닛. 레지스터 파일을 읽고 쓴다.
PE_TCM
- BW로 직렬화된 접근을 갖는 tightly-coupled 스크래치패드. 소유권에 따라 두 개의 논리 영역으로 분할된다 (D5 참조).
외부 참조 컴포넌트 (다른 곳에서 정의됨):
pe_mmu— 접근마다 VA→PA 변환 (ADR-0011 D-VA).pe_ipcq— 집합 통신 링 버퍼와 피어 엔드포인트 메타데이터 (ADR-0023).
D2. 명령 생명주기와 큐
PE_SCHEDULER는 세 개의 논리적 구조를 유지한다:
SubmissionQueue — PE_CPU가 쓰고, 스케줄러가 소비한다.
InflightTable — PE_SCHEDULER만 소유하고 변경한다; 전개된 sub-command,
의존성 상태, 엔진 할당, 완료 상태를 추적한다.
CompletionQueue — PE_SCHEDULER가 쓴다; 최종 완료 레코드를 보관한다.
Single-writer 규칙: PE_SCHEDULER만이 명령 완료 상태를 변경한다.
엔진은 명시적 이벤트 / 메시지로 완료를 보고하며, 이는 스케줄러가
소비한다.
명령 완료: 모든 sub-command가 완료되면 PE_SCHEDULER가 완료 레코드를
발행한다.
D3. 디스패치 모드
D3.1 Simple 명령
simple 명령은 정확히 하나의 엔진 sub-command로 전개된다:
DmaReadCmd/DmaWriteCmd→PE_DMAGemmCmd→PE_GEMMMathCmd→PE_MATH
흐름:
PE_CPU → SubmissionQueue → PE_SCHEDULER → engine queue → engine execution
→ completion → PE_SCHEDULER → CompletionQueue
D3.2 Composite 명령 (단일-op 타일 파이프라인)
기본 CompositeCmd는 단일 컴퓨트 op를 타일 파이프라인 시퀀스로 실행한다:
DMA_READ → FETCH (TCM → RF) → COMPUTE (GEMM | MATH) → STORE (RF → TCM) → DMA_WRITE
PE_SCHEDULER는 DMA 페이로드를 하드웨어 타일로 분할하고, 단조 증가하는
tile_id를 갖는 TileToken을 타일마다 하나씩 발행한다.
타일 의존성 (단일 타일 t 내부):
DMA_READ(t) → FETCH(t) → COMPUTE(t) → STORE(t) → DMA_WRITE(t)
엔진 자원이 허용하는 한 타일 간 오버랩이 허용된다 (D4가 제약을 규정):
DMA_READ(t+1) ∥ COMPUTE(t)
DMA_WRITE(t-1) ∥ COMPUTE(t)
D3.3 Multi-op composite (스코프를 갖는 head + epilogue)
CompositeCmd는 ops: tuple[OpSpec, ...]를 운반하여 multi-op
파이프라인을 표현할 수 있다:
@dataclass(frozen=True)
class OpSpec:
kind: str # "gemm" | "math.exp" | "math.bias_add" | ...
scope: Scope # "per_k_tile" | "per_output_tile" | "once"
...
ops[0](head)이 타일 기하 구조를 정의한다 (예: head GEMM이 M/K/N 분할을 결정).ops[1:](epilogue)는 후속 stage이며scope에 따라 실행 빈도가 결정된다:per_k_tile— 모든 K-리덕션 스텝마다.per_output_tile— 출력 타일당 한 번.once— 커널당 한 번.
크로스-엔진 체인(예: GEMM head → MATH epilogue)은 자연스럽다 — 각 stage는 토큰 자가-라우팅(D6)을 통해 디스패치되므로, GEMM과 MATH는 동일한 컴퓨트 슬롯(D4)을 공유하더라도 동일 composite 내에서 직렬적으로 참여한다.
비어 있는 ops 형식은 레거시 단일-op 경로이다.
D4. 엔진 자원 모델
DMA 엔진:
DMA_READ:simpy.Resource(capacity=1).DMA_WRITE:simpy.Resource(capacity=1).- 두 채널은 동시에 실행된다 (READ ∥ WRITE 허용).
- 채널 내부에서는 요청이 직렬화된다 (READ ∥ READ 불가; WRITE도 동일).
vc_comm은 IPCQ 트래픽을 위한 직교 채널로 ADR-0023 D8에서 정의됨 — 본 ADR 범위 밖.
컴퓨트 엔진:
accel_slot:PE_GEMM과PE_MATH가 공유하는simpy.Resource(capacity=1).- PE 내에서 동시에 최대 한 개의 컴퓨트 op만 실행된다.
- Multi-op composite 체인(D3.3)은 이 슬롯을 통해 컴퓨트 stage를 직렬로 실행한다; 토큰 자가-라우팅(D6)이 이전 컴퓨트가 슬롯을 해제한 후에만 다음 stage가 시작되도록 보장한다.
엔진 완료: 각 엔진은 완료 이벤트를 발행하며, 이는 스케줄러 /
PipelineContext(D6)가 소비한다.
D5. 데이터플로우
입력 경로 (HBM 소스):
HBM → cube NOC → PE_DMA (DMA_READ) → PE_TCM
PE_TCM → PE_FETCH_STORE → Register File
Register File → PE_GEMM | PE_MATH
입력 경로 (공유 SRAM 소스):
Shared SRAM → cube NOC → PE_DMA (DMA_READ) → PE_TCM
PE_TCM → PE_FETCH_STORE → Register File
출력 경로 (HBM 목적지):
Register File → PE_FETCH_STORE → PE_TCM
PE_TCM → PE_DMA (DMA_WRITE) → cube NOC → HBM
GEMM/MATH는 PE_TCM에 직접 접근하지 않는다 — PE_FETCH_STORE가
TCM↔레지스터 파일의 유일한 게이트웨이이다. 이를 통해 TCM BW 경합이
명시적으로 드러나며, fetch 유닛 정책(예: 프리패치)을 컴퓨트 엔진과
독립적으로 교체할 수 있다.
D5.1 PE_TCM 분할
PE_TCM은 두 개의 논리 영역으로 분할된다:
SchedulerReservedTCM
PE_SCHEDULER가 단독으로 소유한다.- composite 명령의 타일 버퍼를 보관한다.
PE_SCHEDULER가 이 영역을 분할하고, DMA_READ / COMPUTE / DMA_WRITE stage마다 버퍼를 할당하며, 입출력 분리를 보장하고, 타일-버퍼 수명을 관리한다.
AllocatableTCM
PEMemAllocator가 관리하는 범용 영역.- 호스트 / DP 가시 할당에 사용된다.
가시성 규칙 (강한 격리): PEMemAllocator는 SchedulerReservedTCM을
보거나 그 내부에 할당해서는 안 된다. 예약 영역은 구성 시점에 할당자가
관리하는 범위에서 제외된다.
타일 버퍼 규칙:
- 타일이 활성 수명 동안
SchedulerReservedTCM내부의 입력 버퍼와 출력 버퍼는 겹쳐서는 안 된다. - 타일 버퍼는 해당
DMA_WRITE가 완료될 때까지 유효하다. - 버퍼 재사용은 소비하는 타일의 수명이 끝난 후에만 허용된다.
D6. TileToken 자가-라우팅 파이프라인
composite의 stage-to-stage 진행은 스케줄러를 거치지 않고 일어난다.
각 컴포넌트는 토큰의 plan을 사용해 토큰을 다음 stage의 컴포넌트로
직접 전달한다:
Scheduler → DMA → Fetch → GEMM → Math (epi) → Store → DMA_WB → (complete)
↑ chaining: no scheduler hop ↑
PipelineContext.complete_tile()
이는 실제 HW의 done-wire 체인을 반영한다. 스케줄러는 초기 디스패치 + 완료 집계만 담당한다.
TilePlan / Stage
class StageType(Enum):
DMA_READ = 0
FETCH = 1
GEMM = 2
MATH = 3
STORE = 4
DMA_WRITE = 5
@dataclass(frozen=True)
class Stage:
stage_type: StageType
component: str # topology node id (e.g., "sip0.cube0.pe0.pe_dma")
params: dict # stage-specific parameters
@dataclass(frozen=True)
class TilePlan:
tile_id: int
stages: tuple[Stage, ...]
TileToken
@dataclass
class TileToken:
tile_id: int
pipeline_ctx: PipelineContext
plan: TilePlan
stage_idx: int
params: dict # cached current stage params
data_op: bool = True # op_log opt-in (ADR-0020 D4)
단일 소유자 불변식: 토큰은 한 시점에 정확히 한 컴포넌트가 소유한다.
생명주기: 스케줄러가 stage_idx=0으로 생성 → 컴포넌트 _process() →
stage_idx 증가 → 다음 stage의 in_port에 put → 마지막 stage가
pipeline_ctx.complete_tile() 호출.
PipelineContext (정확히 한 번 완료)
@dataclass
class PipelineContext:
id: str
total_tiles: int
completed_tiles: int = 0
done_event: simpy.Event = None
def complete_tile(self) -> None:
self.completed_tiles += 1
if self.completed_tiles == self.total_tiles:
self.done_event.succeed()
각 타일의 마지막 stage는 complete_tile()을 정확히 한 번 호출해야
한다. 중복 호출은 버그이다 (SimPy Event는 최대 한 번만 succeed
가능).
Feed 순서
PE_SCHEDULER는 _pending_feeds FIFO를 소비하는 _feed_loop 프로세스를
정확히 하나 갖는다. composite 명령은 제출 순서대로 인큐되며, 한 명령의
타일 feed는 다음 명령의 feed가 시작되기 전에 완료까지 실행된다.
명령 간 타일-feed 인터리빙은 허용되지 않는다.
단일 명령의 타일들 내부에서는 다운스트림 파이프라인 오버랩이 자연스럽게 발생한다 — 이전 타일이 후행 stage를 진행하는 동안 feeder는 남은 타일을 첫 stage 큐로 계속 푸시한다 (SimPy Store 백프레셔가 흐름 제어를 관장한다). 첫 stage 큐가 가득 차면 feeder만 블록되며, 스케줄러 워커의 inbox 처리는 계속된다.
토큰 라우팅 패턴 (기본 클래스)
def _pipeline_worker(self, env):
while True:
token = yield self._inbox.get()
yield from self._process(env, token) # stage-specific logic
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:
token.pipeline_ctx.complete_tile()
각 컴포넌트는 _process()만 구현한다; 체이닝은 기본 클래스에 존재한다.
D7. 관측 가능성 및 트레이스 계약
시뮬레이터는 결정론적 트레이스 이벤트를 발행한다:
command_submittedsub_command_dispatchedengine_startengine_completetile_readycommand_complete
동일한 입력에 대해 트레이스 순서는 결정론적이어야 한다.
D8. 토폴로지 표현
PE 내부 컴포넌트는 cube.pe_template에 선언된다:
pe_template:
components:
pe_cpu: { kind: pe_cpu, impl: builtin.pe_cpu, attrs: { overhead_ns: ... } }
pe_scheduler: { kind: pe_scheduler, impl: builtin.pe_scheduler, attrs: { overhead_ns: ... } }
pe_dma: { kind: pe_dma, impl: builtin.pe_dma, attrs: { rd_engines: 1, wr_engines: 1 } }
pe_fetch_store: { kind: pe_fetch_store, impl: builtin.pe_fetch_store, attrs: { ... } }
pe_gemm: { kind: pe_gemm, impl: builtin.pe_gemm, attrs: { shared_resource: accel_slot, ... } }
pe_math: { kind: pe_math, impl: builtin.pe_math, attrs: { shared_resource: accel_slot, ... } }
pe_tcm: { kind: pe_tcm, impl: builtin.pe_tcm, attrs: { size_mb: ..., read_bw_gbs: ..., write_bw_gbs: ... } }
pe_mmu: { kind: pe_mmu, impl: builtin.pe_mmu, attrs: { ... } } # ADR-0011 D-VA
pe_ipcq: { kind: pe_ipcq, impl: builtin.pe_ipcq, attrs: { ... } } # ADR-0023
links:
# Scheduler dispatch edges (initial)
scheduler_to_dma_mm: 0.0
scheduler_to_fetch_store_mm: 0.0
scheduler_to_gemm_mm: 0.0
scheduler_to_math_mm: 0.0
# Pipeline chaining edges (token self-routing per D6)
dma_to_fetch_store_mm: 0.0
fetch_store_to_gemm_mm: 0.0
fetch_store_to_math_mm: 0.0
gemm_to_fetch_store_mm: 0.0
gemm_to_math_mm: 0.0
math_to_fetch_store_mm: 0.0
fetch_store_to_dma_mm: 0.0
fetch_store_to_tcm_bw_gbs: ...
템플릿은 PE마다 한 번 인스턴스화된다. PE 인스턴스는 cube.pe_layout
(코너 배치)으로부터 파생된다. 외부 연결성(PE_DMA ↔ cube NoC ↔ HBM 등)은
큐브 수준에서 모델링된다 (ADR-0017 D4).
Consequences
Positive
- 각 블록이 독립적인 토폴로지 노드이다 — DI(ADR-0015)를 통해 개별 교체 가능하다.
- PE 내부 구조가 토폴로지 그래프에 가시화된다.
- 컴포넌트는 자신의 다운스트림을 알지 못한다 — plan 기반 라우팅이 유연성을 제공한다 (예: epilogue 체인에 스케줄러 변경이 불필요).
- DMA와 컴퓨트가 SimPy Store 백프레셔를 통해 자연스럽게 오버랩된다.
- Multi-op composite가 융합 연산(예: GEMM + bias_add)을 엔진 수준 결합 없이 표현한다.
- TCM 접근 경합이 현실적이다 —
PE_FETCH_STORE가 TCM↔RF의 유일한 게이트웨이이다.
Negative
- PE 내부 컴포넌트 수가 더 거친 모델보다 많다 (기본 7개 + 외부 참조 2개) — 더 많은 토폴로지 노드/엣지.
- PE 내부 토큰 전달이 트레이스에 명시적으로 드러난다 (HW 충실도와의 허용 가능한 trade-off).
Links
- ADR-0011 D-VA (PE_MMU 컴포넌트, VA 변환)
- ADR-0015 D4 (컴포넌트 포트/와이어 모델)
- ADR-0020 (greenlet 커널 실행 / two-pass)
- ADR-0023 (PE_IPCQ + PE_DMA 가상 채널)
- SPEC R3, R4