Files
kernbench2/docs/adr/ADR-0019-NOC-Local HBM.md
T
ywkang 31c7110da7 Add ADR-0018 (LA/BAAW addressing) and ADR-0019 (NOC per-channel HBM)
ADR-0018: LA replaces VA, BAAW segment-based mapping in PE_DMA,
1:1 (per-channel) and n:1 (aggregated) modes with parameterized
channel count.

ADR-0019: xbar/bridge removal, channel router topology with
horizontal line layout, aggregated router for n:1 mode,
unified NOC path for local/remote HBM access.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 01:05:27 -07:00

514 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0019: CUBE NOC 내 Per-Channel 및 Aggregated HBM 연결 모델
## Status
Proposed
## Context
ADR-0018에서는 LA 기반 주소 추상화와 BAAW를 도입하여,
logical memory access가 다음 두 형태의 request로 변환되도록 정의하였다.
- 1:1 mode: 하나의 logical access → N개의 per-channel request
- n:1 mode: 하나의 logical access → 하나의 aggregated request
여기서 N = `hbm_pseudo_channels / pes_per_cube` (= `channels_per_pe`)이며,
topology 파라미터로 결정된다.
### 기존 구조의 문제
현재 구현(`topology/builder.py`)에서는:
- PE_DMA → NOC → xbar_top/xbar_bot → HBM_CTRL.slice{0-7} 경로를 사용
- HBM은 8개 slice(= PE 수) 노드로 모델링됨
- local/remote access가 서로 다른 경로를 사용:
- local: NOC → xbar → HBM slice
- cross-half: NOC → xbar_top → bridge → xbar_bot → HBM slice
- remote cube: NOC → UCIe → remote NOC → remote xbar → remote HBM slice
이 구조의 한계:
- pseudo-channel 단위 모델링 불가 (slice = PE 단위, channel 단위 아님)
- xbar/bridge가 local/remote 경로를 이원화
- 1:1 / n:1 mode를 일관되게 표현할 수 없음
---
## Decision
### D1. HBM controller는 CUBE당 단일 endpoint로 정의한다
현재의 `hbm_ctrl.slice{0-7}` (8개 노드)를 **`hbm_ctrl` 단일 노드**로 통합한다.
- pseudo channel은 HBM controller 노드 자체가 아니라,
controller에 연결되는 **link의 단위**로 표현한다
- HBM controller 내부의 read/write resource 모델은 유지하되,
mode에 따라 contention 단위가 달라진다:
- 1:1 mode: per-channel link가 BW contention point (controller는 terminal)
- n:1 mode: aggregated link가 BW contention point (controller는 terminal)
노드 네이밍 변경:
| 현재 | 변경 후 |
| ---- | ------- |
| `sip0.cube0.hbm_ctrl.slice0` ~ `slice7` | `sip0.cube0.hbm_ctrl` (단일) |
---
### D2. xbar, bridge 완전 제거
기존 다음 노드 및 관련 edge를 모두 제거한다:
- `{cube}.xbar_top`, `{cube}.xbar_bot`
- `{cube}.bridge.left`, `{cube}.bridge.right`
- `noc_to_xbar`, `xbar_to_noc`, `xbar_to_hbm`, `hbm_to_xbar` 종류의 edge
- `xbar_to_bridge`, `bridge_to_xbar` 종류의 edge
이들의 역할(PE→HBM 라우팅, cross-half 연결)은
channel router 및 horizontal line 연결이 대체한다 (D3, D4 참조).
---
### D3. 1:1 mode: per-channel router 기반 연결
#### channel router 정의
1:1 mode에서 graph compiler는 pseudo-channel 수만큼의 **channel router** 노드를
생성한다. channel router는 NOC의 일부이다.
```text
파라미터 예: hbm_pseudo_channels=64, pes_per_cube=8
→ channels_per_pe = 8, 총 64개 channel router 생성
```
노드 네이밍: `{cube}.ch_r{global_channel_id}`
| PE | 소유 channel routers |
| -- | -------------------- |
| PE0 | ch_r0, ch_r1, ..., ch_r7 |
| PE1 | ch_r8, ch_r9, ..., ch_r15 |
| ... | ... |
| PE7 | ch_r56, ch_r57, ..., ch_r63 |
일반화: PE `p`는 channel `p * channels_per_pe` ~ `(p+1) * channels_per_pe - 1`을 소유.
#### PE_DMA ↔ channel router 연결
각 PE_DMA는 자신의 local channel router N개와 양방향 link로 연결된다:
```text
sip0.cube0.pe0.pe_dma ←→ sip0.cube0.ch_r0 (bw: channel_bw_gbs)
sip0.cube0.pe0.pe_dma ←→ sip0.cube0.ch_r1 (bw: channel_bw_gbs)
...
sip0.cube0.pe0.pe_dma ←→ sip0.cube0.ch_r7 (bw: channel_bw_gbs)
```
- edge kind: `pe_to_ch_router` / `ch_router_to_pe`
- BW: `hbm_channel_bw_gbs` (e.g., 32 GB/s)
- distance: PE에서 channel router까지의 물리적 거리 (layout 기반)
#### channel router ↔ HBM controller 연결
각 channel router는 cube의 hbm_ctrl과 양방향 link로 연결된다:
```text
sip0.cube0.ch_r0 ←→ sip0.cube0.hbm_ctrl (bw: channel_bw_gbs)
sip0.cube0.ch_r1 ←→ sip0.cube0.hbm_ctrl (bw: channel_bw_gbs)
...
sip0.cube0.ch_r63 ←→ sip0.cube0.hbm_ctrl (bw: channel_bw_gbs)
```
- edge kind: `ch_router_to_hbm` / `hbm_to_ch_router`
- BW: `hbm_channel_bw_gbs` (e.g., 32 GB/s)
#### 1:1 mode 전체 데이터 경로
```text
PE0.pe_dma
├→ ch_r0 → hbm_ctrl (32 GB/s)
├→ ch_r1 → hbm_ctrl (32 GB/s)
├→ ...
└→ ch_r7 → hbm_ctrl (32 GB/s)
총 PE0 local BW = N × channel_bw_gbs
```
---
### D4. 1:1 mode: horizontal line 연결 (cross-PE channel 접근)
#### 배치 규칙
같은 **logical index**를 가지는 channel router들을 동일한 horizontal row에 배치한다.
logical index 정의: `logical_idx = global_channel_id % channels_per_pe`
```text
파라미터 예: channels_per_pe=8, pes_per_cube=8
Row 0: ch_r0 (PE0) ↔ ch_r8 (PE1) ↔ ch_r16 (PE2) ↔ ... ↔ ch_r56 (PE7)
Row 1: ch_r1 (PE0) ↔ ch_r9 (PE1) ↔ ch_r17 (PE2) ↔ ... ↔ ch_r57 (PE7)
Row 2: ch_r2 (PE0) ↔ ch_r10 (PE1) ↔ ch_r18 (PE2) ↔ ... ↔ ch_r58 (PE7)
...
Row 7: ch_r7 (PE0) ↔ ch_r15 (PE1) ↔ ch_r23 (PE2) ↔ ... ↔ ch_r63 (PE7)
```
일반화: Row `r`에는 `{ch_r(p * N + r) | p ∈ 0..pes_per_cube-1}`이 위치.
여기서 `N = channels_per_pe`.
#### horizontal line edge
같은 row에서 인접한 channel router끼리 양방향 edge로 연결:
```text
ch_r0 ↔ ch_r8 ↔ ch_r16 ↔ ... ↔ ch_r56
```
- edge kind: `ch_horizontal`
- BW: `hbm_channel_bw_gbs` (or configurable inter-PE channel BW)
- distance: PE 간 물리적 거리
#### cross-PE HBM 접근 경로 (1:1 mode)
PE0이 PE1의 local channel (ch_r8)에 접근하는 경우:
```text
PE0.pe_dma → ch_r0 → ch_r8 (horizontal hop) → hbm_ctrl
```
Dijkstra router가 horizontal line을 통해 최단 경로를 탐색한다.
#### 설계 의도
이 배치 규칙은:
- routing 규칙 단순화: horizontal = cross-PE, vertical = PE-local
- 거리 계산 단순화: row 내 hop 수 = |src_pe - dst_pe|
- 구조적 반복성 확보: 모든 row가 동일한 구조
---
### D5. n:1 mode: aggregated router 기반 연결
#### aggregated router 정의
n:1 mode에서 graph compiler는 PE당 1개의 **aggregated router** 노드를 생성한다.
aggregated router는 NOC의 일부이다.
노드 네이밍: `{cube}.pe{p}.agg_router`
#### 연결 구조
```text
sip0.cube0.pe0.pe_dma ←→ sip0.cube0.pe0.agg_router (bw: N × channel_bw_gbs)
sip0.cube0.pe0.agg_router ←→ sip0.cube0.hbm_ctrl (bw: N × channel_bw_gbs)
```
- edge kind: `pe_to_agg_router` / `agg_router_to_pe`, `agg_to_hbm` / `hbm_to_agg`
- BW: `channels_per_pe × hbm_channel_bw_gbs` (e.g., 8 × 32 = 256 GB/s)
#### cross-PE 접근 (n:1 mode)
PE0이 PE1의 local HBM에 접근하는 경우:
```text
PE0.pe_dma → PE0.agg_router → PE1.agg_router → hbm_ctrl
```
aggregated router 간 연결:
```text
pe0.agg_router ↔ pe1.agg_router ↔ pe2.agg_router ↔ ... ↔ pe7.agg_router
```
- edge kind: `agg_horizontal`
- BW: configurable (inter-PE aggregated BW)
#### n:1 mode 전체 데이터 경로
```text
PE0.pe_dma → PE0.agg_router → hbm_ctrl
(BW = N × channel_bw_gbs = 256 GB/s)
```
---
### D6. local / remote access를 NOC로 통일한다
- 모든 memory access는 NOC(channel router 또는 aggregated router)를 통해 전달된다
- local access도 별도의 fast path(xbar)를 사용하지 않는다
- cross-cube (remote) access 경로:
```text
1:1 mode: PE_DMA → ch_r{local} → ch_r{...} → UCIe → remote_ch_r → remote_hbm_ctrl
n:1 mode: PE_DMA → agg_router → UCIe → remote_agg_router → remote_hbm_ctrl
```
UCIe 연결은 기존 구조를 유지하되,
양쪽 endpoint가 xbar 대신 channel router 또는 aggregated router가 된다.
---
### D7. AddressResolver 변경
현재 `AddressResolver.resolve()`:
```python
# 현재: HBM offset → pe_slice → "sip{s}.cube{c}.hbm_ctrl.slice{pe_slice}"
pe_slice = PhysAddr.hbm_pe_id(addr.hbm_offset, self._slice_size_bytes)
return f"sip{s}.cube{c}.hbm_ctrl.slice{pe_slice}"
```
변경 후:
```python
# 변경: HBM → 단일 endpoint
return f"sip{s}.cube{c}.hbm_ctrl"
```
pe_slice 계산이 제거된다.
BAAW가 이미 dst_node를 결정하므로, PE_DMA의 1:1 mode에서는
resolver를 거치지 않고 BAAW가 직접 channel router node_id를 반환한다.
n:1 mode에서도 BAAW가 aggregated router node_id를 반환한다.
resolver.resolve()는 외부 접근(M_CPU DMA 등) 및 backward compatibility용으로 유지한다.
---
### D8. topology.yaml 설정 변경
#### 추가 설정
```yaml
cube:
memory_map:
hbm_mapping_mode: n_to_one # one_to_one | n_to_one
hbm_pseudo_channels: 64 # 전체 pseudo channel 수
hbm_channels_per_pe: 8 # PE당 local channel 수 (= pseudo_channels / pes_per_cube)
hbm_channel_bw_gbs: 32.0 # per-channel bandwidth (GB/s)
hbm_total_gb_per_cube: 48 # 유지
```
#### 제거 설정
```yaml
# 제거 대상
links:
xbar_to_hbm_bw_gbs: 256.0 # → channel_bw_gbs × channels_per_pe로 대체
xbar_to_hbm_mm: 2.5 # → ch_router_to_hbm_mm으로 대체
xbar_to_bridge_bw_gbs: 128.0 # → 제거 (bridge 없음)
xbar_to_bridge_mm: 3.0 # → 제거
noc_to_xbar_bw_gbs: ... # → 제거
noc_to_xbar_mm: ... # → 제거
```
#### 추가 link 설정
```yaml
links:
pe_to_ch_router_bw_gbs: 32.0 # PE_DMA ↔ channel router
pe_to_ch_router_mm: 1.0 # 물리적 거리
ch_router_to_hbm_bw_gbs: 32.0 # channel router ↔ hbm_ctrl
ch_router_to_hbm_mm: 2.0 # 물리적 거리
ch_horizontal_bw_gbs: 32.0 # channel router 간 horizontal link
ch_horizontal_mm: 1.5 # PE 간 horizontal 거리
# n:1 mode용
pe_to_agg_router_bw_gbs: 256.0 # PE_DMA ↔ aggregated router
agg_to_hbm_bw_gbs: 256.0 # aggregated router ↔ hbm_ctrl
agg_horizontal_bw_gbs: 256.0 # aggregated router 간 link
```
---
### D9. 대역폭 수치 정합
| 구성 | 값 |
| ---- | --- |
| pseudo channels per cube | 64 (파라미터) |
| PEs per cube | 8 (파라미터) |
| channels per PE (N) | `pseudo_channels / pes_per_cube` = 8 |
| per-channel BW | 32 GB/s (파라미터) |
| per-PE local BW | N × 32 = 256 GB/s |
| cube total HBM BW | 64 × 32 = 2048 GB/s |
두 모드에서 PE당 effective BW는 동일:
- 1:1 mode: N개 channel link × channel_bw_gbs = N × 32 = 256 GB/s
- n:1 mode: 1개 aggregated link = N × channel_bw_gbs = 256 GB/s
---
## Consequences
### Positive
- 1:1 mode에서 pseudo-channel 단위 BW contention 모델링이 자연스럽다
- n:1 mode에서 aggregated bandwidth 모델이 단순하다
- local / remote access 경로가 NOC로 통일된다
- graph compiler 기반 topology 생성과 잘 맞는다
- channel 수, PE 수가 모두 파라미터이므로 다양한 구성을 테스트할 수 있다
### Negative
- 1:1 mode에서 router 및 link 수가 크게 증가한다
(64 channel routers + 64 edges to HBM + 56 horizontal edges per cube)
- local access도 NOC 경로를 사용하므로 모델이 더 일반화된다
- 기존 xbar 기반 테스트 전면 재작성 필요
- SimPy 노드 수 증가에 따른 시뮬레이션 성능 영향 가능
---
## Alternatives
### A1. 기존 xbar + HBM slice 유지
- local/remote 경로가 이원화됨
- pseudo-channel 단위 모델링 불가
- 1:1/n:1 mode 전환 불가
### A2. per-channel link를 항상 생성하고 n:1에서만 집계
- topology 구조가 항상 1:1 크기
- n:1 semantics를 link aggregation으로 표현하기 복잡
- router 노드 수 감소 효과 없음
### A3. 단계적 전환 (xbar 유지 + NOC 경로 추가)
- 호환성은 높으나 두 경로 공존으로 복잡도 증가
- 최종적으로 xbar 제거가 필요하므로 중간 단계의 가치가 낮음
---
## Implementation Notes
### topology/builder.py 변경 상세
#### 제거할 코드 (현재 `_instantiate_cube()` 내)
- xbar_top, xbar_bot 노드 생성 (~line 495-508)
- bridge.left, bridge.right 노드 생성
- noc ↔ xbar edge 생성 (~line 540-555)
- xbar ↔ hbm_ctrl.slice edge 생성 (~line 510-538)
- xbar ↔ bridge edge 생성 (~line 557-572)
#### 추가할 코드
1:1 mode:
```python
N = hbm_channels_per_pe # from topology config
total_ch = hbm_pseudo_channels
# channel router 노드 생성
for ch_id in range(total_ch):
pe_id = ch_id // N
nodes[f"{cp}.ch_r{ch_id}"] = Node(
id=f"{cp}.ch_r{ch_id}", kind="noc_router", impl="noc_v1",
attrs={}, pos_mm=(...), # horizontal row = ch_id % N
)
# PE_DMA ↔ local channel router edges
for pe_id in range(pes_per_cube):
for local_ch in range(N):
ch_id = pe_id * N + local_ch
edges.append(Edge(
src=f"{cp}.pe{pe_id}.pe_dma", dst=f"{cp}.ch_r{ch_id}",
bw_gbs=channel_bw, kind="pe_to_ch_router", ...))
edges.append(Edge(
src=f"{cp}.ch_r{ch_id}", dst=f"{cp}.pe{pe_id}.pe_dma",
bw_gbs=channel_bw, kind="ch_router_to_pe", ...))
# channel router ↔ hbm_ctrl edges
for ch_id in range(total_ch):
edges.append(Edge(
src=f"{cp}.ch_r{ch_id}", dst=f"{cp}.hbm_ctrl",
bw_gbs=channel_bw, kind="ch_router_to_hbm", ...))
edges.append(Edge(
src=f"{cp}.hbm_ctrl", dst=f"{cp}.ch_r{ch_id}",
bw_gbs=channel_bw, kind="hbm_to_ch_router", ...))
# horizontal line edges (same logical index)
for row in range(N):
for p in range(pes_per_cube - 1):
ch_a = p * N + row
ch_b = (p + 1) * N + row
edges.append(Edge(
src=f"{cp}.ch_r{ch_a}", dst=f"{cp}.ch_r{ch_b}",
bw_gbs=ch_horizontal_bw, kind="ch_horizontal", ...))
edges.append(Edge(
src=f"{cp}.ch_r{ch_b}", dst=f"{cp}.ch_r{ch_a}",
bw_gbs=ch_horizontal_bw, kind="ch_horizontal", ...))
```
n:1 mode:
```python
# aggregated router 노드 생성
for pe_id in range(pes_per_cube):
nodes[f"{cp}.pe{pe_id}.agg_router"] = Node(
id=f"{cp}.pe{pe_id}.agg_router", kind="noc_router", impl="noc_v1",
attrs={}, pos_mm=(...),
)
agg_bw = N * channel_bw # aggregated BW
# PE_DMA ↔ aggregated router
for pe_id in range(pes_per_cube):
edges.append(Edge(
src=f"{cp}.pe{pe_id}.pe_dma", dst=f"{cp}.pe{pe_id}.agg_router",
bw_gbs=agg_bw, kind="pe_to_agg_router", ...))
edges.append(Edge(
src=f"{cp}.pe{pe_id}.agg_router", dst=f"{cp}.pe{pe_id}.pe_dma",
bw_gbs=agg_bw, kind="agg_router_to_pe", ...))
# aggregated router ↔ hbm_ctrl
for pe_id in range(pes_per_cube):
edges.append(Edge(
src=f"{cp}.pe{pe_id}.agg_router", dst=f"{cp}.hbm_ctrl",
bw_gbs=agg_bw, kind="agg_to_hbm", ...))
edges.append(Edge(
src=f"{cp}.hbm_ctrl", dst=f"{cp}.pe{pe_id}.agg_router",
bw_gbs=agg_bw, kind="hbm_to_agg", ...))
# aggregated router 간 horizontal link
for p in range(pes_per_cube - 1):
edges.append(Edge(
src=f"{cp}.pe{p}.agg_router", dst=f"{cp}.pe{p+1}.agg_router",
bw_gbs=agg_horizontal_bw, kind="agg_horizontal", ...))
edges.append(Edge(
src=f"{cp}.pe{p+1}.agg_router", dst=f"{cp}.pe{p}.agg_router",
bw_gbs=agg_horizontal_bw, kind="agg_horizontal", ...))
```
### 영향받는 기존 테스트
| 테스트 파일 | 영향 |
| ---------- | ---- |
| `tests/test_topology_compile.py` | xbar/bridge 노드 참조 제거, channel router 검증 추가 |
| `tests/test_topology_load.py` | topology.yaml 설정 변경 반영 |
| `tests/test_pe_components.py` | PE_DMA 라우팅 경로 변경 |
| `tests/test_sip_parallel.py` | cross-PE 접근 경로 변경 |
| xbar/bridge를 직접 테스트하는 케이스 | 제거 |
---
## Test Requirements
- 1:1 mode에서 channel별 link로 request가 전달되는지 확인
- n:1 mode에서 aggregated link로 request가 전달되는지 확인
- 두 mode에서 topology가 올바르게 생성되는지 검증:
- 1:1: `total_ch`개 channel router + per-PE link + horizontal link
- n:1: `pes_per_cube`개 aggregated router + per-PE link
- 동일 workload에서 effective BW가 두 모드에서 일관적인지 확인
- cross-PE 접근 시 horizontal line routing이 동작하는지 확인
- cross-cube 접근 시 UCIe를 통한 routing이 동작하는지 확인
- 파라미터 변경 (channels_per_pe = 4, 8, 16 등)에서 topology 생성이 정상인지 확인
---
## Links
- ADR-0018 (LA + BAAW) → addressing 측 연동
- ADR-0017 (Cube NOC 2D Mesh) → 본 ADR이 xbar/bridge 부분을 대체
- ADR-0004 (Memory Semantics) → BW 모델 재정의
- ADR-0014 (PE Internal Execution Model) → PE_DMA 경로 변경 영향