Reti sequenziali complesse
Registri operativi, handshake, sintesi in parte operativa e parte controllo
Con Moore, Mealy e Mealy ritardato si descrivono reti con pochi stati. Appena il problema contiene un numero (contare fino a 200, sommare due byte, aspettare n clock) gli stati esplodono: servono registri operativi che tengano i dati, accanto al registro di stato che tiene il «punto del programma». Questo modulo insegna a descrivere queste reti in Verilog, a farle dialogare in modo sicuro con altre reti tramite gli handshake, e a sintetizzarle con un procedimento quasi meccanico: la scomposizione in parte operativa e parte controllo.
Gli esempi si possono simulare: il simulatore esegue la descrizione Verilog clock per clock e disegna il diagramma di temporizzazione. Il codice si può modificare, e il diagramma si aggiorna.
I registri operativi
Perché il modello di Mealy ritardato non basta
Vogliamo una rete che conti, modulo 16, quante volte riceve la sequenza 00, 01, 10 sui due ingressi x1x0, e presenti il conteggio su 4 bit. Come rete di Mealy ritardato servono 3 stati per riconoscere la sequenza e 16 valori di conteggio: circa 3 · 16 = 48 stati, un registro STAR da 6 bit e un casex con 48 etichette. Ingestibile, e il motivo è chiaro: lo stato deve ricordare due cose indipendenti, a che punto è il riconoscimento e quante sequenze ha contato.
La soluzione è separarle. Lo stato «di controllo» resta in STAR (3 stati); il conteggio va in un registro operativo COUNT da 4 bit, che si incrementa quando serve. Si arriva a un modello più generale:
- un registro di stato STAR, che fa quello che faceva in ogni RSS;
- quanti registri operativi si vuole, della dimensione che si vuole;
- il contenuto dei registri operativi, oltre a quello di STAR, entra nelle reti combinatorie che preparano i nuovi valori: COUNT può dipendere da COUNT stesso. È questo il salto di qualità rispetto al Mealy ritardato;
- le uscite sono tutte sostenute da registri operativi (anche se non tutti i registri operativi sostengono un'uscita).
Il linguaggio di trasferimento tra registri
Le descrizioni si scrivono con un blocco always che contiene un casex(STAR): ogni ramo è uno statement, cioè quello che la rete fa in quello stato. Uno statement contiene:
- zero o più μ-istruzioni: assegnamenti ai registri operativi, come
COUNT <= COUNT+1;; - un μ-salto: l'assegnamento a STAR. Può essere incondizionato (
STAR <= S2;), a due vie (STAR <= (x==1) ? S1 : S0;) o a più vie, con operatori condizionali in cascata.
Tre regole valgono sempre, e da sole evitano metà degli errori d'esame:
Al fronte di salita del clock tutti i registri si aggiornano contemporaneamente. L'ordine in cui gli assegnamenti sono scritti nello statement non conta.
A destra di <= un registro vale quello che conteneva prima del fronte. COUNT <= COUNT-1; STAR <= (COUNT==1) ? … testa il vecchio COUNT, non quello decrementato.
Un registro operativo non assegnato in uno statement conserva il valore (REG <= REG). STAR invece non si omette mai: sarebbe sottinteso «statement successivo», illeggibile e fonte di errori.
Ecco il contatore di sequenze. Nella prima versione il riconoscimento produce un bit in OUTR (come in un Mealy ritardato) e COUNT gli somma OUTR; nella seconda OUTR sparisce e COUNT si incrementa direttamente in S2. Confronta i due diagrammi: con OUTR il conteggio arriva un clock dopo.
Bastano 3 stati invece di 48. La compattezza nasce dal fatto che COUNT compare a destra di un assegnamento: nel modello di Mealy ritardato un registro di uscita non poteva rientrare nelle reti combinatorie. Dal punto di vista della temporizzazione la rete obbedisce alle stesse disuguaglianze del Mealy ritardato.
Statement più compatti con le function
Variazione: contare alternativamente le sequenze 00, 01, 10 e 11, 01, 10. Le due sequenze differiscono solo per il primo passo, e quale aspettarsi dipende dalla parità del conteggio, cioè da COUNT[0]. Una piccola rete combinatoria match dice se l'ingresso è il primo passo giusto:
| COUNT[0] | x1 | x0 | match |
|---|---|---|---|
| 0 | 0 | 0 | 1 |
| 1 | 1 | 1 | 1 |
| tutti gli altri | 0 | ||
In Verilog la tabella diventa una function, e la descrizione resta di tre stati. Le function servono proprio a questo: dare un nome a una tabella di verità o a un calcolo combinatorio, non a scrivere algoritmi.
Dialogare con reti asincrone: gli handshake
L'handshake /dav-rfd
Due reti sono mutuamente asincrone quando evolvono ciascuna per conto proprio, con clock indipendenti (magari di frequenza diversa). Se una, il produttore, deve passare dei dati all'altra, il consumatore, nascono tre problemi: come capisce il consumatore che è arrivato un dato nuovo (magari uguale al precedente)? Come sa il produttore che il consumatore l'ha preso? Che cosa succede se il produttore è più veloce?
Si risolvono una volta per tutte con due fili in più: /dav (data valid, attivo basso, dal produttore) e rfd (ready for data, attivo alto, dal consumatore). Al reset entrambi valgono 1: nessun dato, consumatore pronto. Poi le transizioni si alternano:
Le due regole che il consumatore non deve mai violare.
- Il dato si preleva prima di abbassare rfd, non dopo. Il fronte di discesa di rfd dice al produttore «puoi togliere il dato»: un registro che campiona l'ingresso quando rfd è già a 0 rischia di memorizzare spazzatura. È l'errore più frequente nei compiti.
- rfd si rialza solo dopo aver visto /dav tornare a 1. Se lo rialzasse prima, il produttore potrebbe non accorgersi mai che rfd è sceso, e le due reti resterebbero bloccate ad aspettarsi (deadlock), finché qualcuno non dà il reset.
Esempio: il formatore di impulsi
Un produttore invia ogni tanto un numero naturale su 8 bit; la rete da descrivere tiene l'uscita out a 1 per tanti clock quanto vale il numero (256 se vale 0). Prima di scrivere codice si decidono i registri: uno per ogni uscita (OUT, RFD), un contatore COUNT da 8 bit per i clock che mancano, e STAR. Il dimensionamento si fa a occhio e si rivede alla fine.
out a 1 per altrettanti clock (256 se il numero è 0). Ogni uscita è sostenuta da un registro; COUNT conta i clock che mancano.Poi le condizioni al reset: tutte le reti condividono il reset, quindi si può assumere /dav = 1; la nostra rete deve partire con out = 0 e rfd = 1. Infine il diagramma di massima, a parole:
In S0 si assegna COUNT <= numero a ogni clock: finché /dav vale 1 si campiona spazzatura, ma l'ultimo campionamento, quello fatto con /dav = 0, è corretto, e da lì si va in S1. Prova nel simulatore: il produttore manda 3, 1 e 2. Le righe blu sono pilotate dal produttore, che rispetta l'handshake e reagisce con il ritardo che imposti.
Quanti giri fa un ciclo? La regola k − j + 1
Perché il test in S1 è COUNT==1 e non COUNT==0? Lo si capisce solo con un diagramma di temporizzazione (il simulatore qui sopra lo disegna). Con numero = 1: si entra in S1 con COUNT = 1, il test sul valore vecchio è già vero, si resta in S1 un solo clock e OUT vale 1 per un clock. Con COUNT==0 si resterebbe due clock. La regola generale:
se in Sinit inizializzo il contatore a k, e in Stest lo decremento e esco quando vale j ≤ k, resto in Stest per k − j + 1 clock
Con numero = 0 il test fallisce, COUNT passa a 255 (modulo 28) e si fanno altri 255 giri: 256 clock in tutto, come chiede la specifica, senza scrivere niente di speciale. Quando si può scegliere, conviene inizializzare a n − 1 e testare a 0: per 8 giri, COUNT da 7 a 0 sta in 3 bit invece che in 4.
Da S1 direttamente a S0
Uno stato in più per chiudere l'handshake
Alcuni assegnamenti sono superflui (in S2 RFD è già 0, in S0 OUT è già 0) ma lasciarli rende la descrizione leggibile: si vede subito che cosa vale ogni uscita in ogni stato. Si potrebbe anche tenere RFD a 1 in S1 e abbassarlo solo in S2: è lecito, la rete è solo un po' più lenta a liberare il produttore. Attenzione invece se in S1 si vuole usare numero (ad esempio per gestire il caso 0 a parte): allora RFD deve restare a 1, altrimenti il produttore potrebbe aver già tolto il dato.
L'handshake soc/eoc
Un altro protocollo, tipico dei convertitori analogico/digitali, è soc/eoc (start of conversion, end of conversion). Qui la prima mossa la fa il consumatore, che chiede un dato; a riposo soc = 0, eoc = 1 e il dato presente è valido.
Il formatore di impulsi diventa: in S0 alza SOC e aspetta che eoc scenda; in S1 riabbassa SOC e campiona numero finché eoc non torna a 1 (l'ultimo campionamento, con eoc = 1, è quello buono); in S2 genera l'impulso.
La sintesi: parte operativa e parte controllo
L'idea
Una descrizione dice che cosa fa la rete, non come è fatta. Per arrivare alle porte logiche esiste un procedimento semiautomatico, la scomposizione in parte operativa (PO) e parte controllo (PC). Non cerca la sintesi ottima (se servisse, la farebbe un programma): cerca una sintesi che si ottiene meccanicamente dalla descrizione e che quindi si comporta come la descrizione.
- La PO contiene i registri operativi con le loro reti combinatorie. Riceve le variabili di comando b e produce le variabili di condizionamento c, che possono dipendere direttamente dagli ingressi della rete: è una rete di Mealy.
- La PC contiene STAR. Le sue uscite b dipendono solo da STAR: è una rete di Moore. Per questo l'anello PO → PC → PO non crea problemi: contiene sempre un registro.
Passo 1: ogni registro operativo è un registro multifunzionale
Il trucco da cui discende tutto: si prende un registro operativo alla volta e si elencano le μ-operazioni diverse che subisce nei vari stati. Per il formatore:
| registro | S0 | S1 | S2 | vie del multiplexer |
|---|---|---|---|---|
| RFD | RFD <= 1 | RFD <= 0 | RFD <= 0 | 2 |
| OUT | OUT <= 0 | OUT <= 1 | OUT <= 0 | 2 |
| COUNT | COUNT <= numero | COUNT <= COUNT-1 | conserva | 3 |
Ogni registro diventa un registro multifunzionale: un multiplexer davanti, una via per ogni μ-operazione, e le variabili di comando a scegliere la via. COUNT ha tre vie, quindi servono due variabili di comando:
numero, decrementarsi, conservare), quindi è un registro multifunzionale con un multiplexer a tre vie davanti. Le variabili di comando b1b0 scelgono la via; il confronto con 1 diventa la variabile di condizionamento c0.Le variabili di comando le genera la PC a partire da STAR. Ne servirebbero quattro (una per RFD, una per OUT, due per COUNT), ma si possono riusare: se scelgo b1b0 = 00 in S0, 11 in S1 e 01 in S2, allora OUT = b1, RFD = b0, e COUNT distingue le tre vie con la coppia. Dare la stessa variabile di comando a registri che ne richiedono di uguali è l'unica ottimizzazione che ci si concede: per il resto la sintesi deve corrispondere uno a uno alla descrizione.
Passo 2: le variabili di condizionamento
Si guarda ora STAR e si isolano le condizioni indipendenti che guidano i μ-salti. Nel formatore sono due, dav_==1 (usata sia in S0 sia in S2) e COUNT==1. A ciascuna si associa una variabile che vale 1 quando la condizione è vera; la rete che le calcola è la rete combinatoria di condizionamento:
- c1 =
dav_: un semplice filo (corto circuito); - c0 =
(COUNT==1): una AND a 8 ingressi, COUNT7 · … · COUNT1 · COUNT0. In Verilog si scrive con l'operatore condizionale, più leggibile di un'espressione algebrica.
La PC si riscrive usando solo le c: S0: STAR <= (c1==1) ? S0 : S1; e così via. Quello che si ottiene è la sintesi in Verilog: due moduli collegati, che si possono simulare e confrontare con la descrizione.
Si notino le dichiarazioni: la PO ha in ingresso le b e in uscita le c, la PC il contrario; le istanze collegano i fili per posizione, nell'ordine in cui il modulo elenca le porte (sbagliare l'ordine è un errore classico: il simulatore lo segnala quando le larghezze non tornano). La parte controllo, essendo una rete di Moore con pochi stati, si può poi sintetizzare con i metodi già noti: codifica degli stati, RCA e RCB.
Che cosa va consegnato all'esame. La sintesi PO/PC in Verilog è in corrispondenza biunivoca con un circuito: registri multifunzionali comandati dalle b, reti di condizionamento che producono le c, una parte controllo fatta di STAR e di una ROM. Per dimostrare di averlo capito si disegnano sempre almeno le reti combinatorie di condizionamento e si scrive il contenuto della ROM come tabella di verità; a volte si chiede di disegnare anche qualche registro della PO.
La parte controllo come ROM
Sintesi basata su μ-indirizzi
Per parti controllo con molti stati serve un metodo generale. Supponiamo che ogni μ-salto sia a due vie, STAR <= (c==1) ? Sa : Sb;, oppure incondizionato (che è un caso particolare: le due destinazioni coincidono). La codifica di uno stato si chiama μ-indirizzo; per ognuno si scrive una riga con:
- il μ-codice: il valore delle variabili di comando in quello stato;
- ceff: la codifica (in base 2, sul pedice) della variabile di condizionamento che guida il salto, «–» se il salto è incondizionato;
- il μ-indirizzo True, dove andare se la condizione vale 1;
- il μ-indirizzo False, dove andare se vale 0.
| μ-addr | μ-code b1b0 | ceff | μ-addr T | μ-addr F |
|---|---|---|---|---|
| 00 (S0) | 00 | 1 (c1) | 00 | 01 |
| 01 (S1) | 11 | 0 (c0) | 10 | 01 |
| 10 (S2) | 01 | 1 (c1) | 00 | 10 |
| 11 | –– | – | –– | –– |
Questa tabella è a tutti gli effetti una ROM di 4 celle da 7 bit, e ogni cella è una μ-istruzione: dice tutto ciò che la PC fa in quello stato. La PC diventa STAR più la ROM più due multiplexer:
Sintesi basata su μ-istruzioni
Niente vieta di scambiare di posto ROM e registro: si mette la ROM a monte e un registro STAR «molto grande» a valle, che contiene direttamente la μ-istruzione corrente. I due modelli si confrontano così:
| μ-address based | μ-instruction based | |
|---|---|---|
| STAR contiene | il μ-indirizzo | la μ-istruzione |
| dimensione di STAR | piccola (pochi bit) | grande (anche più di 100 bit) |
| in cascata nel periodo di clock | ROM e reti della PO | reti di condizionamento e ROM |
| clock | più lento | più veloce, perché la RC di condizionamento è di solito leggera |
Salti a più vie: il registro MJR
Un salto a N vie si può sempre spezzare in una catena di salti a due vie, uno stato per condizione. Ma ogni stato costa un clock, e in un processore il salto alla routine giusta dopo la decodifica di un'istruzione ha decine di alternative: perdere decine di clock per istruzione è inaccettabile, e prevedere 50 indirizzi in ogni cella della ROM è uno spreco. La soluzione è un registro operativo apposito, il Multiway Jump Register:
Salto a più vie
Con MJR: due stati per qualunque N
MJR è un registro operativo come gli altri (un multiplexer davanti, che con codifiche costanti si semplifica molto). Nella ROM si aggiunge un bit, il μ-tipo: 0 se il prossimo μ-indirizzo viene dai campi T/F, 1 se viene da MJR. Un esempio:
| μ-addr | bk (carica MJR = 0) | ceff | μ-addr T | μ-addr F | μ-tipo |
|---|---|---|---|---|---|
| S0 | 1 | 00 (x==0) | S0 | S1 | 0 |
| S1 | 1 | –– | S2 | S2 | 0 |
| S2 | 1 | 01 (A!=B) | S5 | S3 | 0 |
| S3 | 1 | 10 (gamma(A)==0) | S1 | S4 | 0 |
| S4 | 1 | –– | S5 | S5 | 0 |
| S5 | 0 | –– | S6 | S6 | 0 |
| S6 | 1 | –– | –– | –– | 1 |
Le condizioni indipendenti sono tre, quindi ceff sta su 2 bit; i salti incondizionati hanno ceff non specificato e destinazioni T e F uguali; solo in S6 il μ-tipo vale 1.
Sottoliste
MJR permette anche di scrivere sottoliste, l'equivalente dei sottoprogrammi: pezzi di μ-programma raggiungibili da punti diversi, al termine dei quali si torna allo stato successivo a quello di partenza.
STAR <= MJR. Con un solo MJR non si possono annidare due chiamate.Il parallelo con i sottoprogrammi si ferma a un livello: con un solo MJR una sottolista non può chiamarne un'altra (servirebbe una pila di MJR). E in generale: MJR si usa solo se è indispensabile, molto comodo o richiesto dal testo. Quasi tutti gli esercizi si risolvono con salti a due vie.
Esercizi d'esame svolti
Perimetro di un rettangolo
Testo. Descrivere la rete XXX che ciclicamente preleva un byte dal Produttore 1 e uno dal Produttore 2, li interpreta come base e altezza di un rettangolo (naturali in base 2) e invia il perimetro al Consumatore. Non si può fare alcuna ipotesi sulla velocità degli interlocutori.
Si procede per approssimazioni successive.
- Registri. Uno per ogni uscita: RFD1, RFD2, DAV_ (1 bit), PERIMETRO (10 bit); STAR, da dimensionare alla fine. Base e altezza si potrebbero salvare in registri, ma probabilmente non serve: una rete combinatoria
mia_retecalcola 2(b + h) direttamente dagli ingressi. - Reset. Gli handshake sono a riposo: /dav1 = /dav2 = 1, rfd = 1 in ingresso; in uscita rfd1 = rfd2 = 1 e /dav = 1. Il valore iniziale di PERIMETRO non conta: la sua validità la stabilisce /dav.
- S0. Tiene rfd1, rfd2, /dav a 1 e aspetta che entrambi i /dav siano a 0. Non ha senso aspettare prima uno e poi l'altro: sono indipendenti e servono tutti e due.
- S1. Abbassa RFD1 e RFD2 (dati presi) e DAV_ (risultato pronto), poi aspetta rfd = 0 dal consumatore. Ma non può tornare in S0 senza controllare che /dav1 e /dav2 siano tornati a 1, altrimenti violerebbe l'handshake con i produttori.
Due trappole. Primo: se PERIMETRO si assegnasse in S1 e si restasse in S1 ad aspettare, dalla seconda iterazione si calcolerebbe su ingressi non più garantiti (i produttori hanno visto rfd scendere). Si sposta l'assegnamento in S0: lì PERIMETRO «balla» finché i /dav non sono a 0, ma l'ultimo valore, quello calcolato quando si esce da S0, è giusto; e nessuno lo legge prima che /dav scenda. Secondo: così non si controlla mai che rfd sia tornato a 1. Si aggiunge la condizione all'uscita da S0: si parte quando {dav1_, dav2_, rfd} == 'B001.
Guardando la descrizione si scopre che RFD1, RFD2 e DAV_ valgono sempre lo stesso bit: basta un solo registro, HS. Ecco la descrizione finale e la sua sintesi PO/PC, che il simulatore fa girare con produttori e consumatore di velocità diverse:
Nella sintesi HS è un registro a due vie (1 in S0, 0 in S1) e PERIMETRO pure (carica in S0, conserva in S1): basta una variabile di comando b0, che vale 1 in S0. Le condizioni indipendenti sono due, quindi due variabili di condizionamento. La ROM della parte controllo:
| μ-addr | μ-code b0 | ceff | μ-addr T | μ-addr F |
|---|---|---|---|---|
| 0 (S0) | 1 | 0 (c0) | 1 (S1) | 0 (S0) |
| 1 (S1) | 0 | 1 (c1) | 0 (S0) | 1 (S1) |
Curiosità: questa parte controllo è proprio un flip-flop JK, con c0 = j, c1 = k e b0 = q.
Resta da specificare la rete mia_rete, che il testo chiede in dettaglio: un sommatore a 8 bit con il riporto uscente come nono bit dà b + h su 9 bit; moltiplicare per 2 è uno shift a sinistra, cioè un filo a massa aggiunto in coda. Nessun moltiplicatore: {(...), 1'B0}.
Prodotto con l'algoritmo di somma e shift
Testo. Stessi collegamenti, ma la rete calcola l'area (16 bit) con l'algoritmo di somma e shift: con P0 = 0, a ogni passo
Pi+1 = ⌊(yi · 28 · X + Pi) / 2⌋ per i = 0, …, 7, P8 = X · Y
Cambiano i registri: AREA (16 bit) contiene Pi; serve un contatore COUNT per gli 8 giri; e serve un bit diverso di Y a ogni giro. Qui c'è una tecnica da ricordare:
Quando serve un bit diverso a ogni clock, si usa un registro che trasla. Si carica il valore in un registro (ALTEZZA), si usa sempre il bit 0 e si trasla a destra a ogni clock: ALTEZZA <= {1'B0, ALTEZZA[7:1]};. Se servissero i bit dal più significativo, si userebbe il bit 7 e si traslerebbe a sinistra. Un registro non è un vettore: ALTEZZA[COUNT] non si scrive, perché non corrisponde a nessun circuito ragionevole.
Il resto segue la regola k − j + 1: per 8 giri si inizializza COUNT a 7 e si testa a 0, e COUNT sta in 3 bit. Due migliorie: abbassare RFD1, RFD2 solo in S2, così i tre registri di handshake tornano a essere uno solo (HS) e base si può usare direttamente dall'ingresso, che il produttore tiene fermo; e inizializzare COUNT al reset, perché quando si esce dal ciclo vale di nuovo 7. La rete combinatoria del passo, infine, non ha bisogno di un sommatore a 17 bit: gli 8 bit bassi del secondo addendo sono zeri, quindi ne basta uno a 9.
Nella sintesi HS, ALTEZZA e AREA sono registri a tre vie (una per stato), COUNT a due (decrementa in S1, conserva altrove): con b1b0 = 00, 01, 10 in S0, S1, S2 basta b0 per COUNT. Le condizioni indipendenti sono tre:
| μ-addr | μ-code b1b0 | ceff | μ-addr T | μ-addr F |
|---|---|---|---|---|
| 00 (S0) | 00 | 00 | 01 | 00 |
| 01 (S1) | 01 | 01 | 10 | 01 |
| 10 (S2) | 10 | 10 | 00 | 10 |
Un'onda triangolare per un convertitore D/A
Testo. La rete riceve con un handshake /dav-rfd un numero N ≠ 0 su 7 bit e manda a un convertitore D/A, un byte per clock, i valori di un'onda triangolare di ampiezza N: 0, 1, …, N, N − 1, …, −N, …, 0. Il convertitore usa la legge binaria bipolare, cioè interpreta i byte come interi rappresentati in traslazione: K = k + 27. Lo zero è quindi 128 ('H80), e nel caso peggiore si inviano i valori da 1 a 255.
Tre cicli: a salire da 128 a 128 + N, a scendere fino a 128 − N, a risalire fino a 128, tenendo i valori estremi per un solo clock. Per non ricalcolare gli estremi a ogni confronto si salvano in due registri, MAX e MIN. Il test va scritto sul valore vecchio, perché OUT cambia ancora al fronte: se lo si incrementa, la condizione è OUT==MAX-1; se lo si decrementa, OUT==MIN+1. Il simulatore disegna OUT come tensione:
Si noti che una nuova onda non parte subito dopo la precedente: in S4 si aspetta che il produttore abbia rialzato /dav, e nessuna specifica chiede di fare in fretta. Variante: se RFD resta a 1 fino a S3, il produttore tiene fermo ampiezza e si possono eliminare MAX e MIN, scrivendo i test come OUT==zero+ampiezza-1 e OUT==zero-ampiezza+1.
Metodo e buone norme
Prima la descrizione, poi la sintesi
Il progetto di qualunque rete logica ha due passi. La descrizione specifica in modo formale, e quindi verificabile, che cosa fa la rete; la sintesi la realizza seguendo un modello, e il procedimento garantisce che la rete sintetizzata si comporti come la descrizione (con il clock dimensionato bene e gli ingressi stabili attorno ai fronti). Saltare la descrizione è assurdo: da una sintesi sequenziale è praticamente impossibile risalire al comportamento, quindi non la si può verificare. E fermarsi alla descrizione dà l'impressione di non saper realizzare quello che si è pensato.
Verificare una descrizione combinatoria è una verifica statica: si controlla la tabella. Verificare una descrizione sequenziale è una verifica dinamica, come il test del software: si simula l'evoluzione a partire dal reset con un diagramma di temporizzazione. Non è mai esaustiva, ma non farla è sbagliato quanto non testare un programma.
| tipo di rete | descrizione | sintesi |
|---|---|---|
| combinatoria semplice | tabella di verità, mappa di Karnaugh | espressioni algebriche (SP, PS, NAND, NOR) |
| combinatoria per l'aritmetica | a parole | schema a blocchi (sommatori, multiplexer…), con approccio euristico |
| sequenziale asincrona, Moore, Mealy | tabella o grafo di flusso | modello scelto (FF-D, JK) + espressioni delle reti combinatorie |
| sequenziale complessa | Verilog (trasferimento tra registri) | Verilog PO/PC + reti di condizionamento disegnate + ROM come tabella |
Il Verilog si usa sia per descrivere sia per sintetizzare: è il formalismo, non il contenuto. assign z = (x1 & x0) | (x2 & ~x1); è una sintesi, perché si mappa direttamente su porte; un casex che elenca le uscite per ogni ingresso è una descrizione.
Buone norme per le descrizioni d'esame
Prima di scrivere il Verilog.
- Disegna i collegamenti con gli interlocutori: quali handshake, quanti bit per ogni dato (la somma di due numeri a 8 bit sta su 9 bit, il doppio su 10).
- Elenca i registri con un dimensionamento di massima: un registro per ogni uscita, un contatore per ogni attesa, un registro per ogni dato da conservare. Ritorna sul dimensionamento alla fine.
- Scrivi le condizioni al reset: gli handshake partono a riposo, e vanno inizializzati tutti i registri che sostengono uscite, più STAR.
- Disegna un diagramma a stati di massima, a parole, e controlla gli handshake su un diagramma di temporizzazione.
Mentre lo scrivi.
- Ogni statement termina con l'assegnamento a STAR, anche se incondizionato.
- Campiona i dati in ingresso prima di abbassare rfd, e rialza rfd solo dopo aver visto /dav = 1.
- Se più handshake sono indipendenti, aspettali nello stesso stato.
- Nei cicli di decremento e test, conta i giri con k − j + 1 e ricordati che il test usa il valore di prima del fronte.
- Per un bit diverso a ogni clock, un registro che trasla; mai
REG[COUNT]. - Le function solo per reti combinatorie (tabelle di verità o calcoli), con un nome che dica che cosa fanno.
- Se due registri contengono sempre lo stesso valore, fondili (HS). Usa MJR solo se serve davvero.
Nella sintesi. Corrispondenza uno a uno con la descrizione; un registro multifunzionale per ogni registro operativo; una variabile di condizionamento per ogni condizione indipendente; disegna le reti di condizionamento e scrivi la ROM.