Esempio di Architettura Computer - Macchina von Neumann
Esempio di esecuzione di istruzioni per un'architettura molto semplice da utilizzare come architettura della Macchina di von Neumann.
Last updated
Was this helpful?
Esempio di esecuzione di istruzioni per un'architettura molto semplice da utilizzare come architettura della Macchina di von Neumann.
Last updated
Was this helpful?
Il 30 giugno 1945 il geniale matematico ungherese John von Neumann pubblicò un famoso rapporto propedeutico alla realizzazione del progetto EDVAC (Electronic Discrete Variable Automatic Calculator) presso la Scuola di Ingegneria Elettrica della Pennsylvania University a Filadelfia, negli Stati Uniti. L’immagine che segue mostra una pagina del progetto originale:
Il rapporto di von Neumann viene universalmente considerato il progetto logico dell’architettura del computer moderno e ancora oggi la struttura interna dei computer è realizzata sulla base delle idee di von Neumann. Ovviamente le tecnologie attuali sono completamente diverse da quelle degli anni Cinquanta del secolo scorso; la figura sotto mostra la fotografia di un moderno microprocessore per PC. EDVAC è stato il primo computer a utilizzare una rappresentazione binaria dei numeri, ma l’idea realmente innovativa di von Neumann consiste nel concetto di «programma memorizzato»: la memoria del computer – oltre ai dati da elaborare e i risultati prodotti – contiene, codificate in formato numerico, anche le istruzioni di elaborazione, che possono essere di conseguenza facilmente modificate o sostituite, rendendo il computer lo strumento versatile che conosciamo.
L’architettura del computer di von Neumann prevede due componenti fondamentali tra loro interconnesse:
l’unità centrale di elaborazione denominata CPU (Central Processing Unit);
la cosiddetta memoria ad accesso casuale. (RAM, Random Access Memory)
La CPU è a sua volta composta dall’unità di controllo (CU, Control Unit), che sovrintende al funzionamento della macchina (in particolare al controllo della sequenza delle istruzioni da eseguire), e dall’unità aritmetico-logica (ALU, Arithmetic-Logic Unit), all’interno della quale si svolgono le operazioni specificate dalle istruzioni.
La memoria RAM è organizzata come una sequenza di locazioni – identificate da indirizzi consecutivi – ciascuna delle quali può contenere un numero intero che codifica un dato o un’istruzione.
La struttura di interconnessione tra CPU e RAM è costituita da un bus unidirezionale per gli indirizzi e da un bus bidirezionale per i dati e le istruzioni (FIGURA 1).
L’unità di controllo può richiedere alla memoria operazioni di lettura (trasferimento di dati o istruzioni dalla RAM alla CPU) o di scrittura (trasferimento di dati dalla CPU alla RAM). L’operazione richiesta coinvolge la locazione di memoria indirizzata dal numero inviato alla RAM mediante il bus indirizzi, mentre i dati (o le istruzioni) transitano da un’unità all’altra attraverso il bus dati/istruzioni.
Il numero di locazioni della memoria RAM è ovviamente finito, ma nei computer di fascia alta è piuttosto elevato (dell’ordine dei miliardi di byte). La dimensione della memoria si misura in multipli di byte che assumono le seguenti denominazioni:
1 Kilobyte (KB) = 1024 byte
1 Megabyte (MB) = 1024 KB = 1 048 576 byte
1 Gigabyte (GB) = 1024 MB = 1 073 741 824 byte
1 Terabyte (TB) = 1024 GB = 1 099 511 627 776 byte
L’uso della decima potenza di 2 (1024 = 2^10 ) rende più agevole l’uso di questi valori in formato binario e, al tempo stesso, i multipli non si discostano troppo dalle usuali potenze di 10 (1000, 1000000, 1000000000, 1000000000000) a cui siamo abituati.
OSSERVAZIONE L’idea fondamentale che sta alla base della macchina ideata da von Neumann più di mezzo secolo fa e su cui ancora oggi è fondata l’architettura dei computer è la coesistenza in un’unica unità di memoria2 sia dei dati da utilizzare per il calcolo, sia dei risultati intermedi e finali prodotti dall’esecuzione del calcolo, sia delle istruzioni che specificano i calcoli da effettuare sui dati memorizzati. Tutte queste «informazioni» sono memorizzate nella memoria della macchina in formato numerico.
L’unità ALU comprende normalmente un registro, denominato accumulatore, per la memorizzazione temporanea del risultato di operazioni aritmetiche effettuate tra il contenuto numerico dello stesso accumulatore e quello di una specifica locazione di memoria individuata da un indirizzo, in simboli:
A ← A ⛛ RAM[ind]
Il simbolo ⛛ rappresenta una qualsiasi delle operazioni aritmetiche che l'unità ALU è in grado di effettuare.
Completano l’architettura di una macchina di von Neumann due registri contenuti nell’unità di controllo:
il registro istruzioni (IR, Instruction Register), dove un’istruzione prelevata dalla memoria viene interpretata per essere eseguita;
il registro contatore di programma (PC, Program Counter), il cui valore individua l’indirizzo della locazione di memoria contenente la prossima istruzione da eseguire.
Il valore del registro PC viene automaticamente incrementato di una posizione – divenendo l’indirizzo della locazione successiva di memoria – nel corso dell’esecuzione da parte della CPU dell’istruzione stessa.
Il funzionamento di un computer è di conseguenza definito dalla seguente sequenza di fasi di lavoro che viene continuamente ripetuta:
FETCH: l’istruzione contenuta nella locazione di memoria indirizzata dal valore del registro PC viene letta e copiata nel registro IR dell’unità di controllo; successivamente viene incrementato il valore del PC in modo da indirizzare l’istruzione successiva da eseguire;
EXECUTE: l’istruzione presente nell’IR viene esaminata per riconoscere l’operazione che essa codifica e quindi eseguita.
I termini fetch (prelevamento) ed execute (esecuzione) identificano le due fasi del ciclo di funzionamento della CPU che un computer attuale è in grado di ripetere anche alcuni miliardi di volte al secondo.
OSSERVAZIONE Memorizzando ordinatamente le istruzioni che si intendono eseguire in locazioni successive della memoria RAM e stabilendo il valore iniziale del registro contatore di programma uguale all’indirizzo della prima di esse, si ottiene da parte del computer l’esecuzione automatica della sequenza di operazioni specificata dalle istruzioni. La sequenza di istruzioni da eseguire è denominata programma.
Ma quali istruzioni è in grado di eseguire un computer? La tabella sotto elenca e descrive un insieme di istruzioni minimo, ma sufficiente al funzionamento di una macchina di von Neumann la cui ALU sia in grado di eseguire le operazioni di addizione, sottrazione, moltiplicazione e divisione di numeri interi positivi e negativi e comprenda un registro accumulatore impegnato per le operazioni di lettura/scrittura dei dati dalla/nella memoria e per contenere i risultati delle operazioni aritmetiche.
Istruzione
Descrizione analitica
Descrizione sintetica
LOD ind
Carica il valore contenuto nella locazione di memoria di indirizzo ind
nel registro accumulatore
A <-- RAM[ind]
LOD #num
Carica direttamente il numero specificato nel registro accumulatore
A <-- num
STO ind
Copia il valore contenuto nel registro accumulatore nella locazione di memoria di indirizzo ind
RAM[ind] <-- A
ADD ind
Addiziona il valore contenuto nel registro accumulatore e il valore contenuto nella locazione di memoria di indirizzo ind
calcolando la somma nel registro accumulatore
A <-- A + RAM[ind]
ADD #num
Addiziona il valore contenuto nel registro accumulatore e il numero specificato calcolando la somma nel registro accumulatore
A <-- A + num
SUB ind
Sottrae il valore contenuto nella locazione di memoria di indirizzo ind
dal valore contenuto nel registro accumulatore calcolando la differenza nel registro accumulatore
A <-- A - RAM[ind]
SUB #num
Sottrae il numero specificato dal valore contenuto nel registro accumulatore calcolando la differenza nel registro accumulatore
A <-- A - num
MUL ind
Moltiplica il valore contenuto nel registro accumulatore con il valore contenuto nella locazione di memoria di indirizzo ind
calcolando il prodotto nel registro accumulatore
A <-- A * RAM[ind]
MUL #num
Moltiplica il valore contenuto nel registro accumulatore con il numero specificato calcolando il prodotto nel registro accumulatore
A <-- A * num
DIV ind
Divide il valore contenuto nel registro accumulatore per il valore contenuto nella locazione di memoria di indirizzo ind
calcolando il quoziente nel registro accumulatore (il quoziente è un numero intero anche se il valore contenuto in A non è divisibile per il valore contenuto nella memoria in questo caso viene trascurato il "resto")
A <-- A / RAM[ind]
DIV #num
Divide il valore contenuto nel registro accumulatore per il numero specificato calcolando il quoziente nel registro accumulatore (il quoziente è un numero intero alche se il valore contenuto in A non è divisibile per il numero specificato: in questo caso viene tralasciato il "resto")
A <-- A / num
JMP ind
Inserisce il valore ind
nel registro PC (la prossima istruzione eseguita sarà l'istruzione contenuta nella locazione di memoria avente indirizzo ind
, anziché l'istruzione contenuta nella posizione di memoria successiva a quella contenente questa istruzione)
PC <-- ind
JMZ ind
Inserisce il valore ind
nel registro PC se il contenuto del registro accumulatore è zero (in questo caso la prossima istruzione eseguita sarà l'istruzione contenuta nella locazione di memoria avente indirizzo ind
, altrimenti sarà l'istruzione contenuta nella locazione di memoria successiva a quella contenente questa istruzione
Se A è zero allora PC <-- ind
NOP
Operazione nulla
HLT
Interrompe il ciclo di funzionamento della macchina
Esempio MOLTIPLICAZIONE di un NUMERO per SUCCESSORE: Il seguente programma – memorizzato nella RAM a partire dall’indirizzo 0 – effettua il prodotto del numero N inizialmente contenuto nella locazione di indirizzo 100 con il suo successore N+ 1 e memorizza il risultato nella locazione di indirizzo 101:
Per comprenderne il funzionamento è utile «tracciare» in una tabella (TABELLA 1) le variazioni del contenuto del registro contatore di programma dell’unità di controllo, del registro accumulatore dell’unità ALU e delle locazioni di memoria interessate durante l’esecuzione del programma, nell’ipotesi che inizialmente la locazione di indirizzo 100 della RAM contenga il valore «9».
OSSERVAZIONE: Il registro PC contiene sempre l’indirizzo della prossima istruzione da eseguire. Esso viene incrementato prima dell’esecuzione dell’istruzione presente nel registro IR (prelevata dalla locazione di memoria indirizzata dal precedente valore del registro PC stesso), di conseguenza nella tabella di traccia risulta sfalsato di una locazione rispetto all’istruzione corrispondente. Normalmente quindi un programma viene eseguito in modo sequenziale.
OSSERVAZIONE: Gli indirizzi di memoria 100 e 101 potrebbero rappresentare locazioni speciali il cui contenuto viene in realtà fornito da un dispositivo di input, come per esempio una tastiera, o a un dispositivo di output, per esempio un monitor. L’unità di input/output dei dati e dei risultati costituisce il terzo componente che von Neumann aveva previsto nel progetto logico della sua macchina.
Esempio PARI e DISPARI: Il seguente programma – memorizzato nella RAM a partire dall’indirizzo 0 – determina la metà del numero N inizialmente contenuto nella locazione di indirizzo 100 se esso è pari, altrimenti – se N è dispari – determina il successore del suo triplo e memorizza il risultato nella locazione di indirizzo 101:
Dovendo diversificare il comportamento del programma per i numeri pari e i numeri dispari, le istruzioni dal 7 al 12 sono eseguite in modo alternativo: le istruzioni contenute nelle locazioni di memoria dall’indirizzo 7 all’indirizzo 9 nel caso che il numero sia dispari, le istruzioni contenute nelle locazioni di memoria dall’indirizzo 11 all’indirizzo 12 nel caso che sia pari; le istruzioni in nero sono eseguite in entrambe le situazioni e in particolare quelle contenute nelle locazioni di memoria dall’indirizzo 0 all’indirizzo 6 determinano se il numero è pari o dispari, calcolando il resto della sua divisione per 2 e sfruttando la caratteristica della ALU di effettuare esclusivamente calcoli con numeri interi. Il confronto tra le tabelle di tracciatura dei valori contenuti nel registro accumulatore dell’unità ALU e nelle locazioni di memoria interessate in due situazioni iniziali diverse – nel primo caso la locazione di indirizzo 100 della RAM contiene il valore «12» (pari), nel secondo caso il valore «13» (dispari) – consente di comprendere come le istruzioni di «salto» ("jump" in inglese) alterano il meccanismo di incremento sequenziale del registro PC (TABELLE 2 e 3).
OSSERVAZIONE: L'istruzione JMP
è nota come salto incondizionato, in quanto il valore del registro contatore di programma viene modificato in ogni caso, mentre l'istruzione JMZ
è nota come salto condizionato perché il valore del registro contatore di programma viene modificato solo se sussiste una specifica condizione (in questo caso data dal fatto che il contenuto del registro accumulatore sia il valore "0").
Esempio SOMMA primi N numeri: Il seguente programma – memorizzato nella RAM a partire dall’indirizzo 0 – determina la somma dei primi N numeri, con il valore N inizialmente contenuto nella locazione di indirizzo 100 e memorizza il risultato nella locazione di indirizzo 101:
Nell’ipotesi che inizialmente la locazione di indirizzo 100 della RAM contenga il valore «4», la somma dei primi 4 numeri è 1 + 2 + 3 + 4 = 10.
OSSERVAZIONE Il programma dell’esempio precedente illustra una tecnica di programmazione della macchina di von Neumann molto utile; le locazioni di memoria di indirizzo 100 e 101 sono utilizzate ciascuna per memorizzare nel corso del calcolo i risultati intermedi: la locazione di indirizzo 101 contiene il risultato parziale della somma, mentre quella di indirizzo 100 contiene il prossimo valore da sommare e, diminuendo fino a divenire 0, determina la fine della ripetizione del calcolo stesso.
A ogni istruzione è associato un codice numerico - usualmente chiamato codice operativo - che le identifica: le istruzioni che devono specificare un valore numerico o un indirizzo di memoria hanno un codice numerico composto dal codice operativo più una parte variabile che consente di specificare il numero o l'indirizzo.
E' compito dell'unità di controllo della CPU leggere il codice numerico dell'istruzione contenuto nella locazione di memoria indirizzata dal registro PC e interpretarlo al fine di eseguire l'operazione specificata.
OSSERVAZIONE: La memoria RAM di un computer contiene esclusivamente numeri, alcuni dei quali codificano le istruzioni del programma da eseguire, mentre altri rappresentano i valori a cui si applicano le operazioni aritmetiche specificate dalle istruzioni eseguite.
Scrivere un programma per il computer di von Neumann che converta la temperatura espressa in °C, specificata nella locazione di memoria di indirizzo 100, in una temperatura espressa in °F nella locazione di memoria di indirizzo 101 (per convertire da °C in °F occorre moltiplicare per 9, dividere per 5 e sommare 32; esempio: 100 °C sono equivalenti a 100 × 9 : 5 + 32 = 900 : 5 + 32 = 180 + 32 = 212°F).
Codice in linguaggio macchina:
Scrivere un programma per il computer di von Neumann che calcoli il fattoriale di un numero N inizialmente contenuto nella locazione di memoria di indirizzo 100, memorizzando il risultato nella locazione di memoria di indirizzo 101.
Il fattoriale di un numero naturale N (in simboli N!) è il prodotto di tutti i numeri naturali compresi tra 1 e N (N incluso); per definizione 0! = 1.
Codice in linguaggio macchina:
Scrivere un programma per il computer di von Neumann che verifichi se un numero N inizialmente contenuto nella locazione di memoria 100 è un multiplo di 11; in caso affermativo dovrà inserire il valore 1 («vero») nella locazione di memoria di indirizzo 101, in caso negativo dovrà inserirvi il valore 0 («falso»).
Codice in linguaggio macchina:
L’ordine di grandezza di un numero intero è il numero di volte per cui è possibile dividere il numero stesso per 10 prima di ottenere 0 come risultato (esempio: 123 → 123 : 10 = 12 → 12 : 10 = 1 → 1 : 10 = 0, l’ordine di grandezza è 2). Scrivere un programma per il computer di von Neumann che determini l’ordine di grandezza di un numero N inizialmente contenuto nella locazione di memoria di indirizzo 100 inserendo il risultato nella locazione di memoria di indirizzo 101.
Codice in linguaggio macchina:
Scrivere un programma per il computer di von Neumann che implementi l’algoritmo definito dal seguente diagramma a blocchi e determinare lo scopo dell’algoritmo.
Codice in linguaggio macchina:
Il programma esegue la moltiplicazione tra X
e Y
con valori memorizzati all'indirizzo 100 e 101 e salva il risultato Z
all'indirizzo 102. Utilizza l'algoritmo del cosiddetto Metodo del Contadino Russo.
Per calcolare la potenza di un numero utilizzando un computer è possibile programmarlo usando il linguaggio del processore, come nel seguente esempio, in cui inizialmente i valori della base e dell’esponente sono rispettivamente memorizzati nelle locazioni di memoria di indirizzo 100 e 101 e il valore del risultato viene memorizzato dal programma nella locazione di memoria di indirizzo 102:
Ma i programmatori preferiscono ricorrere a linguaggi più astratti e meno dipendenti dalle caratteristiche di uno specifico processore. Il seguente frammento di codice C effettua esattamente lo stesso calcolo (inizialmente i valori della base e dell’esponente sono rispettivamente memorizzati nelle variabili x e y e il valore della potenza viene calcolato nella variabile z):
I programmi in linguaggio C, per essere eseguiti da un particolare processore, devono essere prima «compilati»: la compilazione converte il codice sorgente in linguaggio C in istruzioni per lo specifico processore del computer su cui si intende eseguire il programma stesso.
Un simulatore di macchina di Von Neumann è possibile accederci tramite applicazione web a questo indirizzo:
Il simulatore permette di caricare il codice assembler ed eseguirlo, come se fosse la CPU che lo esegue e facendo vedere passo-passo le fasi dell'esecuzione, lo stato dei registri e lo stato della memoria.
Il simulatore a differenza del codice visto sopra, e come in effetti avviene per l'assembler reale, non utilizza l'indirizzo numerico della cella di memoria ma utilizza delle "ETICHETTE" ("LABEL" in inglese) per rappresentare gli indirizzi delle celle di memoria.
Sarà il compilatore del codice assembler (assemblatore) che tradurrà questa "etichetta" in un valore, cioè l'indirizzo della cella di memoria, quando produrrà il codice oggetto.
L'assemblatore, lavora a più step (fasi):
Nella prima fase traduce tutte le occorrenze di un etichetta, in un indirizzo di memoria: costruisce in prima fase una TABELLA DEI SIMBOLO che associa a un'etichetta un indirizzo; utilizza questa tabella per fare la traduzione;
Il codice ottenuto nella prima fase poi viene tradotto in codice macchina: ogni operazione viene tradotta nel proprio opportuno codice binario per produrre il codice eseguibile (codice macchina che la CPU è in grado di eseguire);
Possiamo utilizzare questo simulatore per eseguire i programmi visti sopra, leggermente modificati perché utilizziamo le etichette al posto degli indirizzi di memoria: sarà il programma che eseguirà la conversione da etichette in indirizzi, prima di eseguire il codice.
Il simulatore utilizza ancora gli indirizzi di memoria numerici per le istruzioni di "salto", mentre nell'assembler "reale" anche le istruzioni di "salto" utilizzano le etichette.
Sotto gli esempi che possiamo provare.
In memoria X dato input, in Y dato output
In memoria X data input, in Y l'output.
X l'input, Y l'output
X come input, Y l'output
X input, Y output
X input, Y output
X input, Y output
X, Y input e Z ouput
Codice del simulatore: