Dispense per le esercitazioni del corso Sistemi Elettronici Programmabili 1

Dimensione: px
Iniziare la visualizzazioe della pagina:

Download "Dispense per le esercitazioni del corso Sistemi Elettronici Programmabili 1"

Transcript

1 UNIVERSITA DEGLI STUDI DI GENOVA FACOLTA DI INGEGNERIA Corso di Laurea Specialistica in Ingegneria Elettronica Dispense per le esercitazioni del corso Sistemi Elettronici Programmabili 1 Prof. Davide Anguita A cura di: Basato sul lavoro di: Alessandro Ghio Stefano Pischiutta Anno Accademico Documento realizzato in L A TEX

2 Indice 1 Progetto di un sistema digitale Introduzione Rappresentazione di un sistema digitale Development flow Organizzazione della dispensa Hardware Description Languages Introduzione VHDL Strutture Elementi lessicali Oggetti Tipi di dati e operatori Consigli pratici Istruzioni concorrenti Circuiti combinatori vs. Circuiti sequenziali Istruzioni concorrenti semplici Istruzioni concorrenti condizionali Sintassi Un esempio: multiplexer a 4 ingressi Istruzioni concorrenti di selezione Sintassi Un esempio: multiplexer a 4 ingressi (con istruzioni di selezione) Un secondo esempio: tabella di verità generica Istruzioni sequenziali Il processo Introduzione Processi con sensitivity list

3 4.1.3 Processi con istruzioni wait Istruzioni di assegnazione sequenziale per segnali Istruzioni di assegnazione sequenziale per variabili Costrutto if...then...else Sintassi Un esempio: multiplexer a 4 ingressi Incomplete branch Incomplete assignment Costrutto case...when Sintassi Un esempio: multiplexer a 4 ingressi Costrutto for...loop Sintassi Un esempio: XOR bit a bit a 8 bit Progetto di circuiti combinatori Operator Sharing Functionality Sharing Ottimizzazioni di layout Progetto di circuiti sequenziali Introduzione Circuiti sequenziali Elementi di memoria Classificazione dei circuiti sequenziali Circuiti sequenziali sincroni Programmazione di elementi di memoria elementari Positive Edge Triggered D Flip Flop FF D con reset asincrono Registro a 8 bit RAM ROM Un esempio: contatore sincrono modulo m programmabile Prima soluzione Seconda soluzione Analisi temporale di circuiti sincroni Massima frequenza di clock Altre informazioni Output del sintetizzatore Xilinx Utilizzo di variabili in circuiti sequenziali

4 6.6.1 Progettazione delcontatoremodulo m utilizzando variabili Macchine a stati finiti Introduzione Rappresentazione di una FSM Pallogrammi ASM Alcune considerazioni sulle FSM Considerazioni temporali Macchine di Moore e macchine di Mealy: pro e contro Descrizione VHDL di una FSM Codifica degli stati Look Up Tables (LUT) Che cos èunalut? Pro e contro Codice VHDL per una LUT Codice per la generazione automatica del VHDL Hierarchical Design Introduzione Hierarchical Design: pro e contro Costrutti VHDL per il Hierarchical Design Component Generic Configuration Library Subprogram Package Parameterized VHDL Attributi di un array Array con e senza range Costrutto For...Generate Costrutto if...generate Array bidimensionali Array bi dimensionale effettivo Array bi dimensionale emulato

5 11 Testbench Test di dispositivi digitali Creazione di un testbench Esempi finali e guida a Xilinx ISE Web Pack Introduzione a Xilinx ISE Web Pack 8.2i Esempi Half Adder asincrono Full Adder Decimal Counter a 2 cifre Calcolo con LUT di funzione trigonometrica Generatore di sequenze con FSM Esempio completo Testo Soluzione

6 Capitolo 1 Progetto di un sistema digitale 1.1 Introduzione I dispositivi digitali sono diventati, negli ultimi 40 anni, sempre più diffusi ed utilizzati in un gran numero di svariate applicazioni. A partire dalla loro prima comparsa, la quantità di transistor in ogni chip è cresciuta esponenzialmente, fino a raggiungere anche centinaia di milioni di transistor in un singolo componente. Se agli albori della storia dei dispositivi hardware essi erano prevalentemente usati nei cosiddetti computational systems, ora,grazie alla loro sempre maggiore economia di esercizio ed acquisto e alle notevoli capacità di elaborazione, molti sistemi meccanici, comunicazionistici, elettronici e di controllo vengono digitalizzati ed introdotti in strutture quali DSP e FPGA. Ovviamente, al crescere delle risorse disponibili e delle capacità di elaborazione, cresce anche la complessità di progetto di tali strutture hw. A tal scopo sono nati linguaggi di programmazione che consentono un ottima astrazione ad alto livello, in modo da confinare operazioni poco user friendly al solo range di azione del cosiddetto sintetizzatore, ovvero quel modulo software che tradurrà le istruzioni dell utente nello schema logico vero e proprio (sfruttando strutture e porte logiche presenti sulla scheda di destinazione). Sebbene, quindi, un software possa automatizzare alcune operazioni, esso è comunque in grado di svolgere solo un numero limitato di ottimizzazioni: per buono che sia, un sintetizzatore non potrà mai rendere efficiente uno schema mal progettato o, peggio ancora, errato e mal testato. Pertanto, il progetto e la sintesi di una struttura hardware sono solo due dei molti step che portano alla creazione di un sistema digitale efficace ed efficiente. 5

7 1.2 Rappresentazione di un sistema digitale Progettare un sistema digitale complesso non è per nulla semplice. Ogni passo del processo di produzione richiede informazioni sul sistema, che spaziano dalle specifiche di I/O al layout fisico sulla scheda. Per questo sono nate diverse rappresentazioni (views) di un sistema, le quali rappresentano diverse prospettive attraverso le quali vedere l oggetto in analisi. Vengono anche dette livelli di astrazione, in quanto consentono di concentrarsi di volta in volta solo su quelle caratteristiche vitali, tralasciando per analisi successive problematiche secondarie. Esistono sostanzialmente tre views, qui presentate in ordine di astrazione decrescente: Behavioral view Structural view o Register Transfer Level (RTL) Logic view Layout view. La behavioral view descrive il comportamente e la funzionalità di un sistema. Non si occupa di come possa essere implementata una certa operazione all interno dell FPGA, semplicemente definisce gli input e output trattando il blocco di elaborazione come una black box. Si definisce come il sistema in analisi dovrà reagire a determinati input, quali output dovrà fornire, se gli I/O dovranno essere sincroni con un clock o potranno essere asincroni, e altro ancora. La structural view permette di descrivere la struttura interna di un sistema. In essa, vengono presentati i blocchi di elaborazione e come essi sono interconnessi fra loro. La structural view è spesso descritta attraverso una netlist, ovvero una lista di nodi ed interconnessioni. Le logic e layout view (spesso unite sotto un unica rappresentazione, chiamata physical view) descrivono le caratteristiche fisiche di basso livello del sistema. Si specificano la dimensione dei componenti fisici, il numero di transistor e porte logiche necessarie per le varie operazioni, l ubicazione di esse all interno della scheda, il tracciamento e le caratteristiche dei path di connessione tra i vari blocchi. Un esempio di physical view è il layout di una scheda. Si parte da un astrazione a livello di porta logica (AND/OR) fino ad arrivare anche alla scelta di quali buffer utilizzare per l I/O (layout level). In Figg. 1.1, 1.2, 1.3 e 1.4 viene mostrato un semplice esempio di un half adder a un bit. 6

8 Figura 1.1: Blocco per un half adder a un bit Figura 1.2: Blocco strutturale per un half adder a un bit 7

9 Figura 1.3: Layout di una Xilinx Virtex 4 sulla quale è presente (nel cerchio) l implementazione fisica dell half adder 8

10 Figura 1.4: Ingrandimento del placing su FPGA dell half adder 9

11 1.3 Development flow Il Development Flow (DF ) rappresenta il diagramma di flusso di riferimento per la progettazione di componenti hw su FPGA. Innanzitutto, esso varia notevolmente sulla base della grandezza del progetto che si vuol realizzare, ove per grandezza si intende il numero di porte logiche che verranno utilizzate dal modulo progettato e se esso sfrutterà IP coregià realizzati precedentemente. Di solito, si organizzano i progetti secondo la seguente classificazione: small size design: vengono impiegate meno di 10mila porte logiche e si basano su eventuali IP core precedentemente realizzati, ma di piccole dimensioni e limitate funzionalità; medium size design (quello di nostro interesse): vengono impiegate tra 10 e 50mila porte logiche e si basano su eventuali IP core precedentemente realizzati, ma di piccole/medie dimensioni e limitate funzionalità; largesizedesign: vengono impiegate più di 50mila porte logiche e si basano su eventuali IP core precedentemente realizzati, completi, complessi e spesso a scatola nera. Possono avere come target sia FPGA ma anche ASIC. Concentriamoci su progetti di medio/piccole dimensioni. Il flusso di progetto inizia con la descrizione comportamentale, in cui non specifichiamo come un sistema debba essere implementato, ma solo cosa vogliamo che esso faccia e quali interfacce abbia con l esterno. Passiamo alla structural view, implementando (per esempio, in VHDL) i blocchi che ci permettono di garantire che a determinati input corrispondano certi output. Genereremo, quindi, una netlist RTL che descriverà il sistema. Prima di procedere alla traduzione di tale netlist di alto livello in un insieme di porte logiche per la physical view, dobbiamo verificare che il codice da noi scritto non contenga errori di alto livello: per esempio, se vogliamo realizzare una macchina a stati finiti (FSM), dobbiamo controllare che il diagramma ASM che la descrive sia corretto nel suo progetto e nella sua implementazione. A tale scopo, si utilizzano i testbench, che sono dei semplici file (per esempio, in VHDL) in cui vengono preparati appositi stimoli di input per il controllo delle uscite. In pratica, è come il test di un dispositivo (detto DUT, ovverodevice Under Test) al classico banco hardware: dobbiamo disporre un generatore di forme d onda (word generator, per esempio) cn gli ingressi che vogliamo, connettervi il DUT e osservare le uscite su un Logic State Analyzer. Nel nostro 10

12 caso, il generatore di stimoli è scritto dall utente nel file VHDL, il DUT è il nostro progetto e l output verrà visualizzato su un apposito software di test (ModelSim, per esempio). Si veda anche la Fig Dovremo, inoltre, preparare eventuali file di constraint per quanto riguarda eventuali vincoli temporali o di posizionamento del dispositivo sulla FPGA. Figura 1.5: Esempio di schema a blocchi per il test di un DUT. Nel nostro caso, il generatore di stimoli è il file di testbench, mentre l oscilloscopio è il simulatore (es. ModelSim) A questo punto, il secondo passo è rappresentato dalla simulazione vera e propria del sistema: presi i sorgenti RTL e i testbench preparati, si passa alla verifica che tutto funzioni correttamente. In questo primo passo, non si considerano i ritardi delle porte logiche, ma si fa semplicemente un test di altissimo livello per controllare la bontà sintattica del codice generato. Il passo successivo è rappresentato dalla sintesi: a partire dalla netlist RTL, si genera una netlist di sintesi, in cui le funzioni di alto livello vengono tradotte in connessioni fra porte logiche, Look Up Tables (LUT), buffer e altri componenti elementari. Si effettua una seconda simulazione, questa volta più realistica, in cui si tiene conto dei ritardi delle porte logiche. Attenzione: non è ancora la simulazione definitiva, in quanto semplicemente le funzioni di alto livello sono state tradotte in equazioni booleane. In questa fase si tiene conto solo dei tempi di setup dei flip flop, delle porte logiche, ecc. ma non dei ritardi dovuti, ad esempio, ad interconnessioni fisiche lunghe. Se il dispositivo passa brillantemente la cosiddetta timing simulation, si passa al quinto passo, che consiste nel place and route: a partire dalla netlist di sintesi e dagli eventuali constraint, le porte logiche trovate vengono fisicamente piazzate su uno schema della FPGA che si vuole usare. Ora si ha a disposizione un design completo: le funzioni sono state sintetizzate, e abbiamo anche una traccia di piazzamento (spesso non ottimale) su hw. Viene generata una netlist finale, detta post par netlist (post place and route netlist). A questo punto, si simula nuovamente il comportamento del sistema in condizioni quasi reali (simulazione post place and route). I risultati tengono 11

13 Figura 1.6: Development flow 12

14 conto (attenzione, in modo approssimato e simulato) di tutti i ritardi, dovuti sia a porte logiche sia a piazzamenti e interconnessioni all interno dell FPGA. Se il DUT supera brillantemente anche quest ultimo step, si passa finalmente alla generazione del bitstream, ovvero del file di programmazione vero e proprio della FPGA. Dato che una FPGA può essere vista come un array di unità di calcolo elementari, che possono venire connesse ed attivate o meno attraverso dei fuse, semplicemente il bitstream è una matrice di 0 e 1, i quali rappresentano le locazioni ove effettuare i fuse. L ultimo step è la verifica del corretto funzionamento del nostro DUT sulla FPGA programmata attraverso il bitstream. Lo schema generale è presentato in Fig Possiamo, quindi, pensare di dividere idealmente lo sviluppo di un dispositivo digitale in tre sezioni: 1. Synthesis: comprende tutta la fase che porta dalla stesura del codice (o schematic) sorgente alla generazione della netlist RTL e di sintesi; 2. Verification: comprende lo sviluppo del file di testbench e le varie fasi di test, a partire da quella comportamentale fino alla timing analysis dopo il place and route; 3. Physical Design: comprende tutti gli step che portano all implementazione fisica e al piazzamento dei componenti necessari sulla FPGA. Di solito, si parte da una netlist post sintesi e si giunge fino al bitstream di programmazione. 1.4 Organizzazione della dispensa Queste pagine vogliono fornire un introduzione generale al VHDL 1,approfondendo aspetti importanti nell ambito dello sviluppo di sistemi anche complessi. In particolare: nel Cap. 2, introdurremo i linguaggi orientiati alla descrizione hardware, descrivendo brevemente alcune caratteristiche del VHDL, oggetto degli approfondimenti dei successivi capitoli; nel Cap. 3, analizzeremo i costrutti concorrenti nella programmazione VHDL, distinguendo fra circuiti combinatori e sequenziali; 1 Alcuni spunti sono tratti dal libro P.P. Chu, RTL Hardware Design Using VHDL, J. Wiley & Sons. 13

15 nel Cap. 4, introdurremo le istruzioni sequenziali, il concetto di process e alcune strutture basilari quali if, case e for; nel Cap. 5, studieremo alcuni aspetti avanzati della progettazione di circuiti combinatori e della loro ottimizzazione; nel Cap. 6, ci concentreremo sui circuiti sequenziale e sull analisi di quei componenti fondamentali che li costituiscono: per esempio, elementi di memoria. Vedremo la differenza nell uso di segnali e variabili, e proveremo a progettare qualche semplice circuito; nel Cap. 7, richiameremo alcuni concetti sulle macchine a stati finiti, la loro rappresentazione, la loro codifica e implementazione in VHDL; nel Cap. 8, vedremo cosa sono le Look Up Tables, i vantaggi e gil svantaggi nell uso di tale costrutto, come implementare una LUT in VHDL e come generare tale codice in maniera automatizzata; il Cap. 9 è dedicato ad aspetti avanzati della programmazione gerarchica, utile per dispositivi complessi; nel Cap. 10 approfondiremo un altro aspetto di progettazione modulare, ovvero il VHDL basato su parametri, in modo da personalizzare strutture molto generali; nel Cap. 11 vedremo come effettuare test e simulazione di dispositivi digitali; infine, nel Cap. 12, verrà proposta una brevissima guida all ambiente di sviluppo Xilinx ISE Web Pack 8.2i e verranno svolti alcuni esercizi significativi. 14

16 Capitolo 2 Hardware Description Languages Introdurremo in questo capitolo molti degli aspetti fondamentali dei linguaggi per la descrizione hardware, concentrandoci ovviamente sul VHDL. Questo capitolo vuole essere una panoramica generale: maggiori dettagli verranno presentati nei prossimi capitoli. 2.1 Introduzione Molti linguaggi di programmazione classici (C, Java, C++,...) non si addicono ad una descrizione hardware efficiente. Una delle caratteristiche principali nonchè peculiarità dei circuiti hardware e la capacità di esecuzione di processi in parallelo, normalmente non contemplata da linguaggi quali il C: in essi, le istruzioni sono eseguite in maniera sequenziale seguendo il flow di scrittura. Non solo: di solito, le varie subroutine utilizzano risultati ottenuti da subroutine precedentemente eseguite, il che comporta anche la necessità di manterere non solo l ordine delle istruzioni all interno di una funzione, ma anche l ordine nel quale tali funzioni vengono chiamate. Nel momento in cui si vuole implementare in hardware una certa funzionalità, spesso molti processi possono essere eseguiti in parallelo: non solo, in generale è anche buona norma mantenere una buona modularità ed indipendenza fra processi, in modo anche da limitare lo scambio dati, spesso problematico a causa di ritardi ed interconnessioni. Ciò non toglie che esistano degli adattamenti del C all hw (i cosiddetti HLL, High Level Languages): System C è stato un (pressochè fallimentare) esempio. Il nuovo trend in quest ottica riguarda la ricerca di traduttori C to VHDL, i quali partono da un codice C spolpato 15

17 delle funzioni che meno si addicono all hardware e forniscono in uscita un file VHDL. E palese che, nel momento in cui esiste un linguaggio (per quanto verbose, pesante e poco intuitivo quale il VHDL) nato ad hoc per l hardware, ogni tentativo di riportare linguaggi nativamente sequenziali quali il C ad avere le caratteristiche di un HDL (Hardware Description Language) snatura il codice stesso. Pertanto, spieghiamo meglio cosa sia un linguaggio orientato alla descrizione hw. 2.2 VHDL I primi Hardware Description Languages (HDL) nacquero negli anni 80 per consentire un agevole descrizione di schemi hw attraverso un linguaggio che fosse un buon trade off tra esigenze di basso livello (hardware) e alto livello (programmabilità). Infatti, nel momento in cui si svilupparono chip contenenti sempre una densità maggiore di transistor a parità di superficie, descrivere schemi attraverso netlist scritte a mano divenne, di fatto, improponibile anche per il più paziente degli implementatori. Fu così cheebbero immediata diffusione VHDL e Verilog. Entrambi hanno frecce al proprio arco, ed entrambi hanno difetti. Una delle caratteristiche molto usate del VHDL è la sua capacità di generazione parametrizzata di entità (semplicemente, se abbiamo bisogno, ad esempio, di inserire in un chip 16 sommatori, essi possono essere istanziati attraverso un semplice ciclo FOR...GENERATE), ed è per questo che, al momento, forse il VHDL ha un piccolo vantaggio sul Verilog. VHDL è un acronimo, in verità, di un acronimo: infatti, V equivale a VHSIC, acronimodivery High Speed Integrated Circuit. Nato nei primissimi anni 80, venne diffuso inizialmente dal Dipartimento della Difesa degli USA come standard per la documentazione hw, ed è stato più volte modificato nelle sue caratterizzazioni attraverso diversi standard. Nel 1987, venne definito dall IEEE lo standard definitivo, che tutt oggi utilizziamo. E un linguaggio case insensitive, ma molti programmi di sintesi sono case sensitive (!), quindi attenzione. Esistono poi 7 packages IEEE di VHDL, ovvero 7 librerie che aggiungono funzionalità allo standard base. I principali e più usati sono: , in cui vengono definiti alcuni standard per la sintesi hw; , in cui vengono definiti gli standard per la generazione della netlist RTL; 1164, il più usato, in cui vengono definiti molti tipi di dati, utili in operazioni elementari e bit a bit (i cosiddetti tipi std_logic_1164). 16

18 2.2.1 Strutture Il VHDL si basa su alcune strutture chiave, qui disposte in ordine gerarchico, e che andremo a breve a descrivere in maniera più approfondita: Library: sono le librerie di sistema che contengono i costrutti chiave del linguaggio, come i tipi di dati; Package: sono i pacchetti di strutture dati e hw contenuti all interno delle librerie (per intenderci, una struttura simile al Java); Entity: fornisce la descrizione dell interfaccia del blocco in analisi, ovvero numero e tipo di I/O; Architecture: fornisce la descrizione della funzionalità dell entity a cui è associata; Process: sono i processi in esecuzione concorrente e parallela, contenuti all interno dell architecture; Configuration: in strutture hw molto grandi, è spesso utile definire più architetture aventi la stessa interfaccia. A quel punto, la configuration permette di settare per ogni particolare istanza l architettura voluta. Partiamo da questo codice elementare, in modo da andare ad analizzarlo in tutte le sue parti: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity sum is Port ( a : in b : in s : out c : out end sum; STD_LOGIC; STD_LOGIC; STD_LOGIC; STD_LOGIC); architecture Behavioral of sum is begin 17

19 s <= a xor b; c <= a and b; end Behavioral; Entity Come detto, la entity fornisce la descrizione generale dell interfaccia di un dispositivo hardware. La dichiarazione di un entità è la seguente: entity entity_name is Port ( port_1 : direction port_2 : direction... port_n : direction end entity_name; type; type; type); In pratica, l entità definisce il nome del componente che vogliamo progettare (nel caso precedente, sum), il numero di porti di I/O (nel caso precedente 4, di cui due input e due output), la direzione di ognuno di essi e il tipo (per quest ultimo aspetto, si rimanda ai prossimi paragrafi). In particolare, un porto può avere le seguenti configurazioni: in, porto di sola lettura e di ingresso per il nostro sistema; out, porto di sola scrittura e di uscita per il nostro sistema (attenzione, una volta che si è scritto un valore NON può più essere riletto); inout, portodi ingresso o uscita, aseconda delle condizioni (attenzione, NON può essere contemporaneamente di ingresso e uscita); buffer, analogo a in e out, ma, in quest ultimo caso, il valore eventualmente scritto può anche essere riletto internamente. Architecture L architettura descrive il comportamento dell entity a cui è associata, ne determina la funzionalità o gli opportuni legami tra ingressi e uscite (ne definisce l implementazione). La struttura classica di una architettura è la seguente: 18

20 architecture nome of nome_entity is {parte dichiarativa} begin {istruzioni concorrenti} end architecture nome; Quindi, abbiamo che la struttura di un architettura è divisa dalla parola chiave begin in due sezioni: parte dichiarativa, per segnali, tipi, variabili, costanti, sottoprogrammi e component (si veda il capitolo 9) da utilizzare localmente nella architettura; istruzioni concorrenti, le quali possono essere: istruzioni di assegnazione di un valore ad un segnale (attenzione, SOLO ad un segnale); processi; istanze, ovvero connessioni di blocchi dichiarati come componenti nella parte dichiarativa e che vengono a tutti gli effetti istanziati. Le istruzioni comunicano attraverso segnali e sono eseguite in parallelo, e, più precisamente, in pipeline. Attenzione: non è detto che esse vengano eseguite nello stesso ordine in cui sono state scritte. E il sintetizzatore che sceglie come eseguire ogni processo e/o istruzione, e tale ordine non è in alcun modo modificabile. L architettura di una entità può essere descritta in tre modi differenti: 1. stile comportamentale: prevede la descrizione della logica attraverso uno o più processi dove si modellizza il comportamento del circuito senza fornire dettagli dell implementazione; 2. stile data flow: descrive la funzionalità di un circuito in base alle elaborazioni concorrenti e parallele che subiscono i dati; 19

21 3. stile strutturale: utilizza interconnessioni fra componenti e istanze differenti di vari blocchi, ognuno con una propria architecture e una propria interfaccia. Il componente dev essere dichiarato con la propria interfaccia come una qualsiasi altra variabile nella parte dichiarativa, e dev essere quindi istanziato in maniera concorrente tra le istruzioni dell architettura. Nella creazione dell istanza, vi sarà anche il cosiddetto port mapping, in cui i porti di I/O del componente vengono connessi con i porti e/o segnali dell entità in cui vengono istanziati. Approfondiremo meglio questi aspetti nel capitolo 9. A livello puramente introduttivo, proponiamo un brevissimo esempio: architecture arch of TOP is signal n : bit; component AND port ( A,B : in bit; C : out bit); end component; begin istanza_1: AND port map(a => ingr1, B => ingr2, C => n); end architecture arch; In questo caso, inseriamo all interno dell entità TOP un componente AND, avente due bit di ingresso e uno di uscita. Nel momento in cui istanziamo l oggetto AND all interno della nostra architettura, connettiamo i pin di ingresso A e B con gli ingressi di TOP, mentre creiamo un link fra l uscita C e il segnale n interno alla nostra architettura. Configuration Come già precedentemente anticipato, una entity può avere diverse architectures ad essa associate: può essere utile qualora vi siano più dispositivi aventi la stessa interfaccia. Attenzione, però, alla confusione che ne può nascere: il nome al blocco viene dato dalla entity. Pertanto, si corre il rischio di avere due istanze di una certa entità ENT che svolgono diverse elaborazioni sui dati a seconda della configurazione data ma con lo stesso nome. In generale, una configuration viene utilizzata solo in fase di test pre sintesi: supponendo di voler testare due possibili architetture per una entità, la configuration permette un cambio veloce tra più possibili architetture. Non è praticamente mai considerato un supporto contemplato dai sintetizzatori: l utilizzo di una configuration porta spesso al fallimento di una sintesi. La sintassi del comando è la seguente: 20

22 configuration nome_configuration of nome_entity is for nome_architecture {operazioni di assegnazione} end for; end nome_configuration; Process Un processo è costituito da un insieme di istruzioni sequenziali, eseguite poi nell ordine in cui sono state scritte dall utente. Il process comunica con il resto del progetto leggendo e/o aggiornando determinati segnali e porti di entity dichiarate al di fuori di esso. Più processi sono fra loro concorrenti, ovvero vengono eseguiti in parallelo (più precisamente, in pipeline), ma, in ogni istante, è attiva una sola istruzione per processo. E possibile anche vedere un insieme di processi come tante istruzioni concorrenti. La sintassi è la seguente: [nome_etichetta:] process [sensitivity_list] {dichiarazioni} begin {istruzioni sequenziali} end process [nome_etichetta]; I campi tra parentesi quadre [] sono facoltativi. La sezione dichiarativa definisce elementi locali, visibili solo all interno del particolare processo. Possono essere dichiarate: costanti variabili tipi sottotipi elementi di sottoprogrammi. Il processo si comporta come un loop infinito di un determinato gruppo di istruzioni sequenziali. Un processo può avere una sensitivity list: in pratica, è 21

23 una lista di segnali presenti nella entity e/o architecture, e la variazione di uno di questi segnali provoca l attivazione del loop del processo. In alternativa, si può evitare la sensitivity list e porre una delle seguenti istruzioni subito dopo il begin o subito prima della fine del processo: wait on sensitivity_list; wait for specific_time; wait until condition; Bisogna prestare attenzione, però, nell utilizzo della seconda e terza i- struzione: non tutti i sintetizzatori supportano (spesso, anche perchè non sarebbe realistico...) condizioni per quanto riguarda l attesa di un certo intervallo temporale. Il consiglio è quello di utilizzare, qualora fosse possibile, la sensitivity list. Un processo senza sensitivity list (o, in alternativa, senza condizioni wait) non verrà mai eseguito. E inoltre importante sottolineare come non si possano utilizzare istruzioni wait nel momento in cui è presente una sensitivity list. L eventuale etichetta consente di identificare meglio un processo: all interno della stessa architettura dev essere univoco e non deve richiamare delle parole chiave del VHDL. Per finire, un piccolo esempio di processo con sensitivity list: esempio: process(clk) begin if rising_edge(clk) then o <= i; end if; end process esempio; In questo caso, abbiamo implementato il processo per un architettura di un flip flop D: sul fronte di salita del clock (il comando rising_edge ha lo scopo di controllare il fronte di salita), il segnale sull uscita o assume il valore del segnale sull ingresso i. Il simbolo <= assume il significato di assegnazione, e si legge gets. Attenzione, perchè il simbolo di assegnazione di un segnale ha un significato ben diverso dal simbolo di assegnazione di una costante o variabile: approfondiremo meglio questi aspetti nel paragrafo

