# ADR-0030: IPCQ Physical Addressing — PhysAddr integration ## Status Proposed (Blocked on ADR-0031 — PhysAddr PE-resource extension) ## Context ### 목표 IPCQ ring buffer의 주소 체계를 ADR-0023의 **synthetic parallel namespace** (`_IPCQ_BASE = 1<<60`)에서 **ADR-0001의 PhysAddr**로 이관한다. Routing / allocator / MemoryStore의 정합성을 회복하고, buffer_kind (tcm/hbm/sram)별 physical backing을 구조적 좌표로 표현한다. ### 현재 상태 (ADR-0023 D2.5) `src/kernbench/ccl/install.py:52-56`: ```python _IPCQ_BASE = 1 << 60 def _ipcq_base_for_pe(sip, cube, pe): return _IPCQ_BASE | (sip << 40) | (cube << 32) | (pe << 24) def rx_base(s, c, p, d): return _ipcq_base_for_pe(s, c, p) + direction_idx[d] * bytes_per_direction ``` - **bit 60** 사용 → ADR-0001의 51-bit PhysAddr 공간 밖 (`MAX_51 = (1 << 51) - 1`) - `PhysAddr.decode(addr)` → `PhysAddrError("addr must be a 51-bit value")` - `IpcqEndpoint.rx_base_pa: int` — 타입이 raw int, 구조 없음 - `buffer_kind` (tcm/hbm/sram)와 synthetic 주소의 관계가 coupling 없음 - Allocator (`PEMemAllocator`) 우회 — synthetic unique id per (sip, cube, pe, direction). 진짜 physical allocation이 아님 ADR-0023 D2.5 원문: > This bypasses the topology's address resolver / PhysAddr encoding and > treats IPCQ buffers as a separate, parallel address namespace. Real PA > encoding can be plugged in later without changing the rest of the design. "later"가 이 ADR. ### 왜 지금 다루는가 - ADR-0025 (direction addressing)은 주소-기반 매칭으로 전환. 주소가 correctness에 직접 기여 → 주소 체계가 설계 관점에서 더 중요해짐 - ADR-0001의 "Routing consumes decoded domains, not raw bit-fields" 계약 위반 지속 → 기술 부채 - Routing fabric (cube_noc / UCIe)은 PhysAddr.decode()로 destination을 정함. IPCQ의 synthetic 주소가 fabric routing에서 실제로 어떻게 처리되는지 **검증되지 않음** (별도 경로로 배달되는 것으로 추정) - TCM / HBM / SRAM의 실제 memory layout과 IPCQ ring buffer 위치가 **disjoint** → allocator가 IPCQ 영역을 모르므로 실수로 겹칠 가능성 (현재는 bit 60로 완전 분리되어 문제 없지만 설계 원칙상 건강하지 않음) ### 풀어야 할 문제 1. **IPCQ ring buffer의 PhysAddr 표현**: buffer_kind별로 어떤 PhysAddr factory를 쓸지. 2. **PhysAddr 공간 부족 가능성**: 51-bit 공간에 IPCQ 버퍼를 담을 여유가 있는지. 3. **Allocator 통합**: `PEMemAllocator`에 IPCQ buffer 영역 예약 기능 추가, 또는 기존 pool에서 정상 allocation. 4. **MemoryStore space naming 정리**: 현재는 `{"tcm", "hbm", "sram"}` 문자열로 space 구분. IPCQ buffer도 이 space에 속하면 일반 data와 주소 겹침 방지 필요. 5. **Routing fabric 통합**: PhysAddr 기반 routing이 IPCQ 토큰을 올바른 SIP의 올바른 메모리로 배달. 6. **ADR-0025와의 정합**: 주소-기반 매칭이 PhysAddr에서도 동일하게 작동. --- ## Decision ### D1. IPCQ ring buffer = PhysAddr factory 사용 각 `buffer_kind`가 해당하는 PhysAddr factory를 호출: | buffer_kind | PhysAddr factory | 필요한 인자 | |---|---|---| | `tcm` | `PhysAddr.pe_tcm_addr(rack_id, sip_id, cube_id, pe_id, tcm_offset)` | PE-local TCM | | `hbm` | `PhysAddr.pe_hbm_addr(rack_id, sip_id, cube_id, pe_id, pe_local_hbm_offset, slice_size_bytes)` | PE-local HBM slice | | `sram` | `PhysAddr.cube_sram_addr(rack_id, sip_id, cube_id, sram_offset)` | Cube-shared SRAM | Install plan builder (`build_install_plans` in ADR-0024)가 각 PE의 rx_base를 계산할 때: ```python # ADR-0030 후 install_plan.py (pseudocode) def _compute_rx_base(sip, cube, pe, direction_idx, buffer_kind, n_slots, slot_size, allocator_pool, rack_id=0) -> PhysAddr: bytes_per_direction = n_slots * slot_size offset = direction_idx * bytes_per_direction if buffer_kind == "tcm": # TCM base (per-PE) + direction offset tcm_base = allocator_pool.reserve_pe_tcm_for_ipcq(sip, cube, pe, total_bytes=N_DIR * bytes_per_direction) return PhysAddr.pe_tcm_addr(rack_id=rack_id, sip_id=sip, cube_id=cube, pe_id=pe, tcm_offset=tcm_base + offset) elif buffer_kind == "hbm": hbm_base = allocator_pool.reserve_pe_hbm_for_ipcq(sip, cube, pe, total_bytes=...) return PhysAddr.pe_hbm_addr(rack_id=rack_id, sip_id=sip, cube_id=cube, pe_id=pe, pe_local_hbm_offset=hbm_base + offset, slice_size_bytes=slice_size) elif buffer_kind == "sram": sram_base = allocator_pool.reserve_cube_sram_for_ipcq(sip, cube, total_bytes=...) return PhysAddr.cube_sram_addr(rack_id=rack_id, sip_id=sip, cube_id=cube, sram_offset=sram_base + offset) ``` `IpcqEndpoint.rx_base_pa`의 타입을 `PhysAddr` (또는 encoded `int`)로 변경: ```python @dataclass(frozen=True) class IpcqEndpoint: sip: int cube: int pe: int buffer_kind: str rx_base_pa: int # PhysAddr.encode() 결과 (51-bit) rx_base_va: int n_slots: int slot_size: int ``` 타입은 int 유지 (encoded form), 단 **반드시 PhysAddr.decode()로 복원 가능**한 값임을 invariant으로 둔다. 디코더 호출자는 `PhysAddr.decode(rx_base_pa)`로 구조적 좌표 획득. ### D2. Allocator 확장 — IPCQ 예약 API `PEMemAllocator`에 IPCQ 전용 예약 기능 추가: ```python class PEMemAllocator: def reserve_ipcq_tcm(self, total_bytes: int) -> int: """Reserve TCM region for IPCQ ring buffers at this PE. Returns tcm_offset (to be used in PhysAddr.pe_tcm_addr).""" # TCM에서 `total_bytes` 연속 영역 예약. # Tensor allocation과 겹치지 않도록. def reserve_ipcq_hbm(self, total_bytes: int) -> int: ... # cube-level allocator도 유사 ``` Install plan 빌더가 각 PE allocator에서 예약. 예약 결과(offset)를 PhysAddr factory에 전달. **기존 `_ipcq_base_for_pe` / `_IPCQ_BASE` 제거**. ### D3. MemoryStore space 통합 현재 `MemoryStore`는 `{space_name: {addr: ndarray}}` 구조. IPCQ buffer는 일반 tensor 데이터와 같은 space (tcm/hbm/sram)를 공유하게 됨. 주소 유일성은 ADR-0001의 PhysAddr 계층 보장. Backward compatibility: 기존 IPCQ address (synthetic)을 쓰는 code path는 **제거**하고, 모두 PhysAddr.encode() 결과만 사용. 이 자체는 API 변경이 아니라 값 변경. ### D4. Routing fabric 통합 IPCQ DMA write (`IpcqDmaToken`의 `src_addr → dst_addr`)이 PhysAddr encoding을 사용하므로 **routing fabric이 `PhysAddr.decode(dst_addr)`로 destination SIP/cube/PE를 정확히 찾을 수 있음**. Fabric routing 로직 변경 없음 (기존에도 PhysAddr.decode를 쓰는 것으로 추정). **검증 필요**: 현재 fabric이 bit 60 synthetic 주소를 어떻게 라우팅하는지 확인. 별도 경로가 있다면 제거, PhysAddr 경로로 통합. ### D5. ADR-0025와의 정합 ADR-0025의 주소-기반 매칭 (dst_addr로 direction 식별)은 PhysAddr.encode() 결과를 비교하는 것으로 자연스럽게 호환. 변경 없음. 다만 debug / diagnostic 향상 가능: ```python # pointer_dump 등에서 print(f"E: rx_base_pa={PhysAddr.decode(qp.peer.rx_base_pa)}") # 출력 예: PhysAddr(sip=1, cube=0, pe=0, kind="pe_resource", unit_type=PE, ...) ``` 이전 synthetic 주소는 decode 불가 → diagnostic 질 저하. PhysAddr 전환으로 개선. ### D6. ADR-0023 D2.5 amendment ADR-0023의 "bypasses PhysAddr encoding" 문구를 **Accepted fallback → now replaced by ADR-0030**으로 수정. 본 ADR이 적용되면 ADR-0023 D2.5의 "Real PA encoding can be plugged in later" 약속이 이행된 것. --- ## Migration strategy 단계적 전환 (한 PR로 하지 않는다): ### Phase 1: PhysAddr 공간 재검토 - 51-bit PhysAddr 공간에 IPCQ ring buffer가 실제로 들어갈 수 있는지 확인. - 각 buffer_kind (tcm/hbm/sram)별 factory가 제공하는 `local_offset` 범위가 IPCQ 요구 (4 direction × n_slots × slot_size)를 수용 가능한지. - 부족하면 PhysAddr layout 자체 확장 (ADR-0001 amendment 별도 필요). ### Phase 2: Allocator API 확장 - `PEMemAllocator.reserve_ipcq_*` 메소드 추가. - 기존 tensor allocation과 영역 충돌 방지. ### Phase 3: Install plan builder 전환 - `_ipcq_base_for_pe` 제거, PhysAddr factory 호출로 대체. - `IpcqEndpoint.rx_base_pa`가 PhysAddr.encode() 결과 (51-bit). ### Phase 4: Routing fabric 검증 - IPCQ DMA token이 fabric 정상 경로로 배달되는지 확인. - 별도 fast-path가 있다면 제거, 통합. ### Phase 5: MemoryStore space 검증 - IPCQ buffer 주소가 기존 tensor 주소와 겹치지 않는지. - Allocator 레벨에서 이미 예약했으므로 정상적으로 분리되어야 함. ### Phase 6: ADR-0023 D2.5 업데이트 + 기존 sideband path 제거 (완료) --- ## Dependencies - **ADR-0031** (PhysAddr PE-resource extension) — **Blocker**: PhysAddr가 PE resource (특히 IPCQ ring buffer)를 충분히 표현할 수 있도록 schema 확장이 선행되어야 함. 본 ADR은 ADR-0031 완료 후에만 실행 가능. - **ADR-0001** (PhysAddr layout): 본 ADR의 기반. 51-bit 공간 / factory API의 ADR-0031 확장본을 사용. - **ADR-0023** (IPCQ protocol): 본 ADR은 ADR-0023 D2.5의 "later" 약속 이행. D9 piggyback / credit return 프로토콜 자체는 불변. - **ADR-0024** (launcher + install_plan.py): `build_install_plans`가 PhysAddr factory를 호출하게 됨. - **ADR-0025** (direction addressing): 주소-기반 매칭이 PhysAddr에서도 동일하게 작동. 변경 없음. --- ## Non-goals - **ADR-0001 PhysAddr layout 자체 변경**: 51-bit 공간과 segment 구조는 유지. 부족 시 별도 ADR. - **IPCQ protocol semantic 변경**: ADR-0023 D9 piggyback 등 프로토콜 로직 유지. - **Allocator 전반 재설계**: IPCQ 예약 API 추가만. --- ## Open questions ### 🔴 Critical — Migration 전 반드시 검증 - **PhysAddr 51-bit 공간에 IPCQ 버퍼가 실제로 들어가는가**: 각 PE의 TCM 영역에서 `4 direction × n_slots (default 4) × slot_size (default 4KB)` = 64KB가 PE TCM 공간에 수용 가능. TCM size (e.g., 16MB) 대비 충분. HBM도 여유 많음. SRAM은 cube 공유라 direction × PE 곱이 있음 — 별도 검증 필요. - **Routing fabric의 현재 IPCQ 주소 처리**: 현재 synthetic 주소가 fabric에서 어떻게 routing되는지 trace 필요. `PhysAddr.decode()`로 판독 불가한 값이 fabric에서 정상 배달된다면 어떤 경로를 쓰는지 조사. ### 🟡 Nice-to-have - **IPCQ 전용 kind / sub_offset 인코딩**: `UnitType.PE`의 sub_offset 공간을 IPCQ와 공유. 충돌 방지를 위해 IPCQ 전용 sub-space 정의할지 여부. - **Debug tool**: `pointer_dump`를 PhysAddr 포매팅으로 개선. --- ## Test strategy ### T1. PhysAddr round-trip `tests/test_ipcq_physaddr.py` (new): - `PhysAddr.pe_tcm_addr(...)` → encode → decode → 동일 필드 복원 - TCM / HBM / SRAM 각 factory에 대해 ### T2. Allocator 예약 `tests/test_ipcq_alloc.py` (new): - `PEMemAllocator.reserve_ipcq_tcm` → 반환된 offset이 valid TCM 영역 - 중복 예약 → 에러 또는 non-overlapping offset - Tensor allocation과 충돌 없음 ### T3. Install plan PhysAddr integration `tests/test_ccl_install_plan.py` (확장): - `build_install_plans` 결과의 `rx_base_pa`가 PhysAddr.decode() 가능 - Decoded 좌표가 plan의 (sip, cube, pe)와 일치 - I3.1 invariant (ADR-0025 D6) — rx_base range disjointness가 PhysAddr에서도 성립 ### T4. Routing — IPCQ DMA fabric traversal `tests/test_ipcq_routing.py` (new): - Cross-SIP IPCQ send → fabric이 `PhysAddr.decode(dst_addr)`로 destination SIP 정확히 판단 → 올바른 MemoryStore에 write - UCIe 경로 / cube_noc 경로 모두 검증 ### T5. 회귀 - 기존 IPCQ E2E 테스트 (ring, mesh, tree) 모두 통과 - ADR-0024, ADR-0025 통합 테스트 통과 --- ## Consequences ### Positive - **ADR-0001 정합성 회복**: routing과 addressing이 단일 체계. - **buffer_kind 명확**: TCM/HBM/SRAM이 구조적 좌표로 구분. - **Debug 향상**: PhysAddr.decode()로 사람이 읽을 수 있는 좌표. - **Allocator 통합**: IPCQ 영역이 정상 예약 → tensor와의 충돌 리스크 사전 차단. - **Fabric routing 일원화**: 별도 경로 없이 기존 PhysAddr-based routing 재활용. ### Negative - **Migration 복잡도**: 6 Phase 단계적 전환 필요. 각 Phase마다 regression 리스크. - **PhysAddr 공간 검증 부담**: Phase 1에서 TCM/HBM/SRAM 공간이 IPCQ 요구를 수용하는지 실측 필요. - **Routing fabric 검증**: 현재 fabric이 synthetic 주소를 어떻게 처리하는지 조사 필요. ### Neutral - IPCQ protocol semantic (ADR-0023 D9 등) 불변. - ADR-0025의 direction addressing 로직 불변. --- ## Affected files | File | Change | |------|--------| | `src/kernbench/ccl/install.py` | `_IPCQ_BASE`, `_ipcq_base_for_pe` 제거 | | `src/kernbench/ccl/install_plan.py` (ADR-0024) | D1: PhysAddr factory 호출로 rx_base 계산 | | `src/kernbench/policy/address/allocator.py` (or similar) | D2: IPCQ 예약 API (`reserve_ipcq_tcm` 등) | | `src/kernbench/common/ipcq_types.py` | D1: `IpcqEndpoint.rx_base_pa` 문서화 — PhysAddr.encode 결과 | | `src/kernbench/sim_engine/memory_store.py` | D3: IPCQ buffer가 기존 space와 공유되는지 검증 | | `src/kernbench/sim_engine/engine.py` | D4: IPCQ token routing이 PhysAddr-based fabric 경로 사용 | | `src/kernbench/ccl/diagnostics.py` | D5: pointer_dump를 PhysAddr 포매팅으로 개선 | | `docs/adr/ADR-0023-ipcq-pe-collective.md` | D6: D2.5 amendment note | | `tests/test_ipcq_physaddr.py` (new) | T1 | | `tests/test_ipcq_alloc.py` (new) | T2 | | `tests/test_ccl_install_plan.py` | T3 확장 | | `tests/test_ipcq_routing.py` (new) | T4 |