Sei sulla pagina 1di 15

1 INTRODUZIONE

1

INTRODUZIONE

1

1.1

IL PROGETTO DI SISTEMI DIGITALI: FLUSSO DI PROGETTAZIONE

2

1.2

INTRODUZIONE AL VHDL

12

1.2.1

DICHIARAZIONE DI ENTITÀ

15

1.2.2

ARCHITETTURA

16

1.2.3

CONFIGURAZIONE

18

1.2.4

LO STILE DI DESCRIZIONE CONCORRENTE

19

1.2.5

LO STILE DI DESCRIZIONE SEQUENZIALE

21

1.2.6

RIASSUNTO

23

1.3

LA SIMULAZIONE LOGICA: CENNI INTRODUTTIVI

25

1.4

LA PROGETTAZIONE AUTOMATICA: IL PROGRAMMA SIS

27

L A PROGETTAZIONE AUTOMATICA : IL PROGRAMMA SIS 27 Franco Fummi Università di Verona Mariagiovanna Sami

Franco Fummi

Università di Verona

IL PROGRAMMA SIS 27 Franco Fummi Università di Verona Mariagiovanna Sami Politecnico di Milano La presente

Mariagiovanna Sami

Politecnico di Milano

La presente dispensa è stampabile e riproducibile esclusivamente per gli scopi didattici dei corsi del Politecnico di Milano e dell'Università di Verona; ogni altro uso deve essere preventivamente autorizzato dagli autori.

Febbraio 2000

INTRODUZIONE

1.1 IL PROGETTO DI SISTEMI DIGITALI: FLUSSO DI PROGETTAZIONE

Il progetto di un sistema digitale può essere affrontato a diversi livelli di astrazione:

idealmente, si può considerare un completo flusso di progettazione di tipo top-down – che parte cioè dal livello di astrazione più elevato per giungere a quello fisico – e che può essere rappresentato schematicamente come in figura 1.1:

comportamento

Registro - unità funzionale

Porta logica

maschere

Specifica di

sistema

funzionale Porta logica maschere Specifica di sistema Progetto architetturale Progetto logico Disegno
Progetto architetturale Progetto logico Disegno delle maschere
Progetto architetturale Progetto logico Disegno delle maschere
Progetto architetturale Progetto logico Disegno delle maschere
Progetto architetturale Progetto logico Disegno delle maschere
Progetto architetturale Progetto logico Disegno delle maschere

Progetto

architetturale

Progetto architetturale Progetto logico Disegno delle maschere
Progetto logico

Progetto logico

Progetto logico
Progetto architetturale Progetto logico Disegno delle maschere

Disegno delle

maschere

Progetto architetturale Progetto logico Disegno delle maschere
Progetto architetturale Progetto logico Disegno delle maschere
Progetto architetturale Progetto logico Disegno delle maschere
Progetto architetturale Progetto logico Disegno delle maschere
Progetto architetturale Progetto logico Disegno delle maschere
Progetto architetturale Progetto logico Disegno delle maschere
Produzione
Produzione

Figura 1.1. Flusso di progettazione.

Una descrizione di questo tipo è del tutto indipendente dal tipo di strumenti di progettazione assistita da calcolatore (strumenti di CAD) che si utilizzano – in effetti, è indipendente dal fatto che tali strumenti vengano utilizzati oppure no.

Il committente del progetto fornisce delle specifiche del sistema da progettare – sia in termini funzionali (che cosa il sistema deve fare) sia in termini non funzionali (i vincoli temporali, i limiti di consumo di potenza, ecc.). Quello che descrive è essenzialmente il comportamento del sistema, unitamente ai vincoli non funzionali. Dalle specifiche

2

INTRODUZIONE

funzionali, il progettista digitale deduce uno schema “ad alto livello” che definisce la architettura in termini di unità funzionali (es. unità aritmetiche, multiplexer, contatori…) e di registri, collegati fra loro mediante percorsi di dati e segnali di controllo; lo schema “architetturale” che si ottiene viene spesso indicato come schema RTL (Register Transfer Level). Dalla descrizione formale RTL si deriva – sulla base tipicamente di vincoli non funzionali, quali tempo di elaborazione, costo (in genere riassunto in termini di porte logiche) ecc. – il progetto logico, che fornisce una rete di porte logiche o componenti di comparabile complessità (bistabili, ecc.) opportunamente interconnessi: si noti che a questo livello si è persa ogni visibilità non solo del comportamento, ma anche delle funzionalità svolte e del flusso di controllo.

A ogni passaggio da un livello di astrazione più elevato a uno inferiore corrisponde

un’azione di sintesi, che nel più tradizionale progetto “manuale” viene compiuta dal progettista senza il supporto di strumenti informatici: una sintesi non è solo una trasformazione da un livello a un altro, ma comporta anche una aggiunta di informazione.

Si può anche vedere un passo di sintesi come la trasformazione da una specifica (iniziale

o intermedia) a una implementazione (completa o intermedia). Si consideri ad esempio il

passaggio da uno schema a livello “trasferimento fra registri” (RTL), costituito da registri, unità funzionali e strutture di interconnessione, a uno schema a livello di porta logica: lo schema iniziale fornisce una descrizione della architettura del sistema, e da tale specifica

si deduce una struttura logica. Nel corso del passaggio si compiono delle scelte (ad

esempio, il tipo di “forma” logica scelta, la famiglia di porte logiche, le fasi di ottimizzazione e di conseguenza le relative scelte di merito) che corrispondono ad altrettanti incrementi di informazione. Nelle fasi di sintesi si può ricorrere a strumenti di sintesi automatica, che realizzano il passo richiesto applicando opportuni criteri di ottimizzazione; oggi si tende a non esigere più uno strumento di sintesi “monolitico”, che dalle specifiche comportamentali produca le maschere d’integrazione senza alcun intervento del progettista, ma si preferisce piuttosto prevedere un’interazione col progettista stesso che, da un livello all’altro, può scegliere fra diversi stili di progetto, differenti cifre di merito rispetto a cui ottimizzare, diversi vincoli non-funzionali da introdurre.

Al termine di ogni passo di sintesi, si presenta evidentemente la necessità di verificare in

qualche modo che il risultato della sintesi – il progetto, sia pure parziale, così ottenuto – sia congruente con la specifica da cui la sintesi è stata effettuata; se il progetto ha complessità anche limitata (e la complessità dei moderni sistemi digitali non è affatto limitata!) una verifica “manuale” affidata alla buona volontà e all’intuito del progettista è del tutto insufficiente. Oggi, la verifica di correttezza della sintesi viene normalmente compiuta mediante simulazione, utilizzando strumenti di CAD che “simulano” il

3

INTRODUZIONE

funzionamento del sistema (a un dato livello di astrazione – funzionale, logico, elettrico) in risposta a opportuni insiemi di stimoli d’ingresso (vettori d’ingresso): analizzando i risultati della simulazione, il progettista verifica se essi coincidono con i valori attesi e quindi se il risultato della sintesi è corretto. La simulazione – oltre che essere compiuta a diversi livelli – può fornire anche informazioni di vario tipo: ad esempio, riferendosi al livello di porta logica, si può fare una simulazione “logica”, considerando che tutti i componenti siano ideali e introducano ritardo nullo (in questo caso i risultati ottenuti riguardano solo i valori logici delle uscite e prescindono dall’influenza dei ritardi) oppure si possono introdurre anche le caratteristiche temporali dei componenti e dei segnali e ottenere quindi anche un’analisi temporale dei risultati. La parte del flusso di progettazione rilevata in grigio nella figura 1.1, considerando le attività di CAD, si modifica come viene indicato in figura 1.2:

4

INTRODUZIONE

Definizione iniziale dell’architettura Descrizione in un linguaggio formale Simulazione ad alto livello Sintesi
Definizione iniziale
dell’architettura
Descrizione in un
linguaggio formale
Simulazione ad alto
livello
Sintesi logica
floorplanning
Librerie,
Analisi statica
modelli di
Simulazione a livello logico
delle
componenti
temporizzazioni
Layout guidato dalle
temporizzazioni
Analisi statica
delle
temporizzazioni
Verifica tecnologica
post-layout
Al processo di produzione

