Cube-view: UCIe components inside cube boundary with port boxes

- UCIe-N/S/E/W drawn as component blocks inside cube boundary
  (inset 3mm from edge)
- Each UCIe has c0-c3 connection ports as color-coded boxes inside
- Connector lines from each port box to its attached router
- Removed old UCIe rendering that placed blocks outside cube

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-04 23:58:32 -07:00
parent e766163a25
commit 66ec6cd40c
2 changed files with 222 additions and 129 deletions
+140 -55
View File
@@ -599,11 +599,7 @@ def _render_cube_view_svg(view: ViewGraph, spec: dict) -> str:
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"]))
# UCIe handled separately below
# Position blocks outward from router (away from cube center)
for bi, (label, kind, style) in enumerate(blocks):
@@ -611,29 +607,7 @@ def _render_cube_view_svg(view: ViewGraph, spec: dict) -> str:
# 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 flush against cube edge at router position
direction = label.split("-")[1].split(".")[0] if "-" in label else ""
ucie_w, ucie_h = 22, 10 # smaller blocks for UCIe ports
if direction == "N":
bx = px - ucie_w / 2
by = pad - ucie_h # flush against top edge
blk_w, blk_h = ucie_w, ucie_h
elif direction == "S":
bx = px - ucie_w / 2
by = pad + cube_h * scale # flush against bottom edge
blk_w, blk_h = ucie_w, ucie_h
elif direction == "W":
bx = pad - ucie_w # flush against left edge
by = py - ucie_h / 2
blk_w, blk_h = ucie_w, ucie_h
elif direction == "E":
bx = pad + cube_w * scale # flush against right edge
by = py - ucie_h / 2
blk_w, blk_h = ucie_w, ucie_h
else:
bx, by = px - blk_w / 2, py - r_size - blk_h - 4
elif kind in ("mcpu", "sram"):
if kind in ("mcpu", "sram"):
# M_CPU/SRAM: place to the left of router (avoid mesh overlap)
bx = px - r_size - blk_w - 6
by = py - blk_h / 2 + bi * (blk_h + 2)
@@ -659,33 +633,7 @@ def _render_cube_view_svg(view: ViewGraph, spec: dict) -> str:
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"/>'
)
elif kind in ("mcpu", "sram"):
if kind in ("mcpu", "sram"):
# Horizontal connector (block right edge → router left edge)
parts.append(
f' <line x1="{bx + blk_w:.0f}" y1="{by + blk_h / 2:.0f}" '
@@ -735,6 +683,143 @@ def _render_cube_view_svg(view: ViewGraph, spec: dict) -> str:
f'{agg_bw:.0f}GB/s</text>'
)
# ── UCIe port components (inside cube boundary, on edges) ──
# Collect UCIe connections per direction
ucie_by_dir: dict[str, list[tuple[str, str, float, float]]] = {} # dir → [(conn, rkey, rx, ry)]
for rkey, rval in routers.items():
if rval is None:
continue
rx, ry = rval["pos_mm"]
for a in rval.get("attach", []):
if not a.startswith("ucie_"):
continue
parts_a = a.split(".")
direction = parts_a[0].replace("ucie_", "").upper()
conn = parts_a[1] if len(parts_a) > 1 else "c0"
ucie_by_dir.setdefault(direction, []).append((conn, rkey, rx, ry))
ucie_colors = ["#818cf8", "#a78bfa", "#c084fc", "#e879f9"]
ucie_port_inset = 3 # mm inset from cube edge
for direction, conns in ucie_by_dir.items():
conns.sort(key=lambda x: x[0]) # sort by conn name
n_conn = len(conns)
# UCIe component box position (inside cube, along edge)
if direction == "N":
# Horizontal bar along top edge
# Find X span from attached routers
xs = [mm2px(rx, ry)[0] for _, _, rx, ry in conns]
ux1 = min(xs) - 15
ux2 = max(xs) + 15
uw = ux2 - ux1
uh = 18
ux = ux1
uy = pad + ucie_port_inset * scale
elif direction == "S":
xs = [mm2px(rx, ry)[0] for _, _, rx, ry in conns]
ux1 = min(xs) - 15
ux2 = max(xs) + 15
uw = ux2 - ux1
uh = 18
ux = ux1
uy = pad + cube_h * scale - ucie_port_inset * scale - uh
elif direction == "W":
ys = [mm2px(rx, ry)[1] for _, _, rx, ry in conns]
uy1 = min(ys) - 15
uy2 = max(ys) + 15
uw = 18
uh = uy2 - uy1
ux = pad + ucie_port_inset * scale
uy = uy1
elif direction == "E":
ys = [mm2px(rx, ry)[1] for _, _, rx, ry in conns]
uy1 = min(ys) - 15
uy2 = max(ys) + 15
uw = 18
uh = uy2 - uy1
ux = pad + cube_w * scale - ucie_port_inset * scale - uw
uy = uy1
else:
continue
# UCIe component background
parts.append(
f' <rect x="{ux:.0f}" y="{uy:.0f}" '
f'width="{uw:.0f}" height="{uh:.0f}" '
f'rx="3" fill="#1e1b4b" stroke="#8b5cf6" stroke-width="1.5" opacity="0.8"/>'
)
# UCIe label
if direction in ("N", "S"):
parts.append(
f' <text x="{ux + uw / 2:.0f}" y="{uy - 3:.0f}" text-anchor="middle" '
f'font-family="monospace" font-size="7" font-weight="bold" fill="#8b5cf6">'
f'UCIe-{direction}</text>'
)
else:
parts.append(
f' <text x="{ux + uw / 2:.0f}" y="{uy - 3:.0f}" text-anchor="middle" '
f'font-family="monospace" font-size="7" font-weight="bold" fill="#8b5cf6">'
f'UCIe-{direction}</text>'
)
# Connection port boxes inside UCIe component
for ci, (conn, rkey, crx, cry) in enumerate(conns):
c_color = ucie_colors[ci % len(ucie_colors)]
if direction in ("N", "S"):
# Horizontal layout
cw = max((uw - 4) / n_conn - 1, 6)
ch = uh - 4
cx = ux + 2 + ci * (cw + 1)
cy = uy + 2
else:
# Vertical layout
cw = uw - 4
ch = max((uh - 4) / n_conn - 1, 6)
cx = ux + 2
cy = uy + 2 + ci * (ch + 1)
parts.append(
f' <rect x="{cx:.0f}" y="{cy:.0f}" '
f'width="{cw:.0f}" height="{ch:.0f}" '
f'rx="2" fill="{c_color}" opacity="0.7"/>'
)
# Connection label
lx = cx + cw / 2
ly_t = cy + ch / 2 + 3
parts.append(
f' <text x="{lx:.0f}" y="{ly_t:.0f}" text-anchor="middle" '
f'font-family="monospace" font-size="5" fill="white">'
f'{conn}</text>'
)
# Connector line: port box → attached router
rpx, rpy = mm2px(crx, cry)
if direction == "N":
parts.append(
f' <line x1="{lx:.0f}" y1="{cy + ch:.0f}" '
f'x2="{rpx:.0f}" y2="{rpy - r_size:.0f}" '
f'stroke="{c_color}" stroke-width="1" opacity="0.5"/>'
)
elif direction == "S":
parts.append(
f' <line x1="{lx:.0f}" y1="{cy:.0f}" '
f'x2="{rpx:.0f}" y2="{rpy + r_size:.0f}" '
f'stroke="{c_color}" stroke-width="1" opacity="0.5"/>'
)
elif direction == "W":
parts.append(
f' <line x1="{cx + cw:.0f}" y1="{cy + ch / 2:.0f}" '
f'x2="{rpx - r_size:.0f}" y2="{rpy:.0f}" '
f'stroke="{c_color}" stroke-width="1" opacity="0.5"/>'
)
elif direction == "E":
parts.append(
f' <line x1="{cx:.0f}" y1="{cy + ch / 2:.0f}" '
f'x2="{rpx + r_size:.0f}" y2="{rpy:.0f}" '
f'stroke="{c_color}" stroke-width="1" opacity="0.5"/>'
)
# ── Legend ──
ly = h_px - 35
legend_items = [