Appunti presi durante le lezioni di Architettura degli Elaboratori II, nell’anno accademico 2001/2002. Attenzione: non mi assumo nessuna responsabilità per informazioni errate o imprecise eventualmente presenti.

Sito del corso: http://twiki.dsi.uniroma1.it/twiki/view/Architetture2/WebHome
Sito di questi appunti: http://twiki.dsi.uniroma1.it/twiki/view/Users/AlbertoRocca
E-mail docente: debiase@dsi.uniroma1.it .

Buono studio.

 

A - RICHIAMI

1. VELOCITA' DELLE PORTE

Per velocità di una porta si intende il tempo che questa impiega per adattare il suo output agli input che riceve.

La porta più veloce è la NAND.
Se assumiamo che essa inpiega un tempo T per adattarsi agli input, si può tracciare il seguente schema:

Porta

Tempo

Nand
Not
And
Xor

T
T
2 T
3 T

2. LA WORD

Le stringhe binarie su cui i calcolatori lavorano devono avere lunghezza finita e fissata.
Tali stringhe sono dette parole o word.
La loro lunghezza condiziona tutta l'architettura del calcolatore: ogni circuito sarà in grado di manipolare (addizionare, memorizzare, decodificare, ecc.) solo stringhe di quella lunghezza.

 

B - BLOCCHI BASE

1. INTRODUZIONE

Utilizzeremo blocchi (insiemi) di porte logiche già studiati ad Architetture I.
Di ciascuno di questi blocchi non ci interessa più lo schema circuitale, ma la sua funzione logica (deducibile dalla tabella di verità).

2. BLOCCHI COMBINATORI

Blocchi di porte logiche connesse in modo aciclico t.c. i segnali in uscita in un dato istante dipendono solo dai segnali in ingresso in quello stesso istante.

2.1 Codificatori

2.1.1 Codificatore

2.1.2 Decodificatore

Input
e0 e1 e2

Output
u0 u1 u2 u3 u4 u5 u6 u7

0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

1 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0
0 0 1 0 0 0 0 0
0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0
0 0 0 0 0 1 0 0
0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 1

L'uno è sull'uscita indicata dal numero binario in input.

2.1.3 Transcodificatore

2.1.4 Rom

Input
e0 e1 e2

Output
u0 u1 u2 u3 u4 u5 u6 u7

0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

valori stabiliti dal costruttore

2.2 Commutatori

2.2.1 Multiplexer

Input
e0 e1 e2 e3 i0 i1

Output
u

x y z w 0 0
x y z w 0 1
x y x w 1 0
x y z w 1 1

x
y
z
w

Le linee di indirizzamento i0 ed i1 specificano quale entrata connettere all'uscita.

2.2.2 Demultiplexer

2.2.3 Multiplexer + demultiplexer

2.3 Sommatori aritmetici

2.3.1 Half adder

Input
a b

Output
r s

0 0
0 1
1 0
1 1

0 0
0 1
0 1
1 0

r rappresenta il riporto.

2.3.2 Full adder

Input
a b rin

Output
rout s

0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

0 0
0 1
0 1
1 0
0 1
1 0
1 0
1 1

2.3.3 Addizionatore aritmetico

2.4 Comparatori

2.4.1 Comparatore aritmetico

2.4.2 Comparatore logico

3. BLOCCHI SEQUENZIALI

