paper(ipcq): add latency-model-style flow + stacked architecture diagrams

Adds two new IPCQ architecture views in the latency_model.png aesthetic:
a 2x2 flow figure (one per case) and a 4x1 stacked figure for taller
horizontal panels. Refactors per-case panel drawing into shared helpers
and disambiguates the flit labels (HMQ desc -> Hd so it does not
collide with data D). Two-row legend keys.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-18 14:55:59 -07:00
parent 7c45047e93
commit 37dc48fd27
5 changed files with 264 additions and 1 deletions
@@ -238,6 +238,267 @@ def _plot_architecture() -> Path:
return out
# Module-level flow-diagram helpers and constants.
_FLOW_DATA = "#6699CC" # blue flit (data)
_FLOW_CTRL = "#ED7D65" # orange flit (control)
_FLOW_CRED = "#7BB661" # green (credit / ack)
def _flow_swim(ax, x, y, w, h, label, sub=None):
ax.add_patch(mpatches.FancyBboxPatch(
(x, y), w, h,
boxstyle="round,pad=0.02,rounding_size=0.08",
facecolor="white", edgecolor="#3a3a3a", linewidth=1.6))
ax.text(x + w / 2, y + h / 2 + (0.20 if sub else 0),
label, ha="center", va="center",
fontsize=10.5, fontweight="bold", color="#111")
if sub:
ax.text(x + w / 2, y + h / 2 - 0.35, sub,
ha="center", va="center",
fontsize=8, color="#555", fontstyle="italic")
def _flow_flit(ax, x, y, color, letter="", size=0.32):
ax.add_patch(mpatches.FancyBboxPatch(
(x - size / 2, y - size / 2), size, size,
boxstyle="round,pad=0.01,rounding_size=0.04",
facecolor=color, edgecolor="#222", linewidth=0.8))
if letter:
ax.text(x, y, letter, ha="center", va="center",
fontsize=8.5, fontweight="bold", color="white")
def _flow_wire(ax, x1, x2, y, color="#888"):
ax.plot([x1, x2], [y, y], color=color, lw=1.0, zorder=0)
def _flow_dot(ax, x, y, color):
ax.add_patch(mpatches.Circle((x, y), 0.13,
facecolor=color, edgecolor="#333",
linewidth=0.8, zorder=4))
def _flow_label(ax, x, y, text, color="#222", fontsize=9):
ax.text(x, y, text, ha="center", va="center",
fontsize=fontsize, color=color, fontweight="bold")
def _flow_lead(ax, x_tail, y_tail, x_head, y_head, color="#777"):
ax.plot([x_tail, x_head], [y_tail, y_head],
color=color, lw=0.8, ls=":", zorder=2)
def _flow_setup(ax, kind):
ax.set_xlim(0, 20); ax.set_ylim(0, 10); ax.axis("off")
ax.add_patch(mpatches.FancyBboxPatch(
(0.15, 0.15), 19.7, 9.7,
boxstyle="round,pad=0.05,rounding_size=0.12",
facecolor="white", edgecolor=_COLOR[kind],
linewidth=2.2, zorder=0))
ax.text(10, 0.65, _TITLE[kind],
ha="center", va="center", fontsize=11.5,
fontweight="bold",
color=("#1B5E20" if kind == "ipcq" else "white"),
bbox=dict(
facecolor=("#E8F5E9" if kind == "ipcq" else _COLOR[kind]),
edgecolor=("#2E7D32" if kind == "ipcq" else _COLOR[kind]),
boxstyle="round,pad=0.40", linewidth=1.5))
def _draw_flow_case(ax, kind):
"""Draw one Sender → NoC → Receiver flow panel for `kind`."""
_flow_setup(ax, kind)
if kind == "doorbell":
_draw_case_doorbell(ax)
elif kind == "hmq":
_draw_case_hmq(ax)
elif kind == "rdma":
_draw_case_rdma(ax)
elif kind == "ipcq":
_draw_case_ipcq(ax)
def _draw_case_doorbell(ax):
_flow_swim(ax, 0.7, 4.4, 2.8, 2.0, "Sender\nkernel")
_flow_swim(ax, 7.4, 4.4, 3.0, 2.0, "NoC / UCIe", sub="2 DMA tx")
_flow_swim(ax, 14.4, 4.4, 3.0, 2.0, "Doorbell reg\n+ landing TCM")
_flow_swim(ax, 17.6, 6.7, 2.0, 1.7, "Receiver\n(poll)", sub="poll loop")
_flow_wire(ax, 3.5, 7.4, 5.7); _flow_wire(ax, 10.4, 14.4, 5.7)
for x in [4.0, 4.7, 5.4]:
_flow_flit(ax, x, 5.7, _FLOW_DATA, "D")
for x in [11.0, 11.7]:
_flow_flit(ax, x, 5.7, _FLOW_DATA, "D")
_flow_wire(ax, 3.5, 7.4, 5.1); _flow_wire(ax, 10.4, 14.4, 5.1)
_flow_flit(ax, 4.0, 5.1, _FLOW_CTRL, "F")
_flow_flit(ax, 4.7, 5.1, _FLOW_CTRL, "DB")
_flow_flit(ax, 11.0, 5.1, _FLOW_CTRL, "DB")
for y in (8.0, 7.5, 7.0):
ax.annotate("", xy=(15.5, y), xytext=(17.6, y),
arrowprops=dict(arrowstyle="->", color="#aa3a30",
lw=1.0, ls=":"))
_flow_dot(ax, 5.0, 3.5, "#c5a35a")
_flow_lead(ax, 5.0, 3.6, 5.0, 4.4)
_flow_label(ax, 5.0, 3.2, "fence + 2 DMA writes", color="#5a3e20")
_flow_dot(ax, 17.0, 8.8, "#7BB661")
_flow_lead(ax, 17.0, 8.7, 17.0, 8.0)
_flow_label(ax, 17.0, 9.1, "receiver polling loop", color="#2E7D32")
def _draw_case_hmq(ax):
_flow_swim(ax, 0.7, 4.4, 2.8, 2.0, "Sender CPU")
_flow_swim(ax, 4.6, 6.6, 2.8, 1.8, "HMQ engine", sub="descriptor")
_flow_swim(ax, 4.6, 3.6, 2.8, 1.8, "PE_DMA", sub="data path")
_flow_swim(ax, 9.6, 4.4, 3.0, 2.0, "NoC / UCIe", sub="2 paths")
_flow_swim(ax, 14.4, 6.6, 2.8, 1.8, "Peer HMQ")
_flow_swim(ax, 14.4, 3.6, 2.8, 1.8, "Recv TCM")
_flow_swim(ax, 17.6, 4.4, 2.0, 2.0, "Receiver\nCPU")
_flow_wire(ax, 3.5, 4.6, 7.5); _flow_wire(ax, 7.4, 9.6, 7.5)
_flow_wire(ax, 12.6, 14.4, 7.5); _flow_wire(ax, 17.2, 17.6, 5.4)
_flow_flit(ax, 4.0, 7.5, _FLOW_CTRL, "Hd")
_flow_flit(ax, 8.5, 7.5, _FLOW_CTRL, "Hd")
_flow_flit(ax, 13.5, 7.5, _FLOW_CTRL, "Hd")
_flow_wire(ax, 3.5, 4.6, 4.5); _flow_wire(ax, 7.4, 9.6, 4.5)
_flow_wire(ax, 12.6, 14.4, 4.5)
for x in [4.0, 8.0, 8.7, 13.5]:
_flow_flit(ax, x, 4.5, _FLOW_DATA, "D")
_flow_dot(ax, 11.1, 9.0, "#c5a35a")
_flow_lead(ax, 11.1, 8.9, 11.1, 7.6)
_flow_label(ax, 11.1, 9.25, "duplicated control + data paths",
color="#5a3e20")
def _draw_case_rdma(ax):
_flow_swim(ax, 0.7, 4.4, 2.8, 2.0, "Sender\nkernel")
_flow_swim(ax, 4.8, 4.4, 2.8, 2.0, "WQE +\nPE_DMA")
_flow_swim(ax, 8.5, 4.4, 3.0, 2.0, "NoC / UCIe")
_flow_swim(ax, 13.0, 4.4, 3.0, 2.0, "CQ + landing\nTCM",
sub="auto-CQE")
_flow_swim(ax, 17.0, 6.7, 2.5, 1.7, "Receiver\n(poll CQ)")
_flow_wire(ax, 3.5, 4.8, 5.7)
_flow_flit(ax, 4.0, 5.7, _FLOW_CTRL, "W")
_flow_wire(ax, 7.6, 8.5, 5.4); _flow_wire(ax, 11.5, 13.0, 5.4)
_flow_flit(ax, 8.1, 5.4, _FLOW_DATA, "D")
_flow_flit(ax, 9.2, 5.4, _FLOW_DATA, "D")
_flow_flit(ax, 9.9, 5.4, _FLOW_CTRL, "Q")
_flow_flit(ax, 11.0, 5.4, _FLOW_DATA, "D")
_flow_flit(ax, 12.0, 5.4, _FLOW_CTRL, "Q")
for y in (8.0, 7.5, 7.0):
ax.annotate("", xy=(14.5, y), xytext=(17.0, y),
arrowprops=dict(arrowstyle="->", color="#7c1b78",
lw=1.0, ls=":"))
_flow_dot(ax, 11.0, 3.5, "#c5a35a")
_flow_lead(ax, 11.0, 3.6, 11.0, 4.4)
_flow_label(ax, 11.0, 3.2, "CQE rides with DMA payload",
color="#5a3e20")
_flow_dot(ax, 18.0, 8.8, "#7BB661")
_flow_lead(ax, 18.0, 8.7, 18.0, 8.0)
_flow_label(ax, 18.0, 9.1, "CQ poll / IRQ", color="#2E7D32")
def _draw_case_ipcq(ax):
_flow_swim(ax, 0.7, 4.4, 2.8, 2.0, "Sender PE\n(tl.send)")
_flow_swim(ax, 4.5, 6.6, 3.0, 1.8, "PE_IPCQ", sub="ring push")
_flow_swim(ax, 4.5, 3.6, 3.0, 1.8, "PE_DMA", sub="data push")
_flow_swim(ax, 9.4, 4.4, 3.0, 2.0, "NoC / UCIe", sub="dir-addr")
_flow_swim(ax, 13.8, 6.6, 3.0, 1.8, "PE_IPCQ",
sub="tail++ , credit")
_flow_swim(ax, 13.8, 3.6, 3.0, 1.8, "PE_DMA",
sub="landing TCM")
_flow_swim(ax, 17.6, 4.4, 2.0, 2.0, "Receiver\nPE\n(tl.recv)")
_flow_wire(ax, 3.5, 4.5, 7.5); _flow_wire(ax, 7.5, 9.4, 7.5)
_flow_wire(ax, 12.4, 13.8, 7.5); _flow_wire(ax, 16.8, 17.6, 5.4)
_flow_flit(ax, 4.0, 7.5, _FLOW_CTRL, "M")
_flow_flit(ax, 8.4, 7.5, _FLOW_CTRL, "M")
_flow_flit(ax, 13.0, 7.5, _FLOW_CTRL, "M")
_flow_wire(ax, 3.5, 4.5, 4.5); _flow_wire(ax, 7.5, 9.4, 4.5)
_flow_wire(ax, 12.4, 13.8, 4.5)
_flow_flit(ax, 4.0, 4.5, _FLOW_DATA, "D")
_flow_flit(ax, 8.4, 4.5, _FLOW_DATA, "D")
_flow_flit(ax, 11.0, 4.5, _FLOW_DATA, "D")
_flow_flit(ax, 13.0, 4.5, _FLOW_DATA, "D")
ax.annotate("", xy=(4.5, 9.0), xytext=(16.8, 9.0),
arrowprops=dict(arrowstyle="->", color=_FLOW_CRED,
lw=1.6, ls=(0, (5, 3))))
_flow_label(ax, 10.5, 9.35, "credit piggyback (no separate ACK)",
color=_FLOW_CRED)
_flow_dot(ax, 6.0, 2.6, "#c5a35a")
_flow_lead(ax, 6.0, 2.7, 6.0, 3.6)
_flow_label(ax, 6.0, 2.3, "one in-line ring push + one DMA",
color="#5a3e20")
def _flow_legend(fig, leg_y=0.02):
"""Two-row legend so the long control-flit key doesn't overlap
the shorter data/credit entries."""
row_top = [
(_FLOW_DATA, "Data flit D = payload"),
(_FLOW_CRED, "Credit / piggyback return"),
]
row_bottom = [
(_FLOW_CTRL, "Control flit: W=WQE · Q=CQE · M=ring meta"
" · DB=doorbell · F=fence · Hd=HMQ desc"),
]
def _emit(items, y):
lx = 0.04
for color, text in items:
fig.patches.append(mpatches.FancyBboxPatch(
(lx, y), 0.020, 0.022,
boxstyle="round,pad=0.005,rounding_size=0.005",
facecolor=color, edgecolor="#222", linewidth=0.6,
transform=fig.transFigure))
fig.text(lx + 0.025, y + 0.011, text,
ha="left", va="center", fontsize=10,
fontweight="bold", color="#222")
lx += 0.32
_emit(row_top, y=leg_y + 0.030)
_emit(row_bottom, y=leg_y)
# ─── Diagram 1b : 4-case flow-style architecture (latency_model look)
def _plot_architecture_flow() -> Path:
"""4 panels arranged 2×2 — each a horizontal Sender → NoC → Receiver
flow with small flit-style packets, dot-marker annotations and a
bottom name plate. Aesthetic mirrors latency_model.png."""
fig, axes = plt.subplots(2, 2, figsize=(18.0, 11.5))
_draw_flow_case(axes[0, 0], "doorbell")
_draw_flow_case(axes[0, 1], "hmq")
_draw_flow_case(axes[1, 0], "rdma")
_draw_flow_case(axes[1, 1], "ipcq")
fig.suptitle(
"Per-send architecture = $\\Sigma$ control overhead + "
"data-path DMA + receiver wake",
fontsize=14.5, fontweight="bold", y=0.995)
_flow_legend(fig, leg_y=0.015)
fig.tight_layout(rect=(0, 0.07, 1, 0.96))
out = _OUT_DIR / "ipcq_alternatives_architecture_flow.png"
fig.savefig(out, dpi=150, bbox_inches="tight")
plt.close(fig)
return out
# ─── Diagram 1c : 4-case flow-style architecture — stacked 4×1 ──────
def _plot_architecture_stacked() -> Path:
"""Same per-case flow content as the 2×2 figure, but stacked one
case per row (4 rows × 1 column). Wider panels give each case more
horizontal room."""
fig, axes = plt.subplots(4, 1, figsize=(16.0, 19.0))
for ax, kind in zip(axes, ["doorbell", "hmq", "rdma", "ipcq"]):
_draw_flow_case(ax, kind)
fig.suptitle(
"Per-send architecture = $\\Sigma$ control overhead + "
"data-path DMA + receiver wake",
fontsize=14.5, fontweight="bold", y=0.997)
_flow_legend(fig, leg_y=0.010)
fig.tight_layout(rect=(0, 0.05, 1, 0.97))
out = _OUT_DIR / "ipcq_alternatives_architecture_stacked.png"
fig.savefig(out, dpi=150, bbox_inches="tight")
plt.close(fig)
return out
# ─── Diagram 2 : Per-send latency breakdown (vertical stacks) ───────
def _plot_latency() -> Path:
# (label, short, illustrative cycles, fill)
@@ -782,7 +1043,9 @@ def _plot_control_data_overlay() -> Path:
def main() -> None:
_OUT_DIR.mkdir(parents=True, exist_ok=True)
for plot in (_plot_architecture, _plot_latency,
for plot in (_plot_architecture, _plot_architecture_flow,
_plot_architecture_stacked,
_plot_latency,
_plot_opcounts, _plot_sequence,
_plot_decision_matrix,
_plot_control_data_overlay):