Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion docs/NOW.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
# NOW — deeper net gated (2,5,3,1) + a without-Vivado open-flow refinement (2026-08-08)
# NOW — CI gates 3-input scaling + a free-Vivado cloud runbook (2026-08-08)

Last updated: 2026-08-08

## test+docs: gate 3-input scaling learns + a cloud runbook for free Vivado closure (Refs #1764)

- V2 (stack): the learning gate covered only 2-input tasks (XOR-like). Added a 3-INPUT (3,5,1) net learning a noisy 3-feature majority task, held-out 55/60 (92%), deterministic -- so CI now proves the generator scales along the INPUT dimension, not just depth/width. (3-way parity was too hard for a small net at 72%; majority is the clean, reliable demonstrator.)
- V1 (Vivado path): added `docs/vivado_closure/CLOUD_RUNBOOK.md` -- concrete steps to run the Vivado closure kit on free Vivado ML Standard on an x86-64 Linux box or cloud instance (AWS t3.xlarge / GCP e2-standard-4, build-only, no FPGA on the cloud host; copy the .bit back and flash on the Mac). Free Vivado supports xc7a200t; it just doesn't run on macOS
- Context: the open flow ALREADY trains XOR on silicon (met-timing + seed-search); Vivado is only for deterministic no-seed-search closure + nets > XOR. Board on generated capstone (XOR 4/4). Refs #1764

## test+docs: CI now gates a deeper [2,5,3,1] net learns; met-timing is a partial without-Vivado refinement (Refs #1764)

- V2 (stack): added a deeper [2,5,3,1] (3-layer, 158-step) learning assertion to the generator self-test / CI learning gate -- held-out 59/60 (98%), deterministic. Extends the proven "the method scales beyond XOR" claim to a deeper+wider net
Expand Down
71 changes: 71 additions & 0 deletions docs/vivado_closure/CLOUD_RUNBOOK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Running the Vivado closure kit on free Linux (cloud or a spare box)

Vivado does not run on macOS (arm64 or x86). The **free** edition — *Vivado ML Standard*
(formerly WebPACK) — **does support the AX7203's `xc7a200t`** and runs on **x86-64 Linux or
Windows**. This is the one-time setup to close the deep path deterministically (no
seed-search) and unlock nets larger than XOR. Everything here is off the critical path — the
open flow already trains XOR on silicon via `met-timing + seed-search` (see the methodology).

