• Non ci sono risultati.

Capitolo 4

N/A
N/A
Protected

Academic year: 2021

Condividi "Capitolo 4"

Copied!
33
0
0

Testo completo

(1)

Realizzazione di una Unità Floating Point (FPU)

4.1 Le FPU presenti in letteratura

La ricerca scientifica in questo ambito purtroppo è sviluppata soprattutto in contesto industriale ed i risultati non sono presenti in letteratura, si è potuto pertanto far riferimento solo a poche architetture studiate in ambito accademico. Le soluzioni riscontrate hanno fornito qualche spunto sulle specifiche da assegnare alla FPU e sull’architettura da utilizzare.

Nel capitolo 2 sono state presentate le definizioni e le specifiche dello standard IEEE 754 dalle quali si può constatare che una struttura compatibile con quest'ultimo deve garantire non solo l'utilizzo dei formati stabiliti, ma anche alcune operazioni fondamentali e la gestione sia dei casi speciali che delle eccezioni.

Per quanto riguarda le operazioni aritmetiche da garantire, per soddisfare lo standard è necessario saper eseguire somma e sottrazione, moltiplicazione, divisione e radice quadrata.

Sono stati fatti degli studi sulla frequenza di uso delle operazioni precedentemente elencate nel carico medio di lavoro di un odierno microprocessore [4.1] e nella figura 4.1 viene mostrato un grafico delle frequenze dell'utilizzo delle varie operazioni nel codice dei programmi indicati nel benchmark SPEC92fp.

(2)

Figura 4.1 – Frequenza di utilizzo delle istruzioni nel benchmark SPEC92fp.

Si nota che le operazioni di somma/sottrazione e di moltiplicazione sono le operazioni aritmetiche più frequenti e che le operazioni di divisione e di radice quadrata vengono utilizzate raramente. In letteratura, come detto, non si trovano molte implementazioni di FPU, soprattutto se si cercano architetture complete. Uno degli studi più approfonditi disponibili viene presentato in [4.1]. Per quanto riguarda la struttura della FPU vengono presentate in questo studio tre architetture la cui differenza è rappresentata dalla quantità di logica per effettuare l'arrotondamento che viene condivisa fra le varie unità funzionali.

(3)

Nella prima architettura tutto il calcolo per l'arrotondamento è concentrato in un'unica unità condivisa. In figura 4.2 viene mostrata la struttura di base. Gli operandi sono memorizzati nel register file nel formato IEEE, prima del loro utilizzo vengono convertiti in un formato interno in cui la mantissa viene normalizzata, l'esponente viene portato al suo valore reale e alcuni flag aggiuntivi permettono di mettere in evidenza i casi speciali. Dopo la conversione viene calcolato il risultato esatto dell'operazione nelle unità funzionali quindi i risultati vengono convogliati dalle varie unità funzionali nell'unità di arrotondamento che riconverte il risultato arrotondato nel formato IEEE.

(4)

Nella seconda architettura il calcolo dell'arrotondamento avviene in due fasi, la prima è realizzata all'interno delle singole unità mentre la seconda viene eseguita in un'unità condivisa di arrotondamento graduale come si può vedere in figura 4.3 . Il motivo di questa scomposizione in due fasi è che l'arrotondamento non è perfettamente identico indipendentemente dall'operazione che viene eseguita sui dati, pertanto l'unità di arrotondamento generale presentata nella prima architettura, per poter operare su risultati provenienti da unità differenti, diventa in parte inefficiente. Nella seconda architettura le parti comuni degli algoritmi di arrotondamento vengono condivise attraverso l'unità di arrotondamento graduale mentre le parti che presentano delle differenze vengono integrate nelle singole unità funzionali. Questo tipo di approccio, tuttavia, rende necessaria l'adozione di un formato di rappresentazione intermedio del risultato, gradual result format, che appesantisce il calcolo.

(5)

Nella terza architettura, presentata in figura 4.4 viene adottata una soluzione radicale ai problemi di condivisione: ogni unità funzionale è dotata di una propria logica di arrotondamento ottimizzata per il trattamento dei risultati prodotti dalla stessa. Questo sistema parrebbe essere troppo dispendioso in termini di area, tuttavia, secondo quanto rilevato da Seidel in [4.1], l'incremento di area risulta trascurabile se confrontato con il miglioramento dell'efficienza.

(6)

Un’ulteriore FPU è proposta dalla Portland State University [4.2] :

• Lavora in Singola Precisione (32-bit).

• Realizza addizione, sottrazione e moltiplicazione.

• Contempla i 4 tipi di arrotondamento previsti dallo standard IEEE. • Genera tutte le eccezioni previste dallo standard IEEE.

Figura 4.5 – Schema FPU Portland State University

Figura 4.6 – Architettura FPU Portland State University

La FPU è composta dai due blocchi di pre-normalizzazione che modificano gli operandi a seconda dell’operazione che devono subire. Nello schema ci sono poi tre blocchi distinti, ciascuno dedicato ad una singola operazione, la divisione però non è presente nella realizzazione finale. Per l’addizione e la sottrazione si usa un solo blocco perché la sottrazione può essere vista come un’addizione in cui va cambiato il segno del secondo operando. Vi è in fine il blocco che realizza la post-normalizzazione, uguale per tutti e un blocco a parte che genera le eccezioni previste dallo standard IEEE.

(7)

L’ultima FPU viene pensata per il processore LEON [4.3]. Implementa però solo l’addizione, la sottrazione e la comparazione. Anche in questo caso c’è lo stadio di pre-normalizzazione degli operandi, poi quello che esegue l’operazione, poi lo stadio di post-normalizzazione cui segue quello per l’arrotondamento e la normalizzazione. Infine viene reso disponibile in uscita il risultato assieme alle eccezioni calcolate negli stadi intermedi come si vede nella figura 4.7.

