Struttura di un Calcolatore
G. Manduchi
In questa pagina verranno descritti gli elementi fondamentali di un calcolatore elettronico, ovvero la CPU, la memoria centrale, le periferiche ed il bus di comunicazione. Il seguente schema a blocchi indica come i componenti sono interconnessi: la CPU accede in lettura e scrittura, tramite il bus di comunicazione, alle informazioni immagazzinate nella memoria, e comunica con le periferiche per eseguire le operazioni richieste. Il materiale presentato in questa sezione è ricoperto in maggiore dettaglio dal testo di Calcolatori Elettronici di Congiu, pag. 73-126.
La memoria provvede all'immagazzinamento dell'informazione gestita dal calcolatore. Una quantità maggiore di informazione viene inoltre immagazzinata nella memoria secondaria a supporto magnetico. Attualmente la quantità di memoria centrale gestita da un calcolatore può variare dalle decine di KBytes per i microcontrollori montati a bordo di apparecchiature, alle decine di MBytes per i Personal Computers fino ai GBytes utilizzati nei mainframes. L'elemento di base gestito dalla memoria è normalmente in byte. Ogni byte memorizzato è identificato da un indirizzo univoco. Il numero di bytes necessari per definire l'indirizzo dipenderà dalla dimensione della memoria. A seconda della tecnologia utilizzata nella loro costruzione, le memorie vengono suddivise in memorie bipolari e memorie MOS. Le memorie bipolari utilizzano transistor a giunzione (bjt): sono memorie veloci, ma consentono un grado di integrazione minore delle memorie MOS che utilizzano transistors ad effetto di campo (FET).
A seconda della loro funzionalità le memorie sono divise in:
Poiché l'elemento di base del contenuto di una memoria è il singolo byte, cui corrisponde un indirizzo univoco, sorge il problema di come rappresentare all'interno della memoria quelle informazioni che richiedono più di un byte, quale, ad esempio, un numero rappresentato da un longword. Vi sono due diverse possibilità: la prima è quello di caricare il byte meno significativo all'indirizzo inferiore (little endian) e la seconda è quella di caricare il byte più significativo all'indirizzo inferiore (big endian). Supponiamo ad esempio il numero 254, corrispondente a 000000000000000000000000111111102, o, in maniera più concisa, a $000000FE, memorizzato a partire dall'indirizzo 1000. In formato big endian tale numero verrà memorizzato come
Indirizzo | Valore |
1000 | $00 |
1001 | $00 |
1002 | $00 |
1003 | $FE |
mentre in formato little endian verrà memorizzato come
Indirizzo | Valore |
1000 | $FE |
1001 | $00 |
1002 | $00 |
1003 | $00 |
Non esiste una definizione comune di tale formato. Alcune architetture (es. Pentium) utilizzano il formato little endian, mentre altre (es. Sparc, Motorola 68K) utilizzano il formato big endian. A causa di queste differenze si dovranno eseguire delle operazione di conversione (byte swapping) quando una sequenza di bytes viene trasferita tra due calcolatori che utilizzino un diverso formato.
Il processore, o CPU (Central Processing Unit) rappresenta l'elemento centrale di un calcolatore. Esistono diverse famiglie di processori: ogni processore di una data famiglia rappresenta in generale un'evoluzione e pertanto è in grado di eseguire lo stesso codice eseguito dai processori precedenti della stessa famiglia. La CPU è a sua volta composta da un insieme di elementi, collegati tra di loro da uno o più bus interni (ovvero bus di comunicazione ricavati dallo stesso elemento di silicio della CPU). Tali componenti sono:
Registri
Un registro rappresenta un piccolo elemento di memoria all'interno del processore, in grado normalmente di memorizzare una singola Word, Longword (caso tipico per i processori attuali a 32 bits) o Quadword (8 bytes). I registri vengono utilizzati per conservare alcune informazioni durante la sequenza delle fasi di esecuzione delle singole istruzioni nonché tra un'istruzione e l'altra. Alcuni registri sono accessibili al programmatore, ovvero possono essere riferiti direttamente nelle istruzioni di macchina, altri vengono utilizzati dalla CPU internamente. Tra questi ultimi sono comuni a tutte le architetture i seguenti:
Altri bits di condizione possono essere definiti nelle specifiche architetture. Oltre ai bits di condizione, altre informazioni vengono memorizzate nello status (o condition) register, quali il livello corrente di priorità delle interruzioni, e il livello di privilegio.
L'Unità Logico Aritmetica (ALU)
L'unità logico aritmetica è rappresentata da una rete combinatoria composta di sommatori binari, shift registers, blocchi logici AND, OR, XOR, invertitori e multiplexers. L'attivazione di questi componenti è dettata da un insieme di linee di controllo che, opportunamente comandate dall'unità di controllo, fanno si che la ALU assuma la funzionalità desiderata. Nelle CPU attuali sono normalmente definite più Unità Logico Aritmetiche che possono operare in parallelo.
L'Unità Floating Point (FPU)
L'unità floating point consente di eseguire operazione su numeri reali, normalmente espressi in notazione floating point IEEE 754. Inizialmente la FPU era costituita da un chip separato dalla CPU, ed in mancanza di essa le operazioni su numeri reali dovevano essere eseguite via software, ovvero con un'opportuna sequenza di istruzioni che comportassero solo calcoli su numeri interi. Attualmente le CPU utilizzate nei Personal Computers montano a bordo dello stesso chip l'unità floating point, e pertanto il set di istruzioni della CPU viene esteso alle operazioni di manipolazione dei numeri reali.
Il modulo di controllo
Il modulo di controllo rappresenta la parte centrale della CPU, che coordina la sequenza delle fasi di esecuzione delle istruzioni. L'esecuzione di ogni istruzione infatti richiede una sequenza di fasi diverse: la fase di fetch per leggere dalla memoria l'istruzione da eseguire, la fase di decode per la decodifica dell'istruzione, e la fase di execute, per l'esecuzione dell'istruzione stessa. Queste fasi possono a loro volta essere suddivise in altre fasi. La CPU pertanto non è rappresentata da una rete combinatoria, bensì da una macchina sequenziale, che attraversa un insieme di stati, sincronizzata da un segnale di clock. L'unità di controllo implementa tale macchina sequenziale, e definirà un certo insieme di bits per contenere lo stato. L'ingresso di tale macchina sequenziale sarà rappresentato dall'istruzione da eseguire (o più precisamente da una parte di essa corrispondente al codice operativo), e le uscite saranno le linee di controllo necessarie per la corretta configurazione della ALU, per la connessione dei bus interni, e per la configurazione dei multiplexer utilizzati al di fuori della ALU. L'esecuzione di un'istruzione richiederà pertanto un certo numero di cicli di clock: maggiore sarà la frequenza di tale clock (limitato tuttavia dai tempi di commutazione del circuito), più veloce sarà l'esecuzione delle istruzioni.
Nella configurazione appena vista il modulo di controllo è rappresentato in principio da una rete combinatoria la quale, ricevendo in ingresso la codifica dello stato attuale e l'ingresso corrente, produce la codifica dello stato successivo e l'insieme delle linee di controllo. Tale implementazione prende il nome di logica cablata, e presenta lo svantaggio che una modifica nella sequenza di fasi (per aggiungere una funzionalità o per correggere un errore di implementazione) richiede di dover riprogettare la rete combinatoria.
Una implementazione alternativa dell'unità di controllo definisce invece una memoria ROM, le cui locazioni contengono tanti bits quante sono le linee di controllo che devono essere generate. Il codice operativo dell'istruzione definirà un indirizzo iniziale nella ROM e le locazioni successive conterranno le linee di controllo da produrre in sequenza. Una di queste linee di controllo specificherà la terminazione della sequenza, e quindi inibirà la generazione di indirizzi crescenti ad ogni ciclo di clock, e terminerà la sequenza di esecuzione dell'istruzione corrente. Questa soluzione presenta il vantaggio di una maggiore flessibilità (basterà cambiare il contenuto della ROM per modificare il funzionamento della CPU) ma ha lo svantaggio di essere più lenta rispetto ad una logica cablata. Infatti in questo caso le linee di controllo non sono prodotte dall'uscita di una rete combinatoria, ma da una lettura di memoria e richiede pertanto un tempo maggiore di esecuzione (si ricordi che in una memoria un'operazione di lettura richiede un livello di multiplexing per l'attivazione delle celle di memoria indirizzate e la connessione degli switch tri-state per il trasferimento in uscita del contenuto selezionato). Quest'ultima soluzione implementativa prende il nome di architettura microprogrammata: la sequenza memorizzata nella ROM in corrispondenza alla singola istruzione macchina corrisponde infatti ad un "microprogramma".
Il bus di comunicazione rappresenta il collegamento fisico tra la CPU e gli altri componenti. Diversi tipi di operazioni sono supportati da un bus di comunicazione. Ci concentreremo qui sulle operazioni di lettura e scrittura in cui la CPU scrive una locazione nella memoria o ne legge il contenuto. In questo caso l'operazione è supervisionata dalla CPU che pertanto rappresenta il master nella comunicazione. La memoria in questo caso ha un ruolo passivo, e rappresenta un componente slave. Il bus si compone fisicamente di un insieme di linee conduttrici: dei fili o delle piste conduttrici nel caso il bus sia esterno, delle aree opportunamente drogate nel silicio nel caso il bus sia interno alla CPU. Mettere a disposizione delle linee di conduzione non è tuttavia sufficiente per un trasferimento controllato dell'informazione. A tal scopo sarà necessario definire un protocollo di comunicazione. Il protocollo di comunicazione definisce il ruolo delle singole linee, e le sequenze necessarie per un'esecuzione controllata dei trasferimenti. Diversi protocolli di comunicazione sono utilizzati: tra questi ricordiamo il bus VME, utilizzato nei processori della serie Motorola 68K, cui si farà riferimento in questa esposizione, il bus PCI, utilizzato per la comunicazione con le periferiche in molti personal computers, il bus SCSI utilizzato per la comunicazione con dispositivi quali i dischi fissi. Una caratteristica del protocollo di comunicazione è data dalla frequenza di clock per il bus. Le operazioni sul bus sono infatti sincronizzate da un segnale di clock, la cui frequenza massima dipende dalle caratteristiche elettriche del bus (anche queste parte della specifica del protocollo). Le frequenze per il clock del bus è tipicamente di molto inferiore a quella delle CPU, a causa delle maggiori capacità che si hanno in un bus esterno rispetto all'interno del chip di silicio. Il PCI, ad esempio, rappresenta uno dei bus più veloci ed è caratterizzato da una frequenza di clock di 33 o 66 MHz, ben al di sotto delle frequenze di centinaia di MHz comuni nei processori attuali.
I protocolli di comunicazioni tipicamente dividono le linee del bus in:
Il bus VME definisce le linee di controllo R/W (Read/Write), AS (Address strobe) e DTACK (Data Acknowledge) per la gestione delle operazioni di lettura e scrittura. Il livello della linea R/W indicherà se si sta eseguendo un'operazione di lettura o di scrittura. Il funzionamento delle linee AS e DTACK è esemplificato dal seguente diagramma che definisce l'evoluzione temporale del protocollo.
In un operazione di lettura la CPU inizialmente pone ad un valore definito le linee di indirizzo A1-A23 (in questo bus gli indirizzi sono specificati da 24 linee, ma poichè le operazioni sono eseguiti a words non è necessario specificare il bit meno significativo dell'indirizzo, che sarebbe sempre 0). Una volta che il livello elettrico di queste linee si è stabilizzato, la CPU segnala che un indirizzo valido è presente sul bus postando il livello della linea AS a basso. A questo punto la memoria risponderà ponendo il contenuto dei due bytes corrispondenti all'indirizzo specificato ed al seguente) sulle 16 linee di dati D0-D15. Tale operazione non sarà istantanea, e richiederà il tempo di accesso alle locazioni specificate (tipicamente di qualche decina di nanosecondi). Quando il livello delle linee di dati si sarà stabilizzato, la memoria segnala la disponibilità dal dato postando il livello della linea di controllo DTACK a basso. A questo punto la CPU potrà trasferire il contenuto delle linee di dati nel memory buffer register (MBR)e terminate la sequenza riportando a livello alto la linea di controllo AS.
Un'operazione di scrittura è rappresentata invece dal seguente diagramma temporale:
e differisce dalla precedente operazione di lettura, oltre che dal livello della linea R/W (non riportato nella figura), dal fatto che le linee di dati vengono impostate questa volta dalla CPU. Pertanto, quando il livello della linea AS si porta a basso, sia le linee di dati che di indirizzi saranno impostate, e la memoria in risposta provvederà alla memorizzazione dei due bytes, segnalando tramite la linea DTACK il termine della scrittura.
Un protocollo simile viene utilizzato anche negli altri bus di comunicazione, che attualmente definiscono di norma 32 linee sia per gli indirizzi che per i dati. In alcuni casi le linee di indirizzi vengono utilizzate anche per i dati in un ciclo di clock successivo. Si parlerà in questo caso di bus multiplexati.
Un'altra caratteristica comune a molti bus è la possibilità di eseguire trasferimenti veloci di blocchi di dati (block transfer). In questo caso, poiché i dati stanno ad indirizzi consecutivi, non vi è bisogno di trasferire l'informazione dell'indirizzo dopo aver trasferito il primo dato all'indirizzo iniziale. Ciò rende l'operazione di trasferimento più veloce in quanto non è necessario impostare le linee di indirizzi per i dati successivi, richiedendo quindi un tempo inferiore per il trasferimento del singolo dato.
Si osserva infine che negli esempi presentati, le linee di controllo sono attive quando sono a livello basso. Questo è tipico di molti bus di comunicazione e deriva dal fatto che le linee sono collegate a più elementi (la CPU, le memorie e i dispositivi di I/O collegati al bus) in modalità open collector. Quando nessuno degli elementi guida la linea (ovvero la collega tramite un transistor alla massa) il livello della linea è portato ad alto tramite una resistenza di pull-up. Tale livello va basso se almeno uno degli elementi guida la linea, ma non comporta la rottura degli stadi di uscita se più di un elemento guida il livello della linea (il tutto si comporta come un OR).
I dispositivi esterni, o periferiche, rappresentano il collegamento tra il calcolatore ed il mondo esterno. Un calcolatore senza alcuna periferica sarebbe completamente inutile. Il tipo di periferica dipenderà dal tipo di utilizzo del calcolatore. Se il calcolatore è rappresentato da un microcontrollore che coordina il funzionamento di un'apparecchiatura, le periferiche consisteranno in dispositivi per l'acquisizione dei segnali ed in attuatori, in quanto compito del microcontrollore è quello di acquisire un insieme di segnali per generare un insieme di comandi. In un Personal Computer, le periferiche saranno rappresentate dal terminale, dalla tastiera, dai dischi, dalle stampanti e probabilmente da una scheda di rete o da una scheda audio. Le periferiche possono essere pertanto molto diverse tra loro e quindi possono avere velocità di trasferimento dato molto variabili, passando da qualche migliaio di bits al secondo per i terminali alle decine di milioni di bytes al secondo per i dischi magnetici. E' pertanto necessario che siano definiti dei meccanismi per sincronizzare i dispositivi esterni con la CPU. A tal scopo possono essere definite due diverse architetture: