docs: LaTeX chapters for hardware, chip architecture, and tests/timing
Three chapters (docs/latex/), sourced from the real project docs (BOM.md, PHYSICAL_REALIZATION.md, ARCHITECTURE_ANALYSIS.md, experiments.log) -- no invented numbers: - hardware.tex: board components, DDR3/FPGA/flash/ESP32, real pinout, SPI protocol, boot procedure. - architecture.tex: packed INT8 MAC core, N=8 hybrid systolic architecture (shared-weight broadcast), hierarchical arbiter, result writeback, why N=8 is the DDR3-bandwidth-bound sweet spot. - tests_timing.tex: verification methodology, real functional test results table, full real P&R signoff history (N=2 through N=16), the definitive N=8 signoff (WNS=0.000ns), and an honestly-caveated ESP32-S3 comparison (real historical ECP5 measurement vs. the superseded, never-verified Artix-7 projection). main.tex ties the three together as report chapters. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MUG92aM9m68TRc4rG55BcC
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
\chapter{Architettura: come funziona il chip}
|
||||
\label{chap:architecture}
|
||||
|
||||
\section{Vista d'insieme}
|
||||
|
||||
Il design realizzato in FPGA \`e un acceleratore per reti neurali
|
||||
quantizzate INT8, organizzato come un insieme di \textbf{elementi di
|
||||
elaborazione} (Processing Element, PE) paralleli che condividono un
|
||||
unico canale DDR3 reale. La configurazione attualmente definitiva
|
||||
(Capitolo~\ref{chap:tests}) \`e \textbf{N=8}: 8 PE reali, organizzati
|
||||
in \textbf{2 gruppi da 4 PE ciascuno}, secondo un'architettura
|
||||
sistolica a \emph{broadcast dei pesi condiviso} (\emph{shared-weight
|
||||
broadcast}). Il modulo top-level reale \`e
|
||||
\texttt{hardware/v3/rtl/n8\_system\_ddr3\_top.v}.
|
||||
|
||||
L'intero design gira in un unico dominio di clock reale a
|
||||
\textbf{155.039\,MHz} (\texttt{ui\_clk}, derivato dal MIG DDR3,
|
||||
Capitolo~\ref{chap:hardware}).
|
||||
|
||||
\section{Il nucleo di calcolo: MAC INT8 impacchettati su DSP48E1}
|
||||
|
||||
L'unit\`a base di calcolo \`e \texttt{neural\_processor\_packed.v},
|
||||
usata identicamente in ogni PE. Ogni core usa \textbf{8 DSP48E1},
|
||||
ciascuno configurato per eseguire \textbf{due moltiplicazioni-accumulo
|
||||
INT8 impacchettate per ciclo} (una corsia A e una corsia B, che
|
||||
condividono lo stesso peso residente) --- una tecnica di packing reale
|
||||
verificata sia a livello RTL sia con sintesi Xilinx effettiva:
|
||||
|
||||
\[
|
||||
8~\text{DSP48E1} \times 2~\text{MAC/DSP} = 16~\text{MAC/ciclo per PE}
|
||||
\]
|
||||
|
||||
Al clock reale di 155.039\,MHz, il picco teorico per singolo PE \`e:
|
||||
|
||||
\[
|
||||
16~\text{MAC/ciclo} \times 155.039\times10^6~\text{cicli/s} = 2.48~\text{GMAC/s per PE (calcolo da dati misurati)}
|
||||
\]
|
||||
|
||||
Questo nucleo \`e rimasto \emph{invariato} (stesso numero di DSP,
|
||||
stessa struttura) dalla primissima sintesi reale del progetto fino
|
||||
alla configurazione N=8 attuale: \`e la parte pi\`u stabile ed efficiente
|
||||
del design, e non \`e mai stato il collo di bottiglia delle prestazioni
|
||||
(si veda \S\ref{sec:bottleneck}).
|
||||
|
||||
\section{L'architettura sistolica a gruppi (N=8)}
|
||||
|
||||
\subsection{Motivazione}
|
||||
|
||||
Scalare il numero di PE aumentando semplicemente il numero di core
|
||||
indipendenti significa aumentare linearmente anche il numero di
|
||||
richiedenti sul canale DDR3 condiviso --- ogni PE, in un design
|
||||
``piatto'', dovrebbe rifetchare autonomamente i pesi del layer anche
|
||||
quando pi\`u PE elaborano lo stesso layer in parallelo, sprecando
|
||||
banda DDR3 reale su dati identici gi\`a in transito per un altro PE.
|
||||
|
||||
L'architettura a gruppi risolve questo con un \textbf{unico fetch dei
|
||||
pesi per gruppo}, condiviso via broadcast da tutti i PE del gruppo:
|
||||
|
||||
\begin{itemize}
|
||||
\item Un gruppo (\texttt{systolic\_group.v}) possiede
|
||||
\textbf{un solo} percorso reale di prefetch pesi
|
||||
(\texttt{layer\_prefetch\_ctrl.v} + \texttt{layer\_weight\_buffer.v}
|
||||
+ \texttt{weight\_tile\_gather.v} --- moduli riusati integralmente
|
||||
e non modificati dal design N=2 originale).
|
||||
\item I 4 PE del gruppo (\texttt{packed\_pe.v}, una versione di
|
||||
\texttt{packed\_slot.v} privata del proprio percorso di fetch pesi)
|
||||
ricevono il tile di peso corrente via un bus di broadcast, tramite
|
||||
un meccanismo reale di sincronizzazione a barriera: il gruppo avanza
|
||||
al tile successivo solo quando \emph{tutti e 4} i PE hanno
|
||||
confermato (\texttt{pe\_tile\_ack}) il consumo del tile corrente.
|
||||
\item Ogni PE elabora indipendentemente e in parallelo le proprie
|
||||
posizioni di attivazione (nessuna propagazione di risultati tra PE
|
||||
--- non \`e uno shift register sistolico letterale, ma un vero
|
||||
broadcast condiviso: scelta esplicitamente confermata con l'utente
|
||||
dopo un confronto diretto tra le due alternative).
|
||||
\end{itemize}
|
||||
|
||||
Con questa scelta, il fetch pesi \`e ridotto di un fattore 4 per gruppo
|
||||
rispetto a un design piatto con lo stesso numero di PE, mentre il
|
||||
fetch delle attivazioni --- che non ha riuso possibile tra posizioni
|
||||
diverse --- resta uno per PE, esattamente come nel design N=2 originale.
|
||||
|
||||
\subsection{Struttura a N=8}
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{2 gruppi} $\times$ \textbf{4 PE} = 8 PE reali totali.
|
||||
\item \textbf{64 DSP48E1} totali (8 per PE $\times$ 8 PE).
|
||||
\item Il modulo \texttt{neural\_director\_grouped.v} dispatcha
|
||||
\textbf{ottetti} di job (8 posizioni che condividono la stessa base
|
||||
dei pesi) a un gruppo libero --- estensione diretta della disciplina
|
||||
di accoppiamento a coppie gi\`a usata dal Director originale N=2,
|
||||
generalizzata a 8 posizioni.
|
||||
\end{itemize}
|
||||
|
||||
Un fatto reale rilevante: il protocollo SPI verso l'host
|
||||
\textbf{non cambia affatto} passando da N=2 a N=8 --- l'host continua
|
||||
a sottomettere un job alla volta con la stessa struttura a 16 byte
|
||||
(\S\ref{sec:spi-protocol}); \`e il Director interno a raggruppare
|
||||
automaticamente gli 8 job pi\`u vecchi in coda quando condividono la
|
||||
stessa base dei pesi.
|
||||
|
||||
\section{Arbitraggio gerarchico dell'accesso a DDR3}
|
||||
|
||||
Con 8 PE reali pi\`u 2 unit\`a di fetch pesi di gruppo pi\`u l'accesso
|
||||
diretto dell'host, il numero di richiedenti reali sul singolo canale
|
||||
DDR3 fisico \`e 11. Un arbitro piatto a selezione singola (usato
|
||||
inizialmente per il tentativo a N=16) genera un multiplexer troppo
|
||||
largo, con un impatto reale misurato sul timing dominato dal
|
||||
\emph{routing} fisico (distanza tra le 20+ sorgenti sparse e il punto
|
||||
centrale di arbitraggio), non dalla profondit\`a logica.
|
||||
|
||||
La soluzione reale adottata \`e un \textbf{arbitro gerarchico a due
|
||||
livelli} (\texttt{sdram\_arbiter\_hier.v}):
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{Livello foglia} (uno per gruppo): un'istanza
|
||||
dell'arbitro N-way gi\`a esistente (\texttt{sdram\_arbiter\_n.v},
|
||||
riutilizzato \emph{senza modifiche}), che arbitra i 5 richiedenti
|
||||
locali del gruppo (1 fetch pesi + 4 PE), fisicamente vicini tra
|
||||
loro sul die.
|
||||
\item \textbf{Livello superiore}: una seconda istanza dello stesso
|
||||
arbitro, che arbitra tra i 2 gruppi e l'host, con un vero stadio
|
||||
di pipeline (un ciclo di registro) tra i due livelli --- questo
|
||||
\`e ci\`o che effettivamente permette al piazzatore/router di
|
||||
Vivado di distribuire il problema su due finestre di clock fisiche
|
||||
separate invece di forzare tutte le sorgenti a convergere in un
|
||||
solo ciclo.
|
||||
\item L'accesso dell'host \textbf{bypassa} il primo livello
|
||||
(collegamento diretto al livello superiore) poich\'e non ha mai
|
||||
rappresentato il collo di bottiglia reale.
|
||||
\end{itemize}
|
||||
|
||||
Un vincolo di correttezza gi\`a stabilito nel progetto (dalla prima
|
||||
versione dell'arbitro, 2026) \`e che un richiedente a impulso singolo
|
||||
deve vedere il proprio \emph{grant} nello stesso ciclo in cui asserisce
|
||||
per la prima volta il proprio segnale di attivit\`a --- altrimenti la
|
||||
richiesta viene persa silenziosamente. L'arbitro gerarchico preserva
|
||||
questa garanzia esattamente per ogni PE e per il fetch pesi di ogni
|
||||
gruppo (il grant al livello foglia resta puramente combinatorio); solo
|
||||
il transito effettivo del dato verso il controllore DDR3 fisico viene
|
||||
pipeline-ato, con un costo reale di 1--2 cicli aggiuntivi di latenza
|
||||
per transazione --- trascurabile rispetto alla latenza intrinseca di
|
||||
un vero accesso DDR3 (decine di cicli).
|
||||
|
||||
\section{Scrittura dei risultati}
|
||||
|
||||
Ogni PE, al termine del proprio job, scrive il risultato direttamente
|
||||
in DDR3 tramite \texttt{result\_writeback.v} (un'istanza per PE),
|
||||
usando lo stesso canale di controllo gi\`a condiviso con il fetch
|
||||
attivazioni. Questo elimina un vincolo reale di scalabilit\`a che
|
||||
affliggeva le prime versioni del progetto: esporre i risultati come
|
||||
pin fisici dedicati del package sarebbe stato insostenibile oltre
|
||||
poche unit\`a di PE (a N=16 avrebbe richiesto centinaia di pin).
|
||||
|
||||
\section{Il vincolo reale: banda DDR3, non conteggio dei DSP}
|
||||
\label{sec:bottleneck}
|
||||
|
||||
Un'analisi quantitativa reale (misurata, non stimata) condotta nelle
|
||||
prime fasi del progetto ha stabilito che il sistema \`e
|
||||
\textbf{limitato dalla banda DDR3, non dal numero di DSP}, gi\`a a
|
||||
partire da un solo core:
|
||||
|
||||
\begin{itemize}
|
||||
\item Banda DDR3 fisica reale misurata (canale a 16 bit, traccia
|
||||
JEDEC reale): 1.24\,GB/s.
|
||||
\item Banda richiesta da un singolo core al picco DSP teorico,
|
||||
con il packing delle attivazioni ottimizzato (1 byte DDR3 mosso
|
||||
per MAC utile): 2.48\,GB/s.
|
||||
\end{itemize}
|
||||
|
||||
L'allargamento del canale fisico DDR3 da 16 a 32 bit (due chip
|
||||
Micron in parallelo, Capitolo~\ref{chap:hardware}) raddoppia realmente
|
||||
il tetto fisico a \textbf{2.48\,GB/s}, confermato da un vero
|
||||
place-and-route con timing chiuso (\S~\ref{sec:pnr-history}), non da
|
||||
una proiezione.
|
||||
|
||||
L'occupazione reale di DSP48E1 resta comunque bassa anche alla
|
||||
configurazione N=8 (64/240, 26.7\%) --- conferma diretta che il
|
||||
margine di scalabilit\`a residuo del chip \`e ampio sul lato
|
||||
computazionale, ma \`e vincolato dal canale di memoria condiviso, non
|
||||
dalla logica di calcolo.
|
||||
|
||||
\section{Configurazioni alternative esplorate}
|
||||
|
||||
Lo stesso RTL sistolico, tramite il parametro reale \texttt{N\_GROUPS},
|
||||
\`e stato realmente sintetizzato e verificato anche a:
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{N=4} (1 gruppo, 4 PE, 32 DSP48E1) --- funzionante,
|
||||
margine di timing quasi nullo.
|
||||
\item \textbf{N=16} (4 gruppi, 16 PE, 128 DSP48E1) --- funzionalmente
|
||||
verificato su DDR3 reale, ma con temporizzazione non ancora chiusa
|
||||
(si veda \S~\ref{sec:pnr-history}) --- mantenuto come lavoro futuro
|
||||
documentato, non abbandonato.
|
||||
\end{itemize}
|
||||
|
||||
N=8 \`e stata scelta come configurazione reale definitiva perch\'e
|
||||
\`e la pi\`u grande che chiude realmente il timing con margine
|
||||
positivo (Capitolo~\ref{chap:tests}).
|
||||
@@ -0,0 +1,290 @@
|
||||
\chapter{Descrizione hardware}
|
||||
\label{chap:hardware}
|
||||
|
||||
\section{Panoramica della scheda}
|
||||
|
||||
Il progetto realizza un acceleratore neurale su una scheda custom
|
||||
(non una dev-board commerciale), basata su un componente FPGA nudo
|
||||
Xilinx Artix-7, affiancato da memoria DDR3 reale, una flash di
|
||||
configurazione dedicata e un microcontrollore ESP32 come processore
|
||||
host/centrale. La scheda non utilizza moduli di sviluppo preassemblati:
|
||||
ogni parte è stata scelta, verificata in reperibilità reale (LCSC) e
|
||||
posizionata pin-per-pin a partire da un vero place-and-route Vivado,
|
||||
non da valori stimati.
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
\begin{tabular}{lll}
|
||||
\toprule
|
||||
\textbf{Componente} & \textbf{Parte} & \textbf{Note} \\
|
||||
\midrule
|
||||
FPGA & XC7A100T-CSG324-2 & Speed grade $-2$ \\
|
||||
DDR3 SDRAM & 2$\times$ Micron MT41J128M16JT-125:K & 2Gb, x16, canale fisico a 32 bit \\
|
||||
Flash di configurazione & Winbond W25Q32JVSSIQ & 32Mbit, SOIC-8, esclusiva della FPGA \\
|
||||
Host / processore centrale & ESP32-S3-WROOM-1-N16R8 & 16MB flash, 8MB PSRAM \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{Componenti principali, tutti verificati realmente reperibili su LCSC.}
|
||||
\end{table}
|
||||
|
||||
\section{FPGA: Xilinx XC7A100T-CSG324-2}
|
||||
|
||||
Il componente centrale è un Artix-7 XC7A100T, package CSG324 (324 BGA),
|
||||
speed grade $-2$. La scelta dello speed grade $-2$ (corretta rispetto
|
||||
a un'ipotesi iniziale $-1$) non comporta alcuna differenza di die,
|
||||
package o footprint: offre solamente un margine di timing strettamente
|
||||
migliore, confermato dai reali segni di place-and-route (Capitolo~\ref{chap:tests}).
|
||||
|
||||
Risorse rilevanti utilizzate dal design (target reale N=8,
|
||||
Capitolo~\ref{chap:architecture}):
|
||||
|
||||
\begin{itemize}
|
||||
\item 240 DSP48E1 totali disponibili; 64 utilizzati (26.7\%).
|
||||
\item 63400 LUT disponibili; 12535 utilizzate (19.77\%).
|
||||
\item 126800 registri disponibili; 19902 utilizzati (15.70\%).
|
||||
\item Nessuna BRAM utilizzata.
|
||||
\end{itemize}
|
||||
|
||||
\section{Memoria DDR3}
|
||||
|
||||
Il canale DDR3 è realizzato con due chip Micron
|
||||
MT41J128M16JT-125:K (2Gb, x16, DDR3-1600) collegati in parallelo,
|
||||
formando un canale fisico reale a \textbf{32 bit} (bus dati raddoppiato
|
||||
rispetto alla configurazione iniziale a 16 bit di un solo chip). Le linee
|
||||
di indirizzo/comando/controllo sono condivise e distribuite
|
||||
identicamente a entrambi i chip; le linee DQ/DQS/DM sono invece divise
|
||||
16 bit per chip.
|
||||
|
||||
Il canale DDR3 è pilotato dal MIG (Memory Interface Generator) di
|
||||
Xilinx, generato realmente per questo esatto part number. I due
|
||||
domini di clock reali coinvolti sono:
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{sys\_clk\_p/n} --- clock differenziale della PHY
|
||||
DDR3, \textbf{310.078\,MHz} (periodo 3.225\,ns).
|
||||
\item \textbf{ui\_clk / clk\_pll\_i} --- dominio di calcolo interno,
|
||||
derivato dal PLL del MIG con rapporto 2:1 dal clock PHY,
|
||||
\textbf{155.039\,MHz}. Tutta la logica neurale (Director, arbitraggio,
|
||||
PE) gira in questo dominio.
|
||||
\end{itemize}
|
||||
|
||||
Questa combinazione di frequenze non è una scelta libera: è l'unica
|
||||
uscita reale del calcolatore JEDEC/PLL del wizard MIG di Vivado per
|
||||
questo speed grade e questa larghezza di canale, e determina
|
||||
direttamente il vincolo di temporizzazione usato in ogni place-and-route
|
||||
reale del progetto (Capitolo~\ref{chap:tests}).
|
||||
|
||||
\subsection{Convenzione di indirizzamento in DDR3}
|
||||
|
||||
Pesi e attivazioni condividono lo stesso spazio di indirizzi DDR3,
|
||||
indirizzato a parola. La parola nativa è a 32 bit
|
||||
(\texttt{BURST\_LEN=8} per transazione, cio\`e 256 bit/burst).
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{Pesi}: l'insieme di pesi di un layer parte all'indirizzo
|
||||
parola \texttt{layer\_index * WORDS\_PER\_LAYER}, letto in burst
|
||||
sequenziali in un buffer on-chip una sola volta per job (riuso reale
|
||||
attraverso pi\`u posizioni).
|
||||
\item \textbf{Attivazioni}: quattro tile consecutivi (8 valori INT8
|
||||
ciascuno) condividono un solo burst da 256 bit; la selezione del
|
||||
quarto di burst \`e latenziata al momento della richiesta (non un
|
||||
multiplexer indicizzato a runtime), preservando il margine di
|
||||
temporizzazione.
|
||||
\end{itemize}
|
||||
|
||||
\section{Flash di configurazione}
|
||||
|
||||
Una Winbond W25Q32JVSSIQ (32Mbit / 4MB, SOIC-8) ospita il bitstream
|
||||
di configurazione (circa 30.5Mbit per un XC7A100T pieno) ed \`e
|
||||
collegata \textbf{esclusivamente} alla FPGA --- l'ESP32 non ha alcun
|
||||
collegamento elettrico diretto ad essa. L'unico percorso elettrico
|
||||
dall'ESP32 alla flash \`e un relay software attraverso la FPGA
|
||||
(opcode SPI \texttt{0x40 FLASH\_XFER}, \S\ref{sec:spi-protocol}).
|
||||
|
||||
\section{Host: ESP32-S3}
|
||||
|
||||
Il processore host \`e un modulo ESP32-S3-WROOM-1-N16R8 (16MB flash,
|
||||
8MB PSRAM, dual-core), verificato realmente reperibile su LCSC. Comunica
|
||||
con la FPGA tramite un bus SPI dedicato dove la \textbf{FPGA \`e
|
||||
slave}, e --- tramite la FPGA come relay --- raggiunge la flash di
|
||||
configurazione su un secondo bus SPI fisicamente distinto dove la
|
||||
\textbf{FPGA \`e master}.
|
||||
|
||||
\section{Assegnazione dei pin (reale, dal design instradato)}
|
||||
|
||||
Tutti i pin elencati sotto provengono da un vero file di vincoli
|
||||
generato da Vivado (XDC), non da valori ipotizzati.
|
||||
|
||||
\subsection{DDR3 (fissati dall'hardware PHY della FPGA)}
|
||||
|
||||
I pin DDR3 sono generati dal wizard MIG e non sono una scelta libera:
|
||||
banchi \textbf{34/35}, standard \texttt{SSTL15}/\texttt{DIFF\_SSTL15}
|
||||
(1.5V). Il clock di riferimento differenziale \texttt{clk\_ref\_p/n}
|
||||
vive necessariamente nel \textbf{banco 14} (regola di piazzamento
|
||||
UG586 del wizard per questo package), a \textbf{2.5V} (\texttt{LVDS\_25}) ---
|
||||
un vincolo che ha richiesto lo spostamento del bus SPI della flash
|
||||
(si veda sotto), poich\'e un banco pu\`o avere una sola tensione VCCO.
|
||||
|
||||
\subsection{SPI di gestione (ESP32 $\leftrightarrow$ FPGA, FPGA slave)}
|
||||
|
||||
Banco 15, colonna di bordo del package (tracce corte), \texttt{LVCMOS33}:
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
\begin{tabular}{lll}
|
||||
\toprule
|
||||
\textbf{Segnale} & \textbf{Pin} & \textbf{Direzione (lato FPGA)} \\
|
||||
\midrule
|
||||
sclk & A15 & input \\
|
||||
mosi & B16 & input \\
|
||||
miso & B17 & output \\
|
||||
cs\_n & A16 & input \\
|
||||
sys\_rst & G13 & input (pin provvisorio) \\
|
||||
data\_ready\_n & D14 & output (IRQ sticky attivo-basso) \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{Pinout del bus SPI di gestione, banco 15, VCCO assunto 3.3V.}
|
||||
\end{table}
|
||||
|
||||
\subsection{SPI della flash di configurazione (FPGA $\leftrightarrow$ flash, FPGA master)}
|
||||
|
||||
Originariamente sui pin dedicati di configurazione Master-SPI della
|
||||
FPGA (banco 14); spostati nel \textbf{banco 16} per risolvere un
|
||||
conflitto reale di VCCO con \texttt{clk\_ref} (rilevato da un vero
|
||||
fallimento di \texttt{place\_design}, non ipotizzato):
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
\begin{tabular}{llll}
|
||||
\toprule
|
||||
\textbf{Segnale} & \textbf{Pin} & \textbf{Direzione} & \textbf{Note} \\
|
||||
\midrule
|
||||
flash\_mosi & D9 & output & banco 16 (era K17, banco 14) \\
|
||||
flash\_miso & D10 & input & banco 16 (era K18, banco 14) \\
|
||||
flash\_cs\_n & C9 & output & banco 16 (era L13, banco 14) \\
|
||||
(CCLK) & E9 & output & pilotato internamente via \texttt{STARTUPE2} \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{Pinout del bus SPI della flash, banco 16, VCCO 3.3V.}
|
||||
\end{table}
|
||||
|
||||
\subsection{Controllo di configurazione FPGA (banco 0, non negoziabile)}
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
\begin{tabular}{ll}
|
||||
\toprule
|
||||
\textbf{Segnale} & \textbf{Pin} \\
|
||||
\midrule
|
||||
PROGRAM\_B & P9 \\
|
||||
INIT\_B & P7 \\
|
||||
DONE & P10 \\
|
||||
M0 / M1 / M2 & P12 / P13 / P11 \\
|
||||
CFGBVS & P8 \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
Per il boot autonomo da Master SPI: \texttt{M[2:0] = 001}.
|
||||
|
||||
\subsection{JTAG}
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
\begin{tabular}{ll}
|
||||
\toprule
|
||||
\textbf{Segnale} & \textbf{Pin} \\
|
||||
\midrule
|
||||
TCK & E10 \\
|
||||
TDI & E11 \\
|
||||
TMS & E12 \\
|
||||
TDO & E13 \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
Usato per la programmazione di fabbrica/recupero quando la flash \`e
|
||||
vuota, e per debug --- pilotato via bit-banging da firmware ESP32
|
||||
(non ancora implementato, lavoro software separato).
|
||||
|
||||
\section{Alimentazione (stato di avanzamento)}
|
||||
|
||||
La ricerca dei componenti di alimentazione \`e parziale e onestamente
|
||||
dichiarata incompleta:
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{VCCINT} (1.0V, alimentazione core FPGA): candidato
|
||||
reale identificato, Infineon IR38060 (SupIRBuck integrato, fino a
|
||||
6A) --- \textbf{stock non confermato}.
|
||||
\item \textbf{VTT} (terminazione DDR3, opzionale): candidato reale
|
||||
TI TPS51200 --- la necessit\`a effettiva dipende dalla lunghezza
|
||||
reale delle tracce sul layout, non ancora stabilita da questo
|
||||
progetto (nessuna simulazione di signal integrity eseguita).
|
||||
\item \textbf{VCCAUX (1.8V) e VCCO per banco} (1.5V banchi 34/35,
|
||||
2.5V banco 14, 3.3V banchi 15/16): \textbf{non ancora ricercati}.
|
||||
\end{itemize}
|
||||
|
||||
\section{Oscillatori di clock}
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
\begin{tabular}{lll}
|
||||
\toprule
|
||||
\textbf{Segnale} & \textbf{Frequenza richiesta} & \textbf{Stato} \\
|
||||
\midrule
|
||||
sys\_clk\_p/n & 310.077\,MHz differenziale & Nessun SKU a catalogo a questa
|
||||
esatta frequenza; candidato reale SiTime SiT9122 (oscillatore MEMS
|
||||
programmabile 220--625\,MHz) --- richiede un ordine a specifica, non
|
||||
ancora confermato in stock. \\
|
||||
clk\_ref\_p/n & 200\,MHz differenziale LVDS & \textbf{Confermato reale,
|
||||
in stock}: SiTime SiT9121AC-2CF-33E-200.00000, LCSC C835051. \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
\section{Protocollo SPI di gestione (per il firmware ESP32)}
|
||||
\label{sec:spi-protocol}
|
||||
|
||||
Un byte di opcode (MSB-first) per transazione CS-basso, gestito da
|
||||
\texttt{spi\_host\_bridge\_v3.v}:
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
\begin{tabular}{lp{2.3cm}p{3.5cm}p{5cm}}
|
||||
\toprule
|
||||
\textbf{Opcode} & \textbf{Nome} & \textbf{Payload} & \textbf{Scopo} \\
|
||||
\midrule
|
||||
0x00 & NOP & 0 byte & inerte \\
|
||||
0x0F & RESET & 0 byte & soft-reset \\
|
||||
0x10 & WRITE\_JOB & 16 byte & sottomette un job di inferenza \\
|
||||
0x20 & STATUS & 0 $\to$ 1 byte & bit di stato (busy, ultimo job accettato) \\
|
||||
0x01 & WRITE\_MEM & $4+2N$ byte & scrittura raw in DDR3 ($N$ parole) \\
|
||||
0x02 & READ\_MEM & 6 $\to$ 2$N$ byte & lettura raw da DDR3 ($N$ parole) \\
|
||||
0x30 & REG\_WRITE & 5 byte & scrive un registro di controllo \\
|
||||
0x31 & REG\_READ & 1 $\to$ 4 byte & legge un registro di stato/ID \\
|
||||
0x40 & FLASH\_XFER & $N \to N{+}2$ byte & passthrough grezzo verso la flash \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{Riepilogo del protocollo SPI di gestione. Il layout completo dei campi \`e documentato nell'intestazione di \texttt{spi\_host\_bridge\_v3.v}.}
|
||||
\end{table}
|
||||
|
||||
Un pin dedicato \texttt{data\_ready\_n} (attivo-basso, sticky) permette
|
||||
all'ESP32 di essere interrupt-driven invece di eseguire polling
|
||||
continuo su STATUS.
|
||||
|
||||
\section{Procedura di avvio (boot)}
|
||||
|
||||
Due percorsi reali, entrambi presenti sulla scheda per progetto:
|
||||
|
||||
\begin{enumerate}
|
||||
\item \textbf{Primo avvio / recupero (JTAG, pilotato da ESP32)}:
|
||||
su una scheda vergine la flash \`e vuota --- solo JTAG pu\`o
|
||||
inizializzarla (vincolo reale, non aggirabile via SPI finch\'e
|
||||
la FPGA non esegue gi\`a la logica che relaya i comandi).
|
||||
\item \textbf{Avvio normale (Master SPI, autonomo)}: a ogni
|
||||
accensione successiva la FPGA si auto-configura dalla flash
|
||||
tramite il proprio hardware dedicato, senza intervento dell'ESP32.
|
||||
\item \textbf{Aggiornamento firmware sul campo} (\texttt{FLASH\_XFER}):
|
||||
a FPGA gi\`a in funzione, l'ESP32 pu\`o riscrivere la flash
|
||||
relayando byte SPI-NOR grezzi attraverso la FPGA.
|
||||
\end{enumerate}
|
||||
@@ -0,0 +1,43 @@
|
||||
\documentclass[11pt,a4paper]{report}
|
||||
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[italian]{babel}
|
||||
\usepackage[margin=2.5cm]{geometry}
|
||||
\usepackage{booktabs}
|
||||
\usepackage{longtable}
|
||||
\usepackage{array}
|
||||
\usepackage{xcolor}
|
||||
\usepackage{hyperref}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{listings}
|
||||
\usepackage{caption}
|
||||
|
||||
\hypersetup{
|
||||
colorlinks=true,
|
||||
linkcolor=blue!50!black,
|
||||
urlcolor=blue!50!black,
|
||||
citecolor=blue!50!black
|
||||
}
|
||||
|
||||
\lstset{
|
||||
basicstyle=\ttfamily\small,
|
||||
breaklines=true,
|
||||
frame=single,
|
||||
columns=fullflexible
|
||||
}
|
||||
|
||||
\title{FPGA-Neural \\ \large Acceleratore neurale su FPGA Xilinx Artix-7}
|
||||
\author{}
|
||||
\date{Stato al 2026-09-21 --- target reale definitivo: N=8}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\maketitle
|
||||
\tableofcontents
|
||||
|
||||
\input{hardware.tex}
|
||||
\input{architecture.tex}
|
||||
\input{tests_timing.tex}
|
||||
|
||||
\end{document}
|
||||
@@ -0,0 +1,198 @@
|
||||
\chapter{Verifica funzionale, timing e confronti}
|
||||
\label{chap:tests}
|
||||
|
||||
\section{Metodologia di verifica}
|
||||
|
||||
Il progetto segue una disciplina di verifica a due livelli, applicata
|
||||
sistematicamente a ogni modulo nuovo prima di fidarsi di un risultato
|
||||
di sintesi o di place-and-route:
|
||||
|
||||
\begin{enumerate}
|
||||
\item \textbf{Verifica isolata} (Icarus Verilog / \texttt{iverilog}):
|
||||
ogni modulo nuovo \`e verificato da solo, con un modello di memoria
|
||||
comportamentale semplificato, prima di essere collegato al resto
|
||||
del sistema (``one variable at a time'').
|
||||
\item \textbf{Verifica funzionale reale su DDR3} (Vivado \texttt{xsim}):
|
||||
il sistema completo (o una sua configurazione reale, es. N=8) \`e
|
||||
simulato contro il modello DDR3 \emph{reale} fornito da Xilinx
|
||||
(\texttt{ddr3\_model.sv}, lo stesso modello usato per la
|
||||
certificazione del MIG), con calibrazione realistica e tracce di
|
||||
comando JEDEC reali --- non un modello di memoria semplificato.
|
||||
\end{enumerate}
|
||||
|
||||
Solo dopo che entrambi i livelli passano si procede a un vero
|
||||
place-and-route in contesto (\texttt{synth\_design} + \texttt{opt\_design}
|
||||
+ \texttt{place\_design} + \texttt{route\_design}), sulla parte reale
|
||||
XC7A100T-CSG324-2, mai fuori contesto e mai stimato.
|
||||
|
||||
\section{Risultati della verifica funzionale (reali, non stimati)}
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
\begin{tabular}{lll}
|
||||
\toprule
|
||||
\textbf{Modulo / testbench} & \textbf{Risultato} & \textbf{Note} \\
|
||||
\midrule
|
||||
\texttt{tb\_mig\_native\_adapter.v} & 12/12 PASS & adattatore DDR3 nativo, contro il modello DDR3 reale \\
|
||||
\texttt{tb\_n2\_system\_ddr3.v} & 8/8 PASS & sistema N=2 completo su DDR3 reale \\
|
||||
\texttt{tb\_packed\_slot.v} & 9/9 PASS & include verifica read-after-write reale in DDR3 \\
|
||||
\texttt{tb\_spi\_host\_bridge\_v3.v} & 49/49 PASS & protocollo SPI di gestione completo \\
|
||||
\texttt{tb\_host\_mem\_bridge.v} & 16/16 PASS & percorso di accesso raw DDR3 dell'host \\
|
||||
\texttt{tb\_systolic\_group.v} & 8/8 PASS & un gruppo sistolico isolato, 2 job consecutivi \\
|
||||
\texttt{tb\_neural\_director\_grouped.v} & 4/4 PASS & dispatch a ottetti, stallo su mismatch, wraparound \\
|
||||
\texttt{tb\_sdram\_arbiter\_hier.v} & 23/23 PASS & arbitro gerarchico, contesa cross-gruppo e host \\
|
||||
\texttt{tb\_n16\_system\_ddr3.v} & 32/32 PASS & sistema N=16 completo, su DDR3 reale \\
|
||||
\texttt{tb\_n8\_system\_ddr3.v} & \textbf{16/16 PASS} & \textbf{sistema N=8 completo, su DDR3 reale (target definitivo)} \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{Sottoinsieme rappresentativo dei test funzionali reali eseguiti nel progetto. Ogni riga \`e un vero risultato di simulazione, non una stima.}
|
||||
\end{table}
|
||||
|
||||
\section{Cronologia reale dei segni di timing (place-and-route)}
|
||||
\label{sec:pnr-history}
|
||||
|
||||
Ogni riga della tabella seguente \`e un vero risultato di
|
||||
\texttt{report\_timing\_summary} dopo un vero \texttt{route\_design},
|
||||
sulla stessa parte fisica (XC7A100T-CSG324-2) e sullo stesso dominio
|
||||
di clock reale (155.039\,MHz, \texttt{clk\_pll\_i}) --- mai una stima
|
||||
di sintesi fuori contesto.
|
||||
|
||||
\begin{longtable}{p{1.3cm}p{3.7cm}p{0.9cm}p{2.1cm}p{1.3cm}p{1.3cm}}
|
||||
\toprule
|
||||
\textbf{Exp.} & \textbf{Cosa \`e cambiato} & \textbf{N} & \textbf{WNS (ns)} & \textbf{LUT} & \textbf{DSP48E1} \\
|
||||
\midrule
|
||||
\endhead
|
||||
0074 & prima vera P\&R in contesto: DDR3 + pin & 2 & +0.040 & 5140 & 16 \\
|
||||
0076 & + registri, + pin, + fix SPI & 2 & +0.056 & 5173 & 16 \\
|
||||
0078 & + bridge flash (STARTUPE2 reale) & 2 & +0.013 & 5213 & 16 \\
|
||||
0079 & + motore attivazioni reale & 2 & +0.030 & 5379 & 16 \\
|
||||
0082 & + packing attivazioni pi\`u denso & 2 & +0.068 & 5437 & 16 \\
|
||||
0083 & + DDRManager fase 1, canale 16 bit & 2 & +0.073 & 5644 & 16 \\
|
||||
0084 & canale DDR3 a 32 bit + clock pi\`u veloce (172.4\,MHz) & 2 & $-0.618$ \textbf{(FALLITO)} & 6418 & 16 \\
|
||||
0086 & canale a 32 bit, clock ripristinato a 155.039\,MHz & 2 & +0.096 \textbf{(CHIUSO)} & 6382 & 16 \\
|
||||
0088 & + motore di scrittura risultati in DDR3 & 2 & +0.100 \textbf{(CHIUSO)} & 6642 & 16 \\
|
||||
0093 & architettura sistolica, arbitro piatto a 21 vie & 16 & $-0.913$ \textbf{(FALLITO)} & 19751 & 128 \\
|
||||
0094 & + arbitro gerarchico a 2 livelli & 16 & $-0.646$ (ancora fallito) & 19936 & 128 \\
|
||||
0094 & + tuning delle direttive P\&R & 16 & $-0.338$ (ancora fallito) & 19936 & 128 \\
|
||||
0095 & curva N=4 (1 gruppo) & 4 & $-0.005$ (2 endpoint falliti) & 8794 & 32 \\
|
||||
\textbf{0095/0096} & \textbf{\texttt{n8\_system\_ddr3\_top.v}, definitivo} & \textbf{8} & \textbf{0.000 (CHIUSO)} & \textbf{12535} & \textbf{64} \\
|
||||
\bottomrule
|
||||
\caption{Cronologia reale dei segni di place-and-route, dal primo P\&R in contesto fino al target definitivo N=8.}
|
||||
\end{longtable}
|
||||
|
||||
\subsection{Il segno definitivo: N=8}
|
||||
|
||||
\begin{table}[h]
|
||||
\centering
|
||||
\begin{tabular}{ll}
|
||||
\toprule
|
||||
\textbf{Metrica} & \textbf{Valore reale} \\
|
||||
\midrule
|
||||
Clock PHY DDR3 (sys\_clk\_p/n) & 310.078\,MHz (periodo 3.225\,ns) \\
|
||||
Clock di calcolo (ui\_clk/clk\_pll\_i) & 155.039\,MHz \\
|
||||
WNS (setup slack) & \textbf{0.000\,ns} --- chiuso, 0 endpoint falliti \\
|
||||
WHS (hold slack) & +0.017\,ns \\
|
||||
Endpoint falliti & 0 su 63212 (setup), 0 su 63209 (hold) \\
|
||||
LUT utilizzate & 12535 / 63400 (19.77\%) \\
|
||||
Registri utilizzati & 19902 / 126800 (15.70\%) \\
|
||||
DSP48E1 utilizzati & 64 / 240 (26.7\%) \\
|
||||
Parallelismo reale & 8 elementi di elaborazione paralleli \\
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\caption{Segno di temporizzazione reale, definitivo, per \texttt{n8\_system\_ddr3\_top.v} (EXP-0095/0096).}
|
||||
\end{table}
|
||||
|
||||
\textbf{Nota importante, dichiarata onestamente}: il margine WNS=0.000\,ns
|
||||
\`e \emph{esattamente} zero --- reale e chiuso, ma senza alcuno slack di
|
||||
riserva. Qualunque futura modifica RTL a questo top-level o ai suoi
|
||||
moduli dipendenti richiede un nuovo, vero place-and-route (con la
|
||||
stessa sequenza di direttive: \texttt{opt\_design -directive Explore},
|
||||
\texttt{place\_design -directive ExtraNetDelay\_high},
|
||||
\texttt{phys\_opt\_design -directive AggressiveExplore},
|
||||
\texttt{route\_design -directive AggressiveExplore}) prima di
|
||||
fidarsi nuovamente della temporizzazione.
|
||||
|
||||
\subsection{Il collo di bottiglia reale trovato a N=16}
|
||||
|
||||
Il percorso critico che impedisce la chiusura del timing a N=16
|
||||
\`e stato tracciato realmente (non ipotizzato) fino all'interno del
|
||||
datapath MAC di \texttt{neural\_processor\_packed.v} --- lo stesso
|
||||
nucleo di calcolo descritto nel Capitolo~\ref{chap:architecture},
|
||||
invariato dalla prima versione del progetto, che a N=2 chiudeva gi\`a
|
||||
con un margine estremamente sottile (+0.0999\,ns). A N=16 la maggiore
|
||||
occupazione complessiva del die (31\% LUT) aumenta la congestione di
|
||||
piazzamento a sufficienza da erodere quel margine gi\`a minimo ---
|
||||
un problema diffuso di congestione, non un singolo collo di bottiglia
|
||||
strutturale come quello, gi\`a risolto, dell'arbitro.
|
||||
|
||||
\section{Confronto con un riferimento reale: ESP32-S3}
|
||||
\label{sec:esp32-comparison}
|
||||
|
||||
Un confronto quantitativo con un microcontrollore ESP32-S3 esiste,
|
||||
ma va presentato con la dovuta cautela storica, per non presentare
|
||||
come attuale un dato ormai superato.
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{Dato reale, misurato (architettura precedente, ECP5)}:
|
||||
la versione precedente del progetto (v2, su FPGA Lattice ECP5,
|
||||
ora archiviata) ha misurato realmente uno speedup di
|
||||
\textbf{$\sim$9.5--14$\times$ rispetto a ESP32-S3} (baseline
|
||||
misurata) per un carico di lavoro tipo riconoscimento facciale
|
||||
(classe MobileFaceNet), con 16 core paralleli a 64--97\,MHz.
|
||||
\item \textbf{Proiezione non verificata (superata)}: da quel dato
|
||||
era stata derivata una proiezione di $\sim$55--85$\times$ su
|
||||
ESP32-S3 per l'architettura Artix-7, basata su un'ipotesi di
|
||||
\textbf{30 core paralleli} limitati solo dal conteggio dei DSP
|
||||
disponibili. Questa proiezione \textbf{non \`e mai stata verificata}
|
||||
con una misura reale a livello di sistema completo, ed \`e oggi
|
||||
superata dalla scoperta successiva (\S~\ref{sec:bottleneck}) che il
|
||||
sistema \`e limitato dalla banda DDR3, non dal conteggio dei DSP ---
|
||||
l'ipotesi dei 30 core non \`e pi\`u realistica alla luce di questo
|
||||
vincolo.
|
||||
\end{itemize}
|
||||
|
||||
\textbf{Stato onesto attuale}: non esiste ancora una misura reale,
|
||||
diretta, di throughput aggregato (inferenze al secondo, o MAC/s
|
||||
sostenuti) della configurazione N=8 definitiva confrontata con un
|
||||
benchmark ESP32-S3 aggiornato. Il picco teorico calcolabile per N=8
|
||||
\`e:
|
||||
|
||||
\[
|
||||
8~\text{PE} \times 16~\text{MAC/ciclo} \times 155.039\times10^6~\text{cicli/s} = 19.84~\text{GMAC/s di picco teorico aggregato}
|
||||
\]
|
||||
|
||||
ma questo \`e un limite superiore puramente computazionale: dato il
|
||||
vincolo reale di banda DDR3 (2.48\,GB/s fisici, condivisi tra tutti
|
||||
gli 8 PE), il throughput realmente sostenibile in un carico di lavoro
|
||||
reale sar\`a inferiore, nella stessa misura gi\`a documentata a N=1/2
|
||||
(\S~\ref{sec:bottleneck}) --- una misura reale e diretta di questo
|
||||
throughput sostenuto a N=8, e un nuovo confronto onesto con un
|
||||
benchmark ESP32-S3 aggiornato, restano lavoro futuro non ancora
|
||||
eseguito.
|
||||
|
||||
\section{Lezioni reali dal processo di verifica}
|
||||
|
||||
Alcuni bug reali, trovati e corretti durante la verifica di questo
|
||||
progetto, meritano di essere documentati perch\'e generalizzabili:
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{Impulsi a colpo singolo persi al confine
|
||||
dell'arbitraggio}: un richiedente che genera un impulso di
|
||||
richiesta di un solo ciclo pu\`o essere perso se l'elemento che lo
|
||||
riceve non \`e ancora pronto a elaborarlo nello stesso ciclo ---
|
||||
causa originaria di un bug reale gi\`a nella primissima versione
|
||||
dell'arbitro, e riemerso (in una forma nuova, al confine tra i due
|
||||
livelli dell'arbitro gerarchico) durante lo sviluppo di N=16/N=8;
|
||||
risolto con un'aggancio (\emph{latch}) persistente per richiesta,
|
||||
non con un semplice registro.
|
||||
\item \textbf{Corse (race condition) nei testbench}: pilotare gli
|
||||
stimoli sullo stesso fronte di clock campionato dal modulo sotto
|
||||
test, specialmente in chiamate ravvicinate senza un ciclo di
|
||||
margine naturale, pu\`o causare doppie registrazioni silenziose ---
|
||||
risolto pilotando gli stimoli sul fronte opposto.
|
||||
\item \textbf{Casi limite di larghezza di bus}: espressioni come
|
||||
$\log_2(N)$ diventano zero (e quindi un intervallo di bit non
|
||||
valido) quando $N=1$, un caso non testato fino all'esplorazione
|
||||
della curva N=4/8/16 --- risolto con un valore minimo di 1 bit
|
||||
esplicito.
|
||||
\end{itemize}
|
||||
Reference in New Issue
Block a user