martedì 24 novembre 2015

FPGA: Numeri a virgola fissa (seconda parte)

Dopo aver introdotto i numeri fixed point vediamo un semplice blocco in VHDL che calcola la circonferenza di un cerchio dato il raggio.


Possiamo vedere come sia stata definita una costante two_pi che intuitivamente conterrà il valore 2 * pi greco.

La costante è valorizzata tramite la funzione to_ufixed che provvederà a convertire un numero REAL (ma funziona anche con INTEGER, SIGNED e UNSIGNED)  nel tipo per l'appunto UFIXED senza dover convertire "a mano" il numero nella stringa di bit "00110010", approssimazione di 6.28 data la rappresentazione del numero con soli 3 bit per la parte decimale.

Il valore effettivo ottenuto sarà però 6.25, c'è quindi da porre particolare attenzione alla precisione con la quale si eseguono questo tipo di calcoli, il codice VHDL può infatti facilmente trarre in inganno.

Il raggio è costituito da un numero fixed point ad 8 bit mentre il risultato, per evitare overflow, è un numero di 16 bit, dato che è il prodotto di una moltiplicazione.

Per evitare errori in fase di compilazione bisogna tassativamente seguire le regole di dimensionamento, concepite per evitare problemi involontari di overflow, sotto riportate:



Nel nostro caso ci serviamo della riga A * B della tabella. A'left assume il valore INTEG_BIT-1, dato dal bound sinistro scelto nel tipo ufixed del segnale radius, mentre analogamente B'left vale INTEG_BIT-1, dato dalla costante two_pi.

A'right ed B'right avranno entrambi il valore -DECIM_BIT.

Il risultato dovrà avere, seguendo la tabella, un range pari a 2*(INTEG_BIT-1)+1 downto -2*DECIM_BIT, ovvero 9 downto -6 quindi un numero di 16 bit come già detto.

Il VHDL 2008 coi fixed point ci offre una funzione di ridimensionamento con saturazione (per problematiche di overflow) ed arrotondamento (per problematiche di underflow) chiamata resize per eventualmente adattare il risultato ad un numero con diversa risoluzione, vediamone un esempio:


Dove abbiamo come argomenti di resize il numero da ridimensionare, il bound sinistro ed il bound destro del risultato. In tal modo l'uscita circumference avrà la stessa dimensione di 8 bit dell'ingresso.

Sono disponibili diversi overload della funziona resize e rimando alla Fixed point package user’s guide per una descrizione esaustiva di tutte le funzioni.

Nella funzione resize di default sono abilitati l'arrotondamento e la saturazione, seppur è possibile scegliere di troncare e avvolgere (wrap) il valore per ottenere circuiti di velocità maggiore ed area minore.

Sotto è riportata la vista RTL del circuito generato con arrotondamento e saturazione abilitati

L'implementazione su Cyclone V è di 11 ALM ed 1 blocco DSP. Sotto è riportata una simulazione del blocco sopra descritto:



















E' possibile notare come l'uscita saturi a 31.875 (tutti i bit pari ad 1) in caso di valori d'ingresso eccessivi per la risoluzione utilizzata.

Disabilitando arrotondamento e saturazione, il circuito ottenuto sarà come è facile aspettarsi costituito dal solo moltiplicatore, implementato con un singolo DSP. Purtroppo spesso il risultato così ottenuto è poco utilizzabile.



Per modificare lo stile di arrotondamento e di overflow in tutte le operazioni è possibile modificare le costanti fixed_round_style e fixed_overflow_style nel file fixed_pkg_c.vhdl






Per gestire l'underflow, che può verificarsi quando la parte decimale non ha abbastanza risoluzione per rappresentare il risultato, si può utilizzare una modalità fixed_round in cui la parte decimale viene arrotondata al più vicino valore rappresentabile oppure una modalità fixed_truncate dove sono semplicemente persi i bit meno significativi.

L'underflow può verificarsi nella divisione, dove l'errore può accumularsi a causa della natura iterativa degli algoritmi. Per ridurre l'errore vengono inseriti automaticamente dei bit di guardia (guard bit) che aumentano temporaneamente la risoluzione della parte decimale nei calcoli intermedi. Di default il VHDL 2008 utilizza 3 bit di guardia per operazioni come la divisione.

Per gestire l'overflow è possibile utilizzar la modalità fixed_saturate oppure la modalità fixed_wrap che è spesso il "normale" comportamento dei calcolatori e non comporta l'utilizzo di hardware aggiuntivo, incrementando il numero più grande rappresentabile si otterrà il numero più piccolo.

Per singole operazioni è possibile utilizzare gli overload delle funzioni come per esempio









Non mi rimane che augurarvi buona aritmetica.. coi fixed point!


NB: Per rendere compatibile il codice mostrato con ModelSim e compilabile da Quartus 15 è stato modificato il nome della libreria ieee_proposed in ieee, modificando il file fixed_pkg_c.vhdl di cui si è parlato nel precedente articolo. In tale modo ModelSim non lamenterà la mancanza della libreria ieee_proposed, seppur sia possibile crearla. Compilando il codice con un sintetizzatore con supporto per i fixed point del VHDL 2008 non sarà così necessaria alcuna modifica.