(8)

Lo studio delle precedenti realizzazioni ha portato sia alle scelte delle specifiche della FPU che è stata realizzata sia alla scelta della sua architettura. In alcune di esse era presente anche il codice VHDL di alcuni blocchi, ma la mancanza di commenti ai listati ne ha reso difficoltosa l’analisi ed in parte anche la comprensione, pertanto lo sviluppo in VHDL degli algoritmi presentati nel Capitolo 3 è stato svolto in modo del tutto originale, le soluzioni adottate per la realizzazione dalla FPU oggetto di questo lavoro verranno illustrate dettagliatamente nei prossimi paragrafi.

4.2 Specifiche ed architettura della FPU

Per quanto detto nei paragrafi precedenti la FPU che ho realizzato soddisfa le seguenti specifiche:

• Lavora in Singola Precisione (32-bit). • Realizza le operazioni di :

o Addizione, Sottrazione, Moltiplicazione, Divisione

Radice quadrata, Valore assoluto e Negazione.

• È conforme allo standard IEEE-754 e ne contempla sia i 4 tipi di

arrotondamento, sia le eccezioni previste.

Osservando la figura 4.8 si evince che ogni operazione è realizzata da un singolo blocco, in grado di funzionare anche autonomamente. Prima di giungere ai blocchi delle operazioni i dati in ingresso passano attraverso i due blocchi VSI (Valori Speciali di Ingresso) ed ESEGUI che servono, rispettivamente, per verificare se gli operandi in ingresso alla FPU sono dei valori speciali per lo standard IEEE e per decodificare il tipo di operazione da eseguire.

(9)

Nei singoli blocchi avviene sia la fase di pre-normalizzazione degli operandi che quella di post-normalizzazione sul risultato prodotto dall’operazione. La soluzione adottata serve sia a migliorare l’efficienza della FPU che a rendere autonomi i singoli blocchi a discapito di trascurabile incremento di area [4.1]. Le uscite dei blocchi che eseguono le operazioni vengono inviate al blocco VSI_USCITA che analizza il risultato prodotto per vedere se esso fa parte dei valori speciali dello standard IEEE e ad un blocco di SINCRONIZZAZIONE che fa si che tutte le uscite della FPU siano sincronizzate con gli ingressi.

Figura 4.8 – Architettura della FPU realizzata

Nei prossimi paragrafi verranno illustrati in dettaglio sia i singoli blocchi, sia il funzionamento dell’intera FPU che è stata realizzata utilizzando il linguaggio VHDL.

(10)

4.3 Il linguaggio VHDL

Il VHDL è un linguaggio per la descrizione dell’hardware (un Hardware Description Language), che può essere utilizzato per la documentazione, la simulazione e la sintesi di sistemi digitali. Inizialmente, nei primi anni ’80, lo sviluppo del VHDL è stato supportato dal dipartimento della difesa statunitense, nell’ambito di un progetto denominato VHSIC (Very High Speed Integrated Circuits). VHDL è infatti un acronimo che sta per VHSIC Hardware Description Language. Il linguaggio è nato quindi con lo scopo di fornire una descrizione non ambigua di un sistema digitale, che potesse essere interpretata univocamente dai vari progettisti impegnati nello sviluppo del sistema stesso.

Nel 1987 il VHDL è stato adottato come standard dalla IEEE; questa prima release ufficiale del linguaggio è nota come VHDL-87. Nel 1993 lo standard è stato revisionato dalla IEEE e si è giunti così alla versione attuale del linguaggio, nota come VHDL-93, che differisce solo in pochi aspetti dal VHDL-87.

Una delle caratteristiche richieste al VHDL è la possibilità di simulare il sistema descritto, sia a livello funzionale sia portando in conto i ritardi del circuito. Negli anni seguenti, oltre che per la documentazione e la simulazione, il VHDL ha assunto un ruolo sempre più importante nella fase di sintesi dei sistemi digitali. Un programma di sintesi consente, a partire da una descrizione comportamentale di un sistema, di ottenere automaticamente una descrizione del circuito a basso livello mediante una netlist in cui il sistema viene descritto come interconnessione di porte logiche elementari appartenenti ad un’opportuna libreria. A partire dalla netlist, utilizzando opportuni programmi di piazzamento e collegamento delle celle (place & route), è possibile completare in maniera quasi del tutto automatica il progetto di un sistema integrato. In questo modo il ciclo di

(11)

sviluppo di un sistema integrato diviene simile a quello di un programma software in cui si parte da una descrizione in un linguaggio di programmazione (C, PASCAL ecc.) per ottenere, dopo una fase di compilazione, una descrizione in linguaggio macchina.

L’utilizzo di sofisticati programmi di sintesi è stato uno degli strumenti più importanti che ha consentito lo sviluppo di circuiti integrati sempre più complessi, consentendo al progettista di concentrarsi sulla descrizione ad alto livello del sistema, esplorando come le diverse scelte architetturali possano influire sulle prestazioni del circuito, disinteressandosi dai dettagli implementativi. Il VHDL consente infatti di descrivere efficacemente sistemi complessi cui corrispondono netlist di centinaia di migliaia o milioni di porte logiche elementari, così come in un programma software ad alto livello è possibile ottenere facilmente programmi in linguaggio macchina costituiti da milioni di istruzioni elementari a partire da un listato di poche centinaia di righe. Un ulteriore vantaggio legato all’utilizzo di programmi di sintesi è legato al fatto che la descrizione di un sistema digitale in VHDL può essere indipendente dalla particolare tecnologia prescelta per l’implementazione del circuito, così come un programma descritto in un linguaggio ad alto livello può essere compilato ed eseguito su piattaforme hardware differenti. Poichè il VHDL è nato come linguaggio per la documentazione dei sistemi digitali e solo in un secondo momento sono stati introdotti i programmi di sintesi, non deve stupire il fatto che non tutti i costrutti del VHDL siano sintetizzabili (ad esempio, le operazioni relative all’accesso su files non possono avere una diretta corrispondenza hardware, anche perché non avrebbe senso).

