From 7a0742f47b90ff9337c1bb7f334627354a449eca Mon Sep 17 00:00:00 2001
From: deaneeth
Date: Fri, 5 Jun 2026 21:21:01 +0530
Subject: [PATCH 1/7] ci(rtl): add RTL CI workflow with Verilator 5.036 and
cocotb test suites
Adds .github/workflows/rtl.yml, triggered on push/PR to rtl/** or sim/**.
Ubuntu 24.04 apt ships Verilator 5.020, but cocotb 2.x requires >= 5.036.
Build Verilator v5.036 from source on first run and cache the install prefix
by version tag so subsequent runs skip the ~5 min build step.
Steps:
- Lint RTL with `verilator --lint-only -Wall rtl/*.sv`
- Run `pytest sim/golden.py -q` (numpy golden reference)
- cocotb test_pe, test_systolic_array, test_top via make (WAVES=0 for speed)
Removes .github/workflows/.gitkeep placeholder now that real files exist.
---
.github/workflows/.gitkeep | 0
.github/workflows/rtl.yml | 76 ++++++++++++++++++++++++++++++++++++++
2 files changed, 76 insertions(+)
delete mode 100644 .github/workflows/.gitkeep
create mode 100644 .github/workflows/rtl.yml
diff --git a/.github/workflows/.gitkeep b/.github/workflows/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/.github/workflows/rtl.yml b/.github/workflows/rtl.yml
new file mode 100644
index 0000000..3b78719
--- /dev/null
+++ b/.github/workflows/rtl.yml
@@ -0,0 +1,76 @@
+name: RTL CI
+
+on:
+ push:
+ paths:
+ - 'rtl/**'
+ - 'sim/**'
+ - '.github/workflows/rtl.yml'
+ pull_request:
+ paths:
+ - 'rtl/**'
+ - 'sim/**'
+ - '.github/workflows/rtl.yml'
+
+jobs:
+ rtl-ci:
+ runs-on: ubuntu-24.04
+
+ steps:
+ - uses: actions/checkout@v4
+
+ # cocotb 2.x requires Verilator >= 5.036; the ubuntu-24.04 apt package
+ # is only 5.020, so we build from source and cache by version tag.
+ - name: Cache Verilator build
+ id: cache-verilator
+ uses: actions/cache@v4
+ with:
+ path: ~/verilator-install
+ key: verilator-v5.036-ubuntu-24.04
+
+ - name: Build Verilator v5.036 from source
+ if: steps.cache-verilator.outputs.cache-hit != 'true'
+ run: |
+ sudo apt-get update -qq
+ sudo apt-get install -y autoconf flex bison libfl2 libfl-dev help2man perl
+ git clone --depth 1 --branch v5.036 https://github.com/verilator/verilator.git /tmp/verilator
+ cd /tmp/verilator
+ autoconf
+ ./configure --prefix="$HOME/verilator-install"
+ make -j$(nproc)
+ make install
+
+ - name: Add Verilator to PATH
+ run: echo "$HOME/verilator-install/bin" >> $GITHUB_PATH
+
+ - uses: actions/setup-python@v5
+ with:
+ python-version: '3.12'
+
+ - name: Install Python deps
+ run: pip install cocotb numpy pytest
+
+ - name: Lint RTL
+ run: verilator --lint-only -Wall rtl/*.sv
+
+ - name: Golden model tests
+ run: pytest sim/golden.py -q
+
+ - name: cocotb — PE
+ working-directory: sim
+ run: make MODULE=test_pe TOPLEVEL=pe WAVES=0
+
+ - name: cocotb — Systolic Array
+ working-directory: sim
+ run: |
+ make MODULE=test_systolic_array TOPLEVEL=systolic_array \
+ VERILOG_SOURCES="../rtl/pe.sv ../rtl/systolic_array.sv" \
+ WAVES=0
+
+ - name: cocotb — Top (full matmul vs golden)
+ working-directory: sim
+ run: |
+ make MODULE=test_top TOPLEVEL=tiny_tpu_top \
+ VERILOG_SOURCES="../rtl/pe.sv ../rtl/systolic_array.sv \
+ ../rtl/controller.sv ../rtl/tiny_tpu_top.sv" \
+ WAVES=0
From 637fb448e3c2bccfb164f8f45f34bab7df94c89b Mon Sep 17 00:00:00 2001
From: deaneeth
Date: Fri, 5 Jun 2026 21:21:27 +0530
Subject: [PATCH 2/7] ci(web): add web CI workflow with pnpm store caching
Adds .github/workflows/web.yml, triggered on push/PR to web/**.
Uses pnpm/action-setup@v4 (pnpm 11) + actions/setup-node@v4 (Node 22)
with `cache: pnpm` pointing at web/pnpm-lock.yaml to avoid full reinstalls
on unchanged dependencies.
Runs in order: pnpm install --frozen-lockfile, pnpm lint, pnpm typecheck,
pnpm build. All three must pass; build failure surfaces SSR/WASM import
errors that wouldn't otherwise be caught locally.
---
.github/workflows/web.yml | 44 +++++++++++++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
create mode 100644 .github/workflows/web.yml
diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml
new file mode 100644
index 0000000..6db9bce
--- /dev/null
+++ b/.github/workflows/web.yml
@@ -0,0 +1,44 @@
+name: Web CI
+
+on:
+ push:
+ paths:
+ - 'web/**'
+ - '.github/workflows/web.yml'
+ pull_request:
+ paths:
+ - 'web/**'
+ - '.github/workflows/web.yml'
+
+jobs:
+ web-ci:
+ runs-on: ubuntu-24.04
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: pnpm/action-setup@v4
+ with:
+ version: '11'
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: '22'
+ cache: 'pnpm'
+ cache-dependency-path: web/pnpm-lock.yaml
+
+ - name: Install dependencies
+ working-directory: web
+ run: pnpm install --frozen-lockfile
+
+ - name: Lint
+ working-directory: web
+ run: pnpm lint
+
+ - name: Type check
+ working-directory: web
+ run: pnpm typecheck
+
+ - name: Build
+ working-directory: web
+ run: pnpm build
From 66b998574a24eb5acbaab8e72b453755efbcdf04 Mon Sep 17 00:00:00 2001
From: deaneeth
Date: Fri, 5 Jun 2026 21:21:52 +0530
Subject: [PATCH 3/7] ci(wasm): add WASM build CI workflow with emsdk
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Adds .github/workflows/wasm.yml, triggered on push/PR to rtl/** or wasm/**.
Installs Verilator 5.020 from apt (sufficient for the WASM build path —
cocotb's version gate only applies to the simulation Makefile, not the
em++ compilation step) and Emscripten via mymindstorm/setup-emsdk@v14.
Runs wasm/build.sh and asserts both web/public/tiny_tpu.mjs and
web/public/tiny_tpu.wasm are produced. Does not commit artifacts from CI;
they must be committed manually after local rebuilds.
---
.github/workflows/wasm.yml | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
create mode 100644 .github/workflows/wasm.yml
diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml
new file mode 100644
index 0000000..b0049dd
--- /dev/null
+++ b/.github/workflows/wasm.yml
@@ -0,0 +1,37 @@
+name: WASM Build
+
+on:
+ push:
+ paths:
+ - 'rtl/**'
+ - 'wasm/**'
+ - '.github/workflows/wasm.yml'
+ pull_request:
+ paths:
+ - 'rtl/**'
+ - 'wasm/**'
+ - '.github/workflows/wasm.yml'
+
+jobs:
+ wasm-build:
+ runs-on: ubuntu-24.04
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install Verilator and build deps
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y verilator build-essential
+
+ - name: Set up Emscripten
+ uses: mymindstorm/setup-emsdk@v14
+
+ - name: Build WASM
+ run: bash wasm/build.sh
+
+ - name: Assert artifacts produced
+ run: |
+ test -f web/public/tiny_tpu.mjs || (echo "ERROR: tiny_tpu.mjs not produced" && exit 1)
+ test -f web/public/tiny_tpu.wasm || (echo "ERROR: tiny_tpu.wasm not produced" && exit 1)
+ echo "WASM artifacts verified: $(du -sh web/public/tiny_tpu.wasm | cut -f1) wasm"
From e1988f56754adc77e35446cd1a444e4ba38b75d3 Mon Sep 17 00:00:00 2001
From: deaneeth
Date: Fri, 5 Jun 2026 21:22:09 +0530
Subject: [PATCH 4/7] docs(ci): add CI status badge row to README header
Adds a second badge row beneath the tech-stack badges with live status
links for the three new GitHub Actions workflows: RTL CI, Web CI, and
WASM Build. Badges link directly to the workflow run list so viewers
can inspect recent results without leaving the README.
---
README.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/README.md b/README.md
index 825471c..b6177c2 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,12 @@
+
+
+
+
+
+
From 77617ce37ed1e59b1dee962cb42c96c493553055 Mon Sep 17 00:00:00 2001
From: deaneeth
Date: Fri, 5 Jun 2026 21:22:36 +0530
Subject: [PATCH 5/7] chore(deploy): add vercel.json with WASM MIME type and
cache headers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Vercel may serve .wasm files as text/html without an explicit header rule,
which causes the browser to reject the module with a MIME type error and
shows a blank visualizer. The headers block pins Content-Type to
application/wasm for tiny_tpu.wasm and application/javascript for
tiny_tpu.mjs, ensuring correct browser parsing in production.
Also sets Cache-Control: immutable on both artifacts — they are content-
addressed by the WASM build process so long-lived caching is safe.
vercel.json is placed inside web/ (the configured Vercel root directory).
---
web/vercel.json | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 web/vercel.json
diff --git a/web/vercel.json b/web/vercel.json
new file mode 100644
index 0000000..c406c49
--- /dev/null
+++ b/web/vercel.json
@@ -0,0 +1,30 @@
+{
+ "headers": [
+ {
+ "source": "/tiny_tpu.wasm",
+ "headers": [
+ {
+ "key": "Content-Type",
+ "value": "application/wasm"
+ },
+ {
+ "key": "Cache-Control",
+ "value": "public, max-age=31536000, immutable"
+ }
+ ]
+ },
+ {
+ "source": "/tiny_tpu.mjs",
+ "headers": [
+ {
+ "key": "Content-Type",
+ "value": "application/javascript; charset=utf-8"
+ },
+ {
+ "key": "Cache-Control",
+ "value": "public, max-age=31536000, immutable"
+ }
+ ]
+ }
+ ]
+}
From 15747c022be5793fa8a83ac8fe7e40d6cddb74e3 Mon Sep 17 00:00:00 2001
From: deaneeth
Date: Fri, 5 Jun 2026 21:23:02 +0530
Subject: [PATCH 6/7] fix(lint): fix ESLint errors in gen-og-image.mjs
Two issues in the OG image generation script broke `pnpm lint`:
1. Unused variable: `fill` was computed in peCell() but the SVG template
later switched to inline hardcoded hex values. Removed the dead
declaration so no-unused-vars is satisfied.
2. Undeclared globals: `console` and `Buffer` are Node.js globals, but
the ESLint config only injected browser + es2022 globals for .ts/.tsx
files. Added a separate config block for scripts/**/*.mjs with
globals.node so these built-ins are recognised without any
eslint-disable comments.
`pnpm lint`, `pnpm typecheck`, and `pnpm build` all pass after this fix.
---
web/eslint.config.js | 9 +++++++++
web/scripts/gen-og-image.mjs | 5 -----
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/web/eslint.config.js b/web/eslint.config.js
index d65fbb3..647ded7 100644
--- a/web/eslint.config.js
+++ b/web/eslint.config.js
@@ -39,6 +39,15 @@ export default [
react: { version: "detect" },
},
},
+ {
+ files: ["scripts/**/*.mjs", "scripts/**/*.js"],
+ languageOptions: {
+ globals: {
+ ...globals.node,
+ ...globals.es2022,
+ },
+ },
+ },
{
ignores: ["dist/", ".astro/", "node_modules/", "public/"],
},
diff --git a/web/scripts/gen-og-image.mjs b/web/scripts/gen-og-image.mjs
index 9538829..268711d 100644
--- a/web/scripts/gen-og-image.mjs
+++ b/web/scripts/gen-og-image.mjs
@@ -38,11 +38,6 @@ function peCell(row, col) {
const y = GRID_Y + row * (CELL + CELL_GAP);
const isDiag = row === col;
const isLoading = row === col + 1;
- const fill = isDiag
- ? `color-mix(in oklch, ${LIME} 9%, ${SURFACE})`
- : isLoading
- ? SURFACE
- : SURFACE;
const stroke = isDiag
? LIME
: isLoading
From bf805463d2375fe7f38aff1eb9f36158428b02dc Mon Sep 17 00:00:00 2001
From: deaneeth
Date: Fri, 5 Jun 2026 21:54:44 +0530
Subject: [PATCH 7/7] fix(ci): isolate cocotb sim_build dirs to prevent stale
toplevel reuse
cocotb does not detect when TOPLEVEL changes between sequential make runs
sharing the same sim_build/ directory. The PE test compiled sim_build/Vtop
for module `pe`; the systolic array test then reused that binary and
crashed at runtime with "root handle systolic_array != pe".
Fix: pass SIM_BUILD=sim_build/ to each make invocation so every
test suite compiles into its own isolated directory.
---
.github/workflows/rtl.yml | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/rtl.yml b/.github/workflows/rtl.yml
index 3b78719..e92718e 100644
--- a/.github/workflows/rtl.yml
+++ b/.github/workflows/rtl.yml
@@ -56,16 +56,19 @@ jobs:
- name: Golden model tests
run: pytest sim/golden.py -q
+ # Each suite gets its own SIM_BUILD directory. cocotb does not detect a
+ # TOPLEVEL change across runs sharing the same sim_build/ — it reuses the
+ # previous binary, which causes "root handle not found" at runtime.
- name: cocotb — PE
working-directory: sim
- run: make MODULE=test_pe TOPLEVEL=pe WAVES=0
+ run: make MODULE=test_pe TOPLEVEL=pe WAVES=0 SIM_BUILD=sim_build/pe
- name: cocotb — Systolic Array
working-directory: sim
run: |
make MODULE=test_systolic_array TOPLEVEL=systolic_array \
VERILOG_SOURCES="../rtl/pe.sv ../rtl/systolic_array.sv" \
- WAVES=0
+ WAVES=0 SIM_BUILD=sim_build/systolic_array
- name: cocotb — Top (full matmul vs golden)
working-directory: sim
@@ -73,4 +76,4 @@ jobs:
make MODULE=test_top TOPLEVEL=tiny_tpu_top \
VERILOG_SOURCES="../rtl/pe.sv ../rtl/systolic_array.sv \
../rtl/controller.sv ../rtl/tiny_tpu_top.sv" \
- WAVES=0
+ WAVES=0 SIM_BUILD=sim_build/top