24 Package Il package è una collezione di definizioni che possono essere condivise fra due o più unità di progetto. Tali unità condivise possono essere: tipi di dato costanti componenti sottoprogrammi. Un package si suddivide in: header: contiene le dichiarazioni di tutti gli elementi che potranno essere visti dalle entity che utilizzano quel package. Sono quindi presenti dichiarazioni di sottoprogrammi, costanti, tipi di dato e componenti; body: contiene gli eventuali dettagli implementativi degli elementi dell header. Qualora il package non contenesse sottoprogrammi, potrebbe essere formato anche dal solo header. Esistono package di tipo standard inclusi nel VHDL, e sono quelli di cui abbiamo già parlato in precedenza. Per esempio, molti costrutti definiti nello standard 1164 sono contenuti nel package IEEE.std_logic_1164, ilqualeè molto utilizzato per la maggiore flessibilità rispetto allo standard classico bit. La sintassi del package è la seguente: package nome is {header} end; package body nome is {body} end; Per aggingere un package ad un modulo VHDL, si usa la keyword use. Ad esempio, l inclusione dello standard 1164 si effettua nel seguente modo: use IEEE.STD_LOGIC_1164.ALL; Analogamente al Java, si può importare tutto il package o solo parte di esso: la parola chiave ALL svolge le funzioni del simbolo *. 23

25 Library La prima istruzione in un modulo VHDL (ovvero entity più la/e architettura/e) dev essere l importazione di una libreria, qualora poi si intendano utilizzare dei package e dei componenti già realizzati. Per esempio, se (come di solito avviene) si intendono importare package dello standard IEEE, si deve prima di tutto caricare la libreria dell IEEE stesso. In pratica, la gerarchia è: Library IEEE; use IEEE.STD_LOGIC_1164.ALL; {entity} {architecture(s)} Elementi lessicali Presentiamo qui una serie di elementi lessicali caratterizzanti il VHDL. Commenti I commenti devono essere preceduti in VHDL dal simbolo --, e tutto ciò che segue tale simbolo fino al terminatore di riga viene ignorato dal compilatore. Ad esempio, un commento può essere: a <= b; -- al segnale a viene connesso il segnale b Identificatori Un identificatore rappresenta il nome di un oggetto in VHDL. Affinchè possa essere considerato tale, un identificatore deve rispettare alcune semplici regole: dev essere composto solo di lettere, numeri e underscore; non può cominciare con un numero nè un underscore; non può terminare con un underscore; non sono permessi due o più underscore consecutivi. 24

26 Ad esempio, X10 è un nomevalido, mentre 1A non lo è. Come anticipato, il VHDL è (o, meglio, sarebbe) case insensitive. E altrettanto vero che è buona norma essere consistenti con la propria definizione: se dichiariamo una variabile Anno, non è stilisticamente e praticamente molto comodo modificarla di volta in volta in ANNO o anno o quant altro. Senza contare che alcuni sintetizzatori sono case sensitive... Parole chiave In Fig. 2.1 presentiamo alcune parole chiave del linguaggio VHDL, che non possono essere utilizzate come nome nè etichetta per segnali, processi, ecc. Figura 2.1: Parole chiave del VHDL Numeri, caratteri e stringhe standard Un numero in VHDL può essere un intero (come 37 o 98E+7) ounnumero reale (come 1,2345), entrambi supportati dallo standard di definizione. E altresì possibile rappresentare un numero in altre basi, differenti dalla classica base 10. Lo standard di definizione per una base differente è il seguente: base # numero_nella_base_specificata # Ad esempio, se vogliamo rappresentare 18, possiamo anche esprimerlo in base 2 come 2#10010# o in base esadecimale come 16#12#. Per facilitare la lettura e la comprensione di numeri lunghi, possiamo suddividerli attraverso un underscore: ad esempio, il numero in base 2 precedente può essere scritto come 2#1_0010# senza problemi nè errori. 25

27 Un carattere in VHDL è compreso all interno di singoli apici. Ad esempio, caratteri sono A e 1. Attenzione: scrivere 1 e 1 sono due cose profondamente differenti. Il primo è un numero, mentre il secondo èun carattere (quindi, con proprietà bendifferenti). Una stringa è invece compresa fra doppi apici, ed è sempre considerata una stringa di caratteri: una stringa è, per esempio, "Hello". Ovviamente, è possibile anche esprimere una stringa di bit sotto forma di stringhe di caratteri, ma, a questo punto, diviene impossibile utilizzare il carattere underscore per rendere più leggibile la stringa stessa Oggetti Ci sono quattro tipi di oggetti in VHDL, e sono: costanti, variabili, segnali e file. Gli oggetti di tipo file non sono però sintetizzabili, e permangono nello standard solo per una questione di continuità con le versioni precedenti. Gli alias sono spesso considerati il quinto tipo di oggetto del VHDL. Costanti Una costante contiene un valore assegnatole in fase di dichiarazione e che non può più venire modificato in seguito. La definizione di una costante può avvenire nella sezione apposita di processi, architetture, package,... secondo la seguente sintassi: constant nome : tipo_di_dato := valore; Variabili La definizione dell IEEE è symbolic memory location, ed è abbastanza rappresentativa: sostanzialmente, rappresenta una locazione di memoria locale per un processo, in cui, per comodità di programmazione, si effettua uno storage di un certo valore. La sintassi della dichiarazione (da effettuarsi nelle dichiarazioni di un processo) è la seguente: variable nome : tipo_di_dato [:= valore_iniziale]; I campi fra parentesi quadre sono facoltativi. L assegnazione all interno di un processo a runtime è da considerarsi senza ritardo, perciò effettuato con l utilizzo del simbolo :=: nome := 2; 26

28 Segnali Un segnale è uno degli oggetti più comuni del VHDL. Va dichiarato nella sezione apposita di una architettura, e ha la seguente sintassi: signal nome : tipo_di_dato [:= valore_iniziale]; Come al solito, le parti fra parentesi quadre sono facoltative. L inizializzazione avviene senza ritardo, perciò con il simbolo :=. Un segnale assume un determinato valore se viene connesso ad un porto di input, ad un altro segnale, ad una variabile o ad una costante. Il simbolo di assegnazione è il già citato<=. In tal caso, il segnale modifica il proprio valore SOLO al termine del ciclo del processo, e non istantaneamente: se questo fatto ricalca ciò che poi avviene in un circuito reale, è vero però che obbliga a prestare attenzione. Supponiamo di avere due segnali, a,b, e di inizializzare a:=0. Consideriamo il seguente codice: a <= a+1; b <= a; L assegnazione del valore 1 al primo passo ad a avviene solo alla fine del ciclo. Pertanto, quando assegnamo b<=a, a ha ancora valore 0. Alla fine del ciclo, quindi, b varrà 0. Se, invece, dichiariamo a come variabile, e modifichiamo il codice precedente nel seguente modo: a := a+1; b <= a; Tutto andrebbe come vorremmo. Attenzione però: l utilizzo sconsiderato e poco ponderato di variabili può portare ad un codice non sintetizzabile. Senza contare che in molti processi può essere utile sfruttare questa peculiarità del VHDL. Alias L alias non è un vero e proprio oggetto, ma semplicemente un nome alternativo per chiamare un oggetto o parte di esso. Viene usato per semplicità di programmazione quando si trattano moduli complessi. La maniera più semplice per comprendere l alias è passare attraverso un esempio. Supponiamo di voler progettare un processore, avente istruzioni a 16 bit. Supponiamo che, per esempio, tali istruzioni siano strutturate nel seguente modo: un codice dell operazione da 8 bit; 27

29 due operandi da 4 bit ciascuno. Può essere comodo definire tre alias (non si badi alla definizione dei tipi, sarà chiara dopo la lettura del paragrafo 2.2.4) nel seguente modo: -- istruzione a 16 bit signal word : std_logic_vector(15 downto 0); -- alias per l operazione a 8 bit alias op : std_logic_vector(7 downto 0) is word(15 downto 8); -- alias per i due operandi alias reg1 : std_logic_vector(3 downto 0) is word(7 downto 4); alias reg2 : std_logic_vector(3 downto 0) is word(3 downto 0); E, ovviamente, nettamente più chiaro l utilizzo di reg1 piuttosto che considerare di volta in volta sottostringhe di word. Purtroppo, molti sintetizzatori ancora non supportano gli alias, quindi bisogna prestare attenzione nell utilizzo Tipi di dati e operatori In VHDL ogni oggetto ha un suo tipo, definito attraverso: un range o set di valori limitato che tale oggetto può assumere; un set di operazioni che possono essere eseguite dall /sull oggetto considerato. Il VHDL è strong typed, ovvero possono essere eseguite su un oggetto di un determinato tipo solo e soltanto quelle operazioni definite per quel determinato tipo. Per adattare (qualora sia possibile e consentito) un determinato oggetto al tipo più adatto per una certa operazione, esistono funzioni di conversione di tipo, dette anche funzioni di casting, definite nelle librerie standard. Questa scelta per il VHDL è stata effettuata per semplicità dipro- grammazione e sintesi; d altra parte, però, l utilizzo di operatori di casting rende spesso il codice pesante e difficile da leggere. Il VHDL è ricco di tipi: accanto ai tipi predefiniti dallo standard originale, si sono affiancati i tipi IEEE, molto utilizzati per l enorme quantità di operazioni e valori ad essi applicabili. Partiamo con un analisi dei tipi originari, per poi passare ai tipi IEEE. 28

30 Tipi e operatori predefiniti Esistono circa 12 tipi originari di VHDL. I principali sono: integer: sono i numeri interi. Lo standard non definisce un range di valori preciso, ma garantisce che essi debbano avere almeno un range pari a [ (2 31 1), (2 31 1)], ovvero almeno 32 bit con segno. Esistono sottotipi (come i positive) per rappresentare, per esempio, numeri solo positivi; boolean: definiti nel solo range {true, false}; bit: definiti nel solo range { 0, 1 }; bit_vector: definiti come array mono dimensionale di elementi di tipo bit. Lasintassiperladefinizioneè: bit_vector(a downto b), dovea rappresenta il bit più significativo (MSB) e b il bit meno significativo (LSB), e la lunghezza totale dell array èparia(a b)+1; time: utilizzato per le variabili di tempo, può essere misurato in s, ms, ns, ps e fs; real: rappresenta i numeri reali, ma è un tipo poco utilizzato perchè molto raramente sintetizzabile; character e string: caratteri ASCII e array di questi ultimi. Originariamente, il tipo bit era nato per rappresentare quantità booleane, limitandole ai soli due casi 1 e 0. In verità, nella pratica esistono molte altre necessità: si pensi al caso di indeterminazione, o, ancora, all alta impedenza su un pin. Nacquero così i tipi std_logic e il corrispondente std_logic_vector, definiti nello standard IEEE Molto utilizzata è la definizione di un nuovo tipo di dato, attraverso la parola chiave type: per esempio, è utile definire dei nomi per identificare gli stati di una FSM. La sintassi del comando è la seguente: type nome_tipo is { range_valori } Ad esempio, potremmo definire nuovi tipi per diversi scopi: type stato is {countup, countdown} 29

31 Passiamo a parlare degli operatori standard del VHDL. Sono definiti moltissimi operatori, ed ognuno di essi è applicabile solo a determinati tipi di operandi: in Fig. 2.2 vengono mostrati i principali operatori standard. Attenzione, però, in quanto un operazione come l esponenziale, per quanto presente ed utilizzabile, non sempre viene ottimizzata dal sintetizzatore: pertanto, una semplice istruzione potrebbe portare ad un occupazione di scheda enorme, se non addirittura ad un codice non sintetizzabile. Gli operatori sono utili, ma vanno usati con criterio nel momento in cui si voglia realizzare un progetto serio: spesso, è meglio ottimizzare manualmente le operazioni da eseguire sui dati, in modo da usare quasi esclusivamente operatori booleani. Per quanto riguarda l ordine di precedenza, esistono regole molto complesse per quanto riguarda i vari tipi di operatori: la cosa migliore e che si consiglia SEMPRE (anche per chiarezza nei confronti di chi poi dovrà riutilizzare o anche solo leggere e capire il vostro codice) di fare è utilizzare le parentesi per definire manualmente come operare sui dati. In alcuni casi è d obbligo. Vediamo il seguente esempio. Vogliamo calcolare y = a b + c d. Il seguente codice è sbagliato: y <= a and b or (not c) and d; -- NO!! Infatti, molti sintetizzatori non saprebbero come compilare il precedente codice. Il VHDL prevede una gerarchia di operatori, ma molti sintetizzatori non contemplano una sub gerarchia fra operatori con la stessa priorità. Il risultato è che dovrete riscrivere il codice. Molto più chiara e corretta è la seguente scrittura: y <= (a and b) or ((not c) and d); -- Sì!! Tipi e operatori del package IEEE std logic 1164 I tipi definiti nello standard IEEE 1164 nacquero e vennero definiti per avvicinare la rappresentazione hw/sw del VHDL a quelle che sono le caratteristiche elettriche dei circuiti reali. I tipi e gli operatori definiti in questo standard possono essere inclusi semplicemente con le due seguenti righe di codice, da porre in cima ad un modulo VHDL: Library IEEE; use IEEE.STD_LOGIC_1164.ALL; I tipi più usati sono: 30

32 Figura 2.2: Tabella dei principali operatori standard 31

33 std_logic: rappresenta un alternativa al tipo bit, completandolo con tutti gli ulteriori possibili valori che può assumere un segnale reale; std_logic_vector: è un vettore di bit di tipo std_logic. Itipistd_logic possono variare in un range di 9 possibili valori. particolare: In 0 e 1 : il segnale è forzato a valere 0 oppure 1, con un valore di tensione corrispondente sulla base della logica usata e stabilita dal sintetizzatore. Il segnale è forzato da una corrente (detta driving current) che viene fatta materialmente scorrere nel pin; Z : alta impedenza, utile nel caso di buffer tri state; L e H : come 0 e 1, ma qui la corrente è debole e il segnale viene forzato ai diversi stati logici sulla base della cosiddetta wired logic; X e W : rappresentano lo stato di indeterminazione nel caso di driving current e wired logic. Può essere utilizzato dall utente e dal simulatore in fase di debug per evidenziare eventuali conflitti o collegamenti non precisi; U : significa che un segnale non è stato inizializzato nè è stato in alcun modo forzato ad assumere un certo valore. In questo caso, è vietata l assegnazione da parte di un utente, ma è utilizzato in fase di simulazione; - : è il simbolo don t care. Per quanto riguarda gli operatori, è stato effettuato un overload di molti dei principali operatori presentati in Fig In Fig. 2.3 viene mostrata la tabella degli operatori più usati. Esistono, inoltre, operatori per la conversione di tipo, per passare da tipi dello standard 1164 a tipi dello standard VHDL classico. In Fig. 2.4 vengono presentati questi ultimi. Itipistd_logic_vector sono semplicemente array di std_logic, ovvero di bit. Analogamente a quanto visto per i bit_vector, si può definire un vettore nel seguente modo: signal y : std_logic_vector(7 downto 0); In questo caso, generiamo un segnale y a8bit,ilcuimsbèilbitpiù a sinistra. Se volessimo seguire una rappresentazione opposta, potremmo scrivere: 32

34 Figura 2.3: Operatori di overload per lo standard IEEE 1164 Figura 2.4: Operatori di conversione per lo standard IEEE

35 signal y : std_logic_vector(0 to 7); Di solito, la prima èlapiùusata. Analogamente a quanto visto in Figg. 2.3 e 2.4, esistono operatori di conversione e overload anche per gli array. Peculiarità di questi ultimi sono gli operatori relazionali, di concatenazione e di aggregazione. Per operatori relazionali, si intendono quegli operatori di confronto per verificare, ad esempio, l uguaglianza di due vettori. I principali sono: =,>,<. Escluso l operatore di uguaglianza, che richiede anche egual lunghezza per i due vettori, gli operatori di confronto possono essere utilizzati anche con array aventi un numero diverso di bit. Per esempio, i seguenti operatori restituiscono true: "011" = "011"; "011" > "010"; "0110" < "11"; L operatore di concatenazione & è molto utilizzato per generare array più grandi a partire da vettori più piccoli. Un esempio semplice è il seguente. Supponiamo di avere due vettori, a e b, entrambia8bit,edivolergenerare un vettore c a 16 bit. Semplicemente: c <= a & b; Si possono eseguire svariati tipi di operazioni. Per esempio, replicare il MSB due volte: c <= a(7) & a(7) & a(7 downto 2); O effettuare uno shift di due posizioni (senza utilizzare l apposito operatore): c <= a(1 downto 0) & a(7 downto 2); Infine, gli operatori di aggregazione vengono spesso utilizzati in fase di assegnazione manuale di un certo valore ad un certo segnale. Supponiamo di voler assegnare ad un certo segnale a un valore " ". La maniera più immediata è la seguente: a <= " "; Esistono delle alternative, talvolta utili. La prima è l utilizzo della cosiddetta notazione positional association: 34

36 a <= ( 1, 0, 1, 0, 0, 0, 0, 0 ); Altra alternativa è la notazione index/value, in cui i bit possono essere assegnati in ordine sparso: a <= (7=> 1, 2=> 0, 1=> 0, 4=> 0, 3=> 0, 6=> 0, 5=> 1, 0=> 0 ); Più comodaè la notazione piped index/value: a <= (7 5=> 1, => 0 ); La parola chiave others permette di assegnare un certo valore a tutti gli indici non utilizzati fino a quel momento nell espressione in cui si trova. Ad esempio, la precedente assegnazione può divenire: a <= (7 5=> 1, others => 0 ); La parola chiave others è spesso utilizzata nel momento in cui si vuole inizializzare un intero vettore a 0 o a 1: a <= (others => 0 ); -- equivale a a <= " "; Consigli pratici Ricapitolando, in VHDL esistono tantissimi operatori, tipi di dato, oggetti di svariato tipo, per non parlare dei mille modi in cui si possono definire ed assegnare vettori e segnali. Attenzione, però, all uso che si fa di questa libertà: non scordiamoci mai che ciò che noi programmiamo non è un semplice toy dimostrativo sulla completezza del linguaggio, bensì è un qualcosa che deve poi essere tradotto in un array di 0 e 1 nel bitstream per programmare un oggetto composto per lo più da AND, OR e qualche LUT. Quando possibile, il consiglio è sempre quello di utilizzare tipi standard IEEE 1164, al più interi o caratteri, e di mantenere sempre una certa chiarezza nel momento in cui vengono inizializzati e assegnati valori e segnali. Il consiglio è quello di scrivere, se necessario, anche un po di codice in più, ma che il tutto sia più chiaro e immediato. Attenzione, infine, a non usare costrutti troppo complessi: il sintetizzatore potrebbe riservarvi brutte sorprese... 35

37 Capitolo 3 Istruzioni concorrenti Le istruzioni concorrenti in VHDL sono semplici, eppure molto potenti. Se utilizzate in modo efficiente, il collegamento tra le elaborazioni da esse effettuate e lo schema logico e fisico del circuito è diretto. Questo può aiutare a progettare in maniera più efficace circuiti anche complessi. Per modo efficiente si intende l utilizzo di costrutti elementari, quali and oppure or, tralasciando operatori quali l elevazione a potenza, di difficile ed incerta sintesi. Si dividono in 3 gruppi, ovvero istruzioni concorrenti: 1. semplici; 2. condizionali; 3. di selezione; Vedremo in questo capitolo come le istruzioni semplici siano delle istruzioni concorrenti condizionali, ma senza condizioni. 3.1 Circuiti combinatori vs. Circuiti sequenziali Prima di addentrarci nelle istruzioni concorrenti, vediamo come possono essere classificati i circuiti digitali. Un circuito combinatorio o circuito basato su logica combinatoria non ha memoria interna nè può essere diviso in stati di funzionamento. L output è funzione solo e soltanto degli input, e ad uguali ingressi corrispondono uguali uscite. Sebbene in un circuito reale vi possa essere un periodo di transitorio, il valore di regime, ottenuto dopo un breve lasso di tempo, sarà quello atteso. In termini di implementazione, un circuito combinatorio non presenterà nel 36

38 proprio schema elementi di memoria (quali flip flop o latch) o un loop in retroazione. Un circuito sequenziale, viceversa, è un circuito che al proprio interno prevede la presenza di registri di stato e/o elementi di memoria. L output di un circuito sequenziale è funzione sia degli input che dello stato attuale del circuito stesso. Sebbene le istruzioni concorrenti possano essere utilizzate anche per descrivere circuiti sequenziali, in quest ultimo ambito è preferibile l utilizzo dei processi, che rendono più chiara e semplice anche il debug del circuito stesso (si veda il Cap. 6 per maggiori dettagli). Le istruzioni concorrenti vengono, invece, ampiamente utilizzate nell ambito dei circuiti combinatori. 3.2 Istruzioni concorrenti semplici In generale, un istruzione concorrente semplice per l assegnazione di un certo valore ad un certo segnale segue questa sintassi: destinazione <= nuovo_valore ritardo; Quanto appena riportato è la definizione di istruzione di assegnazione semplice così come viene fornita dal VHDL. Un esempio semplice può essere: y <= a + b after 10 ns; Il segnale y assumerà un valore pari alla somma di a e b dopo 10 ns: per esempio, potremmo voler forzare i ritardi interni dovuti alla logica combinatoria ad essere pari a 10 ns, o ancora potremmo volerli simulare in fase di progettazione. Purtroppo, tutto ciò non è possibile, ma non a causa di un errore sintattico, ma di sintesi: non esiste sintetizzatore al mondo in grado di progettare una logica tale da avere un ritardo voluto a priori. La sintassi corretta anche per la sintesi diviene: y <= a + b; Sarà il progettista a dover poi, eventualmente, tener conto dei ritardi interni nel momento in cui il blocco realizzato comunicherà con altri blocchi hw. Il ritardo dovuto alla logica combinatoria è spesso definito δ-delay. Altra nota: in quest ottica, nulla ci vieta di introdurre un loop in retroazione. Ad esempio: y <= (not y) and b; 37

39 Il fatto è che la porta NOT comporterà un ritardo nell elaborazione (anche se semplice...) del segnale. Se y cambia velocemente a causa, per esempio, di frequenti variazioni di b, la porta NOT come si comporterà? Le uscite saranno prevedibili e verificabili a priori? In generale, introdurre feedback è sempre pericoloso, specie in circuiti combinatori, e l uso in questi casi è fortemente sconsigliato. 3.3 Istruzioni concorrenti condizionali Sintassi La sintassi per un istruzione di assegnazione concorrente condizionale è piuttosto semplice, ed è sostanzialmente l unione di più istruzioni semplici attraverso condizioni di attivazione. Vediamo: segnale <= val_1 when condizione_1 else val_2 when condizione_2 else... val_n when condizione_n else val_default; Le varie condizioni sono espressioni booleane che restituiscono valore true o false. Asegnale viene attribuito un valore val_i, dovelai-esima èlaprima condizione in ordine che ha restituito valore true. In parole povere, si partirà controllando la prima condizione; se essa non restituisce true, si passa a valutare la seconda; si prosegue, fino all i-esima, che supponiamo restituisca true: in tal caso, al nostro segnale attribuiremo valore pari a val_i. Se nessuna condizione è verificata, dev essere sempre presente un termine di assegnazione di default. Nei circuiti combinatori, è uno dei costrutti più utilizzati, ma va prestata attenzione all ordine di scrittura delle condizioni in caso di condizioni e segnali di selezione complessi Un esempio: multiplexer a 4 ingressi Un multiplexer è semplicemente una specie di interruttore fisico hardware in grado di connettere l uscita con uno dei 4 ingressi, ed in particolare con l input scelto attraverso appositi segnali di pilotaggio. Un multiplexer a 4 ingressi e un uscita (detto anche mux 4 1) in particolare avrà: 4 segnali di ingresso, supponiamo a 8 bit ciascuno; 2 segnali di pilotaggio, per selezionare l ingresso 1, 2, 3 oppure 4; 38

40 Tabella 3.1: Tabella di funzionamento per il multiplexer 4 1 Input Output sel o 0 0 a 0 1 b 1 0 c 1 1 d un segnale di uscita. La tabella di funzionamento per il segnale di selezione è riportata in Tab Il seguente codice VHDL è sbagliato. Vediamo perchè: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity mux4 is Port ( a,b,c,d : in std_logic_vector(7 downto 0); sel : in std_logic_vector(1 downto 0); o : out std_logic_vector(7 downto 0)); end mux4; architecture Behavioral of mux4 is begin o <= a when (sel = "00") else b when (sel = "01") else c when (sel = "10") else d; end Behavioral; Apparentemente, sembrerebbe tutto ok. Però attenzione: stiamo utilizzando dei segnali dello standard IEEE 1164, e ricordiamo che essi possono assumere ben 9 valori! Se noi avessimo in ingresso sul segnale di selezione una stringa "X0" per un qualche motivo, noi porremmo in uscita il canale d, il che non è corretto! Il codice precedente può essere modificato in modo da contemplare anche questi casi patologici: 39

41 library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity mux4 is Port ( a,b,c,d : in std_logic_vector(7 downto 0); sel : in std_logic_vector(1 downto 0); o : out std_logic_vector(7 downto 0)); end mux4; architecture Behavioral of mux4 is begin o <= a when (sel = "00") else b when (sel = "01") else c when (sel = "10") else d when (sel = "11") else "XXXXXXXX"; end Behavioral; Nel caso di segnale di selezione non noto precisamente, poniamo in uscita un segnale non determinato. Ogniqualvolta cambierà il segnale di selezione o l ingresso attualmente selezionato, l istruzione verrà ricontrollata e l uscita opportunamente modificata. 3.4 Istruzioni concorrenti di selezione Sintassi Nel caso di istruzioni condizionali, un certo segnale di uscita assume un certo valore sulla base di una o più condizioni booleane, che possono essere molto semplici (come nel caso del multiplexer del paragrafo 3.3.2) o anche molto complesse. Nel caso di condizioni su un singolo segnale, è spesso una valida alternativa l utilizzo di istruzioni di assegnazione concorrente di tipo selettivo, ovvero aventi la seguente sintassi: with segnale_controllo select segnale <= val_1 when scelta_1, 40

42 val_2 when scelta_2,... val_n when scelta_n; Come in precedenza, o si contemplano tutti i casi possibili o è necessaria una condizione di default. Di fatto, rispetto a quanto visto nel precedente paragrafo non cambia nulla, se non per il fatto che si può controllare un segnale invece che un espressione booleana anche complessa. La struttura di questo tipo di istruzioni ricalca quella di un select/case dei linguaggi tradizionali, ed effettua un mapping pressochè immediato di una tabella di verità Un esempio: multiplexer a 4 ingressi (con istruzioni di selezione) Riprendiamo l esempio del mux 4 1 del paragrafo Modifichiamo l architecture in modo da utilizzare le istruzioni concorrenti di selezione. architecture Behavioral of mux4 is begin with sel select o <= a when "00", b when "01", c when "10", d when "11", "XXXXXXXX" when others; end Behavioral; Abbiamo utilizzato la keyword others per evitare di scrivere tutte le possibili combinazioni e offrire una soluzione di default Un secondo esempio: tabella di verità generica Supponiamo di voler implementare il circuito che generi gli output di Tab Tale circuito avrà un ingresso da 3 bit e un output da 1 bit. Il codice corrispondente sarà: library IEEE; 41

43 Tabella 3.2: Tabella di verità Input Output x o use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity tv is Port ( x : in std_logic_vector(2 downto 0); o : out std_logic); end tv; architecture Behavioral of tv is begin with x select o <= 0 when "000", 1 when "001", 1 when "010", 0 when "011", 1 when "100", 1 when "101", 1 when "110", 1 when "111", X when others; end Behavioral; Un alternativa più compatta è la seguente, utilizzando la notazione pipe: architecture Behavioral of tv is 42

44 begin with x select o <= 0 when "000" "011", 1 when "001" "010" "100" "110" "101" "111", X when others; end Behavioral; Notiamo, però, una peculiarità: quando il primo bit di x è pari a 1, a prescindere da quanto valgono gli altri due bit l uscita è sempre 1. Potremmo semplificare la scrittura precedente con il simbolo don t care: architecture Behavioral of tv is begin with x select o <= 0 when "000" "011", 1 when "001" "010" "1--", X when others; end Behavioral; Attenzione però: se in input avessimo una stringa "11X" in uscita otterremo 1, anche se non è ciò che vogliamo. Quindi attenzione all utilizzo dei simboli don t care, specie se si vogliono contemplare anche casi patologici quali quelli di segnali indeterminati. E possibile scrivere l espressione precedente in forma ancora più compatta calcolando l equazione booleana, per esempio, attraverso l utilizzo delle mappe di Karnaugh. Si verifica, infatti, che o = x 2 +x 1 x 0 +x 1 x 0,dovex 2 èilmsbex 0 il LSB. Attenzione, però, nuovamente al fatto che, in questo caso, non gestiamo i casi patologici: talvolta questo può essere un problema. 43

