Files
FPGA-Neural-Datasheet/files/docs/datasheet/chapters/05-memoria.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

194 lines
11 KiB
TeX

\chapter{Sottosistema di memoria}
\label{ch:mem}
\section{Catena di memoria}
Il motore di calcolo lavora con indirizzi e dati a livello di \emph{byte} (INT8), mentre
la PSRAM è un dispositivo a parola da 16~bit. Tre moduli in cascata realizzano la
conversione e l'accesso fisico:
\begin{center}
\begin{tikzpicture}[font=\scriptsize,node distance=8mm]
\node[fnblockD,minimum width=30mm,minimum height=12mm](nm){master byte-level\\{\scriptsize \code{neuron\_memory} / \code{spi\_engine} / \code{layer\_sequencer}}};
\node[fnblockT,right=10mm of nm,minimum width=28mm,minimum height=12mm](ia){\code{int8\_memory\_access}\\{\scriptsize byte $\leftrightarrow$ word 16-bit}};
\node[fnblock,right=10mm of ia,minimum width=26mm,minimum height=12mm](mi){\code{memory\_interface}\\{\scriptsize FSM IDLE/WAIT}};
\node[fnblockA,below=9mm of mi,minimum width=26mm,minimum height=12mm](pc){\code{psram\_controller}\\{\scriptsize bus fisico async 70\,ns}};
\node[fnblock,left=10mm of pc,minimum width=26mm,minimum height=12mm](ps){PSRAM\\{\scriptsize 8\,MB 4M$\times$16}};
\draw[fnbus] (nm)--node[fnlbl,above]{req/wr/addr}(ia);
\draw[fnbus] (ia)--node[fnlbl,above]{16-bit}(mi);
\draw[fnbus] (mi)--(pc);
\draw[fnbus] (pc)--node[fnlbl,above]{DQ/A/ctrl}(ps);
\end{tikzpicture}
\end{center}
\section{\texttt{int8\_memory\_access} --- conversione byte/word}
Converte l'interfaccia INT8 (indirizzo di byte) nell'interfaccia a parola. L'indirizzo
di byte viene diviso per due (\code{addr>>1}) per ottenere l'indirizzo di parola; il
bit meno significativo seleziona il byte:
\begin{itemize}
\item \code{addr[0]=0} $\to$ byte basso: \code{lb\_n=0}, \code{ub\_n=1}, dato su DQ[7:0];
\item \code{addr[0]=1} $\to$ byte alto: \code{lb\_n=1}, \code{ub\_n=0}, dato su DQ[15:8].
\end{itemize}
In lettura estrae il byte corretto da \code{mem\_rdata}. La FSM ha due stati (IDLE,
WAIT) e restituisce \code{ready} come impulso di un ciclo.
\section{\texttt{memory\_interface} --- handshake}
FSM a due stati che serializza una singola transazione: in IDLE, alla richiesta
\code{req}, latcha \code{wr/addr/wdata/lb\_n/ub\_n} ed emette un impulso \code{mem\_req}
di un ciclo verso il controller; in WAIT attende \code{mem\_ready}, cattura
\code{rdata} in lettura e asserisce \code{ready}. Garantisce il contratto ``una
transazione per volta''.
\section{\texttt{psram\_controller} --- bus fisico}
Controller del bus PSRAM parallelo asincrono, con supporto al \textbf{page mode}
di lettura del chip (\S~\ref{sec:pagemode}). La macchina a stati principale è:
\begin{center}
\begin{tikzpicture}[font=\scriptsize]
\node[fnstate](init) at (0,0){INIT};
\node[fnstate](idle) at (3.2,0){IDLE};
\node[fnstate](read) at (7,2.7){READ};
\node[fnstate](popen) at (11,2.7){PAGE\\OPEN};
\node[fnstate](write) at (7,-2.7){WRITE};
\node[fnstate](ww) at (11,-2.7){WRITE\\WAIT};
\draw[fnarrow] (init)--node[fnlbl,above]{INIT\_CYCLES + CR load}(idle);
\draw[fnarrow] (idle)--node[fnlbl,above,sloped]{req \& !wr}(read);
\draw[fnarrow] (idle)--node[fnlbl,below,sloped]{req \& wr}(write);
\draw[fnarrow] (read)--node[fnlbl,above]{ready}(popen);
\draw[fnarrowT] (popen) to[bend left=25] node[fnlbl,below]{req \& !wr}(read);
\draw[fnarrow] (popen) to[bend right=20] node[fnlbl,above,sloped]{req \& wr}(write);
\draw[fnarrow] (popen) to[out=-100,in=15,looseness=1.15] node[fnlbl,pos=0.55]{timeout tCEM}(idle);
\draw[fnarrow] (write)--node[fnlbl,above]{ACCESS\_CYCLES}(ww);
\draw[fnarrow] (ww) to[out=160,in=-70] node[fnlbl,pos=0.5,left]{ready}(idle);
\end{tikzpicture}
\end{center}
Da INIT il controller passa automaticamente per una sotto-sequenza di caricamento del
registro di configurazione (\code{STATE\_CR\_INIT}, 4 passi) prima di raggiungere IDLE
per la prima volta --- vedi \S~\ref{sec:pagemode}. La transizione PAGE~OPEN
$\to$~WRITE (freccia in basso a destra) passa internamente per due micro-stati di
transito, \code{STATE\_PAGE\_CLOSE} e \code{STATE\_PAGE\_REOPEN} (un ciclo ciascuno):
il primo forza CE\#/OE\# alti per almeno un ciclo prima che il controller inizi a
pilotare il bus dati, evitando contesa con l'uscita ancora attiva della PSRAM
($\geq t_{HZ}$); il secondo riavvia la transazione già latchata esattamente come
farebbe IDLE. Non sono disegnati come nodi separati per non appesantire la figura.
\subsection{Temporizzazione}
\begin{fnspec}[Formule di temporizzazione]
$\text{ACCESS\_CYCLES}=\lceil (70\times \text{CLK\_FREQ\_MHZ})/1000\rceil$ \quad
(latenza di accesso casuale, $t_{AA}$/$t_{RC}$ = 70~ns)\\[3pt]
$\text{PAGE\_CYCLES}=\lceil (20\times \text{CLK\_FREQ\_MHZ})/1000\rceil$ \quad
(continuazione nella stessa pagina, $t_{APA}$/$t_{PC}$ = 20~ns)\\[3pt]
$\text{INIT\_CYCLES}=150\times \text{CLK\_FREQ\_MHZ}$ \quad
(inizializzazione di power-up, $t_{PU}$ = 150~\textmu s)\\[3pt]
$\text{PAGE\_TIMEOUT\_CYCLES}=\lceil (6000\times \text{CLK\_FREQ\_MHZ})/1000\rceil$ \quad
(chiusura automatica della pagina, margine di sicurezza sotto $t_{CEM}$ = 8~\textmu s)
\end{fnspec}
Il bus dati è pilotato in tri-state: \code{psram\_dq = dq\_oe ? dq\_out : Z}. In lettura
\code{dq\_oe=0}; in scrittura \code{dq\_oe=1} durante l'impulso di \code{we\_n}. Uno
stato di WRITE\_WAIT mantiene attivi \code{ce\_n/lb\_n/ub\_n} per l'hold finale prima
del rilascio.
\begin{fnwarn}[Non è QSPI]
Questa è un'interfaccia SRAM-asincrona classica, \textbf{non} QSPI: la maggior parte
delle ``PSRAM'' serie/QSPI in commercio non è compatibile con questo controller senza
riscrittura. Vedere il cap.~\ref{ch:hw} per la parte raccomandata (ISSI parallela).
\end{fnwarn}
\section{Page mode di lettura}
\label{sec:pagemode}
Il chip raccomandato (cap.~\ref{ch:hw}) è ``asynchronous/\textbf{page mode}'': una
volta fatto un primo accesso casuale a $t_{AA}$~=~70~ns, letture successive
all'interno della stessa pagina da 16 word (bit di indirizzo sopra \code{A[3]}
invariati) costano solo $t_{APA}$/$t_{PC}$~=~20~ns, perché CE\#/OE\# restano attivi
e cambia solo il bus indirizzi. Il page mode è \textbf{disabilitato di default}
all'accensione (bit~7 del registro di configurazione, CR~=~\texttt{0x0070} di
default) e va abilitato esplicitamente.
\begin{itemize}
\item \textbf{Abilitazione all'avvio}: subito dopo INIT, il controller esegue la
``software-access sequence'' del datasheet (2 letture dummy + 2 scritture,
\texttt{0x0000} di sblocco poi CR reale \texttt{0x00F0} = default con il bit
Page attivo) all'indirizzo più alto del chip --- riusa esattamente la stessa
logica READ/WRITE di ogni altra transazione, quindi passa dagli stessi controlli
di temporizzazione.
\item \textbf{Burst di pagina}: dopo una READ il controller non chiude più
CE\#/OE\# (stato PAGE~OPEN). Una READ successiva nella stessa pagina aspetta solo
PAGE\_CYCLES; una READ che attraversa pagina resta comunque senza toggle di CE\#
ma paga un ACCESS\_CYCLES pieno per quella parola (qualunque cambio a
\code{A[4]} o superiore richiede un nuovo $t_{AA}$). Un contatore chiude la
pagina prima del limite $t_{CEM}$ con margine di sicurezza.
\item \textbf{Solo una WRITE chiude la pagina.} I cambi di \code{lb\_n}/\code{ub\_n}
\emph{non} la chiudono: \code{int8\_memory\_access} alterna questi segnali a quasi
ogni accesso (accesso byte-granulare su bus a 16~bit), quindi trattarli come
condizione di chiusura --- primo tentativo di implementazione --- rendeva il
workload reale \emph{più lento}, non più veloce (misurato: 53.25$\to$61.25
cicli/edge sul gather di \code{graph\_engine}); rimosso, corretto a
53.25$\to$37.53 cicli/edge (banda +42\%, \S~\ref{sec:bandwidth}).
\end{itemize}
\begin{fnwarn}[Nessun beneficio senza pattern sequenziale]
Il page mode accelera solo accessi che restano nella stessa pagina (o quasi) mentre
il controller resta in attesa di una nuova richiesta con la pagina ancora aperta.
Accessi isolati e sparsi (indirizzo casuale ogni volta) pagano comunque
ACCESS\_CYCLES pieno, più un piccolo overhead di chiusura/riapertura se preceduti
da una WRITE o da un timeout $t_{CEM}$: non è un guadagno universale, dipende dal
pattern di accesso del chiamante.
\end{fnwarn}
Fmax reale (\code{nextpnr-ecp5}, cap.~\ref{ch:impl}) sul sistema integrato
\code{spi\_neuron\_top} con Tipo~\#2 abilitato: \textbf{75.73~MHz} a
\code{PARALLEL}=2 (era 55.59~MHz prima dell'aggiunta del page mode) e
\textbf{65.13~MHz} a \code{PARALLEL}=8, entrambe ancora FAIL all'obiettivo di
80~MHz ma non regredite. Il percorso critico resta, in entrambi i casi,
interamente dentro \code{u\_graph\_engine.u\_neuron} (catena di accumulo
\code{mac8}/\code{neuron\_parallel}, cap.~\ref{ch:impl}) --- \code{psram\_controller}
non compare mai nel percorso critico nonostante la crescita di risorse del page
mode.
\section{Mappa degli indirizzi e convenzioni}
Lo spazio di indirizzamento è di \code{ADDR\_WIDTH}=23~bit (indirizzo di \emph{byte}),
per 8~MB pieni. Le regioni non hanno indirizzi cablati: le loro basi sono registri
impostati dall'host via \op{SET\_BASE} (percorso single-layer) o lette dalla tabella
descrittori (percorso multi-layer).
\begin{tabularx}{\textwidth}{L{3.2cm} L{3.4cm} Y}
\toprule
\rowh \thd{Regione} & \thd{Base} & \thd{Contenuto / convenzione} \\
\midrule
Ingresso $X$ & \code{x\_base} & Vettore di ingresso condiviso, letto una volta per invocazione. \\
\rowa Pesi $W$ & \code{w\_base} & Neuron-major: i pesi del neurone $n$ a \code{w\_base + n*N\_INPUTS} byte. \\
Bias & \code{bias\_addr} & Un byte per neurone: bias del neurone $n$ a \code{bias\_addr + n}. \\
\rowa Tabella descrittori & \code{table\_base} & \code{N\_LAYERS} voci da 11 byte (cap.~\ref{ch:seq}). \\
Buffer ping-pong A/B & \code{buf\_a\_base} / \code{buf\_b\_base} & Uscite intermedie tra layer. \\
\bottomrule
\end{tabularx}
\subsection{Indirizzamento fisico della PSRAM}
La PSRAM raccomandata è 4M$\times$16 (8~MB), che richiede un indirizzo di parola a
22~bit (A0--A21). \code{int8\_memory\_access} calcola \code{addr>>1} portando l'indirizzo
di byte a 23~bit in un indirizzo di parola a 22~bit che mappa esattamente su A0--A21; il
bit~22 di \code{psram\_a} è quindi sempre 0 e sul PCB restano 22 linee di indirizzo
reali.
\section{Larghezza di banda}
\label{sec:bandwidth}
Misurata sul gather della lista di edge di \code{graph\_engine} (cap.~\ref{ch:grafo}),
per differenza tra due dimensioni di grafo per isolare il costo per-edge dall'overhead
fisso per-neurone (\code{sim/graph\_engine\_bandwidth\_tb.v}):
\begin{tabularx}{\textwidth}{L{5.2cm} Y Y Y}
\toprule
\rowh \thd{} & \thd{Prima (no page mode)} & \thd{Dopo (page mode)} & \thd{$\Delta$} \\
\midrule
Cicli/edge & 53.25 & 37.53 & $-29.5\%$ \\
\rowa Banda @80\,MHz & 6.01\,MB/s & 8.53\,MB/s & $+41.9\%$ \\
Banda @16\,MHz\textsuperscript{*} & 1.20\,MB/s & 1.71\,MB/s & $+41.9\%$ \\
\bottomrule
\end{tabularx}
\textsuperscript{*}oscillatore reale raccomandato (cap.~\ref{ch:hw}).
Il modello resta comunque memory-bound per costruzione: \code{neuron\_memory} legge
$X$ una volta e rilegge $W$/bias per ciascun neurone (cap.~\ref{ch:seq}), un neurone
per volta; il page mode riduce il costo per-byte dell'accesso sequenziale, non elimina
il pattern di accesso stesso.