Il termine architettura riferito ai sistemi di elaborazione indica l'organizzazione logica dei vari componenti interni della macchina ed il modo in cui essi cooperano per eseguire le operazioni richieste dagli utenti. Quindi l'architettura di un elaboratore può essere dedotta dalla conoscenza della sua struttura interna e dal modo in cui è possibile far cooperare i suoi componenti fisici mediante una serie di comandi o istruzioni (linguaggio macchina).
I moderni elaboratori elettronici, dai piccoli personal computer ai grandi maxi-computer, derivano in buona parte da uno stesso «modello» fondamentale di elaboratore chiamato Macchina di Von Neumann dal nome dello scienziato che nel 1946 pubblicò, insieme ad altri, un progetto per una nuova macchina di calcolo automatico.
Questa macchina è schematizzata in figura 1.1.
Le linee continue rappresentano il flusso di dati, le linee tratteggiate il flusso di controllo, dal quale si capisce che tutte le unità agiscono in base ai segnali inviati da un componente denominato appunto unità di controllo.
Le tre funzioni principali svolte dal sistema di elaborazione sono:
funzione di memoria;
funzione di esecuzione (calcoli e confronti);
funzione di controllo e governo.
L'elaboratore è un sistema «automatico» in quanto tali funzioni vengono realizzate senza l'intervento diretto dell'uomo.
La cooperazione tra le unità di controllo, esecuzione e memoria permette di eseguire algoritmi opportunamente descritti secondo determinati linguaggi ed inseriti in memoria.
L'elaboratore è un sistema «versatile» in quanto può svolgere compiti molto diversi tra loro variando opportunamente gli algoritmi ad esso forniti.
Uno dei concetti fondamentali introdotti da Von Neumann e che costituì un passo avanti decisivo rispetto ai primi elaboratori dell'epoca, è quello di «programma memorizzato», cioè il fatto che il programma da eseguire (cioè la traduzione dell'algoritmo in una forma comprensibile alla macchina) viene immagazzinato in memoria al pari dei dati su cui esso opera.
L'elaborazione consiste nella esecuzione delle istruzioni del programma, da parte della unità di esecuzione sotto il governo della unità di controllo, una alla volta secondo l'ordine in cui sono contenute nella memoria, fino al completamento dell'intero processo previsto dall'algoritmo. L'uomo interviene solo per dare il comando di avvio dell'esecuzione e per introdurre eventuali dati di input. |
Come detto il programma da eseguire deve essere scritto in precedenza in un apposito linguaggio (linguaggio di programmazione), poi tradotto in una forma direttamente eseguibile dalla macchina (linguaggio macchina) e quindi portato nella memoria dell'elaboratore.
Le tre operazioni possono essere così riassunte:
fase di programmazione, eseguita dal programmatore;
fase di traduzione, eseguita da appositi programmi detti «compilatori» (qui non viene considerato il caso di programmi scritti con linguaggi «interpretati» invece che «compilati»);
fase di caricamento, eseguita da un programma detto loader.
In queste dispense non vengono fornite ulteriori informazioni sui linguaggi di programmazione, eccetto per quelli a basso livello, come il linguaggio macchina o l'«assembly», trattati nel capitolo ##ling-macchina##.
Vediamo più in dettaglio in cosa consistono le operazioni svolte da un elaboratore elettronico:
funzione di memoria: codifica di informazione (programmi e dati) su supporti in grado di conservarla per il tempo necessario alla elaborazione;
funzione di esecuzione: esecuzione di una specifica gamma di operazioni, potendo determinare l'operazione da eseguire tra quelle a disposizione e specificando gli «operandi» (dati) su cui essa opera; siccome le operazioni da svolgere sono principalmente di tipo aritmetico-logico, l'unità di esecuzione viene anche chiamata Unità aritmetico logica o ALU (Arithmetic and Logic Unit);
funzione di controllo: riconoscimento delle istruzioni da eseguire e conseguente avvio della loro esecuzione mediante opportuni segnali alla ALU; controllo della successione di istruzioni che compongono il programma;
funzione di ingresso o input: introduzione di dati e programmi nel sistema;
funzione di uscita o output: comunicazione di risultati all'esterno.
Lo schema di Von Neumann introdotto in precedenza, anche se concettualmente ancora valido, deve essere «aggiornato» con quello della figura 1.2.
(sono indicate solo le linee di flusso dei dati e di controllo che entrano o escono dalla «Unità centrale»).
Nella figura si possono individuare:
Unità centrale, adibita all'elaborazione dei dati ed al controllo generale del sistema;
Unità di canale, sono delle unità di controllo ausiliarie (talvolta addirittura degli elaboratori «satellite») adibite al collegamento con le periferiche cioè i dispositivi che permettono il colloquio del sistema con l'ambiente esterno o la memorizzazione permanente di dati e programmi (tastiera, video, stampante, memorie di massa, ecc.).
La CPU (Central Processing Unit) costituisce il cuore dell'elaboratore e il luogo dove avviene l'esecuzione delle istruzioni di un certo programma; è costituita dalla unione della ALU e della unità di controllo CU (Control Unit) nello stesso circuito integrato.
Fisicamente si tratta di una piastrina di silicio contenente circuiti stampati costituiti da molte migliaia o milioni di semiconduttori che prende anche il nome di microprocessore. La CPU è dotata di un certo numero di celle di memoria «locale» (da non confondere con la memoria centrale) che servono a contenere i dati interessati alle operazioni da svolgere, queste celle di memoria si chiamano registri.
La CPU deve essere collegata alle altre componenti del sistema di elaborazione alle quali invia segnali di controllo ed in particolare alla memoria centrale con la quale deve avvenire un continuo scambio di segnali binari corrispondenti ad indirizzi di memoria o al contenuto (dati o istruzioni) delle celle di memoria o dei registri. Questo comporta la necessità di collegamenti fisici che vengono chiamati bus.
Esistono tre tipi di bus:
bus dati;
bus indirizzi;
bus di controllo.
Gli stessi collegamenti devono essere presenti anche all'interno della CPU per assicurare lo scambio di segnali tra le sue componenti interne; in questo caso si parla di bus (dati, indirizzi e di controllo) «interni».
Il motivo per cui si parla di «indirizzi» risiede nella struttura logica e fisica della memoria centrale dell'elaboratore (illustrata più in dettaglio in seguito): essa è costituita da una sequenza di celle o locazioni numerate progressivamente e quindi individuabili tramite il proprio numero d'ordine o indirizzo.
I bus possono essere indipendenti o condivisi; nel secondo caso vengono usate le stesse linee fisiche per trasportare segnali di natura diversa allo scopo di ottenere un risparmio di spazio e di costo, al prezzo di un certo deterioramento delle prestazioni,.
Solitamente ad essere condivisi sono il bus dati e il bus indirizzi e viene usato un opportuno segnale di controllo per «chiarire» la natura degli impulsi trasportati in un certo momento nella linea di comunicazione.
Affinché la CPU possa funzionare correttamente c'è anche l'esigenza di un dispositivo chiamato clock che è un generatore di impulsi di durata costante emessi uno dopo l'altro ad intervalli di tempo regolari; lo scopo di questi impulsi è di sincronizzare tutti i circuiti ed il loro funzionamento all'interno della CPU, in modo che essi funzionino tutti all'unisono.
In altre parole si può dire che gli impulsi generati dal clock innescano le azioni all'interno della CPU; a fronte di una entrata (di segnali) in un certo circuito, si ha la corrispondente uscita solo quando arriva anche un impulso dal clock; maggiore è la sua frequenza, maggiore è la velocità di funzionamento della CPU e quindi la quantità di lavoro da essa svolta in un certo tempo. La velocità del microprocessore (o meglio, del clock) si misura in cicli al secondo (frequenza degli impulsi) cioè in Hertz (Hz); le moderne CPU hanno frequenze di funzionamento dell'ordine dei miliardi di hertz o gigahertz (Ghz).
Nella figura 1.3 viene presentato un possibile schema di unità centrale di elaborazione comprendente i vari bus e il clock.
La ALU è una rete logica in grado di eseguire operazioni logiche e aritmetiche sui dati; l'insieme delle operazioni che la ALU può eseguire costituisce il livello di dettaglio in cui devono essere espresse le istruzioni di un qualunque algoritmo da far eseguire all'elaboratore.
Naturalmente tale livello di dettaglio non deve essere raggiunto direttamente dal programmatore che, come già accennato, scrive i suoi algoritmi in un linguaggio di programmazione e poi provvede alla loro traduzione in forma comprensibile alla macchina mediante la compilazione.
Le operazioni vengono svolte dai circuiti della ALU con l'ausilio di appositi registri e precisamente:
uno o più registri accumulatori (AC) per immagazzinare gli operandi delle operazioni da eseguire;
un registro di stato (RS) nel quale vengono memorizzate, in codifica binaria, alcune informazioni utili sull'andamento della esecuzione come ad esempio: il risultato (vero o falso) dell'ultimo confronto effettuato, il verificarsi di un riporto o di un overflow nell'ultima operazione aritmetica eseguita, la positività o negatività del risultato dell'ultimo calcolo svolto.
Tutte queste informazioni sono di tipo binario (si/no, 1/0) e occupano solo un bit ciascuna, quindi RS contiene una sequenza di bit ciascuno associato ad un tipo di informazione.
Un possibile schema della ALU è quello della figura 1.4:
|
Il registro AC è collegato al bus dati in quanto può contenere operandi dell'operazione da svolgere e risultati, rispettivamente provenienti e diretti verso altre posizioni di memoria (locale o centrale).
Il registro RS è collegato al bus dati in quanto il suo contenuto può essere richiesto dall'unità di controllo per essere esaminato dopo una certa operazione.
Le operazioni non previste dai circuiti, e quindi non eseguibili direttamente tramite l'hardware dell'elaboratore, devono essere programmate ed eseguite via software; ad esempio per fare il prodotto di due numeri, se non abbiamo un circuito che realizza la moltiplicazione, si deve programmare l'operazione come un ciclo di somme.
In molti casi ciò è realizzato mediante microprogrammi registrati su appositi circuiti di memoria; l'insieme dei microprogrammi prende il nome di firmware.
La memoria dove vengono registrati i microprogrammi è di tipo ROM (Read Only Memory) e prende il nome di memoria di controllo.
Le microistruzioni stanno ad un livello inferiore rispetto alle istruzioni macchina; queste ultime infatti vengono eseguite attivando l'apposito gruppo di microistruzioni (microprogramma).
La CU è ancora una RETE LOGICA progettata per eseguire ciclicamente il seguente processo suddiviso in tre fasi:
fase di FETCH: consiste nel prelevamento (lettura) di una istruzione dalla memoria centrale e nel suo trasferimento in un apposito registro della CPU, l'instruction register (IR) dove viene messa a disposizione per la successiva fase;
fase di DECODE: consiste nell'individuazione, in base all'istruzione trasferita, della operazione da effettuare e su quali eventuali operandi; questa fase viene svolta da una unità di decodificazione a ciò adibita denominata decodificatore di istruzioni;
fase di EXECUTE: in base alle segnalazioni della unità di decodifica, che ha interpretato l'istruzione nella fase precedente, l'unità di controllo vera e propria invia le «direttive» di esecuzione alla ALU che esegue l'operazione richiesta.
Questo ciclo si chiama ciclo istruzione e si compone di diversi cicli macchina con i quali vengono eseguite le varie fasi (fetch, decode, execute).
Un ciclo macchina (o passo elementare di esecuzione) è l'intervallo tra due impulsi consecutivi del clock.
Perché questo processo possa continuare ciclicamente è necessario che, al termine della esecuzione di una istruzione, l'unità di controllo sia in grado di determinare l'indirizzo della istruzione successiva (si ricordi, a questo proposito, che le istruzioni che compongono un programma in esecuzione sono memorizzate in posizioni adiacenti della memoria centrale).
A questo scopo viene utilizzato un registro della CPU, il program counter (PC), che contiene l'indirizzo della prossima istruzione da prelevare e viene aggiornato dopo la fase di decodifica aggiungendo al suo contenuto la lunghezza della istruzione corrente decodificata.
La CU può essere schematizzata come nella figura 1.5:
|
Il ciclo di esecuzione di un programma da parte della CPU può essere rappresentato con un diagramma di flusso come in figura 1.6:
|
Prima di illustrare la memoria centrale del sistema di elaborazione vengono fornite alcune nozioni sulle memorie in generale.
Una memoria può essere rappresentata come un insieme di celle (o locazioni) che possono conservare porzioni elementari di informazione.
Nelle celle sono possibili due tipi di operazione: «lettura» di informazione o «scrittura» di informazione; in generale si parla di «accesso» alla memoria quando si compie una delle due operazioni.
Le memorie devono essere organizzate in modo da permettere la loro individuazione per l'effettuazione degli accessi; devono quindi avere un indirizzo univoco per ogni cella.
Le memorie si possono classificare in base a varie caratteristiche; una classificazione molto importante viene effettuata in base alla modalità di accesso:
memorie ad accesso uniforme: in esse una cella è accessibile indipendentemente dalla sua posizione ed in un tempo uguale per tutte le celle presenti nella memoria (esempio memoria centrale);
memorie ad accesso diretto: in esse una cella è accessibile indipendentemente dalla sua posizione ma il tempo impiegato dipende da quest'ultima (esempio: memorie ausiliarie su disco);
memorie ad accesso sequenziale: in esse una cella è accessibile solo passando attraverso tutte le celle che la precedono (esempio: memorie ausiliarie su nastro o memorie ottiche).
La memoria centrale RAM (Random Access Memory) è una memoria ad accesso uniforme; anche essa è una rete logica i cui circuiti sono progettati per conservare informazione in codifica binaria.
La codifica binaria dell'informazione consiste nella rappresentazione di un simbolo alfanumerico mediante un gruppo di bit; la memoria allora viene organizzata in una sequenza di «gruppi di bit», o posizioni, o parole (word) ognuna delle quali è individuata da un indirizzo.
La grandezza della parola dipende dalla macchina ed è sempre un multiplo di 8 bit (1 byte); in certi casi la parola è 2 byte, in altri casi è 4 byte e talvolta si usano anche la mezza parola (half word) e la doppia parola (double word).
La capacità della memoria centrale è molto importante in quanto, come si è detto, tutti i programmi che devono essere eseguiti devono esservi memorizzati; la grandezza della memoria è quindi un indice di potenza dell'elaboratore. L'unità di misura della memoria è il byte con i suoi multipli: Kappabyte (1 KB=1.024 byte), Megabyte (1 MB=1.048.576 byte), Gigabyte (1 GB= 1.073.741.824 byte), Terabyte (1 TB= 1.099.511.627.776 byte).
Per quanto riguarda i circuiti per l'accesso alla memoria centrale si hanno:
un decodificatore degli indirizzi che riceve il suo input da un registro contenente l'indirizzo della parola a cui accedere;
un registro di indirizzamento in memoria (RIM) o MAR (Memory Address Register) contenente l'indirizzo cui accedere;
un registro di lettura e scrittura (RLS) o MBR (Memory Buffer Register) per immagazzinare la parola interessata al trasferimento e cioè la parola appena letta o da scrivere in memoria.
Il tutto si può schematizzare come in figura 1.7:
|
Nel caso di posizioni di memoria costituite da più byte (ad esempio di 16 bit) l'ordine con cui i diversi byte di una stessa posizione sono memorizzati dipende dall'architettura del computer.
I due ordinamenti più diffusi sono:
big endian o big end first: in questo caso le posizioni di memoria sono occupate a partire dal byte più a sinistra del dato, quindi dal più significativo;
little endian o little end first: in questo caso le posizioni di memoria sono occupate a partire dal byte più a destra del dato, quindi dal meno significativo.
Da quanto detto emerge che nel caso di big endian il byte più significativo (MSB Most Significant Byte) ha l'indirizzo di memoria più piccolo, mentre nel caso di little endian è il byte meno significativo (LSB Least Significant Byte) ad avere l'indirizzo più piccolo.
Ad esempio se si deve memorizzare il dato 'AB' a partire dall'indirizzo 100, avremo, nel caso di big endian:
indirizzo 100: A indirizzo 101: B
invece, nel caso di little endian:
indirizzo 100: B indirizzo 101: A
I termini big endian e little endian derivano dai Lillipuziani dei "Viaggi di Gulliver", il cui problema principale era se le uova debbano essere aperte dal lato grande (big endian) o da quello piccolo (little endian); il significato di questa analogia è ovvio: nessuno dei due metodi è migliore dell'altro.
Esiste comunque un problema di compatibilità noto come NUXI problem dovuto al fatto che i processori Intel usano il metodo little endian e quelli Motorola il metodo big endian, si dice anche che hanno endianess diverse.
Il termine NUXI deriva dall'aspetto che avrebbe la parola UNIX se memorizzata in due posizioni consecutive di due byte in little endian.
Si chiama ciclo di memoria la sequenza di azioni che il sistema compie per leggere o scrivere un dato in memoria.
Ipotizzando che il metodo di ordinamento dei dati in memoria sia il big endian, abbiamo, nel caso della lettura:
L'indirizzo del dato viene posto nel MAR.
Il decodificatore di indirizzi accede alla posizione di memoria indirizzata.
Se viene letta una parola, il byte indirizzato dal MAR viene posto nella metà più significativa del MBR e il byte successivo viene posto nella metà meno significativa.
Se viene letto un byte viene posto nella metà meno significativa del MBR e l'altra metà viene riempita con copie del bit più significativo presente nel byte (meccanismo di estensione del segno).
Nel caso della scrittura:
L'indirizzo della cella di memoria di destinazione viene posto nel MAR.
Il dato da memorizzare viene collocato nel MBR.
Se viene scritta una parola, il byte più significativo del MBR viene trasferito nella cella indirizzata dal MAR e il byte meno significativo viene posto nella cella successiva.
Se viene scritto un byte, la metà meno significativa del MBR viene trasferita nella cella di memoria indirizzata dal MAR.
La memoria centrale viene realizzata con materiale semiconduttore; ogni unità elementare di informazione o bit è di solito un multivibratore bistabile (o flip-flop), cioè un circuito che può assumere uno di due diversi stati possibili in modo stabile finché un certo impulso non lo fa passare nell'altro stato.
La lettura e la scrittura in memoria consistono nel rilevare lo stato del flip-flop e nell'inviargli impulsi per cambiarlo. Tali operazioni avvengono con la stessa velocità per tutti i circuiti della memoria ed in maniera indipendente dalla loro posizione; quindi la memoria centrale è una memoria ad accesso «casuale» (random) o «uniforme».
Le tecnologie con cui vengono realizzati i circuiti di memoria sono però due e quella basata sui flip-flop non è, come vedremo, la più utilizzata:
memorie bipolari TTL (Transistor Transistor Logic); in esse un flip-flop viene ottenuto collegando almeno due transistor; in tal caso si ha una RAM statica (SRAM) in quanto l'informazione rimane inalterata finché non interviene un impulso a farla cambiare;
memorie a semiconduttori MOS (Metallo Ossido Semiconduttore) che richiedono un solo condensatore per ogni bit, se carico corrisponde al valore uno, altrimenti a zero; sono più compatte ma meno veloci e necessitano di circuiti aggiuntivi per il continuo «refresh» dei valori perché i condensatori tendono a scaricarsi nel tempo (in pochi millisecondi). Tale tipo di memoria è detto RAM dinamica (DRAM).
La RAM statica è più veloce ma anche molto più costosa; per questo motivo la memoria centrale di un computer è realizzata con RAM dinamica mentre la RAM statica viene utilizzata per la memoria cache.
La cache è una memoria più piccola della memoria centrale ma anche più veloce.
Nei moderni personal troviamo almeno due memorie cache: quella di primo livello (L1) grande alcuni KB, inglobata all'interno della CPU e veloce quanto la CPU e quella di secondo livello, grande qualche MB, situata esternamente alla CPU, meno veloce ma comunque più veloce della memoria centrale.
Il loro ruolo è quello di interporsi tra CPU e RAM allo scopo di immagazzinare dati e istruzioni che si ritiene possano servire alla CPU per le operazioni immediatamente successive, onde evitare che debbano essere letti nella più lenta memoria centrale.
Per questo la CPU cerca dati e istruzioni prima nella memoria cache L1 e poi nella L2 e solo in caso di insuccesso accede alla memoria centrale; inoltre quando una cella viene letta dalla RAM, il suo contenuto viene portato, oltre che alla CPU, anche nelle memorie cache, insieme a quello delle celle limitrofe.
Prima di proseguire nell'esame dei principi di funzionamento della cache presente nell'unità centrale, soffermiamoci brevemente sul concetto più generale di «cache» che incontriamo anche in altri ambiti informatici.
In generale una cache è una memoria tampone che si colloca (logicamente) fra due entità informatiche fra cui avviene uno scambio di dati e che sono notevolmente diverse dal punto di vista della velocità; il suo ruolo è quello di sopperire alla lentezza dell'entità meno veloce ospitando parte dei dati che da questa devono essere trasferiti all'entità più veloce riducendone quindi i tempi di attesa.
Vediamo un paio di esempi in ambiti diversi da quello dell'unità centrale:
la cache del disco fisso: in questo caso l'entità lenta è il disco, quella veloce l'unità centrale e una parte della memoria centrale viene usata come cache del disco per cercare di rendere più rapidi i trasferimenti da quest'ultimo all'unità centrale;
la cache dei programmi di navigazione in Internet: in tal caso l'entità lenta è Internet (o meglio il collegamento alla rete), quella veloce è il programma di navigazione e una parte del disco fisso viene usata come cache per ospitare le pagine Web visitate e renderle rapidamente disponibili in caso di visita successiva.
Tornando alla cache dell'unità centrale notiamo che la sua utilità è giustificata dalla validità, verificata sperimentalmente, dei seguenti due principi:
principio di località degli accessi nello spazio: se la CPU accede ad una cella, molto probabilmente negli istanti successivi accederà alle celle vicine; ciò deriva dal fatto in moltissimi programmi si gestiscono vettori, cioè insiemi di dati memorizzati in celle consecutive;
principio di località degli accessi nel tempo: se la CPU accede ad una cella, molto probabilmente negli istanti successivi accederà di nuovo a quella cella; ciò deriva dal fatto che moltissimi programmi contengono cicli cioè istruzioni che vengono eseguite ripetutamente in breve tempo.
La presenza della memoria cache permette di velocizzare gli accessi in lettura alla RAM; non c'è invece alcun miglioramento per gli accessi in scrittura.
A tale proposito ci sono due metodologie di gestione della cache:
write back: i dati da memorizzare vengono scritti solo in cache e in un momento successivo anche nella memoria centrale; è un metodo più complesso e con qualche rischio di «disallineamento» dei dati, ma che assicura maggiore velocità di scrittura;
write through: i dati da memorizzare vengono scritti contemporaneamente nella cache e nella memoria centrale; è un metodo meno complesso ma leggermente più oneroso riguardo ai tempi di accesso.
Sia le memorie SRAM che le DRAM sono «volatili» cioè perdono il loro contenuto se viene a mancare l'alimentazione elettrica.
Esistono anche memorie non volatili come le ROM citate in precedenza; più in dettaglio abbiamo:
ROM: memorie in sola lettura che vengono scritte in fabbrica e non permettono in nessun modo di essere riscritte;
PROM (Programmable ROM): memorie ROM che possono essere scritte, una sola volta, con apparecchiature apposite;
EPROM (Erasable Programmable ROM): memorie ROM che possono essere cancellate e riscritte con apparecchiature apposite grazie all'esposizione del chip a raggi ultravioletti;
EEPROM (Electrically Erasable Programmable ROM): memorie ROM che possono essere cancellate e riscritte elettricamente direttamente sul computer dove sono utilizzate senza la necessità di rimuoverle e usare altri dispositivi. In questa tipologia di memorie rientrano anche le memorie flash che si stanno diffondendo molto rapidamente come memorie ausiliarie per i computer (ad esempio penne USB) o supporti di memorizzazione nell'elettronica di consumo (ad esempio schede per fotocamere e videocamere digitali); la differenza fondamentale tra una memoria EEPROM «tradizionale» e una memoria flash è nel fatto che alla prima si accede a livello di singole celle o parole, mentre alla seconda si accede «a blocchi» come avviene per i dischi fissi.