Dopo aver presentato la logica booleana di base e sviluppato le porte logiche essenziali, il nostro prossimo obiettivo è quello di concentrarsi su dispositivi più complessi che eseguono operazioni aritmetiche così da progettare la nostra unità aritmetico-logica.

La ALU, dall’inglese Arithmetic Logic Unit, è il fulcro di ogni architettura informatica in quanto va a comporre il cuore di una CPU. Il suo compito è quello di eseguire operazioni quali somme, sottrazioni, moltiplicazioni e divisioni su interi e numeri in virgola.

Numeri Binari

Il sistema numerico binario si basa sull’utilizzo delle cifre 0 e 1.

Convenzionalmente, per indicare che un numero è scritto utilizzando il codice binario, si racchiude tra parentesi tonde mettendo poi un 2 come pedice.

Come il sistema decimale, anche quello binario è posizionale, ovvero assume importanza la posizione assunta da ogni singola cifra all’interno di un numero.

A tal proposito, per convertire un numero da binario a decimale è sufficiente moltiplicare ogni cifra per il suo peso posizionale e poi sommare insieme il tutto:

Dati N bit, i numeri rappresentabili sono 2 elevato alla N.
Ad esempio con 1 bit, gli unici numeri rappresentabili sono 2.

BinarioDecimale
00
11

Con N = 2, il numero sale a 4:

BinarioDecimale
000
011
102
113

Addizionatori Binari

Quanto appena presentato ci permette di definire subito il funzionamento degli addizionatori binari che serviranno alla nostra ALU.

Una coppia di numeri binari possono essere sommati, cifra per cifra, secondo le stesse regole usate per i numeri in base 10.

Si parte aggiungendo le prime 2 cifre poste più a destra, anche chiamate cifre meno significative (LSB in inglese), dei due addendi.

Si passa poi ad aggiungere il bit di riporto (che può essere 0 o 1) alla somma della successiva coppia di cifre. Si continua così fino ad arrivare alle cifre poste più a sinistra dei 2 numeri binari, chiamate cifre più significative (MSB in inglese).

Se l’ultima somma genera un riporto, si genera normalmente il cosìdetto overflow, altrimenti la si cosidera terminata con successo.

Si può quindi notare come l’hardware necessario per sommare 2 numeri binari può essere realizzato partendo da porte logiche capaci di sommare 3 bit, (coppia di bit più il riporto).

Numeri Binari con Segno

Per rappresentare numeri negativi e positivi, la rappresentazione classica prevede di dividere il gruppo di numeri rappresentabili da N bit in 2 set distinti: quelli negativi, che hanno il bit più significativo pari a 1 e quelli positivi, che lo hanno pari a 0.

Sacrificando quindi il bit più significativo, è possibile definire una codifica che si sposa perfettamente con l’operazione di somma descritta nel paragrafo precedente.

La tecnica in questione, usata oggigiorno dalla stragrande maggioranza dei computer in circolazione, prende il nome di rappresentazione in complemento a 2, ed si ottiene banalmente negando bit a bit un numero binario e sommando 1 al risultato finale.

Una caratteristica particolare di questa rappresentazione è che non vi è alcuna modifica da fare in termini di hardware rispetto a quanto sviluppato per sommare due numeri positivi.

Ad esempio, considerando l’operazione:

(-2) + (-3)

E’ necessario semplicemente effettuare la somma tra i numeri binari complementati a 2, senza implementare nulla di specifico.

Il Progetto

Partendo dall’archivio nandt2tetris.zip scompattato nella lezione precedente, nella cartelle projects, troverai una serie di sottocartelle numerate. Per questa lezione ci concentriamo sulla cartella 02, al cui interno troverai una serie di file pronti ad essere caricati nel simulatore.

Per sviluppare la nostra ALU, come riportato in questa pagina dedicata, sarà necessario realizzare 4 addizionatori su cui costruire poi tutto il resto:

  • Half-Adder: circuito che somma 2 bit.
  • Full-Adder: circuito che somma 3 bit.
  • Adder: circuito che somma 2 numeri a 16 bit.
  • Incrementer: circuito che incrementa un numero binario di 1 unità.

Una volta terminati questi sviluppi è possibile passare alla realizzazione dell’Unità Aritmetico Logica su cui baseremo la piattaforma chiamata Hack.

La ALU che andremo a definire prende in pasto 2 numeri, x e y a 16 bit, insieme a 6 bit di controllo (zx, nx, zy, ny, f, no) i quali ci permettono, con le loro diverse combinazioni, di ottenere differenti funzioni in output.

A tutto questo si aggiungono 2 ulteriori bit di uscita, zr e ng, che vengono utilizzati per identificare 2 stati di output particolari.

Il funzionamento di ogni singolo bit di controllo è spiegato di seguito:

Copy to Clipboard

E’ importante notare che l’architettura così definita non prevede l’implementazione di operazioni come moltiplicazione e divisione a livello HW.

Sebbene quest’ultima sia senz’altro più efficiente in termini di performance, il costo di sviluppo è molto elevato.

Nell’architettura presentata, dunque, questo tipo di operazioni saranno implementate a livello OS.

Anche in questo caso, nella risoluzione degli esercizi è obbligatorio partire usando soltanto le porte sviluppate nella lezione precedente, mano a mano poi che si riesce a implementare le porte proposte, è possibile riusarle per realizzare le successive.

Come consigliato anche dagli autori infine, è bene innanzitutto concentrarsi nel sviluppare la ALU senza considerare i bit di stato zr e ng.

Una volta testata questa versione, chiamata ALU-nostat, è possibile proseguire realizzando ciò che manca per aggiungere i 2 bit.

Considera che il modo migliore per imparare è risolvere in autonomia questi esercizi.

Ad ogni modo, di seguito analizziamo passo-passo la soluzione per l’Half-Adder, tutte le altre soluzioni le puoi trovare a questo indirizzo.

Half Adder

Partiamo subito aprendo il file nand2tetris/projects/02/HalfAdder.hdl, all’interno troveremo le specifiche funzionali che questo addizionatore deve soddisfare:

Copy to Clipboard

Il nostro addizionatore quindi prende in ingresso due bit, chiamati a e b e deve restituire il bit di somma e quello di riporto.

Per ottenere questo risultato è sufficiente accoppiare 2 porte logiche viste in precedenza, AND e XOR:

Dunque completiamo il codice HDL con la seguente coppia di istruzioni:

Copy to Clipboard

Testiamo la Soluzione

Ok, adesso abbiamo una possibile soluzione al nostro problema. Non ci resta che valutarne la correttezza.

Per fare questo, lanciamo il simulatore dalla cartella nand2tetris/tools e clicchiamo su File -> Load Chip. Carichiamo il file HalfAdder.hdl che abbiamo appena modificato.

Clicchiamo poi su File -> Load Script e selezioniamo il file HalfAdder.tst contenuto nella cartella nand2tetris/projects/02.

A questo punto, lanciamo il test cliccando Run -> Run e attendiamo che la procedura termini.

Se tutto è andato a buon fine dovremmo vedere un messaggio del tipo: End of script – comparison ended successfully.

Conclusioni

In questa lezione abbiamo presentato il sistema numeri binario e ci siamo divertiti a discutere l’implementazione di un Half-Adder per poter sommare 2 bit tenendo di conto anche del riporto.

Le altre soluzioni a questo capitolo le trovi a questo indirizzo e comprendono anche la realizzazione della ALU che useremo per costruire il nostro computer.

Nella prossima lezione andremo invece a discutere come implementare la memoria RAM.