Files
kernbench2/docs/adr-ko/ADR-0034-dev-hbm-controller-internal-design.md
T
ywkang 168b0c89f0 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>
2026-05-20 08:17:56 -07:00

264 lines
12 KiB
Markdown

# ADR-0034: HBM 컨트롤러 내부 설계
## Status
Accepted
## Context
`HbmCtrlComponent`는 큐브 NOC의 말단(leaf)에 위치하는 PE별 HBM
파티션 엔드포인트이다. 토폴로지 노드
`sip{S}.cube{C}.hbm_ctrl.pe{idx}` 아래에 PE마다 하나의 인스턴스가
생성되며 해당 PE의 라우터에 연결된다 (ADR-0017 D4). 본 컴포넌트는
의사 채널(PC, pseudo-channel)별 스케줄링, 버스트 단위 커밋 타이밍,
주소 기반 PC 선택, 그리고 응답을 요청자에게 되돌리는 라우팅을
모델링한다.
본 ADR은 현재 구현된 컴포넌트를 문서화한다. ADR-0017 D4/D8은 HBM CTRL이
*어디에* 부착되는지와 *어떤* 집계 대역폭을 제공해야 하는지를 정의한다.
ADR-0033 D1/D2는 HBM 모델링의 *어떤 정밀도(fidelity)*가 범위에 포함되는지를
정의한다. 본 ADR은 그 둘 사이의 공백 — 인스턴스별 내부 스케줄링 모델을
채운다.
## Decision
### D1. 역할
`HbmCtrlComponent`는 PE별 HBM 파티션 엔드포인트이다. PE당 하나의
인스턴스(큐브당 기본 8개, `cube.memory_map.hbm_slices_per_cube`로 설정)가
`cube_mesh.yaml``peX.hbm` 부착 목록을 통해 해당 PE의 라우터에 연결된다
(ADR-0017 D4). 기본 n:1 채널 매핑(ADR-0017 D8)에서는 인스턴스가
`channels_per_pe`개의 의사 채널을 하나의 엔드포인트로 집계한다.
본 컴포넌트는 다음을 모델링한다:
- PC별 스케줄링(D2) 및 R/W 명령 버스 공유.
- 주소 기반 PC 선택(D3).
- 버스트 단위 커밋 타이밍(D4).
- Flit 인지 per-flit PC 커밋 및 비동기 finalize(D5, D6).
- 읽기 데이터 드레인(drain)을 위한 명령 전용 Transaction 처리(D7).
- 요청자에게 되돌리는 응답 라우팅(D8).
다음은 모델링하지 않는다:
- Bank 수준의 row-buffer 충돌, refresh, ECC, 열 스로틀링
(ADR-0033 D3).
- 자신의 라우터 엣지를 넘어가는 PE 간 HBM 경합(라우터 메시가 처리 —
ADR-0017 D3).
- 1:1 채널 모드(ADR-0017 D8 향후 작업).
### D2. PC별 스케줄링 모델
`start()`에서 초기화되는 인스턴스별 상태:
- `_pc_avail: list[float]` — 각 PC가 다음에 자유로워지는 가장 빠른
시뮬레이션 시각; 길이 `num_pcs`, 초기값 0.0.
- `_pc_last_dir: list["R"|"W"|None]` — 각 PC의 마지막 커밋 방향, 스위치
페널티 감지에 사용(D4); 초기값 `None`.
`num_pcs``burst_bytes`는 각각 양의 2의 거듭제곱이어야 주소 기반 PC
선택(D3)이 시프트와 마스크로 축약된다.
읽기와 쓰기 요청은 PC별로 동일한 `_pc_avail` 슬롯을 공유한다 — 실제 HW에서
PC별 명령 버스는 읽기와 쓰기 트래픽이 공유하므로, PC k에 쓰기를 발행하면
PC k에 대한 후속 읽기가 정확히 버스트 시간만큼 블록된다.
요청의 방향 `dir`은 요청 타입으로부터 추론된다:
- `MemoryWriteMsg``"W"`.
- `is_write=True``PeDmaMsg``"W"`.
- 그 외 전부(`MemoryReadMsg`, 읽기 `PeDmaMsg`) → `"R"`.
### D3. 주소 기반 PC 선택
접근에 대한 PC 인덱스는 접근 주소로부터 시프트와 마스크로 도출된다:
```text
pc_shift = log2(burst_bytes) # 기본값 8 (burst=256B)
pc_mask = num_pcs - 1 # 기본값 7 (8 PCs)
pc = (address >> pc_shift) & pc_mask
```
대안적인 `(burst_bytes, num_pcs)` 쌍과의 정합성을 유지하기 위해
`start()`에서 토폴로지 설정으로부터 한 번 계산된다. 정규 기본값
`(256, 8)`에서는 PC 선택 필드가 HBM 바이트 오프셋의 비트 `[10:8]`
배치된다: 비트 `[7:0]`은 버스트 내부(같은 PC), 비트 `[10:8]`은 3비트
PC 인덱스, 비트 `[36:11]`은 PC 슬라이스 내부의 row/bank/column이다
(`phyaddr.py` 주석 참조).
주소 기반 스트라이핑은 — 주소를 보지 않는 전역 라운드로빈과 달리 —
오프셋이 분리된 동시 전송들에 대해 PC 병렬성을 보존한다: 각 전송의
버스트는 자신의 바이트 주소가 함의하는 PC 집합 위에 결정론적으로
떨어지므로, 분리된 영역에 접근하는 멀티 PE 워크로드가 단일 PC에서
충돌하지 않는다.
### D4. 버스트 단위 시간 및 PC 커밋 타이밍
단일 PC 커밋에 걸리는 시간:
```text
chunk_time = burst_bytes / pc_bw_gbs # ns
```
- `burst_bytes`(기본 256)는 flit 크기와 일치하는 버스트 단위이다
(ADR-0033 D1).
- `pc_bw_gbs`는 **빌더에서 도출**된다:
`hbm_to_router_bw_gbs / num_pcs` (`topology/builder.py`). 이는 PE당
집계 대역폭이 라우터-HBM 링크 대역폭과 같아야 한다는 ADR-0017 D8의
불변식을 강제한다.
방향 `dir`로 PC `pc`에 도착한 접근에 대한 PC별 커밋 스케줄링:
```text
switch_cost = switch_penalty_ns
if pc_last_dir[pc] not in (None, dir) else 0
start = max(env.now, pc_avail[pc]) + switch_cost
finish = start + chunk_time
pc_avail[pc] = finish
pc_last_dir[pc] = dir
```
기본 `switch_penalty_ns = 0` — 이상적인 HBM 스케줄러가 R/W 스위칭
비용을 분할 상환한다는 Tier 0 가정(ADR-0033 D2). 0이 아닌 값은
교차마다 발생하는 비관적 비용을 모델링한다.
### D5. Flit 인지 per-flit PC 커밋 (주 경로)
`_handle_flit`이 주 워커 경로이다. 각 도착 `Flit`에 대해:
1. 트랜잭션의 **첫 번째** flit인 경우(`tid = id(txn)``_txn_state`
없는 경우):
- `run(env, nbytes)`를 통해 `overhead_ns`를 한 번 적용 — 헤더 디코드
모델, first-flit overhead 패턴(ADR-0033 D1).
- `_txn_state[tid] = {"last_finish": env.now}`로 초기화.
2. `pc = _pc_for_address(flit.address)`를 계산(D3).
3. 요청 방향(D2)을 사용하여 PC별 스케줄(D4)을 적용.
4. `state["last_finish"] = max(state["last_finish"], finish)`로 갱신.
5. `flit.is_last`이면: `_txn_state[tid]`를 pop하고 `_finalize_txn`
spawn(D6).
per-flit 주소 인지 커밋이 분리된 HBM 오프셋으로 향하는 동시 멀티 PE
트래픽이 서로 다른 PC를 통해 병렬로 파이프라인되도록 하는 메커니즘이다.
### D6. 트랜잭션별 비동기 finalize
트랜잭션의 마지막 flit이 스케줄링되고 나면, finalize는 별도로 spawn된
프로세스에서 실행된다:
```python
def _finalize_txn(env, txn, last_finish):
wait = last_finish - env.now
if wait > 0:
yield env.timeout(wait)
yield from _send_response(env, txn)
```
`_handle_flit`은 이를 `env.process(...)`로 spawn한 뒤 즉시 반환하므로,
마지막 PC 커밋이 드레인되는 동안에도 워커는 다음 inbox 메시지를 집어들
수 있다.
이 분리가 없다면 — 즉 워커 자신이 `yield env.timeout(wait)`를 한다면 —
서로 다른 PC에 떨어지는 주소를 가진 동시 단일 flit 트랜잭션들도 결국
워커 내부에서 각각 `chunk_time`만큼 직렬화되어, D3와 D5가 노출하려고
설계한 PC 병렬성을 숨겨버린다.
### D7. 명령 전용 트랜잭션을 위한 non-flit 폴백
`_handle_txn`은 inbox가 `Flit`이 아닌 `Transaction`을 전달할 때 실행된다.
이는 와이어가 flit으로 분할하지 않는 명령 전용 요청에 대한 경로로 —
대표적으로 명령 트랜잭션이 `nbytes=0`을 운반하는 `MemoryReadMsg`
해당한다(데이터 드레인은 HBM CTRL 후처리에서 모델링되며, 인바운드
flit으로 모델링되지 않는다).
절차:
1. `work_bytes = txn.nbytes if txn.nbytes > 0 else int(request.nbytes or 0)`
— 읽기 명령의 경우 작업량은 요청으로 결정된다.
2. `work_bytes > 0`이면 `n_chunks = ceil(work_bytes / burst_bytes)`,
아니면 0.
3. 둘 다 > 0일 때 `chunk_interval = drain_ns / n_chunks` — 청크는
`drain/n_chunks` ns 간격으로 시간상에 스케줄링되어 병목 링크의 데이터
도착 속도를 모델링한다(ADR-0033 D1 청크 루프 드레인).
4. `overhead_ns`를 위해 `run(env, txn.nbytes)`를 한 번 적용.
5. 각 청크 `i`에 대해 `chunk_interval` ns만큼 진행한 뒤
`pc = _pc_for_address(base_address + i * burst_bytes)`로 D4 스케줄을
적용.
6. 모든 청크 스케줄링 후 `last_finish - env.now`만큼 대기한 다음
`_send_response`를 호출.
`_handle_txn``_handle_flit`과 동일한 `_pc_avail` / `_pc_last_dir`
상태를 공유한다 — 두 경로에 걸쳐 PC 스케줄링의 단일 진실 원천이 정확히
하나만 존재한다.
### D8. 응답 라우팅
`_send_response`는 요청 타입과 경로 형상에 따라 디스패치한다:
| 경우 | 트리거 | 응답 |
| --- | --- | --- |
| 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` | `nbytes=request.nbytes`(데이터 반환)인 역방향 경로 Transaction |
| Bypass — Memory Write | `"m_cpu" not in any(txn.path)` AND not Memory Read | `txn.done.succeed()` (쓰기는 로컬에서 완료) |
| 기본 | 그 외 | 역방향 경로상의 신규 `ResponseMsg(correlation_id, request_id, src_cube, src_pe, success=True)` |
"bypass" 분류는 ADR-0015 D4에서 정의된 Memory R/W 패브릭 경로(PCIE_EP →
io_noc → ucie → 큐브 라우터 → hbm_ctrl, M_CPU 미경유)와 일치한다.
PE_DMA 케이스는 내부 루프 DMA를 빠르게 유지하기 위한 전용 역방향 경로이다
(PE_DMA 읽기/쓰기는 ResponseMsg 봉투를 합성하지 않는다).
모든 역방향 경로 케이스에서, 응답 Transaction은
`out_ports[reverse_path[1]]` — 기록된 정방향 경로를 따라 되돌아가는 첫
홉 — 에 put된다. `reverse_path`의 엔트리가 2개 미만이면(축퇴된 경로),
원래의 `txn.done`이 직접 시그널된다.
### D9. 설정 가능한 속성
| 속성 | 기본값 | 출처 | 비고 |
| --- | --- | --- | --- |
| `num_pcs` | 8 | 토폴로지 큐브 `hbm_ctrl.attrs` | 2의 거듭제곱이어야 함 |
| `pc_bw_gbs` | 32.0 | 빌더 도출: `hbm_to_router_bw_gbs / num_pcs` | ADR-0017 D8 불변식 강제 |
| `burst_bytes` | 256 | 토폴로지 attrs | 2의 거듭제곱이어야 함; `flit_bytes`와 동일(ADR-0033 D1) |
| `switch_penalty_ns` | 0.0 | 토폴로지 attrs | Tier 0 기본값; 0이 아니면 비관적 R/W 스위칭 모델링 |
| `efficiency` | 1.0 | 토폴로지 attrs | 빌더 시점에 `hbm_to_router_bw_gbs`에 적용(라우터 엣지 BW 스케일링만) |
| `overhead_ns` | 0.0 | 토폴로지 attrs | First-flit 디코드 오버헤드(D5) |
`pc_bw_gbs`는 yaml 측 중복 없이 PE당 집계 대역폭을 라우터-HBM 링크
대역폭과 일치시키기 위해 직접 설정되지 않고 `topology/builder.py`에서
도출된다.
## Consequences
### Positive
- 주소 기반 PC 선택은 주소를 보지 않는 라운드로빈이 무너뜨릴 멀티 스트림
HBM 병렬성을 보존한다 — 분리된 HBM 영역을 갖는 멀티 PE 워크로드에서
중요하다.
- Flit 인지 경로(D5) + 비동기 finalize(D6)는 웜홀 파이프라이닝을
보존하며, 연속적인 단일 flit 트랜잭션에 대해 PC 병렬성을 노출한다.
- PC 스케줄링의 단일 진실 원천(D4 메커니즘이 D5 flit 경로와 D7 청크 루프
경로 모두에서 사용됨).
- 빌더 도출 `pc_bw_gbs`가 yaml 규율이 아닌 코드에서 ADR-0017 D8을
강제한다.
### Negative
- PC 내부의 bank 수준 충돌 모델링이 없음; bank/row-buffer 재사용에
주소-무관(ADR-0033 D3).
- HBM 스케줄러 없음(FR-FCFS / write-buffer / watermark drain); PC당 고정
FIFO. 버스티한 혼합 R/W는 `switch_penalty_ns`로 근사화된다
(ADR-0033 D2).
- `_txn_state``id(txn)`로 키를 잡는 일반 dict이다; 동시 트랜잭션마다
in-flight 상태가 누적되며 `is_last` 시에만 제거된다. 현재 워크로드에는
충분하다.
## Links
- ADR-0001 (물리 주소 레이아웃 — PC 비트 필드 주석)
- ADR-0015 D4 (Memory R/W 패브릭 경로 — bypass 응답 케이스)
- ADR-0017 D4 (PE별 HBM 파티셔닝 — PE 라우터로의 부착)
- ADR-0017 D8 (HBM 채널 매핑 모드 — 본 ADR이 구현하는 n:1 집계)
- ADR-0017 D9 (AddressResolver — `hbm_ctrl.pe{pe_id}` 엔드포인트 해석)
- ADR-0033 D1 (정확한 모델링 — PC별 병렬성, 스위치 페널티, flit 인지
PC 커밋, first-flit 오버헤드, 청크 루프 드레인)
- ADR-0033 D2 (스위치 페널티 기본값 0 — 이상적 스케줄러의 분할 상환)