Prova Finale Reti Logiche

Codifica Working Zone in VHDL
Politecnico di Milano
A.A. 2019-20
Giorgio Pristia

L'obiettivo del progetto è di sviluppare un componente hardware in VHDL che implementi la codifica Working Zone degli indirizzi forniti. Il componente si interfaccia con una memoria da cui legge i dati in ingresso e in cui scrive il risultato della conversione.

Indice

Funziona​men​to

La funzione del componente è di codificare un indirizzo letto da una memoria secondo la codifica Working Zone, e di srivere in memoria il risultato ottenuto.

L'interfaccia del componente, da specifica, ha i seguenti ingressi
i_clk segnale di clock
i_rst segnale di reset sincrono, porta il componente nello stato iniziale
i_start segnale di start, tenuto a 1 durante l'esecuzione
i_data[8] bus dati in lettura dalla memoria
e le seguenti uscite
o_done segnale di done, portato a 1 al completamento della codifica
o_en segnale di enable della memoria
o_we sengale di write enable della memoria
o_address[16] indirizzo della memoria
o_data[8] bus dati in scrittura in memoria

Il componente avvia l'esecuzione quando i_start è portato a 1. Se il segnale è portato a 0 durante l'esecuzione, questa si interrompe per riprendere quando i_start è nuovamente alto. Al termine dell'esecuzione, il risultato è scritto in memoria e viene alzato o_done, mantenuto alto finché non si abbassa i_start. Quando o_done viene abbassato il componente è pronto per una nuova esecuzione.

Codifica Working Zone

Data una lista di Nwz working zone di dimensione fissa Dwz e non sovrapposte, la codifica permette di rappresentare gli indirizzi appartenenti a una di queste come numero della working zone WZ_NUM e offset rispetto all'indirizzo base di quest'ultima WZ_OFFSET. Il risultato ha un bit aggiuntivo WZ_BIT rispetto all'indirizzo codificato, posto a 1 se l'indirizzo è in una working zone, altrimenti a 0.

Se l'indirizzo da codificare ADDR appartiene a una working zone, il risultato è composto da WZ_BIT posto a 1, WZ_NUM codificato in binario e WZ_OFFSET codificato one-hot. Altrimenti l'indirizzo non viene modificato e il risultato è WZ_BIT posto a 0 concatenato ad ADDR.

Risultato
ADDR ∈ WZ 0 & WZ_NUM & WZ_OFFSET
ADDR ∉ WZ 1 & ADDR

Dato che `WZ_NUM & WZ_OFFSET` ha dimensione uguale ad ADDR e WZ_OFFSET ha dimensione Dwz (one-hot), il massimo numero di working zone utilizzabili per codificare indirizzi a n bit è 2nDwz.

Struttura della memoria

Il componente utilizza Nwz=8 working zone di dimensione Dwz=4, lavora con indirizzi a 7 bit e genera un output a 8 bit.

Indirizzo Contenuto
0..Nwz−1 Indirizzi working zone
Nwz Indirizzo da codificare
Nwz+1 (Risultato della codifica)

La memoria con cui si interfaccia è indirizzabile al byte con indirizzi di 16 bit. Le celle di memoria da 0 a Nwz−1 contengono gli indirizzi delle working zone e la cella all'indirizzo Nwz contiene l'indirizzo da codificare. Il risultato della codifica viene scritto in memoria all'indirizzo Nwz+1.

Si assume che gli indirizzi delle working zone non cambino fra esecuzioni successive, finché non si resetta il componente.

Implemen​ta​zio​ne

