\chapter[Architettura RTL]{Architettura RTL e gerarchia dei moduli} \label{ch:arch} \section{Organizzazione gerarchica} Il design è organizzato per livelli, dal moltiplicatore-accumulatore elementare fino al top-level integrato con interfaccia SPI e PSRAM. Ogni livello incapsula il precedente e ne astrae i dettagli: il datapath validato (\code{mac\_unit}, \code{mac8}, \code{neuron\_parallel}) non viene mai modificato dai livelli di orchestrazione superiori. \begin{center} \begin{tikzpicture}[font=\footnotesize,every node/.style={fnblock,minimum width=40mm}, level distance=13mm,sibling distance=0mm] \node[fnblockD,minimum width=62mm](top){\code{spi\_neuron\_top} \\ {\scriptsize top-level integrato}}; \node[fnblockT,minimum width=62mm,below=8mm of top](arb){\code{mem\_arbiter} \;/\; \code{layer\_sequencer} \\ {\scriptsize arbitraggio 3 porte + sequenza layer}}; \node[fnblock,minimum width=62mm,below=8mm of arb](nm){\code{neuron\_memory} \\ {\scriptsize ponte memoria $\leftrightarrow$ neurone, loop neuroni}}; \node[fnblock,minimum width=62mm,below=8mm of nm](np){\code{neuron\_parallel} \\ {\scriptsize FSM neurone: gruppi, bias, attivazione, saturazione}}; \node[fnblockT,minimum width=62mm,below=8mm of np](m8){\code{mac8} \\ {\scriptsize \code{PARALLEL} MAC + balanced adder tree}}; \node[fnblock,minimum width=62mm,below=8mm of m8](mu){\code{mac\_unit} \\ {\scriptsize $x\cdot w$ + estensione segno + accumulo}}; \foreach \a/\b in {top/arb,arb/nm,nm/np,np/m8,m8/mu} \draw[fnarrow] (\a) -- (\b); % rami memoria a destra \node[fnblockA,minimum width=34mm,right=14mm of nm](ma){\code{int8\_memory\_access}\\{\scriptsize byte $\leftrightarrow$ word 16-bit}}; \node[fnblockA,minimum width=34mm,below=6mm of ma](mi){\code{memory\_interface}\\{\scriptsize handshake req/ready}}; \node[fnblockA,minimum width=34mm,below=6mm of mi](pc){\code{psram\_controller}\\{\scriptsize bus fisico PSRAM}}; \draw[fnarrowT] (ma)--(mi); \draw[fnarrowT] (mi)--(pc); \draw[fnarrowT,dashed] (nm.east) -- (ma.west); % rami SPI a sinistra \node[fnblockA,minimum width=30mm,left=14mm of arb,yshift=6mm](ss){\code{spi\_slave}\\{\scriptsize layer fisico Mode 0}}; \node[fnblockA,minimum width=30mm,below=6mm of ss](se){\code{spi\_engine}\\{\scriptsize FSM opcode + registri}}; \draw[fnarrowT] (ss)--(se); \draw[fnarrowT,dashed] (se.east) -- (arb.west); \end{tikzpicture} \end{center} \section{Ruolo di ciascun modulo} \begin{tabularx}{\textwidth}{L{3.4cm}Y} \toprule \rowh \thd{Modulo} & \thd{Funzione} \\ \midrule \code{mac\_unit} & Singolo prodotto-accumulatore: $\mathrm{acc\_out}=\mathrm{acc\_in}+(x\cdot w)$, con estensione di segno del prodotto ad \code{ACC\_WIDTH}. Parametrico su \code{DATA\_WIDTH}/\code{ACC\_WIDTH}. \\ \rowa \code{mac8} & \code{PARALLEL} istanze di \code{mac\_unit} i cui prodotti vengono sommati da un \emph{balanced binary adder tree} di profondità $\log_2(\text{PARALLEL})$; il risultato è aggiunto all'accumulatore in ingresso. \\ \code{neuron\_parallel} & FSM di un singolo neurone: elabora \code{N\_INPUTS} ingressi in gruppi di \code{PARALLEL}, accumula tra i gruppi, somma il bias, applica l'attivazione e satura a INT8. Include il guard di elaborazione su \code{N\_INPUTS \% PARALLEL} e la larghezza runtime \code{n\_inputs\_real}. \\ \rowa \code{layer} & Istanzia \code{N\_NEURONS} neuroni \emph{in parallelo} sullo stesso vettore di ingresso; \code{busy}=OR, \code{done}=AND dei neuroni. Percorso puramente combinatorio-di-dati usato nei benchmark del datapath. \\ \code{neuron\_memory} & Integra il calcolo con la memoria: legge $X$ (condiviso) una volta, poi per ogni neurone rilegge $W$ e bias dalla RAM e riusa una singola istanza \code{neuron\_parallel} (memory-bound, un neurone per volta). Uscita \code{y\_bus} packed neuron-major. \\ \rowa \code{layer\_sequencer} & Concatena fino a \code{N\_LAYERS} esecuzioni di \code{neuron\_memory} leggendo una tabella descrittori scritta dall'host e alternando i buffer ping-pong in RAM (Fase~5). \\ \code{act\_buffer} & Buffer di attivazione globale in block RAM \code{DP16KD}, indicizzato per id di segnale (Tipo \#2). \\ \rowa \code{graph\_engine} & Motore della rete a grafo (Tipo \#2): gather da \code{act\_buffer}, riusa \code{neuron\_parallel}, scrive le uscite per id (cap.~\ref{ch:grafo}). \\ \code{int8\_memory\_access} & Converte l'interfaccia byte/INT8 (indirizzo di byte) nell'interfaccia a parola 16-bit, selezionando il byte basso/alto tramite \code{lb\_n}/\code{ub\_n} e \code{addr>>1}. \\ \rowa \code{memory\_interface} & FSM di handshake a 2 stati (IDLE/WAIT) che serializza la singola transazione verso il controller. \\ \code{psram\_controller} & Controller del bus PSRAM parallelo asincrono con \textbf{page mode} di lettura: accesso casuale a 70~ns (\code{tAA}), burst nella stessa pagina a 20~ns (\code{tAPA}) con CE\#/OE\# tenuti attivi; abilita il page mode sul chip all'avvio via registro di configurazione (cap.~\ref{ch:mem}, \S~5.5). Pilota \code{ce\_n/oe\_n/we\_n/lb\_n/ub\_n/zz\_n} e il bus dati tri-state. \\ \rowa \code{mem\_arbiter} & Arbitro a priorità fissa (B$>$C$>$A) fra tre master byte-level: \code{spi\_engine} (A), \code{neuron\_memory} (B), \code{layer\_sequencer} (C). \\ \code{spi\_slave} & Layer fisico SPI Mode 0, MSB-first, sincronizzatore CDC a 3 stadi su SCLK/MOSI/CS\_N, shift-register e framing di CS. \\ \rowa \code{spi\_engine} & FSM di protocollo/opcode e banco registri (\code{x\_base}, \code{w\_base}, \code{bias\_addr}, base ping-pong, attivazione, larghezze runtime\ldots), con \code{STATUS.done} sticky/clear-on-read. \\ \code{spi\_neuron\_top} & Top-level: collega SPI, arbitro, sequencer, \code{neuron\_memory} e catena PSRAM; multiplexa il controllo di \code{neuron\_memory} fra sequencer e percorso diretto single-layer. \\ \bottomrule \end{tabularx} \vspace{6pt} \begin{fnnote}[Modelli di simulazione] \code{psram\_model.v} (in \code{sim/}) e \code{memory\_model.v} sono modelli comportamentali della memoria usati nei testbench; non fanno parte del design sintetizzabile ma riproducono la latenza reale per la verifica end-to-end. \end{fnnote} \section{Due percorsi di esecuzione} Il top-level espone due modalità mutuamente esclusive verso lo stesso motore di calcolo \code{neuron\_memory}: \begin{itemize} \item \textbf{Percorso single-layer / manuale}: l'host imposta le basi con \op{SET\_BASE}, avvia con \op{START} e legge con \op{READ\_OUTPUT}. \code{spi\_engine} pilota direttamente \code{neuron\_memory}. \item \textbf{Percorso multi-layer}: l'host scrive la tabella descrittori e avvia con \op{RUN\_NETWORK}; \code{layer\_sequencer} prende possesso del controllo di \code{neuron\_memory} (mentre \code{seq\_busy} è alto) e concatena i layer. \end{itemize} Il multiplexer del top-level commuta le linee di controllo di \code{neuron\_memory} in base a \code{seq\_busy}, restituendo il motore al percorso diretto a fine sequenza.