Figura 1.2. Flusso di progettazione con verifica.

La simulazione, come si evidenzia in figura, richiede la disponibilità di librerie di modelli cui il progettista possa fare riferimento e che vengono richiamate dai programmi di simulazione; le stesse librerie vengono di fato adottate anche nelle sintesi, sebbene l’informazione utilizzata in questo caso sia in genere più ridotta.

Al programma di simulazione si forniscono:

la descrizione del sistema da simulare, in un opportuno linguaggio formale “pe la

5

INTRODUZIONE

descrizione di hardware” (HDL);

la libreria di modelli;

i “vettori di ingresso” (cioè le configurazioni degli ingressi per cui si vogliono valutare le reazioni del sistema)

e si ricavano le risposte (livelli logici alle uscite, eventualmente temporizzazioni e altri parametri). Si identifica subito il principale problema della “verifica per simulazione”, e cioè la necessità di determinare un insieme di vettori di ingresso sufficiente a garantire la correttezza del progetto – o più propriamente la rispondenza del progetto alle specifiche. A meno che il sistema non sia molto piccolo, una simulazione esaustiva è ovviamente improponibile in termini di tempo di calcolo; peraltro, è pressoché impossibile garantire che il sottoinsieme di vettori d’ingresso effettivamente adottato sia sufficiente a ”coprire” tutte le situazioni possibili e quindi a fornire la richiesta garanzia. In genere, il progettista deve identificare sulla base della propria esperienza l’insieme di situazioni che giudica più “critiche” ai fini della sollecitazione del sistema; alcuni casi ben noti di circuiti digitali anche di notevole diffusione che hanno mostrato – una volta già inseriti sul mercato – di essere afflitti da errori di progetto sono dovuti proprio a una fase di verifica per simulazione che non ha sollecitato tutti i possibili percorsi di attivazione all’interno del sistema.

Oltre ai diversi livelli di astrazione che si sono finora citati, è possibile anche che il progettista affronti il sistema digitale partendo da differenti viste: queste sono state schematizzate molto utilmente da Gajski nel cosiddetto “diagramma a Y”, mostrato in figura 1.3:

Vista

comportamentale

Vista strutturale

Livello logico
Livello logico

Figura 1.3. Diagramma a Y delle viste della progettazione digitale.

6

INTRODUZIONE

Viste e livelli sono ortogonali fra di loro:

la vista comportamentale (behavioral) descrive le funzioni indipendentemente dalla implementazione;

la vista strutturale descrive il modello come interconnessione di componenti;

la vista fisica riguarda gli oggetti fisici di un progetto (es., i transistori).

Modelli di sistemi digitali a diversi livelli possono essere osservati sotto diverse viste. Ad esempio, a livello architetturale la vista comportamentale di un circuito indica l’insieme di operazioni compiute e le dipendenze fra di esse; la vista strutturale precisa invece l’interconnessione dei principali blocchi funzionali (unità funzionali, registri etc.) componenti del sistema. A livello logico, invece, la vista comportamentale di un circuito sequenziale sincrono è data dal diagramma degli stati, mentre la vista strutturale dello stesso circuito consiste nella interconnessione di porte logiche e bistabili. La sintesi può ora essere considerata come un insieme di trasformazioni fra due viste assiali. Così, la sintesi a livello architetturale genera una vista strutturale di un modello a livello architetturale; si assegnano le funzioni del circuito a operatori (risorse), di cui si determinano le interconnessioni insieme alla temporizzazione dell'esecuzione delle operazioni. (Questa fase è detta in genere sintesi ad alto livello). La sintesi a livello logico genera poi una vista strutturale di un modello a livello logico, determinando la struttura del circuito costituito da porte logiche. Infine, la sintesi geometrica è il progetto fisico del circuito: tipicamente, nel caso dei circuiti microelettronici produce il layout del circuito stesso. Rifacendosi allo schema di figura 1.3, un percorso di sintesi può essere schematizzato come in figura 1.4:

Vista

comportamentale

Vista strutturale

Livello logico
Livello logico

Figura 1.4. Percorso di sintesi sul "diagramma a Y".

7

INTRODUZIONE

A livello architetturale e a livello logico, la sintesi si muove da una vista

comportamentale a una strutturale, mentre a livello geometrico ci si muove dalla vista strutturale (lo schema delle porte logiche) alla vista fisica (il layout).

Il ricorso a strumenti automatici di simulazione e di sintesi rende evidente

l’indispensabilità di un linguaggio formale per la descrizione dei circuiti digitali: in effetti, un formalismo rigoroso risulta utile anche indipendentemente dalle attività di CAD – ad esempio, per garantire che un circuito anche di grande complessità sia accompagnato da una documentazione completa che resti leggibile e comprensibile a un gruppo di progetto che può cambiare nel tempo o a una vasta utenza del progetto stesso

che voglia integrare il circuito in un proprio sistema. La definizione di linguaggi per la

descrizione di hardware ha coinvolto gli studiosi del settore oramai da diversi decenni: si sono distinte due diverse tendenze – estendere un linguaggio di programmazione esistente, in modo da supportare le esigenze del progetto hardware, oppure introdurre un linguaggio specifico (sia pure allineato con le caratteristiche dei linguaggi di programmazione recenti) di cui le esigenze della progettazione hardware siano le principali linee guida. Un esempio della prima tendenza è stato Hardware-C, definito nell’ambito dell’Università di Stanford; i linguaggi della famiglia HDL oggi di maggiore diffusione (adottati dai più noti pacchetti CAD) seguono invece la seconda tendenza, e sono in particolare Verilog e VHDL. Verilog ha origini essenzialmente commerciali; VHDL nasce da un progetto del ministero della difesa americano (il progetto VHSIC - Very Hign Speed Integrated Circuits) di cui costituiva appunto il linguaggio di descrizione hardware, ed è stato definito in moo da rispondere a un certo numero di requisiti essenziali. In questo corso, si farà riferimento a VHDL, che a livello europeo è diventato de facto lo standard: prima di affrontarlo, nel prossimo capitolo, vale la pena di considerare le principali differenze fra un linguaggio del grupo HDL e un normale linguaggio di programmazione in relazione alle differenze esistenti tra la realizzazione automatica del software e la progettazione di circuiti digitali . Si consideri per esempio la scrittura in C dell'algoritmo per il calcolo del massimo comune divisore (GCD).

#include <stdio.h> int gcd(int xi, int yi)

{

int x, y, temp;

x = xi;

y = yi;

while (x > 0){ if (x <= y){ temp = y;

}

y

x

= x;

= temp;

x = x - y;

main()

{

int

xi, yi, ou;

scanf("%d %d", &xi, &yi);

ou = gcd(xi, yi);

printf("%d\n", ou);

}

8

INTRODUZIONE

}

return(y);

}

Sulla sinistra è riportata la funzione che calcola il GCD, mentre sulla destra è riportato il programma principale che utilizza la funzione. La compilazione di questo codice C produce un programma che è eseguibile su un elaboratore dotato di un sistema operativo. L'insieme composto da elaboratore, sistema operativo e programma, può essere considerato un dispositivo digitale che realizza l'algoritmo del GCD. Sembrerebbe quindi abbastanza ovvio utilizzare la stessa rappresentazione C per descrivere il comportamento di un circuito digitale che realizza unicamente l'algoritmo del GCD. Esaminiamo però alcuni aspetti tipici della progettazione hardware, che nella progettazione del software rimangono spesso nascosti, e che quindi non trovano nel linguaggio C un meccanismo di rappresentazione.

Ingresso/uscita. Le funzioni di libreria del C (scanf e printf) si occupano di risolvere i problemi di acquisizione e codifica dei numeri interi su cui calcolare il GCD

