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 d8ccc9d494
commit ffc1c7ccfc
3 changed files with 88 additions and 41 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}), 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 \code{queue\_empty} is \code{neural\_director.v}'s own dispatch-queue
occupancy, and \code{any\_pending} (new) is an OR-reduce over occupancy, and \code{any\_pending} tracks whether any node is
dependency\_manager's own node table for any node still currently registered but not yet dispatched (\code{WAITING} or
\code{WAITING} or \code{READY} (i.e. registered but not yet \code{READY} --- \code{DISPATCHED} nodes are tracked by the two
dispatched --- \code{DISPATCHED} nodes are tracked by the two signals signals above instead, not here).
above instead, not here). \code{FPGA\_DATA\_READY} is a sticky
\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 register: set on the \code{sys\_busy} $1\to0$ edge, cleared the
instant \code{sys\_busy} goes high again --- self-clearing, no host instant \code{sys\_busy} goes high again --- self-clearing, no host
acknowledgement command needed. acknowledgement command needed.
@@ -105,18 +105,41 @@ CKE,CS\#,RAS\#,CAS\#,WE\#.
\end{fnnote} \end{fnnote}
\subsection{Real, measured clock closure (nextpnr-ecp5, 8 seeds/config)} \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} \begin{tabularx}{\textwidth}{L{4.0cm} C{1.6cm} C{2.2cm} X}
\toprule \toprule
\rowh \thd{Configuration} & \thd{Pass} & \thd{Worst Fmax} & \thd{Notes} \\ \rowh \thd{Configuration} & \thd{Pass} & \thd{Worst / Best Fmax} & \thd{Notes} \\
\midrule \midrule
N\_SLOTS=4 @ 64\,MHz & 8/8 & 66.58\,MHz & Production baseline, GO \\ N\_SLOTS=4 @ 64\,MHz & \textbf{8/8} & 64.55 / 72.37\,MHz & \textbf{Production baseline, GO} \\
\rowa N\_SLOTS=8 @ 64\,MHz & 5/8 & 60.12\,MHz & Open, not production-frozen \\ \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 \\ 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 \bottomrule
\end{tabularx} \end{tabularx}
Real, measured after the ERR-0029 weight-cache hit-index optimization Root cause of the last N\_SLOTS=4 failure (seed1, real critical-path
(serial priority scan $\to$ flat one-hot compare); see trace): \code{neural\_director.job\_out\_slot} $\to$
\code{hardware/v2/logs/errors.log} and \code{decisions.log} DEC-0040. \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 \section{Power supply design (2026-09-07) --- verified against the real
Lattice hardware checklist} 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.} FPGA--RAM/FLASH and PROG sections) pending separately.}
\end{center} \end{center}
\section{Programming architecture (2026-09-07) --- two independent \section{Programming architecture (updated 2026-09-07) --- single
flash devices, ESP32 over JTAG only} boot flash, ESP32 over JTAG only}
\label{sec:prog-addendum} \label{sec:prog-addendum}
\begin{fnwarn}[Real, closed design -- not a placeholder] \begin{fnwarn}[Real, closed design -- superseded once, now final]
Converged after evaluating and rejecting a single-shared-flash and an Originally converged on a two-flash design (\S below described flash
SSPI-every-boot alternative (see \code{decisions.log} DEC-0041 for the \#1 for neural-network data and flash \#2 for boot). Flash \#1 was
full comparison). This is the current, real architecture. 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} \end{fnwarn}
\subsection{Two physically separate flash chips} \subsection{One physical flash chip: boot bitstream only}
\begin{itemize} Connects exclusively to the ECP5's own dedicated sysCONFIG pins,
\item \textbf{Flash \#1} --- neural-network weights/graph data. Master SPI mode, auto-boots every power-up, zero ESP32 involvement in
V1's own real subsystem (\code{flash\_copy\_engine.v}/ normal operation. No second flash device, no on-board neural-network
\code{flash\_slot\_manager.v}), 4 ordinary GPIO balls. V1's own weight persistence in the current design --- the host (ESP32) is
real balls (\code{flash\_sclk}=E3, \code{flash\_mosi}=D3, responsible for pushing weight/activation data into SDRAM fresh each
\code{flash\_miso}=D5, \code{flash\_cs\_n}=E4) are \textbf{not} session via the real SPI application protocol
reusable in V2 --- confirmed conflict, all four already carry (\S\ref{sec:host-addendum}).
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{ESP32 $\leftrightarrow$ ECP5: JTAG only} \subsection{ESP32 $\leftrightarrow$ ECP5: JTAG only}
Neither ESP32-S3 nor ESP32-C6 has a hardware JTAG \emph{master} 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 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 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 tristate with a weak pull-up, so they never contend with another
driver on the same net --- not load-bearing for this specific driver on the same net --- not load-bearing for the current
two-chip architecture (flash \#1/\#2 are physically separate), but single-flash architecture (nothing else shares these pins), but
confirms the mechanism is real should a future revision ever share a confirms the mechanism is real should a future revision ever add a
single chip. second flash device sharing this same chip.