docs: reflect flash #1 removal and the N=4 8/8 timing recovery

Datasheet and pinouts.md updated to the current, real state after
today's session: flash #1 was implemented then removed (user's own
MHz-over-persistence priority call), the clock-closure table now
shows the post-revert, post-DEC-0042 numbers (N_SLOTS=4 @ 64MHz 8/8,
worst 64.55MHz/best 72.37MHz; N_SLOTS=8 deferred by explicit user
request), and the FPGA_DATA_READY any_pending formula is updated to
the real counter-based implementation (credited to the user's own
diagnosis) that fixed the last failing N=4 seed.

Compiled clean (45 pages, 0 errors).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013xXuuRUWZScuo1DeYJxs3v
This commit is contained in:
2026-09-07 19:20:57 +02:00
co-authored by Claude Sonnet 5
parent 481b5d223f
commit 44be4a7d81
4 changed files with 90 additions and 53 deletions
@@ -85,11 +85,35 @@ useful to a host waiting on a result:
\]
where \code{job\_active} is per-slot (already real, \S\ref{ch:sched}),
\code{queue\_empty} is \code{neural\_director.v}'s own dispatch-queue
occupancy, and \code{any\_pending} (new) is an OR-reduce over
dependency\_manager's own node table for any node still
\code{WAITING} or \code{READY} (i.e. registered but not yet
dispatched --- \code{DISPATCHED} nodes are tracked by the two signals
above instead, not here). \code{FPGA\_DATA\_READY} is a sticky
occupancy, and \code{any\_pending} tracks whether any node is
currently registered but not yet dispatched (\code{WAITING} or
\code{READY} --- \code{DISPATCHED} nodes are tracked by the two
signals above instead, not here).
\begin{fnwarn}[Updated 2026-09-07 --- \code{any\_pending} implementation changed]
The first real implementation computed \code{any\_pending} as a
combinational OR-reduce over \code{dependency\_manager}'s own
\code{node\_state[0:N\_NODES-1]} array every cycle. A real 8-seed
\code{nextpnr-ecp5} P\&R sweep later showed this adding genuine
fan-out onto \code{node\_state} --- a signal that also sits on this
project's own worst real critical path
(\code{neural\_director.job\_out\_slot} $\to$
\code{dependency\_manager.node\_resolved}/\code{node\_state}), costing
real Fmax margin (traced to a real 62.47\,MHz failing seed at
N\_SLOTS=4 \S64\,MHz --- see ch.~\ref{ch:hw} \S\ref{sec:clock-closure-current}).
Replaced (the user's own suggestion) with a synchronous up/down
counter: \code{pending\_count} increments on a node's own registration
acceptance (\code{reg\_valid\&\&reg\_ready}) and decrements on its own
dispatch acceptance (\code{ready\_valid\&\&ready\_ready}); \code{any\_pending
= (pending\_count != 0)}. Mathematically identical to the original
OR-reduce (nodes are never reclaimed mid-run, ch.~\ref{ch:sched}), but
reads one small registered counter instead of scanning a 16-wide array
every cycle --- zero added fan-out on the congested signal. Recovered
the last failing N\_SLOTS=4 seed (62.47 $\to$ 64.55\,MHz), closing
8/8. See \code{decisions.log} DEC-0042.
\end{fnwarn}
\code{FPGA\_DATA\_READY} is a sticky
register: set on the \code{sys\_busy} $1\to0$ edge, cleared the
instant \code{sys\_busy} goes high again --- self-clearing, no host
acknowledgement command needed.
@@ -105,18 +105,41 @@ CKE,CS\#,RAS\#,CAS\#,WE\#.
\end{fnnote}
\subsection{Real, measured clock closure (nextpnr-ecp5, 8 seeds/config)}
\label{sec:clock-closure-current}
\begin{fnwarn}[Updated 2026-09-07 --- supersedes the ERR-0029-era numbers below]
Flash \#1 (\S\ref{sec:prog-addendum}, since removed) briefly regressed
N\_SLOTS=4 from 8/8 to 3/8 while it was integrated; that integration
was reverted (user priority: clock frequency over on-board flash
persistence). A further real fix (DEC-0042, replacing a combinational
fan-out with a synchronous counter, credited to the user's own
diagnosis) closed N\_SLOTS=4 back to 8/8 on the flash-free design ---
the numbers below are the CURRENT, real, final state.
\end{fnwarn}
\begin{tabularx}{\textwidth}{L{4.0cm} C{1.6cm} C{2.2cm} X}
\toprule
\rowh \thd{Configuration} & \thd{Pass} & \thd{Worst Fmax} & \thd{Notes} \\
\rowh \thd{Configuration} & \thd{Pass} & \thd{Worst / Best Fmax} & \thd{Notes} \\
\midrule
N\_SLOTS=4 @ 64\,MHz & 8/8 & 66.58\,MHz & Production baseline, GO \\
\rowa N\_SLOTS=8 @ 64\,MHz & 5/8 & 60.12\,MHz & Open, not production-frozen \\
N\_SLOTS=4/8 @ 80\,MHz & 0/8 & --- & NO-GO, genuine \code{ecppll}-regenerated PLL \\
N\_SLOTS=4 @ 64\,MHz & \textbf{8/8} & 64.55 / 72.37\,MHz & \textbf{Production baseline, GO} \\
\rowa N\_SLOTS=8 @ 64\,MHz & 3/8 & --- & Deferred by explicit user request, not pursued further \\
N\_SLOTS=4/8 @ 80\,MHz & 0/8 & --- & NO-GO, genuine \code{ecppll}-regenerated PLL (re-confirmed pre-revert; not re-tested post-revert, expected unchanged) \\
\bottomrule
\end{tabularx}
Real, measured after the ERR-0029 weight-cache hit-index optimization
(serial priority scan $\to$ flat one-hot compare); see
\code{hardware/v2/logs/errors.log} and \code{decisions.log} DEC-0040.
Root cause of the last N\_SLOTS=4 failure (seed1, real critical-path
trace): \code{neural\_director.job\_out\_slot} $\to$
\code{dependency\_manager.node\_resolved}/\code{node\_state}, a
producer-completion broadcast crossing physically distant regions of
the die (75--84\% routing, not a serial logic chain --- already a
flat, parallel 64-way compare, so the ERR-0027/0028/0029 restructuring
fix class does not apply here). The real contributor found: this
chapter's own \code{FPGA\_DATA\_READY} support (\S\ref{sec:host-addendum})
read \code{node\_state[0:N\_NODES-1]} combinationally every cycle,
adding real fan-out onto that same congested signal. Fixed by
replacing the OR-reduce with a synchronous up/down counter (see
\S\ref{sec:host-addendum} for the exact formula) --- worst seed
improved 62.47\,MHz $\to$ 64.55\,MHz, closing the last failing seed.
See \code{decisions.log} DEC-0042 for full detail. A further
pipelining fix on the same broadcast path is a real, identified,
not-yet-attempted option if more margin is ever needed.
\section{Power supply design (2026-09-07) --- verified against the real
Lattice hardware checklist}
@@ -246,34 +269,34 @@ FPGA-TN-02038-2.0 \S3--4. Full schematic capture (BOM, connectors,
FPGA--RAM/FLASH and PROG sections) pending separately.}
\end{center}
\section{Programming architecture (2026-09-07) --- two independent
flash devices, ESP32 over JTAG only}
\section{Programming architecture (updated 2026-09-07) --- single
boot flash, ESP32 over JTAG only}
\label{sec:prog-addendum}
\begin{fnwarn}[Real, closed design -- not a placeholder]
Converged after evaluating and rejecting a single-shared-flash and an
SSPI-every-boot alternative (see \code{decisions.log} DEC-0041 for the
full comparison). This is the current, real architecture.
\begin{fnwarn}[Real, closed design -- superseded once, now final]
Originally converged on a two-flash design (\S below described flash
\#1 for neural-network data and flash \#2 for boot). Flash \#1 was
fully implemented (real V1 subsystem instantiated, a new byte$\leftrightarrow$word
adapter, a new SPI opcode, a dedicated testbench, 64/64 bytes verified
bit-exact) and then \textbf{removed again}, on the user's own explicit
priority call: it measurably regressed N\_SLOTS=4's own real timing
closure (8/8 $\to$ 3/8 PASS at 64\,MHz), and clock frequency was
judged more valuable than on-board persistent weight storage --- the
ESP32 can push weights fresh each session instead. Reverted cleanly
via \code{git revert} (commit \code{59901a4}, fully recoverable from
history if ever needed again). This section now describes the
current, real, single-flash architecture. See \code{decisions.log}
DEC-0041 (original two-flash design) and DEC-0042 (removal + the
timing recovery that followed) for the complete history.
\end{fnwarn}
\subsection{Two physically separate flash chips}
\begin{itemize}
\item \textbf{Flash \#1} --- neural-network weights/graph data.
V1's own real subsystem (\code{flash\_copy\_engine.v}/
\code{flash\_slot\_manager.v}), 4 ordinary GPIO balls. V1's own
real balls (\code{flash\_sclk}=E3, \code{flash\_mosi}=D3,
\code{flash\_miso}=D5, \code{flash\_cs\_n}=E4) are \textbf{not}
reusable in V2 --- confirmed conflict, all four already carry
V2's own SDRAM bus. New balls reserved (bank~7, 3.3\,V):
\code{flash\_sclk}=B2, \code{flash\_mosi}=E2,
\code{flash\_miso}=F2, \code{flash\_cs\_n}=F3. \textbf{Not yet
in the LPF} --- the RTL port does not exist in
\code{fpga\_neural\_v2\_top.v} yet (a real, separate, open
integration task).
\item \textbf{Flash \#2} --- boot bitstream only. Connects
exclusively to the ECP5's own dedicated sysCONFIG pins, Master
SPI mode, auto-boots every power-up, zero ESP32 involvement in
normal operation.
\end{itemize}
\subsection{One physical flash chip: boot bitstream only}
Connects exclusively to the ECP5's own dedicated sysCONFIG pins,
Master SPI mode, auto-boots every power-up, zero ESP32 involvement in
normal operation. No second flash device, no on-board neural-network
weight persistence in the current design --- the host (ESP32) is
responsible for pushing weight/activation data into SDRAM fresh each
session via the real SPI application protocol
(\S\ref{sec:host-addendum}).
\subsection{ESP32 $\leftrightarrow$ ECP5: JTAG only}
Neither ESP32-S3 nor ESP32-C6 has a hardware JTAG \emph{master}
@@ -311,7 +334,7 @@ D1/MISO=V2.
Confirmed real and safe (Lattice FPGA-TN-02039-2.3 sysCONFIG User
Guide, \S6.1.2): once User Mode is reached, the MSPI dedicated pins
tristate with a weak pull-up, so they never contend with another
driver on the same net --- not load-bearing for this specific
two-chip architecture (flash \#1/\#2 are physically separate), but
confirms the mechanism is real should a future revision ever share a
single chip.
driver on the same net --- not load-bearing for the current
single-flash architecture (nothing else shares these pins), but
confirms the mechanism is real should a future revision ever add a
second flash device sharing this same chip.
+2 -12
View File
@@ -83,19 +83,9 @@ W2 PB11B: D0/MOSI/IO0 D0/MOSI verso flash #2
V2 PB11A: D1/MISO/IO1 D1/MISO verso flash #2
U3 CCLK (vedi sopra) MCLK verso flash #2, pull-up debole interna
Questi 4 ball (insieme a PROGRAMN/INITN/DONE sopra) collegano l'FPGA esclusivamente alla flash #2 (boot)MAI alla flash #1 (dati rete neurale), che resta su un bus GPIO ordinario separato (sotto).
Questi 4 ball (insieme a PROGRAMN/INITN/DONE sopra) collegano l'FPGA esclusivamente alla flash di boot — unico chip flash presente nel design attuale.
Flash #1 (dati rete neurale) — ball riservati, RTL non ancora presente in V2
**Importante**: i ball reali usati da V1 per questo stesso bus (`flash_sclk`=E3, `flash_mosi`=D3, `flash_miso`=D5, `flash_cs_n`=E4) sono **già occupati in V2** dal bus SDRAM (E3=sdram_a[4], D3=sdram_a[1], D5=sdram_a[0], E4=sdram_ba[0]) — non riutilizzabili, V2 ha una geometria pin diversa da V1. Individuati 4 ball liberi alternativi, stesso banco 7 (stessa tensione 3.3V del resto del bus SDRAM):
Ball Funzione dual (libera, riusabile come GPIO ordinario)
B2 VREF1_7 (non serve, nessuno standard I/O riferito a VREF in uso)
E2 PCLKC7_0 (ingresso PLL non usato, riusabile come GPIO)
F2 PCLKT7_0 (ingresso PLL non usato, riusabile come GPIO)
F3 PCLKC7_1 (ingresso PLL non usato, riusabile come GPIO)
Assegnazione proposta: `flash_sclk`=B2, `flash_mosi`=E2, `flash_miso`=F2, `flash_cs_n`=F3. **Non ancora nel LPF**: `flash_copy_engine.v`/`flash_slot_manager.v` (RTL V1 reale) non sono stati portati nel top-level di V2 — aggiungere un `LOCATE COMP` per questi segnali ora romperebbe la sintesi, dato che non esiste ancora una porta corrispondente in `fpga_neural_v2_top.v`. Riservati qui solo come ball, in attesa dell'integrazione RTL.
**Aggiornamento 2026-09-07 — Flash #1 (dati rete neurale) rimossa**: era stata realmente integrata (RTL V1 `flash_copy_engine.v`/`flash_slot_manager.v` istanziato, adapter nuovo, opcode SPI dedicato, testbench dedicato, verificata bit-exact) sui ball B2/E2/F2/F3 (banco 7). **Rimossa di nuovo** su scelta esplicita dell'utente: degradava il timing reale di N_SLOTS=4 (8/8→3/8 PASS a 64MHz) e la frequenza di clock è stata giudicata più importante della persistenza locale dei pesi — l'ESP32 può ricaricarli ad ogni sessione via il protocollo SPI applicativo. Revert pulito (`git revert`, commit `59901a4`, recuperabile in futuro). I ball B2/E2/F2/F3 sono di nuovo liberi (nessuna funzione riservata). Vedi `decisions.log` DEC-0041 (design originale) e DEC-0042 (rimozione + recupero del timing).
Decoupling