Funzionamento
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 è 2n−Dwz.
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.
Implementazione
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.
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 |
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' |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 1 | 0 |
done_s | write_s | start | done_s' |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 0 | 1 | 0 |
0 | 1 | 1 | 1 |
1 | 0 | 1 | 1 |
1 | 0 | 0 | 0 |
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 |
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 |
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 segnalewz_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| +------+-----------------------------+---------+------+