Files
FPGA-Neural-Datasheet/files/docs/datasheet/chapters/03-datapath.tex
T
micheleandClaude Sonnet 5 0e73eb4726 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
2026-09-07 05:09:14 +02:00

166 lines
7.4 KiB
TeX

\chapter{Datapath di calcolo}
\label{ch:datapath}
\section{Catena aritmetica INT8/INT32}
Il datapath elementare implementa la sequenza tipica di un neurone quantizzato:
\begin{center}
\begin{tikzpicture}[font=\scriptsize,node distance=3mm,start chain=going right,
every node/.style={fnblock,minimum width=15mm,minimum height=8mm,on chain}]
\node[fnblockT]{INT8\\$\times$\,INT8};
\node{INT16\\prodotto};
\node{sign-ext\\INT32};
\node[fnblockD]{accumulo\\INT32};
\node{$+$ bias};
\node[fnblockA]{attivazione};
\node[fnblockT]{sat. INT8};
\foreach \i [count=\j from 2] in {1,...,6}
\draw[fnarrow] (chain-\i) -- (chain-\j);
\end{tikzpicture}
\end{center}
Ogni prodotto INT8$\times$INT8 sta in 16~bit; viene esteso con segno a 32~bit prima
dell'accumulo, così l'accumulatore non trabocca su vettori lunghi. Bias e attivazione
operano a 32~bit; solo l'uscita finale viene saturata a INT8.
\section{\texttt{mac\_unit} --- moltiplicatore-accumulatore}
Il modulo \code{mac\_unit} è puramente combinatorio e parametrico su \code{DATA\_WIDTH}
e \code{ACC\_WIDTH}. Calcola:
\[
\mathrm{acc\_out} = \mathrm{acc\_in} + \mathrm{signext}_{ACC}(x \cdot w)
\]
Il prodotto ha larghezza $2\times$\code{DATA\_WIDTH} e viene esteso con segno replicando
il bit più significativo. Su ECP5 la moltiplicazione mappa su un blocco DSP
\code{MULT18X18D}.
\begin{lstlisting}[caption={\texttt{rtl/mac\_unit.v} --- nucleo aritmetico},label={lst:macunit}]
localparam PROD_WIDTH = 2 * DATA_WIDTH;
wire signed [PROD_WIDTH-1:0] product = x * w;
wire signed [ACC_WIDTH-1:0] product_ext =
{{(ACC_WIDTH-PROD_WIDTH){product[PROD_WIDTH-1]}}, product};
assign acc_out = acc_in + product_ext;
\end{lstlisting}
\section{\texttt{mac8} --- MAC parallelo e balanced adder tree}
\code{mac8} istanzia \code{PARALLEL} unità \code{mac\_unit} che generano
\code{PARALLEL} prodotti indipendenti, poi li somma con un \emph{albero di addizione
binario bilanciato}. Rispetto alla riduzione lineare
$((((p_0{+}p_1){+}p_2){+}p_3){+}\dots)$, di profondità $O(\text{PARALLEL})$, l'albero
ha profondità $O(\log_2 \text{PARALLEL})$, riducendo drasticamente il percorso
combinatorio.
\begin{center}
\begin{tikzpicture}[font=\scriptsize,level distance=11mm,
every node/.style={fnreg,minimum width=8mm},
level 1/.style={sibling distance=30mm},
level 2/.style={sibling distance=15mm},
level 3/.style={sibling distance=8mm},
edge from parent/.style={fnarrowT,draw}]
\node[fnblockD]{sum}
child {node[fnblockT]{$+$}
child {node[fnblockT]{$+$}
child {node{$p_0$}} child {node{$p_1$}}}
child {node[fnblockT]{$+$}
child {node{$p_2$}} child {node{$p_3$}}}}
child {node[fnblockT]{$+$}
child {node[fnblockT]{$+$}
child {node{$p_4$}} child {node{$p_5$}}}
child {node[fnblockT]{$+$}
child {node{$p_6$}} child {node{$p_7$}}}};
\end{tikzpicture}
\end{center}
\begin{center}\footnotesize\itshape\color{fnGrey}
Esempio con PARALLEL=8: 3 livelli. PARALLEL=16 $\to$ 4 livelli; PARALLEL=32 $\to$ 5
livelli.\end{center}
\begin{fnnote}[PARALLEL potenza di due]
L'albero è pensato per \code{PARALLEL} potenza di due (8, 16, 32\ldots). Questo è anche
il valore usato in tutte le configurazioni del progetto.
\end{fnnote}
\section{\texttt{neuron\_parallel} --- FSM del neurone}
\code{neuron\_parallel} elabora \code{N\_INPUTS} ingressi in gruppi di \code{PARALLEL},
mantenendo l'accumulatore tra un gruppo e il successivo. Alla fine somma il bias,
applica l'attivazione e satura a INT8. Il numero di gruppi è
$\text{GROUPS}=\text{N\_INPUTS}/\text{PARALLEL}$.
\begin{center}
\begin{tikzpicture}[font=\scriptsize,node distance=4mm,start chain=going below,
every node/.style={on chain,fnblock,minimum width=46mm}]
\node[fnblockA]{\code{start}};
\node{gruppo 0 $\to$ accumulo};
\node{gruppo 1 $\to$ accumulo};
\node[draw=none,fill=none]{\vdots};
\node{gruppo GROUPS$-$1 $\to$ accumulo};
\node{$+$ bias};
\node[fnblockA]{attivazione (ACT\_RELU / ACT\_NONE)};
\node[fnblockT]{saturazione INT8};
\node[fnblockD]{\code{done}, \code{y}};
\foreach \i [count=\j from 2] in {1,...,8}
\draw[fnarrow] (chain-\i) -- (chain-\j);
\end{tikzpicture}
\end{center}
\subsection{Guard di parametri (elaboration-time)}
Se \code{PARALLEL} non divide esattamente \code{N\_INPUTS} si verificano due guasti,
entrambi confermati empiricamente in \code{sim/parameter\_sweep\_tb.v}:
\begin{itemize}
\item la divisione intera tronca \code{GROUPS} e gli ingressi in eccesso non vengono
mai letti $\to$ risultato \textbf{errato}, senza errore né avviso;
\item se \code{PARALLEL > N\_INPUTS}, \code{GROUPS=0} e la condizione terminale non è mai
soddisfatta $\to$ il neurone \textbf{si blocca} (busy alto, done mai asserito).
\end{itemize}
La soluzione non modifica il datapath validato: un blocco \code{generate} istanzia un
modulo deliberatamente indefinito quando $\text{N\_INPUTS} \bmod \text{PARALLEL}\neq0$,
forzando un errore in \emph{elaborazione} sia in simulazione sia in sintesi. Per le
configurazioni valide il ramo non viene mai elaborato.
\begin{lstlisting}[caption={\texttt{rtl/neuron\_parallel.v} --- guard di parametri}]
generate
if (N_INPUTS == 0 || N_INPUTS % PARALLEL != 0) begin : PARAMETER_ERROR
neuron_parallel_requires_N_INPUTS_multiple_of_PARALLEL
invalid_parameter_combination();
end
endgenerate
\end{lstlisting}
\begin{fnnote}[Caso limite \texttt{N\_INPUTS=0} (corretto 2026-09-04)]
La condizione originale (\code{N\_INPUTS \% PARALLEL != 0}) non intercetta
\code{N\_INPUTS=0}, poiché $0 \bmod \text{PARALLEL}=0$ per ogni \code{PARALLEL}: il modulo
elaborava con successo (sia in simulazione sia in sintesi reale Yosys) lasciando
\code{x\_bus}/\code{w\_bus} non pilotati e \code{start} silenziosamente inefficace. Trovato
durante la campagna di ri-certificazione (\code{docs/validation/bugs.md}, BUG-002) e
corretto estendendo il guard come sopra --- \code{N\_INPUTS=0} ora fallisce l'elaborazione
esattamente come gli altri casi degeneri.
\end{fnnote}
\section{Funzioni di attivazione}
\code{neuron\_parallel} accetta una porta \code{activation} a 2~bit. Il default è
\code{ACT\_RELU}, l'unico comportamento esistente prima dell'introduzione della porta,
così ogni chiamante preesistente resta invariato.
\begin{tabularx}{\textwidth}{L{2.6cm} C{1.4cm} Y}
\toprule
\rowh \thd{Codifica} & \thd{Valore} & \thd{Comportamento} \\
\midrule
\code{ACT\_NONE} & \code{2'd0} & Lineare: nessun clamp a zero, saturazione bilaterale al range INT8 $[-128,+127]$. \\
\rowa \code{ACT\_RELU} & \code{2'd1} & $\max(0,x)$, poi saturazione positiva a $+127$ (default; fallback anche per codifiche riservate). \\
\bottomrule
\end{tabularx}
\section{Saturazione INT8}
Dopo bias e attivazione, l'accumulatore a 32~bit viene ridotto a INT8:
\[
y=\begin{cases}
+127 & \text{se } \mathrm{final\_acc} > 127\\
-128 & \text{se } \mathrm{final\_acc} < -128 \ \text{(solo ACT\_NONE)}\\
0 & \text{se } \mathrm{final\_acc}\le 0 \ \text{(solo ACT\_RELU)}\\
\mathrm{final\_acc}[7:0] & \text{altrimenti}
\end{cases}
\]
\section{\texttt{layer} --- neuroni in parallelo}
\code{layer} istanzia \code{N\_NEURONS} neuroni che condividono il vettore di ingresso
\code{x\_bus} ma hanno pesi e bias distinti; \code{busy} è l'OR e \code{done} l'AND dei
segnali dei neuroni. È il modulo usato nei benchmark del datapath (cap.~\ref{ch:impl}),
dove tutti i neuroni lavorano simultaneamente. La convenzione di indirizzamento è
neuron-major: i pesi del neurone $n$ occupano \code{weights\_bus[n*N\_INPUTS*DATA\_WIDTH +: N\_INPUTS*DATA\_WIDTH]}.