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>
9.2 KiB
ADR-0036: IO_CPU 컴포넌트 모델
Status
Accepted
Context
IO_CPU는 시뮬레이션 그래프 내부의 IO 칩렛 호스트 대향 엔드포인트이다. PCIE_EP는 런타임 API로부터 호스트 메시지를 수신하여 io_noc를 통해 라우팅한다; 명령을 동반하는 요청(KernelLaunch, MmuMap/Unmap)의 경우 io_noc는 IO_CPU로 전달하며, IO_CPU는 다음을 수행한다:
- 요청을 큐브별 M_CPU로 팬아웃.
- 큐브별 응답을 단일 호스트 가시 완료로 집계.
- 커널 런치의 경우, 타깃이 된 모든 큐브의 모든 PE가 동일한 시뮬레이션
시각에 커널 본체 실행을 시작하도록 전역
target_start_ns배리어를 스탬프함(ADR-0009 D5).
Memory R/W 트래픽은 ADR-0015 D4 / ADR-0016 D3에 따라 IO_CPU를 우회한다; 따라서 본 컴포넌트는 정상 동작에서 명령 평면 트래픽만을 처리한다.
본 ADR은 위의 책임을 실현하는 IO_CPU 컴포넌트 구현을 문서화한다.
Decision
D1. 역할
IO_CPU는 IO 칩렛의 호스트 대향 엔드포인트이다. 두 가지 주요 책임을 갖는다:
- 멀티 큐브 팬아웃 — KernelLaunchMsg / MmuMapMsg / MmuUnmapMsg를 큐브별 M_CPU로 분배.
- 응답 집계 — 큐브별 ResponseMsg를 수집하고, 타깃이 된 모든 큐브가
응답한 후 부모
txn.done을 시그널.
세 번째이자 더 좁은 책임은 KernelLaunchMsg에만 적용된다:
target_start_ns 전역 배리어 스탬핑(D3).
본 컴포넌트는 다음을 하지 않는다:
- 라우팅 결정 — 경로는 라우터에 의해 사전 계산된다(ADR-0002).
- 텐서 또는 커널 내부 디코드 — 그러한 관심사는 M_CPU / PE_CPU / 엔진에 속한다.
- PE 수준 팬아웃 처리 — M_CPU가 큐브 내에서 팬아웃한다(ADR-0009 D3).
- Memory R/W 데이터 경로 처리 — ADR-0015 D4와 ADR-0016 D3에 따라
IO_CPU를 우회한다(
_resolve_cube_targets내의 Memory R/W 해석 코드는 방어적 폴백으로만 존재).
호출당(run()): 들어오는 Transaction마다 설정된 overhead_ns를 한 번
적용한다(D8).
D2. 정방향 경로 — 멀티 큐브 팬아웃
응답이 아닌 Transaction이 도착하면, 워커는:
run()을 통해overhead_ns를 지불._resolve_cube_targets를 호출하여 요청으로부터(sip, cube)타깃 리스트를 도출(D5).- 각 타깃에 대해:
ctx.resolver.find_m_cpu(sip, cube)를 통해 M_CPU 노드 id를 해석.ctx.router.find_node_path(io_cpu, m_cpu)를 통해 경로를 해석.path가 채워진 큐브별 서브 Transaction을 생성하여path[1](io_noc의 첫 홉)으로 전달.
- 집계 상태 등록:
_pending[request_id] = (expected, received=0, parent_done).
D3. KernelLaunch target_start_ns 전역 배리어 (ADR-0009 D5)
IO_CPU는 target_start_ns의 정규 스탬퍼이다. 요청이
KernelLaunchMsg일 때, IO_CPU는 타깃이 된 모든 큐브의 모든 PE를 포괄하는
단일 전역 배리어를 계산한다:
for (sip, cube) in cube_targets:
leg1 = compute_path_latency_ns(io_cpu → m_cpu(sip, cube), nbytes=0)
for pe_id in target_pe_ids:
leg2 = compute_path_latency_ns(m_cpu → pe_cpu(sip, cube, pe_id),
nbytes=0)
latency = leg1 + leg2 - io_overhead_ns - m_overhead_ns
global_max = max(global_max, latency)
target_start_ns = env.now + global_max
이후 요청은 (dataclasses.replace를 통해) 교체되어 스탬프된 값이 팬아웃
전반에 전파된다.
두 가지 오버헤드 보정:
io_overhead_ns는 차감되는데, IO_CPU가 본 메서드 실행 전에run()에서 이미 지불했기 때문이다.m_overhead_ns는 한 번 차감되는데, 경로 레이턴시에서 leg1의 종단점인 동시에 leg2의 시작점으로 두 번 등장하지만 M_CPU는 런타임에 단 한 번만 지불하기 때문이다.
모든 다운스트림 PE_CPU는 커널 본체 실행을 시작하기 전 target_start_ns
까지 yield한다; 이를 통해 개별 디스패치 경로가 얼마나 오래 걸렸는지와
무관하게 모든 PE가 동일한 시뮬레이션 시각에 시작한다.
D4. KernelLaunch 서브 Transaction은 nbytes=0을 운반
KernelLaunchMsg의 큐브별 서브 Transaction은 부모 txn.nbytes를 무시하고
nbytes=0을 강제한다:
- 커널 런치는 제어 메시지이다; 데이터 패브릭 수준에서 페이로드 크기는 무관하다.
nbytes > 0이면 모든 큐브별 서브 트랜잭션이 io_noc의 공유 first-hop 패브릭 BW를 점유한다. 16개 큐브에서는 이로 인해 팬아웃이 직렬화되어 먼 M_CPU들이target_start_ns를 지나치게 되고 D3 불변식이 깨진다.
KernelLaunch가 아닌 서브 Transaction은 txn.nbytes를 보존한다(실제
페이로드 크기를 운반하는 방어적 Memory R/W 폴백 경로에만 관련됨).
D5. 요청 타입별 큐브 타깃 해석
_resolve_cube_targets는 요청 타입에 따라 디스패치한다:
| 요청 타입 | (sip, cube)의 출처 |
target_cubes="all" 의미 |
|---|---|---|
MemoryWriteMsg |
dst_sip, dst_cube (또는 PhysAddr.decode(dst_pa).die_id 폴백) |
PA 디코드로 도출되는 단일 큐브 |
MemoryReadMsg |
src_sip, src_cube (또는 PhysAddr.decode(src_pa).die_id 폴백) |
PA 디코드로 도출되는 단일 큐브 |
KernelLaunchMsg |
shard.sip == my_sip으로 필터링된 텐서 샤드 |
이 SIP 위에서 샤드를 소유하는 모든 큐브 |
MmuMapMsg / MmuUnmapMsg |
본 SIP로 필터링된 target_cubes 리스트 |
스펙으로부터 range(cubes_per_sip) |
각 IO_CPU 인스턴스는 자기 SIP 내에서만 팬아웃한다 — _my_sip()이
노드 id에서 SIP id를 파싱한다(예: sip0.io0.io_cpu → 0).
Memory R/W 행은 방어적 완전성을 위해 존재한다; 엔진의 정상 경로는
Memory R/W를 _process_memory_direct() / find_memory_path()로
라우팅하여 IO_CPU를 완전히 우회한다(ADR-0015 D4 / ADR-0016 D3).
D6. 응답 집계
_pending: dict[request_id → (expected, received, parent_done)]:
- 디스패치 시:
(len(cube_targets), 0, txn.done)을 등록. _worker는is_response=True로 응답을 인식하여_collect_response로 라우팅한다._collect_response는received를 증가시키며,received >= expected가 되면parent_done.succeed()를 호출하고 엔트리를_pending에서 제거한다.
이는 단순한 요청별 카운터이다. 큐브별 정체성 추적이나 부분 실패 처리는 없다 — 누락된 응답은 부모 done을 무기한 스톨시킨다. 프로덕션 스타일의 실패 경로는 현재 시뮬레이터 모델의 범위 밖이다.
D7. target_pe 해석 헬퍼
_resolve_pe_ids(target_pe):
int→[target_pe].tuple[int, ...]→list(target_pe)."all"→range(n_slices), 여기서n_slices는 큐브memory_map.hbm_slices_per_cube(기본 8)에서 가져온다.
D3의 배리어 계산에서 큐브별로 모든 PE 타깃을 열거하는 데 사용된다.
D8. 설정 가능한 overhead_ns
단일 속성이 인스턴스별 레이턴시를 결정한다:
| 사이트 | impl 이름 | overhead_ns |
|---|---|---|
IO 칩렛 io_cpu |
builtin.io_cpu |
10.0 |
Transaction마다 run()에서 한 번 적용된다. IO_CPU에서의 명령 해석 및
디스패치 결정 시간을 모델링한다.
Consequences
Positive
- 크로스 큐브 및 크로스 SIP 커널 런치가 단일 전역 배리어를 공유한다 (D3 + D4) — 시작 시각의 큐브별 분기가 없다.
- nbytes=0 불변식이 팬아웃을 공유 first-hop 패브릭 BW로부터 떼어내, 대규모(16 큐브)에서도 배리어의 정확도를 보존한다.
- 단일 카운터를 통한 응답 집계 → 최소 상태, 결정론적 완료 순서.
- SIP별 스코핑(
_my_sip())이 서로 다른 SIP의 IO_CPU들을 깨끗이 독립시킨다.
Negative
- 부분 실패 의미가 없음 — 누락된 큐브별 응답은 부모를 무기한 스톨시킨다. 시뮬레이션 용도로는 충분하나 프로덕션 스타일의 엔드포인트로는 적합하지 않다.
_pending은 일반 dict이다; in-flight 요청이 상태로 누적된다. 현재 벤치마크 워크로드(미해결 런치가 적음)에는 허용 가능하나, 원리적으로는 무한하다._resolve_cube_targets의 Memory R/W 해석 분기는 정상 엔진 경로에서 데드 코드이다. 방어적으로 남겨두었으나 우회 경로가 변경되면 드리프트 위험을 초래한다.
Links
- ADR-0002 (라우팅 거리 — 경로 계산)
- ADR-0009 D1 (커널 런치는 IO_CPU에 대한 엔드포인트 요청)
- ADR-0009 D3 (M_CPU는 큐브 내에서 팬아웃; IO_CPU는 큐브 사이에서 팬아웃)
- ADR-0009 D5 (IO_CPU에서의 target_start_ns 정규 스탬핑)
- ADR-0011 D-VA3 (MmuMapMsg가 큐브 팬아웃을 위해 IO_CPU를 경유)
- ADR-0012 (호스트 ↔ IO_CPU 메시지 스키마)
- ADR-0015 D4 (Memory R/W는 IO_CPU 우회; 커널 런치는 IO_CPU 경유)
- ADR-0016 D1 (IO 칩렛 io_noc — IO_CPU가 여기 부착됨)
- ADR-0016 D3 (Memory R/W 경로가 IO_CPU 우회)
- ADR-0016 D4 (명령 해석을 위한 IO_CPU 경유 커널 런치 경로)