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