From 164e8c964184ba590a418da941c18e35082b3cfd Mon Sep 17 00:00:00 2001 From: cscaff Date: Sat, 11 Jul 2026 16:18:00 -0400 Subject: [PATCH 1/2] Add hardware & SoC architecture reference page Users had no way to see what's on the other end of mrg run: the board, the fixed LiteX SoC every node boots, or how much of the FPGA is left for their own design. Adds docs/reference/hardware.md with the address space, a Mermaid architecture diagram, and a real (measured, not estimated) resource budget from building the base SoC. Also wires up Mermaid rendering for the custom theme (superfences already emitted mermaid fences, but nothing rendered them), themed to match docs.css's palette. --- docs/examples/ffn-accel.md | 8 ++- docs/javascripts/mermaid-init.js | 44 ++++++++++++ docs/reference/hardware.md | 112 +++++++++++++++++++++++++++++++ docs/reference/index.md | 5 +- mkdocs.yml | 6 ++ theme/css/docs.css | 11 +++ 6 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 docs/javascripts/mermaid-init.js create mode 100644 docs/reference/hardware.md diff --git a/docs/examples/ffn-accel.md b/docs/examples/ffn-accel.md index 482f45f..2a74898 100644 --- a/docs/examples/ffn-accel.md +++ b/docs/examples/ffn-accel.md @@ -41,9 +41,11 @@ The whole dual-engine slave at `tile=4`: | FFNSlave | 3,437 | 840 | 40 | 6 | 1,505 | | **Budget (LFE5UM5G-85F)** | 84k | – | **156** | 208 | – | -Fits with huge headroom next to the VexRiscv+LiteEth SoC. The parallel MAC -grid scales as `tile²` DSPs, 12×12 (144 MACs) is the max all-DSP fit; 16×16 -(256 MACs) needs on-fabric multipliers. +Fits with huge headroom next to the VexRiscv+LiteEth SoC (see +[Hardware & SoC architecture](../reference/hardware.md) for what that SoC +alone costs and how much of the chip is left over before this design is even +added). The parallel MAC grid scales as `tile²` DSPs, 12×12 (144 MACs) is the +max all-DSP fit; 16×16 (256 MACs) needs on-fabric multipliers. ## Run it diff --git a/docs/javascripts/mermaid-init.js b/docs/javascripts/mermaid-init.js new file mode 100644 index 0000000..d708e02 --- /dev/null +++ b/docs/javascripts/mermaid-init.js @@ -0,0 +1,44 @@ +// Mermaid rendering for diagram code fences (see mkdocs.yml's superfences +// "mermaid" custom fence). The custom theme has no light/dark toggle (fixed +// dark, data-md-color-scheme="slate" in main.html), so themeVariables below +// are a one-shot match to theme/css/docs.css's palette rather than a +// light/dark pair. +import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs"; + +mermaid.initialize({ + startOnLoad: false, + theme: "base", + fontFamily: '"Helvetica Neue", Helvetica, Arial, sans-serif', + themeVariables: { + background: "#0e1218", + primaryColor: "#10151d", + primaryTextColor: "#d7dee9", + primaryBorderColor: "#1c2330", + secondaryColor: "#0e1218", + secondaryBorderColor: "#1c2330", + tertiaryColor: "#0e1218", + tertiaryBorderColor: "#1c2330", + lineColor: "#5fb0ff", + textColor: "#d7dee9", + mainBkg: "#10151d", + nodeTextColor: "#d7dee9", + clusterBkg: "#05070d", + clusterBorder: "#1c2330", + titleColor: "#f2f5fa", + edgeLabelBackground: "#0e1218", + fontSize: "14px", + }, + flowchart: { curve: "basis", htmlLabels: true, padding: 14 }, + securityLevel: "loose", +}); + +// pymdownx.superfences emits
...
; +// mermaid's run() reads the element's raw innerHTML and chokes on that +// nested tag ("No diagram type detected... text: ..."). +// Flatten each block to a plain text node (still the exact decoded diagram +// source) before handing it to mermaid. +for (const el of document.querySelectorAll("pre.mermaid")) { + el.textContent = el.textContent; +} + +mermaid.run({ querySelector: "pre.mermaid" }); diff --git a/docs/reference/hardware.md b/docs/reference/hardware.md new file mode 100644 index 0000000..4c388e5 --- /dev/null +++ b/docs/reference/hardware.md @@ -0,0 +1,112 @@ +# Hardware & SoC architecture + +What's on the other end of `mrg run`: the board, the fixed LiteX SoC every +node boots, and the FPGA resources left over for your design. + +## The board + +Every node is a Lattice ECP5 FPGA, device `LFE5UM5G-85F-8BG381`. A cluster of +these boards is the pool `mrg run` schedules onto. See the +[CLI guide](../guides/cli.md) for `status` and `reset`. + +## System architecture + +Every board boots the same LiteX SoC: a VexRiscv CPU, a LiteEth network +stack, and a Wishbone bus, with one pluggable slot for your design. + +```mermaid +flowchart LR + PLL(["On-chip PLL"]) --> SYS["cd_sys
50 MHz, fixed"] + PLL --> USR["cd_user
tunable"] + + subgraph SOC["Fixed LiteX SoC"] + SYS --> CPU["VexRiscv CPU"] + CPU --> BUS(("Wishbone bus")) + BUS --> ROM["Firmware ROM"] + BUS --> SRAM["SRAM"] + BUS --> MAC["Ethernet MAC"] + BUS --> BRIDGE["Clock domain bridge"] + end + + MAC --> PHY["Ethernet PHY"] + BRIDGE --> USR + USR --> YOURS["Your design
2 KB Wishbone window"] + + classDef focus fill:#0072ff1f,stroke:#0072ff,color:#f2f5fa,stroke-width:2px; + class YOURS focus; +``` + +The control plane (`cd_sys`: CPU, bus, bridge firmware, MAC) always runs at a +fixed 50 MHz, identical on every build. Only `cd_user`, the domain your +design is instantiated in, is tunable. A clock sweep only changes your +design's clock, never the known-good infrastructure around it. + +Your side of the contract is a plain Wishbone B4 peripheral, top module +`user_design`: + +```text +input clk, rst +input wb_cyc, wb_stb, wb_we +input [8:0] wb_adr (32-bit word address, 512 words) +input [31:0] wb_dat_w +input [3:0] wb_sel +output [31:0] wb_dat_r +output wb_ack (registered, 1 cycle after cyc & stb) +``` + +That's the only interface between your logic and the rest of the SoC: one +2 KB MMIO window, nothing else. Everything outside that window (firmware, +CPU memory, networking) is off limits and not reachable from `user_design`. + +## Address space + +| Region | Address | Size | Access | +| --- | --- | --- | --- | +| **Your design** | `0x90000000` | 2 KB (512 x 32-bit words) | Read and write, this is yours | +| Everything else | n/a | n/a | Off limits, reserved for the SoC | + +## FPGA resources + +Numbers below are from a real build: the LiteX SoC synthesized and placed +with a no-op placeholder in the user slot, so this is what the CPU, bus, and +networking cost before your design adds a single gate. + +**Total available on the chip:** + +```text +LUT 83,640 +FF 83,640 +BRAM 208 +DSP 156 +PLL 4 +IO 365 +``` + +**Used by the SoC (CPU, bus, networking), before your design:** + +```text +LUT [█▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒] 6% 5,348 +FF [█▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒] 3% 2,718 +BRAM [██▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒] 12% 25 +DSP [▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒] 2% 4 +PLL [█████▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒] 25% 1 +IO [█▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒] 4% 15 +``` + +**Left over for your design:** + +```text +LUT [███████████████████▒] 94% 78,292 +FF [███████████████████▒] 97% 80,922 +BRAM [██████████████████▒▒] 88% 183 +DSP [████████████████████] 98% 152 +PLL [███████████████▒▒▒▒▒] 75% 3 +IO [███████████████████▒] 96% 350 +``` + +The [FFN accelerator example](../examples/ffn-accel.md) is a real design +sized against this same budget (3,437 LUT, 1,505 FF, 40 DSP, 6 BRAM), well +inside what's left over. + +One PLL is already spent generating `cd_sys` and `cd_user`. A design that +needs its own independent clock domain has 3 PLLs left, not 4. diff --git a/docs/reference/index.md b/docs/reference/index.md index 9bddb9d..f0675f6 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -1,7 +1,10 @@ # API Reference Generated from `manhattan-reasoning-gym`'s public docstrings. For a narrative -walkthrough of these same surfaces, see the [SDK guide](../guides/sdk.md). +walkthrough of these same surfaces, see the [SDK guide](../guides/sdk.md). For +what's physically on the other end of these calls, the board, the SoC, and +the FPGA resource budget your design gets, see +[Hardware & SoC architecture](hardware.md). The SDK is organized by role into four namespaces: diff --git a/mkdocs.yml b/mkdocs.yml index 7730a3e..264ed17 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -21,6 +21,11 @@ extra_javascript: # reads as a dark-mode version of the main site. Harmless on pages with no # such canvas. - javascripts/routing-viz.js + # Renders ```mermaid fences (see the superfences custom_fences entry below) + # with themeVariables matched to docs.css's palette. Harmless on pages with + # no .mermaid blocks. + - path: javascripts/mermaid-init.js + type: module markdown_extensions: - admonition @@ -79,6 +84,7 @@ nav: - FFN accelerator: examples/ffn-accel.md - Reference: - Overview: reference/index.md + - Hardware & SoC: reference/hardware.md - mrg.cloud: reference/cloud.md - mrg.build: reference/build.md - mrg.sandbox: reference/sandbox.md diff --git a/theme/css/docs.css b/theme/css/docs.css index cc10204..3718bd1 100644 --- a/theme/css/docs.css +++ b/theme/css/docs.css @@ -346,6 +346,17 @@ a:hover { text-decoration: underline; } /* line-number gutter (anchor_linenums) */ .highlight .linenos { color: var(--dim); user-select: none; } +/* Mermaid diagrams (see docs/javascripts/mermaid-init.js for the matching + themeVariables). Framed like a code block so it reads as part of the same + content system, not a foreign embed. */ +.md-content pre.mermaid { + display: flex; justify-content: center; + background: var(--surface); border: 1px solid var(--line); + border-radius: 10px; padding: 1.4rem; margin: 0 0 1.3rem; + overflow-x: auto; +} +.md-content pre.mermaid svg { max-width: 100%; } + /* Tables */ .md-content table { width: 100%; border-collapse: collapse; margin: 0 0 1.4rem; From d91de3e8f3e12587ade69f9657e2caafdb85060a Mon Sep 17 00:00:00 2001 From: cscaff Date: Sat, 11 Jul 2026 16:20:44 -0400 Subject: [PATCH 2/2] Run the build job on pull_request, not just push to main The deploy step already guarded itself with if: github.event_name != 'pull_request', but nothing ever triggered the workflow on a PR, so mkdocs build --strict never actually ran as a check before merge. Add the missing trigger. --- .github/workflows/deploy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ac044fe..d889c6c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -3,6 +3,7 @@ name: deploy on: push: branches: [main] + pull_request: workflow_dispatch: # Allow the gym repo to trigger a rebuild when its API changes: # gh api repos/ManhattanReasoning/manhattanreasoning-docs/dispatches \