45 Capitolo 4 Istruzioni sequenziali Come può suggerire il nome stesso, le istruzioni sequenziali vengono eseguite in sequenza, e la loro semantica è ancor più simile alle classiche istruzioni di un linguaggio di programmazione tradizionale. Molti costrutti sequenziali, peraltro, sono incompatibili con la natura concorrenziale propria del VHDL, e devono perciò essere incluse all interno di un guscio concorrente detto process e che abbiamo presentato nel paragrafo Se le istruzioni concorrenti fornivano, di fatto, un mapping diretto (o quasi...) del codice nelle caratteristiche più schiettamente hardware, le istruzioni sequenziali, in generale, non possiedono questa proprietà, ma tendono a descrivere in maniera talvolta anche astratta il comportamento del sistema (in particolare, di una entity). Proprio per questa natura quasi astratta, le istruzioni sequenziali vanno utilizzate con molta cautela, al fine di ottenere un codice corretto sotto tutti i punti di vista, non ultima la capacità di essere poi sintetizzato. In questo capitolo, rivedremo molti concetti introdotti sommariamente nel Capitolo 2, e approfondiremo meglio alcuni aspetti fondamentali. 4.1 Il processo Introduzione Un process o processo, comegiàpiù volte detto, è un costrutto che contiene un insieme di azioni, le quali devono essere eseguite in maniera sequenziale. Tali azioni sono le istruzioni sequenziali, mentre il processo in sè può essere considerato come un unica istruzione (o costrutto) concorrente. Le istruzioni sequenziali racchiudono una vasta gamma di costrutti, i quali possono essere utilizzati solo all interno di un processo. Al contrario di 44

46 quanto visto per le istruzioni concorrenti, le istruzioni sequenziali vengono eseguite nell ordine in cui sono state scritte. Molti di questi costrutti sono utilissimi in fase di programmazione, ma non sintetizzabili, perciò bisogna prestare attenzione. In questo capitolo, analizzeremo alcuni costrutti: wait; istruzioni di assegnazione di un certo valore ad un certo segnale; istruzioni di assegnazione di un certo valore ad una certa variabile; if...then...else; case; for...loop. Esistono strutture anche molto più complesse e potenti, facenti parte del cosiddetto stile di programmazione parametrizzato (si veda il Cap. 10). Attenzione anon farconfusione: una istruzione sequenziale è un istruzione VHDL all interno di un processo; un circuito sequenziale è un circuito dotato di elementi di memoria e registri di stato. In generale, mentre le istruzioni concorrenti possono essere utilizzate quasi solo nella descrizione di circuiti combinatori, le istruzioni sequenziali e i processi possono essere usati sia per circuiti combinatori che sequenziali. Per il momento, ci concentreremo sui circuiti combinatori. Per maggiori dettagli sui circuiti sequenziali, si veda il Cap. 6. Un processo, abbiamo visto, deve avere un evento di attivazione, ovvero una causa scatenante. Tale causa può essere settata sia attraverso l utilizzo di una sensitivity list, sia con costrutti come il wait. Procediamo con ordine Processi con sensitivity list La sintassi di un processo con sensitivity list è il seguente: process(sensitivity_list) {dichiarazioni} begin {istruzioni sequenziali} end process; 45

47 La sensitivity list nient altro è che una lista di segnali alla variazione dei quali il processo è sensibile. Infatti, analogamente a quanto succede in hw, un processo può avere due stati: attivato o sospeso. Seè sospeso, il processo è in stato di idle, ovvero non fa nulla, e attende che uno dei segnali della propria sensitivity list vari. Nel momento in cui tale transizione arriva, il processo si attiva, e vengono eseguite le istruzioni sequenziali nell ordine in cui sono state scritte dal programmatore. Una volta che tali istruzioni sono terminate, il processo si sospende in attesa di una nuova variazione di un segnale della sensitivity list. Esistono due tipi di processi con sensitivity list: a lista completa ed incompleta. Supponiamo di avere 3 segnali: signal a, b, y : std_logic; Un processo con sensitivity list completa è il seguente: process(a,b) begin y <= a and b; end process; Un qualsiasi variazione di a o b causa l attivazione del processo e, quindi, un cambio di valore per y. Viceversa, un processo avente sensitivity list incompleta èdeltipo: process(a) begin y <= a and b; end process; In questo caso, il processo viene attivato solo per variazioni di a. Questo significa che se anche b cambia valore, e con esso dovrebbe cambiare anche y, in verità finchè non cambia a l uscita non subisce variazioni Processi con istruzioni wait Un processo che presenta una o più istruzioniditipowait al suo interno non ha di sensitivity list. Come anticipato nel paragrafo 2.2.1, le istruzioni sono: wait on {segnali}: il processo si sospende in quel punto in attesa di una variazione su uno dei segnali selezionati; 46

48 wait until {condizione}: il processo si sospende in attesa che la condizione divenga true; wait for {intervallo_di_tempo}: determinato periodo di tempo. il processo si sospende per un Per esempio, il circuito con sensitivity list completa del precedente paragrafo può essere scritto come: process begin y <= a and b; wait on a, b; end process; Teoricamente, è sintatticamente corretto anche il seguente codice: process begin y <= a and b; wait on b; y <= b; wait on a, b; end process; Nella pratica, un utilizzo dei wait di questo tipo è fortemente sconsigliato, perchè facilmente porta a codice non sintetizzabile. In generale, è sempre preferibile utilizzare una sensitivity list, perchè: il costrutto wait on è sostanzialmente equivalente e complica la leggibilità del codice; se le condizioni del costrutto wait until fossero troppo complicate, porterebbero facilmente alla mancata sintesi del circuito; in generale, a meno di casi molto patologici, il costrutto wait for non è mai sintetizzabile. 47

49 4.2 Istruzioni di assegnazione sequenziale per segnali Abbiamo affrontato in parte questi argomenti già nel paragrafo Ricordiamo che un segnale all interno di un processo assume il suo valore definitivo solo al termine del processo. Pertanto, se noi inizializziamo un segnale tmp: signal tmp : integer := 0; Al primo passo, con il seguente processo, otteniamo y=0 e tmp=1: process(a) begin z <= a; tmp <= tmp + 1; y <= tmp; end process; Di fatto, y sarà in ritardo di un passo rispetto a tmp. Questo permette di effettuare (per quanto non sia buona norma farlo...) più assegnazioni contemporanee:... signal a,b,c,d : std_logic;... process(a,b,c,d) begin y <= 1 ; y <= a and b; y <= c and d; y <= a or c; end process; Il precedente codice equivale a calcolare direttamente:... signal a,b,c,d : std_logic;... process(a,b,c,d) begin y <= a or c; end process; 48

50 Proprio in questa peculiarità si evidenziano le differenze con le variabili e con le istruzioni concorrenti. Un codice come questo, per quanto insensato, è sintetizzabile:... signal a,b,c,d : std_logic;... process(a,b,c,d) begin y <= 1 ; y <= a and b; y <= c and d; y <= a or c; end process; Se non utilizzassimo i processi, ma le istruzioni concorrenti:... signal a,b,c,d : std_logic;... y <= 1 ; y <= a and b; y <= c and d; y <= a or c;... Questo codice non sarebbe nemmeno sintetizzabile, in quanto contemporaneamente vorremmo connettere y al segnale di high, ma anche all uscita di due AND e di un OR, con evidente conflitto. 4.3 Istruzioni di assegnazione sequenziale per variabili Passiamo alla descrizione delle istruzioni per attribuire un certo valore ad una variabile. Sintatticamente, rispetto ai segnali cambia poco: si utilizza il simbolo := invece di <=. Concettualmente, invece, cambia moltissimo: una variabile varia il proprio valore in modo istantaneo nel momento in cui effettuiamo l assegnazione. In certi casi, questa può essere una comodità, ma riempire un processo di variabili può portare all utilizzo di molte unità di memoria nonchè spesso porta anche a codice non sintetizzabile (nella realtà, una variabile non può cambiare in maniera immediata, ma avrà sempreil delta delay). Vediamo un semplice codice, in cui utilizziamo solo segnali: 49

51 ... signal a,b,c,d : std_logic; signal tmp : std_logic;... process(a,b,c,d) begin tmp <= a and b; tmp <= tmp or c; y <= tmp and d; end process; In questo caso, introduciamo dei loop, in quanto questo codice equivale al seguente:... signal a,b,c,d : std_logic; signal tmp : std_logic;... process(a,b,c,d) begin tmp <= tmp or c; y <= tmp and d; end process; Ricordiamo, inoltre, che y assume valore pari all AND fra il valore di tmp prima dell aggiornamento e il valore di d. Se utilizziamo una variabile, le cose cambiano molto:... signal a,b,c,d : std_logic;... process(a,b,c,d) variable tmp : std_logic; begin tmp := a and b; tmp := tmp or c; y <= tmp and d; end process; In questo caso, avremo y = ((a b) +c) d), perchè la variabile tmp viene aggiornata nel proprio valore ad ogni passo. Bisogna prestare attenzione, perchè un uso poco attento di variabili e segnali può portare a malfunzionamenti comportamentali. D altro canto, un utilizzo troppo spinto delle variabili può portare a problemi in fase di sintesi o post place and route. 50

52 4.4 Costrutto if...then...else Sintassi La sintassi di questo costrutto è analoga a quella di un qualsiasi altro linguaggio di programmazione: if condizione_1 then {istruzioni sequenziali}; elsif condizione_2 then {istruzioni sequenziali};... else {istruzioni sequenziali}; end if; Ogni gruppo di istruzioni sequenziali è detto branch, e, in particolare, si hanno i: then branch: gruppo di istruzioni che segue l if iniziale; elsif branch: gruppi di istruzioni che seguono gli elsif ; else branch: gruppo di istruzioni che segue l else finale Un esempio: multiplexer a 4 ingressi Riprendiamo l esempio, presentato nel paragrafo 3.3.2, del multiplexer 4 1. Come detto, può essere implementato attraverso semplici istruzioni concorrenti. Altrettanto semplicemente può essere implementato con istruzioni sequenziali in un processo. Il codice è il seguente: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity mux4 is Port ( a,b,c,d : in std_logic_vector(7 downto 0); s : in std_logic_vector(1 downto 0); o : out std_logic_vector(7 downto 0)); end mux4; 51

53 architecture Behavioral of mux4 is begin process(a,b,c,d,sel) begin if(s="00") then o <= a; elsif(s="01") then o <= b; elsif(s="10") then o <= c; elsif(s="11") then o <= d; else o <= "XXXXXXXX"; end if; end process; end Behavioral; Le differenze con i costrutti analizzati nel Cap. 3 sono, per casi semplici, davvero piccole, ed identificare una scelta ottima tra if e with...select...when è davvero complesso. In generale, è buona norma, mano a mano che si complicano le condizioni da analizzare, cercare di utilizzare il costrutto if assieme ad un processo: rende il tutto più leggibile. Ad esempio, nel caso di una doppia condizione annidata, l utilizzo dell if è immediato ed intuitivo:... signal a,b,c,d : std_logic;... process(a,b,c,d) begin if(a= 1 ) then if(b= 0 ) then o <= c; else o <= d; end if; else 52

54 if(b= 1 ) then o <= c; else o <= d; end if; end process; Incomplete branch Viene definito branch incompleto un costrutto if in cui non vengono contemplate tutte le possibili alternative. In parole povere, il seguente codice è un incomplete branch:... signal a,b,c : std_logic;... process(a,c) begin if(a= 1 ) then b <= c; end if; end process; Il segnale b cambia il proprio valore solo se: vi è stata una variazione di a o c; a ha valore pari a 1. In caso contrario, b non varia. Se b non è stato ancora mai assegnato ad un certo valore, il suo stato è U Incomplete assignment Un costrutto if può avere molti branch, e un entità può averemolti segnali. In alcuni branch alcuni segnali potrebbero non venire assegnati nuovamente. Facciamo un esempio:... signal a,b,c,d : std_logic;... process(a,c) 53

55 begin if(a>c) then b <= c; elsif (a=c) then d <= a; else d <= 0 ; end if; end process; In questo caso, abbiamo un complete branch, ma nel then branch non assegnamo alcun valore a d, mentre nei due casi successivi non assegnamo alcun valore a b. Se non modificato, un segnale mantiene il proprio valore. Però la programmazione è poco precisa. Si consiglia di utilizzare molto raramente gli incomplete branch (limitarli ai casi di controllo su fronte di salita di un clock, per esempio) e ancor più di rado gli incomplete assignment, perchè alcuni segnali potrebbero diventare poco gestibili. 4.5 Costrutto case...when Sintassi La sintassi del costrutto è la seguente: case espressione is when scelta_1 => {istruzioni sequenziali}; when scelta_2 => {istruzioni sequenziali};... when scelta_n => {istruzioni sequenziali}; end case; Si valuta, quindi, il risultato di una certa espressione (booleana o complessa) e si esegue un certo branch (insieme di istruzioni sequenziali) sulla base di tale risultato. Sono possibili, anche in questo costrutto, casi di incomplete branch o assignment, anche se sconsigliabili. E sempre consigliabile l introduzione di una scleta di default, al più utilizzando la parola chiave others. Questo costrutto è simile a quanto visto nel Cap. 3: come già detto nel precedente paragrafo, costrutti sequenziali in processi sono più flessibili, semplici 54

56 ed affidabili (in termini di sintesi hw) rispetto a costrutti concorrenti, e, quindi, sono in generale preferibili Un esempio: multiplexer a 4 ingressi Riprendiamo per l ennesima volta il nostro mux 4 1. Possiamo progettarlo anche nel seguente modo: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity mux4 is Port ( a,b,c,d : in std_logic_vector(7 downto 0); s : in std_logic_vector(1 downto 0); o : out std_logic_vector(7 downto 0)); end mux4; architecture Behavioral of mux4 is begin process(a,b,c,d,sel) begin case s is when "00" => o <= a; when "01" => o <= b; when "10" => o <= c; when "11" => o <= d; when others => o <= "XXXXXXXX"; end case; end process; end Behavioral; 55

57 4.6 Costrutto for...loop Introduciamo il costrutto for...loop. Inverità, in questa forma è raramente utilizzato, perchè spesso porta a codice non sintetizzabile. Approfondiremo questi aspetti e formulazioni alternative per il costrutto for nel Cap Sintassi La sintassi è la seguente: for indice in {range} loop {istruzioni sequenziali}; end loop; La variabile indice tiene traccia del numero di iterazioni, che varieranno all interno di un range fissato. Il branch verrà eseguito, quindi, un numero determinato di volte. Nè la variabile indice nè il range devono essere preventivamente dichiarati. Vediamo un esempio Un esempio: XOR bit a bit a 8 bit Premesso che esiste un operatore XOR anche per vettori di bit, supponiamo di volerne realizzare uno nostro. Il codice può essere: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity xor8 is Port ( a,b : in std_logic_vector(7 downto 0); o : out std_logic_vector(7 downto 0)); end xor8; architecture Behavioral of xor8 is constant WIDTH : integer := 8; begin process(a,b) begin for i in (WIDTH-1) downto 0 loop 56

58 o(i) <= a(i) xor b(i); end loop; end process; end Behavioral; Dichiariamo una costante WIDTH per il numero di bit dei vettori, quindi iniziamo il loop avente come indice i e come range 7 (cioè WIDTH-1) fino a 0. In tutto, quindi, effettueremo 8 step. 57

59 Capitolo 5 Progetto di circuiti combinatori Progettare circuiti combinatori non ècosìsemplicecomepotrebbesem- brare ad un primo approccio. Innanzitutto, come già appare chiaro dai precedenti capitoli, bisogna porre attenzione a scrivere un codice che sia sintetizzabile: per esempio, è necessario evitare condizioni di attesa o ritardo temporale. Mano a mano che un progetto si fa più ampio e complesso, è necessario intervenire anche sulla struttura stessa del circuito: sebbene il VHDL spesso non ricordi molto da vicino uno schematico, bisogna lavorare in modo da rendere più efficace ed efficiente lo schema conclusivo. In parole povere, modificare l input per migliorare l output. Da un punto di vista operativo, tutto ciò si traduce in alcuni semplici ma importanti step: progetto iniziale del sistema; ottimizzazione del progetto; implementazione del progetto ottimizzato; ottimizzazione del codice. Per ottimizzazione del codice VHDL si intende, per esempio, l utilizzo di costrutti semplici, al più scrivendo operatori complessi manualmente, o l eliminazione di elementi quali le variabili. In parte, per semplicità in questo capitolo verremo meno a queste idee per quanto riguarda, soprattutto, l implementazione manuale di alcuni operatori quali la somma o la sottrazione, supponendo che tali miglioramenti vengano effettuati dal sintetizzatore. 58

60 5.1 Operator Sharing Una delle più immediate ottimizzazioni applicabili ad un codice riguarda la diminuzione del numero di strutture pesanti dal punto di vista computazionale e di occupazione di scheda. Non scordiamoci mai che l output del nostro lavoro è pur sempre il bitstream di programmazione, che andrà poi a creare (o non creare) dei fuse su una FPGA. Le FPGA, normalmente, hanno un numero molto grande, ma pur sempre limitato, di porte logiche. Un circuito può essere considerato migliore di un altro circuito che esegua analoghe operazioni se offre una precisione maggiore e/o se occupa un numero di porte logiche e LUT inferiore e/o (nel caso di circuiti sequenziali, che tratteremo nel prossimo capitolo) se fornisce un thorughput maggiore (ovvero, ogni quanti colpi di clock otteniamo un risultato valido). Ogni componente, sia esso un adder o un multiplexer, ha una sua occupazione di scheda che lo caratterizza. E ovviamente improponibile pensare di ricordare l occupazione di tutti i possibili core hardware. In linea di massima, è comunque intuibile che un multiplexer occuperà meno porte logiche di un sommatore, e ancora meno di un moltiplicatore. Anzi, su alcune schede un moltiplicatore non è neppure presente: pertanto, il sintetizzatore provvederà ad usare strutture alternative (ancora più pesanti sotto tutti i punti di vista). E buona norma, quindi, tenere d occhio anche le risorse hw disponibili sulla scheda su cui poi vorremo implementare il nostro circuito. In particolare, l operator sharing prevede l individuazione di risorse che posssono essere utilizzate da diverse operazioni. Talvolta, ciò comporta un aumento del numero totale di componenti: questo è, però, accettabile se aumentano i core leggeri a fronte di una drastica diminuzione di quelli pesanti. Consideriamo il seguente esempio: process(a,b,c,d) begin if(a<c) then r <= a+b; else r <= a+c; end if; end process; In tutto, necessitiamo di due sommatori, un multiplexer (per l if ) e un comparatore. Notiamo, però, come in entrambi i casi sia calcolata una somma tra due quantità. Inoltre, uno dei due operandi èinamboicasia. Pertanto, possiamo scrivere il seguente codice, più efficiente: 59

61 process(a,b,c,d) signal src : std_logic_vector(7 downto 0); begin if(a<c) then src <= b; else src <= c; end if; r <= src + a; end process; In questo modo, utilizziamo un sommatore solo, un comparatore, un multiplexer per l if e uno per la selezione di b o c. Abbiamo, quindi, un sommatore in meno e un multiplexer in più: dato che un mux èmoltomeno ingombrante di un sommatore, questa seconda soluzione è preferibile. In questo caso, l operatore a cui abiamo applicato lo sharing è la somma. Ovviamente, all aumentare del numero di operazioni che risparmiamo aumentano anche i vantaggi che riusciamo a trarre dall operator sharing. Spesso, essendo abbastanza facili da individuare, un sintetizzatore è in grado di effettuare in automatico queste ottimizzazioni. 5.2 Functionality Sharing La functionality sharing prevede l ottimizzazione di un circuito sfruttando le proprietà delle funzioni e dei core che vengono utilizzati nel circuito stesso. Per esempio, se dobbiamo progettare un microprocessore, non si può pensare di implementare un singolo blocco per ogni possibile operazione: la ricerca consta nel voler trovare quelle affinità tra funzioni che permettano di semplificare il numero totale di blocchi da utilizzare. E molto più difficile individuare la possibilità di functionality sharing rispetto all operator sharing, tanto è vero che non esiste sintetizzatore al mondo in grado di effettuare questo tipo di considerazioni (se non in casi banali). Con un esempio tutto risulterà più chiaro. Supponiamo di voler progettare un core in grado di implementare la somma e la sottrazione tra due operandi. Verrà calcolata una piuttosto che l altra quantità sulla base del valore di un segnale di controllo ctrl. Supponiamo che gil addendi siano del tipo signed, ovvero in complemento a 2. Una soluzione è la seguente: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; 60

62 use IEEE.STD_LOGIC_UNSIGNED.ALL; use IEEE.NUMERIC_STD.ALL; entity addsub is Port ( a,b : in std_logic_vector(7 downto 0); ctrl : in std_logic; r : out std_logic_vector(7 downto 0)); end addsub; architecture Behavioral of addsub is signal src0, src1, sum : signed(7 downto 0); begin -- trasformiamo a e b in signed src0 <= signed(a); src1 <= signed(b); sum <= src0 + src1 when ctrl= 0 else src0 - src1; -- trasformiamo sum in std_logic_vector r <= std_logic_vector(sum); end Behavioral; In tutto, utilizziamo un sommatore, un sotrattore e un multiplexer. Dato che sommatore e sotrattore non sono lo stesso componente, non possiamo applicare le tecniche viste nel precedente paragrafo. E altrettanto vero, però, che in complemento a 2 vale la seguente relazione: a b = a + b +1 Dato che un adder ha sempre un ingresso per il carry in, ovvero il resto in ingresso, l operazione precedente implica l utilizzo di un solo sommatore, anche se dobbiamo sommare 3 quantità. A questo punto, possiamo applicare l operator sharing ed ottenere: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; use IEEE.NUMERIC_STD.ALL; entity addsub is 61

63 Port ( a,b : in std_logic_vector(7 downto 0); ctrl : in std_logic; r : out std_logic_vector(7 downto 0)); end addsub; architecture Behavioral of addsub is signal src0, src1, sum : signed(7 downto 0); signal cin : signed(0 downto 0); begin -- trasformiamo a e b in signed src0 <= signed(a); src1 <= signed(b) when ctrl= 0 else signed(not(b)); -- calcolo del carry-in cin <= "0" when ctrl= 0 else "1"; sum <= src0 + src1 + cin; -- trasformiamo sum in std_logic_vector r <= std_logic_vector(sum); end Behavioral; Abbiamo 2 multiplexer, un inverter (per il NOT) e un sommatore: 4 componenti invece di 3, ma risparmiamo un sotrattore (pesante) per introdurre un inverter e un multiplexer (leggeri). 5.3 Ottimizzazioni di layout Un buon progetto passa anche attraverso l attenzione che si presta a come viene generato un circuito alla conclusione del processo di sintesi. Ovviamente, l utente non può agire sul sintetizzatore. Però può utilizzare un po di furbizia nel codice che si passa al sintetizzatore. Uno degli aspetti peggiori dei circuiti, siano essi combinatori o sequenziali, è rappresentato dalla capacità di prevedere quanto tempo (nel caso di circuiti combinatori) o quanti cicli di clock (per i sequenziali) passeranno prima di ottenere gli output, una volta forniti gli input. Questa fase del progetto è molto noiosa, ma altrettanto importante, specie se il blocchetto che si considera è parte integrante di un circuito più grande, dal quale si otterranno gli ingressi e si forniranno le uscite. Ci sarebbero migliaia di considerazioni da fare, ma concentriamoci sull aspetto temporale attraverso un 62

