Università degli studi di Roma
SAPIENZA
Tesina di
Architettura di Sistemi integrati
Prof. Mauro Olivieri
Perticaroli Stefano Patrizi Roberto Di Liberto Alessio Barbieri Stefano
1
2
Specifiche di base Il processore ha una memoria interna da 32 locazioni da 32 bit ognuna numerate da 0 a 31. Le prime 4 locazioni sono particolari, sono infatti collegate a dei piedini esterni del processore e permettono la comunicazione tra più processori identici. Le locazioni di memoria per le comunicazioni sono: 0 = port N 1 = port S
2 = port E 3 = port W
Il processore è in grado di eseguire istruzioni in virgola fissa con formati binari in complemento a due. L’instruction set del μ-transputer è dotato delle seguenti istruzioni di base: Istruzione
Campi
ADD
MODE
DEST REG
MODE
OP_1
MODE
OP_2
SUB
MODE
DEST REG
MODE
OP_1
MODE
OP_2
MUL
MODE
DEST REG
MODE
OP_1
MODE
OP_2
AND
MODE
DEST REG
MODE
R1
XOR
MODE
DEST REG
MODE
R1
Bit 31÷29
28
27÷23
22
21÷17
16
15÷11
MOV
MODE
DEST REG
MODE
R1
Bit 31÷29
28
27÷23
22
21÷17
BRANCH
COND CODE
TARGET
MODE
R1
Bit 31÷29
28
27÷19
18
17÷13
IMOV
MODE
DEST REG
IMMEDIATE DATA
Bit 31÷29
28
27÷23
22÷13
In cui: Rappresentano gli operandi delle istruzioni a 2 operandi, R1 nel caso di istruzioni con un solo ingresso. Sono lunghi ciascuno 5bit. OP_1, OP_2 DEST REG
indica in quale locazione di memoria verranno posti i risultati dell’istruzione.
Rappresenta il modo di indirizzamento, diretto o indiretto. Se è posto ad 1 il processore preleva il dato da uno dei suoi 32 registri (2 5 = 32), in particolare dal registro indirizzato dai 5 bit OP. Se è posto a 0, i 32 bit del registro OP rappresentano l’indirizzo dell’istruzione nella memoria esterna. MODE
Le prime 5 istruzioni a due operandi sono autoesplicative così come MOVE, che prende il contenuto del registro R1 e lo sposta in DEST_REG. Qualche parola sulle altre 2. aggiorna il program counter spostandosi di un numero di locazioni pari a TARGET, un intero di 9 bit con segno che permette di saltare in avanti di 255 posizioni oppure indietro di 256 se è verificata la condizione COND CODE su registro R1. Se COND CODE = 0 il salto si ha solo se il registro R1 contiene tutti bit nulli, altrimenti se COND CODE = 1 il salto si ha solo se R1 è positivo (bit 31 = 0). BRANCH
IMOVE
Copia un operando immediato (IMMEDIATE DATA) in un registro di destinazione (DEST REG)
3
L'instruction set è stato parzialmente modificato rispetto alle specifiche, per via dell'elevato numero di bit ancora disponibili. Sono state aggiunte altre istruzioni e per alcune istruzioni sono stati utilizzati più bit di quelli richiesti. Istruzioni come la imove o come il branch, utilizzano operandi binari a 22 bit invece dei precedenti 9 o 10. Inoltre sono state aggiunte le istruzioni di Shift, Or, Not, Bit clear, Bit set ed un codice per una No operation (NOP). Lo shift legge il contenuto di un registro sorgente e ne trasla tutti i bit di una posizione a destra o a sinistra in base al valore di un bit di controllo, memorizzando il risultato nel registro di destinazione. Il comando OR esegue l'or logico, bit a bit tra due registri, il NOT inverte tutti i bit di un registro. Bit clear (BCLR) e Bit set (BSET) impostano il valore di un bit rispettivamente ad 0 ed a 1. Il bit impostabile è selezionato da un indice (5 bit) che punta ad uno tra i 32 bit del dato, numerati in ordine crescente da sinistra verso destra.
Formato istruzioni Conservando l'impostazione data nelle specifiche il più possibile anche con l'aggiunta delle nuove istruzioni, risulta un formato regolare, sebbene talvolta un campo debba essere splittato in posizioni non adiacenti, come è avvenuto ad esempio per il codice istruzioni. Le istruzioni in totale sono 13, per cui sono richiesti 4 bit di codice a fronte dei tre previsti inizialmente dalle specifiche. Il quarto bit è stato aggiunto perciò in decima posizione, lasciando immutati gli altri bit come da specifiche. I codici di 4 bit per ciascuna istruzione sono elencati nella seguente tabella: Istruzione
4
Opcode Istruzione
somma
ADD
0000
differenza
SUB
0010
prodotto
MUL
0100
no operation
NOP
0101
and logico
AND
0110
mov
MOV
0111
xor logico
XOR
1000
shift
SHIFT
1001
or logico
OR
1010
imov
IMOV
1011
bit clear
BCLR
1100
bit set
BSET
1101
branch
BRANCH
1110
not logico
NOT
1111
Istruzioni che richiedono gli stessi campi (fino a tre operandi, una costante oppure un offset in cui saltare, un indice oppure qualche bit di controllo che specifica il comportamento delle istruzioni), utilizzano gli stessi bit dell'istruzione per lo stesso scopo: ad esempio il codice istruzione è sempre posto nei tre bit più a destra (dal 29 al 31), e nel 10 bit per ciascuna istruzione. Campi diversi in istruzioni diverse sono sovrapposti sugli stessi bit in modo da non creare conflitti, l'utilizzo corretto dei bit dell'istruzione è affidato a segnali di controllo che stabiliscono quale parte della microarchitettura deve essere attivata coerentemente con lo scopo ed il reale utilizzo dei campi dell'istruzione. In questo modo, come risulta chiaro osservando il funzionamento dell'unità di controllo, la struttura stessa della CU risulta semplificata, rendendo più agevole l'esecuzione delle istruzioni. Esplicitiamo quanto detto, cominciando proprio a vedere quali sono i campi delle istruzioni. Abbiamo quattro bit di opcode, che occupano i bit dal 29 alla 31 e nel bit 10, per ciascuna istruzione. Lo schema seguente indica la loro posizione all'interno della stringa dei 32 bit di un'istruzione. I bit interessati sono evidenziato con O, il numero sopra di essi indica la posizione a partire dal bit più a destra (il bit 0) in ordine crescente man mano che ci si sposta a sinistra: 31
30
29
10
O O O
O
1
0
Possono esserci fino a tre campi di registro di 5 bit ciascuno. Due campi (A, B ) indicano proprio due operandi sui quali eseguire le istruzioni, il terzo campo (D ) indica il registro di destinazione nel quale memorizzare il risultato dell'operazione. A questi si affiancano tre bit per il modo (M ) di indirizzamento che indicano se leggere il dato direttamente da uno dei 32 registri (indirizzato con 5 bit), o se utilizzare i 32 bit del registro come indirizzo per la memoria (che può raggiungere 232 = 4 G locazioni di memoria). Ciascun campo registro segue il proprio bit di modo formando tre blocchi da 6 bit posizionati a partire dal bit 28 in cui si ha l'operando in letura a, segue l'operando in scrittura b, infine l'ultimo campo è per il risultato in scrittura. 28
27
M A
23
A
A
A
22
21
A M
B
17
B
B
B
16
15
11
B M D D D D D
I campi descritti sono sufficienti per scrivere istruzioni a due operandi come una somma. Ad esempio per sommare il quinto (00101) ed il sesto (00110) registro e memorizzare il risultato nel settimo registro (00111) con tecnica di indirizzamento diretta il codice istruzione da utilizzare è il seguente: 000 100101 100110 100111 0 0000000000
Gli ultimi 10 bit sono inutilizzati e nell'istruzione sono stati posti a zero, and, or, xor, differenza e prodotto utilizzano istruzioni della stessa forma.
5
Per le operazioni che necessitano di costanti (C ) il campo utilizzato può essere di 22 bit, in tal caso copre le posizioni da 0 a 10 e da 17 a 28, oppure può essere da 15 bit, quelli compresi tra 0 e 10 e tra 17 e 21. Un opportuno controllo interno al processore determina se i bit più significativi appartengono o meno all'istruzione in base all'opcode, come mostrato nella descrizione della CU. 28
C
C
C
C
C
C
22
21
C
C
C
C
C
17
10
C
C
0
C
C
C
C
C
C
C
C
C C
Ad esempio il codice per eseguire una imov che memorizza la costante 2 (...10) nel quinto (00101) registro, sempre indirizzato direttamente: 101 000000000000 1001011 0000000010 (nessun bit inutilizzato);
Se invece avessi considerato un'istruzione di branch che avanza di 3 posizioni (...011) se il sesto registro (00110) è maggiore di zero il codice corretto sarebbe stato: 111 100110 0 00000 000000 0 0000000011
In questo caso 10 bit non sono utilizzati, nell'esempio sono stati riempiti con degli zeri. Gli ultimi due campi sono l'indice del bit da settare ad uno oppure a zero, e la direzione di shift. L'indice (I ) utilizza 5 bit, dal 22 al 18, mentre lo shift (S ) necessita di un solo bit posto in ventiduesima posizione. 28
27
23
22
21
S/I
I
18
I
I
17
16
15
11
I
Ad esempio per eseguire il bit clear del quinto (00101) registro, memorizzando il risultato nel terzo (00011) registro il codice corretto è: 100 100101 10110 0 100011 1 0000000000
con 11 bit inutilizzati evidenziati in grigio. Stessa forma per bit set. Volendo invece eseguire lo shift del quinto (00101) registro verso sinistra con risultato nel settimo (00111) registro (anche in questo caso in modo diretto), il codi corretto è: 100 100101 1 00000 100111 1 0000000000
in cui sono 15 i bit inutilizzati. Il massimo numero di operandi facenti riferimento a registri di link sono due, uno in lettura, uno in scrittura.
Microarchitettura Le istuzioni utilizzano un opcode con 5 bit per operando. I 5 bit sono utilizzati per indirizzare un registro tra i 32 disponibili, che fornisce una parola da 32 bit. Questa può essere un operando, oppure un registro di una memoria (che può raggiungere al massimo 4G linee) a sua volta a 32 bit per parola. I blocchi registri e memoria sono connessi in cascata. Le tre uscite dati dei registri costituiscono altrettanti ingressi per la memoria. Nel caso in cui il modo di indirizzamento è diretto (quindi il relativo bit di modo è posto ad 1 nell'opcode) e quindi il contenuto dei registri deve essere utilizzato come operando, la memoria 6
farà semplicemente passare il dato dall'ingresso all'uscita, proprio in base al valore dei bit di modo. Al contrario nel caso in cui il modo di indirizzamento e indiretto, nell'opcode avrò il bit di modo pari a 0, ed i 5 bit di indirizzo nei registri saranno espansi dai registri a 32 bit di indirizzo che la memoria utilizzerà per prendere un operando in uno spazio di indirizzamento di 2^32 = 4G. La memoria prende tre ingressi a 32 bit dai registri, dei quali solamente due funzionano nel modo descritto, il terzo ingresso (c) infatti serve esclusivamente come indirizzo per la scrittura di dati provenienti dalla porta data_c. Le due uscite data_a, data_b collegate agli ingressi a, b invece sono collegate ai bus operandi dell'ALU, bus_a, bus_b. Il risultato calcolato dall'ALU (bus_c) viene riportato in ingresso nella memoria, nei registri, nell'unità di controllo e nel PC. Il risultato viene memorizzato nei registri o nella memoria in base al bit di modo, può essere utilizzato dalla CU per realizzare il branch, oppure può essere memorizzato nel PC nel caso in cui l'ultima operazione è stata svolta sul PC. Nello schema della microarchitettura si nota inoltre l'ALU completamente combinatoria con due bus di ingresso che possono essere scritti dalla memoria oppure dai blocchi PC e Bit expander. PC è il registro utilizzato per memorizzare il program counter, mentre il bit expander serve per riportare al numero corretto di bit una costante presa dall'opcode di un'istruzione o comunque imposta dalla CU: L'unità di controllo infine è il blocco che si occupa di coordinare le parti descritte al fine di svolgere le operazioni previste dalle specifiche del processore. Contiene il registro istruzioni (IR) nel quale viene memorizzato l'opcode dell'istruzione corrente. L'istruzione proviene da una porta apposita detta fetched pilotata in scrittura dalla memoria che vi pone il contenuto della parola indirizzato dai 32 bit presenti in ingresso su data_c. Nello schema alla pagina seguente è riportato uno schema dell'architettura descritta. Le connessioni in blu indicano un bus di 32 bit, le linee sottili in nero indicano semplici collegamenti ad un bit, infine i collegamenti in nero più spessi indicano altri segnali a più bit. I segnali utilizzati dal link manager sono esterni al processore e non sono riportati, così come sono assenti i segnali di clock o reset!
7
data_c G ovflow
Control Unit
PC
Bit expander
IR
data_a data_b data_c
a b c
mem_write mem_out_en f
w out_enable f
fetched ma mb mc
fetched ma mb mc write_enable m
ack err
sel_a sel_b sel_c
lmngr_ack lmngr_err
ctrl_ALU indice_reg shift_type
sel_a sel_b sel_c
G
clk reset Pluto
O
exp_en exp_bitnumeber exp_constant PC_enable PC_update
BUS_B
BUS_A
CMP
data_a data_b
data_in
segnali lnkmngr
Me mory ALU
Re g_w ith_link ck
ck
data_c
BUS_C
ALU L’ALU, blocco completamente combinatorio, prende gli operandi direttamente dai 2 bus in ingresso, bus_a e bus_b, ai quali sono collegati tutti i blocchi che realizzano le operazioni specifiche. Le operazioni ALU a due operandi sono add, sub, mul, and, or, xor, mentre shift, bit clear, bit set necessitano di un solo operando. Il risultato posto in uscita sul bus_c è selezionato dal multiplexer pilotato tramite un codice di controllo binario a 4 bit. Normalmente tale codice proviene da quattro bit del codice istruzione, altrimenti può essere impostato dalla CU che può anche selezionare direttamente uno dei due operandi a, o b. Nella seguente tabella sono elencati i codici per l'ALU e la relativa funzione svolta. Come si può notare sono tutti identici agli opcode delle istruzioni, in più compaiono due codici per passare uno dei due operandi a, b, dal bus d'ingresso al bus in uscita, che non possono essere programmati dall'esterno ma sono impostati esclusivamente dalla CU. a
0001
b
0011
ADD
0000
SUB
0010
MUL
0100
AND
0110
XOR
1000
SHIFT
1001
OR
1010
BCR
1100
BSR
1101
9
I registri sono costituiti da un array di 32 parole da 32 bit ciascuna, i primi 4 registri sono particolari, poiché sono utilizzati come link per interfacciare altri rpocessore dello stesso tipo e saranno considerati nel paragrafo seguente.
write_enable m
ack err
sel_a sel_b sel_c
Registri
data_in
segnali lnkmngr
Re g_w ith_link ck
data_a data_b data_c
a b c
Ogni elemento è indirizzabile con 5 bit, contenuti direttamente nell'istruzione. Poiché le istruzioni possono gestire fino a tre operandi dei quali due in lettura, due in scrittura, ci sono tre ingressi a 5 bit per tre indirizzi simultanei, cioè sel_a e sel_b e sel_c. Ad ogni fronte di salita del clock sulle uscite data_a, data_b saranno presenti i 32 bit indirizzati rispettivamente da sel_a, sel_b.
Diverso il comportamento di sel_c e rispettivo data_c per via delle differenze operative tra modo diretto ed indiretto. La lettura dai registri infatti, deve avvenire sempre e comunque allo stesso modo. Sarà poi la memoria a discriminare tra indirizzamento diretto, nel qual caso i bit in uscita dai registri sono proprio quelli richiesti dall'istruzione che pertanto saranno passati ai bus operandi, o tra indirizzamento indiretto, in cui la memoria utilizza i bit provenienti dai registri (che sono comunque necessari) come indirizzo al quale prelevare il dato richiesto dall'istruzione! Quindi se l'operando c e indirizzato in modo diretto allora l'uscita data_c non conterrà alcun dato utile, il segnale di sel è utilizzato come indirizzo in cui memorizzare il segnale data_in. Se invece l'indirizzamento è indiretto allora si procede analogamente a quanto fatto per gli operandi a, b, passando il contenuto dei registri attraverso data_c alla memoria, che utilizzerà tale valore come indirizzo in cui scrivere. La scrittura ovviamente è abilitata solamente se viene attivato il segnale write_enable (attivo alto), inoltre il bit di modo m in ingresso indica ai registri come comportarsi. I restanti controlli ack ed err sono utilizzati dal link manager che andiamo ora ad esaminare.
Link manager È l’unità che gestisce le comunicazioni tra il processore e le altre cpu, funziona in modo bloccante per permettere di sincronizzare l’elaborazione, e si trova all’interno dei registri. Utilizza due segnali di controllo in ingresso e due in uscita per ogni link. Il primo segnale è di request, viene attivato dal microtransputer quando deve eseguire un'operazione su un link. Il secondo segnale è un selettore lettura scrittura, ed è: 1 = Scrittura 0 = Lettura L’idea di fondo è che lo scambio di dati tra processori deve essere coerente, cioè un risultato parziale di un’elaborazione viene passato ad un’altra CPU quando questo risultato è richiesto ed è stato calcolato. La comunicazione quindi, avviene solo se contemporaneamente un processore scrive un dato in uno dei registri speciali della memoria ed il processore collegato a quella porta 10
richiede la lettura del dato. Fino a quando non si verifica questa condizione (cioè un processore pronto a leggere ed un processore pronto a scrivere) il programma non prosegue! Si distinguono fondamentalmente 2 casi, in un primo caso la scrittura precede la lettura, nel secondo caso si ha l’inverso. Dal punto di vista fisico per ogni link il processore ha 32 linee per i dati, 2 segnali di controllo di uscita, dunque i 2 segnali simmetrici in ingresso. Saranno identificati r/w_in, ack/req_in e gli omonimi _out riferendoci ad un processore
CPU1
Req_out Rw_out Link (32) Rw_in Req_in
Req_in Rw_in Link (32) Rw_out Req_out
CPU2
Consideriamo per un solo link il caso in cui la scrittura preceda la lettura. In primo luogo il dato da scrivere viene memorizzato all'interno dei registri, contemporaneamente inizia la procedura per la gestione dei link. La scrittura sarà la fase finale di una qualche istruzione che adopera uno dei 4 registri di link come destinazione del risultato. L’esecuzione del programma viene arrestata e vengono alzati i segnali richiesta scrittura (req/ack = 1, r/w = 1) relativi al link attivato. Al fronte successivo del clock il contenuto del registro viene posto sul link corrispondente se questo è disponibile per la scrittura, cioè se l'altro processore non sta già tentando di scrivere sul link. Il processore permane poi in questo stato fino a quando non legge che req/ack = 1 e r/w = 0, cioè fino a quando l'altro processore invia una richiesta di lettura sul link. Se req/ack fosse pari ad uno si avrebbe un’errore, entrambi i processori scrivono sullo stesso link. Se il link è usato correttamente, al fronte successivo del clock l’altro processore scrive il dato, infine un ciclo dopo vengono ripristinate le impostazioni di base: il link è posto in alta impedenza e tutti i controlli sono posti a zero. Un segnale di ack interno al processore indica alla CU che l’operazione è conclusa. Nell'immagine seguente è illustrato lo schema di principio del funzionamento Ciclo scrittura -> lettura Req_out Rw_out
1=lettura
Link (32) ZZZZZ Req_in
REG DATA
ZZZZZ
<-letto!
Rw_in
Se invece consideriamo il caso duale in cui è la lettura di un dato a precedere la scrittura, mi troverò nella fase iniziale dell'istruzione a dover inviare su uno dei due bus operandi un dato che non è ancora presente all’interno del processore. I selettori del registro indicheranno una delle prime 4 locazioni particolari.
11
Anche in questo caso l’unità di controllo dovrà attendere che la procedura di gestione dei link completi il trasferimento, deve attendere cioè che il segnale di ack che viene inviato in concomitanza con la presenza del dato sul bus sia pari ad uno. Gli altri segnali di controllo (tipo read) vengono ignorati in questa fase, così come indifferente è il bit di modo del registro. Anche in questo caso se si verificano errori dovuti alla simultanea lettura o scrittura di un link, viene inviato un segnale di errore (ponendo err = 1) alla CU, che invia un analogo segnale all'esterno del processore. Il seguente diagramma temporale è stato ottenuto simulando il comportamento di due link e mostra anche altri segnali che non sono stati considerati nella precedente descrizione!
a b c
data_a data_b
Me mory
ck
12
w out_enable f
fetched ma mb mc
Memoria
data_c
La memoria è un array di parole da 32 bit ciascuna, implementabile in diverse capacità ma fino ad un massimo di 4G linee, corrispondenti ad indirizzi a 32 bit. Dispone di tre ingressi a 32 bit indicati con a, b e c, che possono contenere degli operandi oppure degli indirizzi, due uscite a 32 bit per i bus chiamate data_a, data_b, un'uscita utilizzata per il fetch delle istruzioni verso la CU ed un ingresso dal bus_c dei risultati. Consideriamo in primo luogo un'operazione di lettura. La lettura può essere diretta o indiretta, nel caso in cui sia diretta la memoria riceve agli ingressi degli operandi che deve semplicemente passare sui bus istruzioni dell'ALU, altrimenti utilizza gli ingressi a, b come indirizzi, il contenuto della memoria viene posto su uno dei due bus esterni. Il tipo di accesso in
memoria è indicato da un apposito bit nell'opcode dell'istruzione, in base a questo bit (presente sugli ingressi ma, mb, mc) la memoria discrimina tra uno dei due accessi descritti. Entrambi gli ingressi vengono trattati simultaneamente, per cui occorre un solo ciclo di clock per leggere entrambi gli operandi di un'istruzione. La scrittura è controllata da un apposito segnale di write attivo alto, e dal bit di modo mc. Poiché la scrittura in memoria avviene solamente se il registro di destinazione è indiretto devono essere attivi entrambi i controlli di scrittura e di modo (quest'ultimo è attivo basso per la memoria, in quanto se il bit di modo è zero allora il modo di indirizzamento dell'operando è indiretto). L'indirizzo di destinazione della memoria viene preso dal'ingresso c. Il fetch di un'istruzione richiede di accedere in memoria alla locazione indicata dal PC. Con la microarchitettura implementata il PC viene aggiornato con lo stesso sommatore presente nell'ALU facendo la somma tra il PC ed 1, il risultato sarà presente sul bus_c che è utilizzato in questo caso dalla memoria come indirizzo al quale prelevare un'istruzione. L'istruzione è poi passata all'unità di controllo dall'uscita fetched. In questo caso appare evidente inoltre la necessità di disporre di un controllo, out_enable attivo alto, in grado di porre la uscite in alta impedenza per poter utilizzare il bus con in PC per operazioni come il branch, o per utilizzare il bit expander come nel caso di imove.
Altri blocchi Uno sguardo infine agli altri blocchi che compongono la microarchitettura del processore. PC è il registro del Program Counter che ovviamente contiene l'indirizzo dell'ultima istruzione prelevata dalla memoria. Completamente asincrono, il PC reagisce immediatamente ai segnali di controllo, cioè un segnale per la memorizzazione del PC, ed un segnale PC2a attivo alto che abilità l'uscita del PC che altrimenti è posta in alta impedenza. Il bit expander invece serve a prendere le costanti contenute nelle istruzioni di lunghezza inferiore a 32 bit, per portarle in uscita (u) in binari a 32 bit in complemento a 2 adatti per l?ALU conservandone il segno. Se en = 0 l?uscita è in alta impedenza ed il blocco è disattivato, infine il controllo exp_bitnumber indica di quandi bit è composto il binario in ingresso. Il blocco indicato come cmp necessario per l'esecuzione del branch è indicato in figura con un ingresso connesso al bus operandi a, esso è il comparatore. Il suo scopo è di prendere in ingresso il contenuto del bus_a e di porre il bit di uscita ad uno se questo è positivo. L'uscita è passata alla CU che memorizza questo bit come flag!
Control Unit L'unità di controllo è organizzata come macchina a stati, ad ogni stato corrisponde una fase di un'istruzione, con i corrispondenti segnali asseriti o negati per ciascuno stato. Al suo interno contiene il registro istruzioni (IR) contenente l'opcode dell'istruzione in fase di esecuzione. La maggior parte dei bit dell'IR è permanentemente collegata a dei segnali di controllo in uscita. Abbiamo ad esempio i bit di modo dei registri coinvolti nelle operazioni permanentemente collegati alle relative uscite ma, mb, mc, i tre campi da 5 bit dell'IR che indicano i registri coinvolti nell'operazione sono collegati ai segnali di selezione (sel a, b, c) dei registri. Altri, come ad esempio i 4 bit di controllo dell'ALU sono applicati presi dall'IR nella maggior parte delle operazioni, ma la CU ha la possibilità di applicare segnali differenti quando necessario, come ad esempio nelle istruzioni di branch in cui l'unità di controllo pone l'uscita ctrl_alu al valore necessario per effettuare la somma. Non tutte le istruzioni utilizzano gli stessi campi dell'IR nello 13
stesso modo! In caso di sovrapposizione i bit sono connessi ad entrambe le possibili uscite, saranno gli altri controlli a determinare quale blocco opera, quindi la correttezza delle assegnazioni! Inoltre la CU contiene un registro FLAG formato da due bit che indicano (in ordine dal più significativo al meno significativo) se c'è stato overflow, e se il risultato è maggiore di 0. Vediamo ora come vengono svolte alcune operazioni con l'architettura proposta: Istruzioni con due operandi ADD, SUB, MUL, AND, XOR, OR Formato istruzione 31
29 28 27
O O O M R R
23 22 21 R
R
17 16 15
11
0
R M R R R R R M R R R R R O
Registro dest
Operando 1
Operando 2
Opcode
Consideriamo l’istruzione già carica nell’instruction register. Dall’unità di controllo i bit dell’opcode vengono posti sui controlli dell’ALU, i 5 bit che identificano ognuno dei 3 registri coinvolti vengono inviati ai registri (sel), ed i bit di modo degli operandi infine controlleranno i segnali read_a, b, dei registri oppure i segnali fetch_a, b, dell’EMI. Se il modo di indirizzamento è diretto (M = 1) un ciclo dopo si sta propagando il risultato che posso memorizzare già sul secondo fronte di salita del clock, altrimenti devo attendere il prelievo del dato che sarà pronto sul bus solamente quando l’EMI attiva il segnale di ack. La logica di controllo vedrà tale segnale solamente al fronte successivo del clock, cioè quando anche il risultato si sarà propagato, quindi quando la CU legge il segnale di ack deve immediatamente memorizzare il dato. Il risultato di un’operazione può essere scritto nei registri interni del processore oppure nella memoria esterna, sempre in base al valore del bit di modo M. Nel primo caso basterà porre ad 1 il segnale write dei registri, ricordando però che se cerco di scrivere su uno dei registri dal quale sto leggendo le uscite dei registri vanno in alta impedenza ed il dato non è memorizzato. Contemporaneamente vengono memorizzati i flag dell’istruzione eseguita negli appositi registri In caso di indirizzamento indiretto occorre alzare il segnale fetch, dopodiché basta attendere l’ack.
store
dell’EMI e negare i segnali di
Nel caso in cui le operazioni facciano uso dei registri speciali, per ogni lettura o scrittura in una delle 4 locazioni si attende il segnale di ack dal link manager dei registri. In tal caso non serve una temporizzazione dei segnali di controllo per gestire prelievo da un’altro processore e poi la scrittura da un’altro processore, il link manager infatti è in grado di attendere il dato da un’altro processore, aspettare la propagazione del risultato e poi inviarlo ad un’altro processore e solamente ad operazione conclusa inviare il segnale di ack al processore, anche se i controlli sono stati impostati tutti dall’inizio. CMP L’istruzione di comparazione è a due operandi, ma non contiene nessun registro esplicito in cui memorizzare il risultato. Infatti l’uscita di tale istruzione è il semplice bit di flag pari ad 1 se a>b. Il comparatore di per se funziona anche se in modo diverso durante l’esecuzione di una qualsiasi 14
istruzione per fornire i 2 bit di flag. Anche in questo caso le operazioni per prelevare i due operandi sono standard, cambia la fase di store che deve registrare esclusivamente il flag. Istruzioni ad un solo operando SHIFT, BCLR, BSET Le istruzioni ad un solo operando necessitano del solo bus a. Il dato viene caricato su tale bus nel modo usuale, settati correttamente i controlli per l’ALU il risultato si ottiene in un solo ciclo, e viene memorizzato nella maniera usuale. In particolare però i flag non vengono aggiornati al termine di tali operazioni Altre istruzioni BRANCH Formato istruzione 31
29 28 27
19 18 17
13 12
0
O O O C T T T T T T T T T M R R R R R O Target
Registro
Opcode
Per prima cosa devo porre il registro sul bus selezionando tale operando come uscita dell’ALU. Sul bus_c ritrovo quindi lo stesso registro invariato, in più il comparatore interno all’ALU avrà stabilito se il numero è positivo o uguale a zero. Se il bit di condizione C è zero (devo saltare se il contenuto del registro è zero) e il flag Z è uno, allora si ha il salto. Pongo quindi in alta impedenza le uscite dell’EMI e dei registri sul bus, l’operando a diviene il program counter (alzo un segnale di tipo Program counter al bus a PC2a) invio l’offset dall’unità di controllo al registro costanti indicato con l’1 sopra il bus b e richiedo una somma algebrica. Sul bus_c ho il nuovo PC, lo salvo nel registro e lo prelevo con l’EMI asserendo store_c ed uno dei segnali di fetch. Un ciclo dopo devo riportare il bus in alta impedenza per permettere all’EMI di scrivere su di esso la successiva istruzione, che passerà attraverso l’ALU opportunamente settata per tale scopo, infine l’unità di controllo preleva l’istruzione dal bus_c. Durante l’esecuzione di tali istruzioni vengono ignorati i flag in uscita dall’ALU. Nel caso in cui non si abbia il salto devo semplicemente incrementare il PC in pratica la differenza sta nel valore di offset che sarà pari ad 1 invece che a quanto specificato nell’istruzione. Nel caso di branch incondizionato o condizionato dai flag il ciclo dell’istruzione risulta semplificato. In tal caso infatti è l’unità di controllo a settare correttamente tutti i controlli già al primo ciclo, poiché non sono richieste fasi specifiche per il prelievo di un registro e la verifica del suo contenuto. Partendo dunque dal codice istruzione per prima cosa mi rendo conto che si tratta di un salto dovuto a flag, per cui confronto immediatamente il flag per sapere se saltare o meno. Se salto allora sommerò l’offset al PC, altrimenti il PC verrà incrementato di un’unità. Se il salto è incondizionato non è necessario neppure controllare i flag, sommo semplicemente l’offset al PC.
15
IMOVE In questo caso è l’unità di controllo a porre l’operando immediato sul bus_b, passando attraverso il blocco che gestisce l’espansione a 32 bit delle costanti i nove bit del campo immediate data. Successivamente abilito la scrittura dei registri. Il banco di registri deve consentire la scrittura, le uscite sui bus a, b sono in alta impedenza. Si può fare ponendo i controlli di lettura in modo che siano attivate le uscite per l’EMI, oppure ponendo sel_c uguale ad uno dei 2 sel a, b con write posto ad 1 che porta tutto in alta impedenza. L’alta impedenza per l’EMI si realizza negando i segnali di fetch. In entrambi i casi occorre attendere un ciclo poiché sono unità che reagiscono ai segnali applicati, solo sul fronte di clock. MOVE Un registro viene chiamato sul bus (ne occorre uno solo posso utilizzare il bus_a) dai registri o dall’EMI, lo faccio passare attraverso l’ALU, dopodiché si abilitano i controlli per la scrittura. Anche in questo caso può essere diretta o indiretta, nulla di nuovo NOP Eseguire una nop significa non fare nulla. Tutti i controlli sono posti a zero, per cui i registri non leggono, l’EMI non preleva dati dall’esterno, PC ed il modulo di espansione costanti presentano le uscite in alta impedenza... Fetch istruzione Il fetch di un’istruzione dalla memoria esterna non è un’istruzione programmabile, ma è un’operazione che deve essere comunque effettuata dall’unità di controllo tra ogni istruzione, tranne che in seguito ad istruzioni tipo branch. Il modo di procedere ricalca invece il comportamento delle istruzioni di branch, anche in questo caso infatti deve essere eseguita la somma tra il PC e 1, verranno trascurati i valori di riporto, aggiornato il PC col nuovo valore calcolato che sarà anche utilizzato dall’EMI per prelevare l’istruzione corrispondente in memoria. Altre operazioni L’unità di controllo infine dovrà gestire il reset del processore e l’inizializzazione del processore stesso. Ciò deve avvenire non appena viene asserito dall’esterno un apposito segnale di reset (o wake up). Lo stato di riposo corrisponde ad una indefinita successione di nop, dopo il reset deve essere effettuato il fetch di un’istruzione senza utilizzare il PC. L’unità di controllo invia una costante nulla al modulo di espansione delle costanti, e seleziona l’ingresso b come uscita dell’ALU. Ottengo quindi l’indirizzo 0, che viene memorizzato nel program counter mentre l’Emi viene impostata per prelevare l’istruzione dalla memoria esterna. Caricata l’istruzione si procede al solito modo.
16
Simulazioni Il processore è stato sottoposto a molteplici simulazioni, sia dei singoli componenti sia del processore nel suo insieme. Come ultimo test è stato creato un file ascii contenente stringhe di 1 e 0, per un totale di 32 per riga. Il file è stato caricato in memoria ed il processore ha interpretato le sequenze di bit come istruzioni, eseguendole. Di seguito riportiamo il programma utilizzato per le simulazioni con la spiegazione delle sequenze binarie. Codice
Istruzione
Descrizione
0
101 111111111111
100101 1 1111111110
imove
Costante #-2 in reg 5
1
101 000000000000
100110 1 0000000100
imove
Costante #+6 in reg 6
2
101 000000000000
100111 1 0000100111
imove
Costante #39 in reg 7
3
000 100101 100110 000111 0 0000000000
sum
Somma, mem 7 = reg 5 + reg 6
4
101 000000000000
imove
Costante #40 in reg 8
5
010 100111 100110 001000 0 0000000000
mul
Moltiplica mem 8 = reg 7 * reg 6
6
011 001000 000000 111111 1 0000000000
move
Muovi mem 8 in reg 31
7
110 111111 000100 111110 0 0000000000
bclr
Azzera il bit 2 del reg 31 in reg 31
8
101 000000000000
101001 1 0000100000
imove
Costante #32 in reg 9
9
011 111110 000000 001001 1 0000000000
move
Muovi reg 31 in mem 9
10
111 100110 000000 000000 0 0000000011
branch
Salta di #+3 se reg 5>0
11
000 000000 000000 000000 0 0000000000
sum
Somma mem 0 = mem 0 + mem 0
12
000 000000 000000 000000 0 0000000000
sum
Somma mem 0 = mem 0 + mem 0
13
101 000000000000
imove
Costante #33 in reg 9
14
110 100111 100100 001001 1 0000000000
bset
Azzera il bit 2 del reg 7 in mem 9
15
101 000000000000
imove
Costante #34 in reg 9
16
010 100101 100110 001001 0 0000000000
mul
Moltiplica mem 9 = reg 5 * reg 6
17
010 000000000000000000
nop
18
011 111110 000000 100000 1 0000000000
move
Muovi reg 30 in link 0
19
101 000000000000
imove
Costante #35 in reg 9
20
000 100101 100000 001001 0 0000000000
sum
Somma mem 9 = reg 5 + link 0
101000 1 0000101000
101001 1 0000100001
101001 1 0000100010
1 0000000000
101001 1 0000100011
17
21
101 000000000000
22
imove
Costante #36 in reg 9
111 100111 000000 001001 1 0000000000
not
Mem 9 = reg 7 negato bit a bit
23
010 000000000000000000
1 0000000000
nop
24
010 000000000000000000
1 0000000000
nop
25
010 000000000000000000
1 0000000000
nop
26
010 000000000000000000
1 0000000000
nop
27
010 000000000000000000
1 0000000000
nop
28
010 000000000000000000
1 0000000000
nop
29
010 000000000000000000
1 0000000000
nop
30
010 000000000000000000
1 0000000000
nop
31
010 000000000000000000
1 0000000000
nop
32
000 000000 000000 000000 0 0010001000
sum
Somma Link 0 = link 0 + link 0
33
000 000000 000001 000000 0 0000100111
sum
Somma link 0 = link 0 + link 0
34
111 111111 111111 111111 1 1111110111
not
Reg 31 = reg 31 negato bit a bit
35
000 000000 000000 000000 0 0010000110
sum
Somma link 0 = link 0 + link 0
36
111 111111 111111 111111 1 1111011000
not
Reg 31 = reg 31negato bit a bit
18
101001 1 0000100100
Elenco file Il processore è stato dunque realizzato in VHDL strutturale, le varie parti precedentemente descritte sono state realizzate come entità indipendenti ciascuna scritta in un proprio file e poi connesse insieme. Vediamo quindi quali sono i file che compongono il processore con una descrizione sommaria del loro funzionamento e la loro posizione gerarchica, e la loro descrizione VHDL. one_bit_adder.vhd Ingressi Uscite Descrizione:
abit, bbit, cinbit (1) sumbit, coutbit (1)
Addizionatore ad un bit. Somma sumbit = abit + bbit + cinbit. In oltre ho il riporto coutbit.
add32.vhd Ingressi Uscite Descrizione:
a,b (32), cin (1) sum (32) , cout (1)
Calcola l'addizione con 32 bit. Sum = a+ b + cin, e genera il riporto cout. L’ L’ingresso cin è sempre essere impostato a 0.
sub32.vhd Ingressi Uscite Descrizione:
as, bs (32), cins(1) sums (32), couts (1)
Calcola la sottrazione as – bs. Anche in questo caso il roporto è 0, sempre
badd32.vhd Ingressi Uscite Descrizione:
Sommatore di booth utilizzato nell'omonimo moltiplicatore
bmul32.vhd Ingressi Uscite Descrizione:
a,b (32), c (32), mul_oflow (1)
Moltiplicatore di booth. Calcola il prodotto tra a e b, l'uscita c è a 32 bit.Il riporto in uscita serve al solito per impostare il flag di overflow.
shifter_32bit.vhdl Ingressi Uscite Descrizione:
Input (32); shift_type (1) output (32)
Effettua lo shift di 32 bit in ingresso a destra se shift_type = 0, a sinistra se è 1. I bit aggiunti agli estremi sono sempre pari a 0
xor32.vhdl Ingressi Uscite Descrizione:
a,b (32) outl (32)
L'uscita c si ottiene dallo xor logico tra a, b.
and32.vhdl Ingressi Uscite Descrizione:
a, b (32) c (32)
L'uscita c si ottiene da l'and logico tra a, b.
or32.vhdl Ingressi Uscite Descrizione:
a, b (32) c (32)
L'uscita c si ottiene da l'or logico tra a, b.
Reg_with_link.vhdl Ingressi Uscite Descrizione:
Vedi documento
bit_clear.vhdl Ingressi Uscite Descrizione:
reg (32), indice_reg (5); temp (32);
Prende in ingresso il binario a 32 bit indicato con reg (contenuto nel bus_a della microarchitettura), ed imposta il bit indice_reg-esimo (a partire da destra) a 0.
Cmp.vhd Ingressi uscite descrizione
a, b (32); c (1);
È un comparatore, l’uscita è 1 se a > b
bit_set.vhdl Ingressi Uscite Descrizione:
reg (32), indice_reg (5); temp (32);
Analogo a bit clear, ma imposta il valore del bit selezionato ad 1..
ALU.vhd Ingressi
a, b (32);
Uscite Descrizione:
y (32), ovflow (1), cmp (1);
ctrl (4), indice_reg (4), shift_type (1); Mette insieme i pezzi definiti per fare operazioni aritmetico logiche, in modo del tutto combinatorio. Oltre ai segnali ovvi quali due ingressi a, b, e l’uscita y, ci cono 4 bit per selezionare l’operazione da svolgere (ctrl), alcuni segnali per le operazioni di shift, bit clear, bit set, il segnale di overflow in uscita aggiornato all’ultima operazione che può dare overflow, è il segnale del comparatore.
Memoria.vhd Ingressi Uscite
Descrizione:
È la memoria del processore. Vedi documento per maggiori informaz.
PC_reg.vhd Ingressi Uscite Descrizione:
i (32); PC2a, PCw (1); u (32);
È il registro del PC. Completamente asincrono, il PC reagisce immediatamente ai segnali di controllo. Sul fronte di salita di PCw il PC in ingresso (i) viene memorizzato. Quando il segnale PC2a è alto il PC è abilitato sul bus_a, altrimenti la sua uscita (u) è posta in alta impedenza.
bit_expander.vhd Ingressi Uscite Descrizione:
i (10), sel, en (1); u (32)
Serve a prendere le costanti contenute nelle istruzioni (i), lunghe 9 o 10 bit, ed a trasformarle in uscita (u) in binari a 32 bit adatti per l’ALU. Se en = 0 l’uscita è in alta impedenza, se sel = 1 si considerano tutti i 10 bit dell’ingresso.
CU.vhd Descrizione:
L’unità di controllo del sistema
Microtransputer.vhd Descrizione:
L’entity per eccellenza, raccoglie tutti i pezzi precedenti
clock.vhdl É il clock di sistema, usato per le simulazioni
Struttura Microtransputer EMI.vhd reg_with_link.vhd CU.vhd ALU.vhd add32.vhd one_bit_adder.vhd
bmul32.vhd badd32.vhd one_bit adder add32
one_bit adder
sub32.vhd …
shifter_32bit.vhdl xor32.vhdl and32.vhdl or32.vhdl bit_clear.vhd bit_set.vhd cmp.vhd
bit_expander.vhd PC_reg
memory.vhd clock.vhdl