Update web viewer for router mesh topology (ADR-0019)

Remove all xbar/bridge rendering from cube detail view.
Replace 8 HBM slices with single HBM_CTRL block.
Add green dotted lines showing router-to-HBM connectivity.
Update legend, event animation, and PE view NOC destinations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-04 17:56:05 -07:00
parent 5917b3497c
commit 624161f52f
+54 -213
View File
@@ -26,8 +26,8 @@
--pe-stroke: #a855f7;
--io-fill: #3d2b1f;
--io-stroke: #f97316;
--xbar-fill: #1f2d3d;
--xbar-stroke: #06b6d4;
--router-fill: #1f2d3d;
--router-stroke: #06b6d4;
--link-color: #475569;
--link-active: #3b82f6;
}
@@ -405,8 +405,8 @@ body {
PE
</div>
<div class="legend-item">
<div class="legend-swatch" style="background:var(--xbar-fill);border-color:var(--xbar-stroke)"></div>
XBAR / NOC
<div class="legend-swatch" style="background:var(--router-fill);border-color:var(--router-stroke)"></div>
Router Mesh
</div>
</div>
@@ -716,7 +716,7 @@ function drawCubeNode(svg, x, y, idx) {
g.appendChild(pt);
}
// Center block: xbar + NOC
// Center block: router mesh
g.appendChild(svgEl("rect", {
x: x + 30, y: y + 30, width: CUBE_W - 60, height: CUBE_H - 56,
rx: 3, fill: "#1f2d3d", stroke: "#06b6d466", "stroke-width": 0.8
@@ -728,7 +728,7 @@ function drawCubeNode(svg, x, y, idx) {
"font-size": "7",
fill: "#06b6d4aa"
});
xt.textContent = "NOC+XBAR";
xt.textContent = "Router Mesh";
g.appendChild(xt);
// HBM indicators (top and bottom)
@@ -871,51 +871,6 @@ function drawCubeView(svg, cubeIdx) {
}
}
// ── PE router → XBAR_TOP paths (90-degree angled, matching reference) ──
// r0c0 → XBAR_TOP left: down then right
const xbarTopY = OY + 145; // reference: rect at y=145
const xbarBotY = OY + 355; // reference: rect at y=355
const xbarX = OX + 150; // reference: x=150
const xbarW = 400; // reference: width=400
svg.appendChild(svgEl("path", {
d: `M ${OX} ${OY+16} V ${xbarTopY+6} H ${xbarX}`,
fill: "none", stroke: "#f97316", "stroke-width": 1.5, "stroke-dasharray": "4,3"
}));
svg.appendChild(svgEl("path", {
d: `M ${OX+140} ${OY+16} V ${xbarTopY} H ${xbarX}`,
fill: "none", stroke: "#f97316", "stroke-width": 1.5, "stroke-dasharray": "4,3"
}));
svg.appendChild(svgEl("path", {
d: `M ${OX+560} ${OY+107} V ${xbarTopY} H ${xbarX+xbarW}`,
fill: "none", stroke: "#f97316", "stroke-width": 1.5, "stroke-dasharray": "4,3"
}));
svg.appendChild(svgEl("path", {
d: `M ${OX+700} ${OY+107} V ${xbarTopY+6} H ${xbarX+xbarW}`,
fill: "none", stroke: "#f97316", "stroke-width": 1.5, "stroke-dasharray": "4,3"
}));
// ── XBAR_TOP bar ──
svg.appendChild(svgEl("rect", {
x: xbarX, y: xbarTopY, width: xbarW, height: 22,
rx: 5, fill: "#f97316", stroke: "#ea580c", "stroke-width": 2
}));
const xtT = svgEl("text", {
x: xbarX + xbarW / 2, y: xbarTopY + 15, "text-anchor": "middle",
"font-family": "monospace", "font-size": "9", "font-weight": "bold", fill: "white"
});
xtT.textContent = "XBAR_TOP | xbar_v1 | 2.0ns";
svg.appendChild(xtT);
// ── XBAR_TOP → HBM0-3 arrows ──
const hbmArrowXs = [OX + 225, OX + 320, OX + 415, OX + 475];
for (const ax of hbmArrowXs) {
svg.appendChild(svgEl("line", {
x1: ax, y1: xbarTopY + 22, x2: ax, y2: OY + 198,
stroke: "#059669", "stroke-width": 1.5
}));
}
// ── HBM ZONE ──
const hbmZoneX = OX + 145, hbmZoneY = OY + 195, hbmZoneW = 410, hbmZoneH = 152;
svg.appendChild(svgEl("rect", {
@@ -926,181 +881,71 @@ function drawCubeView(svg, cubeIdx) {
x: hbmZoneX + hbmZoneW / 2, y: hbmZoneY + 16, "text-anchor": "middle",
"font-family": "monospace", "font-size": "9", "font-weight": "bold", fill: "#047857"
});
hzmLabel.textContent = "HBM 9.0 x 5.0 mm | hbm_ctrl_v1 x 8";
hzmLabel.textContent = "HBM 9.0 x 5.0 mm | hbm_ctrl_v1";
svg.appendChild(hzmLabel);
// HBM0-3 (top row)
const hbmSliceW = 85, hbmSliceH = 28;
const hbmTopSlices = [
{ x: OX + 168, label: "HBM0" }, { x: OX + 260, label: "HBM1" },
{ x: OX + 352, label: "HBM2" }, { x: OX + 444, label: "HBM3" }
];
for (const hs of hbmTopSlices) {
const g = svgEl("g", { class: "node-group", "data-id": hs.label.toLowerCase() });
g.appendChild(svgEl("rect", {
x: hs.x, y: hbmZoneY + 23, width: hbmSliceW, height: hbmSliceH,
rx: 4, fill: "#047857", stroke: "#065f46", "stroke-width": 1.5
// Single HBM_CTRL block (centered in HBM zone)
const hbmCtrlG = svgEl("g", { class: "node-group", "data-id": "hbm_ctrl" });
hbmCtrlG.appendChild(svgEl("rect", {
x: hbmZoneX + 40, y: hbmZoneY + 28, width: hbmZoneW - 80, height: 40,
rx: 6, fill: "#047857", stroke: "#065f46", "stroke-width": 1.5
}));
const t = svgEl("text", {
x: hs.x + hbmSliceW / 2, y: hbmZoneY + 23 + 18, "text-anchor": "middle",
"font-family": "monospace", "font-size": "8", "font-weight": "bold", fill: "white"
const hbmCtrlT = svgEl("text", {
x: hbmZoneX + hbmZoneW / 2, y: hbmZoneY + 53, "text-anchor": "middle",
"font-family": "monospace", "font-size": "10", "font-weight": "bold", fill: "white"
});
t.textContent = hs.label;
g.appendChild(t);
svg.appendChild(g);
}
hbmCtrlT.textContent = "HBM_CTRL";
hbmCtrlG.appendChild(hbmCtrlT);
svg.appendChild(hbmCtrlG);
// Exclusion zone label
const hexLabel = svgEl("text", {
x: hbmZoneX + hbmZoneW / 2, y: hbmZoneY + 75, "text-anchor": "middle",
x: hbmZoneX + hbmZoneW / 2, y: hbmZoneY + 85, "text-anchor": "middle",
"font-family": "monospace", "font-size": "7", fill: "#ef4444aa"
});
hexLabel.textContent = "Router exclusion: r2c2, r2c3, r3c2, r3c3";
svg.appendChild(hexLabel);
// HBM4-7 (bottom row)
const hbmBotSlices = [
{ x: OX + 168, label: "HBM4" }, { x: OX + 260, label: "HBM5" },
{ x: OX + 352, label: "HBM6" }, { x: OX + 444, label: "HBM7" }
// "All routers connect to HBM" annotation
const hbmAnnot = svgEl("text", {
x: hbmZoneX + hbmZoneW / 2, y: hbmZoneY + 100, "text-anchor": "middle",
"font-family": "monospace", "font-size": "6", fill: "#059669aa"
});
hbmAnnot.textContent = "All routers → HBM_CTRL (mesh-connected)";
svg.appendChild(hbmAnnot);
// ── HBM connectivity indicators (thin green dotted lines from edge routers to HBM zone) ──
// Draw thin green dotted lines from routers adjacent to HBM zone down/up to HBM
const hbmConnRouters = [
{ r: 1, c: 2 }, { r: 1, c: 3 }, // top edge of HBM zone
{ r: 4, c: 2 }, { r: 4, c: 3 }, // bottom edge of HBM zone
{ r: 2, c: 1 }, { r: 3, c: 1 }, // left edge of HBM zone
{ r: 2, c: 4 }, { r: 3, c: 4 }, // right edge of HBM zone
];
for (const hs of hbmBotSlices) {
const g = svgEl("g", { class: "node-group", "data-id": hs.label.toLowerCase() });
g.appendChild(svgEl("rect", {
x: hs.x, y: hbmZoneY + hbmZoneH - hbmSliceH - 23 + 10, width: hbmSliceW, height: hbmSliceH,
rx: 4, fill: "#065f46", stroke: "#064e3b", "stroke-width": 1.5
}));
const t = svgEl("text", {
x: hs.x + hbmSliceW / 2, y: hbmZoneY + hbmZoneH - hbmSliceH - 23 + 10 + 18, "text-anchor": "middle",
"font-family": "monospace", "font-size": "8", "font-weight": "bold", fill: "white"
});
t.textContent = hs.label;
g.appendChild(t);
svg.appendChild(g);
}
// ── XBAR_BOT → HBM4-7 arrows (upward) ──
for (const ax of hbmArrowXs) {
for (const hr of hbmConnRouters) {
const rp = rXY(hr.r, hr.c);
// Draw line toward the HBM zone center
const hbmCenterX = hbmZoneX + hbmZoneW / 2;
const hbmCenterY = hbmZoneY + hbmZoneH / 2;
// Compute endpoint clipped to HBM zone edge
let ex = hbmCenterX, ey = hbmCenterY;
if (hr.r <= 1) { ey = hbmZoneY; ex = rp.x; } // top routers → top of HBM zone
else if (hr.r >= 4) { ey = hbmZoneY + hbmZoneH; ex = rp.x; } // bottom routers → bottom of HBM zone
else if (hr.c <= 1) { ex = hbmZoneX; ey = rp.y; } // left routers → left of HBM zone
else { ex = hbmZoneX + hbmZoneW; ey = rp.y; } // right routers → right of HBM zone
svg.appendChild(svgEl("line", {
x1: ax, y1: xbarBotY, x2: ax, y2: OY + 315,
stroke: "#059669", "stroke-width": 1.5
x1: rp.x, y1: rp.y, x2: ex, y2: ey,
stroke: "#05966988", "stroke-width": 1, "stroke-dasharray": "3,3"
}));
}
// ── XBAR_BOT bar ──
svg.appendChild(svgEl("rect", {
x: xbarX, y: xbarBotY, width: xbarW, height: 22,
rx: 5, fill: "#f97316", stroke: "#ea580c", "stroke-width": 2
}));
const xbT = svgEl("text", {
x: xbarX + xbarW / 2, y: xbarBotY + 15, "text-anchor": "middle",
"font-family": "monospace", "font-size": "9", "font-weight": "bold", fill: "white"
});
xbT.textContent = "XBAR_BOT | xbar_v1 | 2.0ns";
svg.appendChild(xbT);
// ── PE router → XBAR_BOT paths (90-degree angled) ──
svg.appendChild(svgEl("path", {
d: `M ${OX} ${OY+409} V ${xbarBotY+16} H ${xbarX}`,
fill: "none", stroke: "#f97316", "stroke-width": 1.5, "stroke-dasharray": "4,3"
}));
svg.appendChild(svgEl("path", {
d: `M ${OX+140} ${OY+409} V ${xbarBotY+10} H ${xbarX}`,
fill: "none", stroke: "#f97316", "stroke-width": 1.5, "stroke-dasharray": "4,3"
}));
svg.appendChild(svgEl("path", {
d: `M ${OX+560} ${OY+508} V ${xbarBotY+10} H ${xbarX+xbarW}`,
fill: "none", stroke: "#f97316", "stroke-width": 1.5, "stroke-dasharray": "4,3"
}));
svg.appendChild(svgEl("path", {
d: `M ${OX+700} ${OY+508} V ${xbarBotY+16} H ${xbarX+xbarW}`,
fill: "none", stroke: "#f97316", "stroke-width": 1.5, "stroke-dasharray": "4,3"
}));
// ── BRIDGES (purple/violet, matching reference) ──
const brgLeftX = OX + 100, brgRightX = OX + 600;
// Left bridge vertical line
svg.appendChild(svgEl("line", {
x1: brgLeftX, y1: xbarTopY + 10, x2: brgLeftX, y2: xbarBotY + 12,
stroke: "#a78bfa", "stroke-width": 2.5, "stroke-dasharray": "8,4"
}));
// Left bridge horizontal stubs
svg.appendChild(svgEl("line", {
x1: brgLeftX, y1: xbarTopY + 6, x2: xbarX, y2: xbarTopY + 6,
stroke: "#a78bfa", "stroke-width": 2, "stroke-dasharray": "6,3"
}));
svg.appendChild(svgEl("line", {
x1: brgLeftX, y1: xbarBotY + 16, x2: xbarX, y2: xbarBotY + 16,
stroke: "#a78bfa", "stroke-width": 2, "stroke-dasharray": "6,3"
}));
// Left bridge label
svg.appendChild(svgEl("rect", {
x: brgLeftX - 28, y: OY + 248, width: 56, height: 30,
rx: 4, fill: "#1e1b4b", stroke: "#a78bfa", "stroke-width": 1.5
}));
let bt = svgEl("text", {
x: brgLeftX, y: OY + 259, "text-anchor": "middle",
"font-family": "monospace", "font-size": "6", "font-weight": "bold", fill: "#a78bfa"
});
bt.textContent = "XBAR BRG";
svg.appendChild(bt);
bt = svgEl("text", {
x: brgLeftX, y: OY + 272, "text-anchor": "middle",
"font-family": "monospace", "font-size": "7", "font-weight": "bold", fill: "#a78bfa"
});
bt.textContent = "LEFT";
svg.appendChild(bt);
bt = svgEl("text", {
x: brgLeftX - 36, y: OY + 263, "text-anchor": "end",
"font-family": "monospace", "font-size": "6", fill: "#a78bfa88"
});
bt.textContent = "3mm";
svg.appendChild(bt);
// Right bridge vertical line
svg.appendChild(svgEl("line", {
x1: brgRightX, y1: xbarTopY + 10, x2: brgRightX, y2: xbarBotY + 12,
stroke: "#a78bfa", "stroke-width": 2.5, "stroke-dasharray": "8,4"
}));
// Right bridge horizontal stubs
svg.appendChild(svgEl("line", {
x1: brgRightX, y1: xbarTopY + 6, x2: xbarX + xbarW, y2: xbarTopY + 6,
stroke: "#a78bfa", "stroke-width": 2, "stroke-dasharray": "6,3"
}));
svg.appendChild(svgEl("line", {
x1: brgRightX, y1: xbarBotY + 16, x2: xbarX + xbarW, y2: xbarBotY + 16,
stroke: "#a78bfa", "stroke-width": 2, "stroke-dasharray": "6,3"
}));
// Right bridge label
svg.appendChild(svgEl("rect", {
x: brgRightX - 28, y: OY + 248, width: 56, height: 30,
rx: 4, fill: "#1e1b4b", stroke: "#a78bfa", "stroke-width": 1.5
}));
bt = svgEl("text", {
x: brgRightX, y: OY + 259, "text-anchor": "middle",
"font-family": "monospace", "font-size": "6", "font-weight": "bold", fill: "#a78bfa"
});
bt.textContent = "XBAR BRG";
svg.appendChild(bt);
bt = svgEl("text", {
x: brgRightX, y: OY + 272, "text-anchor": "middle",
"font-family": "monospace", "font-size": "7", "font-weight": "bold", fill: "#a78bfa"
});
bt.textContent = "RIGHT";
svg.appendChild(bt);
bt = svgEl("text", {
x: brgRightX + 36, y: OY + 263,
"font-family": "monospace", "font-size": "6", fill: "#a78bfa88"
});
bt.textContent = "3mm";
svg.appendChild(bt);
// ── M_CPU (r2c0) and SRAM (r3c0) labels ──
const mcpuP = rXY(2, 0);
svg.appendChild(svgEl("rect", {
x: mcpuP.x - 42, y: mcpuP.y + 18, width: 84, height: 18,
rx: 4, fill: "#f59e0b", stroke: "#d97706", "stroke-width": 1.5
}));
bt = svgEl("text", {
let bt = svgEl("text", {
x: mcpuP.x, y: mcpuP.y + 31, "text-anchor": "middle",
"font-family": "monospace", "font-size": "8", "font-weight": "bold", fill: "white"
});
@@ -1358,8 +1203,7 @@ function drawCubeView(svg, cubeIdx) {
{ color: "#e2e8f0", label: "Relay", textColor: "#475569" },
{ color: "#8b5cf6", label: "UCIe Router" },
{ color: "#f59e0b", label: "M_CPU/SRAM" },
{ color: "#a78bfa", label: "Bridge", type: "line" },
{ color: "#f97316", label: "XBAR", type: "rect" },
{ color: "#059669", label: "HBM Link", type: "line" },
{ color: "#047857", label: "HBM Ctrl", type: "rect" },
{ color: "#ef4444", label: "PE (~5mm2)", type: "rect" },
{ color: "#8b5cf6", label: "UCIe Port", type: "rect", rectFill: "#1e1b4b" },
@@ -1394,7 +1238,7 @@ function drawCubeView(svg, cubeIdx) {
const dpT = svgEl("text", {
x: 60, y: legY + 24, "font-family": "monospace", "font-size": "7", fill: "#64748b"
});
dpT.textContent = "Data: PE_DMA→NOC→XBAR→HBM | Cross-half: XBAR_TOP→Bridge(3mm)→XBAR_BOT→HBM4-7";
dpT.textContent = "Data: PE_DMA → Router Mesh → HBM_CTRL | All traffic routed through 6x6 mesh";
svg.appendChild(dpT);
}
@@ -1454,7 +1298,7 @@ function drawPeView(svg, cubeIdx, peIdx) {
// NOC destinations (inside NOC column)
const nocDests = [
{ label: "XBAR", sub: "→ HBM", y: nocTop + 50, fill: "#f97316", bg: "#3d2b1f" },
{ label: "HBM", sub: "ctrl", y: nocTop + 50, fill: "#059669", bg: "#052e16" },
{ label: "SRAM", sub: "128x4", y: nocTop + 86, fill: "#f59e0b", bg: "#3d2b1f" },
{ label: "UCIe", sub: "inter", y: nocTop + 122, fill: "#8b5cf6", bg: "#1e1b4b" },
{ label: "M_CPU", sub: "cmd", y: nocTop + 158, fill: "#f59e0b", bg: "#3d2b1f" },
@@ -1967,7 +1811,7 @@ function applyHotPaths(svg, t) {
}
} else if (currentView === "cube") {
// ── CUBE VIEW: highlight router mesh links + XBAR paths ──
// ── CUBE VIEW: highlight router mesh links ──
const linkTraffic = {};
for (const hop of activeHops) {
const linkId = hopToCubeLink(hop);
@@ -1984,16 +1828,13 @@ function applyHotPaths(svg, t) {
inflight++;
}
}
// Highlight XBAR/HBM components referenced in events
// Highlight HBM component referenced in events
const activeProcesses = allEvents.filter(e =>
e.type === "process" && e.t_ns <= t && e.t_ns >= t - 30
);
for (const proc of activeProcesses) {
const comp = proc.component || "";
if (comp.includes("xbar_top")) highlightComponent(svg, "xbar_top");
if (comp.includes("xbar_bot")) highlightComponent(svg, "xbar_bot");
const hbmMatch = comp.match(/hbm_ctrl\.slice(\d+)/);
if (hbmMatch) highlightComponent(svg, `hbm${hbmMatch[1]}`);
if (comp.includes("hbm_ctrl")) highlightComponent(svg, "hbm_ctrl");
}
} else if (currentView === "pe") {