e di scrittura dei risultati su un dispositivo di uscita come il video. Nella specifica di un dispositivo hardware bisogna invece tenere conto di questi aspetti che devono essere esplicitamente modellati. Deve quindi essere descritta l'interfaccia di ingresso/uscita, il protocollo adottato per acquisire i dati (per esempio caricamento seriale o parallelo) e

le convenzioni scelte per la codifica dei numeri e la loro rappresentazione (per esempio

numeri in complemento a due o in modulo e segno).

Ampiezza delle variabili. Al discorso precedente si collega direttamente la scelta della precisione richiesta alle variabili utilizzate. Se in linguaggio C una variabile viene definita di tipo int si sa che potranno essere eseguite su di essa operazioni intere, ma si possono tralasciare gli aspetti relativi alla codifica che vengono trattati dal compilatore. Per esempio, a fronte dello stesso programma C, un compilatore operante

su un elaboratore a 32 bit, allocherà per ogni intero 4 byte, mentre un compilatore

operante su un elaboratore a 64 bit ne allocherà 8. Per la maggioranza delle applicazioni software queste scelte sono ininfluenti. Nel caso di specifica dello hardware, invece, l'ampiezza delle variabili ha un impatto notevole sulle dimensioni e

prestazioni del dispositivo realizzato. Deve quindi essere possibile, pur utilizzando tipi

di dati astratti o predefiniti, specificare l'ampiezza in bit di tutte le variabili utilizzate.

Operatori e operandi. Scrivendo un programma in C si possono utilizzare tutti gli operatori definiti nelle librerie che vengono collegate al codice oggetto. Il compilatore provvede a descrivere questi operatori nel linguaggio macchina del microprocessore che esegue il programma. Alcune operazioni (per esempio la moltiplicazione) sono istruzioni eseguite direttamente dal microprocessore, altre operazioni (per esempio

9

INTRODUZIONE

l'elevamento a potenza) vengono convertite in una serie di operazioni elementari. Al contrario, nella specifica dello hardware, l'utilizzo di un operatore implica la presenza nel circuito realizzato dell'operando che esegue quell'operazione. Gli operatori devono quindi essere utilizzati con cura poiché ad ogni tipo di operazione corrisponde un particolare circuito hardware che la realizza. Deve anche essere possibile associare ad

un operando un particolare operatore, per esempio un sommatore più o meno veloce.

Temporizzazione. Il programma in C compilato e assemblato viene eseguito dal microprocessore alla sua frequenza di funzionamento. La temporizzazione è quindi implicita e dipende dal microprocessore su cui viene eseguito il programma. Nel caso della descrizione di un dispositivo hardware, la temporizzazione deve invece essere dichiarata esplicitamente e deve essere rappresentata da uno o più segnali di clock.

Elementi di memoria. Il modulo di ottimizzazione del compilatore associa, in maniera trasparente al programmatore, ogni variabili utilizzata a celle di memoria e ai registri del microprocessore. Al contrario, i programmi di sintesi automatica, riconoscono dalla semantica dell'algoritmo descritto, quali variabili diventeranno registri e quali potranno essere rappresentate con dei semplici fili. Il progettista deve sapere quali criteri sono utilizzati dal programma di sintesi per prendere queste decisioni per poter tenere sotto controllo i risultati prodotti. L'inserimento di registri altera infatti la temporizzazione di un circuito che rischia quindi di non rispettare i vincoli imposti al progetto.

Esecuzione delle istruzioni. In un linguaggio come il C è possibile specificare

unicamente delle istruzioni che vengono eseguite in maniera puramente sequenziale.

Al

contrario un dispositivo hardware è implicitamente parallelo poiché in ogni istante

di

funzionamento rivaluta concorrentemente tutti i segnali che lo compongono. È

quindi necessario che un linguaggio per la progettazione dello hardware permetta di

descrivere sia comportamenti sequenziali che concorrenti.

Sincronizzazione tra i moduli. La chiamata a funzione o procedura non richiede in C

alcun meccanismo di sincronizzazione esplicito poiché è il compilatore che si occupa

di generare l'opportuno codice eseguibile per gestire lo stack, il record di attivazione,

ecc., e che permette di effettuare il passaggio dei parametri e la sincronizzazione tra

programma principale e sottoprogramma. In un dispositivo hardware, la sincronizzazione tra componenti deve essere invece esplicitamente definita da un apposito protocollo. Deve quindi essere possibile definire dei vincoli temporali per modellare questo tipo di comportamenti.

Da questa serie di considerazioni risulta evidente che un linguaggio per la progettazione del software come il C non permette di descrivere tutti gli aspetti necessari alla specifica

10

INTRODUZIONE

dello hardware. Con un linguaggio HD deve infatti essere possibile specificare le caratteristiche della macchina virtuale che eseguirà l'algoritmo che nella progettazione software è predefinita (microprocessore + sistema operativo + ambiente di sviluppo) mentre nella progettazione hardware viene definita durante la realizzazione dell'algoritmo stesso.

Questo è il motivo per cui sono stati ideati dei linguaggi appositi le cui caratteristiche principali possono essere così riassunte.

Una prima caratteristica che un linguaggio HD deve mostrare è la capacità di rappresentare adeguatamente la concorrenza. Le varie parti di un circuito o di un sistema digitale operano in modo essenzialmente concorrente - si può dire che la simultaneità di esecuzione fra le varie parti è la regola, mentre la sequenzializzazione deve essere “forzata” e controllata opportune unità di controllo. La normale esecuzione di un programma software avviene invece su un processore singolo che esegue in modo sequenziale il programma (anche là dove esiste nella CPU un grado di parallelismo questo viene mascherato all’utente, come si vedrà più avanti).

Un secondo requisito di un linguaggio HD è la possibilità di rappresentare in qualche modo la informazione strutturale relativa a un circuito - identificare ad esempio i collegamenti fra le porte d’ingresso di un’unità aritmetica e la porta di uscita di un registro: questo deve essere possibile a diversi livelli di astrazione - un linguaggio HD deve essere capace di supportare descrizioni gerarchiche del sistema digitale, in modo da

supportare sia un flusso di progetto top down (come quello ideale introdotto prima) sia un progetto bottom-up. Il linguaggio deve anche supportare un progetto modulare; ed essere strutturato in modo che una modifica interna a un'unità del progetto non richieda una ricompilazione dell'intero progetto. Sempre in un’ottica di modularità, il linguaggio deve consentire il riuso di unità di progetto precedentemente create - quindi costruzione e uso

di

librerie.

La

possibilità di muoversi a vari livelli di astrazione è il terzo requisito dei linguaggi HD.

Un quarto punto riguarda la necessità di fornire (almeno in alcuni casi) precisi vincoli temporali: questo è indispensabile per simulare sistemi reali, ai cui componenti siano associati sia ritardi inerziali sia ritardi di trasporto, come anche per potere sincronizzare fra loro diverse parti di un circuito o diversi sottosistemi (ciò implica, fra l’altro, la possibilità di definire dei clock).

Infine, il quinto (ma non certo meno importante) requisito riguarda la possibilità di supportare descrizioni rispondenti alle diverse viste del sistema, come prima definite. In particolare, occorre supportare sia viste comportamentali, sia viste strutturali, e ciò indipendentemente dal livello di astrazione cui si opera.

11

INTRODUZIONE

Nel prossimo capitolo, si definiranno le principali caratteristiche del linguaggio VHDL.

1.2 INTRODUZIONE AL VHDL

VHDL riprende alcuni concetti di ADA: in particolare, adotta la filosofia “specification- and-body”, che distingue (e separa) esplicitamente la specifica – che rappresenta le interfacce di un componente – dal corpo del componente stesso. Ciò consente di associare

a un’unica specifica più corpi, che rappresentano altrettante viste o anche diversi progetti alternativi. Le alternative che si riferiscono a un’unica funzione devono riferirsi a un'unica interfaccia esplicita, e poter essere indicate con un nome univoco. La possibilità

di creare alternative deve valere a tutti i livelli di astrazione: le diverse viste possono

essere riassunte nei seguenti termini:

La vista comportamentale deve supportare descrizioni algoritmiche o procedurali. L’ordinamento lineare delle istruzioni in una descrizione comportamentale indica la normale sequenza di elaborazione: occorrono quindi appositi costrutti di controllo per modificare la sequenza “di default”. Per definizione, la descrizione puramente behavioral non deve implicare una struttura architetturale o circuitale.

L’approccio architetturale invece deve supportare la descrizione di un progetto circuitale con tecniche non procedurali. La struttura viene implicata usando condizioni di tipo logico per controllare l'esecuzione di ogni singola istruzione. La semantica del linguaggio deve

permettere espressioni di controllo parallele, concorrenti e sequenziali

consentire la valutazione di condizioni parallele in punti che possono essere determinati sulla base di transazioni (handshake implicito), eventi (handshake esplicito) o periodicità (clock implicito).

La

descrizione architetturale deve implicare una struttura. Il progettista può facilmente

separare flusso di controllo e flusso di dati. La logica del flusso di controllo viene rappresentata dalle condizioni poste sull'esecuzione di ogni istruzione; l'istruzione eseguita rappresenta il flusso dei dati. La sequenza “puro comportamento/architettura/ pura struttura” deve essere vista come un tutto continuo.

La descrizione strutturale deve

permettere l'identificazione delle unità strutturali usate e dei loro connettivi;

estendersi su più livelli di astrazione;

fornire solo informazioni sull'interconnessione fra i componenti, senza associare

12

INTRODUZIONE

contenuto comportamentale ai componenti stessi.

La principale astrazione hardware VHDL è la design entity, che consente quella separazione fra interfaccia e funzione indicata prima. Una design entity consta di una interfaccia - che descrive la “piedinatura” del modulo mediante le porte (che definiscono i canali di comunicazione fra design entity e mondo esterno) e fornisce altri parametri (ad

esempio, le caratteristiche temporali) necessari per usare il modulo – e di un corpo o body che descrive l’organizzazione e/o il comportamento (a seconda dello stile adottato) del modulo stesso. A un’interfaccia possono corrispondere più corpi diversi, ognuno dei quali rappresenta una diversa realizzazione o mette in luce un diverso aspetto progettuale (dà cioè una diversa “vista” dell'entità). Così, ad esempio, un corpo può descrivere il comportamento, un altro la struttura scomposta in interconnessione di sottocomponenti,

un terzo il funzionamento in termine di trasferimenti a livello RT.

Una design entity può riferirsi a un modulo di complessità qualunque, dalla porta logica a

un intero calcolatore. L’interfaccia contiene informazione comune ai corpi alternativi; una

parte di tale informazione - la specifica di porte (ports) e generici (generics) è visibile esternamente.

Passando a un livello di astrazione più elevato, una design entity precedentemente specificata può essere a sua volta usata come componente a livello di astrazione più elevato: l'interfaccia deve corrispondere a quella del componente (l'informazione visibile esternamente viene usata per le verifiche di consistenza).

VHDL è un linguaggio orientato a librerie:, una volta che un testo sorgente è stato compilato correttamente, il risultato viene inserito in una libreria gestita dal library manager del VHDL. Il linguaggio è organizzato sulla base di unità di progetto (design units) compilabili separatamente: la compilazione portata a buon fine di un file VHDL

aggiunge alla libreria corrente una o più unità di progetto. Si possono introdurre più unità

di progetto in un unico file; non si può spezzare un'unità di progetto unica su più files.

L’unità compilata correttamente viene inserita nella libreria di lavoro, gestita dal compilatore VHDL: il progettista lavora sulla propria libreria referenziandone - a seconda della necessità - altre (precostituite, o contenenti i modelli generali o oggetti di utilità

generale) dette librerie delle risorse.

La descrizione hardware viene vista come un insieme di modelli e di algoritmi che i modelli usano. Il modello è costituito da una vista esterna (che mostra le sue connessioni col resto del mondo) e da una vista interna (che ne descrive la realizzazione).

Un modello VHDL corrisponde alla già citata entità: la sua vista esterna è la dichiarazione di entità, la vista interna costituisce l’architettura. Ognuna delle due parti è una unità di progetto. È ragionevole separare le diverse unità per poter modificare

13

INTRODUZIONE

facilmente il funzionamento interno di un modello senza modificarne le viste esterne, associare a una vista esterna più soluzioni interne, ecc.

Ogni oggetto in VHDL deve appartenere a un tipo e non può cambiare il tipo cui appartiene. Esistono quattro famiglie di tipi:

1) scalari: interi, in virgola mobile, tipi fisici e tipi enumerati;

