Files
FPGA-Neural/docs/PHYSICAL_REALIZATION.md
T
micheleandClaude Sonnet 5 43a12379a5 feat: MILESTONE - real activation-fetch engine, full N=2 system verified on real DDR3 (EXP-0079)
Closes the last major disclosed functional gap: packed_slot.v's
activation data was read through a combinational stand-in since
EXP-0062. New act_tile_fetch.v reads activation tiles directly from
DDR3 (no on-chip buffering needed, unlike weights -- activation data
has no reuse), sharing each slot's existing ctrl port with its own
weight-prefetch engine. Real memory layout: one full BURST_LEN=8-word
burst per tile, deliberately avoiding any runtime-indexed part-select
given this project's thin P&R timing margin (EXP-0078).

Verified at three levels: act_tile_fetch.v alone (6/6), packed_slot.v
with real preloaded activation data (9/9), and the full N=2 system
against real DDR3 via xsim (8/8, 0 errors) -- the first time this
project's compute path has been verified end-to-end with real DDR3
for both weights and activations.

Retired hardware/v3/rtl/n2_system_top.v and its testbench (pre-DDR3
SDR-placeholder era, fully superseded by n2_system_ddr3_top.v).

Also: docs/PHYSICAL_REALIZATION.md (real pinout/parts/timing/protocol
reference for the physical board) and CLAUDE.md (persistent project
instructions for future Claude Code sessions).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
2026-09-20 08:13:15 +02:00

