\chapter{Host / graph-loader interface} \label{ch:host} \begin{fnwarn}[Scope of this chapter] V1's own host interface is a real, placed, physically-verified SPI Mode~0 slave (ch.~7 of the V1 datasheet). V2's equivalent --- a node-registration bus into \code{neural\_multiprocessor.v} --- has, in this revision, been exercised exclusively from Verilator testbenches and unconstrained synthesis top-levels. This chapter describes the \textbf{logical} protocol only; no real host-side driver (SPI or otherwise) has been built or placed yet. See ch.~\ref{ch:roadmap}. \end{fnwarn} \section{Node registration protocol} A simple valid/ready producer interface, backpressure-safe: the loader holds \code{reg\_valid} and the node's own fields until \code{reg\_ready} is observed high on the same cycle, exactly like registering into any FIFO. \code{reg\_ready} for a given \code{reg\_node\_id} is asserted whenever that node's own table slot is \code{EMPTY} (\S\ref{ch:sched}). \begin{tabularx}{\textwidth}{L{3.2cm} C{1.6cm} Y} \toprule \rowh \thd{Field} & \thd{Width} & \thd{Meaning} \\ \midrule \code{reg\_node\_id} & $\lceil\log_2\text{N\_NODES}\rceil$ & This node's own id --- doubles as its table slot index. \\ \rowa \code{reg\_required} & $\lceil\log_2(\text{MAX\_DEPS}{+}1)\rceil$ & How many of \code{reg\_producer\_ids} are meaningful (0 $\Rightarrow$ immediately \code{READY}). \\ \code{reg\_producer\_ids} & \code{MAX\_DEPS}$\times\lceil\log_2\text{N\_NODES}\rceil$ & Packed array of producer node ids this node depends on. \\ \rowa \code{reg\_x\_base} & \code{ADDR\_WIDTH} & Base byte address of this node's activation vector. \\ \code{reg\_w\_base} & \code{ADDR\_WIDTH} & Base byte address of this node's weight vector. \\ \rowa \code{reg\_n\_tiles} & 16 & Number of P\_IN-wide tiles to accumulate. \\ \code{reg\_result\_addr} & \code{ADDR\_WIDTH} & Byte address the computed INT8 result is written to. \\ \bottomrule \end{tabularx} \begin{fnnote}[A node id is a real, finite resource] Because dispatched node table slots are never reclaimed (\S\ref{ch:sched}), a loader driving many independent jobs over a long session must use a fresh \code{reg\_node\_id} for each one, within \code{N\_NODES}. Reusing a value before the system has been reset will simply be refused (\code{reg\_ready} stays low for an occupied, non-\code{EMPTY} node id) --- it will not corrupt anything, but it will also not register. \end{fnnote} \section{Result readback} The computed INT8 result is written to \code{reg\_result\_addr} through the same real PSRAM chain every other memory access uses --- there is no separate result-readback port; the host/loader reads the result byte back from PSRAM directly, the same convention every V2 testbench in this project uses for verification. \section{What a real host driver would still need to add} \begin{itemize} \item Per-job \code{bias}/\code{activation} selection, currently hardcoded to \code{bias=0}/\code{ACT\_RELU} for every job (\S\ref{ch:datapath}). \end{itemize} \section{Addendum (2026-09-07) --- real physical transport and completion signal, both now closed} \label{sec:host-addendum} \begin{fnwarn}[Supersedes the two items removed from the list above] Both real gaps this chapter used to list are closed. This section is the current, real state. \end{fnwarn} \textbf{Physical transport}: \code{spi\_host\_bridge.v}, a real SPI Mode~0 slave, is the board's actual node-registration transport --- real ball assignments (\code{spi\_sclk}/\code{spi\_mosi}/ \code{spi\_miso}/\code{spi\_cs\_n}) verified, real place\&route (see ch.~\ref{ch:hw}). WRITE\_JOB carries the full table from \S\ref{ch:host} above as an 18-byte payload (grew from 15 after the 64MB memory upgrade widened every address field from 3 to 4 bytes --- \code{decisions.log} DEC-0039). \textbf{Completion notification}: \code{FPGA\_DATA\_READY}, a real output pin (ball \code{G3}, bank~7), closes the exact gap this chapter used to flag. It is a system-idle detector, not a per-job pulse --- deliberately, since ``the whole graph has an answer'' and ``one neuron finished'' are different questions and only the former is useful to a host waiting on a result: \[ \text{sys\_busy} = \big(\textstyle\bigvee \text{job\_active}\big) \;\lor\; \lnot\text{queue\_empty} \;\lor\; \text{any\_pending} \] 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} 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\&\®\_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. \begin{fnnote}[Real, disclosed assumption] This is correct only if the host finishes registering every node of a graph before the first one completes. Realistic for this architecture's own real timing (SPI registration: microseconds; per-neuron compute: $\sim$195 real measured cycles, \S\ref{ch:impl2}) but not proven for every conceivable host registration pattern --- a host that deliberately staggers registration across a long enough gap could observe a premature \code{FPGA\_DATA\_READY} pulse after only the first node completes. \end{fnnote} Bit-exact regression re-verified with an explicit assertion on this signal (N\_SLOTS=4 and 8, both PASS, see \code{decisions.log} DEC-0041) and a real \code{nextpnr-ecp5} placement check (0 errors, \code{data\_ready} placed at \code{G3}).