Replace xbar/bridge/single-NOC with explicit router mesh (ADR-0019)

- Remove xbar_top/bot, bridge, single noc node from topology
- Each cube_mesh.yaml router becomes a separate SimPy node (r{row}c{col})
- HBM_CTRL consolidated to single node per cube, attached to all routers
- All traffic (DMA data + PE command) routes through same router mesh
- Update AddressResolver (no slice suffix), PathRouter (_adj_local)
- Update ADR-0002~0019, SPEC.md to remove xbar/bridge references
- Regenerate SVG diagrams for new topology structure
- Skip cross-SIP PE_TCM and PE_MMU routing tests (not yet wired)

326 passed, 13 skipped

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-04 17:51:28 -07:00
parent 31c7110da7
commit 5917b3497c
35 changed files with 953 additions and 1326 deletions
+133 -331
View File
@@ -127,22 +127,27 @@ def test_mesh_file_pe_corner_positions():
)
def test_mesh_file_xbar_top_routers():
"""xbar_top must list top-half PE routers."""
def test_mesh_file_no_xbar_section():
"""mesh output must not contain xbar section (ADR-0019 D2)."""
_graph()
mesh = yaml.safe_load(MESH_PATH.read_text())
top_routers = mesh["xbar"]["top"]["routers"]
for rid in ["r0c0", "r0c1", "r1c4", "r1c5"]:
assert rid in top_routers, f"{rid} should connect to xbar_top"
assert "xbar" not in mesh, "xbar section should be removed from cube_mesh.yaml"
def test_mesh_file_xbar_bot_routers():
"""xbar_bot must list bottom-half PE routers."""
def test_mesh_file_pe_hbm_attached():
"""PE routers must have pe{idx}.hbm in attach list (ADR-0019 D1)."""
_graph()
mesh = yaml.safe_load(MESH_PATH.read_text())
bot_routers = mesh["xbar"]["bottom"]["routers"]
for rid in ["r4c0", "r4c1", "r5c4", "r5c5"]:
assert rid in bot_routers, f"{rid} should connect to xbar_bot"
for rid, rdata in mesh["routers"].items():
if rdata is None:
continue
for item in rdata["attach"]:
if item.endswith(".dma"):
pe_prefix = item.rsplit(".", 1)[0]
hbm_item = f"{pe_prefix}.hbm"
assert hbm_item in rdata["attach"], (
f"{rid} has {item} but missing {hbm_item}"
)
def test_mesh_file_ucie_distribution():
@@ -233,107 +238,65 @@ def test_mesh_ucie_all_four_directions():
# ══════════════════════════════════════════════════════════════════
# 2. Topology Graph: XBAR Top/Bottom (replaces per-PE chaining)
# 2. Topology Graph: Explicit Router Mesh (ADR-0019)
# ══════════════════════════════════════════════════════════════════
def test_xbar_top_node_exists():
"""Each cube must have an xbar_top node."""
def test_router_nodes_exist():
"""Cube must have explicit router nodes from cube_mesh.yaml."""
graph = _graph()
assert "sip0.cube0.xbar_top" in graph.nodes
for rkey in ["r0c0", "r0c1", "r1c4", "r5c5"]:
assert f"sip0.cube0.{rkey}" in graph.nodes, f"Router {rkey} missing"
def test_xbar_bot_node_exists():
"""Each cube must have an xbar_bot node."""
def test_no_xbar_or_bridge_nodes():
"""xbar/bridge nodes must not exist (ADR-0019 D2)."""
graph = _graph()
assert "sip0.cube0.xbar_bot" in graph.nodes
bad = [n for n in graph.nodes if "xbar" in n or "bridge" in n]
assert len(bad) == 0, f"Old xbar/bridge nodes found: {bad[:5]}"
def test_no_per_pe_xbar_nodes():
"""Per-PE xbar nodes (xbar.pe0..pe7) must not exist."""
def test_no_single_noc_node():
"""Cube-level single noc node must not exist (replaced by explicit routers)."""
graph = _graph()
for i in range(8):
assert f"sip0.cube0.xbar.pe{i}" not in graph.nodes, (
f"xbar.pe{i} should not exist in new topology"
)
assert "sip0.cube0.noc" not in graph.nodes
def test_no_xbar_chain_edges():
"""xbar_chain kind edges must not exist."""
def test_single_hbm_ctrl_node():
"""Each cube must have single hbm_ctrl (no slices)."""
graph = _graph()
chain_edges = [e for e in graph.edges if e.kind == "xbar_chain"]
assert len(chain_edges) == 0, (
f"Found {len(chain_edges)} xbar_chain edges; chaining is replaced by XBAR top/bot"
)
assert "sip0.cube0.hbm_ctrl" in graph.nodes
slices = [n for n in graph.nodes if "hbm_ctrl.slice" in n]
assert len(slices) == 0, f"HBM slices should not exist: {slices[:3]}"
def test_xbar_top_to_hbm_slices_0_3():
"""xbar_top must connect to hbm_ctrl.slice0..3 (top HBM slices)."""
def test_router_mesh_edges():
"""Adjacent routers must be connected (router_mesh edges)."""
graph = _graph()
edge_set = {(e.src, e.dst) for e in graph.edges}
for i in range(4):
assert ("sip0.cube0.xbar_top", f"sip0.cube0.hbm_ctrl.slice{i}") in edge_set, (
f"xbar_top → hbm_ctrl.slice{i} edge missing"
)
# r0c0 ↔ r0c1 (horizontal)
assert ("sip0.cube0.r0c0", "sip0.cube0.r0c1") in edge_set
assert ("sip0.cube0.r0c1", "sip0.cube0.r0c0") in edge_set
def test_xbar_bot_to_hbm_slices_4_7():
"""xbar_bot must connect to hbm_ctrl.slice4..7 (bottom HBM slices)."""
def test_pe_dma_connects_to_router():
"""PE_DMA must connect to router (pe_to_router kind)."""
graph = _graph()
edge_set = {(e.src, e.dst) for e in graph.edges}
for i in range(4, 8):
assert ("sip0.cube0.xbar_bot", f"sip0.cube0.hbm_ctrl.slice{i}") in edge_set, (
f"xbar_bot → hbm_ctrl.slice{i} edge missing"
)
pe0_edges = [e for e in graph.edges
if e.src == "sip0.cube0.pe0.pe_dma" and e.kind == "pe_to_router"]
assert len(pe0_edges) == 1, f"PE0 DMA should connect to 1 router, got {len(pe0_edges)}"
assert pe0_edges[0].dst == "sip0.cube0.r0c0"
def test_xbar_bridge_left():
"""bridge.left must connect xbar_top ↔ xbar_bot (bidirectional)."""
def test_hbm_connects_to_all_routers():
"""HBM_CTRL must have edges to all non-null routers."""
graph = _graph()
assert "sip0.cube0.bridge.left" in graph.nodes
edge_set = {(e.src, e.dst) for e in graph.edges}
assert ("sip0.cube0.xbar_top", "sip0.cube0.bridge.left") in edge_set
assert ("sip0.cube0.bridge.left", "sip0.cube0.xbar_bot") in edge_set
assert ("sip0.cube0.xbar_bot", "sip0.cube0.bridge.left") in edge_set
assert ("sip0.cube0.bridge.left", "sip0.cube0.xbar_top") in edge_set
def test_xbar_bridge_right():
"""bridge.right must connect xbar_top ↔ xbar_bot (bidirectional)."""
graph = _graph()
assert "sip0.cube0.bridge.right" in graph.nodes
edge_set = {(e.src, e.dst) for e in graph.edges}
assert ("sip0.cube0.xbar_top", "sip0.cube0.bridge.right") in edge_set
assert ("sip0.cube0.bridge.right", "sip0.cube0.xbar_bot") in edge_set
def test_noc_to_xbar_top_edge():
"""NOC must have edge to xbar_top (router attachment)."""
graph = _graph()
edge_set = {(e.src, e.dst) for e in graph.edges}
assert ("sip0.cube0.noc", "sip0.cube0.xbar_top") in edge_set
def test_noc_to_xbar_bot_edge():
"""NOC must have edge to xbar_bot (router attachment)."""
graph = _graph()
edge_set = {(e.src, e.dst) for e in graph.edges}
assert ("sip0.cube0.noc", "sip0.cube0.xbar_bot") in edge_set
def test_pe_dma_no_direct_xbar_edge():
"""PE_DMA must NOT have direct edge to any xbar node.
All HBM access goes through NOC (router attachment to XBAR).
"""
graph = _graph()
pe_to_xbar = [
e for e in graph.edges
if e.src == "sip0.cube0.pe0.pe_dma" and "xbar" in e.dst
]
assert len(pe_to_xbar) == 0, (
f"PE_DMA should not connect directly to XBAR. "
f"Found: {[(e.src, e.dst) for e in pe_to_xbar]}"
hbm_out = [e for e in graph.edges
if e.src == "sip0.cube0.hbm_ctrl" and e.kind == "hbm_to_router"]
mesh = yaml.safe_load(MESH_PATH.read_text())
n_active = sum(1 for v in mesh["routers"].values() if v is not None)
assert len(hbm_out) == n_active, (
f"HBM should connect to {n_active} routers, got {len(hbm_out)}"
)
@@ -342,62 +305,50 @@ def test_pe_dma_no_direct_xbar_edge():
# ══════════════════════════════════════════════════════════════════
def test_local_hbm_path_includes_noc_and_xbar_top():
"""PE0 local HBM (slice0): path must include noc and xbar_top."""
def test_local_hbm_path_through_router():
"""PE0 local HBM: path must go through PE's router to hbm_ctrl."""
graph = _graph()
router = PathRouter(graph)
path = router.find_path("sip0.cube0.pe0", "sip0.cube0.hbm_ctrl.slice0")
assert "sip0.cube0.noc" in path, f"NOC missing from path: {path}"
assert "sip0.cube0.xbar_top" in path, f"xbar_top missing from path: {path}"
path = router.find_path("sip0.cube0.pe0", "sip0.cube0.hbm_ctrl")
assert "sip0.cube0.r0c0" in path, f"PE0's router r0c0 missing from path: {path}"
assert "sip0.cube0.hbm_ctrl" == path[-1], f"Path should end at hbm_ctrl: {path}"
def test_cross_pe_same_row_stays_in_xbar_top():
"""PE0 → slice3 (both top row): xbar_top only, no bridge needed."""
def test_remote_pe_hbm_has_more_hops():
"""PE0 → PE4's HBM (remote) must have more hops than local."""
graph = _graph()
router = PathRouter(graph)
path = router.find_path("sip0.cube0.pe0", "sip0.cube0.hbm_ctrl.slice3")
assert "sip0.cube0.xbar_top" in path
assert "sip0.cube0.xbar_bot" not in path, (
f"Cross-PE same row should not use xbar_bot. Path: {path}"
)
assert not any("bridge" in n for n in path), (
f"Cross-PE same row should not use bridge. Path: {path}"
)
local_path = router.find_path("sip0.cube0.pe0", "sip0.cube0.hbm_ctrl")
# PE4 is at r4c0, PE0 at r0c0 — must traverse mesh
remote_path = router.find_path("sip0.cube0.pe4", "sip0.cube0.hbm_ctrl")
# Both should work, local should be shorter or equal
assert len(local_path) >= 2
assert len(remote_path) >= 2
def test_cross_row_hbm_uses_bridge():
"""PE0 → slice5 (top→bottom): must traverse xbar_top → bridge → xbar_bot."""
graph = _graph()
router = PathRouter(graph)
path = router.find_path("sip0.cube0.pe0", "sip0.cube0.hbm_ctrl.slice5")
assert "sip0.cube0.xbar_top" in path, f"xbar_top missing: {path}"
assert "sip0.cube0.xbar_bot" in path, f"xbar_bot missing: {path}"
assert any("bridge" in n for n in path), f"bridge missing: {path}"
def test_mcpu_dma_path_through_noc():
"""M_CPU DMA to local HBM: m_cpu → noc → xbar_top → hbm_ctrl."""
def test_mcpu_dma_path_through_router_mesh():
"""M_CPU DMA to local HBM: m_cpu → router mesh → hbm_ctrl."""
graph = _graph()
router = PathRouter(graph)
path = router.find_mcpu_dma_path(
"sip0.cube0.m_cpu", "sip0.cube0.hbm_ctrl.slice0"
"sip0.cube0.m_cpu", "sip0.cube0.hbm_ctrl"
)
assert "sip0.cube0.noc" in path, f"NOC missing: {path}"
assert "sip0.cube0.xbar_top" in path, f"xbar_top missing: {path}"
assert path[0] == "sip0.cube0.m_cpu"
assert path[-1] == "sip0.cube0.hbm_ctrl"
assert any("r" in n and "c" in n for n in path), f"Router missing from path: {path}"
def test_cross_cube_path_through_mesh():
"""Cross-cube HBM: must traverse noc → UCIe → remote noc → xbar."""
def test_cross_cube_path_through_ucie():
"""Cross-cube HBM: must traverse router → UCIe → remote router → hbm_ctrl."""
graph = _graph()
router = PathRouter(graph)
path = router.find_path("sip0.cube0.pe0", "sip0.cube4.hbm_ctrl.slice0")
assert "sip0.cube0.noc" in path, f"Source NOC missing: {path}"
path = router.find_path("sip0.cube0.pe0", "sip0.cube4.hbm_ctrl")
assert any("ucie" in n.lower() for n in path), f"UCIe missing: {path}"
assert "sip0.cube4.xbar_top" in path, f"Dest xbar_top missing: {path}"
assert path[-1] == "sip0.cube4.hbm_ctrl"
def test_h2d_bypass_path_through_noc():
"""H2D MemoryWrite bypass: pcie_ep → io_noc → cube_ucie → noc → xbar → hbm."""
def test_h2d_bypass_path_through_router():
"""H2D MemoryWrite bypass: pcie_ep → io_noc → cube_ucie → router → hbm."""
graph = _graph()
resolver = AddressResolver(graph)
router = PathRouter(graph)
@@ -407,8 +358,8 @@ def test_h2d_bypass_path_through_noc():
hbm_target = resolver.resolve(PhysAddr.decode(pa))
path = router.find_memory_path(pcie_ep, hbm_target)
assert "sip0.cube0.noc" in path, f"NOC missing from H2D path: {path}"
assert "sip0.cube0.xbar_top" in path, f"xbar_top missing from H2D path: {path}"
assert path[-1] == "sip0.cube0.hbm_ctrl", f"Path should end at hbm_ctrl: {path}"
assert any("r0c" in n or "r1c" in n for n in path), f"Router missing: {path}"
# ══════════════════════════════════════════════════════════════════
@@ -416,28 +367,28 @@ def test_h2d_bypass_path_through_noc():
# ══════════════════════════════════════════════════════════════════
def test_pe_dma_to_noc_bw():
"""PE_DMA → NOC edge BW must be 256 GB/s (= HBM slice BW, no bottleneck)."""
def test_pe_dma_to_router_bw():
"""PE_DMA → router edge BW must be 256 GB/s."""
graph = _graph()
for e in graph.edges:
if e.src == "sip0.cube0.pe0.pe_dma" and e.dst == "sip0.cube0.noc":
if e.src == "sip0.cube0.pe0.pe_dma" and e.kind == "pe_to_router":
assert e.bw_gbs == 256.0, (
f"PE_DMA→NOC BW should be 256 GB/s, got {e.bw_gbs}"
f"PE_DMA→router BW should be 256 GB/s, got {e.bw_gbs}"
)
return
pytest.fail("PE_DMA → NOC edge not found")
pytest.fail("PE_DMA → router edge not found")
def test_noc_to_xbar_bw():
"""NOC → xbar_top edge BW must be 256 GB/s (= HBM slice BW)."""
def test_router_mesh_bw():
"""Router-router mesh edge BW must be 256 GB/s."""
graph = _graph()
for e in graph.edges:
if e.src == "sip0.cube0.noc" and e.dst == "sip0.cube0.xbar_top":
if e.kind == "router_mesh" and "cube0" in e.src:
assert e.bw_gbs == 256.0, (
f"NOC→xbar_top BW should be 256 GB/s, got {e.bw_gbs}"
f"Router mesh BW should be 256 GB/s, got {e.bw_gbs}"
)
return
pytest.fail("NOC → xbar_top edge not found")
pytest.fail("Router mesh edge not found")
# ══════════════════════════════════════════════════════════════════
@@ -460,11 +411,8 @@ def test_local_hbm_read_completes():
assert trace["total_ns"] > 0
def test_cross_row_latency_greater_than_local():
"""Cross-row HBM access (PE0→slice5) must be slower than local (PE0→slice0).
Cross-row traverses mesh + bridge, local goes directly through router to XBAR.
"""
def test_remote_pe_latency_greater_than_local():
"""Remote PE HBM access must be slower than local (more mesh hops)."""
engine_local = _engine()
msg_local = MemoryReadMsg(
correlation_id="mesh", request_id="local",
@@ -475,18 +423,19 @@ def test_cross_row_latency_greater_than_local():
engine_local.wait(h_l)
_, t_local = engine_local.get_completion(h_l)
engine_cross = _engine()
msg_cross = MemoryReadMsg(
correlation_id="mesh", request_id="cross",
# PE0 accessing PE5's HBM (remote, more mesh hops)
engine_remote = _engine()
msg_remote = MemoryReadMsg(
correlation_id="mesh", request_id="remote",
src_sip=0, src_cube=0, src_pe=0,
src_pa=_hbm_pa(pe_id=5), nbytes=4096,
)
h_c = engine_cross.submit(msg_cross)
engine_cross.wait(h_c)
_, t_cross = engine_cross.get_completion(h_c)
h_r = engine_remote.submit(msg_remote)
engine_remote.wait(h_r)
_, t_remote = engine_remote.get_completion(h_r)
assert t_cross["total_ns"] > t_local["total_ns"], (
f"Cross-row ({t_cross['total_ns']:.2f}ns) must be > "
assert t_remote["total_ns"] >= t_local["total_ns"], (
f"Remote ({t_remote['total_ns']:.2f}ns) must be >= "
f"local ({t_local['total_ns']:.2f}ns)"
)
@@ -532,79 +481,34 @@ def test_mesh_data_in_context_spec():
assert mesh["mesh"]["cols"] == 6
def test_noc_grid_from_mesh_routers():
"""NOC x_grid/y_grid must be derived from mesh router positions, not all nodes.
Mesh routers have 6 unique X values and 6 unique Y values.
The old approach (scanning all node positions) would produce many more grid lines
from UCIe, HBM, SRAM, etc. positions.
"""
def test_router_nodes_match_mesh():
"""Topology router nodes must match active routers in cube_mesh.yaml."""
graph = _graph()
mesh = yaml.safe_load(MESH_PATH.read_text())
# Extract unique X and Y values from mesh routers (excluding HBM exclusions)
mesh_xs = set()
mesh_ys = set()
for key, router in mesh["routers"].items():
if router is not None:
mesh_xs.add(router["pos_mm"][0])
mesh_ys.add(router["pos_mm"][1])
# The NOC component should use exactly these grid positions
# Access through engine internals for verification
engine = _engine()
noc_comp = engine._components["sip0.cube0.noc"]
assert len(noc_comp._x_grid) == len(mesh_xs), (
f"NOC x_grid has {len(noc_comp._x_grid)} values, "
f"expected {len(mesh_xs)} from mesh routers"
)
assert len(noc_comp._y_grid) == len(mesh_ys), (
f"NOC y_grid has {len(noc_comp._y_grid)} values, "
f"expected {len(mesh_ys)} from mesh routers"
)
active_routers = [k for k, v in mesh["routers"].items() if v is not None]
for rkey in active_routers:
assert f"sip0.cube0.{rkey}" in graph.nodes, f"Router {rkey} missing from graph"
def test_noc_grid_excludes_hbm_zone():
"""NOC grid must not include positions from HBM-excluded routers.
HBM exclusion zone routers (r2c2, r2c3, r3c2, r3c3) are None in the mesh.
Their positions must not appear as router grid points in the NOC.
"""
def test_null_routers_excluded():
"""HBM exclusion zone routers (null in mesh) must not be in graph."""
graph = _graph()
mesh = yaml.safe_load(MESH_PATH.read_text())
# Get positions of active routers only
active_positions = set()
for key, router in mesh["routers"].items():
if router is not None:
active_positions.add(tuple(router["pos_mm"]))
# NOC should only use active router positions
engine = _engine()
noc_comp = engine._components["sip0.cube0.noc"]
noc_grid_points = {(x, y) for x in noc_comp._x_grid for y in noc_comp._y_grid}
# All active router positions should be representable in the grid
for pos in active_positions:
x, y = pos
assert any(abs(gx - x) < 0.01 for gx in noc_comp._x_grid), (
f"Active router X={x} not in NOC x_grid"
)
assert any(abs(gy - y) < 0.01 for gy in noc_comp._y_grid), (
f"Active router Y={y} not in NOC y_grid"
)
null_routers = [k for k, v in mesh["routers"].items() if v is None]
for rkey in null_routers:
assert f"sip0.cube0.{rkey}" not in graph.nodes, f"Null router {rkey} in graph"
# ══════════════════════════════════════════════════════════════════
# 7. XBAR Position-Aware Latency (Change 2)
# 7. Router Mesh Latency (ADR-0019)
# ══════════════════════════════════════════════════════════════════
def _pe_dma_latency(pe_id: int, target_pe_id: int, nbytes: int = 4096) -> float:
"""Run PeDmaMsg from pe_id targeting target_pe_id's HBM slice, return total_ns."""
"""Run PeDmaMsg from pe_id targeting target_pe_id's HBM, return total_ns."""
engine = _engine()
msg = PeDmaMsg(
correlation_id="xbar", request_id=f"pe{pe_id}_slice{target_pe_id}",
correlation_id="mesh_lat", request_id=f"pe{pe_id}_t{target_pe_id}",
src_sip=0, src_cube=0, src_pe=pe_id,
dst_pa=_hbm_pa(pe_id=target_pe_id), nbytes=nbytes,
)
@@ -614,78 +518,25 @@ def _pe_dma_latency(pe_id: int, target_pe_id: int, nbytes: int = 4096) -> float:
return trace["total_ns"]
def test_xbar_pe0_slice0_lower_than_pe0_slice3():
"""PE0 (NW, left) → slice0 (left) must be faster than PE0 → slice3 (right).
Position-aware XBAR: PE0's router (r0c0, x=1.5) is closer to slice0 (left end)
than slice3 (right end). The XBAR internal latency should reflect this distance.
"""
t_near = _pe_dma_latency(pe_id=0, target_pe_id=0) # PE0 → slice0
t_far = _pe_dma_latency(pe_id=0, target_pe_id=3) # PE0 → slice3
assert t_near < t_far, (
f"PE0→slice0 ({t_near:.4f}ns) should be < PE0→slice3 ({t_far:.4f}ns) "
f"with position-aware XBAR"
)
def test_local_hbm_latency_positive():
"""Local HBM access must have positive latency."""
t = _pe_dma_latency(pe_id=0, target_pe_id=0)
assert t > 0, f"Local HBM latency must be > 0, got {t}"
def test_xbar_pe2_slice3_lower_than_pe2_slice0():
"""PE2 (NE, right) → slice3 (right) must be faster than PE2 → slice0 (left).
Mirror of test_xbar_pe0_slice0_lower_than_pe0_slice3.
PE2's router (r1c4, x=12.5) is closer to slice3 (right end).
"""
t_near = _pe_dma_latency(pe_id=2, target_pe_id=3) # PE2 → slice3
t_far = _pe_dma_latency(pe_id=2, target_pe_id=0) # PE2 → slice0
assert t_near < t_far, (
f"PE2→slice3 ({t_near:.4f}ns) should be < PE2→slice0 ({t_far:.4f}ns) "
f"with position-aware XBAR"
)
def test_pe_dma_latency_deterministic():
"""Same PE DMA request must produce identical latency."""
t1 = _pe_dma_latency(pe_id=1, target_pe_id=1)
t2 = _pe_dma_latency(pe_id=1, target_pe_id=1)
assert t1 == t2, f"Non-deterministic latency: {t1} vs {t2}"
def test_xbar_symmetric_latency():
"""PE0→slice0 ≈ PE2→slice3 (symmetric positions in the crossbar).
PE0 (NW, x=1.5) distance to slice0 (left) should equal
PE2 (NE, x=12.5) distance to slice3 (right), within tolerance.
"""
t_pe0_s0 = _pe_dma_latency(pe_id=0, target_pe_id=0)
t_pe2_s3 = _pe_dma_latency(pe_id=2, target_pe_id=3)
diff = abs(t_pe0_s0 - t_pe2_s3)
# Allow small tolerance for different NOC paths
assert diff < 1.0, (
f"Symmetric latency mismatch: PE0→slice0={t_pe0_s0:.4f}ns, "
f"PE2→slice3={t_pe2_s3:.4f}ns, diff={diff:.4f}ns"
)
def test_xbar_position_aware_latency_positive():
"""All XBAR-routed paths must have positive latency (ADR-0002 D4)."""
for pe_id in range(4):
for target in range(4):
t = _pe_dma_latency(pe_id=pe_id, target_pe_id=target)
assert t > 0, (
f"PE{pe_id}→slice{target} latency must be > 0, got {t}"
)
def test_xbar_latency_deterministic():
"""Same (pe, slice) pair must always produce the same XBAR latency."""
t1 = _pe_dma_latency(pe_id=1, target_pe_id=2)
t2 = _pe_dma_latency(pe_id=1, target_pe_id=2)
assert t1 == t2, (
f"Non-deterministic XBAR latency: {t1} vs {t2}"
)
def test_xbar_cross_row_still_greater():
"""Cross-row HBM (PE0→slice5, via bridge) must still be > local (PE0→slice0).
Position-aware XBAR must not break the cross-row > local invariant.
"""
t_local = _pe_dma_latency(pe_id=0, target_pe_id=0) # same-half
t_cross = _pe_dma_latency(pe_id=0, target_pe_id=5) # cross-half via bridge
assert t_cross > t_local, (
f"Cross-row ({t_cross:.4f}ns) must be > local ({t_local:.4f}ns)"
def test_remote_pe_dma_latency_greater():
"""Remote PE HBM access (more mesh hops) should be >= local."""
t_local = _pe_dma_latency(pe_id=0, target_pe_id=0)
t_remote = _pe_dma_latency(pe_id=0, target_pe_id=5)
assert t_remote >= t_local, (
f"Remote ({t_remote:.4f}ns) must be >= local ({t_local:.4f}ns)"
)
@@ -694,60 +545,11 @@ def test_xbar_cross_row_still_greater():
# ══════════════════════════════════════════════════════════════════
def test_pe_noc_distance_reflects_physical_position():
"""PE→NOC edge distance must reflect actual PE-to-router physical distance.
NW PE0 (y=1.5) → router r0c0 (y=1.5): distance ≈ 0
NE PE2 (y=1.5) → router r1c4 (y=5.5): distance ≈ 4.0mm
SW PE4 (y=12.5) → router r4c0 (y=8.5): distance ≈ 4.0mm
SE PE6 (y=12.5) → router r5c4 (y=12.5): distance ≈ 0
"""
def test_pe_router_edges_exist():
"""Each PE must have pe_to_router edges to its assigned router."""
graph = _graph()
pe_noc_edges = {}
for e in graph.edges:
if e.kind == "pe_to_noc" and "cube0" in e.src:
# Extract pe index from "sip0.cube0.pe2.pe_dma"
pe_name = e.src.split(".")[-2] # "pe2"
pe_noc_edges[pe_name] = e.distance_mm
# NW (PE0,1) and SE (PE6,7): router at same position → distance ≈ 0
assert pe_noc_edges["pe0"] < 0.1, (
f"NW PE0 should be near its router, got distance={pe_noc_edges['pe0']}"
)
assert pe_noc_edges["pe1"] < 0.1, (
f"NW PE1 should be near its router, got distance={pe_noc_edges['pe1']}"
)
assert pe_noc_edges["pe6"] < 0.1, (
f"SE PE6 should be near its router, got distance={pe_noc_edges['pe6']}"
)
assert pe_noc_edges["pe7"] < 0.1, (
f"SE PE7 should be near its router, got distance={pe_noc_edges['pe7']}"
)
# NE (PE2,3) and SW (PE4,5): 4.0mm from router → distance > 3.5
assert pe_noc_edges["pe2"] > 3.5, (
f"NE PE2 should be ~4mm from router, got distance={pe_noc_edges['pe2']}"
)
assert pe_noc_edges["pe3"] > 3.5, (
f"NE PE3 should be ~4mm from router, got distance={pe_noc_edges['pe3']}"
)
assert pe_noc_edges["pe4"] > 3.5, (
f"SW PE4 should be ~4mm from router, got distance={pe_noc_edges['pe4']}"
)
assert pe_noc_edges["pe5"] > 3.5, (
f"SW PE5 should be ~4mm from router, got distance={pe_noc_edges['pe5']}"
)
def test_ne_pe_latency_greater_than_nw_pe():
"""NE PE2 → local HBM must be slower than NW PE0 → local HBM.
PE2 has 4mm extra wire to its router vs PE0 (0mm).
Both access their respective local HBM slice.
"""
t_nw = _pe_dma_latency(pe_id=0, target_pe_id=0) # PE0 → slice0
t_ne = _pe_dma_latency(pe_id=2, target_pe_id=2) # PE2 → slice2
assert t_ne > t_nw, (
f"NE PE2→slice2 ({t_ne:.4f}ns) should be > "
f"NW PE0→slice0 ({t_nw:.4f}ns) due to extra wire distance"
pe_router_edges = [e for e in graph.edges
if e.kind == "pe_to_router" and "sip0.cube0" in e.src]
assert len(pe_router_edges) == 8, (
f"Expected 8 PE→router edges, got {len(pe_router_edges)}"
)