2) compositi (tabelle e record)

3) accesso (puntatori)

4) files.

Gli oggetti possono appartenere a tre classi: costanti, variabili, segnali. Le costanti hanno un valore fissato una volta per tutte - eventualmente, dopo una fase di inizializzazione. Le variabili hanno un valore che può essere modificato con un’istruzione di assegnamento. I segnali sono caratteristici della descrizione hardware: modellano infatti l’informazione che passa su un collegamento, un bus ecc. Per mettere in evidenza la differenza fra variabili e segnali, si considerino dapprima due variabili A e B: si può scrivere A=B e successivamente modificare A e/o B - una variabile è infatti per sua natura dinamica (corrisponde al contenuto di una parola di memoria). Un segnale invece riflette una realtà strutturale ed è per natura statico, ed esiste indipendentemente dalla sua zona di visibilità. La notazione A <= B stabilisce un collegamento definitivo fra i due segnali; se B cambia, anche A potrà risultare modificato (eventualmente dopo opportune verifiche di realizzabilità), ma il segnale mantiene la propria “storia”, cioè l’insieme di informazioni che lo pilota.

Il VHDL supporta cinque tipi di unità di progetto: tre di questi sono tipi di unità di progetto primarie e due tipi di unità secondarie. Una unità primaria descrive quello che viene fatto da una '”scatola nera”, mentre una unità secondaria descrive come viene fatto.

Le unità di progetto esistenti in VHDL sono:

1) dichiarazioni di entità (entity declaration);

2) architetture (architecture);

3) configurazioni (configuration);

4) dichiarazioni di pacchetto (package declaration);

5) corpi di pacchetto (package body).

Nel seguito si indicheranno brevemente sintassi e caratteristiche delle unità di progetto e si presenteranno alcuni aspetti rilevanti che differenziano VHDL da altri linguaggi di programmazione; per uno studio più approfondito si rimanda ai testi specifici riguardanti

14

INTRODUZIONE

VHDL.

1.2.1 Dichiarazione di entità

Come si è già detto, l’entità VHDL consta della coppia dichiarazione di entità - architettura. La dichiarazione di entità descrive l'interfaccia unica fra architetture e ambiente esterno, l’architettura descrive la funzionalità (in senso esteso) dell'entità. L'entità può essere usata a livello di astrazione più elevato come un unico componente, e così via. Sono possibili più architetture diverse per la stessa dichiarazione di entità. Le informazioni contenute nella dichiarazione di entità sono note a tutte le architetture che fanno riferimento a quell'entità.

Si consideri per esempio l’entità FULL_ADDER(STRUTTURALE); FULL_ADDER è il nome della dichiarazione, STRUTTURALE il nome dell'architettura scelta; questa è un’entità diversa da FULL_ADDER(COMPORTAMENTALE), in quanto le architetture sono diverse.

Riprendendo l'esempio del GCD, è possibile descrivere la sua interfaccia con l'esterno nel seguente modo:

CCLLOOCCKK

RREESSEETT

G G C C D D

GGCCDD

G G C C D D
G G C C D D

OOUU

C C L L O O C C K K R R E E S S

XXII

O O C C K K R R E E S S E E T T

YYII

K K R R E E S S E E T T G G C C

