UNIVERSITÀ DI CATANIA
Facoltà di Ingegneria
corso di laurea in Ingegneria Informatica
Massimo Trubia
PROGETTO E IMPLEMENTAZIONE DI UN MODELLO DI
FLOATING POINT UNIT IN UN SIMULATORE DI CPU MIPS64
Tesi di Laurea
Relatore:
Chiar.mo Prof. V. Catania
Correlatori:
Ing. F. Fazzino
Ing. D. Patti
Anno Accademico 2006/2007
Indice
1 Introduzione
1.1
2
Obiettivo della tesi . . . . . . . . . . . . . . . . . . . . . . . . . .
2
1.1.1
EduMIPS64 . . . . . . . . . . . . . . . . . . . . . . . . . .
3
1.2
Contributi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3
1.3
Panoramica della tesi . . . . . . . . . . . . . . . . . . . . . . . . .
4
2 L’architettura MIPS64
5
2.1
Storia dei processori RISC . . . . . . . . . . . . . . . . . . . . . .
5
2.2
Architettura Load/Store . . . . . . . . . . . . . . . . . . . . . . .
7
2.3
Registri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
2.4
Indirizzamento . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
2.5
Instruction set . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9
2.5.1
Formati delle istruzioni . . . . . . . . . . . . . . . . . . . .
9
2.5.2
Raggruppamento per funzionalità . . . . . . . . . . . . . .
11
Pipelining . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
2.6.1
Implementazione di una pipeline a cinque stadi . . . . . .
16
2.6.2
Dipendenze . . . . . . . . . . . . . . . . . . . . . . . . . .
17
2.6.3
Hazard . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
20
2.6
I
3 La floating point unit dei processori MIPS64
23
3.1
Numeri in virgola mobile . . . . . . . . . . . . . . . . . . . . . . .
23
3.2
Standard IEEE 754-1985 . . . . . . . . . . . . . . . . . . . . . . .
24
3.2.1
Memorizzazione di un numero floating point . . . . . . . .
25
3.2.2
Valori speciali . . . . . . . . . . . . . . . . . . . . . . . . .
26
3.2.3
Eccezioni . . . . . . . . . . . . . . . . . . . . . . . . . . .
28
3.2.4
Arrotondamenti . . . . . . . . . . . . . . . . . . . . . . . .
30
3.2.5
Operazioni floating point . . . . . . . . . . . . . . . . . . .
31
3.3
Floating point unit e standard IEEE 754 . . . . . . . . . . . . . .
34
3.4
Registri floating point . . . . . . . . . . . . . . . . . . . . . . . .
35
3.4.1
FCSR . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
35
3.4.2
Altri registri . . . . . . . . . . . . . . . . . . . . . . . . . .
37
Instruction set . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
38
3.5.1
Trasferimento dati . . . . . . . . . . . . . . . . . . . . . .
38
3.5.2
Aritmetiche . . . . . . . . . . . . . . . . . . . . . . . . . .
39
3.5.3
Conversione . . . . . . . . . . . . . . . . . . . . . . . . . .
39
3.5.4
Spostamento operandi formattati . . . . . . . . . . . . . .
40
3.5.5
Salto condizionale . . . . . . . . . . . . . . . . . . . . . . .
40
Pipelining . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
41
3.6.1
Concorrenza sul register file . . . . . . . . . . . . . . . . .
42
3.6.2
Rilevamento degli hazard . . . . . . . . . . . . . . . . . . .
43
3.6.3
Pipeline FP del MIPS R4000 . . . . . . . . . . . . . . . .
44
3.5
3.6
4 La floating point unit di EduMIPS64
4.1
46
Operazioni floating point . . . . . . . . . . . . . . . . . . . . . . .
46
4.1.1
48
Gestore delle eccezioni sincrone . . . . . . . . . . . . . . .
II
4.2
4.3
4.4
4.1.2
Addizione . . . . . . . . . . . . . . . . . . . . . . . . . . .
49
4.1.3
Moltiplicazione . . . . . . . . . . . . . . . . . . . . . . . .
50
4.1.4
Divisione . . . . . . . . . . . . . . . . . . . . . . . . . . . .
51
Registri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
53
4.2.1
FCSR . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
53
Instruction set . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
57
4.3.1
Classe Instruction . . . . . . . . . . . . . . . . . . . . . . .
57
4.3.2
Classi base dell’instruction set . . . . . . . . . . . . . . . .
58
4.3.3
Ereditarietà dalla classe ComputationalInstructions . . . .
59
4.3.4
Ereditarietà dalla classe FlowControlInstructions . . . . .
65
4.3.5
Ereditarietà dalla classe LDSTInstructions . . . . . . . . .
66
Pipelining . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
67
4.4.1
Pipeline floating point . . . . . . . . . . . . . . . . . . . .
67
4.4.2
File di configurazione della FPU . . . . . . . . . . . . . . .
68
4.4.3
Interazione tra CPU ed FPU . . . . . . . . . . . . . . . . .
69
5 Conclusioni
78
A Manuale utente della FPU di EduMIPS64
80
A.1 Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
80
A.2 Valori speciali . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
81
A.2.1 NaN o Invalid Operation . . . . . . . . . . . . . . . . . . .
81
A.2.2 Zeri o Underflow . . . . . . . . . . . . . . . . . . . . . . .
81
A.2.3 Infiniti od Overflow . . . . . . . . . . . . . . . . . . . . . .
82
A.2.4 Infiniti o Divide by zero . . . . . . . . . . . . . . . . . . .
82
A.3 Configurazione delle eccezioni . . . . . . . . . . . . . . . . . . . .
82
III
A.4 Direttiva .double . . . . . . . . . . . . . . . . . . . . . . . . . . .
84
A.5 Registro FCSR . . . . . . . . . . . . . . . . . . . . . . . . . . . .
84
A.6 Instruction set . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
85
IV
Elenco delle figure
2.1
Formati dell’ISA MIPS64 . . . . . . . . . . . . . . . . . . . . . . .
10
2.2
Pipeline di EduMIPS64 . . . . . . . . . . . . . . . . . . . . . . . .
16
3.1
Formato dei numeri FP in doppia precisione . . . . . . . . . . . .
25
3.2
Formato dei numeri FP in singola precisione . . . . . . . . . . . .
25
3.3
Mantisse prima della moltiplicazione . . . . . . . . . . . . . . . .
32
3.4
Risultato del prodotto fra le mantisse prima della normalizzazione
32
3.5
Prodotto normalizzato prima dell’arrotondamento . . . . . . . . .
32
3.6
Mantisse prima dell’addizione . . . . . . . . . . . . . . . . . . . .
33
3.7
Risultato della somma delle mantisse prima della normalizzazione
34
3.8
Mantissa normalizzata prima dell’arrotondamento . . . . . . . . .
34
3.9
Registro FCSR . . . . . . . . . . . . . . . . . . . . . . . . . . . .
36
3.10 Pipeline a 5 stadi . . . . . . . . . . . . . . . . . . . . . . . . . . .
41
3.11 Register file di un processore MIPS64 . . . . . . . . . . . . . . . .
42
4.1
Sommatore floating point (R1+R2=R3) . . . . . . . . . . . . . .
50
4.2
Moltiplicatore floating point . . . . . . . . . . . . . . . . . . . . .
52
4.3
Divisore floating point . . . . . . . . . . . . . . . . . . . . . . . .
52
4.4
Diagramma delle classi dei registri di EduMIPS64 . . . . . . . . .
54
4.5
Modalità di accesso alla classe FCSRRegister . . . . . . . . . . . .
54
V
4.6
Classe Instruction . . . . . . . . . . . . . . . . . . . . . . . . . . .
58
4.7
Classi base dell’istruction set . . . . . . . . . . . . . . . . . . . . .
59
4.8
Classi ereditate da ComputationalInstructions . . . . . . . . . . .
60
4.9
Ereditarietà dalla classe FlowControlInstructions
. . . . . . . . .
65
4.10 Ereditarietà dalla classe LDSTInstructions . . . . . . . . . . . . .
66
4.11 Diagramma UML delle classi della pipeline FP . . . . . . . . . . .
68
A.1 Configurazione delle trap per le eccezioni IEEE . . . . . . . . . .
83
A.2 Opzione che maschera le eccezioni sincrone(disabilita tutte le trap)
83
A.3 Finestra che notifica la trap . . . . . . . . . . . . . . . . . . . . .
83
A.4 Registro FCSR in EduMIPS64 . . . . . . . . . . . . . . . . . . . .
85
A.5 Configurazione modalità di arrotondamento . . . . . . . . . . . .
90
VI
Elenco delle tabelle
2.1
Campi delle istruzioni
. . . . . . . . . . . . . . . . . . . . . . . .
11
3.1
Dati di conversione decimale/single . . . . . . . . . . . . . . . . .
26
3.2
Parametri di formato dello standard IEEE 754 . . . . . . . . . . .
26
3.3
Rappresentazione dei valori speciali . . . . . . . . . . . . . . . . .
27
3.4
Istruzioni di trasferimento dati . . . . . . . . . . . . . . . . . . . .
39
3.5
Istruzioni aritmetiche . . . . . . . . . . . . . . . . . . . . . . . . .
39
3.6
Istruzioni di conversione . . . . . . . . . . . . . . . . . . . . . . .
40
3.7
Istruzioni di spostamento degli operandi formattati . . . . . . . .
40
3.8
Istruzioni di salto condizionale . . . . . . . . . . . . . . . . . . . .
40
3.9
Operazioni primitive della pipeline FP dell’R4000 . . . . . . . . .
45
3.10 Operazioni FP dell’R4000 . . . . . . . . . . . . . . . . . . . . . .
45
3.11 Stalli nelle unità funzionali . . . . . . . . . . . . . . . . . . . . . .
45
A.1 Esempi sui tipi di arrotondamento . . . . . . . . . . . . . . . . . .
90
VII
Listati
4.1
Scrittura dei codici condizionali sull’FCSR . . . . . . . . . . . . .
55
4.2
Scrittura dei bit di causa sull’FCSR . . . . . . . . . . . . . . . . .
55
4.3
Metodo che memorizza i dati della GUI sull’FCSR . . . . . . . . .
56
4.4
Porzione della funzione che effettua il confronto tra registri . . . .
63
4.5
Invocazione metodo EX e gestione eccezioni sincrone . . . . . . .
73
4.6
Gestione dello stadio ID . . . . . . . . . . . . . . . . . . . . . . .
76
1
Capitolo 1
Introduzione
1.1
Obiettivo della tesi
Nei corsi universitari in cui si studiano le architetture dei calcolatori si usano,
come ausilio didattico, simulatori che modellano in modo realistico questi sistemi.
In particolare, studiando vari tipi di processori e le istruzioni in codice Assembly
che essi possono elaborare, i simulatori assumono un ruolo preponderante, in
quanto consentono l’ispezione dei valori delle celle di memoria, dei registri e della
pipeline, mentre le istruzioni sono processate; valutano statistiche di calcolo, ed
eseguono diverse altre operazioni.
Di seguito sono elencati alcuni motivi per i quali, tra i vari tipi di processori,
sempre più spesso nei corsi universitari e nelle scuole tecniche si studiano le architetture MIPS. I progettisti hanno realizzato un set di istruzioni ”pulito”: le
istruzioni sono divise in soli tre tipi (R, I e J) che saranno illustrati nei capitoli
successivi. I processori RISC sono intrinsecamente più semplici rispetto ai CISC,
e da non trascurare è il fatto che MIPS è dominante nelle applicazioni embedded,
come fotocamere digitali, TV digitali, consolle da gioco, router di rete e cosı̀ via.
2
CAPITOLO 1. INTRODUZIONE
1.1.1
EduMIPS64
EduMIPS64 è originariamente nato come semplice porting GPL1 a Java Swing
del simulatore WinMIPS64. Il gruppo di sviluppatori di EduMIPS64 aveva deciso,
nella prima metà del 2006, di dar vita ad un progetto per creare un programma
con interfaccia utente molto simile a WinMIPS64, quest’ultimo eseguibile solo
su Microsoft Windows2 , disponibile agli utenti di tutte le piattaforme in quanto
totalmente riprogettato in Java.
Il programma è cresciuto molto rapidamente grazie al continuo supporto dei
vecchi sviluppatori, che lo hanno reso sempre più robusto, e alla crescita del gruppo, alimentato dagli allievi autori di tesine del corso di Laboratorio di Calcolatori.
Tale supporto ha reso, in meno di un anno, il simulatore abbastanza stabile per
essere utilizzato nei corsi di Laboratorio di Calcolatori dell’Università di Catania.
EduMIPS64 non ha ancora raggiunto la versione 1.0 perchè è stata finora assente la FPU (Floating Point Unit), componente essenziale di un simulatore di
CPU. L’obiettivo di questa tesi è la progettazione di un modello che emuli il coprocessore 1 floating point delle moderne architetture MIPS64 e la sua integrazione
in EduMIPS64.
1.2
Contributi
La FPU implementata modella con buona approssimazione concettuale la reale
architettura di un processore MIPS64; tuttavia, per evidenti esigenze di ottimizzazione nei calcoli, sono state impiegate alcune classi già presenti nel Java Runtime
Environment 1.5.
1
2
GPL - General Public License
WinMIPS64 è stato scritto in Microsoft Visual C++
3
CAPITOLO 1. INTRODUZIONE
L’implementazione delle eccezioni sincrone floating point, definite dallo standard IEEE 754, sfrutta il gestore degli interrupt già presente in EduMIPS64 e
sviluppato in un precedente lavoro di tesi.
Tutto il codice scritto per questa tesi sarà integrato nella versione 1.0 del
simulatore, tuttavia, fino all’uscita di questa versione, è possibile provare le nuove
funzionalità apportate scaricando, dal sito ufficiale [3], la versione svn.
1.3
Panoramica della tesi
Nel Capitolo 2 viene discussa l’archittettura delle CPU MIPS64. Si parla,
in particolare, dei dettagli implementativi di registri, memoria e pipeline. Nel
capitolo 3 viene illustrata la floating point unit di questo tipo di CPU e il modo
in cui è implementato lo standard IEEE 754 per che regola l’aritmetica floating
point dei calcolatori. Si approfondiscono le problematiche insite nel pipelining
delle istruzioni floating point, utilizzando come metro di paragone la pipeline del
processore R4000. Nel Capitolo 4 si parla della floating point unit di EduMIPS64
e dei relativi dettagli progettuali. Vengono illustrate le soluzioni alle più comuni
problematiche del pipelining, evidenziando le modifiche apportate al codice del
simulatore per simulare l’esecuzione delle istruzioni floating point. Nell’Appendice
A è presente il manuale utente della floating point unit di EduMIPS64; essa
verrà estratta e inserita, come capitolo, nel Manuale utente di EduMIPS64 per la
versione 1.0 del simulatore.
4
Capitolo 2
L’architettura MIPS64
In questo capitolo viene illustrata l’architettura dei processori MIPS64. Dopo
una breve prospettiva storica che si può approfondire in [12], riguardante l’evoluzione
di queste CPU, vengono illustrati i 32 registri general-purpose e come essi coadiuvano l’indirizzamento in memoria. Nel Paragrafo 2.5 viene fornita una panoramica dell’instruction set ISA MIPS64, mostrando il raggruppamento funzionale delle
istruzioni. Dal Paragrafo 2.6 sono illustrati gli stadi e la struttura di una pipeline a
5 stadi. Successivamente si parla delle problematiche dovute alla sovrapposizione
dell’esecuzione delle istruzioni e come esse vengono risolte limitando parzialmente
la sovrapposizione.
2.1
Storia dei processori RISC
L’architettura dominante nel mercato dei PC è denominata CISC (Complex
Instruction Set Computer): tale nome deriva dalla natura complessa del set di
istruzioni (ISA)1 .
Istruzioni complesse supportano operazioni e strutture dati usate dai linguaggi
1
ISA - Instruction set architecture
5
CAPITOLO 2. L’ARCHITETTURA MIPS64
di alto livello (HLLs)2 , ma la loro lunghezza cresce a dismisura dal momento che
esse devono includere ad esempio gli indirizzi degli operandi in memoria. Tutto
ciò complica la decodifica , lo scheduling e il pipelining delle stesse.
Nei primi anni ’80 si iniziò a pensare a set di istruzioni più semplici e fu coniato
il termine RISC (Reduced Instruction Set Computer), anche se l’obiettivo non era
ridurre il numero di istruzioni, bensı̀ la complessità.
L’origine dei processori RISC si può ricondurre a tre progetti: l’IBM 801, il
RISC di Berkeley, e il MIPS di Standford; essi riscossero grande successo per via
dei vantaggi delle loro performance rispetto alle tradizionali architetture.
L’IBM 801 fu soltanto un progetto sperimentale, mentre gli altri due ebbero
successivi sviluppi. Nel 1980, Patterson e i suoi colleghi dell’Università di Berkeley costruirono due computer chiamati RISC-I e RISC-II, i quali supportavano in
memoria istruzioni a 16 e 32 bit. Nel 1981, Hennessy e i suoi colleghi pubblicarono
una descrizione del MIPS Standford. Quest’ ultimo presentava un efficiente pipelining e uno scheduling compiler-assisted della pipeline, entrambi aspetti originali
dell’architettura MIPS.
L’ISA MIPS si è evoluto nel tempo rispetto all’originale MIPS I, attraverso
l’ISA MIPS V, fino alle attuali architetture MIPS32 e MIPS64. I punti di svolta
si presentarono nel MIPS III, con l’aggiunta di interi e indirizzi a 64 bit, e nelle
versioni MIPS IV-V, con netti miglioramenti delle operazioni floating point, del
codice generato e dello spostamento dati.
L’architettura MIPS32 è basata sull’ISA MIPS II, con l’aggiunta di istruzioni
MIPS III,MIPS IV e MIPS V. Invece l’architettura MIPS64 si basa sull’ISA MIPS
V e gode della compatibilità all’indietro con MIPS32.
2
HLLs - Higher-Level Languages
6
CAPITOLO 2. L’ARCHITETTURA MIPS64
In conclusione, entrambe le architetture forniscono un sostanziale vantaggio nel
compromesso costo/prestazioni risultato dei miglioramenti ottenuti in discipline
contigue: tecnologia VLSI, organizzazione delle CPU, progettazione di sistemi
operativi e compilatori.
2.2
Architettura Load/Store
I processori MIPS usano il design load/store per eliminare i lunghi tempi di
accesso alla memoria. Generalmente occorre più tempo ad eseguire un’operazione
in memoria di quanto ne sia necessario se gli operandi si trovassero già nei registri.
Sfruttando questo principio i processori vengono dotati di diversi registri on-chip
in modo che le operazioni vengano eseguite solo su di essi. L’accesso in memoria
è comunque garantito esclusivamente tramite l’uso di apposite istruzioni di load
e store. Tale tecnica consente di ridurre il numero di accessi alla memoria e
semplificare drasticamente il set di istruzioni.
2.3
Registri
L’architettura MIPS64 è costituita da 32 registri general-purpose (GPR) ognuno di 64 bit, due dei quali vengono usati per funzioni speciali; il registro 0,
chiamato anche $zero, è cablato in hardware e serve a fornire lo zero stesso, mentre il registro 31, detto anche $ra, è usato per memorizzare l’indirizzo di ritorno
di una chiamata a procedura. Altri due registri speciali, LO e HI, vengono usati
nelle operazioni di moltiplicazione e divisione, per memorizzarne i risultati. Nella moltiplicazione tra interi di 64 bit è prodotto un risultato lungo 128 bit, la
cui metà più significativa è posta nel registro HI, e l’altra nel registro LO. Nella
7
CAPITOLO 2. L’ARCHITETTURA MIPS64
divisione intera, invece, il quoziente è memorizzato in LO, e il resto nel registro
HI.
È pratica comune, come si è visto per i registri speciali, usare dei nomi
mnemonici o alias in luogo del numero del registro. Gli alias per i 32 registri
general-purpose sono nell’ordine: zero, at, v0, v1, a0, a1, a2, a3, t0, t1, t2, t3, t4,
t5, t6, t7, s0, s1, s2, s3, s4, s5, s6, s7, t8, t9, k0, k1, gp, sp, fp, ra. Convenzionalmente, si consiglia di usare i registri $v0 e $v1 per ritornare i risultati da una
procedura, e i registri da $a0 ad $a3 per passare, al più, quattro argomenti alle
procedure; i rimanenti argomenti vengono passati mediante stack. I registri da $t0
a $t7 sono temporanei e non dovrebbero essere riservati a chiamate di procedura,
al contrario dei registri da $s0 ad $s7, che, essendo callee-saved, sono riservati a
questo scopo. L’ultimo fra i più importanti è $sp, che punta alla cima dello stack.
Per l’utilizzo degli altri registri consultare [10].
2.4
Indirizzamento
La modalità di indirizzamento si riferisce a come gli operandi sono specificati.
Nelle architetture MIPS l’indirizzo di memoria di un operando è calcolato sommando il contenuto di un registro ad una costante. L’indirizzo si può generalizzare
con la notazione offset(base), dove base è il nome del registro nel formato Rx3 o
$alias, al quale verrà sommato un offset, cioé un numero con segno di 16 bit.
Segue un esempio di un’istruzione dell’ISA MIPS64 che consente il caricamento
dei dati della prima cella di memoria sul registro R8:
LD $t0, 0($zero);per i nomi degli alias vedere 4.2.
L’accesso in memoria è regolamentato dai vincoli di allineamento. La memoria
3
x - intero compreso tra 0 e 31
8
CAPITOLO 2. L’ARCHITETTURA MIPS64
è composta da doubleword (64 bit). Ogni doubleword è composta da due word (32
bit) affiancate, formate a loro volta da due halfword (16 bit) ciascuna. Le halfword,
infine, sono costituite ognuna da due byte. In funzione di queste definizioni, gli
accessi alle doubleword devono prevedere indirizzi divisibili per 8, quelli alle word
divisibili per 4, quelli alle halfword per 2 e quelli ai byte indirizzi qualunque entro
i limiti di dimensione della memoria.
Nonostante ne sia sconsigliato l’utilizzo, se non per applicazioni particolari,
gli istruction set MIPS32 e MIPS64 mettono a disposizione alcune istruzioni di
load-store non allineato che riguardano solo doubleword e word: LWL, LWR, SWL,
SWR, LDL, LDR, SDL, SDR.
2.5
Instruction set
Come accennato in precedenza, i processori RISC hanno degli istruction set
più semplici rispetto alle macchine CISC. Ciò è dovuto a diversi fattori, tra cui:
il fatto che tutte le operazioni sui registri ne cambiano interamente il valore; il
fatto che le sole operazioni che accedono alla memoria sono le load e le store; il
numero limitato di istruzioni e l’uniformità della loro lunghezza, fissata a 32 bit
per i processori MIPS. Tutti questi aspetti consentono di realizzare solo pochi
formati predefiniti per le istruzioni.
2.5.1
Formati delle istruzioni
L’ISA MIPS64 prevede tre formati di istruzioni, illustrati in Figura 2.1:
• Immediate (I-type): È il formato usato da tutte le istruzioni load-store. Il
valore immediato è uno short, e viene usato sia nelle operazioni aritmetiche, alle quali si vuole fornire come operando un piccolo numero senza
9
CAPITOLO 2. L’ARCHITETTURA MIPS64
caricarlo prima in un registro, sia nelle istruzioni di salto, che possono incrementare/decrementare il PC di un offset a 18 bit, ovvero saltare in una
finestra di 128 Kbytes(PC-relative branches). Per evitare questa limitazione
nel salto si usano le istruzioni J-Type.
• Jump (J-type): Un campo di 26 bit di questo formato viene combinato con
i bit di ordine più alto del PC per ottenere l’indirizzo assoluto. Formando
l’indirizzo a cui saltare mediante questa concatenazione, piuttosto che sommare un offset al PC, si ha il vantaggio di muoversi in una regione di 256
MB (PC-region branch).
• Register (R-type): È il formato usato dalle istruzioni aritmetiche e logiche,
nonché dalle istruzioni di salto, che utilizzano un registro contenente l’indirizzo di memoria a cui saltare.
Figura 2.1: Formati dell’ISA MIPS64
10
CAPITOLO 2. L’ARCHITETTURA MIPS64
Campo
Descrizione
Dim. bit
opcode
codice binario dell’istruzione
6
rd
registro di destinazione
5
rs
registro sorgente
5
rt
registro target (sorgente o destinazione)
5
immediate immediato con segno usato per operandi logici,
aritmetici con segno, offset e salti PC-relative
16
inst index fornisce i 26 bit che vengono traslati di due a
sinistra per dare i 28 bit meno significativi
dell’indirizzo di destinazione del salto.
26
sa
entità dello shift
5
function
usato per specificare funzioni all’interno
dell’opcode SPECIAL
6
Tabella 2.1: Campi delle istruzioni
2.5.2
Raggruppamento per funzionalità
Le istruzioni dell’ISA MIPS64 possono anche essere raggruppate secondo questi
gruppi funzionali:
- Load e Store
- Computazionali
- Jump e Branch
- Multiformi
- Coprocessore
per ulteriori approfondimenti sulle singole istruzioni una lista completa è disponibile in [8].
Load e Store
Le istruzioni Load e Store sono le uniche a poter spostare dati dalla memoria ai registri e viceversa. Per ogni unità di dati (si veda il paragrafo 2.4) es11
CAPITOLO 2. L’ARCHITETTURA MIPS64
istono almeno un’istruzione di caricamento con segno, una senza segno, e una di
memorizzazione dell’unità stessa.
Alcune istruzioni speciali consentono di selezionare le modalità di indirizzamento (per esempio, SDXC1 nella FPU) e di effettuare aggiornamenti atomici delle celle di memoria (per esempio, lettura-modifica-scrittura - LL/SC) per
implementare meccanismi di sincronizzazione come semafori e mutex.
Computazionali
A questa categoria appartengono le istruzioni che eseguono l’aritmetica a complemento a 2 su interi rappresentati con questa notazione. Le istruzioni di somma
e sottrazione in cui compare il termine ”unsigned”, contrariamente al significato
del termine, non sono soggette alla verifica dell’overflow.
Sono incluse anche delle versioni unsigned di divisione e moltiplicazione, e una
serie di istruzioni che effettuano operazioni logiche e di shift. Le operazioni logiche
non sono sensibili alla lunghezza del registro.
Le istruzioni computazionali possono essere raggruppate in tre categorie: ALU
immediate, ALU a due operandi, ALU a tre operandi, istruzioni di shift, istruzioni
di divisione e moltiplicazione. L’operando immediato è trattato come un valore
con segno per le istruzioni aritmetiche e di confronto, e come valore logico per le
istruzioni di questo tipo.
Jump e Branch
Le istruzioni che consentono di effettuare un salto PC-relative (si veda il paragrafo 2.5) sono condizionali, in quanto è possibile definire un test la cui verità
dà il via al salto, mentre quelle che saltano in una PC-region sono dette uncon-
12
CAPITOLO 2. L’ARCHITETTURA MIPS64
ditional, e la maggior parte della loro lunghezza è riservata alla memorizzazione
dell’indirizzo di destinazione.
L’ultima tipologia è il salto assoluto, praticato sia sostituendo al PC l’indirizzo
in un registro puntato dall’istruzione, sia eseguendo, oltre a questa azione, il
salvataggio in un registro (di solito R31) del PC, per consentirne successivamente
il recupero a seguito di una chiamata ad una procedura.
I salti hanno un ritardo architetturale di un’istruzione, e l’istruzione immediatamente dopo quella di salto si dice essere nel branch delay slot. Se l’istruzione di
salto è una branch, allora quella nel delay slot verrà anch’essa eseguita; se quella
di salto è un’istruzione branch-likely, l’istruzione nel delay slot non verrà eseguita
se il salto non avverrà (branch not taken).
Varie
Appartengono a questo gruppo le istruzioni di eccezione (SYSCALL, TRAP
e BREAK), quelle di spostamento condizionale, ed infine l’istruzione NOP. Le
istruzioni di eccezione trasferiscono il controllo al gestore delle eccezioni nel kernel.
Esistono due tipi di eccezioni: conditional e unconditional. Le prime vengono
lanciate dalle istruzioni di trap (per esempio TEQ,TGE, TLT), le seconde vengono
lanciate dalle system call e dalle istruzioni di breakpoint (BREAK,SYSCALL).
L’istruzione SYSCALL non definisce con precisione i servizi che il sistema operativo deve fornire, ma essa contiene un codice recuperabile dal gestore delle
eccezioni per invocare un’opportuna system call. Per ulteriori informazioni sulla
SYSCALL implementata in EduMIPS64 consultare [1].
L’ISA MIPS64 include istruzioni per lo spostamento condizionato di un GPR4
4
GPR - General Purpose Register
13
CAPITOLO 2. L’ARCHITETTURA MIPS64
in un altro, basato sul valore di un terzo GPR (per esempio MOVF,MOVZ). Per
gli spostamenti condizionali floating point si veda il Paragrafo A.6.
L’istruzione NOP è attualmente codificata come una istruzione all-zero, ed
effettua una non-operazione. È interpretata dall’hardware come SLL r0,r0,0 e
viene generalmente usata per riempire i delay slots e le sequenze di allineamento.
Coprocessore
I coprocessori sono unità di esecuzione alternative alla CPU. L’architettura
MIPS ne prevede quattro numerati da 0 a 3 (CP0 a CP3); essi vengono abilitati
mediante operazioni privilegiate fornite dal System Control Coprocessor (CP0).
Il coprocessore 0 serve, dunque, al controllo del sistema, mentre gli altri tre sono
usati per la FPU.
Le istruzioni coprocessore si dividono in due gruppi: istruzioni load e store,
che hanno il compito di scrivere e leggere i registri di un dato coprocessore (per
esempio LDCz,SDCz con z=1 o z=2), e le istruzioni coprocessor-specific, definite
interamente dal coprocessore in uso.
2.6
Pipelining
Ciascuna istruzione dell’ISA MIPS64, nell’ipotesi in cui non avvenisse il pipelining, verrebbe elaborata in 5 cicli di clock successivi del processore:
1. Instruction Fetch(IF): il PC viene prima inviato alla memoria per prelevare l’istruzione corrente, poi aggiornato aggiungendo 4 al suo valore (ogni
istruzione è lunga 4 byte).
2. Instruction Decode(ID): L’istruzione è decodificata, e vengono rilevati i registri sorgenti dal register file. In questo ciclo viene effettuato il test di
14
CAPITOLO 2. L’ARCHITETTURA MIPS64
uguaglianza per un eventuale salto; viene inoltre esteso, se richiesto, il segno
dell’offset, e calcolata la destinazione del possibile salto aggiungendo l’offset
al PC già incrementato.
3. Execution(EX): L’ALU lavora sugli operandi preparati nel precedente ciclo.
Se l’istruzione comporta l’accesso in memoria, è calcolato l’ indirizzo effettivo come somma di base e offset; se ha due registri sorgenti, viene letto
l’opcode ed effettuata la relativa operazione sugli operandi; se, infine, presenta un immediato e un registro sorgente, è necessario estendere il segno
dell’immediato prima di effettuare l’operazione.
4. Memory Access (MEM): Se l’istruzione è una load, la memoria effettua una
lettura usando l’indirizzo effettivo calcolato nel ciclo precedente; se è una
store, invece, scrive i dati del registro sorgente nella locazione all’indirizzo
calcolato.
5. Write-back (WB): In questo ciclo, per le istruzioni che implicano la scrittura
sui registri e per quelle di caricamento, si effettua la scrittura dei risultati.
Per l’esecuzione in pipeline di istruzioni che effettuano operazioni aritmetiche
su operandi in virgola mobile, di per sè più complesse rispetto alle operazioni intere, che richiedono un solo ciclo di clock per lo stadio EX, si usufruisce delle varie
unità funzionali della floating point unit residente su un coprocessore dedicato.
In particolare, l’addizionatore esegue lo stadio EX di somme e sottrazioni in 4
cicli, il moltiplicatore esegue il prodotto in 7 cicli, e il divisore effettua la divisione
in 24 cicli (si veda la Figura 2.2). Nel capitolo successivo si approfondiranno le
problematiche del pipelining di istruzioni floating point.
15
CAPITOLO 2. L’ARCHITETTURA MIPS64
Figura 2.2: Pipeline di EduMIPS64
2.6.1
Implementazione di una pipeline a cinque stadi
Nonostante l’esecuzione di ogni istruzione continui ad impiegare 5 cicli per
essere completata, si può apportare un miglioramento all’algoritmo di esecuzione
precedente sfruttando il fatto che, durante ogni ciclo di clock, l’hardware può
iniziare ad eseguire una nuova istruzione, e contemporaneamente delle porzioni
diverse di altre 5, purché le risorse usate non siano le stesse. Ad esempio, l’ALU
non può eseguire il calcolo di un indirizzo di memoria ed effettuare contemporaneamente una sottrazione.
In base a quanto detto, si possono fare le seguenti considerazioni:
- dal momento che gli accessi in memoria avvengono sia nello stadio IF, in cui
vengono prelevate le istruzioni, che in MEM, in cui si opera la scrittura/lettura dei dati, occorre dividere la memoria dati da quella delle istruzioni.
- Il register file, o banco registri, è usato sia in ID che in WB, rispettivamente
in lettura e in scrittura; per tale ragione occorre separare le due operazioni,
16
CAPITOLO 2. L’ARCHITETTURA MIPS64
realizzando nel primo mezzo ciclo di clock la scrittura, e nel secondo mezzo
ciclo la lettura del file.
- Per iniziare una nuova istruzione ad ogni tick di clock, durante lo stadio
IF, si deve incrementare il PC, e calcolare l’indirizzo di destinazione del
salto che si potrebbe verificare in ID. Un altro problema, che sarà affrontato
successivamente, è che, nel caso di istruzione di salto, il PC verrà modificato
soltanto nello stadio ID.
- Per assicurare che le istruzioni in stadi differenti della pipeline non interferiscano tra loro, vengono usati dei pipeline registers (per esempio EX/MEM,
MEM/WB), che riportano i risultati dell’esecuzione di uno stadio come
ingressi per il successivo stadio.
- La realizzazione di una pipeline incrementa il numero di istruzioni completate per unità di tempo, ma non riduce il tempo di esecuzione di una
singola istruzione. L’idea di creare una sovrapposizione nell’esecuzione delle
istruzioni, risalente al 1985, prende il nome di Instruction-Level Parallelism
(ILP); si vedrà tuttavia nel paragrafo 2.6.3 che, per via degli hazard, le
prestazioni ottenute con questa tecnica sono inferiori a quelle che si otterrebbero con una pipeline ideale (un ciclo per istruzione ovvero CPI=1).
2.6.2
Dipendenze
Per comprendere fino a che punto l’Instruction-Level Parallelism può essere
sfruttato, occorre determinare come un’istruzione può dipendere da un’altra.
Soltanto se non c’è alcuna dipendenza le istruzioni possono essere eseguite let-
17
CAPITOLO 2. L’ARCHITETTURA MIPS64
teralmente in parallelo, altrimenti si ha una parziale sovrapposizione e dev’essere
mantenuto l’ordine di esecuzione.
Si hanno tre tipi di dipendenza:
• Dati: quando il risultato prodotto da un’istruzione dev’essere usato da un’altra istruzione, si può allargare questo principio ad n istruzioni, creando una
catena di n-1 dipendenze dati. Nell’esempio sottostante si evince la dipendenza della DSUB dalla DADD: DSUB non può leggere gli operandi (R1,R5)
finché il valore memorizzato in R1 non diventi valido (DADD raggiunge lo
stadio WB e scrive R1).
DADD R1 , R2 , R3
DSUB R4 , R1 , R5
• Nomi: quando due istruzioni usano lo stesso registro o la stessa cella di
memoria nonostante non ci sia un flusso dati tra esse. Si ha un’antidipendenza
dati se un’istruzione deve leggere un registro/cella appena scritto da un’altra istruzione, una dipendenza di output se due istruzioni devono scrivere
sullo stesso registro/cella. In entrambi i casi l’ordine di esecuzione delle
istruzioni dev’essere mantenuto, per garantire, rispettivamente, di aver letto il valore appena aggiornato, e scritto per ultimo il registro/cella. Anche
se molto simile alla dipendenza dati, la dipendenza nomi può essere risolta con una tecnica chiamata register renaming, nella quale si utilizzano dei
registri non permanentemente associati all’architettura, ma dinamicamente
allocati quando richiesto.
Nell’esempio che segue, scritto in pseudocodice assembly MIPS, è illustrata
una dipendenza nomi e come questa può essere risolta rinominando il registro
in conflitto;
18
CAPITOLO 2. L’ARCHITETTURA MIPS64
MUL
ADD
ADD
DIV
R2 , R2 , R3
R4 , R2 , 1
R2 , R3 , 1
R5 , R2 , R4
;
;
;
;
R2
R4
R2
R5
=
=
=
=
R2
R2
R3
R2
∗
+
+
/
R3
1
1
R4
L’istruzione 3 non può andare avanti finché la 1 non ha terminato, perchè
c’è una dipendenza di output fra le istruzioni 1 e 3 (entrambe scrivono su
R2). Esiste anche un’antidipendenza dati tra le istruzioni 2 e 3 (l’istruzione
3 sovrascrive uno degli argomenti della 2). Se rinominasse il registro R2,
creandone tre versioni cronologiche diverse (R21,R22,R23)
MUL
ADD
ADD
DIV
R21 , R20 , R3
R4 , R21 , 1
R22 , R3 , 1
R5 , R22 , R4
l’istruzione 3 potrebbe eseguire subito, perchè si servirebbe di un registro diverso da quello delle istruzioni 1 e 2. Per ulteriori approfondimenti sui metodi di implementazione di questa tecnica, come i buffer di riordino, consultare
[2]
• Controlli: quando l’esecuzione di un’istruzione dipende dal verificarsi di
qualche condizione. Un semplice esempio usato in [5] ne agevola la comprensione:
i f p1 {
S1 ;
}
i f p2 {
S2 ;
}
S1 è control dipendent da p1, ed S2 è control dipendent da p2, ma non da p1.
L’esecuzione delle istruzioni nell’ordine assicura, dunque, che un’istruzione
che si trova prima di un salto condizionato (branch) venga eseguita prima
19
CAPITOLO 2. L’ARCHITETTURA MIPS64
del salto stesso; analogamente, un’istruzione che è control dipendent da un
branch non viene eseguita finché il salto non va nella sua direzione.
2.6.3
Hazard
Un hazard si può definire come una situazione di allarme per cui, essendosi
verificata una dipendenza fra istruzioni molto vicine, se si continua ad eseguire le
istruzioni con la massima sovrapposizione prevista dal pipelining c’è il rischio di
un errore durante il flusso di esecuzione del programma
In queste circostanze può essere necessario introdurre uno stallo per alcune
istruzioni fino a quando l’hazard non venga risolto. Quando un’istruzione subisce
uno stallo, anche quelle emesse successivamente devono essere fermate, mentre le
istruzioni emesse prima possono continuare l’esecuzione.
Hazard strutturale
Un hazard strutturale si verifica quando due o più istruzioni si contendono
una risorsa non duplicabile, per cui la pipeline deve fermare alcune istruzioni
finchè l’unità richiesta non diventi disponibile. Questo tipo di hazard si verifica
sia per le unità funzionali che non sono fully-pipelined, ovvero non applicano il
pipelining delle istruzioni in quanto ne possono elaborare una alla volta (il divisore
floating point ne è un esempio), sia per i riferimenti contemporanei alla memoria
condivisa. Nel paragrafo 4.4 vengono approfonditi i dettagli progettuali della
pipeline, tenendo in grande considerazione l’eventualità di hazard strutturali.
Data hazard
Un data hazard si verifica quando, in presenza di una dipendenza dati, potrebbe
essere cambiato l’ordine di accesso agli operandi coinvolti nella dipendenza. Se
20
CAPITOLO 2. L’ARCHITETTURA MIPS64
questo si verificasse, l’ordine di esecuzione delle istruzioni differirebbe rispetto a
quello di un processore che non implementa la pipeline. I data hazard, a seconda
dell’ordine di lettura o scrittura degli operandi, si distinguono in tre categorie:
1. RAW (read after write): è l’hazard più comune, corrispondente ad una
dipendenza dati. Si verifica quando un’istruzione legge una sorgente prima
che questa venga scritta, ottenendone cosı̀ il valore non aggiornato.
2. WAW (write after write): Un’istruzione scrive un operando non attendendo
che lo faccia prima un’altra istruzione che la precede, nel registro viene
scritto cosı̀ il valore più obsoleto.
Questo hazard corrisponde ad una dipendenza di output e si verifica, come
si vedrà nel capitolo successivo, con l’utilizzo delle unità funzionali della
floating point unit. Un altro caso è quando si applica il riordinamento delle
istruzioni, approfondito in [5].
3. WAR (write after read): Si verifica quando un’istruzione scrive una destinazione prima che questa venga letta da un’istruzione che la precede:
quest’ultima legge cosı̀ il nuovo valore. Tale hazard corrisponde al caso
di antidipendenza, ma non può mai verificarsi nelle pipeline che prevedono
le letture in ID e le scritture in WB.
Control hazard
Quando un’istruzione di branch viene eseguita, il valore del PC potrebbe essere incrementato soltanto di 4 (branch not taken - salto non avvenuto); oppure,
potrebbe assumere il valore dell’indirizzo dell’istruzione puntata dal salto (branch
taken). Se il branch è taken si effettua il fetch dell’istruzione puntata5 ; ciò in5
L’esecuzione dello stadio IF è in sè uno stallo
21
CAPITOLO 2. L’ARCHITETTURA MIPS64
vece non è necessario se il branch è not taken, in quanto il fetch dell’istruzione
successiva è implicito nella dinamica della pipeline.
22
Capitolo 3
La floating point unit dei
processori MIPS64
In questo capitolo, dopo una breve panoramica sull’aritmetica floating point,
che può essere approfondita in [9, 11, 4], e dello standard IEEE 754, si discute
dell’architettura della floating point unit di un processore MIPS64, e il modo in
cui essa si adegua al suddetto standard. Nel Paragrafo 4.2 si parla dei registri
floating point, del registro di controllo FCSR e di altri registri speciali della FPU.
Nel Paragrafo 3.5 vengono illustrate solo le istruzioni floating point di interesse
per questa tesi, raggruppate secondo categorie funzionali. Dopo questa panoramica, nel Paragrafo 3.6, vengono introdotte le problematiche relative al pipelining
delle istruzioni floating point. Alla fine del capitolo si fa cenno alla pipeline del
processore MIPS R4000 [6].
3.1
Numeri in virgola mobile
In ambito scientifico si è soliti trattare numeri molto grandi o molto piccoli,
che non possono essere manipolati con l’aritmetica in virgola fissa1 . Mediante
la notazione scientifica, lo stesso numero può essere scritto in modi diversi: per
1
Quella che prevede la virgola a destra della cifra meno significativa
23
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
esempio la distanza della Luna dalla Terra in Km si può esprimere sia come
38.44 × 104 , che come 3.844 × 105 (3.844 è la mantissa e 5 l’esponente);
quest’ultima forma è detta normalizzata. I calcolatori memorizzano i numeri in
virgola mobile in un’equivalente notazione scientifica con base 2; lo stesso numero
viene dunque arrotondato e trattato da un computer come 1.4663696 × 218 .
Dal momento che, in decimale, la mantissa si può espandere in serie di potenze
P∞
( n=0 an xn ), ponendo la variabile argomento x = 10−1
1.4663696 = 1 + 4 × 10−1 + 6 × 10−2 + 6 × 10−3 + 3 × 10−4 + 6 × 10−5 + ...
analoga cosa si può fare per scriverne l’equivalente binario, ponendo x = 2−1 .
1.01110111011001 = 1 + 0 × 2−1 + 1 × 2−2 + 1 × 2−3 + 1 × 2−4 + 0 × 2−5 + ...
Nel paragrafo successivo si vedrà la rappresentazione di questo numero secondo
lo standard IEEE 754.
3.2
Standard IEEE 754-1985
Circa trent’anni fa, nell’ambito dell’aritmetica floating point implementata
nel software dei microprocessori, regnava la totale anarchia. Venivano utilizzate
numerose rappresentazioni dei numeri in virgola mobile, con diverse grandezze,
precisioni, modi di arrotondamento ed eccezioni. Nel 1977 il Prof. W.Kahan
dell’università di Berkeley, ottenne il permesso a partecipare alla realizzazione
di uno standard che mettesse fine a tale confusione, preparò una bozza chiamata ”K-C-S”. Dopo un’estenuante battaglia con la DEC, che aveva già messo sul
mercato i processori VAX, la bozza fu sottoposta a varie revisioni di compromesso, fino a quando il gruppo di lavoro 754 la inviò al comitato degli standard
24
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
dei microprocessori della IEEE. Denominata IEEE 754-1985 for Binary FloatingPoint Arithmetic, la bozza rettificata divenne lo standard de facto, usato tuttora
nell’aritmetica floating point.
3.2.1
Memorizzazione di un numero floating point
Lo standard IEEE 754 specifica tre formati di numeri floating point: single
(32 bit), double (64 bit) e double-extended (maggiore di 10 byte). Come visto nel
precedente paragrafo, ogni numero è rappresentato con la notazione
mantissa × 2esponente
ma l’esponente non è mai memorizzato come numero con segno, bensı̀ polarizzato.
Con tale tecnica viene aggiunta all’esponente una costante di polarizzazione(bias),
che consente di traslare nel semiasse positivo dei numeri interi l’intervallo di valori
che l’esponente può assumere2 . Cosı̀ facendo, il confronto fra numeri floating point
può essere realizzato con lo stesso hardware che manipola i numeri interi.
Figura 3.1: Formato dei numeri FP in doppia precisione
Figura 3.2: Formato dei numeri FP in singola precisione
I numeri nel formato IEEE normalizzati soddisfano la relazione 1 ≤ mantissa < 2.
Sfruttando tale proprietà, la mantissa si presenta sempre nella forma 1, xx..x3 , per
cui si può non salvare il suo bit più significativo poiché vale sempre 1(hidden bit).
2
Il valore di esponente 1 è il più negativo assumibile con lo spazio dati a disposizione
Per errori di traduzione mantissa è intesa sia col suo vero significato (significand ), sia come
la sua parte decimale che è quella memorizzata nei formati IEEE754(fraction)
3
25
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
Campo della mantissa
Campo dell’esponente
Campo del segno
Numero memorizzato
Valore decimale Valore binario
0.4663696
01110111011001000000000
18 + 127
10010001
0
0
1.4663696 × 218
0 10010001 01110111011001000000000
Tabella 3.1: Dati di conversione decimale/single
Per scrivere in formato single il numero del precedente esempio, la cui mantissa binaria è 1.01110111011001 e l’esponente 18, tenendo conto che la costante
di polarizzazione per i single è 127 e il numero dev’essere espresso nella forma
1.mantissa × 2esponente−127 , si hanno i risultati della Tabella 3.1.
Bit di precisione
Esponente massimo (Emax )
Esponente minimo (Emin )
Bias
Single Single extended
24
≥ 32
127
≥ 1023
-126
≤ −1022
127
Double
53
1023
-1022
1023
Double extended
≥ 64
≥ 16383
≤ −16382
Tabella 3.2: Parametri di formato dello standard IEEE 754
3.2.2
Valori speciali
I numeri più piccolo e più grande assumibili dall’esponente polarizzato4 (per i
single 0 e 255) vengono usati per rappresentare valori speciali. Quando l’esponente
polarizzato vale 255, se il campo mantissa contiene solo zeri, il numero rappresenta l’infinito positivo o negativo a seconda del segno, mentre se esso contiene un
valore diverso da zero, il numero rapprenta un NaN (Not a Number), usato per
fornire un risultato alle operazioni matematiche non valide (per esempio 0 × ∞
oppure 00 ). Nei processori MIPS il bit più significativo della mantissa ne determina la tipologia (Quit NaN oppure Signalling NaN). Quando entrambi i campi
4
L’esponente polarizzato è detto biased in quanto è ottenuto dalla somma tra l’esponente e
la costante di polarizzazione(bias)
26
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
dell’esponente polarizzato e della mantissa valgono zero il numero rappresenta lo
zero5 . Ii numeri denormals rappresentano un ultimo tipo di valore speciale. In
molti sistemi floating point, un numero più piccolo di 1.0 × 2Emin non può essere rappresentato, e dunque considerato uguale a zero. Lo standard IEEE 754
prevede la rappresentazione di questi numeri, la cui mantissa è minore di uno,
per avvicinarsi all’eccezione di underflow (si veda il paragrafo 3.2.3) in modo
graduale.
I numeri denormals vengono rappresentati con la notazione 0.mantissa × 2Emin ,
in essi il campo dell’esponente vale zero, mentre la mantissa qualunque valore
diverso da zero.
È da notare che, se l’esponente non polarizzato appartiene all’intervallo [Emin , Emax ],
la mantissa è normalizzata, (scritta nella forma 1,xx..x) e il bit nascosto vale 1;
mentre per poter rappresentare i numeri denormals, il bit nascosto è 0 e il campo
dell’esponente assume il valore più piccolo in assoluto (Emin − 1), che polarizzato
equivale a zero.
Campo esponente Campo mantissa Valore rappresentato
e = Emin − 1
m=0
±0
e = Emin − 1
m 6= 0
0.m × 2Emin
Emin ≤ e ≤ Emax
–
1.m × 2e
e = Emax + 1
m=0
±∞
e = Emax + 1
m = 0xx..x, m 6= 0
Quiet NaN
e = Emax + 1
m = 1xx..x
Signalling NaN
Tabella 3.3: Rappresentazione dei valori speciali
5
Anche se ciò può sembrare banale si rifletta sul fatto che non esiste soluzione all’equazione
1, .. × 2x = 0
27
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
3.2.3
Eccezioni
Una delle più importanti regole fornite dallo standard IEEE 754 è che il
calcolo, se espressamente voluto, può continuare nonostante sia stato prodotto un
risultato ”eccezionale”:
- Indeterminato: prodotto, per esempio, da operazioni del tipo
0
0
oppure 0×∞
- Infinito: come per il risultato di una divisione per zero
- Troppo grande in valore assoluto: una moltiplicazione tra numeri grandi
potrebbe avere per risultato un numero non rappresentabile data la sua
grandezza (overflow)
- Troppo piccolo in valore assoluto: la rappresentazione di un tale numero è
problematica per via della perdita di precisione (underflow)
- Inaccurato: come per la rappresentazione di
1
3
che necessita di un arroton-
damento tra i vari possibili
Le cinque eccezioni previste dallo standard sono: operazione non valida, divisione
per zero (Divide by zero), underflow, overflow e numero inesatto(Inexact).
Operazione non valida
Alcune delle operazioni aritmetiche non valide che forniscono come risultato
NaN sono:
√
x, x < 0
0×∞
∞
∞
0.0
0.0
∞−∞
Nella floating point unit dei processori MIPS64 è utilizzato uno speciale registro chiamato FCSR, descritto nel paragrafo 3.4.1, il quale memorizza delle
28
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
informazioni sullo stato delle eccezioni. In esso si trovano le flag di abilitazione
delle trap relative alle 5 eccezioni, e le flag che indicano se qualcuna di queste si
è mai verificata e non è stata ancora rimossa. Quando la flag dell’FCSR relativa
all’attivazione della trap dell’eccezione Operazione non valida è posta a 0, nessuna
trap si verifica, e il valore QNaN dev’essere fornito come risultato.
Divisione per zero
Questa eccezione si verifica quando, in una divisione, il divisore è zero e il
dividendo diverso da zero. Se la relativa trap non è attiva viene generato un
valore infinito con segno.
Underflow
Come visto nella precedente sezione, i numeri dell’intervallo ±1.0 × 2Emin sono
rappresentati usando mantisse minori di uno. Tale processo, che inizia con la
formazione di numeri denormals, è chiamato underflow graduale e termina con
l’eccezione stessa quando si raggiunge il più piccolo numero rappresentabile in
valore assoluto.
Overflow
L’eccezione di overflow è segnalata quando il risultato di un’operazione è più
grande di quello che può essere rappresentato, ciò è dovuto all’intervallo ristretto
dell’esponente. Tale eccezione non viene segnalata quando uno degli operandi è
infinito per via della correttezza dell’aritmetica tra infiniti.
Numero inesatto(Inexact)
Viene segnalata quando il risultato di un’operazione aritmetica ha infinite
cifre decimali (per esempio 13 ), esso dunque dev’essere arrotondato. La trap del29
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
l’eccezione inexact è disabilitata e la relativa flag ignorata dalla maggior parte del
software che lavora con numeri floating point, per tale ragione è stato ritenuto
non necessaria la sua implementazione in EduMIPS64.
3.2.4
Arrotondamenti
Le rappresentazioni floating point hanno una base β e una precisione p. Se
β = 10 e p = 3 il numero 0.1 verrebbe rappresentato come 1.00 × 10−1 . Se β = 2
e p = 24 lo stesso numero non può essere rappresentato esattamente, ma in modo
approssimato (1.10011001100110011001101 × 2−4 ), dove per approssimazione si
intende una funzione che dipende dalla modalità di arrotondamento e dalle cifre
successive alla p-esima della mantissa.
Lo standard IEEE 754 prevede 4 modalità di arrotondamento (Hp:β = 10, p = 1):
- Al più vicino: arrotonda al più vicino valore rappresentabile. Se la distanza
da due valori rappresentabili è la stessa, il risultato è arrotondato a quello
pari (2.6 ∼
= 3, 3.4 ∼
= 3, 4.5 ∼
= 4, 5.5 ∼
= 6).
- Verso lo zero: arrotonda al numero, preso in valore assoluto, più vicino
allo zero; ciò equivale a troncarne la parte decimale (−3.9 ∼
= −3, −3.1 ∼
=
−3, 4.1 ∼
= 4, 4.9 ∼
= 4)
- Verso l’infinito positivo: arrotonda al valore rappresentabile più vicino ma
non più piccolo del numero da arrotondare (−2.8 ∼
=
= −2, 6.1 ∼
= −2, −2.1 ∼
7, 6.9 ∼
= 7).
- Verso l’infinito negativo: arrotonda al valore rappresentabile più vicino ma
non più grande del numero da arrotondare (−2.8 ∼
= −3, −2.1 ∼
= −3, 6.1 ∼
=
6, 6.9 ∼
= 6)
30
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
3.2.5
Operazioni floating point
Le operazioni possono essere eseguite soltanto fra numeri floating point dello
stesso tipo, altrimenti il risultato è UNPREDICTABLE. Supponiamo di rappresentare il generico numero floating point in base 2 con la notazione
Fi = (−1)Si · 1.f · 2Ei −bias
dove Si è il segno del numero floating point, f è la parte decimale (fraction)
della mantissa Mi , ed Ei = Eisign + bias è l’esponente polarizzato. Si noti che
Eisign è l’esponente naturale (con segno). Infine sia p il numero di bit necessario
a rappresentare la mantissa, compreso il bit nascosto.
Moltiplicazione
Si vuole calcolare il prodotto
F3 = F1 × F2 = (−1)S1 · (−1)S2 · (M1 · M2 ) × 2E1 +E2 −bias
Si calcola E3 sommando gli esponenti polarizzati E1 ed E2 , si sottrae successivamente la costante di polarizzazione (per i single 127, per i double 1023). Ci si può
convincere di tale operazione in quanto
E3 = E3sign + bias = E1sign + E2sign + bias = E1sign + bias + E2sign + bias − bias = E1 + E2 − bias
Se i segni S1 ed S2 concordano allora S3 è positivo, altrimenti è negativo. Si
moltiplicano le due mantisse M1 ed M2 , ottenendo una mantissa di 2p bit che
dovrà essere arrotondata a p bit.
Si normalizza il prodotto se il bit più significativo (carry out bit o Cout ) è 1,
ovvero si sposta la virgola a sinistra6 incrementando l’esponente. Si valuta dunque
se si è verificata una condizione di eccezione.
6
Ciò equivale a traslare i bit a destra (right shift)
31
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
Si arrotonda il prodotto se l’espressione logica R(M0 +S) è vera, dove M0 ed R
rappresentano il p−esimo e il (p + 1)esimo bit del prodotto normalizzato partendo
dall’estrema sinistra della mantissa, e il bit S (sticky) è la somma logica di tutti
i bit alla destra di R. Se la condizione di arrotondamento è vera, viene aggiunto
1 al p−esimo bit del prodotto normalizzato. In quest’ultimo caso, se i bit più
significativi sono pari a 1, l’arrotondamento può generare un riporto, quindi si
deve rifare la normalizzazione.
Figura 3.3: Mantisse prima della moltiplicazione
Figura 3.4: Risultato del prodotto fra le mantisse prima della normalizzazione
Figura 3.5: Prodotto normalizzato prima dell’arrotondamento
Addizione o sottrazione
Si effettua il confronto degli esponenti e se ne calcola il valore assoluto della
differenza. Si tiene conto che l’esponente più grande è quello più probabile per il
risultato.
32
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
Si sposta il punto decimale della mantissa del numero col minore esponente di
un numero di posti pari alla differenza tra i due esponenti. Due dei bit traslati e
non più visibili della mantissa allineata vengono chiamati guardia (G) e round (R).
Cosı̀ per mantisse di p bit, l’effettiva grandezza delle mantisse allineate dev’essere
p + 2 bit. Si aggiunge un terzo bit chiamato sticky (S) all’estrema destra della
mantissa allineata; questo bit è dato dalla somma logica di tutti i bit alla destra
di R.
Si sommano (sottraggono per la sottrazione) le due mantisse usando un sommatore di p + 3 bit. Si chiami il risultato di tale operazione SOMMA.
Si controlla il bit più significativo di SOMMA per un eventuale riporto durante
l’addizione. In questo caso, si sposta la virgola di SOMMA incrementando l’esponente di 1. Per la sottrazione si deve controllare se ci sono zeri non significativi
alla sinistra del bit più significativo, in questo caso si deve traslare SOMMA verso
sinistra fino a che il bit più significativo del risultato è 1, sottraendo il relativo
valore dall’esponente. Si valutano le condizioni di eccezione.
Si arrotonda il risultato se la condizione logica R(M0 + S) è vera, dove le tre
variabili hanno lo stesso significato illustrato per la moltiplicazione. Nel caso in
cui i p bit più significativi fossero 1 si ha lo stesso epilogo della moltiplicazione.
Figura 3.6: Mantisse prima dell’addizione
33
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
Figura 3.7: Risultato della somma delle mantisse prima della normalizzazione
Figura 3.8: Mantissa normalizzata prima dell’arrotondamento
3.3
Floating point unit e standard IEEE 754
L’implementazione dello standard IEEE 754 nelle architetture di CPU presenta
due grosse problematiche.
La prima è che il pipelining diventa molto difficoltoso dal momento che devono essere intercettati i risultati ”eccezionali”. Nel normale funzionamento di
una CPU, quando si verifica una condizione di eccezione IEEE vengono prodotti
i relativi risultati speciali, o eventualmente si può verificare una trap; successivamente è richiesto che tutte le istruzioni precedenti a quella che ha provocato
l’eccezione forniscano i risultati, e che tutte le istruzioni successive eseguano come
se niente fosse accaduto. Tali requisiti pongono un limite all’implementazione
dell’ILP per le istruzioni floating point, e ciò è dovuto al fatto che esse non possono affrontare lo stadio di esecuzione in un solo ciclo di clock. Quindi prima di
eseguirne una si deve essere a conoscenza del fatto che essa non provocherà un’eccezione, nel caso contrario i risultati di un’altra istruzione, che nel frattempo ha
iniziato o sta finendo di usare una qualunque unità funzionale, verrebbero corrotti.
In fase di fetch degli operandi, l’istruzione che provocherà l’eccezione dev’essere
fermata finchè il conflitto termina. Per risolvere tale inconveniente l’ISA MIPS
34
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
IV introduce un meccanismo di ”rilassamento” delle eccezioni sincrone. Ciò rende
il modello computazionale non completamente compatibile con i dettami dello
standard.
La seconda problematica è dovuta al fatto che i risultati eccezionali possono
essere riutilizzati come operandi. Le floating point unit MIPS hanno grossi problemi a gestire questi valori in quanto esse sono costituite da una componente di
hardware che gestisce la maggior parte delle operazioni, e soltanto nei pochi casi
in cui essa fallisce interviene il software. Quest’ultimo viene richiamato mediante
un’eccezione non IEEE chiamata unimplemented operation, dopodichè il codice
dell’eccezione passa ad un relativo gestore per le applicazioni floating point. Nella
maggior parte dei casi, l’eccezione unimplemented operation si ha al verificarsi di
un’eccezione IEEE.
3.4
Registri floating point
Le CPU MIPS64, oltre ad essere dotate di 32 registri general-purpose, possiedono
32 registri floating point (FPR) da 64 bit ciascuno, i cui nomi vanno da F0 a F31.
Ogni registro può memorizzare numeri sia in formato single, occupando i 32 bit
più bassi, che numeri double usando l’intero registro. Un’ulteriore tipologia di
dati memorizzati è chiamata paired-single, costituita da due single affiancati in un
unico registro.
3.4.1
FCSR
Come già accennato nel Paragrafo 3.2.3, i processori MIPS64 sono dotati di
uno speciale registro chiamato FCSR(Floating Point Control and Status Register)
di 32 bit che controlla le operazioni della floating point unit.
35
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
Il registro FCSR ha le seguenti funzioni: seleziona la modalità di arrotondamento, abilita le trap per le eccezioni IEEE, riporta qualunque eccezione IEEE si
sia verificata durante l’esecuzione delle ultime istruzioni e indica i risultati delle
istruzioni di confronto.
Figura 3.9: Registro FCSR
Di seguito sono elencate le funzioni dei singoli campi di questo registro:
FCC : Codici condizionali floating point. Essi memorizzano il risultato delle
istruzioni di confronto (per esempio C.LT.D) e vengono verificati dalle istruzioni di
branch (per esempio BC1F) e da quelle di spostamento condizionale (per esempio
MOVT). Si veda il Paragrafo A.6 per comprenderne l’utilizzo con queste istruzioni.
FS(Flush to Zero): Quando vale uno, i risultati denormalizzati vengono ”portati a zero” invece di essere rappresentati con la relativa notazione speciale.
Impl : Disponibile per funzionalità aggiuntive della floating point unit.
0 : Riservato per utilizzi futuri. Non presente in EduMIPS64.
Cause: Bit di causa. Indicano se durante l’esecuzione dell’ultima istruzione
si è verificata una condizione di eccezione IEEE. Tali bit vengono scritti per ogni
operazione floating point appena eseguita. Il bit relativo all’eccezione viene posto
a uno se si è verificata quella condizione di eccezione, avviando, se abilitata, la trap
corrispondente. Il bit viene posto a zero se non c’è alcuna condizione di eccezione.
Se si è verificata una trap, prima di continuare l’esecuzione del programma, si
deve riportare a zero il relativo bit. I nomi mnemonici dei bit, usati anche per i
36
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
campi Enables e Flags, sono i seguenti: V (Operazione non valida), Z (Divisione
per zero), O (Overflow), U (Underflow), I (Inexact).
Enables: Bit di abilitazione trap. Gestiscono l’attivazione o la disattivazione
delle trap relative alle condizioni di eccezione IEEE. La trap si verifica solo se
entrambi i bit del campo Cause e del campo Enables appartenenti alla stessa
eccezione sono posti a uno.
Flags: Bit di eccezione mascherata. Questo campo mostra le condizioni di
eccezione che si sono verificate per le istruzioni completate. I bit vengono posti a
uno solo quando si è appena verificata una condizione di eccezione IEEE, ma non
si è avuta alcuna trap in quanto questa era disabilitata dal campo Enables.
RM :Modalità di arrotondamento. I due bit di questo campo vengono usati per
definire quale delle quattro modalità di arrotondamento, viste nel Paragrafo 3.2.4,
è usata, codificandola con un numero da 0 a 3.
Riassumendo l’attività di gestione delle eccezioni di una cpu MIPS64, al verificarsi di una condizione di eccezione IEEE vengono attivati i relativi bit del campo
Cause. Si effettua il controllo del campo Enables; se il bit dell’eccezione è attivo
si disabilita nuovamente il bit del campo Cause e si ha una trap, se al contrario il
bit dell’eccezione non è attivo si disabilita nuovamente il bit del campo Cause, e
si pone a 1 il bit del campo Flag in quanto non si ha la trap. Per ulteriori dettagli
sul registro FCSR consultare [7].
3.4.2
Altri registri
Esistono altri registri che costituiscono un’alternativa al registro FCSR. Tuttavia non è stata ritenuta necessaria la loro presenza nel simulatore.
- FCCR (Floating point Condition Code Register): è un modo alternativo per
37
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
leggere e scrivere i codici condizionali che appaiono nel registro FCSR.
- FEXR (Floating point Exceptions Register): è un registro in cui compaiono
soltanto i campi Cause e Flags del registro FCSR.
- FENR(Floating point ENables Register): presenta le flag di abilitazione delle
trap relative alle eccezioni IEEE, il campo FS e quello per impostare gli
arrotondamenti.
3.5
Instruction set
Le istruzioni dell’ISA MIPS64 possono essere raggruppate secondo i sei gruppi funzionali elencati di seguito. Per ragioni di brevità, verranno menzionate
soltanto le istruzioni implementate in EduMIPS64, i cui dettagli progettuali sono
approfonditi nel Paragrafo 4.3. Per un elenco completo consultare [8].
3.5.1
Trasferimento dati
L’architettura della FPU è anch’essa load/store, ovvero tutti i calcoli vengono
eseguiti sui valori presenti nei registri. I dati sono trasferiti senza che ne venga
effettuato alcun controllo di formato, per cui non è possibile alcuna eccezione
IEEE. Si hanno diversi tipi di trasferimento: dagli FPR alla memoria, e viceversa,
vengono trasferite solo word e doubleword. Lo stesso vale anche per i trasferimenti
tra gli FPR e i GPR, mentre per i trasferimenti tra i registri di controllo della
FPU (per esempio l’FCSR) e i GPR sono implicate soltanto word. Come per
le istruzioni intere di caricamento, anche quelle floating point devono riferirsi a
dati ”naturalmente allineati”, altrimenti si verifica l’eccezione Address Error. La
FPU, oltre ad utilizzare l’indirizzamento register+offset visto nel Paragrafo 2.4,
può utilizzare anche una modalità diversa chiamata register+register.
38
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
Nome
LDC1
LWC1
SDC1
SWC1
DMFC1
DMTC1
MFC1
MTC1
Descrizione
Carica una cella di memoria (doubleword) in un FPR
Carica una semicella (word) di memoria in un FPR
Memorizza una doubleword da un FPR alla memoria
Memorizza una word da un FPR alla memoria
Copia una doubleword da un FPR ad un GPR
Copia una doubleword da un GPR ad un FPR
Copia una word da un FPR ad un GPR
Copia una word da un GPR ad un FPR
Tabella 3.4: Istruzioni di trasferimento dati
3.5.2
Aritmetiche
Operano su unità dati formattate, e i risultati della maggior parte delle operazioni floating point è soggetta al rispetto dello standard IEEE 754. I calcoli sono
eseguiti con precisione infinita, e successivamente i risultati vengono arrotondati
al formato specificato usando la modalità di arrotondamento corrente.
Nome
ADD.D
DIV.D
MUL.D
SUB.D
C.cond.D
Descrizione
Addizione double
Divisione double
Moltiplicazione double
Sottrazione double
Verifica il predicato cond tra due FPR
Tabella 3.5: Istruzioni aritmetiche
3.5.3
Conversione
Eseguono conversioni da numeri in virgola mobile a numeri in virgola fissa
e viceversa. Ciascuna istruzione converte valori da uno specifico formato ad un
altro (per esempio CVT.L.D converte un double in un long). Alcune istruzioni consentono di specificare la modalità di arrotondamento da utilizzare, altre prelevano
tale informazione dal registro FCSR.
39
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
Nome
Descrizione
CVT.D.formato Converte numeri a virgola fissa in double
(f ormato ∈ {L, W }, L = Long, W = int)
CVT.L.D
Converte un double in un long
CVT.W.D
Converte un double in un int
Tabella 3.6: Istruzioni di conversione
3.5.4
Spostamento operandi formattati
Copiano valori tra gli FPR. Alcune istruzioni di questa categoria ( spostamento
condizionale) effettuano la copia verificando prima una condizione (zero di un
registro, condizione booleana); altre la effettuano in modo incondizionato. Nessun
controllo viene eseguito sul formato del registro sorgente, che può quindi non essere
formattato secondo uno dei tipi floating point.
Nome
MOV.D
MOVF.D
MOVT.D
MOVN.D
MOVZ.D
Descrizione
Sposta valori
Sposta valori
Sposta valori
Sposta valori
Sposta valori
double
double
double
double
double
tra FPR
se il codice condizionale è vero
se il codice condizionale è falso
tra FPR se un GPR è diverso da 0
tra FPR se un GPR è uguale a 0
Tabella 3.7: Istruzioni di spostamento degli operandi formattati
3.5.5
Salto condizionale
Anche la FPU è dotata di istruzioni che effettuano salti PC-relative (si veda
il Paragrafo 2.5.1) dopo aver verificato i codici condizionali del registro FCSR.
Nome
BC1F
Descrizione
Salta se il codice condizionale dell’FCSR è falso
(F CSR(cc) = f also, cc ∈ [0, 7])
BC1T Salta se il codice condizionale dell’FCSR è vero
Tabella 3.8: Istruzioni di salto condizionale
40
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
3.6
Pipelining
Nella Figura 3.10 è illustrata la struttura di una pipeline a 5 stadi che supporta il pipelining delle istruzioni floating point. Il moltiplicatore (M) e l’addizionatore (A) floating point sono ”fully pipelined” poichè possono praticare l’ILP sulle
istruzioni che li occupano; queste unità funzionali hanno una latenza rispettivamente di 7 e 4 stadi. Il divisore (DIV) non esegue il pipelining, ma necessita
comunque di 24 cicli per completare il calcolo.
A causa della diramazione nello stadio ID, occorre realizzare connessioni separate con gli stadi EX,A1,M1 e DIV; per cui si utilizzano i pipeline registers ID/EX,
ID/A1, ID/M1 e ID/DIV. Come accennato nel Paragrafo 3.3, l’ILP delle istruzioni
Figura 3.10: Pipeline a 5 stadi
floating point è difficile da realizzare; si possono verificare stalli strutturali perchè
le unità funzionali che non effettuano il pipelining (divisore) potrebbero non essere
disponibili, o anche per altri motivi illustrati nel paragrafo 3.6.1. Inoltre, poichè
le istruzioni floating point hanno un tempo di esecuzione differente, lo stadio WB
41
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
potrebbe essere raggiunto in modo disordinato rispetto al flusso di esecuzione;
sono possibili quindi dipendenze di output con successivi stalli WAW. Il numero
di stalli RAW, infine, è considerevole per via della lunga latenza delle operazioni
floating point.
3.6.1
Concorrenza sul register file
Il register file di un processore MIPS64, illustrato nella Figura 3.11, comprende
il set di 32 registri da 64 bit e la logica che controlla le operazioni di scrittura e
lettura. Ipotizzando che esso abbia solamente una porta di scrittura, le operazioni
floating point che scrivono su un registro nello stesso ciclo di clock andranno in
conflitto. Aumentare il numero di porte di scrittura non è una soluzione adeguata
perchè esse sarebbero raramente utilizzate. Il problema viene risolto in due modi
differenti: il primo è quello di prevedere, nello stadio ID, se si verificherà un
conflitto di questo tipo, ed eventualmente fermare un’istruzione nel secondo stadio
della pipeline. Il vantaggio è aver seguito il principio secondo cui tutti gli hazard
devono essere prevenuti nello stadio ID. Lo svantaggio è l’utilizzo di uno shift
register e della logica di controllo dei conflitti sul register file.
Figura 3.11: Register file di un processore MIPS64
42
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
Il secondo metodo consiste nell’inserire uno stallo quando le istruzioni in conflitto accedono allo stadio MEM o WB. Generalmente viene data maggiore priorità all’istruzione che ha latenza maggiore; l’ordine di priorità è dunque divisione,
moltiplicazione, addizione/sottrazione e istruzione intera. Il vantaggio è la facilità
di verificare l’hazard strutturale direttamente all’uscita dello stadio di esecuzione,
e di concedere l’ingresso allo stadio successivo solo all’istruzione con maggiore
priorità. Lo svantaggio è introdurre una maggiore complessità nel controllo della
pipeline, questo perchè gli hazard strutturali possono ora verificarsi in due punti
differenti di essa. La complicazione è dovuta al fatto che, fermando le istruzioni
che occupano gli stadi EX,M7 o A4, si devono fermare anche quelle che le seguono.
3.6.2
Rilevamento degli hazard
Se la pipeline rileva gli hazard nello stadio ID, prima che un’istruzione possa
essere ”emessa”, devono essere eseguiti dei controlli sui tre hazard che possono
verificarsi:
- Hazard strutturali: non considerando il caso di stallo strutturale visto nel
paragrafo precedente, l’unico altro caso che si possa verificare riguarda il
divisore. Prima di fare avanzare una divisione, si deve attendere che questa
unità funzionale non sia occupata, e la porta di scrittura del registro di
destinazione disponibile quando richiesto.
- Hazard RAW: si deve attendere affinchè i registri sorgenti dell’istruzione
diventino validi.
- Hazard WAW: si deve attendere affinchè il registro di destinazione venga
scritto da un’altra istruzione che sta ancora affrontando gli stadi A1..A4 e
M1..M7.
43
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
3.6.3
Pipeline FP del MIPS R4000
Il processore R4000 implementa l’ISA MIPS64, ma non usa una pipeline a 5
stadi, bensı̀ a 8. Gli stadi supplementari, oltre a permettere frequenze di clock
elevate, servono a ”spalmare” su due cicli la lentezza nell’accesso alla cache; questa
strategia, usata nelle moderne pipeline, è chiamata superpipelining.
La FPU è composta dalle tre unità funzionali illustrate nella Figura 3.10.
L’addizionatore, oltre ad essere impiegato per le somme e le sottrazioni, è usato
nello stadio finale di moltiplicazione e divisione.
La generica unità funzionale può eseguire una varietà di 8 operazioni primitive,
le quali vengono combinate in vari modi per realizzare le operazioni floating point.
Nella tabella 3.11 sono illustrate le fasi del pipelining di una MUL.D seguita da
una ADD.D.
Si supponga che le due istruzioni non presentino alcuna dipendenza; nonostante ciò, la moltiplicazione, nel suo settimo stadio di esecuzione (N+A), che
avviene nel ciclo 6, usa l’addizionatore. La ADD.D, dovendo anch’essa farne uso
(stadi A+R ed S+A), deve attendere per due cicli fino a che la MUL.D abbia
terminato anche lo stadio R.
44
CAPITOLO 3. LA FLOATING POINT UNIT DEI PROCESSORI MIPS64
Stadio
A
D
E
M
N
R
S
U
Unità funzionale Descrizione
Addizionatore
Somma delle mantisse
Divisore
Divisione FP
Moltiplicatore
Test delle eccezioni
Moltiplicatore
Primo stadio moltiplicazione
Moltiplicatore
Secondo stadio moltiplicazione
Addizionatore
Arrotondamento
Addizionatore
Shift degli operandi
”Spacchettamento” numeri FP
Tabella 3.9: Operazioni primitive della pipeline FP dell’R4000
Istruzione FP
Addizione, sottrazione
Moltiplicazione
Divisione
Confronto
Latenza (cicli) Stadi pipeline
4
U, S+A, A+R, R+S
8
U, E+M,M,M,M,N,N+A,R
36
U, A,R,D27 ,D+A,D+R,D+A,D+R,A,R
3
U,A,R
Tabella 3.10: Operazioni FP dell’R4000
Istr.
Stato 0
MUL.D Esec. U
ADD.D Esec.
Esec.
Esec.
Stallo
Stallo
1
2
E+M M
U
S+A
U
3
M
A+R
S+A
U
4
M
R+S
A+R
S+A
U
5
N
8
R+S
A+R R+S
S+A A+R R+S
U
S+A A+R R+S
Tabella 3.11: Stalli nelle unità funzionali
45
6
7
N+A R
Capitolo 4
La floating point unit di
EduMIPS64
In questo capitolo vengono illustrate le scelte progettuali nella costruzione di
un modello ad oggetti che simuli le dinamiche della floating point unit di un’architettura MIPS64. Nel paragrafo 4.1 viene introdotto il gruppo computazionale
floating point, comprendente le funzioni primitive che eseguono le quattro operazioni fondamentali e gli oggetti che modellano il luogo in cui esse avvengono: le
unità funzionali della FPU. Nel Paragrafo 4.2 si parla delle classi che modellano
i registri floating point e l’FCSR. Nel Paragrafo 4.3 viene illustrata l’estensione
dell’istruction set, implementata in EduMIPS64, con le istruzioni floating point e i
relativi dettagli progettuali. Infine, nel Paragrafo 4.4, si parla della pipeline floating point di EduMIPS64, e come questa interagisce con la pipeline già esistente
nel formare un’unica pipeline.
4.1
Operazioni floating point
In EduMIPS64 è stato ritenuto necessario, per esigenze di ottimizzazione nell’esecuzione delle quattro operazioni fondamentali floating point, l’utilizzo di classi
46
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
presenti nel Java Runtime Environment 1.5.
In particolare, si è evitato lo stretto accoppiamento tra il tipo di dato più
grande utilizzato nel simulatore (double), e la classe che lo modella (Double).
Si usa, al contrario, la classe BigDecimal, che permette di rappresentare numeri
decimali con segno a precisione arbitraria, e di scegliere la modalità di arrotondamento mediante l’ausilio della classe MathContext; fornisce, inoltre, metodi che
eseguono le quattro operazioni fondamentali su due oggetti BigDecimal.
Nonostante queste grosse potenzialità, la classe BigDecimal presenta delle limitazioni: essa non è conforme, per due motivi, ai requisiti richiesti per la progettazione di un modello computazionale floating point da integrare nel simulatore.
1. Il costruttore BigDecimal (double val) genera l’eccezione NumberFormatException, restituendo il messaggio di uscita
java.lang.NumberFormatException: Infinite or NaN
sia che ad esso si passi un qualunque infinito (es. Double.POSITIVE INFINITY),
sia un NaN (Double.NaN). Questo fatto, oltre a rendere praticamente indistinguibili i valori speciali infinito positivo, infinito negativo, QNaN ed
SNaN, fa dedurre che non è possibile creare un oggetto BigDecimal che
rappresenti tutti i valori speciali IEEE.
2. La classe Double non presenta attributi statici che rappresentano gli zeri.
Non essendo possibile distinguere uno zero positivo da uno negativo, non lo
è neanche creare un oggetto BigDecimal che lo rappresenti.
A causa dell’inefficienza delle due classi Double e BigDecimal, nelle routine
delle operazioni è stato implementato uno strato funzionale intermedio. Esso si
47
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
occupa di intercettare gli eventuali valori speciali degli operandi ed eventualmente
restituire il relativo risultato ”speciale”; il gestore delle eccezioni sincrone interviene solo se, per una certa eccezione IEEE, è attivata la relativa trap. Della classe
BigDecimal vengono usati, quindi, soltanto i metodi che eseguono le operazioni.
Al contrario delle architetture reali, in cui lo stadio di esecuzione delle istruzioni
floating point non può essere concluso in un solo ciclo di clock, in EduMIPS64 le
quattro operazioni floating point vengono eseguite in un’unica chiamata di procedura, anche se viene simulata la latenza dovuta al pipelining delle istruzioni nelle
unità funzionali.
Ogni istruzione aritmetica FP, quando giunge nell’unità funzionale che le compete, usufruisce dei metodi statici offerti dalla classe FPInstructionUtils. Inizialmente, il metodo EX dell’istruzione invoca il metodo specifico dell’operazione
aritmetica da eseguire, passando a quest’ultimo il valore binario dei due registri
con gli operandi (per esempio doubleSum(F1,F2)); dopo il controllo degli operandi, se il metodo invocato non ha ceduto il controllo al gestore delle eccezioni1 , nè
ha prodotto e ritornato alcun valore speciale, passa il risultato decimale dell’operazione al metodo doubleToBin(), che lo converte in binario secondo lo standard
IEEE 754 e lo ritorna al chiamante (il metodo EX dell’istruzione FP).
4.1.1
Gestore delle eccezioni sincrone
I metodi statici della classe FPInstructionUtils eseguono le quattro operazioni floating point fondamentali. Essi, in determinate circostanze, terminano in
anticipo l’esecuzione perchè è stato generato un valore speciale oppure perchè si
1
Trasferire il controllo al gestore delle eccezioni significa lanciare un’eccezione sincrona nel
metodo; essa viene poi catturata dalla CPU e gestita opportunamente (visualizzazione grafica
del messaggio di eccezione)
48
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
deve passare il controllo al gestore delle eccezioni sincrone; quest’ultimo è stato
sviluppato in un precedente lavoro di tesi.
Le eccezioni previste dallo standard IEEE 754, e viste nel Paragrafo 3.2.3,
sono implementate in EduMIPS64 estendendo la classe SynchronousException.
Le 4 nuove eccezioni java che modellano quelle IEEE (escludendo l’eccezione
non implementata Inexact) sono: FPOverflowException, FPUnderflowException,
FPInvalidOperationException, FPDivideByZeroException.
4.1.2
Addizione
La somma tra due numeri floating point viene realizzata, come accennato in
precedenza, chiamando il metodo statico doubleSum() che, a sua volta, si serve
del metodo doubleToBin() per ritornare il risultato binario della somma. Per
evitare ripetizioni, si ometterà la descrizione della sottrazione; essa viene eseguita dal metodo statico doubleSubtraction() e presenta le stesse problematiche
dell’addizione. Il metodo doubleSum() esegue queste operazioni:
- Verifica se le stringhe binarie degli operandi passati corrispondono ai pattern di QNaN ed SNaN. In questo caso viene attivato il bit di causa V del
registro FCSR e, se la trap per l’eccezione Invalid Operation è attivata, si
passa il controllo al gestore delle eccezioni sincrone, lanciando l’eccezione
FPInvalidOperationException. Nel caso opposto viene generato e ritornato
al metodo chiamante (EX() dell’istruzione di somma) un QNaN che verrà
memorizzato nel registro di destinazione.
- Verifica se gli operandi sono due infiniti di segno opposto, eventualmente si
agisce con l’eccezione Invalid Operation, come visto in precedenza.
49
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
- Verifica se uno dei due operandi è infinito e l’altro diverso da un XNaN, in
questo caso viene ritornato alla funzione chiamante un infinito con un segno
opportuno.
- Viene eseguita la somma col metodo BigDecimal::add() e il risultato decimale ottenuto viene passato al metodo FPInstructionUtils::doubleToBin()
- Il metodo doubleToBin() verifica che il numero decimale possa essere rappresentato in double; vengono dunque effettuati i controlli di underflow ed
overflow. Se si ha una condizione di overflow, può essere ritornato un opportuno infinito (trap disattivate) o generata l’eccezione FPOverflowException.
La stessa cosa avviene per l’underflow, (FPUnderflowException) con la sola
differenza che i numeri eventualmente ritornati sono zeri positivi o negativi.
Figura 4.1: Sommatore floating point (R1+R2=R3)
4.1.3
Moltiplicazione
Il prodotto fra due numeri floating point è realizzato mediante l’invocazione
del metodo doubleMultiplication(). Esso accetta, analogamente al metodo
doubleSum(), i valori binari dei registri con gli operandi, passando il risultato
50
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
decimale al metodo doubleToBin(); questo provvede a ritornare una stringa binaria pronta ad essere memorizzata. Il metodo doubleMultiplication() effettua
le seguenti operazioni:
- Verifica se le stringhe binarie degli operandi passati corrispondono ai pattern
di QNaN ed SNaN. E procede nel modo illustrato per l’addizione.
- Verifica se gli operandi sono uno zero e un infinito, agendo, eventualmente,
con l’eccezione Invalid Operation vista in precedenza.
- Verifica se uno dei due operandi è infinito e l’altro diverso da un XNaN, in
questo caso viene ritornato alla funzione chiamante un infinito con un segno
opportuno.
- Verifica se entrambi gli operandi sono zeri e ne effettua il prodotto dei segni
ritornando un opportuno zero.
- Viene eseguito il prodotto col metodo BigDecimal::multiply() e il risultato viene passato al metodo FPInstructionUtils::doubleToBin() .
- Il metodo doubleToBin() agisce come spiegato per l’addizione.
Nella Figura 4.2 è illustrato il blocco relativo al prodotto, da sostituire nella
Figura 4.1
4.1.4
Divisione
La divisione fra due numeri floating point è realizzata mediante l’invocazione
del metodo doubleDivision(). Esso esegue le seguenti operazioni:
- Verifica i pattern XNaN, il rapporto tra infiniti, e il rapporto tra zeri.
Procede come spiegato al primo punto dell’addizione.
51
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
Figura 4.2: Moltiplicatore floating point
- Verifica se il dividendo è un qualunque zero e il divisore un valore diverso
da un XNaN, fornendo un opportuno zero come valore di ritorno.
- Verifica se il divisore è un qualunque zero e fornisce un opportuno infinito oppure, se attivata, la trap dell’eccezione Divide by zero(FPDivideByZeroException).
- Verifica se il dividendo è un infinito e, escludendo il caso del primo punto,
fornisce un opportuno infinito
- Viene eseguito il prodotto col metodo BigDecimal::divide() e il risultato
è passato al metodo doubleToBin() che agisce nel modo spiegato prima.
Figura 4.3: Divisore floating point
52
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
4.2
Registri
I register file della CPU e della FPU, visti nel Paragrafo 3.6.1, sono modellati,
in EduMIPS64, da vettori di 32 oggetti istanziati dalle classi Register e RegisterFP ; una serie di metodi presenti nella classe CPU (es. getRegisterFP())
simula, invece, la logica di controllo per la lettura e la scrittura dei singoli registri.
I registri floating point utilizzano la stessa classe base di quelli general-purpose
(FixedBitset 2 ); da questa si ereditano le classi BitSet64FP e RegisterFP, come
illustrato nella Figura 4.4. Entrambe le classi Register ed FPRegister sono dotate
di due semafori: il primo è di scrittura e simula l’abilitatore della porta di scrittura del register file3 . Durante la decodifica, ogni istruzione che, nello stadio WB,
scriverà il registro deve incrementare il valore di tale semaforo, consentendo di
rilevare e prevenire l’hazard per le istruzioni che eseguiranno la stessa operazione.
Il secondo semaforo (di WAW) serve, invece, ad anticipare lo sblocco di un registro
per evitare che ciò avvenga nello stadio WB; questo argomento verrà approfondito
nel Paragrafo 4.3.
4.2.1
FCSR
Il registro FCSR è rappresentato dalla classe FCSRRegister, che estende la
classe BitSet32 e costutuisce un membro privato della classe CPU. Le scritture
e le letture di tale registro passano sempre per la classe CPU; se esse vengono
effettuate dal suo interno, si accede direttamente al membro, altrimenti CPU
fornisce l’interfaccia di accesso esclusivamente ad un numero ridotto di metodi
2
Questa classe è usata per modellare un set di bit affiancati il cui numero è specificato dalla
classe che la estende
3
A differenza di un’architettura reale, dove per la porta di scrittura passano tutti i dati scritti
nel banco di registri, nel simulatore ogni scrittura viene eseguita mediante un metodo della cpu
che aggiorna il vettore dei registri
53
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
Figura 4.4: Diagramma delle classi dei registri di EduMIPS64
Figura 4.5: Modalità di accesso alla classe FCSRRegister
(si veda la Figura 4.5). I metodi del tipo setFCSRnomecampo richiedono, come
parametri, una stringa denominata tag, che rappresenta il nome mnemonico del
bit da modificare (V,Z,O,U,I), e il valore binario da assegnare al bit.
Dopo la descrizione di questo registro, fatta nel Paragrafo 3.4.1, si vedranno
ora i dettagli implementativi dei singoli campi in EduMIPS64. Di seguito vengono
riportati soltanto i campi presenti nel simulatore.
FCC : Codici condizionali floating point. Le istruzioni di confronto, illustrate
nel Paragrafo A.6, utilizzano come parametro un immediato di 3 bit; questo
54
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
permette di descrivere 8 possibili destinazioni, nel registro FCSR, per la memorizzazione del risultato booleano del confronto. Come si è visto nella Figura 3.9,
il campo FCC, per ragione di compatibilità con le vecchie architetture, presenta
un bit di discontinuità occupato dal campo FS. Il metodo che permette di scrivere i codici condizionali su questo registro è visto nel Listato 4.1, si tenga in
considerazione che la classe FCSRRegister numera i bit da 0 a 31 partendo dalla
sinistra.
public void setFCSRConditionCode ( int cc , int condition ) {
f i n a l int FCC0 =8;
f i n a l int DISCONTINUITY =1;
f i n a l int OFFSET=FCC0−DISCONTINUITY ;
i f ( cc==0)
setBits ( String . valueOf ( condition ) , FCC0 ) ;
else
setBits ( String . valueOf ( condition ) , OFFSET−cc ) ;
}
Listato 4.1: Scrittura dei codici condizionali sull’FCSR
Cause: Bit di causa. Il funzionamento di questo campo è molto simile a quello
dell’architettura MIPS64. L’unica differenza sta nel fatto che l’azione di reset del
bit relativo all’eccezione, che avviene subito prima la trap o la scrittura del campo
flag, non viene effettuata. I bit vengono lasciati a 1 per darne visione all’ambiente
”user-mode”, che altrimenti non sarebbe cosciente del cambiamento di tali bit. Il
metodo mediante cui i bit di causa vengono cambiati è visto nel Listato 4.3; ad
esso viene passata una stringa che identifica il nome mnemonico del bit da variare,
e il valore binario che il bit deve assumere. Metodi analoghi vengono usati per
modificare i campi Enables e Flags.
public void setFCSRCause ( String tag , int value ) {
i f ( tag . compareTo ( "V")==0)
setBits ( String . valueOf ( value ) , 1 5 ) ;
e l s e i f ( tag . compareTo ( "Z")==0)
55
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
setBits ( String . valueOf ( value ) , 1 6 ) ;
e l s e i f ( tag . compareTo ( "O")==0)
setBits ( String . valueOf ( value ) , 1 7 ) ;
e l s e i f ( tag . compareTo ( "U")==0)
setBits ( String . valueOf ( value ) , 1 8 ) ;
e l s e i f ( tag . compareTo ( "I")==0) // non i m p l e m e n t a t a
setBits ( String . valueOf ( value ) , 1 9 ) ;
}
Listato 4.2: Scrittura dei bit di causa sull’FCSR
Enables:Bit di abilitazione trap. L’attivazione di questi bit, effettuata con un
metodo simile a quello del listato precedente, è pilotata da una finestra di configurazione nella GUI (Graphic User Interface), che sarà vista nel Paragrafo A.3.
Tale finestra si serve del suo file di configurazione (edumips64.conf) per mostrare
all’utente, tra le altre cose, lo stato di attivazione delle trap; per ogni ciclo di
esecuzione, la classe CPU preleva le nuove configurazioni dalla finestra e li scrive
nell’FCSR con il Listato 4.3 della classe FCSRRegister.
public void setFPExceptions ( CPU . FPExceptions exceptionName ,
boolean value ) {
switch ( exceptionName ) {
case DIVIDE_BY_ZERO :
setFCSREnables ( "Z" , value ? 1 : 0 ) ;
break ;
case OVERFLOW :
setFCSREnables ( "O" , value ? 1 : 0 ) ;
break ;
case UNDERFLOW :
setFCSREnables ( "U" , value ? 1 : 0 ) ;
break ;
case INVALID_OPERATION :
setFCSREnables ( "V" , value ? 1 : 0 ) ;
}
}
Listato 4.3: Metodo che memorizza i dati della GUI sull’FCSR
Flags: Bit di eccezione mascherata. Questi bit vengono attivati nello stesso
56
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
modo previsto dall’architettura MIPS64, e vengono posti a zero soltanto con la
funzione di reset della CPU, effettuata per ogni nuovo programma eseguito. Si
noti che in EduMIPS64, dal momento che non è implementata l’eccezione Inexact,
l’attivazione di questi bit è indice della generazione, per via di un’eccezione IEEE
la cui trap era disattivata dal campo Enables, di un valore speciale. È probabile,
dunque, che la computazione porterà a risultati non previsti rispetto al normale
flusso di esecuzione del programma.
RM :Modalità di arrotondamento. EduMIPS64 implementa le quattro modalità
di arrotondamento usate nei processori MIPS64. Per eseguire le istruzioni di
conversione da un numero double a uno long (CVT.L.D) viene usato il metodo
FPInstructionUtils::doubleToBigInteger(). Tale metodo permette di troncare la parte decimale di un double utilizzando la modalità di arrotondamento
corrente.
4.3
Instruction set
Dopo una breve panoramica su alcune delle istruzioni dell’ISA MIPS64, elencate nel Paragrafo 3.5, si affrontano adesso i dettagli progettuali dell’instruction
set in EduMIPS64.
4.3.1
Classe Instruction
La classe Instruction non è stata sviluppata in questo lavoro di tesi; tuttavia,
per comprendere come la libreria di classi delle istruzioni floating point è usata dal simulatore, nella Figura 4.6 ne viene proposto il diagramma UML, in
cui sono stati aggiunti solo attributi e metodi più rilevanti. Il metodo statico
buildInstruction() implementa il design pattern Factory, mediante il quale
57
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
Figura 4.6: Classe Instruction
viene definita l’interfaccia per specificare quale oggetto istruzione istanziare; le
classi invocano, a tal fine, il metodo factory (buildInstruction()), passando ad
esso il nome dell’istruzione.
La classe Parser, responsabile della lettura e dell’interpretazione dei file sorgenti (.s), e maggiore utilizzatrice dell’interfaccia, istanzia inizialmente le istruzioni,
passa loro, col metodo setParams(), i parametri richiesti, e infine le memorizza
in memoria.
La classe CPU utilizza una struttura dati mediante cui simula la pipeline a 5
stadi. Per ogni istruzione passata dalla memoria alla struttura, invoca il metodo
corrispondente allo stadio della pipeline(IF(),ID(),EX(),MEM(),WB()); per tale
ragione ogni istruzione deve fornire tutti i 5 metodi.
4.3.2
Classi base dell’instruction set
Per riutilizzare il codice esistente del simulatore, le nuove classi dell’instruction set della FPU hanno la stessa radice delle istruzioni intere; per cui ereditano
dalle tre classi base ComputationalInstructions, FlowControlInstructions e LDSTInstructions. Dalla prima ereditano tutte le classi che modellano le istruzioni
58
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
aritmetiche, di spostamento dati tra registri e condizionali; dalla seconda tutte
le istruzioni che effettuano branch e dalla terza tutte le istruzioni load e store.
Nei paragrafi che seguono saranno omessi tutti i riferimenti alle istruzioni floating
point che manipolano operandi single; il loro sviluppo non è previsto in questo
lavoro di tesi, anche se nei raggruppamenti effettuati esse sono implicitamente
incluse4 .
Figura 4.7: Classi base dell’istruction set
4.3.3
Ereditarietà dalla classe ComputationalInstructions
La classe astratta ComputationalInstructions concretizza soltanto il metodo IF(); il codice del metodo scrive l’informazione di fetch del sottoinsime di
istruzioni derivate da questa classe sul trace file, che viene successivamente inviato al simulatore di cache Dinero. Gli altri quattro metodi restano astratti
perchè vengono implementati nelle sottoclassi. Come si vede dalla Figura 4.8,
le classi che ereditano da ComputationalInstructions sono: FPArithmeticInstructions, FPC cond DInstructions, FPFormattedOperandMoveInstructions, FPConversionFCSRInstructions, FPConditionalCC DMoveInstructions, FPConditionaZerosMoveInstructions e infine FPMoveToAndFromInstructions.
4
Sono tutte quelle istruzioni il cui parametro X, visto più avanti, vale S
59
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
Figura 4.8: Classi ereditate da ComputationalInstructions
FPArithmeticInstructions
Questa classe descrive il modello generale delle istruzioni aritmetiche floating point. Vengono risolte le problematiche dovute alla presenza di due registri
sorgenti che potrebbero non essere da subito validi (devono essere scritti prima
di poter essere letti), e un registro di destinazione che deve essere scritto mantenendo l’ordine di esecuzione, non sempre garantito dalle computazioni floating
point. I metodi della classe, tutti implementati tranne EX(), vengono ereditati
dalle istruzioni aritmetiche che la estendono. Si consideri, per fissare le idee,
una generica istruzione in aritmetica double con sintassi ISTR.D fd,fs,ft. Le
operazioni eseguite nel metodo ID() sono le seguenti:
- si verifica se i registri sorgenti fs ed ft sono validi; se il loro semaforo è incrementato, si sta verificando una dipendenza dati tra le istruzioni, quindi
si verifica lo stallo RAW; la classe CPU ferma l’istruzione corrente nella pipeline, eseguendo in continuazione il metodo ID() fino a quando la
dipendenza non sarà risolta.
- si copiano i valori dei registri sorgenti su due registri ausiliari, affinchè i dati
letti nello stadio ID vengano trasmessi da uno stadio all’altro della pipeline.
Questa azione simula il funzionamento dei pipeline registers, illustrati nel
Paragrafo 2.6.1.
60
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
- si verifica se il semaforo di scrittura del registro di destinazione fd è incrementato, in questo caso si sta verificando una dipendenza di output, a cui
seguirà lo stallo WAW.
- si incrementa il semaforo di scrittura e di WAW del registro fd
Nel metodo MEM() si è scelto di eseguire il decremento del semaforo di WAW;
questa azione consente alle istruzioni, la cui esecuzione era stata fermata per via
di una dipendenza di output, di proseguire nonostante il registro su cui dovranno
scrivere sia ancora bloccato. Ciò è permesso perchè le istruzioni a lunga latenza,
cioè quelle ereditate da questa classe, possono scambiarsi l’ordine di esecuzione5
soltanto nelle unità funzionali della floating point; uscendo da esse (stadio MEM),
l’ordine non può essere più variato perchè il percorso di esecuzione non è più
multiplo.
Con il metodo WB(), il risultato memorizzato in un registro ausiliario viene
scritto in fd, e il suo semaforo viene decrementato. Infine, con il metodo pack(),
viene generato il codice binario a 32 bit dell’istruzione, a partire dai parametri da
essa gestiti. Per una guida esaustiva sul formato della codifica di ogni istruzione
dell’ISA MIPS64 consultare [8].
Dalla classe FPArithmeticInstructions vengono ereditate le quattro istruzioni
che implementano le operazioni fondamentali:ADD.D, SUB.D, MUL.D e DIV.D, illustrate nel Paragrafo A.6.
FPC cond DInstructions
Da questa classe vengono ereditate le istruzioni condizionali floating point.
A differenza delle istruzioni intere, che si servono dei registri per memorizzare il
5
Una divisione FP che occupa il divisore al ciclo n lo lascerà al ciclo n+24, mentre
un’addizione che occupa l’addizionatore nel ciclo n+1 lo lascerà nel ciclo n+5
61
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
valore booleano di un confronto (per esempio SLT), le istruzioni floating point
utilizzano il registro FCSR per tener traccia di questa informazione.
La classe implementa tutti i metodi della classe Instruction. Il metodo ID()
effettua la lettura dei registri sorgenti se questi sono validi, altrimenti lancia l’eccezione RAWException, che causa uno stallo della pipeline. Questa classe è tra le
poche a presentare anche l’implementazione del metodo EX(), che generalmente è
eseguita al livello dell’istruzione. Si è fatta questa scelta perchè si usa una funzione
a cui si passano due registri e il predicato da verificare, ottenendone il risultato
booleano; l’istruzione ha solo il compito di specificare quale predicato dev’essere
verificato.
Si supponga di servirsi della terna di flag [less, equal, unordered] per
descrivere un determinato predicato di confronto. Per fissare le idee, il predicato
non ordinato6 o uguale verrebbe indicato con la notazione [falso,vero,vero].
Si supponga, adesso, di caratterizzare ogni istruzione che effettua un confronto con una maschera della verità relativa al predicato da verificare; la terna di
flag viene, quindi, codificata in binario in un determinato campo dell’istruzione,
e nel caso specifico il numero memorizzato è 011. Si indichi la generica cifra
di questo numero con la notazione condi , i ∈ [0, 2]. Se si vogliono confrontare
i due registri fs ed ft, si devono valutare i valori booleani delle flag della terna
[less=(fs<ft),equal=(fs==ft),unordered=(unordered(fs,ft))], e successivamente effettuare la ”messa in AND” della terna, in binario, con la maschera
della verità.
Sia isXNaN() una funzione che verifica se un FPR contiene i valori speciali
SNaN o QNaN, cc l’identificativo del bit nel campo FCC del registro FCSR, in
6
I valori QNaN ed SNaN non appartengono ad alcuna metrica, quindi non possono essere
confrontati nè tra di essi nè con numeri reali
62
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
cui verrà memorizzato il risultato del confronto, e FCSR setFCC() la funzione
che esegue quest’ultima operazione. Nel Listato 4.4 è illustrata una porzione, in
pseudocodice, della funzione di confronto.
i f ( isXNaN ( FPR [ fs ] ) | | isXNaN ( FPR [ ft ] ) )
less=f a l s e
equal=f a l s e
unordered=true
else
less=(fs<ft )
equal=(fs= =ft )
unordered=f a l s e
end i f
condition=(cond [ 0 ] and less ) or ( cond [ 1 ] and equal )
or ( cond [ 2 ] and unordered )
FCSR_setFCC ( cc , condition )
Listato 4.4: Porzione della funzione che effettua il confronto tra registri
Si è ritenuta sufficiente l’implementazione di solo due istruzioni di confronto:
C.LT.D e C.EQ.D, illustrate nel Paragrafo A.6.
FPFormattedOperandMoveInstructions
Questa classe modella il sottoinsieme di istruzioni per lo spostamento degli
operandi formattati ; esso è costituito dalla sola istruzione MOV.D7 , che si occupa
dello spostamento dei dati da un FPR all’altro in modo incondizionato. La sintassi
di questa istruzione è MOV.D fd,fs; il metodo ID() controlla la disponibilità per
la scrittura di fd, facendo fermare l’istruzione nel caso di dipendenze dati o di
output. Gli altri metodi eseguono le solite operazioni.
7
Se fossero implementate le istruzioni che manipolano operandi single apparterrebbe a questo
gruppo anche l’istruzione MOV.S
63
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
FPConversionFCSRInstructions
Le istruzioni di conversione ereditano dalla classe FPConversionFCSRInstructions, non presente nella Figura 4.8, a sua volta ereditata dalla classe FPFormattedOperandMoveInstructions, per via dell’analogia del lavoro compiuto su due
registri. La generica istruzione di questo gruppo ha la forma CVT.X.Y fs,fd
con (X, Y ) ∈ ({L, W, D}, {L, W, D}), X 6= Y, L = long, W = int, D = double;
tuttavia diverse coppie sono state omesse dall’instruction set.
FPConditionalCC DMoveInstructions
Le istruzioni che effettuano lo spostamento degli operandi formattati in modo
condizionale, leggendo il codice FCC del registro FCSR, sono modellate da questa
classe. La notazione generica per le istruzioni è MOVX.D fd,fs,cc, dove X si
riferisce al predicato da verificare (F=falso, T=true) affinchè la condizione sia
vera; per ulteriori dettagli consultare il Paragrafo A.6. La classe implementa tutti
i metodi di Instruction; in particolare EX(), usando l’informazione del predicato
da verificare (F,T), specificata al livello dell’istruzione, accede al registro FCSR
e se riscontra la condizione di verità; effettua, poi, la copia di fs in un registro
ausiliario. Tale registro viene infine copiato su fd nello stadio WB.
FPConditionalZerosMoveInstructions
Questa classe modella le istruzioni che effettuano lospostamento degli operandi
formattati, copiando valori tra FPR se un GPR contiene, o meno, il valore zero.
La generica istruzione di questa categoria è MOVX.D fd,fs,rt, dove X si riferisce
al predicato da verificare; se è Z, lo spostamento avviene quando il registro rt vale
zero, se vale N lo spostamento avviene sul ”Non zero” di questo registro.
Vengono implementati tutti i metodi tranne EX(); l’unica differenza con le
64
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
Figura 4.9: Ereditarietà dalla classe FlowControlInstructions
altre classi è dovuta al fatto che il metodo ID(), oltre a leggere il registro fs, legge
il registro intero rt, che pilota l’esito dello spostamento.
FPMoveToAndFromInstructions
Il sottoinsieme di istruzioni di trasferimento dati, che si occupa di copiare valori da un GPR ad un FPR e viceversa, è modellato da questa classe. Le istruzioni
non ereditano direttamente da essa, ma da due sue sottoclassi: FPMoveToInstructions ed FPMoveFromInstructions. Dalla prima si ereditano le istruzioni
che trasferiscono word o doubleword dai GPR agli FPR (MTC1,DMTC1), dalla
seconda le istruzioni che effettuano l’operazione inversa (MFC1,DMFC1).
4.3.4
Ereditarietà dalla classe FlowControlInstructions
Anche la classe astratta FlowControlInstructions concretizza soltanto il metodo IF(), che esegue l’operazione accennata in precedenza per le istruzioni di
branch e jump. L’unica classe che eredita da FlowControlInstructions è FPConditionalBranchesInstructions, come si vede dalla Figura 4.9. Questa classe non
presenta l’implementazione dei metodi di Instruction, ma dichiara le costanti per
l’operazione di packing 8 . Le istruzioni ereditate da questa classe hanno il formato
8
Questa operazione è eseguita col metodo pack() illustrato nelle istruzioni aritmetiche
65
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
BC1X cc,offset, dove X è il predicato da verificare 9 (F=falso, T=true) prima di
eseguire un salto PC-relative.
4.3.5
Ereditarietà dalla classe LDSTInstructions
Le istruzioni floating point di load ereditano dalla classe FPLoading, la quale
implementa tutti i metodi di Instruction tranne EX(). La generica istruzione ha
la notazione LXC1 ft,offset(base), dove X può assumere i valori D, per caricare
nel registro floating point una doubleword, e W per caricare una word nei 32 bit
più bassi del registro.
Figura 4.10: Ereditarietà dalla classe LDSTInstructions
Il metodo ID() effettua le seguenti operazioni:
- verifica se è possibile leggere dal registro base, ed eventualmente lancia
l’eccezione RAWException per segnalare la dipendenza alla classe CPU.
- calcola l’indirizzo effettivo come somma di base e offset, salvandone il valore
in un registro ausiliario
- verifica se esiste una dipendenza di output, lanciando eventualmente l’eccezione WAWException
9
Se il bit cc nel campo FCC del registro FCSR è X avviene il salto
66
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
- incrementa i semafori di scrittura e di WAW del registro ft
Il metodo MEM() effettua solamente il decremento del semaforo di WAW, mentre
WB() effettua sia la scrittura sul registro ft che il relativo sblocco.
Le istruzioni floating point di store ereditano dalla classe FPStoring, e genericamente sono descritte dal pattern SXC1 ft,offset(base), con X che può assumere i valori di cui sopra. Il metodo ID() si differenzia da quello delle istruzioni
di load perchè determina gli eventuali blocchi inerenti alla lettura dei registri base
ed ft; infatti, al contrario del caso precedente, il registro ft o la sua word destra
devono essere copiati in una cella di memoria che, in EduMIPS64, non prevede il
blocco.
4.4
Pipelining
Prima dello sviluppo della FPU, la pipeline di EduMIPS64 implementava l’ILP
per le sole istruzioni intere. Non essendo presenti le istruzioni floating point, non
erano previste nè dipendenze di output che portassero agli stalli WAW, nè contese
tra unità funzionali, perchè si seguiva un percorso di esecuzione unico per tutte
le istruzioni. L’introduzione della pipeline floating point, e la sua interazione con
la pipeline intera, presenta le complicazioni illustrate nel Paragrafo 3.6.
4.4.1
Pipeline floating point
Il modello ad oggetti che simula il funzionamento della pipeline FP implementa il design pattern Facade, il quale consente di ridurre l’accoppiamento tra un
insieme di classi strettamente correlate (unità funzionali), e il resto del sistema.
La classe ”facciata” fornisce un’interfaccia di alto livello verso un sottosistema, in-
67
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
Figura 4.11: Diagramma UML delle classi della pipeline FP
vocando i metodi delle classi di basso livello; essa è ”opaca”, nel senso che le classi
che usano il sistema non possono accedere a quelle di basso livello direttamente.
Nella Figura 4.11 è mostrata la classe ”facciata”, ovvero FPPipeline, che coordina le operazioni di accesso alle tre unità funzionali da parte della classe CPU.
Le tre unità funzionali, rappresentate dalle classi Adder,Multiplier e Divider, implementano l’interfaccia FPFunctionalUnit, la quale specifica i 5 metodi mediante
cui è assicurata la loro connessione con la classe FPPipeline.
4.4.2
File di configurazione della FPU
La nuova pipeline di EduMIPS64, comprensiva di quella intera e quella floating
point, richiede un adeguamento funzionale rispetto all’aggiunta di nuove istruzioni
dell’ISA MIPS64. Particolare interesse è rivolto a due tipi di istruzioni: aritmetiche FP e di terminazione del programma. Le prime devono essere note alla classe CPU, che deve spostarle dallo stadio ID alle relative unità funzionali.
Le seconde devono essere riconosciute per impedir loro di fermare la pipeline
68
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
in anticipo (prima che essa si svuoti); nel successivo paragrafo si affronteranno
meglio queste problematiche. La classe FPUConfigurator, invocata nel contruttore della CPU, effettua il parsing del file fpu.properties, contenente linee del tipo
<tag>istruzione</tag>, e carica le due liste
knownFPInstructions e terminatingInstructionsOPCodes. La prima contiene
i nomi delle istruzioni aritmetiche FP, la seconda gli opcode10 delle istruzioni di
terminazione.
4.4.3
Interazione tra CPU ed FPU
EduMIPS64 si avvale di 2 thread che, in modo approssimativo, separano la
gestione della GUI da quella delle operazioni di calcolo. Il thread che si occupa di
”far lavorare” la CPU si chiama CPUGUIThread. Esso è in attività per la durata
del singolo ciclo, successivamente va in stato di attesa fino a che l’utente non da
il comando per eseguire il ciclo successivo. Per ogni ciclo, questa classe invoca il
metodo step() della classe CPU, in cui si eseguono delle operazioni iniziali:
1. Si aggiornano, attivandole o disattivandole, le trap per le 4 eccezioni IEEE.
Poichè esse sono configurabili dall’utente, le informazioni sul loro stato
vengono prelevate da una struttura dati11 che configura la GUI.
2. Nello stesso modo illustrato prima, si configura la modalità di arrondamento,
aggiornando i bit RM del registro FCSR.
3. Si esegue lo stadio WB.
10
L’opcode è un campo del codice binario dell’istruzione che la identifica univocamente in un
ISA
11
La classe Config fornisce i metodi per caricare, leggere o scrivere una hashmap java
contenente le configurazioni della GUI
69
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
Successivamente, per ogni istruzione nella pipeline, si invocano i metodi dello
stadio corrispondente12 . Ciascuna sezione di codice che si occupa dell’invocazione
dei metodi delle istruzioni, ha subito profondi cambiamenti per risolvere le problematiche insite nell’ILP delle istruzioni floating point. L’unico stadio che non
presenta alcuna modifica è IF, per tale ragione è stato omesso dalla trattazione
che segue.
Stadio WB
Il metodo WB delle istruzioni di terminazione (HALT e SYSCALL 0) invoca l’eccezione HaltException, che imposta lo stato della CPU ad HALTED. Nell’ottica
di una pipeline intera, questa logica non causa problemi; le istruzioni di terminazione attraversano la pipeline intera e, poichè sono le ultime ad essere eseguite
in un programma, possono segnalare alla CPU di fermare la pipeline.
Trattare invece con le istruzioni floating point a lunga latenza, al contrario,
rende possibili nuovi scenari. Una qualunque istruzione aritmetica floating point,
subito seguita da una di terminazione, uscirà dalla pipeline diversi cicli dopo
quella di terminazione; la latenza delle unità funzionali provoca, cioè, l’inversione
dell’ordine di uscita dalla pipeline delle istruzioni.
Nella nuova situazione, il metodo WB delle istruzioni, che potrebbe contenere
anche del codice che fa terminare la pipeline (istruzioni di terminazione), non può
essere eseguito senza un preventivo controllo. Nel listato che segue, la variabile
booleana notWBable viene sommata logicamente alle condizioni che devono verificarsi per non eseguire il metodo WB() dell’istruzione: l’istruzione in WB è di
terminazione e qualche unità funzionale sta ancora calcolando, oppure, mantenen12
Se ad esempio un’istruzione è al terzo posto dell’hash map che rappresenta la pipeline intera,
si invoca il metodo EX()
70
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
do la prima condizione, le unità funzionali non lavorano, ma un’istruzione occupa
lo stadio MEM.
boolean notWBable=f a l s e ;
notWBable=notWBable | | ( terminatorInstrInWB && ! fpPipe . isEmpty ( ) ) ;
notWBable=notWBable | | ( terminatorInstrInWB && fpPipe . isEmpty ( ) &&
pipe . get ( PipeStatus . MEM ) . getName ( ) ! = " " ) ;
i f ( ! notWBable )
pipe . get ( PipeStatus . WB ) . WB ( ) ;
Un ultimo problema da risolvere è come fermare la pipeline appena ”escono”
tutte le istruzioni, dal momento che si è praticamente ignorata l’istruzione di terminazione. La soluzione era già presente nel codice del simulatore; l’esecuzione
dello stadio ID delle istruzioni di terminazione provoca il cambiamento di stato
della CPU da RUNNING a STOPPING. È sufficiente rilevare questo cambiamento per lanciare l’eccezione HaltException dopo aver verificato che la pipeline è
effettivamente vuota.
i f ( arePipelinesEmpties ( ) && getStatus()==CPUStatus . STOPPING ) {
setStatus ( CPU . CPUStatus . HALTED ) ;
throw new HaltException ( ) ;
}
Stadio MEM
A questo stadio non sono state apportate importanti modifiche. L’unico cambiamento riguarda la ”messa a null” dell’oggetto puntato dalla chiave MEM nell’hash map che simula la pipeline. Tale azione è richiesta perchè l’esecuzione di
un qualunque stadio diverso da WB è permesso solo se è garantito lo spostamento
successivo in avanti dell’oggetto istruzione13 . Per esempio, l’istruzione in EX può
essere eseguita solo se MEM vale null. Se questo non è possibile, più avanti sono
mostrate le tecniche per risolvere il problema.
13
Se un’istruzione non potrà essere spostata in avanti al successivo ciclo di clock, vuol dire
che si verificherà uno stallo strutturale
71
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
currentPipeStatus = PipeStatus . MEM ;
i f ( pipe . get ( PipeStatus . MEM )!= null )
pipe . get ( PipeStatus . MEM ) . MEM ( ) ;
pipe . put ( PipeStatus . WB , pipe . get ( PipeStatus . MEM ) ) ;
pipe . put ( PipeStatus . MEM , null ) ;
Stadio EX
La difficoltà nella gestione di questo stadio è dovuta al fatto che si deve
prevedere se, nel ciclo successivo, avverrà un hazard strutturale, perchè più istruzioni14
giungono allo stadio MEM contemporaneamente.
Le condizioni sufficienti affinchè si verifichi un hazard strutturale di questo
tipo, nel ciclo n + 1, sono che nel ciclo n:
- un’istruzione intera occupa lo stadio EX, e almeno un’istruzione aritmetica
floating point si trova negli stadi A4,M7,DIV24
- nessuna istruzione intera occupa lo stadio EX, ma almeno due istruzioni
aritmetiche FP occupano due degli stadi A4,M7,DIV24.
Come si è visto, il verificarsi di un hazard strutturale, dovuto al conflitto sullo
stadio MEM, è da addebitare esclusivamente alle istruzioni aritmetiche floating
che concorrono tra esse, o con l’istruzione intera, per occupare questo stadio.
Il sistema di previsione di questi hazard si deve quindi basare sul controllo della
pipeline floating point. La classe FPPipeline, vista nel Paragrafo 4.4.1, fornisce
il metodo getInstruction(), al quale viene passato come parametro uno due
possibili valori booleani SIMUL MODE ENABLED o SIMUL MODE DISABLED. Col primo
parametro, il metodo simula le operazioni della pipeline FP ottenute col ciclo di
clock n + 1, senza apportare alcuna modifica all’interno di essa (non si spostano
14
Lo stadio MEM è condiviso dall’istruzione intera che ha appena eseguito EX, e dalle
eventuali istruzioni aritmetiche FP che escono dalle unità funzionali
72
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
le istruzioni in avanti). Se l’oggetto ritornato è un’istruzione, e non un null,
nel ciclo n + 1 esso pretenderà di occupare lo stadio MEM. Passando al metodo il
booleano SIMUL MODE DISABLED, invece, l’eventuale istruzione candidata ad uscire
dalla pipeline FP viene definitivamente estratta, e la sua precedente posizione
posta a null.
La prima modifica, apportata nella classe CPU al livello della gestione dello stadio EX per le istruzioni intere, riguarda l’esecuzione in modo condizionato del codice estente; quest’ultimo invoca il metodo EX() delle istruzioni intere
catturando le eventuali eccezioni sincrone e gestendole.
try {
currentPipeStatus = PipeStatus . EX ;
// s e l o s t a d i o EX è non v u o t o s i i n v o c a EX( ) d e l l ’ i s t r u z i o n e
i f ( pipe . get ( PipeStatus . EX )!= null )
pipe . get ( PipeStatus . EX ) . EX ( ) ;
} catch ( SynchronousException e ) {
i f ( masked )
// q u a l u n q u e t r a p non può a v v e n i r e p e r c hè l ’ e c c e z i o n e
// s i n c r o n a è mascherata
edumips64 . Main . logger . exception ( "[ MASKED ] " + e . getCode ( ) ) ;
else {
i f ( terminate ) {
// programma t e r m i n a t o s e l ’ e c c e z i o n e è non mascherata
edumips64 . Main . logger . log ( " Terminazione " ) ;
throw new SynchronousException ( e . getCode ( ) ) ;
} else
// i l programma c o n t i n u a ad e s e g u i r e anche s e
// l ’ e c c e z i o n e è non mascherata
syncex = e . getCode ( ) ;
}
}
Listato 4.5: Invocazione metodo EX e gestione eccezioni sincrone
Se il metodo getInstruction(SIMUL MODE ENABLED), invocato dalla classe CPU
sulla classe FPPipeline, ritorna null, viene eseguito il codice del Listato 4.5, pas-
73
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
sando l’istruzione intera in MEM. In questo caso, nessuna istruzione, nel ciclo che
si sta eseguendo15 (n + 1), uscirà dalle unità funzionali, e non si verificherà alcuno
stallo strutturale.
Se il metodo, invece, restituisce un’oggetto istruzione diverso da una bubble 16 ,
nel ciclo corrente (n + 1) si potrebbe verificare uno stallo strutturale. Prima di
eseguire il codice del listato devono effettuarsi queste operazioni:
- se lo stadio EX è occupato da un’istruzione che non sia una bubble, oppure
se le istruzioni aritmetiche FP, che nel ciclo n+1 usciranno dalla pipeline FP
(fpPipe), sono più di una, si deve incrementare il contatore memoryStalls,
il quale tiene il conto degli hazard strutturali in cui è implicata la memoria.
i f ( ( pipe . get ( PipeStatus . EX )!= null &&
! ( pipe . get ( PipeStatus . EX ) . getName ( ) . compareTo ( " " ) = = 0 ) ) | |
fpPipe . getNReadyToExitInstr () >1)
memoryStalls++;
- col metodo getInstruction(SIMUL MODE DISABLED), si ottiene l’istruzione
FP della quale si invocherà il metodo EX()
instr=fpPipe . getInstruction ( SIMUL_MODE_DISABLED ) ;
Dopo queste operazioni si può eseguire un codice analogo a quello del Listato 4.5.
instr=fpPipe . getInstruction ( SIMUL_MODE_DISABLED ) ;
try {
currentPipeStatus = PipeStatus . EX ;
instr . EX ( ) ;
....
Un’ultima operazione, che conclude lo stadio EX, consiste nel far traslare le
istruzioni nella pipeline FP invocando il metodo FPPipeline::step().
15
Si conosce lo stato della pipeline apportato dal ciclo n, e ci si trova dentro il ciclo (n+1);
ma ancora non è stato apportato alcun cambiamento allo stato del ciclo n
16
Istruzione usata per riempire gli spazi vuoti della pipeline
74
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
Stadio ID
Prima di introdurre la pipeline FP, il codice della classe CPU spostava qualunque
istruzione dallo stadio ID a quello EX. La gestione delle istruzioni aritmetiche
floating point, che sono le uniche ad utilizzare la pipeline FP, introduce la possibilità che l’istruzione in ID debba essere ”deviata” verso le unità funzionali. Il
discriminante per le due modalità di esecuzione è la presenza dell’istruzione, che si
trova in ID, nella lista delle istruzioni aritmetiche FP (knownFPInstructions). Se
l’istruzione è di quest’ultimo tipo, si effettuano le seguenti operazioni: si invoca,
in modalità simulazione, il metodo putInstruction() della classe FPPipeline passandogli l’oggetto istruzione in ID. Se non viene restituito alcun codice di errore,
nel ciclo n + 1 non ci sarà alcuno stallo strutturale (il divisore non è occupato); si
invoca quindi il metodo ID() dell’istruzione in questo stadio, e successivamente
il metodo putInstruction in modo non simulativo. Se, al contrario, viene restituito un codice di errore, si aprono due possibili scenari: si sta inserendo una
divisione nel divisore ma questo è già occupato; non essendo fully pipelined, viene
lanciata l’eccezione FPDividerNotAvailableException(). Oppure, si sta inserendo
un’istruzione diversa dalla divisione in una delle altre due unità funzionali che non
la può accettare; ciò è dovuto al fatto che si ha un ”ingorgo” di istruzioni, causato
da una serie di hazard strutturali successivi per la non disponibilità memoria. Si
supponga, per esempio, questa sequenza di istruzioni non affette da dipendenze:
MUL.D,MUL.D,MUL.D,ADD.D,ADD.D,ADD.D,ADD.D,ADD.D
l’ultima ADD.D non può accedere da subito alla pipeline FP perchè l’addizionatore
ha tutti gli stadi pieni, e le istruzioni che li occupano saranno fermate all’altra
estremità fino a che tutte le MUL.D, che hanno maggiore priorità, libereranno il moltiplicatore. Fino a quando permane questa situazione, viene lanciata
75
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
l’eccezione FPFunctionalUnitNotAvailableException, per fermare il fetch di nuove
istruzioni e la parte iniziale della pipeline.
Se l’istruzione è invece intera, si deve affrontare un problema simile a quello visto in precedenza. Le istruzioni intere, avendo la minore priorità ad acquisire l’accesso allo stadio MEM quando concorrono con le istruzioni FP, sono
più soggette ad essere fermate nello stadio EX. Dell’istruzione in ID, quindi, non
si può invocare il relativo metodo fino a che quella in EX non viene spostata.
Durante questo intervallo, devono essere fermati i fetch e la parte sinistra della
pipeline con l’eccezione EXNotAvailableException. Segue il Listato 4.6 che illustra
quanto è stato detto.
currentPipeStatus = PipeStatus . ID ;
i f ( pipe . get ( PipeStatus . ID )!= null ) {
i f ( knownFPInstructions . contains ( pipe . get ( PipeStatus . ID ) . getName ( ) ) ) {
// l ’ i s t r u z i o n e è a r i t m e t i c a f l o a t i n g p o i n t
i f ( fpPipe . putInstruction ( pipe . get ( PipeStatus . ID ) , SIMUL_MODE_ENABLED )==0){
//L ’ u n i t à f u n z i o n a l e è l i b e r a
pipe . get ( PipeStatus . ID ) . ID ( ) ;
fpPipe . putInstruction ( pipe . get ( PipeStatus . ID ) , SIMUL_MODE_DISABLED ) ;
pipe . put ( PipeStatus . ID , null ) ;
} else {
//L ’ u n i t à f u n z i o n a l e è o c c u p a t a
i f ( pipe . get ( PipeStatus . ID ) . getName ( ) . compareToIgnoreCase ( "DIV.D")==0)
// s t a l l o s t r u t t u r a l e p e r c hè i l d i v i s o r e è o c c u p a t o
throw new FPDividerNotAvailableException ( ) ;
else
// s t o p f e t c h p e r c hè c ’ è i n g o r g o
throw new FPFunctionalUnitNotAvailableException ( ) ;
}
}
else {
// l ’ i s t r u z i o n e i n ID è i n t e r a
i f ( pipe . get ( PipeStatus . EX)==null | |
pipe . get ( PipeStatus . EX ) . getName ( ) . compareTo ( " ")==0) {
// l o s t a d i o EX è d i s p o n i b i l e ad a c c e t t a r e un ’ i s t r u z i o n e ( v u o t o o b o l l a )
pipe . get ( PipeStatus . ID ) . ID ( ) ;
pipe . put ( PipeStatus . EX , pipe . get ( PipeStatus . ID ) ) ;
pipe . put ( PipeStatus . ID , null ) ;
76
CAPITOLO 4. LA FLOATING POINT UNIT DI EDUMIPS64
}
else {
// l o s t a d i o EX è o c c u p a t o
throw new EXNotAvailableException ( ) ;
}
}
}
Listato 4.6: Gestione dello stadio ID
77
Capitolo 5
Conclusioni
In questa tesi sono state illustrate le scelte progettuali per la costruzione
di un’infrastruttura ad oggetti, in Java, che modella i componenti hardware e
software di una floating point unit, da integrare in un simulatore di CPU MIPS64.
Il simulatore in questione è EduMIPS64; la FPU in esso sviluppata si basa
su un modello generico di processore MIPS64, di cui non si conosce a priori la
strategia computatazionale mediante cui le operazioni floating point vengono eseguite. Per tale ragione, sono stati trascurati alcuni dettagli implementativi della
pipeline che riguardano i conflitti tra le unità funzionali. Un interessante sviluppo di questa tesi sarebbe, oltre completare l’instruction set implementando le
istruzioni floating point in singola precisione, progettare una piattaforma che configuri sia la pipeline ad emulare un modello specifico di processore MIPS64, che
l’istruction set ad essere eseguito con i relativi stadi. Ciò consentirebbe di colmare
ulteriormente il divario tra simulatore e architettura reale.
Tutto il codice scritto per questa tesi sarà integrato nella versione 1.0 di EduMIPS64, tuttavia, fino all’uscita di questa versione, è possibile provare le nuove
funzionalità apportate scaricando, dal sito ufficiale [3], la versione svn.
Il codice è rilasciato sotto la licenza GNU General Public License (GPL), come
78
CAPITOLO 5. CONCLUSIONI
tra l’altro l’intero progetto EduMIPS64.
79
Appendice A
Manuale utente della FPU di
EduMIPS64
A.1
Introduzione
Questa appendice completa il Manuale utente di EduMIPS64[1] con il capitolo
relativo all’utilizzo della floating point unit. Esso verrà integrato nel manuale a
partire dalla versione 1.0 del simulatore. Nel Paragrafo A.2 viene introdotto il
formato double, i valori speciali definiti dallo standard IEEE754, e le condizioni
di eccezioni che i calcoli floating point possono provocare.
Nel Paragrafo A.3 viene illustrato come EduMIPS64 consente di attivare e
disattivare le trap relative alle condizioni di eccezione IEEE.
Nel Paragrafo A.4 si parla del modo in cui i valori double e i valori speciali
vengono accettati da EduMIPS64 per essere caricati in memoria.
Nel Paragrafo A.5 è introdotto il registro FCSR, usato dalla FPU per autogestirsi. In esso vengono memorizzate le informazioni riguardanti l’arrotondamento,
i codici condizionali e le politiche di gestione delle eccezioni IEEE.
Infine, nel Paragrafo A.6 sono elencate tutte le istruzioni dell’ISA MIPS64
implementate in EduMIPS64.
80
APPENDICE A. MANUALE UTENTE DELLA FPU DI EDUMIPS64
A.2
Valori speciali
L’aritmetica in virgola mobile dei calcolatori è caratterizzata dal fatto che,
anche in presenza di un’operazione matematica non valida, si potrebbe scegliere
di continuare la computazione ignorando quanto è accaduto. In questo scenario,
operazioni come divisioni tra zeri, oppure radici quadrate di numeri negativi devono generare comunque un risultato che, non essendo un numero (Not a Number), è trattato come qualcosa di diverso. Prima di continuare questa trattazione,
vengono definiti i numeri in virgola mobile (floating point) a doppia precisione.
Vengono chiamati double e possono rappresentare valori compresi nel dominio
[−1.79E308, −4.94E − 324] ∪ {0} ∪ [4.94E − 324, 1.79E308].
A.2.1
NaN o Invalid Operation
Lo standard IEEE 754, il quale regolamenta la manipolazione dei numeri floating point nei calcolatori, ha definito che le operazioni matematiche non valide
possono sia provocare una segnalazione durante l’esecuzione del programma (trap
per la condizione di eccezione IEEE Invalid Operation), che fornire, come risultato, il valore speciale QNaN (Quit Not a Number). Un altro valore NaN, che
genera incondizionatamente la stessa trap appena viene rilevato come operando,
è SNaN (Signalling NaN). Tale valore è raramente utilizzato nelle applicazioni, e
storicamente è stato usato per inizializzare le variabili.
A.2.2
Zeri o Underflow
Un altro valore speciale definito dallo standard è lo zero. Dal momento che
il formato double non include lo zero nel dominio dei valori rappresentati, esso è
considerato alla stregua di un valore speciale. Esistono uno zero positivo e uno
81
APPENDICE A. MANUALE UTENTE DELLA FPU DI EDUMIPS64
negativo: quando si tenta di rappresentare un valore negativo molto vicino allo
zero, ma fuori dal dominio rappresentabile dai double (∈] − 4.94E − 324, 0[),
e si desidera a tutti i costi un risultato (non una trap per la condizione di eccezione
Underflow), allora il numero restituito è −0. Analogamente, viene restituito +0
se si tenta di rappresentare un numero nell’intervallo [0, 4.94E − 324[, e non si è
preferito avere la stessa trap.
A.2.3
Infiniti od Overflow
Quando si tenta di rappresentare un numero estremamente grande (∈]1.79E308, +∞[),
o estrememente piccolo (∈] − ∞, −1.79E308[), al di fuori cioè del dominio rappresentabile dal formato double, vengono restituiti i valori speciali +∞, nel primo
caso, e −∞ nel secondo caso. In alternativa, si può avere una trap per via della
condizione di eccezione Overflow.
A.2.4
Infiniti o Divide by zero
Gli infiniti potrebbero anche essere restituiti da una divisione per zero, nelle
quali si effettua il prodotto tra il segno dello zero e quello del divisore, per restituire
un opportuno infinito. Nel caso in cui non si voglia alcun valore restituito, si
verifica la trap per la condizione di eccezione Divide by zero.
A.3
Configurazione delle eccezioni
EduMIPS64 consente di abilitare o disabilitare le trap relative alle 4 delle 5
condizioni di eccezione IEEE, implementate dalla scheda Eccezioni FPU della
finestra Configura→ Impostazioni. Se esse sono disabilitate, verrà fornito un
risultato per qualunque operazione speciale la FPU effettui (si veda il Paragrafo
A.2). Nel caso illustrato in Figura A.1, in cui alcune caselle di controllo sono
82
APPENDICE A. MANUALE UTENTE DELLA FPU DI EDUMIPS64
spuntate, se la CPU non maschera le eccezioni sincrone nel simulatore (Figura
A.2), verrà simulata una trap relativa alla condizione di eccezione IEEE che si è
verificata (Figura A.3).
Figura A.1: Configurazione delle trap per le eccezioni IEEE
Figura A.2: Opzione che maschera le eccezioni sincrone(disabilita tutte le trap)
Figura A.3: Finestra che notifica la trap
83
APPENDICE A. MANUALE UTENTE DELLA FPU DI EDUMIPS64
A.4
Direttiva .double
La direttiva .double, da inserire nella sezione .data del file sorgente (.s),
consente di allocare una cella della memoria di EduMIPS64, dove inserire un
valore formattato double. Le sintassi valide del comando sono
nome_variabile : . double numero_double
nome_variabile : . double parola_chiave
dove numero double può essere rappresentato sia in forma estesa (1.0,0.003),
oppure in notazione scientifica (3.7E-12,0.5E32). Invece, parola chiave può
assumere i valori POSITIVEINFINITY, NEGATIVEINFINITY,
POSITIVEZERO, NEGATIVEZERO, SNAN e QNAN, consentendo l’inserimento diretto
in memoria dei valori speciali.
A.5
Registro FCSR
L’FCSR(Floating point Control Status Register) è il registro che controlla i
diversi aspetti funzionali della FPU. Esso è lungo 32 bit e, fino alla ridisegnazione
grafica di EduMIPS64, sarà posto nella finestra delle statistiche. Il campo FCC
è costituito da 8 bit, identificati con numeri da 0 a 7. Le istruzioni condizionali
(C.EQ.D,C.LT.D) lo utilizzano per memorizzare il risultato booleano di un confronto tra due registri. I campi Cause, Enables e Flag gestiscono la dinamica
delle eccezioni IEEE, illustrate nel Paragrafo A.2. Essi sono costituiti, ognuno,
da 5 bit identificati con le lettere V (Invalid operation), Z (Divide by zero), O
(Overflow),U (Underflow) e I (Inexact); quest’ultimo bit non viene al momento
utilizzato.
Il campo Cause indica se si è verificata una qualunque eccezione IEEE durante
la simulazione, presentando un 1 nel relativo bit. È utile quando si esegue un
84
APPENDICE A. MANUALE UTENTE DELLA FPU DI EDUMIPS64
Figura A.4: Registro FCSR in EduMIPS64
programma dall’inizio alla fine senza fermarlo, per determinare se si è verificata
una qualunque eccezione.
Il campo Enable mostra le eccezioni IEEE per le quali è attiva la trap. I
bit di questo campo vengono modificati, anche senza resettare il simulatore, dalla
finestra di configurazione della Figura A.1.
Il campo Flag mostra le eccezioni IEEE che si sono verificate ma, non avendo
la relativa trap attivata, hanno fornito come risultato dei valori speciali, illustrati
nel Paragrafo A.2.
Il campo RM mostra la modalità di arrotondamento corrente usata, in EduMIPS64, per le istruzioni che convertono numeri floating point in interi (si veda
l’istruzione CVT.L.D per ulteriori dettagli).
A.6
Instruction set
Per una consultazione efficiente, le istruzioni dell’ISA MIPS64, implementate
in EduMIPS64, vengono elencate in ordine alfabetico. Le operazioni eseguite ven85
APPENDICE A. MANUALE UTENTE DELLA FPU DI EDUMIPS64
gono rappresentate mediante uno pseudocodice in cui l’i−esima cella di memoria
è indicata con memory[i], i bit del campo FCC del registro FCSR mediante
FCSR FCC[cc], con cc ∈ [0, 7]. In alcune istruzioni, per evitare ambiguità, i registri sono indicati come GPR[i] e FPR[i], con i ∈ [0, 31], ma nella maggior
parte dei casi essi vengono indicati qualitativamente con la notazione rx o fx,
dove x ∈ {d, s, t}. Le tre lettere servono solo a distinguere, al più, tre registri per ogni istruzione. Infine, i valori ritornati dalle operazioni di conversione
vengono indicati con la notazione convert tipoconversione(registro[,tipo
arrotondamento]), dove il parametro tra parentesi quadre è presente solo in
certe circostanze.
Per prendere confidenza con le istruzioni floating point, alcuni file sorgenti
possono essere scaricati dal link
http://www.edumips.org/attachment/wiki/Upload/FPUMaxSamples.rar.
ADD.D
Sintassi:
Descrizione:
Eccezioni:
ADD.D fd, fs, ft
fd = fs + ft
Le trap di Overflow e Underflow vengono generate se il risultato non può essere rappresentato secondo lo standard IEEE
754. Invalid Operation è generata se fs o ft contengono
QNaN o SNaN, o se viene eseguita un’operazione non valida
(+∞ − ∞).
86
APPENDICE A. MANUALE UTENTE DELLA FPU DI EDUMIPS64
BC1F
Sintassi:
Descrizione:
Esempio:
BC1F cc, offset
if FCSR FCC[cc] == 0 then branch
Verifica se il valore booleano FCSR FCC[cc] è falso ed effettua, in tal caso, un salto PC-relative. Questa istruzione può
accedere al registro FCSR solo in lettura; l’informazione dev’essere scritta da una precedente istruzione condizionale del
tipo C.condizione.D.
C.EQ.D 7,f1,f2
BC1F 7,label
In questo esempio C.EQ.D verifica l’uguaglianza tra i registri
f1 ed f2, scrivendo il risultato booleano del confronto nel bit
7 del campo FCC del registro FCSR. Dopodichè BC1F verifica
se quel bit vale 0 (falso) e salta a label.
BC1T
Sintassi:
Descrizione:
Esempio:
BC1T cc, offset
if FCSR FCC[cc] == 1 then branch
Verifica se il valore booleano FCSR FCC[cc] è vero ed effettua, in tal caso, un salto PC-relative. Questa istruzione può
accedere al registro FCSR solo in lettura; l’informazione dev’essere scritta da una precedente istruzione condizionale del
tipo C.condizione.D.
C.EQ.D 7,f1,f2
BC1T 7,label
In questo esempio, C.EQ.D verifica l’uguaglianza tra i registri
f1 ed f2, scrivendo il risultato booleano del confronto nel bit
7 del campo FCC del registro FCSR. Dopodichè BC1T verifica
se quel bit vale 1 (vero) e salta a label.
87
APPENDICE A. MANUALE UTENTE DELLA FPU DI EDUMIPS64
C.EQ.D
Sintassi:
Descrizione:
Esempio:
Eccezioni:
C.EQ.D cc, fs, ft
FCSR FCC[cc] = (fs==ft)
Verifica il predicato ”uguale a” tra i due registri fs ed ft e salva
il risultato booleano in FCSR FCC[cc]. Questo valore verrà
utilizzato da un’istruzione successiva per effettuare un salto
condizionato (branch) o un movimento di dati condizionato
tra registri floating point.
C.EQ.D 2,f1,f2
MOVT.D f8,f9,2
In questo esempio C.EQ.D verifica l’uguaglianza tra i registri
f1 ed f2, scrivendo il risultato booleano del confronto nel bit 2
del campo FCC del registro FCSR. Dopodichè MOVT.D verifica
se quel bit vale 1 (vero), e copia il registro f9 su f8.
Invalid Operation è lanciata quando fs o ft contengono valori
QNaN (se attiva, si ha una trap) o SNaN(si ha sempre una
trap).
C.LT.D
Sintassi:
Descrizione:
Esempio:
Eccezioni:
C.LT.D cc, fs, ft
FCSR FCC[cc] = (fs<ft)
Verifica il predicato ”minore di”(Less Than) tra i due registri fs ed ft, e salva il risultato booleano in FCSR FCC[cc].
Questo valore verrà utilizzato da un’istruzione successiva, per
effettuare un salto condizionato (branch), o per un movimento
di dati condizionato tra registri floating point.
C.LT.D 2,f1,f2
BC1T 2,target
In questo esempio, C.LT.D verifica se f1 è minore di f2, scrivendo il risultato booleano del confronto nel bit 2 del campo FCC
del registro FCSR. Dopodichè, BC1T verifica se quel bit vale 1
(vero), e salta a target
Invalid Operation è lanciata quando fs o ft contengono valori
QNaN (se attiva, si ha una trap) o SNaN(si ha sempre una
trap).
88
APPENDICE A. MANUALE UTENTE DELLA FPU DI EDUMIPS64
CVT.D.L
Sintassi:
Descrizione:
Esempio:
Eccezioni:
CVT.D.L fd, fs
fd = convert longToDouble(fs)
Converte un long in un double
DMTC1 r6,f5
CVT.D.L f5,f5
In questo esempio, DMTC1 copia il valore del GPR r6 nell’FPR
f5. Successivamente, CVT.D.L converte il numero in f5 da long
a double.
Supponendo r6 = 52, dopo l’esecuzione di DMTC1, l’equivalente binario di 52 viene copiato nel registro f5 (f5 non contiene
ancora il valore 52.0 perchè non è stato formattato ancora
come double). Dopo l’esecuzione di CVT.D.L, f 5 = 52.0.
Invalid Operation è lanciata quando fs contiene QNaN,SNaN
o Infinito
CVT.D.W
Sintassi:
Descrizione:
Esempio:
Eccezioni:
CVT.D.W fd, fs
fd = convert IntToDouble(fs)
Converte un int in un double
MTC1 r6,f5
CVT.D.W f5,f5
In questo esempio, MTC1 copia i 32 bit più bassi del GPR r6
nell’FPR f5. Successivamente, CVT.D.W, leggendo prima f5
come int, lo sovrascrive in double.
Supponendo r6=0xAAAAAAAABBBBBBBB, dopo l’esecuzione di
MTC1 si ha che f5=0xXXXXXXXXBBBBBBBB; si noti che i suoi
32 bit più alti (XX..X) sono UNDEFINED (non sono stati
sovrascritti). CVT.D.W legge f5 come int (f5=-1145324613),
formattandolo poi in double (f5=0xC1D1111111400000
=-1.145324613E9).
Invalid Operation è lanciata quando fs contiene QNaN,SNaN
o Infinito
89
APPENDICE A. MANUALE UTENTE DELLA FPU DI EDUMIPS64
CVT.L.D
Sintassi:
Descrizione:
Esempio:
Eccezioni:
CVT.L.D fd, fs
fd = convert doubleToLong(fs, CurrentRoundingMode)
Converte, dapprima arrotondandolo, un double in un long
CVT.L.D f5,f5
DMFC1 r6,f5
In questo esempio, CVT.L.D converte il double in f5 in un
long. Dopodichè, DMFC1 copia l’FPR f5 nel GPR r6. Il risultato di questa istruzione dipende dalla modalità di arrotondamento corrente, che viene impostata dalla scheda Arrotondamenti FPU della finestra Configura → Impostazioni, come
in Figura A.5.
Invalid Operation è lanciata quando fs vale Infinito, XNaN, o
il risultato è fuori dall’intervallo dei long [−263 , 263 − 1]
Figura A.5: Configurazione modalità di arrotondamento
Tipo
Campo RM
Al più vicino
0
Al più vicino
0
Al più vicino
0
Al più vicino
0
Verso lo zero
1
Verso lo zero
1
Verso +∞
2
Verso +∞
2
Verso −∞
3
Verso −∞
3
Registro f5
6.4
6.8
6.5
7.5
7.1
-2.3
4.2
-3.9
4.2
-3.9
Registro r6
6
7
6 (al pari)
8 (al pari)
7
-2
5
-3
4
-4
Tabella A.1: Esempi sui tipi di arrotondamento
90
APPENDICE A. MANUALE UTENTE DELLA FPU DI EDUMIPS64
CVT.W.D
Sintassi:
Descrizione:
Eccezioni:
CVT.W.D fd, fs
fd = convert DoubleToInt(fs, CurrentRoundingMode)
Converte un double in un int utilizzando la modalità di
arrotondamento corrente, illustrata per l’istruzione CVT.L.D
Invalid Operation è lanciata quando fs è Infinito, XNaN,
o il risultato è fuori dall’intervallo degli interi con segno
[−231 , 231 − 1]
DIV.D
Sintassi:
Descrizione:
Eccezioni:
DIV.D fd, fs, ft
fd = fs ÷ ft
Le trap di Overflow e Underflow vengono generate se il risultato non può essere rappresentato secondo lo standard IEEE
754. Invalid Operation è generata se fs o ft contengono
QNaN o SNaN, o se viene eseguita un’operazione non valida (0 ÷ 0,∞ ÷ ∞). Divide by zero è generata se è eseguita
una divisione per zero che non ha per dividendo un XNaN
(5 ÷ 0).
DMFC1
Sintassi:
Descrizione:
DMFC1 rt,fs
rt = fs
Copia l’intero contenuto binario dell’FPR fs nel GPR rt. Nessun controllo di formato viene eseguito su fs prima della
copia.
DMTC1
Sintassi:
Descrizione:
DMTC1 rt, fs
fs = rt
Copia il contenuto binario del GPR rt nell’ FPR fs.
L.D (”DEPRECATED”: retrocompabitibilità con WinMIPS64)
Sintassi:
Descrizione:
L.D ft, offset(base)
memory[GPR[base] + offset]
Carica una doubleword (64 bit) dalla memoria all’FPR ft.
Questa istruzione non appartiene all’ISA MIPS64; si consiglia
l’utilizzo di LDC1.
91
APPENDICE A. MANUALE UTENTE DELLA FPU DI EDUMIPS64
LDC1
Sintassi:
Descrizione:
LDC1 ft, offset(base)
memory[GPR[base] + offset]
Carica una doubleword (64 bit) dalla memoria all’FPR ft
LWC1
Sintassi:
Descrizione:
LWC1 ft, offset(base)
memory[GPR[base] + offset]
Carica una doubleword (64 bit) dalla memoria all’FPR ft
MFC1
Sintassi:
Descrizione:
Esempio:
MFC1 rt, fs
rt = readInt(fs)
Legge l’FPR fs come int e scrive il GPR rt come long
MFC1 r6,f5
SD r6,inmemoria(R0)
Si supponga f5=0xAAAAAAAABBBBBBBB; MFC1 legge f5 come
int, cioè i 32 bit più bassi (interpreta BBBBBBBB come
-1145324613), e lo scrive in r6 (64 bit). Dopo l’esecuzione di
MFC1, r6=0xFFFFFFFFBBBBBBBB, che equivale a -1145324613
leggendo questo registro come long. Quindi in memoria, pur
utilizzando l’istruzione SD, verrà scritta una doubleword con
valore -1145324613. Questa operazione di conversione è chiamata estensione del segno, il cui approfondimento esula dagli
scopi di questo manuale.
MOVF.D
Sintassi:
Descrizione:
MOVF.D fd, fs, cc
if FCSR FCC[cc] == 0 then fd=fs
Verifica se la condizione di confronto booleana in FCSR FCC[cc] è falsa e copia fs su fd. Nessun controllo sul
formato viene realizzato su fs.
MOVT.D
Sintassi:
Descrizione:
MOVT.D fd, fs, cc
if FCSR FCC[cc] == 1 then fd=fs
Verifica se la condizione di confronto booleana in FCSR FCC[cc] è vera, e copia fs su fd. Nessun controllo sul
formato viene realizzato su fs.
92
APPENDICE A. MANUALE UTENTE DELLA FPU DI EDUMIPS64
MOV.D
Sintassi:
Descrizione:
MOV.D fd, fs
fd = fs
Copia fs su fd senza alcun controllo del formato di fs
MOVN.D
Sintassi:
Descrizione:
MOVN.D fd, fs, rt
if rt != 0 then fd=fs
Copia fs su fd, senza alcun controllo del formato di fs, se il
GPR rt è diverso da zero
MOVZ.D
Sintassi:
Descrizione:
MOVZ.D fd, fs, rt
if rt == 0 then fd=fs
Copia fs su fd, senza alcun controllo del formato di fs, se il il
GPR rt è uguale a zero
MTC1
Sintassi:
Descrizione:
Esempio:
MTC1 rt, fs
fs = rt0..31
Copia la word più bassa di rt scrivendola sull’FPR fs
MTC1 r6,f5
Si supponga r5=0xAAAAAAAABBBBBBBB; MTC1 legge i 32 bit più
bassi di r5 copiandoli nei 32 bit più bassi di f5. Dopo l’esecuzione di MTC1, f5=0xXXXXXXXXBBBBBBBB; si noti che i suoi
32 bit più alti (XX..X) sono UNDEFINED (non sono stati
sovrascritti).
MUL.D
Sintassi:
Descrizione:
Eccezioni:
MUL.D fd, fs, ft
fd = fs×ft
Overflow e Underflow vengono generati se il risultato non può
essere rappresentato secondo lo standard IEEE754. Invalid
Operation è generata se fs o ft contiene QNaN o SNaN, o se
si effettua un’operazione non valida (0 × ∞, QNaN×numero)
93
APPENDICE A. MANUALE UTENTE DELLA FPU DI EDUMIPS64
S.D (”DEPRECATED”: retrocompabitibilità con WinMIPS64)
Sintassi:
Descrizione:
S.D ft, offset(base)
memory[base+ offset] = ft
Copia la doubleword (64 bit) dell’FPR ft in memoria.
SDC1
Sintassi:
Descrizione:
SDC1 ft, offset(base)
memory[base+ offset] = ft
Salva la doubleword (64 bit) dell’FPR ft in memoria.
SUB.D
Sintassi:
Descrizione:
Eccezioni:
SUB.D fd, fs, ft
fd = fs-ft
Overflow and Underflow vengono generati se il risultato non
può essere rappresentato secondo lo standard IEEE754. Invalid Operation è generata se fs o ft contengono QNaN o
SNaN, o se viene eseguita un’operazione non valida (∞ − ∞).
SWC1
Sintassi:
Descrizione:
SWC1 ft, offset(base)
memory[base+ offset] = ft
Salva la word (32 bit) dell’FPR ft in memoria.
94
Bibliografia
[1] Andrea Spadaccini - traduzione italiana di Simona Ullo. Manuale utente
di EduMIPS64. http://www.edumips.org/attachment/wiki/Upload/Manualit.pdf.
[2] Dr.
N.
A.
Harman,
sity
of
Wales
Department
Swansea.
of
High
Computer
Performance
Science,
Univer-
Microprocessors.
http://www.cs.swan.ac.uk/c̃sneal/.
[3] Gruppo
di
sviluppo
di
EduMIPS64.
Sito
web
ufficiale.
http://www.edumips.org.
[4] Israel Koren - University of Massachussetts, Amherst. Computer Arithmetic
Algorithms. A K Peters Natick, Massachusetts, Second edition, 2002.
[5] John L. Hennessy, David A. Patterson.
Computer Architecture - A
quantitative approach. Morgan Kaufmann, Third edition, 2002.
[6] MIPS Technologies Inc.
MIPS R4000 Microprocessor Users Manual.
http://cag.csail.mit.edu/raw/documents/R4400 Uman book Ed2.pdf, 1994.
[7] MIPS Technologies Inc. MIPS64 Architecture For Programmers Volume I:
Introduction to MIPS64 Architecture. http://www.mips.com, Luglio 2005.
95
BIBLIOGRAFIA
[8] MIPS Technologies Inc. MIPS64 Architecture for Programmers Volume II:
The MIPS64 Instruction Set. http://www.mips.com, Luglio 2005.
[9] Morgan Kaufmann. See MIPS Run.
[10] Sivarama Dandamundi. Guide to RISC processors. Springer, 2005.
[11] Sun Microsystems, Inc. What Every Computer Scientist Should Know About
Floating-Point Arithmetic. http://docs-pdf.sun.com/800-7895/800-7895.pdf,
Giugno 1992.
[12] William N.Joy Co-Founder, and Vice President for Research Sun Microsystems,Inc. RISC: Academic Interplay Drives Computer Performance Forward.
http://www.cs.washington.edu.
96
Scarica

UNIVERSIT`A DI CATANIA Facolt`a di Ingegneria corso di laurea in