Abbiamo visto come un modello VHDL possa essere suddiviso su piu' files. In particolare abbiamo trattato il caso di alcuni modelli che utilizzavano funzioni definite in una libreria separata (vector.vhd). Tuttavia ci rimane ancora da vedere come definire un modello VHDL collegando dei componenti che a loro volta sono delle entita' definite in VHDL. Questo ci permettera' di costruire modelli con una stratificazione gerarchica dove entita' piu' complesse sono costruite collegando entita' piu semplici. Fissiamo ora l' attenzione su un particolare problema: vogliamo definire una architettura per una entita' che ha il seguente schema logico.
che
avra' come interfaccia
entity sr is port(s,r:bit; q,not_q:out bit); end sr;
Supponiamo ora di disporre di un modello per una porta NOR con ritardo e supponiamo di voler definire il modello per la rete di cui sopra. Vogliamo dunque costruire il modello del flip-flop collegando in qualche modo il modello di due porte NOR. Questo modello ha dunque dentro di se una piccola gerarchia nel senso che il modello del flip-flop SR usera' il modello della porta NOR:
entity nor_g is port(a:bit;b:bit; c:out bit);end nor_g; architecture nor_bh_rit of nor_g is begin c <= not(a or b) after 1ns; end;
Il modo piu' semplice di fare questo e' di scrivere in uno stesso file sia l' interfaccia e l' architettura di nor_g che quelle di sr. In questo caso la soluzione per l' architettura di sr potrebbe essere data dal frammento VHDL (si puo' simulare questa prima versione usando il file sr1.vhd):
architecture sr_st of sr is component this_nor_g port(a:bit;b:bit; c:out bit); end component; for nor1:this_nor_g use entity nor_g;for nor2:this_nor_g use entity nor_g; begin nor1:this_nor_g port map (r,not_q,q); nor2:this_nor_g port map (s,q,not_q); end sr_st;
Vediamo il significato della linea component this_nor_g port(a:bit;b:bit; c:out bit); end component;. Notiamo innanzi tutto che tale linea e' molto simile all' interfaccia: entity nor_g is port(a:bit;b:bit; c:out bit);end nor_g; per cui si consiglia di scriverla editando la dichiarazione di entity. Questa riga dice semplicemente che nell' architettura in questione si intende usare un componente che chiamiamo this_nor_g che ha quella tale interfaccia. Questa dichiarazione di intenti prende il nome di dichiarazione del componente (component_declaration). In generale dunque una dichiarazione di componenente ha la forma:
component_declaration ::= component identifier [ local_port_clause ] end component <;>
dove la local_port_clause e' la lista del nome dei segnali ciascuno con il suo tipo e modo (in, out, inout).
Alla dichiarazione di intenti deve seguire la finalizzazione della stessa. A questo punto diremo quali sono le istanze dei componenti che realmente vogliamo usare nella architettura. A questo punto per ogni istanza dobbiamo dire:
la riga: for nor1:this_nor_g use entity nor_g; su dice configurazione (configuration specitication) e va letta come: c'e' una istanza di this_nor_g che si chiama nor1 e per questa istanza useremo l' entita' nor_g. Poiche' possiamo usare piu' istanze di una stesso componente avremo una dichiarazione per ogni istanza. Supponiamo ora che la dichiarazione e l' architettura di nor_g venissero collocati in un file separato, ad esempio norlib.vhd e mettiamo in tale file anche un' altra possibile architetture per il nor
-- file norlib.vhd entity nor_g is port(a:bit;b:bit; c:out bit);end nor_g; architecture nor_bh_rit of nor_g is begin c <= not(a or b) after 1ns; end; architecture nor_bh of nor_g is begin c <= not(a or b); end;
A questo punto la configurazione diventa for nor1:this_nor_g use entity norlib.nor_g;. Visto che non si e' specificata l' architettura, il sistema scegliera' per l' entita' l' ultima architettura (si puo' simulare una versione sviluppata in questo modo usando il file sr2.vhd). Se si vuole indicare con precisione quale architettura prendere per l' entita' nor1, scegliendo l' architettura nor_bh_rit, scrivero': for nor1:this_nor_g use entity norlib.nor_g(nor_bh_rit); (si puo' simulare un'alta versione, abbastanza definitiva, che usa la seconda architettura usando il file sr3.vhd). La sintassi completa della clausola di configurazione e' poi data dalle seguenti produzioni:
configuration_specification ::= for component_specification use binding_indication <;> component_specification ::= instantiation_list <:> component_name binding_indication ::= entity_aspect [ generic_map_aspect ] [ port_map_aspect] instantiation_list ::= name_list | others | all entity-aspect : entity entity_name [ <(> architecture_name <)> ]
Come si nota sono possibili anche le forme: for all:this_nor_g use entity .... e for others:this_nor_g use entity .... che si leggono rispettivamente: per tutte le istanze di tipo this_nor_g si usi l'entita' .... e per tutte le altre istanze di tipo this_nor_g si usi l'entita' .... . Veniamo ora ad alcune considerazioni pratiche. Il meccanismo VHDL della configurazione e' molto flessibile e si presta ad essere usato in vari modi. alcuni decisamente poco leggibili. Al fine di semplificarci la vita cercheremo sempre di seguire queste regole:
Con queste posizioni il flip-flop set-reset andrebbe scritto:
entity sr is port(s,r:bit; q,not_q:out bit); end sr; library norlib; use norlib.all; architecture sr_st of sr is component nor_g port(a:bit;b:bit; c:out bit); end component; for all:nor_g use entity norlib.nor_g(nor_bh_rit); begin nor1:nor_g port map (r,not_q,q); nor2:nor_g port map (s,q,not_q); end sr_st;
Si puo' provare a compilare e simulare quest' ultima versione che da' il paradigma gerarchico con cui costruiremo i modelli VHDL, a tal proposito si veda il file sr4.vhd).
Abbiamo visto che un componente
La dichiarazione di un componente dice quali componenti intendiamo usare nella nostra architettura, quali sono i loro nomi e le loro interfaccie. La configurazione definisce ulteriormente il componente indicando dove prendere il modello del componente. E' solo nell' istanziazione, pero', che si creano realmente i componenti. Avremo quindi una istanziazione per ogni componente nell' architettura. Nel caso del flip-flop set-reset abbiamo dichiarato e configurato un solo componente ma lo istanziamo due volte perche' abbiamo bisogno di due porte nor che chiameremo nor1 e nor2. La scrittura nor1:nor_g ... si legge: ...in questa architettura c'e' una istanza del componente nor_g che si chiama nor1, ovvero, nor1 e' una istanza del componente nor_g. Veniamo poi alla seconda parte della istruzione di istanziazione. ovvero a quel port map (r,not_q,q) e a questo proposito riconsideriamo lo schema del flip-flop:
Vediamo che nel nor1 entrano i segnali r e not_q ed esce il segnale q. Bisogna quindi indicare questa associazione tra gli ingressi e/o uscite del componente e i segnali indicati. Poiche' la dichiarazione del componente all' interno della architettura e': component nor_g port(a:bit;b:bit; c:out bit); end component;, l'associazione che abbiamo in mente sara' (a=>r,b=>not_q,c=>q). Questo tipo di associazione tecnicamente si dice port mapping (assegnazione delle porte). Come gia per gli aggregati potremo avere mappings dati in maniera posizionale come: port map (r,not_q,q); o associazioni date in maniera nominale come in: nor1:nor_g port map (a=>r,b=>not_q,c=>q);. Si noti che entrambi i mapping hanno lo stesso risultato e sono entrambi corretti. Vediamo pero' alcune raccomandazioni e chiarimenti.
La sintassi con cui istanziare un componente e con cui mappare le porte e' dunque la seguente:
component_instantiation_statement ::= instantiation_label <:> component_name [ port_map_aspect ] <;> port_map_aspect ::= port map <(> port_association_list <)>
Ci sono delle situazioni in cui ci si aspetta dei valori per dei segnali in ingresso e questi, per comodita' o per dimenticanza non vengono forniti. Questo puo' accadere anche quando si vuole simulare un' entita' e non si agganciano abbastanza files (.waw o .wav) agli ingressi. Questo non e' un errore a patto che siano stati fissati dei valori di default per quei segnali. I valori di default per gli ingressi di una entita' possono essere indicati quando si dichiara l' entita' aggiungendo una assegnazione dopo la dichiarazione secondo la sintassi dichiarazione:=espressione. L' espressione dovra' fornire un valore adatto per il segnale in questione (quindi per un bit_vector dovremo metterci un aggregato). Possiamo ad esempio provare a simulare il file sr5.vhd dove sono fissati dei valori di defualt per gli ingressi con la dichiarazione:
entity sr is port(s:bit:='1';r:bit:='0'; q,not_q:out bit); end sr;
e dove non vengono fornite forme d'onda in ingresso durante la simulazione. In generale e' bene non usare valori di default per le porte di una entita' perche' mascherano gli errori. Infatti se sono previsti dei valori di defaults e per errore forniamo meno segnali del dovuto, verranno applicati i segnali di defaults e non ci accorgeremo del nostro errore.
Per chiarezza e' spesso utile, se non indispensabile, dichiarare dei segnali aggiuntivi che non sono ne quelli in ingresso ne quelli in uscita. Non e' poi scritto da nessuna parte che i segnali dichiarati in VHDL debbano essere tutti riferibili a dei fili presenti nel disegno. Per esempio era possibile riordinare l' architettura del flip-flop nella maniera seguente.
entity srx is port(s,r:bit; q,not_q:out bit); end srx; library norlib; use norlib.all; architecture srx_st of srx is component nor_g port(a:bit;b:bit; c:out bit); end component; for all:nor_g use entity norlib.nor_g(nor_bh_rit); signal int_not_q,int_q:bit; begin nor1:nor_g port map (r,int_not_q,int_q); nor2:nor_g port map (s,int_q,int_not_q); q<=int_q; not_q<=not int_q; end srx_st;
Questa variazione apparentemente innocua in realta' mostra che architetture apparentemente simili possono avere un comportamento diverso. Si simuli per esempio sr4.vhd e srx.vhd e si constati come l' uscita not_q sia diversa nei due casi. Per riallineare le due entita' bisognera' modificare l' architettura srx_st e scrivere o not_q<=not int_q after 1ns o for all:nor_g use entity norlib.nor_g (e la ''o'' di prima e' esclusiva).
Qui di seguito sono riportati tre esercizi che richiedono la codifica di reti combinatorie usando le porte della libreria sgates.vhd che sono tutte definite con un ritardo standard di 5ns. Come abbiamo gia' visto questo non e' il modo piu' furbo di procedere se vogliamo ottenere un qualsiasi modello di una rete combinatoria. Infatti per ogni uscita U di una rete combinatoria esiste sempre una espressione (Exp) fatta di AND, OR, NOT, XOR, NAND e NOR che combina gli ingressi e ci da' questa uscita. Come abbiamo gia' visto, possiamo ottenere un modello VHDL semplicemente scrivendo (U<=Exp). Da questo punto di vista i modelli riportati qui di seguito sono inutilmente complessi. Tuttavia questi modelli forniscono l' esatto comportamento della rete quando questa venga realizzata con porte logiche fisiche. Quindi questo approccio puo' essere utilizzato per verificare la presenza di alee di commutazione. Per la maggior parte degli scopi pratici questo tipo di analisi non e' necessario e quanto qui riportato va preso solo come esercizio sui port map. In generale, nella costruzione di un modello VHDL e' sempre meglio usare una espressione ed evitare di usare reti di entita' AND, OR, NOT.
Si costruisca una entita' test_bench con interfaccia:
entity test_bench is port(a,b:bit;u:out bit); end test_bench;
ed utilizzando le porte logiche definite nel file sgates.vhd. si definisca tale entita' come quella che realizza l' espressione u<=(a and b) or (a and not b) [soluzione].
Si simuli l' architettura cosi ottenuta e si osservi che non e' sempre e' verificata l' identita' a=ab+a(not b). Questo e' un esempio di alea di commutazione.
Si costruisca, nel file ha_st.vhd una entita' ha con la solita interfaccia:
entity ha is port (a,b: in bit; s,r: out bit); end ha;
che sia un modello di un semisommatore. Questa volta pero' si utilizzino le porte logiche definite nel file sgates.vhd [soluzione e simulazione]
Dopo aver risolto l' esercizio precedente si utilizzi la rete cosi' ottenuta per realizzare una rete che incrementa di una unita' un numero di 7 bit secondo lo schema
[soluzione e simulazione]