Il circuito legge i due numeri interi XI e YU e produce il risultato sul segnale OU. I segnali di CLOCK e di RESET, sono stati aggiunti per permettere di temporizzare il circuito e di inizializzarlo in una configurazione nota. Questa interfaccia viene descritta in VHDL dalla seguente dichiarazione di entità:

entity GCD is port (

CLOCK, RESET : in bit; XI,YI : in integer; OU : out integer

);

end gcd;

dove alle porte di ingresso ed uscita è associato un tipo predefinito (BIT e integer).

15

INTRODUZIONE

Tornando all’esempio del sommatore completo, la sua interfaccia è:

entity FULL_ADDER is

port (

A,B,CIN; in BIT; S,COU: out BIT);

end FULL_ADDER;

Le porte A,B,CIN,S,COUT sono dette formali in quanto sono relative al modello FULL_ADDER. Se l'addizionatore verrà o referenziato come componente (component) o utilizzato (se ne richiamerà cioè una istanza), ai nomi si associeranno valori o segnali indicati come porte effettive (actual).

Le porte sono essenzialmente segnali. Hanno un tipo indicato nella dichiarazione; a ogni porta si associa un modo che distingue porte d'entrata (in), di uscita (out), bidirezionali (inout) (oltre che di modo buffer e linkage).

1.2.2 Architettura

La descrizione dell'architettura (che specifica “che cosa fa” l'entità definita con una dichiarazione) può essere in stile: struttuale, dataflow, comportamentale, o utilizzare una qualsiasi mescolanza di questi tre stili. Dichiarazione d'entità e architetture associate devono trovarsi nella stessa libreria. La sintassi di un'architettura è:

architecture nome_dell'architettura of nome_della_specifica_di_entità is

begin

parte

per dichiarazioni

Istruzioni

concorrenti

end nome_dell'architettura;

(Sono ammesse dichiarazioni, che si specificheranno in seguito.)

L’architettura fa parte del dominio concorrente: non vi si possono quindi dichiarare variabili al primo livello di astrazione: è questo il dominio dei segnali. La funzione dell'architettura viene descritta mediante istruzioni concorrenti eseguite in modo asincrono

Si vedano vari esempi di architetture per il full adder:

a. descrizione strutturale, cascata di due semisommatori con una porta OR che riceve in ingresso i riporti e genera il riporto in uscita COUT:

architecture DESC_STRUTT of FULL_ADDER is component HALF_ADDER port (A,B: in BIT; COUT,S: out BIT); end component component PORTA_USCITA port (A,B: in BIT; S: out BIT); end component signal N1, N2, N3: BIT;

begin -- connessione dei tre componenti:

16

INTRODUZIONE

C1:HALF_ADDER port map (A,B,N1,N2); C2:HALF_ADDER port map (N2,CIN,N3,S); C3:PORTA_USCITA port map (N1,N3,COUT); end DESC_STRUTT;

b. descrizione dataflow, traduce le equazioni:

V

S

COUT = (A . B) + (V . CIN)

cui corrisponde

architecture DESC_FLOW of FULL_ADDER is signal INTER:BIT; -- segnale intermedio begin INTER <=A xor B after 5 ns;

= A

B

= V CIN

S <= INTER xor CIN after 5 ns;

COUT <= (A and B) or (INTER and CIN) after 10 ns; end DESC_FLOW

c. descrizione comportamentale, viene creata a partire dalla tabella delle verità ed è:

architecture DESC_BEHAV of FULL_ADDER is begin

process variable I:INTEGER; constant TABELLA_S: BIT_VECTOR (0 to 3):=”0101” constant TABELLA_COUT: BIT_VECTOR (0 to 3):=”0011” begin

--ecco l'algoritmo

I:=0;

if A='1' then I:=1 end if ; if B='1' then I:=I+1 end if ; if CIN='1' then I:=I+1 end if ;

--assegnamento dei segnali

S <= TABELLA_S(I) after 10 ns;

COUT <= TABELLA_COUT(I) after 15 ns;

-- controllo degli eventi su A, B, CIN:

wait on A,B,CIN; end process

end DESC_BEHAV;

d. descrizione mista, che usa uno stile strutturale per la parte semisommatore e uno

comportamentale (dataflow) per la porta OR:

architecture DESC_MISTA of FULL_ADDER is component HALF_ADDER port (A,B: in BIT; COUT, S: out BIT); end component; signal N1,N2,N3:BIT begin

C1:HALF_ADDER port map (A,B,N1,N2); C2:HALF_ADDER port map (N2,CIN,N3,S); C3:COUT<=N1 or N3; - qui si potrebbe inserire un ritardo

17

INTRODUZIONE

end DESC_MISTA;

1.2.3 Configurazione

Ogni volta che in una descrizione VHDL si utilizza un componente, è possibile configurarlo immediatamente, cioè specificare il modello di cui è l'istanza, i parametri (se è generico) e la corrispondenza fra le sue porte formali e quelle effettive. Può però convenire rinviare la configurazione per inserirla nell'unità di progetto chiamata configuration.

architettura strutturale configurazione arch. arch. comportam. strutt.
architettura
strutturale
configurazione
arch.
arch.
comportam.
strutt.
configurazione arch. arch. comportam. strutt. specifica di entità porta configuration nome_conf of

specifica di

entità porta
entità
porta

configuration nome_conf of nome_dichiarazione_entità is {parte dichiarazione (espressione use e specifica di attributi)} {parte configurazione } end {nome_della_configurazione};

Nella parte dichiarazione sono ammesse solo l’espressione use e le specifiche di attributi. La parte configurazione descrive la realizzazione fisica dei componenti entro i blocchi (il blocco è l’elemento base della gerarchia VHDL): si traduce mediante una serie di

end for; indentate, che designano il blocco in cui si trovano i

componenti che si vogliono configurare. La sintassi di ogni componente all'interno della

espressioni for

sequenza indentata di for è indicata in modo preciso e ha una sintassi del tipo:

for label_dell'istanza_di_componente:nome_del_componente use

entity nome_dell'entità

{parametri_genericità}

{corrispondenza_porte_formali/effettive}; end for;

Ci si riferisca ancora il sommatore completo: si configurano lo half adder mediante l'entità di architettura HALF_ADDER CN78975S (componente commerciale) e l'OR mediante l'entità OR_GATE CNS11122S, scrivendo:

use WORK_FULL_ADDER_ - - espressione che permette di “vedere” l’entitàFULL_ADDER nella libreria WORK

18

INTRODUZIONE

configuration PROVA1 of FULL_ADDER is for DESC_STRUTT for C1,C2:HALF_ADDER use entity WORK.HALF_ADDER(CN78975S); end for

for C3:PORTA_OR use entity WORK.PORTA_OR(CN11122S); end for end for end PROVA1

1.2.4 Lo stile di descrizione concorrente

Nel capitolo precedente, si è indicata una semplicissima descrizione strutturale, definita assegnando le corrispondenze delle porte dei vari componenti. Si vedranno ora i due possibili stili di descrizione del comportamento di un circuito - uno stile concorrente e uno sequenziale.

L’istruzione

elementare

può

essere

vista

come

corrispondente

all’assegnamento

di

segnale:

a<=b

che sta a indicare “a assume il valore di b”. Un assegnamento di segnale viene eseguito tutte le volte che la parte destra dell’assegnamento (in questo caso, il segnale b) cambia valore: il segnale b costituisce qui la sensitivity list dell’espressione (la lista che ne controlla l’esecuzione). Più in generale, ogni volta che un segnale nella sensitivity list di un assegnamento di segnale cambia valore, l’assegnamento viene eseguito. Se, come risultato dell’assegnamento, anche il segnale-obiettivo cambia valore, potremo dire che su tale segnale si è verificato un evento (che verrà debitamente programmato per la simulazione del circuito); altrimenti, non si presenta alcun evento ma viene comunque generata una transazione.