## What you need
- An **x86-64 Linux** host: a spare PC/VM, or a cloud instance (see sizing below). ~80 GB
free disk, ≥ 8 GB RAM (16 GB comfortable), no GPU needed.
- A free **AMD/Xilinx account** (account.amd.com) to download Vivado.
- The board RTL (this repo's generated core + the working UART wrapper) and this kit.

## Cloud sizing (if you don't have a Linux box)
- **AWS:** `t3.xlarge` (4 vCPU / 16 GB) or `c5.2xlarge` (faster synth), Ubuntu 22.04, a
100 GB gp3 root volume. Synthesis of this small design is minutes; the cost is the
~1-hour install + download, so a spot instance is fine. Terminate when done.
- **GCP:** `e2-standard-4` (4 vCPU / 16 GB), Ubuntu 22.04, 100 GB balanced disk.
- Bitstream generation is **build-only** — you do **not** need the FPGA attached to the
cloud host. Copy the resulting `.bit` back to the Mac and flash it there over JTAG.

## Install Vivado ML Standard (free)
1. Download the **"AMD Unified Installer for FPGAs & Adaptive SoCs" (Linux Self Extracting
Web Installer)** from the AMD downloads page (needs the free account).
2. On the Linux host:
```bash
sudo apt-get update && sudo apt-get install -y libtinfo5 libncurses5 default-jre # common deps
chmod +x FPGAs_AdaptiveSoCs_Unified_*_Lin64.bin
./FPGAs_AdaptiveSoCs_Unified_*_Lin64.bin
```
3. In the installer: sign in → choose **Vivado** → **Vivado ML Standard** (the free tier,
not Enterprise) → on the device screen make sure **Artix-7** (7-series) is selected
(that pulls in `xc7a200t`). Install to e.g. `/tools/Xilinx`.
4. Source the settings each shell:
```bash
source /tools/Xilinx/Vivado/*/settings64.sh
vivado -version # confirm it runs
```

## Build the bitstream
Put the RTL next to this kit and run the batch script:
```bash
# from docs/vivado_closure/ (copy the working board RTL in — paths as in your board dir)
cp /path/to/board/bpseq_capstone.v bpseq.v
cp /path/to/board/uart_bpseq.v /path/to/board/gft_smul.v /path/to/board/gft_sadd.v .
vivado -mode batch -source vivado_build.tcl
```
The script prints the worst setup slack and writes `bpseq_vivado.bit`. **Expect WNS ≥ 0**
— all paths met, including the shared-core `rf→rf` path relaxed by the
`set_multicycle_path` in `bpseq_vivado.xdc`. If `timing_summary.rpt` shows a violation,
loosen the board `create_clock` period (the design tolerates a slower clock — the `settle`
window covers it) or raise the multicycle factor; it is not seed-dependent.

## Flash & verify (back on the Mac)
```bash
scp user@host:.../bpseq_vivado.bit .
openFPGALoader -c digilent_hs2 --busdev-num 002:002 bpseq_vivado.bit
python3 board/drive_bpseq.py /dev/cu.usbserial-2120
```
Success looks like the open-flow good seed — XOR climbs to 4/4 and stays — but now it is
**deterministic**: no seed-search, and the same result every build. That determinism is the
prerequisite for training the larger nets (the `(2,4,1)`+ topologies) where open-flow
seed-search runs out.

## Why bother, given the open flow already works
- Open flow **today**: trains XOR on silicon via met-timing + seed-search (works, but you
hunt a good seed and it caps around XOR-scale nets).
- Vivado: **deterministic** closure (no seed hunt) **and** headroom for bigger nets — it can
*hear* the `set_multicycle_path` constraint that `nextpnr-xilinx` cannot express. This
turns the one documented open-toolchain limit into a solved problem.
28 changes: 28 additions & 0 deletions tools/gft_backprop_microcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,34 @@ def _predw(a, b):
te = sum(1 for a, b, t in tew if _predw(a, b) == t)
assert te >= int(0.9 * len(tew)), f"deep [2,5,3,1] held-out too low: {te}/{len(tew)}"
print(f"self-test: deep [2,5,3,1] (158 steps) learns nonlinear task, held-out {te}/{len(tew)} (>=90%) -- OK")
# input-dimension scaling: a 3-INPUT net (3,5,1) learns a noisy 3-feature task (majority
# of signs), proving the generator scales along inputs, not only depth/width.
reg, steps = gen(3, 5, 1); rf = [0] * len(reg)
random.seed(3)
for j in range(5):
for k in range(3): rf[reg[f"W{j}_{k}"]] = enc(round(random.uniform(-0.8, 0.8), 3))
rf[reg[f"b{j}"]] = enc(round(random.uniform(-0.5, 0.5), 3))
for j in range(5): rf[reg[f"v0_{j}"]] = enc(round(random.uniform(-0.8, 0.8), 3))
def _maj(a, b, c): return int((int(a > 0) + int(b > 0) + int(c > 0)) >= 2)
def _ds3(n, seed):
random.seed(seed); d = []
while len(d) < n:
a, b, c = random.uniform(-1, 1), random.uniform(-1, 1), random.uniform(-1, 1)
if min(abs(a), abs(b), abs(c)) < 0.15: continue
d.append((a, b, c, _maj(a, b, c)))
return d
tr3, te3 = _ds3(200, 7), _ds3(60, 99)
def _pred3(a, b, c):
sav = rf[:]; rf[reg["x0"]] = enc(a); rf[reg["x1"]] = enc(b); rf[reg["x2"]] = enc(c); rf[reg["t0"]] = 0
run(steps, rf); y = dec(rf[reg["y0"]])
for i in range(len(rf)): rf[i] = sav[i]
return int(y > 0.5)
for _ in range(80):
for a, b, c, t in tr3:
rf[reg["x0"]] = enc(a); rf[reg["x1"]] = enc(b); rf[reg["x2"]] = enc(c); rf[reg["t0"]] = enc(float(t)); run(steps, rf)
te = sum(1 for a, b, c, t in te3 if _pred3(a, b, c) == t)
assert te >= int(0.9 * len(te3)), f"3-input (3,5,1) held-out too low: {te}/{len(te3)}"
print(f"self-test: 3-input (3,5,1) learns a noisy 3-feature task, held-out {te}/{len(te3)} (>=90%) -- OK")
vd = emit_verilog_deep([2, 4, 3, 1], "deep431")
assert "module deep431" in vd and "for(gi=0;gi<" in vd
print("emit_verilog_deep: [2,4,3,1] module generated -- OK")
Expand Down
Loading