Blocchi di porte logiche t.c. i segnali in uscita in un dato istante NON dipendono solo dai segnali in ingresso in quello stesso istante (c'è una memoria finita degli input passati, che può influenzare gli output attuali).

3.1 Flip-Flop

3.1.1 Tipo JK

Input in t
J K Clk

Output in t
Q

0 0 |
0 1 |
1 0 |
1 1 |
x x _

Q(t-1)
0
1
¬Q(t-1)
Q(t-1)

3.2 Contatori

3.2.1 Contatore binario

3.2.2 Contatore binario preselezionabile

3.3 Registri o buffer

3.3.1 Registro di memorizzazione

3.3.2 Registro a scorrimento (shift register)

 

C - MACCHINE DI VON NEUMANN

È consigliato lo studio di "D - Dati e Istruzioni" e di "E - Interruzioni ed Eccezioni" parallelamente a questo capitolo.

1. Introduzione

La maggior parte dei calcolatori attualmente in uso sono macchine di Von Neumann, dal nome dell'ideatore dell'architettura (struttura logica) con cui sono costruiti.

Una macchina di Von Neumann è costituita da un sistema di interconnessione tramite cui vengono collegati 4 tipi di unità logiche (ognuna con un certo compito logico):

Queste unità logiche sono ottenute combinando i blocchi di cui si è parlato nel capitolo precedente, a creare circuiti più complessi.

CU e ALU sono quasi sempre integrate su un unico chip che prende il nome di CPU (Central Processing Unit).
Per questo, CU e ALU sono in genere connessi tramite multiplexer. CPU, memoria centrale, I/O, invece, sono in genere connessi tramite bus.
Architettura

2. Sistemi di Interconnessione

Per spostare una stringa da un punto ad un altro in un calcolatore esistono due metodi di trasmissione: seriale e parallelo.

Per realizzare questi due metodi di trasferimento esistono tre modi.

Schema

Si supponga ad esempio che A debba comunicare con B. A, tramite le linee di indirizzamento, attiva l'uscita B del suo demultiplexer. Manda quindi serialmente all'ingresso del suo demultiplexer la stringa binaria da inviare. Questa viene ricevuta dal multiplexer di B, insieme alle linee di indirizzamento che A ha mandato al suo demultiplexer. In questo modo, l'ingresso A del multiplexer di B viene attivano. La stringa quindi passa attraverso il multiplexer di B e raggiunge B.

Possono comunicare fra loro più di due registri alla volta, ma se si vuole cambiare il numero di elementi che compongono la rete (nell'esempio sono tre, A,B,C), va rifatto tutto da capo (struttura rigida), nel senso che bisogna usare multiplexer e demultiplexer con un numero di ingressi o uscite adeguato.
Affiancando più linee si può effettuare un trasferimento parallelo.

Tristate

Input
a c

Output
b

x 0
x 1

disconnesso
x

Dunque, quando ricevono un segnale basso su c, qualunque sia il valore di a, esse introducono una resistenza tale che non c'è alcun segnale sull'uscita b. Questo significa disconnesso. Quando c vale 1, invece, mandano in output lo stesso segnale che ricevono in input.
Inizialmente tutte le unità sono disconnesse; quando due di esse vogliono colloquiare, un opportuno circuito di controllo connette le loro porte tristate. I segnali del mittente transitano sul bus e, pur raggiungendo tutte le unità, vengono captati solo dal ricevente.
Il circuito di controllo è detto arbitratore del bus e cede il bus a chi lo richiede solo quando questo è libero, utilizzando una certa gerarchia di priorità (ad es. la CPU ha precedenza sugli altri componenti).
La capacità di trasferimento del bus è misurata in byte al secondo e viene detta troughput.

NON possono comunicare fra loro più di due registri alla volta, ma se si vuole cambiare il numero di elementi che compongono la rete (nell'esempio sono tre, A,B,C), non è necessario rifare tutto da capo.
Affiancando più BUS si può effettuare un trasferimento parallelo.

3. Unità Aritmetica e Logica

3.1 Introduzione

Schema a blocchi:
ALU
Funzione logica: operazioni logiche (not, and, or, ..., comparazione logica, ...) e operazioni aritmetiche (somma, differenza, shift, complementazione, opposto, comparazione aritmetica,...).

La ALU non è in grado di eseguire direttamente nè moltiplicazioni, nè divisioni. Inoltre può lavorare direttamente solo su numeri naturali o interi e su stringhe con significato logico.

3.2 Funzionamento

Operandi: ingressi ai quali mandare le stringhe binarie su cui compiere l'operazione scelta.
Funzione: ingressi su cui mandare il codice che identifica l'operazione da compiere. Ad esempio:

Codice

Operazione

0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

AND
OR
NOT
COMPARAZIONE logica e aritmetica
TEST B
ADD
SHIFT LEFT
SHIFT RIGHT

Risultato: uscite su cui si ottiene la stringa risultante, in caso di operazioni aritmetiche.
Flag: uscite che segnalano determinati eventi logici.

Flag

Evento

Z

segnala se il risultato dell'operazione logica o aritmetica compiuta ha come risultato la stringa nulla

N

segnala se il risultato dell'operazione compiuta è un numero negativo;
questa segnalazione ha senso solo per operazioni aritmetiche,
cioè quando le stringhe operando hanno significato numerico;
tuttavia la ALU segnala comunque come negative anche le stringhe con significato logico,
perchè si preoccupa soltanto di rilevare se il MSB è 1, quasiasi sia la natura della stringa.

C

segnala il trabocco di un uno sul MSB o LSB
(trabocchi di zero non contano visto che il numero non cambia: 00214 è uguale a 214).

V

segnala un overflow o underflow.

Il trabocco segnala che una stringa rappresentante un numero naturale o un valore logico (cioè in base due "standard" - senza bit segno e NON in complemento a due) ha oltrepassato le dimensioni della word e quindi non rappresenta più il valore voluto. Per fare ciò la ALU controlla che l'uscita di trabocco dei vari circuiti da cui proviene la stringa sia attiva.
L'overflow segnala invece che una stringa rappresentante un numero intero (cioè in complemento a due) ha oltrepassato la dimensione della word e quindi non rappresenta più il valore voluto. Per fare ciò la ALU controlla il MSB degli operandi: se entrambe gli operandi hanno MSB pari ad uno, c'è stato overflow e il flag V viene attivato.
Il trabocco non ha senso per numeri rappresentati con stringhe in complemento a due
(ad es. 1100Ca2+1010Ca2 da un risultato errato 0110Ca2, mentre 1100Ca2+1100Ca2 da un risultato giusto 1000Ca2, dunque il trabocco non dà alcuna indicazione sulla correttezza del risultato),
così come l'overflow non ha senso per numeri naturali e valori logici rappresentati in base due con metodo "standard".
La ALU, tuttavia, segnala entrambe le condizioni senza occuparsi del significato delle stringhe che elabora.

4. Memoria Centrale

4.1 Introduzione

Schema a blocchi:
MemoriaCentrale
Insieme di registri di memoria di capienza pari alla lunghezza della word; ogni registro prende nome di cella di memoria.
Funzione logica: scrittura di stringhe in memoria e lettura di stringhe precedentemente memorizzate.

4.2 Funzionamento

A: ingressi a cui va mandata la stringa da memorizzare
B: ingressi a cui va mandato l'indirizzo del registro su cui si vuole memorizzare A / da cui si vuole leggere C. Per semplicità progettuale e per comodità, anche questo ingresso accetta solo stringhe lunghe una word
C: uscita su cui si può leggere la stringa in memoria
Funzione: specifica se si vuole leggere o scrivere sulla memoria

Il programmatore assembly vede la memoria centrale come vettore unidimensionale il cui indice, detto indirizzo, identifica univocamente ogni cella e permette di accedervi sia in lettura che in scrittura.

L'entrata B teoricamente dovrebbe essere gestita da un decodificatore che riceve in input l'indirizzo del registro e con tante uscite quante sono le celle di memoria, così da poter mandare ad ognuna di esse un opportuno segnale. Si vedrà che in pratica ciò non è possibile. Ad ogni modo si può ragionare come se ciò fosse fattibile, poiché vengono usati trucchi trasparenti al programmatore assembly.
Dunque, se ad es. la memoria avesse 8 registri e lavorasse con parole da 4 bit, il decodificatore che gestisce B dovrebbe avere 8 uscite e quindi 4 ingressi, di cui soltanto tre realmente utilizzati:

ingressi = log28
2ingressi = 2log28
2ingressi = 8
2ingressi = 23
ingressi = 3

I 4 ingressi sono necessari perché, come già specificato, per semplicità progettuale e per comodità, B accetta solo stringhe lunghe una word.

La memoria centrale è una memoria ad accesso casuale: per raggiungere un registro non è necessario scorrere tutte le celle precedenti, come succede invece per le memorie dette ad accesso sequenziale. Per questo la memoria centrale viene spesso chiamata RAM (Random Access Memory).

Caratteristica delle macchine di Von Neumann è che la memoria centrale contenga sia istruzioni che dati. Questo aumenta le possibilità di commettere errori. Per proteggersi da ciò, la RAM viene spesso organizzata in "compartimenti stagni", ognuno atto a contenere solo un tipo di stringa. In genere esiste una parte dedicata alle istruzioni del programma, un'altra atta a contenere i dati, una parte riservata al sistema operativo [ad es. per il vettore delle interruzioni e per tutto il codice del S.O.] ed infine una riservata allo stack. Si noti che questa non è una suddivisione hardware, ma logica.

È da notare, infine, che prima che una operazione di lettura o scrittura venga completata, trascorre un certo tempo detto tempo di accesso, dovuto alla lentezza delle porte che costituiscono registri e decodificatori.

4.4 Accorgimenti pratici

4.4.1 Frazionamento degli Indirizzi

L'entrata B della memoria dovrebbe essere gestita da un decodificatore con input l'indirizzo del registro e con tante uscite quante sono le celle di memoria, così da poter mandare ad ognuna di esse gli opportuni segnali. Se si considera che B, sugli attuali computer, è in genere di 32 bit, si capisce che il decoder dovrebbe avere 4 miliardi di piedini di uscita (232). Attualmente, però, il massimo numero di piedini che si riesce ad inserire in un chip è dell'ordine di qualche centinaio.
Diventa quindi necessario usare un "trucco" a livello hardware (invisibile al programmatore assembly, che continua a vedere la memoria come un vettore unidimensionale) per poter indirizzare un tale quantitativo di celle.
Il trucco consiste nel frazionare l'indirizzo B.

Si prenda, ad esempio, il caso di indirizzi di 32 bit. B viene diviso in pezzi da 8 bit ciascuno. Ogni pezzo è considerato idealmente come una coordinata: x, y, z, w. In questo modo, la memoria non viene più pensata come un vettore unidimensionale, ma viene pensata scomposta come in figura.
Schema
Ogni pezzo da 8 bit è gestito da un decodificatore con otto ingressi e quindi 256 uscite (che è un numero di piedini accettabile). Le uscite dei 4 decodificatori vengono poi messe insieme per ottenere l’indirizzo finale.
Frazionamento
Si ricorda che tutto ciò avviene a livello hardware e non logico e pertanto risulta trasparente al programmatore assembly.

4.4.2 Assemblamento di Chip

In commercio si trovano chip di memoria costruiti come visto con registri, input, input per indirizzo, uscite. Essi hanno capacità prestabilita, nonché ingressi e uscite in grado di accogliere stringhe di lunghezza fissata.
Assemblando tali circuiti si ottengono i banchi di memoria veri e propri, in grado di gestire stringhe dalla lunghezza e dalla capienza che si vuole.

Si consideri il caso in cui si abbiano a disposizione chip in grado di contenere 4 parole da 4 bit e quindi con 4 linee di indirizzamento di cui solo 2 relamente utilizzate, 4 linee di input, 4 linee di output e 4 celle di memoria:
Schema

Come prima eventualità, si supponga di voler costruire con essi una memoria con capacità identica a quella dei chip disponibili, ma che possa lavorare su parole lunghe il doppio. Ecco come effettuare le connessioni:
Schema
Aggiungendo altri chip si possono gestire parole più lunghe.

Si supponga ora di voler costruire con essi una memoria che lavori con parole di lunghezza identica a quella dei chip disponibili, ma con capacità doppia (otto parole invece di quattro). Ecco come effettuare le connessioni:

Schema

Per gestire 8 parole servono 4 linee di indirizzamento, di cui solo 3 reamente utilizzate. Di queste, due vanno connesse a quelle dei chip, una viene usata come segnale abilitatore degli input dei chip (stabilisce quale dei due chip utilizzare).
Le uscite dei chip vengono gestite da porte OR: basta che da parte di un chip arrivi un uno ad una porta OR perchè l'output sia uno.
Aggiungendo altri chip si può ulteriormente aumentare la capacità.

Si possono combinare i due precedenti metodi per ottenere banchi di memoria di capacità doppia ed in grado di gestire parole lunghe il doppio.

4.4.3 Pagine di Memoria

L'ingresso B è lungo una parola. Questa è una limitazione alla quantità di memoria indirizzabile dalla macchina. Se ad es. la parola è di 16 bit, si possono indirizzare al massimo 216 (circa 65000) celle: come si usa dire, il campo di indirizzamento va da 0 a 216-1.
Può essere necessario disporre di più indirizzi, ovvero può essere necessario avere una memoria centrale di dimensioni maggiori rispetto a questo limite.

Pagine

A tal proposito si usa un registro di memoria speciale detto registro di estensione, che si trova in una zona della RAM di nome MM (Memory Managment). Tale registro contiene i bit in più rispetto alla lunghezza della parola che bisogna utilizzare per indirizzare la memoria.
Si può pensare in questi termini: la memoria viene divisa in pagine e ogni pagina è direttamente indirizzabile tramite la word che arriva all'ingresso B. Nel registro, invece, viene memorizzato il numero di pagina che si stà considerando.

Per quanto illustrato, la memoria centrale viene divisa in memoria fisica - quella realmente presente "on board" - e memoria indirizzabile - quella parte di memoria fisica direttamente indirizzabile tramite una parola.

Introdurre una struttura a pagine ha i seguenti svantaggi: occorrono nuove istruzione per gestire il registro di estensione (ad es l'istruzione "salto di pagina"); si ha un aumento del tempo di accesso; un programma non può occupare più di una pagina e i salti fra moduli di programma in pagine diverse sono più complicati [le istruzioni di salto hanno uno spazio per l'offset o indirizzo che non può gestire il registro di estensione, dunque occorre usare anche istruzioni speciali].

5. Control Unit

5.1 Introduzione

La Control Unit è costituita dai seguenti elementi di base:

e dai seguenti elementi non di base:

CU

5.2 Program Counter

Il PC è un contatore preselezionabile.
Il suo compito è di gestire la memoria centrale: le sue uscite sono collegate agli ingressi di indirizzamento della memoria centrale.
Essendo un contatore, scandisce la memoria in modo sequenziale. Se, però, è necessario interrompere la sequenzialità del conteggio per saltare ad un certo indirizzo, si può usare l'ingresso preselezionabile, collegato al codificatore di comandi.

Caratteristica delle macchine di Von Neumann è di avere un solo PC. Ciò è equivalente ad affermare che le macchine di Von Neumann sono macchine sequenziali: possono eseguire una sola istruzione alla volta.
Dall'unicità del PC si deduce anche l'unicità della memoria centrale: solo il PC ha accesso alla memoria e se esso è unico, anche la memoria lo deve essere.

5.3 Decodificatore di Istruzioni

La stringa letta dalla memoria tramite il PC viene inviata al decodificatore di istruzioni.
Questo decodifica i vari campi dell'istruzione e li manda al codificatore di comandi.

5.4 Codificatore di Comandi

Questo circuito riceve le istruzioni decodificate e fa tutto il necessario per eseguirle.
Ad es. si connette all'ingresso "funzione" della ALU e le comunica che operazione fare; connette quindi la memoria centrale o i registri interni alle entrate della ALU; connette poi l'uscita della ALU con la memoria centrale o i registri interni per memorizzare il risultato.
Si fa presente che non tutte le istruzioni richiedono l'uso della ALU.

Si noti la differenza fra istruzioni e comandi: le istruzioni sono comandi codificati, i comandi sono istruzioni decodificate.
A onor del vero, si potrebbero costruire macchine che abbiano solo comandi e non istruzioni. Così facendo non sarebbe più necessaria le fase di decodifica, aumentando velocità, economicità ed affidabilità dei calcolatori. Questo però non avviene perchè i comandi occuperebbero troppa memoria (esistono molti comandi, ognuno può avere molti modi di indirizzamento degli operandi). La codifica è una sorta di compressione e per questo in memoria si trovano soltanto comandi codificati - ovvero istruzioni - e non comandi veri e propri.

5.5 Microprogrammazione

Visto il gran numero di istruzioni e indirizzamenti possibili, il decodificatore di istruzioni risulta essere un circuito combinatori estremamente complesso e quindi anche costoso da progettare e costruire.

Talvolta, per risparmiare, esso viene sostituito da una micromacchina, ovvero una macchina di Von Neuman "in miniatura", con micro-PC, micro-CPU e ROM al posto della RAM.

L'istruzione da decodificare arriva al micro-PC che controlla la ROM. Nella ROM, al contrario delle macchine di Von Neumann vere e proprie, ci sono sia istruzioni che comandi. Nel caso che il micro-PC trovi direttamente il comando corrispondente all'istruzione ricevuta, il comando viene subito inviato in output. Se, invece, l'istruzione non ha un comando associato direttamente, significa che le è associato un micro-programma , anch’esso presente nella ROM, che,una volta eseguito dalla micro-CPU, da in output il comando corrispondente all'istruzione.
Ovviamente ci sarà un registro di memorizzazione sull’uscita della micromacchina che accumula i vari pezzi del comando via via ottenuti dall’elaborazione dalla micro-CPU. Solo quando tutta l’istruzione è decodificata, viene abilitata l’uscita ed il comando ottenuto arriva al codificatore di comandi.

L'insieme dei microprogrammi viene detto firmware. Le microistruzioni con cui scrivere questi programmi sono molto poche.

Tutti questi passaggi sono trasparenti al programmatore assembly, tranne per il fatto che introducono un rallentamento nell'esecuzione dei programmi, a fronte, però, di una maggiore economicità.
Questo rallentamento può essere più o meno marcato a seconda del numero di microistruzioni e del numero di comandi direttamente presenti nella ROM. È possibile espandere il set di microistruzioni e comandi semplicemente cambiando la ROM (o utilizzando una EPROM - tipo di memoria di cui non parleremo).

5.6 Generatore di Fase

Perché una macchina di Von Neumann possa fare quello che le è richiesto tramite un’istruzione sono necessarie le seguenti fasi:

Il generatore di fase conosce la fase nella quale ci si trova e la segnala al decodificatore di comandi.
Vedremo che sapere in quale delle tre fasi si trova la macchina, in alcuni casi, si rivela molto utile (ad es. canalizzazione).
Per ora basta sapere che se ci si trova nella prima o nella seconda fase il calcolatore deve fare le stesse operazioni qualsiasi sia l'istruzione; se, però, ci si trova nell'ultima fase, i compiti che il decodificatore di comandi deve svolgere variano da istruzione ad istruzione. Utilizzando una micromacchina, sia nella seconda che nella terza fase i compiti variano da istruzione ad istruzione. Appare evidente, quindi, che serve sapere in quale delle tre fasi il sistema si trova.

5.7 Registri Interni alla CPU

Sono normali registri di memorizzazione lunghi una parola.

Il loro compito è identico a quello delle celle di memoria centrale: memorizzare i dati su cui si sta operando.
Da un punto di vista logico, quindi, risultano superflui, ma la pratica mostra che è molto comodo averli.
Rispetto alla memoria centrale, infatti, essi sono più vicini e meno numerosi. Questo significa che sono più velocemente accedibili e più facilmente indirizzabili della memoria centrale.

Si usa dividere tali registri in due gruppi:

Fra quelli ad uso speciali ci sono:

6. Input \ Output

6.1 Introduzione

Questa unità logica permette al calcolatore di comunicare con l’uomo e con l’ambiente esterno.

La comunicazione avviene tramite opportuni dispositivi detti periferiche.

Nella comunicazione con l’ambiente esterno, tramite opportune periferiche, il calcolatore riceve segnali che misurano grandezze fisiche e manda segnali per modificare tali grandezze.

Nella comunicazione con l’uomo, tramite opportune periferiche, l’operatore può inserire dati ed istruzioni nel calcolatore, mentre il calcolatore può mostrare all’operatore il risultato delle sue elaborazioni.

Si può avere, infine, anche una comunicazione fra calcolatore e calcolatore.

6.2 Realizzazione

Ci sono due metodi per implementare l'unità di I\O.

6.3 Funzionamento

Il problema principale nel colloquio fra periferiche e CPU risiede nella differenza di velocità fra questi componenti.
Per riuscire a farle comunicare, quindi, bisogna usare vari accorgimenti.
Innanzitutto è necessario che le periferiche bidirezionali (periferiche su cui si può leggere e scrivere) abbiano almeno i seguenti tre registri:

La comunicazione consiste nel trasferimento di dati fra memoria centrale o registri interni e registri di dato in ingresso o di dato in uscita della periferica.

Esistono i seguenti tre metodi per realizzare lo scambio di dati:

1: istruzioni del programma che non richiedono I\O

2: inizializzazione:
caricamento in una variabile dell'indirizzo di memoria centrale da cui scrivere i dati trasferiti;
caricamento in una variabile della condizione di fine: o il numero di word da trasferire o la particolare word che appena letta fa terminare il trasferimento;

3: test:
se il bit di pronto in lettura del registro di stato della periferica è basso, ripete questo test finchè non diventa alto, altrimenti continua

4: istruzione che trasferisce la word dal registro di dato in uscita della periferica alla RAM o ad un registro interno alla macchina;

5: test:
se la condizione di fine non è verificata ripete da 3, altrimenti continua

6: parte restante del programma

Riassumendo: la CPU cicla finchè la periferica non è pronta; appena la periferica è pronta, la CPU cicla leggendo un dato alla volta, finchè non li legge tutti.
Rispetto agli altri metodi, il programmato è molto inefficiente perchè tiene occupata la CPU a ciclare per trasferire una word alla volta, pertanto è ormai abbandonato

1: abilitazione di quella periferica a lanciare interruzioni, alzando l'apposito bit del suo registro di stato

2: dichiarazione della posizione di memoria in cui si trova il programma di servizio di quella interruzione, inserendo l'indirizzo nel vettore delle interruzioni

3: istruzioni del programma che non richiedono I\O

4: inizializzazione:
caricamento dell'indirizzo di memoria centrale da cui scrivere i dati trasferiti (qui o nel programma di servizio?)
caricamento della condizione di fine: o il numero di word da trasferire o la particolare word che appena letta fa terminare il trasferimento (qui o nel programma di servizio?)

6: istruzione di lettura da periferiferica:
o il programma ha un'istruzione di attesa - il calcolatore si ferma ad aspettare che la periferiferica sia pronta in lettura senza poter fare niente - o ha un'istruzione di trap verso il sistema operativo - il sistema operativo cede il controllo della CPU ad altri programmi finchè non arriva il segnale di pronto -
quando il bit di pronto in and con il bit di abilitazione delle interruzioni della periferica è alto, la periferica lancia una interruzione e viene attivato il programma di servizio, che trasferisce la word dal registro di dato in uscita della periferica alla RAM o ad un registro interno al calcolatore; ciò si ripete finchè non viene raggiunta la condizione di fine; a questo punto, il programma di servizio ritorna e cancella l'interruzione

7: parte restante del programma

Riassumendo: la CPU aspetta l’interruzione di periferica pronta; il programma di servizio legge un dato e ritorna; questo avviene finchè tutti i dati non sono esauriti.
Il metodo ad interruzioni è utilizzato per le periferiche che non trasferiscono molti dati (ad es. la tastiera)

1: istruzioni del programma che non richiedono I\O

2: caricamento nel registro di indirizzo del controllore di DMA dell'indirizzo di memoria centrale da cui scrivere i dati trasferiti

3: caricamento nel registro di dato del controllore di DMA della condizione di fine: o il numero di word da trasferire o la particolare word che appena letta fa terminare il trasferimento

4: abilitazione di quella periferica a lanciare interruzioni, alzando l'apposito bit del suo registro di stato

5: dichiarazione della posizione di memoria in cui si trova il programma di servizio di quella interruzione, inserendo l'indirizzo nel vettore delle interruzioni

6: istruzione di avvio del trasferimento:
o il programma ha un'istruzione di attesa - il calcolatore si ferma ad aspettare che la periferiferica abbia trasferito tutti i dati, senza poter fare niente - o ha un'istruzione di trap verso il sistema operativo - il sistema operativo cede il controllo della CPU ad altri programmi finchè non arriva l'interruzione di fine trasferimento -
quando l'interruzione di fine trasferimento arriva, o finisce l'attesa o arriva un'istruzione di trap al sistema operativo che decide se far ripartire il programma

7: parte restante del programma

E’ un metodo pensato per liberare la CPU dall'incombenza di gestire lo scambio di dati fra periferiche e calcolatore: la differenza con gli altri due metodi, infatti, sta nell'inviare una interruzione non per ogni word trasferita, ma per ogni blocco di word trasferito; in questo modo la CPU non deve eseguire un'istruzione di move da un indirizzo ad un altro per ogni dato da scambiare.

DMA

Questo è possibile perchè alle periferiche viene permesso di accedere alla memoria centrale direttamente, senza l'intervento della CPU. Ciò avviene tramite un opportuno circuito detto controllore di DMA, unico e a cui si possono collegare più feriferiche e che è a sua volta collegato al bus. Questo componente ha (almeno) tre registri, che vengono usati al posto dei registri delle periferiche: un registro di stato, un registro contatore di indirizzo, un registro contatore di dato. Il programmatore assembly specifica, tramite un opportuno bit del registro di stato, se la periferica deve leggere o scrivere sulla memoria centrale. Specifica quindi nel registro contatore di indirizzo l'indirizzo di partenza della memoria centrale, su cui leggere o scrivere. Nel registro contatore di dato mette il numero delle informazioni da leggere o scrivere, ovvero il numero di celle di memoria da usare. Avvia quindi il trasferimento alzando un opportuno bit nel registro di stato. Eseguite queste poche istruzioni, la CPU non viene più chiamata in causa e può svolgere altri compiti. Durante il trasferimento, il registro contatore di indirizzo viene incrementato ed il registro contatore di dato decrementato. Quando il registro contatore di dato arriva a zero, la periferica segnala la fine del trasferimento alzando un opportuno bit del registro di stato per mandare un'interruzione alla CPU.
Alla luce di quanto esposto sinora, appare evidente che il metodo del DMA non è trasparente al programmatore assembly: egli deve fare in modo che il sistema operativo imposti correttamente il controllore di DMA.
Un'altra importante cosa da notare è che il registro contatore d'indirizzo svolge le veci del program counter della CPU. L'unica differenza è che il PC è un contatore preselezionabile, mentre il registro del controllore di DMA non lo è.
Ci si potrebbe poi chiedere come facia il controllore di DMA a colloquiare con la RAM se in quel momento il BUS è già occupato dalla CPU. Ebbene, esso sfrutta i buchi di tempo in cui la CPU non utilizza il bus. Come già visto, infatti, l'esecuzione di istruzioni da parte della CPU implica tre fasi distinte: caricamento, decodifica o elaborazione della micromacchina ed esecuzione. Durante la decodifica, la CPU sicuramente non usa il BUS. Questo potrebbe accadere anche durante l'esecuzione, dipende dal tipo di istruzione. E' in queste fasi che il controllore di DMA può usare il bus. Il tutto è ovviamente gestito dall'arbitratore del BUS.
Il metodo del DMA viene usato solo per le periferiche che scambiano grandi quantità di dati col calcolatore. Ciò avviene a causa del suo costo sia hardware, che software (impostazione dei suoi tre registri). Inoltre, se le periferiche che utilizzano un controllore di DMA fossero troppe, si avrebbe la saturazione del bus, ovvero gli spazi lasciati liberi dalla CPU non sarebbero sufficienti per accontentare tutte le richieste di trasferimento, ottenendo un'esplosione dei tempi di attesa.

7. ALTRO

7.1 Introduzione

ALU, CU, memoria centrale e I\O sono le unità logicamente essenziali per le macchine di Von Neumann.
Tramite queste, un calcolatore può svolgere tutte le operazioni che normalmente i calcolatori sanno fare.

Spesso, però, a queste unità logiche essenziali, vengono affiancate altre unità, specializzate in vari compiti.
Tali unità non sono logicamente essenziali, dato che i compiti che esse svolgono potrebbero venire svolti anche soltanto da ALU, CU, memoria centrale e I\O via software (ovvero combinando varie istruzioni).
Ad es.: la ALU non sa calcolare direttamente il prodotto di due numeri, ma per fare un prodotto basta fare una serie di somme, cosa che la ALU sa fare. Basta quindi scrivere un programma che faccia ciò. Ad es.: per operare con numeri di lunghezza doppia al normale basta spezzare manualmente questi numeri su due word.
Vengono tuttavia aggiunte perchè velocizzano queste operazioni, dato che sanno eseguirle direttamente, con una sola istruzione - via hardware, come si dice - perchè hanno circuiti in grado di farlo. In questo modo si risparmiano tempo e fatica.

Di queste unità aggiuntive vedremo soltanto l'unità aritmetica.

7.2 AU (Unità Aritmetica)

L'unità aritmetica (altrimenti detta coprocessore aritmetico o unità a virgola mobile) si occupa di moltiplicare e dividere numeri interi, nonchè di operare sui numeri reali rappresentati in virgola mobile e sui numeri in lunghezza doppia (lunghi cioè due word anzichè una).

 

D - DATI ED ISTRUZIONI

1. Introduzione

Le stringhe binarie che i calcolatori usano si possono dividere in due grandi classi, in base a ciò che esse rappresentano:

Le istruzioni dicono al calcolatore cosa fare. I dati sono le entità su cui tali istruzioni possono agire.

Si noti che istruzioni e dati, in una macchina di Von Neumann, convivono nello stesso luogo: la memoria centrale. Ciò rappresenta una debolezza di questi calcolatori, poichè non c'è alcuna protezione fra i due tipi (stringhe di dati possono essere scambiate per istruzioni e viceversa). Sta ai programmatori scrivere programmi sufficientemente corretti da non creare problemi in questo senso. La sintassi dei linguaggi di alto livello (controlli sui tipi, ecc.) serve proprio a questo.

2. Istruzioni

2.1 Introduzione

[da migliorare]Un programma scritto in linguaggio macchina (cioè direttamente comprensibile dal calcolatore) è un insieme di istruzioni binarie. Un programmatore è la persona che scrive tali stringhe. In realtà i programmatori scrivono programmi in linguaggi che usano codici mnemonici al posto di sequenze binarie. Queste sequenze vengono poi tradotte da appositi programmi in stringhe binarie.

2.2 Tipi di Istruzione

Esistono 4 classi di istruzioni:

2.3 Campi delle Istruzioni

Le stringhe binarie rappresentanti istruzioni sono divise idealmente in due campi logici: (esempio fittizio a 32 bit)

16 bit: codice istruzione

16 bit: referenziamento operandi

Il primo campo contiene il codice dell'istruzione: ogni istruzione è identificata univocamente tramite un numero binario di massimo 16 bit (verrà interpretato dal decodificatore di istruzione).
Il secondo campo contiene il referenziamento degli operandi: ogni istruzione agisce su un certo numero di dati (eventualmente zero); la loro posizione nel calcolatore viene indicata in questa parte dell'istruzione (verranno recuperati dal PC tramite preselezione inviata dal codificatore dei comandi).

Il referenziamento degli operandi può essere di due tipi: esplicito ed implicito.
Nel caso di referenziamento esplicito, nel campo "referenziamento degli operandi" viene effettivamente indicato dove si trovano gli operandi (memoria centrale o registri di memorizzazione interni alla CPU) oppure viene messo direttamente il loro valore.
Nel caso di referenziamento implicito, il campo "referenziamento degli operandi" non esiste. In questo caso, infatti, ci si aspetta di trovare gli operandi in posizioni fisse, prestabilite dal progettista della macchina (determinati registri della CU detti accumulatori).

Parlando del referenziamento esplicito, sorge un altro problema: se si decide di usare operandi che si trovano nella memoria centrale, lo spazio di 16 bit non basta neanche per indirizzarne uno (servono 32 bit per ogni indirizzo).
Esistono due soluzioni.
La prima consiste nell'utilizzare istruzioni più lunghe di una parola, dette istruzioni a lunghezza variabile poichè si possono avere istruzioni di 1, 2, 3, ecc. parole; ogni parola in più contiene l'indirizzo di un operando oppure l’operando stesso.
La seconda consiste nell'usare i registri di memorizzazione interni alla CU. Questi, essendo in numero esiguo, sono indirizzabili tramite stringhe molto corte, che riescono quindi a stare nei 16 bit disponibili. In questo caso si possono usare istruzioni a lunghezza fissa.

Sempre riguardo l'indirizzamento esplicito, esistono le seguenti possibilità di referenziamento degli operandi (vengono evidenziati i principali):

16 bit: codice istruzione

16 bit: operando

16 bit: codice istruzione

16 bit: indirizzo

Indirizzo della memoria centrale in cui si trova l'operando.

16 bit: codice istruzione

16 bit: registro

Numero del registro di memorizzazione interno alla CU in cui si trova l'operando.

16 bit: codice istruzione

16 bit: registro

Numero del registro di memorizzazione interno alla CU in cui si trova l'indirizzo della memoria centrale in cui si trova l'operando.

16 bit: codice istruzione

16 bit: indirizzo

Indirizzo della memoria centrale in cui si trova l'indirizzo della memoria centrale in cui si trova l'operando.

16 bit: codice istruzione

16 bit: registro

Numero del registro di memorizzazione interno alla CU in cui si trova l'indirizzo della memoria centrale in cui si trova l'indirizzo della memoria centrale in cui si trova l'operando.

16 bit: codice istruzione

16 bit: offset

Numero che sommato al contenuto del PC, dà l'indirizzo della memoria centrale in cui si trova l'operando (di quanto bisogna spostarsi dalla posizione corrente in memoria centrale).

16 bit: codice istruzione

16 bit: offset

Numero che sommato al contenuto di un opportuno registro di memorizzazione interno alla CU che sia indice (cioè che contenga un indirizzo di memoria centrale), dà l'indirizzo della memoria centrale in cui si trova l'operando (di quanto bisogna spostarsi, non dalla posizione corrente in memoria centrale, ma da una certa posizione memorizzata).

16 bit: codice istruzione

16 bit: registro

Numero del registro di memorizzazione interno alla CU in cui si trova l'indirizzo della memoria centrale in cui si trova l'operando.

16 bit: codice istruzione

16 bit: indirizzo

Indirizzo della memoria centrale in cui si trova l'indirizzo di un registro locale alla CU che contiene l'offset che sommato al PC da l'operando.

16 bit: codice istruzione

16 bit: indirizzo

Indirizzo della memoria centrale in cui si trova l'indirizzo di un registro locale alla CU che contiene l'offset che sommato al PC da l'indirizzo di memoria centrale in cui si trova l'operando.

16 bit: codice istruzione

16 bit:

...

16 bit: codice istruzione

16 bit:

...

16 bit: codice istruzione

16 bit:

...

16 bit: codice istruzione

16 bit:

...

16 bit: codice istruzione

16 bit:

Come il terzo caso, è differito a registro, ma ogni volta che si passa per il registro, il suo contenuto viene incrementato di uno.

16 bit: codice istruzione

16 bit:

Come il terzo caso, è differito a registro, ma ogni volta che si passa per il registro, il suo contenuto viene decrementato di uno.

Utilizzando il referenziamento implicito, le istruzioni sono poco potenti: ne servono molte per fare anche le operazioni più semplici. Si consideri ad esempio una istruzione che somma due interi. Bisogna prima portare i due operandi dalla memoria o dai registri in cui sono ai registri predefiniti; bisogna quindi richiamare l'istruzione ed infine spostare il risultato dal registro in cui viene di default messo, alla memoria o al registro in cui si intende conservarlo.
Le istruzioni che utilizzano l'indirizzamento esplicito, invece, sono più potenti proprio perchè più complesse. Utilizzando ancora l'esempio della somma di due interi, basta una sola istruzione, nella quale vengono contestualmente indicate le posizioni degli operandi e del risultato. Si veda a tal proposito il capitolo sulle macchine CISC e sulle macchine CISC.

Riassumendo, la potenza di una istruzione indica "quante cose riesce a fare quella istruzione tutte in una volta". Il livello di complessità è legato al modo di indirizzamento degli operandi e a tutto ciò che è di contorno all'istruzione. Non dipende, invece, dall’istruzione di persè, che è sempre la stessa (nell’esempio è "somma di interi").

2.4 Fasi

Perchè un'istruzione sia eseguita da una macchina di Von Neumann sono necessarie le seguenti fasi:

Nel caricamento, il PC e la memoria centrale si mettono in comunicazione su richiesta del PC; l'indirizzo che PC punta raggiunge l'ingresso "Indirizzi" della memoria, la quale manda in output la stringa trovata; memoria centrale e decodificatore di istruzione si connettono su richiesta del PC; la stringa trovata in memoria raggiunge il decodificatore di istruzione.
Nella decodifica (riconoscimento), agisce il decodificatore di istruzione oppure la micromacchina, che decodifica il campo "codice istruzione".
Nella esecuzione, agisce il codificatore dei comandi, che manda istruzioni a tutte le unità che servono (ad esempio fa saltare il PC alla locazione di memoria dove si trova un certo operando, ecc.).
Vedremo che sapere in quale delle tre fasi si trova la macchina, in alcuni casi, si rivela molto utile (ad es. canalizzazione).

3. Dati

3.1 Introduzione

Nei calcolatori tutti i tipi di dato vengono rappresentati tramite stringhe di bit di lunghezza fissa.

Ad ognuna di queste stringhe è associato un significato, che è proprio il dato che essa rappresenta.

3.2 Dati Numerici

Il significato che più di frequente si usa è quello numerico.
In questo caso, ogni stringa binaria rappresenta un numero.

Per rappresentare i numeri naturali si usa la rappresentazione binaria standard.
Per rappresentare i numeri interi si usa la rappresentazione in complemento a 2.
Per rappresentare i numeri reali si usa la rappresentazione in virgola mobile.

3.2.1 Rappresentazione di Naturali

3.2.2 Operazioni su Naturali

3.2.3 Rappresentazione di Interi

Viene utilizzata la rappresentazione in complemento alla base (che nel caso binario è, appunto, 2). Questa è identica alla rappresentazione dei numeri naturali, tranne per il fatto che il bit più significativo viene interpretato in due modi: come indicatore del segno (1 per il meno, 0 per il più) e come cifra vera e propria. In questa rappresentazione, infatti, esiste la proprietà che se un numero è negativo inizia con uno e se è positivo inizia con zero.
Lo zero, per convenzione, viene rappresentato con una sequenza di zero ed è dunque considerato positivo (nonchè pari).

Esempio (per stringhe di 4 bit):

Stringa binaria

Interpretazione binaria standard

Interpretazione complemento a due

0 0 0 0
0 0 0 1
0 0 1 0
0 0 1 1

0 1 0 0
0 1 0 1
0 1 1 0
0 1 1 1

1 0 0 0
1 0 0 1
1 0 1 0
1 0 1 1

1 1 0 0
1 1 0 1
1 1 1 0
1 1 1 1

0
1
2
3

4
5
6
7

8
9
10
11

12
13
14
15

0
1
2
3

4
5
6
7

-8 (non usato)
-7
-6
-5

-4
-3
-2
-1

Il -8 non viene usato perchè se si calcolasse il suo complemento si otterrebbe zero invece che +8 (lo zero "ruba" uno spazio ai positivi). Si preferisce, dunque, non usarlo.

3.2.4 Operazioni su Interi

Le operazioni si fanno come per i numeri naturali, senza considerare il MSB come bit di segno.

Una proprietà di questa rappresentazione è che per ottenere l'opposto di un numero basta complementarne tutti i bit e sommare uno alla cifra meno significativa.
Questa caratteristica è molto utile perchè permette di usare un circuito addizionatore sia per sommare che per sottrarre due numeri. Per sottrarli, infatti, basta sommare al primo l'opposto del secondo e l'opposto del secondo si ottiene con un invertitore (porte not) ed un sommatore.

3.2.5 Rappresentazione di Reali

Viene utilizzata la rappresentazione in virgola mobile. Ogni numero reale r viene espresso nella forma seguente:

r = s * m * be

dove

s rappresenta il bit di segno del numero
m rappresenta la mantissa, presa senza segno (visto che il segno è considerato nel bit di segno) e normalizzata a zero (ovvero deve essere del tipo 0,parte_decimale)
b rappresenta la base del sistema di numerazione scelto: 2 per il sistema binario, 10 per il decimale, ecc.
e rappresenta l'esponente espresso in complemento alla base.

Nella cella di memoria che dovrà contenere un numero reale così rappresentato vengono memorizzate soltanto le seguenti informazioni:

bit di segno

parte frazionaria della mantissa

esponente

Ognuna di queste 3 informazioni occupa un ben determinato numero di bit, che ora non ricordo, e tutte insieme sono sufficienti per ricostruire il numero di partenza.

3.2.6 Operazioni su Reali

Addizioni e sottrazioni: Moltiplicazioni: Divisioni:

3.3 Verso di Memorizzazione

Analizzando la logica di funzionamento di un calcolatore, appare evidente che esistono due metodi per tenere in memoria (ad es. in una word) una stringa binaria rappresentante un dato:

MSB

LSB

LSB

MSB

Il metodo usato viene stabilito dal costruttore e, ovviamente, condiziona tutta l’architettura del calcolatore. Nelle macchine RISC si usa il metodo big endian, nelle CISC il metodo little endian.

3.4 Allineamento della memoria

La memoria centrale è organizzata in word. Questo significa che gli indirizzi di memoria puntano ognuno all’inzio di una serie di celle di memoria in grado di contenere una word di bit.
Talvolta, però, può essere più comodo utilizzare tipi di dato più corti di una word.
Se ad esempio la word è di 4 byte, può essere comodo lavorare su un tipo di dato lungo un solo byte, pensando la memoria organizzata in byte.
Pensare la memoria in byte, però, non significa che al posto delle parole si indirizzano i singoli byte - ciò non è possibile se non esplicitamente previsto in fase di progettazione -, ma significa considerare solo il primo byte di ogni parola (oppure l’ultimo, a seconda della scelta progettuale). Tutto il resto della parola può contenere una sequenza di zero oppure l'estensione del segno se vi era memorizzato un numero in complemento a due.

Tutto ciò, quindi, risulta essere necessario perché solo le parole sono direttamente indirizzabili.

 

E - INTERRUZIONI ED ECCEZIONI

1. Introduzione

Per un calcolatore lo scorrere del tempo è legato al cambiamento di stato del segnale di clock. Quando il segnale emesso dal circuito temporizzatore passa da alto a basso o viceversa, per il computer è passata un'unità di tempo.
Alla luce di ciò, se si interrompe il clock, è possibile fermare il "tempo macchina", così che il calcolatore non si accorga più del passare del tempo.

Partendo da questa idea è nato il concetto di interruzione ed eccezione. Al presentarsi di un apposito segnale che arriva alla CPU dall'hardware o dal software, il normale flusso del programma in esecuzione viene interrotto.
Sarà possibile interrompere il programma in esecuzione "non facendo più arrivare il segnale di clock a quel programma", ovvero interrompendo lo scandire delle sue istruzioni da parte del PC. Questo, però, non è sufficiente: soltanto salvando nello stack tutti i registri utilizzati dal programma (sia quelli interni alla CPU che le celle di memoria della RAM), nonchè il valore puntato dal program counter e il registro di stato della macchina, qualsiasi cosa succeda, quando si ritorna ad eseguire il programma sospeso, esso non si accorgerà di essere stato interrotto.
Il salvataggio e ripristino di tutti questi registri viene detto rispettivamente salvataggio e ripristino dello stato volatile della macchina.
Viene utilizzato lo stack proprio per la sua filosofia di accesso LIFO, ideale per questi casi di salvataggio e successivo ripristino, nonchè per l’annidamento di più eccezioni e/o interruzioni.
Ci si potrebbe chiedere il motivo della necessità di salvare anche il registro di stato della macchina. Questo è necessario perché, mentre il programma viene sospeso, potrebbe succedere qualsiasi cosa. Ad esempio la macchina potrebbe essere spenta ed il contenuto del registro di stato andrebbe perso. Potrebbe anche succedere che i flag logici vengano modificati, eccetera.

2. Funzionamento

Un'interruzione o eccezione viene richiesta alla macchina tramite un opportuno segnale che arriva alla Control Unit.
Questo segnale può avere tre provenienze, generando tre tipi di interruzioni

Questa suddivisione è puramente concettuale, dato che la macchina tratta tutti e tre i tipi di interruzione allo stesso modo.

Ricapitolando, le interruzioni esterne ed interne vengono generate dalla macchina stessa, mentre quelle software, dette eccezioni, vengono generate dal programmatore assembly.

Alla luce di quanto sinora esposto, appare evidente che le interruzioni (e non le eccezioni) sono eventi asincroni, ovvero possono accadere in qualsiasi momento, senza possibilità di prevedere quando.

Di seguito, con il termine interruzione verranno indicate sia le interruzioni vere e proprie che le eccezioni.

Quando la CPU riceve una richiesta di interruzione, sia essa interna, esterna o software, esegue i seguenti passi:

Il programmatore assembly che scrive i programmi di servizio come prima cosa deve provvedere a salvare nello stack tutti i registri che modificherà. Di conseguenza, al termine del programma di servizio dovrà ripristinare tali registri. Questi due passaggi sono assolutamente necessari perchè il programma interrotto non si accorga di nulla.

Il salto al sottoprogramma di servizio è per molti versi simile ad un salto a subroutine. Esistono tuttavia alcune differenze, che comportano l'esistenza di istruzioni diverse per il salto ed il ritorno da subroutine e per il salto ed il ritorno da sottoprogrammi di servizio. La prima differenza è che nel salto a sottoprogramma di servizio viene salvato, oltre al contenuto del PC, anche il contenuto del registro di stato della CPU, cosa che non accade nel salto a sottoprocedura. Un'altra differenza sta nel fatto che il salto a sottoprogramma di servizio ha un tramite: il vettore delle interruzioni.

3. Utilizzi

3.1 Multiprogrammazione

Per multiprogrammazione si intende la possibilità di far girare più programmi contemporaneamente su una stessa macchina. Si badi bene al significato del termine contemporaneamente, che non indica la contemporaneità di esecuzione delle istruzioni dei due programmi - cosa questa che contrasterebbe con la definizione di macchina di Von Newman -, ma indica che più programmi si "contendono" l’uso della CPU, interrompendosi a vicenda (le macchine di Von Neumann hanno un solo PC, quindi per eseguire più programmi contemporaneamente non possono far altro che "zompettare" da uno all'altro).

E’ possibile quindi eseguire un'altro programma mentre quello precedentemente in esecuzione rimane in sospeso, "non facendo più arrivare il segnale di clock a quel programma", ovvero interrompendo lo scandire delle sue istruzioni da parte del PC, che passerà a scandire le istruzioni del secondo programma. Questo, però, non è sufficiente: soltanto salvando nello stack lo stato volatile della macchina, quando si ritorna ad eseguire il programma sospeso, questo non si accorgerà di essere stato interrotto.

La multiprogrammazione ha rivoluzionato il modo di utilizzare i calcolatori. In sua assenza, l’unico modo per eseguire un programma in contemporanea ad un altro sarebbe quello di linkarli (si veda il capitolo seguente per una trattazione del linker, strumento che comunque dovrebbe essere già noto sia dal corso di Programmazione, che dal corso di Laboratorio di Architetture).
Si pensi che ai nostri giorni ci sono sempre almeno due programmi in esecuzione contemporaneamente in un calcolatore: il sistema operativo e il programma che si sta utilizzando. Senza la multiprogrammazione, i sistemi operativi non potrebbero esistere, o meglio, potrebbero esistere, ma ogni volta che si avesse la necessità di aggiungere un nuovo programma, bisognerebbe linkarlo col resto dei programmi, ricompilando il tutto.

3.2 Altro

In genere, il colloquio fra CPU e periferiche esterne al calcolatore avviene tramite interruzioni. Basti pensare alla tastiera: ogni volta che si batte un carattere, viene generata un'interruzione in attesa dell'immissione del carattere successivo. In questo modo, fra l'immissione di un carrattere e quello successivo, il calcolatore può eseguire altri programmi.

Le interruzioni interne vengono quasi sempre utilizzate per gestire errori. Errori tipici che generano interruzioni interne sono: istruzione inesistente, cella di memoria inesistente, violazione dello stack (stack overflow), errori aritmetici, ecc.
Ciò non toglie che anche le interruzioni esterne e software possano essere utilizzate per gestire errori.
A tal proposito, un secondo tipo di classificazione delle interruzioni è il seguente:

La gestione degli errori segnalati tramite interruzioni spetta poi al sottoprogramma di servizio.

Le interruzioni verranno trattate approfonditamente anche nel corso di "Sistemi Operativi". Si vedrà, ad esempio, che per evitare che i programmi che non generano mai interruzioni monopolizzino l'uso della CPU finchè non terminano, il S.O. genera interruzioni fasulle a cadenza fissa (sistemi operativi a time sharing che usano il real-time clock).

Interruzioni ed Eccezioni nel MIPS

[Lezione di Laboratorio di Architetture; ho deciso di trattare questo argomento, vista la sua difficoltà]

Interruzioni ed eccezioni sono eventi che alterano il normale svolgimento del programma.
Quando si verificano, il programma viene interrotto e il controllo passa ad un programma di servizio che decide cosa fare.
Le eccezioni sono causate dal programma utente in esecuzione ed in genere segnalano che l'istruzione corrente ha creato problemi al processore mentre tentava di eseguirla. Le interruzioni sono dovute, al contrario, a cause esterne ed in genere vengono utilizzate per l'I\O su periferiche.

L'architettura del MIPS prevede un apposito coprocessore - detto coprocessore zero - per gestire le interruzioni\eccezioni (reagisce ai segnali di interruzione\eccezione, memorizza nei suoi registri se si tratta di una interruzione o di una eccezione, memorizza la periferica che l'ha generata nel caso di interruzioni, ecc.).
SPIM simula soltanto 4 registri di questo coprocessore:

  • status ($12): abilita o meno il coprocessore a servire le interruzioni\eccezioni;
    il bit meno significativo è detto interrupt enable e se è alzato abilita le interruzioni;
    il bit successivo è detto kernel\user e stabilisce se l'interruzione è avvenuta in modalità utente o sistema operativo; esistono dunque due livelli di priorità: kernel ad alta priorità e user a bassa priorità. Quando la CPU sta servendo una interruzione kernel disabilita automaticamente tutte le interruzioni; questi due bit vengono ripeturi 3 volte, tenendo una traccia storica delle interruzioni, shiftata via via a sinistra di due posizioni (LIFO).
    nei bit dal 10 al 15 si trova la interrupt mask: ogni bit corrisponde all'abilitazione o meno delle interruzioni di una certa periferica; tastiera e schermo corrispondono ai due bit meno significativi (forse).
  • cause ($13): identifica il tipo di interruzione e maschera le eccezioni;
    i bit dal decimo al quindicesimo contengono le pending interrupt ovvero quelle interruzioni che si sono verificate mentre erano disabilitate le interruzioni e che quindi non sono state ancora servite (maschera le interruzioni);
    i bit dal 2 al 5 contengono l'exception code: un numero che identifica il tipo di interruzione\eccezione che si è verificata
    Ad esempio:

0->interruzione: interruzione da periferica
4->eccezione: accesso ad un indirizzo di memoria inesistente con load
5->eccezione: accesso ad un indirizzo di memoria inesistente con store
6,7->eccezione: problemi col bus
8->eccezione: syscall
12->eccezione: overflow

In genere: se è zero è una interruzione, altrimenti è una eccezione. E' necessario distinguere i due casi, poichè vengono trattati in modo leggermente diverso fra loro.

  • epc ($14): (exception program counter) contiene l'indirizzo di memoria dell'istruzione che ha causato l'eccezione\durante la quale si è verificata l'interruzione
  • baddvaddr ($8): (bad virtual address) se l'eccezione è stata causata da una istruzione che ha tentato di accedere ad un indirizzo di memoria non esistente, questo registro contiene tale indirizzo.

A questi registri si accede tramite le istruzioni:

  • mfc0 rdest,rsrc: (move from coprocessor 0) copia il contenuto del registro rsrc del coprocessore 0 al registro rdest del processore principale
  • mtc0 rsrc,rdest: (move to coprocessor 0) viceversa
  • lwc0 rdest,address: analogo a lw
  • swc0 rsrc,address: analogo a sw

L'istruzione rfe (return from exception) ripristina il contenuto registro status del coprocessore zero al termine della gestione di una interruzione\eccezione.
Le direttive .ktext e .kdata sono analoghe a .text e .data, ma agiscono nell'area di memoria che finora abbiamo definito riservata al kernel e quindi servono per scrivere il programma di servizio.

Il programma di servizio per interruzioni ed eccezioni è unico qualsiasi tipo esse siano (interruzioni ed eccezioni non sono vettorizzate: il programma in esecuzione salta allo stesso entry point) ed è contenuto in un opportuno file.
In SPIM il file è "trap.handler" e gestisce solo le eccezioni.
In realtà, questo file svolge le veci di sistema operativo della macchina MIPS, gestendo eccezioni, caricando il programma che si vuole eseguire e chiudendolo. Per questo, quando viene lanciato, SPIM carica "trap.handler" nella zona di memoria a partire dall'indirizzo 0x80000080 che finora abbiamo detto essere riservata al kernel.

Per gestire le eccezioni (non sono sicuro che sia giusto):

  • viene interrotta l'esecuzione dell'istruzione che ha causato l'eccezione
  • si passa a modo sistema operativo e il controllo va al programma di servizio nel quale bisogna:
    • salvare in EPC il conteuto del PC più quattro (è necessario tornare all'istruzione successiva a quella che ha causato l'eccezione, altrimenti si avrebbe di nuovo l'eccezione)
    • salvare in memoria centrale il registro di stato del coprocessore zero; nota: non si può usare lo stack perchè l'eccezione potrebbe essere stata causata proprio da un'errore sullo stack
    • caricare nei bit dal 2 al 5 del registro cause del coprocessore zero il codice dell'eccezione verificatasi
    • caricare nel PC della CPU l'indirizzo di "trap.handler"
    • servire l'eccezione
    • tornare al programma che era in esecuzione: rfe...i registri $k1 e $k0 della CPU...
      oppure, in caso di errore irreversibile, terminare l'esecuzione del programma

Per gestire le interruzioni:

  • viene interrotta l'esecuzione dell'istruzione che ha causato l'eccezione
  • si passa a modo sistema operativo e il controllo va al programma di servizio nel quale bisogna:
    • caricare in EPC il contenuto del PC (non è necessario tornare all'istruzione successiva a quella durante la quale si è verificata l'interruzione - ATTENZIONE: per un errore in trap.handler, anche in questo caso viene sommato 4)
    • salvare in memoria centrale il registro di stato del coprocessore zero
    • disattivare tutte le interruzioni: mettere a zero il bit del registro status
    • caricare nei bit dal 2 al 5 del registro cause del coprocessore zero il codice dell'interruzione verificatasi
    • caricare nella maschera del registro cause gli opportuni valori
    • caricare nel PC della CPU l'indirizzo di "trap.handler"
    • servire l'interruzione
    • tornare al programma che era in esecuzione: rfe...i registri $k1 e $k0 della CPU...

SPIM simula due periferiche che utilizzano l'I\O con interruzioni: tastiera (input) e schermo (output).
Queste funzionano tramite una estenzione all'esterno della memoria centrale (ai loro registri si accede tramite indirizzi di memoria centrale). ATTENZIONE: perchè ciò avvenga bisogna attivare l'opzione "mapped I\O" del programma.
La tastiera lancia una interruzione subito dopo che è stato premuto un tasto.
I registri della tastiera che vengono simulati sono:

  • reciver control (0xffff0000):
    il bit meno significativo è detto ready flag e, se è zero, indica che è stato digitato un carattere e che questo non è ancora stato letto; durante questa fase, il carattere da leggere si trova nel registro reciver data;
    il bit successivo indica se questa periferica può lanciare o meno interruzioni: non basta abilitare le interruzioni nel coporcessore zero, ma occorre abilitarle anche nella periferica; ATTENZIONE: una volta deciso che alla tastiera si accede tramite interruzioni (alzando questo bit), non vi si può più accedere con la syscall
  • reciver data (0xffff0004):
    il byte meno significativo contiene il carattere da leggere; appena viene letto, il ready flag del registro control viene alzato; ATTENZIONE: SPIM simula anche i ritardi nel trsferimento di dati tastiera-calcolatore

Il monitor lancia una interruzione quando l'operazione di scrittura su schermo è terminata.
I registri dello schermo che vengono simulati sono:

  • transmitter control (0xffff0008):
    il bit meno significativo è detto ready flag e, se è basso, indica che l'operazione di stampa a video è ancora in corso; durante questa fase, il carattere da scrivere si trova nel registro transmitter data; quando è uno, la periferica è pronta ad accettare un nuovo carattere;
    il bit successivo indica se questa periferica può lanciare o meno interruzioni: non basta abilitare le interruzioni nel coporcessore zero, ma occorre abilitarle anche nella periferica; ATTENZIONE: una volta deciso che allo schermo si accede tramite interruzioni (alzando questo bit), non vi si può più accedere con la syscall
  • transmitter data (0xffff000c):
    il byte meno significativo contiene il carattere da stampare; appena viene letto, il ready flag del registro control viene alzato; ATTENZIONE: SPIM simula anche i ritardi nel trasferimento di dati calcolatore-schermo

 

F - SOFTWARE

1. Assembly

1.0 Premessa

Le cose che ho scritto di seguito sono scritte molto meglio nel materiale del corso "Laboratorio di Architetture I", disponibile sul sito http://twiki.dsi.uniroma1.it/twiki/view (Twiki).

1.1 Il Linguaggio

L'assembly è IL linguaggio di programmazione a basso livello (più vicino alla macchina).
Viene "costruito" sull'architettura della macchina e per questo ne sfrutta tutte le caratteristiche.

Questo è il motivo per cui viene studiato nel corso di Architettura degli Elaboratori e questo è il motivo per cui, in questo corso, ogni volta che si parla di "programmatore", si intende "programmatore assembly", ovvero che scrive in assembly i suoi programmi.

L’assembly è composto da:

1.2 Funzionamento

Quando un file o modulo sorgente scritto in assembly viene tradotto in modulo oggetto, l'assemblatore trasforma il codice mnemonico in stringhe binarie e risolve soltanto i salti interni al modulo.
Il modulo oggetto sarebbe già perfettamente eseguibile se non ci fossero salti e riferimenti irrisolti ad altri moduli oggetto.

Un programma di nome linker si occupa di fondere in modo corretto i moduli oggetto che servono. I moduli oggetto fusi, possono derivare anche da altri linguaggi di programmazione, oltre all'assembly. Viene creato così il modulo eseguibile.

In particolare, nei moduli oggetto: gli indirizzi assoluti vengono fatti partire da zero; i salti ed i riferimenti ad etichette esterne non sono risolti.

1.3 L'importanza delle interruzioni software

Se non esistessero, per far funzionare più programmi contemporaneamente bisognerebbe linkarli. Le istruzioni di trap, in fondo, eseguono una specie di link tramite gli indirizzi assoluti del vettore delle interruzioni: la stessa cosa che fa il linker quando cambia gli indirizzi assoluti dei moduli che unisce.

Se non esistessero, più programmi che usano una stessa libreria dovrebbero essere linkati ognuno alla sua copia della libreria. Tramite trap, invece, viene caricata una sola copia della libreria a cui i vari programmi accedono a turni.

Se non esistessero, non si potrebbe avere la struttura di sistema operativo che si ha attualmente: un nucleo gestisce il colloquio tra applicazioni e periferiche interne ed esterne. Questi colloqui, infatti, nel caso nucleo-applicazioni vengono gestiti tramite trap.

2. Software di base

Il software che deve essere fornito dal produttore della macchina.
Senza questi programmi, la macchina sarebbe inutilizzabile.

Ciò che assolutamente non può mancare nel software di base:

Per avere una macchina efficiente, sarebbe opportuno scrivere almeno il software di base in assembly.

 

G - ACCORGIMENTI E TRUCCHI

1. Gerarchia di memoria

1.1 Introduzione

La velocità dei circuiti elettronici dipende dalla velocità di commutazione delle loro porte logiche. Purtroppo, maggiore è la velocità di una porta, più elevato è il suo prezzo.
La memoria centrale è composta da moltissime porte, almeno 4 per ogni bit che può contenere. La CPU è composta da molte meno porte. Questo comporta che la CPU possa essere costruita con porte più veloci di quelle della memoria centrale. Avere la RAM più lenta, però, rallenta di conseguenza la CPU quando questa ha bisogno di recuperare stringhe dalla memoria (molto spesso), vanificando la sua maggiore velocità.
Per evitare tutto ciò è stata introdotta la gerarchia di memoria, un accorgimento esclusivamente pratico.

1.2 La memoria cache

Fra CPU e RAM viene interposta una memoria costruita con la stessa tecnologia della CPU, detta cache memory o memoria nascosta. Essendo realizzata con porte costose (le stesse della CPU), deve essere in quantità molto minore rispetto alla RAM, per limitare i costi; ciononostante, essa può contenere centinaia di word.
La CPU non colloquia direttamente con la RAM, ma con la cache. Quando la control unit richiede un nuovo indirizzo alla cache, se questa ha la stringa relativa a quell'indirizzo, la manda alla CPU direttamente, altrimenti carica dalla memoria centrale - passando attraverso il bus - non solo quella stringa,

Cache

ma tutto un blocco di stringhe limitrofe. Questo trasferimento da memoria centrale a cache avviene in modo parallelo: in una sola volta vengono trasferite più stringhe. Tramite questo accorgimento, la gerarchia di memoria riesce ad essere molto efficiente e permette alla CPU di lavorare quasi sempre alla sua massima velocità, colloquiando solo con la cache e minimizzando gli accessi alla memoria centrale.

Perchè tutto funzioni correttamente, è necessario che la cache lavori con una logica diversa da quella della memoria centrale. La cache è, infatti, una memoria di tipo associativo.
Questi tipi di memoria ricevono una maschera (una parte) del contenuto che si cerca e restituiscono tutto il contenuto associato a quella maschera. In particolare, nella cache ogni registro non contiene soltanto il dato vero e proprio, ma anche il suo indirizzo in memoria centrale (che è la maschera). Alla cache arriva una parte della stringa cercata - l'indirizzo - ed essa restituisce tutto il dato - indirizzo + dato vero e proprio.
Ciò è necessario perchè permette di memorizzare dati in qualsiasi ordine e non sequenzialmente (dopo il dato di indirizzo 0000, nella RAM ci deve essere il dato di indirizzo 0001, mentre nella cache ci può essere qualsiasi dato), fattore, questo, che risulta indispensabile proprio per come funziona la cache.
Ovviamente, perché ogni registro della cache contenga indirizzo + dato, è necessario utilizzare registri più capienti di quelli della RAM, aumentando ulteriormente il prezzo.

Ultime osservazioni:
questo meccanismo gerarchico risulta trasparente al programmatore assembly;
programmi con molte istruzioni di salto e in cui tali salti sono particolarmente ampi sono poco efficienti perchè richiedono dati non contenuti nella cache; per questo alcuni costruttori forniscono istruzioni assembly di salto con un offset limitato;
in questo corso non si tratteranno i criteri per la sostituzione dei dati all'interno della cache.

1.3 Livelli di cache

Ci possono essere diversi livelli di cache, via via più lenti, ma anche più capienti e meno costosi. In genere, la cache di primo livello (L1) si trova integrata nel chip della CPU (il core), la cache di secondo livello (L2) si trova sulla scheda della CPU ed una eventuale cache di terzo livello (L3) viene posta nei pressi della CPU.

2. Canalizzazione

2.1 Introduzione

Come visto in precedenza, le macchine di Von Neumann hanno bisogno di tre fasi per arrivare a fare quello che è richiesto da una istruzione. Queste fasi sono: caricamento, decodifica, esecuzione.

Senza

Ognuna di esse richiede un certo tempo: il caricamento richiede un tempo Δt1 fisso, la decodifica richiede un tempo Δt2 fisso se la macchina non è microprogrammata o variabile altrimenti, l'esecuzione richiede un tempo Δt3 variabile.
Ogni volta che una istruzione deve essere eseguita, quindi, occorre un tempo Δt=Δt
1+Δt2+Δt3.
L'idea alla base della canalizzazione o pipelining è di far eseguire queste fasi contemporaneamente.

2.2 Applicazione

Con

Progettando il calcolatore in modo tale che le varie fasi non interferiscano l'una con l'altra, è possibile iniziare il caricamento dell'istruzione successiva mentre si stà ancora decodificando l'istruzione corrente.
Ciò è possibile progettando macchine in cui le tre fasi richiedano lo stesso tempo, altrimenti i tempi non si incastrerebbero bene [si vedano le macchine RISC].
Grazie alla canalizzazione, al tendere ad infinito delle istruzioni eseguite, si riduce il tempo di un terzo (tre volte più veloce).

Ultime osservazioni:
in realtà il tempo diminuisce di uno fratto il numero di fasi, quindi di 1\3 nel nostro caso; esistono tuttavia calcolatori progettati per avere molte più fasi, così da ottenere una riduzione più marcata di tempo (computer vettoriali, studiati ad Architetture III);
il meccanismo di pipelining è trasparente al programmatore assembly;
quando si hanno istruzioni di salto il metodo perde la sua efficacia perchè l'istruzione successiva già acquisita non serve più; questo problema si avverte ancora di più sui calcolatori con molte fasi (bisogna svuotare la pipeline di tutte le istruzioni che ci si stanno trattando in anticipo); per ovviare a ciò sono stati introdotti algoritmi di branch prediction, ovvero di previsione dei salti, in grado di aumentare di molto l’efficienza del pipelining.

3. Memoria Virtuale

3.1 Introduzione

Può capitare di dover eseguire un programma più grande della memoria centrale disponibile. Quando ciò avviene, il programma non può essere caricato nella RAM e quindi non può essere eseguito. In realtà esiste un metodo per risolvere tale problema: la memoria virtuale.

L'idea di base è la seguente: innanzitutto serve una memoria diversa dalla RAM per contenere quella parte di programma che non vi entra - in genere viene utilizzata una periferica, come ad esempio una unità disco; quando il programma deve essere eseguito, si carica nella RAM solo la parte che entra, mantenendo il resto sulla periferica.

Questo principio deve essere implementato in due modi diversi, a seconda che la memoria indirizzabile sia minore o maggiore della memoria fisica. Come già visto, infatti, per indirizzare la memoria si usano stringhe numeriche di n bit, dove in genere n coincide con la dimensione di una word. In questo modo si possono indirizzare 2n celle. Esistono tre eventualità: la memoria fisica ha N=2n celle; la memoria fisica ha N>2n celle (più celle di quante se ne possono indirizzare e quindi è divisa in pagine); la memoria fisica ha N<2n celle (meno celle di quante se ne possono indirizzare). Tenendo presente che il primo caso si può trattare sia come il secondo che come il terzo, rimane da vedere come realizzare una memoria virtuale negli ultimi due casi.

3.2 Overlay

Si supponga che N>2n .

In questo caso, il programma più grande della memoria deve essere spezzato in un main che richiami subroutines.

Subroutines

In memoria viene caricato un ramo alla volta, ad esempio il ramo Main-S1-S2-S4.

Finchè si lavora su quel ramo non si hanno problemi. Se però da quel ramo bisogna passare a S5, per arrivarci occorre togliere dalla RAM S4 e metterci S5. Se invece bisogna andare ad S7, è necessario togliere S4 ed S2 per mettere S3 ed S7. Per andare infine ad S15, sarebbe stato necessario togliere S4, S2, S1 per mettere S12, S13 ed S15.
In questo caso, con togliere e mettere si possono intendere due azioni distinte: togliere e mettere in un'altra pagina o, se tutte le pagine sono occupate, togliere e mettere sulla periferica utilizzata.

Perchè tutto funzioni occorre che:

Per questo, la memoria virtuale ad overlay (che significa sovrapposizione) permette di eseguire programmi lunghi a piacere ma non è trasparente al programmatore assembly, perchè questo deve scrivere un opportuno linker e deve spezzare opportunamente i programmi in subroutines.

3.2 Swapping

Si supponga che N<2n.
In questo caso il programmatore assembly non si preoccupa che N<2n, ma sfrutta tutti i 2n indirizzi possibili, anche se questi non corrispondono a nessuna cella. Questi indirizzi scritti dal programmatore assembly prendono nome di indirizzi virtuali.

Si supponga, ad esempio, di trovarsi nel caso in cui un programma di 2MB debba essere caricato in una RAM da 1 MB per essere eseguito.
Come prima cosa, la macchina carica quel che può: il primo MB.
I problemi nascono quando il programmatore effettua un salto ad un'istruzione presente nel secondo MB, oppure quando si arriva all'ultima istruzione del primo MB. Quando ciò avviene, si ha una interruzione interna perchè ci si è riferiti ad un indirizzo inesistente. Si ha quindi un salto al programma di servizio che toglie il MB già caricato, salvandolo su disco, e carica il rimanente MB al suo posto.

Ciò comporta due problemi. Il primo è che gli indirizzi del secondo MB risultano tutti sfasati di un MB. Il secondo è che scaricate tutto quello che c'era in memoria e caricare tutto quello che non entrava è molto inefficiente.
Riguardo alla prima questione, basta una minima modifica harware: bisogna inserire un registro di offset che agisca sul P.C. .
Si ha quindi che:

indirizzo virtuale =
indirizzo fisico (valore del P.C.) +
offset (numero nel registro di offset)

L'offset è di 0 MB quando ci si trova nel primo MB ed è di 1 MB quando viene caricato il secondo pezzo.
Riguardo alla seconda questione, in realtà non viene scaricato e poi caricato tutto il contenuto della RAM, ma "pezzi" di RAM detti pagine. Si dice quindi che la memoria è paginata e l'operazione che carica e scarica pagine fra disco e memoria centrale è detta swapping. ATTENZIONE ! Queste pagine non hanno niente a che vedere con le pagine di memoria sinora trattate: dire che una memoria ha una struttura a pagine è completamente diverso dal dire che è paginata; sono due concetti differenti ed indipendenti l'uno dall'altro.

La tecnica dello swapping non è trasparente al programmatore assembly, perchè questo deve scrivere un opportuno programma di servizio dell’interruzione.

 

H - MACCHINE CISC E MACCHINE RISC

1. Caratteristiche

RISC: reduced instruction set computer.
CISC: complex instruction set computer.
Le macchine RISC hanno istruzioni più semplici, cioè meno potenti.
Le macchine CISC hanno istruzioni più complesse, cioè più potenti.

La potenza di una istruzione indica "quante cose riesce a fare quella istruzione tutte in una volta".
La complessità ovvero potenza di una istruzione è legata al modo di indirizzamento degli operandi e più in generale a tutto ciò che è di contorno all'istruzione.

Utilizzando l'indirizzamento implicito, le istruzioni sono poco potenti: ne servono molte per fare anche le operazioni più semplici.
Una istruzione di somma, ad esempio, è sempre una istruzione che attiva l'opportuno circuito della CPU, ma se ci si riferisce agli operandi implicitamente, allora è meno potente e più semplice, se al contrario gli operandi sono indicati espicitamente, è più potente e complessa. Nel caso dell'istruzione semplice, bisogna prima portare i due operandi dalla memoria o dai registri in cui sono ai registri predefiniti; bisogna quindi chiamare l'istruzione; bisogna infine spostare il risultato dal registro in cui viene di default messo, alla memoria o al registro in cui si intende conservarlo. Per la somma con indirizzamento esplicito, invece, basta una sola istruzione, nella quale vengono contestualmente indicate le posizioni degli operandi e del risultato.

Le istruzioni delle macchine RISC, avendo una carenza di modi di indirizzamento proprio perché sono poco potenti, operano direttamente sui registri della CPU e non sulla memoria centrale. Vengono a tal proposito fornite solo due istruzioni che accedono alla RAM: load e store. Con queste si caricano le celle di memoria su cui si vuole operare nei registri della CPU e viceversa. L’istruzione move, al contrario, può agire solo sui registri della CPU.

Un'altro degli elementi di contorno che determina la maggiore o minore complessità è il formato delle istruzioni: nelle macchine CISC può essere variabile (come già visto, questo significa che la lunghezza delle istruzione può variare di multipli di word), nelle RISC è fisso ed in genere pari ad una word.

Ovviamente esistono macchine intermedie fra RISC e CISC.

I motivi della distinzione fra macchine RISC e macchine CISC sono storici:

Vantaggi delle macchine RISC: non utilizzano la microprogrammazione (utilizzando istruzioni semplici, il decodificatore non è troppo complesso); permettono una più semplice ed efficace canalizzazione (le tre fasi di caricamento, decodifica, esecuzione richiedono lo stesso tempo, visto anche il fatto che la microprogrammazione non è necessaria e perchè è più facile fare in modo che le tre fasi non interferiscano fra loro); sono più facili da realizzare; spesso si tende a non utilizzare molte delle istruzioni complesse delle macchine CISC; sono più veloci; permettono una portabilità del codice più agevole (essendo più semplici delle CISC, permettono ai vari costruttori di realizzare macchine più simili e con set di istruzioni molto vicini fra loro).

2. Macchina RISC di esempio

Un esempio di macchina RISC si studia nel Laboratorio di questo corso: la macchina MIPS.

3. Macchina CISC di esempio

3.1 Introduzione

Come esempio verrà presa la famiglia di calcolatori PDP 11, prodotta negli anni settanta dalla Digital.

Per approfondire l’argomento si consigliano i seguenti siti:
http://simh.trailing-edge.com/pdp11.html
http://www.conknet.com/~w_kranz/pdp11/pdp11.htm

http://www.pdp11.org/
.

3.2 Caratteristiche generali

Questa famiglia di calcolatori aveva le seguenti caratteristiche:

-

N Z V C

TRAP (1 bit)

PRIORITÀ (3 bit)

MODO FUNZIONAMENTO (2 bit)

-

con N, Z, V e C risultati logici attuali, TRAP t.c. ogni volta che il programmatore lanciava una eccezione si alzava fino al ritorno dal programma di servizio (serviva per il debugging), PRIORITÀ del processo attuale (da 0 a 7 con 0 il massimo), scritto dal programma in esecuzione, che stabiliva da chi questo poteva accettare interruzioni\eccezioni, MODO DI FUNZIONAMENTO comprendente tre opzioni: sistema operativo, utente e intermedio; il set di istruzioni veniva modificato non permettendo l'accesso ad istruzioni pericolose (come lo spegnimento della macchina) all'utente

3.2 Modi di indirizzamento

I modi di indarizzamento possibili erano (il numero che li identifica veniva messo nel campo "modo di indirizzamento" delle istruzioni):

Sul registro 7 della CPU (P.C.) si potevano utilizzare solo 2,3,6,7. Nel paragrafo 3.8 si vedrà come fosse possibile utilizzare anche gli indirizzamenti immediato ed assoluto grazie a questa caratteristica del registro 7.

3.3 Istruzioni a due operandi d'ingresso

Il formato di queste istruzioni era il seguente:

1 bit

3 bit

6 bit

6 bit

Se il primo bit era 1, segnalava che gli operandi erano mezze parole, se era zero segnalava che erano word: poiché era possibile indirizzare sia mezze parole che parole, tutte le istruzioni potevano lavorare su entrambe i formati.
I 3 bit seguenti indicavano l'operazione che si voleva eseguire (codice dell'istruzione). Il massimo numero di istruzioni a due operandi d’ingresso possibile era quindi di otto (23).
Nei successivi 6 bit si trovava il primo operando. Questi 6 bit venivano divisi in due parti da 3 bit ognuna: nei primi veniva indicato il modo di indizzamento (infatti c'erano otto modi - 23) mentre negli ultimi veniva indicato il registro a cui si riferiva il modo di indirizzamento (infatti c'erano otto registri nella CPU - 23).
Gli ultimi 6 bit erano analoghi ai precedenti, ma contenevano il secondo operando.
Nel caso di operazioni con un valore di ritorno, il risultato veniva messo in questi ultimi 6 bit, sovrascrivendo il secondo operando.

Le istruzioni avevano i seguenti codici mnemonici:
MOV: spostava una word da un posto ad un'altro
CMP: comparava i due operandi, mettendo il risultato nei bit logici di PSW
BIT: and logico
BIC: mascheramento - azzerava tutti i bit del I operando che corrispondevano agli uno del II operando (che era la maschera)
BIS: or esclusivo (xor)
ADD: somma e sottrazione
Tutte queste istruzioni operavano su parole.
Aggiungendo una B alla fine del nome si ottenevano istruzioni operanti su mezze parole.

3.4 Istruzioni ad un operando d'ingresso

Il formato di queste istruzioni era il seguente:

1 bit

9 bit

6 bit

Se il primo bit era 1, segnalava che l'operando era una mezza parola, se era zero segnalava che era di una word: poiché era possibile indirizzare sia mezze parole che parole, tutte le istruzioni potevano lavorare su entrambe i formati.
I 9 bit seguenti indicavano l'operazione che si voleva eseguire (codice dell'istruzione), che quindi erano molte di più delle precedenti (29).
Negli ultimi 6 bit si trovava l'operando. Questi 6 bit venivano divisi in due parti da 3 bit ognuna: nei primi veniva indicato il modo di indizzamento (infatti c'erano otto modi - 23) mentre negli ultimi veniva indicato il registro a cui si riferiva il modo di indirizzamento (infatti c'erano otto registri nella CPU - 23).
Nel caso di operazioni con un valore di ritorno, il risultato veniva messo in questi ultimi 6 bit, sovrascrivendo l'operando.

Le istruzioni avevano i seguenti codici mnemonici:
JMP: salto incodizionato con un offset di 6 bit
SWAB: scambiava le due mezze parole di una parola fra loro
CLR: azzerava l'operando
COM: ???
INC: incrementava di uno l'operando
DEC: decrementava di uno l'operando
NEG: negava l'operando complementando i suoi bit
ADC: addizionava il riporto di una somma precedente, contenuto nei bit dei risultati logici di PSW; serviva per addizionare stringhe più lunghe di una word
SBC: sottraeva il prestito di una differenza precedente, contenuto nei bit dei risultati logici di PSW; serviva per sottrarre stringhe più lunghe di una word
TST: testava la positività o negatività dell'operando rappresentato in Ca2, controllando il MSB; il risultato veniva scritto nei risultati logici in PSW
ROR: shift logico a destra di una posizione (i bit che uscivano da un lato rientravano dal lato opposto: serviva per fare operazioni bit a bit)
ROL: shift logico a sinistra di una posizione (i bit che uscivano da un lato rientravano dal lato opposto: serviva per fare operazioni bit a bit)
ASR: shift aritmetico a destra di una posizione (i bit che escivano da dx NON rientravano da sn, perchè a sn entrava lo stesso bit che c'era per primo, per mantenere il segno: serviva per dividere per multipli di due l'operando)
ASL: shift aritmetico a sinistra di una posizione (i bit che escivano da sn NON rientravano da dx, perchè a dx entravano zeri, per mantenere il numero: serviva per moltiplicare per multipli di due l'operando)
SXT: estendeva il segno per i numeri in Ca2 (ad es. passando da 8 bit a 16 bit, ripeteva a sinistra il bit più significativo fino a coprire tutti i 16 bit della word)

Aggiungendo una B alla fine del nome si ottenevano istruzioni operanti su mezze parole.

3.5 Istruzioni di salto

Il formato di queste istruzioni era il seguente:

8 bit

8 bit

I primi otto bit rappresentavano il codice dell'istruzione, gli ultimi otto bit rappresentavano l'offset (quindi indirizzamento relativo). Si potevano pertanto effettuare salti a +127 e –128 posizioni dal contenuto del P.C. .

Le istruzioni avevano i seguenti codici mnemonici:
BR: salto incodizionato con un offset di 8 bit
BNE: saltava se nel bit Z dei risultati logici di PSW c'era 0 (non uguale)
BEQ: saltava se nel bit Z dei risultati logici di PSW c'era 1 (uguale)
BGE: saltava se maggiore o uguale
BLT: saltava se minore
BGT: saltava se maggiore
BLE: saltava se minore o uguale
BLP: saltava se positivo
BMI: saltava se negativo
BHI: saltava se maggiore o uguale per naturali non in Ca2
BLOS: saltava se minore o uguale per naturali non in Ca2
???: saltava se overflow (Ca2)
???: saltava se trabocco (naturali non in Ca2)
JSR: saltava a subroutine (salvando P.C. nello stack)
RTS: ritornava da subroutine (ripristinando P.C. dallo stack)

3.6 Istruzioni di trap

Il formato di queste istruzioni era il seguente:

8 bit

8 bit

I primi otto bit rappresentavano il codice dell'istruzione, gli ultimi otto bit rappresentavano l'opportuna locazione del vettore delle interruzioni (che quindi aveva 28 posizioni).

Le istruzioni avevano i seguenti codici mnemonici:
EMT: saltava alla specificata locazione del vettore delle interruzioni (salvando registro di stato e P.C.) - veniva raccomandato di non utilizzare questa istruzione perchè riservata alla casa costruttrice per costruire il software di base
TRAP: come la precedente ma utilizzabile liberamente; separando le due istruzioni si evitavano conflitti fra software di base e programmi utente; nulla vietava cmq di utilizzare EMT
BPT: saltava alla posizione 14 del vettore delle interruzioni, in cui si trovava l'indirizzo del programma di servizio scritto dal produttore che dopo ogni istruzione eseguita da parte del programma utente interrompeva la macchina (usato per debugging)
IOT: saltava alla posizione 20 del vettore delle interruzioni, in cui si trovava l'indirizzo del programma di servizio scritto dal produttore che gestiva l'I\O con interruzioni
RTI: ritornava dal programma di servizio (ripristinando registro di stato e P.C.)

3.7 Istruzioni di controllo

HALT: arrestava il calcolatore
HOP: perdeva un ciclo di clock senza far nulla (utile per sincronizzazione / temporizzazione)
WAIT: arrestava la macchina in attesa di una interruzione: appena veniva lanciata una qualsiasi interruzione, il calcolatore ripartiva
RESET: metteva a zero tutti i registri interni ed esterni (periferiche) del calcolatore

3.8 Istruzioni a formato variabile

Non esistevano istruzioni "native" a formato variabile.
Si è detto tuttavia che il settimo registro della CPU conteneva l’indirizzo puntato dal P.C. .
Questo permetteva di creare istruzioni a formato variabile per realizzare gli indirizzamenti assoluto e immediato. Analizzando le istruzioni sinora trattate, infatti, si nota che questi due tipi di indirizzamento non erano possibili, poichè nel campo dell'istruzione contenente gli operandi non ci sarebbe stato sufficiente spazio per mettere direttamente il valore di questi (sarebbero serviti 16 bit per ogni operando).
Ad esempio scrivendo:
ADD (R1), (R2)
gli operandi non erano contenuti nella word di ADD, dove si trovavano, invece, soltanto i nomi dei registri che li contenevano.
Scrivendo:
ADD (R7)+, (R7)+
veniva comunicato alla macchina che gli operandi si trovavano nelle due word successive a quella dell'istruzione ADD. Si otteneva, in pratica, una istruzione di tre word di cui una contenente ADD e le altre due contenenti gli operandi.
Ovviamente si poteva utilizzare questo trucco anche su di un solo operando, così da ottenere una istruzione di 2 word:
ADD (R1), (R7)+

 

I - PERIFERICHE

1. Introduzione

Sono dispositivi collegati al calcolatore tramite il canale di I\O.

2. Unità disco

Supporto magnetico in grado di conservare nel tempo stringhe binarie e restituirle su richiesta del calcolatore.
Caratteristiche di questa periferica sono l'elevata capienza, la possibilità di conservare informazioni anche a calcolatore spento e in assenza di corrente, nonchè la latenza molto maggiore rispetto a quella della RAM (per via dell'architettura costruttiva).

All'interno dei calcolatori, dunque, convivono vari tipi di memoria, che possono essere suddivisi in maniera generale in memoria volatile e memoria permanente. Questa suddivisione si riflette nello studio della psicologia del pensiero, dove viene teorizzata la presenza di due tipi di memoria anche all'interno del cervello umano. Una memoria a breve termine sarebbe, infatti, responsabile della capacità di tenere a mente un numero di telefono dopo una breve lettura per consentire di digitarlo sulla tastiera del telefono, mentre quella a lungo termine si manifesterebbe nei ricordi e nella capacità di trattenere una grande quantità di informazioni per un lungo periodo di tempo, tipica del cervello umano. Dalla collaborazione tra questi due tipi di memoria, sostengono gli studiosi, si realizzano la comprensione del discorso e molte altre facoltà complesse. Questo tipo di suddivisione funziona perfettamente anche in ambito informatico, dove esistono memorie di massa, che possono conservare le informazioni in modo permanente senza grossi problemi di spazio, e memorie volatili, come registri e RAM, che sono deputate alle funzioni operative ma a cui non è richiesto di conservare i dati anche dopo lo spegnimento del computer.

Piatti

Questo tipo di unità viene realizzata tramite dischi concentrici, detti piatti, in grado di ruotare a velocità variabile attorno ad un asse centrale comune. I piatti vengono ricoperti con un particolare materiale magnetico. Su di esso sono presenti numerosi magneti disposti inizialmente in maniera casuale, ciascuno con un polo positivo "+" ed uno negativo "-". Accoppiando i magneti due a due, a seconda dell’orientamento dei poli è possibile rappresentare i bit 0 ed 1. In particolare con lo schema "+" "-","+" "-" si rappresenta uno zero, mentre con "+" "-", "-" "+" si rappresenta un uno.

Magneti

Opportune testine (due per ogni disco: una per la faccia superiore, una per quella inferiore) sono in grado sia di allineare i microscopici magneti, che di leggere il loro campo.

Settori

Le unità disco sono così organizzate: ogni piatto ha due facce, ogni faccia è divisa in cerchi concentrici detti tracce o cilindrici e ogni traccia è divisa in settori. Dunque, per accedere ad una certa stringa memorizzata, si usano indirizzi che indicano la faccia, la traccia e quindi il settore dove esso si trova. Fatto ciò, il circuito che controlla il funzionamento del disco seleziona la testina, la sposta meccanicamente sulla traccia indicata e poi aspetta che il giusto settore passi sotto di essa per iniziare la lettura o scrittura. Per verificare di aver posizionato la testina nella giusta posizione, all'inizio di ogni settore è ripotato il suo indirizzo. Tale associazione settore - indirizzo avviene al momento della formattazione del disco.

Trasferire (scrivere o leggere) un dato alla volta, però, è poco efficiente, vista la lentezza di questo tipo di periferiche rispetto alla CPU. Per minimizzare gli accessi, quindi, si effettua il trasferimento di un settore alla volta. Il quantitativo di dati che occupano un settore viene detto record. Si può quindi dire che viene trasferito un record alla volta. Si noti che il quantitativo di dati che occupano un settore è costante su tutto il disco, nonostante i settori interni siano più corti di quelli esterni. Questo perchè all'interno i dati sono via via più "fitti".

I dati vengono scritti in modo non contiguo. Ciò serve per evitare di sprecare spazio inutilmente quando si libera un settore precedente all'ultimo, ma i dati che si devono scrivere sono più grandi del settore. Questa caratteristica pone un nuovo problema: ritrovare tutti i pezzi di dato che interessano. Vengono utilizzate varie tecniche. Una di queste consiste nell'avere memorizzata all'inizio del disco una tabella con l'indirizzo di tutte le parti di ogni dato. Una seconda tecnica consiste nel mettere alla fine di ogni settore l'indirizzo del settore in cui continua il dato. Di tutto questo si occupa il sistema operativo.
Dovendo ricercare i vari pezzi, comunque, si ha una maggiore perdita di tempo. Per questo esiste la possibilità di riservare zone di disco alla memorizzazione contigua. In queste zone andranno tutti quei dati che necessitano di un accesso particolarmente veloce, come ad esempio gli swap file per la memoria virtuale.

Poichè ogni settore contiene molte word, quando si trasferisce un settore viene generato un notevole traffico di stringhe, quindi le unità disco vengono in genere collegate al calcolatore tramite il metodo del DMA.

Memoria virtuale: quando il disco è utilizzato per lo swapping, le pagine sono grandi proprio un record, per comodità.

Esistono due classi di unità disco: i dischi morbidi, in cui le testine sono a contatto con i piatti, e quelli rigidi, in cui le testine volano a pochi micron di distanza dai piatti, sostenute dal cuscino d'aria che si crea grazie alla rotazione degli stessi piatti.
Fra le due tecnologie, la prima è più lenta a causa dell'attrito, ma più resistente, la seconda è più delicata (le testine possono precipitare sul disco - crash di testine), ma più veloce (i dischi possono ruotare molto più velocemente).

3. Unità nastro

Supporto magnetico in grado di conservare nel tempo stringhe binarie e restituirle su richiesta del calcolatore.

Vengono realizzati tramite nastri magnetici avvolti in bobine.

Tracce

Parallele al nastro, scorrono 9 tracce. Sulle prime otto vengono memorizzati i byte, perpendicolarmente al nastro. Sull'ultima, per ogni byte memorizzato, si trova un bit di parità trasversale. Dopo un certo numero di byte, per comodità in quantità pari ad un record, si trova un byte di parità longitudinale. In questo modo è possibile identificare, come in una matrice, i bit errati e correggerli (basta complementarli).

La parità è una tecnica di rilevazione degli errori in cui si stabilisce che il numero di bit di ogni stringa deve essere pari. Per fare ciò, si aggiunge un bit alla stringa; questo prende il valore zero se già c'è un numero pari di bit e prende il valore uno se ce n'è un numero dispari, così da renderli in numero pari. In questo modo, se un numero dispari di bit è errato a causa della smagnetizzazione del nastro, la parità non torna più e l'errore può essere rilevato e corretto. Se però è errato un numero pari di bit, non si riesce a rilevarlo. Per questo si dice che la parità è una tecnica di protezione leggera. Esistono, invece, tecniche di protezione dette forti.
C'è da dire, però, che in tutte le tecniche di rilevazione degli errori bisogna aggiungere informazioni ridondanti alle stringhe che si vogliono "proteggere". In genere, più una tecnica è forte e maggiore è l’aumento di informazioni ridondanti. Via via che la protezione aumenta, dunque, lo spazio occupato è sempre maggiore: questo è il prezzo da pagare.
Le tecniche di rilevamento degli errori sono molto usate anche nella trasmissione di dati digitali.

Mentre i dischi vengono utilizzati per l’achiviazione in linea, i nastri vengono utilizzati per l'archiviazione non in linea.
L'archiviazione non in linea consiste nel memorizzare grandi quantità di dati a cui si accede raramente, ma che interessa conservare per periodi molto lunghi. Vengono per questo utilizzati dispositivi magnetici come i nastri, che hanno il vantaggio di poter essere tolti dal calcolatore e conservati a parte quando non si deve scrivere o leggere da essi. Pur essendo dispositivi magnetici, e quindi soggetti a smagnetizzazione, i nastri dispongono di varie tecniche di rilevazione e correzione degli errori, anche molto più complesse della parità e per questo risultano molto indicati per l’archiviazione non in linea.
Tuttavia, per conservare un archivio non in linea memorizzato su nastro magnetico, bisogna periodicamente eseguire la copia del nastro vecchio su di uno nuovo, così da eliminare gli errori di smagnetizzazione prima che diventino troppi per essere corregibili.
Ulteriore vantaggio dei nastri magnetici è che la scrittura e lettura da questi dispositivi è standardizzata (al contrario delle unità a disco), cioè è indipendente dal calcolatore e dal sistema operativo utilizzato.
L'archiviazione in linea, invece, consiste nella memorizzazione di dati a cui si accede più spesso e che devono comunque essere conservati anche a computer spento.

4. Interfacce di comunicazione

Dispositivi in grado di trasmettere dati da un calcolatore ad un altro.

Attualmente i più diffusi sono i modem. Questi sfruttano le linee telefoniche per trasferire word di bit.
Le linee telefoniche sono pensate per trasmettere la voce umana (onde sonore), in particolare quella parte di frequenze che va da 10 a 3000 Hz circa.
Si è stabilito che ad una frequenza di circa 900 Hz corrispondesse il bit 1 e che ad una frequenza di circa 950 Hz corrispondesse il bit zero. L'orecchio umano percepisce i due suoni in questione come fischi, uno più alto e uno più basso.
Applicando due filtri che rilevano queste due frequenze entrando in risonanza con esse, è possibile riconvertire le due frequenze in bit. Questo metodo è detto a modulazione di frequenza e significa che le onde che sono trasmesse oscillano tra frequenze ben determinate (900 e 950 Hz in questo caso).
Un vantaggio di utilizzare le onde sonore è che la trasmissione risulta quasi del tutto indipendente dalla potenza del segnale, poichè i filtri comunque entrano in risonanza ed amplificano le due frequenze in questione, anche se esse arrivano molto deboli.

Come visto nel paragrafo precedente, anche per la trasmissione di dati digitali vengono utilizzate diverse tecniche per individuare e correggere errori che possono avvenire durante la comunicazione (ad es. disturbi sulla linea, ecc.).

-VARICELLA martedì 28 maggio 2002-

5. Schermo

-VARICELLA martedì 28 maggio 2002-

6. Stampante

-VARICELLA martedì 28 maggio 2002-

Terminali video: tastiera/monitor

 

 L’utente inserisce tramite la tastiera dati che vengono trasmessi al calcolatore e possono essere visualizzati su monitor.

 L’unità di scambio è il carattere, formato di solito da 7 (ASCII) o 8 bit.

 Dato che l’interazione è con operatori umani non serve una grande velocità di trasmissione  viene usato collegamento seriale.

 Nel caso di ingresso da tastiera quando l’utente rilascia il tasto premuto viene generato un segnale elettrico interpretato dal trasduttore della tastiera e tradotto in una sequenza di bit di codice ASCII e trasmessa al modulo di I/O del calcolatore.

 Dispositivo trasmittente e ricevente devono utilizzare la stessa informazione di temporizzazione (segnale di clock): trasmissione sincrona (segnale unico) o asincrona (due segnali di clock con frequenze simili e opportuni controlli).

 

 

 Trasmissione asincrona con tecnica start-stop:

o trasmissione di caratteri alfanumerici codificati con 8 bit, preceduti da un carattere 0, start, e uno o più bit 1, stop.

o la linea di trasmissione è nello stato 1 quando inattiva (rimane a 1 dopo il bit stop).

 

 

A - RICHIAMI *

1. VELOCITA' DELLE PORTE *

2. LA WORD *

B - BLOCCHI BASE *

1. INTRODUZIONE *

2. BLOCCHI COMBINATORI *

2.1 Codificatori *

2.1.2 Decodificatore *

2.1.4 Rom *

2.2 Commutatori *

2.2.1 Multiplexer *

2.2.2 Demultiplexer *

2.2.3 Multiplexer + demultiplexer *

2.3 Sommatori aritmetici *

2.3.1 Half adder *

2.3.2 Full adder *

2.3.3 Addizionatore aritmetico *

2.4 Comparatori *

2.4.1 Comparatore aritmetico *

2.4.2 Comparatore logico *

3. BLOCCHI SEQUENZIALI *

3.1 Flip-Flop *

3.1.1 Tipo JK *

3.2 Contatori *

3.2.1 Contatore binario *

3.2.2 Contatore binario preselezionabile *

3.3 Registri o buffer *

3.3.1 Registro di memorizzazione *

3.3.2 Registro a scorrimento (shift register) *

C - MACCHINE DI VON NEUMANN *

1. Introduzione *

2. Sistemi di Interconnessione *

3. Unità Aritmetica e Logica *

3.1 Introduzione *

3.2 Funzionamento *

4. Memoria Centrale *

4.1 Introduzione *

4.2 Funzionamento *

4.4 Accorgimenti pratici *

4.4.1 Frazionamento degli Indirizzi *

4.4.2 Assemblamento di Chip *

4.4.3 Pagine di Memoria *

5. Control Unit *

5.1 Introduzione *

5.2 Program Counter *

5.3 Decodificatore di Istruzioni *

5.4 Codificatore di Comandi *

5.5 Microprogrammazione *

5.6 Generatore di Fase *

5.7 Registri Interni alla CPU *

6. Input \ Output *

6.1 Introduzione *

6.2 Realizzazione *

6.3 Funzionamento *

7. ALTRO *

7.1 Introduzione *

7.2 AU (Unità Aritmetica) *

D - DATI ED ISTRUZIONI *

1. Introduzione *

2. Istruzioni *

2.1 Introduzione *

2.2 Tipi di Istruzione *

2.3 Campi delle Istruzioni *

2.4 Fasi *

3. Dati *

3.1 Introduzione *

3.2 Dati Numerici *

3.2.1 Rappresentazione di Naturali *

3.2.2 Operazioni su Naturali *

3.2.3 Rappresentazione di Interi *

3.2.4 Operazioni su Interi *

3.2.5 Rappresentazione di Reali *

3.2.6 Operazioni su Reali *

3.3 Verso di Memorizzazione *

3.4 Allineamento della memoria *

E - INTERRUZIONI ED ECCEZIONI *

1. Introduzione *

2. Funzionamento *

3. Utilizzi *

3.1 Multiprogrammazione *

3.2 Altro *

Interruzioni ed Eccezioni nel MIPS *

F - SOFTWARE *

1. Assembly *

1.0 Premessa *

1.1 Il Linguaggio *

1.2 Funzionamento *

1.3 L'importanza delle interruzioni software *

2. Software di base *

G - ACCORGIMENTI E TRUCCHI *

1. Gerarchia di memoria *

1.1 Introduzione *

1.2 La memoria cache *

1.3 Livelli di cache *

2. Canalizzazione *

2.1 Introduzione *

2.2 Applicazione *

3. Memoria Virtuale *

3.1 Introduzione *

3.2 Overlay *

3.2 Swapping *

H - MACCHINE CISC E MACCHINE RISC *

1. Caratteristiche *

2. Macchina RISC di esempio *

3. Macchina CISC di esempio *

3.1 Introduzione *

3.2 Caratteristiche generali *

3.2 Modi di indirizzamento *

3.3 Istruzioni a due operandi d'ingresso *

3.4 Istruzioni ad un operando d'ingresso *

3.5 Istruzioni di salto *

3.6 Istruzioni di trap *

3.7 Istruzioni di controllo *

3.8 Istruzioni a formato variabile *

I - PERIFERICHE *

1. Introduzione *

2. Unità disco *

3. Unità nastro *

4. Interfacce di comunicazione *

5. Schermo *

6. Stampante *