Assegnamenti come quelli visti ora sono di tipo concorrente: si tratta di espressioni eseguite ogni volta che si verifica un evento nella sensitivity list, indipendentemente dall’ordine in cui sono scritti - più espressioni controllate dallo stesso evento vengono eseguite simultaneamente dal simulatore VHDL. Si consideri ad esempio la descrizione di un multiplexer a quattro ingressi, MUX4, con due ingressi di selezione a,b, quattro ingressi dati i0…i3 e uscita q. La descrizione comportamentale in stile concorrente del multiplexer sarà la seguente (si dichiarano due packages standard che contengono strumenti di modellazione di uso generale, e che servono solo per poter simulare direttamente l’architettura):

use STD.std_logic.ALL; use STD.std_cmos.ALL; entity mux4 is

19

INTRODUZIONE

port (i0, i1, i2, i3, a, b: in t_wlogic; q: out t_wlogic);

end mux4;

architecture mux4 of mux4is signal sel: integer; begin with sel select

q<=i0 after 10 ns when 0; i1 after 10 ns when 1; i2 after 10 ns when 2; i1 after 10 ns when 3; FX after 10 ns when others; sel <= 0 when a =’0’ and b = ‘0’ else

1

when a =’1’ and b = ‘0’ else

2

when a =’0’ and b = ‘1’ else

3

when a =’1’ and b = ‘1’ else

4;

end mux4;

L’entità ha sei porte d’ingresso e una di uscita; quattro delle porte d’ingresso (da i0 a i3) rappresentano segnali che verranno assegnati all’uscita q: di volta in volta un solo segnale sarà assegnato a q, sulla base del valore degli altri due segnali d’ingresso a e b. Per realizzare la funzionalità del multiplexer usiamo un’istruzione di assegnamento condizionale di segnale e una di assegnamento selezionato di segnale.

L’assegnamento condizionale è quello che assegna il valore al segnale sel ed in esso si trova ripetutamente la parola-chiave when: le condizioni che si trovano in tale istruzione vengono eseguite una per volta, in sequenza, fino a quando se ne trova una vera - la prima istruzione per cui la verifica dà risultato positivo viene eseguita e assegna il valore al segnale di uscita (anche se vi fossero istruzioni successive per cui la verifica risulterebbe positiva, queste vengono ignorate).

L’assegnato selezionato (la prima istruzione dopo begin) sceglie fra diverse opzioni il valore corretto da assegnare al segnale d’uscita. A questo fine si valuta l’espressione dopo la parola chiave with (nel nostro caso, si valuta il segnale sel) e si crea l’assegnamento sulla base della verifica di tale valore. Si noti che in VHDL occorre garantire una verifica positiva per tutti i possibili valori dell’espressione che controlla la selezione (eventualmente, si inserisce la clausola others). Nel nostro caso, potrebbe solo verificarsi che a e b avessero valori sconosciuti, nel corso della simulazione: in corrispondenza si forza il valore sconosciuto (FX) anche sull’uscita. 1

Considerando il modello ora scritto nell’ottica di un normale linguaggio sequenziale,

1 Il “valore sconosciuto” normalmente indicato con u (unknown) viene usato nella simulazione logica per contraddistinguere valori di segnale definiti ma che non possono essere determinati in base all’informazione disponibile (si noti che il valore sconosciuto è profondamente diverso da una condizione di indifferenza).

20

INTRODUZIONE

dovremmo affermare che non può funzionare, dato che si usa il segnale sel prima di avergli assegnato un valore; entra qui in gioco il concetto di concorrenza. L’istruzione di assegnamento condizionato che dà valore a sel viene eseguita tutte le volte che a o b cambiano valore (a e b ne sono la sensitivity list): solo in tal caso il segnale sel viene a sua volta modificato. A sua volta, l’assegnamento selezionato viene eseguito solo quando sel cambia valore - sel è la sua sensitivity list. Si ha quindi la garanzia di corretta esecuzione indipendentemente dall’ordine in cui le istruzioni sono scritte.

1.2.5 Lo stile di descrizione sequenziale

Una descrizione sequenziale presuppone l’esecuzione seriale delle istruzioni,, come nei normali linguaggi di programmazione. Le sezioni sequenziali sono incluse in istruzioni process - l’istruzione process in sé è di tipo concorrente: può trovarsi all’interno di un’architettura e definisce regioni dell’architettura all’interno delle quali tutte le istruzioni sono sequenziali.

Un processo include:

una sezione dichiarativa, in cui si dichiarano costanti, variabili, sottoprogrammi etc.;

una sezione istruzioni che contiene solo istruzioni sequenziali (es., case ì, if then else, loop, etc.).

L’istruzione process può essere dotata di una sensitivity list esplicita, che definisce i segnali la cui variazione provoca l’esecuzione del processo; in alternativa, deve avere un’istruzione wait che ne controlla l’esecuzione. Si consideri un primo esempio molto semplice, cioè l’architettura behavioral di un NAND a due ingressi:

use std.std_logic all; use std.std_cmos all; entity nand2 is port (a,b: in t_wlogic; c: out t_wlogic);

end nand2;

architecture nand2 of nand2 is begin process (a,,b) variable temp :t_wlogic begin temp:=not (a and b); if (temp=’1’) then c<= temp after 6 ns; elsif (temp=’0’) then c<= temp after 5 ns;

else

c<= temp after 6ns; endif; end process; end nand2;

21

INTRODUZIONE

La parte dichiarativa dichiara la variabile locale temp; la parte istruzioni contiene due istruzioni sequenziali, e precisamente l’assegnamento di variabile (temp :=…) e l’istruzione if then else. La sensitivity list è esplicita e contiene due segnali - a e b - che coincidono con le due porte d’ingresso; le porte creano segnali che possono poi essere usati come segnali d’ingresso (dualmente si opera con le porte di uscita e con le porte inout).

Consideriamo ora come creare una descrizione sequenziale dello stesso multiplexer per cui si è fornita prima una descrizione concorrente; si vedrà dapprima una descrizione formalmente corretta ma che porterebbe a una simulazione errata, e poi una descrizione corretta.

Per ambedue le soluzioni la dichiarazione di entità è la stessa:

entity mux is port (i0, i1, i2, i3, a, b: in t_wlogic; q: out t_wlogic);

end mux;

Ls soluzione errata si presenta come segue:

architecture wrong of mux is signal muxval: integer; begin process (i0, i1, i2, i3, a, b) begin muxval <= 0; if (a=’1’) then muxval <= muxval+1; end if; if (b=’1’) then muxval <= muxval+2; end if; case muxval is when 0=> q<=i0 after 10 ns; when 1=> q<=i1 after 10 ns; when 2=> q<=i2 after 10 ns; when 3=> q<=i3 after 10 ns; when others => null; end case; end process; end wrong;

Si è inizializato a 0 il segnale interno muxval con la prima istruzione; le istruzioni successive modificano il valore di muxval a seconda della configurazione sugli ingressi a e b. L’istruzione case sceglie - sulla base del valore di muxval - l’ingresso dati da propagare all’uscita. L’errore di base deriva dal fatto che lo 0 dell’inizializzazione viene

22

INTRODUZIONE

considerato come il prossimo evento sul segnale muxval (non essendosi specificati ritardi, l’evento diventerà ativo al prossimo delta di simulazione): al momento di eseguire l’istruzione successiva, il valore usato per muxval sarà l’ultimo che vi è stato propagato - non quello dell’inizializzazione; muxval avrà quindi un valore “spazzatura” che cambierà solo dopo l’esecuzione di tutte le istruzioni sequenziali nel processo.

Una realizzazione corretta si genera definendo muxval non come un segnale (che chiede assegnamenti di segnale) ma come una variabile:

architecture better of mux is begin process (i0, i1, i2, i3, a, b) variable muxval: integer; begin

muxval:=0;

if (a=’1’) then

muxval:=muxval+1;

end if; if (b=’1’) then

muxval:=muxval+2;

end if; if (a=’1’) then case muxval is when 0=> q<=i0 after10 ns; when 1=> q<=i1 after10 ns; when 2=> q<=i2 after10 ns; when 3=> q<=i3 after10 ns; when others => null;

end case;

