# Il processore

Il processore è la componente dell'unità centrale che fornisce la capacità di elaborazione delle informazioni contenute nella memoria principale.&#x20;

L' elaborazione avviene in accordo a sequenze di istruzioni che definiscono quale debba essere il comportamento del processore stesso. Tali sequenze prendono il nome di "**programmi**". Per ogni tipo di processore è definito un insieme di istruzioni (chiamate "**istruzioni macchina**"), ognuna delle quali corrisponde ad una operazione elementare che il processore stesso è in grado di svolgere. Il linguaggio in cui tali istruzioni sono espresse prende il nome di "**linguaggio macchina**".&#x20;

Ogni istruzione macchina (o istruzione in linguaggio macchina) corrisponde ad una operazione elementare; operazioni più complesse possono essere portate avanti dal processore mediante sequenze di istruzioni elementari: tali sequenze costituiscono i "**programmi in linguaggio macchina**". Ad esempio, se supponiamo che una istruzione elementare permetta di sommare due numeri, la somma di molti numeri può essere ottenuta mediante una sequenza di istruzioni di somma elementare che costituisce quindi un programma per sommare dei numeri.

Possiamo quindi affermare che il ruolo del processore è quello di eseguire programmi in linguaggio macchina.&#x20;

Ogni istruzione in linguaggio macchina dovrà operare su un insieme di dati che devono essere trasformati (si pensi ai numeri da sommare o a un testo da elaborare).

&#x20;La caratteristica dei processori attuali è che tanto un programma in linguaggio macchina che deve essere eseguito quanto i dati su cui tale programma opera devono essere, al momento dell'esecuzione, in memoria principale.&#x20;

Possiamo quindi completare la discussione del paragrafo precedente sulla memoria principale osservando che tale memoria deve contenere due tipi di informazioni:

* la sequenza di istruzioni che devono essere svolte dal processore, ossia il "programma" in esecuzione;
* l'insieme di dati su cui tali istruzioni operano.

Il processore non è, sia dal punto di vista fisico che da quello funzionale, un'unica componente ma è costituito da componenti di natura diversa e che svolgono compiti differenti.

