Documents four cross-cutting surfaces one layer deeper than the prior G4 batch: - 0050 par-ccl-algorithm-module-contract: how to author a new CCL algorithm in src/kernbench/ccl/algorithms/. Pairs with ADR-0045's bench-module contract. Pins the four required public symbols (kernel, kernel_args, TOPO_NAME_TO_KIND constants, kernel alias), the 9 + tl standardized kernel signature, the kernel_args tuple format, sip_topo_kind dispatch, and the ccl.yaml entry workflow. - 0051 lat-routing-helper-api: every public method of AddressResolver (resolve, find_m_cpu, find_pcie_ep, find_io_cpu, find_all_pcie_eps) and PathRouter (find_path, find_path_with_distance, find_mcpu_dma_path, find_memory_path, find_node_path + 2 shims). Pins the four adjacency graphs (_adj_all / _adj / _adj_mcpu_dma / _adj_local) and the edge-kind exclusion sets they use, plus the single-owner naming convention. - 0052 dev-oplog-memory-store-schemas: OpRecord's 7 fields, the per-op_name params matrix (dma_read, dma_write, gemm_*, math, math reduction, composite_gemm, ipcq_copy, unknown), snapshot timing rules (math = all inputs, dma_write = HBM-only — ADR-0027 race avoidance), TileToken stage_type capture, and MemoryStore's (space, addr) two-level dict with reference-store semantics. - 0053 dev-topology-builder-algorithms: the 6-stage compile pipeline, cube_mesh.yaml's source_hash cache and its 5 input fields, the cube NoC auto-layout algorithm (row/col placement, HBM exclusion zone, PE/M_CPU/SRAM attachment via nearest-router, UCIe N/S/E/W distribution), the node naming convention (single-owner with router.py), the edge-kind catalog, the 4 view projections, and a table of spec-field changes vs mesh regeneration. Bilingual pair verifier passes for all four EN/KO pairs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
15 KiB
ADR-0053: Topology Builder + Visualizer Algorithms
Status
Accepted (2026-05-22).
topology/builder.py, topology/mesh_gen.py, topology/visualizer.py 가
함께 수행하는 토폴로지 컴파일·시각화 파이프라인의 핵심 알고리즘 선택
(placement-driven router attachment, mesh auto-layout, source_hash 캐시,
view projection, SVG rendering) 을 명시한다. ADR-0006 가 topology
compilation 의 high-level intent (compiled topology, distance extraction,
automatic diagram generation) 를 정의하나, builder 가 실제로 어떤
알고리즘을 사용하는지 는 코드 grep 으로만 확인 가능했다.
First action (제일 처음에 하는 일)
resolve_topology(path_str) 가 호출되면 다음 4 단계가 순서대로 일어난다:
- 경로 검증 (
builder.py::resolve_topology):Path(path_str).expanduser().resolve(), 존재 확인, file 여부 확인. 실패 시FileNotFoundError또는ValueError. - YAML 파싱 (
_read_spec):yaml.safe_load. parse error 면 line/ column 정보 포함한ValueError. dict 가 아니면 reject. - mesh 자동 생성 (
mesh_gen.ensure_mesh_file): topology yaml 과 같은 디렉터리에cube_mesh.yaml을 만들거나 (캐시 invalid 시) 재사용 (캐시 hit 시). 이 단계가 cube NoC 의 라우터 grid 와 부착 정보를 결정. - graph 컴파일 (
_compile_graph): system → IO chiplets → cubes → inter-cube edges → IO↔cube edges → system↔IO edges 순으로 nodes/edges 를 누적, 그 다음 4 개의 view projection (system, sip, cube, pe) 을 생성하여TopologyGraph로 묶음.
즉, topology compile 의 첫 일은 "topology.yaml 을 dict 로 읽고, 동일 디렉터리에 cube_mesh.yaml 을 생성/검증한 뒤, system→sip→cube→pe 순으로 flat graph + 4-view projection 을 만드는 것" 이다.
Context
topology/ 패키지의 책임:
- builder.py (1207 줄): topology.yaml 을 받아
TopologyGraph(nodes- edges + 4 view projections) 를 컴파일.
- mesh_gen.py (305 줄): cube NoC 의 라우터 grid 와 PE/UCIe/M_CPU/SRAM
부착 위치를 자동 결정하여
cube_mesh.yaml로 캐시. - visualizer.py (887 줄):
TopologyGraph로부터 SVG 다이어그램 4종 (system / sip / cube / pe) 을 생성.
ADR-0006 가 "topology compilation 의 결과는 distance metadata 와 diagram generation 의 single source" 라는 high-level 결정을 정의하나, 구체 알고리즘 (예: placement-driven nearest-router attachment, HBM 제외 zone 산출, source_hash 의 어떤 필드가 invalidation 을 트리거하는가) 은 ADR 에 없다.
특히 다음 결정들이 ADR-level 에 부재:
- 왜 mesh_gen 이 별도 파일 (
cube_mesh.yaml) 로 캐시되는가? - source_hash 가 어떤 필드를 포함하며, 어떤 변경이 재생성을 강제하는가?
- placement coordinate 가 cube 좌표가 아닌 mm 단위인 이유?
- HBM zone 제외와 UCIe N/S/E/W 분배가 mesh 안에서 어떻게 결정되는가?
- view projection 4 개 (system/sip/cube/pe) 의 추상화 레벨 차이?
이 ADR 이 이 결정들을 한 곳에 정리한다.
Decision
D1. compile 파이프라인 — 6 단계
_compile_graph(spec):
- 시스템 노드 생성 (
_instantiate_system):fabric.switch0, host CPU 등 system-level 노드 추가. - per-SIP loop (
for sip_id in range(system.sips.count)):- IO chiplets (
_instantiate_io_chiplets): pcie_ep / io_cpu / io_noc / io_ucie PHY / conn 노드 + 내부 양방향 edge 생성. - cube instantiation (
_instantiate_cube): cube_mesh.yaml 의 router grid 를 토대로 cube-별 라우터, PE sub-components (pe_cpu, pe_dma, pe_fetch_store, pe_gemm, pe_math, pe_mmu, pe_tcm, pe_scheduler, pe_ipcq), m_cpu, sram, hbm_ctrl 인스턴스화 + 내부 edge 깔기. - inter-cube edges (
_add_inter_cube_edges): UCIe N/S/E/W mesh edge. - IO ↔ cube edges (
_add_io_to_cube_edges): io_noc 와 cube 의 edge UCIe phy 사이 연결.
- IO chiplets (
- switch ↔ IO edges (
_add_system_to_io_edges):fabric.switch0와 각 SIP 의pcie_ep사이 양방향 edge (ADR-0038 D3 + ADR-0010 의 cross-SIP IPCQ 경로). - view projections 4 종 build:
_build_system_view(spec)— Tray 레벨, SIP 들과 system switch._build_sip_view(spec)— SIP 안의 cube mesh + IO chiplet._build_cube_view(spec)— 단일 cube 안의 router grid + PE/M_CPU/SRAM/ HBM_CTRL 부착._build_pe_view(spec)— 단일 PE 안의 9 sub-components + 내부 edge.
- TopologyGraph 리턴:
TopologyGraph(spec, nodes, edges, system_view, sip_view, cube_view, pe_view).
이 6 단계는 순서가 의미를 가진다: cubes 가 만들어진 후에야 inter-cube edges 가 valid 한 src/dst 를 갖고, IO chiplet 이 먼저 만들어져야 IO ↔ cube edge 가 그를 참조할 수 있다. 새 노드 종류를 끼울 때는 의존 관계를 보고 적절한 위치에 삽입해야 한다.
D2. cube_mesh.yaml — 별도 파일 + source_hash 캐시
mesh_gen.ensure_mesh_file(cube_spec, mesh_path):
source_hash = _compute_source_hash(cube_spec)산출. 입력 필드:geometry(cube_mm.w/h 등).pe_layout(corners, pe_per_corner).ucie.n_connections.memory_map.hbm_mapping_mode.placement(m_cpu/sram pos_mm).
mesh_path(=topology.yaml와 같은 디렉터리의cube_mesh.yaml) 이 존재하고existing.source_hash == source_hash면 재사용 (캐시 hit).- 아니면
_generate_mesh(cube_spec, source_hash)로 새 mesh 생성 후 yaml 로 저장.
별도 파일로 캐시하는 이유:
- mesh 생성은 PE/UCIe/router 부착 계산이 들어가 매번 다시 하기 무거움.
- 같은 cube spec 으로 여러 번 실행 시 동일 mesh 가 보장되어야 함.
- 사람이 직접 mesh 를 inspect / debug 할 수 있는 artifact 가 됨.
source_hash 가 list 한 5 개 필드가 mesh 형상을 결정하는 핵심이며, 그
외 (예: bandwidth, overhead_ns) 변경은 mesh 재생성을 트리거하지 않는다.
D3. cube NoC mesh auto-layout 알고리즘
_generate_mesh(cube_spec):
D3.1. 행/열 결정
pe_positions = _corner_pe_positions(cube_w, cube_h): 4 corner (NW/NE/ SW/SE) 마다 PE center 좌표 (mm). hardcoded(1.5, 1.5)/(cube_w-1.5, cube_h-1.5)패턴 +pe_per_corner=2면 각 corner 에 2 PE 위치.col_xs = _compute_col_positions(...): PE 들의 x 좌표 union +max_spacing = 3.0 mm보다 큰 gap 에 relay 컬럼 삽입.row_ys, rows_per_half = _compute_row_positions(cube_h, n_connections, pe_positions):n_conn = max(n_connections, 2)(hot path minimum).rows_per_half = ceil(n_conn / 2).- top 절반 + HBM 두 row + bottom 절반. HBM 은 `(cube_h/2 - 1.5, cube_h/2
- 1.5)
에 위치. PE rows 와 HBM rows 사이hbm_gap = 1.5 mm`.
- 1.5)
D3.2. HBM 제외 zone
hbm_row_start = rows_per_half, hbm_row_end = rows_per_half + 1.
hbm_col_start = n_cols // 2 - 1, hbm_col_end = n_cols // 2.
이 (row, col) 사각형 안의 router 슬롯은 None 으로 마킹 (라우터 없음).
실제 HBM 컨트롤러는 별도 hbm_ctrl.pe{X} 노드로 ADR-0017 D9 의 per-PE
파티션 패턴을 따라 부착.
D3.3. PE 부착
각 corner 의 PE 들은 다음 row 에 매핑:
- Top half: NW → row 0, NE → row 1 (top_corners 안의 index).
- Bottom half: SW → row
hbm_row_end + 1, SE → rowhbm_row_end + 2.
각 PE 의 x 좌표가 가장 가까운 col 의 router 에 부착 (min(range(n_cols), key=lambda c: abs(col_xs[c] - pe_x))). 부착 항목은 pe{pe_idx}.dma,
pe{pe_idx}.cpu, pe{pe_idx}.hbm 세 가지 (router 별 attach list 에 push).
D3.4. M_CPU / SRAM 부착 — nearest router by Euclidean distance
placement.m_cpu.pos_mm (default [1.5, 5.5]) 와 placement.sram.pos_mm
(default [1.5, 8.5]) 의 좌표에서 가장 가까운 router 를 Euclidean
distance 로 찾아 attach list 에 "m_cpu" / "sram" 추가.
D3.5. UCIe N/S/E/W 분배
ucie_pe_rows = top_pe_rows + bot_pe_rows (총 2 * rows_per_half 개).
- UCIe-E: 매 PE row 마다 rightmost col 의 router 에
ucie_e.c{i}. - UCIe-W: leftmost col 의 router 에
ucie_w.c{i}(E 의 mirror). - UCIe-N/S: PE column 들 중 절반을 좌측, 절반을 우측으로 나눠 top row / bottom row 의 해당 col 에 부착.
각 UCIe connection 은 c{i} index 가 붙어 ucie_n_connections 만큼의 PHY
가 분산된다 (ADR-0017 D5+).
D4. node 명명 규칙 — 단일 소유자
builder.py 는 다음 명명 규칙으로 노드를 만든다 (ADR-0051 D5 의 단일 소유자 원칙):
fabric.switch0— system-level switch.sip{S}.{io_id}.{pcie_ep|io_cpu|io_noc|io_ucie.{dir}|conn.{id}}— IO chiplet.sip{S}.cube{C}.{m_cpu|sram|hbm_ctrl.pe{X}|noc.r{R}c{C}|...}— cube 내부.sip{S}.cube{C}.pe{P}.{pe_cpu|pe_dma|pe_fetch_store|pe_gemm|pe_math|pe_mmu|pe_tcm|pe_scheduler|pe_ipcq}— PE sub-components.
이 명명 규칙을 변경하려면 builder.py 와 router.py (ADR-0051) 의 helper 양쪽이 함께 갱신되어야 한다. 컴포넌트는 명명 규칙을 직접 알지 못하고 helper 만 호출한다.
D5. edge kind 분류
각 edge 가 부여받는 kind 가 라우팅 정책 (ADR-0051 D2) 의 입력. 주요
kind 값:
"pe_internal"— PE 내부 sub-component 간."pe_to_router"— PE_DMA ↔ cube NoC router."router_mesh"— cube NoC router 간."router_to_hbm","router_to_mcpu","router_to_sram","sram_to_router"등 — cube-attached component 간."ucie_internal","ucie_conn_to_router","router_to_ucie_conn","ucie_conn_to_noc","noc_to_ucie_conn","ucie_mesh"— UCIe 관련."io_internal"— IO chiplet 내부."io_to_cube","cube_to_io"— IO ↔ cube 경계."pcie"— switch ↔ pcie_ep."command"— control-plane only edges (M_CPU ↔ NOC 등; PE DMA path 에서 제외).
새 edge kind 를 추가하면 router.py 의 4 adjacency graph (ADR-0051 D2) 의
어느 카테고리에 속할지 결정해야 한다 — 그렇지 않으면 default 로 _adj_all
에만 포함되어 의도와 다른 routing 발생 가능.
D6. view projection — 4 추상화 레벨
TopologyGraph 는 flat (nodes + edges) 외에 4 개의 view projection 을
보유:
- system_view (
_build_system_view): Tray 레벨. SIP 박스들 +fabric. switch0. PCIE 링크 표시. 외부 발표용 high-level overview. - sip_view (
_build_sip_view): 한 SIP 안. cube mesh + IO chiplet (pcie_ep + io_cpu + io_noc). UCIe N/S/E/W 가 cube 간 연결로 보임. - cube_view (
_build_cube_view): 한 cube 안. router grid + PE/M_CPU/ SRAM/HBM_CTRL 부착 + UCIe PHY edge 부분. cube 내부 라우팅 / placement 진단용. - pe_view (
_build_pe_view): 한 PE 안. 9 sub-components + 내부 edge (pe_internal kind). 자세한 PE 내부 dataflow 검토용.
view 는 spec 에서 visualization.emit_views: [system, sip, cube] 같이
선택적으로 출력 (ADR-0006). pe view 는 기본 출력에서 빠져 있으나 코드는
유지 (자세한 디버그용).
D7. visualizer.py — SVG 다이어그램 출력
emit_diagrams(graph, out_dir) 가 모든 view 를 SVG 로 렌더. 핵심 함수:
_render_view_svg(view)— 일반적인 view 렌더 (router grid 가 없는 경우)._render_cube_view_svg(view, spec)— cube view 전용 (HBM block 그리기, router grid layout, PE/M_CPU/SRAM/HBM positioning)._draw_node,_draw_edge— 노드 / edge 의 시각적 표현._pick_scale,_compute_node_sizes— 자동 스케일링.
visualizer 는 derived artifact (ADR-0006) 로 분류되며, 코드 변경 시 production check 대상이 아니다. CLAUDE.md 의 "Derived Artifacts" 항목과 정합.
D8. spec 변경의 영향 범위
| spec 필드 | 영향 | mesh 재생성 |
|---|---|---|
system.sips.count |
SIP 갯수, node 수 | No |
sip.cube_mesh.w/h |
cube mesh 형상 | No |
cube.geometry.cube_mm.w/h |
cube 크기 (mm) | Yes |
cube.pe_layout.corners/pe_per_corner |
PE 부착 위치 | Yes |
cube.ucie.n_connections |
UCIe PHY 분배 | Yes |
cube.memory_map.hbm_mapping_mode |
HBM 분배 모드 | Yes |
cube.placement |
M_CPU/SRAM 위치 | Yes |
cube.memory_map.* (위 제외) |
HBM 용량 / BW | No |
*.links.*.bw_gbs |
edge bandwidth | No |
*.attrs.overhead_ns |
컴포넌트 latency | No |
위 표가 D2 의 _compute_source_hash 입력과 일치. mesh 재생성이 필요한
변경은 cube_mesh.yaml 의 source_hash 가 자동 invalidate.
Alternatives Considered
A1. mesh 를 별도 캐시 파일 없이 매 compile 시 재생성
기각. 같은 spec 으로 여러 번 호출되는 케이스 (CLI run, probe, test) 마다 mesh 생성 비용을 다시 지불. 또한 사람이 mesh 를 inspect 할 수 있는 artifact 가 사라짐.
A2. mesh 생성을 builder.py 에 합치기
기각 (현재). 305 줄 짜리 자체 알고리즘이며, mesh layout 의 결정 (placement- driven router attachment, HBM exclusion zone) 이 builder 의 일반적인 node/edge 생성 책임과 다르다. 분리 유지가 단일 책임 원칙에 더 부합.
A3. placement coordinate 를 cube 좌표 (col/row) 로 표현
기각. mm 단위 좌표가 시각화 측 (visualizer) 과 mesh layout 측 (nearest- router 산출) 양쪽에서 일관되게 쓰인다. cube 좌표는 router grid 가 결정 되기 전까지는 정의되지 않으므로 placement 입력에 부적절.
A4. view projection 을 lazy 하게 생성
기각 (현재). 4 개 view 의 생성 비용이 작고 (보통 < 100 ms), eager 생성이
TopologyGraph 를 통한 single source of truth 를 보장.
A5. visualizer 출력 형식을 SVG 외 (PNG/PDF) 도
기각. SVG 가 vector + 텍스트 검색 가능 + 브라우저 직접 렌더가 가능한 가장 유연한 형식. PNG 변환이 필요하면 별도 도구 (rsvg-convert 등) 로 후처리.
Consequences
- ADR-0006 의 high-level intent 가 D1–D7 로 구체화되어, topology 변경 영향을 D8 표로 빠르게 가늠 가능.
- D3 의 mesh auto-layout 알고리즘이 ADR-level 에서 굳어져, 추후 새 PE 부착 패턴 (예: HBM 의 6-zone 분할) 도입 시 어느 단계가 영향받는지 명확.
- D5 의 edge kind 목록과 D7 의 view 구조가 명시되어, 새 component 종류 추가 시 (builder + router + visualizer) 어디까지 손대야 하는지 PR reviewer 가 한눈에 파악 가능.
- D2 의 source_hash invalidation 규칙이 명시되어, cube_mesh.yaml 이 stale 하게 남는 경우 (예: bw 값만 바꿨을 때) 가 정상 동작임이 분명.