Per realizzare la FPU descritta in questo lavoro si è usato il VHDL per i vantaggi che ne derivano, pertanto si invita il lettore interessato ad ulteriori approfondimenti a consultare i numerosi testi disponibili sull’argomento [4.4].

(12)

4.4 Il blocco per segnalare valori speciali in ingresso

L’acronimo VSI sta per Valori Speciali in Ingresso. Il blocco riceve in ingresso il segnale di clock, i due operandi e il tipo di arrotondamento e in uscita invia gli ingressi senza modifiche e genera i segnali che indicano ai blocchi successivi se stanno per operare su numeri che sono valori speciali per lo standard IEEE. Il blocco è cosi definito:

ENTITY VSI -- Blocco che rileva i valori speciali dell standard IEEE 754 in ingresso alla FPU PORT( clk opa opb arr_IN arr_OUT opaU opbU opa_00 opa_dn opa_inf opa_snan opa_qnan opb_00 opb_dn opb_inf opb_snan opb_qnan : IN : IN : IN : IN : OUT : OUT : OUT : OUT : OUT : OUT : OUT : OUT : OUT : OUT : OUT : OUT : OUT std_logic; std_logic_vector (31 downto 0) ; std_logic_vector (31 downto 0) ; std_logic_vector ( 1 downto 0) ; std_logic_vector ( 1 downto 0) ; std_logic_vector (31 downto 0) ; std_logic_vector (31 downto 0) ; std_logic ; std_logic ; std_logic ; std_logic ; std_logic ; std_logic ; std_logic ; std_logic ; std_logic ; std_logic

-- clock del sistema -- operando a -- operando b -- tipo di arrotondamento -- tipo di arrotondamento -- operando a -- operando b -- indica che a è zero

-- indica che a è denormalizzato -- indica che a è infinito

-- indica che a è un SNAN -- indica che a è un QNAN -- indica che b è zero

-- indica che b è denormalizzato -- indica che b è infinito

-- indica che b è un SNAN -- indica che b è un QNAN );

END VSI ;

Il suo funzionamento è semplice: il blocco effettua una verifica del valore dell’esponente e della frazione di ogni operando e nel caso che venga riconosciuto un valore speciale viene messo a ‘1’ il segnale relativo alla peculiarità rilevata che altrimenti resterebbe a ‘0’. Per raggiungere lo scopo si sono utilizzate le variabili and_reduce e or_reduce che una volta calcolate permettono la facile generazione dei segnali che servono. Per i dettagli relativi alla realizzazione si rimanda all’appendice A in cui è stato riportato integralmente e commentato tutto il codice VHDL della FPU. Nella figura 4.9 è riportata una simulazione del funzionamento del blocco.

(13)

Figura 4.9 – Simulazione sul blocco VSI in ingresso

Osservando i valori preseti nella colonna Value in corrispondenza del marker di figura 4.9, si osserva che gli operandi in uscita dal Blocco sono opaU = 7FFF0000 che corrisponde alla codifica del valore speciale QNAN e opbU = 7F8F0000 che corrisponde alla codifica del valore speciale – SNAN. Pertanto i valori speciali in delle uscite opa_qnan e opb_snan sono settati a ‘1’ mentre tutti gli altri segnali di valore speciale per l’operando in uscita sono ‘0’.

Nella figura sono riportate anche altre prove, per una sua corretta lettura va ricordato che i segnali di valore speciale in uscita sono relativi ai valori degli operandi in uscita dal blocco.

(14)

4.5 Il blocco per decodificare l’operazione da eseguire (ESEGUI)

Il blocco ESEGUI compie la decodifica dell’istruzione da eseguire. L’istruzione arriva con la codifica prevista dallo SPARC V8 e il blocco setta a ‘1’ il valore di una sua uscita a secondo dell’operazione da eseguire. Si riporta il listato VHDL della definizione del blocco e della codifica delle operazioni, per i dettagli della realizzazione si rimanda all’appendice A.

--- --Operazioni che compie la FPU

--in accordo con Istruction set dello SPARC V8 -- ingresso operazione: -- "001000001" = add -- "001000101" = sub -- "001001001" = mul -- "001001101" = div -- "000001001" = abs -- "000000101" = neg -- "000101001" = sqrt ( 041 in esadecimale ) ( 045 in esadecimale ) ( 049 in esadecimale ) ( 04D in esadecimale ) ( 009 in esadecimale ) ( 005 in esadecimale ) ( 029 in esadecimale ) ENTITY ESEGUI PORT( clk fpu_op add sub mul div sqrt absA negA : IN : IN : OUT : OUT : OUT : OUT : OUT : OUT : OUT std_logic; std_logic_vector (8 downto 0) ; std_logic ; std_logic ; std_logic ; std_logic ; std_logic ; std_logic ; std_logic ); END ESEGUI ;

-- "others" = NESSUNA OPERAZIONE

Nella figura 4.10 è riportata l’esito di una simulazione eseguita sul blocco. Va ricordato che le uscite di questo blocco sono sincronizzate con le uscite del blocco VSI in ingresso, per cui i blocchi successivi ricevono in maniera corretta i valori degli operandi ed dell’operazione da eseguire.

(15)

4.6 Il blocco per l’addizione o la sottrazione (ADD/SUB)

Questo blocco è deputato alla realizzazione delle operazioni di addizione e sottrazione. Le due operazioni sono state accorpate perché l’una può essere intesa come l’altra a cui è stato cambiato il segno del secondo operando. Nel blocco la sottrazione viene considerata come una somma in cui al primo operando va sommato il valore negato del secondo operando.

