Circuiti numerici
Passiamo ora a vedere come, grazie all'uso di tecniche di codifica binaria, possiamo
definire dei circuiti booleani in grado di effettuare delle manipolazioni di tipo
numerico su rappresentazioni di numeri. In generale il metodo consiste nel definire un
algoritmo di manipolazione su rappresentazioni binarie finite. La definizione viene data
sotto forma esaustiva mediante la definizione della tavola di verità. La correttezza
della realizzazione dipende dall'uso della tecnica di codifica assunta per la
progettazione del circuito.
Sommatori
Cominciamo a considerare il caso semplice di rappresentazioni binarie di numeri su un
solo bit (ossia numeri interi compresi tra 0 e 1). Notiamo subito che la somma dei
valori 1+1=2 genera un valore di uscita non rappresentabile in forma binaria su un bit;
per consentire la rappresentazione del risultato occorre infatti che il risultato venga
rappresentato con almeno un bit in più rispetto al numero di bit usati per la
rappresentazione degli addendi (in modo da poter codificare un valore massimo almeno
doppio rispetto al massimo valore dei due addendi). La tavola di verità che specifica il
comportamento di un tale circuito addizionatore é quindi la seguente:
s1:
a
b
U1
U0
0
0
0
0
0
1
0
1
1
0
0
1
1
1
1
0
Possiamo notare che la colonna u0 corrisponde alla tavola di verità della funzione XOR,
mentre la colonna u1 corrisponde a quella della funzione AND. Le due uscite del
circuito possono quindi essere ottenute mediante l'uso di queste due funzioni
elementari applicate alla stessa coppia di ingressi a e b. Un tale circuito con due
ingressi e due uscite viene normalmente chiamato half adder (semi addizionatore),
per il motivo che scopriremo tra poco.
Passiamo ora al caso di somma tra due numeri rappresentati su 2 bit (con risultato
rappresentato su 3 bit). Il funzionamento del circuito, usando la normale codifica
binaria, può essere definito mediante la seguente mappa:
\ b1 | 0 : 0 : 1 : 1 |
\ b0 | 0 : 1 : 0 : 1 |
a1 a0 \ | : : : |
--------\--+-----:-----:-----:-----+
0 0
| 000 : 001 : 010 : 011 |
..........|.....:.....:.....:.....|
0 1
| 001 : 010 : 011 : 100 |
..........|.....:.....:.....:.....|
1 0
| 010 : 011 : 100 : 101 |
..........|.....:.....:.....:.....|
1 1
| 011 : 100 : 101 : 110 |
-----------+-----:-----:-----:-----+
La realizzazione diretta di tale circuito risulta evidentemente molto più complessa di
quella di un semi addizionatore. In oltre, la complessità di realizzazione diventa
rapidamente proibitiva se aumentiamo ulteriormente il numero di bit di
rappresentazione per gli addendi e per il risultato.
La soluzione che consente di realizzare in modo semplice ed efficiente circuiti
addizionatori per rappresentazioni su due o più bit consiste nel modularizzare la
realizzazione.
In questo caso la corretta modularizzazione può essere individuata facendo ricorso
all'algoritmo di somma cifra per cifra: ad ogni passo consideriamo una sola cifra
dell'addendo a e una sola cifra dell'addendo b, oltre all'eventuale cifra "di riporto"
derivante dall'applicazione dello stesso algoritmo alle cifre precedenti. Quindi ad ogni
passo operiamo su rappresentazioni binarie di un solo bit; l'unica differenza rispetto
al circuito semi sommatore visto in precedenza é che ora dobbiamo considerare la
presenza di 3 addendi da un bit (a, b e la cifra di riporto c).
La specifica di un circuito in grado di effettuare tale somma (chiamato full adder, o
sommatore completo a 1 bit) é data dalla seguente tavola di verità:
s2:
a
b
c
r
u
0
0
0
0
0
0
0
1
0
1
0
1
0
0
1
0
1
1
1
0
1
0
0
0
1
1
0
1
1
0
1
1
0
1
0
1
1
1
1
1
L'uscita u corrisponde ad una funzione XOR a tre ingressi, mentre l'uscita r deve
essere generata da un circuito in grado di riconoscere la condizione "almeno due
ingressi assumono il valore 1". Indichiamo in modo compatto un circuito di tipo fulladder mediante il seguente simbolo:
Tali moduli possono essere combinati in modo da realizzare l'operazione di somma tra
rappresentazioni binarie su più cifre, usando un modulo per ogni cifra; ciascun modulo
genera una cifra di "riporto" verso il modulo successivo. In figura viene riportato lo
schema di connessione di due moduli per formare un addizionatore per addendi
rappresentati su due bit (e risultato rappresentato su 3 bit).
Notiamo che questa realizzazione di un circuito addizionatore su N bit presenta
vantaggi e svantaggi rispetto ad una realizzazione diretta mediante logica a 3 livelli
dalla tavola di verità (ammesso che questa fosse possibile anche per grandi valori di
N).
Il vantaggio principale (derivante dalla modularità della realizzazione) é che la
complessità della realizzazione cresce linearmente all'aumentare del numero N di bit
di rappresentazione degli addendi (in quanto l'aggiunta di una cifra comporta
esattamente l'aggiunta di un modulo full-adder).
Lo svantaggio principale é dovuto alla tecnica di propagazione del riporto (chiamata
ripple carry). Questo fa sì che anche il tempo di assestamento del risultato a seguito
di una variazione dei valori in ingresso cresca linearmente al crescere del numero N di
bit della rappresentazione degli addendi. In particolare, notiamo che il tempo di
assestamento per la cifra più significativa sarà N volte più grande di quello per la
cifra meno significativa, a causa della possibile propagazione del riporto attraverso
tutti i moduli full-adder.
Questo problema in pratica viene risolto con l'aggiunta di ulteriori circuiti combinatori
realizzati in logica a 3 livelli per la "previsione delle cifre di riporto" (carry
lookahead). Per esempio, la cifra binaria più a sinistra nelle caselle della mappa del
circuito sommatore a 2 bit può essere usata per specificare un circuito di previsione
del riporto sulla terza cifra. Una possibile realizzazione della funzione di previsione
del riporto sulla terza cifra in logica a due livelli (nessuna variabile appare in forma
negata in questo esempio) potrebbe quindi essere definita dalla formula (normale
disgiuntiva):
(a1·b1)+(a0·a1·b0)+(a0·b1·b0).
Notiamo infine che un circuito sommatore su N bit progettato per operare su
rappresentazioni binarie di numeri senza segno può essere usato anche per operare su
rappresentazioni di numeri con segno in complemento a 2 su N bit. L'unica accortezza
che occorre osservare in tal caso é quella di definire il risultato su N bit (senza
considerare l'N+1-esimo bit di riporto), e quindi di accertarsi che il risultato di una
operazione di somma tra numeri dello stesso segno non generi condizioni di overflow.
Un caso particolare di dispositivo sommatore é costituito dal caso di un
"incrementatore" che somma un valore costante K ad un addendo variabile A. Nella
maggior parte dei casi ci si riduce a voler sommare la costante K=1 ad un valore
arbitrario in ingresso. In tal caso il dispositivo può essere realizzato in modo molto
semplice usando N moduli di tipo "half-adder" (quello sulla vifra meno significativa
somma 1 ad a0, quelli sulle cifre successive sommano ai+ri). In questo caso anche la
funzione
"carry
lookahead"
può
essere
estremamente
semplificata:
ri = a(i-1) · a(i-2) · ... · a0