end process;

end better;

In questo caso, all’esecuzione della prima istruzione la variabile muxval riceve immediatamente il valore 0; dato che si tratta di una variabile, non si pone il problema di effettuare l’assegnamento solo dopo un opportuno ordinamento temporale. Il nuovo valore è immediatamente utilizzabile per le istruzioni successive, che provocano a loro volta assegnamenti immediati; al momento di eseguire l’istruzione case, muxval ha sicuramente assunto il valore corretto.

1.2.6 Riassunto

Lo stile di descrizione sequenziale si avvicina maggiormente allo stile di descrizione utilizzato nei linguaggi di programmazione. Ritornando all'esempio del GCD descritto in C, è quindi ora possibile darne la descrizione in VHDL e verificare che gli aspetti tipici della progettazione hardware, elencati nell'introduzione, siano esprimibili in VHDL.

23

INTRODUZIONE

Una descrizione comportamentale del GCD, direttamente derivata dalla sua descrizione in linguaggio C, è la seguente:

Library IEEE; use IEEE.STD_LOGIC_1164.ALL

entity GCD is port (

CLOCK, RESET : in bit; XI,YI : in UNSIGNED (SIZE-1 downto 0); OU : out UNSIGNED (SIZE-1 downto 0)

);

end gcd;

architecture BEHAVIORAL of GCD is begin process variable X, Y,TEMP : UNSIGNED (SIZE-1 downto 0); begin wait until CLOCK = '1';

X := XI;

Y := YI;

while (X > 0) loop

if (X <= Y) then TEMP:=Y; Y := X; X:=TEMP; end if; X := X - Y; end loop; OU <= Y; end process; end behavioral;

Si può evidenziare come il VHDL proponga una soluzione per gli aspetti tipici della progettazione hardware.

Ingresso/uscita. La definzione dell'interfaccia del dispositivo viene espressa mediante le porte di ingresso e uscita riportate nella dichiarazione della ENTITY.

Ampiezza delle variabili. Utilizzando il tipo array di BIT è possibile definire esattamente l'ampiezza delle variabili e dei segnali. Inoltre, in questo esempio, l'utilizzo del tipo UNSIGNED permette di specificare che le operazioni avverranno tra numeri in modulo, dato che l'algoritmo del GCD non funziona per numeri negativi.

Operatori e operandi. Tutte gli operatori utilizzati sono descritti nel package STD_LOGIC_1164 e sono associati a componenti di libreria predefiniti che verranno utilizzati durante la sintesi.

Temporizzazione. Il segnale esplicito di CLOCK e la presenza dell'istruzione WAIT, permettono di sincronizzare il circuito prodotto con la frequenza di questo segnale.

Elementi di memoria. Gli elementi di memoria vengono associati alle variabili in base al modo in cui l'algoritmo è stato descritto. Per esempio, visto che alla variabile X

24

INTRODUZIONE

viene assegnato il suo valore precedente meno Y, nel circuito sintetizzato, alla variabile X deve essere associato un registro dell'ampiezza di SIZE bit.

Esecuzione delle istruzioni. Avendo descritto l’algoritmo del GCD con il costrutto processo, è stata scelta una rappresentazione sequenziale del comportamento che è particolarmente adatta alla descrizione di algoritmi. Si nota infatti che la descrizione del GCD in VHDL è molto simile alla descrizione in linguaggio C. Il VHDL ha permesso di utilizzare questo tipo di descrizione, ma permette anche di rappresentare, per esempio a livello RT, lo stesso circuito mediante componenti interagenti (registri, multiplexer, sommatori, etc.). É quindi possibile rappresentare in VHDL l’interazione tra componenti concorrenti, tipica dei circuiti elettronici, descrivendo al contempo il loro comportamento mediante sequenze di istruzioni.

Sincronizzazione tra i moduli. L’algoritmo del GCD è stato descritto a livello di astrazione comportamentale con un unico modulo. I programmi di sintesi possono tradurre questa rappresentazione in descrizioni a livello RT ed a livello logico composte da entità interagenti. Queste descrizioni possono essere rappresentate nuovamente in VHDL, in stile strutturale, e la sincronizzazione tra i moduli è garantita dal segnale di CLOCK esplicitamente modellato.

1.3 LA SIMULAZIONE LOGICA: CENNI INTRODUTTIVI

Una volta descritto in un linguaggio formale, un sistema digitale può essere simulato mediante un opportuno programma; come si è già accennato, la simulazionecostituisce la forma più diffusa di verifica del progetto, sia essa in termini logici o coinvolga anche gli aspetti temporali. La simulazione di un sistema digitale può essere ricondotta allo schema generale di figura 1.5:

Stimoli e controllo

allo schema generale di figura 1.5: Stimoli e controllo Programma di simulazione Risultati Modello interno Figura

Programma di

simulazione

di figura 1.5: Stimoli e controllo Programma di simulazione Risultati Modello interno Figura 1.5. Schema generale

Risultati

Modello interno
Modello interno

Figura 1.5. Schema generale della simulazione.

La simulazione spesso sostituisce la realizzazione di un prototipo hardware del circuito

25

INTRODUZIONE

(prototipo che tradizionalmente veniva realizzato nel corso del progetto) mediante un prototipo software, più facilmente realizzabile e modificabile; Nel caso di un circuito integrato, la tradizionale prototipazione - che, per motivi di costo, è inevitabilmente effettuata con “logica sparsa” o comunque con tecnologie differenti da quella finale - fornisce in realtà informazioni meno precise di quelle che si possono ottenere mediante una simulazione, a patto che il modello interno dei componenti (in particolare, per quanto riguarda ritardi, comportamento elettrico ecc.) sia sufficientemente fedele.

La verifica di un progetto basata su simulazione consente inoltre di:

controllare condizioni di errore;

variare i ritardi in modo da controllare condizioni “di caso pessimo”;

verificare i valori attesi secondo le specifiche dell'utente;

inizializzare il circuito simulato in uno stato arbitrario;

controllare in modo preciso la temporizzazione di eventi asincroni (es.: interrupt);

fornire un ambiente per la successiva verifica dei guasti (testing) del circuito simulato.

Vale la pena di notare che non tutte queste operazioni potrebbero, in genere, essere compiute sul sistema fisico (o comunque non potrebbero essere compiute in modo economicamente accettabile); quindi la simulazione è indispensabile per identificare il comportamento del sistema anche in tali situazioni.

Quando si voglia operare una verifica del progetto basata su simulazione, si presentano tre problemi correlati:

come generare gli stimoli d'ingresso;

come verificare che i risultati ottenuti siano corretti;

come garantire (o in quale misura) che gli stimoli applicati forniscano una verifica “completa”.

Gli stimoli d'ingresso sono abitualmente organizzati in una sequenza di casi di prova (test), ognuno dei quali deve verificare un certo aspetto del comportamento del modello. I risultati sono considerati corretti quando corrispondono a quelli previsti nelle specifiche. È importante rilevare la differenza fra generazione dei test per la verifica del progetto (lo scopo è l'identificazione degli errori di progetto) e generazione dei test per l'identificazione dei guasti fisici in un sistema prodotto (lo scopo è verificare l’integrità fisica del prodotto). Molto spesso, in ambedue i casi si opera a livello logico; infatti, la maggior parte dei guasti fisici possono essere modellati mediante guasti logici, il cui effetto sul comportamento del sistema è ben definito. Si può basare il test per un certo

26

INTRODUZIONE

guasto sulla differenza di comportamento fra il sistema in cui il guasto è presente e il sistema sano.