ENTITY Add_Sub -- blocco che fa addizione o sottrazione PORT( clk opa opb add sub arrotondamento opa_00 opa_dn opa_inf opa_snan opa_qnan opb_00 opb_dn opb_inf opb_snan opb_qnan risultato_add_sub underflow_add_sub overflow_add_sub : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : OUT : OUT : OUT std_logic; std_logic_vector (31 downto 0) ; std_logic_vector (31 downto 0) ; std_logic; std_logic; std_logic_vector (1 downto 0) ; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic_vector (31 downto 0); std_logic; std_logic

-- clock del sistema

-- a questo ingresso va operando a -- a questo ingresso va operando b -- indica se devo fare addizzione -- indica se devo fare sottrazione -- arrotondamento da eseguire -- indica che a è zero

-- indica che a è denormalizzato -- indica che a è infinito

-- indica che a è un SNAN -- indica che a è un QNAN -- indica che b è zero

-- indica che b è denormalizzato -- indica che b è infinito

-- indica che b è un SNAN -- indica che b è un QNAN -- risultato dell'operazione -- segnala che si è avuto underflow -- segnala che si è avuto overflow );

END Add_Sub;

Il blocco riceve in ingresso il clock, i due operandi, i segnali add e sub che indicano quale operazione va eseguita, il segnale per l’arrotondamento, tutti i segnali di presenza di un valore speciale per lo standard IEEE e restituisce in uscita il risultato dell’operazione ed i segnali di overflow o underflow settati a ‘1’ se il risultato dell’operazione non sia rappresentabile con i 32 bit utilizzati dal formato in singola precisione. Per la realizzazione dettagliata del blocco si veda l’appendice A, di seguito ne verrà illustrato il funzionamento ponendo l’accento sulle soluzioni che sono state adottate per realizzare l’algoritmo presentato nel capitolo 3 (figura 3.1).

(16)

La realizzazione dell’operazione comincia col memorizzare i valori degli ingressi in variabili di “lavoro” e cambiando il segno del secondo operando nel caso in cui l’operazione da eseguire sia una sottrazione.

Se entrambi gli operandi non appartengono ai casi speciali dello standard IEEE, trattati a parte, vengono ordinati stabilendo chi è il maggiore e chi il minore. Fatto questo si assegna il valore dell’esponente dell’operando maggiore come valore dell’esponente del risultato. Occorre poi allineare le mantisse dei due operandi in modo che abbiano lo stesso “peso aritmetico”, si calcola la differenza degli esponenti e si trasla verso destra la mantissa del numero minore fino a che non ha lo stesso esponente del maggiore. Questa operazione può causare degli errori di precisione nel caso in cui la differenza degli esponenti sia maggiore del numero di bit destinati a memorizzare la mantissa dell’operando da traslare. Al fine di ottenere la massima precisione consentita dalla rappresentazione adottata, poichè la massima differenza possibile tra il numero più grande rappresentabile e quello più piccolo è pari a 255 e le mantisse sono lunghe 25 bit, per consentire la traslazione di 255 posizioni della mantissa minore è necessario che le variabili che contengono le mantisse dei due operandi utilizzate per fare il calcolo siano lunghe 280 bit. Si esegue poi la somma algebrica delle mantisse verificando l’eventuale occorrenza di un overflow o di un underflow. A questo punto, dopo l’eventuale “normalizzazione” della mantissa memorizzata nella variabile di lavoro, poiché la mantissa del risultato in uscita è lunga solo 23 bit ( più il bit implicito da tener presente), si esegue l’arrotondamento richiesto verificando che non produca un overflow e si produce il risultato in uscita.

Se almeno uno dei due operandi è un valore speciale dello standard IEEE l’operazione viene gestita in accordo con la tabella 3.1, mostrata nel capitolo precedente, ed il risultato viene prodotto in uscita.

(17)

Poiché il blocco opererà all’interno di una FPU che esegue anche altre operazioni, nel caso che i segnali add e sub siano entrambi zero, ad indicare che la FPU esegue un’altra operazione, tutti i segnali di uscita sono posti a zero. Nella figura 4.11 è riportato l’esito di una simulazione sul blocco.

Figura 4.11 – Simulazione sul blocco ADD / SUB

In corrispondenza del marker di figura 4.11 viene eseguita un’addizione tra due operandi che valgono entrambi –1 ed il risultato è –2. Per facilitare la comprensione della figura riporto di seguito la codifica in decimale dei valori degli operandi :

0 = 00000000 1 = 3F800000 2 = 40000000 -0 = 80000000 -1 = BF800000 -2 = C0000000

Si tratta di una simulazione fatta per mostrare il funzionamento del blocco che verrà testato esaustivamente, con tutta la FPU, nel paragrafo 4.13.

(18)

4.7 Il blocco per la moltiplicazione (MUL)

Il blocco MUL esegue la moltiplicazione.

ENTITY Moltiplicazione_FP -- Blocco che fa moltiplicazione PORT( clk opa opb mul arrotondamento opa_00 opa_dn opa_inf opa_snan opa_qnan opb_00 opb_dn opb_inf opb_snan opb_qnan risultato_mul underflow_mul overflow_mul : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : OUT : OUT : OUT std_logic; std_logic_vector (31 downto 0) ; std_logic_vector (31 downto 0) ; std_logic; std_logic_vector (1 downto 0) ; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic_vector (31 downto 0); std_logic; std_logic

-- clock del sistema -- operando a -- operando b

-- mi dice se devo fare addizione -- arrotondamento da effettuare -- indica che a è zero

-- indica che a è denormalizzato -- indica che a è infinito

-- indica che a è un SNAN -- indica che a è un QNAN -- indica che b è zero

-- indica che b è denormalizzato -- indica che b è infinito

-- indica che b è un SNAN -- indica che b è un QNAN -- risultato della moltiplicazione -- segnala che si è avuto underflow -- segnala che si è avuto overflow );

END Moltiplicazione_FP;

Il blocco riceve in ingresso il clock, i due operandi, il segnale mul (attivo alto) che indica se l’operazione va eseguita o meno, il segnale di arrotondamento, tutti i segnali di presenza di un valore speciale per lo standard IEEE e restituisce in uscita il risultato dell’operazione ed i segnali di overflow o underflow. Di seguito verrà descritto il funzionamento del blocco che la cui descrizione VHDL è interamente riportata nell’appendice A. Si comincia con il determinare il segno del risultato della moltiplicazione, si determina poi il valore dell’esponente del risultato. Per verificare gli eventuali overflow o underflow il valore dell’esponente del risultato viene memorizzato nella variabile esponente_MUL_lungo che è a 10 bit in modo che i due 2 bit più significativi della variabile indicano che tipo di risultato abbiamo ottenuto in accordo con la tabella 4.1, mentre gli altri bit sono il valore che deve assumere l’esponente del risultato.

(19)

bit 9 bit 8 Tipo risultato 1 0 Risultato corretto 1 1 overflow 0 1 underflow 0 0 Underflow

Tabella 4.1 – Verifica overflow e underflow

La variabile esponente_MUL_lungo si ottiene sommando gli esponenti dei due operandi, opportunamente predisposti, e sottraendo il fattore di polarizzazione, seguendo l’algoritmo illustrato nella figura 3.3 del capitolo precedente. Si procede poi a moltiplicare le mantisse e a memorizzare il risultato nella variabile mantissa_MUL_lunga di 48 bit adatta a contenere il prodotto delle mantisse dei due operandi che sono a 24 bit. In questo il risultato ha la massima precisione consentita dalla rappresentazione adottata. La mantissa del risultato viene eventualmente normalizzata e se uno dei due operandi o entrambi sono denormalizzati si corregge il valore dell’esponente del risultato. Si verificano eventuali overflow o underflow e si procede con l’arrotondamento perché la variabile mantissa_MUL_lunga è a 48 bit mentre l’uscita, per la rappresentazione adottata, ne richiede 24 (bit implicito incluso). A questo punto vengono mandati i risultati in uscita. Nel caso in cui almeno uno dei due operandi è un valore speciale dello standard IEEE la moltiplicazione viene gestita in accordo con tabella 3.1 ed il risultato viene prodotto in uscita.

Se il segnale mul è zero, ad indicare che la FPU esegue un’altra operazione, tutti i segnali di uscita sono posti a zero. Nella figura 4.12 è riportato l’esito di una simulazione sul blocco.

(20)

Figura 4.12 – Simulazione sul blocco MUL

Possiamo osservare che in corrispondenza del marker di figura 4.12 viene correttamente eseguita la moltiplicazione tra C6D62800 (–27412) e C7F6DF00 (–126398) = 4F4E84FC (+3464821976). Si osserva inoltre che se uno dei due operandi è zero il risultato in uscita è zero. Anche in questo caso si tratta di una simulazione fatta per mostrare il funzionamento del blocco che verrà testato esaustivamente nel paragrafo 4.13.

(21)

4.8 Il blocco per la divisione (DIV)

Il blocco DIV esegue la divisione.

ENTITY Divisione_FP -- Blocco che fa divisione PORT( clk opa opb div arrotondamento opa_00 opa_dn opa_inf opa_snan opa_qnan opb_00 opb_dn opb_inf opb_snan opb_qnan risultato_div underflow_div overflow_div divisione_per_zero resto_div inesatto_div : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : IN : OUT : OUT : OUT :OUT : OUT : OUT std_logic; std_logic_vector (31 downto 0) ; std_logic_vector (31 downto 0) ; std_logic; std_logic_vector ( 1 downto 0); std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic_vector (31 downto 0); std_logic; std_logic; std-logic; std_logic_vector (23 downto 0); std_logic

-- clock del sistema

-- a questo ingresso va operando a -- a questo ingresso va operando b -- mi dice se devo fare la divisione -- arrotondamento da effettuare -- indica che a è zero

-- indica che a è denormalizzato -- indica che a è infinito

-- indica che a è un SNAN -- indica che a è un QNAN -- indica che b è zero

-- indica che b è denormalizzato -- indica che b è infinito

-- indica che b è un SNAN -- indica che b è un QNAN -- risultato della divisione

-- segnala che si è avuto underflow -- segnala che si è avuto overflow -- è ‘1’ se b è zero

-- è il resto della divisione

-- è ‘1’ se il resto è diverso da zero );

END Divisione_FP;

Il blocco riceve in ingresso il clock, i due operandi, il segnale div che indica se l’operazione va eseguita, il segnale per l’arrotondamento, tutti i segnali di presenza di un valore speciale per lo standard IEEE e restituisce in uscita il risultato dell’operazione, il valore del resto della divisione, il segnale che indica se si è cercato di effettuare una divisione per zero, il segnale inesatto_div, che se vale ‘1’ indica che il resto della divisione è diverso da zero, ed i segnali di overflow o underflow.

Per realizzare l’algoritmo di divisione tra numeri floating point, illustrato in figura 3.3 del precedente capitolo, è stato innanzitutto realizzata una funzione che esegue la divisione tra le mantisse degli operandi. È stato necessario realizzare questa funzione perché il linguaggio VHDL non implementa (perché non può essere sintetizzato) il comando di divisione tra

(22)

variabili std_logic_vector. La funzione divisione_24_bit realizza quindi la divisione tra due operandi entrambi a 24 bit e restituisce in un’unica uscita a 88 bit il quoziente (su 24 bit) e il resto (su 64 bit) di questa divisione. L’algoritmo usato nella funzione tiene conto del fatto che le mantisse sono normalizzate e che quindi hanno almeno uno dei 2 bit più significativi che vale ‘1’, il quoziente e il resto vengono costruiti passo a passo tramite sottrazioni successive che tengono conto di quanto il dividendo ‘è contenuto’ nel divisore. Per come sono le mantisse del formato adottato, il dividendo può essere contenuto 1 o 0 volte nel divisore, facendo queste verifiche si costruisce il quoziente. Il resto si ottiene sottraendo al dividendo, di volta in volta, i valori in esso ‘contenuti’.

A questo punto si può partire con la realizzazione dell’algoritmo che esegue la divisione floating point. Si comincia con il calcolare il segno del risultato della divisione. Si calcola poi il valore dell’esponente del risultato, ripristinando il fattore di polarizzazione e tenendo conto di eventuali operandi denormalizzati. La verifica di overflow o underflow è realizzata utilizzando lo stesso sistema usato nella moltiplicazione. Si dividono le mantisse utilizzando la funzione prima descritta e si arrotonda il risultato ottenuto. Infine vengono mandati i risultati in uscita.

Nel caso in cui almeno uno dei due operandi è un valore speciale dello standard IEEE la divisione viene gestita in accordo con tabella 3.1 ed il risultato viene prodotto in uscita.

Se il segnale div è zero, ad indicare che la FPU esegue un’altra operazione, tutti i segnali di uscita sono posti a zero. Nella figura 4.13 è riportato l’esito di una simulazione sul blocco.

(23)

Figura 4.13 – Simulazione sul blocco DIV

In corrispondenza del marker di figura 4.13 viene eseguita correttamente la divisione 1 : 2 = 0,5. Per facilitare la comprensione della figura riporto di seguito la codifica in decimale dei valori degli operandi :

0.5 = 3F000000 1 = 3F800000 2 = 40000000 -0.5 = BF000000 -1 = BF800000 -2 = C0000000

Anche in questo caso si tratta di una simulazione fatta per mostrare il funzionamento del blocco testato esaustivamente nel paragrafo 4.13.

(24)

4.9 Il blocco per la radice quadrata (SQRT)

Il blocco SQRT esegue la radice quadrata. Questo blocco è stato realizzato applicando come algoritmo di calcolo il metodo di Newton-Raphson perchè esso permette di ottenere il risultato dell’operazione riutilizzando il lavoro già fatto in VHDL per eseguire le operazioni di somma, sottrazione e divisione.Il metodo di Newton-Raphson o metodo delle tangenti serve per risolvere in generale una qualsiasi equazione ed è applicabile al calcolo della radice quadrata, vista come soluzione dell’equazione

.

( )

x =0 f

( )

x =x2 a=0 f

In particolare, assegnato un qualsiasi valore iniziale di tentativo ed applicando iterativamente la formula

x       + = + n n n x a x x 2 1 1 l’algoritmo converge

rapidamente alla radice quadrata di a.

ENTITY SQRT -- Blocco che fa la radice quadrata PORT( clk sqrt arrotondamento opa opa_00 opa_dn opa_inf opa_snan opa_qnan risultato_sqrt inesatto_sqrt : IN : IN : IN : IN : IN : IN : IN : IN : IN : OUT : OUT std_logic; std_logic; std_logic_vector ( 1 downto 0) ; std_logic_vector (31 downto 0) ; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic_vector (31 downto 0); std_logic -- clock di sistema

-- segnale per attivare il blocco -- arrotondamento da effettuare -- operando su cui fare la radice

-- indica che a è zero

-- indica che a è denormalizzato -- indica che a è infinito

-- indica che a è un SNAN -- indica che a è un QNAN -- risultato in uscita

-- ‘1’ se il risultato è sbagliato );

END SQRT ;

Il blocco riceve in ingresso il clock, l’operando su cui fare la radice quadrata, il segnale sqrt che indica se l’operazione va eseguita, il segnale di arrotondamento, tutti i segnali di presenza di un valore speciale per lo standard IEEE e restituisce in uscita il risultato dell’operazione ed il segnale inesatto_sqrt che vale ‘1’ se il risultato in uscita non è corretto.

(25)

Si comincia col dichiarare le procedure Divisione_FP e Add_FP per realizzare la divisione e la somma tra due numeri floating point tenendo presente i due blocchi già realizzati. Si comincia col indicare che la radice quadrata dei numeri negativi non esiste e si da in uscita uno SNAN. Si realizza subito l’algoritmo di Newton-Raphson scegliendo come primo valore di : . Bisogna decidere quante iterazioni fare, con 6 0 7 già da buona precisione, ma per avere il risultato esatto della radice di 7F7FFFFF che è il più grande numero rappresentabile occorrono almeno 67 iterazioni, per ottenere valore esatto con il numero 000000002, che è il numero più piccolo rappresentabile che da risultato corretto, ne occorrono 96 e pertanto si è scelto 96 come numero di iterazioni da eseguire. Se al termine delle iterazioni il valore di è uguale al valore

che lo precedeva vuol dire che abbiamo trovato il valore che stavamo cercando, in caso contrario settiamo a ‘1’ l’uscita inesatto_sqrt. Per tutte le operazioni viste in precedenza il risultato in uscita dai blocchi era esatto, nel caso della radice quadrata però i valori denormalizzati da x"00000001" a x"000000FF" producono in uscita un risultato affetto da errore, che può essere (per i primi 16 numeri) anche maggiore del 10% e nel caso di 00000001 maggiore del 50%. Si è deciso allora di realizzare una tabella di corrispondenza per i più piccoli 256 numeri denormalizzati rappresentabili nello standard. Poiché l'errore in uscita sul valore della radice calcolata per tutti gli altri numeri denormalizati non supera il 3 per mille, con la tabella si ha che questo è il massimo errore possibile sui numeri denormalizzati. Per i numeri normalizzati invece si osserva che l’errore in uscita è sempre minore del 0,00007%. (vedere Appendice E).

0 x    ≤ > = 1 1 1 0 a se a se a x 1 + n x n x

(26)

Se il segnale sqrt è zero, ad indicare che la FPU esegue un’altra operazione, tutti i segnali di uscita sono posti a zero. Nella figura 4.13 è riportato l’esito di una simulazione sul blocco.

Figura 4.14 – Simulazione sul blocco SQRT

In corrispondenza del marker di figura 4.14 viene eseguita correttamente la radice quadrata di 40800000 (4) che è 40000000 (2). Anche in questo caso si tratta di una simulazione fatta per mostrare il funzionamento del blocco che verrà testato esaustivamente nel paragrafo 4.13.

(27)

4.10 Il blocco per il valore assoluto e la negazione (ABS/NEG)

Il funzionamento di questo blocco è semplice, riceve in ingresso il clock, l’operando A e i segnali absA e negA che indicano l’operazione da eseguire. Nel caso del calcolo del valore assoluto in uscita viene mandato l’operando A con il bit del segno messo a ‘0’, nel caso di negazione in uscita va l’operando A con il bit del segno negato. Nel caso di non utilizzo del blocco l’uscita è nulla.

ENTITY ABS_NEG -- blocco che fa valore assoluto o negazione PORT( clk opa absA negA uscita_ABS_NEG : IN : IN : IN : IN : OUT std_logic; std_logic_vector (31 downto 0) ; std_logic; std_logic; std_logic_vector (31 downto 0);

-- clock del sistema

-- a questo ingresso va operando a -- indica se devo fare valore assoluto -- indica se devo fare la negazione -- risultato dell'operazione );

END ABS_NEG ;

Nella figura 4.15 è mostrato il funzionamento del blocco.

In corrispondenza del marker di figura 4.15 il segnale negA è settato a ‘1’, quindi viene richiesto al blocco di effettuare la negazione dell’operando A in ingresso. In uscita quindi, dopo aver atteso un ciclo di clock, si ritrova l’operando A con il bit del segno cambiato. Si osserva facilmente che il blocco funziona correttamente.

(28)

4.11 Il blocco per segnale valori speciali in uscita

L’acronimo VSI_USCITA sta per Valori Speciali in uscita.

ENTITY VSI_USCITA -- Rileva se uscita è un valore speciale per dello standard IEEE 754

PORT( clk risultato uscita risultato_00 risultato_dn risultato_inf risultato_snan risultato_qnan : IN : IN : OUT : OUT : OUT : OUT : OUT : OUT std_logic; std_logic_vector (31 downto 0) ; std_logic_vector (31 downto 0) ; std_logic; std_logic; std_logic; std_logic; std_logic ); END VSI_USCITA ;

Il blocco funziona allo stesso modo di quello in ingresso, riceve in ingresso il segnale di clock, il risultato dell’operazione eseguita ed in uscita manda il risultato e nel caso venga riconosciuto un valore speciale dello standard IEEE 754 setta ad ‘1’ il segnale corrispondente che altrimenti resta a ‘0’.

4.12 Il blocco per sincronizzare le uscite

Poiché il risultato prodotto dai blocchi che eseguono le operazioni prima di essere mandato in uscita attraversa il blocco VSI_USCITA, questo blocco serve per sincronizzare gli altri segnali di uscita della FPU con il suo risultato. In questo modo la FPU ha una pipe-line a 3 stadi e opportunamente pilotata è in grado di lavorare con trough-put pari a 1.

ENTITY SINCRONIZZA_USCITE PORT( clk overflow_in underflow_in div_00_in inesatto_in overflow_out underflow_out div_00_out inesatto_out : IN : IN : IN : IN : IN : OUT : OUT : OUT : OUT std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic ); END SINCRONIZZA_USCITA;

(29)

4.13 Verifica del funzionamento della FPU con UCBTEST

Per verificare il funzionamento della FPU ho utilizzato l’UCBTEST [4.5]. Si tratta di un test che esamina il comportamento dei dispositivi che utilizzano l’aritmetica in virgola mobile con lo standard IEEE 754.

Il test è stato sviluppato a Berkeley presso la University of California e consiste nel mandare in ingresso alla FPU degli operandi prestabiliti ed il tipo di arrotondamento voluto e nel verificare che l’uscita sia uguale a quella che ci si attende.

Per fare ciò è stato realizzato in VHDL un test-bench che legge i vettori da testare da un file di ingresso “test_vectors.txt” e riporta il risultato del confronto tra uscita attesa ed uscita reale, oltre che a video, in un file di uscita “uscita_test_vectors.txt”.

L’UCBTEST prevede, per le FPU in singola precisione, 5810 vettori di test, 2639 per l’addizione e la sottrazione, 1302 per la moltiplicazione, 1515 per la divisione e 354 per la radice quadrata.

Tutti i 5810 vettori dell’UCBTEST sono stati processati dalla FPU e il risultato da essa prodotto è sempre stato uguale a quello che ci si aspettava. Nell’appendice B sono riportati i file di uscita generati dal test-bench.

(30)

4.14 Interfaccia per connettere la FPU al LEON

Nel capitolo 1 abbiamo visto che il LEON può essere configurato in modo da possedere una interfaccia generica special-purpose per un coprocessore, che gli permette di lavorare in parallelo all’integer unit in modo da aumentare le prestazioni. Il protocollo di comunicazione è il seguente:

Il protocollo inizia portando a ‘1’ il segnale di avvio (cpi.start) insieme ad una istruzione valida (cpi.opcode). Gli operandi sono inviati nel seguente ciclo di clock insieme al segnale del caricamento (cpi.load). Se il coprocessore utilizzerà più di un ciclo per completare l’istruzione, deve settare a ‘1’ il segnale di occupato (cpi.busy) dal ciclo successivo a quello in cui il segnale di avvio è stato portato a ‘1’ fino al ciclo precedente a quello in cui il risultato sarà valido. Il risultato (cpo.result), il segnale cpo.cc e le informazioni di eccezione (cpo.exc) sono validi dal ciclo dopo in cui il segnale di occupato è stato portato a ‘0’ fino al nuovo segnale di inizio di una nuova istruzione. Se l'esecuzione dell’istruzione eseguita dal coprocessore deve essere interrotta prima che abbia prodotto un risultato valido, il segnale cpi.flush sarà portato a ‘1’ per due cicli di clok. Il coprocessore, in questo caso, deve essere portato alla condizione di idle.

(31)

ENTITY Interfaccia IS PORT( clk

-- Ingressi dal Leon op1 op2 opcode RoundingMode start load flush

--Uscite per il Leon res

cc exc busy

-- Ingressi dalla FPU risultato inesatto div_per_zero underflow overflow qnan snan

--Uscite per la FPU opa opb operazione arrotondamento : IN : IN : IN : IN : IN : IN : IN : IN : OUT : OUT : OUT : OUT : IN : IN : IN : IN : IN : IN : IN : OUT : OUT : OUT : OUT std_logic; std_logic_vector (63 downto 0); std_logic_vector (63 downto 0); std_logic_vector ( 9 downto 0); std_logic_vector ( 1 downto 0); std_logic; std_logic; std_logic; std_logic_vector (63 downto 0); std_logic_vector ( 1 downto 0); std_logic_vector ( 5 downto 0); std_logic; std_logic_vector (31 downto 0); std_logic; std_logic; std_logic; std_logic; std_logic; std_logic; std_logic_vector (31 downto 0); std_logic_vector (31 downto 0); std_logic_vector ( 8 downto 0); std_logic_vector ( 1 downto 0) -- clock di sistema

-- operando op1 in ingresso -- operando op2 in ingresso -- codice op. in SPARC V8 -- arrot., da MEIKO FPU -- segnale di inizio handshake -- per caricare gli operandi -- annullamento dell'operazione -- risultato dell'operazione -- codifica confronto -- eccezioni

-- segnale di attesa del risultato -- risultato dalla FPU

-- si setta se risultato è sbagliato -- si setta con divisione e b=0 -- si setta per un underflow -- si setta per un overflow -- si setta per un QNAN -- si setta per un SNAN -- operando opa per la FPU -- operando opb per la FPU -- codice op. con SPARC V8 -- arroto. da MEIKO FPU ); END Interfaccia ;

Figura 4.16 – Posizionamento dell’interfaccia per il LEON L E O N F P U I N T E R F A C C I A

(32)

L’interfaccia è realizzata con una serie di registri in cui vengono memorizzati i dati scambiati tra FPU e LEON.

I registri che contengono i dati per la FPU hanno come segnale di set il segnale load, quelli per gli operandi e l’arrotondamento, e il segnale start, quello per il codice dell’operazione. Tutti questi registri vengono resettati se il LEON manda il segnale di flush a ‘1’ per almeno 2 cicli di clok.

I registri che contengono i dati per il LEON, quello per il risultato, cc ed ex, hanno come segnale di set un segnale prodotto dalla FPU dopo che sono passati 4 cicli di clock dal caricamento dei nuovi operandi. Il segnale busy è generato a partire dal segnale load e viene tenuto alto fino al ciclo di clock precedente al caricamento dei risultati nei registri per il LEON. Anche questi registri vengono resettati se il LEON manda il segnale di flush a ‘1’ per almeno 2 cicli di clok.

Il segnale per caricare i risultati nei registri per il LEON e quello per azzerare i registri sono generati all’interno dell’interfaccia.

Figura 4.17 – Simulazione dell’interfaccia FPU - LEON

Dalla figura si vede che il segnale di flush non ha alcun effetto se viene dato per 1 solo ciclo di clock, mentre se dura almeno 2 cicli i registri con i dati per il LEON sono subito posti a zero assieme al segnale di busy.

(33)

Bibliografia

[4.1] P.-M. Seidel, “On the design of IEEE compliant floating-point units and their quantitative analysis”. Tesi di dottorato, Universitat des Saarlandes, Gennaio 2000.

[4.2] www.ee.pdx.edu/~mperkows/CLASS_VHDL/ VHDL_CLASS_2001/Floating/projreport.html

[4.3] Martin Kasprzyk, e00mk , “Floating Point Unit Digital IC project 2001”,Gennaio 2002

[4.4] Carlo Brandolese, “Introduzione al linguaggio VHDL – Aspetti teorici ed esempi di progetazione”

Riferimenti

Documenti correlati

Licenze Tutte le soluzioni offerte non devono prevedere alcuna limitazione sul numero delle licenze d'uso per coloro che svolgono attività per il Comune di

Come punto di partenza dell’analisi del testo e della rappresentazione teatrale mi sono posto una domanda: vista la peculiarità e la qualità del testo di Chlebnikov, vista

In particolare sono stati determinati i parametri più significativi per la caratterizzazione spazio-temporale del canale: path loss exponent, fattore-K, profilo temporale di

o entro 6 mesi successivi alla data di sottoscrizione dell’Atto di Impegno, gli Apporti previsti devono essere versati per un importo almeno pari alle

Su istanza del Responsabile del Progetto, considerata la necessità di garantire il corretto svolgimento delle attività di ricerca, si rende necessario modificare

Il capitolo 113.1928 “Incarichi libero professionali di studi, ricerca e consulenza”, viene diminuito di € 19.001,02;. Il capitolo 113.1930 “Collaborazioni coordinate e

Considerata la necessità di garantire il corretto compimento delle attività di ricerca afferenti alla Struttura di Ricerca 4 - Strumenti e Metodi per la

Essi sono essenzialmente funzione dell area A, del perimetro P e della lunghezza dell asta principale L..