64 esempio semplice. Vogliamo implementare un circuito che fornisca un uscita y tale che: { (a b)+c se ctrl =0 y = ((a b) c) d se ctrl =1 dove è lo XOR bit a bit. Un implementazione diretta è: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity esempio is Port ( a,b,c,d : in std_logic; ctrl : in std_logic; r : out std_logic); end esempio; architecture Behavioral of esempio is begin process(a,b,c,d,ctrl) begin if(ctrl= 0 ) then y <= (a and b) or c; else y <= ((a xor b) xor c) xor d; end if; end process; end Behavioral; Lo schema circuitale è rappresentato in Fig Nasce un problema: se ctrl vale 0, abbiamo due stadi di logica di ritardo; altrimenti, ne abbiamo 3. Come risultato otteniamo un diverso δ delay, e quindi un circuito poco preciso e prevedibile. Come sistemarlo? La prima soluzione è quella di aggiungere un buffer di ritardo sulla prima soluzione. Dato che questa scelta non è proprio il massimo della vita, si può sfruttare la proprietà associativa dello XOR. Pertanto, si ha: ((a b) c) d =(a b) (c d) 63

65 Quindi, scriviamo il nostro codice come: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity esempio is Port ( a,b,c,d : in std_logic; ctrl : in std_logic; r : out std_logic); end esempio; architecture Behavioral of esempio is begin process(a,b,c,d,ctrl) begin if(ctrl= 0 ) then y <= (a and b) or c; else y <= (a xor b) xor (c xor d); end if; end process; end Behavioral; In uscita otterremo uno schema come quello di Fig. 5.2, ovvero bilanciato. 64

66 Figura 5.1: Circuito sbilanciato nei δ delays 65

67 Figura 5.2: Circuito bilanciato nei δ delays, sfruttando la proprietà associativa dello XOR 66

68 Capitolo 6 Progetto di circuiti sequenziali 6.1 Introduzione Circuiti sequenziali Come detto nel paragrafo 3.1, un circuito sequenziale è caratterizzato da un proprio stato interno e da elementi di memoria. Quindi, mentre in un circuito combinatorio gil output, istante per istante, sono funzione solo di quel singolo input, in un circuito sequenziale la funzione che genera le uscite è funzione non solo dell ingresso istantaneo, ma di tutti gli ingressi precedenti, i quali vengono memorizzati nello stato interno del circuito. Il nome sequenziale deriva proprio dal fatto che l output è funzione della sequenza di input. I circuiti sequenziali si dividono in asincroni e sincroni: in questi ultimi, tutti gli elementi di memoria sono controllati da uno o più segnali di sincronizzazione (clock). I circuiti sequenziali sincroni sono largamente i più utilizzati e rappresentano l argomento di interesse per quanto riguarda la nostra trattazione Elementi di memoria Elementi di memoria possono essere aggiunti ad un circuito in due modi: la prima tecnica è l utilizzo di retroazione in un circuito combinatorio classico. Questa tecnica però è pericolosa e sconsigliabile a causa dei ritardi di propagazione, che possono anche portare ad uscite non desiderate nè previste; la seconda soluzione è rappresentata dall uso di flip flop e latch. 67

69 In Fig. 6.1 sono presenti i principali tipi di latch e flip flop (FF). Dato che, sostanzialmente, il latch non èsincronoe,sec è 1 copia l ingresso d, l utilizzo di questo elemento può portare al cosiddetto fenomeno dei running delays, ovvero propagazione di ritardi. L utilizzo di elementi sincroni, quali i flip flop D in figura, è consigliato, in quanto crea una sorta di cuscinetto, per esempio, nell I/O di un componente per ammortizzare i ritardi e favorire la propagazione del dato nel circuito. Non si entra nei particolari dei flip flop, che dovrebbero essere noti, se non per una notazione: q* rappresenta lo stato successivo che assumerà il FF. Figura 6.1: I vari tipi di flip flop D type E importante approfondire, invece, le caratteristiche temporali dei flip flop, mostrate graficamente in Fig Analizziamo le diverse voci: T cq : clock to Q delay, ovvero il tempo di propagazione necessario al segnale d per propagarsi sull uscita q dopo il sampling del fronte di clock; T setup :èilsetup time, ovvero l intervallo di tempo per il quale il segnale d deve rimanere stabile prima del fronte del clock; 68

70 T hold : hold time, ovvero per quanto tempo dopo il fronte del clock il segnale d deve rimanere stabile. Figura 6.2: Timing diagram di un flip flop D Quindi, mentre T cq rappresenta il classico ritardo di propagazione, gli altri due sono vincoli temporali, i quali entrano in gioco in un circuito sincrono per la stima della massima frequenza di clock ammessa: infatti, se i segnali dovessero variare troppo rapidamente (o il periodo di clock fosse troppo piccolo) violeremmo uno di questi constraints. In particolare, tale violazione porta il FF in uno stato cosiddetto metastabile Classificazione dei circuiti sequenziali A seconda di come viene organizzata la rete di distribuzione interna del clock per i vari FF, possiamo classificare i circuiti sequenziali in: circuiti totalmente sincroni (o, più semplicemente, circuiti sincroni ): tutto il circuito utilizza FF sincronizzati da un unico segnale di clock comune a tutto il dispositivo. E la soluzione più utilizzata,inquantofacilita la progettazione, l interfacciamento, il test e la verifica dei progetti. Non solo, è utile sia in sistemi di grandi dimensioni (es. processori) sia in dispositivi elementari (es. contatori); circuiti asincroni localmente sincroni: a causa di vincoli progettuali (ad esempio, il piazzamento di certi componenti in parti distanti della scheda), potrebbe essere difficile far condividere a tutto il sistema lo stesso clock. Si può, però, dividere il tutto in tanti sotto sistemi più 69

71 semplici, all interno dei quali creare circuiti sincroni con un proprio clock. A questo punto, il vero problema consiste nella sincronizzazione tra tali sotto sistemi: esistono tecniche ad hoc, ma che non tratteremo in quanto non di nostro diretto interesse; circuiti totalmente asincroni (o, più semplicemente, circuiti asincroni): nessun componente del circuito utilizza un clock globale, quindi o i FF vengono auto gestiti attraverso una politica basata su comuni segnali usati come clock, oppure ogni flip flop utilizza un clock proprio e non legato a nessun altro segnale di sincronizzazione. Circuiti di questo tipo sono di difficile gestione e progettazione, pertanto non verranno trattati. 6.2 Circuiti sequenziali sincroni Passiamo ad analizzare i circuiti sequenziali sincroni, ovvero quelli di maggiore interesse dal nostro punto di vista. Innanzitutto, descriviamo lo schema generale di circuito sincrono sequenziale di Fig Il blocco di elaborazione next state logic riceve in ingresso i segnali di input e, eventualmente, lo stato del sistema (in pratica, la traccia degli input e output agli istanti precedenti): il suo compito è quello, sulla base di tali informazioni, di generare lo stato successivo per il blocco di calcolo in uscita dal circuito. In pratica, ricevuti gli input, genera i dati per il calcolo dell output vero e proprio, eseguito poi dalla output logic. Nel mezzo, si introduce un FF di tipo D per la sincronizzazione. Questa è una struttura a stato sincronizzato: esistono varianti a ingressi/uscite registrate, che non prevedono il FF per lo stato ma l aggiunta di un flip flop in ingresso e/o uscita per sincronizzare gli I/O. Figura 6.3: Schema a blocchi generale per un circuito sequenziale sincrono 70

72 Possiamo identificare 3 tipi di circuiti sequenziali: 1. Regular Sequential Circuits: la rappresentazione dello stato e la transizione fra essi è semplice da gestire, come nel caso di contatori o shift registers. Spesso, lo stesso stato della macchina è l output del circuito; 2. Random Sequential Circuits: la gestione della transizione fra stati è più complessa, in quanto l assegnazione di essi è randomica rispetto all output della macchina. E il caso delle macchine a stati finiti (FSM) semplici, in cui l uscita è determinata solo dallo stato del sistema; 3. Combined Sequential Circuits: è l unione dei due casi precedenti. Una FSM è utilizzata per la gestione degli stati, ma l output è generato sulla base di una elaborazione su uno o più input. Vengono dette macchine a stati finiti con datapath (FSMD), anche se spesso, per semplicità, si usa anche per esse il nome FSM. Approfondiremo le FSM e le FSMD nel Cap Programmazione di elementi di memoria elementari Tutte le schede che si utilizzano sono composte da: slice, ovvero celle di calcolo elementari (per lo più AND e OR); slice flip flop, ovvero celle contnenti flip flop per la sincronizzazione; LUTs, ovvero piccole celle di memoria, in cui l ingresso è costituito dall indirizzo della cella di memoria e l output è il dato in essa memorizzato (le analizzeremo più approfonditamente nel Cap. 8). Nel momento in cui sintetizziamo un codice VHDL, il sintetizzatore è in grado di tradurre le nostre righe di codice in componenti presenti on board sulla scheda di detinazione del nostro lavoro: è per questo che non ha senso pensare di scrivere un codice VHDL senza conoscere bene su quale device vogliamo implementare il nostro circuito. Quindi, se implementiamo una architettura avente il comportamento di un flip flop D, in automatico il sintetizzatore trasformerà il componente in un elemento della libreria e lo piazzerà nelle apposite slice. Fatta questa premessa, vediamo nei prossimi paragrafi come implementare alcune semplici unità di memorizzazione. 71

73 6.3.1 Positive Edge Triggered D Flip Flop La tabella di attivazione del FF D positive edge è presente in Fig Vediamo come potrebbe essere implementato: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity ffd is Port ( d : in STD_LOGIC; clk : in STD_LOGIC; q : out STD_LOGIC); end ffd; architecture Behavioral of ffd is begin process(clk) begin if rising_edge(clk) then q <= d; end if; end process; end Behavioral; Sul fronte di salita del clock (controllato dal comando rising_edge facente parte dello standard IEEE 1164), il segnale di ingresso viene copiato sull uscita. Altrimenti, non viene fatto nulla. Come mostrato in Fig. 6.4, il sintetizzatore riconosce che il componente da noi progettato è proprio un flip flop di tipo D positive edge triggered FF D con reset asincrono Vediamo un secondo esempio. Modifichiamo la precedente struttura aggiungendo un reset (ipotizzandolo attivo basso), il quale agisce in maniera totalmente asincrona rispetto al clock: quando vale 0 tale segnale (attivo basso), l uscita si deve annullare. library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; 72

74 Figura 6.4: Schema RTL di sintesi per un flip flop D use IEEE.STD_LOGIC_UNSIGNED.ALL; entity ffdr is Port ( d : in STD_LOGIC; clk : in STD_LOGIC; reset : in STD_LOGIC; q : out STD_LOGIC); end ffdr; architecture Behavioral of ffdr is begin process(clk, reset) begin if reset= 0 then q <= 0 ; elsif rising_edge(clk) then q <= d; end if; end process; end Behavioral; Si notino la modifica nella sensitivity list e l ordine dei branch nell if :se avessimo controllato il reset dopo il clock, avremmo avuto un comportamento errato. Sintetizzando, si ottiene lo schema di Fig. 6.5: nella libreria della scheda utilizzata, non era contemplato un flip flop con reset attivo basso, ma solo con un clear attivo alto. Pertanto, cosa accade? Il sintetizzatore inserisce un inverter al fine di utilizzare il componente disponibile. 73

75 Figura 6.5: Schema RTL di un flip flop D con reset asincrono E se volessimo un reset sincrono? Modifichiamo l architettura precedente nel seguente modo: architecture Behavioral of ffdr is begin process(clk) begin if rising_edge(clk) then if reset= 0 then q <= 0 ; else q <= d; end if; end if; end process; end Behavioral; In questo caso, la sensitivity list include solo il clock, e il segnale di reset viene analizzato solo sui fronti di salita. Anche il sintetizzatore modifica il componente utilizzato, come mostrato in Fig Registro a 8 bit Un registro a 8 bit non è nient altro che un insieme di 8 FF D classici. Il codice è piuttosto semplice: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; 74

76 Figura 6.6: Sintesi RTL di un FF D con reset sincrono use IEEE.STD_LOGIC_UNSIGNED.ALL; entity reg8 is Port ( d : in STD_LOGIC_VECTOR (7 downto 0); clk : in STD_LOGIC; q : out STD_LOGIC_VECTOR (7 downto 0)); end reg8; architecture Behavioral of reg8 is begin process(clk) begin if rising_edge(clk) then q <= d; end if; end process; end Behavioral; RAM La Random Access Memory è uno dei tipi di memoria in assoluto più utilizzati, ma anche più difficili da implementare in VHDL. Infatti, tecnicamente una RAM si può rappresentare come un insieme di latch, con molti segnali di handshaking in fase di gestione. Di fatto, si consiglia vivamente di fare riferimento alla documentazione della propria scheda e sintetizzatore per verificare (qualora sia prevista...) l eventuale presenza nelle librerie di sistema di un blocco RAM già realizzato ed utilizzare quel componente. 75

77 6.3.5 ROM La Read Only Memory è un altro componente molto utilizzato, specie per le LUT (vedi Cap. 8). Rispetto alle RAM, è molto meno efficiente, ma più semplice da implementare in VHDL: in generale, a struttura case con indirizzo e dato, il sintetizzatore fa corrispondere un componente ROM. Attenzione: se in una scheda non sono disponibili ROM esterne alla FPGA, l implementazione delle celle di memoria avverrà on board sulla scheda, con grande occupazione di slice (a meno che, ovviamente, i dati da salvare non siano pochi...). Tratteremo meglio in seguito questi aspetti. 6.4 Un esempio: contatore sincrono modulo m programmabile Un contatore modulo m è un componente hardware che, come dice il nome stesso, effettua un conteggio, partendo da 0 e terminando quando raggiunge il valore m 1, per iniziare nuovamente da 0. Per esempio, un contatore modulo 3 effettuerà i seguenti passaggi: 0, 1, 2, 0, 1,... Nel nostro esempio, supporremo che m venga espresso a 4 bit: pertanto, il range di valori ammissibili per m varierà fra 2(ovvero"0010") e 15(ovvero"1111"). Nel seguente codice, si potrebbe anche introdurre una notazione mai vista in precedenza, ma utile nel momento in cui si utilizzano i tipi IEEE Possiamo inserire stringhe esadecimali aggiungendo un prefisso X alla stringa di bit. Ogni carattere esadecimale rappresenta 4 bit: pertanto, il range di m può essere espresso tra X"2" e X"F". Nel nostro caso, dovendo trattare quantità aritmetiche senza segno, sfrutteremo invece i tipi di dato unsigned dello standard IEEE La struttura generale ricalca quella di Fig Prima soluzione Vediamo una prima soluzione: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity contm is Port ( clk : in STD_LOGIC; reset : in STD_LOGIC; 76

78 end contm; m : in STD_LOGIC_VECTOR (3 downto 0); q : out STD_LOGIC_VECTOR (3 downto 0)); architecture Behavioral of contm is signal r_reg : unsigned(3 downto 0); signal r_next : unsigned(3 downto 0); begin -- state-register process(clk, reset) begin if reset= 0 then r_reg <= (others => 0 ); elsif rising_edge(clk) then r_reg <= r_next; end if; end process; -- next-state logic r_next <= (others => 0 ) when r_reg=(unsigned(m)-1) else r_reg + 1; -- output logic q <= std_logic_vector(r_reg); end Behavioral; La sintesi di questa soluzione in moduli hw di una FPGA Xilinx èpresente in Fig Seconda soluzione Notiamo che c è un opportunità di ottimizzazione attraverso sharing. Infatti, la seguente operazione: r_reg=(unsigned(m)-1) richiede l utilizzo di un decrementatore. Sela riscriviamo come: r_reg+1=unsigned(m) 77

79 Figura 6.7: Sintesi della prima soluzione proposta per il contatore modulo m potremmo pensare di utilizzare il sommatore per il calcolo di r_next anche per la condizione del when, eliminando così un sottrattore. Possiamo scrivere, quindi, il precedente codice anche nel seguente modo, con l aggiunta di un terzo segnale di appoggio : architecture Behavioral of contm is signal r_reg : unsigned(3 downto 0); signal r_next : unsigned(3 downto 0); signal r_inc : unsigned(3 downto 0); begin -- state-register process(clk, reset) begin if reset= 0 then r_reg <= (others => 0 ); elsif rising_edge(clk) then r_reg <= r_next; end if; end process; -- next-state logic r_inc <= r_reg + 1; r_next <= (others => 0 ) when r_inc=(unsigned(m)) else r_inc; -- output logic q <= std_logic_vector(r_reg); 78

80 end Behavioral; Anche dallo schema di sintesi RTL di Fig. 6.8 appare subito una maggiore semplicità, con l assenza del sotrattore. Figura 6.8: modulo m Sintesi della seconda soluzione ottimizzata per il contatore 6.5 Analisi temporale di circuiti sincroni Nel momento in cui sintetizziamo un circuito, sia esso combinatorio o sequenziale, sincrono o asincrono, in output otteniamo alcune informazioni molto importanti. Innanzitutto, la più importante è se il circuito è sintetizzabile o meno: per quanto possa sembrare banale, nel progetto di componenti molto complessi potrebbe esserci sfuggita una struttura non sintetizzabile, o, magari in qualche tentativo di sharing, potremmo aver creato qualche corto circuito o conflitto nei collegamenti. Altre informazioni sono l occupazione totale di porte logiche e le caratteristiche temporali dell oggetto realizzato: un buon progetto non deve occupare più slice di quante non siano effettivamente necessarie, e (per circuiti sincroni) deve avere una frequenza massima di funzionamento quanto più possibile elevata. Concentriamoci sui circuiti sincroni. Per quanto riguarda quest ultimo aspetto, è necessaria qualche ulteriore precisazione preliminare. Nel momento in cui progettiamo un sistema hw complesso, al fine di incrementarne le prestazioni, possiamo strutturarlo sostanzialmente in due modi: 79

81 struttura parallela; struttura pipeline. In generale, una struttura parallela è in grado di elaborare contemporaneamente molti input contemporaneamente, fornendo in uscita gli output. Più le elaborazioni sui dati sono indipendenti fra loro, più risulta efficiente tale strategia. In uscita si ha un circuito che occupa tendenzialmente molte slide (a causa delle molte strutture di calcolo) e ha una frequenza di funzionamento non elevatissima a causa della necessaria sincronizzazione fra le varie componenti. Tale struttura è anche caratterizzata da una latenza (ovvero, il numero di cicli di clock intercorsi fra l arrivo dell input e la generazione del corrispondente output) contenuta e da un throughput (numero di output validi a fronte del numero di cicli di clock intercorsi) variabile a seconda del problema. Una struttura pipeline prevede l elaborazione di un solo input a ciclo di clock, e l elaborazione successiva di esso su molti stage. L occupazione totale è di solito più bassa o in linea con un circuito parallelo, ma ha una frequenza di clock consentita più elevata grazie alla sincronizzazione offerta, di fatto, dal clock globale. La latenza iniziale è molto alta (di solito, molto più alta di una struttura parallela), mentre per il thoroughput bisogna distinguere due casi: a regime, di fronte ad una frequenza di arrivo degli input di circa un ingresso valido ogni ciclo di clock, il thorughput è pari a 1 (efficienza massima); se la frequenza di un nuovo ingresso valido è bassa, la pipeline non è efficiente, è ha un throughput bassissimo. Quando scegliere una struttura piuttosto che un altra? Dipende dal problema. Se, a regime, abbiamo un ingresso ogni ciclo di clock e le elaborazioni da eseguire sono complesse (e richiedono alcuni cicli di clock) è consigliabile la struttura pipeline. Approfondiamo, però, alcuni aspetti temporali per i circuiti sincroni Massima frequenza di clock In Fig. 6.9, viene proposta l analisi temporale generica per il circuito sequenziale proposto in Fig Oltre ai classici vincoli dovuti al FF (già presentati nel paragrafo 6.1.2), si aggiungono T nextmin e T nextmax, che rappresentano i ritardi di propagazione nello schema per le variazioni dello stato 80

82 successivo, generate dall apposito circuito. A questo punto, al fine di evitare violazioni del setup time, è immediato verificare che il periodo minimo per un ciclo di clock Tclk è dato dalla seguente formula: T clk = T cq + T nextmax + T setup (6.1) Ovviamente, la frequenza massima di funzionamento è: f clk = 1 T cq + T nextmax + T setup (6.2) Figura 6.9: Analisi temporale di un circuito sequenziale sincrono Altre informazioni Oltre alla frequenza massima di funzionamento (di gran lunga la più importante), un sintetizzatore è in grado di fornire altre informazioni, utili soprattutto per l interfacciamento tra dispositivi all interno di progetti di grandi dimensioni: quanto tempo prima, rispetto al fronte di clock, deve essere stabile l ingresso al nostro sistema; 81

83 quanto tempo dopo, rispetto al fronte di clock, avremo a disposizione l output desiderato; l eventuale ritardo di propagazione dovuto alla presenza di logica combinatoria all interno di unn circuito sequenziale (soluzione raramente utilizzata) Output del sintetizzatore Xilinx Premesso che il sintetizzatore messo a disposizione da Xilinx non è il migliore disponibile su mercato, vediamo come leggere un output del sintetizzatore. Recuperando l esempio del contatore del paragrafo 6.4, andiamo, per esempio, ad osservare l occupazione di porte logiche su una Xilinx Virtex 4: Macro Statistics # Adders/Subtractors : 1 4-bit adder : 1 # Registers : 4 Flip-Flops : 4 # Comparators : 1 4-bit comparator equal : 1 Optimizing... Device utilization summary: Selected Device : 4vsx25ff Number of Slices: 6 out of % Number of Slice Flip Flops: 4 out of % Number of 4 input LUTs: 13 out of % Number of IOBs: 10 out of 320 3% Number of GCLKs: 1 out of 32 3% Il primo blocco di risultati è quello fornito dal sintetizzatore senza ottimizzazioni: in pratica, è una traduzione in forma testuale dello schema RTL di Fig A questo punto, dopo ottimizzazioni varie (per esempio, sul routing e di tipo sharing), troviamo il riassunto dell occupazione. Per la FPGA selezionata (identificata dal codice 4vsx25ff668-12), abbiamo un occupazione totale di 6 slice di logica, 4 flip flop, utilizziamo 13 LUT (molte delle 82

84 quali per evitare l utilizzo del sommatore, lento, sostituendolo con locazioni di ROM, ad accesso pressochè istantaneo), 10 porti di I/O (abbiamo 4 bit per m, 4 per l output, il clock e il segnale di reset) e un segnale di clock (che è stato riconosciuto e sintetizzato come tale). Per quanto riguarda l analisi temporale abbiamo: Timing Summary: Speed Grade: -12 Minimum period: 2.259ns (Maximum Frequency: MHz) Minimum input arrival time before clock: 2.760ns Maximum output required time after clock: 4.013ns Maximum combinational path delay: No path found Ritroviamo le voci presentate in precedenza. Attenzione: si noti come il tempo di arrivo minimo per un segnale di input prima del fronte di clock dev essere 2.76 ns, ovvero addirittura superiore al periodo minimo del clock. Com è possibile? Che senso ha? Il problema nasce dal fatto che il periodo minimo di clock è calcolato considerando nulli i tempi di routing dal pad di ingresso/uscita al porto di I/O del nostro componente. Se, infatti, approfondiamo l analisi, per esempio, della seconda voce, si ha: =============================================================== Timing constraint: Default OFFSET IN BEFORE for Clock clk Total number of paths / destination ports: 16 / Offset: 2.760ns (Levels of Logic = 4) Source: m<2> (PAD) Destination: r_reg_2 (FF) Destination Clock: clk rising Data Path: m<2> to r_reg_2 Gate Net Cell:in->out fanout Delay Delay IBUF:I->O LUT4:I0->O LUT4_L:I1->LO LUT4:I2->O

85 FDC:D Total 2.760ns (1.492ns logic, 1.268ns route) (54.1% logic, 45.9% route) In poche parole, se eliminiamo i ritardi dovuti al routing e consideriamo i soli delay dovuti alla logica, abbia ns, che, pertanto, èinferioreal limite del clock. Per diminuire i ritardi dovuti alla logica (che, in caso di componente unico sulla FPGA, possono essere molto fastidiosi), di solito si usano i file di constraint per il sintetizzatore, che forzano l implementazione in particolari zone della FPGA (magari, in prossimità deipaddii/o). Cosa succede se aumentiamo il numero di bit? Magari passando da 4 bit a16bit? Device utilization summary: Selected Device : 4vsx25ff Number of Slices: 21 out of % Number of Slice Flip Flops: 16 out of % Number of 4 input LUTs: 41 out of % Number of IOBs: 34 out of % Number of GCLKs: 1 out of 32 3% Timing Summary: Speed Grade: -12 Minimum period: 4.340ns (Maximum Frequency: MHz) Minimum input arrival time before clock: 3.410ns Maximum output required time after clock: 3.935ns Maximum combinational path delay: No path found Vediamo come, complicandosi la logica, a fronte di un aumento piuttosto contenuto dell occupazione hardware, diminuisca di un fattore 2 la frequenza massima di funzionamento del nostro device. 84

86 6.6 Utilizzo di variabili in circuiti sequenziali Fino ad ora abbiamo sempre utilizzato i segnali, anche come elemento di memoria. In generale, questa è la tecnica più sicura e piùusataper l implementazione di circuiti sequenziali. Tuttavia, non è errato, specie in strutture semplici, utilizzare anche le variabili all interno dei processi. Data la natura molto locale delle variabili, è conveniente utilizzarle soprattutto come variabili di appoggio. Vediamo la differenza fra segnali e variabili attraverso un semplice esempio. Ecco il codice: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity varsign is Port ( a : in STD_LOGIC; b : in STD_LOGIC; clk : in STD_LOGIC; q1 : out STD_LOGIC; q2 : out STD_LOGIC; q3 : out STD_LOGIC); end varsign; architecture Behavioral of varsign is signal tmp_sig1 : std_logic; begin -- prima soluzione process(clk) begin if rising_edge(clk) then tmp_sig1 <= a and b; q1 <= tmp_sig1; end if; end process; -- seconda soluzione process(clk) variable tmp_sig2 : std_logic; begin if rising_edge(clk) then 85

87 tmp_sig2 := a and b; q2 <= tmp_sig2; end if; end process; -- terza soluzione process(clk) variable tmp_sig3 : std_logic; begin if rising_edge(clk) then q3 <= tmp_sig3; tmp_sig3 := a and b; end if; end process; end Behavioral; Questo modulo presenta tre uscite, corrispondenti alla stessa elaborazione sul segnale (a b) ma eseguita in 3 modi diversi. Nel primo caso, utilizziamo il classico segnale per l aggiornamento; nel secondo, utilizziamo una variabile di appoggio (in modo da non creare ritardi nella propagazione del risultato); nel terzo, la variabile viene aggiornata dopo l assegnazione al segnale. Di fatto, il primo e il terzo metodo sono equivalenti (come mostrato anche dalla simulaizone temporale comportamentale di Fig. 6.10), e, di fatto, la variabile in questo caso è utilizzata in maniera non utile. Nel secondo caso, la variabile viene istantaneamente aggiornata, quindi otterremo in output su q2 il risultato di a b con un ciclo di clock di anticipo (Fig. 6.10). Questo è, ovviamente, vantaggioso, ma altrettanto pericoloso: in circuiti molto complessi, potrebbe non essere possibile tale aggiornamento istantaneo, e avremmo risultati non voluti. Figura 6.10: Simulazione temporale comportamentale per evidenziare le differenze fra segnali e variabili 86

88 6.6.1 Progettazione del contatore modulo m utilizzando variabili Modifichiamo l architettura dell esempio del paragrafo 6.4 in modo da utilizzare variabili. architecture Behavioral of contm is signal r_reg : unsigned(3 downto 0); begin process(clk,reset) variable q_tmp : unsigned(3 downto 0); begin if reset= 1 then r_reg <= (others => 0 ); elsif rising_edge(clk) then q_tmp := r_reg + 1; if(q_tmp = unsigned(m)) then r_reg <= (others => 0 ); else r_reg <= q_tmp; end if; end if; end process; q <= std_logic_vector(r_reg); end Behavioral; Il comportamento è identico a quanto visto in precedenza. Ma l utilizzo di variabili ha giovato anche ai risultati della sintesi? Vediamo le caratteristiche di occupazione e temporali: Device utilization summary: Selected Device : 4vsx25ff Number of Slices: 6 out of % Number of Slice Flip Flops: 4 out of % Number of 4 input LUTs: 11 out of % 87

89 Number of IOBs: 10 out of 320 3% Number of GCLKs: 1 out of 32 3% Timing Summary: Speed Grade: -12 Minimum period: 2.445ns (Maximum Frequency: MHz) Minimum input arrival time before clock: 3.048ns Maximum output required time after clock: 3.971ns Maximum combinational path delay: No path found Utilizziamo un paio di LUT in meno, ma paghiamo questa leggera semplificazione con un maggiore ritardo nella logica di connessione interna, e, quindi, con una frequenza di picco leggermente inferiore alla precedente. Tale effetto è amplificato se passiamo a 16 bit: Timing Summary: Speed Grade: -12 Minimum period: 6.019ns (Maximum Frequency: MHz) Minimum input arrival time before clock: 5.102ns Maximum output required time after clock: 3.933ns Maximum combinational path delay: No path found 88

90 Capitolo 7 Macchine a stati finiti 7.1 Introduzione Una macchina a stati finiti (finite state machine, FSM ), come dice il nome stesso, è una macchina caratterizzata da un certo numero di possibili stati interni. A contrario di quanto visto nel precedente capitolo per i circuiti sequenziali più elementari(i Regular Sequential Circuits analizzati in alcuni esempi nel precedente capitolo), la logica di controllo per lo stato interno nel caso di FSM è più complesso, tanto da essere considerato randomico. Una FSM è costituita da 5 entità: stati; segnali di input; segnali di output; funzione per il calcolo dello stato successivo (next state function); funzione per il calcolo dell output (output function). Lo stato caratterizza per intero una FSM. Il funzionamento è definito dalle transizioni da uno stato al successivo: se tali passaggi sono gestiti da unsegnalediclock,siparladifsmsincrone, edèilcasodigranlungapiù comune. La output function può essere funzione semplicemente dello stato, oppure anche degli ingressi al sistema: nel primo caso si parla di macchina di Moore, nel secondo di macchina di Mealy. Se la FSM svolge calcoli non banali sugli input, più precisamente si parla di FSM con datapath o FSMD. Per semplicità, nel seguito confonderemo FSM e FSMD. Di solito, una FSM complessa è una FSMD con uscite di tipo Moore e Mealy. 89

91 7.2 Rappresentazione di una FSM Il progetto di una macchina a stati finiti si basa su una descrizione grafica astratta, che utilizzi una rappresentazione simbolica degli stati e indichi sotto quali condizioni vengono posti in uscita determinati output. Le rappresentazioni più comuni sono gli state diagram o pallogrammi e gli ASM Pallogrammi Un pallogramma è caratterizzato da stati, indicati con dei bubbles o nodi, e degli archi di transizione. All interno di un nodo, vi può essere un uscita caratterizzante quel determinato stato: in tal caso si parla di uscite di Moore. Se un arco non contiene una condizione, la transizione allo stato successivo avviene senza incertezze al colpo di clock successivo; viceversa, se un arco contiene una condizione, la transizione avverrà solo quando essa è verificata. Se, su un arco, ad una condizione è associata anche un uscita, si parla di uscite di Mealy. Un esempio di nodo e archi è riportato in Fig Figura 7.1: Notazione per un nodo di un pallogramma Analizziamo un breve esempio. Si consideri la FSM di Fig. 7.2, in cui viene presentato un ipotetico (e semplificato) schema per un controller di memoria. Al reset, il sistema si pone in idle, e vi rimane fino a quando il segnale mem vale 0 (condizione mem ). Quando mem vale 1, dobbiamo controllare anche il valore di rw: se vale 0, abbiamo un uscita di Mealy we me pari a 1, e passiamo nello stato write; se vale 1, abbiamo la transizione allo stato read1. Se ci troviamo in write, avremo un uscita di Moore we pari a 1, e al colpo di clock successivo torneremo in idle. Se ci troviamo in read1, 90

