diff --git a/docs/latex/architecture.tex b/docs/latex/architecture.tex new file mode 100644 index 0000000..48d69f9 --- /dev/null +++ b/docs/latex/architecture.tex @@ -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}). diff --git a/docs/latex/hardware.tex b/docs/latex/hardware.tex new file mode 100644 index 0000000..f656f37 --- /dev/null +++ b/docs/latex/hardware.tex @@ -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} diff --git a/docs/latex/main.tex b/docs/latex/main.tex new file mode 100644 index 0000000..18a66e6 --- /dev/null +++ b/docs/latex/main.tex @@ -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} diff --git a/docs/latex/tests_timing.tex b/docs/latex/tests_timing.tex new file mode 100644 index 0000000..0101da8 --- /dev/null +++ b/docs/latex/tests_timing.tex @@ -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}