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
+191
View File
@@ -0,0 +1,191 @@
\chapter[Rete a grafo (Tipo \#2)]{Configurazione a due livelli: rete a grafo (Tipo \#2)}
\label{ch:grafo}
\section{Due tipi di rete}
L'engine espone due \emph{tipi di rete} selezionabili dall'host, con lo stesso comando di
avvio che instrada verso il motore corretto:
\begin{itemize}
\item \textbf{Tipo \#1 --- rete classica (dense).} Layer con neuroni per layer, fully
connected tra layer consecutivi. È il percorso di \code{layer\_sequencer}
(cap.~\ref{ch:seq}), avviato da \op{RUN\_NETWORK}. Connessioni \emph{implicite per
posizione}: non si enumera nulla, si definiscono solo i pesi indirizzati come
\code{w\_base + k*n\_inputs + j}.
\item \textbf{Tipo \#2 --- grafo arbitrario (sparse).} A partire dagli id dei neuroni di
ingresso si definiscono le connessioni di ogni neurone fino all'uscita, tramite una
\emph{edge-list sparsa} per-neurone. Connessioni \emph{esplicite per enumerazione}: ogni
connessione è un edge \code{(src\_id, peso)}; se non è nella lista, non esiste.
\end{itemize}
\begin{fnnote}[La differenza in una riga]
Dense: definisci i \emph{pesi} per posizione in una matrice. Graph: definisci ogni
\emph{connessione} come edge \code{(src\_id, peso)} in una lista per-neurone. Le due
tabelle descrittori hanno lo stesso formato di 11~byte ma campi diversi; il registro
\code{net\_type} dice al motore quale interpretazione usare.
\end{fnnote}
\section{Buffer di attivazione globale}
Il Tipo \#2 introduce un \textbf{buffer di attivazione} indicizzato per \emph{id di
segnale}, un byte INT8 per id, realizzato in \textbf{block RAM on-chip \code{DP16KD}}
(\code{rtl/act\_buffer.v}). Gli id \code{0..N\_in-1} sono gli ingressi; ogni neurone
scrive la propria uscita nel proprio id. Il gather delle sorgenti legge da qui a
\emph{accesso random a un ciclo}: è ciò che rende economico il grafo, perché è l'accesso
che la PSRAM (70~ns, sequenziale) non potrebbe accelerare.
\begin{fnspec}[Dimensionamento V1]
\code{N\_TOTAL}=4096 segnali, id a 16~bit (spazio fino a 65.536 senza cambiare formato).
Buffer = 4~KB, cioè 2 blocchi \code{DP16KD} su 108. Il vincolo reale diventa la capacità
PSRAM per gli edge ($\approx$2\,M edge a 4~B), non la block RAM.
\end{fnspec}
\section{DAG feed-forward e vincolo \texttt{src\_id < out\_id}}
Il grafo è un DAG feed-forward: ogni connessione punta a un id \textbf{già calcolato}
(\code{src\_id < out\_id}). I neuroni si elaborano in ordine di id crescente, così quando
si calcola un neurone tutte le sue sorgenti sono pronte nel buffer. Cicli e ricorrenza
sono fuori scope per la V1. Il vincolo è verificato a due livelli: dall'assemblatore host
(a compile time) e da un guard a runtime in \code{graph\_engine} (\code{STATUS.err}),
nella stessa filosofia del guard di elaborazione su \code{N\_INPUTS \% PARALLEL}.
\section{Formati dati}
Entrambi i descrittori sono da 11~byte/voce, MSB-first, a \code{table\_base}.
\subsection{Descrittore Tipo \#2 (grafo)}
\begin{tabularx}{\textwidth}{L{3.4cm} C{1.6cm} Y}
\toprule
\rowh \thd{Campo} & \thd{Byte} & \thd{Significato} \\
\midrule
\code{conn\_ptr} & 3 & Indirizzo byte in PSRAM del blocco edge del neurone. \\
\rowa \code{n\_conn} & 2 & Connessioni reali (pre-padding). \\
\code{out\_id} & 2 & Id in cui scrivere l'uscita del neurone. \\
\rowa \code{activation} & 1 & \code{ACT\_RELU} / \code{ACT\_NONE} (2 bit bassi). \\
\code{bias} & 1 & Bias del neurone (INT8). \\
\rowa \code{reserved} & 2 & 0. \\
\midrule
\rowh \thd{Totale} & \thd{11} & voci in ordine di \code{out\_id} crescente \\
\bottomrule
\end{tabularx}
\subsection{Edge del grafo (4~byte, allineato)}
\begin{tabularx}{\textwidth}{L{3.4cm} C{1.6cm} Y}
\toprule
\rowh \thd{Campo} & \thd{Byte} & \thd{Significato} \\
\midrule
\code{src\_id} & 2 & Id sorgente (uint16 BE). \\
\rowa \code{weight} & 1 & Peso (INT8). \\
\code{reserved} & 1 & 0 (allineamento a 4~byte). \\
\bottomrule
\end{tabularx}
\begin{fnnote}[Padding a \texttt{PARALLEL}]
\code{n\_conn} arbitrario non è multiplo di \code{PARALLEL}: la edge-list del neurone è
riempita fino al multiplo con edge a \textbf{peso zero} (spreco $\le$\code{PARALLEL}$-1$
per neurone). Così il datapath e il suo guard restano intatti.
\end{fnnote}
\section{\texttt{graph\_engine} --- motore del grafo}
\code{rtl/graph\_engine.v} orchestra il Tipo \#2 \textbf{riusando \code{neuron\_parallel}
senza modificarlo}, come fa \code{neuron\_memory} per il caso denso. Differenza chiave: tra
i due modi cambia \emph{solo l'indirizzamento di X}. In Tipo \#1 l'input è contiguo
(\code{x\_base + i}); in Tipo \#2 è un gather (\code{act\_buf[src\_id]}). Il core aritmetico
non si tocca.
\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}: descrittore del neurone k};
\node{\code{READ\_EDGES}: stream edge + gather \code{act\_buf[src\_id]}};
\node{\code{START\_N} / \code{WAIT\_N}: gruppo da \code{PARALLEL} $\to$ \code{neuron\_parallel}};
\node[fnblockT]{\code{WRITE\_ACT}: y $\to$ \code{act\_buf[out\_id]}};
\node{neurone successivo (ordine di id)};
\node[fnblockD]{\code{WRITE\_OUTPUTS}: ultimi \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}
Le uscite sono gli \textbf{ultimi \code{n\_out}} id: nei DAG con l'ordinamento
\code{src\_id < out\_id} i neuroni di uscita (sink, non riusati come sorgente) finiscono
naturalmente con gli id più alti. A fine esecuzione \code{graph\_engine} copia questi
\code{n\_out} byte in una regione PSRAM a \code{out\_base}, che l'host rilegge con
\op{READ\_RAM}.
\section{Opcode e registri del Tipo \#2}
La selezione del tipo avviene con un nuovo opcode; \op{RUN\_NETWORK} fa il dispatch sul
registro \code{net\_type} (dettagli in cap.~\ref{ch:spi}).
\begin{tabularx}{\textwidth}{L{2.6cm} L{3.4cm} Y}
\toprule
\rowh \thd{Opcode / sel} & \thd{Nome} & \thd{Funzione} \\
\midrule
\op{0x11} & SET\_NET\_TYPE & \code{type(1B)}: \code{0x01}=dense (\#1), \code{0x02}=graph (\#2). Default dopo \op{RESET}=dense. \\
\rowa \code{SET\_BASE sel 9} & num\_neurons\_graph & Numero di neuroni del grafo (uint16). \\
\code{SET\_BASE sel 10} & n\_out & Numero di id di uscita (uint16). \\
\bottomrule
\end{tabularx}
\begin{fnnote}[Zero regressioni sul Tipo \#1]
Con \code{net\_type=dense} (valore di default dopo \op{RESET}) il percorso \#1 è
bit-identico a prima: \op{RUN\_NETWORK} mantiene il payload \code{num\_layers(1B)} e il
framing degli opcode esistenti non cambia.
\end{fnnote}
\section{Occupazione (Tipo \#2 abilitato)}
Sintesi Yosys del sistema completo \code{spi\_neuron\_top} con Tipo \#2 abilitato
(\code{PARALLEL}=2):
\begin{tabularx}{\textwidth}{L{4.6cm} Y}
\toprule
\rowh \thd{Risorsa} & \thd{Uso} \\
\midrule
\code{DP16KD} (block RAM) & 2 (buffer di attivazione) \\
\rowa \code{MULT18X18D} (DSP) & 4 (2 \code{neuron\_memory} + 2 \code{graph\_engine}) \\
LUT4 & 2619 \\
\rowa TRELLIS\_FF & 2467 \\
\code{\$\_TBUF\_} (bus PSRAM) & 16 \\
\bottomrule
\end{tabularx}
Il device (108 \code{DP16KD}, 72 DSP, $\approx$44k LUT/FF) resta ampiamente sotto la
saturazione: il Tipo \#2 aggiunge una modalità completa a costo di risorse contenuto.
LUT4/TRELLIS\_FF sono cresciuti rispetto a una misura precedente (2367/2406) per via del
page mode PSRAM aggiunto al controller (cap.~\ref{ch:mem}, \S~5.5) --- sotto il 6\% di
utilizzo, nessun impatto pratico.
\section{Banda del gather (misurata)}
Il costo per-edge del gather è stato \textbf{isolato} costruendo due grafi identici per
struttura ma con conteggio edge diverso e differenziando i cicli: la sottrazione cancella
l'overhead fisso per-neurone e lascia il solo costo dell'edge.
\begin{fnspec}[Costo per-edge]
\textbf{37.53 cicli/edge} con il page mode PSRAM abilitato (cap.~\ref{ch:mem},
\S~5.5) --- \textbf{53.25 cicli/edge} senza (baseline pre-page-mode, coerente con la
teoria: 4~byte/edge $\times$ $\approx$13 cicli/byte via PSRAM asincrona
$\approx$52). A 80~MHz: $\approx$2.13\,M edge/s ($\approx$8.5~MB/s, +42\% vs
baseline); al clock reale di 16~MHz: $\approx$426\,k edge/s ($\approx$1.71~MB/s).
\end{fnspec}
Il page-mode read (roadmap G7, cap.~\ref{ch:roadmap}) è stato implementato e misurato:
l'accesso sequenziale del gather ne beneficia direttamente, riducendo il costo per-edge
del 29.5\% (53.25$\to$37.53 cicli/edge). Ogni edge continua comunque a pagare l'accesso
byte-granulare di \code{int8\_memory\_access} (4 byte/edge); il page mode riduce il costo
di ciascun byte sequenziale, non il numero di accessi.
\section{Assemblatore host \texttt{netasm}}
La configurazione leggibile della rete non richiede logica dedicata in FPGA: uno
pseudo-assembly viene compilato \emph{sull'host} (\code{tools/netasm/}) nei byte esatti
delle tabelle e degli edge, poi caricati con \op{WRITE\_RAM}. L'assemblatore valida a
compile time (\code{src\_id < out\_id}, limiti \code{N\_TOTAL}, padding a \code{PARALLEL}),
complementando il guard runtime.
\begin{lstlisting}[language=,caption={Esempio di pseudo-assembly (grafo)},basicstyle=\ttfamily\scriptsize]
NET graph
INPUTS 4 ; id 0..3
NEURON n4 relu bias=2
CONN 0 w=5
CONN 1 w=-3
NEURON n5 none bias=0
CONN n4 w=2 ; riferimento simbolico all'uscita di n4
CONN 2 w=7
OUTPUT n5
END
\end{lstlisting}