92 avremo un output di Moore oe pari a 1, quindi dovremo controllare burst, e così via in maniera analoga ASM Spesso, sono più utilizzati dei diagrammi aventi molto in comune cooi pallogrammi, ma talvolta di più semplice interpretazione: gli Algorithmic State Machine, o,più semplicemente, ASM. Gli ASM sono anche più semplici da tradurre in VHDL, ed esistono molti generatori di codice automatici. Inoltre, con gli ASM è facile descrivere anche le FSMD, più complesse da descrivere con pallogrammi. Vediamo quali sono le strutture elementari. Stato Uno stato è rappresentato da un rettangolo simile a quello mostrato in Fig Analizziamo brevemente i simboli che lo caratterizzano. La stringa di caratteri sulla destra (in questo caso, st) rappresenta il nome associato allo stato dall utente: è un nome simbolico e non influisce sul funzionamento generale del sistema. La stringa di bit in alto a destra (in questo caso, 01) rappresenta il codice identificativo dello stato, e sarà poi il codice che la next state logic dovrà opportunamente generare a runtime. E opportuno, quindi, generarla con una certa logica: per facilitare la sintesi hw, può essere una buona idea cercare (per quanto possibile) di mantenere piccola la distanza di Hamming 1 fra i codici degli stati. In alto a sinistra, il rombo nero semplicemente indica che lo stato mostrato è lo stato attivato al reset. Condizione Una condizione è rappresentata da un rombo, all interno del quale è presenteilsegnaledacontrollare. Asecondachetalesegnalevalga0o1, lo stato successivo può variare o si può avere una diversa uscita di Mealy (analizzeremo il tutto a breve). Un esempio è mostrato in Fig. 7.4, in cui controlliamo il valore di IN. Per implementare condizioni più complesse, semplicemente di rappresentano una cascata di controlli. Macchine di Moore Per rappresentare le uscite in una macchina avente solo output di Moore, semplicemente scriviamo all interno degli stati il valore dell uscita che vogliamo 1 Si ricorda che per distanza di Hamming si intende il numero di bit per i quali differiscono due stringhe binarie. Ad esempio, 001 e 010 hanno distanza 2, 001 e 011 hanno distanza 1 (differiscono per un solo bit, il secondo per la precisione. 91

93 Figura 7.2: Esempio di pallogramma per un semplificato controller per una memoria 92

94 Figura 7.3: Simbolo ASM di uno stato Figura 7.4: Simbolo ASM per una condizione 93

95 forzare a 1. Facciamo un semplice esempio. Supponiamo di voler generare la FSM per un contatore modulo 4 capace di contare in avanti e indietro. In particolare, la direzione di conteggio viene controllata attraverso un segnale DIR, ilqualevariaperò solo nello stato iniziale (uscita a 0). In tutto, il nostro componente avrà: un ingresso per il clock; un ingresso per il reset; un ingresso per il segnale DIR; un output a 2 bit per il risultato del contatore. Inoltre, la nostra macchina dovrà essere caratterizzata da un certo numero di registri interni, per memorizzare le variabili caratterizzanti lo stato. Un possibile ASM è quello presentato in Fig. 7.5, in cui C0 e C1 rappresentano rispettivamente LSB e MSB per l uscita del contatore. Lo schema complessivo del componente generato è mostrato, invece, in Fig Semplicemente, si nota come, ad ogni stato, si hanno le uscite che dipendono solo dallo stato in cui ci si trova. In questo caso, abbiamo 7 stati, quindi il numero di registri minimo è dato dalla formula: num reg = log 2 num stati = log 2 7) =3 (7.1) Macchine di Mealy Per le macchine di Mealy si utilizzano le uscite condizionate, mostrate in Fig Un uscita condizionata va posta dopo lo stato per il quale dev essere generata l eventuale output di Mealy. La cosa migliore è utilizzare un esempio: modifichiamo lo schema del paragrafo precedente. Sfruttando l ipotesi che il segnale di controllo DIR vari solo all istante iniziale, si può proporre lo schema di Fig Analizziamo questo ASM. Ad ogni passo, analizziamo il segnale DIR, e, asecondacheessovalga0o1,contiamoinavantioindietroponendoin uscita i valori contenuto nelle uscite condizionate. Ad esempio, nello stato d avremo come output 11 se stiamo contando in avanti, altrimenti 01 se contiamo all indietro. Lo stato c non necessita di uscite condizionate, in quanto sia che si conti in avanti sia che si conti al contrario l uscita èsempre 10. In questo caso usiamo un output di Moore. Notiamo subito un grande vantaggio: utilizziamo 4 stati invece di 7. In termini di registri, possiamo usarne uno in meno. Ciò significa una logica di 94

96 Figura 7.5: ASM di un contatore avanti/indietro modulo 4 95

97 Figura 7.6: Schematico per il contatore avanti/indietro modulo 4 96

98 Figura 7.7: Blocco ASM per l uscita condizionata controllo più semplice. Tutto ciò si paga con uno schema più complesso da analizzare e meno immediato da tradurre in codice, nonchè con una logica per gli output più complessa. Nell ambito di progetti su grande scala, questo fatto può rappresentare un problema. 7.3 Alcune considerazioni sulle FSM Come già in precedenza, nel resto della trattazione, a meno di indicazioni contrarie, ci riferiremo sempre a FSM sincrone, gestite da un clock globale Considerazioni temporali Ricordiamoci che una macchina a stati finiti non è nient altro che un caso particolare di cicuito sequenziale. Pertanto, essa sarà sincronizzata attraverso un clock, mentre la sua logica sarà costituita da flip flop per la gestione dei segnali. Possiamo, quindi, recuperare la trattazione del paragrafo La frequenza di funzionamento massima consentita si otterrà secondo la formula (6.2) Macchine di Moore e macchine di Mealy: pro e contro Vedremo meglio le differenze implementative fra una macchina puramente di Moore e una puramente di Mealy (o mista Moore/Mealy) nel prossimo paragrafo. A livello introduttivo si può dire che: una macchina di Moore in generale è caratterizzata da una next state 97

99 Figura 7.8: ASM di un contatore avanti/indietro modulo 4 sfruttante output di Mealy 98

100 logic abbastanza complessa e da una logica di uscita elementare (ad un particolare stato corrisponde una particolare uscita); una macchina di Mealy (o mista) è caratterizzata da una next state logic più snella, ma da una logica di output più difficile (di fatto, i controlli che nella macchina di Moore vengono eseguiti nella logica di stato qui vengono implementati nella output logic). Quale delle due soluzioni conviene usare? In generale, una soluzione mista èlapiùvantaggiosa. Anche perchè spesso si può trovare qualche semplificazione per ottenere una logica meno pesante. Tuttavia, ci sono dei casi in cui una macchina di Moore può essere una buona soluzione (la logica di controllo è comunque meno critica della logica di output). Ancora una volta, non esiste una soluzione universalmente ottima, ma tante buone soluzioni a seconda dei problemi che ci troviamo ad analizzare. 7.4 Descrizione VHDL di una FSM Veniamo, ora, a come descrivere una FSM in VHDL. Come anticipato in fase di introduzione, esistono molti software in grado di generare il codice VHDL a partire dal pallogramma o dall ASM. E importante, comunque, sapere analizzare tali codici e, all occorrenza, modificarli o scriverli per intero. Il primo consiglio consiste nel creare un nuovo tipo di dato, in modo da nominare gli stati con lo stesso nome presente nell ASM, per esempio. Ovvero, nell architecture, introdurre: TYPE states is ( state_a, state_b, state_d, state_c ); A questo punto, è sufficiente dichiarare due segnali per la gestione interna degli stati (attuale e futuro): SIGNAL State, Next_State: states; Per quanto riguarda l entity, nessun problema: gli I/O di una macchina a stati sono esattamente gli stessi di un qualsiasi altro componente, e vengono definiti in modo classico. Per quanto riguarda l architecture, dobbiamo prestare qualche attenzione in più. Sappiamo che una FSM è composta, in sostanza, da 3 parti: 99

101 1. una next state logic per il calcolo dello stato successivo; 2. un registro di stato; 3. una logica per le uscite. E ovvio pensare, quindi, di creare 3 processi all interno dell architettura. Riprendiamo l esempio della FSM di Fig In questo caso, la logica di controllo sullo stato è piuttosto semplice: utilizzando le uscite condizionate, di fatto la transizione avviene sempre tra uno stato e il successivo senza problemi. Per il registro di stato possiamo quindi scrivere: -- Registro di stato REG: process( Ck, Reset ) begin if (Reset = 0 ) then State <= state_a; elsif rising_edge(ck) then State <= Next_State; end if; end process; A questo punto, introduciamo la next state logic per il calcolo dello stato successivo. Utilizzando uscite di Mealy, essa risulta piuttosto snella: -- Next State Logic FSM: process( State ) begin CASE State IS when state_a => Next_State <= state_b; when state_b => Next_State <= state_c; when state_c => Next_State <= state_d; when state_d => Next_State <= state_a; END case; end process; Notiamo come questo processo non sia sensibile al clock: semplicemente, in generale la next state logic entra in gioco quando si modifica lo stato 100

102 attuale nell apposito registro. A quel punto, si ha una variazione di del segnale State e quindi si attiva il processo per il next state, che modifica il segnale Next_State. Concludiamo con l output logic: -- Output Logic OUTPUTS: process( State ) begin -- Valori di default: C0 <= 0 ; C1 <= 0 ; -- Output funzione dell input e dello stato: CASE State IS when state_b => if (DIR = 1 ) then C0 <= 1 ; else C0 <= 1 ; C1 <= 1 ; end if; when state_c => C1 <= 1 ; when state_d => if (DIR = 1 ) then C0 <= 1 ; C1 <= 1 ; else C0 <= 1 ; end if; when OTHERS => C0 <= 0 ; C1 <= 0 ; END case; end process; Anche in questo caso, per semplificare la logica, possiamo pensare che la sincronizzazione avvenga non attraverso una sensibilità ai fronti di clock, bensì alle variazioni del registro di stato. Settati i valori di default, analizziamo lo stato e gli input per generare le uscite corrette. Attenzione: se supponiamo che il segnale DIR possa variare in maniera asincrona e debbano seguire tale standard anche gli output, è necessario aggiungere l input alla sensitivity list. Se, invece, manteniamo valide le supposizioni fatte nella generazione dell ASM, DIR non dev essre incluso nella list. 101

103 Il modulo nel suo complesso sarà: LIBRARY ieee; USE ieee.std_logic_1164.all; ENTITY asm2es IS PORT( -- Clock & Reset: Ck: IN std_logic; Reset: IN std_logic; -- Inputs: DIR: IN std_logic; -- Outputs: C0: OUT std_logic; C1: OUT std_logic ); END asm2es; ARCHITECTURE behave OF asm2es IS TYPE states is ( state_a, state_b, state_d, state_c ); SIGNAL State, Next_State: states; BEGIN -- Next State Logic FSM: process( State ) begin CASE State IS when state_a => Next_State <= state_b; when state_b => Next_State <= state_c; when state_c => Next_State <= state_d; when state_d => Next_State <= state_a; END case; end process; -- Registro di stato 102

104 REG: process( Ck, Reset ) begin if (Reset = 0 ) then State <= state_a; elsif rising_edge(ck) then State <= Next_State; end if; end process; -- Output Logic OUTPUTS: process( State ) begin -- Valori di default: C0 <= 0 ; C1 <= 0 ; -- Output funzione dell input e dello stato: CASE State IS when state_b => if (DIR = 1 ) then C0 <= 1 ; else C0 <= 1 ; C1 <= 1 ; end if; when state_c => C1 <= 1 ; when state_d => if (DIR = 1 ) then C0 <= 1 ; C1 <= 1 ; else C0 <= 1 ; end if; when OTHERS => C0 <= 0 ; C1 <= 0 ; END case; end process; END behave; 103

105 Cosa cambia se utilizziamo lo schema di Moore? Riportiamo il codice dell architecture (l unica a modificarsi): ARCHITECTURE behave OF asm1es IS TYPE states is ( state_0, state_1i, state_1a, state_3a, state_3i, state_2i, state_2a ); SIGNAL State, Next_State: states; BEGIN -- Next State Combinational Logic FSM: process( State ) begin CASE State IS when state_0 => if (DIR = 1 ) then Next_State <= state_1a; else Next_State <= state_3i; end if; when state_3i => Next_State <= state_2i; when state_2i => Next_State <= state_1i; when state_1i => Next_State <= state_0; when state_1a => Next_State <= state_2a; when state_2a => Next_State <= state_3a; when state_3a => Next_State <= state_0; when OTHERS => Next_State <= state_0; END case; end process; 104

106 -- State Register REG: process( Ck, Reset ) begin if (Reset = 0 ) then State <= state_0; elsif rising_edge(ck) then State <= Next_State; end if; end process; -- Outputs Combinational Logic OUTPUTS: process( State ) begin -- Set output defaults: C0 <= 0 ; C1 <= 0 ; -- Set output as function of current state and input: CASE State IS when state_3i => C0 <= 1 ; C1 <= 1 ; when state_2i => C1 <= 1 ; when state_1i => C0 <= 1 ; when state_1a => C0 <= 1 ; when state_2a => C1 <= 1 ; when state_3a => C0 <= 1 ; C1 <= 1 ; when OTHERS => C0 <= 0 ; C1 <= 0 ; END case; end process; END behave; 105

107 Notiamo come la output logic sia molto più snella (a stato corrisponde uscita), mentre la next state abbia il controllo supplementare sul segnale DIR. Ciò che rende, comunque, questa struttura svantaggiosa (in termini implementativi) rispetto all utilizzo di uscite condizionate è il fatto che il numero di stati pressochè raddoppia, costringendo ad un numero elevato di controlli. 7.5 Codifica degli stati Per quanto possa sembrare un problema di poco conto, l assegnazione degli stati è una questione tutt altro che da sottovalutare: l utilizzo di una tecnica furba può far risparmiare non poche porte logiche. L assegnazione degli stati si ripercuote nel codice VHDL nella dichiarazione del tipo states: infatti, il sintetizzatore, ad ognuno nei termini nell elenco, associa un codice binario crescente. In pratica, se noi scriviamo: TYPE states is ( state_a, state_b, state_d, state_c ); abbiamo associato allo stato a il codice 0, a b il codice 1, a d il codice 2 e a c il codice 3. Pertanto, la precedente scrittura e la seguente: TYPE states is ( state_a, state_b, state_c, state_d ); sono differenti, in quanto, nella seconda, abbiamo invertito i codici per c e d. Supponiamo di considerare una FSM con n stati e r registri da un bit. Le tecniche principali per la cosiddetta codifica degli stati sono: Assegnazione sequenziale binaria: in pratica, a stati contigui associamo codici a distanza 1 decimale. In parole povere, la codifica avviene in ordine 0, 1, 2,..., n 1. Il numero di registri necessari è dato da r = log 2 n. Il problema di questa tecnica consiste nelle difficoltà, per n elevati, delle transizioni fra stati. Supponiamo n = 8: nel momento in cui dovremo trasformare il segnale di stato da 111 a 000, per esempio, dovremo cambiare ben 3 bit. Spesso, questa tecnica non viene utilizzata; 106

108 Codifica a distanza di Hamming minima: stati contigui, per quanto possibile, hanno distanza distanza di Hamming minima, e possibilmente unitaria. Con 4 stati, in binario ciò equivale ad una codifica 00, 01, 11, 10, analogamente a quanto fatto nell esempio di Fig Anche in questo caso, sono necessari r = log 2 n registri; Codifica One Hot: ad ogni stato è associata una stringa di n bit, tutti a 0 tranne 1. Ad esempio, con 4 stati, si ha 0001, 0010, 0100, Quindi, si hanno r = n registri. In questo caso, la next state logic è molto semplice, e consiste in un semplice shifter (si paga tutto ciò però con la proliferazione dei registri); Codifica Almost One Hot: è identica alla One Hot, ma uno stato può avere codifica pari a una stringa di tutti 0. Quindi, sono necessari r = n 1 registri. La next state logic diviene leggermente più complessa, e di fatto non si ha un grande guadagno in termini di registri. Pertanto, è raramente utilizzata. Abbiamo detto che nella dichiarazione del tipo per gli stati in VHDL ad ogni nome è associato, in ordine, un numero crescente. E se, per esempio, volessimo utilizzare una codifica One Hot? La cosa più semplice è utilizzare variabili dummy. Riprendiamo l ASM di Fig. 7.8, e supponiamo di usare una codifica One Hot. Una possibile scrittura per il tipo di stato in VHDL è: TYPE states is ( dummy_0000, state_a, state_b, dummy_0011, state_c, dummy_0101, dummy_0110, dummy_0111, state_d, dummy_1001, dummy_1010, dummy_1011, dummy_1100, dummy_1101, dummy_1110, dummy_1111 ); Le variabili dummy non verranno mai utilizzate nel codice, ma servono solo come riempitivo per il sintetizzatore. Per quanto sia inutile, di fatto, 107

109 nel codice precedente l inserimento delle variabili dalla 1001 in poi, è spesso consigliabile di inserire comunque tutte le possibilità, anche per facilitare il sintetizzatore: così, avrà vita facile a capire che si tratta di una codifica One Hot. 108

110 Capitolo 8 Look Up Tables (LUT) 8.1 Che cos è una LUT? Per Look Up Table (LUT) si intende una struttura dati, generalmente un array, usata per sostituire operazioni di calcolo a runtime con una più semplice operazione di consultazione (Look Up, in inglese). Il guadagno di velocità può essere significativo, poiché recuperare un valore dalla memoria è spesso più veloce che sottoporsi a calcoli con tempi di esecuzione dispendiosi. Un tipico esempio di utilizzo di LUT è l implementazione hardware di funzioni trigonometriche: dato che la valutazione di un seno o coseno può essere molto pesante computazionalmente, si salvano i valori (con la precisione desiderata) nelle LUT, e a runtime sarà sufficiente leggere tali valori. Supponiamo di voler implementare una generica y = f(x). Se decidiamo di utilizzare le LUT, avremo le seguenti corrispondenze: x sarà l indirizzo della locazione di memoria; y sarà il contenuto di tale locazione. Il numero di celle totali dipende dalla precisione con cui si vuole definire l ingresso e calcolare l uscita, sulla base anche del tipo di ROM (in generale) che abbiamo a disposizione. Facciamo un esempio. Supponiamo di voler calcolare una certa funzione con ingressi a 16 bit e uscite con precisione pari a 16 bit. Supponiamo, inoltre, di avere a disposizione ROM da 16 bit. Il calcolo è piuttosto semplice: necessiteremo di 2 16 = locazioni per la memorizzazione. Se abbiamo a disposizione tale quantità dispazio,il risultato della funzione sarà disponibile in un solo colpo di clock (il tempo necessario alla lettura della ROM), a prescindere da quanto complicata la funzione possa essere. 109

111 Supponiamo, ora, di aver a disposizione ROM da 8 bit: ciò significa che per salvare un dato dovremo usufruire di 2 locazioni. Senza contare che servirà una logica di gestione dell inidirizzamento per le ROM. In tutto ci serviranno = locazioni, più altro spazio per la logica di controllo. 8.2 Pro e contro Le LUT hanno l enorme vantaggio di essere indipendenti dalla complessità della funzione, e offrono sempre una soluzione velocissima anche per operazioni che necessitano di alta precisione. Il problema è che, spesso, non vi è abbastanza spazio su una FPGA per ospitare on board tali LUT. Si può far ricorso ad una memoria esterna, ma parte del vantaggio viene a perdersi a causa della logica di interfacciamento e del tempo perso per via degli accessi in lettura. Per complesso che sia, un CORDIC (il quale permette di calcolare funzioni trigonometriche) occupa molto meno spazio delle LUT. Senza contare che, se si riesce ad usare la pipeline, il throughput a regime è uguale per LUT e CORDIC. Per diminuire il numero di LUT necessarie si può ricorrere ad approssimazioni o interpolazioni sui valori salvati, ma si perde in precisione (senza contare che si complica la logica e il throughput rischia di diminuire). Allora, quando usare le LUT? Innanzitutto, se sappiamo che, per esempio, la nostra FPGA dovrà sempre e solo calcolare un coseno, potrebbe essere utile usare le LUT; oppure possono essere usate per salvare dei dati in memoria con alta precisione (per esempio, per certe operazioni è necessario salvare il valore del log 2, e si può utilizzare una LUT); più in generale, possono essere usate quando l utilizzo di un componente che esegua la corrispondente operazione diventa troppo dispendioso. 8.3 Codice VHDL per una LUT Una LUT è facilissima da implementare. Vediamo un esempio semplice per il calcolo di y = x 2, supponendo l ingresso a 4 bit e l output a 16 bit: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity LUT1 is 110

112 Port ( addr : in STD_LOGIC_VECTOR (3 downto 0); clk : in STD_LOGIC; ris : out STD_LOGIC_VECTOR (7 downto 0)); end LUT1; architecture Behavioral of LUT1 is begin process(clk) begin if rising_edge(clk) then case addr is when X"0" => ris <= X"00"; when X"1" => ris <= X"01"; when X"2" => ris <= X"04"; when X"3" => ris <= X"09"; when X"4" => ris <= X"0F"; when X"5" => ris <= X"19"; when X"6" => ris <= X"24"; when X"7" => ris <= X"31"; when X"8" => ris <= X"40"; when X"9" => ris <= X"51"; when X"A" => ris <= X"64"; when X"B" => ris <= X"79"; when X"C" => ris <= X"90"; when X"D" => ris <= X"A9"; when X"E" => ris <= X"C4"; when X"F" => ris <= X"E1"; when others => ris <= (others => X ); end case; end if; end process; end Behavioral; In pratica, scriviamo un unico ciclo select...case per l indirizzamento in memoria. Come notiamo dallo schema di Fig. 8.1, il sintetizzatore riconosce la struttura a Look Up Tables, aggiungendo un FF in uscita per sincronizzarci con il clock. 111

113 Figura 8.1: Schema RTL dell implementazione attraverso LUT 8.4 Codice per la generazione automatica del VHDL Ovviamente, mano a mano che i circuiti da implementare attraverso LUT diventano più complessi, appare improponibile la scrittura manuale di tutte le righe di codice necessarie. Pertanto, è utile avere come riferimento un codice piuttosto generale in C/C++/C#, in grado di adattarsi alle nostre esigenze sia per quanto riguarda la funzione da implementare, ma anche i bit di precisione di I/O. Quanto segue vuol essere un piccolo esempio guida: #include "stdafx.h" #define bin 4 #define bout 8 #using <mscorlib.dll> using namespace System; int funzione(int x) { return(x*x); } char* tobool(int x, int b) { char* ris=new char[b]; for(int j=0;j<b;j++) ris[j]= 0 ; if(x==0) return ris; else if(x==1) 112

114 } { ris[b-1]= 1 ; return ris; } int i=0; while(x!=1) { int r=x%2; x=(int)(math::floor((double)x/2.0)); ris[b-1-i]=(char)(r+48); i++; } ris[b-1-i]= 1 ; return ris; void main() { FILE* fp; fp=fopen("lut.vhd","w"); //intestazione fprintf(fp,"library IEEE;\n"); fprintf(fp,"use IEEE.STD_LOGIC_1164.ALL;\n"); fprintf(fp,"use IEEE.STD_LOGIC_ARITH.ALL;\n"); fprintf(fp,"use IEEE.STD_LOGIC_UNSIGNED.ALL;\n\n"); //entity fprintf(fp,"entity LUT1 is\n"); fprintf(fp,"\tport ( addr : in STD_LOGIC_VECTOR (%d downto 0);\n", (bin-1)); fprintf(fp,"\t\tclk : in STD_LOGIC;\n"); fprintf(fp,"\t\tris : out STD_LOGIC_VECTOR (%d downto 0));\n", (bout-1)); fprintf(fp,"end LUT1;\n\n"); //architecture fprintf(fp,"architecture Behavioral of LUT1 is\n"); fprintf(fp,"begin\n"); fprintf(fp,"\tprocess(clk)\n"); fprintf(fp,"\tbegin\n"); 113

115 fprintf(fp,"\t\tif rising_edge(clk) then\n"); fprintf(fp,"\t\t\tcase addr is\n"); } for(int i=0;i<math::pow(2.0,bin);i++) { char* x=tobool(i,bin); int ris=funzione(i); char* y=tobool(ris,bout); fprintf(fp,"\t\t\t\twhen \""); for(int j=0;j<bin;j++) fprintf(fp,"%c",x[j]); fprintf(fp,"\" => ris <= \""); for(int j=0;j<bout;j++) fprintf(fp,"%c",y[j]); fprintf(fp,"\";\n"); } fprintf(fp,"\t\t\t\twhen others => ris <= (others => X );\n"); fprintf(fp,"\t\t\tend case;\n"); fprintf(fp,"\t\tend if;\n"); fprintf(fp,"\tend process;\n"); fprintf(fp,"end Behavioral;"); fclose(fp); E un codice C++, realizzato in Microsoft Visual Studio.NET La generalità del codice è sottolineata dal fatto che, se si vogliono variare i bit per l I/O, è sufficiente modificare le definizioni iniziali, mentre, per la funzione da implementare, bisogna modificare l apposito metodo C++. Il resto è sostanzialmente interfacciamento con file. Il metodo tobool è stato realizzato ad hoc per trovare stringhe di caratteri da stampare su file. Il codice così realizzato può essere direttamente importato nell ambiente di sviluppo della FPGA. 114

116 Capitolo 9 Hierarchical Design 9.1 Introduzione La metodologia Hierarchical Design prevede la divisione di un sistema in sotto moduli in maniera ricorsiva, in modo tale da poter progettare i sotto moduli in maniera indipendente. Con il termine ricorsivamente si intende che, a loro volta, i sotto moduli possono essere divisi in unità ancora più elementari, fino ad arrivare a strutture molto semplici da progettare ed utilizzare. Figura 9.1: Esempio di progetto diviso ricorsivamente in unità elementari 115

117 Consideriamo l esempio proposto in Fig Il sistema complesso (un moltiplilcatore sequenziale, ovvero un dispositivo che calcola il risultato di una moltiplicazione svolgendo una serie sequenziale di somme, utile quando la FPGA utilizzata sia sprovvista di moltiplicatori) viene inizialmente diviso in control path, ovvero un sistema di controllo, e data path per il calcolo vero e proprio delle somme. Il primo dei due sotto moduli è, di fatto, una FSM e viene diviso sulla base dello schema proposto in Fig Il data path viene anch esso suddiviso in registri per la memorizzazione del dato, unità di rete e componenti di routing ed interfacciamento. Le unità funzionali sono, poi, dei semplici sommatori e sotrattori (che permettono di contare il numero di somme ancora necessarie) Hierarchical Design: pro e contro La divisione di un progetto molto complesso in tanti sotto moduli elementari porta, di fatto, moltissimi vantaggi: un sistema modulare èpiù semplice da progettare (specie se il progetto è in team) e molti ocmponenti possono poi essere riutilizzati in lavori successivi. Molto spesso, le aziende di progettazione hardware creano delle proprie librerie di componenti, progettati e migliorati nel corso degli anni. Ogni componente è detto IP core. Progettare un sotto modulo elementare è vantaggioso sia per chi progetta, il quale può concentrarsi sugli aspetti critici dell unità ottenendo un codice migliore, sia per il sintetizzatore: è facile mostrare come l ottimizzazione di un sintetizzatore sia più efficace su sistemi semplici rispetto a moduli complessi. Sembrerebbe non esistano difetti in un tale approccio. L unico grande problema di tale approccio è il fatto che l interfacciamento tra i vari moduli può divenire pesante: bisogna, quindi, stare attenti a non esagerare con la modularità. In aiuto, spesso, al posto del codice VHDL possono essere usati gli schematici: realizziamo i componenti elementari in VHDL, creiamo i simboli circuitali, e utilizziamo schematics per le interconnessioni. Un apposito compilatore provvederà all istanziazione e alla sintesi del corrispondente codice Costrutti VHDL per il Hierarchical Design In questo capitolo analizzeremo i principali costrutti VHDL per il Hierarchical Design. Come detto, in casi di interconnessione semplice, può essere furbo anche utilizzare gli schematici. I principali costrutti (che abbiamo presentato in maniera approssimativa nel Cap. 2) sono: 116

118 Component; Generic; Configuration; Library; Package; Subprogram. 9.2 Component Come detto, la metodologia Hierarchical Design si basa sulla creazione di unità elementari, da includere in dispositivi più complessi. Tale inclusione avviene attraverso l utilizzo del costrutto component. La sintassi generale è: component nome_component generic ( {dichiarazioni}... ); port ( {porti di I/O} ); end component; Per quanto riguarda la parte generic, approfondiremo il discorso nel prossimo paragrafo. Vediamo come passare da una generica entity ad un componente. Si consideri la seguente entity: ENTITY asm1es IS PORT( Ck: IN std_logic; Reset: IN std_logic; DIR: IN std_logic; C0: OUT std_logic; C1: OUT std_logic ); END asm1es; che è la struttura proposta per la FSM di Fig Un componente può essere dichiarato nel seguente modo: 117

