ADR: translate adr-ko/ to Korean, fix ADR-0013 slug, refine Status check

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>
This commit is contained in:
2026-05-20 08:17:56 -07:00
parent a796c1d2f7
commit 168b0c89f0
29 changed files with 2631 additions and 2651 deletions
+5 -2
View File
@@ -402,8 +402,11 @@ Mechanics:
- When editing a KO ADR, propagate to EN the same way. - When editing a KO ADR, propagate to EN the same way.
- Filename mirror: `docs/adr/X.md``docs/adr-ko/X.md` (no language - Filename mirror: `docs/adr/X.md``docs/adr-ko/X.md` (no language
suffix in either path). suffix in either path).
- The `## Status` block content must remain byte-identical between - The `## Status` *lifecycle keyword* (`Accepted`, `Proposed`,
the EN and KO files (e.g., both say `Accepted`). `Stub (Future Work)`, `Draft`, `Superseded by ADR-NNNN`,
`Merged into ADR-NNNN`) must match between EN and KO. Parenthetical
commentary and any list items that follow the keyword may be
translated naturally (the verify tool ignores them when comparing).
- Conflict policy: if the two diverge despite the rule, treat EN as - Conflict policy: if the two diverge despite the rule, treat EN as
authoritative and overwrite KO. Surface the divergence to the user authoritative and overwrite KO. Surface the divergence to the user
before reconciling. before reconciling.
+121 -125
View File
@@ -1,10 +1,10 @@
# ADR-0001: 51-bit Physical Address Layout & Decoding Contract # ADR-0001: 51비트 물리 주소 레이아웃 및 디코딩 계약
## Status ## Status
Accepted (Revision 2 — 2026-04-27: concrete bit layout, rack_id removal, Accepted (Revision 2 — 2026-04-27: 구체적인 비트 레이아웃, rack_id 제거,
Tray->SIP / SIP->DIE renaming, PE/MCPU/IOCPU sub-unit tables. Tray->SIP / SIP->DIE 명칭 변경, PE/MCPU/IOCPU 서브 유닛 표.
Supersedes ADR-0031.) ADR-0031을 대체함.)
## Date ## Date
@@ -12,40 +12,39 @@ Supersedes ADR-0031.)
## Context ## Context
KernBench requires a stable, parsable physical address scheme that: KernBench에는 다음과 같은 요건을 만족하는 안정적이고 파싱 가능한 물리 주소 체계가 필요하다.
- can be decoded into routing domains (SIP / die / HBM / PE-resource / IOCPU) - 라우팅 도메인(SIP / die / HBM / PE-resource / IOCPU)으로 디코딩 가능
- remains topology-agnostic (no hardcoded counts) - 토폴로지에 비의존적(개수를 하드코딩하지 않음)
- supports swappable policy and DI-first components - 교체 가능한 정책과 DI-first 컴포넌트를 지원
- covers multiple SIPs, AHBM dies, and IO chiplet dies in a unified space - 다수의 SIP, AHBM die, IO chiplet die를 통합된 공간에서 다룸
### History ### 연혁
- Original ADR-0001 defined a 51-bit layout with `rack_id(4) + sip_id(4) + - 최초 ADR-0001`rack_id(4) + sip_id(4) + sip_seg(5) + local_offset(38)`
sip_seg(5) + local_offset(38)`. `rack_id` was never used in practice. 로 구성된 51비트 레이아웃을 정의했다. `rack_id`는 실제로 사용된 적이 없다.
- ADR-0031 (stub) requested PE-resource range partition but was never - ADR-0031(스텁)은 PE-resource 범위 분할을 요청했으나 구현되지 않았다.
implemented.
Revision 2 removes `rack_id`, renames `sip_seg -> die_id`, and provides Revision 2에서는 `rack_id`를 제거하고 `sip_seg``die_id`로 개명하며,
concrete sub-unit tables for PE, MCPU, CUBE_SRAM, and IOCPU resources. PE, MCPU, CUBE_SRAM, IOCPU 리소스에 대한 구체적인 서브 유닛 표를 제공한다.
ADR-0031 is superseded. ADR-0031은 본 ADR로 대체된다.
## Decision ## Decision
We define a **PhysAddr value object** and an **address decoding contract** **PhysAddr 값 객체**와, 정수 주소를 라우팅 도메인으로 변환하는
that converts an integer address into routing domains. **주소 디코딩 계약**을 정의한다.
### D1. PhysAddr is an immutable value object ### D1. PhysAddr는 불변 값 객체이다
- PhysAddr is immutable and comparable as a pure value. - PhysAddr는 불변이며 순수한 값으로 비교 가능하다.
- Any allocator returns a **fully specified PhysAddr** (not partial metadata). - 모든 할당자는 **완전히 명세된 PhysAddr**(부분적인 메타데이터가 아님)를 반환한다.
- No global state may be required to interpret a PhysAddr. - PhysAddr를 해석하기 위해 전역 상태를 필요로 해서는 안 된다.
### D2. 51-bit Physical Address Layout ### D2. 51비트 물리 주소 레이아웃
A 51-bit physical address is adopted. 51비트 물리 주소를 채택한다.
#### 2.1 Top-Level Address Map #### 2.1 최상위 주소 맵
```text ```text
[50:47] sip_id (4) -- 16 SIPs [50:47] sip_id (4) -- 16 SIPs
@@ -60,17 +59,17 @@ A 51-bit physical address is adopted.
+---------+----------+-------------------------+ +---------+----------+-------------------------+
``` ```
#### 2.2 die_id Allocation #### 2.2 die_id 할당
| die_id | Meaning | | die_id | 의미 |
|--------|---------| |--------|---------|
| 0..15 | AHBM dies | | 0..15 | AHBM dies |
| 16..20 | IOCHIPLET dies | | 16..20 | IOCHIPLET dies |
| 21..31 | Reserved | | 21..31 | Reserved |
#### 2.3 AHBM Die Layout #### 2.3 AHBM Die 레이아웃
Only lower 256 GB of the 4 TB die-local window is assigned. 4 TB die-local 윈도우 중 하위 256 GB만 할당된다.
```text ```text
[41:38] MBZ (4) [41:38] MBZ (4)
@@ -78,35 +77,35 @@ Only lower 256 GB of the 4 TB die-local window is assigned.
[36: 0] sub-address (37) [36: 0] sub-address (37)
``` ```
| addr_space | Meaning | | addr_space | 의미 |
|------------|---------| |------------|---------|
| 0 | Local resource | | 0 | Local resource |
| 1 | HBM memory | | 1 | HBM memory |
##### 2.3.1 HBM Window (addr_space = 1) ##### 2.3.1 HBM 윈도우 (addr_space = 1)
```text ```text
[36:0] hbm_offset (37) -- 128 GB decode window [36:0] hbm_offset (37) -- 128 GB decode window
``` ```
The architectural decode window is fixed at 128 GB. Implemented capacity 아키텍처상의 디코드 윈도우는 128 GB로 고정된다. 실제 구현 용량은
may be smaller depending on SKU/topology (see D4). SKU/토폴로지에 따라 더 작을 수 있다(D4 참조).
##### 2.3.2 Resource Window (addr_space = 0) ##### 2.3.2 Resource 윈도우 (addr_space = 0)
```text ```text
[36:34] resource_kind (3) [36:34] resource_kind (3)
[33: 0] kind_local (34) -- 16 GB per kind [33: 0] kind_local (34) -- 16 GB per kind
``` ```
| resource_kind | Meaning | | resource_kind | 의미 |
|---------------|---------| |---------------|---------|
| 000 | PE_LOCAL | | 000 | PE_LOCAL |
| 001 | MCPU_LOCAL | | 001 | MCPU_LOCAL |
| 010 | CUBE_SRAM | | 010 | CUBE_SRAM |
| 011..111 | Reserved | | 011..111 | Reserved |
Each kind gets a 16 GB decode region. 각 kind는 16 GB 디코드 영역을 갖는다.
##### 2.3.3 PE_LOCAL (resource_kind = 000) ##### 2.3.3 PE_LOCAL (resource_kind = 000)
@@ -117,9 +116,9 @@ Each kind gets a 16 GB decode region.
[24: 0] sub_offset (25) -- 32 MB per slot [24: 0] sub_offset (25) -- 32 MB per slot
``` ```
16 PEs x 16 sub-unit slots x 32 MB = 8 GB active decode. 16 PE x 16 서브 유닛 슬롯 x 32 MB = 8 GB 활성 디코드.
| pe_sub_unit | Name | Budget | | pe_sub_unit | 이름 | 예산 |
|-------------|------|--------| |-------------|------|--------|
| 0 | PE_CPU_DTCM | 8 KB | | 0 | PE_CPU_DTCM | 8 KB |
| 1 | MATH_ENGINE_DTCM | 8 KB | | 1 | MATH_ENGINE_DTCM | 8 KB |
@@ -138,9 +137,9 @@ Each kind gets a 16 GB decode region.
[24: 0] sub_offset (25) -- 32 MB per slot [24: 0] sub_offset (25) -- 32 MB per slot
``` ```
1 GB active decode. 1 GB 활성 디코드.
| mcpu_sub_unit | Name | Budget | | mcpu_sub_unit | 이름 | 예산 |
|---------------|------|--------| |---------------|------|--------|
| 0 | MCPU_ITCM | 512 KB | | 0 | MCPU_ITCM | 512 KB |
| 1 | MCPU_DTCM | 512 KB | | 1 | MCPU_DTCM | 512 KB |
@@ -157,32 +156,32 @@ Each kind gets a 16 GB decode region.
[24: 0] sram_offset (25) -- flat 32 MB [24: 0] sram_offset (25) -- flat 32 MB
``` ```
#### 2.4 IOCHIPLET Die Layout #### 2.4 IOCHIPLET Die 레이아웃
Only lower 1 TB of the 4 TB die-local window is assigned. 4 TB die-local 윈도우 중 하위 1 TB만 할당된다.
```text ```text
[41:40] MBZ (2) [41:40] MBZ (2)
[39: 0] chiplet_offset (40) -- 1 TB [39: 0] chiplet_offset (40) -- 1 TB
``` ```
Region split by address range: 주소 범위별 영역 구분:
| Range | Meaning | Decode condition | | 범위 | 의미 | 디코드 조건 |
|-------|---------|------------------| |-------|---------|------------------|
| [0, 2 GB) | IOCPU resource | chiplet_offset < 0x8000_0000 | | [0, 2 GB) | IOCPU resource | chiplet_offset < 0x8000_0000 |
| [2 GB, 1 TB) | UAL | chiplet_offset >= 0x8000_0000 | | [2 GB, 1 TB) | UAL | chiplet_offset >= 0x8000_0000 |
##### 2.4.1 IOCPU Region ##### 2.4.1 IOCPU 영역
```text ```text
[30:27] iocpu_sub_unit (4) [30:27] iocpu_sub_unit (4)
[26: 0] sub_offset (27) -- 128 MB per slot [26: 0] sub_offset (27) -- 128 MB per slot
``` ```
16 x 128 MB slots. 2 GB active decode. 16 x 128 MB 슬롯. 2 GB 활성 디코드.
| iocpu_sub_unit | Name | Budget | | iocpu_sub_unit | 이름 | 예산 |
|----------------|------|--------| |----------------|------|--------|
| 0 | IOCPU_ITCM | 512 KB | | 0 | IOCPU_ITCM | 512 KB |
| 1 | IOCPU_DTCM | 512 KB | | 1 | IOCPU_DTCM | 512 KB |
@@ -192,110 +191,107 @@ Region split by address range:
| 5 | IO_SRAM | 64 MB | | 5 | IO_SRAM | 64 MB |
| 6..15 | Reserved | -- | | 6..15 | Reserved | -- |
##### 2.4.2 UAL Region ##### 2.4.2 UAL 영역
Sub-layout TBD (separate ADR). 서브 레이아웃은 별도 ADR에서 정의한다(TBD).
#### 2.5 Addressing Rules #### 2.5 주소 지정 규칙
1. MBZ bits must be zero. An address with non-zero MBZ bits is 1. MBZ 비트는 반드시 0이어야 한다. MBZ 비트가 0이 아닌 주소는
**architecturally invalid**. Implementation may raise a decode fault **아키텍처적으로 유효하지 않다**. 구현체는 디코드 폴트를 발생시키거나
or return an error -- behavior is not prescribed by this ADR. 오류를 반환할 수 있다 — 본 ADR은 동작을 규정하지 않는다.
2. Fixed slot sizes are chosen for simple hardware decode; actual 2. 단순한 하드웨어 디코드를 위해 고정된 슬롯 크기를 채택한다. 실제 구현
implemented capacity may be smaller than the slot. 용량은 슬롯보다 작을 수 있다.
3. Access beyond a sub-unit's implemented budget within a slot is 3. 슬롯 내에서 서브 유닛의 구현 예산을 초과하는 접근은 **아키텍처적으로
**architecturally invalid** (same policy as MBZ). 유효하지 않다**(MBZ와 동일한 정책).
### D3. Bitfield decoding is deterministic ### D3. 비트필드 디코딩은 결정론적이다
Given an integer address, field extraction (`sip_id`, `die_id`, `kind`, 정수 주소가 주어지면 필드 추출(`sip_id`, `die_id`, `kind`, `sub_unit`,
`sub_unit`, `offset`) is purely positional. No runtime state is required. `offset`)은 순수하게 위치 기반이다. 런타임 상태가 필요하지 않다.
Decoding deterministically maps an integer address to destination domains: 디코딩은 정수 주소를 결정론적으로 목적지 도메인(`sip_id`, `die_id`,
`sip_id`, `die_id`, target kind (HBM / PE_LOCAL / MCPU_LOCAL / CUBE_SRAM / 타깃 종류 HBM / PE_LOCAL / MCPU_LOCAL / CUBE_SRAM / IOCPU / UAL)으로 매핑한다.
IOCPU / UAL).
### D4. Capacity validation may depend on topology config ### D4. 용량 검증은 토폴로지 설정에 의존할 수 있다
Whether a decoded address falls within **implemented capacity** (e.g., 디코딩된 주소가 **구현된 용량** 안에 들어가는지(예: 특정 SKU의 HBM 96 GB)는
HBM 96 GB on a specific SKU) is checked against topology parameters DI/설정을 통해 제공된 토폴로지 파라미터로 검증한다. 디코딩 자체(D3)는
provided via DI/config. Decode itself (D3) never consults topology -- 토폴로지를 참조하지 않으며 — 검증 단계에서만 참조한다. 이러한 파라미터는
only validation does. These parameters must live in the topology/config 컴포넌트 구현이 아니라 토폴로지/설정 레이어에 존재해야 한다.
layer, not in node implementations.
### D5. Routing consumes decoded domains, not raw bits ### D5. 라우팅은 원시 비트가 아닌 디코딩된 도메인을 소비한다
Routing policy uses decoded domains: 라우팅 정책은 디코딩된 도메인을 사용한다.
- `src` location (sip / die / pe or node_id) - `src` 위치 (sip / die / pe 또는 node_id)
- `dst` domains derived from PhysAddr decoding - PhysAddr 디코딩에서 도출된 `dst` 도메인
- `size_bytes` for size-aware link latency - 크기 인지 링크 레이턴시를 위한 `size_bytes`
Routing must not inspect raw bit-fields directly except inside the 라우팅은 디코딩 모듈 내부를 제외하고는 원시 비트필드를 직접 들여다보아서는
decoding module. 안 된다.
## Alternatives Considered ## 고려된 대안
1. **Keep `rack_id` (4 bits)**: Rejected -- never used in practice, 1. **`rack_id`(4비트) 유지**: 기각 — 실제로 사용된 적이 없으며, 4비트를
consumes 4 bits that enable die-local expansion to 42 bits 소비함으로써 die-local 확장을 42비트(IOCHIPLET 1 TB)까지 가능하게 하는
(IOCHIPLET 1 TB). 기회를 막는다.
2. **Uniform 256 GB per die**: Rejected -- IOCHIPLET UAL requires ~1 TB. 2. **die당 256 GB로 균일화**: 기각 — IOCHIPLET UAL은 약 1 TB가 필요하다.
Freed rack_id bits enable 42-bit local_offset. 해제된 rack_id 비트를 활용하여 42비트 local_offset을 가능하게 한다.
3. **Variable-width die windows (AHBM 256 GB, CHIPLET 1 TB via multi-seg 3. **가변 폭 die 윈도우(AHBM 256 GB, CHIPLET 1 TB를 다중 seg 스패닝으로 구현)**:
spanning)**: Rejected -- complicates D3 (deterministic decoding). 기각 — D3(결정론적 디코딩)를 복잡하게 만든다. MBZ 패딩을 갖는 균일한
Uniform 4 TB window with MBZ padding is simpler. 4 TB 윈도우가 더 단순하다.
4. **Use raw integers everywhere, decode ad-hoc in routing**: Rejected -- 4. **모든 곳에서 원시 정수를 사용하고, 라우팅에서 임시로 디코딩**: 기각 —
leads to duplicated logic, inconsistent routing, and hidden 로직이 중복되고 라우팅이 일관성을 잃으며 가정이 숨겨진다.
assumptions.
5. **Hardcode topology sizes (SIP/CUBE/PE counts) into decoding**: 5. **토폴로지 크기(SIP/CUBE/PE 개수)를 디코딩에 하드코딩**: 기각 —
Rejected -- violates SPEC R3 and breaks swappability. SPEC R3를 위반하고 교체 가능성을 깬다.
6. **Put decoding inside memory controllers or routers**: Rejected -- 6. **디코딩을 메모리 컨트롤러나 라우터 내부에 둠**: 기각 — 정책이 컴포넌트로
leaks policy into components, violates SPEC R4 / D5. 누출되며 SPEC R4 / D5를 위반한다.
## Consequences ## 결과
### Positive ### 긍정적
- Simple hierarchical decoder: SIP -> die -> kind -> sub-unit. - 단순한 계층적 디코더: SIP -> die -> kind -> 서브 유닛.
- Clean separation of memory (HBM) vs local resource (PE/MCPU/SRAM/IOCPU). - 메모리(HBM)와 로컬 리소스(PE/MCPU/SRAM/IOCPU)의 깔끔한 분리.
- Deterministic routing domains enable clear test invariants (SPEC R1, R5). - 결정론적 라우팅 도메인은 명확한 테스트 불변식을 가능하게 한다(SPEC R1, R5).
- Expandable: 11 reserved die_id slots, reserved resource_kind / sub-unit - 확장 가능: 11개의 예약된 die_id 슬롯, 예약된 resource_kind / 서브 유닛
slots, reserved MBZ bits. 슬롯, 예약된 MBZ 비트.
- DI-first: decoder can be swapped without changing components (SPEC R4). - DI-first: 컴포넌트를 변경하지 않고도 디코더를 교체할 수 있다(SPEC R4).
### Tradeoffs ### 트레이드오프
- Sparse address holes due to power-of-2 slot alignment. - power-of-2 슬롯 정렬로 인한 희소한 주소 공백.
- Large reserved/MBZ regions (intentional for future extension). - 큰 예약/MBZ 영역(향후 확장을 위해 의도된 것).
- Requires explicit configuration for topology-derived sizes (D4). - 토폴로지에서 유도된 크기에 대해 명시적인 설정이 필요하다(D4).
- Introduces a single "blessed" decoding module that must remain stable - 안정적이고 잘 테스트된 상태로 유지되어야 하는 단일 "정통" 디코딩 모듈이
and well-tested. 도입된다.
## Supersedes ## 대체 대상
- **ADR-0031 (PhysAddr PE-Resource Extension)**: stub status. The - **ADR-0031 (PhysAddr PE-Resource Extension)**: 스텁 상태였음. D2.3.3-D2.3.5의
PE_LOCAL / MCPU_LOCAL / CUBE_SRAM sub-unit tables in D2.3.3-D2.3.5 PE_LOCAL / MCPU_LOCAL / CUBE_SRAM 서브 유닛 표가 ADR-0031에서 제시한
fulfill ADR-0031's stated goals. 목표를 충족한다.
## Implementation Notes (Non-normative) ## 구현 메모 (비규범적)
- Recommended module: `src/kernbench/policy/address/phyaddr.py` - 권장 모듈: `src/kernbench/policy/address/phyaddr.py`
- Tests should cover: encode/decode round-trip per kind, MBZ enforcement, - 테스트는 다음을 커버해야 한다: kind별 인코딩/디코딩 라운드트립, MBZ 강제,
die_id dispatch (AHBM / IOCHIPLET / reserved), sub-unit boundary die_id 디스패치(AHBM / IOCHIPLET / 예약), 서브 유닛 경계값, 팩토리 API의
values, backward compatibility of factory APIs. 후방 호환성.
- Factory methods: `hbm_addr`, `pe_hbm_addr`, `pe_tcm_addr`, - 팩토리 메서드: `hbm_addr`, `pe_hbm_addr`, `pe_tcm_addr`, `cube_sram_addr`
`cube_sram_addr` retain signatures (minus `rack_id`); `cube_id` 시그니처를 유지한다(`rack_id` 제외). `cube_id` 파라미터는 `die_id`
parameter renamed to `die_id`. 개명된다.
- New factories: `pe_resource_addr`, `mcpu_resource_addr`, - 신규 팩토리: `pe_resource_addr`, `mcpu_resource_addr`, `iocpu_resource_addr`,
`iocpu_resource_addr`, `ual_addr`. `ual_addr`.
## Appendix A. Address Examples ## 부록 A. 주소 예시
### A.1 AHBM HBM access ### A.1 AHBM HBM 접근
sip=2, die=5, HBM offset=0x1000 sip=2, die=5, HBM offset=0x1000
@@ -347,7 +343,7 @@ chiplet_offset = (2 << 27) | 0x20000
(< 0x8000_0000 -> IOCPU region) (< 0x8000_0000 -> IOCPU region)
``` ```
### A.5 IOCHIPLET -- UAL region, offset=4 GB ### A.5 IOCHIPLET -- UAL 영역, offset=4 GB
```text ```text
sip_id = 0 -> [50:47] = 0 sip_id = 0 -> [50:47] = 0
@@ -355,7 +351,7 @@ die_id = 16 -> [46:42] = 10000 (IOCHIPLET[0])
chiplet_offset = 0x1_0000_0000 (4 GB >= 2 GB -> UAL region) chiplet_offset = 0x1_0000_0000 (4 GB >= 2 GB -> UAL region)
``` ```
## Links ## 링크
- SPEC.md: R1 (routing), R3 (configurable topology), R4 (DI-first), - SPEC.md: R1 (routing), R3 (configurable topology), R4 (DI-first),
R5 (multi-domain comm) R5 (multi-domain comm)
+68 -70
View File
@@ -1,4 +1,4 @@
# ADR-0002: Routing Distance, Ordering & Bypass Rules # ADR-0002: 라우팅 거리, 순서 및 우회 규칙
## Status ## Status
Accepted Accepted
@@ -7,96 +7,94 @@ Accepted
2026-02-27 2026-02-27
## Context ## Context
The KernBench Graph Latency Simulator must compare kernel execution time KernBench Graph Latency Simulator는 서로 다른 아키텍처·토폴로지에 대한
across different architectures and topologies by computing end-to-end 커널 실행 시간을 비교해야 하며, 그래프 순회로부터 end-to-end 레이턴시를
latency from graph traversal. 계산하여 이를 달성한다.
To support meaningful comparison: 의미 있는 비교를 지원하려면:
- routing must be deterministic - 라우팅이 결정론적이어야 한다
- latency must reflect actual interconnect structure - 레이턴시가 실제 인터커넥트 구조를 반영해야 한다
- local vs remote traffic must be distinguishable - 로컬과 리모트 트래픽이 구분 가능해야 한다
- bypass” optimizations must not undermine debuggability or correctness - "우회(bypass)" 최적화가 디버깅 가능성이나 정확성을 훼손해서는 안 된다
The simulator also aims to avoid software-managed metadata and hidden 또한 시뮬레이터는 소프트웨어가 관리하는 메타데이터 및 제어 경로를
shortcuts that obscure control paths. 가리는 숨겨진 지름길을 피하는 것을 목표로 한다.
## Decision ## Decision
### D1. Distance is accumulated latency, not hop count ### D1. 거리(distance)는 hop 수가 아니라 누적 레이턴시이다
- Routing “distance” is defined as the **sum of per-node and per-link latency**. - 라우팅 "거리"는 **노드별·링크별 레이턴시의 합**으로 정의된다.
- Hop count alone must not be used for ordering or path selection. - 순서 결정이나 경로 선택에 hop 수만을 사용해서는 안 된다.
- Size-aware serialization latency (bytes / BW) contributes to distance. - 크기 인지(size-aware) 직렬화 레이턴시(bytes / BW)가 거리에 기여한다.
### D2. Routing order is derived from graph traversal ### D2. 라우팅 순서는 그래프 순회에서 유도된다
- The chosen route is the path with minimum accumulated latency - 선택된 경로는 구성된 그래프와 라우팅 정책 하에서
given the constructed graph and routing policy. 누적 레이턴시가 최소인 경로이다.
- Deterministic ordering must be guaranteed for identical inputs - 동일 입력(토폴로지 + 정책 + 요청)에 대해 결정론적 순서가 보장되어야 한다.
(topology + policy + request).
### D3. Bypass is explicit and graph-represented ### D3. 우회는 명시적이며 그래프로 표현된다
- All paths must be explicitly represented in the graph and subject to latency accumulation. - 모든 경로는 그래프에 명시적으로 표현되며 레이턴시 누적의 대상이 되어야 한다.
- Example: PE_DMA connects to the NOC router mesh (ADR-0017 D7). All destinations - : PE_DMA는 NOC 라우터 메시(ADR-0017 D7)에 연결된다. 모든 목적지
(HBM, shared SRAM, inter-cube UCIe) are reached via explicit mesh hops. (HBM, 공유 SRAM, 큐브 간 UCIe)는 명시적 메시 hop을 통해 도달한다.
Local HBM access has minimal hops (switching overhead only); remote access 로컬 HBM 접근은 hop 수가 최소(스위칭 오버헤드만)이며, 리모트 접근은
traverses additional routers. 추가 라우터를 거친다.
- Implicit or “magic” bypass paths are disallowed. - 암묵적이거나 "마법 같은" 우회 경로는 금지된다.
### D4. No zero-latency end-to-end paths ### D4. end-to-end 레이턴시가 0인 경로는 없다
- Every routed request must incur **end-to-end** latency > 0. - 모든 라우팅 요청은 **end-to-end** 레이턴시가 > 0이어야 한다.
- Individual fabric segments (e.g., NOC hops) MAY have distance_mm = 0 - 개별 패브릭 세그먼트(예: NOC hop)는 패브릭이 분산되어 있고 해당 granularity에서
when the fabric is distributed and distance is not meaningful at that granularity. 거리가 의미가 없을 때 distance_mm = 0을 가질 수 있다.
This is allowed because other components on the same path (e.g., PE_DMA, SRAM, 이는 같은 경로상의 다른 컴포넌트(예: PE_DMA, SRAM, UCIe 엔드포인트)가
UCIe endpoints) contribute non-zero latency, ensuring the end-to-end invariant holds. 0이 아닌 레이턴시에 기여하여 end-to-end 불변성을 유지하므로 허용된다.
- Fully zero-latency end-to-end paths are disallowed, except for explicit - end-to-end가 완전히 0 레이턴시인 경로는 금지된다. 단, 명시적으로
test-only stubs clearly marked as such. 표시된 테스트 전용 stub만 예외이다.
### D5. Policy vs topology responsibility split ### D5. 정책과 토폴로지의 책임 분리
- Topology builder: - 토폴로지 빌더:
- defines nodes and links and their latency/BW parameters - 노드와 링크 및 그들의 레이턴시/BW 파라미터를 정의한다
- Routing policy: - 라우팅 정책:
- selects among available graph paths based on decoded domains - 디코딩된 도메인을 바탕으로 사용 가능한 그래프 경로 중에서 선택한다
- Routing policy must not assume missing links; missing connectivity - 라우팅 정책은 누락된 링크를 가정해서는 안 된다. 누락된 연결성은
is a topology construction error. 토폴로지 구성 오류이다.
### D6. No software-managed routing metadata ### D6. 소프트웨어 관리 라우팅 메타데이터 금지
- Routing decisions must not rely on per-request software-managed metadata - 라우팅 결정은 그래프 모델 외부에서 거리·hop 수·순서를 추적하는
that tracks distance, hop count, or ordering outside the graph model. 요청별 소프트웨어 관리 메타데이터에 의존해서는 안 된다.
- All distance/order computation is derived from traversal itself. - 모든 거리·순서 계산은 순회 자체에서 유도된다.
## Alternatives Considered ## Alternatives Considered
1) **Hop-count based routing** 1) **Hop 수 기반 라우팅**
- Rejected: ignores heterogeneous latency/BW and misrepresents - 기각: 이질적인 레이턴시·BW를 무시하고 아키텍처 차이를 잘못 표현한다.
architectural differences.
2) **Implicit local shortcuts** 2) **암묵적 로컬 지름길**
- Rejected: breaks debuggability and violates traversal-based latency. - 기각: 디버깅 가능성을 해치고 순회 기반 레이턴시 원칙을 위반한다.
3) **Software-managed distance metadata** 3) **소프트웨어 관리 거리 메타데이터**
- Rejected: increases control overhead and obscures routing semantics. - 기각: 제어 오버헤드를 증가시키고 라우팅 시맨틱을 모호하게 만든다.
## Consequences ## Consequences
### Positive ### 긍정적
- Clear, debuggable hop-by-hop traces (SPEC R2, R4). - 명확하고 디버깅 가능한 hop-by-hop 트레이스 (SPEC R2, R4).
- Architecture comparisons reflect real interconnect structure. - 아키텍처 비교가 실제 인터커넥트 구조를 반영한다.
- Routing behavior is reproducible and deterministic. - 라우팅 동작이 재현 가능하고 결정론적이다.
### Tradeoffs / Costs ### 트레이드오프 / 비용
- Graph construction must be correct and complete. - 그래프 구성이 정확하고 완전해야 한다.
- Bypass modeling requires explicit graph representation, - 우회 모델링이 명시적 그래프 표현을 요구하므로 토폴로지 기술이
which slightly increases topology description complexity. 약간 더 복잡해진다.
## Implementation Notes (Non-normative) ## Implementation Notes (Non-normative)
- Recommended responsibilities: - 권장 책임 분담:
- Graph builder: ensure all required paths exist. - 그래프 빌더: 필요한 모든 경로가 존재함을 보장.
- Router: select next hop based on decoded domains and policy. - 라우터: 디코딩된 도메인과 정책을 바탕으로 다음 hop 선택.
- Tests should assert: - 테스트가 검증해야 할 항목:
- non-zero end-to-end latency - end-to-end 레이턴시 > 0
- deterministic routing for identical inputs - 동일 입력에 대한 결정론적 라우팅
- bypass paths appear explicitly in emitted traces - 우회 경로가 출력 트레이스에 명시적으로 나타남
## Links ## Links
- SPEC.md: R1 (routing), R2 (latency), R3 (topology), R5 (multi-domain comm) - SPEC.md: R1 (라우팅), R2 (레이턴시), R3 (토폴로지), R5 (다중 도메인 통신)
- ADR-0001: PhysAddr layout & decoding contract - ADR-0001: PhysAddr 레이아웃 및 디코딩 계약
@@ -1,4 +1,4 @@
# ADR-0003: Target System Hierarchy & Modeling Scope # ADR-0003: 타겟 시스템 계층 및 모델링 범위
## Status ## Status
@@ -6,63 +6,63 @@ Accepted
## Context ## Context
We need a system-level simulator to evaluate LLM kernel performance on our AI Accelerator platform. 자사 AI Accelerator 플랫폼에서 LLM 커널 성능을 평가하기 위해 시스템 수준의 시뮬레이터가 필요하다.
The platform is organized as a compute tray containing multiple identical SIPs connected via PCIe or UAL 해당 플랫폼은 PCIe 또는 UAL을 통해 스위칭 패브릭으로 연결된 다수의 동일한 SIP를 포함하는 컴퓨트 트레이로 구성되며,
through switching fabrics, with a host CPU issuing commands/kernels. 호스트 CPU가 명령/커널을 발급한다.
## Decision ## Decision
We model the system hierarchy explicitly: 시스템 계층을 다음과 같이 명시적으로 모델링한다.
### D1. Tray-level ### D1. Tray-level
- A compute tray contains: - 하나의 컴퓨트 트레이는 다음을 포함한다:
- Host CPU (issues requests / coordinates runtime & data placement) - 호스트 CPU (요청 발급 / 런타임 및 데이터 배치 조정)
- Multiple identical SIPs (accelerators) - 다수의 동일한 SIP (가속기)
- Interconnect fabric between SIPs (PCIe and/or UAL via switches) - SIP 간 인터커넥트 패브릭 (스위치를 통한 PCIe 및/또는 UAL)
### D2. SIP-level ### D2. SIP-level
- A SIP is a multi-die package composed of: - SIP는 다음으로 구성된 멀티 다이 패키지이다:
- Multiple CUBEs (HBM die + compute PEs + UCIe) - 다수의 CUBE (HBM 다이 + 컴퓨트 PE + UCIe)
- One or more IO chiplets (host/SIP interfaces) - 하나 이상의 IO 칩렛 (호스트/SIP 인터페이스)
- IO chiplets: - IO 칩렛:
- provide interfaces: PCIe-EP, IO_CPU, optionally UAL-EP - 다음 인터페이스를 제공한다: PCIe-EP, IO_CPU, 선택적으로 UAL-EP
- can be multiple per SIP - SIP 당 다수가 존재할 수 있다
- placement constrained to SIP shoreline (top/bottom/left/right); each shoreline may host 12 IO chiplets - 배치는 SIP shoreline(상/하/좌/우)으로 제약되며, 각 shoreline에는 1~2개의 IO 칩렛이 위치할 수 있다
### D3. CUBE-level ### D3. CUBE-level
- A CUBE contains: - 하나의 CUBE는 다음을 포함한다:
- HBM + memory controller (HBM_CTRL) - HBM + 메모리 컨트롤러 (HBM_CTRL)
- NOC (on-die fabric): carries all intra-cube traffic including HBM data, - NoC (on-die 패브릭): HBM 데이터, 큐브 간(UCIe) 트래픽, 명령(M_CPU↔PE_CPU),
inter-cube (UCIe), command (M_CPU↔PE_CPU), and shared SRAM access. 공유 SRAM 액세스를 포함한 모든 큐브 내부 트래픽을 운반한다.
Must provide: full-BW PE↔local HBM path, PE↔SRAM connectivity, 반드시 제공해야 하는 것: 풀-대역폭 PE↔로컬 HBM 경로, PE↔SRAM 연결성,
PE↔UCIe connectivity, M_CPU↔PE command path. PE↔UCIe 연결성, M_CPU↔PE 명령 경로.
NOC topology is an implementation choice (e.g., 2D mesh, ring, crossbar); NoC 토폴로지는 구현 선택사항(예: 2D 메시, 링, 크로스바)이며,
current implementation uses a 2D mesh with XY routing (see ADR-0017). 현재 구현은 XY 라우팅 방식의 2D 메시를 사용한다(ADR-0017 참조).
HBM_CTRL is attached to each PE's local NOC port (local HBM = minimal hop). HBM_CTRL은 각 PE의 로컬 NoC 포트에 부착된다(로컬 HBM = 최소 홉).
- Shared SRAM: cube-level shared memory accessible by all PEs via NOC - 공유 SRAM: 모든 PE가 NoC를 통해 액세스 가능한 큐브 수준 공유 메모리
- management/control CPU (M_CPU) coordinating PE command distribution and completion aggregation - PE 명령 분배 및 완료 집계를 조정하는 관리/제어 CPU (M_CPU)
- multiple PEs - 다수의 PE
- up to 4 UCIe endpoints (N/E/W/S) for CUBE↔CUBE and CUBE↔IO connectivity - CUBE↔CUBE CUBE↔IO 연결성을 위한 최대 4개의 UCIe 엔드포인트 (N/E/W/S)
### D4. PE-level ### D4. PE-level
- A PE can execute one kernel instance - 하나의 PE는 하나의 커널 인스턴스를 실행할 수 있다
- PE contains internal control + accelerators (modeled at PE view granularity): - PE는 내부 제어 + 가속기를 포함한다 (PE 뷰 단위로 모델링):
- PE_CPU, command handler, PE_TCM, DMA/GEMM/MATH engines, internal queues - PE_CPU, 명령 핸들러, PE_TCM, DMA/GEMM/MATH 엔진, 내부 큐
## Consequences ## Consequences
- The simulator supports abstraction by “views”: - 시뮬레이터는 "뷰" 단위의 추상화를 지원한다:
- SIP view hides PE internals - SIP 뷰는 PE 내부를 숨긴다
- CUBE view treats each PE as a single block - CUBE 뷰는 각 PE를 단일 블록으로 다룬다
- PE view expands PE internals - PE 뷰는 PE 내부를 전개한다
- Topology remains parameterized; sizes/counts/links come from configuration. - 토폴로지는 매개변수화된 상태로 유지되며, 크기/개수/링크는 설정으로부터 주어진다.
## Links ## Links
- SPEC R3/R5 - SPEC R3/R5
- ADR-0005 (diagram views) - ADR-0005 (다이어그램 뷰)
- ADR-0017 (cube NOC 2D mesh architecture) - ADR-0017 (큐브 NoC 2D 메시 아키텍처)
@@ -1,4 +1,4 @@
# ADR-0004: Memory Semantics & Local-HBM Bandwidth Guarantee # ADR-0004: 메모리 시맨틱 및 로컬 HBM 대역폭 보장
## Status ## Status
@@ -6,71 +6,73 @@ Accepted
## Context ## Context
Accurately modeling PE↔HBM behavior is essential for kernel latency estimation. PE↔HBM 동작을 정확하게 모델링하는 것은 커널 레이턴시 추정에 필수적이다.
Each PE has a notion of “local HBM” that must guarantee full HBM bandwidth, independent of intervening on-die fabric bandwidth. 각 PE는 "로컬 HBM"이라는 개념을 가지며, 이는 중간 온칩 패브릭 대역폭과
무관하게 HBM 전체 대역폭을 보장해야 한다.
## Decision ## Decision
### D1. Local HBM definition ### D1. 로컬 HBM의 정의
- Each PE is assigned a logically defined “local HBM” region. - 각 PE에는 논리적으로 정의된 "로컬 HBM" 영역이 할당된다.
- Local HBM corresponds to the pseudo-channel subset directly attached to that PEs - 로컬 HBM은 NOC 메시(ADR-0017 D4) 내에서 해당 PE의 라우터에 직접 연결된
router in the NOC mesh (ADR-0017 D4). pseudo-channel 부분집합에 대응한다.
- The path is: PE_DMA → local router → HBM_CTRL (switching overhead only, 0 mesh hops). - 경로는: PE_DMA → 로컬 라우터 → HBM_CTRL (스위칭 오버헤드만, 메시 hop 0개).
- The mapping (HBM pseudo-channels → PE local regions) is derived from topology configuration. - 매핑(HBM pseudo-channel → PE 로컬 영역)은 토폴로지 구성에서 유도된다.
### D2. Local HBM bandwidth guarantee contract ### D2. 로컬 HBM 대역폭 보장 계약
- Accesses from a PE to its local HBM MUST guarantee full effective HBM - PE에서 자신의 로컬 HBM으로의 접근은 중간 패브릭 대역폭 제한과
read/write bandwidth independent of intervening fabric bandwidth limits. 무관하게 HBM의 유효 read/write 대역폭 전부를 보장해야 한다.
- Effective HBM bandwidth = spec bandwidth x efficiency factor. - 유효 HBM 대역폭 = 스펙 대역폭 × 효율 계수.
The efficiency factor (configured via `hbm_ctrl.attrs.efficiency`, default 0.8) 효율 계수(`hbm_ctrl.attrs.efficiency`로 설정, 기본값 0.8)는 실세계 DRAM의
models real-world DRAM inefficiencies (refresh cycles, bank conflicts, page 비효율(리프레시 사이클, 뱅크 충돌, 페이지 미스 등)을 모델링한다.
misses). For example: 256 GB/s spec x 0.8 = 204.8 GB/s effective. : 256 GB/s 스펙 × 0.8 = 204.8 GB/s 유효 대역폭.
- The topology builder applies the efficiency factor to router-to-hbm edge - 토폴로지 빌더는 그래프 구성 시점에 router-to-hbm 에지의 대역폭에
bandwidth at graph construction time, so all downstream routing and latency 효율 계수를 적용하므로, 이후의 모든 라우팅·레이턴시 계산은 유효 값을
computation uses the effective value. 사용한다.
- This guarantee is modeled by: - 이 보장은 다음으로 모델링된다:
- a dedicated logical path and/or service model that enforces HBM BW at the PE-local-HBM interaction point, - PE-로컬-HBM 상호작용 지점에서 HBM 대역폭을 강제하는 전용 논리 경로
- while still incurring non-zero latency along explicitly modeled components. 그리고/또는 서비스 모델,
- HBM CTRL internal modeling (PC striping, cut-through, scheduling fidelity) - 명시적으로 모델링된 컴포넌트들을 따라 0이 아닌 레이턴시를 여전히 발생시킨다.
is consolidated in ADR-0033 (Latency Model: Assumptions and Known - HBM CTRL 내부 모델링(PC 스트라이핑, cut-through, 스케줄링 충실도)은
Simplifications). The aggregate BW guarantee here remains the contract; ADR-0033 (레이턴시 모델: 가정 및 알려진 단순화)에 통합되어 있다.
ADR-0033 documents how the per-PC model realizes it and which scheduler 여기서의 총 대역폭 보장은 계약으로 유지되며, ADR-0033은 PC 단위 모델이
effects are intentionally simplified. 이를 어떻게 실현하는지와 어떤 스케줄러 효과가 의도적으로 단순화되었는지를
기록한다.
### D3. Remote PE HBM semantics (intra-cube) ### D3. 리모트 PE HBM 시맨틱 (큐브 내)
- A PE that accesses another PE's local HBM traverses the NOC: - PE가 다른 PE의 로컬 HBM에 접근할 때는 NOC를 거친다:
- PE_DMA → NOC → (fabric hops) → target PE's NOC port → HBM_CTRL - PE_DMA → NOC → (패브릭 hop) → 대상 PE NOC 포트 → HBM_CTRL
- NOC bandwidth and hop count may limit remote HBM access relative to local access. - NOC의 대역폭과 hop 수에 의해 리모트 HBM 접근이 로컬 접근 대비 제한될 수 있다.
### D4. Non-local HBM semantics (inter-cube / inter-SIP) ### D4. 비로컬 HBM 시맨틱 (큐브 간 / SIP)
- Accesses from a PE to HBM in a different cube or SIP MAY be limited by: - PE에서 다른 큐브나 SIP에 있는 HBM으로의 접근은 다음에 의해 제한될 수 있다:
- NOC bandwidth within the cube, - 큐브 내 NOC 대역폭,
- inter-cube UCIe links, - 큐브 간 UCIe 링크,
- inter-SIP fabric (PCIe/UAL). - SIP 간 패브릭 (PCIe/UAL).
- These paths MUST be explicit and traceable. - 이 경로들은 명시적이고 추적 가능해야 한다.
### D5. Shared SRAM semantics ### D5. 공유 SRAM 시맨틱
- Each CUBE contains a shared SRAM accessible by all PEs in that CUBE. - 각 CUBE는 해당 CUBE의 모든 PE가 접근 가능한 공유 SRAM을 포함한다.
- Access path: PE_DMA → NOC → shared SRAM. - 접근 경로: PE_DMA → NOC → 공유 SRAM.
- Shared SRAM bandwidth is limited by the NOC↔SRAM link bandwidth. - 공유 SRAM의 대역폭은 NOC↔SRAM 링크 대역폭으로 제한된다.
- Shared SRAM is not part of the HBM address space; it is a separate memory domain. - 공유 SRAM은 HBM 주소 공간의 일부가 아니라 별도의 메모리 도메인이다.
## Verification Notes ## Verification Notes
Tests should cover: 테스트가 다뤄야 할 케이스:
- local-HBM case: BW matches HBM BW regardless of fabric BW parameter - 로컬 HBM 케이스: 패브릭 BW 파라미터와 무관하게 대역폭이 HBM 대역폭과 일치
- remote PE HBM case: latency includes mesh hop traversal - 리모트 PE HBM 케이스: 레이턴시가 메시 hop 순회를 포함
- non-local cases (inter-cube/inter-SIP): BW/latency respond to fabric/link parameters - 비로컬 케이스(큐브 간/SIP): 패브릭/링크 파라미터에 대역폭·레이턴시가 반응
- shared SRAM case: access via NOC with correct BW - 공유 SRAM 케이스: NOC 경유 접근이 올바른 대역폭으로 수행됨
## Links ## Links
- SPEC R2/R5 - SPEC R2/R5
- ADR-0002 (distance/order & explicit bypass) - ADR-0002 (거리/순서 및 명시적 우회)
- ADR-0017 D7 (PE DMA data paths through NOC to HBM) - ADR-0017 D7 (NOC를 통한 PE DMA → HBM 데이터 경로)
@@ -1,4 +1,4 @@
# ADR-0005: Diagram Views & Distance-Aware Layout Rules # ADR-0005: 다이어그램 뷰 및 거리 기반 레이아웃 규칙
## Status ## Status
@@ -6,17 +6,17 @@ Accepted
## Context ## Context
We require verifiable and inspectable system modeling for a large-scale, 대규모, 매개변수화된 AI Accelerator 시스템에 대해 검증 가능하고 점검 가능한
parameterized AI Accelerator system. 시스템 모델링이 필요하다.
Humans must be able to: 사람이 다음을 할 수 있어야 한다:
- visually inspect the modeled topology, - 모델링된 토폴로지를 시각적으로 점검하고,
- reason about communication structure and relative distance, - 통신 구조와 상대적 거리에 대해 추론하고,
- do so at multiple abstraction levels without being overwhelmed by detail. - 세부 사항에 압도되지 않으면서 여러 추상화 수준에서 이를 수행한다.
The simulator models distance (accumulated latency) as a first-class concept. 시뮬레이터는 거리(누적 레이턴시)를 1급 개념(first-class concept)으로 모델링한다.
Diagrams must reflect this distance by default. 다이어그램은 기본적으로 이 거리를 반영해야 한다.
--- ---
@@ -24,163 +24,163 @@ Diagrams must reflect this distance by default.
### D1. Global Defaults ### D1. Global Defaults
- All diagrams MUST be **distance-aware by default**. - 모든 다이어그램은 기본적으로 **거리 인식(distance-aware)** 이어야 한다.
- All diagrams MUST render **representative views** of the architecture. - 모든 다이어그램은 아키텍처의 **대표 뷰(representative view)** 를 렌더링해야 한다.
- Instance indices (e.g., sip0, cube2, pe3) MUST NOT be required for diagram generation. - 인스턴스 인덱스(예: sip0, cube2, pe3)는 다이어그램 생성에 필수가 아니어야 한다.
- Instance indices MAY be used ONLY: - 인스턴스 인덱스는 다음의 경우에만 사용될 수 있다:
- to define a distance anchor in asymmetric or debugging scenarios, or - 비대칭 또는 디버깅 시나리오에서 거리 앵커를 정의하기 위한 경우, 또는
- when explicitly requested. - 명시적으로 요청된 경우.
--- ---
### D2. Representative Rendering Rule ### D2. Representative Rendering Rule
- All CUBEs share the same internal structure. - 모든 CUBE는 동일한 내부 구조를 공유한다.
- All PEs share the same internal structure. - 모든 PE는 동일한 내부 구조를 공유한다.
Therefore: 따라서:
- SIP-level diagrams render representative CUBEs and IO chiplets. - SIP 수준 다이어그램은 대표 CUBE와 IO 칩렛을 렌더링한다.
- CUBE-level diagrams render representative PEs as opaque blocks. - CUBE 수준 다이어그램은 대표 PE를 불투명 블록으로 렌더링한다.
- PE-level diagrams render a representative PE with fully expanded internals. - PE 수준 다이어그램은 내부가 완전히 전개된 대표 PE를 렌더링한다.
Diagrams MUST NOT depend on specific SIP, CUBE, or PE indices 다이어그램은 명시적으로 요청되지 않는 한
unless explicitly requested. 특정 SIP, CUBE, 또는 PE 인덱스에 의존해서는 안 된다.
--- ---
### D3. Diagram Views ### D3. Diagram Views
#### View A — SIP-Level Diagram #### View A — SIP 수준 다이어그램
**Purpose** **목적**
Explain system-scale structure and connectivity. 시스템 규모의 구조와 연결성을 설명한다.
**Visible elements** **가시 요소**
- SIP boundaries (optional) - SIP 경계 (선택사항)
- CUBEs (opaque blocks) - CUBE (불투명 블록)
- IO chiplets (opaque blocks) - IO 칩렛 (불투명 블록)
- Optional UCIe stubs only if needed to clarify connectivity - 연결성 명확화에 필요한 경우에만 선택적 UCIe 스텁
**Hidden elements** **비가시 요소**
- PE internals - PE 내부
- CUBE internal fabric - CUBE 내부 패브릭
- IO chiplet internals - IO 칩렛 내부
**Visible links** **가시 링크**
- Host ↔ IO chiplets (PCIe) - 호스트 ↔ IO 칩렛 (PCIe)
- SIP ↔ SIP (PCIe / UAL via switches) - SIP ↔ SIP (스위치를 통한 PCIe / UAL)
- IO ↔ CUBE (on-package links) - IO ↔ CUBE (온패키지 링크)
--- ---
#### View B — CUBE-Level Diagram #### View B — CUBE 수준 다이어그램
**Purpose** **목적**
Explain cube-internal structure and data/control flow. 큐브 내부 구조와 데이터/제어 흐름을 설명한다.
**Visible elements** **가시 요소**
- Router mesh: 2D grid of NOC routers (from cube_mesh.yaml), all traffic routes through mesh - 라우터 메시: NoC 라우터의 2D 격자 (cube_mesh.yaml로부터), 모든 트래픽은 메시를 통해 라우팅됨
- HBM_CTRL attached to PE routers (local HBM = 0 hop) - PE 라우터에 부착된 HBM_CTRL (로컬 HBM = 0 )
- HBM subsystem (HBM_CTRL) - HBM 서브시스템 (HBM_CTRL)
- Shared SRAM: cube-level shared memory - 공유 SRAM: 큐브 수준 공유 메모리
- Management CPU (M_CPU) - 관리 CPU (M_CPU)
- PEs as opaque blocks (PE[0..N1]) - 불투명 블록으로 표현된 PE (PE[0..N1])
- UCIe endpoints (N/E/W/S) as ports - 포트로 표현된 UCIe 엔드포인트 (N/E/W/S)
**Hidden elements** **비가시 요소**
- PE internals - PE 내부
**Visible links** **가시 링크**
- PE → router (HBM + non-HBM data path via mesh) - PE → 라우터 (메시를 통한 HBM + -HBM 데이터 경로)
- Router ↔ HBM_CTRL (local HBM access) - 라우터 ↔ HBM_CTRL (로컬 HBM 액세스)
- Router ↔ Router (mesh hops for remote access) - 라우터 ↔ 라우터 (원격 액세스를 위한 메시 홉)
- Router ↔ UCIe endpoints - 라우터 ↔ UCIe 엔드포인트
- Router ↔ shared SRAM - 라우터 ↔ 공유 SRAM
- M_CPU ↔ router (command path) - M_CPU ↔ 라우터 (명령 경로)
- Router → PE_CPU (command delivery, collapsed into PE block) - 라우터 → PE_CPU (명령 전달, PE 블록 내부로 축약됨)
--- ---
#### View C — PE-Level Diagram #### View C — PE 수준 다이어그램
**Purpose** **목적**
Explain internal PE behavior and execution structure. PE 내부 동작과 실행 구조를 설명한다.
**Visible elements** **가시 요소**
- PE_CPU - PE_CPU
- Command handler / scheduler - 명령 핸들러 / 스케줄러
- PE_TCM (local SRAM) - PE_TCM (로컬 SRAM)
- HW accelerators (DMA, GEMM, MATH, etc.) - HW 가속기 (DMA, GEMM, MATH)
- Local HBM interface - 로컬 HBM 인터페이스
- Optional IPCQ / messaging endpoints - 선택적 IPCQ / 메시징 엔드포인트
**Visible links** **가시 링크**
- Control paths (CPU → scheduler → engines) - 제어 경로 (CPU → 스케줄러 → 엔진)
- Data paths (engines ↔ TCM, DMA ↔ local HBM) - 데이터 경로 (엔진 ↔ TCM, DMA ↔ 로컬 HBM)
- External fabric ports as abstract ports only - 외부 패브릭 포트는 추상 포트로만 표현
--- ---
### D4. Distance-Aware Layout (Default) ### D4. 거리 기반 레이아웃 (기본)
#### Distance definition #### 거리 정의
- Distance is defined as **accumulated latency**, consistent with ADR-0002. - 거리는 ADR-0002와 정합되도록 **누적 레이턴시(accumulated latency)** 로 정의된다.
- Distance is computed from a single anchor node. - 거리는 단일 앵커 노드로부터 계산된다.
#### Default anchor selection #### 기본 앵커 선택
- SIP view: IO chiplet (or Host CPU if present) - SIP : IO 칩렛 (또는 존재한다면 호스트 CPU)
- CUBE view: a representative PE - CUBE 뷰: 대표 PE
- PE view: PE_CPU or Command Handler - PE : PE_CPU 또는 명령 핸들러
Anchors are **implicit defaults** and MUST NOT be required to be specified. 앵커는 **암묵적 기본값**이며, 지정이 강제되어서는 안 된다.
#### Layout rules #### 레이아웃 규칙
- Diagrams MUST be laid out in layers based on distance buckets. - 다이어그램은 거리 버킷에 기반한 레이어로 배치되어야 한다.
- Layout direction MUST be consistent within a view type - 레이아웃 방향은 뷰 유형 내에서 일관되어야 한다
(preferred: left-to-right). (선호: 좌→우).
- Nodes with equal distance MUST have stable ordering - 동일 거리의 노드는 결정론적으로 안정된 순서를 가져야 한다
(by role or identifier, deterministically). (역할 또는 식별자 기준).
Cycles MAY be rendered using dashed or curved edges for readability, 가독성을 위해 사이클은 점선 또는 곡선 엣지로 렌더링될 수 있으며,
without affecting distance semantics. 이는 거리 의미에 영향을 주지 않는다.
--- ---
### D5. Generation Contract (for Tools / Claude Code) ### D5. 생성 컨트랙트 (도구 / Claude Code)
When generating diagrams: 다이어그램 생성 시:
- Assume distance-aware layout by default. - 기본적으로 거리 기반 레이아웃을 가정한다.
- Assume representative rendering by default. - 기본적으로 대표 렌더링을 가정한다.
- Do NOT ask for SIP/CUBE/PE indices unless required. - 필요한 경우가 아니면 SIP/CUBE/PE 인덱스를 묻지 않는다.
- Do NOT expand hidden abstraction levels. - 숨겨진 추상화 수준을 전개하지 않는다.
- Prefer architectural clarity over micro-hop fidelity. - 마이크로 홉의 정밀도보다 아키텍처적 명확성을 우선한다.
--- ---
## Consequences ## Consequences
- Diagrams are stable across topology scaling. - 다이어그램은 토폴로지 스케일링에 걸쳐 안정적으로 유지된다.
- Changes in distance or routing policy are reflected visually. - 거리 또는 라우팅 정책의 변경이 시각적으로 반영된다.
- Diagrams serve as verifiable artifacts derived from the simulator model, - 다이어그램은 수작업으로 유지되는 문서가 아닌, 시뮬레이터 모델로부터
not as hand-maintained documentation. 파생된 검증 가능한 산출물의 역할을 한다.
--- ---
## Links ## Links
- SPEC Section 4 (Output, Debuggability, and Diagrams) - SPEC Section 4 (Output, Debuggability, and Diagrams)
- ADR-0002 (Routing distance semantics) - ADR-0002 (라우팅 거리 의미)
- ADR-0006 (Topology compilation & automatic diagram generation) - ADR-0006 (토폴로지 컴파일 및 자동 다이어그램 생성)
@@ -1,4 +1,4 @@
# ADR-0006: Topology Compilation, Distance Extraction, and Automatic Diagram Generation # ADR-0006: 토폴로지 컴파일, 거리 추출, 그리고 자동 다이어그램 생성
## Status ## Status
@@ -6,125 +6,125 @@ Accepted
## Context ## Context
The simulator compiles topology configuration (e.g., topology.yaml) into an explicit model graph, 시뮬레이터는 토폴로지 설정(예: topology.yaml)을 명시적인 모델 그래프로 컴파일하고,
and computes routing and accumulated latency (distance). 라우팅 및 누적 레이턴시(거리)를 계산한다.
Diagrams should be generated from these authoritative artifacts to ensure consistency and avoid 정합성을 보장하고 수작업으로 유지되는 토폴로지 도면을 피하기 위해,
hand-maintained topology drawings. 다이어그램은 이 권위 있는 산출물로부터 생성되어야 한다.
Additionally, for usability, diagrams should be emitted automatically into a stable location 또한 사용성을 위해, 다이어그램은 안정적인 위치로 자동 방출되어
so that developers can preview them immediately in the repository. 개발자가 저장소 내에서 즉시 미리볼 수 있어야 한다.
--- ---
## Decision ## Decision
### D1. Topology compilation is the single source of truth ### D1. 토폴로지 컴파일은 유일한 진실 공급원이다
- topology.yaml (or equivalent config) is compiled into: - topology.yaml(또는 동등한 설정)은 다음으로 컴파일된다:
- an explicit system graph, - 명시적인 시스템 그래프,
- node/link attributes, - 노드/링크 속성,
- routing policies. - 라우팅 정책.
This compiled graph is the authoritative representation of the system. 이 컴파일된 그래프가 시스템의 권위 있는 표현이다.
### D2. Distance extraction during compilation ### D2. 컴파일 중 거리 추출
- During or immediately after topology compilation, the simulator MUST compute distance metadata - 토폴로지 컴파일 중 또는 그 직후, 시뮬레이터는 ADR-0002와 정합되는
(accumulated latency) consistent with ADR-0002. 거리 메타데이터(누적 레이턴시)를 계산해야 한다.
- Distance metadata MUST be sufficient to support distance-aware diagram layout as defined in ADR-0005. - 거리 메타데이터는 ADR-0005에서 정의한 거리 기반 다이어그램 레이아웃을 지원하기에 충분해야 한다.
- Distributed fabric segments (e.g., NOC) MAY have distance_mm = 0 per ADR-0002 D4; - 분산된 패브릭 세그먼트(예: NoC)는 ADR-0002 D4에 따라 distance_mm = 0을 가질 수 있다.
layout placement for such nodes uses explicit position metadata rather than distance buckets. 이러한 노드의 레이아웃 배치는 거리 버킷이 아닌 명시적 위치 메타데이터를 사용한다.
### D3. Diagram generation is a derived artifact ### D3. 다이어그램 생성은 파생 산출물이다
- Diagrams MUST be generated from: - 다이어그램은 다음으로부터 생성되어야 한다:
- the compiled topology graph, - 컴파일된 토폴로지 그래프,
- extracted distance metadata, - 추출된 거리 메타데이터,
- view/layout rules defined in ADR-0005. - ADR-0005에 정의된 뷰/레이아웃 규칙.
- Diagram generation MUST NOT require additional hand-written topology descriptions. - 다이어그램 생성은 추가적인 수작업 토폴로지 기술을 요구해서는 안 된다.
### D4. Automatic diagram emission to the repository ### D4. 저장소로의 자동 다이어그램 방출
- As part of topology compilation, the implementation MUST produce the following diagrams by default: - 토폴로지 컴파일의 일부로서, 구현은 기본적으로 다음 다이어그램을 생성해야 한다:
- SIP-level diagram (representative, distance-aware) - SIP 수준 다이어그램 (대표, 거리 인식)
- CUBE-level diagram (representative, distance-aware) - CUBE 수준 다이어그램 (대표, 거리 인식)
- PE-level diagram (representative, distance-aware) - PE 수준 다이어그램 (대표, 거리 인식)
- The default output directory is: - 기본 출력 디렉터리는 다음과 같다:
- `docs/diagrams/` - `docs/diagrams/`
- The generator MUST overwrite/update only when the compiled topology (or diagram rules) changes. - 생성기는 컴파일된 토폴로지(또는 다이어그램 규칙)가 변경되었을 때에만 덮어쓰기/업데이트해야 한다.
### D5. View-specific projection and layout ### D5. 뷰별 투영 및 레이아웃
For each view (SIP / CUBE / PE): 각 뷰(SIP / CUBE / PE)에 대해:
- The generator MUST project the compiled graph into a reduced view graph: - 생성기는 컴파일된 그래프를 축소된 뷰 그래프로 투영해야 한다:
- hide/collapse nodes according to ADR-0005, - ADR-0005에 따라 노드를 숨기거나 축약하고,
- preserve connectivity semantics relevant to that view, - 해당 뷰와 관련된 연결성 의미를 보존하고,
- compute distance buckets and assign layout layers deterministically. - 거리 버킷을 계산하여 레이아웃 레이어를 결정론적으로 할당한다.
- CUBE-level projection MUST include: - CUBE 수준 투영은 다음을 포함해야 한다:
- Router mesh (from cube_mesh.yaml), HBM_CTRL, shared SRAM, M_CPU, UCIe ports, - 라우터 메시 (cube_mesh.yaml로부터), HBM_CTRL, 공유 SRAM, M_CPU, UCIe 포트,
and PEs as opaque blocks. 그리고 불투명 블록으로 표현된 PE.
- All paths (HBM, non-HBM, command) route through the same router mesh (ADR-0017). - 모든 경로(HBM, -HBM, 명령)는 동일한 라우터 메시를 통해 라우팅된다 (ADR-0017).
- Default anchors are implicit (ADR-0005) and MUST NOT require instance indices. - 기본 앵커는 암묵적이며 (ADR-0005) 인스턴스 인덱스를 요구해서는 안 된다.
### D6. Output formats and determinism ### D6. 출력 포맷과 결정론
- The generator MUST output at least one of: - 생성기는 다음 중 최소 하나를 출력해야 한다:
- Mermaid (Markdown-native) - Mermaid (Markdown 네이티브)
- Graphviz DOT (rank-based control) - Graphviz DOT (rank 기반 제어)
- SVG (mm-accurate layout, no external dependencies) - SVG (mm 단위 정확도 레이아웃, 외부 의존성 없음)
- SVG is preferred when mm-accurate position metadata is available from the compiled topology. - 컴파일된 토폴로지로부터 mm 단위 정확도의 위치 메타데이터가 가용한 경우 SVG가 선호된다.
- Output MUST be deterministic: - 출력은 결정론적이어야 한다:
- same topology + same rules → identical diagram text - 동일한 토폴로지 + 동일한 규칙 → 동일한 다이어그램 텍스트
- File naming MUST be deterministic and stable (see "Output Conventions"). - 파일 이름은 결정론적이고 안정적이어야 한다 (아래의 "출력 컨벤션" 참조).
### D7. Performance and caching ### D7. 성능 및 캐싱
- Diagram generation MAY be lazy and/or cached, as long as the outputs in `docs/diagrams/` - 다이어그램 생성은 지연(lazy) 및/또는 캐시될 수 있으며, `docs/diagrams/`의 출력이
remain consistent with the compiled topology. 컴파일된 토폴로지와 정합을 유지하는 한 그렇다.
- The implementation SHOULD use a cache key based on: - 구현은 다음을 기반으로 한 캐시 키를 사용해야 한다(SHOULD):
- topology content hash, - 토폴로지 콘텐츠 해시,
- routing policy version, - 라우팅 정책 버전,
- diagram rules version, - 다이어그램 규칙 버전,
- view type (SIP/CUBE/PE). - 뷰 유형 (SIP/CUBE/PE).
--- ---
## Output Conventions ## 출력 컨벤션
### Directory ### 디렉터리
- `docs/diagrams/` is the canonical output directory for generated diagrams. - `docs/diagrams/`는 생성된 다이어그램의 표준 출력 디렉터리이다.
### File names (recommended, deterministic) ### 파일 이름 (권장, 결정론적)
- `system_view.svg` / `system_view.mmd` / `system_view.dot` - `system_view.svg` / `system_view.mmd` / `system_view.dot`
- `sip_view.svg` / `sip_view.mmd` / `sip_view.dot` - `sip_view.svg` / `sip_view.mmd` / `sip_view.dot`
- `cube_view.svg` / `cube_view.mmd` / `cube_view.dot` - `cube_view.svg` / `cube_view.mmd` / `cube_view.dot`
- `pe_view.svg` / `pe_view.mmd` / `pe_view.dot` - `pe_view.svg` / `pe_view.mmd` / `pe_view.dot`
Optionally, for multi-topology workflows: 선택적으로, 멀티 토폴로지 워크플로우용:
- `sip_view__{topology_id}.svg` - `sip_view__{topology_id}.svg`
- `cube_view__{topology_id}.svg` - `cube_view__{topology_id}.svg`
- `pe_view__{topology_id}.svg` - `pe_view__{topology_id}.svg`
### Repository policy ### 저장소 정책
- Generated diagram files MAY be committed to the repository to enable diff-based review. - 생성된 다이어그램 파일은 diff 기반 리뷰가 가능하도록 저장소에 커밋될 수 있다.
- If committed, they MUST be reproducible from topology compilation. - 커밋된 경우, 이는 토폴로지 컴파일로부터 재현 가능해야 한다.
--- ---
## Consequences ## Consequences
- Diagrams are always consistent with simulator behavior. - 다이어그램은 항상 시뮬레이터 동작과 정합한다.
- Architectural changes automatically propagate to visualizations. - 아키텍처 변경이 시각화에 자동으로 전파된다.
- Diagram diffs become meaningful indicators of architectural change. - 다이어그램 diff는 아키텍처 변경의 의미 있는 지표가 된다.
--- ---
## Links ## Links
- SPEC Section 4 (Output, Debuggability, and Diagrams) - SPEC Section 4 (Output, Debuggability, and Diagrams)
- ADR-0002 (Distance semantics) - ADR-0002 (거리 의미)
- ADR-0005 (Diagram views and layout rules) - ADR-0005 (다이어그램 뷰 및 레이아웃 규칙)
@@ -1,4 +1,4 @@
# ADR-0007: Runtime API and Simulation Engine Boundaries # ADR-0007: 런타임 API 및 시뮬레이션 엔진 경계
## Status ## Status
@@ -6,90 +6,90 @@ Accepted
## Context ## Context
The simulator consists of multiple layers with distinct responsibilities: 시뮬레이터는 책임이 명확히 다른 여러 계층으로 구성된다:
- a host-facing API layer used by benchmarks and user code, - 벤치마크와 사용자 코드가 사용하는 호스트 대상 API 계층,
- a discrete-event simulation engine that executes requests, - 요청을 실행하는 이산 이벤트 시뮬레이션 엔진,
- device components that model hardware behavior. - 하드웨어 동작을 모델링하는 디바이스 컴포넌트.
Without strict boundaries, orchestration logic can leak into components, 엄격한 경계가 없으면 오케스트레이션 로직이 컴포넌트로 누출되거나
or simulation internals can become entangled with user-facing APIs. 시뮬레이션 내부가 사용자 대상 API와 얽힐 수 있다.
This ADR defines clear responsibility boundaries between: 본 ADR은 다음 사이의 명확한 책임 경계를 정의한다:
- runtime API, - 런타임 API,
- simulation engine (sim_engine), - 시뮬레이션 엔진 (sip_engine),
- hardware components. - 하드웨어 컴포넌트.
--- ---
## Decision ## Decision
### D1. Runtime API is host-facing orchestration only ### D1. 런타임 API는 호스트 대상 오케스트레이션만 담당
The runtime API represents host/driver-level behavior and MUST: 런타임 API는 호스트/드라이버 수준의 동작을 표현하며 다음을 해야 한다:
- expose high-level operations (tensor deployment, kernel launch), - 고수준 동작 노출 (텐서 배포, 커널 launch),
- submit requests only to endpoint components (e.g., IO_CPU), - 엔드포인트 컴포넌트(예: IO_CPU)에만 요청 제출,
- await completion via futures/handles, - futures/handles로 완료 대기,
- own and persist host-side metadata (tensor allocation maps, kernel bindings). - 호스트측 메타데이터(텐서 할당 맵, 커널 바인딩)의 소유와 영속화.
The runtime API MUST NOT: 런타임 API가 해서는 안 되는 것:
- hardcode hop-by-hop routing or fan-out, - hop-by-hop 라우팅 또는 fan-out 하드코딩,
- directly invoke internal components (M_CPU, PE_CPU, engines), - 내부 컴포넌트(M_CPU, PE_CPU, 엔진) 직접 호출,
- embed topology- or routing-specific assumptions. - 토폴로지나 라우팅 관련 가정 내장.
--- ---
### D2. Simulation engine wires components and tracks completion ### D2. 시뮬레이션 엔진은 컴포넌트를 연결하고 완료를 추적
The simulation engine (sim_engine) MUST: 시뮬레이션 엔진(sim_engine)은 다음을 해야 한다:
- wire components at initialization (create port stores + start wire - 초기화 시점에 컴포넌트 연결 (컴포넌트 포트/와이어 프레임워크에 따라
processes per the component port/wire framework — ADR-0015), 포트 store 생성 + 와이어 프로세스 시작 — ADR-0015),
- inject requests into the compiled topology graph at entry components - 컴파일된 토폴로지 그래프의 진입 컴포넌트(예: 메모리 동작은 PCIE_EP,
(e.g., PCIE_EP for memory operations, IO_CPU for kernel launch), 커널 launch는 IO_CPU)에 요청 주입,
- schedule and execute events using a discrete-event model, - 이산 이벤트 모델로 이벤트 스케줄링과 실행,
- manage correlation ids and completion tracking. - correlation ID와 완료 추적 관리.
The simulation engine MUST NOT: 시뮬레이션 엔진이 해서는 안 되는 것:
- define tensor semantics, - 텐서 시맨틱 정의,
- define kernel execution policies, - 커널 실행 정책 정의,
- expose internal graph details to the runtime API, - 런타임 API에 내부 그래프 세부사항 노출,
- walk the topology path during request execution, - 요청 실행 중에 토폴로지 경로를 따라 걷기,
- call component `run()` methods directly, - 컴포넌트의 `run()` 메서드 직접 호출,
- track per-hop latency or decompose fan-out (components own this). - hop별 레이턴시 추적 또는 fan-out 분해 (컴포넌트의 책임).
--- ---
### D3. Components own fan-out and aggregation ### D3. 컴포넌트가 fan-out과 집계를 담당
Device-side components MUST: 디바이스측 컴포넌트는 다음을 해야 한다:
- fan-out requests to downstream domains - 요청을 하위 도메인으로 fan-out
(IO_CPU → M_CPU → PE_CPU → schedulers/engines), (IO_CPU → M_CPU → PE_CPU → 스케줄러/엔진),
- aggregate completion and failure signals, - 완료·실패 신호 집계,
- propagate results deterministically upstream. - 결정론적으로 상위로 결과 전파.
Neither the runtime API nor the simulation engine may orchestrate 런타임 API와 시뮬레이션 엔진 모두 컴포넌트 수준의 fan-out을 명시적으로
component-level fan-out explicitly. 오케스트레이션해서는 안 된다.
--- ---
## Consequences ## Consequences
- Runtime APIs remain stable as topology and routing evolve. - 토폴로지와 라우팅이 변해도 런타임 API는 안정적이다.
- Simulation internals can change without affecting user-facing code. - 시뮬레이션 내부는 사용자 대상 코드에 영향을 주지 않고 변경 가능하다.
- Component implementations remain swappable via DI. - 컴포넌트 구현은 DI로 교체 가능한 상태가 유지된다.
--- ---
## Links ## Links
- SPEC R4, R7, R8 - SPEC R4, R7, R8
- ADR-0008 (Tensor deployment) - ADR-0008 (텐서 배포)
- ADR-0009 (Kernel execution) - ADR-0009 (커널 실행)
- ADR-0015 (Component port/wire model and engine role) - ADR-0015 (컴포넌트 포트/와이어 모델과 엔진 역할)
- ADR-0010 (CLI surface and execution semantics — runtime API consumer) - ADR-0010 (CLI 표면과 실행 시맨틱 — 런타임 API 소비자)
@@ -1,4 +1,4 @@
# ADR-0008: Tensor Deployment and Allocation (Host Allocator, PA-first) # ADR-0008: 텐서 배포 및 할당 (호스트 할당기, PA 우선)
## Status ## Status
@@ -6,95 +6,95 @@ Accepted
## Context ## Context
Benchmarks require PyTorch-like tensor semantics: 벤치마크는 PyTorch와 유사한 텐서 시맨틱을 요구한다:
- tensor creation (empty, fill), - 텐서 생성 (empty, fill),
- deployment to accelerator devices (tensor.to()). - 가속기 디바이스로의 배포 (tensor.to()).
In the realistic system, host software manages allocation/mapping and installs 현실적인 시스템에서는 호스트 소프트웨어가 할당·매핑을 관리하고 DMA/MMU
mappings for DMA/MMU. For Phase 0 we simplify (ADR-0011): 매핑을 설치한다. Phase 0에서는 (ADR-0011) 다음으로 단순화한다:
- device memory operations use PA only, - 디바이스 메모리 동작은 PA만 사용,
- VA/MMU/IOMMU is not modeled. - VA/MMU/IOMMU는 모델링하지 않는다.
To keep the host↔device interface minimal, we avoid a separate 호스트↔디바이스 인터페이스를 최소로 유지하기 위해 별도의
AllocateTensorMeta message. Instead, host allocation produces a PA shard map AllocateTensorMeta 메시지는 피한다. 대신 호스트 할당은 PA 샤드 맵을
that is used directly by MemoryWrite/Read and KernelLaunch. 생성하여 MemoryWrite/Read KernelLaunch가 직접 사용한다.
--- ---
## Decision ## Decision
### D1. Tensor is a host-owned handle with PA shard mapping ### D1. Tensor는 PA 샤드 매핑을 가진 호스트 소유 핸들
A Tensor object is a host-owned handle that encapsulates: Tensor 객체는 다음을 캡슐화하는 호스트 소유 핸들이다:
- shape and dtype, - shape dtype,
- initialization intent, - 초기화 의도,
- device placement and allocation metadata as a PA shard map. - PA 샤드 맵 형태의 디바이스 배치 및 할당 메타데이터.
After deployment, the Tensor handle MUST contain: 배포 이후 Tensor 핸들은 다음을 포함해야 한다:
- a list of shards, each with (sip,cube,pe,pa,nbytes,offset_bytes). - 각각 (sip, cube, pe, pa, nbytes, offset_bytes)를 가진 샤드 리스트.
This PA shard mapping is the single source of truth for kernel argument binding. 이 PA 샤드 매핑이 커널 인수 바인딩의 단일 진실 원천이다.
--- ---
### D2. Deployment uses a host allocator (Phase 0) ### D2. 배포는 호스트 할당기를 사용한다 (Phase 0)
In Phase 0, tensor deployment produces PA shard mappings via a host allocator: Phase 0에서 텐서 배포는 호스트 할당기를 통해 PA 샤드 매핑을 생성한다:
- placement (split/replicate/hybrid) is decided by a DP policy, - 배치(split/replicate/hybrid)는 DP 정책에 의해 결정,
- allocation assigns PA ranges at the PE level and returns shard mappings, - 할당은 PE 수준에서 PA 범위를 부여하고 샤드 매핑을 반환,
- the Tensor handle stores the resulting shard list deterministically. - Tensor 핸들은 결정론적으로 결과 샤드 리스트를 저장.
No separate host-visible device allocation RPC is required in Phase 0. Phase 0에서는 호스트가 보는 별도의 디바이스 할당 RPC는 필요하지 않다.
--- ---
### D3. Data initialization and transfer uses MemoryWrite/Read only ### D3. 데이터 초기화와 전송은 MemoryWrite/Read만 사용
Any data initialization or transfer implied by a tensor (e.g., fill, copy) 텐서가 함의하는 모든 데이터 초기화나 전송(예: fill, copy)
MUST be represented using Host ↔ IO_CPU messages only: Host ↔ IO_CPU 메시지만으로 표현되어야 한다:
- MemoryWrite - MemoryWrite
- MemoryRead - MemoryRead
Rules: 규칙:
- MemoryWrite/Read MUST reference PA + (sip,cube,pe) tags (ADR-0012). - MemoryWrite/Read PA + (sip, cube, pe) 태그를 참조해야 한다 (ADR-0012).
- Allocation metadata MUST NOT be embedded as a separate allocation message. - 할당 메타데이터는 별도의 할당 메시지로 임베드되어서는 안 된다.
- Bulk tensor data MUST NOT be embedded in Phase 0 messages. - 대량 텐서 데이터는 Phase 0 메시지에 임베드되어서는 안 된다.
The simulation engine schedules MemoryWrite/Read through the graph so that 시뮬레이션 엔진은 MemoryWrite/Read를 그래프를 통해 스케줄하므로 레이턴시는
latency is computed by explicit traversal. 명시적 순회로 계산된다.
--- ---
### D4. Extension path (non-breaking) ### D4. 확장 경로 (호환성 유지)
Future ADRs MAY introduce optional VA/MMU/IOMMU modeling by adding: 향후 ADR이 다음을 추가하여 선택적인 VA/MMU/IOMMU 모델링을 도입할 수 있다:
- virtual addressing in tensor handles, - 텐서 핸들에 가상 주소,
- mapping install steps, - 매핑 설치 단계,
- translation latency/page granularity. - 변환 레이턴시·페이지 granularity.
The Phase 0 PA shard map remains a valid fast-path configuration. Phase 0 PA 샤드 맵은 유효한 fast-path 구성으로 유지된다.
--- ---
## Consequences ## Consequences
- Host↔IO_CPU contract remains minimal (MemoryRead/Write + KernelLaunch). - Host↔IO_CPU 계약이 최소(MemoryRead/Write + KernelLaunch)로 유지된다.
- KernelLaunch can pass per-PE data placement explicitly via shard tags. - KernelLaunch가 샤드 태그를 통해 PE별 데이터 배치를 명시적으로 전달할 수 있다.
- Early implementation stays simple and testable. - 초기 구현이 단순하고 테스트 가능하게 유지된다.
--- ---
## Links ## Links
- ADR-0011 (Memory Addressing — PA / VA / LA) - ADR-0011 (메모리 주소 지정 — PA / VA / LA)
- ADR-0012 (Host↔IO_CPU schema) - ADR-0012 (Host↔IO_CPU 스키마)
- ADR-0007 (runtime_api vs sim_engine boundaries) - ADR-0007 (runtime_api vs sim_engine 경계)
- ADR-0009 (Kernel execution) - ADR-0009 (커널 실행)
@@ -1,4 +1,4 @@
# ADR-0009: Kernel Execution Messaging and Completion Semantics # ADR-0009: 커널 실행 메시징 및 완료 시맨틱
## Status ## Status
@@ -6,89 +6,86 @@ Accepted
## Context ## Context
Kernel execution is initiated by the host and proceeds through 커널 실행은 호스트에서 시작되어 디바이스 측 제어 컴포넌트를 통해 진행된다:
device control components:
Host → IO_CPU → M_CPU → PE_CPU → schedulers → engines Host → IO_CPU → M_CPU → PE_CPU → 스케줄러 → 엔진
Completion propagates in reverse order. 완료는 역방향으로 전파된다.
To keep benchmarks simple and topology-agnostic, 벤치마크를 단순하고 토폴로지에 비의존적으로 유지하기 위해, 커널 실행은
kernel execution must be endpoint-driven with deterministic aggregation. 엔드포인트 기반(endpoint-driven)이어야 하며 완료 집계는 결정론적이어야 한다.
--- ---
## Decision ## Decision
### D1. Kernel launch is an endpoint request ### D1. 커널 런치는 엔드포인트 요청이다
A kernel launch is initiated by submitting a single KernelLaunch request 커널 런치는 IO_CPU 엔드포인트에 단일 KernelLaunch 요청을 제출함으로써
to the IO_CPU endpoint. 시작된다.
The runtime API MUST: runtime API는 반드시:
- construct the kernel launch request, - 커널 런치 요청을 구성하고,
- submit it to IO_CPU, - 이를 IO_CPU로 제출하며,
- await a single completion result. - 단일 완료 결과를 대기해야 한다.
The runtime API MUST NOT orchestrate internal fan-out. runtime API는 내부 팬아웃(fan-out)을 직접 조율해서는 안 된다.
--- ---
### D2. Tensor arguments are passed by metadata ### D2. 텐서 인자는 메타데이터로 전달된다
KernelLaunch requests MUST reference tensor arguments via: KernelLaunch 요청은 텐서 인자를 다음을 통해 참조해야 한다:
- host-owned tensor handles, or - 호스트가 소유한 텐서 핸들, 또는
- resolved device address maps derived from those handles. - 그러한 핸들로부터 해석된 디바이스 주소 맵.
Bulk tensor data MUST NOT be embedded in kernel launch messages. 대용량 텐서 데이터는 커널 런치 메시지에 임베드되어서는 안 된다.
--- ---
### D3. Fan-out and aggregation are component responsibilities ### D3. 팬아웃과 집계는 컴포넌트의 책임이다
- IO_CPU fans out work to M_CPUs. - IO_CPU는 작업을 M_CPU들에게 팬아웃한다.
- M_CPU fans out work to PE_CPUs. - M_CPU는 작업을 PE_CPU들에게 팬아웃한다.
- PE_CPU manages kernel execution and engine dispatch. - PE_CPU는 커널 실행과 엔진 디스패치를 관리한다.
Completion semantics: 완료 시맨틱:
- M_CPU completes when all targeted PEs complete or a failure policy triggers. - M_CPU는 대상 PE들이 모두 완료되거나 실패 정책이 트리거되면 완료된다.
- IO_CPU completes when all targeted CUBEs complete or a failure policy triggers. - IO_CPU는 대상 큐브들이 모두 완료되거나 실패 정책이 트리거되면 완료된다.
--- ---
### D4. Completion and failure propagation ### D4. 완료 및 실패 전파
- All messages MUST carry correlation identifiers. - 모든 메시지는 correlation ID를 포함해야 한다.
- Completion and failure MUST propagate deterministically to the host. - 완료와 실패는 호스트로 결정론적으로 전파되어야 한다.
- The simulation engine provides futures/handles to observe completion. - 시뮬레이션 엔진은 완료를 관찰할 수 있는 future/handle을 제공한다.
--- ---
### D5. Launch timing is endpoint-synchronized ### D5. 런치 타이밍은 엔드포인트 동기화된다
All PEs targeted by a single kernel launch MUST begin executing the kernel 단일 커널 런치가 지정한 모든 PE는 런치 진입점으로부터의 디스패치 경로 길이와
body at the same simulated time, regardless of their dispatch path length 무관하게, 동일한 시뮬레이션 시각에 커널 본문 실행을 시작해야 한다.
from the launch entry point.
Rationale. The dispatch tree Host → IO_CPU → M_CPU → PE_CPU has variable 근거. 디스패치 트리 Host → IO_CPU → M_CPU → PE_CPU는 모든 레벨에서 가변
latency at every level. PEs near their M_CPU receive the launch earlier 레이턴시를 가진다. M_CPU에 가까운 PE는 멀리 있는 PE보다 런치를 더 일찍
than PEs farther away; cubes near an IO_CPU receive it earlier than cubes 수신하고, IO_CPU에 가까운 큐브는 먼 큐브보다 더 일찍 수신한다. 동기화가
farther away. Without synchronization, each PE's kernel begins at a 없으면 각 PE의 커널은 서로 다른 `env.now`에서 시작되어, `pe_exec_ns`와 같은
different `env.now`, making per-PE metrics such as `pe_exec_ns` a function PE별 메트릭이 커널 자체의 동작이 아니라 디스패치 경로 기하 구조의 함수가
of dispatch-path geometry rather than of the kernel's behavior — 된다 — 그 결과 커널 내부 대기(예: 큐브 간 또는 SIP 간 홉에서의 `tl.recv`)를
producing measurement artifacts in benchmarks that time kernel-internal 타이밍하는 벤치마크에서 측정 아티팩트가 발생한다.
waits (for example `tl.recv` on cross-cube or cross-SIP hops).
Mechanism. 메커니즘.
- `KernelLaunchMsg` carries an optional `target_start_ns: float | None`. - `KernelLaunchMsg`는 선택적 `target_start_ns: float | None`을 포함한다.
- **IO_CPU** is the canonical stamper. On fan-out to M_CPUs, it - **IO_CPU**가 정식 스탬프 주체이다. M_CPU들로 팬아웃할 때, 모든 대상
computes `target_start_ns = env.now + max_latency` where (sip, cube, pe) 튜플에 대한 **두 단계 디스패치 체인**의 최대값을
`max_latency` is the maximum, over every target (sip, cube, pe) `max_latency`로 하여 `target_start_ns = env.now + max_latency`
tuple, of the **two-leg dispatch chain**: 계산한다:
``` ```
max_latency(sip, cube, pe) = max_latency(sip, cube, pe) =
@@ -98,49 +95,44 @@ Mechanism.
- m_cpu.overhead_ns - m_cpu.overhead_ns
``` ```
This models the actual dispatch as **two sequential Transactions** 이는 실제 디스패치를 **두 개의 순차적 Transaction**(IO_CPU → M_CPU,
(IO_CPU → M_CPU, then M_CPU → PE_CPU). Each leg's 그리고 M_CPU → PE_CPU)으로 모델링한다. 각 구간의
`compute_path_latency_ns` adds its endpoints' `overhead_ns`; `compute_path_latency_ns`는 양 끝점의 `overhead_ns`를 더하는데,
`io_cpu.overhead_ns` is subtracted because IO_CPU has already `io_cpu.overhead_ns`는 이 메서드가 실행되기 전 IO_CPU가 이미 지불했으므로
paid it before this method runs, and `m_cpu.overhead_ns` is 차감하고, `m_cpu.overhead_ns`는 구간1의 끝점인 동시에 구간2의 시작점으로
subtracted once because it appears as endpoint of leg1 *and* 나타나지만 런타임에는 한 번만 지불되므로 한 번 차감한다. 단일
start of leg2 but is paid only once at run time. A single `find_node_path(io_cpu, pe_cpu)` 순회는 **동등하지 않다** — M_CPU를
`find_node_path(io_cpu, pe_cpu)` walk is **not** equivalent — 우회하는 그래프 경로를 선택할 수 있어 먼 큐브에 대해 예측값이 조용히
it can pick a graph path that bypasses M_CPU and silently 과소평가되며, D5 불변식을 위반하게 된다.
under-shoots the prediction for far cubes, breaking the D5
invariant.
The fanned-out sub-Transactions carry **`nbytes = 0`** for 팬아웃된 하위 Transaction은 `KernelLaunchMsg`에 대해
`KernelLaunchMsg` (control message only). Without this, **`nbytes = 0`**을 운반한다(제어 메시지에 한함). 이를 적용하지 않으면
large kernel-launch payloads would occupy fabric BW on the 큰 커널 런치 페이로드가 공유되는 첫 홉의 패브릭 대역폭을 점유하여
shared first hop and serialize the per-cube dispatch, pushing 큐브별 디스패치를 직렬화하고, 먼 M_CPU들이 `target_start_ns`를
far M_CPUs past `target_start_ns` and re-introducing the 지나가게 되어 늦은 도착 위반이 다시 발생한다.
late-arrival violation. - **M_CPU**는 이미 스탬프된 `target_start_ns`를 변경 없이 그대로 전달한다.
- **M_CPU** passes an already-stamped `target_start_ns` through 값이 없는 경우(예: M_CPU로 직접 런치하는 단위 테스트)에만 M_CPU가 큐브별
unchanged. Only when the value is absent (e.g. a direct 배리어 `env.now + max(로컬 명령 경로 레이턴시)`를 계산한다.
launch-to-M_CPU unit test) does M_CPU compute a per-cube barrier - **PE_CPU**는 `_execute_kernel`의 최상단에서 `pe_exec_start`를 기록하고
`env.now + max(local command-path latency)`. 커널 본문을 호출하기 전에 `env.timeout(target_start_ns - env.now)`
- **PE_CPU** yields `env.timeout(target_start_ns - env.now)` at the top yield한다.
of `_execute_kernel`, before recording `pe_exec_start` and invoking - `target_start_ns is None`인 경우 PE_CPU는 레거시 비동기 동작으로 빠진다
the kernel body. — 하위 호환성을 보존한다.
- When `target_start_ns is None`, PE_CPU falls through to the legacy
unsynchronized behavior — preserving backward compatibility.
IO_CPU-level stamping guarantees every PE across every targeted cube IO_CPU 레벨 스탬핑은 모든 대상 큐브의 모든 PE가 동일한 배리어 시뮬레이션
uses the same barrier sim-time, eliminating both the within-cube 시각을 사용하도록 보장하여, 큐브 내 디스패치 오프셋 아티팩트와 다중 큐브
dispatch-offset artifact *and* the cross-cube offset artifact in 런치에서의 큐브 간 오프셋 아티팩트를 모두 제거한다. 실제 하드웨어의
multi-cube launches. Models a real-hardware timed-broadcast launch 타이밍 브로드캐스트 런치(레이턴시 등화 디스패치 트리)를 모델링한다.
(latency-equalized dispatch tree).
The synchronization is internal to the engine / IO_CPU / M_CPU / PE_CPU 이 동기화는 엔진 / IO_CPU / M_CPU / PE_CPU 제어 평면 내부에서 수행된다 —
control plane — runtime API and application kernels are unchanged. runtime API와 애플리케이션 커널은 변경되지 않는다.
--- ---
## Links ## Links
- SPEC R1, R2, R7, R8 - SPEC R1, R2, R7, R8
- ADR-0007 (Runtime API boundaries) - ADR-0007 (Runtime API 경계)
- ADR-0008 (Tensor deployment) - ADR-0008 (텐서 배치)
- ADR-0013 (Verification strategy — V2 fan-out tests) - ADR-0013 (검증 전략 — V2 팬아웃 테스트)
- ADR-0015 D4 (concrete fabric path for kernel launch) - ADR-0015 D4 (커널 런치의 구체적 패브릭 경로)
@@ -1,4 +1,4 @@
# ADR-0010: Command Line Interface and Execution Semantics # ADR-0010: 명령줄 인터페이스 및 실행 시맨틱
## Status ## Status
@@ -6,126 +6,120 @@ Accepted
## Context ## Context
The `kernbench` CLI is the user-facing entry point of the simulator. It `kernbench` CLI는 시뮬레이터의 사용자 대면 진입점이다. 세 개의 서브명령을
exposes three subcommands: 노출한다:
- `run`execute a benchmark against a topology. - `run`토폴로지에 대해 벤치마크를 실행한다.
- `probe`diagnostic utility for latency / BW measurement. - `probe`레이턴시 / 대역폭 측정을 위한 진단 유틸리티.
- `web`interactive topology viewer. - `web`인터랙티브 토폴로지 뷰어.
Device enumeration is centralized in the CLI; neither the runtime API 디바이스 열거는 CLI에 중앙 집중화되어 있다. runtime API와 시뮬레이션 엔진
nor the simulation engine enumerates devices. Benchmarks remain 모두 디바이스를 열거하지 않는다. 벤치마크는 설계상 단일 디바이스를
single-device by design and accept a device identifier as input. 유지하며 입력으로 디바이스 식별자를 받는다.
## Decision ## Decision
### D1. Benchmark contract — single-device by design ### D1. 벤치마크 계약 — 설계상 단일 디바이스
- A benchmark MUST define behavior for a single device only. - 벤치마크는 반드시 단일 디바이스에 대한 동작만 정의해야 한다.
- A benchmark MUST accept a device identifier as input. - 벤치마크는 반드시 디바이스 식별자를 입력으로 받아야 한다.
- Benchmarks MUST NOT enumerate or loop over multiple devices. - 벤치마크는 다중 디바이스를 열거하거나 루프해서는 안 된다.
Multi-device execution is the CLI's concern (D3), not the benchmark's. 다중 디바이스 실행은 벤치마크의 관심사가 아니라 CLI의 관심사이다(D3).
### D2. `kernbench run` — benchmark execution ### D2. `kernbench run` — 벤치마크 실행
Required arguments: 필수 인자:
- `--topology <path>`: topology YAML file path. Loaded via - `--topology <path>`: 토폴로지 YAML 파일 경로. `resolve_topology()`
`resolve_topology()`. 통해 로드된다.
- `--bench <name>`: benchmark name. Resolved via - `--bench <name>`: 벤치마크 이름. `benches.loader.resolve_bench()`
`benches.loader.resolve_bench()`. 통해 해석된다.
Optional arguments: 선택 인자:
- `--device <selector>` (default: `all`): - `--device <selector>` (기본값: `all`):
- `all`run once per discovered SIP (see D3). - `all`발견된 SIP마다 한 번씩 실행한다(D3 참고).
- `sip:<N>`run only on SIP N. - `sip:<N>`SIP N에서만 실행한다.
- Parsed via `resolve_device()`. - `resolve_device()`를 통해 파싱된다.
- `--verify-data` (default: off) — enable Phase 2 data verification - `--verify-data` (기본값: off) — Phase 2 데이터 검증을 활성화한다
(see ADR-0020). When set, `engine_factory` constructs the engine (ADR-0020 참고). 설정되면 `engine_factory`가 엔진을
with `enable_data=True`. After the benchmark runs, a diagnostic `enable_data=True`로 구성한다. 벤치마크 실행 후, 기록된 op들의 진단
summary of recorded ops is printed. 요약이 출력된다.
Each invocation runs the benchmark once within a single simulation 각 호출은 단일 시뮬레이션 인스턴스 내에서 벤치마크를 한 번 실행한다.
instance.
### D3. Multi-device execution is logically parallel ### D3. 다중 디바이스 실행은 논리적으로 병렬이다
When `--device all` (or omitted) and the topology has multiple SIPs: `--device all`(또는 생략) 상태이며 토폴로지에 SIP가 여러 개일 때:
- Benchmark executions are submitted to a single simulation engine - 벤치마크 실행은 단일 시뮬레이션 엔진 인스턴스에 제출된다.
instance. - 시뮬레이션 시간 상에서 실행은 논리적으로 병렬이다.
- Executions are logically parallel in simulation time. - 디바이스 간 경합(공유 패브릭 대역폭, SIP 간 트래픽 등)이 자연스럽게
- Inter-device contention is naturally modeled (shared fabric 모델링된다.
bandwidth, cross-SIP traffic, etc.).
The CLI does NOT spawn multiple OS processes or independent CLI는 여러 OS 프로세스나 독립된 시뮬레이션 실행을 생성하지 **않는다**
simulation runs — parallelism is internal to one simulation instance. 병렬성은 단일 시뮬레이션 인스턴스 내부에서 일어난다.
### D4. `kernbench probe` — latency / BW diagnostic utility ### D4. `kernbench probe` — 레이턴시 / 대역폭 진단 유틸리티
Required argument: 필수 인자:
- `--topology <path>`: topology YAML file path. - `--topology <path>`: 토폴로지 YAML 파일 경로.
Optional argument: 선택 인자:
- `--case <name>` (default: `all`) — run a predefined traffic - `--case <name>` (기본값: `all`) — 미리 정의된 트래픽 패턴을 실행하거나,
pattern, or `all` to run every defined case. `all`로 정의된 모든 케이스를 실행한다.
Probe runs each pattern through the simulation engine and reports Probe는 시뮬레이션 엔진을 통해 각 패턴을 실행하고 케이스별로 다음을
per case: 보고한다:
- End-to-end latency (ns). - 종단 간 레이턴시(ns).
- Effective bandwidth (nbytes / total_ns). - 유효 대역폭(nbytes / total_ns).
- Bottleneck bandwidth (min edge BW along the chosen path). - 병목 대역폭(선택된 경로상의 최소 엣지 BW).
- Utilization (effective / bottleneck). - 활용률(유효 / 병목).
Probe additionally validates monotonicity invariants — for example Probe는 추가로 단조성 불변식을 검증한다 — 예를 들어 local-HBM 접근 ≤
that local-HBM access ≤ cross-PE-within-cube ≤ cross-cube ≤ 큐브 내 PE 간 ≤ 큐브 간 ≤ SIP 간 — 그리고 위반을 보고한다. Probe는
cross-SIP — and reports violations. Probe is a developer tool for 레이턴시 / 대역폭 모델을 검증하기 위한 개발자 도구이다; 벤치마크가
verifying the latency / BW model; it is not a benchmark. 아니다.
### D5. `kernbench web` — topology viewer ### D5. `kernbench web` — 토폴로지 뷰어
Optional arguments: 선택 인자:
- `--port <N>` (default: `8765`) — HTTP port. - `--port <N>` (기본값: `8765`) — HTTP 포트.
- `--no-open`do not auto-open the browser. - `--no-open`브라우저를 자동으로 열지 않는다.
Launches a local HTTP server that renders the compiled topology in 컴파일된 토폴로지를 브라우저에서 렌더링하는 로컬 HTTP 서버를 띄운다.
the browser. Distinct from the static `docs/diagrams/` artifacts: 정적인 `docs/diagrams/` 산출물과는 구별된다:
- `docs/diagrams/` files are derived at topology-compile time - `docs/diagrams/` 파일은 토폴로지 컴파일 시점에 파생된다(ADR-0006).
(ADR-0006). - `kernbench web`은 인터랙티브이다 — 팬/줌, 컴포넌트 속성 호버,
- `kernbench web` is interactive — pan/zoom, hover for component SIP / CUBE / PE 뷰 간 전환.
attributes, switch between SIP / CUBE / PE views.
### D6. Runtime API and simulation engine remain device-scoped ### D6. runtime API와 시뮬레이션 엔진은 디바이스 스코프를 유지한다
- Runtime API calls operate on one device per invocation. - runtime API 호출은 호출당 하나의 디바이스에서 동작한다.
- The simulation engine schedules all requests deterministically. - 시뮬레이션 엔진은 모든 요청을 결정론적으로 스케줄링한다.
- Neither layer enumerates devices. - 어느 레이어도 디바이스를 열거하지 않는다.
This invariant keeps each layer testable in isolation; device 이 불변식은 각 레이어를 독립적으로 테스트 가능하게 유지한다; 디바이스
enumeration and multi-device fan-out live only in the CLI's `run` 열거와 다중 디바이스 팬아웃은 오직 CLI `run` 명령에만 존재한다(D3).
command (D3).
## Consequences ## Consequences
- Benchmark authors write single-device logic; multi-device behavior - 벤치마크 작성자는 단일 디바이스 로직을 작성한다; 다중 디바이스 동작은
emerges from the CLI dispatching across SIPs. CLI가 SIP들에 걸쳐 디스패치함으로써 자연스럽게 도출된다.
- Adding a new subcommand (e.g., trace export, replay) does not - 새로운 서브명령(예: 트레이스 내보내기, 리플레이) 추가는 벤치마크나
require benchmark or runtime-API changes — the CLI is the runtime API 변경을 요구하지 않는다 — CLI가 확장 포인트이다.
extension point. - `probe``web`은 진단/시각화 도구이며 벤치마크가 아니다; 벤치마크 로더
- `probe` and `web` are diagnostic / visualization tools, not 경로를 우회한다.
benchmarks; they bypass the benchmark loader path.
## Links ## Links
- SPEC R7, R8, R9 - SPEC R7, R8, R9
- ADR-0007 (Runtime API and Simulation Engine Boundaries) - ADR-0007 (Runtime API와 시뮬레이션 엔진 경계)
- ADR-0020 (Two-pass data execution`--verify-data`) - ADR-0020 (Two-pass 데이터 실행`--verify-data`)
- ADR-0006 (Topology compilation and diagram generation — - ADR-0006 (토폴로지 컴파일과 다이어그램 생성 — `kernbench web`의 배경)
background for `kernbench web`)
@@ -1,288 +1,273 @@
# ADR-0011: Memory Addressing — PA / VA / LA Address Models # ADR-0011: 메모리 주소 지정 — PA / VA / LA 주소 모델
## Status ## Status
Accepted. Accepted.
- **VA model: currently implemented (default).** - **VA 모델: 현재 구현됨 (기본값).**
- PA model: implemented as PageFault fallback in PE_DMA. - PA 모델: PE_DMA의 PageFault fallback으로 구현됨.
- LA model: proposed, not implemented. - LA 모델: 제안됨, 미구현.
## Context ## Context
KernBench's address model evolved through three design points, each KernBench의 주소 모델은 각 단계마다 이전 단계의 한계를 해결하면서
addressing a limitation of the previous. This ADR documents all three 세 단계의 설계 지점을 거쳐 발전해 왔다. 본 ADR은 미래의 구현 작업이
in one place because future implementation work selects among them. 이 셋 중 하나를 선택해야 하므로 셋 모두를 한 곳에 기록한다.
### PA-only baseline ### PA 단독 베이스라인
Phase 0 of KernBench treated all device memory operations KernBench Phase 0는 모든 디바이스 메모리 동작(MemoryRead/MemoryWrite)을
(MemoryRead/MemoryWrite) as raw physical-address transfers. No 순수 물리 주소 전송으로 다뤘다. 호스트측 가상 주소 지정 없음, MMU/IOMMU
host-side virtual addressing, no MMU/IOMMU translation. Allocators 변환 없음. 할당기는 PA 매핑을 반환하고, DMA 요청은 PA를 직접 운반했다.
returned PA mappings; DMA requests carried PA directly.
This was sufficient for early correctness/latency work but 이는 초기 정확성·레이턴시 작업에는 충분했지만, 샤딩된 텐서에 대해
insufficient for running standard Triton kernels that use `base_addr + offset` 패턴을 사용하는 표준 Triton 커널을 실행하기에는
`base_addr + offset` patterns on sharded tensors: each PE's shard 부족했다. 각 PE의 샤드는 서로 다른 PA를 갖지만, 커널은 offset을 계산하기
has a different PA, but the kernel needs a single contiguous address 위해 연속된 단일 주소 공간이 필요하기 때문이다.
space to compute offsets.
### Why VA/MMU (current default) ### VA/MMU를 채택한 이유 (현재 기본값)
A realistic system uses host-side virtual addressing and an 현실적인 시스템은 호스트측 가상 주소 지정과 DMA를 위한 MMU/IOMMU 스타일
MMU/IOMMU-style translation path for DMA: the host allocates physical 변환 경로를 사용한다. 호스트는 PE 수준에서 물리 메모리를 할당하고,
memory at PE level, maps it into a virtual address space, installs 그것을 가상 주소 공간에 매핑하여 매핑을 설치한 뒤, DMA 요청은 가상
mappings, and DMA requests use virtual addresses that are translated 주소를 사용하며 그것이 물리 주소로 변환된다.
to physical addresses.
Adopting this model lets kernels use `base_addr + offset` over a 이 모델을 채택하면 커널이 연속된 VA 범위에 대해 `base_addr + offset`
contiguous VA range while the device-side MMU translates each access 사용할 수 있고, 디바이스측 MMU가 각 접근을 적절한 PA로 변환한다.
to the appropriate PA.
### Why LA/BAAW (proposed) ### LA/BAAW를 제안한 이유
VA/MMU treats HBM as a single backing space. KernBench needs to VA/MMU는 HBM을 단일 backing 공간으로 다룬다. KernBench는 HBM이 병렬로
explore architectures where HBM is composed of multiple pseudo 여러 pseudo channel로 구성된 아키텍처를 탐색해야 한다:
channels in parallel:
- CUBE's HBM has 32 or 64 pseudo channels. - CUBE HBM 32 또는 64개의 pseudo channel을 갖는다.
- In a PE-Local-HBM model, each PE is assigned N pseudo channels - PE-Local-HBM 모델에서 각 PE에는 N개의 pseudo channel이 할당된다
(N = `hbm_pseudo_channels / pes_per_cube`). (N = `hbm_pseudo_channels / pes_per_cube`).
- Per-channel BW (e.g. 32 GB/s) determines aggregate PE BW - 채널당 대역폭(예: 32 GB/s)이 PE의 총 대역폭을 결정한다
(N × per-channel). (N × 채널당).
Two channel-mapping modes need to be modelable: 두 가지 채널 매핑 모드를 모델링할 수 있어야 한다:
- **1:1 mode** — one logical access → N per-channel requests. - **1:1 모드** — 하나의 논리 접근 → N개의 채널별 요청.
Precise per-channel BW contention modelling. 채널별 대역폭 경쟁을 정밀하게 모델링.
- **n:1 mode (default)** — one logical access → one aggregated - **n:1 모드 (기본값)** — 하나의 논리 접근 → 하나의 집계 요청.
request. Channels are assumed to interleave; aggregated BW model. 채널들이 interleave된다고 가정; 집계된 대역폭 모델.
VA's `tl.load(va_ptr)` produces a single DMA request to a single VA `tl.load(va_ptr)`은 하나의 목표에 대한 하나의 DMA 요청을 생성한다.
target. Decomposing that into per-channel requests inside PE_DMA 이를 PE_DMA 내부에서 채널별 요청으로 분해하려면 주소 계층이 채널을
requires the address layer to be aware of channels. This is the 인지해야 한다. 이것이 BAAW(Logical-to-Physical Mapping Unit)를 가진
role of the LA (Logical Address) abstraction with BAAW LA(Logical Address) 추상화의 역할이다.
(Logical-to-Physical Mapping Unit).
Core requirements driving the LA design: LA 설계를 이끄는 핵심 요구사항:
- PE_DMA → HBM_CTRL effective bandwidth semantics must be identical - PE_DMA → HBM_CTRL 유효 대역폭 시맨틱이 두 모드에서 동일해야 한다
in both modes (only request shape and resource model differ). (요청 형태와 자원 모델만 다름).
- Kernel programming model is unchanged — physical channel - 커널 프로그래밍 모델은 변경되지 않는다 — 물리 채널 정보는 커널 코드에
information is never exposed to kernel code. 절대 노출되지 않는다.
- Mode switch is a topology-level configuration. - 모드 전환은 토폴로지 수준의 설정이다.
### Design space summary ### 설계 공간 요약
| Model | Status | Key idea | | 모델 | 상태 | 핵심 아이디어 |
|-------|--------|----------| |------|------|--------------|
| PA | fallback (implemented) | Direct physical addressing, no translation | | PA | fallback (구현됨) | 직접 물리 주소 지정, 변환 없음 |
| VA | current default (implemented) | Per-tensor contiguous VA range; MMU translates per access | | VA | 현재 기본값 (구현됨) | 텐서별 연속 VA 범위; MMU가 접근별로 변환 |
| LA | proposed | LA + BAAW resolves to (PA, channel); supports 1:1 and n:1 channel mapping modes | | LA | 제안됨 | LA + BAAW가 (PA, 채널)로 해석; 1:1 n:1 채널 매핑 모드 지원 |
--- ---
## Decision ## Decision
This ADR defines three address models. At any given time the system 본 ADR은 세 개의 주소 모델을 정의한다. 어느 시점에도 시스템은 정확히
operates in exactly one model. Selection is topology- / configuration- 한 모델로 동작한다. 선택은 토폴로지·설정 주도이며, 단일 시뮬레이션 실행
driven; coexistence within one simulation run is not required. 내에서의 공존은 요구되지 않는다.
--- ---
### Address Model: PA (Physical Address) — fallback ### 주소 모델: PA (물리 주소) — fallback
#### D-PA1. PA-only semantics #### D-PA1. PA 단독 시맨틱
- All device memory accesses (MemoryRead/MemoryWrite) operate on - 모든 디바이스 메모리 접근(MemoryRead/MemoryWrite)은 디바이스 물리 주소(PA)와
device physical addresses (PA) plus size. 크기에 대해 동작한다.
- PA-only mode remains functional via the PageFault fallback path in - PA 단독 모드는 PE_DMA의 PageFault fallback 경로를 통해 여전히 동작한다.
PE_DMA: if a DMA src/dst address has no MMU mapping, PE_DMA treats DMA src/dst 주소에 MMU 매핑이 없으면 PE_DMA는 그 값을 PA로 직접 다룬다.
the value as a PA directly.
#### D-PA2. Allocation produces PA mappings #### D-PA2. 할당은 PA 매핑을 생성한다
Device allocation selects PE-local memory regions and returns PA 디바이스 할당은 PE 로컬 메모리 영역을 선택하고 커널 실행 및 DMA 요청
mappings sufficient to execute kernels and issue DMA requests. 발행에 충분한 PA 매핑을 반환한다.
PA model is retained primarily for backward compatibility with PA-only PA 모델은 주로 PA 단독 테스트와의 하위 호환성을 위해, 그리고 VA / LA
tests and as the underlying physical layer that VA / LA models resolve 모델이 해석되어 들어가는 기저 물리 계층으로 유지된다.
into.
--- ---
### Address Model: VA (Virtual Address with MMU) — current default ### 주소 모델: VA (MMU를 동반한 가상 주소) — 현재 기본값
#### D-VA1. Virtual Address Model #### D-VA1. 가상 주소 모델
- Each tensor gets a single contiguous VA range (`TensorHandle.va_base`). - 각 텐서는 하나의 연속된 VA 범위(`TensorHandle.va_base`)를 가진다.
- `TensorShard` does NOT carry a `va` field — shard VA is derived as - `TensorShard``va` 필드를 가지지 **않는다** — 샤드 VA는
`va_base + offset_bytes`. `va_base + offset_bytes`로 유도된다.
- Kernels receive `va_base` as their pointer argument (via - 커널은 포인터 인수로 `va_base`를 받는다(`TensorArg.va_base` 경유).
`TensorArg.va_base`). - `DmaReadCmd.src_addr``DmaWriteCmd.dst_addr`는 VA(PA가 아님)를 운반한다.
- `DmaReadCmd.src_addr` and `DmaWriteCmd.dst_addr` carry VA (not PA).
#### D-VA2. PE_MMU Component #### D-VA2. PE_MMU 컴포넌트
- Hybrid design: SimPy component (inbox for `MmuMapMsg`) + utility - 하이브리드 설계: SimPy 컴포넌트(`MmuMapMsg`용 inbox) + 유틸리티
(synchronous `translate()` called by PE_DMA). (PE_DMA가 호출하는 동기식 `translate()`).
- Page-aligned dict lookup for O(1) VA → PA translation. - 페이지 정렬 dict 조회로 O(1) VA → PA 변환.
- `tlb_overhead_ns` configurable per-access latency. - `tlb_overhead_ns`로 접근당 레이턴시 설정 가능.
- PageFault fallback: if VA has no mapping, PE_DMA treats it as PA - PageFault fallback: VA에 매핑이 없으면 PE_DMA가 그것을 PA로 직접
directly (preserves PA model for backward compatibility). 다룬다 (PA 모델과의 하위 호환성 유지).
#### D-VA3. Mapping Installation #### D-VA3. 매핑 설치
- `MmuMapMsg` traverses the fabric: Host → PCIE_EP → IO_CPU (cube - `MmuMapMsg`는 패브릭을 순회한다: Host → PCIE_EP → IO_CPU (큐브 fan-out)
fan-out) → M_CPU (PE fan-out) → NOC → PE_MMU. Latency is measured → M_CPU (PE fan-out) → NOC → PE_MMU. 레이턴시는 end-to-end로 측정된다.
end-to-end. - `MmuMapMsg.target_sips`는 SIP 수준 라우팅을 제어하여 복제 텐서의
- `MmuMapMsg.target_sips` controls SIP-level routing to prevent cross-SIP 매핑 오염을 방지한다.
cross-SIP mapping contamination for replicated tensors. - `DPPolicy.cube`에 기반한 매핑 전략:
- Mapping strategy based on `DPPolicy.cube`: - **Replicate** (`cube="replicate"`): (sip, cube)별 로컬 매핑만.
- **Replicate** (`cube="replicate"`): per-(sip, cube) local mapping 각 큐브의 PE들은 자신의 로컬 PA만 본다. cross-cube 매핑은 설치되지
only. Each cube's PEs see only their local PA. No cross-cube 않는다.
mapping installed. - **Sharded** (`cube="column_wise"` 등): 모든 샤드 매핑을 모든 대상
- **Sharded** (`cube="column_wise"`, etc.): broadcast all shard 큐브로 브로드캐스트. cross-PE 및 cross-cube DMA를 가능하게 한다.
mappings to all target cubes. Enables cross-PE and cross-cube
DMA.
#### D-VA4. Tensor Lifecycle #### D-VA4. 텐서 라이프사이클
- `del tensor` triggers automatic cleanup via `Tensor.__del__` + - `del tensor``Tensor.__del__` + `RuntimeContext`에 대한 `weakref`
`weakref` to `RuntimeContext`. Sends `MmuUnmapMsg` through fabric, 통해 자동 정리를 트리거한다. 패브릭을 통해 `MmuUnmapMsg`를 보내고
returns VA and PA space. VA와 PA 공간을 반환한다.
- `with RuntimeContext(...) as ctx:` provides scope-based bulk cleanup. - `with RuntimeContext(...) as ctx:`는 스코프 기반 일괄 정리를 제공한다.
- `RuntimeContext._tensors` uses `weakref.ref` to avoid preventing GC. - `RuntimeContext._tensors`는 GC 방지를 피하기 위해 `weakref.ref`를 사용.
- `PEMemAllocator` uses free-list with coalescing (not bump allocator). - `PEMemAllocator`는 coalescing이 있는 free-list를 사용한다(bump allocator 아님).
- `VirtualAllocator` uses free-list with coalescing for VA space. - `VirtualAllocator`는 VA 공간에 대해 coalescing이 있는 free-list를 사용한다.
#### D-VA5. Allocators #### D-VA5. 할당기
- `VirtualAllocator`: device-wide VA space, page-aligned alloc/free - `VirtualAllocator`: 디바이스 전체의 VA 공간, coalescing을 동반한
with coalescing. 페이지 정렬 alloc/free.
- `PEMemAllocator`: per-PE HBM/TCM, free-list based alloc/free with - `PEMemAllocator`: PE HBM/TCM, coalescing을 동반한 free-list 기반
coalescing. alloc/free.
- Page size configurable via `topology.yaml` `pe_mmu` attrs - 페이지 크기는 `topology.yaml` `pe_mmu` attrs로 설정 가능
(default 4096). (기본 4096).
#### Consequences (VA model) #### Consequences (VA 모델)
- Triton kernels use `base_addr + offset` patterns naturally on - Triton 커널은 샤딩된 텐서에 대해 `base_addr + offset` 패턴을 자연스럽게
sharded tensors. 사용한다.
- All latency remains explicit via graph traversal, including MMU - 모든 레이턴시는 MMU 매핑 설치와 접근당 TLB 오버헤드를 포함하여
mapping installation and per-access TLB overhead. 그래프 순회를 통해 명시적이다.
- PA-only mode retained as fallback (PageFault → treat as PA). - PA 단독 모드는 fallback으로 유지된다 (PageFault → PA로 처리).
- IPCQ and other fixed-address resources bypass MMU (use PA directly). - IPCQ와 그 외 고정 주소 자원은 MMU를 우회한다 (PA 직접 사용).
--- ---
### Address Model: LA (Logical Address with BAAW) — proposed ### 주소 모델: LA (BAAW를 동반한 논리 주소) — 제안됨
LA replaces VA when channel-level HBM modelling is required. LA는 채널 수준 HBM 모델링이 필요할 때 VA를 대체한다.
Adopting this model removes the VA/MMU infrastructure (D-LA1 lists the 이 모델을 채택하면 VA/MMU 인프라가 제거된다 (D-LA1이 제거되는 산출물을
removed artifacts). Coexistence with VA in the same run is not a goal. 나열한다). 동일 실행 내에서 VA와의 공존은 목표가 아니다.
#### D-LA1. LA introduction — replaces VA infrastructure #### D-LA1. LA 도입 — VA 인프라 대체
LA is the sole address space used by kernel code (`tl.load`, LA는 커널 코드(`tl.load`, `tl.store`, `tl.composite`)가 사용하는
`tl.store`, `tl.composite`). Properties: 유일한 주소 공간이다. 속성:
- Can map a Tensor to a contiguous logical space (like VA). - Tensor를 연속된 논리 공간에 매핑할 수 있다 (VA처럼).
- Expresses `(logical buffer + offset)`. - `(논리 버퍼 + offset)`을 표현한다.
- Does NOT contain physical channel information directly. - 물리 채널 정보를 직접 포함하지 **않는다**.
- Stays as an intermediate abstraction until physical resolution. - 물리적 해석이 일어나기 전까지는 중간 추상화로 유지된다.
LA address space: LA 주소 공간:
| Item | Value | | 항목 | 값 |
|------|-------| |------|-------|
| LA start | `0x1_0000_0000` (4 GB, preserves former VA start) | | LA 시작 | `0x1_0000_0000` (4 GB, 이전 VA 시작과 동일) |
| LA space size | 64 GB per PE | | LA 공간 크기 | PE당 64 GB |
| Alignment unit | segment (see D-LA3) | | 정렬 단위 | segment (D-LA3 참조) |
LA is PE-local: different PEs may use the same LA value; BAAW segment LA는 PE 로컬이다: 서로 다른 PE가 동일한 LA 값을 사용할 수 있지만,
tables differ → they resolve to different PAs. BAAW segment 테이블이 다르므로 서로 다른 PA로 해석된다.
VA infrastructure removed when LA is adopted: LA가 채택되면 제거되는 VA 인프라:
| Removed | Replacement | | 제거 | 대체 |
|---------|-------------| |---------|-------------|
| `policy/address/va_allocator.py` (VirtualAllocator) | LA allocator (same free-list approach, renamed) | | `policy/address/va_allocator.py` (VirtualAllocator) | LA allocator (동일한 free-list 접근, 이름 변경) |
| `policy/address/pe_mmu.py` (PeMMU) | BAAW segment table (inside PE_DMA) | | `policy/address/pe_mmu.py` (PeMMU) | BAAW segment 테이블 (PE_DMA 내부) |
| `components/builtin/pe_mmu.py` (PeMmuComponent) | Removed — BAAW is internal PE_DMA logic, not a separate component | | `components/builtin/pe_mmu.py` (PeMmuComponent) | 제거 — BAAW는 별도 컴포넌트가 아니라 PE_DMA 내부 로직 |
| `runtime_api/kernel.py`: `MmuMapMsg`, `MmuUnmapMsg` | `BaawSegmentInstallMsg` | | `runtime_api/kernel.py`: `MmuMapMsg`, `MmuUnmapMsg` | `BaawSegmentInstallMsg` |
| `runtime_api/context.py`: VA alloc + MMU install | LA alloc + BAAW segment install | | `runtime_api/context.py`: VA alloc + MMU install | LA alloc + BAAW segment install |
| `runtime_api/tensor.py`: `va_base` | `la_base` | | `runtime_api/tensor.py`: `va_base` | `la_base` |
| `topology.yaml`: `pe_mmu` component entry | Removed | | `topology.yaml`: `pe_mmu` 컴포넌트 entry | 제거 |
#### D-LA2. Mapping mode setting #### D-LA2. 매핑 모드 설정
Topology-level (cube) configuration: 토폴로지 수준(큐브) 설정:
```yaml ```yaml
cube: cube:
memory_map: memory_map:
hbm_mapping_mode: n_to_one # one_to_one | n_to_one hbm_mapping_mode: n_to_one # one_to_one | n_to_one
hbm_pseudo_channels: 64 # total pseudo channel count hbm_pseudo_channels: 64 # 전체 pseudo channel
hbm_channels_per_pe: 8 # per-PE local channel count hbm_channels_per_pe: 8 # PE당 로컬 채널 수
hbm_channel_bw_gbs: 32.0 # per-channel bandwidth hbm_channel_bw_gbs: 32.0 # 채널당 대역폭
``` ```
Consumed by the graph compiler (topology builder) and BAAW 그래프 컴파일러(토폴로지 빌더)와 BAAW 초기화가 이 값을 소비한다.
initialisation.
#### D-LA3. Segment and BAAW #### D-LA3. Segment BAAW
Segment partitions the LA space; each segment maps to a specific HBM Segment는 LA 공간을 분할한다. 각 segment는 특정 HBM 채널 또는 채널
channel or channel group. Created at tensor deploy time by the runtime 그룹에 매핑된다. 텐서 deploy 시점에 런타임 할당기가 생성한다. BAAW는
allocator. BAAW resolves LA → physical request(s) using the segment segment 테이블을 사용하여 LA → 물리 요청(들)로 해석한다.
table.
```python ```python
@dataclass @dataclass
class BaawSegment: class BaawSegment:
la_base: int # segment start LA la_base: int # segment 시작 LA
la_size: int # segment size (bytes) la_size: int # segment 크기 (bytes)
mode: str # "one_to_one" | "n_to_one" mode: str # "one_to_one" | "n_to_one"
# 1:1 mode fields # 1:1 모드 필드
channel_count: int # channels assigned to this segment (e.g. 8) channel_count: int # 이 segment에 할당된 채널 수 (예: 8)
pa_bases: list[int] # per-channel PA bases (len = channel_count) pa_bases: list[int] # 채널별 PA base (len = channel_count)
channel_ids: list[int] # per-channel logical IDs (e.g. [0..7]) channel_ids: list[int] # 채널별 논리 ID (예: [0..7])
channel_size: int # per-channel size (la_size // channel_count) channel_size: int # 채널당 크기 (la_size // channel_count)
# n:1 mode fields # n:1 모드 필드
agg_pa_base: int # aggregated PA base agg_pa_base: int # 집계 PA base
agg_node_id: str # aggregated router node_id agg_node_id: str # 집계 라우터 node_id
``` ```
Segment lifecycle: Segment 라이프사이클:
1. **Allocate** (tensor deploy): RuntimeContext allocates LA from LA 1. **할당** (텐서 deploy): RuntimeContext가 LA allocator에서 LA
allocator. PEMemAllocator allocates per-channel PA (1:1) or 할당한다. PEMemAllocator가 채널별 PA(1:1) 또는 집계 PA(n:1)를
aggregated PA (n:1). `BaawSegmentInstallMsg` registers the segment 할당한다. `BaawSegmentInstallMsg`가 segment를 PE_DMA에 등록한다.
with PE_DMA. 2. **사용** (커널 실행): 커널 `tl.load(la_ptr)` → `DmaReadCmd
2. **Use** (kernel run): kernel `tl.load(la_ptr)` → `DmaReadCmd (src_addr=LA)`. PE_DMA의 BAAW 프론트엔드가 segment를 조회하여
(src_addr=LA)`. PE_DMA's BAAW front-end looks up the segment and PA(들)로 변환한다.
converts to PA(s). 3. **해제** (텐서 free): segment가 테이블에서 제거되고 LA와 PA가
3. **Free** (tensor free): segment removed from table; LA and PA 반환된다.
returned.
#### D-LA4. BAAW resolution logic #### D-LA4. BAAW 해석 로직
BAAW is a front-end stage inside PE_DMA, not a separate SimPy BAAW는 PE_DMA 내부의 프론트엔드 단계이며, 별도의 SimPy 컴포넌트가 아니다.
component. Synchronous address-resolution logic executed at the start PE_DMA의 `handle_command()` 시작 시점에 실행되는 동기식 주소 해석 로직.
of PE_DMA's `handle_command()`.
Input: `(LA, nbytes)`. Output: 입력: `(LA, nbytes)`. 출력:
- **1:1 mode**: `list[PhysicalRequest]` — one per channel. - **1:1 모드**: `list[PhysicalRequest]` — 채널당 하나.
- **n:1 mode**: single `PhysicalRequest`. - **n:1 모드**: 단일 `PhysicalRequest`.
```python ```python
@dataclass @dataclass
class PhysicalRequest: class PhysicalRequest:
pa: int # 51-bit Physical Address pa: int # 51-bit 물리 주소
nbytes: int # transfer size for this request nbytes: int # 이 요청의 전송 크기
dst_node: str # target node_id (channel router or aggregated router) dst_node: str # 대상 node_id (채널 라우터 또는 집계 라우터)
def resolve(self, la: int, nbytes: int) -> list[PhysicalRequest]: def resolve(self, la: int, nbytes: int) -> list[PhysicalRequest]:
@@ -305,65 +290,65 @@ def resolve(self, la: int, nbytes: int) -> list[PhysicalRequest]:
return requests return requests
``` ```
BAAW responsibilities: BAAW의 책임:
- Convert logical access → physical request units. - 논리 접근 → 물리 요청 단위로 변환.
- Apply mode-dependent fan-out (1:1) or pass-through (n:1). - 모드에 따라 fan-out(1:1) 또는 pass-through(n:1) 적용.
- Compute PA and target node. - PA와 대상 노드 계산.
BAAW non-responsibilities: BAAW가 하지 않는 것:
- Performing actual data movement. - 실제 데이터 이동 수행.
- Executing NOC routing. - NOC 라우팅 실행.
- Simulating bandwidth occupation (downstream components' job). - 대역폭 점유 시뮬레이션 (하위 컴포넌트의 역할).
BAAW output is directly usable by the simulator's routing and resource BAAW의 출력은 추가적인 주소 디코딩 없이 시뮬레이터의 라우팅·자원
model without additional address decoding. 모델에서 바로 사용 가능하다.
#### D-LA5. PE_DMA `handle_command()` change #### D-LA5. PE_DMA `handle_command()` 변경
Current (VA-based) flow: 현재(VA 기반) 흐름:
``` ```
DmaReadCmd.src_addr (VA) DmaReadCmd.src_addr (VA)
→ MMU.translate(VA) → PA → MMU.translate(VA) → PA
→ PhysAddr.decode(PA) → PhysAddr object → PhysAddr.decode(PA) → PhysAddr 객체
→ resolver.resolve(PhysAddr) → dst_node_id → resolver.resolve(PhysAddr) → dst_node_id
→ router.find_path(pe_prefix, dst_node_id) → path → router.find_path(pe_prefix, dst_node_id) → path
→ 1 sub-Transaction → fabric inject → 1 sub-Transaction → 패브릭 주입
``` ```
LA-based flow: LA 기반 흐름:
``` ```
DmaReadCmd.src_addr (LA) DmaReadCmd.src_addr (LA)
→ BAAW.resolve(LA, nbytes) → list[PhysicalRequest] → BAAW.resolve(LA, nbytes) → list[PhysicalRequest]
for each PhysicalRequest: PhysicalRequest에 대해:
→ router.find_path(pe_prefix, req.dst_node) → path → router.find_path(pe_prefix, req.dst_node) → path
→ compute_drain_ns(path, req.nbytes) → drain → compute_drain_ns(path, req.nbytes) → drain
→ sub-Transaction → fabric inject → sub-Transaction → 패브릭 주입
await all sub-Transactions 모든 sub-Transaction 대기
→ pe_txn.done.succeed() → pe_txn.done.succeed()
``` ```
Key changes: 주요 변경:
- MMU reference removed → BAAW resolve. - MMU 참조 제거 → BAAW resolve.
- `PhysAddr.decode()` + `resolver.resolve()` → BAAW returns `dst_node` - `PhysAddr.decode()` + `resolver.resolve()` → BAAW `dst_node`
directly. 직접 반환.
- 1 request → N parallel requests in 1:1 mode. - 1 요청 → 1:1 모드에서 N개의 병렬 요청.
#### D-LA6. 1:1 mode detail #### D-LA6. 1:1 모드 상세
- One logical access → N physical requests (N = `channels_per_pe`). - 하나의 논리 접근 → N개의 물리 요청 (N = `channels_per_pe`).
- N = `hbm_pseudo_channels / pes_per_cube`. - N = `hbm_pseudo_channels / pes_per_cube`.
- Each request: fully-resolved 51-bit PA, targets a specific channel - 각 요청: 완전히 해석된 51-bit PA, 특정 채널 라우터를 대상으로 함
router (`{pe_prefix}.ch_r{channel_id}`). (`{pe_prefix}.ch_r{channel_id}`).
- Per-channel link models BW contention. - 채널별 링크가 대역폭 경쟁을 모델링.
- PE_DMA injects N sub-transactions concurrently. - PE_DMA가 N개의 sub-transaction을 동시에 주입.
Example: `hbm_pseudo_channels=64`, `pes_per_cube=8` → `channels_per_pe=8`. : `hbm_pseudo_channels=64`, `pes_per_cube=8` → `channels_per_pe=8`.
PE0 owns ch0-7. PE0 ch0-7을 소유.
```text ```text
Tensor A (4 KB) → LA 0x1_0000_0000, size=4096 bytes Tensor A (4 KB) → LA 0x1_0000_0000, size=4096 bytes
@@ -375,32 +360,32 @@ BAAW segment: {
channel_size: 512, channel_size: 512,
} }
BAAW resolve result (8 requests): BAAW resolve 결과 (8 요청):
→ PhysicalRequest(pa=PA_ch0, nbytes=512, dst_node="sip0.cube0.pe0.ch_r0") → PhysicalRequest(pa=PA_ch0, nbytes=512, dst_node="sip0.cube0.pe0.ch_r0")
→ PhysicalRequest(pa=PA_ch1, nbytes=512, dst_node="sip0.cube0.pe0.ch_r1") → PhysicalRequest(pa=PA_ch1, nbytes=512, dst_node="sip0.cube0.pe0.ch_r1")
→ ... → ...
→ PhysicalRequest(pa=PA_ch7, nbytes=512, dst_node="sip0.cube0.pe0.ch_r7") → PhysicalRequest(pa=PA_ch7, nbytes=512, dst_node="sip0.cube0.pe0.ch_r7")
PE_DMA: 8 sub-transactions parallel inject PE_DMA: 8 sub-transaction 병렬 주입
per-channel router → hbm_ctrl link (channel_bw_gbs) per channel 채널별 라우터 → hbm_ctrl 링크 (channel_bw_gbs) per channel
Total effective BW = 8 × channel_bw_gbs 전체 유효 BW = 8 × channel_bw_gbs
``` ```
Other N values: 다른 N 값:
- `hbm_pseudo_channels=32`, `pes_per_cube=8` → `channels_per_pe=4`, - `hbm_pseudo_channels=32`, `pes_per_cube=8` → `channels_per_pe=4`,
4 requests 4 요청
- `hbm_pseudo_channels=64`, `pes_per_cube=4` → `channels_per_pe=16`, - `hbm_pseudo_channels=64`, `pes_per_cube=4` → `channels_per_pe=16`,
16 requests 16 요청
#### D-LA7. n:1 mode detail #### D-LA7. n:1 모드 상세
- One logical access → one aggregated request. - 하나의 논리 접근 → 하나의 집계 요청.
- Target: aggregated router → hbm_ctrl (see ADR-0017 D8). - 대상: 집계 라우터 → hbm_ctrl (ADR-0017 D8 참조).
- Aggregated link BW = `channels_per_pe × channel_bw_gbs` - 집계 링크 BW = `channels_per_pe × channel_bw_gbs`
(e.g. 8 × 32 = 256 GB/s). (예: 8 × 32 = 256 GB/s).
- Single queue / resource for modelling. - 모델링을 위한 단일 큐 / 자원.
- No per-channel PA decomposition. - 채널별 PA 분해 없음.
```text ```text
Tensor A (4 KB) → LA 0x1_0000_0000, size=4096 bytes Tensor A (4 KB) → LA 0x1_0000_0000, size=4096 bytes
@@ -411,111 +396,108 @@ BAAW segment: {
agg_node_id: "sip0.cube0.pe0.agg_router", agg_node_id: "sip0.cube0.pe0.agg_router",
} }
BAAW resolve result: BAAW resolve 결과:
→ PhysicalRequest(pa=PA_agg, nbytes=4096, dst_node="sip0.cube0.pe0.agg_router") → PhysicalRequest(pa=PA_agg, nbytes=4096, dst_node="sip0.cube0.pe0.agg_router")
PE_DMA: 1 sub-transaction PE_DMA: 1 sub-transaction
aggregated router → hbm_ctrl link (256 GB/s) 집계 라우터 → hbm_ctrl 링크 (256 GB/s)
``` ```
#### D-LA8. Kernel model preserved #### D-LA8. 커널 모델 보존
- Kernel still issues single memory ops (`tl.load`, `tl.store`, - 커널은 여전히 단일 메모리 op(`tl.load`, `tl.store`,
`tl.composite`). `tl.composite`)을 발행한다.
- LA is the address scheme exposed to kernel code. - LA가 커널 코드에 노출되는 주소 체계이다.
- Channel decomposition / aggregation happens inside PE_DMA's BAAW. - 채널 분해·집계는 PE_DMA BAAW 내부에서 일어난다.
- Kernel code never sees physical channel information. - 커널 코드는 물리 채널 정보를 절대 보지 않는다.
#### Consequences (LA model, proposed) #### Consequences (LA 모델, 제안됨)
Positive: 긍정적:
- 1:1 vs n:1 semantics live in one place (BAAW). - 1:1 vs n:1 시맨틱이 한 곳(BAAW)에 모인다.
- Kernel abstraction preserved — no kernel code changes. - 커널 추상화 보존 — 커널 코드 변경 없음.
- Topology-based policy control (mode switch via yaml). - 토폴로지 기반 정책 제어 (yaml로 모드 전환).
- Improved simulation-model consistency and debuggability. - 시뮬레이션 모델의 정합성·디버깅 가능성 향상.
- Segment-based mapping is simpler than page tables; lower overhead. - Segment 기반 매핑이 페이지 테이블보다 단순하며 오버헤드도 적다.
Negative: 부정적:
- Full VA/MMU code refactor required. - 전체 VA/MMU 코드 리팩터가 필요하다.
- Request-generation path more complex (N requests in 1:1 mode). - 요청 생성 경로가 더 복잡 (1:1 모드에서 N 요청).
- Reduced per-channel visibility in n:1 mode. - n:1 모드에서 채널별 가시성 감소.
- VA-related tests need rewriting. - VA 관련 테스트 재작성 필요.
--- ---
## Migration Path ## Migration Path
- **PA → VA** was an extension. PA mode is retained as the PageFault - **PA → VA**는 확장이었다. PA 모드는 PE_DMA 내부의 PageFault fallback으로
fallback inside PE_DMA. Switching does not require removing PA 유지된다. 전환은 PA 코드 제거를 요구하지 않는다.
code. - **VA → LA**는, 채택될 경우, 공존이 아닌 대체이다. VA 인프라 제거
- **VA → LA**, if adopted, is a replacement, not coexistence. See 목록은 D-LA1 참조. PA fallback은 테스트를 위해 PE_DMA 내부에 직교적으로
D-LA1 for the VA infrastructure removal list. PA fallback inside 유지될 수 있다.
PE_DMA may be retained orthogonally for tests.
## Alternatives Considered (LA model) ## Alternatives Considered (LA 모델)
1. **Keep VA + fan-out in MMU**: MMU returns per-channel PAs. 1. **VA 유지 + MMU에서 fan-out**: MMU가 채널별 PA를 반환한다.
Rejected: MMU's role would grow beyond translation to request 기각: MMU의 역할이 변환을 넘어 요청 분해까지 확장되며, 집계(n:1)를
decomposition; aggregation (n:1) becomes awkward to express. 표현하기 어색해진다.
2. **Channel-aware kernel API**: kernels call per-channel load/store 2. **채널 인지 커널 API**: 커널이 채널별 load/store를 직접 호출한다.
directly. Rejected: abstraction leakage, portability loss, all 기각: 추상화 누출, 이식성 손실, 모든 벤치마크 재작성 필요.
benchmarks need rewriting. 3. **항상 PA (LA 없음)**: 런타임이 커널에 채널별 PA를 직접 전달한다.
3. **Always PA (no LA)**: runtime passes per-channel PA to kernel 기각: 집계와 양립 불가; 변환 시점이 불명확; 채널 정보가 커널로 누출.
directly. Rejected: incompatible with aggregation; conversion
timing unclear; channel info leaks to kernel.
## Test Requirements ## Test Requirements
### VA model (current, regression) ### VA 모델 (현재, regression)
- Cross-PE / cross-cube DMA paths over installed mappings. - 설치된 매핑을 따라 cross-PE / cross-cube DMA 경로.
- `MmuMapMsg` / `MmuUnmapMsg` fabric traversal with measured latency. - 측정된 레이턴시를 동반한 `MmuMapMsg` / `MmuUnmapMsg`의 패브릭 순회.
- TLB-overhead-per-access timing. - 접근당 TLB 오버헤드 타이밍.
- PageFault fallback path preserves PA-only behaviour. - PageFault fallback 경로가 PA 단독 동작을 보존하는지.
### LA model (when implemented) ### LA 모델 (구현 시)
- 1:1 mode: same logical access → N per-channel requests. - 1:1 모드: 동일 논리 접근 → N개의 채널별 요청.
- n:1 mode: same logical access → 1 aggregated request. - n:1 모드: 동일 논리 접근 → 1개의 집계 요청.
- Bandwidth equivalence between modes for identical workload. - 동일 워크로드에 대해 두 모드 사이의 대역폭 동치.
- 1:1 mode: per-channel contention modelled correctly. - 1:1 모드: 채널별 경쟁이 올바르게 모델링됨.
- n:1 mode: aggregated bandwidth correctly reflected. - n:1 모드: 집계된 대역폭이 올바르게 반영됨.
- Kernel code unchanged across mode switch. - 모드 전환에 걸쳐 커널 코드가 변경되지 않음.
- BAAW segment install / uninstall correctness. - BAAW segment install / uninstall 정확성.
- Multiple tensors in distinct segments do not collide. - 별개 segment 안의 여러 텐서가 충돌하지 않음.
## Implementation Order (LA, when scheduled) ## Implementation Order (LA, 일정 잡힐 때)
1. LA type (`policy/address/la_allocator.py`). 1. LA 타입 (`policy/address/la_allocator.py`).
2. BAAW segment table (`policy/address/baaw.py`). 2. BAAW segment 테이블 (`policy/address/baaw.py`).
3. `BaawSegmentInstallMsg` (`runtime_api/kernel.py`). 3. `BaawSegmentInstallMsg` (`runtime_api/kernel.py`).
4. PE_DMA BAAW integration (`components/builtin/pe_dma.py` 4. PE_DMA BAAW 통합 (`components/builtin/pe_dma.py`
`handle_command()`). `handle_command()`).
5. RuntimeContext: LA alloc + segment install 5. RuntimeContext: LA alloc + segment install
(`runtime_api/context.py`). (`runtime_api/context.py`).
6. `Tensor.va_base` → `Tensor.la_base` (`runtime_api/tensor.py`). 6. `Tensor.va_base` → `Tensor.la_base` (`runtime_api/tensor.py`).
7. Remove VA/MMU code. 7. VA/MMU 코드 제거.
8. Remove `pe_mmu` from `topology.yaml`; add mapping mode settings. 8. `topology.yaml`에서 `pe_mmu` 제거; 매핑 모드 설정 추가.
9. Test migration: 9. 테스트 이전:
| Test file | Action | | 테스트 파일 | 조치 |
|-----------|--------| |-----------|--------|
| `tests/test_mmu_component.py` | Remove → BAAW segment install tests | | `tests/test_mmu_component.py` | 제거 → BAAW segment install 테스트 |
| `tests/test_mmu_fabric.py` | Remove → BAAW + fabric integration tests | | `tests/test_mmu_fabric.py` | 제거 → BAAW + 패브릭 통합 테스트 |
| `tests/test_pe_mmu.py` | Remove | | `tests/test_pe_mmu.py` | 제거 |
| `tests/test_va_allocator.py` | Replace with LA allocator tests | | `tests/test_va_allocator.py` | LA allocator 테스트로 교체 |
| `tests/test_va_integration.py` | Replace with LA + BAAW integration tests | | `tests/test_va_integration.py` | LA + BAAW 통합 테스트로 교체 |
| `tests/test_va_offset.py` | Replace with LA offset tests | | `tests/test_va_offset.py` | LA offset 테스트로 교체 |
## Links ## Links
- ADR-0007 (runtime_api vs sim_engine boundaries) - ADR-0007 (runtime_api vs sim_engine 경계)
- ADR-0008 (tensor deployment) - ADR-0008 (텐서 배포)
- ADR-0009 (kernel execution) - ADR-0009 (커널 실행)
- ADR-0014 (PE-internal execution model) - ADR-0014 (PE 내부 실행 모델)
- ADR-0015 (component port/wire model) - ADR-0015 (컴포넌트 포트/와이어 모델)
- ADR-0017 (Cube NOC and HBM connectivity — LA model topology consumer) - ADR-0017 (큐브 NOC와 HBM 연결성 — LA 모델 토폴로지 소비자)
- ADR-0013 (Verification strategy — V1 PA tagging) - ADR-0013 (검증 전략 — V1 PA 태깅)
- SPEC R2 (latency by traversal), R10 (memory addressing) - SPEC R2 (순회 기반 레이턴시), R10 (메모리 주소 지정)
@@ -1,4 +1,4 @@
# ADR-0012: Host ↔ IO_CPU Message Schema (PA-first, PE-tagged) # ADR-0012: Host ↔ IO_CPU 메시지 스키마 (PA-우선, PE-태깅)
## Status ## Status
@@ -6,63 +6,65 @@ Accepted
## Context ## Context
Phase 0 uses a PA-first memory model (ADR-0011): Phase 0은 PA-우선 메모리 모델을 사용한다(ADR-0011):
- memory operations use device physical addresses (PA) only, - 메모리 연산은 디바이스 물리 주소(PA)만 사용한다,
- VA/MMU/IOMMU is not modeled. - VA/MMU/IOMMU는 모델링하지 않는다.
The host-facing runtime API interacts with the device via the IO_CPU endpoint. 호스트 대면 runtime API는 IO_CPU 엔드포인트를 통해 디바이스와
We define stable, minimal message schemas for Host ↔ IO_CPU so that: 상호작용한다. 다음을 보장하기 위해 Host ↔ IO_CPU에 대한 안정적이고
최소한의 메시지 스키마를 정의한다:
- benchmarks remain stable, - 벤치마크는 안정적으로 유지된다,
- IO_CPU-internal fan-out/aggregation can evolve independently, - IO_CPU 내부의 팬아웃/집계는 독립적으로 진화할 수 있다,
- completion and failure propagation is deterministic. - 완료와 실패 전파는 결정론적이다.
We also require PE-tagging (A 방식): each shard explicitly carries (sip,cube,pe) 또한 PE-태깅(A 방식)을 요구한다: 각 샤드는 (sip,cube,pe)를 명시적으로
so IO_CPU can deterministically route/fan-out without relying on PA decoding. 운반하여, IO_CPU가 PA 디코딩에 의존하지 않고 결정론적으로
라우팅/팬아웃할 수 있도록 한다.
--- ---
## Decision ## Decision
### D1. Contract scope ### D1. 계약 범위
This schema is the stable contract ONLY for Host ↔ IO_CPU. 본 스키마는 오직 Host ↔ IO_CPU에 대해서만 안정적인 계약이다.
Messages beyond IO_CPU (to M_CPU, PE_CPU, schedulers, engines) are component-internal IO_CPU를 넘어선 메시지(M_CPU, PE_CPU, 스케줄러, 엔진으로 가는 것)는
and are NOT part of this host contract in Phase 0. 컴포넌트 내부 사항이며 Phase 0에서 이 호스트 계약의 일부가 아니다.
--- ---
### D2. Required message set ### D2. 필수 메시지 집합
The runtime API MUST use only these message types for Host ↔ IO_CPU: runtime API는 Host ↔ IO_CPU에 대해 오직 다음 메시지 타입만 사용해야 한다:
- MemoryWrite - MemoryWrite
- MemoryRead - MemoryRead
- KernelLaunch - KernelLaunch
All operations required by benchmarks (tensor init/copy, kernel run) MUST be expressible 벤치마크가 필요로 하는 모든 연산(텐서 초기화/복사, 커널 실행)은 이
with these messages. 메시지들로 표현 가능해야 한다.
--- ---
### D3. Common envelope (mandatory for all requests) ### D3. 공통 envelope (모든 요청에 필수)
All Host ↔ IO_CPU requests MUST include: 모든 Host ↔ IO_CPU 요청은 반드시 다음을 포함해야 한다:
- `msg_type: str` - `msg_type: str`
- `correlation_id: str` - `correlation_id: str`
- generated by the host - 호스트에서 생성
- used to match responses deterministically - 응답을 결정론적으로 매칭하는 데 사용
- `request_id: str` - `request_id: str`
- unique within a correlation_id - correlation_id 내에서 고유함
- `target_device: str` - `target_device: str`
- device identifier (e.g., "sip:0") - 디바이스 식별자(예: "sip:0")
- `timestamp_tag: str | None` (optional) - `timestamp_tag: str | None` (선택)
- debug tag only; MUST NOT affect determinism - 디버그 태그 전용; 결정성에 영향을 주어서는 안 됨
All Host ↔ IO_CPU responses MUST include: 모든 Host ↔ IO_CPU 응답은 반드시 다음을 포함해야 한다:
- `correlation_id: str` - `correlation_id: str`
- `request_id: str` - `request_id: str`
@@ -70,120 +72,123 @@ All Host ↔ IO_CPU responses MUST include:
--- ---
### D4. Completion schema (mandatory) ### D4. Completion 스키마 (필수)
`Completion` MUST have: `Completion`은 반드시 다음을 가져야 한다:
- `ok: bool` - `ok: bool`
- `error_code: str | None` - `error_code: str | None`
- `error_message: str | None` - `error_message: str | None`
Rules: 규칙:
- If `ok == true` then `error_code` and `error_message` MUST be null. - `ok == true`이면 `error_code` `error_message`는 반드시 null이어야 한다.
- If `ok == false` then `error_code` MUST be non-null. - `ok == false`이면 `error_code`는 반드시 null이 아니어야 한다.
- Completion semantics MUST be deterministic. - 완료 시맨틱은 결정론적이어야 한다.
--- ---
### D5. MemoryWrite schema (PA-first, PE-tagged) ### D5. MemoryWrite 스키마 (PA-우선, PE-태깅)
`MemoryWrite` represents a host-initiated write/initialize operation to device memory. `MemoryWrite`는 호스트에서 시작된 디바이스 메모리 쓰기/초기화 연산을
나타낸다.
Mandatory fields: 필수 필드:
- common envelope fields (D3) - 공통 envelope 필드 (D3)
- destination placement tags (A 방식): - 목적지 배치 태그 (A 방식):
- `dst_sip: int` - `dst_sip: int`
- `dst_cube: int` - `dst_cube: int`
- `dst_pe: int` - `dst_pe: int`
- `dst_pa: int` - `dst_pa: int`
- destination physical address in the destination PE's address space - 목적지 PE의 주소 공간 내 목적지 물리 주소
- `nbytes: int` - `nbytes: int`
- `src_kind: "pattern" | "host_buffer_ref"` - `src_kind: "pattern" | "host_buffer_ref"`
- Phase 0 MUST support "pattern" - Phase 0은 반드시 "pattern"을 지원해야 한다
- `pattern: Pattern | None` - `pattern: Pattern | None`
- required if `src_kind == "pattern"` - `src_kind == "pattern"`인 경우 필수
`Pattern` (Phase 0 mandatory support): `Pattern` (Phase 0 필수 지원):
- `pattern_kind: "zero" | "fill_u8" | "fill_u16" | "fill_u32" | "fill_fp16" | "fill_fp32"` - `pattern_kind: "zero" | "fill_u8" | "fill_u16" | "fill_u32" | "fill_fp16" | "fill_fp32"`
- `value: number | None` - `value: number | None`
- required for fill_*; ignored for zero - fill_*에 필요; zero에서는 무시됨
Optional fields: 선택 필드:
- `dst_mem_kind: "HBM" | "TCM" | "AUTO"` (default "AUTO") - `dst_mem_kind: "HBM" | "TCM" | "AUTO"` (기본값 "AUTO")
- `debug_label: str | None` - `debug_label: str | None`
Notes: 비고:
- This message MUST NOT embed bulk tensor data in Phase 0. - 이 메시지는 Phase 0에서 대용량 텐서 데이터를 임베드해서는 안 된다.
- All latency MUST come from explicit graph traversal and modeled components. - 모든 레이턴시는 명시적인 그래프 순회 및 모델링된 컴포넌트로부터
발생해야 한다.
--- ---
### D6. MemoryRead schema (PA-first, PE-tagged) ### D6. MemoryRead 스키마 (PA-우선, PE-태깅)
`MemoryRead` represents a host-initiated read from device memory. `MemoryRead`는 호스트에서 시작된 디바이스 메모리 읽기를 나타낸다.
Mandatory fields: 필수 필드:
- common envelope fields (D3) - 공통 envelope 필드 (D3)
- source placement tags (A 방식): - 소스 배치 태그 (A 방식):
- `src_sip: int` - `src_sip: int`
- `src_cube: int` - `src_cube: int`
- `src_pe: int` - `src_pe: int`
- `src_pa: int` - `src_pa: int`
- `nbytes: int` - `nbytes: int`
Optional fields: 선택 필드:
- `dst_kind: "host_sink" | "discard"` (default "host_sink") - `dst_kind: "host_sink" | "discard"` (기본값 "host_sink")
- `debug_label: str | None` - `debug_label: str | None`
Response payload: 응답 페이로드:
- actual bytes are NOT required in Phase 0 (latency/traces focus) - Phase 0에서는 실제 바이트는 필요하지 않다(레이턴시/트레이스 중심)
- implementations MAY return lightweight stats or hashes later via a new ADR - 구현은 추후 새로운 ADR을 통해 가벼운 통계나 해시를 반환할 수 있다
--- ---
### D7. KernelLaunch schema (PA-first, PE-tagged shards) ### D7. KernelLaunch 스키마 (PA-우선, PE-태깅된 샤드)
`KernelLaunch` represents launching a kernel on a target device via IO_CPU. `KernelLaunch`는 IO_CPU를 통해 대상 디바이스에서 커널을 런치하는 것을
나타낸다.
Mandatory fields: 필수 필드:
- common envelope fields (D3) - 공통 envelope 필드 (D3)
- `kernel_ref: KernelRef` - `kernel_ref: KernelRef`
- `args: list[KernelArg]` - `args: list[KernelArg]`
`KernelRef` MUST have: `KernelRef`는 반드시 다음을 가져야 한다:
- `name: str` - `name: str`
- `kind: "deployed" | "builtin"` - `kind: "deployed" | "builtin"`
- `deploy_pa: int | None`PA where kernel binary was deployed (required for "deployed") - `deploy_pa: int | None`커널 바이너리가 배치된 PA("deployed"에 필수)
- `deploy_sip: int`SIP where binary resides - `deploy_sip: int`바이너리가 위치한 SIP
- `deploy_cube: int`cube where binary resides - `deploy_cube: int`바이너리가 위치한 큐브
- `deploy_pe: int`PE where binary resides - `deploy_pe: int`바이너리가 위치한 PE
- `nbytes_code: int`kernel binary size (for BW modeling) - `nbytes_code: int`커널 바이너리 크기(BW 모델링용)
Kernel binaries MUST be pre-deployed to device memory via MemoryWrite. 커널 바이너리는 MemoryWrite를 통해 디바이스 메모리에 사전 배치되어야 한다.
KernelLaunch MUST NOT embed kernel source code or IR in the launch message. KernelLaunch는 커널 소스 코드나 IR을 런치 메시지에 임베드해서는 안 된다.
`KernelArg` supports tensor args by PA mapping and scalars by value. `KernelArg`는 PA 매핑을 통한 텐서 인자와 값을 통한 스칼라 인자를 지원한다.
Tensor arg (mandatory): 텐서 인자 (필수):
- `arg_kind: "tensor"` - `arg_kind: "tensor"`
- `tensor_pa_map: TensorPAMap` - `tensor_pa_map: TensorPAMap`
`TensorPAMap` MUST have: `TensorPAMap`은 반드시 다음을 가져야 한다:
- `shards: list[TensorShard]` - `shards: list[TensorShard]`
`TensorShard` MUST have (A 방식 강제): `TensorShard`는 반드시 다음을 가져야 한다 (A 방식 강제):
- `sip: int` - `sip: int`
- `cube: int` - `cube: int`
@@ -192,42 +197,43 @@ Tensor arg (mandatory):
- `nbytes: int` - `nbytes: int`
- `offset_bytes: int` - `offset_bytes: int`
Scalar arg (mandatory): 스칼라 인자 (필수):
- `arg_kind: "scalar"` - `arg_kind: "scalar"`
- `dtype: "i32" | "i64" | "fp16" | "fp32" | "bool"` - `dtype: "i32" | "i64" | "fp16" | "fp32" | "bool"`
- `value: number | bool` - `value: number | bool`
Optional KernelLaunch fields: KernelLaunch 선택 필드:
- `grid: dict | None` - `grid: dict | None`
- `meta: dict | None` - `meta: dict | None`
- `failure_policy: "fail_fast" | "collect_all"` (default "fail_fast") - `failure_policy: "fail_fast" | "collect_all"` (기본값 "fail_fast")
- `debug_label: str | None` - `debug_label: str | None`
Notes: 비고:
- KernelLaunch MUST NOT embed bulk tensor data. - KernelLaunch는 대용량 텐서 데이터를 임베드해서는 안 된다.
- KernelLaunch MUST be submitted only to the IO_CPU endpoint. - KernelLaunch는 오직 IO_CPU 엔드포인트에만 제출되어야 한다.
- IO_CPU MUST fan-out work internally using the shard (sip,cube,pe) tags. - IO_CPU는 샤드의 (sip,cube,pe) 태그를 사용하여 내부적으로 작업을
팬아웃해야 한다.
--- ---
## Verification Notes ## Verification Notes
Tests SHOULD validate: 테스트는 다음을 검증해야 한다:
- schema validation rejects missing mandatory fields, - 스키마 검증이 필수 필드 누락을 거부함,
- deterministic correlation/response matching, - 결정론적 correlation/응답 매칭,
- MemoryWrite/Read/KernelLaunch produce explicit hop traces, - MemoryWrite/Read/KernelLaunch가 명시적인 홉 트레이스를 생성함,
- all routed requests incur latency > 0. - 라우팅된 모든 요청은 레이턴시 > 0을 가짐.
--- ---
## Links ## Links
- ADR-0011 (Memory Addressing — PA / VA / LA) - ADR-0011 (메모리 주소 지정 — PA / VA / LA)
- ADR-0007 (runtime_api vs sim_engine boundaries) - ADR-0007 (runtime_api sim_engine 경계)
- ADR-0009 (kernel execution fan-out/aggregation) - ADR-0009 (커널 실행 팬아웃/집계)
- ADR-0013 (Verification strategy — V1 message schema validation) - ADR-0013 (검증 전략 — V1 메시지 스키마 검증)
- SPEC R2, R7, R8 - SPEC R2, R7, R8
@@ -0,0 +1,145 @@
# ADR-0013: 검증 전략 및 Phase 1 테스트 계획
## Status
Accepted
## Context
KernBench는 시스템 레벨 시뮬레이터이며, 그 정확성은 다음으로 정의된다:
- SPEC에 정의된 불변식 준수,
- 결정성과 디버깅 가능성,
- 라우팅과 레이턴시의 명시적 모델링.
진화하는 구현을 고려할 때, 점진적 개발을 허용하면서도 아키텍처적
편향(drift)을 방지하는 안정적인 검증 전략이 필요하다.
본 ADR은 Phase 1 검증 계획과 초기 구현에 대해 "올바른 동작"이 무엇인지를
정의한다.
---
## Decision
### D1. 검증은 계약 기반이다
검증은 반드시 다음으로부터 도출되어야 한다:
- SPEC 요구사항,
- 채택된 ADR들.
테스트는 부수적인 구현 세부사항이 아니라 아키텍처 계약을 검증해야 한다.
---
### D2. Phase 1 검증 범위
Phase 1 검증은 다음에 초점을 둔다:
- 메시지 계약 유효성 (ADR-0012),
- IO_CPU 경계에서의 라우팅과 팬아웃 시맨틱 (ADR-0009),
- PA-우선 메모리 주소 지정 및 샤드 태깅 (ADR-0011),
- 핵심 레이턴시 및 트레이스 불변식 (SPEC 0.1, R2).
마이크로아키텍처 정확도, 대역폭 경합, 사이클 레벨 동작은 Phase 1의
범위에서 명시적으로 제외된다.
---
### D3. 필수 Phase 1 검증 케이스
다음 검증 케이스는 구현에서 반드시 지원되어야 한다:
#### V1. 메시지 스키마 검증
- 텐서 샤드 중 어느 하나라도 `(sip, cube, pe)`가 누락된 KernelLaunch
요청은 반드시 거부되어야 한다.
- 목적지/소스 배치 태그가 누락된 MemoryWrite/MemoryRead 요청은 반드시
거부되어야 한다.
- Completion 결과는 반드시 `ok / error_code / error_message` 계약을
따라야 한다.
#### V2. IO_CPU 팬아웃과 집계
다음 조건이 주어졌을 때:
- SIP 1개, CUBE 1개, PE 2개로 구성된 토폴로지,
- 서로 다른 PE를 대상으로 하는 두 개의 텐서 샤드를 포함하는
KernelLaunch 요청,
시스템은 반드시:
- 단일 KernelLaunch를 IO_CPU에 제출하고,
- 내부적으로 두 PE에 작업을 팬아웃하며,
- 완료를 집계하여 호스트에 단일의 결정론적 완료를 반환해야 한다.
#### V3. 레이턴시 및 트레이스 불변식
모든 유효한 요청에 대하여:
- 홉별 트레이스는 반드시 비어 있지 않아야 한다,
- 총 레이턴시는 반드시 0보다 커야 한다,
- 동일한 입력으로 반복 실행 시 반드시 동일한 트레이스를 생성해야 한다.
#### V4. 토폴로지 독립성과 교차 도메인 커버리지
검증 케이스는 다음을 포함한 다양한 토폴로지 형태에서 통과해야 한다:
- 최소: (SIP 1, CUBE 1, PE 1)
- 다중 PE: (SIP 1, CUBE 1, PE N개)
- SIP 내 다중 CUBE: (SIP 1, CUBE M개, CUBE당 PE ≥1)
- 다중 SIP 트레이: (SIP K개, SIP당 CUBE ≥1, CUBE당 PE ≥1)
다중 CUBE 및 다중 SIP 토폴로지에 대해 Phase 1 검증은 다음에 초점을
둔다:
- 명시적 연결성(필요한 링크가 존재함),
- 결정론적 라우팅과 제어 경로 순회,
- 대표적인 교차 도메인 요청(CUBE 간 및 SIP 간 경로)에 대해 비어 있지
않은 트레이스와 레이턴시 > 0.
테스트는 토폴로지 크기, 노드 ID, 링크 수를 하드코딩해서는 안 된다.
대신 컴파일된 토폴로지 메타데이터로부터 기대값을 도출해야 한다.
---
### D4. Phase 1 산출물
Phase 1은 다음을 포함할 수 있다:
- 검증 전용 테스트 코드,
- 토폴로지 픽스처,
- 트레이스 검사 유틸리티.
Phase 1은 다음을 요구해서는 안 된다:
- 단지 테스트를 만족시키기 위한 프로덕션 코드 변경,
- 진행을 위한 테스트의 약화 또는 제거.
---
### D5. Phase 2 강제
Phase 2(Apply)는 반드시:
- Phase 1 검증 케이스를 실행하고,
- 검증이 실패하면 모든 변경을 롤백하며,
- 테스트를 권위 있는 계약으로 보존해야 한다.
---
## Consequences
- 아키텍처 정확성은 초기에 강제된다.
- 테스트는 시스템 동작의 실행 가능한 문서로 기능한다.
- 구현은 엄정성을 잃지 않으면서도 유연성을 유지한다.
---
## Links
- SPEC 0.1, R2, R6
- ADR-0011 (메모리 주소 지정 — PA / VA / LA)
- ADR-0012 (Host ↔ IO_CPU 메시지 스키마)
- ADR-0009 (커널 실행 시맨틱)
@@ -1,4 +1,4 @@
# ADR-0014: PE Pipeline Execution Model # ADR-0014: PE 파이프라인 실행 모델
## Status ## Status
@@ -6,153 +6,149 @@ Accepted
## Context ## Context
This ADR defines the PE-internal kernel execution model: 본 ADR은 PE 내부 커널 실행 모델을 정의한다:
- Role decomposition of PE-internal components - PE 내부 컴포넌트의 역할 분담
- Command dispatch paths (simple / composite / multi-op composite with epilogue) - 명령 디스패치 경로 (simple / composite / epilogue를 포함한 multi-op composite)
- TileToken-based self-routing pipeline (scheduler does dispatch + completion only) - TileToken 기반 자가-라우팅 파이프라인 (스케줄러는 디스패치와 완료 처리만 담당)
- TCM-centric dataflow with a register-file intermediary - 레지스터 파일을 매개로 한 TCM 중심 데이터플로우
- Engine resource model - 엔진 자원 모델
- Observability and trace contract - 관측 가능성 및 트레이스 계약
- Topology representation - 토폴로지 표현
PE-internal structure (7 components in scope; 2 cross-referenced): PE 내부 구조 (본 ADR 범위 7개 컴포넌트 + 외부 참조 2개):
- `pe_cpu`, `pe_scheduler`, `pe_dma`, `pe_fetch_store`, `pe_gemm`, `pe_math`, - `pe_cpu`, `pe_scheduler`, `pe_dma`, `pe_fetch_store`, `pe_gemm`, `pe_math`,
`pe_tcm`defined here `pe_tcm`본 ADR에서 정의
- `pe_mmu` — VA model, defined in ADR-0011 D-VA - `pe_mmu` — VA 모델, ADR-0011 D-VA에서 정의
- `pe_ipcq`collective communication, defined in ADR-0023 - `pe_ipcq`집합 통신, ADR-0023에서 정의
The goal is a deterministic, trace-friendly execution contract that keeps 목표는 결정론적이고 트레이스 친화적인 실행 계약을 통해 각 블록이 독립적으로
each block independently swappable. 교체 가능하도록 유지하는 것이다.
## Decision ## Decision
### D1. PE-internal component roles ### D1. PE 내부 컴포넌트의 역할
**PE_CPU** **PE_CPU**
- Executes kernel instruction stream / control logic. - 커널 명령어 스트림 / 제어 로직을 실행한다.
- Generates PE commands and submits them to `PE_SCHEDULER` (via - PE 명령을 생성하여 `PE_SCHEDULER`에 제출한다 (`PeInternalTxn`을 통해).
`PeInternalTxn`). - 엔진 큐에 직접 작업을 넣지 않는다.
- Does NOT enqueue work directly into engine queues.
**PE_SCHEDULER** **PE_SCHEDULER**
- Sole dispatcher inside a PE. - PE 내부의 유일한 디스패처.
- Receives commands from `PE_CPU`. Dispatch by command type: - `PE_CPU`로부터 명령을 수신한다. 명령 타입별 디스패치:
- Simple command (`DmaReadCmd`, `DmaWriteCmd`, `GemmCmd`, `MathCmd`) - Simple 명령 (`DmaReadCmd`, `DmaWriteCmd`, `GemmCmd`, `MathCmd`)
forward directly to the target engine. 대상 엔진으로 직접 전달.
- `CompositeCmd` generate a `TilePlan`, feed tiles into the pipeline - `CompositeCmd``TilePlan`을 생성하고, 단일 `_feed_loop`를 통해
via a single `_feed_loop` (D6). 파이프라인에 타일을 공급한다 (D6).
- Does not participate in stage-to-stage chaining within a composite; - composite 내부의 stage-to-stage 체이닝에는 관여하지 않는다;
that is handled by token self-routing (D6). 이는 토큰 자가-라우팅(D6)으로 처리된다.
**PE_DMA** **PE_DMA**
- Handles memory transfers between TCM and external memory domains - 큐브 NoC를 통해 TCM과 외부 메모리 도메인(HBM, 공유 SRAM, 큐브 간 UCIe)
(HBM, shared SRAM, cross-cube UCIe) through the cube NOC. 사이의 메모리 전송을 처리한다.
- Two execution channels: - 두 개의 실행 채널:
- `DMA_READ` (capacity = 1) and `DMA_WRITE` (capacity = 1) — see D4. - `DMA_READ` (capacity = 1) `DMA_WRITE` (capacity = 1) — D4 참조.
- Additional virtual channels: - 추가 가상 채널:
- `vc_compute` — load/store/writeback traffic for GEMM/MATH tiles. - `vc_compute` GEMM/MATH 타일의 load/store/writeback 트래픽.
- `vc_comm` — IPCQ collective send data (defined in ADR-0023 D8). - `vc_comm` — IPCQ 집합 통신 송신 데이터 (ADR-0023 D8에서 정의).
**PE_FETCH_STORE** **PE_FETCH_STORE**
- TCM ↔ Register File transfer unit. - TCM ↔ 레지스터 파일 전송 유닛.
- Isolates register-file access semantics from compute engines so that - 레지스터 파일 접근 시맨틱을 컴퓨트 엔진으로부터 격리하여
GEMM/MATH stay pure compute components. GEMM/MATH가 순수한 컴퓨트 컴포넌트로 유지되도록 한다.
- BW-based latency model; TCM access contention naturally serializes - BW 기반 레이턴시 모델; TCM 접근 경합은 `PE_TCM`의 BW 자원을 통해
through `PE_TCM`'s BW resource. 자연스럽게 직렬화된다.
**PE_GEMM** **PE_GEMM**
- MAC array. Reads operands from the register file; writes results to - MAC 어레이. 레지스터 파일에서 피연산자를 읽고, 결과를 레지스터 파일에
the register file. Does not touch `PE_TCM` directly. 쓴다. `PE_TCM`에 직접 접근하지 않는다.
**PE_MATH** **PE_MATH**
- Element-wise / reduction / SIMD unit. Reads / writes the register file. - 원소별 / 리덕션 / SIMD 유닛. 레지스터 파일을 읽고 쓴다.
**PE_TCM** **PE_TCM**
- Tightly-coupled scratchpad with BW-serialized access. Two logical - BW로 직렬화된 접근을 갖는 tightly-coupled 스크래치패드. 소유권에 따라
regions partitioned by ownership (see D5). 두 개의 논리 영역으로 분할된다 (D5 참조).
**Cross-referenced components** (defined elsewhere): **외부 참조 컴포넌트** (다른 곳에서 정의됨):
- `pe_mmu` — VA→PA translation per access (ADR-0011 D-VA). - `pe_mmu` 접근마다 VA→PA 변환 (ADR-0011 D-VA).
- `pe_ipcq`collective ring buffers and peer endpoint metadata - `pe_ipcq`집합 통신 링 버퍼와 피어 엔드포인트 메타데이터
(ADR-0023). (ADR-0023).
### D2. Command lifecycle and queues ### D2. 명령 생명주기와 큐
`PE_SCHEDULER` maintains three logical structures: `PE_SCHEDULER`는 세 개의 논리적 구조를 유지한다:
**SubmissionQueue**written by `PE_CPU`; consumed by the scheduler. **SubmissionQueue**`PE_CPU`가 쓰고, 스케줄러가 소비한다.
**InflightTable**owned and mutated only by `PE_SCHEDULER`; tracks **InflightTable**`PE_SCHEDULER`만 소유하고 변경한다; 전개된 sub-command,
expanded sub-commands, dependency state, engine assignment, and 의존성 상태, 엔진 할당, 완료 상태를 추적한다.
completion status.
**CompletionQueue** written by `PE_SCHEDULER`; holds final completion **CompletionQueue**`PE_SCHEDULER`가 쓴다; 최종 완료 레코드를 보관한다.
records.
**Single-writer rule**: only `PE_SCHEDULER` mutates command completion **Single-writer 규칙**: `PE_SCHEDULER`만이 명령 완료 상태를 변경한다.
state. Engines report completion via explicit events / messages 엔진은 명시적 이벤트 / 메시지로 완료를 보고하며, 이는 스케줄러가
consumed by the scheduler. 소비한다.
**Command completion**: when all sub-commands complete, `PE_SCHEDULER` **명령 완료**: 모든 sub-command가 완료되면 `PE_SCHEDULER`가 완료 레코드를
publishes a completion record. 발행한다.
### D3. Dispatch modes ### D3. 디스패치 모드
#### D3.1 Simple command #### D3.1 Simple 명령
A simple command expands to exactly one engine sub-command: simple 명령은 정확히 하나의 엔진 sub-command로 전개된다:
- `DmaReadCmd` / `DmaWriteCmd``PE_DMA` - `DmaReadCmd` / `DmaWriteCmd``PE_DMA`
- `GemmCmd``PE_GEMM` - `GemmCmd``PE_GEMM`
- `MathCmd``PE_MATH` - `MathCmd``PE_MATH`
Flow: 흐름:
```text ```text
PE_CPU → SubmissionQueue → PE_SCHEDULER → engine queue → engine execution PE_CPU → SubmissionQueue → PE_SCHEDULER → engine queue → engine execution
→ completion → PE_SCHEDULER → CompletionQueue → completion → PE_SCHEDULER → CompletionQueue
``` ```
#### D3.2 Composite command (single-op tiled pipeline) #### D3.2 Composite 명령 (단일-op 타일 파이프라인)
The default `CompositeCmd` runs a single compute op as a tile-pipelined 기본 `CompositeCmd`는 단일 컴퓨트 op를 타일 파이프라인 시퀀스로 실행한다:
sequence:
```text ```text
DMA_READ → FETCH (TCM → RF) → COMPUTE (GEMM | MATH) → STORE (RF → TCM) → DMA_WRITE DMA_READ → FETCH (TCM → RF) → COMPUTE (GEMM | MATH) → STORE (RF → TCM) → DMA_WRITE
``` ```
`PE_SCHEDULER` splits the DMA payload into hardware tiles and emits one `PE_SCHEDULER`는 DMA 페이로드를 하드웨어 타일로 분할하고, 단조 증가하는
`TileToken` per tile with a monotonically increasing `tile_id`. `tile_id`를 갖는 `TileToken`을 타일마다 하나씩 발행한다.
Tile dependency (within one tile `t`): 타일 의존성 (단일 타일 `t` 내부):
```text ```text
DMA_READ(t) → FETCH(t) → COMPUTE(t) → STORE(t) → DMA_WRITE(t) DMA_READ(t) → FETCH(t) → COMPUTE(t) → STORE(t) → DMA_WRITE(t)
``` ```
Inter-tile overlap is allowed wherever engine resources permit 엔진 자원이 허용하는 한 타일 간 오버랩이 허용된다
(D4 governs the constraints): (D4가 제약을 규정):
```text ```text
DMA_READ(t+1) ∥ COMPUTE(t) DMA_READ(t+1) ∥ COMPUTE(t)
DMA_WRITE(t-1) ∥ COMPUTE(t) DMA_WRITE(t-1) ∥ COMPUTE(t)
``` ```
#### D3.3 Multi-op composite (head + epilogue with scope) #### D3.3 Multi-op composite (스코프를 갖는 head + epilogue)
A `CompositeCmd` MAY carry `ops: tuple[OpSpec, ...]` to express a `CompositeCmd` `ops: tuple[OpSpec, ...]`를 운반하여 multi-op
multi-op pipeline: 파이프라인을 표현할 수 있다:
```python ```python
@dataclass(frozen=True) @dataclass(frozen=True)
@@ -162,48 +158,46 @@ class OpSpec:
... ...
``` ```
- `ops[0]` (head) defines tile geometry (e.g., the head GEMM determines - `ops[0]` (head)이 타일 기하 구조를 정의한다 (예: head GEMM이 M/K/N
M/K/N partition). 분할을 결정).
- `ops[1:]` (epilogue) are subsequent stages whose `scope` decides how - `ops[1:]` (epilogue)는 후속 stage이며 `scope`에 따라 실행 빈도가
often they fire: 결정된다:
- `per_k_tile`every K-reduction step. - `per_k_tile`모든 K-리덕션 스텝마다.
- `per_output_tile`once per output tile. - `per_output_tile`출력 타일당 한 번.
- `once`once per kernel. - `once`커널당 한 번.
Cross-engine chains (e.g., GEMM head → MATH epilogue) are natural 크로스-엔진 체인(예: GEMM head → MATH epilogue)은 자연스럽다
each stage is dispatched via token self-routing (D6), so GEMM and MATH stage는 토큰 자가-라우팅(D6)을 통해 디스패치되므로, GEMM MATH
participate serially within the same composite even though they share 동일한 컴퓨트 슬롯(D4)을 공유하더라도 동일 composite 내에서 직렬적으로
the compute slot (D4). 참여한다.
The empty-`ops` form is the legacy single-op path. 비어 있는 `ops` 형식은 레거시 단일-op 경로이다.
### D4. Engine resource model ### D4. 엔진 자원 모델
**DMA engine**: **DMA 엔진**:
- `DMA_READ`: `simpy.Resource(capacity=1)`. - `DMA_READ`: `simpy.Resource(capacity=1)`.
- `DMA_WRITE`: `simpy.Resource(capacity=1)`. - `DMA_WRITE`: `simpy.Resource(capacity=1)`.
- Both channels run concurrently (READ ∥ WRITE allowed). - 두 채널은 동시에 실행된다 (READ ∥ WRITE 허용).
- Within a channel, requests serialize (READ ∥ READ disallowed; same - 채널 내부에서는 요청이 직렬화된다 (READ ∥ READ 불가; WRITE도 동일).
for WRITE). - `vc_comm`은 IPCQ 트래픽을 위한 직교 채널로 ADR-0023 D8에서 정의됨 —
- `vc_comm` is an orthogonal channel for IPCQ traffic defined in 본 ADR 범위 밖.
ADR-0023 D8 — out of scope for this ADR.
**Compute engine**: **컴퓨트 엔진**:
- `accel_slot`: `simpy.Resource(capacity=1)` shared by `PE_GEMM` and - `accel_slot`: `PE_GEMM``PE_MATH`가 공유하는 `simpy.Resource(capacity=1)`.
`PE_MATH`. - PE 내에서 동시에 최대 한 개의 컴퓨트 op만 실행된다.
- At most one compute op runs at a time within a PE. - Multi-op composite 체인(D3.3)은 이 슬롯을 통해 컴퓨트 stage를 직렬로
- Multi-op composite chains (D3.3) execute their compute stages serially 실행한다; 토큰 자가-라우팅(D6)이 이전 컴퓨트가 슬롯을 해제한 후에만
through this slot; token self-routing (D6) ensures the next stage 다음 stage가 시작되도록 보장한다.
starts only after the previous compute releases the slot.
**Engine completion**: each engine emits a completion event consumed by **엔진 완료**: 각 엔진은 완료 이벤트를 발행하며, 이는 스케줄러 /
the scheduler / `PipelineContext` (D6). `PipelineContext`(D6)가 소비한다.
### D5. Dataflow ### D5. 데이터플로우
**Input path (HBM source)**: **입력 경로 (HBM 소스)**:
```text ```text
HBM → cube NOC → PE_DMA (DMA_READ) → PE_TCM HBM → cube NOC → PE_DMA (DMA_READ) → PE_TCM
@@ -211,60 +205,58 @@ PE_TCM → PE_FETCH_STORE → Register File
Register File → PE_GEMM | PE_MATH Register File → PE_GEMM | PE_MATH
``` ```
**Input path (shared SRAM source)**: **입력 경로 (공유 SRAM 소스)**:
```text ```text
Shared SRAM → cube NOC → PE_DMA (DMA_READ) → PE_TCM Shared SRAM → cube NOC → PE_DMA (DMA_READ) → PE_TCM
PE_TCM → PE_FETCH_STORE → Register File PE_TCM → PE_FETCH_STORE → Register File
``` ```
**Output path (HBM destination)**: **출력 경로 (HBM 목적지)**:
```text ```text
Register File → PE_FETCH_STORE → PE_TCM Register File → PE_FETCH_STORE → PE_TCM
PE_TCM → PE_DMA (DMA_WRITE) → cube NOC → HBM PE_TCM → PE_DMA (DMA_WRITE) → cube NOC → HBM
``` ```
GEMM/MATH never touch `PE_TCM` directly`PE_FETCH_STORE` is the GEMM/MATH`PE_TCM`에 직접 접근하지 않는다`PE_FETCH_STORE`
single TCM↔register-file gateway. This makes TCM BW contention TCM↔레지스터 파일의 유일한 게이트웨이이다. 이를 통해 TCM BW 경합이
explicit and lets fetch unit policies (e.g., prefetch) be replaced 명시적으로 드러나며, fetch 유닛 정책(예: 프리패치)을 컴퓨트 엔진과
independently of compute engines. 독립적으로 교체할 수 있다.
#### D5.1 PE_TCM partitioning #### D5.1 PE_TCM 분할
`PE_TCM` is split into two logical regions: `PE_TCM`은 두 개의 논리 영역으로 분할된다:
**SchedulerReservedTCM** **SchedulerReservedTCM**
- Owned exclusively by `PE_SCHEDULER`. - `PE_SCHEDULER`가 단독으로 소유한다.
- Holds composite-command tile buffers. - composite 명령의 타일 버퍼를 보관한다.
- `PE_SCHEDULER` partitions this region, assigns buffers per DMA_READ / - `PE_SCHEDULER`가 이 영역을 분할하고, DMA_READ / COMPUTE / DMA_WRITE
COMPUTE / DMA_WRITE stage, guarantees input/output separation, and stage마다 버퍼를 할당하며, 입출력 분리를 보장하고, 타일-버퍼 수명을
manages tile-buffer lifetimes. 관리한다.
**AllocatableTCM** **AllocatableTCM**
- General-purpose region managed by `PEMemAllocator`. - `PEMemAllocator`가 관리하는 범용 영역.
- Used for host / DP-visible allocations. - 호스트 / DP 가시 할당에 사용된다.
**Visibility rule (hard isolation)**: `PEMemAllocator` MUST NOT see or **가시성 규칙 (강한 격리)**: `PEMemAllocator``SchedulerReservedTCM`
allocate inside `SchedulerReservedTCM`. The reserved region is excluded 보거나 그 내부에 할당해서는 안 된다. 예약 영역은 구성 시점에 할당자가
from allocator-managed ranges by construction. 관리하는 범위에서 제외된다.
**Tile buffer rules**: **타일 버퍼 규칙**:
- Input and output buffers within `SchedulerReservedTCM` MUST NOT - 타일이 활성 수명 동안 `SchedulerReservedTCM` 내부의 입력 버퍼와 출력
overlap during a tile's active lifetime. 버퍼는 겹쳐서는 안 된다.
- A tile buffer remains valid until the corresponding `DMA_WRITE` - 타일 버퍼는 해당 `DMA_WRITE`가 완료될 때까지 유효하다.
completes. - 버퍼 재사용은 소비하는 타일의 수명이 끝난 후에만 허용된다.
- Buffer reuse is permitted only after the consuming tile's lifetime
ends.
### D6. TileToken self-routing pipeline ### D6. TileToken 자가-라우팅 파이프라인
A composite's stage-to-stage progression happens **without** routing composite stage-to-stage 진행은 스케줄러를 거치지 **않고** 일어난다.
through the scheduler. Each component forwards the token directly to 각 컴포넌트는 토큰의 `plan`을 사용해 토큰을 다음 stage의 컴포넌트로
the next stage's component using the token's `plan`: 직접 전달한다:
```text ```text
Scheduler → DMA → Fetch → GEMM → Math (epi) → Store → DMA_WB → (complete) Scheduler → DMA → Fetch → GEMM → Math (epi) → Store → DMA_WB → (complete)
@@ -272,8 +264,8 @@ Scheduler → DMA → Fetch → GEMM → Math (epi) → Store → DMA_WB → (co
PipelineContext.complete_tile() PipelineContext.complete_tile()
``` ```
This mirrors real-HW done-wire chains. The scheduler handles only 이는 실제 HW done-wire 체인을 반영한다. 스케줄러는 **초기 디스패치 +
**initial dispatch + completion aggregation**. 완료 집계**만 담당한다.
#### TilePlan / Stage #### TilePlan / Stage
@@ -311,12 +303,12 @@ class TileToken:
data_op: bool = True # op_log opt-in (ADR-0020 D4) data_op: bool = True # op_log opt-in (ADR-0020 D4)
``` ```
Single-owner invariant: a token is owned by exactly one component at a 단일 소유자 불변식: 토큰은 한 시점에 정확히 한 컴포넌트가 소유한다.
time. Lifecycle: scheduler creates with `stage_idx=0` → component 생명주기: 스케줄러가 `stage_idx=0`으로 생성 → 컴포넌트 `_process()`
`_process()` → increment `stage_idx` → put to next stage's `in_port` `stage_idx` 증가 → 다음 stage `in_port`에 put → 마지막 stage가
last stage calls `pipeline_ctx.complete_tile()`. `pipeline_ctx.complete_tile()` 호출.
#### PipelineContext (exactly-once completion) #### PipelineContext (정확히 한 번 완료)
```python ```python
@dataclass @dataclass
@@ -332,25 +324,24 @@ class PipelineContext:
self.done_event.succeed() self.done_event.succeed()
``` ```
Each tile's last stage MUST call `complete_tile()` exactly once. 각 타일의 마지막 stage는 `complete_tile()`을 정확히 한 번 호출해야
Duplicate calls are bugs (SimPy `Event` can succeed at most once). 한다. 중복 호출은 버그이다 (SimPy `Event`는 최대 한 번만 succeed
가능).
#### Feed ordering #### Feed 순서
`PE_SCHEDULER` has exactly one `_feed_loop` process consuming a `PE_SCHEDULER``_pending_feeds` FIFO를 소비하는 `_feed_loop` 프로세스를
`_pending_feeds` FIFO. Composite commands are enqueued in submission 정확히 하나 갖는다. composite 명령은 제출 순서대로 인큐되며, 한 명령의
order; tile feed for a command runs to completion before the next 타일 feed는 다음 명령의 feed가 시작되기 전에 완료까지 실행된다.
command's feed begins. **Tile-feed interleaving between commands is **명령 간 타일-feed 인터리빙은 허용되지 않는다.**
disallowed.**
Within a single command's tiles, downstream pipeline overlap arises 단일 명령의 타일들 내부에서는 다운스트림 파이프라인 오버랩이 자연스럽게
naturally — earlier tiles progress through later stages while the feeder 발생한다 — 이전 타일이 후행 stage를 진행하는 동안 feeder는 남은 타일을
keeps pushing remaining tiles into the first stage queue (SimPy Store 첫 stage 큐로 계속 푸시한다 (SimPy Store 백프레셔가 흐름 제어를
backpressure governs flow control). If the first-stage queue is full, 관장한다). 첫 stage 큐가 가득 차면 feeder만 블록되며, 스케줄러 워커의
only the feeder blocks; the scheduler worker's inbox processing inbox 처리는 계속된다.
continues.
#### Token routing pattern (base class) #### 토큰 라우팅 패턴 (기본 클래스)
```python ```python
def _pipeline_worker(self, env): def _pipeline_worker(self, env):
@@ -367,12 +358,11 @@ def _pipeline_worker(self, env):
token.pipeline_ctx.complete_tile() token.pipeline_ctx.complete_tile()
``` ```
Each component implements only `_process()`; chaining lives in the 각 컴포넌트는 `_process()`만 구현한다; 체이닝은 기본 클래스에 존재한다.
base class.
### D7. Observability and trace contract ### D7. 관측 가능성 및 트레이스 계약
The simulator emits deterministic trace events: 시뮬레이터는 결정론적 트레이스 이벤트를 발행한다:
- `command_submitted` - `command_submitted`
- `sub_command_dispatched` - `sub_command_dispatched`
@@ -381,11 +371,11 @@ The simulator emits deterministic trace events:
- `tile_ready` - `tile_ready`
- `command_complete` - `command_complete`
For identical inputs, trace ordering MUST be deterministic. 동일한 입력에 대해 트레이스 순서는 결정론적이어야 한다.
### D8. Topology representation ### D8. 토폴로지 표현
PE-internal components are declared in `cube.pe_template`: PE 내부 컴포넌트는 `cube.pe_template`에 선언된다:
```yaml ```yaml
pe_template: pe_template:
@@ -416,36 +406,36 @@ pe_template:
fetch_store_to_tcm_bw_gbs: ... fetch_store_to_tcm_bw_gbs: ...
``` ```
Template is instantiated once per PE. PE instances are derived from 템플릿은 PE마다 한 번 인스턴스화된다. PE 인스턴스는 `cube.pe_layout`
`cube.pe_layout` (corner placement). External connectivity (PE_DMA ↔ (코너 배치)으로부터 파생된다. 외부 연결성(PE_DMA ↔ cube NoC ↔ HBM 등)은
cube NOC ↔ HBM, etc.) is modeled at the cube level (ADR-0017 D4). 큐브 수준에서 모델링된다 (ADR-0017 D4).
## Consequences ## Consequences
### Positive ### Positive
- Each block is an independent topology node — individually swappable - 각 블록이 독립적인 토폴로지 노드이다 — DI(ADR-0015)를 통해 개별
via DI (ADR-0015). 교체 가능하다.
- PE-internal structure is visible in the topology graph. - PE 내부 구조가 토폴로지 그래프에 가시화된다.
- Components do not know their downstream — plan-based routing gives - 컴포넌트는 자신의 다운스트림을 알지 못한다 — plan 기반 라우팅이
flexibility (e.g., epilogue chains require no scheduler change). 유연성을 제공한다 (예: epilogue 체인에 스케줄러 변경이 불필요).
- DMA and compute overlap naturally via SimPy Store backpressure. - DMA와 컴퓨트가 SimPy Store 백프레셔를 통해 자연스럽게 오버랩된다.
- Multi-op composite expresses fused operations (e.g., GEMM + bias_add) - Multi-op composite가 융합 연산(예: GEMM + bias_add)을 엔진 수준
without engine-level coupling. 결합 없이 표현한다.
- TCM access contention is realistic`PE_FETCH_STORE` is the single - TCM 접근 경합이 현실적이다`PE_FETCH_STORE`가 TCM↔RF의 유일한
TCM↔RF gateway. 게이트웨이이다.
### Negative ### Negative
- Intra-PE component count is higher than a coarser model (7 base + 2 - PE 내부 컴포넌트 수가 더 거친 모델보다 많다 (기본 7개 + 외부 참조
cross-referenced) — more topology nodes/edges. 2개) — 더 많은 토폴로지 노드/엣지.
- Intra-PE token forwarding is explicit in traces (acceptable trade for - PE 내부 토큰 전달이 트레이스에 명시적으로 드러난다 (HW 충실도와의
HW fidelity). 허용 가능한 trade-off).
## Links ## Links
- ADR-0011 D-VA (PE_MMU component, VA translation) - ADR-0011 D-VA (PE_MMU 컴포넌트, VA 변환)
- ADR-0015 D4 (component port/wire model) - ADR-0015 D4 (컴포넌트 포트/와이어 모델)
- ADR-0020 (greenlet kernel execution / two-pass) - ADR-0020 (greenlet 커널 실행 / two-pass)
- ADR-0023 (PE_IPCQ + PE_DMA virtual channels) - ADR-0023 (PE_IPCQ + PE_DMA 가상 채널)
- SPEC R3, R4 - SPEC R3, R4
@@ -1,4 +1,4 @@
# ADR-0015: Component Port/Wire Model and Fabric Routing # ADR-0015: 컴포넌트 포트/와이어 모델과 패브릭 라우팅
## Status ## Status
@@ -6,46 +6,44 @@ Accepted
## Context ## Context
Realistic hardware modeling — queues, contention, fan-out — requires 현실적인 하드웨어 모델링 — 큐, 경합, fan-out — 을 위해서는
that components own fabric traversal while the simulation engine 컴포넌트가 패브릭 순회를 소유하고, 시뮬레이션 엔진은 초기화와 완료
handles only initialization and completion observation. Direct method 관측만 처리해야 한다. 컴포넌트 간의 직접 메서드 호출이나 엔진 내부의
calls between components, or path-walking inside the engine, defeat 경로 탐색은 큐잉과 경합 시맨틱을 무력화한다.
queueing and contention semantics.
This ADR defines: 본 ADR은 다음을 정의한다:
- how components communicate via typed port queues, - 컴포넌트가 타입드 포트 큐를 통해 통신하는 방식,
- how propagation delay is modeled (wire processes with BW occupancy), - 전파 지연을 모델링하는 방식 (BW 점유를 포함한 와이어 프로세스),
- the fabric paths for Memory R/W (M_CPU bypass) and Kernel Launch - Memory R/W (M_CPU 우회)와 Kernel Launch (M_CPU 경유)의 패브릭 경로,
(via M_CPU), - 엔진의 축소된 역할 (와이어 초기화 + 완료 관측만),
- the engine's reduced role (wire init + completion observation only), - M_CPU의 내부 서브컴포넌트로서의 M_CPU.DMA.
- M_CPU.DMA as an internal subcomponent of M_CPU.
--- ---
## Decision ## Decision
### D1. Component port model ### D1. 컴포넌트 포트 모델
Each component has typed input/output ports modeled as SimPy Stores: 각 컴포넌트는 SimPy Store로 모델링된 타입드 입출력 포트를 갖는다:
```text ```text
in_ports: dict[str, simpy.Store] # keyed by source node_id in_ports: dict[str, simpy.Store] # keyed by source node_id
out_ports: dict[str, simpy.Store] # keyed by destination node_id out_ports: dict[str, simpy.Store] # keyed by destination node_id
``` ```
Ports are created at engine initialization based on graph edges. 포트는 그래프 엣지를 기반으로 엔진 초기화 시 생성된다.
Each directed edge (src → dst) results in: 각 유향 엣지(src → dst)는 다음을 생성한다:
- `src.out_ports[dst]`the sending end - `src.out_ports[dst]`송신측
- `dst.in_ports[src]`the receiving end - `dst.in_ports[src]`수신측
--- ---
### D2. Wire process (propagation delay + BW occupancy) ### D2. 와이어 프로세스 (전파 지연 + BW 점유)
For each directed edge (src, dst) in the topology graph, a SimPy wire process 토폴로지 그래프의 각 유향 엣지 (src, dst)에 대해 SimPy 와이어 프로세스가
models propagation delay and BW occupancy: 전파 지연과 BW 점유를 모델링한다:
```python ```python
def wire_process(env, out_port, in_port, delay_ns, bw_gbs): def wire_process(env, out_port, in_port, delay_ns, bw_gbs):
@@ -63,39 +61,39 @@ def wire_process(env, out_port, in_port, delay_ns, bw_gbs):
yield in_port.put(cmd) yield in_port.put(cmd)
``` ```
Wire processes are started at engine initialization. 와이어 프로세스는 엔진 초기화 시점에 시작된다.
Each directed edge maintains an `available_at` timestamp tracking when the link 각 유향 엣지는 링크가 다음 트랜잭션을 위해 비워지는 시점을 추적하는
becomes free for the next transaction. When a transaction occupies a link, the `available_at` 타임스탬프를 유지한다. 한 트랜잭션이 링크를 점유하는 동안,
next transaction on the same directed link must wait until occupancy clears 동일 유향 링크의 다음 트랜잭션은 점유가 해제될 때까지 대기해야 한다
(back-to-back serialization). TX and RX directions are independent (separate (연속 직렬화). TX와 RX 방향은 독립적이다 (각각의 `available_at` 상태를
wire processes with separate `available_at` state). 갖는 별개의 와이어 프로세스).
--- ---
### D3. Engine role (reduced) ### D3. 엔진 역할 (축소)
The simulation engine MUST: 시뮬레이션 엔진은 다음을 수행해야 한다:
- wire components at initialization (create port Stores, start wire processes), - 초기화 시점에 컴포넌트 와이어링 (포트 Store 생성, 와이어 프로세스 시작),
- identify the entry component for each request type (PCIE_EP), - 각 요청 타입별 진입 컴포넌트 식별 (PCIE_EP),
- put the request into the entry component's in_port, - 진입 컴포넌트의 in_port에 요청을 put,
- wait for a completion event. - 완료 이벤트 대기.
The simulation engine MUST NOT: 시뮬레이션 엔진은 다음을 해서는 안 된다:
- walk the topology path during request execution, - 요청 실행 중 토폴로지 경로 탐색,
- call component `run()` methods directly, - 컴포넌트 `run()` 메서드 직접 호출,
- track per-hop latency or decompose fan-out. - hop별 레이턴시 추적이나 fan-out 분해.
--- ---
### D4. Fabric paths for Memory R/W and Kernel Launch ### D4. Memory R/W Kernel Launch의 패브릭 경로
Memory R/W and Kernel Launch use **different** fabric paths. Memory R/W Kernel Launch**서로 다른** 패브릭 경로를 사용한다.
Memory operations bypass M_CPU and route directly to HBM via the crossbar. 메모리 연산은 M_CPU를 우회하여 크로스바를 통해 직접 HBM으로 라우팅된다.
Kernel Launch routes through M_CPU for PE fan-out. Kernel Launch는 PE fan-out을 위해 M_CPU를 경유한다.
**Memory R/W forward path (pcie_ep → hbm_ctrl, M_CPU bypass):** **Memory R/W forward 경로 (pcie_ep → hbm_ctrl, M_CPU 우회):**
```text ```text
pcie_ep → io_noc → io_ucie pcie_ep → io_noc → io_ucie
@@ -103,14 +101,14 @@ pcie_ep → io_noc → io_ucie
→ target cube: ucie_in → router mesh → hbm_ctrl → target cube: ucie_in → router mesh → hbm_ctrl
``` ```
**Memory R/W completion path:** **Memory R/W 완료 경로:**
```text ```text
hbm_ctrl → router mesh → [transit cubes: ucie → router mesh → ucie] hbm_ctrl → router mesh → [transit cubes: ucie → router mesh → ucie]
→ io_ucie → io_noc → pcie_ep → io_ucie → io_noc → pcie_ep
``` ```
**Kernel Launch forward path (pcie_ep → io_cpu → M_CPU → PE):** **Kernel Launch forward 경로 (pcie_ep → io_cpu → M_CPU → PE):**
```text ```text
pcie_ep → io_noc → io_cpu → io_noc → io_ucie pcie_ep → io_noc → io_cpu → io_noc → io_ucie
@@ -118,7 +116,7 @@ pcie_ep → io_noc → io_cpu → io_noc → io_ucie
→ target cube: ucie_in → noc → M_CPU → PE[0..n] (parallel fan-out) → target cube: ucie_in → noc → M_CPU → PE[0..n] (parallel fan-out)
``` ```
**Kernel Launch completion path:** **Kernel Launch 완료 경로:**
```text ```text
PE[0..n] all complete → M_CPU (aggregation) PE[0..n] all complete → M_CPU (aggregation)
@@ -126,77 +124,79 @@ PE[0..n] all complete → M_CPU (aggregation)
→ io_ucie → io_noc → io_cpu → io_noc → pcie_ep → io_ucie → io_noc → io_cpu → io_noc → pcie_ep
``` ```
**Rationale for M_CPU bypass on Memory R/W:** **Memory R/W가 M_CPU를 우회하는 근거:**
Memory write/read operations do not require command interpretation or PE 메모리 write/read 연산은 명령 해석이나 PE 디스패치가 필요하지 않다 —
dispatch — they are direct data transfers to/from HBM. Routing through M_CPU HBM으로의/로부터의 직접 데이터 전송이다. M_CPU를 경유하면 기능적 이득
would add unnecessary overhead (5ns) without functional benefit. The io_noc 없이 불필요한 오버헤드(5ns)를 추가한다. IO 칩렛 내부의 io_noc가 라우팅
inside the IO chiplet handles the routing decision: memory operations go 결정을 처리한다: 메모리 연산은 큐브 패브릭으로 직접 가고, kernel
directly to cube fabric, while kernel launches are forwarded to io_cpu first. launch는 io_cpu로 먼저 전달된다.
--- ---
### D5. M_CPU.DMA is an internal subcomponent of M_CPU ### D5. M_CPU.DMA는 M_CPU의 내부 서브컴포넌트이다
M_CPU.DMA is NOT a separate topology node. M_CPU.DMA는 별개의 토폴로지 노드가 아니다.
It is an internal subcomponent owned by the M_CPU component implementation. M_CPU 컴포넌트 구현이 소유하는 내부 서브컴포넌트이다.
M_CPU.DMA: M_CPU.DMA:
- owns the DMA READ and DMA WRITE queues (capacity=1 each, per ADR-0014 D4), - DMA READ DMA WRITE 큐를 소유한다 (각 capacity=1, ADR-0014 D4),
- issues memory requests over the NOC to hbm_ctrl, - NoC를 통해 hbm_ctrl에 메모리 요청을 발행한다,
- receives completion from hbm_ctrl via the NOC, - NoC를 통해 hbm_ctrl로부터 완료를 수신한다,
- reports completion to M_CPU, - M_CPU에 완료를 보고한다,
- is created and managed inside M_CPU's `__init__` and `run()`. - M_CPU `__init__` `run()` 내부에서 생성·관리된다.
M_CPU.DMA does not appear as a node in the compiled topology graph. M_CPU.DMA는 컴파일된 토폴로지 그래프에서 노드로 나타나지 않는다.
--- ---
### D6. Transit cube forwarding ### D6. Transit 큐브 포워딩
A cube that is not the target of a memory or kernel request acts as a transit node. 메모리나 커널 요청의 대상이 아닌 큐브는 transit 노드로 동작한다.
Transit cubes forward requests without consuming them: Transit 큐브는 요청을 소비하지 않고 포워딩한다:
```text ```text
ucie_in (from upstream) → noc → ucie_out (to downstream) ucie_in (from upstream) → noc → ucie_out (to downstream)
``` ```
Transit forwarding is implemented entirely within the ucie_in component. Transit 포워딩은 ucie_in 컴포넌트 내부에서 전적으로 구현된다.
The noc and ucie_out components in a transit cube forward the packet without modification. transit 큐브의 noc와 ucie_out 컴포넌트는 패킷을 수정 없이 포워딩한다.
--- ---
### D7. _formula_latency is preserved as a lower-bound cross-check ### D7. _formula_latency는 하한 교차 검증 용도로 유지된다
The path-based formula latency function (`_formula_latency`) is preserved in the engine 경로 기반 공식 레이턴시 함수(`_formula_latency`)는 정확성 검증을 위한
as a lower bound for correctness verification. 하한값으로 엔진 내에 유지된다.
Invariant: 불변식:
- Phase 0: `_formula_latency == component model total_ns` - Phase 0: `_formula_latency == component model total_ns`
- Phase 1+: `_formula_latency <= component model total_ns` (contention adds queueing) - Phase 1+: `_formula_latency <= component model total_ns` (경합이
큐잉을 추가)
This function is independent of the port/wire model and requires only the topology graph. 이 함수는 포트/와이어 모델과 독립적이며 토폴로지 그래프만 요구한다.
It is used for shard comparison in `_route_kernel` and as a regression guard. `_route_kernel`의 샤드 비교와 회귀 가드로 사용된다.
--- ---
## Consequences ## Consequences
- Components model realistic hardware behavior (queues, contention, fan-out). - 컴포넌트가 현실적인 하드웨어 동작(큐, 경합, fan-out)을 모델링한다.
- Propagation delay is modeled accurately per edge. - 전파 지연이 엣지마다 정확하게 모델링된다.
- Engine is decoupled from routing policy. - 엔진이 라우팅 정책으로부터 분리된다.
- Component implementations remain swappable via DI (ADR-0007 D3). - 컴포넌트 구현이 DI(ADR-0007 D3)를 통해 교체 가능하게 유지된다.
--- ---
## Links ## Links
- ADR-0007 D2 (engine role boundary) - ADR-0007 D2 (엔진 역할 경계)
- ADR-0009 D3 (kernel execution fan-out hierarchy) - ADR-0009 D3 (커널 실행 fan-out 계층)
- ADR-0014 D4 (DMA engine capacity=1) - ADR-0014 D4 (DMA 엔진 capacity=1)
- ADR-0012 D1 (host ↔ IO_CPU message schema; M_CPU.DMA is component-internal) - ADR-0012 D1 (호스트 ↔ IO_CPU 메시지 스키마; M_CPU.DMA는 컴포넌트
- ADR-0016 (IOChiplet NOC and memory data path) 내부)
- ADR-0017 (cube NOC 2D mesh architecture) - ADR-0016 (IOChiplet NoC와 메모리 데이터 경로)
- ADR-0033 (Latency model assumptions built on these mechanisms) - ADR-0017 (큐브 NoC 2D 메시 아키텍처)
- ADR-0033 (이러한 메커니즘 위에 구축된 레이턴시 모델 가정)
@@ -1,4 +1,4 @@
# ADR-0016: IOChiplet NOC and Memory Data Path # ADR-0016: IOChiplet NoC와 메모리 데이터 경로
## Status ## Status
@@ -6,72 +6,73 @@ Accepted
## Context ## Context
ADR-0003 D2 defines IO chiplets as SIP-level components providing PCIe-EP and ADR-0003 D2 IO chiplet을 PCIe-EP 및 IO_CPU 인터페이스를 제공하는 SIP
IO_CPU interfaces, but does not specify internal routing within the IO chiplet. 레벨 컴포넌트로 정의하지만, IO chiplet 내부의 라우팅은 명세하지 않는다.
ADR-0015 D4 was updated to document the M_CPU bypass for Memory R/W, but the ADR-0015 D4는 Memory R/W에 대한 M_CPU 우회를 문서화하도록 갱신되었지만,
IO chiplet's internal NOC architecture that enables this routing was not 이 라우팅을 가능하게 하는 IO chiplet의 내부 NoC 아키텍처는 형식적으로
formally documented. 문서화되지 않았다.
The IO chiplet needs an internal routing fabric (io_noc) to: IO chiplet은 다음을 위해 내부 라우팅 패브릭(io_noc)을 필요로 한다:
- connect pcie_ep, io_cpu, and per-cube UCIe PHY ports - pcie_ep, io_cpu, 그리고 큐브당 UCIe PHY 포트들을 연결한다
- route memory operations (MemoryWrite/Read) directly to cube fabric without - 메모리 연산(MemoryWrite/Read)을 io_cpu를 거치지 않고 큐브 패브릭으로
passing through io_cpu 직접 라우팅한다
- route kernel launch commands through io_cpu for command interpretation - 커널 런치 명령을 명령 해석을 위해 io_cpu를 통해 라우팅한다
## Decision ## Decision
### D1. IOChiplet internal NOC (io_noc) ### D1. IOChiplet 내부 NoC (io_noc)
Each IO chiplet instance contains an internal NOC node (`io_noc`) that connects: IO chiplet 인스턴스는 다음을 연결하는 내부 NoC 노드(`io_noc`)
포함한다:
- `pcie_ep`host-facing PCIe endpoint - `pcie_ep`호스트 대면 PCIe 엔드포인트
- `io_cpu`command processor for kernel launch interpretation - `io_cpu`커널 런치 해석용 명령 프로세서
- `io_ucie-{PHY}.conn{N}`per-PHY connection nodes to cube UCIe ports - `io_ucie-{PHY}.conn{N}`큐브 UCIe 포트들로 가는 PHY별 연결 노드
The io_noc is a forwarding-only fabric (`forwarding_v1` implementation) with io_noc은 오버헤드가 0인 포워딩 전용 패브릭(`forwarding_v1` 구현)이다.
zero overhead. All routing decisions are made by the simulation engine based 모든 라우팅 결정은 io_noc 자체가 아니라 메시지 타입에 기반하여 시뮬레이션
on message type, not by io_noc itself. 엔진이 내린다.
### D2. IOChiplet UCIe decomposition ### D2. IOChiplet UCIe 분해
Each IO chiplet PHY port is decomposed into: IO chiplet PHY 포트는 다음으로 분해된다:
- `io_ucie-{PHY}` the UCIe protocol endpoint (overhead = 8ns) - `io_ucie-{PHY}` — UCIe 프로토콜 엔드포인트(overhead = 8ns)
- `io_ucie-{PHY}.conn{N}` N connection nodes between io_noc and io_ucie - `io_ucie-{PHY}.conn{N}` — io_noc io_ucie 사이의 N개 연결 노드
This mirrors the cube-side UCIe decomposition (ADR-0015 D1) and allows 이는 큐브 측 UCIe 분해(ADR-0015 D1)를 미러링하며, PHY당 여러 독립적인
multiple independent NOC-to-UCIe connections per PHY. NoC-UCIe 연결을 허용한다.
### D3. Memory R/W path (M_CPU bypass) ### D3. Memory R/W 경로 (M_CPU 우회)
Memory operations (MemoryWrite, MemoryRead) are routed directly from pcie_ep 메모리 연산(MemoryWrite, MemoryRead)은 pcie_ep에서 io_noc을 거쳐 대상
through io_noc to the target cube, bypassing io_cpu entirely: 큐브로 직접 라우팅되며, io_cpu를 완전히 우회한다:
```text ```text
pcie_ep → io_noc → conn → io_ucie → [cube UCIe] → router mesh → hbm_ctrl pcie_ep → io_noc → conn → io_ucie → [cube UCIe] → router mesh → hbm_ctrl
``` ```
This avoids the 10ns io_cpu overhead for pure data transfers. The simulation 이는 순수 데이터 전송에 대해 10ns io_cpu 오버헤드를 회피한다.
engine's `_process_memory_direct()` method uses `find_memory_path()` which 시뮬레이션 엔진의 `_process_memory_direct()` 메서드는 pcie_ep에서 대상
resolves the shortest path from pcie_ep to the target HBM node. HBM 노드까지의 최단 경로를 해석하는 `find_memory_path()`를 사용한다.
### D4. Kernel Launch path (via io_cpu) ### D4. 커널 런치 경로 (io_cpu 경유)
Kernel launch commands require io_cpu for command interpretation and PE 커널 런치 명령은 명령 해석 및 PE 팬아웃 설정을 위해 io_cpu를 필요로
fan-out setup: 한다:
```text ```text
pcie_ep → io_noc → io_cpu → io_noc → conn → io_ucie → [cube UCIe] pcie_ep → io_noc → io_cpu → io_noc → conn → io_ucie → [cube UCIe]
→ noc → m_cpu → PE → noc → m_cpu → PE
``` ```
The engine's `_entry_points()` method routes KernelLaunchMsg through both 엔진의 `_entry_points()` 메서드는 KernelLaunchMsg를 pcie_ep(진입)와
pcie_ep (entry) and io_cpu (command processing). io_cpu(명령 처리) 양쪽 모두를 통해 라우팅한다.
### D5. IOChiplet-to-cube port mapping ### D5. IOChiplet-to-큐브 포트 매핑
Each IO chiplet instance declares which cube ports it connects to: IO chiplet 인스턴스는 자신이 연결되는 큐브 포트를 선언한다:
```yaml ```yaml
cube_ports: cube_ports:
@@ -79,20 +80,20 @@ cube_ports:
- { cube: {xy: [1,0]}, cube_side: N, phy: P1, distance_mm: 2.0 } - { cube: {xy: [1,0]}, cube_side: N, phy: P1, distance_mm: 2.0 }
``` ```
The topology builder creates edges from io_ucie PHY nodes to the 토폴로지 빌더는 io_ucie PHY 노드에서 해당 큐브 UCIe 포트 노드로의 엣지를
corresponding cube UCIe port nodes, with the specified distance and 지정된 거리 및 IO chiplet의 `per_connection_bw_gbs`를 링크 대역폭으로
the IO chiplet's `per_connection_bw_gbs` as link bandwidth. 하여 생성한다.
## Consequences ## Consequences
- IO chiplet has a well-defined internal routing fabric - IO chiplet은 잘 정의된 내부 라우팅 패브릭을 가진다
- Memory operations avoid unnecessary io_cpu overhead - 메모리 연산은 불필요한 io_cpu 오버헤드를 회피한다
- Kernel launch commands still get proper command interpretation - 커널 런치 명령은 여전히 적절한 명령 해석을 받는다
- The io_noc pattern is consistent with cube-level NOC design - io_noc 패턴은 큐브 레벨 NoC 설계와 일관된다
- ADR-0003 D2 is extended (not contradicted) by this ADR - ADR-0003 D2는 본 ADR에 의해 확장된다(모순되지 않는다)
## Links ## Links
- ADR-0003 D2 (IO chiplet definition) - ADR-0003 D2 (IO chiplet 정의)
- ADR-0015 D4 (fabric paths for Memory R/W and Kernel Launch) - ADR-0015 D4 (Memory R/W 및 커널 런치의 패브릭 경로)
- ADR-0012 D1 (host-to-IO_CPU message schema) - ADR-0012 D1 (호스트-IO_CPU 메시지 스키마)
@@ -1,4 +1,4 @@
# ADR-0017: Cube NOC and HBM Connectivity # ADR-0017: 큐브 NoC와 HBM 연결성
## Status ## Status
@@ -6,78 +6,74 @@ Accepted
## Context ## Context
The CUBE-level NOC is a 2D router mesh that carries every intra-cube CUBE 레벨의 NoC는 모든 큐브 내부 요청을 운반하는 2D 라우터 메시이다:
request: PE-to-HBM data, PE-to-PE traffic, command paths PE-HBM 데이터, PE-PE 트래픽, 명령 경로(M_CPU↔PE_CPU), 공유 SRAM 접근,
(M_CPU↔PE_CPU), shared SRAM access, and inter-cube UCIe traffic. 큐브 간 UCIe 트래픽.
The CUBE's HBM is exposed through per-PE controller endpoints attached CUBE HBM은 PE 라우터에 부착된 PE별 컨트롤러 엔드포인트를 통해 노출된다.
to PE routers. This per-PE partitioning makes local-vs-remote HBM 이러한 PE별 분할 덕분에 로컬-vs-원격 HBM이 메시 거리로 구분 가능하다:
distinguishable by mesh distance: a PE's own HBM partition sits at its PE 자신의 HBM 파티션은 자신의 라우터에 위치하고(스위칭 오버헤드만 발생),
own router (switching overhead only); another PE's HBM partition is 다른 PE의 HBM 파티션은 해당 PE의 라우터로 메시 hop을 거쳐 도달 가능하다.
reachable by mesh hops to that PE's router.
Two channel-mapping modes are supported in the design space: 설계 공간에서는 두 가지 채널 매핑 모드를 지원한다:
- **n:1 (default, implemented)** — each PE's HBM partition aggregates - **n:1 (default, 구현됨)** — PE HBM 파티션이 `channels_per_pe`
`channels_per_pe` pseudo-channels into one endpoint. Effective pseudo-channel을 하나의 엔드포인트로 집계한다. 유효 PE당 BW =
per-PE BW = N × per-channel BW. N × per-channel BW.
- **1:1 (future)** — each PE router decomposes into per-channel - **1:1 (future)** — 각 PE 라우터가 채널별 미니 라우터로 분해된다;
mini-routers; per-channel BW contention is modeled directly. 채널별 BW 경합을 직접 모델링한다.
In both modes the per-PE effective BW is identical; only the connectivity 두 모드 모두 PE당 유효 BW는 동일하다; 연결 입도만 다르다.
granularity differs.
## Decision ## Decision
### D1. 2D router mesh ### D1. 2D 라우터 메시
Each cube contains a 2D mesh of NOC routers generated by `mesh_gen.py`. 각 큐브는 `mesh_gen.py`가 생성하는 2D 라우터 메시를 포함한다.
- Node naming: `sip{S}.cube{C}.r{row}c{col}` (e.g., `sip0.cube0.r0c0`). - 노드 명명: `sip{S}.cube{C}.r{row}c{col}` (예: `sip0.cube0.r0c0`).
- Implementation: `forwarding_v1`. NOC `overhead_ns = 0`. - 구현: `forwarding_v1`. NoC `overhead_ns = 0`.
- Default 6×6 grid (sized from PE corner placement + UCIe attachment - 기본 6×6 그리드 (PE 코너 배치 + UCIe 부착 개수로 산정); 더 큰 PE
count); larger PE counts scale the grid up. 개수는 그리드를 확장한다.
- HBM exclusion zone: center rows/columns are excluded where HBM die - HBM 제외 영역: HBM 다이가 물리적으로 점유하는 중앙 행/열을 제외한다
physically occupies space (e.g., r2c2, r2c3, r3c2, r3c3 for a 6×6). (예: 6×6의 경우 r2c2, r2c3, r3c2, r3c3).
- Latency = Manhattan distance × `ns_per_mm`. - 레이턴시 = Manhattan 거리 × `ns_per_mm`.
### D2. XY routing algorithm ### D2. XY 라우팅 알고리즘
Deterministic XY routing: 결정론적 XY 라우팅:
1. Horizontal segment: route from source X to destination X at source Y. 1. 수평 구간: 소스 X에서 목적지 X까지 소스 Y에서 라우팅.
2. Vertical segment: route from destination X at source Y to destination Y. 2. 수직 구간: 소스 Y의 목적지 X에서 목적지 Y까지 라우팅.
Each directed segment carries a unique key: 각 유향 구간은 고유 키를 운반한다:
- Horizontal: `("H", y_band, x_min, x_max, direction)` - 수평: `("H", y_band, x_min, x_max, direction)`
- Vertical: `("V", x_band, y_min, y_max, direction)` - 수직: `("V", x_band, y_min, y_max, direction)`
Grid positions are snapped to the router grid, excluding the HBM zone. 그리드 위치는 HBM 영역을 제외하고 라우터 그리드에 스냅된다.
### D3. Per-segment contention model ### D3. 구간별 경합 모델
Each directed XY segment is a `simpy.Resource(capacity=1)`. Transactions 각 유향 XY 구간은 `simpy.Resource(capacity=1)`이다. 동일 구간을 공유하는
sharing a segment (same row or column band, same direction) contend for 트랜잭션(동일한 행 또는 열 밴드, 동일한 방향)은 자원을 두고 경합한다 —
the resource — modelling link-level serialization in a wormhole-routed wormhole 라우팅 메시에서의 링크 수준 직렬화를 모델링한다.
mesh.
With no contention, NOC traversal latency equals Manhattan distance × 경합이 없을 때 NoC 순회 레이턴시는 Manhattan 거리 × `ns_per_mm`이다.
`ns_per_mm`. Under contention, SimPy's resource scheduling adds queueing 경합이 있을 때는 SimPy의 자원 스케줄링이 큐잉 지연을 추가한다.
delay.
### D4. NOC attachment points (per-PE HBM partition) ### D4. NoC 부착 지점 (PE HBM 파티션)
Every PE router carries three attachments: `pe{idx}.dma`, `pe{idx}.cpu`, 모든 PE 라우터는 세 개의 부착을 갖는다: `pe{idx}.dma`, `pe{idx}.cpu`,
and `pe{idx}.hbm`. The last is the per-PE HBM controller endpoint — 그리고 `pe{idx}.hbm`. 마지막은 PE HBM 컨트롤러 엔드포인트로
`sip{S}.cube{C}.hbm_ctrl.pe{idx}` — which owns one slice of the cube's `sip{S}.cube{C}.hbm_ctrl.pe{idx}`이며, 큐브 HBM의 한 슬라이스를
HBM (one pseudo-channel group; see D8). 소유한다 (하나의 pseudo-channel 그룹; D8 참조).
Other attachments: 기타 부착:
- M_CPU and shared SRAM each occupy a dedicated edge router. - M_CPU와 공유 SRAM은 각각 전용 edge 라우터를 점유한다.
- UCIe endpoints (N/S/E/W) each expose 4 connection routers distributed - UCIe 엔드포인트(N/S/E/W)는 각각 해당 변에 분산된 4개의 연결 라우터를
along that edge (see D6). 노출한다 (D6 참조).
```text ```text
UCIe-N (conn x4) UCIe-N (conn x4)
@@ -102,35 +98,34 @@ PE4.cpu <--+ +hbm.pe4| | +hbm.pe6+--< PE6.cpu
UCIe-S (conn x4) UCIe-S (conn x4)
``` ```
Per-PE HBM partitioning is the key invariant that makes local vs PE HBM 분할은 로컬 vs 크로스-PE HBM을 메시 거리로 구분 가능하게 만드는
cross-PE HBM distinguishable by mesh distance (see D7). 핵심 불변식이다 (D7 참조).
### D5. NOC edge bandwidths and distances ### D5. NoC 엣지 대역폭과 거리
| Connection | BW (GB/s) | Distance | Notes | | Connection | BW (GB/s) | Distance | Notes |
| ----------------------------- | ---------- | ------------- | ------------------------------------------- | | ----------------------------- | ---------- | ------------- | ------------------------------------------- |
| PE_DMA → NOC | 256.0 | Physical (PE) | Matches local-HBM aggregate BW | | PE_DMA → NOC | 256.0 | Physical (PE) | 로컬-HBM 집계 BW와 일치 |
| NOC → PE_CPU | — | 0.0 mm | Command path only | | NOC → PE_CPU | — | 0.0 mm | 명령 경로 전용 |
| Router ↔ hbm_ctrl.pe{idx} | 256.0 | 0.0 mm | Per PE router; N × per-channel BW (see D8) | | Router ↔ hbm_ctrl.pe{idx} | 256.0 | 0.0 mm | PE 라우터당; N × per-channel BW (D8 참조) |
| NOC ↔ M_CPU | — | 0.0 mm | Command path | | NOC ↔ M_CPU | — | 0.0 mm | 명령 경로 |
| NOC ↔ SRAM | 128.0 × 4 | 0.0 mm | 512 GB/s aggregate | | NOC ↔ SRAM | 128.0 × 4 | 0.0 mm | 512 GB/s 집계 |
| NOC ↔ UCIe conn | 128.0 | 0.0 mm | Per connection; 4 conn per port | | NOC ↔ UCIe conn | 128.0 | 0.0 mm | 연결당; 포트당 4 conn |
`0.0 mm` distances reflect the distributed nature of the NOC; actual `0.0 mm` 거리는 NoC의 분산 특성을 반영한다; 실제 순회 거리는 라우터
traversal distance is computed via Manhattan distance within the router 그리드 내에서 Manhattan 거리로 계산된다.
grid.
### D6. UCIe decomposition and inter-cube traffic ### D6. UCIe 분해와 큐브 간 트래픽
Each of the 4 UCIe ports (N, S, E, W) decomposes into: 4개의 UCIe 포트(N, S, E, W) 각각은 다음으로 분해된다:
- 1 `ucie-{PORT}` node: UCIe protocol endpoint (`overhead = 8.0 ns`). - `ucie-{PORT}` 노드 1개: UCIe 프로토콜 엔드포인트 (`overhead = 8.0 ns`).
- 4 `ucie-{PORT}.conn{0-3}` nodes: connection bridges between NOC and UCIe. - `ucie-{PORT}.conn{0-3}` 노드 4개: NoC와 UCIe 간 연결 브리지.
This decomposition gives 4 independent NOC↔UCIe connections per port, 이 분해로 포트당 4개의 독립 NoC↔UCIe 연결이 생성되며, 각각 128 GB/s
each with 128 GB/s bandwidth (512 GB/s aggregate per port). 대역폭을 갖는다 (포트당 집계 512 GB/s).
Inter-cube traffic path: 큐브 간 트래픽 경로:
```text ```text
Source: PE_DMA → NOC → conn{i} → ucie-{PORT} Source: PE_DMA → NOC → conn{i} → ucie-{PORT}
@@ -138,56 +133,56 @@ Source: PE_DMA → NOC → conn{i} → ucie-{PORT}
Target: ucie-{PORT} → conn{i} → r{x}c{y} → (mesh hops) → hbm_ctrl.pe{idx} Target: ucie-{PORT} → conn{i} → r{x}c{y} → (mesh hops) → hbm_ctrl.pe{idx}
``` ```
UCIe overhead (8.0 ns) is applied at each `ucie-{PORT}` node, so a full UCIe 오버헤드(8.0 ns)는 각 `ucie-{PORT}` 노드에서 적용되므로 전체 횡단은
crossing incurs 16 ns (TX port + RX port). 16 ns(TX 포트 + RX 포트)가 소요된다.
### D7. Data paths through the NOC ### D7. NoC를 통한 데이터 경로
All intra-cube traffic uses the same router mesh — no separate fast 모든 큐브 내부 트래픽은 동일한 라우터 메시를 사용한다 — 별도의 fast path는
paths. 없다.
**Local HBM** (same PE's own partition; 0 mesh hops): **로컬 HBM** (동일 PE의 자신 파티션; 0 메시 hop):
```text ```text
PE_DMA → r{x}c{y} → hbm_ctrl.pe{idx} (switching overhead only) PE_DMA → r{x}c{y} → hbm_ctrl.pe{idx} (switching overhead only)
``` ```
**Cross-PE HBM within cube** (target PE's partition, reached by mesh): **큐브 내 크로스-PE HBM** (대상 PE의 파티션, 메시로 도달):
```text ```text
PE_DMA → r{x}c{y} → (mesh hops) → r{x'}c{y'} → hbm_ctrl.pe{idx'} PE_DMA → r{x}c{y} → (mesh hops) → r{x'}c{y'} → hbm_ctrl.pe{idx'}
``` ```
Example: PE0 (on `r0c0`) accessing PE2's HBM (PE2 on `r1c4`): 예시: PE0(`r0c0` 위)이 PE2 HBM(PE2 `r1c4` 위)에 접근:
```text ```text
PE0.pe_dma → r0c0 → r0c1 → r0c2 → r0c3 → r0c4 → r1c4 → hbm_ctrl.pe2 PE0.pe_dma → r0c0 → r0c1 → r0c2 → r0c3 → r0c4 → r1c4 → hbm_ctrl.pe2
``` ```
Dijkstra computes the shortest path within the mesh. Dijkstra가 메시 내 최단 경로를 계산한다.
**Cross-cube HBM** (UCIe traversal): **큐브 간 HBM** (UCIe 횡단):
```text ```text
PE_DMA → r{x}c{y} → conn → ucie-{PORT} → [seam] → ucie-{PORT'} → conn PE_DMA → r{x}c{y} → conn → ucie-{PORT} → [seam] → ucie-{PORT'} → conn
→ r{x'}c{y'} → hbm_ctrl.pe{idx'} → r{x'}c{y'} → hbm_ctrl.pe{idx'}
``` ```
**Kernel launch command to PE**: **PE로의 커널 launch 명령**:
```text ```text
[from io_noc] → ucie → conn → r{x}c{y} → (mesh) → M_CPU → (mesh) → PE_CPU [from io_noc] → ucie → conn → r{x}c{y} → (mesh) → M_CPU → (mesh) → PE_CPU
``` ```
**Shared SRAM access**: **공유 SRAM 접근**:
```text ```text
PE_DMA → r{x}c{y} → (mesh) → SRAM PE_DMA → r{x}c{y} → (mesh) → SRAM
``` ```
### D8. HBM channel mapping mode ### D8. HBM 채널 매핑 모드
Channel mapping is configured at cube scope: 채널 매핑은 큐브 범위에서 구성된다:
```yaml ```yaml
cube: cube:
@@ -200,37 +195,35 @@ cube:
hbm_total_gb_per_cube: 48 hbm_total_gb_per_cube: 48
``` ```
**n:1 mode (default, implemented).** Each PE's HBM partition is a single **n:1 모드 (default, 구현됨).** PE HBM 파티션은 `channels_per_pe`
endpoint `hbm_ctrl.pe{idx}` that aggregates `channels_per_pe` pseudo- pseudo-channel을 집계하는 단일 엔드포인트 `hbm_ctrl.pe{idx}`이다.
channels. The `Router ↔ hbm_ctrl.pe{idx}` link bandwidth equals `Router ↔ hbm_ctrl.pe{idx}` 링크 대역폭은 `channels_per_pe ×
`channels_per_pe × hbm_channel_bw_gbs`. Pseudo-channels are assumed to hbm_channel_bw_gbs`와 같다. Pseudo-channel은 인터리브된다고 가정하며,
interleave; only aggregate per-PE BW is modeled. No separate aggregated PE당 집계 BW만 모델링한다. 별도의 집계 라우터 노드는 존재하지 않는다 —
router node exists — the per-PE router itself serves that role. PE별 라우터 자체가 그 역할을 한다.
**1:1 mode (future).** Each PE router decomposes into N channel **1:1 모드 (future).** 각 PE 라우터가 N개의 채널 미니 라우터로
mini-routers; per-channel routing carries fully-resolved PA + channel ID. 분해된다; 채널별 라우팅이 완전히 해석된 PA + channel ID를 운반한다.
A `ChannelSplitter` resolves a logical access to N per-channel physical `ChannelSplitter`가 논리적 접근을 N개의 채널별 물리 요청으로 해결한다.
requests. Per-channel link models BW contention. Cross-PE channel 채널별 링크가 BW 경합을 모델링한다. 크로스-PE 채널 접근 시맨틱은
access semantics are deferred to the implementation ADR. 구현 ADR로 연기된다.
**BW math (defaults).** **BW 계산 (default).**
| Parameter | Value | | Parameter | Value |
| ---------------------------------- | -------------------------- | | ---------------------------------- | -------------------------- |
| pseudo channels per cube | 64 (parameter) | | 큐브당 pseudo channel | 64 (parameter) |
| PEs per cube | 8 (parameter) | | 큐브당 PE | 8 (parameter) |
| channels per PE (N) | 64 / 8 = 8 | | PE당 channel (N) | 64 / 8 = 8 |
| per-channel BW | 32 GB/s (parameter) | | 채널당 BW | 32 GB/s (parameter) |
| per-PE local BW | N × 32 = 256 GB/s | | PE당 로컬 BW | N × 32 = 256 GB/s |
| cube total HBM BW | 64 × 32 = 2048 GB/s | | 큐브 전체 HBM BW | 64 × 32 = 2048 GB/s |
Both modes give the same per-PE effective BW; only the request shape and 두 모드 모두 PE당 유효 BW는 동일하다; 요청 형태와 경합 모델만 다르다.
contention model differ.
### D9. AddressResolver — per-PE HBM endpoint ### D9. AddressResolver — PE HBM 엔드포인트
The address resolver decodes a PA's HBM offset to the owning PE's 주소 리졸버는 PA HBM 오프셋을 소유 PE의 파티션으로 디코딩한다:
partition:
```python ```python
# policy/routing/router.py # policy/routing/router.py
@@ -241,51 +234,49 @@ if addr.kind == "hbm":
return f"sip{s}.cube{d}.hbm_ctrl.pe{pe_id}" return f"sip{s}.cube{d}.hbm_ctrl.pe{pe_id}"
``` ```
The pe_id computation is intrinsic to the routing layer (not a pe_id 계산은 라우팅 레이어의 본질적 일부이다 (토폴로지 시점 관심사가
topology-time concern). Any HBM PA falls within exactly one partition, 아니다). 모든 HBM PA는 정확히 하나의 파티션에 속하므로 결정론적 라우팅이
yielding deterministic routing. 보장된다.
External callers (e.g., M_CPU DMA, Memory R/W from PCIE_EP) follow the 외부 호출자(예: M_CPU DMA, PCIE_EP로부터의 Memory R/W)도 동일한 리졸버
same resolver path — there is no separate fast path. 경로를 따른다 — 별도의 fast path는 존재하지 않는다.
### D10. Mesh generation parameters ### D10. 메시 생성 파라미터
`mesh_gen.py` produces `cube_mesh.yaml` from: `mesh_gen.py`는 다음으로부터 `cube_mesh.yaml`을 생성한다:
- `cube.pe_layout`: corner placement (NW, NE, SW, SE) and PEs per corner. - `cube.pe_layout`: 코너 배치(NW, NE, SW, SE)와 코너당 PE 개수.
- `cube.geometry`: cube physical dimensions and HBM zone. - `cube.geometry`: 큐브 물리 치수와 HBM 영역.
- `cube.ucie.n_connections`: determines router count for UCIe attachment. - `cube.ucie.n_connections`: UCIe 부착용 라우터 개수를 결정.
Output `mesh_data` dictionary contains: 출력 `mesh_data` 딕셔너리는 다음을 포함한다:
- Router grid with positions and HBM exclusion zones. - 위치 및 HBM 제외 영역을 갖는 라우터 그리드.
- PE-to-router attachments (`pe{idx}.dma`, `pe{idx}.cpu`, `pe{idx}.hbm` - PE-라우터 부착 (PE별 `pe{idx}.dma`, `pe{idx}.cpu`, `pe{idx}.hbm`).
per PE). - UCIe-라우터 부착 (N/S/E/W가 edge 라우터에 분산).
- UCIe-to-router attachments (N/S/E/W distributed across edge routers). - M_CPU와 SRAM 라우터 부착.
- M_CPU and SRAM router attachments.
## Consequences ## Consequences
- Local HBM (0 mesh hops, switching overhead only) and cross-PE HBM - 로컬 HBM(0 메시 hop, 스위칭 오버헤드만)과 크로스-PE HBM(메시 hop)이
(mesh hops) are naturally distinguishable, satisfying SPEC R5 자연스럽게 구분되어 SPEC R5(다중 도메인 통신)와 ADR-0002(end-to-end
(multi-domain communication) and ADR-0002 (no zero-latency end-to-end 제로 레이턴시 경로 금지)를 만족한다.
paths). - 모든 큐브 내부 트래픽이 하나의 메시를 통해 라우팅된다 — 단일 경합
- All cube-internal traffic routes through one mesh — single contention 모델, 단일 레이아웃, 단일 엣지 BW 집합.
model, single layout, single set of edge BWs. - PE별 HBM 분할이 LA 모델(ADR-0011)에 깔끔하게 매핑된다: 각 PE의
- Per-PE HBM partitioning maps cleanly to the LA model (ADR-0011): each 파티션은 할당된 pseudo-channel의 n:1 집계이다.
PE's partition is the n:1 aggregate of its assigned pseudo-channels. - 1:1 모드 확장이 구조적으로 자연스럽다 — 각 PE 라우터를 N개의 채널
- 1:1 mode extension is structurally natural — split each PE router into 라우터로 분해한다.
N channel routers. - 메시 생성이 `topology.yaml`로 완전히 파라미터화된다; PE/큐브 기하
- Mesh generation is fully parameterised by `topology.yaml`; PE/cube 변경이 코드 수정 없이 전파된다.
geometry changes propagate without code edits.
## Links ## Links
- ADR-0002 (Routing distance, ordering, no zero-latency paths) - ADR-0002 (라우팅 거리, 순서, 제로 레이턴시 경로 금지)
- ADR-0003 D3 (cube-level NOC definition — extended here) - ADR-0003 D3 (큐브 레벨 NoC 정의 — 본 ADR에서 확장)
- ADR-0004 (Memory semantics, local HBM) - ADR-0004 (메모리 시맨틱, 로컬 HBM)
- ADR-0011 (Memory addressing — LA model consumes per-PE partition) - ADR-0011 (메모리 주소 지정 — LA 모델이 PE별 파티션을 소비)
- ADR-0014 D1 (PE_DMA egress via router mesh) - ADR-0014 D1 (라우터 메시를 통한 PE_DMA egress)
- ADR-0015 D4 (fabric paths for Memory R/W and Kernel Launch) - ADR-0015 D4 (Memory R/W Kernel Launch의 패브릭 경로)
- ADR-0016 (IOChiplet io_noc — analogous pattern at IO chiplet level) - ADR-0016 (IOChiplet io_noc — IO 칩렛 레벨에서의 유사 패턴)
- ADR-0033 (Latency model: per-PC parallelism, switch penalty) - ADR-0033 (레이턴시 모델: PC당 병렬성, 스위치 패널티)
+34 -24
View File
@@ -1,4 +1,4 @@
# ADR-0022: 2D Grid program_id Semantics # ADR-0022: 2D 그리드 program_id 시맨틱
## Status ## Status
@@ -6,38 +6,43 @@ Accepted
## Context ## Context
Triton kernels use `tl.program_id(axis)` to identify their position in a launch grid. Triton 커널은 `tl.program_id(axis)`를 사용해 launch 그리드 내 자신의
Our hardware has a 2-level hierarchy: **cubes** contain **PEs**. 위치를 식별한다. 본 하드웨어는 2단계 계층을 갖는다: **큐브**가 **PE**를
The previous implementation ignored the `axis` parameter and always returned a flat PE index, 포함한다. 이전 구현은 `axis` 파라미터를 무시하고 항상 평탄화된 PE
making it impossible for kernels to distinguish their cube-local position from their cube identity. 인덱스를 반환했기 때문에, 커널이 큐브 내부 위치와 큐브 식별자를 구분할
수 없었다.
## Decision ## Decision
Map `tl.program_id` and `tl.num_programs` to the 2D hardware grid: `tl.program_id` `tl.num_programs`를 2D 하드웨어 그리드에 매핑한다:
| Call | Returns | Description | | Call | Returns | Description |
|------|---------|-------------| |------|---------|-------------|
| `tl.program_id(axis=0)` | `local_pe_id` | PE index within cube | | `tl.program_id(axis=0)` | `local_pe_id` | 큐브 내 PE 인덱스 |
| `tl.program_id(axis=1)` | `cube_id` | Cube index | | `tl.program_id(axis=1)` | `cube_id` | 큐브 인덱스 |
| `tl.num_programs(axis=0)` | `num_pes_per_cube` | PEs per cube | | `tl.num_programs(axis=0)` | `num_pes_per_cube` | 큐브당 PE 개수 |
| `tl.num_programs(axis=1)` | `num_cubes` | Total cubes | | `tl.num_programs(axis=1)` | `num_cubes` | 전체 큐브 개수 |
Global PID is derived as: 전역 PID는 다음과 같이 도출된다:
```python ```python
global_pid = tl.program_id(axis=1) * tl.num_programs(axis=0) + tl.program_id(axis=0) global_pid = tl.program_id(axis=1) * tl.num_programs(axis=0) + tl.program_id(axis=0)
``` ```
### Axis mapping rationale ### 축 매핑 근거
- **axis=0 = PE (innermost)**: PEs within a cube share HBM and communicate via local NOC mesh. This is the fast, tightly-coupled dimension — analogous to threads within a block. - **axis=0 = PE (최내부)**: 큐브 내부 PE들은 HBM을 공유하고 로컬 NoC
- **axis=1 = Cube (outer)**: Cross-cube communication goes through UCIe with higher latency. This is the coarser scheduling dimension — analogous to blocks in a grid. 메시를 통해 통신한다. 빠르고 강하게 결합된 차원이다 — 블록 내부의
스레드와 유사하다.
- **axis=1 = 큐브 (외부)**: 큐브 간 통신은 더 높은 레이턴시의 UCIe를
통한다. 더 거친 스케줄링 차원이다 — 그리드 내의 블록과 유사하다.
## Implementation ## Implementation
### TLContext (`triton_emu/tl_context.py`) ### TLContext (`triton_emu/tl_context.py`)
Added `cube_id` and `num_cubes` constructor parameters. `program_id()` and `num_programs()` dispatch on `axis`: `cube_id` `num_cubes` 생성자 파라미터를 추가했다. `program_id()`
`num_programs()``axis`로 디스패치한다:
```python ```python
def program_id(self, axis: int = 0) -> int: def program_id(self, axis: int = 0) -> int:
@@ -53,18 +58,22 @@ def num_programs(self, axis: int = 0) -> int:
### PE_CPU (`components/builtin/pe_cpu.py`) ### PE_CPU (`components/builtin/pe_cpu.py`)
- Extracts `num_cubes` from `ctx.spec["system"]["sips"]["cubes_per_sip"]` - `ctx.spec["system"]["sips"]["cubes_per_sip"]`에서 `num_cubes`
- Passes `cube_id` (already available as `self._cube_idx`) and `num_cubes` to TLContext 추출한다.
- `cube_id`(이미 `self._cube_idx`로 사용 가능)와 `num_cubes`
TLContext에 전달한다.
### KernelRunner (`triton_emu/kernel_runner.py`) ### KernelRunner (`triton_emu/kernel_runner.py`)
- Receives `num_cubes` from PE_CPU - PE_CPU로부터 `num_cubes`를 수신한다.
- Passes `cube_id` and `num_cubes` to TLContext in greenlet mode - greenlet 모드에서 `cube_id` `num_cubes` TLContext에 전달한다.
## Backward Compatibility ## Backward Compatibility
- Existing code using `tl.program_id(0)` or `tl.program_id()` is unchanged — returns the same PE index as before. - `tl.program_id(0)` 또는 `tl.program_id()`를 사용하는 기존 코드는
- `cube_id` and `num_cubes` default to `0` and `1`, so callers that don't provide them (e.g. unit tests) continue to work. 변경되지 않는다 — 이전과 동일한 PE 인덱스를 반환한다.
- `cube_id``num_cubes`는 기본값이 `0``1`이므로, 이를 제공하지
않는 호출자(예: 유닛 테스트)도 계속 동작한다.
## Usage Example ## Usage Example
@@ -74,7 +83,7 @@ def sharded_gemm_kernel(a_ptr, b_ptr, out_ptr, M, K, N, tl):
cube_id = tl.program_id(axis=1) # which cube cube_id = tl.program_id(axis=1) # which cube
global_pid = cube_id * tl.num_programs(axis=0) + local_pid global_pid = cube_id * tl.num_programs(axis=0) + local_pid
# Column-wise sharding across global PID # 전역 PID에 걸친 column-wise 샤딩
n_per_pid = N // (tl.num_programs(axis=1) * tl.num_programs(axis=0)) n_per_pid = N // (tl.num_programs(axis=1) * tl.num_programs(axis=0))
col_start = global_pid * n_per_pid col_start = global_pid * n_per_pid
@@ -86,5 +95,6 @@ def sharded_gemm_kernel(a_ptr, b_ptr, out_ptr, M, K, N, tl):
## Consequences ## Consequences
- Benchmarks can now express cube-aware sharding and addressing without hardcoding topology dimensions. - 벤치마크가 토폴로지 차원을 하드코딩하지 않고 큐브 인식 샤딩과 주소
- Future axis=2 (SIP-level) can be added following the same pattern if needed. 지정을 표현할 수 있다.
- 필요 시 axis=2(SIP 레벨)를 동일한 패턴을 따라 향후 추가할 수 있다.
+132 -133
View File
@@ -1,4 +1,4 @@
# ADR-0032: Intercube All-Reduce — pe0 cube-mesh reduce + multi-SIP exchange # ADR-0032: 큐브 간 All-Reduce — pe0 큐브-메시 리듀스 + 다중-SIP 교환
## Status ## Status
@@ -6,112 +6,109 @@ Accepted (supersedes ADR-0029).
## Context ## Context
### Goal ### 목표
Define a single all-reduce algorithm that exploits the topology hierarchy: 토폴로지 계층을 활용하는 단일 all-reduce 알고리즘을 정의한다: 각 SIP
cube mesh within each SIP (intercube) + inter-SIP exchange. One kernel, 내부의 큐브 메시(큐브 간) + SIP 간 교환. 단일 커널, 단일 SFR 구성
one SFR configuration path, driven by `topology.yaml` and `ccl.yaml`. 경로이며 `topology.yaml` `ccl.yaml`로 구동된다.
### Why replace ADR-0029 (hierarchical 3-level) ### ADR-0029(계층적 3-레벨)를 대체하는 이유
ADR-0029 proposed a 3-level (intra-cube → inter-cube → inter-SIP) algorithm ADR-0029는 시스템의 모든 PE가 참여하는 3-레벨(큐브 내 → 큐브 간 →
where every PE in the system participates. In practice this adds the SIP 간) 알고리즘을 제안했다. 실제로는 텐서가 큐브 내 PE 단위가 아니라
intra-cube PE-to-PE stage complexity (bidirectional reduce + chain broadcast) **큐브 단위로 샤딩되는** 일반적 워크로드 패턴과 맞지 않으면서, 큐브 내
without matching the common workload pattern where the tensor is sharded PE-PE stage 복잡성(양방향 reduce + 체인 브로드캐스트)을 추가한다.
**per cube** (not per PE within a cube).
Moreover, the hierarchical design required: 또한 계층적 설계는 다음을 요구했다:
- per-PE neighbor graph installation (`_build_pe_installs` multi-level) - PE별 이웃 그래프 설치 (`_build_pe_installs` 다중 레벨)
- multi-level topology schema (`hierarchical_3level`) - 다중 레벨 토폴로지 스키마 (`hierarchical_3level`)
- `all_pes` mapper + `multi_pe_sip_local` validator infrastructure - `all_pes` 매퍼 + `multi_pe_sip_local` 검증자 인프라
The intercube algorithm below removes all of that: **pe0-only same-lane 아래의 큐브 간 알고리즘은 이 모든 것을 제거한다: **4×4 큐브 메시 위에서
intercube reduce on the 4×4 cube mesh**, then inter-SIP exchange on the pe0만의 same-lane 큐브 간 reduce**, 그 다음 루트 큐브에서 SIP 간 교환,
root cube, then broadcast back. Simpler kernel, simpler wiring, same 그 다음 다시 브로드캐스트. 더 단순한 커널, 더 단순한 와이어링,
bandwidth characteristics for the common per-cube DP workload. 일반적인 큐브당 DP 워크로드에 대해 동일한 대역폭 특성을 갖는다.
### Current state ### 현재 상태
- `src/kernbench/ccl/algorithms/intercube_allreduce.py`kernel - `src/kernbench/ccl/algorithms/intercube_allreduce.py`커널
- `src/kernbench/ccl/sfr_config.py``configure_sfr_intercube_multisip` - `src/kernbench/ccl/sfr_config.py``configure_sfr_intercube_multisip`
- `src/kernbench/runtime_api/distributed.py``AhbmCCLBackend` wires this - `src/kernbench/runtime_api/distributed.py``AhbmCCLBackend`
automatically at `init_process_group` time. `init_process_group` 시점에 자동으로 와이어링한다.
- Old `ring_allreduce`, `mesh_allreduce`, `tree_allreduce`, - 기존 `ring_allreduce`, `mesh_allreduce`, `tree_allreduce`,
`hierarchical_allreduce` modules and their tests are **removed**. `hierarchical_allreduce` 모듈과 그 테스트는 **제거됨**.
--- ---
## Decision ## Decision
### D1. Algorithm structure — 5 phases ### D1. 알고리즘 구조 — 5단계
For each SIP (launched concurrently by `mp.spawn`): 각 SIP에 대해 (`mp.spawn`으로 동시에 launch):
``` ```
Phase 1 — Row reduce W → E (cube mesh, pe0 only): Phase 1 — Row reduce W → E (큐브 메시, pe0만):
col=0 sends E → col=1 accumulates, sends E → ... → col=3 holds row sum. col=0이 E로 송신 → col=1이 누적, E로 송신 → ... → col=3 row sum 보유.
Phase 2 — Col reduce N → S on rightmost column (pe0, col = mesh_w-1): Phase 2 — 최우측 열에서 Col reduce N → S (pe0, col = mesh_w-1):
row=0 sends S → row=1 accumulates, sends S → ... → root cube (15) row=0이 S로 송신 → row=1이 누적, S로 송신 → ... → 루트 큐브 (15)
holds the full SIP sum. 전체 SIP sum 보유.
Phase 3 — Inter-SIP exchange on root cube (pe0 of root cube only): Phase 3 — 루트 큐브에서 SIP 간 교환 (루트 큐브의 pe0만):
Ring / torus-2d row+col ring / mesh-2d chain reduce+broadcast — Ring / torus-2d row+col ring / mesh-2d chain reduce+broadcast —
selected by sip_topo_kind (from topology.yaml sips.topology). sip_topo_kind(topology.yaml sips.topology)로 선택.
Phase 4 — Col broadcast S → N on rightmost column. Phase 4 — 최우측 열에서 Col 브로드캐스트 S → N.
Phase 5 — Row broadcast E → W across the cube mesh. Phase 5 — 큐브 메시 전반에 걸친 Row 브로드캐스트 E → W.
``` ```
After all phases every cube's pe0 holds the global sum. 모든 단계가 끝나면 모든 큐브의 pe0이 전역 sum을 보유한다.
The kernel is a single function parameterised by `sip_topo_kind ∈ {0, 1, 2}` 커널은 `sip_topo_kind ∈ {0, 1, 2}`(ring_1d, torus_2d, mesh_2d_no_wrap)로
(ring_1d, torus_2d, mesh_2d_no_wrap). Phases 1-2 and 4-5 are identical 파라미터화된 단일 함수이다. Phase 1-2와 4-5는 토폴로지 전반에서 동일하며,
across topologies; only phase 3 branches. Helper functions phase 3만 분기한다. 헬퍼 함수 `_inter_sip_ring`, `_inter_sip_torus_2d`,
`_inter_sip_ring`, `_inter_sip_torus_2d`, `_inter_sip_mesh_2d` encode the `_inter_sip_mesh_2d`가 세 가지 교환 패턴을 인코딩한다.
three exchange patterns.
### D2. Tensor layout (rank = SIP, per-worker) ### D2. 텐서 레이아웃 (rank = SIP, 워커별)
Per ADR-0024 rank = SIP at the process-group level. Each worker allocates ADR-0024에 따라 프로세스 그룹 레벨에서 rank = SIP이다. 각 워커가
its own cube-mesh-spanning tensor: 자신의 큐브-메시 전체 텐서를 할당한다:
```python ```python
dp = DPPolicy(cube="row_wise", pe="replicate", num_cubes=16, num_pes=1) dp = DPPolicy(cube="row_wise", pe="replicate", num_cubes=16, num_pes=1)
tensor = torch.zeros((n_cubes, n_elem), dtype="f16", dp=dp) tensor = torch.zeros((n_cubes, n_elem), dtype="f16", dp=dp)
``` ```
Shard layout: 16 shards per SIP, one per cube on pe0. The kernel addresses 샤드 레이아웃: SIP당 16개 샤드, 큐브별 pe0에 하나씩. 커널은 각 큐브의
each cube's shard as `pe_addr = t_ptr + cube_id * n_elem * 2`. 샤드를 `pe_addr = t_ptr + cube_id * n_elem * 2`로 주소 지정한다.
### D3. SFR / IPCQ wiring — `configure_sfr_intercube_multisip` ### D3. SFR / IPCQ 와이어링 — `configure_sfr_intercube_multisip`
Replaces the rank-to-2-PE install from ADR-0024. Wires PE_IPCQ neighbor ADR-0024의 rank-to-2-PE 설치를 대체한다. 어느 큐브가 루트인지 또는 어느
tables for **every cube's pe0 across every SIP** — regardless of which SIP 토폴로지가 선택되었는지와 무관하게 **모든 SIP의 모든 큐브의 pe0**에
cube is the root or which SIP topology is selected. This lets the kernel 대해 PE_IPCQ 이웃 테이블을 와이어링한다. 이를 통해 커널이 런타임에 루트
elect the root cube at runtime and supports topology switches without 큐브를 선출할 수 있고, 재와이어링 없이 토폴로지 전환을 지원한다.
re-wiring.
| Level | Direction labels | Scope | | Level | Direction labels | Scope |
|---|---|---| |---|---|---|
| Intercube within SIP | N / S / E / W | pe0 of every cube → pe0 of mesh neighbors (no wrap) | | SIP 내부 큐브 간 | N / S / E / W | 모든 큐브의 pe0 → 메시 이웃의 pe0 (랩어라운드 없음) |
| Inter-SIP (all cubes) | global_E / global_W / global_N / global_S | pe0 of cube c on sip A → pe0 of cube c on peer SIP per `sips.topology` | | SIP 간 (모든 큐브) | global_E / global_W / global_N / global_S | sip A의 큐브 c의 pe0 → `sips.topology`에 따른 피어 SIP의 큐브 c의 pe0 |
Inter-SIP directions use the `global_*` prefix to keep the namespace SIP 간 방향은 `global_*` 접두사를 사용하여 큐브 간 방향과 네임스페이스를
disjoint from intercube directions. ADR-0025's `_OPPOSITE_DIR` is extended 분리한다. ADR-0025 `_OPPOSITE_DIR``global_E ↔ global_W`
with `global_E ↔ global_W` and `global_N ↔ global_S` so the reverse- `global_N ↔ global_S`로 확장되어, 2-SIP 양방향 ring에 대한 역방향
direction resolver handles 2-SIP bidirectional rings correctly. 리졸버가 올바르게 처리되도록 한다.
Internally the function calls `install_ipcq` with: 내부적으로 이 함수는 다음 인자로 `install_ipcq`를 호출한다:
- `world_size = n_sips × n_cubes` - `world_size = n_sips × n_cubes`
- `rank_to_pe = [(sip, cube, 0) for sip in range(n_sips) for cube in range(n_cubes)]` - `rank_to_pe = [(sip, cube, 0) for sip in range(n_sips) for cube in range(n_cubes)]`
- A closure-captured `neighbors()` function that builds the map above. - 위 매핑을 생성하는 클로저로 캡처된 `neighbors()` 함수.
This `world_size` is internal to IPCQ wiring and does not leak to the `world_size`는 IPCQ 와이어링 내부적이며 프로세스-그룹 rank로 유출되지
process-group rank. 않는다.
### D4. SIP topology — from `topology.yaml` ### D4. SIP 토폴로지 — `topology.yaml`에서
```yaml ```yaml
system: system:
@@ -120,35 +117,36 @@ system:
topology: ring_1d # or torus_2d, mesh_2d_no_wrap topology: ring_1d # or torus_2d, mesh_2d_no_wrap
``` ```
- `ring_1d`: n_sips-1 rounds of `send global_E / recv global_W`. - `ring_1d`: n_sips-1 라운드의 `send global_E / recv global_W`.
- `torus_2d`: sqrt(n_sips)×sqrt(n_sips) wrapping mesh. Row ring on - `torus_2d`: sqrt(n_sips)×sqrt(n_sips) 랩핑 메시. `global_E/W`에서
`global_E/W` then col ring on `global_S/N`. row ring, 이어서 `global_S/N`에서 col ring.
- `mesh_2d_no_wrap`: square mesh without wrap-around. Chain reduce + - `mesh_2d_no_wrap`: 랩어라운드 없는 정사각형 메시. 차원별 chain
broadcast per dimension. reduce + 브로드캐스트.
2D variants require `n_sips` to be a perfect square. 2D 변형은 `n_sips`가 완전 제곱수여야 한다.
### D5. Process-group integration — `AhbmCCLBackend` ### D5. 프로세스-그룹 통합 — `AhbmCCLBackend`
At `init_process_group` time the backend: `init_process_group` 시점에 백엔드는:
1. Loads `ccl.yaml` + `topology.yaml`. 1. `ccl.yaml` + `topology.yaml`을 로드한다.
2. Derives `sip_topo_kind, sip_topo_w, sip_topo_h` from 2. 알고리즘 모듈의 `TOPO_NAME_TO_KIND`를 사용하여
`system.sips.topology` using the algorithm module's `TOPO_NAME_TO_KIND`. `system.sips.topology`로부터 `sip_topo_kind, sip_topo_w, sip_topo_h`
3. Calls `configure_sfr_intercube_multisip(engine, spec, cfg)` — one-time 도출한다.
SFR wiring, mirrors NCCL communicator creation. 3. `configure_sfr_intercube_multisip(engine, spec, cfg)`를 호출한다 —
일회성 SFR 와이어링, NCCL 커뮤니케이터 생성을 모방한다.
At each `dist.all_reduce(tensor)` call: `dist.all_reduce(tensor)` 호출 시:
1. Resolves `kernel_fn` from `cfg["module"]`. 1. `cfg["module"]`로부터 `kernel_fn`을 해석한다.
2. Builds args: `(n_elem, cube_w, cube_h, n_sips)` from 2. `kernel_args(world_size, n_elem)`로부터 인자
`kernel_args(world_size, n_elem)`. `(n_elem, cube_w, cube_h, n_sips)`를 구성한다.
3. Appends `(sip_rank, sip_topo_kind, sip_topo_w, sip_topo_h)` where 3. `(sip_rank, sip_topo_kind, sip_topo_w, sip_topo_h)`를 추가하며,
`sip_rank` is the current greenlet's bound rank. 여기서 `sip_rank`는 현재 greenlet에 바인딩된 rank이다.
4. Launches with `_defer_wait=True`; the main scheduler drains pending 4. `_defer_wait=True`로 launch; 모든 워커가 제출한 후 메인 스케줄러가
handles after all workers submit (per ADR-0027 D0.4). pending 핸들을 드레인한다 (ADR-0027 D0.4).
### D6. Config schema ### D6. 구성 스키마
`ccl.yaml`: `ccl.yaml`:
@@ -178,38 +176,39 @@ sip:
cube_mesh: { w: 4, h: 4 } cube_mesh: { w: 4, h: 4 }
``` ```
### D7. Algorithm module contract ### D7. 알고리즘 모듈 계약
Modules loaded via `cfg["module"]` must export: `cfg["module"]`로 로드되는 모듈은 다음을 export해야 한다:
| Name | Purpose | | Name | Purpose |
|---|---| |---|---|
| `kernel` | callable, signature `(t_ptr, n_elem, cube_w, cube_h, n_sips, sip_rank, sip_topo_kind, sip_topo_w, sip_topo_h, tl)` | | `kernel` | callable, 시그니처 `(t_ptr, n_elem, cube_w, cube_h, n_sips, sip_rank, sip_topo_kind, sip_topo_w, sip_topo_h, tl)` |
| `kernel_args(world_size, n_elem) -> tuple` | returns the first 4 scalar args (per-tensor) | | `kernel_args(world_size, n_elem) -> tuple` | 처음 4개의 scalar 인자(텐서별) 반환 |
| `TOPO_NAME_TO_KIND: dict[str, int]` | maps `system.sips.topology` name to kernel branch code | | `TOPO_NAME_TO_KIND: dict[str, int]` | `system.sips.topology` 이름을 커널 분기 코드로 매핑 |
| `SIP_TOPO_RING`, `SIP_TOPO_TORUS`, `SIP_TOPO_MESH` | integer constants (0, 1, 2) | | `SIP_TOPO_RING`, `SIP_TOPO_TORUS`, `SIP_TOPO_MESH` | 정수 상수 (0, 1, 2) |
--- ---
## Dependencies ## Dependencies
- **ADR-0023**: IPCQ protocol (neighbor table, send/recv, credit return). - **ADR-0023**: IPCQ 프로토콜 (이웃 테이블, 송수신, credit 반환).
- **ADR-0024**: rank = SIP launcher, `mp.spawn`, greenlet-local rank. - **ADR-0024**: rank = SIP launcher, `mp.spawn`, greenlet-로컬 rank.
- **ADR-0025**: Address-based IPCQ direction matching; extended - **ADR-0025**: 주소 기반 IPCQ 방향 매칭; `global_*` 쌍으로 확장된
`_OPPOSITE_DIR` with `global_*` pairs. `_OPPOSITE_DIR`.
- **ADR-0027**: Worker-wait / collective-pending drain in main scheduler. - **ADR-0027**: 메인 스케줄러에서의 worker-wait / 집합 통신 pending
드레인.
## Non-goals ## Non-goals
- **Per-PE allreduce** (intra-cube PE-to-PE reduce). Out of scope — the - **PE allreduce** (큐브 내 PE-PE reduce). 범위 밖 — 본 알고리즘의
workload for this algorithm is per-cube DP. 워크로드는 큐브당 DP이다.
- **Asymmetric SIP topologies** (non-square mesh/torus). `torus_2d` and - **비대칭 SIP 토폴로지** (정사각형이 아닌 메시/토러스).
`mesh_2d_no_wrap` require `n_sips = k²`. `torus_2d``mesh_2d_no_wrap` `n_sips = k²`를 요구한다.
- **Pipelined chunks**: single-tile per cube, no pipelining yet. - **파이프라인 청크**: 큐브당 단일 타일, 아직 파이프라이닝 없음.
- **Root cube runtime election**: the kernel currently uses - **루트 큐브의 런타임 선출**: 커널은 현재 SE 코너로 하드코딩된
`root_cube = (mesh_h - 1) * mesh_w + (mesh_w - 1)` hardcoded to the SE `root_cube = (mesh_h - 1) * mesh_w + (mesh_w - 1)`을 사용한다. SFR
corner. SFR wiring covers all cubes, so runtime election is a pure kernel 와이어링이 모든 큐브를 커버하므로, 필요해질 때 런타임 선출은 순수
change when needed. 커널 변경이다.
--- ---
@@ -217,24 +216,24 @@ Modules loaded via `cfg["module"]` must export:
### Positive ### Positive
- **Single kernel, single install path** for all-reduce — replaces four - **단일 커널, 단일 설치 경로**로 all-reduce를 처리 — 제거된 네 개의
removed modules (`ring`, `mesh`, `tree`, `hierarchical`). 모듈(`ring`, `mesh`, `tree`, `hierarchical`)을 대체한다.
- **Topology-agnostic kernel**: ring / torus / mesh selected via one - **토폴로지 무관 커널**: ring / torus / mesh를 정수 파라미터 하나로
integer param, no kernel duplication. 선택, 커널 중복 없음.
- **Automatic via `dist.all_reduce`**: no bench-level or user-level - **`dist.all_reduce`를 통한 자동화**: 벤치 레벨이나 사용자 레벨의
algorithm selection needed; config-driven end-to-end. 알고리즘 선택 불필요; end-to-end 구성 기반.
- **Full SFR wiring**: every cube on every SIP has inter-SIP links - **완전한 SFR 와이어링**: 모든 SIP의 모든 큐브가 SIP 간 링크를 보유 —
available — supports future dynamic root-cube election. 향후 동적 루트 큐브 선출을 지원한다.
### Negative ### Negative
- **Not suitable for per-PE sharded tensors**: TP-layer-style tensors that - **PE별 샤딩된 텐서에 부적합**: 큐브 하나 내부에서 8개 PE에 걸쳐
shard within one cube across 8 PEs are not addressable by this kernel. 샤딩되는 TP-레이어 스타일 텐서는 본 커널로 주소 지정할 수 없다. 이러한
Such workloads would need a separate intra-cube all-reduce path (not 워크로드에는 별도의 큐브 내 all-reduce 경로가 필요하다 (아직 구현되지
yet implemented). 않음).
- **`configure_sfr_intercube_multisip` always wires all pe0s**: even if a - **`configure_sfr_intercube_multisip`는 항상 모든 pe0을 와이어링**:
given run only needs a subset (e.g. 1 SIP, ring only). Install cost is 주어진 실행이 부분집합(예: 1 SIP, ring만)만 필요하더라도. 설치 비용은
small but not zero. 작지만 영(zero)은 아니다.
--- ---
@@ -242,15 +241,15 @@ Modules loaded via `cfg["module"]` must export:
| File | Change | | File | Change |
|---|---| |---|---|
| `src/kernbench/ccl/algorithms/intercube_allreduce.py` (new) | Kernel + `_inter_sip_*` helpers + `TOPO_NAME_TO_KIND` | | `src/kernbench/ccl/algorithms/intercube_allreduce.py` (신규) | 커널 + `_inter_sip_*` 헬퍼 + `TOPO_NAME_TO_KIND` |
| `src/kernbench/ccl/sfr_config.py` (new) | `configure_sfr_intercube_multisip` | | `src/kernbench/ccl/sfr_config.py` (신규) | `configure_sfr_intercube_multisip` |
| `src/kernbench/ccl/topologies.py` | Added `torus_2d`, `mesh_2d_no_wrap` | | `src/kernbench/ccl/topologies.py` | `torus_2d`, `mesh_2d_no_wrap` 추가 |
| `src/kernbench/ccl/install.py` | Extended `_OPPOSITE_DIR` with `global_*` pairs | | `src/kernbench/ccl/install.py` | `_OPPOSITE_DIR` `global_*` 쌍으로 확장 |
| `src/kernbench/runtime_api/distributed.py` | `AhbmCCLBackend` uses `configure_sfr_intercube_multisip` + appends sip_rank/topo args | | `src/kernbench/runtime_api/distributed.py` | `AhbmCCLBackend` `configure_sfr_intercube_multisip` 사용 + sip_rank/topo 인자 추가 |
| `ccl.yaml` | Single `intercube_allreduce` entry | | `ccl.yaml` | 단일 `intercube_allreduce` 항목 |
| `topology.yaml` | Added `system.sips.topology` | | `topology.yaml` | `system.sips.topology` 추가 |
| `benches/ccl_allreduce.py` | Row-wise cube-mesh tensor layout | | `benches/ccl_allreduce.py` | Row-wise 큐브-메시 텐서 레이아웃 |
| `tests/test_allreduce_multidevice.py` (new) | Config-driven ring/torus/mesh | | `tests/test_allreduce_multidevice.py` (신규) | 구성 기반 ring/torus/mesh |
| `tests/test_distributed_intercube_allreduce.py` (new) | Full `dist.all_reduce` path | | `tests/test_distributed_intercube_allreduce.py` (신규) | 전체 `dist.all_reduce` 경로 |
| `tests/test_intercube_sfr_config.py` (new) | SFR wiring verification | | `tests/test_intercube_sfr_config.py` (신규) | SFR 와이어링 검증 |
| Removed | `ring_allreduce.py`, `mesh_allreduce.py`, `tree_allreduce.py`, `hierarchical_allreduce.py`, `hello_send.py`, `testing.py` and their tests | | 제거 | `ring_allreduce.py`, `mesh_allreduce.py`, `tree_allreduce.py`, `hierarchical_allreduce.py`, `hello_send.py`, `testing.py` 및 그 테스트 |
@@ -1,4 +1,4 @@
# ADR-0033 — Latency Model: Assumptions and Known Simplifications # ADR-0033 — 레이턴시 모델: 가정 및 알려진 단순화
## Status ## Status
@@ -6,157 +6,147 @@ Accepted
## Context ## Context
The simulator is an analytical, event-driven performance model — not a 이 시뮬레이터는 분석적·이벤트 기반 성능 모델이지, 사이클 정확(cycle-accurate)
cycle-accurate or RTL-level simulator. Many real-HW effects are approximated 시뮬레이터나 RTL 수준 시뮬레이터가 아니다. 실제 HW의 많은 효과들이 설계상
or omitted by design. To keep the model auditable and reviewable as a whole, 근사되거나 생략되었다. 모델 전체를 감사·리뷰할 수 있도록 유지하기 위해,
this ADR consolidates the assumptions in one place. Individual component ADRs 본 ADR은 그런 가정들을 한 곳에 통합한다. 개별 컴포넌트 ADR(ADR-0015,
(ADR-0015, ADR-0017, ADR-0004) define the *mechanisms*; this document defines ADR-0017, ADR-0004)들이 *메커니즘*을 정의하고, 본 문서는 *충실도의 한계*를
the *limits of fidelity*. 정의한다.
## Decisions ## Decisions
### D1. Modeled precisely ### D1. 정밀하게 모델링되는 것
- **Per-directed-edge BW occupancy** (FIFO serialization via `available_at`) — - **방향 에지별 BW 점유** (`available_at`을 통한 FIFO 직렬화) —
ADR-0015 D2. ADR-0015 D2.
- **Per-component switching/overhead latency** (`overhead_ns` attr). - **컴포넌트별 스위칭/오버헤드 레이턴시** (`overhead_ns` attr).
- **HBM per-pseudo-channel parallelism** via stateless `pc_avail[N]` array - **HBM pseudo-channel별 병렬성**: 주소 기반 PC 선택을 동반한
with address-based PC selection (ADR-0034 D3). Burst granularity tunable stateless `pc_avail[N]` 배열로 (ADR-0034 D3). 버스트 granularity는 조정 가능
(`burst_bytes`, default 256B). Read and write share each PC's (`burst_bytes`, 기본 256B). 각 PC의 `available_at`은 read와 write가 공유한다
`available_at` (real HW command bus is per-PC shared). (실제 HW의 명령 버스가 PC별로 공유되기 때문).
- **HBM direction switching penalty mechanism**: per-PC last-direction - **HBM 방향 전환 페널티 메커니즘**: PC last-direction 추적 +
tracking + configurable `switch_penalty_ns`. Default 0 — see D2. 설정 가능한 `switch_penalty_ns`. 기본값 0 — D2 참조.
- **Wire chunk-streaming (Phase 2c)**: each wire decomposes Transactions - **와이어 청크 스트리밍 (Phase 2c)**: 각 와이어는 payload가 있는
with payload into `Flit` objects of `flit_bytes` (default = HBM Transaction을 `flit_bytes` 단위의 `Flit` 객체로 분해한다(기본 = HBM
`burst_bytes` = 256B). The wire emits each flit individually after `burst_bytes` = 256B). 와이어는 각 flit을 `prop_ns + flit_nbytes/bw_gbs`
`prop_ns + flit_nbytes/bw_gbs` so the link's bandwidth throttles 이후에 개별적으로 방출하므로 링크의 대역폭이 실제 HW의 wormhole 시맨틱대로
flit arrival rate per real-HW wormhole semantics. flit 도착률을 조절한다.
- **Separate Stores per directed edge** (Phase 2c key fix): the wire - **방향 에지별로 분리된 Store** (Phase 2c 핵심 수정): 와이어는
is the *only* conduit between `src.out_ports[dst]` and `src.out_ports[dst]``dst.in_ports[src]` 사이의 *유일한* 통로이다.
`dst.in_ports[src]`. Earlier the two were aliased to the same 이전에는 둘이 동일한 `simpy.Store`로 별칭되어 있었다. 와이어가 청크화된
`simpy.Store`; when the wire put a chunkified flit back, the flit을 되돌려 넣을 때 목적지의 `fan_in`이 와이어가 대역폭 지연을 적용하기
destination's `fan_in` could pull it before the wire applied 전에 그것을 끌어가, flit의 절반이 병목을 우회할 수 있었다.
bandwidth delay, leaving half the flits bypassing the bottleneck. - **Flit 인지 pass-through** (`TransitComponent`, `HbmCtrlComponent`):
- **Flit-aware pass-through** (`TransitComponent`, `HbmCtrlComponent`): 각 flit을 직렬로 전달하며 트랜잭션 오버헤드는 첫 flit 도착 시점에 한 번만
forward each flit serially with per-transaction overhead applied 적용된다(헤더 디코드 모델). 이후의 flit들은 추가 지연 없이 파이프라인을
ONCE on the first-flit arrival (header decode model). Subsequent 통과한다. 다중 hop 경로 전반에서 wormhole이 자연스럽게 발현된다.
flits pipeline through with no extra delay. Wormhole emerges - **HBM CTRL의 flit별 PC commit**: HBM CTRL에 도착하는 각 flit은
naturally across multi-hop paths. `max(env.now, pc_avail[pc]) + chunk_time`에 PC commit을 스케줄하며,
- **HBM CTRL per-flit PC commit**: each flit arriving at HBM CTRL `is_last` flit이 마지막 PC commit을 기다린 후 `txn.done`을 신호한다.
schedules a PC commit at `max(env.now, pc_avail[pc]) + chunk_time`, - **Flit 비인지 컴포넌트(기본)는 ``_fan_in``에서 flit을 재조립**하여
with the `is_last` flit waiting for the last PC commit before 레거시 `_forward_txn` 경로가 실행되도록 한다. 이는 아직 flit 인지
signaling `txn.done`. 처리로 마이그레이션되지 않은 컴포넌트(예: `MCpuComponent`,
- **Non-flit-aware components (default) reassemble flits at `IoCpuComponent`의 sub-txn 생성기)에 대한 하위 호환성을 보존한다. 그런
``_fan_in``** before the legacy `_forward_txn` path runs. This 컴포넌트들은 *leg 경계마다 한 번* 재조립하며, hop마다는 아니다 —
preserves backward compatibility for components that have not yet flit 인지 라우터 체인을 통한 다중 hop wormhole 타이밍이 보존된다.
been migrated to flit-aware processing (e.g., `MCpuComponent`,
`IoCpuComponent` sub-txn generators). Such components reassemble
*once per leg boundary*, NOT per hop — multi-hop wormhole timing
through a chain of flit-aware routers is preserved.
### D2. Approximated (with known directional error) ### D2. 근사됨 (알려진 방향성 오차와 함께)
| Effect | Real HW | Our model | Error direction | | 효과 | 실제 HW | 본 모델 | 오차 방향 |
|--------|---------|-----------|----------------| |--------|---------|-----------|----------------|
| Router output port arbitration | Round-robin / weighted | Wire edge FIFO + serial worker | Fair when one txn per cycle; multi-stream sharing not modeled at flit level | | 라우터 출력 포트 중재 | Round-robin / weighted | 와이어 에지 FIFO + 직렬 워커 | 사이클당 한 txn일 때 공정; multi-stream 공유는 flit 수준에서 모델링 안 됨 |
| HBM scheduler / write buffer | FR-FCFS + watermark drain | FIFO, no reordering | Pessimistic for mixed R/W when alternations are dense — default `switch_penalty_ns = 0` assumes ideal scheduler amortizes | | HBM 스케줄러 / 쓰기 버퍼 | FR-FCFS + watermark drain | FIFO, 재정렬 없음 | 교번이 조밀한 혼합 R/W에 대해 비관적 — 기본 `switch_penalty_ns = 0`은 이상적 스케줄러가 amortize한다고 가정 |
| Flit ↔ burst granularity | 32B flit < 256B burst | `flit_bytes = burst_bytes = 256B` | Sub-flit fine-grained timing noise; affects very small wire arbitration windows only | | Flit ↔ burst granularity | 32B flit < 256B burst | `flit_bytes = burst_bytes = 256B` | sub-flit 미세 타이밍 노이즈; 매우 작은 와이어 중재 윈도우에서만 영향 |
| Wire-level RR fairness | Per-cycle multi-flow arbitration on shared link | Single serial wire process per edge | Fair only when one transaction is in flight on a given edge at a time. Multi-stream concurrent traffic on the same edge serializes by FIFO order | | 와이어 수준 RR 공정성 | 공유 링크에서 사이클별 multi-flow 중재 | 에지마다 단일 직렬 와이어 프로세스 | 주어진 에지에 한 트랜잭션만 in-flight일 때만 공정. 동일 에지에서 동시 멀티 스트림 트래픽은 FIFO 순서로 직렬화됨 |
### D3. Ignored (out of scope) ### D3. 무시됨 (범위 외)
- Bank-level row buffer conflict penalty (assume no conflicts — best case; - 뱅크 수준의 row buffer 충돌 페널티 (충돌 없음 가정 — 최적 케이스;
the model has no per-bank state within a PC, so same-bank reuse cannot be 모델은 PC 내부에 뱅크별 상태를 갖지 않으므로 동일 뱅크 재사용을 감지할 수 없다).
detected). - HBM tRP / tRCD / tFAW / tRC 타이밍 제약 (정상 상태의
- HBM tRP / tRCD / tFAW / tRC timing constraints (absorbed into the steady-state `burst_time = burst_bytes / pc_bw_gbs`에 흡수).
`burst_time = burst_bytes / pc_bw_gbs`). - 리프레시, ECC, 열 throttling, 전력 게이팅.
- Refresh, ECC, thermal throttling, power gating. - 클럭 도메인 교차, PLL lock 시간.
- Clock domain crossings, PLL lock time. - 하위 버퍼 점유로 인한 상위 backpressure (입력 포트는 unbounded
- Upstream backpressure due to downstream buffer occupancy (input ports use `simpy.Store`를 사용).
unbounded `simpy.Store`). - 라우터에서의 sub-flit 사이클 수준 중재 (flit granularity가 본 모델의
- Sub-flit cycle-level arbitration at routers (flit granularity is our 최소 단위).
smallest unit).
### D4. Workload sensitivity ### D4. 워크로드 민감도
Workloads where the above simplifications meaningfully affect results: 위 단순화들이 결과에 의미 있게 영향을 미치는 워크로드:
- **Random scatter/gather**: bank conflict ignored → model optimistic. - **무작위 scatter/gather**: 뱅크 충돌 무시 → 모델이 낙관적.
- **Heavy mixed R/W intensive** (e.g., GEMM bias accumulation): HBM scheduler - **혼합 R/W가 강한 워크로드** (예: GEMM 바이어스 누적): HBM 스케줄러
absent. With default `switch_penalty_ns = 0` we assume ideal amortization; 부재. 기본 `switch_penalty_ns = 0`은 이상적 amortization을 가정;
setting it non-zero models pessimistic per-alternation cost. 0이 아닌 값은 교번당 비관적 비용을 모델링.
- **High concurrency (>10 active flows on one link)**: HoL blocking and VC - **고동시성 (한 링크에 활성 흐름 >10개)**: HoL blocking과 VC 제한이
limits not modeled → model optimistic. 모델링되지 않음 → 모델이 낙관적.
- **Very small (sub-flit) transactions**: flit quantization noise. - **매우 작은(sub-flit) 트랜잭션**: flit 양자화 노이즈.
- **Concurrent multi-flow on a single wire**: wire is serial FIFO at the - **단일 와이어상의 동시 multi-flow**: 와이어는 flit 수준에서 직렬
flit level, so per-flow fairness within a single edge is not modeled. FIFO이므로 단일 에지 내에서의 흐름별 공정성은 모델링되지 않는다.
Pre-edge merging (multiple sources arriving at a router and being Pre-edge 병합(여러 source가 라우터에 도착하여 동일한 downstream
forwarded to the same downstream wire) is correctly modeled via the 와이어로 전달되는 경우)은 flit 인지 라우터의 직렬 워커를 통해 올바르게
flit-aware router's serial worker. 모델링된다.
### D5. Verification policy ### D5. 검증 정책
For workloads in D4, cross-check against real HW or a cycle-accurate D4의 워크로드에 대해 절대값 결론을 내리기 전에 실제 HW나 사이클 정확
simulator before drawing absolute-magnitude conclusions. The model remains 시뮬레이터와 cross-check 할 것. 모델은 모델링된 영역 내에서의 **상대적
accurate for **relative comparisons** within the modeled regime. 비교**에 대해서는 여전히 정확하다.
### D6. Future work ### D6. 향후 작업
Note: multi-stream merging at routers IS modeled correctly — each 참고: 라우터에서의 multi-stream 병합은 올바르게 모델링되고 있다 — 각
in_port has its own fan_in process, all push to a shared inbox, and in_port가 자신의 fan_in 프로세스를 가지며 모두 공유 인박스로 push하고,
the router worker forwards in inbox FIFO order. Flits from different 라우터 워커가 인박스 FIFO 순서로 전달한다. 서로 다른 상위 스트림의 flit들이
upstream streams naturally interleave at flit granularity. The items flit granularity에서 자연스럽게 인터리브된다. 아래 항목들은 별개의 관심사이며,
below are different concerns, ordered by expected workload impact. 예상되는 워크로드 영향 순으로 정렬되어 있다.
**Higher impact (workload accuracy gap)**: **영향이 큼 (워크로드 정확도 격차)**:
- [ ] **Bank-level conflict modeling** within a PC (opt-in via - [ ] PC 내의 **뱅크 수준 충돌 모델링** (`track_banks: true`로 opt-in).
`track_banks: true`). Currently we assume no same-bank reuse; 현재는 동일 뱅크 재사용이 없다고 가정; 무작위 scatter/gather 워크로드는
random scatter/gather workloads are optimistic here. 이 부분에서 낙관적이다.
- [ ] **HBM scheduler** with write buffer + watermark drain (Tier 2 - [ ] write buffer + watermark drain을 동반한 **HBM 스케줄러** (설계
from the design discussion). Default `switch_penalty_ns=0` is the 논의에서의 Tier 2). 기본 `switch_penalty_ns=0`은 이상적 amortization의
ideal-amortization stand-in; bursty mixed R/W workloads benefit stand-in; 버스티한 혼합 R/W 워크로드는 명시적 모델링으로부터 이득을 본다.
from explicit modeling. - [ ] 유한한 컴포넌트 버퍼에 대한 **Backpressure** 모델링. 버퍼 점유가
- [ ] **Backpressure** modeling for finite component buffers. Matters 상위 stall을 유발하는 고동시성/지속적 포화 상황에서 중요.
at high concurrency / sustained saturation where buffer occupancy - [ ] **청크 스트리밍과 op_log 통합**: 현재 op_log는 청크화되지 않는
causes upstream stalls. PE 내부 명령 메시지(DmaReadCmd, DmaWriteCmd, GemmCmd, MathCmd)에 대해
- [ ] **Op_log integration with chunk-streaming**: currently op_log 발화한다. 통합은 flit 인지 컴포넌트들이 트랜잭션당 op_log start/end
fires on PE-internal command messages (DmaReadCmd, DmaWriteCmd, hook(첫 flit에 start, is_last에 end)을 함께 방출하도록 요구한다.
GemmCmd, MathCmd) which are not chunkified. Integration would
require flit-aware components to also emit op_log start/end hooks
per transaction (start on first flit, end on is_last).
**Lower impact (academic / specific use cases)**: **영향이 작음 (학술적 / 특정 use case)**:
- [ ] **Cycle-accurate router arbitration policies** (RR with - [ ] **사이클 정확 라우터 중재 정책** (우선순위·age를 동반한 RR, iSLIP).
priorities, age, iSLIP). The FIFO inbox is already approximately FIFO 인박스는 스트림 간 flit 도착 시간이 약간씩 다를 때 이미 근사적으로
fair when flit arrival times differ slightly between streams (the 공정하다(유사한 비율의 워크로드에서 흔한 경우). 실질적 영향은 (a)
common case for similar-rate workloads). True impact appears only 우선순위/QoS 모델링, (b) 지속적 포화에서의 스트림별 tail latency 분석에서만
for: (a) priority/QoS modeling, (b) per-stream tail latency 나타난다. makespan이나 평균 레이턴시 연구에는 결정적이지 않음.
analysis under sustained saturation. Not critical for makespan or - [ ] 더 미세한 와이어 중재 사이클을 위한 **Sub-flit (32B) granularity**.
average-latency studies. 본 모델의 `flit_bytes`는 burst(256B)와 같지만, 실제 HW는 32B flit마다
- [ ] **Sub-flit (32B) granularity** for finer wire arbitration 중재한다. 대부분 워크로드에서는 영향이 작다(작은 메시지에 대한 sub-flit
cycles. Our `flit_bytes` equals burst (256B); real HW arbitrates 타이밍 노이즈).
per 32B flit. Effect is small for most workloads (sub-flit timing
noise on small messages).
## Consequences ## Consequences
- Single review point for all model fidelity questions. Each future PR - 모든 모델 충실도 질문에 대한 단일 리뷰 지점. 레이턴시를 건드리는 향후
touching latency must update the relevant section here. 모든 PR은 본 문서의 해당 절을 갱신해야 한다.
- Workload-specific magnitude error envelopes are explicit. - 워크로드별 규모 오차 envelope이 명시적이다.
- Builder-side derivation of `pc_bw_gbs = hbm_to_router_bw_gbs / num_pcs` - 빌더측 `pc_bw_gbs = hbm_to_router_bw_gbs / num_pcs` 유도가
enforces the ADR-0017 D8 invariant in code rather than relying on yaml yaml의 수동 일관성에 의존하지 않고 코드 내에서 ADR-0017 D8의 불변성을
manual consistency. 강제한다.
- Wire transfer time is charged once per bottleneck-link transit (Phase 2c - 와이어 전송 시간은 터미널의 `drain_ns` 주입을 통해서가 아니라
per-flit timing) rather than via terminal `drain_ns` injection. Single 병목 링크 통과당 한 번 부과된다(Phase 2c flit별 타이밍). 단일 트랜잭션은
transactions land at `drain + commit_time + small_overheads`; multi-hop `drain + commit_time + small_overheads`에 도달; 다중 hop은 wormhole
preserves wormhole pipelining; multi-stream merge correctly serializes 파이프라이닝을 보존; multi-stream 병합은 공유 와이어의 FIFO에서 올바르게
at the shared wire's FIFO. 직렬화된다.
## Cross-references ## Cross-references
- ADR-0015 — component / port / wire model. - ADR-0015 — 컴포넌트 / 포트 / 와이어 모델.
- ADR-0017 — Cube NOC architecture and HBM connectivity. - ADR-0017 — 큐브 NOC 아키텍처 및 HBM 연결성.
- ADR-0004 — memory semantics, local HBM. - ADR-0004 — 메모리 시맨틱, 로컬 HBM.
- ADR-0034 — HBM controller internal design. - ADR-0034 — HBM 컨트롤러 내부 설계.
@@ -1,4 +1,4 @@
# ADR-0034: HBM Controller Internal Design # ADR-0034: HBM 컨트롤러 내부 설계
## Status ## Status
@@ -6,111 +6,108 @@ Accepted
## Context ## Context
`HbmCtrlComponent` is the per-PE HBM partition endpoint at the leaf of `HbmCtrlComponent`는 큐브 NOC의 말단(leaf)에 위치하는 PE HBM
the cube NOC. One instance is created per PE under the topology node 파티션 엔드포인트이다. 토폴로지 노드
`sip{S}.cube{C}.hbm_ctrl.pe{idx}` and attaches to that PE's router `sip{S}.cube{C}.hbm_ctrl.pe{idx}` 아래에 PE마다 하나의 인스턴스가
(ADR-0017 D4). The component models per-pseudo-channel (PC) scheduling, 생성되며 해당 PE의 라우터에 연결된다 (ADR-0017 D4). 본 컴포넌트는
burst-granular commit timing, address-based PC selection, and response 의사 채널(PC, pseudo-channel)별 스케줄링, 버스트 단위 커밋 타이밍,
routing back to the requester. 주소 기반 PC 선택, 그리고 응답을 요청자에게 되돌리는 라우팅을
모델링한다.
This ADR documents the component as currently implemented. ADR-0017 D4/D8 본 ADR은 현재 구현된 컴포넌트를 문서화한다. ADR-0017 D4/D8은 HBM CTRL이
defines *where* HBM CTRL attaches and *what* aggregate BW it must *어디에* 부착되는지와 *어떤* 집계 대역폭을 제공해야 하는지를 정의한다.
deliver. ADR-0033 D1/D2 defines *what fidelity* of HBM modelling is in ADR-0033 D1/D2는 HBM 모델링의 *어떤 정밀도(fidelity)*가 범위에 포함되는지를
scope. This ADR fills the gap between those two — the per-instance 정의한다. 본 ADR은 그 둘 사이의 공백 — 인스턴스별 내부 스케줄링 모델을
internal scheduling model. 채운다.
## Decision ## Decision
### D1. Role ### D1. 역할
`HbmCtrlComponent` is a per-PE HBM partition endpoint. One instance per `HbmCtrlComponent` PE HBM 파티션 엔드포인트이다. PE당 하나의
PE (default 8 per cube, set by `cube.memory_map.hbm_slices_per_cube`) 인스턴스(큐브당 기본 8개, `cube.memory_map.hbm_slices_per_cube`로 설정)가
attaches to that PE's router via the `peX.hbm` attachment list in `cube_mesh.yaml``peX.hbm` 부착 목록을 통해 해당 PE의 라우터에 연결된다
`cube_mesh.yaml` (ADR-0017 D4). In the default n:1 channel mapping (ADR-0017 D4). 기본 n:1 채널 매핑(ADR-0017 D8)에서는 인스턴스가
(ADR-0017 D8) the instance aggregates `channels_per_pe` pseudo-channels `channels_per_pe`개의 의사 채널을 하나의 엔드포인트로 집계한다.
into one endpoint.
The component models: 본 컴포넌트는 다음을 모델링한다:
- Per-PC scheduling (D2) with R/W command-bus sharing. - PC별 스케줄링(D2) R/W 명령 버스 공유.
- Address-based PC selection (D3). - 주소 기반 PC 선택(D3).
- Burst-granular commit timing (D4). - 버스트 단위 커밋 타이밍(D4).
- Flit-aware per-flit PC commit and async finalize (D5, D6). - Flit 인지 per-flit PC 커밋 및 비동기 finalize(D5, D6).
- Command-only Transaction handling for read-data drain (D7). - 읽기 데이터 드레인(drain)을 위한 명령 전용 Transaction 처리(D7).
- Response routing back to the requester (D8). - 요청자에게 되돌리는 응답 라우팅(D8).
It does not model: 다음은 모델링하지 않는다:
- Bank-level row-buffer conflicts, refresh, ECC, thermal throttling - Bank 수준의 row-buffer 충돌, refresh, ECC, 열 스로틀링
(ADR-0033 D3). (ADR-0033 D3).
- Cross-PE HBM contention beyond its own router edge (handled by the - 자신의 라우터 엣지를 넘어가는 PE 간 HBM 경합(라우터 메시가 처리 —
router mesh — ADR-0017 D3). ADR-0017 D3).
- 1:1 channel mode (ADR-0017 D8 future work). - 1:1 채널 모드(ADR-0017 D8 향후 작업).
### D2. Per-PC scheduling model ### D2. PC별 스케줄링 모델
Per-instance state initialised in `start()`: `start()`에서 초기화되는 인스턴스별 상태:
- `_pc_avail: list[float]`earliest sim-time each PC is free; length - `_pc_avail: list[float]`각 PC가 다음에 자유로워지는 가장 빠른
`num_pcs`, initial 0.0. 시뮬레이션 시각; 길이 `num_pcs`, 초기값 0.0.
- `_pc_last_dir: list["R"|"W"|None]`direction of the last commit on - `_pc_last_dir: list["R"|"W"|None]`각 PC의 마지막 커밋 방향, 스위치
each PC, used for switch-penalty detection (D4); initial `None`. 페널티 감지에 사용(D4); 초기값 `None`.
`num_pcs` and `burst_bytes` must each be a positive power of two so `num_pcs` `burst_bytes`는 각각 양의 2의 거듭제곱이어야 주소 기반 PC
that address-based PC selection (D3) reduces to a shift-and-mask. 선택(D3)이 시프트와 마스크로 축약된다.
Read and write requests share the same `_pc_avail` slot per PC — the 읽기와 쓰기 요청은 PC별로 동일한 `_pc_avail` 슬롯을 공유한다 — 실제 HW에서
real HW per-PC command bus is shared between read and write traffic, so PC별 명령 버스는 읽기와 쓰기 트래픽이 공유하므로, PC k에 쓰기를 발행하면
issuing a write to PC k blocks a subsequent read to PC k by exactly the PC k에 대한 후속 읽기가 정확히 버스트 시간만큼 블록된다.
burst time.
Direction `dir` for a request is inferred from the request type: 요청의 방향 `dir`은 요청 타입으로부터 추론된다:
- `MemoryWriteMsg``"W"`. - `MemoryWriteMsg``"W"`.
- `PeDmaMsg` with `is_write=True``"W"`. - `is_write=True``PeDmaMsg``"W"`.
- All others (`MemoryReadMsg`, `PeDmaMsg` read) → `"R"`. - 그 외 전부(`MemoryReadMsg`, 읽기 `PeDmaMsg`) → `"R"`.
### D3. Address-based PC selection ### D3. 주소 기반 PC 선택
PC index for an access is derived from the access address by shift and 접근에 대한 PC 인덱스는 접근 주소로부터 시프트와 마스크로 도출된다:
mask:
```text ```text
pc_shift = log2(burst_bytes) # default 8 (burst=256B) pc_shift = log2(burst_bytes) # 기본값 8 (burst=256B)
pc_mask = num_pcs - 1 # default 7 (8 PCs) pc_mask = num_pcs - 1 # 기본값 7 (8 PCs)
pc = (address >> pc_shift) & pc_mask pc = (address >> pc_shift) & pc_mask
``` ```
Computed once in `start()` from topology config so alternative 대안적인 `(burst_bytes, num_pcs)` 쌍과의 정합성을 유지하기 위해
`(burst_bytes, num_pcs)` pairs stay consistent. For the canonical `start()`에서 토폴로지 설정으로부터 한 번 계산된다. 정규 기본값
default `(256, 8)` this places the PC select field at bits `[10:8]` of `(256, 8)`에서는 PC 선택 필드가 HBM 바이트 오프셋의 비트 `[10:8]`
the HBM byte offset: bits `[7:0]` are within-burst (same PC), bits 배치된다: 비트 `[7:0]`은 버스트 내부(같은 PC), 비트 `[10:8]`은 3비트
`[10:8]` are the 3-bit PC index, bits `[36:11]` are row/bank/column PC 인덱스, 비트 `[36:11]`은 PC 슬라이스 내부의 row/bank/column이다
within the PC slice (see `phyaddr.py` comment). (`phyaddr.py` 주석 참조).
Address-based striping — as opposed to address-blind global 주소 기반 스트라이핑은 — 주소를 보지 않는 전역 라운드로빈과 달리 —
round-robin — preserves PC parallelism for offset-disjoint concurrent 오프셋이 분리된 동시 전송들에 대해 PC 병렬성을 보존한다: 각 전송의
transfers: each transfer's bursts land deterministically on the PC set 버스트는 자신의 바이트 주소가 함의하는 PC 집합 위에 결정론적으로
implied by its byte addresses, so multi-PE workloads accessing disjoint 떨어지므로, 분리된 영역에 접근하는 멀티 PE 워크로드가 단일 PC에서
regions do not collide on a single PC. 충돌하지 않는다.
### D4. Burst granularity and PC commit timing ### D4. 버스트 단위 시간 및 PC 커밋 타이밍
A single PC commit takes: 단일 PC 커밋에 걸리는 시간:
```text ```text
chunk_time = burst_bytes / pc_bw_gbs # ns chunk_time = burst_bytes / pc_bw_gbs # ns
``` ```
- `burst_bytes` (default 256) is the burst granularity matching the - `burst_bytes`(기본 256)는 flit 크기와 일치하는 버스트 단위이다
flit size (ADR-0033 D1). (ADR-0033 D1).
- `pc_bw_gbs` is **builder-derived** from - `pc_bw_gbs`는 **빌더에서 도출**된다:
`hbm_to_router_bw_gbs / num_pcs` (`topology/builder.py`), enforcing `hbm_to_router_bw_gbs / num_pcs` (`topology/builder.py`). 이는 PE당
the ADR-0017 D8 invariant that aggregate per-PE BW equals the 집계 대역폭이 라우터-HBM 링크 대역폭과 같아야 한다는 ADR-0017 D8의
router-to-HBM link BW. 불변식을 강제한다.
Per-PC commit scheduling for an arriving access on PC `pc` with 방향 `dir`로 PC `pc`에 도착한 접근에 대한 PC별 커밋 스케줄링:
direction `dir`:
```text ```text
switch_cost = switch_penalty_ns switch_cost = switch_penalty_ns
@@ -121,33 +118,32 @@ pc_avail[pc] = finish
pc_last_dir[pc] = dir pc_last_dir[pc] = dir
``` ```
Default `switch_penalty_ns = 0`Tier 0 assumption that an ideal HBM 기본 `switch_penalty_ns = 0`이상적인 HBM 스케줄러가 R/W 스위칭
scheduler amortises R/W switching cost (ADR-0033 D2). Non-zero values 비용을 분할 상환한다는 Tier 0 가정(ADR-0033 D2). 0이 아닌 값은
model pessimistic per-alternation cost. 교차마다 발생하는 비관적 비용을 모델링한다.
### D5. Flit-aware per-flit PC commit (primary path) ### D5. Flit 인지 per-flit PC 커밋 (주 경로)
`_handle_flit` is the primary worker path. For each arriving `Flit`: `_handle_flit`이 주 워커 경로이다. 각 도착 `Flit`에 대해:
1. On the **first** flit of a transaction (`tid = id(txn)` not in 1. 트랜잭션의 **첫 번째** flit인 경우(`tid = id(txn)``_txn_state`
`_txn_state`): 없는 경우):
- Apply `overhead_ns` once via `run(env, nbytes)` — header decode - `run(env, nbytes)`를 통해 `overhead_ns`를 한 번 적용 — 헤더 디코드
model, first-flit overhead pattern (ADR-0033 D1). 모델, first-flit overhead 패턴(ADR-0033 D1).
- Initialise `_txn_state[tid] = {"last_finish": env.now}`. - `_txn_state[tid] = {"last_finish": env.now}`로 초기화.
2. Compute `pc = _pc_for_address(flit.address)` (D3). 2. `pc = _pc_for_address(flit.address)`를 계산(D3).
3. Apply the per-PC schedule (D4) using the request direction (D2). 3. 요청 방향(D2)을 사용하여 PC별 스케줄(D4)을 적용.
4. Update `state["last_finish"] = max(state["last_finish"], finish)`. 4. `state["last_finish"] = max(state["last_finish"], finish)`로 갱신.
5. If `flit.is_last`: pop `_txn_state[tid]` and spawn `_finalize_txn` 5. `flit.is_last`이면: `_txn_state[tid]`를 pop하고 `_finalize_txn`
(D6). spawn(D6).
Per-flit address-aware commit is the mechanism that lets concurrent per-flit 주소 인지 커밋이 분리된 HBM 오프셋으로 향하는 동시 멀티 PE
multi-PE traffic to disjoint HBM offsets pipeline through distinct PCs 트래픽이 서로 다른 PC를 통해 병렬로 파이프라인되도록 하는 메커니즘이다.
in parallel.
### D6. Async finalize per transaction ### D6. 트랜잭션별 비동기 finalize
When a transaction's last flit has been scheduled, finalisation runs in 트랜잭션의 마지막 flit이 스케줄링되고 나면, finalize는 별도로 spawn된
a separately-spawned process: 프로세스에서 실행된다:
```python ```python
def _finalize_txn(env, txn, last_finish): def _finalize_txn(env, txn, last_finish):
@@ -157,115 +153,111 @@ def _finalize_txn(env, txn, last_finish):
yield from _send_response(env, txn) yield from _send_response(env, txn)
``` ```
`_handle_flit` spawns this via `env.process(...)` and returns `_handle_flit`은 이를 `env.process(...)`로 spawn한 뒤 즉시 반환하므로,
immediately, so the worker can pick up the next inbox message while the 마지막 PC 커밋이 드레인되는 동안에도 워커는 다음 inbox 메시지를 집어들
last PC commit drains. 수 있다.
Without this split — i.e. if the worker itself did 이 분리가 없다면 — 즉 워커 자신이 `yield env.timeout(wait)`를 한다면 —
`yield env.timeout(wait)` — concurrent single-flit transactions whose 서로 다른 PC에 떨어지는 주소를 가진 동시 단일 flit 트랜잭션들도 결국
addresses hit distinct PCs would still serialise at `chunk_time` each 워커 내부에서 각각 `chunk_time`만큼 직렬화되어, D3와 D5가 노출하려고
inside the worker, hiding the PC parallelism that D3 and D5 are 설계한 PC 병렬성을 숨겨버린다.
designed to expose.
### D7. Non-flit fallback for command-only transactions ### D7. 명령 전용 트랜잭션을 위한 non-flit 폴백
`_handle_txn` runs when the inbox delivers a `Transaction` rather than a `_handle_txn`은 inbox가 `Flit`이 아닌 `Transaction`을 전달할 때 실행된다.
`Flit`. This is the path for command-only requests that the wire does 이는 와이어가 flit으로 분할하지 않는 명령 전용 요청에 대한 경로로 —
not chunk into flits — most notably `MemoryReadMsg` whose command txn 대표적으로 명령 트랜잭션이 `nbytes=0`을 운반하는 `MemoryReadMsg`
carries `nbytes=0` (data drain is modelled at HBM CTRL post-processing, 해당한다(데이터 드레인은 HBM CTRL 후처리에서 모델링되며, 인바운드
not as inbound flits). flit으로 모델링되지 않는다).
Procedure: 절차:
1. `work_bytes = txn.nbytes if txn.nbytes > 0 else int(request.nbytes or 0)` 1. `work_bytes = txn.nbytes if txn.nbytes > 0 else int(request.nbytes or 0)`
for read commands, work is sized by the request. 읽기 명령의 경우 작업량은 요청으로 결정된다.
2. `n_chunks = ceil(work_bytes / burst_bytes)` if `work_bytes > 0` else 2. `work_bytes > 0`이면 `n_chunks = ceil(work_bytes / burst_bytes)`,
0. 아니면 0.
3. `chunk_interval = drain_ns / n_chunks` (when both > 0) — chunks are 3. 둘 다 > 0일 때 `chunk_interval = drain_ns / n_chunks` — 청크는
scheduled over time at `drain/n_chunks` ns intervals to model the `drain/n_chunks` ns 간격으로 시간상에 스케줄링되어 병목 링크의 데이터
bottleneck-link's data arrival rate (ADR-0033 D1 chunk-loop drain). 도착 속도를 모델링한다(ADR-0033 D1 청크 루프 드레인).
4. Apply `run(env, txn.nbytes)` once for `overhead_ns`. 4. `overhead_ns`를 위해 `run(env, txn.nbytes)`를 한 번 적용.
5. For each chunk `i`, advance `chunk_interval` ns then apply the D4 5. 각 청크 `i`에 대해 `chunk_interval` ns만큼 진행한 뒤
schedule with `pc = _pc_for_address(base_address + i * burst_bytes)`. `pc = _pc_for_address(base_address + i * burst_bytes)`로 D4 스케줄을
6. After scheduling all chunks, wait `last_finish - env.now` then call 적용.
`_send_response`. 6. 모든 청크 스케줄링 후 `last_finish - env.now`만큼 대기한 다음
`_send_response`를 호출.
`_handle_txn` shares the same `_pc_avail` / `_pc_last_dir` state with `_handle_txn``_handle_flit`과 동일한 `_pc_avail` / `_pc_last_dir`
`_handle_flit` — there is exactly one source of PC scheduling truth 상태를 공유한다 — 두 경로에 걸쳐 PC 스케줄링의 단일 진실 원천이 정확히
across both paths. 하나만 존재한다.
### D8. Response routing ### D8. 응답 라우팅
`_send_response` dispatches on request type and path geometry: `_send_response`는 요청 타입과 경로 형상에 따라 디스패치한다:
| Case | Trigger | Response | | 경우 | 트리거 | 응답 |
| --- | --- | --- | | --- | --- | --- |
| PE_DMA | `isinstance(txn.request, PeDmaMsg)` | New reverse-path Transaction (`is_response=True`, `nbytes=0`), same `done` | | PE_DMA | `isinstance(txn.request, PeDmaMsg)` | 신규 역방향 경로 Transaction(`is_response=True`, `nbytes=0`), 동일한 `done` |
| Bypass — Memory Read | `"m_cpu" not in any(txn.path)` AND `MemoryReadMsg` | Reverse-path Transaction with `nbytes=request.nbytes` (data return) | | Bypass — Memory Read | `"m_cpu" not in any(txn.path)` AND `MemoryReadMsg` | `nbytes=request.nbytes`(데이터 반환)인 역방향 경로 Transaction |
| Bypass — Memory Write | `"m_cpu" not in any(txn.path)` AND not Memory Read | `txn.done.succeed()` (write completes locally) | | Bypass — Memory Write | `"m_cpu" not in any(txn.path)` AND not Memory Read | `txn.done.succeed()` (쓰기는 로컬에서 완료) |
| Default | otherwise | New `ResponseMsg(correlation_id, request_id, src_cube, src_pe, success=True)` on reverse path | | 기본 | 그 외 | 역방향 경로상의 신규 `ResponseMsg(correlation_id, request_id, src_cube, src_pe, success=True)` |
The "bypass" classification matches the Memory R/W fabric path defined "bypass" 분류는 ADR-0015 D4에서 정의된 Memory R/W 패브릭 경로(PCIE_EP →
in ADR-0015 D4 (PCIE_EP → io_noc → ucie → cube router → hbm_ctrl, io_noc → ucie → 큐브 라우터 → hbm_ctrl, M_CPU 미경유)와 일치한다.
without M_CPU). The PE_DMA case is its own dedicated reverse-path to PE_DMA 케이스는 내부 루프 DMA를 빠르게 유지하기 위한 전용 역방향 경로이다
keep the inner-loop DMA fast (PE_DMA reads/writes do not synthesise a (PE_DMA 읽기/쓰기는 ResponseMsg 봉투를 합성하지 않는다).
ResponseMsg envelope).
In all reverse-path cases, the response Transaction is put onto 모든 역방향 경로 케이스에서, 응답 Transaction은
`out_ports[reverse_path[1]]`the first hop back along the recorded `out_ports[reverse_path[1]]`기록된 정방향 경로를 따라 되돌아가는 첫
forward path. If `reverse_path` has fewer than 2 entries (degenerate 홉 — 에 put된다. `reverse_path`의 엔트리가 2개 미만이면(축퇴된 경로),
path), the original `txn.done` is signalled directly. 원래의 `txn.done`이 직접 시그널된다.
### D9. Configurable attributes ### D9. 설정 가능한 속성
| Attribute | Default | Source | Notes | | 속성 | 기본값 | 출처 | 비고 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `num_pcs` | 8 | topology cube `hbm_ctrl.attrs` | Must be power of 2 | | `num_pcs` | 8 | 토폴로지 큐브 `hbm_ctrl.attrs` | 2의 거듭제곱이어야 함 |
| `pc_bw_gbs` | 32.0 | builder-derived: `hbm_to_router_bw_gbs / num_pcs` | Enforces ADR-0017 D8 invariant | | `pc_bw_gbs` | 32.0 | 빌더 도출: `hbm_to_router_bw_gbs / num_pcs` | ADR-0017 D8 불변식 강제 |
| `burst_bytes` | 256 | topology attrs | Must be power of 2; equals `flit_bytes` (ADR-0033 D1) | | `burst_bytes` | 256 | 토폴로지 attrs | 2의 거듭제곱이어야 함; `flit_bytes`와 동일(ADR-0033 D1) |
| `switch_penalty_ns` | 0.0 | topology attrs | Tier 0 default; non-zero models pessimistic R/W switching | | `switch_penalty_ns` | 0.0 | 토폴로지 attrs | Tier 0 기본값; 0이 아니면 비관적 R/W 스위칭 모델링 |
| `efficiency` | 1.0 | topology attrs | Applied at builder time to `hbm_to_router_bw_gbs` (router-edge BW scaling only) | | `efficiency` | 1.0 | 토폴로지 attrs | 빌더 시점에 `hbm_to_router_bw_gbs`에 적용(라우터 엣지 BW 스케일링만) |
| `overhead_ns` | 0.0 | topology attrs | First-flit decode overhead (D5) | | `overhead_ns` | 0.0 | 토폴로지 attrs | First-flit 디코드 오버헤드(D5) |
`pc_bw_gbs` is derived by `topology/builder.py` rather than configured `pc_bw_gbs`는 yaml 측 중복 없이 PE당 집계 대역폭을 라우터-HBM 링크
directly so the aggregate per-PE BW matches the router-to-HBM link BW 대역폭과 일치시키기 위해 직접 설정되지 않고 `topology/builder.py`에서
without yaml-side duplication. 도출된다.
## Consequences ## Consequences
### Positive ### Positive
- Address-based PC selection preserves multi-stream HBM parallelism - 주소 기반 PC 선택은 주소를 보지 않는 라운드로빈이 무너뜨릴 멀티 스트림
that an address-blind round-robin would collapse — important for HBM 병렬성을 보존한다 — 분리된 HBM 영역을 갖는 멀티 PE 워크로드에서
multi-PE workloads with disjoint HBM regions. 중요하다.
- Flit-aware path (D5) + async finalize (D6) preserves wormhole - Flit 인지 경로(D5) + 비동기 finalize(D6)는 웜홀 파이프라이닝을
pipelining and exposes PC parallelism for back-to-back single-flit 보존하며, 연속적인 단일 flit 트랜잭션에 대해 PC 병렬성을 노출한다.
transactions. - PC 스케줄링의 단일 진실 원천(D4 메커니즘이 D5 flit 경로와 D7 청크 루프
- Single source of PC scheduling truth (D4 mechanism, used by both D5 경로 모두에서 사용됨).
flit path and D7 chunk-loop path). - 빌더 도출 `pc_bw_gbs`가 yaml 규율이 아닌 코드에서 ADR-0017 D8을
- Builder-derived `pc_bw_gbs` enforces ADR-0017 D8 in code, not yaml 강제한다.
discipline.
### Negative ### Negative
- No bank-level conflict modelling within a PC; address-blind to - PC 내부의 bank 수준 충돌 모델링이 없음; bank/row-buffer 재사용에
bank/row-buffer reuse (ADR-0033 D3). 주소-무관(ADR-0033 D3).
- No HBM scheduler (FR-FCFS / write-buffer / watermark drain); fixed - HBM 스케줄러 없음(FR-FCFS / write-buffer / watermark drain); PC당 고정
FIFO per PC. Bursty mixed R/W is approximated by `switch_penalty_ns` FIFO. 버스티한 혼합 R/W는 `switch_penalty_ns`로 근사화된다
(ADR-0033 D2). (ADR-0033 D2).
- `_txn_state` is a regular dict keyed by `id(txn)`; in-flight state - `_txn_state``id(txn)`로 키를 잡는 일반 dict이다; 동시 트랜잭션마다
accumulates per concurrent transaction and is removed only on in-flight 상태가 누적되며 `is_last` 시에만 제거된다. 현재 워크로드에는
`is_last`. Adequate for current workloads. 충분하다.
## Links ## Links
- ADR-0001 (Physical address layout — PC bit field comment) - ADR-0001 (물리 주소 레이아웃 — PC 비트 필드 주석)
- ADR-0015 D4 (Memory R/W fabric path — bypass response case) - ADR-0015 D4 (Memory R/W 패브릭 경로 — bypass 응답 케이스)
- ADR-0017 D4 (Per-PE HBM partitioning — attachment to PE routers) - ADR-0017 D4 (PE별 HBM 파티셔닝 — PE 라우터로의 부착)
- ADR-0017 D8 (HBM channel mapping mode — n:1 aggregate this ADR - ADR-0017 D8 (HBM 채널 매핑 모드 — 본 ADR이 구현하는 n:1 집계)
implements) - ADR-0017 D9 (AddressResolver — `hbm_ctrl.pe{pe_id}` 엔드포인트 해석)
- ADR-0017 D9 (AddressResolver — `hbm_ctrl.pe{pe_id}` endpoint - ADR-0033 D1 (정확한 모델링 — PC별 병렬성, 스위치 페널티, flit 인지
resolution) PC 커밋, first-flit 오버헤드, 청크 루프 드레인)
- ADR-0033 D1 (Modelled precisely — per-PC parallelism, switch penalty, - ADR-0033 D2 (스위치 페널티 기본값 0 — 이상적 스케줄러의 분할 상환)
flit-aware PC commit, first-flit overhead, chunk-loop drain)
- ADR-0033 D2 (Switch-penalty default 0 — ideal scheduler amortisation)
@@ -1,4 +1,4 @@
# ADR-0035: M_CPU and M_CPU.DMA Component Model # ADR-0035: M_CPU M_CPU.DMA 컴포넌트 모델
## Status ## Status
@@ -6,51 +6,47 @@ Accepted
## Context ## Context
M_CPU is the cube-level command processor. It receives commands from M_CPU는 큐브 수준의 명령 프로세서이다. IO_CPU로부터(또는 엔진이
IO_CPU (or from PCIE_EP when the engine routes Memory R/W through Memory R/W를 폴백으로 M_CPU를 거쳐 라우팅할 때 PCIE_EP로부터) 명령을
M_CPU as a fallback), fans them out to the PEs in its cube, and 수신하여 자신의 큐브 내 PE들로 팬아웃하고, PE별 응답을 단일 ResponseMsg로
aggregates per-PE responses into a single ResponseMsg sent back to 집계하여 역방향 경로를 통해 IO_CPU로 되돌려 보낸다.
IO_CPU on the reverse path.
M_CPU.DMA is the cube-level DMA channel pair that handles Memory R/W M_CPU.DMA는 Memory R/W 팬아웃을 처리하는 큐브 수준의 DMA 채널 쌍이다.
fan-out. Per ADR-0015 D5 it is **not** a separate topology node — ADR-0015 D5에 따라 별도의 토폴로지 노드가 **아니다** `MCpuComponent`
it lives as internal state of `MCpuComponent`. 내부 상태로서 존재한다.
This ADR documents the M_CPU component implementation that realizes 본 ADR은 위의 책임들을 실현하는 M_CPU 컴포넌트 구현을 문서화한다. 여기에는
those responsibilities, including the three distinct fan-out paths 세 가지 구별되는 팬아웃 경로(Memory R/W, Kernel Launch, MMU Map/Unmap),
(Memory R/W, Kernel Launch, MMU Map/Unmap), the M_CPU.DMA resource M_CPU.DMA 자원 모델, 그리고 응답 집계 계약이 포함된다.
model, and the response aggregation contract.
## Decision ## Decision
### D1. Role ### D1. 역할
M_CPU has three responsibilities: M_CPU는 세 가지 책임을 갖는다:
1. **Transit forwarding** — when not the terminal hop (e.g., on the 1. **Transit 포워딩** — 종단 홉이 아닐 때(예: 역방향 응답 경로 PE →
reverse response path PE → M_CPU → IO_CPU), forwards Transactions M_CPU → IO_CPU), 사전 계산된 경로의 `next_hop`으로 Transaction
to `next_hop` in their pre-computed path. 전달한다.
2. **Multi-PE fan-out at terminal hop** — dispatches to one of three 2. **종단 홉에서의 멀티 PE 팬아웃** — 요청 타입에 따라 세 팬아웃 경로
fan-out paths based on request type (D2). 중 하나로 디스패치한다(D2).
3. **Response aggregation** — collects per-PE responses, sends a 3. **응답 집계** — PE별 응답을 수집하여 역방향 경로를 통해 단일 집계
single aggregate ResponseMsg back to IO_CPU on the reverse path. ResponseMsg를 IO_CPU로 되돌려 보낸다.
Per invocation (`run()`): applies `overhead_ns` once per incoming 호출당(`run()`): 들어오는 Transaction마다 `overhead_ns`를 한 번 적용한다.
Transaction.
M_CPU does **not**: M_CPU는 다음을 하지 **않는다**:
- Decide routing — paths are pre-computed by the router (ADR-0002). - 라우팅 결정 — 경로는 라우터에 의해 사전 계산된다(ADR-0002).
- Handle PE-internal execution — PE_CPU / PE_SCHEDULER / engines - PE 내부 실행 처리 — PE_CPU / PE_SCHEDULER / 엔진들이 담당(ADR-0014).
(ADR-0014). - 주소 디코드 — `ctx.resolver.resolve(pa)`가 PE별 `hbm_ctrl.pe{X}`
- Decode addresses — `ctx.resolver.resolve(pa)` returns the per-PE 직접 반환한다(ADR-0017 D9).
`hbm_ctrl.pe{X}` directly (ADR-0017 D9). - 텐서 또는 커널 의미 해석 — 팬아웃 디스패치는 Python isinstance
- Interpret tensor or kernel semantics — fan-out dispatch by Python 체크만으로 이루어진다.
isinstance check only.
### D2. Three fan-out paths dispatched by request type ### D2. 요청 타입으로 디스패치되는 세 가지 팬아웃 경로
At the terminal hop the worker dispatches by request type: 종단 홉에서 워커는 요청 타입에 따라 디스패치한다:
```python ```python
elif self.ctx is not None and txn.request is not None: elif self.ctx is not None and txn.request is not None:
@@ -62,99 +58,95 @@ elif self.ctx is not None and txn.request is not None:
env.process(self._dma_fanout(env, txn)) env.process(self._dma_fanout(env, txn))
``` ```
Each path uses a different router method: 각 경로는 서로 다른 라우터 메서드를 사용한다:
- `_dma_fanout` uses `ctx.router.find_mcpu_dma_path()` — the - `_dma_fanout` `ctx.router.find_mcpu_dma_path()`를 사용 — PE 파이프라인
M_CPU-specific DMA path that avoids PE pipeline nodes. 노드를 우회하는 M_CPU 전용 DMA 경로.
- `_kernel_launch_fanout` uses `ctx.router.find_node_path()` — the - `_kernel_launch_fanout` `ctx.router.find_node_path()`를 사용 — PE_CPU로
generic NOC command path to PE_CPU. 향하는 범용 NOC 명령 경로.
- `_mmu_msg_fanout` uses `ctx.router.find_node_path()` — NOC command - `_mmu_msg_fanout` `ctx.router.find_node_path()`를 사용 — PE_MMU로
path to PE_MMU. 향하는 NOC 명령 경로.
### D3. M_CPU.DMA internal subcomponent (ADR-0015 D5) ### D3. M_CPU.DMA 내부 서브 컴포넌트 (ADR-0015 D5)
`MCpuComponent.start()` initializes two SimPy resources: `MCpuComponent.start()`는 두 개의 SimPy 자원을 초기화한다:
```python ```python
self._dma_write = simpy.Resource(env, capacity=1) # MemoryWriteMsg self._dma_write = simpy.Resource(env, capacity=1) # MemoryWriteMsg
self._dma_read = simpy.Resource(env, capacity=1) # MemoryReadMsg self._dma_read = simpy.Resource(env, capacity=1) # MemoryReadMsg
``` ```
Properties: 특성:
- **Not a topology node** — managed entirely inside `MCpuComponent`; - **토폴로지 노드가 아님** — 전적으로 `MCpuComponent` 내부에서 관리됨;
does not appear in `topology.yaml` or in the compiled graph. `topology.yaml`이나 컴파일된 그래프에 나타나지 않는다.
- **Independent read and write channels** — concurrent in-flight - **독립된 읽기/쓰기 채널** — 동시 in-flight Memory R/W가 허용된다.
Memory R/W is allowed. - **채널당 capacity=1**은 본 M_CPU에서 동시 in-flight Memory R/W 요청의
- **Capacity=1 per channel** serializes the **dispatch step** **디스패치 단계**(`yield self.out_ports[...].put(...)`)를 직렬화한다.
(`yield self.out_ports[...].put(...)`) of concurrent in-flight Memory 실제 패브릭 전송 시간은 컴포넌트 사이의 와이어 프로세스(ADR-0015 D2)와
R/W requests at this M_CPU. Actual fabric transfer time is modeled 종단 홉의 `drain_ns`로 모델링되며, DMA 자원은 전송 지속 시간을
by wire processes between components (ADR-0015 D2) and by 게이팅하지 않는다.
`drain_ns` at terminal hops; the DMA resource does not gate
transfer duration.
Resource selection is request-type-based: 자원 선택은 요청 타입에 기반한다:
```python ```python
dma_res = self._dma_write if isinstance(request, MemoryWriteMsg) else self._dma_read dma_res = self._dma_write if isinstance(request, MemoryWriteMsg) else self._dma_read
``` ```
### D4. Transit forwarding at non-terminal hops ### D4. 비종단 홉에서의 transit 포워딩
When `txn.next_hop` is not None — typical for the reverse response `txn.next_hop`이 None이 아닐 때 — 전형적으로 역방향 응답 경로(PE →
path (PE → M_CPU → IO_CPU) — the worker forwards normally: M_CPU → IO_CPU)에서워커는 정상적으로 전달한다:
```python ```python
if next_hop: if next_hop:
yield self.out_ports[next_hop].put(txn.advance()) yield self.out_ports[next_hop].put(txn.advance())
``` ```
The fan-out branches fire only at the terminal hop. The same component 팬아웃 분기는 종단 홉에서만 발화한다. 따라서 동일한 컴포넌트가 정방향
therefore serves both forward command dispatch and reverse response 명령 디스패치 역할과 역방향 응답 중계 역할을 모두 수행한다.
relay roles.
### D5. DMA fan-out (`_dma_fanout` — Memory R/W) ### D5. DMA 팬아웃 (`_dma_fanout` — Memory R/W)
For each Memory R/W request at terminal hop: 종단 홉에서 각 Memory R/W 요청에 대해:
1. `_resolve_dma_destinations(request)` returns a per-PE 1. `_resolve_dma_destinations(request)`가 요청의 PA로부터
`hbm_ctrl.pe{X}` derived from the request's PA via `ctx.resolver.resolve(PhysAddr.decode(pa))`를 통해 도출된 PE별
`ctx.resolver.resolve(PhysAddr.decode(pa))` (ADR-0017 D9). `hbm_ctrl.pe{X}`를 반환한다(ADR-0017 D9).
2. For each destination: 2. 각 목적지에 대해:
- Acquire the appropriate DMA resource (`_dma_write` or - `with dma_res.request() as req`를 통해 적절한 DMA 자원(`_dma_write`
`_dma_read`) via `with dma_res.request() as req`. 또는 `_dma_read`)을 획득.
- Resolve path via `ctx.router.find_mcpu_dma_path()`. - `ctx.router.find_mcpu_dma_path()`로 경로를 해석.
- Compute `drain_ns = ctx.compute_drain_ns(path, nbytes)`. - `drain_ns = ctx.compute_drain_ns(path, nbytes)`를 계산.
- Create sub-Transaction carrying `drain_ns` and dispatch to - `drain_ns`를 운반하는 서브 Transaction을 생성하여 `path[1]`
`path[1]`. 디스패치.
3. Track `max_drain_ns` across destinations and record it as 3. 목적지들에 걸쳐 `max_drain_ns`를 추적하고, 모든 응답 도착 후
`txn.result_data["xfer_ns"]` after all responses arrive. `txn.result_data["xfer_ns"]`로 기록한다.
4. After all per-PE responses are collected (D8), send an aggregate 4. PE별 응답이 모두 수집된 후(D8), IO_CPU로 되돌아가는 역방향 명령
ResponseMsg on the reverse command path back to IO_CPU. 경로로 집계 ResponseMsg를 전송한다.
PA decode fallback (`f"{cube_prefix}.hbm_ctrl"`) is legacy dead code PA 디코드 폴백(`f"{cube_prefix}.hbm_ctrl"`)은 레거시 데드 코드이다
no such node exists after ADR-0017 D4's per-PE partitioning. Kept ADR-0017 D4의 PE별 파티셔닝 이후로 그러한 노드는 존재하지 않는다.
defensively but does not route to a real destination. 방어적으로 남겨두었으나 실제 목적지로 라우팅되지는 않는다.
### D6. Kernel launch fan-out (`_kernel_launch_fanout`) ### D6. Kernel launch 팬아웃 (`_kernel_launch_fanout`)
For `KernelLaunchMsg` at terminal hop: 종단 홉에서 `KernelLaunchMsg`에 대해:
1. `_resolve_pe_ids(target_pe)`list of PE ids in this cube. 1. `_resolve_pe_ids(target_pe)`본 큐브 내 PE id 리스트.
2. For each PE: find path to `f"{cube_prefix}.pe{pe_id}.pe_cpu"` via 2. 각 PE에 대해: `ctx.router.find_node_path()`를 통해
`ctx.router.find_node_path()`. `f"{cube_prefix}.pe{pe_id}.pe_cpu"`로의 경로를 찾음.
3. **`target_start_ns` handling** (ADR-0009 D5): 3. **`target_start_ns` 처리**(ADR-0009 D5):
- If the request already carries `target_start_ns` (stamped by - 요청에 이미 `target_start_ns`가 실려 있으면(IO_CPU가
IO_CPU per ADR-0036 D3): **pass through unchanged**. ADR-0036 D3에 따라 스탬프함): **변경 없이 통과**.
- If absent (direct-to-M_CPU launch in unit tests): compute a - 없으면(단위 테스트에서의 직접 M_CPU 런치):
per-cube barrier `env.now + max(per-PE leg latency)` and stamp `env.now + max(PE leg 레이턴시)`로 큐브별 배리어를 계산하고
via `dataclasses.replace`. `dataclasses.replace`로 스탬프.
4. Dispatch sub-Transactions with `nbytes=0` (kernel launch is a 4. `nbytes=0`인 서브 Transaction으로 디스패치(커널 런치는 제어 메시지;
control message; preserving nbytes=0 keeps fan-out off the shared nbytes=0 유지는 팬아웃을 공유 first-hop 패브릭 BW에서 떼어내며,
first-hop fabric BW, mirroring ADR-0036 D4). ADR-0036 D4를 미러링).
5. After all per-PE responses arrive (D8), aggregate per-PE metrics 5. PE별 응답이 모두 도착한 후(D8), 각 서브 Transaction의 `result_data`로부터
from each sub-Transaction's `result_data` into the parent PE별 메트릭을 부모 트랜잭션으로 집계한다:
transaction:
```python ```python
txn.result_data["pe_exec_ns"] = max(existing, max(pe_exec_values)) txn.result_data["pe_exec_ns"] = max(existing, max(pe_exec_values))
@@ -162,125 +154,120 @@ For `KernelLaunchMsg` at terminal hop:
txn.result_data["compute_ns"] = max(existing, max(compute_values)) txn.result_data["compute_ns"] = max(existing, max(compute_values))
``` ```
The max-merge with the existing value matters because cross-cube 기존 값과의 max 병합이 중요한 이유는 크로스 큐브 IO_CPU 팬아웃이
IO_CPU fan-out shares the same parent `result_data`; merging 동일한 부모 `result_data`를 공유하기 때문이다; 병합을 통해 한 큐브가
prevents one cube from clobbering another's metric. 다른 큐브의 메트릭을 덮어쓰는 일을 방지한다.
6. Send aggregate ResponseMsg on reverse path back to IO_CPU. 6. IO_CPU로 되돌아가는 역방향 경로로 집계 ResponseMsg를 전송.
### D7. MMU map/unmap fan-out (`_mmu_msg_fanout`) ### D7. MMU map/unmap 팬아웃 (`_mmu_msg_fanout`)
For `MmuMapMsg` / `MmuUnmapMsg` at terminal hop: 종단 홉에서 `MmuMapMsg` / `MmuUnmapMsg`에 대해:
1. `_resolve_pe_ids(target_pe)` → PE ids. 1. `_resolve_pe_ids(target_pe)` → PE id.
2. For each PE: find path to `f"{cube_prefix}.pe{pe_id}.pe_mmu"` via 2. 각 PE에 대해: `find_node_path()`를 통해
`find_node_path()`. `f"{cube_prefix}.pe{pe_id}.pe_mmu"`로의 경로를 찾음.
3. Dispatch sub-Transactions with `nbytes=0`. 3. `nbytes=0`인 서브 Transaction으로 디스패치.
4. PE_MMU is a terminal node — it does **not** send a ResponseMsg 4. PE_MMU는 종단 노드이다 — ResponseMsg를 되돌려 보내지 **않는다**.
back. Instead, the sub-Transaction's own `sub_done` event is the 대신 서브 Transaction 자체의 `sub_done` 이벤트가 완료 시그널 역할을
completion signal. 한다.
5. Wait for all `sub_done` events in-line (does **not** use 5. 모든 `sub_done` 이벤트를 인라인으로 기다림(`_pending` 카운터를 사용
`_pending` counter — D8 is for response-bearing fan-out only). **하지 않음** — D8은 응답을 동반하는 팬아웃 전용).
6. Send aggregate ResponseMsg on reverse path back to IO_CPU. 6. IO_CPU로 되돌아가는 역방향 경로로 집계 ResponseMsg를 전송.
### D8. Response aggregation (`_pending` + `_parent_txns`) ### D8. 응답 집계 (`_pending` + `_parent_txns`)
For DMA and kernel-launch fan-out (which expect per-PE ResponseMsg DMA kernel-launch 팬아웃(역방향 경로로 도착하는 PE ResponseMsg
arriving on the reverse path): 예상함)에 대해:
```python ```python
self._pending: dict[str, tuple[int, int, simpy.Event]] = {} self._pending: dict[str, tuple[int, int, simpy.Event]] = {}
self._parent_txns: dict[str, Any] = {} self._parent_txns: dict[str, Any] = {}
``` ```
- On dispatch: register `(expected, received=0, all_done)` and - 디스패치 시: `(expected, received=0, all_done)`을 등록하고 부모
remember the parent transaction. 트랜잭션을 기억.
- `_worker` recognises responses by `is_response=True` and routes - `_worker`는 `is_response=True`로 응답을 인식하여 `_collect_response`로
them to `_collect_response`, which increments `received` and 라우팅하며, `_collect_response`는 `received`를 증가시키고 `received >=
signals `all_done` when `received >= expected`. expected`일 때 `all_done`을 시그널한다.
- After `yield all_done`, the fan-out path constructs the aggregate - `yield all_done` 후, 팬아웃 경로는 집계 ResponseMsg를 구성한다:
ResponseMsg:
```python ```python
resp_msg = ResponseMsg( resp_msg = ResponseMsg(
correlation_id=request.correlation_id, correlation_id=request.correlation_id,
request_id=request.request_id, request_id=request.request_id,
src_cube=cube_id, src_cube=cube_id,
src_pe=-1, # -1 = M_CPU aggregate, not a single PE src_pe=-1, # -1 = M_CPU 집계, 단일 PE가 아님
success=True, # no failure semantics implemented success=True, # 실패 의미는 구현되어 있지 않음
) )
``` ```
- The response Transaction travels on `list(reversed(txn.path))` - 응답 Transaction `list(reversed(txn.path))`를 따라 IO_CPU로
back to IO_CPU. 되돌아간다.
MMU fan-out (D7) uses a simpler in-line list of `sub_done` events MMU 팬아웃(D7)은 PE_MMU가 종단이므로 더 단순한 `sub_done` 이벤트의
because PE_MMU is terminal — there is no ResponseMsg path to 인라인 리스트를 사용한다 — 가로챌 ResponseMsg 경로가 없다.
intercept.
### D9. Helpers and configurable attribute ### D9. 헬퍼와 설정 가능한 속성
`_resolve_pe_ids(target_pe)`: `_resolve_pe_ids(target_pe)`:
- `int` → `[target_pe]` - `int` → `[target_pe]`
- `tuple[int, ...]` → `list(target_pe)` - `tuple[int, ...]` → `list(target_pe)`
- `"all"` → `range(n_slices)` where `n_slices` comes from cube - `"all"` → `range(n_slices)`, 여기서 `n_slices`는 큐브
`memory_map.hbm_slices_per_cube` (default 8). `memory_map.hbm_slices_per_cube`(기본 8)에서 가져온다.
Used by kernel-launch and MMU fan-out paths. Kernel-launch MMU 팬아웃 경로에서 사용된다.
Single configurable attribute drives per-instance latency: 인스턴스별 레이턴시를 결정하는 단일 설정 가능 속성:
| Site | impl name | overhead_ns | | 사이트 | impl 이름 | overhead_ns |
| --- | --- | --- | | --- | --- | --- |
| Cube `m_cpu` | `builtin.m_cpu` | 5.0 | | 큐브 `m_cpu` | `builtin.m_cpu` | 5.0 |
Applied once in `run()` per Transaction — models command Transaction마다 `run()`에서 한 번 적용 — M_CPU에서의 명령 해석 및
interpretation and dispatch-decision time at M_CPU. 디스패치 결정 시간을 모델링한다.
## Consequences ## Consequences
### Positive ### Positive
- Three fan-out paths are clearly separated by request type — adding - 세 가지 팬아웃 경로가 요청 타입에 의해 명확히 분리됨 — 새로운 요청
a new request kind is an isinstance branch + one fan-out method. 종류 추가는 isinstance 분기 한 줄과 팬아웃 메서드 하나로 가능.
- M_CPU.DMA channels are independent (read and write run concurrently) - M_CPU.DMA 채널은 독립적이며(읽기/쓰기가 동시 실행됨) capacity=1에서
and serialize only the dispatch step at capacity=1. 디스패치 단계만 직렬화된다.
- Transit-vs-terminal behavior is a single `if next_hop` check, so - Transit 대 종단 동작이 단일 `if next_hop` 체크이므로, 동일한 컴포넌트가
the same component handles forward dispatch and reverse response 역할 중복 없이 정방향 디스패치와 역방향 응답 중계를 처리한다.
relay without role duplication. - `target_start_ns` 통과(D6)는 IO_CPU가 수립한 크로스 큐브 배리어
- `target_start_ns` passthrough (D6) preserves the cross-cube barrier (ADR-0036 D3)를 보존하며, 폴백 계산은 직접 M_CPU 단위 테스트가 계속
established by IO_CPU (ADR-0036 D3), while the fallback computation 동작하도록 한다.
keeps direct-to-M_CPU unit tests working. - 부모 `result_data`의 기존 값에 대한 PE별 메트릭의 `max` 병합은 동일한
- Per-PE metric `max`-merge against existing parent `result_data` 부모를 공유하는 크로스 큐브 IO_CPU 팬아웃에 견고하다.
values is robust to cross-cube IO_CPU fan-out sharing the same
parent.
### Negative ### Negative
- No partial-failure semantics — a missing per-PE response stalls the - 부분 실패 의미가 없음 — 누락된 PE별 응답은 부모 `all_done`을 무기한
parent `all_done` indefinitely. Acceptable for simulation; not 스톨시킨다. 시뮬레이션 용도로는 수용 가능하나 프로덕션 스타일의
suitable as a production-style endpoint. 엔드포인트로는 적합하지 않다.
- `_resolve_dma_destinations`'s cube-wide hbm_ctrl fallback is dead - `_resolve_dma_destinations`의 큐브 전역 hbm_ctrl 폴백은 데드 코드이다
code (no such node exists post-ADR-0017 D4). Kept defensively; (ADR-0017 D4 이후 그런 노드는 존재하지 않음). 방어적으로 남겨두었으나
invites confusion and merits a follow-up cleanup. 혼동을 유발하므로 후속 정리가 권장된다.
- DMA resource serialization applies only at dispatch (the `put` call - DMA 자원 직렬화는 디스패치에만 적용된다(언바운드 store에서 `put`
is instantaneous in unbounded stores). The capacity=1 channel 호출은 즉시적). capacity=1 채널은 "본 M_CPU에서 동시에 in-flight인
models "one request in flight at a time at this M_CPU", not 요청은 하나"를 모델링하며 "전송 지속 시간 직렬화"를 모델링하지 않는다
"transfer duration serialization" — readers must consult wire — 실제 전송 병렬성은 와이어 프로세스(ADR-0015 D2)와 `drain_ns`를
processes (ADR-0015 D2) and `drain_ns` for actual transfer 참조해야 한다.
parallelism.
## Links ## Links
- ADR-0009 D3 (M_CPU fan-out and aggregation completion semantics) - ADR-0009 D3 (M_CPU 팬아웃 및 집계 완료 의미)
- ADR-0009 D5 (`target_start_ns` — passed through unchanged when - ADR-0009 D5 (`target_start_ns` — 존재 시 변경 없이 통과; 부재 시
present; computed as per-cube barrier when absent) 큐브별 배리어로 계산)
- ADR-0011 D-VA3 (MmuMapMsg fabric path includes M_CPU as PE fan-out - ADR-0011 D-VA3 (MmuMapMsg 패브릭 경로에 M_CPU PE 팬아웃 지점으로
point) 포함됨)
- ADR-0014 D4 (DMA engine capacity=1; M_CPU.DMA mirrors the same - ADR-0014 D4 (DMA 엔진 capacity=1; M_CPU.DMA가 큐브 수준에서 동일한
contract at cube level) 계약을 미러링)
- ADR-0015 D5 (M_CPU.DMA is internal subcomponent of M_CPU, not a - ADR-0015 D5 (M_CPU.DMA는 M_CPU의 내부 서브 컴포넌트이며 토폴로지
topology node) 노드가 아님)
- ADR-0017 D9 (AddressResolver returns per-PE `hbm_ctrl.pe{X}`) - ADR-0017 D9 (AddressResolver PE `hbm_ctrl.pe{X}`를 반환)
- ADR-0036 D3 / D4 (IO_CPU stamps `target_start_ns`; M_CPU passes - ADR-0036 D3 / D4 (IO_CPU `target_start_ns`를 스탬프; M_CPU는 변경
through unchanged; nbytes=0 invariant preserved through fan-out) 없이 통과; 팬아웃 전반에서 nbytes=0 불변식 보존)
+127 -138
View File
@@ -1,4 +1,4 @@
# ADR-0036: IO_CPU Component Model # ADR-0036: IO_CPU 컴포넌트 모델
## Status ## Status
@@ -6,72 +6,70 @@ Accepted
## Context ## Context
IO_CPU is the IO chiplet's host-facing endpoint inside the simulation IO_CPU는 시뮬레이션 그래프 내부의 IO 칩렛 호스트 대향 엔드포인트이다.
graph. PCIE_EP receives host messages from the runtime API and routes PCIE_EP는 런타임 API로부터 호스트 메시지를 수신하여 io_noc를 통해
them via the io_noc; for command-bearing requests (KernelLaunch, 라우팅한다; 명령을 동반하는 요청(KernelLaunch, MmuMap/Unmap)의 경우
MmuMap/Unmap) the io_noc forwards to IO_CPU, which: io_noc는 IO_CPU로 전달하며, IO_CPU는 다음을 수행한다:
- Fans out the request to per-cube M_CPUs. - 요청을 큐브별 M_CPU로 팬아웃.
- Aggregates per-cube responses into a single host-visible completion. - 큐브별 응답을 단일 호스트 가시 완료로 집계.
- For kernel launches, stamps a global `target_start_ns` barrier so - 커널 런치의 경우, 타깃이 된 모든 큐브의 모든 PE가 동일한 시뮬레이션
every PE across every targeted cube begins kernel body execution at 시각에 커널 본체 실행을 시작하도록 전역 `target_start_ns` 배리어를
the same simulated time (ADR-0009 D5). 스탬프함(ADR-0009 D5).
Memory R/W traffic bypasses IO_CPU per ADR-0015 D4 / ADR-0016 D3; Memory R/W 트래픽은 ADR-0015 D4 / ADR-0016 D3에 따라 IO_CPU를 우회한다;
this component therefore handles only command-plane traffic in normal 따라서 본 컴포넌트는 정상 동작에서 명령 평면 트래픽만을 처리한다.
operation.
This ADR documents the IO_CPU component implementation that realizes 본 ADR은 위의 책임을 실현하는 IO_CPU 컴포넌트 구현을 문서화한다.
those responsibilities.
## Decision ## Decision
### D1. Role ### D1. 역할
IO_CPU is the host-facing endpoint of the IO chiplet. It has two IO_CPU는 IO 칩렛의 호스트 대향 엔드포인트이다. 두 가지 주요 책임을
primary responsibilities: 갖는다:
1. **Multi-cube fan-out** — distribute KernelLaunchMsg / MmuMapMsg / 1. **멀티 큐브 팬아웃** KernelLaunchMsg / MmuMapMsg / MmuUnmapMsg를
MmuUnmapMsg to per-cube M_CPUs. 큐브별 M_CPU로 분배.
2. **Response aggregation** — collect per-cube ResponseMsg, signal 2. **응답 집계** — 큐브별 ResponseMsg를 수집하고, 타깃이 된 모든 큐브가
parent `txn.done` when all targeted cubes have responded. 응답한 후 부모 `txn.done`을 시그널.
A third, narrower responsibility applies only to KernelLaunchMsg: 세 번째이자 더 좁은 책임은 KernelLaunchMsg에만 적용된다:
**`target_start_ns` global barrier stamping** (D3). **`target_start_ns` 전역 배리어 스탬핑**(D3).
The component does **not**: 본 컴포넌트는 다음을 하지 **않는다**:
- Decide routing — paths are pre-computed by the router (ADR-0002). - 라우팅 결정 — 경로는 라우터에 의해 사전 계산된다(ADR-0002).
- Decode tensor or kernel internals — those concerns belong to - 텐서 또는 커널 내부 디코드 — 그러한 관심사는 M_CPU / PE_CPU / 엔진에
M_CPU / PE_CPU / engines. 속한다.
- Handle PE-level fan-out — M_CPU fans out within a cube (ADR-0009 D3). - PE 수준 팬아웃 처리 — M_CPU가 큐브 내에서 팬아웃한다(ADR-0009 D3).
- Handle Memory R/W data path — those bypass IO_CPU per ADR-0015 D4 - Memory R/W 데이터 경로 처리 — ADR-0015 D4와 ADR-0016 D3에 따라
and ADR-0016 D3 (Memory R/W resolution code in IO_CPU를 우회한다(`_resolve_cube_targets` 내의 Memory R/W 해석 코드는
`_resolve_cube_targets` exists as a defensive fallback only). 방어적 폴백으로만 존재).
Per invocation (`run()`): applies the configured `overhead_ns` once 호출당(`run()`): 들어오는 Transaction마다 설정된 `overhead_ns`를 한 번
per incoming Transaction (D8). 적용한다(D8).
### D2. Forward path — multi-cube fan-out ### D2. 정방향 경로 — 멀티 큐브 팬아웃
When a non-response Transaction arrives, the worker: 응답이 아닌 Transaction이 도착하면, 워커는:
1. Pays `overhead_ns` via `run()`. 1. `run()`을 통해 `overhead_ns`를 지불.
2. Calls `_resolve_cube_targets` to derive the list of `(sip, cube)` 2. `_resolve_cube_targets`를 호출하여 요청으로부터 `(sip, cube)` 타깃
targets from the request (D5). 리스트를 도출(D5).
3. For each target: 3. 각 타깃에 대해:
- Resolves M_CPU node id via `ctx.resolver.find_m_cpu(sip, cube)`. - `ctx.resolver.find_m_cpu(sip, cube)`를 통해 M_CPU 노드 id를 해석.
- Resolves the path via `ctx.router.find_node_path(io_cpu, m_cpu)`. - `ctx.router.find_node_path(io_cpu, m_cpu)`를 통해 경로를 해석.
- Creates a per-cube sub-Transaction with `path` populated and - `path`가 채워진 큐브별 서브 Transaction을 생성하여 `path[1]`
forwards it to `path[1]` (the first hop on the io_noc). (io_noc의 첫 홉)으로 전달.
4. Registers aggregation state: `_pending[request_id] = (expected, 4. 집계 상태 등록: `_pending[request_id] = (expected, received=0,
received=0, parent_done)`. parent_done)`.
### D3. KernelLaunch `target_start_ns` global barrier (ADR-0009 D5) ### D3. KernelLaunch `target_start_ns` 전역 배리어 (ADR-0009 D5)
IO_CPU is the canonical stamper for `target_start_ns`. When the IO_CPU `target_start_ns`의 정규 스탬퍼이다. 요청이
request is a `KernelLaunchMsg`, IO_CPU computes a single global `KernelLaunchMsg`일 때, IO_CPU는 타깃이 된 모든 큐브의 모든 PE를 포괄하는
barrier covering every targeted PE across every targeted cube: 단일 전역 배리어를 계산한다:
```text ```text
for (sip, cube) in cube_targets: for (sip, cube) in cube_targets:
@@ -85,132 +83,123 @@ for (sip, cube) in cube_targets:
target_start_ns = env.now + global_max target_start_ns = env.now + global_max
``` ```
The request is then replaced (via `dataclasses.replace`) so the 이후 요청은 (`dataclasses.replace`를 통해) 교체되어 스탬프된 값이 팬아웃
stamped value propagates through the fan-out. 전반에 전파된다.
Two overhead corrections: 두 가지 오버헤드 보정:
- `io_overhead_ns` is subtracted because IO_CPU has already paid it - `io_overhead_ns`는 차감되는데, IO_CPU가 본 메서드 실행 전에 `run()`에서
in `run()` before this method runs. 이미 지불했기 때문이다.
- `m_overhead_ns` is subtracted once because it appears as the - `m_overhead_ns`는 한 번 차감되는데, 경로 레이턴시에서 leg1의 종단점인
endpoint of leg1 *and* the start of leg2 in path latency, but 동시에 leg2의 시작점으로 두 번 등장하지만 M_CPU는 런타임에 단 한 번만
M_CPU pays it only once at run time. 지불하기 때문이다.
Every downstream PE_CPU yields until `target_start_ns` before 모든 다운스트림 PE_CPU는 커널 본체 실행을 시작하기 전 `target_start_ns`
beginning kernel body execution; all PEs therefore start at the same 까지 yield한다; 이를 통해 개별 디스패치 경로가 얼마나 오래 걸렸는지와
simulated time regardless of how long their individual dispatch path 무관하게 모든 PE가 동일한 시뮬레이션 시각에 시작한다.
took.
### D4. KernelLaunch sub-Transactions carry `nbytes=0` ### D4. KernelLaunch 서브 Transaction `nbytes=0`을 운반
Per-cube sub-Transactions for KernelLaunchMsg force `nbytes=0`, KernelLaunchMsg의 큐브별 서브 Transaction은 부모 `txn.nbytes`를 무시하고
overriding the parent `txn.nbytes`: `nbytes=0`을 강제한다:
- Kernel launch is a control message; payload size is irrelevant at - 커널 런치는 제어 메시지이다; 데이터 패브릭 수준에서 페이로드 크기는
the data-fabric level. 무관하다.
- If `nbytes > 0`, every per-cube sub-txn occupies fabric BW on the - `nbytes > 0`이면 모든 큐브별 서브 트랜잭션이 io_noc의 공유 first-hop
io_noc's shared first hop. With 16 cubes this serializes fan-out, 패브릭 BW를 점유한다. 16개 큐브에서는 이로 인해 팬아웃이 직렬화되어
pushing far M_CPUs past `target_start_ns` and breaking the D3 먼 M_CPU들이 `target_start_ns`를 지나치게 되고 D3 불변식이 깨진다.
invariant.
Non-KernelLaunch sub-Transactions preserve `txn.nbytes` (only relevant KernelLaunch가 아닌 서브 Transaction `txn.nbytes`를 보존한다(실제
for the defensive Memory R/W fallback path, which carries actual 페이로드 크기를 운반하는 방어적 Memory R/W 폴백 경로에만 관련됨).
payload sizes).
### D5. Per-request-type cube target resolution ### D5. 요청 타입별 큐브 타깃 해석
`_resolve_cube_targets` dispatches by request type: `_resolve_cube_targets`는 요청 타입에 따라 디스패치한다:
| Request type | Source of `(sip, cube)` | `target_cubes="all"` semantics | | 요청 타입 | `(sip, cube)`의 출처 | `target_cubes="all"` 의미 |
| --- | --- | --- | | --- | --- | --- |
| `MemoryWriteMsg` | `dst_sip`, `dst_cube` (or `PhysAddr.decode(dst_pa).die_id` fallback) | single cube derived from PA decode | | `MemoryWriteMsg` | `dst_sip`, `dst_cube` (또는 `PhysAddr.decode(dst_pa).die_id` 폴백) | PA 디코드로 도출되는 단일 큐브 |
| `MemoryReadMsg` | `src_sip`, `src_cube` (or `PhysAddr.decode(src_pa).die_id` fallback) | single cube derived from PA decode | | `MemoryReadMsg` | `src_sip`, `src_cube` (또는 `PhysAddr.decode(src_pa).die_id` 폴백) | PA 디코드로 도출되는 단일 큐브 |
| `KernelLaunchMsg` | tensor shards filtered by `shard.sip == my_sip` | every cube that owns a shard on this SIP | | `KernelLaunchMsg` | `shard.sip == my_sip`으로 필터링된 텐서 샤드 | 이 SIP 위에서 샤드를 소유하는 모든 큐브 |
| `MmuMapMsg` / `MmuUnmapMsg` | `target_cubes` list, filtered to this SIP | `range(cubes_per_sip)` from spec | | `MmuMapMsg` / `MmuUnmapMsg` | 본 SIP로 필터링된 `target_cubes` 리스트 | 스펙으로부터 `range(cubes_per_sip)` |
Each IO_CPU instance fans out only within its own SIP — `_my_sip()` IO_CPU 인스턴스는 자기 SIP 내에서만 팬아웃한다 — `_my_sip()`
parses the SIP id from the node id (e.g., `sip0.io0.io_cpu` → 0). 노드 id에서 SIP id를 파싱한다(예: `sip0.io0.io_cpu` → 0).
The Memory R/W rows exist for defensive completeness; the engine's Memory R/W 행은 방어적 완전성을 위해 존재한다; 엔진의 정상 경로는
normal path routes Memory R/W via `_process_memory_direct()` / Memory R/W `_process_memory_direct()` / `find_memory_path()`로
`find_memory_path()`, bypassing IO_CPU entirely (ADR-0015 D4 / 라우팅하여 IO_CPU를 완전히 우회한다(ADR-0015 D4 / ADR-0016 D3).
ADR-0016 D3).
### D6. Response aggregation ### D6. 응답 집계
`_pending: dict[request_id → (expected, received, parent_done)]`: `_pending: dict[request_id → (expected, received, parent_done)]`:
- On dispatch: register `(len(cube_targets), 0, txn.done)`. - 디스패치 시: `(len(cube_targets), 0, txn.done)`을 등록.
- `_worker` recognises responses by `is_response=True` and routes - `_worker`는 `is_response=True`로 응답을 인식하여 `_collect_response`로
them to `_collect_response`. 라우팅한다.
- `_collect_response` increments `received`; when `received >= - `_collect_response` `received`를 증가시키며, `received >= expected`가
expected`, `parent_done.succeed()` is invoked and the entry is 되면 `parent_done.succeed()`를 호출하고 엔트리를 `_pending`에서
removed from `_pending`. 제거한다.
This is a simple per-request counter. There is no per-cube identity 이는 단순한 요청별 카운터이다. 큐브별 정체성 추적이나 부분 실패 처리는
tracking and no partial-failure handling — a missing response 없다 — 누락된 응답은 부모 done을 무기한 스톨시킨다. 프로덕션 스타일의
indefinitely stalls the parent done. Production-style failure paths 실패 경로는 현재 시뮬레이터 모델의 범위 밖이다.
are out of scope for the current simulator model.
### D7. `target_pe` resolution helper ### D7. `target_pe` 해석 헬퍼
`_resolve_pe_ids(target_pe)`: `_resolve_pe_ids(target_pe)`:
- `int` → `[target_pe]`. - `int` → `[target_pe]`.
- `tuple[int, ...]` → `list(target_pe)`. - `tuple[int, ...]` → `list(target_pe)`.
- `"all"` → `range(n_slices)`, where `n_slices` comes from cube - `"all"` → `range(n_slices)`, 여기서 `n_slices`는 큐브
`memory_map.hbm_slices_per_cube` (default 8). `memory_map.hbm_slices_per_cube`(기본 8)에서 가져온다.
Used in D3's barrier computation to enumerate every PE target per D3의 배리어 계산에서 큐브별로 모든 PE 타깃을 열거하는 데 사용된다.
cube.
### D8. Configurable `overhead_ns` ### D8. 설정 가능한 `overhead_ns`
A single attribute drives per-instance latency: 단일 속성이 인스턴스별 레이턴시를 결정한다:
| Site | impl name | overhead_ns | | 사이트 | impl 이름 | overhead_ns |
| --- | --- | --- | | --- | --- | --- |
| IO chiplet `io_cpu` | `builtin.io_cpu` | 10.0 | | IO 칩렛 `io_cpu` | `builtin.io_cpu` | 10.0 |
Applied once in `run()` per Transaction. Models command Transaction마다 `run()`에서 한 번 적용된다. IO_CPU에서의 명령 해석 및
interpretation + dispatch-decision time at IO_CPU. 디스패치 결정 시간을 모델링한다.
## Consequences ## Consequences
### Positive ### Positive
- Cross-cube and cross-SIP kernel launches share a single global - 크로스 큐브 및 크로스 SIP 커널 런치가 단일 전역 배리어를 공유한다
barrier (D3 + D4) — no per-cube divergence in start time. (D3 + D4) — 시작 시각의 큐브별 분기가 없다.
- nbytes=0 invariant keeps fan-out off the shared first-hop fabric - nbytes=0 불변식이 팬아웃을 공유 first-hop 패브릭 BW로부터 떼어내,
BW, preserving the barrier's accuracy at scale (16 cubes). 대규모(16 큐브)에서도 배리어의 정확도를 보존한다.
- Response aggregation via a single counter → minimal state, - 단일 카운터를 통한 응답 집계 → 최소 상태, 결정론적 완료 순서.
deterministic ordering of completion. - SIP별 스코핑(`_my_sip()`)이 서로 다른 SIP의 IO_CPU들을 깨끗이
- Per-SIP scoping (`_my_sip()`) keeps IO_CPUs in different SIPs 독립시킨다.
cleanly independent.
### Negative ### Negative
- No partial-failure semantics — a missing per-cube response - 부분 실패 의미가 없음 — 누락된 큐브별 응답은 부모를 무기한
indefinitely stalls the parent. Adequate for simulation but not 스톨시킨다. 시뮬레이션 용도로는 충분하나 프로덕션 스타일의
suitable as a production-style endpoint. 엔드포인트로는 적합하지 않다.
- `_pending` is a regular dict; in-flight requests accumulate state. - `_pending`은 일반 dict이다; in-flight 요청이 상태로 누적된다. 현재
Acceptable for current benchmark workloads (few concurrent 벤치마크 워크로드(미해결 런치가 적음)에는 허용 가능하나, 원리적으로는
outstanding launches); unbounded in principle. 무한하다.
- The Memory R/W resolution branches in `_resolve_cube_targets` are - `_resolve_cube_targets`의 Memory R/W 해석 분기는 정상 엔진 경로에서
dead code in the normal engine path. Kept defensively but invite 데드 코드이다. 방어적으로 남겨두었으나 우회 경로가 변경되면 드리프트
drift if the bypass path ever changes. 위험을 초래한다.
## Links ## Links
- ADR-0002 (Routing distance — path computation) - ADR-0002 (라우팅 거리 — 경로 계산)
- ADR-0009 D1 (Kernel launch is an endpoint request to IO_CPU) - ADR-0009 D1 (커널 런치는 IO_CPU에 대한 엔드포인트 요청)
- ADR-0009 D3 (M_CPU fans out within a cube; IO_CPU fans out across - ADR-0009 D3 (M_CPU는 큐브 내에서 팬아웃; IO_CPU는 큐브 사이에서 팬아웃)
cubes) - ADR-0009 D5 (IO_CPU에서의 target_start_ns 정규 스탬핑)
- ADR-0009 D5 (target_start_ns canonical stamping at IO_CPU) - ADR-0011 D-VA3 (MmuMapMsg가 큐브 팬아웃을 위해 IO_CPU를 경유)
- ADR-0011 D-VA3 (MmuMapMsg routes through IO_CPU for cube fan-out) - ADR-0012 (호스트 ↔ IO_CPU 메시지 스키마)
- ADR-0012 (Host ↔ IO_CPU message schema) - ADR-0015 D4 (Memory R/W는 IO_CPU 우회; 커널 런치는 IO_CPU 경유)
- ADR-0015 D4 (Memory R/W bypasses IO_CPU; Kernel Launch via IO_CPU) - ADR-0016 D1 (IO 칩렛 io_noc — IO_CPU가 여기 부착됨)
- ADR-0016 D1 (IO chiplet io_noc — IO_CPU attaches here) - ADR-0016 D3 (Memory R/W 경로가 IO_CPU 우회)
- ADR-0016 D3 (Memory R/W path bypasses IO_CPU) - ADR-0016 D4 (명령 해석을 위한 IO_CPU 경유 커널 런치 경로)
- ADR-0016 D4 (Kernel Launch path through IO_CPU for command
interpretation)
+123 -138
View File
@@ -1,4 +1,4 @@
# ADR-0037: Forwarding Component (forwarding_v1) # ADR-0037: Forwarding 컴포넌트 (forwarding_v1)
## Status ## Status
@@ -6,86 +6,78 @@ Accepted
## Context ## Context
The simulation graph has many node positions that exist purely to model 시뮬레이션 그래프에는 순전히 패브릭 통과를 모델링하기 위해 존재하는 노드
fabric traversal — NOC mesh routers, switches, UCIe protocol endpoints, 위치들이 많다 — NOC 메시 라우터, 스위치, UCIe 프로토콜 엔드포인트, IO
IO chiplet io_noc, transit cubes. These share a common pattern: receive 칩렛 io_noc, transit 큐브. 이들은 공통 패턴을 공유한다: 메시지를 수신하고,
a message, apply per-component overhead (modeling header decode + 컴포넌트별 오버헤드(헤더 디코드 + 라우팅 결정 시간을 모델링)를 적용하며,
routing decision time), forward to the next hop along the pre-computed 사전 계산된 경로를 따라 다음 홉으로 전달한다.
path.
This ADR defines the contract for these transit nodes: a single 본 ADR은 이러한 transit 노드에 대한 계약을 정의한다: 웜홀 cut-through
component type (`TransitComponent`) that handles flit-aware forwarding 의미로 flit 인지 포워딩을 처리하는 단일 컴포넌트 타입(`TransitComponent`)이며,
with wormhole cut-through semantics, used under multiple impl names 각 인스턴스가 수행하는 개념적 역할에 따라 여러 impl 이름 아래에 사용된다.
according to the conceptual role each instance plays.
## Decision ## Decision
### D1. Role ### D1. 역할
The Forwarding component (`TransitComponent` class) is a **stateless Forwarding 컴포넌트(`TransitComponent` 클래스)는 시뮬레이션 그래프의
transit node** in the simulation graph. It models any fabric position **상태 없는 transit 노드**이다. 메시지가 물리적으로 통과하지만 의미론적
where a message physically traverses but no semantic processing 처리는 일어나지 않는 모든 패브릭 위치를 모델링한다.
happens.
Per traversal, the component: 통과당 컴포넌트는:
1. Reads an incoming Transaction or Flit from an `in_port`. 1. `in_port`에서 들어오는 Transaction 또는 Flit을 읽는다.
2. Applies the configured per-component overhead (`overhead_ns`), 2. 설정된 컴포넌트별 오버헤드(`overhead_ns`)를 적용한다. 멀티 flit
applied **once per Transaction** even across multi-flit payloads 페이로드라도 **Transaction당 한 번** 적용된다(D2 참조).
(see D2). 3. Transaction의 사전 계산된 `path`를 따라 다음 홉을 조회한다.
3. Looks up the next hop along the Transaction's pre-computed `path`. 4. 해당 `out_port`로 전달한다; 종단 노드(다음 홉 없음)에서는 `is_last`
4. Forwards to the corresponding `out_port`; at the terminal node flit이 도착하면 `txn.done`을 시그널한다.
(no next hop), signals `txn.done` once the `is_last` flit arrives.
The component **does NOT**: 본 컴포넌트는 다음을 하지 **않는다**:
- Decide routing — paths are pre-computed by the router (ADR-0002 / - 라우팅 결정 — 경로는 라우터에 의해 사전 계산된다(ADR-0002 /
ADR-0017 D2). Forwarding only executes the per-hop step. ADR-0017 D2). Forwarding은 홉별 단계만 실행한다.
- Model wire propagation or bandwidth occupancy — separate wire - 와이어 전파나 대역폭 점유 모델링 — 컴포넌트 사이의 별도 와이어
processes between components handle that (ADR-0015 D2). 프로세스가 처리한다(ADR-0015 D2).
- Resolve addresses — the AddressResolver does that (ADR-0017 D9). - 주소 해석 — AddressResolver가 담당한다(ADR-0017 D9).
- Aggregate completion — terminal endpoints (IO_CPU, M_CPU, HBM_CTRL) - 완료 집계 — 종단 엔드포인트(IO_CPU, M_CPU, HBM_CTRL)가 담당한다.
handle that.
### D2. First-flit overhead model (header decode) ### D2. First-flit 오버헤드 모델 (헤더 디코드)
Per-Transaction `overhead_ns` is applied **exactly once**, at first Transaction `overhead_ns`는 첫 flit 도착 시 **정확히 한 번** 적용된다:
flit arrival:
- `_txn_decoded: set[int]` tracks which Transactions have already - `_txn_decoded: set[int]`이 본 노드에서 이미 오버헤드를 지불한
paid the overhead at this node. Transaction들을 추적한다.
- On first-flit arrival for a Transaction: `yield self.run(env, - 어떤 Transaction의 첫 flit 도착 시: `yield self.run(env, msg.txn.nbytes)`
msg.txn.nbytes)` — pays the overhead. — 오버헤드를 지불한다.
- Subsequent flits of the same Transaction skip the overhead — they - 동일 Transaction의 후속 flit들은 오버헤드를 건너뛰고 추가 지연 없이
pipeline through with no extra delay. 파이프라인 통과한다.
- On `is_last` flit: remove the Transaction from `_txn_decoded`. - `is_last` flit 시: Transaction`_txn_decoded`에서 제거한다.
This models the real-HW behavior where header decode and routing 이는 실제 HW의 동작 — 헤더 디코드와 라우팅 결정이 첫 flit에서 한 번
decision happen once on first flit; payload flits then stream through 일어나고, 이후 페이로드 flit들은 같은 경로로 스트리밍되는(웜홀
the same path (wormhole cut-through). Multi-hop pipelining emerges cut-through) — 을 모델링한다. 멀티 홉 파이프라이닝은 자연스럽게
naturally — each hop adds its own first-flit overhead, but flits 발현된다 — 각 홉이 자신의 first-flit 오버헤드를 추가하지만, 첫 flit
after the first do not re-pay overhead at any hop they have already 이후의 flit들은 이미 첫 flit이 통과한 어떤 홉에서도 오버헤드를 다시
passed first. 지불하지 않는다.
### D3. Serial worker forwarding (preserves order) ### D3. 직렬 워커 포워딩 (순서 보존)
The component's worker is a single SimPy process that consumes flits 본 컴포넌트의 워커는 `_inbox`에서 flit을 소비하여 도착 순서대로 직렬
from `_inbox` and forwards them serially in arrival order. The 포워딩하는 단일 SimPy 프로세스이다. 컴포넌트는 flit마다
component does NOT spawn `env.process(...)` per flit. `env.process(...)`를 spawn하지 **않는다**.
Rationale: if the first flit yields on `overhead_ns` while subsequent 근거: 첫 flit이 `overhead_ns`에서 yield하는 동안 후속 flit이 병렬
flits run in parallel processes, the later flits can overtake the 프로세스에서 실행되면, 후속 flit이 첫 flit을 추월할 수 있다. 이는 순서가
first. This produces out-of-order delivery and lets the `is_last` 어긋난 전달을 낳고, `is_last` flit이 첫 flit보다 먼저 목적지에 도착하게
flit arrive at the destination before the first flit — corrupting 하여 — 트랜잭션의 완료 의미와 다운스트림의 flit 인덱스 기반 처리 모두를
both the transaction's completion semantics and any flit-index-based 손상시킨다.
processing downstream.
### D4. Path-based next-hop routing ### D4. 경로 기반 next-hop 라우팅
Routing is **not** a Forwarding-component concern. The Transaction 라우팅은 Forwarding 컴포넌트의 관심사가 **아니다**. Transaction은 라우터에
arrives with a pre-computed `path` (built by the router; ADR-0002 / 의해 사전 계산된 `path`(ADR-0002 / ADR-0017 D2)와 함께 도착한다.
ADR-0017 D2). The component just looks up its own position in the 컴포넌트는 단지 자신의 경로상 위치를 찾아 `path[index + 1]`로 전달한다:
path and forwards to `path[index + 1]`:
```python ```python
def _next_hop_in_path(self, txn): def _next_hop_in_path(self, txn):
@@ -97,104 +89,97 @@ def _next_hop_in_path(self, txn):
return None return None
``` ```
If `next_hop` is found and present in `out_ports`, the flit is `next_hop`이 발견되고 `out_ports`에 존재하면 flit이 전달된다. 그렇지
forwarded. Otherwise (terminal node), `txn.done.succeed()` is 않으면(종단 노드) `is_last` flit이 도착할 때 `txn.done.succeed()`
invoked when the `is_last` flit arrives. 호출된다.
### D5. Flit-aware mode with Non-Flit fallback ### D5. Flit 인지 모드와 Non-Flit 폴백
`_FLIT_AWARE = True` opts this component out of the base class's `_FLIT_AWARE = True`는 본 컴포넌트가 베이스 클래스의 `_fan_in` 내 flit
flit-reassembly logic in `_fan_in`. Flits are placed directly on 재조립 로직에서 제외되도록 한다. Flit은 재조립 없이 `_inbox`에 직접
`_inbox` (no reassembly), enabling per-flit handling in the worker 놓이며, 이는 워커 루프(D2, D3)에서의 per-flit 처리를 가능케 한다.
loop (D2, D3).
Non-Flit messages — zero-byte control Transactions and other Non-Flit 메시지 — 0바이트 제어 Transaction이나 그 외 청크화되지 않는
non-chunkified payloads — fall through to the base class's legacy 페이로드 — 는 `env.process`를 통해 베이스 클래스의 레거시 `_forward_txn`
`_forward_txn` path via `env.process`. This preserves backward 경로로 빠진다. 이는 flit 수준 처리의 이득이 없는 제어 평면 트래픽에
compatibility for control-plane traffic that does not benefit from 대한 하위 호환성을 보존한다.
flit-level processing.
### D6. Multi-stream merging at the base class ### D6. 베이스 클래스에서의 멀티 스트림 병합
Multi-stream FIFO merging at routers is the base class's 라우터에서의 멀티 스트림 FIFO 병합은 Forwarding이 아닌 베이스 클래스의
responsibility, not Forwarding's. The base class's `_fan_in` spawns 책임이다. 베이스 클래스의 `_fan_in``in_port`마다 하나의 프로세스를
one process per `in_port`; all push to a single shared `_inbox`. spawn한다; 모두가 공유된 단일 `_inbox`에 push한다. 따라서 서로 다른
Flits from different upstream streams therefore interleave at 업스트림 스트림의 flit들은 `_inbox`의 FIFO 순서로 flit 단위에서
flit granularity in `_inbox`'s FIFO order. 인터리브된다.
The Forwarding worker simply consumes `_inbox` in arrival order Forwarding 워커는 단지 `_inbox`를 도착 순서대로 소비할 뿐이다
correctly modeling per-router multi-flow arbitration as 공유 inbox 위의 공정 FIFO로 라우터별 멀티 플로우 중재를 올바르게
fair-FIFO over the shared inbox. 모델링한다.
### D7. Single implementation under multiple impl names ### D7. 여러 impl 이름 아래의 단일 구현
A single `TransitComponent` class is registered under four impl names 단일 `TransitComponent` 클래스가 `components.yaml`에서 네 가지 impl
in `components.yaml`: 이름으로 등록된다:
- `builtin.forwarding` — generic forwarding (e.g., `io_noc`, - `builtin.forwarding`범용 forwarding (예: `io_noc`, `noc_router`,
`noc_router`, UCIe conn bridges) UCIe conn 브리지)
- `builtin.switch` — tray-level switch - `builtin.switch`트레이 수준 스위치
- `builtin.noc` — cube-level NOC fabric (legacy singleton; current - `builtin.noc`큐브 수준 NOC 패브릭(레거시 싱글톤; 현재 NOC
NOC routers use `builtin.forwarding`) 라우터는 `builtin.forwarding`을 사용)
- `builtin.ucie` — UCIe protocol endpoint - `builtin.ucie` — UCIe 프로토콜 엔드포인트
All four aliases instantiate the same class with the same behavior. 네 별칭 모두 동일한 동작을 갖는 동일한 클래스를 인스턴스화한다.
Per-instance differentiation lives only in `attrs.overhead_ns`. 인스턴스별 차별화는 `attrs.overhead_ns`에만 존재한다. 별도 impl 이름이
Separate impl names exist as intent tags for readability and to 존재하는 것은 가독성을 위한 의도 태그이자, 하위 호환을 깨지 않고 향후
allow future divergence without backward-incompatible config 분기를 허용하기 위함이다.
changes.
### D8. Configurable `overhead_ns` ### D8. 설정 가능한 `overhead_ns`
A single attribute drives per-instance latency: 단일 속성이 인스턴스별 레이턴시를 결정한다:
| Usage site | impl name | overhead_ns | | 사용 사이트 | impl 이름 | overhead_ns |
| --- | --- | --- | | --- | --- | --- |
| Tray-level switch | `builtin.switch` | 5.0 | | 트레이 수준 스위치 | `builtin.switch` | 5.0 |
| Cube NOC router | `builtin.forwarding` | 2.0 | | 큐브 NOC 라우터 | `builtin.forwarding` | 2.0 |
| IO chiplet io_noc | `builtin.forwarding` | 0.0 | | IO 칩렛 io_noc | `builtin.forwarding` | 0.0 |
| UCIe protocol endpoint (`ucie-{N,S,E,W}`) | `builtin.ucie` | 8.0 | | UCIe 프로토콜 엔드포인트(`ucie-{N,S,E,W}`) | `builtin.ucie` | 8.0 |
| UCIe conn bridge (`ucie-{PORT}.conn{N}`) | `builtin.forwarding` | 0.0 | | UCIe conn 브리지(`ucie-{PORT}.conn{N}`) | `builtin.forwarding` | 0.0 |
Default is 0.0. The attribute is read at each `run()` invocation, so 기본값은 0.0이다. 속성은 매 `run()` 호출에서 읽히므로 동적 재설정이
dynamic reconfiguration is possible but not currently used. 가능하나 현재는 사용되지 않는다.
## Consequences ## Consequences
### Positive ### Positive
- A single class handles all transit-node roles in the simulation - 단일 클래스가 시뮬레이션 그래프의 모든 transit 노드 역할을 처리한다
graph — minimal code surface for a high-population component type. — 개체 수가 많은 컴포넌트 타입에 대한 최소 코드 표면.
- Flit-aware processing + serial worker preserves wormhole semantics - Flit 인지 처리 + 직렬 워커는 per-flit 프로세스 오버헤드 없이 멀티 홉
across multi-hop paths without per-flit process overhead. 경로 전반에 걸쳐 웜홀 의미를 보존한다.
- `overhead_ns` is the only per-instance tunable; routing, BW, and - `overhead_ns`만이 유일한 인스턴스별 튜너블이다; 라우팅, 대역폭, 주소
address resolution stay cleanly separated in their own components / 해석은 자체 컴포넌트/모듈에서 깨끗이 분리되어 있다.
modules. - 멀티 스트림 병합이 베이스 클래스 구조에서 자연스럽게 발현된다; 라우터
- Multi-stream merging emerges from the base-class structure; no 전용 로직이 공정 FIFO 중재를 중복 구현하지 않는다.
router-specific logic duplicates fair-FIFO arbitration. - Non-Flit 폴백 경로는 모든 메시지를 flit 프레임워크로 강제하지 않고도
- Non-Flit fallback path keeps control-plane traffic working without 제어 평면 트래픽이 계속 동작하도록 한다.
forcing every message into the flit framework.
### Negative ### Negative
- The single class hides usage-site intent inside `attrs.overhead_ns` - 단일 클래스가 사용 사이트의 의도를 `attrs.overhead_ns` 설정 안에
configuration; readers must consult `topology.yaml` + 숨긴다; 어떤 impl 이름이 어떤 동작 클래스로 매핑되는지 보려면 독자가
`components.yaml` to see which impl name maps to which behavior `topology.yaml` + `components.yaml`을 참조해야 한다.
class. - per-flit 직렬 워커는 `overhead_ns`가 크고 같은 라우터에 다수의 동시
- Per-flit serial worker is a bottleneck if `overhead_ns` is large 트랜잭션이 도착할 때 병목이 된다; 현재 값(0–8 ns)에서는 무시할 만한
and many concurrent transactions arrive at the same router; current 수준이다.
values (08 ns) make this negligible.
## Links ## Links
- ADR-0002 (Routing distance — path computation) - ADR-0002 (라우팅 거리 — 경로 계산)
- ADR-0015 D1 (Component port model) - ADR-0015 D1 (컴포넌트 포트 모델)
- ADR-0015 D2 (Wire process — BW + propagation, separate from this - ADR-0015 D2 (와이어 프로세스 — 본 컴포넌트와 별개의 BW + 전파)
component) - ADR-0015 D6 (Transit 큐브 forwarding 패턴)
- ADR-0015 D6 (Transit cube forwarding pattern) - ADR-0016 D1 (IO 칩렛 io_noc — 본 컴포넌트 사용)
- ADR-0016 D1 (IO chiplet io_noc — uses this component) - ADR-0017 D1 (큐브 NOC 라우터 — 본 컴포넌트 사용)
- ADR-0017 D1 (Cube NOC routers — use this component) - ADR-0017 D6 (UCIe 분해 — `ucie-{PORT}` 인스턴스가 본 컴포넌트 사용)
- ADR-0017 D6 (UCIe decomposition — `ucie-{PORT}` instances use this - ADR-0033 D1 (Flit 인지 통과, first-flit 오버헤드, 멀티 스트림 병합
component) 의미)
- ADR-0033 D1 (Flit-aware pass-through, first-flit overhead,
multi-stream merge semantics)
@@ -1,139 +0,0 @@
# ADR-0013: Verification Strategy and Phase 1 Test Plan
## Status
Accepted
## Context
KernBench is a system-level simulator whose correctness is defined by:
- adherence to SPEC-defined invariants,
- determinism and debuggability,
- explicit modeling of routing and latency.
Given the evolving implementation, we need a stable verification strategy
that prevents architectural drift while allowing incremental development.
This ADR defines the Phase 1 verification plan and what constitutes
"correct behavior" for early implementations.
---
## Decision
### D1. Verification is contract-based
Verification MUST be derived from:
- SPEC requirements,
- accepted ADRs.
Tests MUST validate architectural contracts, not incidental implementation details.
---
### D2. Phase 1 verification scope
Phase 1 verification focuses on:
- message contract validity (ADR-0012),
- routing and fan-out semantics at the IO_CPU boundary (ADR-0009),
- PA-first memory addressing and shard tagging (ADR-0011),
- core latency and trace invariants (SPEC 0.1, R2).
Microarchitectural accuracy, bandwidth contention, and cycle-level behavior
are explicitly out of scope in Phase 1.
---
### D3. Required Phase 1 verification cases
The following verification cases MUST be supported by the implementation:
#### V1. Message schema validation
- KernelLaunch requests missing `(sip, cube, pe)` in any tensor shard MUST be rejected.
- MemoryWrite/MemoryRead requests missing destination/source placement tags MUST be rejected.
- Completion results MUST follow the `ok / error_code / error_message` contract.
#### V2. IO_CPU fan-out and aggregation
Given:
- a topology with one SIP, one CUBE, and two PEs,
- a KernelLaunch request containing two tensor shards targeting different PEs,
The system MUST:
- submit a single KernelLaunch to IO_CPU,
- fan-out work internally to both PEs,
- aggregate completion and return a single deterministic completion to the host.
#### V3. Latency and trace invariants
For any valid request:
- the hop-by-hop trace MUST be non-empty,
- total latency MUST be greater than zero,
- repeated runs with identical inputs MUST produce identical traces.
#### V4. Topology independence and cross-domain coverage
Verification cases MUST pass for multiple topology shapes, including:
- minimal: (1 SIP, 1 CUBE, 1 PE)
- multi-PE: (1 SIP, 1 CUBE, N PEs)
- multi-CUBE within a SIP: (1 SIP, M CUBEs, ≥1 PE per CUBE)
- multi-SIP tray: (K SIPs, ≥1 CUBE per SIP, ≥1 PE per CUBE)
For multi-CUBE and multi-SIP topologies, Phase 1 verification focuses on:
- explicit connectivity (required links exist),
- deterministic routing and control-path traversal,
- non-empty traces and latency > 0 for representative cross-domain requests
(inter-CUBE and inter-SIP paths).
Tests MUST NOT hardcode topology sizes, node ids, or link counts.
Instead, tests MUST derive expectations from the compiled topology metadata
---
### D4. Phase 1 artifacts
Phase 1 MAY include:
- verification-only test code,
- topology fixtures,
- trace inspection utilities.
Phase 1 MUST NOT require:
- production code changes solely to satisfy tests,
- weakening or removing tests to allow progress.
---
### D5. Phase 2 enforcement
Phase 2 (Apply) MUST:
- run the Phase 1 verification cases,
- rollback all changes if any verification fails,
- preserve tests as authoritative contracts.
---
## Consequences
- Architectural correctness is enforced early.
- Tests serve as executable documentation of system behavior.
- Implementation remains flexible without losing rigor.
---
## Links
- SPEC 0.1, R2, R6
- ADR-0011 (Memory Addressing — PA / VA / LA)
- ADR-0012 (Host ↔ IO_CPU message schema)
- ADR-0009 (Kernel execution semantics)
+48 -4
View File
@@ -52,7 +52,35 @@ def test_status_mismatch_fails(tmp_path: Path) -> None:
_make_adr(tmp_path / "docs/adr/ADR-0001-foo-bar.md", "0001", status="Accepted") _make_adr(tmp_path / "docs/adr/ADR-0001-foo-bar.md", "0001", status="Accepted")
_make_adr(tmp_path / "docs/adr-ko/ADR-0001-foo-bar.md", "0001", status="Proposed") _make_adr(tmp_path / "docs/adr-ko/ADR-0001-foo-bar.md", "0001", status="Proposed")
errs = v.verify(tmp_path) errs = v.verify(tmp_path)
assert any("Status block mismatch" in e for e in errs) assert any("Status keyword mismatch" in e for e in errs)
def test_status_parenthetical_translation_passes(tmp_path: Path) -> None:
"""Parenthetical commentary in Status may be translated; only the
lifecycle keyword needs to match across EN/KO."""
_make_adr(
tmp_path / "docs/adr/ADR-0001-foo-bar.md", "0001",
status="Accepted (Revision 2 - concrete bit layout)",
)
_make_adr(
tmp_path / "docs/adr-ko/ADR-0001-foo-bar.md", "0001",
status="Accepted (Revision 2 - 비트 레이아웃 확정)",
)
assert v.verify(tmp_path) == []
def test_status_list_after_keyword_passes(tmp_path: Path) -> None:
"""Trailing bullet list in Status may be translated; only first-line
keyword is compared."""
_make_adr(
tmp_path / "docs/adr/ADR-0011-foo-bar.md", "0011",
status="Accepted.\n\n- VA model: implemented (default).",
)
_make_adr(
tmp_path / "docs/adr-ko/ADR-0011-foo-bar.md", "0011",
status="Accepted.\n\n- VA 모델: 구현됨 (기본값).",
)
assert v.verify(tmp_path) == []
def test_title_id_mismatch_fails(tmp_path: Path) -> None: def test_title_id_mismatch_fails(tmp_path: Path) -> None:
@@ -77,6 +105,21 @@ def test_multiline_status_with_parenthetical_passes(tmp_path: Path) -> None:
assert v.verify(tmp_path) == [] assert v.verify(tmp_path) == []
def test_superseded_keyword_must_match(tmp_path: Path) -> None:
"""Multi-word lifecycle keywords like 'Superseded by ADR-NNNN' must
match in full (the ADR ref is part of the keyword)."""
_make_adr(
tmp_path / "docs/adr/ADR-0001-foo-bar.md", "0001",
status="Superseded by ADR-0031",
)
_make_adr(
tmp_path / "docs/adr-ko/ADR-0001-foo-bar.md", "0001",
status="Superseded by ADR-0099",
)
errs = v.verify(tmp_path)
assert any("Status keyword mismatch" in e for e in errs)
def test_crlf_normalization(tmp_path: Path) -> None: def test_crlf_normalization(tmp_path: Path) -> None:
"""KO has CRLF, EN has LF; Status content is otherwise identical -> pass.""" """KO has CRLF, EN has LF; Status content is otherwise identical -> pass."""
en = tmp_path / "docs/adr/ADR-0001-foo-bar.md" en = tmp_path / "docs/adr/ADR-0001-foo-bar.md"
@@ -105,9 +148,10 @@ def test_em_dash_title_separator_recognized(tmp_path: Path) -> None:
def test_underscore_in_slug_recognized(tmp_path: Path) -> None: def test_underscore_in_slug_recognized(tmp_path: Path) -> None:
"""ADR-0013 uses an underscore in its slug; the regex must accept it.""" """Defensive: slug regex must accept underscores even though current
_make_adr(tmp_path / "docs/adr/ADR-0013-ver-verification_strategy.md", "0013") ADRs use kebab-case (in case future ADRs reintroduce them)."""
_make_adr(tmp_path / "docs/adr-ko/ADR-0013-ver-verification_strategy.md", "0013") _make_adr(tmp_path / "docs/adr/ADR-0099-cat-foo_bar.md", "0099")
_make_adr(tmp_path / "docs/adr-ko/ADR-0099-cat-foo_bar.md", "0099")
assert v.verify(tmp_path) == [] assert v.verify(tmp_path) == []
+29 -6
View File
@@ -10,8 +10,10 @@ Checks:
- every docs/adr/<X>.md has a matching docs/adr-ko/<X>.md - every docs/adr/<X>.md has a matching docs/adr-ko/<X>.md
- every docs/adr-ko/<X>.md has a matching docs/adr/<X>.md (no orphans) - every docs/adr-ko/<X>.md has a matching docs/adr/<X>.md (no orphans)
- title line `# ADR-NNNN:` of each pair matches the filename's NNNN - title line `# ADR-NNNN:` of each pair matches the filename's NNNN
- `## Status` block content is byte-equal (after CRLF/LF normalization) - `## Status` keyword (e.g., Accepted / Proposed / Superseded by ADR-NNNN)
between EN and KO matches between EN and KO. Parenthetical commentary and trailing
list items in the Status block may differ in wording (e.g., be
translated); only the leading lifecycle keyword is compared.
Exit code: 0 if all OK, 1 if any mismatch. Exit code: 0 if all OK, 1 if any mismatch.
""" """
@@ -70,6 +72,24 @@ def extract_status_block(text: str) -> str | None:
return "\n".join(collected).strip() return "\n".join(collected).strip()
def normalize_status_keyword(block: str) -> str:
"""Reduce a Status block to its lifecycle keyword for cross-language compare.
Drops parenthetical commentary (which is allowed to be translated)
and trailing list items / extra lines, then strips trailing
punctuation. e.g.:
'Accepted' -> 'Accepted'
'Accepted.' -> 'Accepted'
'Accepted (Revision 2 - foo)' -> 'Accepted'
'Accepted (supersedes ADR-0029)' -> 'Accepted'
'Accepted.\\n\\n- VA model: ...' -> 'Accepted'
'Superseded by ADR-0031' -> 'Superseded by ADR-0031'
"""
no_parens = re.sub(r"\([^)]*\)", "", block).strip()
first_line = next((l for l in no_parens.splitlines() if l.strip()), "")
return first_line.strip().rstrip(".,;:").strip()
def verify(root: Path) -> list[str]: def verify(root: Path) -> list[str]:
errors: list[str] = [] errors: list[str] = []
en_dir = root / "docs" / "adr" en_dir = root / "docs" / "adr"
@@ -110,11 +130,14 @@ def verify(root: Path) -> list[str]:
errors.append(f"{name}: EN missing `## Status` section") errors.append(f"{name}: EN missing `## Status` section")
if ko_status is None: if ko_status is None:
errors.append(f"{name}: KO missing `## Status` section") errors.append(f"{name}: KO missing `## Status` section")
if en_status is not None and ko_status is not None and en_status != ko_status: if en_status is not None and ko_status is not None:
en_kw = normalize_status_keyword(en_status)
ko_kw = normalize_status_keyword(ko_status)
if en_kw != ko_kw:
errors.append( errors.append(
f"{name}: Status block mismatch\n" f"{name}: Status keyword mismatch\n"
f" EN: {en_status!r}\n" f" EN: {en_kw!r}\n"
f" KO: {ko_status!r}" f" KO: {ko_kw!r}"
) )
return errors return errors