Il componente è stato progettato in modo modulare e parametrico. Il modulo principale, main, dichiara tre parametri generic: data_sz (dimensione dell'output), Dwz e Nwz, che possono essere assegnati arbitrariamente col vincolo Nwz ≤ 2data_sz−1−Dwz perché il numero di working zone sia rappresentabile nella dimensione data.

Le working zone lette vengono salvate in registri interni, così da poter confrontare velocemente diversi indirizzi con lo stesso set di working zone. Il componente impiega un ciclo di clock per ogni working zone letta, uno per leggere l'indirizzo da codificare e uno per scrive il risultato, in totale 8+2 = 10 cicli di clock per la prima codifica, 2 per le codifiche successive, escluso l'overhead del protocollo (aspettare che venga abbassato e poi rialzato il segnale di start).

L'entità top level, project_reti_logiche, istanzia il modulo principale main con i parametri data_sz=8, Dwz=4, Nwz=8, e fornisce l'interfaccia esterna del componente. Gli indirizzi in ingresso hanno dimensione data_sz−1, perciò l'ultimo bit dei dati letti dalla memoria non è significativo e rimane scollegato; inoltre il componente internamente ignora la dimensione della memoria da cui legge i dati e usa indirizzi di log2(Nwz+2) bit (4 bit con 8 working zone), la dimensione minima per rappresentare il range di indirizzi utilizzati, l'interfaccia esterna estende l'indirizzo alla dimensione corretta, che in questo caso è 16 bit.

project_reti_logiche top level schematic

L'entità main istanzia e collega i due moduli che costituiscono il componente: control e convert. Il primo si occupa del controllo dell'esecuzione, il secondo esegue la conversione degli indirizzi in ingresso.

Control

Questo modulo gestisce lo stato e la logica di controllo. Riceve in ingresso il clock e i segnali di start e reset dall'interfaccia e genera i segnali di controllo per la memoria e per il modulo convert. Ha solo il parametro Nwz per gestire gli indirizzi di lettura dalla memoria, data_sz e Dwz non sono rilevanti.

Ingressi:
clk segnale di clock
rst segnale di reset sincrono
start segnale di start
Uscite:
done segnale di done
mem_en segnale di enable della memoria
mem_we sengale di write enable della memoria
mem_addr[log2(Nwz+2)] indirizzo della memoria
wz_id[log2(Nwz)] id della working zone in lettura
convert posto a 1 se il dato in lettura è l'indirizzo da convertire e wz_id non è significativo
control schematic

Internamente il modulo ha tre registri di stato: wz_id_st, write_s e done_s. wz_id_st è il contatore del numero di working zone lette dalla memoria e incrementa ogni ciclo di clock man mano che queste vengono lette e salvate, fino al valore Nwz, valore mantenuto anche fra esecuzioni successive. Il valore Nwz segnala che sono state lette tutte le working zone e viene letto nel ciclo corrente l'indirizzo da convertire, quindi prepara per il ciclo successivo lo stato write_s a 1. write_s resta alto per un ciclo di clock in cui avviene la scrittura all'indirizzo Nwz+1, selezionato da un muxer. Nel ciclo successivo viene alzato done_s che resta alto finché start non diventa 0.

Di seguito le tabelle di transizione per gli stati write_s e done_s con i valori dei segnali assunti durante l'esecuzione

write_s wz ≥ Nwz write_s'
000
011
110
done_s write_s start done_s'
0000
0010
0111
1011
1000

I due registri wz_id_cur e convert_s servono da buffer per ritardare di un ciclo di clock i segnali di controllo per il modulo convert, che devono essere sincronizzati con il dato letto dalla memoria.

Convert

Questo modulo esegue la conversione. Riceve in ingresso il clock, i dati in lettura dalla memoria, e il controllo dell'input dal modulo control. Ha come uscita il risultato della conversione da scrivere in memoria.

Ingressi:
clk segnale di clock
wz_id[log2(Nwz)] id della working zone in lettura
convert posto a 1 se il dato in lettura è l'indirizzo da convertire e wz_id non è significativo
address_in[data_sz−1] indirizzo in ingresso, interpretato in base al valore di wz_id e convert
Uscite:
address_out[data_sz] risultato della conversione
convert schematic

Il modulo salva gli indirizzi base delle working zone e confronta l'indirizzo in ingresso con ognuna in parallelo, istanziando un sottomodulo encode per ogni working zone. Se c'è un match, l'indirizzo in uscita è l'indirizzo codificato con il bit WZ_BIT posto a 1, in caso contrario un muxer seleziona l'indirizzo in ingresso e concatena WZ_BIT a 0.

L'indirizzo codificato è selezionato con una catena di or collegati ai moduli encode, che hanno output a 0 in caso di mismatch. Da contratto le working zone non devono sovrapporsi, in caso contrario se si ha un match multiplo a causa di una configurazione sbagliata degli indirizzi base, l'output ha un valore non valido.

Encode

Questo è un sottomodulo combinatorio che confronta l'indirizzo selezionato con l'indirizzo base di una working zone e in caso di match calcola la codifica dell'indirizzo. Ha un parametro aggiuntivo, id, ovvero l'indice della working zone di riferimento WZ_NUM necessario per comporre il risultato.

Ingressi:
wz_base[data_sz−1] indirizzo base della working zone di riferimento
address_in[data_sz−1] indirizzo da confrontare
Uscite:
match posto a 1 se l'indirizzo appartiene alla working zone
address_out[data_sz−1] il risultato della codifica escluso WZ_BIT se match vale 1, altrimenti tutti i bit sono posti a 0
encode schematic

Test

L'approccio modulare ha consentito di testare oltre al componente nell'insieme anche le singole parti indipendentemente. I moduli control e convert sono stati testati con unit test qualitativi usando valori dei parametri minori, per rendere più semplice il debugging guardando la forma d'onda in risposta a un ingresso noto.

Gli integration test invece sono gestiti da una procedure, comune a tutti i testbench, che esegue i test in batch. Ogni insieme di working zone viene testato convertendo diversi indirizzi in esecuzioni consecutive e verificandone automaticamente la correttezza rispetto al valore atteso, dopo viene dato il reset e si passa al test successivo.

I dati di ogni testbench sono generati da uno script Python che calcola il valore atteso per ogni indirizzo da convertire, con indirizzi da testare dati o generati casualmente. Nei test generati gli indirizzi delle working zone non sono ordinati. Siccome con indirizzi a 7 bit il range è molto limitato, ogni set di working zone è stato testato con tutti i 128 indirizzi possibili, in ordine sparso.

Il modulo main è stato testato anche con parametri diversi da quelli utilizzati nel componente, verificandone il funzionamento in vari casi, tra cui con un numero di working zone minore del massimo, con una sola working zone, con working zone di un solo indirizzo.

Sintesi e simulazione

Il componente è sintetizzabile correttamente con tre diversi warning dovuti alla configurazione dei segnali che non causano effetti collaterali o problemi:

port o_address[4] driven by constant 0
ripetuto dal bit 4 al bit 15, dovuto al fatto che quei bit di indirizzo non sono mai usati e sono aggiunti solo per interfacciarsi con la memoria.
unconnected port i_data[7]
siccome gli indirizzi sono a 7 bit, il bit 7 dei dati letti dalla memoria non è utilizzato.
null assignment ignored
generato nel modulo control da un'estensione del segnale wz_id_st da log2(Nwz+1) bit a log2(Nwz+2) bit. Per generalità l'estensione è necessaria, ma nel caso considerato log2(9) = log2(10) = 4 (ceil) e per questo viene ignorata.

Oltre che in Behavioral, il componente sintetizzato passa tutti i test in Post Synthesis Simulation, sia Functional che Timing, con un periodo di clock che può essere portato fino a un minimo di 8ns.

Report

Il componente viene sintetizzato con 106 LUT e 66 FF. Di seguito i punti salienti del report di sintesi:

Hierarchical RTL Component report
Module control
Detailed RTL Component Info :
+---Adders :
	   2 Input      4 Bit       Adders := 2
+---Registers :
	                4 Bit    Registers := 1
	                3 Bit    Registers := 1
	                1 Bit    Registers := 3
Module encode
Detailed RTL Component Info :
+---Adders :
	   3 Input      8 Bit       Adders := 1
Module convert
Detailed RTL Component Info :
+---Registers :
	                7 Bit    Registers := 8
+---Muxes :
	   2 Input      8 Bit        Muxes := 1
Report Cell Usage:
+------+-------+------+
|      |Cell   |Count |
+------+-------+------+
|1     |BUFG   |     1|
|2     |CARRY4 |    16|
|3     |LUT1   |     1|
|4     |LUT2   |    62|
|5     |LUT3   |     4|
|6     |LUT4   |    10|
|7     |LUT5   |     1|
|8     |LUT6   |    32|
|9     |FDRE   |    66|
|10    |IBUF   |    10|
|11    |OBUF   |    27|
+------+-------+------+
Report Instance Areas:
+------+-----------------------------+---------+------+
|      |Instance                     |Module   |Cells |
+------+-----------------------------+---------+------+
|1     |top                          |         |   230|
|2     |  main_u                     |main     |   192|
|3     |    control_u                |control  |    22|
|4     |    convert_u                |convert  |   170|
|5     |      \encode_l[0].encode_u  |encode   |     9|
|6-11  |      \encode_l[...          |...      |     9|
|12    |      \encode_l[7].encode_u  |...      |     9|
+------+-----------------------------+---------+------+