226 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# FPGA-Neural V3 — Physical Realization Reference
Real, verified data for the custom PCB (bare XC7A100T-CSG324-2 + DDR3, no dev
board). Every pin/part/setting below comes from a real Vivado-generated
constraint file, a real datasheet, or a real place-and-route run — none of it
is guessed. See `hardware/v2/logs/experiments.log` (EXP-0059 onward) for the
full derivation history.
## 1. Core components (real, verified availability)
| Component | Part | Notes |
|---|---|---|
| FPGA | **XC7A100T-CSG324-2** | Speed grade **-2** (corrected from an initial -1 assumption, EXP-0074) — same die/package/footprint as -1, strictly better timing margin. |
| DDR3 SDRAM | **Micron MT41J128M16JT-125:K** | 2Gb, x16, DDR3-1600-rated (run at 310.078MHz here due to -2 timing closure, see §3). Verified in-stock on LCSC. |
| Config flash | **Winbond W25Q32JVSSIQ** | 32Mbit/4MB, SOIC-8. Comfortably fits the ~30.5Mbit full XC7A100T bitstream. Verified in-stock on LCSC. Wired **exclusively** to the FPGA (see §5). |
## 2. FPGA pin assignments (real, from the routed design)
### 2.1 DDR3 (fixed by the FPGA's own PHY hardware — not a free choice)
Generated by the Vivado MIG wizard (`mig_7series_0.xdc`), all `SSTL15` /
`DIFF_SSTL15` (1.5V), banks 34/35:
| Signal | Pin | Signal | Pin | Signal | Pin |
|---|---|---|---|---|---|
| ddr3_dq[0] | G4 | ddr3_dq[8] | M1 | ddr3_addr[0] | B1 |
| ddr3_dq[1] | G3 | ddr3_dq[9] | K3 | ddr3_addr[1] | A3 |
| ddr3_dq[2] | J3 | ddr3_dq[10] | L3 | ddr3_addr[2] | A4 |
| ddr3_dq[3] | J2 | ddr3_dq[11] | M3 | ddr3_addr[3] | B4 |
| ddr3_dq[4] | K2 | ddr3_dq[12] | M2 | ddr3_addr[4] | C4 |
| ddr3_dq[5] | K1 | ddr3_dq[13] | K5 | ddr3_addr[5] | E7 |
| ddr3_dq[6] | H6 | ddr3_dq[14] | L4 | ddr3_addr[6] | E5 |
| ddr3_dq[7] | H5 | ddr3_dq[15] | L6 | ddr3_addr[7] | E6 |
| ddr3_addr[8] | C7 | ddr3_addr[9] | D8 | ddr3_addr[10] | B6 |
| ddr3_addr[11] | B7 | ddr3_addr[12] | C5 | ddr3_addr[13] | C6 |
| ddr3_ba[0] | B2 | ddr3_ba[1] | B3 | ddr3_ba[2] | A1 |
| ddr3_ras_n | D5 | ddr3_cas_n | D4 | ddr3_we_n | E3 |
| ddr3_reset_n | F6 (LVCMOS15) | ddr3_cke[0] | D7 | ddr3_odt[0] | H2 |
| ddr3_cs_n[0] | D3 | ddr3_dm[0] | G6 | ddr3_dm[1] | L1 |
| ddr3_dqs_p[0] | J4 (DIFF) | ddr3_dqs_n[0] | H4 (DIFF) | | |
| ddr3_dqs_p[1] | N2 (DIFF) | ddr3_dqs_n[1] | N1 (DIFF) | | |
| ddr3_ck_p[0] | A6 (DIFF) | ddr3_ck_n[0] | A5 (DIFF) | | |
| sys_clk_i | E2 (SSTL15, bank 35) | clk_ref_i | C9 (LVCMOS25, bank 16) | | |
**Bank voltage requirements**: bank 34/35 → **1.5V** (DDR3 SSTL15), bank 16 →
**2.5V** (clk_ref_i, LVCMOS25).
`INTERNAL_VREF` for banks 34/35 is set to 0.750V by the MIG constraints
(required for SSTL15 single-ended inputs) — this is a Vivado-side setting,
not a board component, but note it if you ever inspect bitstream generation
warnings about VREF.
### 2.2 Neural-processor management SPI (ESP32 ↔ FPGA, FPGA is **slave**)
Bank 15, package edge column A/B, physically adjacent (short traces), `LVCMOS33`:
| Signal | Pin | Direction (FPGA side) |
|---|---|---|
| sclk | A15 | input |
| mosi | B16 | input |
| miso | B17 | output |
| cs_n | A16 | input |
**Bank 15 VCCO**: assumed **3.3V** — change the XDC's IOSTANDARD if your
board power plan uses a different rail for this bank.
### 2.3 Config flash SPI (FPGA ↔ flash, FPGA is **master**)
These are the FPGA's own dedicated Master-SPI configuration pins, **reclaimed
as ordinary fabric I/O after configuration completes** (requires
`BITSTREAM.CONFIG.PERSIST = FALSE`, the Vivado default — already set
explicitly in the project XDC). Bank 14, `LVCMOS33`:
| Signal | Pin | Direction (FPGA side) | Notes |
|---|---|---|---|
| flash_mosi | K17 | output | = D00_MOSI (config pin, reclaimed) |
| flash_miso | K18 | input | = D01_DIN (config pin, reclaimed) |
| flash_cs_n | L13 | output | = FCS_B (config pin, reclaimed) |
| (CCLK) | E9 | output | **Not a top-level port** — driven internally via the `STARTUPE2` primitive. Wire the flash's own CLK pin to package pin **E9**. |
**Bank 14 VCCO**: assumed **3.3V** (matches the flash's own VCC, typically
1.83.6V for the W25Q32JV — check its datasheet's exact operating range
against whatever VCCO you choose for bank 14).
**Reserved, do not use** (bank 14, same reasons as above but unused by this
design — kept clear for any future Quad-SPI/BPI expansion): `L16` (EMCCLK),
`R16` (RDWR_B), `V15` (CSI_B). The project's own XDC `PROHIBIT`s these so
Vivado's auto-placement never claims them by accident.
### 2.4 FPGA configuration control (dedicated, bank 0, not negotiable)
| Signal | Pin | Purpose |
|---|---|---|
| PROGRAM_B | P9 | pulse low to force a full reconfiguration from flash |
| INIT_B | P7 | goes low during config; can indicate a config error if it re-asserts |
| DONE | P10 | goes high once configuration succeeds — wire to a status LED if desired |
| M0 | P12 | mode select |
| M1 | P13 | mode select |
| M2 | P11 | mode select |
| CFGBVS | P8 | tie to match bank 0's VCCO logic level (see UG470) |
**Mode pin setting for Master SPI boot** (the flash-based autonomous boot
path, see §5): `M[2:0] = 001` (per UG470's mode pin table) — tie via pull-up/
pull-down resistors on the board, not driven dynamically.
### 2.5 JTAG (always available, independent of flash content)
| Signal | Pin |
|---|---|
| TCK | E10 |
| TDI | E11 |
| TMS | E12 |
| TDO | E13 |
Used for: (a) first-ever/factory programming when the flash is blank (see
§5), (b) recovery, (c) development/debug. This project's own plan drives
these from an ESP32 doing real JTAG bit-banging (TAP state machine, IR/DR
shifting) rather than a bench programmer — that firmware is separate,
software-side work, not covered here.
## 3. Real timing signoff (EXP-0078, the current, trustworthy number)
Real in-context Vivado place-and-route (not out-of-context, not estimated):
| Metric | Value |
|---|---|
| DDR3 PHY clock (sys_clk_i) | **310.078 MHz** (3.225ns period) |
| Compute domain clock (ui_clk, PLL-derived 2:1 from sys_clk_i) | **155.039 MHz** |
| WNS (setup slack) | **+0.013 ns** — real, but very thin. Re-verify with a fresh P&R after ANY further logic addition. |
| WHS (hold slack) | +0.032 ns |
| Failing endpoints | 0 / 17473 (setup), 0 / 17470 (hold) |
| LUTs used | 5213 / 63400 (8.22%) |
| DSP48E1 used | 16 / 240 (6.67%) — 8 per compute core × 2 cores |
| Block RAM used | 0 |
| STARTUPE2 used | 1 / 1 (100%) — the config-flash bridge |
## 4. Real DDR3 memory layout convention
Both weight data and activation data share the same DDR3 address space
(word-addressed, 16-bit words, `BURST_LEN=8` per transaction = 128 bits/burst).
- **Weights**: one layer's weight set starts at word address `layer_index *
WORDS_PER_LAYER` (`WORDS_PER_LAYER = LAYER_BYTES/2`). Densely packed —
`layer_prefetch_ctrl.v` reads full bursts sequentially into the on-chip
weight buffer once per job.
- **Activations** (real engine since EXP-0079, `act_tile_fetch.v`): **each
tile (P_IN=8 INT8 values) occupies its own full `BURST_LEN=8`-word
(128-bit) burst slot** — the 8 useful bytes sit in the low 64 bits, the
upper 64 bits are unused padding. This is deliberately 2× wasteful of DDR3
capacity, in exchange for needing zero runtime-indexed bit-selects in the
fetch logic (a real Fmax risk this project's thin P&R margin, §3, can't
currently afford). Tile `t`'s word address is `base + t*BURST_LEN`, always
burst-aligned by construction.
- `base` (a job's own `x_base_a`/`x_base_b`) is chosen freely by whoever
submits jobs (the SPI host) — just keep each position's own activation
array in its own non-overlapping `N_TILES * BURST_LEN`-word region.
## 5. FPGA configuration (boot) procedure
Two complementary paths, both present on this board by design:
1. **Factory-first / recovery (JTAG, ESP32-driven)**: the flash starts
blank on a fresh board — no other path can bootstrap it (a real chicken-
and-egg constraint: the FPGA can't relay flash-programming commands over
SPI, §5.2, until it's already running logic that does that). The ESP32
bit-bangs JTAG (§2.5) to load a bitstream directly, or to run Vivado's own
"indirect SPI flash programming" sequence to write the flash for the
first time. One-time (or rare/recovery-only) step.
2. **Normal boot (Master SPI, autonomous)**: every subsequent power-on, the
FPGA self-configures from the flash via its own dedicated hardware (mode
pins set to Master SPI, §2.4) — no ESP32 involvement needed.
3. **Field firmware updates (SPI-through-FPGA, `FLASH_XFER` opcode 0x40)**:
once the FPGA is running, the ESP32 can rewrite the flash by relaying raw
SPI-NOR bytes through the FPGA over the management SPI bus (§2.2) — the
FPGA then re-transmits them as master on the flash bus (§2.3). This is the
**only** electrical path from ESP32 to the flash; there is no direct
connection (by design, per explicit requirement).
- **Real SPI-NOR opcodes** (verified against the actual W25Q32JV
datasheet, for whoever writes the ESP32-side flashing routine):
`0x06` Write Enable, `0x04` Write Disable, `0x05` Read Status Register-1
(bit0=BUSY, bit1=WEL), `0x02` Page Program, `0x03` Read Data, `0x20`
Sector Erase (4KB), `0x52` 32KB Block Erase, `0xD8` 64KB Block Erase,
`0xC7`/`0x60` Chip Erase.
- **Protocol timing note**: `FLASH_XFER` relays are NOT instantaneous —
each relayed byte's real flash response is only stable starting **two**
host-clocked bytes later (not one), so the host must clock 2 trailing
dummy bytes after its last real command byte to safely receive the
final response. See `spi_host_bridge_v3.v`'s own header for the full
real-measured reasoning (EXP-0077).
- After writing a new bitstream to the flash, reconfigure either by
pulsing `PROGRAM_B` externally, or (future work, not built yet) via a
`ICAPE2`-based warm self-reconfiguration triggered over the same SPI bus.
## 6. Management SPI protocol summary (for ESP32 firmware)
One opcode byte (MSB-first) per CS-low transaction, driven by
`spi_host_bridge_v3.v`:
| Opcode | Name | Payload | Purpose |
|---|---|---|---|
| 0x00 | NOP | 0 bytes | inert |
| 0x0F | RESET | 0 bytes | pulses a soft-reset |
| 0x10 | WRITE_JOB | 16 bytes | submit one inference job (node_id, x_base, w_base, n_tiles, result_addr) |
| 0x20 | STATUS | 0 bytes → 1 byte out | job_busy / mem_busy / last_job_accepted bits |
| 0x01 | WRITE_MEM | 4+2N bytes | raw DDR3 word write (N words) |
| 0x02 | READ_MEM | 6 bytes → 2N bytes out | raw DDR3 word read (N words) |
| 0x30 | REG_WRITE | 5 bytes | write a control register |
| 0x31 | REG_READ | 1 byte → 4 bytes out | read a status/ID register (0x00 DEVICE_ID, 0x01 CONTROL, 0x02 STATUS incl. DDR3-ready + Director-error, 0x03 N_SLOTS) |
| 0x40 | FLASH_XFER | N bytes → N bytes out (+2 margin) | raw passthrough to the config flash, see §5.3 |
Full byte-level field layouts are documented in `spi_host_bridge_v3.v`'s own
header comment — treat that file as the authoritative protocol spec, this
table is a summary/index.
## 7. Known-open items (honestly disclosed, not hidden)
- Scaling past N=2 compute cores (silicon budget allows up to ~30 per the
DSP48E1 count) is not yet built or timing-verified.
- The reset pin and other very-low-pin-count signals have no fixed PCB
location yet — assign once the rest of the board layout (reset circuit,
status LEDs, etc.) is decided.
- The §3 timing margin (+0.013ns) is real but thin — do not add logic
without a fresh real P&R to confirm it still closes.
- ESP32-side JTAG bit-banging firmware (§5.1) does not exist yet — it's
software work on the host side, not part of this FPGA RTL.