![Componenti fondamentali di un processore](https://875337804-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lt58INaXg-cFL27pnx0%2F-MKADCo9T6oIMXQAduUh%2F-MKADNUWVFDwyNgkhz37%2FProcessore.png?alt=media\&token=894a3d80-e6ea-4137-bfbf-1073e6d28abb)

La figura è estremamente astratta e molte delle componenti e sotto-componenti non verranno analizzate in dettaglio da noi; le componenti che analizzeremo sono quelle che è fondamentale prendere in considerazione nella analisi funzionale di un processore. Il ruolo che tali componenti giocano sarà analizzato in dettaglio nei prossimi sotto-paragrafi per cui una visione globale del modo di operare del processore sarà possibile solo al termine di tale discussione. Si noti che diverse componenti all'interno del processore sono collegate tra loro da uno (o più) **bus**. Il bus è semplicemente un canale veloce di comunicazione.

### Unità di Controllo (U.C.)

L'Unità di Controllo (abbreviata U.C. nel seguito) svolge il ruolo di coordinamento delle diverse attività che devono essere svolte all'interno del processore. Si può pensare che il processore svolga la sua attività in modo ciclico: ogni ciclo corrisponde alla esecuzione di una istruzione macchina (e quindi più cicli corrispondono alla esecuzione di un programma). All'interno di ogni ciclo vengono svolte diverse attività che sono appunto controllate e coordinate dalla U.C. Più in particolare (la descrizione verrà dettagliata e resa più precisa nel seguito, man mano che analizzeremo le varie componenti del processore), ad ogni ciclo:

* viene letta dalla memoria principale la prossima istruzione da eseguire;
* viene eseguita tale istruzione (che può comportare l'esecuzione di operazioni all'interno del processore - ad esempio calcoli - oppure operazioni di lettura e scrittura in memoria).

La frequenza con cui vengono eseguiti tali cicli di esecuzione è scandita da una ulteriore componente: il "**clock**". Tale componente genera impulsi a distanza di tempo costante (ossia con frequenza) prefissata; ad ogni impulso la U.C. esegue un ciclo di esecuzione di una istruzione macchina. La velocità di elaborazione di un processore dipende quindi direttamente dalla frequenza del suo clock. Tipici valori di frequenza dei processori attuali canno da 16 MHz a 150 MHz (ossia da 16 a 150 milioni di impulsi al secondo). Ciò significa che un processore può eseguire decine di milioni di istruzioni macchina al secondo.

Le unità di misura della frequenza in fisica è l'Hertz (abbreviato Hz). Un evento che si ripete con la frequenza di 1 Hz si ripete una volta al secondo; il MHz corrisponde a un milione di Hz per cui un evento che ha frequenza di 1 MHz si ripete (a intervalli di tempo costanti) un milione di volte al secondo.

### I registri

Il processore contiene al suo interno un certo numero di registri, ognuno dei quali è una piccola unità di memoria estremamente veloce. Tipicamente ogni registro ha dimensione di pochi byte (2 o 4 byte; tale dimensione è di solito uguale a quella delle parole di memoria) e tempi di accesso (lettura e scrittura) molto più bassi (più veloci) di quelli della memora principale. I registri vengono usati per mantenere informazioni di necessità immediata per il processore. Nel seguito analizzeremo il ruolo che tali registri svolgono durante l'elaborazione.

#### Il Program Counter (P.C.)

Abbiamo osservato che il processore esegue, ad ogni ciclo, una istruzione prelevata dalla memoria principale; il problema è allora quello di sapere in quale cella di memoria tale istruzione si trovi. Il registro Program Counter (P.C.) contiene l'indirizzo in memoria principale della prossima istruzione da eseguire. Prima dell'inizio dell'esecuzione di un programma viene caricato nel registro P.C. l'indirizzo della prima istruzione del programma (tale istruzione viene svolta dal sistema operativo, impareremo più avanti); ad ogni ciclo di esecuzione il valore del registro P.C. viene modificato in modo da contenere l'indirizzo della istruzione successiva (spesso quella successiva in memoria; vedremo tra breve in modo più preciso come tale modifica venga effettuata). In questo modo, data la sequenza di istruzioni di un programma, il registro P.C. indica a quale punto il processore è arrivato nell'esecuzione. Il ciclo di funzionamento del processore può essere reso più dettagliato. Ad ogni ciclo:

* viene letta dalla memoria principale l'istruzione che si trova all'indirizzo indicato dal registro P.C.;
* viene modificato il valore del P.C. aumentandolo di 1 (in modo da avere in P.C. l'indirizzo della istruzione successiva che sarà la prossima da mandare in esecuzione - si vedano i commenti più avanti a proposito delle istruzioni in linguaggio macchina);
* viene eseguita l'istruzione letta dalla memoria.

#### Il Registro Istruzioni (R.I.)

Un secondo importante registro all'interno del processore è il registro istruzioni (R.I.). In tale registro viene memorizzata, ad ogni ciclo, l'istruzione di cui è in corso l'esecuzione. Ogni istruzione specifica una azione che il processore deve svolgere. Dopo che l'istruzione è stata letta dalla memoria e scritta nel registro istruzioni R.I. è compito dell'unità di controllo U.C. il decodificare l'istruzione - ossia comprendere quale sia l'azione che deve essere svolta - e comportarsi di conseguenza. Possiamo quindi fare un ulteriore passo nello specificare il modo di funzionamento del processore. Ad ogni ciclo:

* viene letta dalla memoria principale l'istruzione che si trova all'indirizzo indicato dal registro P.C.; l'istruzione viene scritta all'interno del registro R.I;
* la Unità di controllo U.C. decodifica l'istruzione e individua la sequenza di azioni che devono essere svolte all'interno del processore;
* viene modificato il valore del P.C. aumentandolo di 1 (si vedano i commenti più avanti);
* vengono eseguite le azioni specificate dall'istruzione

Le azioni che devono essere svolte sono divise per diversi tipi di istruzioni e possono quindi coinvolgere diverse componenti all'interno del processore (come vedremo in seguito).

#### Il registro di stato (P.S.)

Il registro di stato (P.S.) contiene informazioni sulla stato di esecuzione del processore. In particolare, esso può segnalare eventuali errori avvenuti durante l'esecuzione. Vi sono diversi tipi di errori che possono avvenire nel corso dell'esecuzione di un programma. Un primo tipo di errori sono quelli di tipo matematico, quale l'_overflow_ nell'esecuzione di un'operazione. Altri errori sono dovuti al fatto che l'utente tenti di fare operazioni che non gli sono consentite (si capirà quando si tratterà il sistema operativo).

Una parte del contenuto del registro di stato è costituita da 4 bit, detti codici di condizione, ed indicati simbolicamente con le lettere **Z** (zero), **N** (negativo), **C** (carry, cioè riporto) e **V** (overflow).&#x20;

<img src="https://875337804-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lt58INaXg-cFL27pnx0%2Fuploads%2FXTjIiUCWZ3KsKvMtryel%2Ffile.excalidraw.svg?alt=media&#x26;token=31059d1c-e386-485a-8908-1d3363954d59" alt="Registro di stato" class="gitbook-drawing">

Dopo l'esecuzione di qualsiasi istruzione, eccetto l'istruzione di salto, i codici di condizione contengono un valore che dipende dal risultato dell'istruzione stessa.

Per esempio, dopo l'esecuzione di un'istruzione di addizione (ADD) i bit hanno il seguente significato:

* Z vale 1 solo se il risultato dell'addizione è 0;
* N vale 1 solo se il risultato dell'addizione è negativo;
* C vale 1 solo se l'addizione ha provocato un riporto rispetto alla posizione più significativa;
* V vale 1 solo se l'addizione ha dato luogo a overflow, cioè se la somma di due numeri positivi risulta essere un numero negativo, oppure se la somma di due negativi risulta essere un numero positivo.

Il registra di stato, P.S., contiene, quindi, alcune informazioni sull'effetto dell'esecuzione di un'istruzione che possono venire analizzate con successive istruzioni di **salto condizionato** (Vedi [Esecuzione delle istruzioni in linguaggio macchina](#esecuzione-delle-istruzioni-in-linguaggio-macchina), la parte sulle istruzioni di salto).

#### I registri generali

Oltre ai registri specializzati (quali P.C, R.I. e P.S. ed altri che analizzeremo di seguito), all'interno del processore vi è un certo numero di registri che può essere utilizzato come  memorie temporanee per svolgere operazioni. Il numero e le dimensioni di tali registri variano da processore a processore. Ad esempio, è oggi comune avere 16 registri di 4 byte (anche le dimensioni di tali registri coincidono con quelle delle parole di memoria).

### L'Unità Aritmetico Logica (A.L.U.)

L'unità aritmetico-logica (**Arithmetic-Logic Unit** - A.L.U.) è un insieme di circuiti in grado di svolgere operazioni di pipo aritmetico (e logico), tipicamente su dati contenuti all'interno dei registri generali. Il risultato di tali operazioni viene a sua volta memorizzato in uno dei registri generali. Ad esempio, vi sono circuiti in grado di eseguire la somma di due numeri binari contenuti in due registri e che depositano il risultato in un altro registro. In alcuni elaboratori in cui si vogliono effettuare pesantemente le operazioni su numeri _floating point_ (ed operazioni più complesse), al processore (alla A.L.U.) viene affiancato un processore specializzato per effettuare operazioni su tali numeri (il **coprocessore matematico**).

### Esecuzione delle istruzioni in linguaggio macchina

Possiamo a questo punto entrare in maggiore dettaglio sulla discussione di quali siano le tipiche istruzioni macchina di un elaboratore e di quali azioni debbano essere svolte da processore (coordinate dalla U.C.) per eseguire tali istruzioni. Ogni istruzione è caratterizzata da un nome (codificato mediante una sequenza di bit) e da un certo numero di argomenti. Tipici esempi di istruzioni in linguaggio macchina sono:

* **Istruzioni di lettura dalla memoria**: tali istruzioni richiedono che il contenuto di una cella di memoria (il cui indirizzo viene specificato come argomento dell'istruzione) venga letto e scritto in uno dei registri generali (anch'esso specificato come argomento).
* **Istruzioni di scrittura in memoria**: tali istruzioni richiedono che il contenuto di un registro generale (specificato come argomento dell'istruzione) venga letto e scritto in una cella di memoria (il cui indirizzo è specificato come argomento dell'istruzione).
* **Istruzioni aritmetiche**: tali istruzioni richiedono l'esecuzione di una operazione aritmetica (ad esempio, una addizione) su operandi contenuti in registri (i registri sono specificati come argomenti dell'istruzione). Il risultato viene a sua volta depositato in un registro (uno diverso specificato come argomento oppure uno di quelli in cui si trovano gli operandi).
* **Istruzioni logiche**: in questo caso vengono effettuate operazioni logiche (ad esempio confronto o inversione di bit) su operandi contenuti in registri (tali argomenti sono specificati come argomenti dell'istruzione), depositando il risultato in un registro.
* **Istruzioni di spostamento di informazioni da una cella all'altra di memoria** (gli indirizzi delle celle sono specificati come argomenti dell'istruzione).
*   **Istruzioni di salto**: Fino ad ora abbiamo considerato un programma come una serie di istruzioni che devono essere eseguite in sequenza per cui una istruzione è sempre seguita dalla successiva nella sequenza (per questo abbiamo assunto che ad ogni ciclo il valore del registro P.C. venga incrementato di uno).&#x20;

    In realtà la semplice esecuzione in sequenza di istruzioni elementari non è sufficiente per scrivere programmi per risolvere qualunque tipo di problema. E' necessario, infatti avere anche altri modi per controllare il flusso dell'esecuzione delle istruzioni (riprenderemo più in dettaglio su questo argomento quando parleremo della produzione del software discutendo il concetto di algoritmo).&#x20;

    In particolare, si ha in molti casi l'esigenza di introdurre sequenze alternative o di ripetere più volte l'esecuzione di un verto gruppo di istruzioni. In linguaggio macchina esistono istruzioni particolari (le istruzioni di salto - o istruzioni "goto") che permettono di specificare quale sarà la prossima istruzione da eseguire.&#x20;

    Più in dettaglio, tali istruzioni hanno un argomento che permette di specificare quale sia l'indirizzo della prossima istruzione che deve essere eseguita. In tal modo è possibile, sotto condizioni che possono essere specificate dalle istruzioni, modificare il flusso sequenziale di esecuzione.

Visto quali siano i più comuni tipi di istruzioni macchina, possiamo definire in dettaglio quale sia il funzionamento del processore ossia quali siano le attività coordinate dall'unità di controllo. Ad ogni ciclo di clock:

* viene letta dalla memoria principale l'istruzione che si trova all'indirizzo indicato dal registro P.C.: l'istruzione viene scritta all'interno del registro R.I.;
* la U.C. decodifica l'istruzione e individua la sequenza di azioni che devono essere  svolte all'interno del processore;
* viene modificato il valore del P.C. aumentandolo di 1;
* vengono eseguite le azioni corrispondenti all'istruzione; in particolare:
  * in corrispondenza a istruzioni di lettura dalla memoria, la U.C. attiva i meccanismi di lettura (che vediamo dopo);
  * in corrispondenza a istruzioni di scrittura in memoria, la U.C: attiva i meccanismi di scrittura (che vedremo dopo);
  * in corrispondenza a istruzioni aritmetiche o logiche, la U.C: attiva la A.L.U. per eseguire l'operazione desiderata;
  * in corrispondenza a istruzioni di salto viene modificato il valore del registro P.C. scrivendo in tale registro l'indirizzo dell'istruzione cui si deve saltare.

### Lettura e scrittura in memoria

Nella discussione precedente abbiamo visto come la lettura di un dato dalla memoria e la scrittura di un dato in memoria siano due operazioni basilari per il funzionamento di un processore. Si tratta ora di analizzare come tali operazioni possano essere eseguite e quindi come avvenga la comunicazione tra il processore e la memoria principale,

Abbiamo osservato più volte che per effettuare una operazione di lettura e scrittura in memoria è necessario specificare quale sia l'indirizzo della cella su cui si vuole operare. Si ha allora che la comunicazione tra processore e memoria principale avviene attraverso tre registri:

* il **Registro Indirizzi Memoria** (R.I.M.), mediante il quale il processore può specificare quale sia l'indirizzo della cella su cui si vuole operare;
* il **Registro Dati Memoria** (R.D.M.) nel quale viene depositata dalla memoria l'informazione letta o in cui il processore deposita l'informazione che deve essere scritta;
* il **Registro di Controllo** (R.C.) mediante il quale il processore specifica quale operazione debba essere eseguita e mediante il quale al termine dell'operazione, la memoria segnala eventuali errori che possono essere avvenuti.

Più in dettaglio, le due operazioni di lettura e scrittura vengono effettuate nel modo seguente:

*   **Lettura da memoria**

    in questo caso l'Unità di Controllo opera come segue:

    * scrive l'indirizzo della cella da leggere nel registro R.I.M.;
    * scrive il codice dell'operazione "leggi" nel registro R.C.;

    ciò corrisponde ad inviare un comando di lettura alla memoria. Come effetto di tale comando si ha che:

    * il contenuto della cella da leggere viene scritto nel registro R.D.M.

    Se vi sono errori, questi vengono segnalati nella memoria all'interno del registro R.C. (mediante codici particolari). Se non vi sono errori l'unità di controllo U.C. può prelevare il dato letto dal registro R.D.M. e copiarlo nel registro desiderato per poterlo poi utilizzare.
*   **Scrittura in memoria**

    In questo caso l'Unità di Controllo opera come segue:

    * scrive l'indirizzo della cella in cui scrivere nel registro R.I.M;
    * scrive l'informazione che deve essere scritta nel registro R.D.M.;
    * scrive il codice dell'operazione "scrivi" nel registro R.C.

    Ciò corrisponde ad inviare un comando di scrittura alla memoria; come nel caso precedente, se vi sono errori, questi vengono segnalati dalla memoria all'interno del registro R.C., altrimenti al termine dell'operazione il contenuto della cella il cui indirizzo è in R.I.M. è stato modificato e conterrà il valore specificato in R.D.M.

In caso di errore l'unità di controllo può decidere se tentare di effettuare nuovamente l'operazione o semplicemente segnalare un errore.

Dal punto di vista hardware possiamo pensare che il collegamento tra il processore e la memoria sia realizzato come segue.

![Connessione tra la memoria e i registri](https://875337804-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lt58INaXg-cFL27pnx0%2F-MKAElj-E1hk7hna76dT%2F-MKC0k7fy_NVMk5xt3BY%2Fmemoria.png?alt=media\&token=50996582-77b5-4f20-8e0d-aa694e4dae6d)

Il registro R.I.M. è collegato attraverso un insieme di circuiti (detti "circuiti di decodifica" alle diverse celle di memoria. Nel momento in cui viene posto un indirizzi nel registro R.I.M e viene inviato un comando di lettura o scrittura, solo il collegamento tra il registro R.I.M. e la cella il cui indirizzo è in R.I.M. viene attivato; allo stesso modo viene anche attivato il collegamento tra la cella stessa e il registro R.D.M. A questo punto a seconda che l'operazione sia di lettura o scrittura si ha che il contenuto della cella viene fatto passare nel registro R.D.M. (lettura) o viceversa (scrittura). L'insieme di tali operazioni è quello che costituisce il tempo di accesso alla memoria (che richiede poche decine di nanosecondi).

### Un semplice esempio di esecuzione di un programma

Vediamo ora brevemente come avvenga l'esecuzione di un piccolo frammento di programma in linguaggio macchina. Supponiamo, in particolare, di considerare un programma al cui interno si trovano le tre seguenti istruzioni:

*   `LOAD 3568 R1`

    Operazione di lettura dalla memoria: richiede la lettura del valore nella cella con indirizzo 3568 e il suo caricamento nel registro R1;
*   `ADD R1 R2`

    Operazione aritmetica di somma: prevede la somma del contenuto dei registri R1 e R2 con caricamento del risultato nel registro R1;
*   `STORE R1 3568`

    Operazione di scrittura in memoria: richiede la scrittura del valore nel registro R1 nella cella con indirizzo 3568;
*   `JUMP 1000`

    Istruzione di salto: prevede il salto all'istruzione con indirizzo 1000.

Il programma sarà memorizzato in memoria (supponiamo a partire dall'indirizzo 1000). Si ha dunque la seguente configurazione di memoria:

![](https://875337804-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Lt58INaXg-cFL27pnx0%2F-MKADxie4nwVRYoX-A6X%2F-MKAECuA_e9_GQsp5bXW%2Fesecuzione.png?alt=media\&token=67943b6a-a66c-4498-a58e-9ec71a3b47e7)

All'inizio dell'esecuzione del frammento, il registro PC conterrà il valore 1000 che è l'inizio della prima istruzione da eseguire. La unità di controllo provvede a fare le seguenti operazioni:

* lettura della prossima istruzione: il valore del P.C. (1000) viene copiato nel registro R.I.M e viene scritto il comando di lettura nel registro RC. Come effetto, il contenuto della cella di indirizzo 1000 viene copiato nel registro R.D.M che conterrà quindi l'istruzione "LOAD 3568 R1". Il contenuto di R.D.M (ossia l'istruzione) viene quindi copiato nel registro R.I.
* il valore del registro P.C. viene incrementato di 1 e conterrà ora il valore 1001 che è l'indirizzo della prossima istruzione da eseguire.
* la U.C. si preoccupa a questo punto di decodificare l'istruzione in R.I. da eseguire; poiché si accorge che si tratta dell'istruzione LOAD di lettura dalla memoria predispone le azioni per tale operazione di lettura. In particolare l'indirizzo della cella da leggere (3568) viene scritto nel registro R.I.M e viene dato il comando di lettura. Appena il dato è disponibile in R.D.M., esso viene letto e copiato nel registro R1, come richiesto dall'istruzione (il registro R1 conterrà quindi, al termine dell'operazione, il numero 25).
* L'esecuzione dell'istruzione 1000 è a questo punto terminata e si può passare alla successiva, ossia all'istruzione 1001 specificata dal registro P.C.. Questo avviene al successivo impulso di clock. Dopo aver letto l'istruzione e averla scritta nel registro R.I. (ciò avviene esattamente come nel caso precedente), l'Unità di controllo incrementa di uno il valore del P.C. (nuovo valore 1002), decodifica l'istruzione e, accorgendosi che si tratta dell'istruzione aritmetica ADD, attiva gli opportuni circuiti della A.L.U. affinché il contenuto dei registri R1 e R2 venga sommato (come specificato nell'istruzione). Se supponiamo che il registro R2 contenga il valore 30, si ha che il risultato della somma è 55 che viene scritto nel registro generale R1 (assumiamo che il risultato della somma venga depositato nel primo dei due registri da sommare).
* Al successivo impulso di clock si passa, quindi, a leggere l'istruzione specificata dal P.C. (1002), copiandola nel registro R.I. per decodificarla (intanto il valore del registro P.C: viene aumentato di 1 e diventa 1003). L'istruzione richiede la scrittura in memoria del contenuto del registro R1. Vengono quindi attivate le operazioni di scrittura in memoria; al loro termine il nuovo valore memorizzato nella cella di indirizzo 3568 sarà 55.
* Infine, si passa ad occuparsi dell'istruzione nell'indirizzo 1003 (che viene letta e decodificata come nei casi precedenti). In questo caso si tratta dell'istruzione JUMP di salto per cui la U.C. va a scrivere l'indirizzo a cui saltare (1000 nell'esempio) nel registro P.C. La prossima istruzione sarà quindi di nuovo quella con indirizzo 1000 per cui si ha una ripetizione di esecuzione di istruzioni.

### RIFERIMENTI

"Introduzione all'Informatica" di Luca Console - UTET Libreria

### SLIDE su Ciclo Fetch/Execute della CPU

{% embed url="https://github.com/checksound/MaterialeTecnologiaTerza/blob/main/lezione13-dams.pdf" %}

Esempio di ciclo _**fetch/execute**_ della CPU in interazione con la memoria principale.

























