Simulazione, testbench, vincoli di temporizzazione e flusso su FPGA
In questa pagina 6
Dopo aver scritto un circuito in VHDL (VHDL - struttura, tipi di dato e processiVHDL è un linguaggio di descrizione dell'hardware: un listato non è una sequenza di istruzioni eseguite da un processore ma la descrizione di un circuito, che la sintesi traduce in uno schema (LUT, flip-flop, multiplexer). Ogni modulo ha una entity (interfaccia: port con modo in, out, inout e tipo; eventuali generic) e una architecture (parte dichiarativa: segnali, costanti, componenti; parte assertiva dopo begin: assegnazioni concorrenti e processi). Le istruzioni nell'architecture sono concorrenti: l'ordine in cui sono scritte non conta. Un process esegue le proprie istruzioni in modo sequenziale quando un segnale della lista di sensibilità cambia. Segnali (<=, aggiornati alla fine del delta cycle, definiti nella parte dichiarativa) e variabili (:=, immediate, solo dentro il processo) hanno semantica diversa. Tipi: bit, boolean, integer, std_logic (a 9 valori, tra cui 'Z', 'X', 'U'), std_logic_vector, unsigned e signed (numeric_std).VHDL - struttura, tipi di dato e processi →, VHDL - logica combinatoriaUna logica combinatoria in VHDL si descrive con assegnazioni concorrenti (semplici, condizionate when-else con priorità, selezionate with-select senza priorità) o con processi sensibili a tutti gli ingressi (if/elsif, case). Nel processo ogni uscita va assegnata in tutti i rami e la lista di sensibilità deve contenere tutti i segnali letti: altrimenti la sintesi inferisce un latch (memoria). L'aritmetica si fa con unsigned/signed di numeric_std (to_unsigned, to_integer, resize, conversioni di tipo) e con un bit in più per il riporto. Cicli for e istruzioni generate creano hardware replicato (non cicli nel tempo). La descrizione strutturale istanzia componenti con component e port map (per posizione o per nome, anche con generic map), e permette di costruire sistemi gerarchici.VHDL - logica combinatoria →, VHDL - logica sequenziale e registriIn VHDL la logica sequenziale si descrive con processi sensibili al clock. Un registro D (flip-flop sul fronte) si scrive process(clk) con if rising_edge(clk); un latch è un processo sensibile al livello (if clk='1' then q<=d) senza altri rami. Il reset è sincrono se testato dentro rising_edge(clk) (la lista di sensibilità contiene solo clk), asincrono se testato prima, con RST nella lista di sensibilità (if rst='1' then ... elsif rising_edge(clk)). Registri a n bit con generic, registri a scorrimento (con load), contatori (up/down, con enable) si ottengono con la stessa struttura. In un progetto sincrono tutti i registri usano lo stesso clock (nessun clock generato da logica: si usano abilitazioni), i segnali esterni asincroni come i pulsanti si campionano con uno o più flip-flop (debouncing) e le cifre di un display si multiplexano con un contatore.VHDL - logica sequenziale e registri →) bisogna verificarlo e portarlo sulla scheda. Questa nota raccoglie il flusso completo, dal testbench al bitstream, e i vincoli di temporizzazione.
Flusso di progetto su FPGA
- Descrizione del circuito in VHDL (e dei vincoli).
- Simulazione funzionale (comportamentale): il simulatore a eventi esegue il codice senza tener conto dei ritardi; verifica la correttezza logica.
- Sintesi: il codice è tradotto in una rete di primitive della FPGA (LUT, flip-flop, multiplexer, blocchi RAM e DSP: FPGA - LUT, slice e risorse della famiglia 7Una FPGA è una matrice di blocchi logici configurabili (CLB) immersi in una griglia di interconnessioni programmabili, con risorse dedicate: block RAM, blocchi DSP, gestione del clock (MMCM, PLL), blocchi di I/O e transceiver. La memoria di configurazione è SRAM. L'elemento base è la LUT: una memoria SRAM con 2^k celle che realizza una qualsiasi funzione di k ingressi (i segnali di selezione del multiplexer sono gli ingressi della funzione). Nella famiglia 7 di Xilinx un CLB ha 2 slice; uno slice ha 4 LUT a 6 ingressi (ciascuna divisibile in due LUT a 5), multiplexer larghi F7/F8 (funzioni a 7-8 ingressi), una catena di riporto veloce, 4 flip-flop/latch più 4 flip-flop. Nei SLICEM la LUT può essere usata come RAM distribuita o registro a scorrimento (SRL32), nei SLICEL solo come logica.FPGA - LUT, slice e risorse della famiglia 7 →); si ottiene lo schema circuitale (RTL e netlist) e una stima delle risorse usate.
- Implementazione: piazzamento (assegnazione delle primitive alle risorse fisiche) e instradamento (connessioni nella griglia di routing). Qui sono noti i ritardi reali, perché nelle FPGA dominano le interconnessioni.
- Analisi di temporizzazione (statica) sul progetto implementato e simulazione post-implementazione con i ritardi.
- Generazione del bitstream (i bit di configurazione, Dispositivi logici programmabili - PROM, PLA, PAL e CPLDUn componente logico programmabile (PLD) è un circuito integrato a catalogo la cui funzione logica viene fissata da una programmazione che altera la struttura del circuito (non va confusa con la programmazione software di un microprocessore). I meccanismi sono fusibili, antifusibili (una volta), EPROM/EEPROM/Flash (reversibili e non volatili) e celle SRAM (reversibili ma volatili). Le architetture semplici (SPLD) sono due piani AND-OR: PROM (AND fisso, cioè un decoder di tutti i mintermini, OR programmabile), PLA (entrambi programmabili, dimensione indipendente dal numero di ingressi, più lenta) e PAL (AND programmabile, OR fisso, più veloce). I CPLD sono matrici di blocchi SPLD con interconnessioni programmabili, le FPGA hanno molti blocchi logici piccoli (LUT + registro) in una griglia di interconnessioni complessa, configurata da SRAM.Dispositivi logici programmabili - PROM, PLA, PAL e CPLD →) e programmazione della scheda.
- Verifica sulla scheda del comportamento reale e miglioramenti.
In laboratorio si usa un ambiente integrato (Vivado) con la scheda Nexys (dispositivo Artix-7): un progetto "RTL" con i file sorgente, il file dei vincoli (XDC) con la mappa dei collegamenti della scheda (pin dei switch, LED, pulsanti, display, clock), e un clock di sistema a . Il percorso di lavoro non deve contenere spazi.
Testbench
Un testbench è un modulo VHDL senza porte che contiene il circuito sotto test (UUT, unit under test), genera i segnali di ingresso e osserva le uscite. Non è sintetizzabile: usa costrutti solo di simulazione (wait for, after, file, assert).
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity seven_tb is end entity; -- nessuna porta
architecture BHV of seven_tb is
component bin2seven_seg is -- interfaccia del circuito sotto test
port (data_in : in std_logic_vector(3 downto 0);
point_in : in std_logic;
data_out : out std_logic_vector(7 downto 0));
end component;
signal A : unsigned(3 downto 0) := "0000";
signal B : std_logic;
signal C : std_logic_vector(7 downto 0);
signal CLK : std_logic := '0';
begin
-- generatore di clock: periodo 20 ns
Clock: process
begin
wait for 10 ns;
CLK <= not CLK;
end process;
-- stimolo: A conta ad ogni fronte
process (CLK)
begin
if rising_edge(CLK) then A <= A + 1; end if;
end process;
P1: bin2seven_seg port map (data_in => std_logic_vector(A), point_in => B, data_out => C);
B <= '1' after 20 ns; -- ingresso che cambia a un istante dato
end BHV;Elementi:
- componente e istanza della UUT (con
port map: VHDL - logica combinatoriaUna logica combinatoria in VHDL si descrive con assegnazioni concorrenti (semplici, condizionate when-else con priorità, selezionate with-select senza priorità) o con processi sensibili a tutti gli ingressi (if/elsif, case). Nel processo ogni uscita va assegnata in tutti i rami e la lista di sensibilità deve contenere tutti i segnali letti: altrimenti la sintesi inferisce un latch (memoria). L'aritmetica si fa con unsigned/signed di numeric_std (to_unsigned, to_integer, resize, conversioni di tipo) e con un bit in più per il riporto. Cicli for e istruzioni generate creano hardware replicato (non cicli nel tempo). La descrizione strutturale istanzia componenti con component e port map (per posizione o per nome, anche con generic map), e permette di costruire sistemi gerarchici.VHDL - logica combinatoria →); - segnali con valore iniziale per tutti gli ingressi;
- generatore di clock: un
processsenza lista di sensibilità conwait for T/2; clk <= not clk;(oclk <= '0'; wait for T/2; clk <= '1'; wait for T/2;); - stimoli:
after, oppure un processo con una sequenza di assegnazioni ewait for; l'istruzionewait until READY'event and READY='1'attende un evento; - controllo automatico (self-checking) con
assert condizione report "messaggio" severity livello;(se la condizione è falsa il simulatore stampa il messaggio; i livelli sononote,warning,error,failure); - file di testo (libreria
std.textio): si leggono vettori di ingresso confile_open,readline,read, e si scrivono le uscite conwrite/writeline: utile per confrontare il circuito con un modello di riferimento (per esempio un moltiplicatore con un programma software). Poichéwaiteafteresistono solo in simulazione, un testbench non descrive hardware.
Si esaminano le forme d'onda (valori dei segnali nel tempo) e i messaggi: gli errori tipici trovati sono uscite 'U' (non inizializzate: manca un reset), 'X' (conflitti: due driver sullo stesso segnale) e latch non voluti.
Vincoli di temporizzazione
I vincoli dicono agli strumenti quali prestazioni raggiungere e come sono collegati i pin:
- vincoli fisici: assegnazione dei segnali di ingresso/uscita ai pin della scheda (standard elettrico compreso);
- vincolo di clock:
create_clock -period 10.0 [get_ports clk]per un clock a ; - vincoli sui ritardi di ingresso/uscita (rispetto al clock esterno).
L'analisi statica di temporizzazione controlla ogni cammino tra due registri (e da/verso i pin) con gli stessi vincoli visti per il registro (Latch, registri e parametri temporaliUn circuito sequenziale ha uno stato memorizzato in due modi: retroazione positiva (due inverter in anello, bistabile, i latch statici) o immagazzinamento di carica (circuiti dinamici). Un latch è sensibile al livello del clock (trasparente quando il clock è attivo, memorizza quando non lo è), un registro (flip-flop) è sensibile al fronte e si realizza con due latch sulle fasi opposte (master-slave). Il latch SR a porte NOR/NAND non ammette la combinazione S = R = 1 perché passando da lì alla memoria lo stato finale è imprevedibile. Parametri temporali del registro: ritardo clock-uscita t_c-q, tempo di setup t_su (dato stabile prima del fronte), tempo di hold t_hold (dato stabile dopo il fronte). Periodo minimo: T_min = t_c-q + t_p,logic(max) + t_su; vincolo di hold: t_cd,reg + t_cd,logic > t_hold. La pipeline spezza la logica con registri per aumentare la frequenza (a spese della latenza).Latch, registri e parametri temporali →):
- slack di setup: ; se il cammino rispetta il periodo di clock ; il minimo su tutti i cammini è il WNS (worst negative slack). Il cammino con lo slack minore è il cammino critico.
- slack di hold: (indipendente da ).
- Frequenza massima: se .
Esempio. (). Un cammino ha , logica e routing , (): ritardo totale , slack , . Se il cammino fosse lungo : slack : la frequenza di non è raggiungibile, .
Come si chiude la temporizzazione (slack negativo): ridurre la profondità logica (meno LUT in cascata), inserire registri di pipeline (accorciano ogni cammino; costo: latenza), usare le risorse dedicate (catena di riporto, DSP), restringere il piazzamento, ridurre la frequenza. Un clock lento e abilitazioni (clock enable) evitano di generare clock con la logica.
Sintesi e inferenza
La sintesi riconosce nei costrutti VHDL le primitive: if rising_edge(clk) flip-flop; assegnazione condizionata completa multiplexer e LUT; + su unsigned catena di riporto; memoria con lettura/scrittura sincrona block RAM; moltiplicazioni DSP. I segnali non usati vengono eliminati. Un latch compare per un'assegnazione incompleta in un processo combinatorio (VHDL - logica combinatoriaUna logica combinatoria in VHDL si descrive con assegnazioni concorrenti (semplici, condizionate when-else con priorità, selezionate with-select senza priorità) o con processi sensibili a tutti gli ingressi (if/elsif, case). Nel processo ogni uscita va assegnata in tutti i rami e la lista di sensibilità deve contenere tutti i segnali letti: altrimenti la sintesi inferisce un latch (memoria). L'aritmetica si fa con unsigned/signed di numeric_std (to_unsigned, to_integer, resize, conversioni di tipo) e con un bit in più per il riporto. Cicli for e istruzioni generate creano hardware replicato (non cicli nel tempo). La descrizione strutturale istanzia componenti con component e port map (per posizione o per nome, anche con generic map), e permette di costruire sistemi gerarchici.VHDL - logica combinatoria →).
Esempi di laboratorio
- Contatore a 4 bit up/down sui LED, reset, simulazione, sintesi, implementazione e bitstream.
- Display a 7 segmenti con tecnica stroboscopica, VHDL strutturale: decoder , contatore e decoder per gli anodi (VHDL - logica sequenziale e registriIn VHDL la logica sequenziale si descrive con processi sensibili al clock. Un registro D (flip-flop sul fronte) si scrive process(clk) con if rising_edge(clk); un latch è un processo sensibile al livello (if clk='1' then q<=d) senza altri rami. Il reset è sincrono se testato dentro rising_edge(clk) (la lista di sensibilità contiene solo clk), asincrono se testato prima, con RST nella lista di sensibilità (if rst='1' then ... elsif rising_edge(clk)). Registri a n bit con generic, registri a scorrimento (con load), contatori (up/down, con enable) si ottengono con la stessa struttura. In un progetto sincrono tutti i registri usano lo stesso clock (nessun clock generato da logica: si usano abilitazioni), i segnali esterni asincroni come i pulsanti si campionano con uno o più flip-flop (debouncing) e le cifre di un display si multiplexano con un contatore.VHDL - logica sequenziale e registri →).
- Conversione binario-BCD (double dabble) e visualizzazione di numeri a bit su cifre decimali; contatore lento comandato (Rappresentazione dei numeri e dell'informazioneUn'informazione digitale è una stringa di bit. Numeri senza segno: N bit rappresentano 0…2^N − 1; in esadecimale ogni cifra raggruppa 4 bit. Numeri con segno: complemento a due (range −2^(N−1)…2^(N−1) − 1; si nega invertendo i bit e sommando 1; il bit più significativo è il segno e l'estensione di segno lo replica). Il BCD codifica ogni cifra decimale con 4 bit (0…9) e si ottiene dal binario con l'algoritmo double dabble (shift a sinistra, e +3 a ogni gruppo maggiore di 4 prima dello shift). Il codice Gray cambia un solo bit tra valori consecutivi. I numeri frazionari si trattano in virgola fissa (4 bit di parte frazionaria = multipli di 1/16), i caratteri in ASCII, gli errori con un bit di parità.Rappresentazione dei numeri e dell'informazione →).
- Sensore di temperatura con protocollo I2C: dato a bit (segno, bit di parte intera, di parte frazionaria con risoluzione ), conversione della parte frazionaria su – e visualizzazione su display; uso del block design e di IP core.
- Applicazioni sul chip (schede e sistemi più grandi): tag temporali di impulsi di rivelatori di singolo fotone, interfacce video (clock VGA a , periodo ), tastiera.
Errori comuni
- Dimenticare di inizializzare i segnali del testbench (uscite
'U'). - Cercare di sintetizzare codice con
wait foroafter. - Confrontare solo la simulazione funzionale: la correttezza dei tempi va verificata dopo l'implementazione.
- Interpretare uno slack positivo come "il circuito funziona": serve anche lo slack di hold e il rispetto degli ingressi esterni.
- Creare un clock con un contatore e usarlo come clock di altri registri.
Versione ripasso
- Flusso: VHDL simulazione funzionale sintesi (LUT, FF, DSP, BRAM) implementazione (piazzamento + instradamento) analisi di temporizzazione / simulazione post-implementazione bitstream programmazione e verifica sulla scheda (Vivado, Nexys/Artix-7, clock MHz).
- Testbench: entity senza porte; UUT (component + port map); clock con
wait for T/2; clk <= not clk;; stimoli conafter/wait;assert ... report ... severity(note,warning,error,failure); file contextio; non sintetizzabile. Segni di errore:'U','X', latch. - Vincoli: pin della scheda e
create_clock -period 10( MHz). - Temporizzazione: setup ; WNS = minimo; hold (indip. da ); ( ns: MHz; slack ns: MHz). Rimedi: meno LUT in cascata, pipeline, catena di riporto, DSP.
- Inferenza:
rising_edgeFF;+carry chain; memoria sincrona BRAM; assegnazione incompleta latch. - Errori: segnali non inizializzati; sintesi di
wait/after; solo simulazione funzionale; hold ignorato; clock da contatore.