docs: bring datasheet/ into the main repo under hardware/v2/docs

Was a separate, untracked directory (DataSheet/) outside the repo.
Renamed to lowercase and moved in as hardware/v2/docs/datasheet/, with
its own .gitignore for LaTeX build byproducts (compiled PDFs stay
tracked, .aux/.log/.toc/etc do not). Now versioned and shares this
repo's own remote instead of living untracked on disk.

Content: IT+EN LaTeX chapter sources, reference manufacturer PDFs, and
compiled datasheet PDFs including the 2026-09-07 SDRAM upgrade
addendum (AS4C32M16SB-7BIN part/pinout/timing) in the v2-en chapters.

Note: hardware/v2/docs/DatasheetLatex/ (and the v1 sibling) is a
separate, already-tracked, differently-structured LaTeX document that
predates this move -- left untouched, not merged, since its chapter
set and content differ and merging was not requested.

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 05:09:14 +02:00
co-authored by Claude Sonnet 5
commit 0e73eb4726
66 changed files with 7652 additions and 0 deletions
@@ -0,0 +1,190 @@
\chapter[Graph network (Type \#2)]{Two-level configuration: graph network (Type \#2)}
\label{ch:grafo}
\section{Two network types}
The engine exposes two \emph{network types} selectable by the host, with the same start
command dispatching to the correct engine:
\begin{itemize}
\item \textbf{Type \#1 --- classic network (dense).} Layers with neurons per layer, fully
connected between consecutive layers. It is the \code{layer\_sequencer} path
(ch.~\ref{ch:seq}), started by \op{RUN\_NETWORK}. Connections are \emph{implicit by
position}: nothing is enumerated, only the weights are defined, addressed as
\code{w\_base + k*n\_inputs + j}.
\item \textbf{Type \#2 --- arbitrary graph (sparse).} Starting from the input neuron ids,
each neuron's connections up to the output are defined through a per-neuron \emph{sparse
edge-list}. Connections are \emph{explicit by enumeration}: each connection is an edge
\code{(src\_id, weight)}; if it is not in the list, it does not exist.
\end{itemize}
\begin{fnnote}[The difference in one line]
Dense: you define the \emph{weights} by position in a matrix. Graph: you define each
\emph{connection} as an edge \code{(src\_id, weight)} in a per-neuron list. The two
descriptor tables share the same 11-byte format but different fields; the
\code{net\_type} register tells the engine which interpretation to use.
\end{fnnote}
\section{Global activation buffer}
Type \#2 introduces an \textbf{activation buffer} indexed by \emph{signal id}, one INT8
byte per id, implemented in \textbf{on-chip \code{DP16KD} block RAM}
(\code{rtl/act\_buffer.v}). Ids \code{0..N\_in-1} are the inputs; each neuron writes its
own output into its own id. The source gather reads from here with \emph{single-cycle
random access}: this is what makes the graph cheap, because it is the access that PSRAM
(70~ns, sequential) could not accelerate.
\begin{fnspec}[V1 sizing]
\code{N\_TOTAL}=4096 signals, 16-bit id (room to 65\,536 without changing the format).
Buffer = 4~KB, i.e. 2 \code{DP16KD} blocks out of 108. The real constraint becomes the
PSRAM edge capacity ($\approx$2\,M edges at 4~B), not block RAM.
\end{fnspec}
\section{Feed-forward DAG and the \texttt{src\_id < out\_id} rule}
The graph is a feed-forward DAG: every connection points to an \textbf{already-computed}
id (\code{src\_id < out\_id}). Neurons are processed in ascending id order, so that when a
neuron is computed all its sources are ready in the buffer. Cycles and recurrence are out
of scope for V1. The rule is checked at two levels: by the host assembler (compile time)
and by a runtime guard in \code{graph\_engine} (\code{STATUS.err}), in the same philosophy
as the elaboration guard on \code{N\_INPUTS \% PARALLEL}.
\section{Data formats}
Both descriptors are 11~bytes/entry, MSB-first, at \code{table\_base}.
\subsection{Type \#2 descriptor (graph)}
\begin{tabularx}{\textwidth}{L{3.4cm} C{1.6cm} Y}
\toprule
\rowh \thd{Field} & \thd{Bytes} & \thd{Meaning} \\
\midrule
\code{conn\_ptr} & 3 & Byte address in PSRAM of the neuron's edge block. \\
\rowa \code{n\_conn} & 2 & Real connections (pre-padding). \\
\code{out\_id} & 2 & Id into which the neuron's output is written. \\
\rowa \code{activation} & 1 & \code{ACT\_RELU} / \code{ACT\_NONE} (low 2 bits). \\
\code{bias} & 1 & Neuron bias (INT8). \\
\rowa \code{reserved} & 2 & 0. \\
\midrule
\rowh \thd{Total} & \thd{11} & entries in ascending \code{out\_id} order \\
\bottomrule
\end{tabularx}
\subsection{Graph edge (4~bytes, aligned)}
\begin{tabularx}{\textwidth}{L{3.4cm} C{1.6cm} Y}
\toprule
\rowh \thd{Field} & \thd{Bytes} & \thd{Meaning} \\
\midrule
\code{src\_id} & 2 & Source id (uint16 BE). \\
\rowa \code{weight} & 1 & Weight (INT8). \\
\code{reserved} & 1 & 0 (4-byte alignment). \\
\bottomrule
\end{tabularx}
\begin{fnnote}[Padding to \texttt{PARALLEL}]
An arbitrary \code{n\_conn} is not a multiple of \code{PARALLEL}: the neuron's edge-list
is padded up to the multiple with \textbf{zero-weight} edges (waste
$\le$\code{PARALLEL}$-1$ per neuron). This keeps the datapath and its guard intact.
\end{fnnote}
\section{\texttt{graph\_engine} --- graph engine}
\code{rtl/graph\_engine.v} orchestrates Type \#2 \textbf{reusing \code{neuron\_parallel}
unmodified}, as \code{neuron\_memory} does for the dense case. Key difference: between the
two modes only the \emph{X addressing} changes. In Type \#1 the input is contiguous
(\code{x\_base + i}); in Type \#2 it is a gather (\code{act\_buf[src\_id]}). The arithmetic
core is untouched.
\begin{center}
\begin{tikzpicture}[font=\scriptsize,node distance=4mm,start chain=going below,
every node/.style={on chain,fnblock,minimum width=52mm}]
\node[fnblockA]{\code{COPY\_INPUTS}: PSRAM \code{x\_base} $\to$ \code{act\_buf[0..N\_in-1]}};
\node{\code{READ\_DESC}: descriptor of neuron k};
\node{\code{READ\_EDGES}: stream edges + gather \code{act\_buf[src\_id]}};
\node{\code{START\_N} / \code{WAIT\_N}: group of \code{PARALLEL} $\to$ \code{neuron\_parallel}};
\node[fnblockT]{\code{WRITE\_ACT}: y $\to$ \code{act\_buf[out\_id]}};
\node{next neuron (id order)};
\node[fnblockD]{\code{WRITE\_OUTPUTS}: last \code{n\_out} $\to$ PSRAM \code{out\_base}};
\foreach \i [count=\j from 2] in {1,...,6}
\draw[fnarrow] (chain-\i) -- (chain-\j);
\end{tikzpicture}
\end{center}
The outputs are the \textbf{last \code{n\_out}} ids: in a DAG with the
\code{src\_id < out\_id} ordering the output neurons (sinks, not reused as sources)
naturally end up with the highest ids. At the end \code{graph\_engine} copies these
\code{n\_out} bytes into a PSRAM region at \code{out\_base}, which the host reads back with
\op{READ\_RAM}.
\section{Type \#2 opcodes and registers}
The type is selected with a new opcode; \op{RUN\_NETWORK} dispatches on the
\code{net\_type} register (details in ch.~\ref{ch:spi}).
\begin{tabularx}{\textwidth}{L{2.6cm} L{3.4cm} Y}
\toprule
\rowh \thd{Opcode / sel} & \thd{Name} & \thd{Function} \\
\midrule
\op{0x11} & SET\_NET\_TYPE & \code{type(1B)}: \code{0x01}=dense (\#1), \code{0x02}=graph (\#2). Default after \op{RESET}=dense. \\
\rowa \code{SET\_BASE sel 9} & num\_neurons\_graph & Number of graph neurons (uint16). \\
\code{SET\_BASE sel 10} & n\_out & Number of output ids (uint16). \\
\bottomrule
\end{tabularx}
\begin{fnnote}[Zero regression on Type \#1]
With \code{net\_type=dense} (the default value after \op{RESET}) the \#1 path is
bit-identical to before: \op{RUN\_NETWORK} keeps its \code{num\_layers(1B)} payload and the
framing of the existing opcodes does not change.
\end{fnnote}
\section{Occupancy (Type \#2 enabled)}
Yosys synthesis of the full \code{spi\_neuron\_top} system with Type \#2 enabled
(\code{PARALLEL}=2):
\begin{tabularx}{\textwidth}{L{4.6cm} Y}
\toprule
\rowh \thd{Resource} & \thd{Use} \\
\midrule
\code{DP16KD} (block RAM) & 2 (activation buffer) \\
\rowa \code{MULT18X18D} (DSP) & 4 (2 \code{neuron\_memory} + 2 \code{graph\_engine}) \\
LUT4 & 2619 \\
\rowa TRELLIS\_FF & 2467 \\
\code{\$\_TBUF\_} (PSRAM bus) & 16 \\
\bottomrule
\end{tabularx}
The device (108 \code{DP16KD}, 72 DSP, $\approx$44k LUT/FF) stays well below saturation:
Type \#2 adds a complete mode at a contained resource cost. LUT4/TRELLIS\_FF grew from an
earlier measurement (2367/2406) because of the PSRAM page mode added to the controller
(ch.~\ref{ch:mem}, \S~5.5) --- under 6\% utilization, no practical impact.
\section{Gather bandwidth (measured)}
The per-edge gather cost was \textbf{isolated} by building two structurally-identical
graphs with different edge counts and differencing the cycles: the subtraction cancels the
fixed per-neuron overhead and leaves the edge cost alone.
\begin{fnspec}[Per-edge cost]
\textbf{37.53 cycles/edge} with PSRAM page mode enabled (ch.~\ref{ch:mem}, \S~5.5) ---
\textbf{53.25 cycles/edge} without it (pre-page-mode baseline, consistent with theory:
4~bytes/edge $\times$ $\approx$13 cycles/byte over async PSRAM $\approx$52). At 80~MHz:
$\approx$2.13\,M edges/s ($\approx$8.5~MB/s, +42\% vs. baseline); at the real 16~MHz
clock: $\approx$426\,k edges/s ($\approx$1.71~MB/s).
\end{fnspec}
Page-mode read (roadmap G7, ch.~\ref{ch:roadmap}) has been implemented and measured: the
gather's sequential access benefits directly, cutting the per-edge cost by 29.5\%
(53.25$\to$37.53 cycles/edge). Each edge still pays \code{int8\_memory\_access}'s
byte-granular access (4 bytes/edge); page mode reduces the cost of each sequential byte,
not the number of accesses.
\section{\texttt{netasm} host assembler}
Readable network configuration needs no dedicated FPGA logic: a pseudo-assembly is
compiled \emph{on the host} (\code{tools/netasm/}) into the exact bytes of the tables and
edges, then loaded with \op{WRITE\_RAM}. The assembler validates at compile time
(\code{src\_id < out\_id}, \code{N\_TOTAL} bounds, padding to \code{PARALLEL}),
complementing the runtime guard.
\begin{lstlisting}[language=,caption={Pseudo-assembly example (graph)},basicstyle=\ttfamily\scriptsize]
NET graph
INPUTS 4 ; ids 0..3
NEURON n4 relu bias=2
CONN 0 w=5
CONN 1 w=-3
NEURON n5 none bias=0
CONN n4 w=2 ; symbolic reference to n4's output
CONN 2 w=7
OUTPUT n5
END
\end{lstlisting}