Cube-view: draw all attached components as separate blocks

All router-attached components (PE, M_CPU, SRAM, UCIe) rendered as
labeled blocks with explicit connector lines to their router.
UCIe blocks positioned at cube edges matching port direction.
Router→HBM_CTRL lines shown for all 32 routers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-04 22:09:08 -07:00
parent e94f1de078
commit 109c9b4483
2 changed files with 308 additions and 209 deletions
+126 -89
View File
@@ -529,120 +529,157 @@ def _render_cube_view_svg(view: ViewGraph, spec: dict) -> str:
)
break
# ── Router nodes ──
r_size = 10 # px radius
pe_routers: dict[str, str] = {} # rkey → pe label
# ── Router nodes + attached component blocks ──
r_size = 8 # px radius for router circle
blk_w, blk_h = 32, 16 # px for component blocks
# Component style definitions
_COMP_STYLE = {
"pe": {"fill": "#2d1f3d", "stroke": "#a855f7", "text": "#a855f7"},
"mcpu": {"fill": "#451a03", "stroke": "#f59e0b", "text": "#f59e0b"},
"sram": {"fill": "#1c1917", "stroke": "#d97706", "text": "#d97706"},
"ucie": {"fill": "#1e1b4b", "stroke": "#8b5cf6", "text": "#8b5cf6"},
}
for rkey, rval in routers.items():
if rval is None:
continue
rx, ry = rval["pos_mm"]
px, py = mm2px(rx, ry)
attach = rval.get("attach", [])
is_top = ry < cube_h / 2
# Determine router type by attachments
has_pe = any(a.endswith(".dma") for a in attach)
has_mcpu = "m_cpu" in attach
has_sram = "sram" in attach
has_ucie = any(a.startswith("ucie_") for a in attach)
if has_pe:
fill, stroke = "#3b82f6", "#1d4ed8"
pe_name = [a for a in attach if a.endswith(".dma")][0].split(".")[0]
pe_routers[rkey] = pe_name.upper()
elif has_mcpu:
fill, stroke = "#f59e0b", "#d97706"
elif has_sram:
fill, stroke = "#f59e0b", "#d97706"
elif has_ucie:
fill, stroke = "#8b5cf6", "#6d28d9"
else:
fill, stroke = "#334155", "#475569"
# ── Router circle ──
has_attach = len(attach) > 0
r_fill = "#475569" if has_attach else "#334155"
r_stroke = "#64748b" if has_attach else "#475569"
parts.append(
f' <circle cx="{px:.0f}" cy="{py:.0f}" r="{r_size}" '
f'fill="{fill}" stroke="{stroke}" stroke-width="1.5"/>'
f'fill="{r_fill}" stroke="{r_stroke}" stroke-width="1"/>'
)
# Router label
parts.append(
f' <text x="{px:.0f}" y="{py + 3:.0f}" text-anchor="middle" '
f'font-family="monospace" font-size="7" font-weight="bold" fill="white">'
f'font-family="monospace" font-size="6" fill="white">'
f'{rkey}</text>'
)
# Attachment labels below router
label_parts = []
if has_pe:
label_parts.append(pe_routers[rkey])
if has_mcpu:
label_parts.append("M_CPU")
if has_sram:
label_parts.append("SRAM")
ucie_items = [a for a in attach if a.startswith("ucie_")]
if ucie_items:
label_parts.append(f"UCIe×{len(ucie_items)}")
if label_parts:
# ── Router → HBM_CTRL line (all routers connect to HBM) ──
hbm_edge_y = hbm_y if py < hbm_y else hbm_y + hbm_h
r_edge_y = py + r_size if py < hbm_y else py - r_size
# Only draw if not inside HBM zone
if abs(r_edge_y - hbm_edge_y) > 10:
parts.append(
f' <text x="{px:.0f}" y="{py + r_size + 10:.0f}" text-anchor="middle" '
f'font-family="monospace" font-size="6" fill="#94a3b8">'
f'{", ".join(label_parts)}</text>'
f' <line x1="{px:.0f}" y1="{r_edge_y:.0f}" '
f'x2="{px:.0f}" y2="{hbm_edge_y:.0f}" '
f'stroke="#10b981" stroke-width="0.8" opacity="0.25"/>'
)
# ── PE → HBM connection line (for PE routers) ──
if has_pe:
# Draw line from router to HBM zone edge
target_y = hbm_y if py < hbm_y else hbm_y + hbm_h
# ── Attached component blocks ──
# Collect components to draw, positioned outward from router
blocks: list[tuple[str, str, dict]] = [] # (label, kind, style)
pe_items = [a for a in attach if a.endswith(".dma")]
if pe_items:
pe_name = pe_items[0].split(".")[0].upper()
blocks.append((pe_name, "pe", _COMP_STYLE["pe"]))
if "m_cpu" in attach:
blocks.append(("M_CPU", "mcpu", _COMP_STYLE["mcpu"]))
if "sram" in attach:
blocks.append(("SRAM", "sram", _COMP_STYLE["sram"]))
ucie_items = [a for a in attach if a.startswith("ucie_")]
for ui in ucie_items:
direction = ui.split(".")[0].replace("ucie_", "").upper()
conn = ui.split(".")[1] if "." in ui else ""
blocks.append((f"UCIe-{direction}.{conn}", "ucie", _COMP_STYLE["ucie"]))
# Position blocks outward from router (away from cube center)
for bi, (label, kind, style) in enumerate(blocks):
# Determine placement direction: PE/components go outward
# Use left/right offset for multiple blocks on same router
offset_x = (bi - (len(blocks) - 1) / 2) * (blk_w + 4)
if kind == "ucie":
# UCIe: place at cube edge direction
direction = label.split("-")[1].split(".")[0] if "-" in label else ""
if direction == "N":
bx, by = px + offset_x - blk_w / 2, pad - blk_h - 4
elif direction == "S":
by_base = pad + cube_h * scale
bx, by = px + offset_x - blk_w / 2, by_base + 4
elif direction == "W":
bx, by = pad - blk_w - 4, py + offset_x - blk_h / 2
elif direction == "E":
bx_base = pad + cube_w * scale
bx, by = bx_base + 4, py + offset_x - blk_h / 2
else:
bx, by = px + offset_x - blk_w / 2, py - r_size - blk_h - 4
else:
# PE/M_CPU/SRAM: place above (top half) or below (bottom half)
bx = px + offset_x - blk_w / 2
if is_top:
by = py - r_size - blk_h - 4 - bi * (blk_h + 2)
else:
by = py + r_size + 4 + bi * (blk_h + 2)
# Block rect
parts.append(
f' <line x1="{px:.0f}" y1="{py + r_size:.0f}" '
f'x2="{px:.0f}" y2="{target_y:.0f}" '
f'stroke="#10b981" stroke-width="1.5" opacity="0.5" '
f'stroke-dasharray="4,3"/>'
f' <rect x="{bx:.0f}" y="{by:.0f}" '
f'width="{blk_w}" height="{blk_h}" '
f'rx="3" fill="{style["fill"]}" stroke="{style["stroke"]}" stroke-width="1"/>'
)
# BW annotation
bw_y = (py + r_size + target_y) / 2
# Label
font_sz = 6 if len(label) > 6 else 7
parts.append(
f' <text x="{px + 12:.0f}" y="{bw_y:.0f}" '
f' <text x="{bx + blk_w / 2:.0f}" y="{by + blk_h / 2 + 3:.0f}" '
f'text-anchor="middle" font-family="monospace" font-size="{font_sz}" '
f'font-weight="bold" fill="{style["text"]}">{_escape(label)}</text>'
)
# Connector line: block → router
if kind == "ucie":
# Line from block edge toward router
if direction == "N":
parts.append(
f' <line x1="{bx + blk_w / 2:.0f}" y1="{by + blk_h:.0f}" '
f'x2="{px:.0f}" y2="{py - r_size:.0f}" '
f'stroke="{style["stroke"]}" stroke-width="1" opacity="0.6"/>'
)
elif direction == "S":
parts.append(
f' <line x1="{bx + blk_w / 2:.0f}" y1="{by:.0f}" '
f'x2="{px:.0f}" y2="{py + r_size:.0f}" '
f'stroke="{style["stroke"]}" stroke-width="1" opacity="0.6"/>'
)
elif direction == "W":
parts.append(
f' <line x1="{bx + blk_w:.0f}" y1="{by + blk_h / 2:.0f}" '
f'x2="{px - r_size:.0f}" y2="{py:.0f}" '
f'stroke="{style["stroke"]}" stroke-width="1" opacity="0.6"/>'
)
elif direction == "E":
parts.append(
f' <line x1="{bx:.0f}" y1="{by + blk_h / 2:.0f}" '
f'x2="{px + r_size:.0f}" y2="{py:.0f}" '
f'stroke="{style["stroke"]}" stroke-width="1" opacity="0.6"/>'
)
else:
# Vertical connector
ly1 = by + blk_h if is_top else by
ly2 = py - r_size if is_top else py + r_size
parts.append(
f' <line x1="{px + offset_x:.0f}" y1="{ly1:.0f}" '
f'x2="{px:.0f}" y2="{ly2:.0f}" '
f'stroke="{style["stroke"]}" stroke-width="1" opacity="0.6"/>'
)
# ── PE router → HBM BW annotation ──
if pe_items:
bw_x = px + 14
bw_y = (r_edge_y + hbm_edge_y) / 2
parts.append(
f' <text x="{bw_x:.0f}" y="{bw_y:.0f}" '
f'font-family="monospace" font-size="6" fill="#10b98188">'
f'{agg_bw:.0f}GB/s</text>'
)
# ── PE blocks (drawn next to their router) ──
pe_w, pe_h = 30, 18 # px
for rkey, rval in routers.items():
if rval is None:
continue
attach = rval.get("attach", [])
pe_dma = [a for a in attach if a.endswith(".dma")]
if not pe_dma:
continue
pe_name = pe_dma[0].split(".")[0] # "pe0"
pe_label = pe_name.upper()
rx, ry = rval["pos_mm"]
px, py = mm2px(rx, ry)
# Position PE block: top-half PEs above router, bottom-half below
is_top = ry < cube_h / 2
pe_x = px - pe_w / 2
pe_y = py - r_size - pe_h - 4 if is_top else py + r_size + 4
parts.append(
f' <rect x="{pe_x:.0f}" y="{pe_y:.0f}" '
f'width="{pe_w}" height="{pe_h}" '
f'rx="3" fill="#2d1f3d" stroke="#a855f7" stroke-width="1"/>'
)
parts.append(
f' <text x="{px:.0f}" y="{pe_y + pe_h / 2 + 4:.0f}" text-anchor="middle" '
f'font-family="monospace" font-size="8" font-weight="bold" fill="#a855f7">'
f'{pe_label}</text>'
)
# PE ↔ router link
link_y1 = pe_y + pe_h if is_top else pe_y
link_y2 = py - r_size if is_top else py + r_size
parts.append(
f' <line x1="{px:.0f}" y1="{link_y1:.0f}" '
f'x2="{px:.0f}" y2="{link_y2:.0f}" '
f'stroke="#a855f7" stroke-width="1.5" opacity="0.7"/>'
)
# ── Legend ──
ly = h_px - 35
legend_items = [