119 COMPONENT asm1es PORT( Ck: IN std_logic; Reset: IN std_logic; DIR: IN std_logic; C0: OUT std_logic; C1: OUT std_logic ); END COMPONENT; Se all interno di un modulo VHDL complesso vogliamo utilizzare un componente, dobbiamo dichiararlo nella sezione dichiarativa di una architettura. Una volta dichiarato, il componente va istanziato, ovvero dobbiamo dire al sintetizzatore come pensiamo di connettere i porti del componente ai segnali o porti del nostro dispositivo. Un istanziazione generica ha la seguente sintassi: etichetta : nome_component generic map ( {associazioni} ) port map ( {associazioni} ); Le associazioni possono essere effettuate in modo esplicito o implicito. Nel primo caso, ad ogni segnale associamo manualmente un porto del nostro componente, e la sintassi è del tipo: porto => segnale Nel secondo caso, scriviamo semplicemente l elenco dei segnali da associare, e il collegamento è fatto sull ordine di dichiarazione nella entity e sulla base dell elenco fornito dal programmatore. Una dichiarazione implicita è sempre sconsigliabile, perchè rende anche poco chiaro il codice. Supponendo, quindi, di usare una associazione esplicita, vediamo l istanziazione per un componente del tipo FSM visto in precedenza: ARCHITECTURE behavior OF tfsm1_vhd IS -- Dichiarazione COMPONENT asm1es PORT( Ck : IN std_logic; 118

120 BEGIN Reset : IN std_logic; DIR : IN std_logic; C0 : OUT std_logic; C1 : OUT std_logic ); END COMPONENT; --Inputs SIGNAL Clk : std_logic := 0 ; SIGNAL Res : std_logic := 0 ; SIGNAL D : std_logic := 0 ; --Outputs SIGNAL Tot : std_logic_vector(1 downto 0); -- Instanziazione dut: asm1es PORT MAP( Ck => Clk, Reset => Res, DIR => D, C0 => Tot(0), C1 => Tot(1) );... END; 9.3 Generic Il costrutto generic è il metodo per creare e passare un informazione generica ad una entità e componente. Sostanzialmente, è un parametro che permette di creare un componente molto generale. Supponiamo, ad esempio, di voler scrivere il codice per un registro a W bit, ma senza precisare a priori il valore di W. La sua entità potrebbe essere: ENTITY reg IS GENERIC ( W: natural ); PORT ( Clk: IN std_logic; Reset: IN std_logic; 119

121 END reg; D: IN std_logic_vector(w-1 downto 0); Q: OUT std_logic_vector(w-1 downto 0) ); A questo punto, il modulo così creato non può più essere utilizzato in maniera indipendente, ma dovrà essere sempre istanziato. Per esempio, generiamo due istanze di registri, uno a 4 e il secondo a 16 bit: ARCHITECTURE behavior OF tfsm1_vhd IS -- Dichiarazione COMPONENT reg GENERIC ( W: natural ); PORT ( Clk: IN std_logic; Reset: IN std_logic; D: IN std_logic_vector(w-1 downto 0); Q: OUT std_logic_vector(w-1 downto 0) ); END COMPONENT; --Inputs SIGNAL Clk : std_logic := 0 ; SIGNAL Reset : std_logic := 0 ; SIGNAL D4 : std_logic_vector(3 downto 0) := X"0"; SIGNAL D16 : std_logic_vector(15 downto 0) := X"0000"; BEGIN --Outputs SIGNAL Q4 : std_logic_vector(3 downto 0); SIGNAL Q16 : std_logic_vector(15 downto 0); -- Instanziazione 1 dut4: reg GENERIC MAP ( W => 4 ) PORT MAP( Clk => Clk, Reset => Reset, D => D4, Q => Q4 ); -- Instanziazione 2 120

122 dut16: reg GENERIC MAP ( W => 16 ) PORT MAP( Clk => Clk, Reset => Reset, D => D16, Q => Q16 );... END; 9.4 Configuration Talvolta, è possibile associare più architetture ad una stessa entità. Prendiamo il caso della FSM progettata nel paragrafo 7.2.2: abbiamo proposto un architettura basata su una macchina con output di Moore e una mista Moore/Mealy, ma la entity era uguale. Si pensi ancora ad un contatore: potremmo realizzare un architettura per un contatore in avanti e una per un contatore all indietro. Nel momento in cui vogliamo dichiarare un componente, occorre però precisare quale configurazione si voglia usare. La sintassi è la seguente: CONFIGURATION nome_conf OF entità IS FOR nome_architettura FOR etichetta: nome_componente USE ENTITY libreria.entità(architettura); END FOR;... END FOR; END; Spesso, in maniera più semplice, può essere utilizzata una configurazione diretta in fase di dichiarazione e non di istanziazione. Vediamo un esempio. Supponiamo di aver realizzato un contatore, avente un architettura su per quando conta in avanti, e giu per contare all indietro. Se volessimo introdurre un componente contatore in avanti e uno all indietro, potremmo scrivere: ARCHITECTURE behavior OF tfsm1_vhd IS -- Dichiarazione COMPONENT contatore IS 121

123 PORT ( Clk: IN std_logic; Q: OUT std_logic_vector(7 downto 0) ); END COMPONENT; FOR av: contatore USE ENTITY work.contatore(su); FOR ind: contatore USE ENTITY work.contatore(giu);... A questo punto, si procede tranquillamente utilizzando due istanze av e ind di cui poi effettueremo l istanziazione classica. La libreria work è quella in cui vengono inseriti di default i moduli che generiamo all interno di un singolo progetto. 9.5 Library Una libreria è una raccolta di moduli VHDL, siano essi entità, architetture, configurazioni, package,... Spesso, in progetti di grandi dimensioni, è utile raccogliere il nostro lavoro in librerie separate, ad esempio, in base alle operazioni svolte dai moduli e così via. Per creare una libreria, una volta realizzati i moduli che vogliamo aggiungere, semplicemente si usa l ambiente di sviluppo e si impone la creazione di una nuova libreria, aggiungendo tutti i file che desideriamo. Per importare una libreria, si usa la parola chiave library. La struttura è gerarchica, ovvero una libreria può contenere altre librerie al proprio interno. In tal caso, si usa la seguente notazione per la parola chiave use: library libreria; use libreria.elemento.all; La struttura è simile al Java, e la keyword all svolge il ruolo del simbolo *. Ovviamente, si può decidere di importare un intera libreria, o solo alcune parti di essa: in tal caso vanno precisate con la precedente struttura gerarchica. Nel momento in cui si implementa un progetto, viene sempre e comunque realizzata una libreria work, importata di default. 9.6 Subprogram Spesso, se le operazioni da svolgere in differenti punti della nostra architettura su dati dello stesso tipo sono ripetitive ed identiche, può essere 122

124 comodo definire delle function e procedure: l unica differenza fra le due è che una funzione ritorna un valore di un certo tipo, mentre la procedura è void, ovvero senza valore di ritorno. Ci concentriamo sulle funzioni, perchè sono di gran lunga le più usate. Esse vanno incluse nella parte dichiarativa di una architettura (tra la dichiarazione della architettura e il BEGIN, per capirci), e hanno la seguente sintassi: FUNCTION funz(parametri : tipi) RETURN tipo IS {dichiarazioni} BEGIN {istruzioni sequenziali} RETURN risultato; END; FUNCTION funz(parametri : tipi) RETURN tipo; viene detta dichiarazione della funzione. Attenzione però: spesso le funzioni utilizzano variabili al proprio interno, e sappiamo che un uso sconsiderato di queste ultime può portare alla fallita sintesi. Senza contare che anche funzioni troppo complesse possono condurre ad un codice non implementabile in hw. Vediamo un breve esempio. In questo caso, vogliamo trovare una funzione che effettui la seguente operazione: y =(a b) + c. Possiamo scrivere: FUNCTION oper(a,b,c : std_logic) RETURN std_logic IS variable tmp : std_logic; BEGIN tmp := (a and b) or (not c); RETURN tmp; END; La variabile tmp è palesemente inutile, ma è stata introdotta con scopi esemplificativi. 9.7 Package Concludiamo questa carrellata con l ultimo costrutto, il package. Mano a mano che un sistema diviene sempre più complesso e racchiude in sè sempre più componenti, dichiarazioni, costanti, tipi,..., la parte dicharativa di una architettura può diventare effettivamente molto pesante, rendendo poco leggibile il codice. Il package ha proprio lo scopo di racchiudere in sè una 123

125 collezione di tutti quegli elementi che altrimenti andrebbro a ridurre la leggibilità. Un package creato entra a far parte di default della libreria work, o può essere manualmente incluso in qualsiasi altra libreria user defined. Un package è composto da 2 parti: una sezione didichiarazioni e un body. Nella prima vengono incluse tutte le dichiarazioni generali, nel body le implementazioni delle eventuali funzioni dichiarate nella sezione dichiarativa. Se non è presente alcun sottoprogramma, non è necessaria la scrittura di alcun body. La sintassi è la seguente: -- parte dichiarativa (obbligatoria) PACKAGE nome IS {dichiarazioni} END nome; -- body (solo con soubprograms) PACKAGE BODY nome IS {sottoprogrammi} END nome; 124

126 Capitolo 10 Parameterized VHDL Progetti sempre più complessi possono necessitare di strutture molto generali, personalizzabili a seconda delle esigenze di progetto. Esistono costrutti che permettono uno sfruttamento molto generale di strutture parametrizzabili: lo scopo del progettista diventa, in questi casi, realizzare un modulo quanto più riutilizzabile. Un primo esempio di VHDL caratterizzato da parametri è stato proposto nel paragrafo 9.3: la keyword generic permette di rendere una entità molto generale. In questo capitolo, viene proposta una panoramica generale su alcuni costrutti avanzati, i quali però sono, al solito, armi a doppio taglio: un uso non controllato può portare a codice illeggibile, non ottimizzato o non sintetizzabile. Il VHDL con parametri è da utilizzare SOLO quando effettivamente necessario e vantaggioso Attributi di un array Un attributo di un segnale (sia esso un singolo valore o un array) fornisce informazioni sulle caratteristiche del segnale stesso. La sintassi di un attributo è semplice: (nome_segnale) (attributo) I tipi di attributi sono molti, specie per gli array. Essi spaziano dagli eventi riguardanti il segnale alle caratteristiche del segnale stesso. Elenchiamo i più utilizzati: event: resituisce un boolean true se il segnale ha subito una modifica in uno dei suoi valori; 125

127 left e right: rappresentano estremo sinistro e destro del range di un array; length: restituisce il numero di elementi totali contenuti nell array; range: resituisce il range di indici valido per un certo segnale; reverse_range: restituisce il range di indici, ma in ordine invertito. Per esempio, consideriamo il seguente segnale: signal s1 : std_logic_vector(8 to 15); Alcuni attributi sono: s1 left=8, s1 right=15; s1 length=8; s1 range=8 to 15; s1 reverse_range=15 downto 8. Gli attributi possono essere utili per cicli for (per ottenere i range per gli indici), ma anche in fase di dichiarazione di segnali. Per esempio: signal s2 : std_logic_vector(s1 reverse_range); è equivalente a: signal s2 : std_logic_vector(15 downto 8); 10.2 Array con e senza range Spesso, si può pensare di dichiarare un nuovo tipo di dato, per esempio un array, ma di voler porre dei limiti alla libertà implementativa. Nel momento in cui vogliamo dichiarare un nuovo tipo di array, la dichiarazione ha il seguente formato: type nuovo_array is array(15 downto 0) of std_logic; 126

128 Di fatto, dichiariamo un nuovo tipo di vettore di bit, il cui indice sarà intero positivo (natural), ma che potrà avere al massimo un range per i propri indici che varia fra 0 e 15. Pertanto, si hanno dichiarazioni consentite e non permesse: -- ok signal s1 : nuovo_array(15 downto 8); signal s2 : nuovo_array(13 downto 0); -- ERRORE!! signal s3 : nuovo_array(31 downto 0); Allo stesso modo, possiamo dichiarare array senza range di valore per gli indici. In tal caso, la dichiarazione diventa: type nuovo_array_unbound is array(natural range <>) of std_logic; 10.3 Costrutto For...Generate I costrutti di generazione caratterizzati dalla keyword generate sono gli unici in tutto il VHDL ad essere considerati quali istruzioni concorrenti contenenti al proprio interni istruzioni anch esse concorrenti. In particolare, in questo paragrafo analizziamo il for...generate: questo costrutto è molto utile qualora si debbano implementare molte operazioni di egual tipo sui dati in maniera parallela, oppure nel caso in cui, all interno di un dispositivo gerarchicamente più importante, si debbano istanziare molti componenti di un certo tipo. La sintassi è la seguente: etichetta: FOR indice IN range GENERATE {istruzioni concorrenti} END GENERATE; Facciamo un breve esempio. Supponiamo di voler implementare un decoder con un ingresso a di dimensioni settabili attraverso parametro generico, e di voler generare tutti i possibili confronti con i possibili codici che taleinput può assumere. Il VHDL corrispondente risulta: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.numeric_std.ALL; 127

129 entity bin_decoder is Generic ( W : natural); Port ( a : in STD_LOGIC_VECTOR (W-1 downto 0); code : out STD_LOGIC_VECTOR (2**W-1 downto 0)); end bin_decoder; architecture Behavioral of bin_decoder is begin es: for i in 0 to (2**W-1) generate code(i) <= 1 when i=to_integer(unsigned(a)) else 0 ; end generate; end Behavioral; Oppure, un ciclo for...generate può essere usato, come detto, per l istanziazione di componenti: es: for i in (W-1) downto 0 generate dff_array: dff port map( clk => clk, d => q_reg(i+1), q => q_reg(i)); end generate; 10.4 Costrutto if...generate Analogamente a quanto visto per il for...generate, anche questo costrutto al suo interno contiene istruzioni concorrenti. L uso è assolutamente analogo aquantovistoperilfor...generate. Un esempio può essere il seguente: es: for i in (W-1) downto 0 generate left_gen: if i=1 generate tmp(i) <= a(i) xor a(0); end generate; else_gen: if i>1 generate tmp(i) <= a(i) xor a(i-1); end generate; end generate; 128

130 10.5 Array bidimensionali Un tipo nativo per descrivere una matrice in VHDL non esiste. Si possono, comunque, sfruttare le proprietà di dichiarazione di tipi per generare array multi dimensionali. Esistono, di fatto, due tecniche (in verità si può anche costruire un array di array, ma è una tecnica usata pochissimo), entrambe valide Array bi dimensionale effettivo Concentriamoci su array bidimensionali (sopra le due dimensioni diventa effettivamente un problema difficile da trattare). E possibile definire un tipo matrice nel seguente modo: TYPE matrice IS array(range_1, range_2) OF tipo_dato; Ovviamente, possiamo costruire matrici con limiti per il range o senza limiti. A questo punto l inizializzazione può avvenire nei 3 modi seguenti: -- associazione in base alla posizione t1 <= ("0000", "0110", "0001"); -- inizializzazione per righe -- si inizializzano tutte a 0110 t2 <= (others => "0110"); -- inizializzazione completa a 0 t3 <= (others=>(others=> 0 )); L accesso e la scrittura dati può avvenire attraverso la seguente notazione: -- input t1(1,0) <= 0 ; -- output e <= t(2,2); E una notazione molto usata per emulare banchi di RAM onboard su FPGA. 129

131 Array bi dimensionale emulato Un alternativa è rappresentata dalla possibilità di rappresentare una matrice attraverso un vettore. Se la matrice ha m righe e n colonne, la dimensione dell array sarà m n e, se vogliamo leggere o scrivere l elemento (i, j), è sufficiente usare un indice k sull intero array pari a: k = i n + j (10.1) A questo punto, è sufficiente introdurre un classico vettore, e una funzione per il cambio di indice: constant R : natural := 4; constant C : natural := 6; signal s : std_logic_vector(r*c-1 downto 0); function ix(i,j : natural) return natural is begin return(i*r+j); end ix;... a <= s(2,1); 130

132 Capitolo 11 Testbench 11.1 Test di dispositivi digitali Una volta progettato, un dispositivo digitale deve essere testato. I test e le verifiche per sistemi basati su FPGA esistono a diversi livelli del Development Flow (si veda il paragrafo 1.3 e la Fig. 1.6): si inizia con una simulazione comportamentale, per verificare di aver scritto un codice coerente; si passa alle varie simulazioni, con informazioni temporali sempre più realistiche; infine, vi è il test post implementazione, per verificare che la programmazione sia andata a buon fine. Nonostante le prime simulazioni siano astratte, nel senso che vengono effettuate utilizzando un simulatore software (che, per perfetto che sia, non è comunque una scheda in un ambiente reale...), mentre l ultima verifica sia molto concreta, le strutture sono molto simili. Ad un Device Under Test (DUT ) viene connesso in input un generatore di stimoli, mentre le uscite del DUT vengono analizzate da un oscilloscopio o un Logic State Analyzer (più in generale, potremmo parlare di un visualizzatore di output). Lo schema generale è quello di Fig In un caso di test fisico di un componente, il generatore di stimoli può essere, ad esempio, un Word Generator, ilqualeè un generatore di forme d onda digitali, ovvero di sequenze di ingresso per il DUT. Il Word Generator fornisce gli input al DUT, e sul visualizzatore (ad esempio, un Logic State Analyzer) potremo confrontare le uscite generate con quelle desiderate, e controllare il corretto funzionamento del dispositivo. Nel caso di test simulativo, l input è fornito da un file VHDL particolare, detto testbench module o testbench, ilquale,difatto,svolgeilruolodelgen- eratore di forme d onda. Il visualizzatore, invece, è un apposito simulatore: ipiùcomuni sono ModelSim e, in ambiente Xilinx, Xilinx ISE Simulator. In 131

133 particolare, i principali tipi di simulazione sono: simulazione comportamentale: non tiene conto dei ritardi dovuti al routing, alla logica,..., ed è utile per controllare la correttezza e la coerenza del codice scritto; simulazione post place and route: il sintetizzatore simula il piazzamento su FPGA del nostro dispositivo, e simula il comportamento, tenendo conto di tutti i possibili ritardi, glitch, Creazione di un testbench Come detto, un testbench è un modulo VHDL che ha, però, alcune peculiarità: non ha una sua entity, in quanto non dev essere un componente vero e proprio, ma solo un file che fornisce gli ingressi al simulatore; contiene sempre un istanza del DUT; ha sempre dei segnali interni in egual numero rispetto ai porti di I/O del DUT: in tal modo, vengono connessi gli ingressi e le uscite per l analisi simulativa; in generale, contiene un numeor di processi pari al numero di input: infatti, in ogni processo viene fatto variare secondo una cadenza prestabilita un certo segnale; in generale, non contiene processi con sensitivity list, ma processi con strutture wait...for; tale modulo non dev essere sintetizzato, pertanto si possono utilizzare anche costrutti normalmente non sintetizzabili (quali, per esempio, proprio il wait...for). In quasi tutti gli ambienti di sviluppo hardware, esistono strumenti per la generazione automatica di un testbench per un certo dispositivo. Vediamo, ad esempio, come potrebbe essere scritto un testbench per l esempio di FSM proposto nel paragrafo LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_unsigned.all; 132

134 USE ieee.numeric_std.all; ENTITY tfsm_vhd IS END tfsm_vhd; ARCHITECTURE behavior OF tfsm_vhd IS COMPONENT asm1es PORT( Ck : IN std_logic; Reset : IN std_logic; DIR : IN std_logic; C0 : OUT std_logic; C1 : OUT std_logic ); END COMPONENT; --Inputs SIGNAL Ck : std_logic := 0 ; SIGNAL Reset : std_logic := 0 ; SIGNAL DIR : std_logic := 0 ; --Outputs SIGNAL C0 : SIGNAL C1 : std_logic; std_logic; BEGIN -- Instantiate (DUT) dut: asm1es PORT MAP( Ck => Ck, Reset => Reset, DIR => DIR, C0 => C0, C1 => C1 ); res : PROCESS BEGIN reset <= 0 ; wait for 70 ns; 133

135 END; reset <= 1 ; wait; -- will wait forever END PROCESS; clk: process begin ck <= 0 ; wait for 50 ns; ck <= 1 ; wait for 50 ns; end process; dir_proc: process begin dir <= 0 ; wait for 450 ns; dir <= 1 ; wait; end process; Dopo aver istanziato il DUT, abbiamo i 3 processi per gestire i 3 ingressi: il reset (attivo basso) viene portato inizialmente a 0 per 70 ns, quindi viene forzato a 1 per il resto della simulazione; il segnale DIR viene forzato a 0 per i primi 450 ns, quindi viene portato a 1 per il resto della simulazione; il clock dato in ingresso ha un periodo di 100 ns con un duty cycle del 50%, quindi ad una frequenza di 10 MHz. A questo punto, lanciamo la simulazione comportamentale. Il risultato è mostrato in Fig Il circuito si comporta correttamente. Possiamo, quindi, passare alla simulazione post place and route. l output di tale simulazione è presentato in Fig. 11.2: si può notare solo una piccola fase di ritardo nella stabilizzazione dell output, ma per il resto nessun problema. Fin qui tutto bene, ma ci siamo limitati ad un clock di 10 MHz. Se analizziamo i file di sintesi, i valori temporali ci dicono che la frequenza massima di funzionamento è circa 750 MHz, quindi ben al di sopra di quella da noi settata. Proviamo ad incrementare la frequenza del clock a 100 MHz, 134

136 Figura 11.1: Diagramma temporale per una simulazione comportamentale Figura 11.2: Diagramma temporale per una simulazione post place and route modificando i periodi in 5 ns. il risultato è mostrato in Fig. 11.3: notiamo come in fase di conteggio all indietro nascano dei fastidiosi glitch dovuti ai ritardi della logica. Nessun problema, comunque, se leggiamo gli output sul fronte di salita del clock, come mostrato in Fig Questo, comunque, ci fa già capire come i dati del sintetizzatore siano spesso molto indicativi: e siamo ancora totalmente in fase simulativa! Figura 11.3: Diagramma post place and route a 100 MHz E se sforiamo la frequenza massima? Supponiamo di voler portare il circuito a 1 GHz. Che succede nella post place and route? Lo si può vedere in Fig. 11.5: una bella marea di glitch e ritardi, con output totalmente errati. Tutto ciò a causa dei ritardi di propagazione. Concludiamo questo capitolo con un utile consiglio pratico. Spesso, quando si vogliono far variare molti segnali con frequenze multiple o sotto multiple della frequenza del clock, o anche solo per maggiore chiarezza, è utile definire a livello di architecture una costante per il periodo (o semiperiodo) del clock stesso. In parole povere, per esempio: ARCHITECTURE behavior OF tfsm1_vhd IS 135

137 Figura 11.4: Ingrandimento di un glitch e un fronte di salita a 100 MHz Figura 11.5: Diagramma post place and route a 1 GHz 136

138 -- Component Declaration for the Unit Under Test (UUT) COMPONENT asm1es PORT( Ck : IN std_logic; Reset : IN std_logic; DIR : IN std_logic; C0 : OUT std_logic; C1 : OUT std_logic ); END COMPONENT; --Inputs SIGNAL Ck : std_logic := 0 ; SIGNAL Reset : std_logic := 0 ; SIGNAL DIR : std_logic := 0 ; --Outputs SIGNAL C0 : SIGNAL C1 : std_logic; std_logic; BEGIN Definizione del semiperiodo CONSTANT T : time := 50 ns; Aquestopunto,ilprocessoperilclock,adesempio,diviene: clk: process begin ck <= 0 ; wait for T; ck <= 1 ; wait for T; end process; 137

139 Capitolo 12 Esempi finali e guida a Xilinx ISE Web Pack 12.1 Introduzione a Xilinx ISE Web Pack 8.2i Vediamo come è possibile realizzare un nuovo progetto all interno dell ambiente di sviluppo Xilinx. Il software si presenta, in molti aspetti, simile a Visual Studio, e di fatto le funzionalità non sono poi così diversi (per quanto profondamente differenti siano gli obiettivi dei due strumenti). In Fig viene mostrato l ambiente di sviluppo, così comesipresentaalprimolancio. La prima cosa da creare è un progetto: all interno di esso andremo a creare i vari dispositivi, scrivendo i moduli VHDL corrispondenti. Un progetto è caratterizzato da: un nome. Attenzione: l ambiente Xilinx richiede che nell intero path di creazione della cartella per il nuovo progetto NON vi siano spazi; un top level source, ovvero un dispositivo che, nell ordine gerarchico, costituisca l oggetto da implementare fisicamente su scheda; il tipo di top level source: VHDL o schematico; il tipo di dispositivo di destinazione del nostro progetto: categoria della scheda (militare, general purpose,...); famiglia (Virtex 4, Spartan 2,...); tipo di device della particolare famiglia; package (ovvero, caratteristiche di produzione); 138

140 Figura 12.1: Interfaccia grafica iniziale dell ISE Web Pack speed grade, ovvero i nanosecondi di ritardo per ogni grado di logica; sintetizzatore; simulatore. Il tutto è riassunto in Fig Un nuovo progetto può essere generato cliccando su File New Project. A questo punto, se vogliamo creare i file per il nostro progetto, andiamo con il mouse nella finestra Sources, clicchiamo con il testo destro e selezioniamo New Source. A questo punto dobbiamo selezionare il tipo di file da generare. I principali sono: VHDL Module: è un modulo VHDL standard. Se lo selezioniamo, una volta scleto il nome per il modulo, dovremo indicare l I/O per il nostro dispositivo, e il codice VHDL verrà generato in automatico; VHDL Test Bench: è un modulo per il test comportamentale e post place and route del tipo analizzato nel Cap. 11. Al passo successivo, ci viene chiesto quale modulo VHDL o schematico fra quelli disponibili intendiamo testare; 139

141 Figura 12.2: Dialog box per le caratteristiche di un nuovo progetto Schematic: genera un nuovo schematico da implementare con blocchi già disponibili o realizzati. Molto utile per l interfacciamento gerarchico. Supponiamo di aver creato un modulo o uno schematico, e supponiamo che esso sia il nostro top level source (se non lo è, supponiamo di settarlo come tale cliccando sul file con il tasto destro e selezionando Set as Top module, in quanto richiesto da Xilinx). In basso a sinistra, nella finestra Processes, sono disponibili molte utili voci. Basta cliccare due volte su ognuna di esse per lanciare le funzioni messe a disposizione. Vediamo le principali: Synthesize Check Syntax: permette di verificare che il VHDL scritto sia corretto o le connessioni in uno schematico non creino pericolosi corto circuiti o conflitti; Design Utilities Create Schematic Symbol: crea il simbolo corrispondente al nostro modulo, il quale può essere poi utilizzato negli schematici; View Synthesis Report: permette di visualizzare i report di sintesi con tutte le caratteristiche temporali e di occupazione del modulo realizzato; 140

142 all interno del menù Implement Design Place and Route: cliccare due volte su Place and Route per generare il modello piazzato su scheda; View Design permette di vedere il nostro progetto piazzato su FPGA. Esiste inoltre il menù Edit Constraints per creare i vincoli temporali e/o spaziali, e il menù Generate Programming File per la creazione del bitstream. Se, invece, abbiamo creato un testbench, ricordiamoci, per visualizzarlo, si scegliere Behavioral (per la simulazione comportamentale) o Post Route (per la simulazione post par) dal menù a tendina Sources. Dal menù Xilinx ISE Simulator possiamo controllare la sintassi del testbench o lanciare la simulazione Esempi Nei prossimi esempi utilizzeremo come scheda di riferimento una Spartan 3 XC3S200, package FT256, speed grade -5. Vediamo alcune applicazioni, in ordine di complessità crescente Half Adder asincrono Partiamo con un progetto semplicissimo: un half adder a un bit asincrono. Il codice è semplicissimo: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity halfadder is Port ( A : in STD_LOGIC; B : in STD_LOGIC; C : out STD_LOGIC; O : out STD_LOGIC); end halfadder; architecture Behavioral of halfadder is 141

