• Non ci sono risultati.

end process;

La differenza `e proprio sull’ultima istruzione di wait: nel caso di segnali ape- riodici, il processo completati gli assegnamenti viene sospeso indefinitamente, viceversa nei segnali periodici - non essendoci un wait indefinito - il processo, al risveglio dall’ultima istruzione di wait inizia un nuovo loop. Uno schema del genere `e generalmente utilizzato per la creazione, rispettivamente, dei segnali di reset e di clock.

Un problema che si pu`o manifestare `e che il simulatore non fornisce la possi- bilit`a di controllare quanto tempo di simulazione `e trascorso, in altre parole, se la simulazione `e stata avviata in Run non vincolato, essa proseguir`a fino al massimo tempo predefinito dall’ambiente (time’high) indipendentemente dal- la assenza di altri istanti nella event list. Esistono un paio di work around per risolvere questo problema: una prima forma prevede l’utilizzo di un costrutto assert che verifichi l’arrivo ad un certo istante temporale o la variazione di uno specifico segnale (ad esempio, la commutazione ad ‘1’ del segnale End ). Ad esempio:

assert NOW <= 1000ns

report "Simulation completed successfully" severity error;

Un altro modo `e interrompere il processo di generazione del clock, con una condizione del tipo (vincolata al raggiungimento di un particolare istante temporale o ad una condizione sul valore di un segnale):

if NOW > 1000ns then wait;

end if;

1.2

Assegnamenti dinamici

Utilizzando il package textio, presente nella libreria STD, `e possibile ope- rare su file, ossia accedere in lettura o scrittura a file presenti sul filesystem. Ovviamente, rispetto a linguaggi di pi`u alto livello, le possibilit`a di gestione

sono molto limitate: il file da caricare `e codificato staticamente all’interno del codice e non sono disponibili metodi di supporto per fare altre operazioni sul filesystem (ad esempio la cancellazione di un file, ecc. - anche perch`e le stesse non rientrano nell’ottica di principio del VHDL stesso).

Il linguaggio VHDL definisce il tipo file solo come strumento di supporto per la memorizzazione e lo scambio di dati, infatti un file non ha un corrispettivo fisico e le sue istruzioni non sono sintetizzabili.

Un file `e gestito come un insieme di righe, il package definisce quindi due fun- zioni (sia per l’input che per l’output) per operare con essi. L’apertura di un file avviene con la dichiarazione file <handle>: text is in <file>; un file pu`o aperto in lettura/scrittura (dipende solo dalle operazioni effettuate). Da un file si leggono unicamente stringhe (non interpretate) ed `e necessario dichiarare almeno una variabile (di tipo line) per la lettura di linee di testo. Solitamente la lettura di un file `e realizzata con un ciclo loop, la cui condizione `

e data dalla funzione endfile (valore true alla lettura dell’ultima riga del file). Ad ogni loop `e letta una riga del file, attraverso la funzione readline() (che restituisce un tipo di dato line, concettualmente un buffer della dimensione di una linea), la lettura dei singoli valori `e fatta dalla linea cos`ı estrapolata attraverso l’istruzione read() (che restituisce il tipo di dato atteso dal target dell’assegnazione). Questo comportamento evidenzia la necessit`a di conosce- re in anticipo il tipo di dati presenti nel file e la loro posizione; in altre parole `

e necessario definire una semantica per il contenuto del file stesso.

La scrittura su un file avviene simmetricamente, con la scrittura dei singoli valori sulla linea buffer (tramite la funzione write()) e la successiva scrittura del buffer nel file stesso, mediante la funzione writeline().

2

Pattern per la scrittura di testbench

Scrivere un testbench che operi correttamente non `e particolarmente difficile, ma - soprattutto all’aumentare dei segnali coinvolti - `e bene seguire sempre

5.2 Pattern per la scrittura di testbench 127 delle regole di riferimento; queste sono presentate all’interno della sezione corrente.

Quando si progetta un processo per la generazione del clock, `e importante utilizzare istruzioni di sospensione (wait for) e non istruzioni di scheduling (after) per creare il timing. Ci`o `e dovuto al seguente motivo: il ciclo di simulazione ignora le clausole after all’interno di istruzioni di assegnamento ai segnali, poich`e il riferimento temporale `e assoluto (quindi sempre riferito all’origine); ad esempio il seguente codice non `e corretto:

junk : process begin

CLK <= ‘0’, ‘1’ after 25 ns; -- errore wait for 50 ns;

end process;

La prima esecuzione del loop avviene correttamente, perch`e la variazione del segnale `e schedulata correttamente a 25 ns, ma successivamente all’istruzio- ne wait for 50 ns il tempo di simulazione si porta a 75 ns (si ricordi che il tempo in una istruzione wait for `e relativo, mentre nella after `e appunto assoluto), chiaramente al riavvio del loop l’istruzione di assegnazione non `e pi`u valida (la soluzione corretta `e mostrata nella sez. 1.1).

Pu`o presentarsi la necessit`a di avere pi`u clock sincronizzati tra loro, ad esem- pio generandoli a partire dal clock principale. In questo caso, bisogna presta- re attenzione ai delta cycle introdotti tra il clock principale e quelli derivati. Questi delta potrebbero causare comportamenti indesiderati se all’interno del circuito sono utilizzati sia il clock principale che quelli secondari. E’ possibile prevenire il problema con il seguente tipo di codice:

divider: process begin clk50 <= ‘0’; clk100 <= ‘0’; clk200 <= ‘0’; loop -- forever

for j in 1 to 2 loop for k in 1 to 2 loop wait on clk; clk200 <= not clk200; end loop; clk100 <= not clk100; end loop; clk50 <= not clk50; end loop;

end process divider;

Si noti che, essendo all’interno di un unico processo, i clock derivati (clk50, clk100 e clk200 ) hanno la stessa frequenza del clock principale (clk ), ma so- no ritardati rispetto al principale di un delta cycle (questo `e perfettamente normale, per quanto visto in cap. 2, sez. 3.1). Ad ogni modo, i clock deri- vati sono aggiornati contemporaneamente e devono essere gli unici ad essere utilizzati nel circuito, in altre parole il clock principale deve essere utilizzato solo per la generazione dei derivati (eventualmente un suo derivato pu`o essere in rapporto 1:1 con il principale).

Nella scrittura di un testbench `e importante prevedere un processo che si occupi della generazione del segnale di reset, questo ha la caratteristica di far partire il sistema da una configurazione nota e stabile. Un processo del genere `e equivalente alla generazione di un segnale asincrono, come mostrato nella sez. 1.1.

Nella generazione di dati `e necessario evitare corse critiche tra gli stessi e il clock: l’applicazione contemporanea di dati e del fronte attivo del clock pu`o causare risultati inaspettati. Per mantenere i dati sincronizzati ed evitare le corse critiche, i dati vanno applicati in un momento differente dal fronte attivo, ad esempio al fronte inattivo.