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
166 lines
7.4 KiB
TeX
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]}.
|