143 begin O <= A XOR B; C <= A AND B; end Behavioral; Generiamo un testbench per verificare che il tutto funzioni correttamente: LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_unsigned.all; USE ieee.numeric_std.all; ENTITY test_ha_vhd IS END test_ha_vhd; ARCHITECTURE behavior OF test_ha_vhd IS COMPONENT halfadder PORT( A : IN std_logic; B : IN std_logic; C : OUT std_logic; O : OUT std_logic ); END COMPONENT; --Inputs SIGNAL A : std_logic := 0 ; SIGNAL B : std_logic := 0 ; --Outputs SIGNAL C : SIGNAL O : std_logic; std_logic; BEGIN uut: halfadder PORT MAP( A => A, B => B, 142

144 ); C => C, O => O END; var : PROCESS BEGIN A <= 0 ; B <= 0 ; WAIT FOR 100 NS; A <= 0 ; B <= 1 ; WAIT FOR 100 NS; A <= 1 ; B <= 0 ; WAIT FOR 100 NS; A <= 1 ; B <= 1 ; WAIT FOR 100 NS; END PROCESS; Lanciando la simulazione, si può verificare il corretto funzionamento del sistema Full Adder Sulla base dell half adder precedente, utilizziamo gli schematici per ottenere il full adder con riporto in ingresso. Dopo aver creato il simbolo circuitale, si può disegnare lo schema di Fig Figura 12.3: Esempio di full adder a un bit realizzato con schematico 143

145 Decimal Counter a 2 cifre Vediamo una struttura più complessa. Studiamo un contatore di tipo BCD (Binary Coded Decimal) a 2 cifre. In poche parole, un contatore BCD interpreta ogni cifra decimale con 4 bit. Quindi, per esempio, 39 equivale a (ovvero la codifica binaria di 3 e 9). A questo punto, supponiamo di voler implementare un circuito sequenziale per un contatore decimale a 2 cifre. Dovremo gestire la next state logic, i registri di stato e la logica di output. Supponendo di non usare una FSM (cosa sensata, dato che dovremmo contemplare molti stati), i registri di stato e la logica di uscita è piuttosto elementare: semplicemente avremo una trasferimento di dati con trasformazione dal comodo formato unsigned al classico std_logic_vector. Nella next state logic, invece, dovremo gestire le trasformazioni dei numeri: pertanto, quando arriveremo a 9 nel registro delle unità, dovremo tener traccia dell aumento nel registro delle decine, e riazzereremo il registro delle unità, e così via. Supponiamo, quindi, di avere 2 registri interni, implementati con i 4 classici segnali, ovvero stato attuale e futuro di: registro delle unità; registro delle decine. La cosa migliore da fare è dare un occhiata al codice, nel quale si è supposta la presenza di un reset asincrono ed attivo basso: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity DecimalCounter is Port ( clk : in STD_LOGIC; reset : in STD_LOGIC; d1 : out STD_LOGIC_VECTOR (3 downto 0); d10 : out STD_LOGIC_VECTOR (3 downto 0)); end DecimalCounter; architecture Behavioral of DecimalCounter is signal d1_r, d10_r : unsigned(3 downto 0); signal d1_n, d10_n : unsigned(3 downto 0); begin -- register process(clk, reset) begin if reset= 0 then d1_r <= (others=> 0 ); 144

146 d10_r <= (others=> 0 ); elsif rising_edge(clk) then d1_r <= d1_n; d10_r <= d10_n; end if; end process; -- next-state logic d1_n <= X"0" when (d1_r=9) else d1_r+1; d10_n <= X"0" when (d1_r=9 and d10_r=9) else d10_r+1 when (d1_r=9) else d10_r; -- output d1 <= std_logic_vector(d1_r); d10 <= std_logic_vector(d10_r); end Behavioral; Il testbench per questo dispositivo è altrettanto semplice: LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_unsigned.all; USE ieee.numeric_std.all; ENTITY t_deccount_vhd IS END t_deccount_vhd; ARCHITECTURE behavior OF t_deccount_vhd IS COMPONENT DecimalCounter PORT( clk : IN std_logic; reset : IN std_logic; d1 : OUT std_logic_vector(3 downto 0); d10 : OUT std_logic_vector(3 downto 0) ); END COMPONENT; 145

147 BEGIN END; --Inputs SIGNAL clk : std_logic := 0 ; SIGNAL reset : std_logic := 0 ; --Outputs SIGNAL d1 : std_logic_vector(3 downto 0); SIGNAL d10 : std_logic_vector(3 downto 0); constant T : time := 5 ns; -- Instantiate the Unit Under Test (UUT) uut: DecimalCounter PORT MAP( clk => clk, reset => reset, d1 => d1, d10 => d10 ); res : PROCESS BEGIN reset <= 0 ; wait for (T+T/2); reset <= 1 ; wait; END PROCESS; ck : process begin clk <= 0 ; wait for T; clk <= 1 ; wait for T; end process; Al solito, si è deciso di definire una costante T per il semiperiodo del clock. La simulazione comportamentale offre il risultato corretto (si può verificare per esercizio), mentre per la simulazione post place and route il 146

148 clock impostato a 100 MHz si è rivelato a frequenza troppo elevata (a causa dei ritardi di propagazione, dato che il componente in sè può arrivare oltre i 200 MHz). Abbassando il clock a 40 MHz, si ottiene il risultato di Fig Figura 12.4: Simulazione post par a 40 MHz per il Decimal Counter a 2 cifre: si notano dei glitch, ma sul fronte di salita del clock il dato è pulito Calcolo con LUT di funzione trigonometrica Si implementi mediante LUT la seguente funzione: y =sin(x) (12.1) per x [0, 1). L ingresso sia a 8 bit in binario puro, e l output si richiede con precisione pari a 8 bit. Il grafico della funzione richiesta nell intervallo è mostrato in Fig La soluzione più semplice è quella, semplicemente, di rappresentare i bit di ingresso e uscita con una classica rappresentazione: val intero = val float 2 bit (12.2) Ad esempio, possiamo rappresentare a 8 bit come: v = = =

149 Figura 12.5: La funzione y =sin(x) nell intervallo x [0, 1) 148

150 Ovviamente, è necessaria l aggiunta di un codificatore apposito esterno alla FPGA che trasferisca i dati in maniera adeguata, ma questo non fa ovviamente parte delle specifiche di progetto. Si noti come non si possa avere overflow, essendo il valore 1 non compreso nell intervallo di validità di x. Per gli output, possiamo usare la stessa notazione, ovvero: val float = val intero /2 bit (12.3) Notiamo, però, come il valore massimo del seno sia: y max =sin ( 1 2 8) Si potrebbe pensare di normalizzare tutti i risultati per 0.84, per esempio, complicando il decoder di uscita (esterno alla FPGA e non di nostro interesse) ma sfruttando tutto il range dei bit che abbiamo a disposizione. Il codice C++ di generazione del VHDL è il seguente: #include "stdafx.h" #define bin 8 #define bout 8 #using <mscorlib.dll> using namespace System; double seno(double in) { return Math::Sin(in); } char* tobool(int x, int b) { char* ris=new char[b]; for(int j=0;j<b;j++) ris[j]= 0 ; if(x==0) return ris; else if(x==1) { ris[b-1]= 1 ; return ris; } 149

151 } int i=0; while(x!=1) { int r=x%2; x=(int)(math::floor((double)x/2.0)); ris[b-1-i]=(char)(r+48); i++; } ris[b-1-i]= 1 ; return ris; void main() { FILE* fp; fp=fopen("lut.vhd","w"); //intestazione fprintf(fp,"library IEEE;\n"); fprintf(fp,"use IEEE.STD_LOGIC_1164.ALL;\n"); fprintf(fp,"use IEEE.STD_LOGIC_ARITH.ALL;\n"); fprintf(fp,"use IEEE.STD_LOGIC_UNSIGNED.ALL;\n\n"); //entity fprintf(fp,"entity seno is\n"); fprintf(fp,"\tport ( addr : in STD_LOGIC_VECTOR (%d downto 0);\n", (bin-1)); fprintf(fp,"\t\tclk : in STD_LOGIC;\n"); fprintf(fp,"\t\tris : out STD_LOGIC_VECTOR (%d downto 0));\n", (bout-1)); fprintf(fp,"end seno;\n\n"); //architecture fprintf(fp,"architecture Behavioral of seno is\n"); fprintf(fp,"begin\n"); fprintf(fp,"\tprocess(clk)\n"); fprintf(fp,"\tbegin\n"); fprintf(fp,"\t\tif rising_edge(clk) then\n"); fprintf(fp,"\t\t\tcase addr is\n"); for(int i=0;i<math::pow(2.0,bin);i++) 150

152 } { char* x=tobool(i,bin); double tmp=(double)i/math::pow(2.0,bin); double tmp2=seno(tmp); int ris=math::floor(((tmp2/0.84)*256.0)); char* y=tobool(ris,bout); fprintf(fp,"\t\t\t\twhen \""); for(int j=0;j<bin;j++) fprintf(fp,"%c",x[j]); fprintf(fp,"\" => ris <= \""); for(int j=0;j<bout;j++) fprintf(fp,"%c",y[j]); fprintf(fp,"\";\n"); } fprintf(fp,"\t\t\t\twhen others => ris <= (others => X );\n"); fprintf(fp,"\t\t\tend case;\n"); fprintf(fp,"\t\tend if;\n"); fprintf(fp,"\tend process;\n"); fprintf(fp,"end Behavioral;"); fclose(fp); Analizziamo per prova un paio di uscite. Proviamo a calcolare il seno di 0.5, e analizziamone la precisione. In uscita otteniamo 146. A questo punto, traduciamolo secondo lo schema introdotto: Il seno di 0.5 vale: ris = sin(0.5) Calcoliamo i bit di precisione: log 2 ( sin(0.5) ris ) 11bit Proviamo a calcolare ora il seno di I bit di precisione risultano essere: log 2 ( sin(0.95) ) 8.4bit 151

153 Generatore di sequenze con FSM Gli esempi con macchina a stati finiti sono stati realizzati utilizzando il simulatore DEEDS 1, il quale contiene l ambiente d FsM per la progettazione e la sintesi di macchine a stati finiti. Non solo: tramite il comando Export VHDL, viene generato in automatico il codice per la FSM realizzata. Supponiamo di voler realizzare un generatore di sequenze: esso ha, in ingresso, un clock, un reset e due segnali, C0 e C1, tali da comandare l attivazione di un segnale di uscita sull unico output T ad un preciso colpo di clock. Lo schema è presentato in Fig Supponiamo che la sequenza dei due ingressi di controllo possa variare solo durante il ciclo T0 e resti stabile per i seguenti colpi di clock. A questo punto, possiamo generare una FSM puramente di Moore o con uscite di Mealy (Figg e 12.8 rispettivamente). Figura 12.6: Schema del generatore di sequenze Da un analisi temporale comportamentale si può verificare che entrambe le soluzioni funzionano correttamente (è stata riportata in Fig l analisi per la soluzione come macchina di Mealy a titolo di esempio) Esempio completo Vediamo, in conclusione, un esempio completo, a partire dal testo per finire con l implementazione e lo schema su scheda. 1 Scaricabile all indirizzo 152

154 Figura 12.7: FSM con uscite di Moore per il generatore di sequenze 153

155 Figura 12.8: FSM con uscite di Mealy 154

156 Figura 12.9: Analisi temporale comportamentale per la soluzione con FSM Mealy Testo Si realizzi un Pulse Width Modulation Circuit (PWM). In pratica, esso riceve in ingresso un clock ed un segnale (di dimensione variabile) che viene codificato nel duty cycle del segnale di uscita. Il segnale di uscita avrà una frequenza 2 s volte inferiore, dove s è il numero di bit dell ingresso, rispetto al clock originario, e un duty cycle pari a w 2 s,dovew è il segnale da modulare. Scrivere il codice VHDL per l implementazione, utilizzando tecniche basate su generic, e realizzare un testbench per il sistema. Visualizzare simulazione comportamentale e post par, nonchè il piazzamento su FPGA e le principali caratteristiche temporali e di occupazione, per un segnale di ingresso a 4 bit Soluzione Viene qui proposta una possibile soluzione. Data la relativa semplicità, dovrebbe essere intuitivo: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity pwm is Generic ( S : natural ); Port ( clk : in STD_LOGIC; reset : in STD_LOGIC; w : in STD_LOGIC_VECTOR (S-1 downto 0); pulse : out STD_LOGIC); end pwm; architecture Behavioral of pwm is signal r_reg : unsigned(s-1 downto 0); signal r_next : unsigned(s-1 downto 0); 155

157 signal buf_reg : std_logic; signal buf_next : std_logic; constant ref : std_logic_vector(s-1 downto 0) := (others => 0 ); begin -- registri e gestione buffer di output process(clk, reset) begin if(reset= 0 ) then r_reg <= (others => 0 ); buf_reg <= 0 ; elsif rising_edge(clk) then r_reg <= r_next; buf_reg <= buf_next; end if; end process; -- next-state r_next <= r_reg + 1; -- output logic buf_next <= 1 when ((r_reg < unsigned(w)) or (w=ref)) else 0 ; pulse <= buf_reg; end Behavioral; Si noti l introduzione della costante ref per rendere il controllo indipendente dai bit di ingresso. Realizziamo un blocco implementante il pwm a 4 bit. Semplicemente: library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity pwm4 is Port ( clk : in STD_LOGIC; reset : in STD_LOGIC; w : in STD_LOGIC_VECTOR (3 downto 0); pulse : out STD_LOGIC); 156

158 end pwm4; architecture Behavioral of pwm4 is COMPONENT pwm Generic ( S : natural ); Port ( clk : in STD_LOGIC; reset : in STD_LOGIC; w : in STD_LOGIC_VECTOR (S-1 downto 0); pulse : out STD_LOGIC); END COMPONENT; begin uut: pwm generic map ( S => 4 ) PORT MAP( clk => clk, reset => reset, w => w, pulse => pulse ); end Behavioral; Testiamolo con un testbench: LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.std_logic_unsigned.all; USE ieee.numeric_std.all; ENTITY testpwm4_vhd IS END testpwm4_vhd; ARCHITECTURE behavior OF testpwm4_vhd IS COMPONENT pwm4 PORT( clk : IN std_logic; reset : IN std_logic; w : IN std_logic_vector(3 downto 0); pulse : OUT std_logic 157

159 ); END COMPONENT; --Inputs SIGNAL clk : std_logic := 0 ; SIGNAL reset : std_logic := 0 ; SIGNAL w : std_logic_vector(3 downto 0) := (others=> 0 ); --Outputs SIGNAL pulse : std_logic; constant T : time := 5 ns; BEGIN -- Instantiate the Unit Under Test (UUT) uut: pwm4 PORT MAP( clk => clk, reset => reset, w => w, pulse => pulse ); -- Test Bench Statements clock : PROCESS BEGIN clk <= 1 ; wait for T; clk <= 0 ; wait for T; END PROCESS clock; res: process begin reset <= 0 ; wait for 5*T; reset <= 1 ; wait; end process; input: process begin 158

160 w <= X"4"; wait for 500 ns; w <= X"8"; wait; end process; END; Figura 12.10: Simulazione comportamentale del PWM a 4 bit I risultati di tale simulazione (clock a 100 MHz) sono mostrati in Fig , e si sono dimostrati corretti. Passiamo alla sintesi. Le principali caratteristiche temporali ci dicono che: Timing Summary: Minimum period: 3.413ns (Maximum Frequency: MHz) 159

161 Minimum input arrival time before clock: 5.195ns Maximum output required time after clock: 6.216ns Maximum combinational path delay: No path found Vediamo subito che, probabilmente, a causa dei ritardi di propagazione sarà difficile raggiungere frequenze superiori a circa 100 MHz. Vediamo l occupazione di porte logiche: Device utilization summary: Selected Device : 3s200ft256-5 Number of Slices: 5 out of % Number of Slice Flip Flops: 5 out of % Number of 4 input LUTs: 9 out of % Number of IOBs: 7 out of 173 4% Number of GCLKs: 1 out of 8 12% Non ci rimane che simulare il comportamento post place and route. Lanciamo la simulazione. Come mostrato in Fig , tutto ok. Attraverso prove iterative, a dispetto dei pessimistici dati (worst case) del sintetizzatore, si può mostrare come il clock massimo raggiungibile (dato dalla simulazione post par) è pari circa a 200 MHz. Mostriamo, infine, in Fig il piazzamento del nostro dispositivo sulla Spartan 3: tutto sommato, i dati pessimistici del routing fornito dal sintetizzatore non si sono fatti eccessivamente sentire grazie ad un piazzamento tutto sommato fortunato, in quanto non lontanissimo dai pin di I/O. 160

162 Figura 12.11: Simulazione post place and route del PWM a 4 bit 161

163 Figura 12.12: Piazzamento su Spartan 3 del PWM progettato 162

IL VHDL. Perché si usa un linguaggio di descrizione dell'hardware? Permette di formalizzare il progetto di sistemi digitali complessi

IL VHDL. Perché si usa un linguaggio di descrizione dell'hardware? Permette di formalizzare il progetto di sistemi digitali complessi IL VHDL Cosa è il VHDL? NON è un linguaggio di programmazione! E' uno standard IEEE per la descrizione dell'hardware VHDL: VHSIC Hardware Description Language VHSIC: Very High Speed Integrated Circuit

Dettagli

interfacciamento statico e dinamico analisi di interconnessioni, driver e receiver

interfacciamento statico e dinamico analisi di interconnessioni, driver e receiver Elettronica per telecomunicazioni 1 Contenuto dell unità D Interconnessioni interfacciamento statico e dinamico Integrità di segnale analisi di interconnessioni, driver e receiver Diafonia accoppiamenti

Dettagli

library ieee; use ieee.std_logic_1164.all; library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; library STD; use STD.textio.

library ieee; use ieee.std_logic_1164.all; library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; library STD; use STD.textio. VHDL Linguaggio di descrizione dell'hardware VHSIC Hardware Description Language VHSIC: Very High Speed Integrated Circuits VHDL Processi Attivati da qualche segnale Assegnazioni concorrenti A

Dettagli

Progettazione di circuiti integrati

Progettazione di circuiti integrati Architetture e Reti logiche Esercitazioni VHDL a.a. 2003/04 Progettazione di circuiti integrati Stefano Ferrari Università degli Studi di Milano Dipartimento di Tecnologie dell Informazione Stefano Ferrari

Dettagli

Introduzione al VHDL. Alcuni concetti introduttivi

Introduzione al VHDL. Alcuni concetti introduttivi Introduzione al VHDL Alcuni concetti introduttivi Riferimenti The VHDL Cookbook, Peter J. Ashenden, Reperibile nel sito: http://vlsilab.polito.it/documents.html The VHDL Made Easy, David Pellerin, Douglas

Dettagli

Progettazione di circuiti integrati

Progettazione di circuiti integrati Architetture e reti logiche Esercitazioni VHDL a.a. 2007/08 Progettazione di circuiti integrati Stefano Ferrari UNIVERSITÀ DEGLI STUDI DI MILANO DIPARTIMENTO DI TECNOLOGIE DELL INFORMAZIONE Stefano Ferrari

Dettagli

Lezione E15. Sistemi embedded e real-time

Lezione E15. Sistemi embedded e real-time Lezione E15 Logiche Sistemi embedded e real-time 24 gennaio 2013 Dipartimento di Ingegneria Civile e Ingegneria Informatica Università degli Studi di Roma Tor Vergata SERT 13 E15.1 Di cosa parliamo in

Dettagli

Un linguaggio per la descrizione dello hardware: il VHDL

Un linguaggio per la descrizione dello hardware: il VHDL Un linguaggio per la descrizione dello hardware: il VHDL Gli Hardware Description Languages Gli HDL consentono lo sviluppo di un modello del comportamento dei sistema digitali. Gli HDL permettono l eseguibilità

Dettagli

RELAZIONE DEL PROGETTO DI UN CONTATORE BINARIO UP/DOWN MODULO 4 PER IL CORSO DI APPARATI ELETTRONICI 1. INTRODUZIONE

RELAZIONE DEL PROGETTO DI UN CONTATORE BINARIO UP/DOWN MODULO 4 PER IL CORSO DI APPARATI ELETTRONICI 1. INTRODUZIONE RELAZIONE DEL PROGETTO DI UN CONTATORE BINARIO UP/DOWN MODULO 4 PER IL CORSO DI APPARATI ELETTRONICI 1. INTRODUZIONE In generale un contatore è un dispositivo che memorizza (e a volte visualizza) il numero

Dettagli

(b) LOGIC SYNTHESIS DESIGN FLOW

(b) LOGIC SYNTHESIS DESIGN FLOW 1 (b) LOGIC SYNTHESIS DESIGN FLOW ASIC HDL DIGITAL CIRCUITS DESIGN FLOW FPGA HDL DESIGN FLOW SINTESI DI TENTATIVO E SIMULAZIONE POST SINTESI DEL PROCESSORE MU0 OTTIMIZZAZIONE DELLA SINTESI DEL PROCESSORE

Dettagli

Un linguaggio per la descrizione dello hardware: il VHDL

Un linguaggio per la descrizione dello hardware: il VHDL Un linguaggio per la descrizione dello hardware: il VHDL Gli Hardware Description Languages Gli HDL consentono lo sviluppo di un modello del comportamento dei sistema digitali. Gli HDL permettono l eseguibilità

Dettagli

Laboratorio di Architettura degli Elaboratori A.A. 2016/17 Circuiti Logici

Laboratorio di Architettura degli Elaboratori A.A. 2016/17 Circuiti Logici Laboratorio di Architettura degli Elaboratori A.A. 2016/17 Circuiti Logici Per ogni lezione, sintetizzare i circuiti combinatori o sequenziali che soddisfino le specifiche date e quindi implementarli e

Dettagli

Laboratorio di Programmazione Laurea in Ingegneria Civile e Ambientale

Laboratorio di Programmazione Laurea in Ingegneria Civile e Ambientale Dipartimento di Ingegneria dell Informazione Università degli Studi di Parma Laboratorio di Programmazione Laurea in Ingegneria Civile e Ambientale Algebra di Boole Stefano Cagnoni Algebra di Boole L algebra

Dettagli

Indice. Prefazione. sommario.pdf 1 05/12/

Indice. Prefazione. sommario.pdf 1 05/12/ Prefazione xi 1 Introduzione 1 1.1 Evoluzione della progettazione dei sistemi digitali 1 1.2 Flusso di progettazione dei sistemi digitali 2 1.3 Obiettivi del libro 6 1.4 Struttura ragionata del libro 7

Dettagli

Calcolatori Elettronici M Modulo Introduzione all ambiente Xilinx ISE 12.3 e ISIM

Calcolatori Elettronici M Modulo Introduzione all ambiente Xilinx ISE 12.3 e ISIM Calcolatori Elettronici M Modulo 2 06 Introduzione all ambiente Xilinx ISE 12.3 e ISIM 1 Questi lucidi forniscono una sintetica introduzione all ambiente di sviluppo Xilinx ISE 12.3 utilizzando come riferimento

Dettagli

AXO - Architettura dei Calcolatori e Sistema Operativo. organizzazione strutturata dei calcolatori

AXO - Architettura dei Calcolatori e Sistema Operativo. organizzazione strutturata dei calcolatori AXO - Architettura dei Calcolatori e Sistema Operativo organizzazione strutturata dei calcolatori I livelli I calcolatori sono progettati come una serie di livelli ognuno dei quali si basa sui livelli

Dettagli

Sviluppo di programmi

Sviluppo di programmi Sviluppo di programmi Per la costruzione di un programma conviene: 1. condurre un analisi del problema da risolvere 2. elaborare un algoritmo della soluzione rappresentato in un linguaggio adatto alla

Dettagli

Simulazione. Simulazione verilog. Testbench. Testbench

Simulazione. Simulazione verilog. Testbench. Testbench Simulazione Simulazione verilog Lucidi del Corso di Elettronica Digitale Modulo 8 Università di Cagliari Dipartimento di Ingegneria Elettrica ed Elettronica Laboratorio di Elettronica (EOLAB) Il verilog

Dettagli

RAPPRESENTAZIONE GLI ALGORITMI NOTAZIONE PER LA RAPPRESENTAZIONE DI UN ALGORITMO

RAPPRESENTAZIONE GLI ALGORITMI NOTAZIONE PER LA RAPPRESENTAZIONE DI UN ALGORITMO RAPPRESENTAZIONE GLI ALGORITMI NOTAZIONE PER LA RAPPRESENTAZIONE DI UN ALGORITMO Rappresentazione degli algoritmi Problema Algoritmo Algoritmo descritto con una qualche notazione Programma Defne del procedimento

Dettagli

Corso di Matematica per la Chimica. Dott.ssa Maria Carmela De Bonis a.a

Corso di Matematica per la Chimica. Dott.ssa Maria Carmela De Bonis a.a Dott.ssa Maria Carmela De Bonis a.a. 2013-14 Programmi Un elaboratore riceve dei dati in ingresso, li elabora secondo una sequenza predefinita di operazioni e infine restituisce il risultato sotto forma

Dettagli

PROBLEMI ALGORITMI E PROGRAMMAZIONE

PROBLEMI ALGORITMI E PROGRAMMAZIONE PROBLEMI ALGORITMI E PROGRAMMAZIONE SCIENZE E TECNOLOGIE APPLICATE CLASSE SECONDA D PROGRAMMARE = SPECIFICARE UN PROCEDIMENTO CAPACE DI FAR SVOLGERE AD UNA MACCHINA UNA SERIE ORDINATA DI OPERAZIONI AL

Dettagli

Rappresentazione con i diagrammi di flusso (Flow - chart)

Rappresentazione con i diagrammi di flusso (Flow - chart) Rappresentazione con i diagrammi di flusso (Flow - chart) Questo tipo di rappresentazione grafica degli algoritmi, sviluppato negli anni 50, utilizza una serie di simboli grafici dal contenuto evocativo

Dettagli

Dalla prima lezione. LABORATORIO DI PROGRAMMAZIONE Corso di laurea in matematica 7 VARIABILI E COSTANTI 28/02/2016. Concetto di algoritmo

Dalla prima lezione. LABORATORIO DI PROGRAMMAZIONE Corso di laurea in matematica 7 VARIABILI E COSTANTI 28/02/2016. Concetto di algoritmo LABORATORIO DI PROGRAMMAZIONE Corso di laurea in matematica 7 VARIABILI E COSTANTI Marco Lapegna Dipartimento di Matematica e Applicazioni Universita degli Studi di Napoli Federico II wpage.unina.it/lapegna

Dettagli

Programma del corso. Elementi di Programmazione. Introduzione agli algoritmi. Rappresentazione delle Informazioni. Architettura del calcolatore

Programma del corso. Elementi di Programmazione. Introduzione agli algoritmi. Rappresentazione delle Informazioni. Architettura del calcolatore Programma del corso Introduzione agli algoritmi Rappresentazione delle Informazioni Architettura del calcolatore Reti di Calcolatori Elementi di Programmazione Algoritmi e programmi Algoritmo Sequenza

Dettagli

Corso di Informatica Generale (C. L. Economia e Commercio) Ing. Valerio Lacagnina Rappresentazione dei numeri relativi

Corso di Informatica Generale (C. L. Economia e Commercio) Ing. Valerio Lacagnina Rappresentazione dei numeri relativi Codice BCD Prima di passare alla rappresentazione dei numeri relativi in binario vediamo un tipo di codifica che ha una certa rilevanza in alcune applicazioni: il codice BCD (Binary Coded Decimal). È un

Dettagli

Flusso di Progetto Mixed Signal in ambiente CADENCE. Approccio Analog Centric. Corso di Progettazione Mixed Signal 19/12/2013 Prof.

Flusso di Progetto Mixed Signal in ambiente CADENCE. Approccio Analog Centric. Corso di Progettazione Mixed Signal 19/12/2013 Prof. Flusso di Progetto Mixed Signal in ambiente CADENCE Approccio Analog Centric Ambiente per Progetto Analogico Full-Custom Ambiente CAD: CADENCE Virtuoso Schematic Virtuoso Schematic Editor Simulation ADE:

Dettagli

METODOLOGIE PROGETTUALI CMOS

METODOLOGIE PROGETTUALI CMOS METODOLOGIE PROGETTUALI CMOS Un sistema elettronico/circuito integrato può essere descritto in tre diversi domini, comportamentale (behavior), strutturale e fisico. All interno di ciascun dominio la descrizione

Dettagli

Esercitazione : REALIZZAZIONE IMPIANTO SEMAFORICO

