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
199 lines
10 KiB
TeX
199 lines
10 KiB
TeX
\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}
|