Nella sua prima versione, la CPU si compone soltanto di registri utili ad accedere alla memoria per leggere il codice operativo da eseguire, come di vede nella figura successiva.
Il modulo più semplice che si può analizzare è l'oscillatore che serve a produrre il segnale di clock. Si tratta di un oscillatore costruito con una serie di porte logiche invertenti, per creare un ritardo di propagazione sufficiente a produrre un'oscillazione a una frequenza gestibile. Per attivare l'oscillazione si richiede un impulso iniziale che, dopo una breve pausa a zero, si attiva stabilmente. La figura successiva mostra l'oscillatore e l'impulso di avvio necessario per l'attivazione. È importante osservare che la serie di porte invertenti deve essere in numero dispari, come se si trattasse di una sola porta invertente, ma con un lungo ritardo di propagazione. Il risultato viene poi passato a un divisore di frequenza, composto in questo caso da una catena di flip-flop T, sincroni, in modo da non sfasare l'oscillazione a ogni divisione; in uscita si hanno tante linee raggruppate assieme, ognuna delle quali permette di prelevare un'oscillazione a una frequenza differente. Il divisore di frequenza è inizializzato dallo stesso impulso iniziale, il quale parte da uno stato a zero. Nel caso degli esempi viene usata una frequenza molto bassa, corrispondente all'ultimo stadio di divisione.
L'impulso iniziale viene prodotto da un componente sintetizzato attraverso del codice Verilog, in quanto diversamente servirebbero componenti elettronici non logici e la loro trattazione esula dallo scopo di questo studio.
|
L'unità di controllo, contenuta nel modulo CTRL
, è molto simile a quella descritta nella sezione u0.3, con la differenza che l'ingresso è individuato dalla variabile I a 8 bit (la lettera «I» sta per «istruzione») e che l'uscita ha un rango molto maggiore, costringendo a utilizzare due unità di memoria in parallelo. Il contatore che serve a scandire le istruzioni nel blocco finale di memoria è complessivamente a 16 bit, ma per convenienza, ne sono stati usati due da 8 in cascata.
L'ingresso I dell'unità di controllo è alimentato dal contenuto del registro IR (instruction register).
I moduli IR
, MAR
e MDR
, sono registri semplici, costruiti con flip-flop D, connessi al bus attraverso dei buffer a tre stati, dai quali è possibile prelevare copia del valore memorizzato da un'uscita supplementare, denominata data. Il registro IR (instruction register), a cui si è già accennato, ha lo scopo di conservare il codice operativo che l'unità di controllo deve eseguire; il registro MAR (memory address register) ha lo scopo di conservare l'indirizzo di memoria a cui si vuole accedere; il registro MDR (memory data register) serve ad accumulare quanto viene letto dalla memoria per qualche motivo o ciò che vi deve essere scritto.
Il modulo PC
è un registro simile agli altri, con la differenza che può incrementare il valore che contiene quando è attivo l'ingresso Inc. Il registro PC (program counter) ha lo scopo di contenere l'indirizzo di memoria del codice successivo da eseguire.
Il modulo RAM
è sostanzialmente differente dagli altri, in quanto racchiude la memoria RAM usata dalla CPU. A tale memoria si accede attraverso l'indirizzo fornito tramite l'ingresso Addr, a 8 bit, e anche il contenuto della memoria è organizzato a celle da 8 bit. Il modulo condivide con gli altri gli ingressi di controllo dell'accesso al bus; tuttavia, quando il modulo riceve l'indirizzo ed è abilitata la lettura dal bus, il valore contenuto in memoria viene aggiornato subito (salvo il ritardo di propagazione), senza attendere l'impulso di clock.
Il modulo RAM
riceve l'indirizzo dal registro MAR (memory address register), il quale è così dedicato a contenere e conservare l'indirizzo di memoria a cui si vuole accedere.
La prima cosa di cui si deve occupare la struttura appena descritta, consiste nel caricamento di un'istruzione, seguito poi dall'esecuzione della stessa: ciò è noto come ciclo di caricamento (fetch). Nella struttura in questione, il registro PC contiene l'indirizzo dell'istruzione da eseguire: questo valore deve essere trasferito nel registro MAR e il registro PC viene incrementato; dalla memoria RAM si ottiene l'istruzione contenuta nell'indirizzo MAR che viene copiata nel registro IR. Ciò si può rappresentare sinteticamente come segue:
MAR = PC
PC++
IR = RAM[MAR]
Le figure successive mostrano proprio questi tre passaggi, evidenziando i valori degli ingressi br, bw e Inc, attraverso dei LED che diventano rossi nel momento dell'attivazione della linea a cui sono connessi. Le figure mostrano sempre solo il momento in cui il segnale di clock diventa attivo.
All'interno dell'unità di controllo (il modulo CTRL
) il tempo è scandito allo stesso modo, a parte il fatto che i contatori cnt sono pilotati da un segnale di clock invertito, per anticipare l'attivazione delle linee di controllo rispetto all'impulso relativo alla gestione del bus dati. Inizialmente i contatori dell'unità di controllo si trovano a essere azzerati e per questo vanno a ricercare nella memoria sottostante la prima microistruzione, corrispondente alla richiesta di eseguire l'operazione MAR=PC. Successivamente il complesso dei due contatori cnt viene incrementato e ciò fa passare alla seconda microistruzione, corrispondente alla richiesta di incremento del registro PC. Nel terzo istante si ha un incremento ulteriore, facendo emergere la microistruzione IR=RAM[MAR].
A questo punto, l'unità di controllo dispone dell'istruzione da eseguire nell'ingresso I ed è pronta per recepirla. Per farlo, la microistruzione successiva richiede al contatore interno di accettare il valore in ingresso. Questo valore corrisponde al contenuto della memoria m0, la quale tratta l'istruzione in ingresso come indirizzo, dal quale produce a sua volta l'indirizzo del microcodice successivo a cui saltare. Negli esempi delle figure, l'istruzione in questione corrisponde al codice operativo 000000002, ovvero all'istruzione nulla (not_operate
).
Dopo l'azzeramento dei contatori dell'unità di controllo, si ricomincia dal microcodice iniziale (le prime tre fasi) con il quale si richiede il caricamento di una nuova istruzione.
Va osservato che durante la quarta fase (salto al microcodice di esecuzione dell'istruzione richiesta) e durante la fase conclusiva (salto al microcodice iniziale che attua il ciclo di caricamento), nel bus dati non succede nulla.
Per fermare il funzionamento del circuito descritto, esiste l'istruzione stop
(111111112), con la quale viene fermato il segnale di clock. La figura successiva mostra questa situazione.
Dovrebbero essere disponibili due video, nei quali si dimostra l'esecuzione di due sole istruzioni (macroistruzioni):
not_operate
stop
Il primo video ogv http://www.youtube.com/watch?v=Z8bTO8WjYYc mostra ciò che accade nel bus dati; il secondo, invece, mostra l'interno dell'unità di controllo ogv http://www.youtube.com/watch?v=pPxCQz7IFbM.
Per descrivere il contenuto delle memorie, incluso quello della memoria RAM, viene usato un file sorgente scritto secondo la sintassi adatta a gmac di Tkgate 2. Le prime direttive descrivono i banchi di memoria, i quali sono organizzati così: ctrl.m0 corrisponde alla prima memoria in alto dell'unità di controllo; ctrl.m1 e ctrl.m2 sono le due memorie che contengono il microcodice e che si trovano in basso nello schema dell'unità di controllo; ram.m3 è invece la memoria contenuta nel modulo RAM
del bus dati e ospita il macrocodice che inizialmente si limita solo a not_operate
e stop
.
|
Si passa quindi alla descrizione dei campi in cui è suddivisa ogni cella di memoria che rappresenta il microcodice (ctrl.m1 e ctrl.m2). Per esempio, il bit meno significativo si chiama ctrl_start, mentre il più significativo si chiama stop. Va osservato che non sono descritti tutti i 36 bit della cella che rappresenta una microistruzione, perché al momento il codice si limita a rappresentare la riduzione della CPU nella sua prima versione.
|
Vengono poi descritti i tipi di operandi che possono avere le istruzioni (le macroistruzioni). Si prevede di gestire istruzioni senza operandi, oppure con un solo operando di 8 bit. Il significato della sintassi utilizzata per descrivere il tipo op_0 e il tipo op_1, va approfondito, eventualmente, nella documentazione di Tkgate.
|
Si passa poi alla descrizione dei codici operativi; per esempio, si vede che l'istruzione not_operate
corrisponde al codice zero (000000002), mentre l'istruzione jump
ha il codice 15 (000011112). Va osservato che nel primo caso (not_operate
) non ci sono argomenti, mentre nel secondo si richiede un argomento.
|
Inizia quindi la definizione del microcodice, il quale viene collocato a partire dall'indirizzo zero della coppia di memorie ctrl.m1 e ctrl.m2. Si può osservare che si inizia proprio dalla descrizione del ciclo di caricamento (fetch) che si conclude con il salto alla microistruzione che inizia la procedura che mette in pratica la macroistruzione recepita; inoltre, alla fine della descrizione di ogni macroistruzione (in forma di microcodice), viene richiesto di saltare nuovamente alla prima microistruzione, con la quale si ripete il ciclo di caricamento.
|
Infine, inizia il macrocodice, ovvero il codice assemblatore da immettere nella memoria RAM:
|
|
Il file descritto dovrebbe essere disponibile all'indirizzo allegati/circuiti-logici/scpu-sub-a.gm. Per compilarlo con gmac di Tkgate 2, si dovrebbe procedere con il comando successivo:
$
gmac20 -o scpu-sub-a.mem -m scpu-sub-a.map
\
\ scpu-sub-a.gm
[Invio]
Il file scpu-sub-a.mem
che si ottiene è quello che serve a Tkgate 2 per caricare i contenuti delle memorie previste. Eventualmente, dovrebbe essere disponibile anche il file allegati/circuiti-logici/scpu-sub-a.v che contiene la rappresentazione completa di questa prima versione della CPU dimostrativa nel formato di Tkgate 2.
Prima di concludere la descrizione della versione iniziale della CPU dimostrativa, va osservato che esiste una terza istruzione che non è ancora stata usata in un esempio: jump
. Questa si realizza semplicemente con i passaggi seguenti:
MAR = PC
PC = RAM[MAR]
In pratica: nel registro MAR viene copiato l'indirizzo contenuto nel registro PC, il quale corrisponde all'indirizzo successivo all'istruzione appena letta e in corso di esecuzione (jump
), ma il contenuto della memoria corrispondente a tale indirizzo, viene copiato di nuovo nel registro PC (senza incrementarlo).
L'istruzione jump
precede un argomento, costituito dall'indirizzo a cui si vuole saltare incondizionatamente; pertanto, tale indirizzo si colloca subito dopo il codice dell'istruzione e viene letto attraverso l'indice del registro PC, come se si trattasse di un'istruzione; poi, però, il contenuto della memoria in corrispondenza di quell'indirizzo, non viene inviato al registro IR, ma viene immesso nuovamente nel registro PC, in maniera tale che la prossima istruzione a essere caricata sia quella a cui si vuole saltare.
A titolo di esempio, il macrocodice (ovvero il codice assemblatore) potrebbe essere modificato come segue:
|
Durante la compilazione, #start viene rimpiazzato dall'indirizzo corrispondente all'etichetta start: che in pratica è semplicemente zero. Questo piccolo programma si limita a non fare nulla (not_operate
) e a ripeterlo indefinitivamente, tanto che l'istruzione stop
non può mai essere eseguita. Le figure successive mostrano ciò che accade dopo l'esecuzione dell'istruzione not_operate
nel bus dati.
Dovrebbe essere disponibile un video che mostra l'esecuzione del macrocodice descritto: ogv http://www.youtube.com/watch?v=Z8bTO8WjYYc.
«a2» 2013.11.11 --- Copyright © Daniele Giacomini -- appunti2@gmail.com http://informaticalibera.net