Esercitazione : REALIZZAZIONE IMPIANTO SEMAFORICO Esercitazione : REALIZZAZIONE IMPIANTO SEMAFORICO Strumenti utilizzati Strumento Marca e modello Caratteristiche Alimentatore Scheda ALTERA Fotocamera digitale Topward electronics TPS- 4000 ALTERA Max

Dettagli

Addizionatori: metodo Carry-Lookahead. Costruzione di circuiti combinatori. Standard IEEE754

Addizionatori: metodo Carry-Lookahead. Costruzione di circuiti combinatori. Standard IEEE754 Addizionatori: metodo Carry-Lookahead Costruzione di circuiti combinatori Standard IEEE754 Addizionatori Il circuito combinatorio che implementa l addizionatore a n bit si basa su 1-bit adder collegati

Dettagli

FUNZIONI BOOLEANE. Vero Falso

FUNZIONI BOOLEANE. Vero Falso FUNZIONI BOOLEANE Le funzioni booleane prendono il nome da Boole, un matematico che introdusse un formalismo che opera su variabili (dette variabili booleane o variabili logiche o asserzioni) che possono

Dettagli

Appunti di informatica. Lezione 3 anno accademico Mario Verdicchio

Appunti di informatica. Lezione 3 anno accademico Mario Verdicchio Appunti di informatica Lezione 3 anno accademico 2015-2016 Mario Verdicchio Numeri binari in memoria In un calcolatore, i numeri binari sono tipicamente memorizzati in sequenze di caselle (note anche come

Dettagli

Strutture dati e loro organizzazione. Gabriella Trucco

Strutture dati e loro organizzazione. Gabriella Trucco Strutture dati e loro organizzazione Gabriella Trucco Introduzione I linguaggi di programmazione di alto livello consentono di far riferimento a posizioni nella memoria principale tramite nomi descrittivi

Dettagli

Programmazione Orientata agli Oggetti. Emilio Di Giacomo e Walter Didimo

Programmazione Orientata agli Oggetti. Emilio Di Giacomo e Walter Didimo Programmazione Orientata agli Oggetti Emilio Di Giacomo e Walter Didimo Una metafora dal mondo reale la fabbrica di giocattoli progettisti Un semplice giocattolo Impara i suoni Dall idea al progetto Toy

Dettagli

Christian Pilato

Christian Pilato Politecnico di Milano Introduzione al VHDL Christian Pilato pilato@elet.polimi.it Sommario Introduzione Struttura di un modello Interfaccia Funzionalità Concetti base Livelli di astrazione Concorrenza

Dettagli

Array in Fortran 90. Ing. Luca De Santis. Anno accademico 2006/2007. DIS - Dipartimento di informatica e sistemistica

Array in Fortran 90. Ing. Luca De Santis. Anno accademico 2006/2007. DIS - Dipartimento di informatica e sistemistica Array in Fortran 90 Ing. Luca De Santis DIS - Dipartimento di informatica e sistemistica Anno accademico 2006/2007 Fortran 90: array DIS - Dipartimento di informatica e sistemistica 1 / 25 Cosa vedremo

Dettagli

Funzioni, Stack e Visibilità delle Variabili in C

Funzioni, Stack e Visibilità delle Variabili in C Funzioni, Stack e Visibilità delle Variabili in C Programmazione I e Laboratorio Corso di Laurea in Informatica A.A. 2016/2017 Calendario delle lezioni Lez. 1 Lez. 2 Lez. 3 Lez. 4 Lez. 5 Lez. 6 Lez. 7

Dettagli

Sistemi Web per il turismo - lezione 3 -

Sistemi Web per il turismo - lezione 3 - Sistemi Web per il turismo - lezione 3 - Software Si definisce software il complesso di comandi che fanno eseguire al computer delle operazioni. Il termine si contrappone ad hardware, che invece designa

Dettagli

Unità F1. Obiettivi. Il linguaggio C. Il linguaggio C++ Linguaggio C. Pseudolinguaggio. Primi programmi

Unità F1. Obiettivi. Il linguaggio C. Il linguaggio C++ Linguaggio C. Pseudolinguaggio. Primi programmi Obiettivi Unità F1 Primi programmi Conoscere il significato di dichiarazione e definizione di variabili Conoscere i tipi di dato numerici Essere in grado di realizzare semplici algoritmi in pseudolinguaggio

Dettagli

Addizione tra numeri binari

Addizione tra numeri binari Addizione tra numeri binari A=a n-1 a n-2...a i...a 0 B=b n-1 b n-2...b i...b 0 s i =a i b i c in c out =a i b i + a i c in + b i c in a i b i FA c out c in S=s n s n-1 s n-2...s i...s 0 s i a n 1 b n

Dettagli

Caratteristiche di un linguaggio ad alto livello

Caratteristiche di un linguaggio ad alto livello Caratteristiche di un linguaggio ad alto livello Un linguaggio ad alto livello deve offrire degli strumenti per: rappresentare le informazioni di interesse dell algoritmo definire le istruzioni che costituiscono

Dettagli

Introduzione alla programmazione Algoritmi e diagrammi di flusso. Sviluppo del software

Introduzione alla programmazione Algoritmi e diagrammi di flusso. Sviluppo del software Introduzione alla programmazione Algoritmi e diagrammi di flusso F. Corno, A. Lioy, M. Rebaudengo Sviluppo del software problema idea (soluzione) algoritmo (soluzione formale) programma (traduzione dell

Dettagli

Lez. 5 La Programmazione. Prof. Salvatore CUOMO

Lez. 5 La Programmazione. Prof. Salvatore CUOMO Lez. 5 La Programmazione Prof. Salvatore CUOMO 1 2 Programma di utilità: Bootstrap All accensione dell elaboratore (Bootsrap), parte l esecuzione del BIOS (Basic Input Output System), un programma residente

Dettagli

Il calcolatore. Architettura di un calcolatore (Hardware)

Il calcolatore. Architettura di un calcolatore (Hardware) Il calcolatore Prima parlare della programmazione, e' bene fare una brevissima introduzione su come sono strutturati i calcolatori elettronici. I calcolatori elettronici sono stati progettati e costruiti

Dettagli

CONCETTI E ARCHITETTURA DI UN SISTEMA DI BASI DI DATI

CONCETTI E ARCHITETTURA DI UN SISTEMA DI BASI DI DATI CONCETTI E ARCHITETTURA DI UN SISTEMA DI BASI DI DATI Introduzione alle basi di dati (2) 2 Modelli dei dati, schemi e istanze (1) Nell approccio con basi di dati è fondamentale avere un certo livello di

Dettagli

Indice. Prefazione. 3 Oggetti e Java 53

Indice. Prefazione. 3 Oggetti e Java 53 Prefazione xv 1 Architettura dei calcolatori 1 1.1 Calcolatori e applicazioni 1 1.1.1 Alcuni esempi di applicazioni 3 1.1.2 Applicazioni e interfacce 4 1.2 Architettura dei calcolatori 7 1.2.1 Hardware

Dettagli

Linguaggio C: le funzioni. Introduzione e sintassi

Linguaggio C: le funzioni. Introduzione e sintassi Dipartimento di Elettronica ed Informazione Politecnico di Milano Informatica e CAD (c.i.) - ICA Prof. Pierluigi Plebani A.A. 2008/2009 Linguaggio C: le funzioni. Introduzione e sintassi La presente dispensa

Dettagli

Macchina Astratta: struttura e realizzazione.

Macchina Astratta: struttura e realizzazione. Macchina Astratta: struttura e realizzazione. Sommario Macchina Astratta e l interprete di Macchina Hight e Low Level Languages Implementazione di un Linguaggio Macchina Intermedia Gerarchia di Macchine

Dettagli

Modulo 2: Strutture fondamentali della programmazione Java

Modulo 2: Strutture fondamentali della programmazione Java Modulo 2: Strutture fondamentali della programmazione Java Argomenti Trattati: Un semplice programma Java: Presentazione di un primo Esempio; Introduzione alla struttura; Compilazione ed esecuzione. Argomenti

Dettagli

Architettura degli Elaboratori

Architettura degli Elaboratori circuiti combinatori: ALU slide a cura di Salvatore Orlando, Marta Simeoni, Andrea Torsello 1 ALU ALU (Arithmetic Logic Unit) circuito combinatorio all interno del processore per l esecuzione di istruzioni

Dettagli

Esercitazioni di Reti Logiche

Esercitazioni di Reti Logiche Esercitazioni di Reti Logiche Sintesi di Reti Sequenziali Zeynep KIZILTAN Dipartimento di Scienze dell Informazione Universita degli Studi di Bologna Anno Academico 2007/2008 Sintesi dei circuiti sequenziali

Dettagli

Tipi di segnali (logici) predefiniti. Rappresentazione dei segnali in VHDL. Tipo bit (definito nel package standard)

Tipi di segnali (logici) predefiniti. Rappresentazione dei segnali in VHDL. Tipo bit (definito nel package standard) Tipi di segnali (logici) predefiniti Tipo bit (definito nel package standard) Rappresentazione dei segnali in VHDL Approfondimento del corso di Linguaggi di descrizione dell hardware type Bit is ('0',

Dettagli

Corso di Linguaggi di Programmazione + Laboratorio

Corso di Linguaggi di Programmazione + Laboratorio Corso di inguaggi di Programmazione + aboratorio Capitolo 1 - Introduzione Si ringrazia il Dott. Marco de Gemmis per la collaborazione nella predisposizione del materiale didattico Apprendimento di un

Dettagli

Variabili. Unità 2. Domenico Daniele Bloisi. Corso di Programmazione e Metodi Numerici Ingegneria Aerospaziale BAER

Variabili. Unità 2. Domenico Daniele Bloisi. Corso di Programmazione e Metodi Numerici Ingegneria Aerospaziale BAER Corso di Programmazione e Metodi Numerici Ingegneria Aerospaziale BAER Domenico Daniele Bloisi Docenti Metodi Numerici prof. Vittoria Bruni vittoria.bruni@sbai.uniroma1.it Programmazione prof. Domenico

Dettagli

Linguaggio C: le funzioni. Introduzione e sintassi

Linguaggio C: le funzioni. Introduzione e sintassi ISIS "Guido Tassinari" di Pozzuoli Indirizzo Informatico - Articolazione Informatica Informatica Prof. A.S. 2012/2013 Linguaggio C: le funzioni. Introduzione e sintassi 21/10/2012 Introduzione Spesso alcuni

Dettagli

Codice binario. Codice. Codifica - numeri naturali. Codifica - numeri naturali. Alfabeto binario: costituito da due simboli

Codice binario. Codice. Codifica - numeri naturali. Codifica - numeri naturali. Alfabeto binario: costituito da due simboli Codice La relazione che associa ad ogni successione ben formata di simboli di un alfabeto il dato corrispondente è detta codice. Un codice mette quindi in relazione le successioni di simboli con il significato

Dettagli

Descrizioni VHDL Behavioral

Descrizioni VHDL Behavioral 1 Descrizioni VHDL Behavioral In questo capitolo vedremo come la struttura di un sistema digitale è descritto in VHDL utilizzando descrizioni di tipo comportamentale. Outline: process wait statements,

Dettagli

Cap. 2 - Rappresentazione in base 2 dei numeri interi

Cap. 2 - Rappresentazione in base 2 dei numeri interi Cap. 2 - Rappresentazione in base 2 dei numeri interi 2.1 I NUMERI INTERI RELATIVI I numeri relativi sono numeri con il segno: essi possono essere quindi positivi e negativi. Si dividono in due categorie:

Dettagli

Programmazione in Java (I modulo)

Programmazione in Java (I modulo) Programmazione in Java (I modulo) Lezione 4 Variabili di tipo primitivo. Dichiarazione di costanti Conversioni di tipo: operatore cast Altri operatori di assegnamento Operazioni aritmetiche e di confronto

Dettagli

Analizzatori Lessicali con JLex. Giuseppe Morelli

Analizzatori Lessicali con JLex. Giuseppe Morelli Analizzatori Lessicali con JLex Giuseppe Morelli Terminologia Tre concetti sono necessari per comprendere la fase di analisi lessicale: TOKEN: rappresenta un oggetto in grado di rappresentare una specifica

Dettagli

PORTE LOGICHE. Si effettua su due o più variabili, l uscita assume lo stato logico 1 se almeno una variabile di ingresso è allo stato logico 1.

PORTE LOGICHE. Si effettua su due o più variabili, l uscita assume lo stato logico 1 se almeno una variabile di ingresso è allo stato logico 1. PORTE LOGICHE Premessa Le principali parti elettroniche dei computer sono costituite da circuiti digitali che, come è noto, elaborano segnali logici basati sullo 0 e sull 1. I mattoni fondamentali dei

Dettagli

ELETTRONICA dei SISTEMI DIGITALI Universita di Bologna, sede di Cesena

ELETTRONICA dei SISTEMI DIGITALI Universita di Bologna, sede di Cesena ELETTRONICA dei SISTEMI DIGITALI Universita di Bologna, sede di Cesena Fabio Campi Aa 2003-2004 Elettronica dei Sistemi Digitali Fabio Campi, fcampi@deis.unibo.it (con parsimonia ) 051/2093834 http://www.micro.deis.unibo.it/~campi/esd_2004

Dettagli

Esercitazioni di Reti Logiche. Lezione 4

Esercitazioni di Reti Logiche. Lezione 4 Esercitazioni di Reti Logiche Lezione 4 Progettazione dei circuiti logici combinatori Zeynep KIZILTAN zkiziltan@deis.unibo.it Argomenti Procedura di analisi dei circuiti combinatori. Procedura di sintesi

Dettagli

Introduzione alle stringhe e algoritmi collegati

Introduzione alle stringhe e algoritmi collegati Introduzione alle stringhe e algoritmi collegati Algoritmicamente August 15, 2009 1 Introduzione 1.1 Introduzione ai caratteri In informatica o più generalmente in una terminologia scientifica, il carattere

Dettagli

Progettazione di basi di dati

Progettazione di basi di dati Progettazione di basi di dati Sistemi Informativi L-B Home Page del corso: http://www-db.deis.unibo.it/courses/sil-b/ Versione elettronica: progettazionedb.pdf Sistemi Informativi L-B Progettazione di

Dettagli

Hardware, software e periferiche. Facoltà di Lettere e Filosofia anno accademico 2008/2009 secondo semestre

Hardware, software e periferiche. Facoltà di Lettere e Filosofia anno accademico 2008/2009 secondo semestre Hardware, software e periferiche Facoltà di Lettere e Filosofia anno accademico 2008/2009 secondo semestre Riepilogo - Concetti di base dell informatica L'informatica è quel settore scientifico disciplinare

Dettagli

Macchine combinatorie: encoder/decoder e multiplexer/demultiplexer

Macchine combinatorie: encoder/decoder e multiplexer/demultiplexer Corso di Calcolatori Elettronici I A.A. 2011-2012 Macchine combinatorie: encoder/decoder e multiplexer/demultiplexer Lezione 12 Prof. Antonio Pescapè Università degli Studi di Napoli Federico II Facoltà

Dettagli

La "macchina" da calcolo

La macchina da calcolo La "macchina" da calcolo Abbiamo detto che gli algoritmi devono essere scritti in un linguaggio "comprensibile all'esecutore" Se il nostro esecutore è il "calcolatore", questo che linguaggio capisce? che

Dettagli

Fondamenti di Informatica

Fondamenti di Informatica Fondamenti di Informatica Algebra di Boole e Circuiti Logici Prof. Christian Esposito Corso di Laurea in Ingegneria Meccanica e Gestionale (Classe I) A.A. 2016/17 Algebra di Boole e Circuiti Logici L Algebra

Dettagli

Laboratorio di Elettronica Introduzione al VHDL

Laboratorio di Elettronica Introduzione al VHDL Laboratorio di Elettronica 1 Introduzione al VHDL HDL nel flusso di progettazione digitale Elementi base del VHDL Meccanismo di simulazione Meccanismo di sintesi 2 1 Organizzazione del corso Lezione 1:

Dettagli

Linguaggio C: introduzione

Linguaggio C: introduzione Dipartimento di Elettronica ed Informazione Politecnico di Milano Informatica e CAD (c.i.) - ICA Prof. Pierluigi Plebani A.A. 2008/2009 Linguaggio C: introduzione La presente dispensa e da utilizzarsi

Dettagli

Macchine astratte, linguaggi, interpretazione, compilazione

Macchine astratte, linguaggi, interpretazione, compilazione Macchine astratte, linguaggi, interpretazione, compilazione 1 Macchine astratte una collezione di strutture dati ed algoritmi in grado di memorizzare ed eseguire programmi componenti della macchina astratta

Dettagli

Lezione 7 Sommatori e Moltiplicatori

Lezione 7 Sommatori e Moltiplicatori Architettura degli Elaboratori e delle Reti Lezione 7 Sommatori e Moltiplicatori Proff. A. Borghese, F. Pedersini Dipartimento di Scienze dell Informazione Università degli Studi di Milano L 7 /36 Sommario

Dettagli

Traduzione ed Interpretazione

Traduzione ed Interpretazione Traduzione ed Interpretazione Queste sconosciute Siano L Linguaggio ad alto livello M L Macchina astratta di L M 0 Macchina ospite Implementazione interpretativa di L Implementazione compilativa di L Simulazione

Dettagli

Informatica ALGORITMI E LINGUAGGI DI PROGRAMMAZIONE. Francesco Tura. F. Tura

Informatica ALGORITMI E LINGUAGGI DI PROGRAMMAZIONE. Francesco Tura. F. Tura Informatica ALGORITMI E LINGUAGGI DI PROGRAMMAZIONE Francesco Tura francesco.tura@unibo.it 1 Lo strumento dell informatico: ELABORATORE ELETTRONICO [= calcolatore = computer] Macchina multifunzionale Macchina

Dettagli

Dispositivi Logici Programmabili

Dispositivi Logici Programmabili Dispositivi Logici Programmabili Introduzione ROM (Read Only Memory) PLA (Programmable Logic Array) PAL (Programmable Array Logic) PLA e PAL avanzate Logiche programmabili Sono dispositivi hardware che

Dettagli

La codifica digitale

La codifica digitale La codifica digitale Codifica digitale Il computer e il sistema binario Il computer elabora esclusivamente numeri. Ogni immagine, ogni suono, ogni informazione per essere compresa e rielaborata dal calcolatore

Dettagli

Basi di Dati. Progettazione di una Base di Dati. Progettazione di una Base di Dati

Basi di Dati. Progettazione di una Base di Dati. Progettazione di una Base di Dati Basi di Dati Cosa vuol dire progettare una base di dati? Il DBMS non va progettato il DBMS si acquista o esiste già è impossibile pensare di sviluppare un DBMS anni di sviluppo necessità di elevate competenze

Dettagli

Come ragiona il computer. Problemi e algoritmi

Come ragiona il computer. Problemi e algoritmi Come ragiona il computer Problemi e algoritmi Il problema Abbiamo un problema quando ci poniamo un obiettivo da raggiungere e per raggiungerlo dobbiamo mettere a punto una strategia Per risolvere il problema

Dettagli

Modularizzazione del software

Modularizzazione del software Modularizzazione del software Ing. Luca De Santis DIS - Dipartimento di informatica e sistemistica Anno accademico 2006/2007 Fortran 90: Subroutine e function DIS - Dipartimento di informatica e sistemistica

Dettagli

Laboratorio di Informatica. Esercitazione su algoritmi e diagrammi di flusso

Laboratorio di Informatica. Esercitazione su algoritmi e diagrammi di flusso Laboratorio di Informatica Esercitazione su algoritmi e diagrammi di flusso Algoritmi, programmi e dati Algoritmo = insieme di istruzioni che indicano come svolgere operazioni complesse su dei dati attraverso

Dettagli

Concetti Introduttivi. Il Computer

Concetti Introduttivi. Il Computer Concetti Introduttivi Il Computer Introduzione Informazione Notizia, dato o elemento che consente di avere conoscenza più o meno esatta di fatti, situazioni, modi di essere Messaggio Tutto ciò che porta

Dettagli

DESCRIZIONE DEL FUNZIONAMENTO

DESCRIZIONE DEL FUNZIONAMENTO I FLIP FLOP 1.1. Flip Flop Set Reset In figura è rappresentato un f/f set reset con porte NAND. Si tratta del blocco fondamentale alla base di tutti i tipi di F/F. Tabella di verità del Flip Flop Set Reset

Dettagli

Algoritmo. La programmazione. Algoritmo. Programmare. Procedimento di risoluzione di un problema

Algoritmo. La programmazione. Algoritmo. Programmare. Procedimento di risoluzione di un problema Algoritmo 2 Procedimento di risoluzione di un problema La programmazione Ver. 2.4 Permette di ottenere un risultato eseguendo una sequenza finita di operazioni elementari Esempi: Una ricetta di cucina

Dettagli

Corso di Architettura (Prof. Scarano) 09/04/2002

Corso di Architettura (Prof. Scarano) 09/04/2002 Corso di Architettura (Prof. Scarano) 09/0/2002 Un quadro della situazione Lezione 15 Il Set di Istruzioni (1) Vittorio Scarano Architettura Corso di Laurea in Informatica Università degli Studi di Salerno

Dettagli

Facoltà di Ingegneria Corso di Studi in Ingegneria Informatica. Metodologie e strumenti per il reengineering del workflow management

Facoltà di Ingegneria Corso di Studi in Ingegneria Informatica. Metodologie e strumenti per il reengineering del workflow management Descrizione di Macchine a Stati finiti in VHDL Descrizioni di Macchine a Stati finiti in VHDL In questa lezione vedremo come un sistema digitale sequenziale può essere descritto in VHDL. Outline: Macchine

Dettagli

PROCESSI NON SEQUENZIALI E TIPI DI INTERAZIONE

PROCESSI NON SEQUENZIALI E TIPI DI INTERAZIONE PROCESSI NON SEQUENZIALI E TIPI DI INTERAZIONE 1 ALGORITMO, PROGRAMMA, PROCESSO Algoritmo Procedimento logico che deve essere eseguito per risolvere un determinato problema. Programma Descrizione di un

Dettagli

CORSO DI ELEMENTI DI INFORMATICA

CORSO DI ELEMENTI DI INFORMATICA CORSO DI ELEMENTI DI INFORMATICA Corso di Laurea Triennale in Ingegneria Gestionale della Logistica e della Produzione Area didattica Ingegneria Elettrica a.a. 2016/2017 Docente: Ing. Domenico Amalfitano

Dettagli

ESECUZIONE DI PROGRAMMI C SU MACCHINE REALI. Docente: Giorgio Giacinto AA 2008/2009. formalizzazione degli algoritmi in linguaggio C

ESECUZIONE DI PROGRAMMI C SU MACCHINE REALI. Docente: Giorgio Giacinto AA 2008/2009. formalizzazione degli algoritmi in linguaggio C Università degli Studi di Cagliari Corso di Laurea Specialistica in Ingegneria per l Ambiente ed il Territorio Corso di Laurea Specialistica in Ingegneria Civile - Strutture FONDAMENTI DI INFORMATICA 2

Dettagli

TUTORIAL 3. Realizzazione di un contatore su scheda XSA50. A cura di De Pin Alessandro

TUTORIAL 3. Realizzazione di un contatore su scheda XSA50. A cura di De Pin Alessandro TUTORIAL 3 Realizzazione di un contatore su scheda XSA50 A cura di De Pin Alessandro 1 Problema proposto In questo tutorial ci si propone di realizzare un contatore che, associato ad un display a sette

Dettagli

Sistemi e Tecnologie per l'automazione LS. HW per elaborazione digitale in automazione: Microcontrollori e DSP

Sistemi e Tecnologie per l'automazione LS. HW per elaborazione digitale in automazione: Microcontrollori e DSP Laurea Specialistica in Ingegneria Informatica Laurea Specialistica in Ingegneria Elettronica e delle Telecomunicazioni Sistemi e Tecnologie per l'automazione LS HW per elaborazione digitale in automazione:

Dettagli

Sintesi Sequenziale Sincrona Sintesi Comportamentale di reti Sequenziali Sincrone

Sintesi Sequenziale Sincrona Sintesi Comportamentale di reti Sequenziali Sincrone Sintesi Sequenziale Sincrona Sintesi Comportamentale di reti Sequenziali Sincrone Il problema dell assegnamento degli stati versione del 9/1/03 Sintesi: Assegnamento degli stati La riduzione del numero

Dettagli

Analogico vs. Digitale. LEZIONE II La codifica binaria. Analogico vs digitale. Analogico. Digitale

Analogico vs. Digitale. LEZIONE II La codifica binaria. Analogico vs digitale. Analogico. Digitale Analogico vs. Digitale LEZIONE II La codifica binaria Analogico Segnale che può assumere infiniti valori con continuità Digitale Segnale che può assumere solo valori discreti Analogico vs digitale Il computer

Dettagli

Introduzione. Caratteristiche generali. Sistemi e Tecnologie per l'automazione LS. HW per elaborazione digitale in automazione: Microcontrollori e DSP

Introduzione. Caratteristiche generali. Sistemi e Tecnologie per l'automazione LS. HW per elaborazione digitale in automazione: Microcontrollori e DSP Laurea Specialistica in Ingegneria Informatica Laurea Specialistica in Ingegneria Elettronica e delle Telecomunicazioni Sistemi e Tecnologie per l'automazione LS HW per elaborazione digitale in automazione:

Dettagli

Rappresentazione dell Informazione

Rappresentazione dell Informazione Rappresentazione dell Informazione Rappresentazione delle informazioni in codice binario Caratteri Naturali e Reali positivi Interi Razionali Rappresentazione del testo Una stringa di bit per ogni simbolo

Dettagli

Laboratorio di Architettura lezione 5. Massimo Marchiori W3C/MIT/UNIVE

Laboratorio di Architettura lezione 5. Massimo Marchiori W3C/MIT/UNIVE Laboratorio di Architettura lezione 5 Massimo Marchiori W3C/MIT/UNIVE Da Alto a Basso livello: compilazione Come si passa da un linguaggio di alto livello a uno di basso livello? Cioe a dire, come lavora

Dettagli

Descrizione delle operazioni di calcolo. Espressioni costanti semplici

Descrizione delle operazioni di calcolo. Espressioni costanti semplici Descrizione delle operazioni di calcolo Come abbiamo detto l interprete è in grado di generare nuovi valori a partire da valori precedentemente acquisiti o generati. Il linguaggio di programmazione permette

Dettagli

6 - Blocchi e cicli. Programmazione e analisi di dati Modulo A: Programmazione in Java. Paolo Milazzo

6 - Blocchi e cicli. Programmazione e analisi di dati Modulo A: Programmazione in Java. Paolo Milazzo 6 - Blocchi e cicli Programmazione e analisi di dati Modulo A: Programmazione in Java Paolo Milazzo Dipartimento di Informatica, Università di Pisa http://pages.di.unipi.it/milazzo milazzo di.unipi.it

Dettagli

L hardware da solo non è sufficiente per il funzionamento dell elaboratore È necessario introdurre il software:

L hardware da solo non è sufficiente per il funzionamento dell elaboratore È necessario introdurre il software: Il Software L hardware da solo non è sufficiente per il funzionamento dell elaboratore È necessario introdurre il software: un insieme di programmi che permettono di trasformare un insieme di circuiti

Dettagli

Esercizio 1.A Aritmetica binaria (nel presentare le soluzione mostrare, almeno nei passaggi piú significativi, i calcoli eseguiti) (3 punti)

Esercizio 1.A Aritmetica binaria (nel presentare le soluzione mostrare, almeno nei passaggi piú significativi, i calcoli eseguiti) (3 punti) Cognome e Nome: Matr.: Architettura degli Elaboratori Inf A 14 febbraio 2013 Esercizio 1.A Aritmetica binaria (nel presentare le soluzione mostrare, almeno nei passaggi piú significativi, i calcoli eseguiti)

Dettagli

ELEMENTI DI PROGRAMMAZIONE a.a. 2012/13 MACCHINE, ALGORITMI, PROGRAMMI

ELEMENTI DI PROGRAMMAZIONE a.a. 2012/13 MACCHINE, ALGORITMI, PROGRAMMI ELEMENTI DI PROGRAMMAZIONE a.a. 22/3 MACCHINE, ALGORITMI, PROGRAMMI Andrea Prevete, UNINA2 23 UNA GERARCHIA DI MACCHINE macchine combinatorie macchine sequenziali (automi a stati finiti)... macchine di

Dettagli