Mentre in genere i modelli di guasto logico rendono i guasti numerabili, e la qualità del test (cioè dell’insieme di stimoli adottati per il collaudo) può essere valutata come rapporto fra numero di guasti rilevati dal test e numero totale di guasti nel modello (copertura di guasto), al contrario lo spazio degli errori di progetto non è ben definito, e l'insieme degli errori di progetto non è numerabile: è quindi impossibile sviluppare algoritmi di generazione dei test per la verifica del progetto o definire misure rigorose della qualità per i test stessi. La verifica del progetto mediante simulazione ha gravi limiti. la produzione degli stimoli d'ingresso è una procedura euristica, basata sull'intuito e l'esperienza del progettista; se un sistema “passa” la verifica, si è dimostrato solo che il progetto è corretto rispetto ai casi di test applicati - cioè si dimostra una correttezza parziale (e non è in genere possibile dimostrare la completezza del test). Nonostante questi limiti, la simulazione è uno strumento molto utile ed efficace per il collaudo di un progetto (di fatto, l'unico di uso comune oggi).

1.4 LA PROGETTAZIONE AUTOMATICA: IL PROGRAMMA SIS

La teoria della progettazione digitale descritta nei prossimi capitoli sarà esemplificata,

quando possibile, con l'ausilio del programma SIS di Berkeley. La scelta di questo programma è dovuta in primo luogo alla possibilità di usarlo in maniera libera su diverse

architetture hardware/software (unix, windows, ecc). Inoltre, pur essendo dotato di una

interfaccia utente molto rudimentale, permette di eseguire tutte le fasi di ottimizzazione logica per circuiti combinatori, sequenziali sincroni e asincroni. Programmi commerciali

di sintesi automatica risultano indubbiamente più semplici da usare, ma nascondono

all'utente la maggior parte delle operazioni di sintesi effettuate. Il capitolo 8 riprenderà

comunque l'utilizzo del VHDL, brevemente introdotto in questo capitolo, facendo riferimento ad un qualsiasi compilatore/simulatore commerciale di questo linguaggio.

Il programma di progettazione SIS si basa sulla descrizione testuale di componenti

(model) su cui si possono eseguire operazioni di ottimizzazione. Il concetto di

componente è equivalente a quello di design-entity del VHDL.

La

descrizione di un modello, come descritto nel seguito, può essere gerarchica e si basa

sul

formato .blif (Berkeley Logic Interchange Format). Ogni componente è identificato da

nome, l'elenco degli ingressi e delle uscite e da una rete di nodi (network) che descrive la relazione tra gli ingressi e le uscite.

un

Riferendosi al VHDL, si può dire che il formato .blif permette di dare descrizioni di tipo

27

INTRODUZIONE

data-flow o strutturali, ma non algoritmiche.

Per esempio, si riprenda l'entity del full adder descritta in VHDL nel seguente modo:

entity SOMMATORE is

port (

A,B,CIN; in BIT; O,COU: out BIT);

end SOMMATORE;

La sua descrizione in formato .blif è la seguente:

.model SOMMATORE .inputs A B CIN .outputs O COUT .end

Si noti che, tutte le parole chiave del formato .blif iniziano con un punto, non c'è distinzione tra lettere maiuscole e minuscole, il modello si conclude con la parola chiave .end e nessuna network è associta a questo modello. Ossia, questo modello non fornisce una corrispondenza tra ingressi ed uscite che deve essere fornita mediante la tabella delle verità del full adder. Infine, tutti i segnali in formato .blif sono di tipo BIT.

Se si crea il file di testo sommatore.blif con la descrizione del modello, è possibile utilizzare SIS per eseguire i comandi di base (in grassetto) 2 :

UC Berkeley, SIS 1.3 (compiled 31-Dec-98 at 11:17 AM) sis> read_blif sommatore.blif

Il modello del sommatore viene caricato in memoria.

sis> print_stats

 

SOMMATORE

pi= 3

po= 2

nodes=

2

latches= 0

lits(sop)=

0

Il modello in memoria si chiama SOMMATORE, ha 3 bit di ingresso (pi), 2 bit di uscita (po), 0 elementi di memoria (latches) e la rete associata è composta da 2 nodi (nodes) a cui non sono associate equazioni poiché il numero di letterali (lits), calcolato in somma di prodotti (sop), è zero.

sis> write_blif .model SOMMATORE .inputs A B CIN .outputs O COUT .names O .names COUT .end

La lettura del modello ha quindi fatto sì che SIS creasse una network in cui le uscite sono poste a zero come si vede stampando sullo schermo il modello corrente.

sis> quit

2 è possibile avere la descrizione di ogni comando eseguendo help nome_comando.

28

INTRODUZIONE

In questo modo si abbandona il programma.

Le uscite sono poste a 0 perché vengono descritte dal comando .names mediante il loro ON-SET, ossia mediantel'elenco dei mintermini per cui la funzione vale 1. Nel caso precedente questo elenco è nullo quindi, l'ON-SET è nullo, quindi la funzione vale sempre 0.

Associamo invece adesso una rete al modello del full adder. è sufficiente descrivere i valori che i segnali O e COUT devono assumere in funzione degli ingressi A, B e CIN.

Riprendiamo, per esempio, la rappresentazione data-flow del full adder:

V = A B

O = V CIN

COUT = (A B) + (V CIN)

Il file sommatore.blif che descrive queste equazioni diventa il seguente:

.model SOMMATORE .inputs A B CIN .outputs O COUT

# calcolo valore V .names A B V

10

01

calcolo valore O .names V CIN O

#

1

1

10

01

calcolo carry out

.names A B V CIN COUT 11-- 1 --11 1 .end

#

1

1

Il caricamento in SIS di questo nuovo file e la stampa delle statistiche producono ora:

sis> print_stats

 

SOMMATORE

pi= 3

po= 2

nodes=

3

latches= 0

lits(sop)=

12

Per verificare la correttezza del modello descritto è anche possibile simularlo assegnando in successione dei valori agli ingressi con il comando simulate. SIS calcolerà i valori delle uscite in funzione degli ingressi utilizzando la descrizione della rete. La simulazione del sommatore è la seguente:

29

INTRODUZIONE

sis> simulate 0 0 0

sis> simulate 0 0 1

Network simulation:

Network simulation:

Outputs: 0 0 sis> simulate 0 1 0 Network simulation:

Outputs: 1 0 sis> simulate 0 1 1 Network simulation:

Outputs: 1 0 sis> simulate 1 0 0 Network simulation:

Outputs: 0 1 sis> simulate 1 0 1 Network simulation:

Outputs: 1 0 sis> simulate 1 1 0 Network simulation:

Outputs: 0 1 sis> simulate 1 1 1 Network simulation:

Outputs: 0 1

Outputs: 1 1

Oltre ad uno stile data-flow la rete in formato .blif può descrivere l'interconnessione di componenti. Per esempio il sommatore a 2 bit può essere descritto collegando opportunamente due full adder. Il file sommatore2.blif è il seguente:

.model SOMMATORE2 .inputs A1 A0 B1 B0 CIN .outputs O1 O0 COUT

.subckt SOMMATORE A=A0 B=B0 CIN=CIN \ O=O0 COUT=C0

.subckt SOMMATORE A=A1 B=B1 CIN=C0 \ O=O1 COUT=COUT .end

.search sommatore.blif

Questo collegamento avviene mediante la parola chiave:

.subckt nome_modello nome_formale=nome_attuale nome_formale=nome_attuale …

Il nome formale corrisponde al nome di un ingresso (o una uscita) del modello utilizzato, mentre il nome attuale è il nome di un segnale del modello del data-path. Attraverso l'assegnamento di nomi attuali a nomi formali si ottiene l'interconnessione di più modelli. Notare che durante il caricamento di un modello col comando read_blif, SIS richiede di avere a disposizione tutti i modelli utilizzati. Nel caso sommatore2 è quindi necessario includere nel file sommatore2.blif la direttive .search che permette a SIS di trovare il modello del sommatore nel file di libreria sommatore.blif 3 .

SIS permette di descrivere circuiti combinatori e sequenziali (sincroni e asincroni) e di effettuare la loro sintesi. Nei prossimo capitoli verranno proposti degli esempi di uso.

3 I file di libreria sono ricercati da SIS nelle directory elencate nella variabile OPEN_PATH il cui valore può essere modificato col il comando set interno a SIS.

30