Sei sulla pagina 1di 6

01/03/18-Lezione 1

Linguaggi di programmazione per sistemi industriali (LPSI)


Stefano Michieletto: stefano.michieletto@unipd.it

Linguaggi: C e MIPS (basso livello)

Due turni a settimana di laboratorio da scegliere

Libri: Programmazione in C, King/Foundations of Computer Science C edition Alfred V. Aho, Jeffrey D.


Ullman

25 marzo fine ultimo per pensare all’idea di progetto facoltativo da proporre al prof con cui si aggiusterà.

Si può anche proporre il progetto e poi anche non farlo.

Esame ufficiale: Scritto con programmazione in C o in MIPS.

Teoria delle architetture degli elaboratori (Architettura di Von Neumann)


CPU

Main Memory Unit BUS


Secondary Memory Unit

Input Output Devices

Il BUS permette la comunicazione tra i vari componenti del calcolatore. L’unità di controllo in questo modo
può interfacciarsi con tutte le altre entità allo stesso momento.

Dettaglio dell’architettura di Von Neumann

Dentro alla CPU c’è un’unità di controllo (CU) che gestisce il flusso del programma (le varie istruzioni che
vanno eseguite dal calcolatore) e l’unità aritmetico logica che permette di eseguire le operazioni di tipo
matematico (somma, prodotto, divisione, confronto). Per fare entrambe le cose la CPU ha bisogno di
accedere alla memoria. L’idea per Von Neumann era che l’accesso per la memoria (volatile e non) era lo
stesso (dato che istruzioni e dati sono codificate allo stesso modo).

I dati e le istruzioni si mescolano insieme all’interno della memoria.

BUS
Tutte le parti sono connesse da un unico BUS di connessione

La memoria e i gli altri devices sono controllati mediante CPU. I dati passano attraverso il BUS tramite il
metodo “half-duplex” ossia metà bus riceve e metà manda i dati.

Dati e istruzioni nell’architettura Von Neumann


La cosa più rivoluzionaria dell’architettura di Von Neumann è che i dati sono insieme alle istruzioni.

I dati e le istruzioni sono poste in memoria in ordine lineare, indicizzate da un numero di indirizzo (location-
number). Non importa il tipo di dato che contiene, ogni blocchetto di memoria ha un indirizzo. L’intero
blocco di memoria può essere suddiviso in diversi blocchi di memoria di diversa dimensione (i sistemi
operativi a 64 bit usano la memoria in blocchi da 64, al contrario i sistemi a 32 a 32).

I programmi (sequenza ordinata di istruzioni) vanno caricati in maniera ordinata in modo che il processo
avvenga in maniera lineare.

Come si fa a gestire il cambio di programma o come l’istruzione viene eseguita?


Tutti i cambi di istruzione vengono eseguiti tramite dei JUMP ossia delle istruzioni che permette di saltare
da un punto di memoria (fine del programma) a un altro (inizio di un nuovo programma). Il “jump” può
essere condizionato o meno da opportune condizioni proprie per ogni programma.

Le istruzioni vengono memorizzate come una serie di numeri binari (serie di 1 e 0).

I numeri binari vengono poi rappresentati attraverso una notazione esadecimale (per facilità di utilizzo).

[Foto slide rappresentazione della memoria.]

N.B. ogni indirizzo in memoria ha al suo interno 4 blocchi di memoria da 8 bit. Quando si passa da un blocco
al successivo si salta quindi di quattro indirizzi.

Vantaggi dell’architettura Von Neumann


L’architettura Von Neumann ha i seguenti vantaggi:
 i dati e le istruzioni possono essere gestiti allo stesso modo dalla CU, ciò semplifica molto la
gestione della macchina alla CU.
 con questa modalità la complessità passa al software, l’hardware rimane più semplice (costo di
produzione più basso, tempi più bassi).
 essendo il BUS lo stesso, anche i dati dai device esterni rimangono accessibili allo stesso modo (i
vantaggi rimangono gli stessi).
 l’organizzazione della memoria è in mano al programmatore (16, 32 o 64 bit): si ha più possibilità di
espandere il programma che si ha in mente.

Gli svantaggi dell’architettura (punto chiave nelle architetture moderne)


Gli svantaggi di questa architettura sono:
 non è possibile accedere ai dati e all’istruzione allo stesso momento (dato che il canale di
comunicazione è lo stesso non possono essere eseguite due operazioni insieme). Si crea quindi un
collo di bottiglia nel BUS. Se il BUS è occupato da un’altra informazio non si può comunicare.
 dato che i dati e le istruzioni sono nella stessa area di memoria è possibile che un programma scriva
sopra a se stesso.

Architettura Harvard

È un’architettura che è andata in competizione con quella di Von Neumann.

Si ha sempre una unità di processing. Da un lato si usa un bus per eseguire il collegamento tra Input/Output
devices e le memorie. Mentre si ha un bus separato per accedere al program-memory. In questo caso i dati
sono separati dalle istruzioni.

Vantaggi dell’architettura Harvard


 Si può accedere parallelamente ai dati e alle istruzioni (si velocizza il processo)
 Dati e istruzioni possono avere lo stesso indirizzo ma non si avrà mai confusione tra gli indirizzi
 Le diverse memorie possono avere dimensioni differenti (32, 64 bit ecc)
 I programmi non si possono sovrascrivere da soli (portando a crash e bug nei programmi).

Gli svantaggi dell’architettura Harvard


 Le unità di controllo sono più complesse perché devono essere capaci di accedere a due tipi di
memorie che potrebbero essere diverse (costano di più perché più difficili da progettare e da
produrre)
 Le due memorie, dato che sono divise non possono essere utilizzate per memorizzare dati e
programmi insieme (i dati vanno in una memoria il programma nell’altra).

Qual è la migliore architettura, quindi?


Nei computer, ora si utilizza un’architettura Harvard modificata (al posto di avere due memorie distinte) si
usa una memoria cache. Questa è una memoria speciale nella quale è possibile caricare in maniera veloce il
programma per essere eseguito.
Fino a 20 anni fa Von Neumann veniva utilizzato per computer con architetture molto complesse.
L’architettura Harvard era consigliata per sistemi embedded (importanza di risposta real-time).

Componenti della CPU


Ha tre parti principali: Arithmetic Logic Unit (che effettua le operazioni matematiche), Control-unit (gestisce
il fluire del programma), Registri (memoria utilizzata per accedere velocemente ai dati richiesti)

Le CPUs lavorano in maniera ciclica. La frequenza di lettura dei dati che viene eseguita ciclicamente è
gestita da un clock avente una frequenza ben definita.

Operazioni cicliche eseguite dalla Cpu:


 Fetch: recupera l’istruzione seguente e la carica in memoria (l’istruzione puntata dall’instruction-
register)
 Decode: interpreta l’istruzione data, decodifica eventuali indirizzi di memoria utili per caricare dati
 Execute: esegue l’istruzione e fa sì che il sistema si aggiorni (incrementa il program-counter in
maniera sequenziale).
Il ciclo a questo punto ricomincia.

Il linguaggio macchina
A una serie di 0 e 1 corrisponde un’istruzione o un dato che possono essere eseguiti in modo diretto dalla
CPU. L’insieme d’istruzioni disponibili varia da CPU a CPU.

02/03/17-Lezione 2

Set di istruzioni
 Data transfer: caricare e memorizzare dati in memoria
 Aritmetico logico: eseguite dalla ALU
 Jumps: servono per cambiare il flusso del programma. Si dividono in condizionati (si esegue il salto
solo se la condizione è soddisfatta) e incondizionati (si salta in ogni caso). Nei salti si va a cambiare il
valore del program counter

Linguaggi assembler
Un programma (assebler) che viaggia all’interno della CPU traduce le istruzioni in dei comandi leggibili dalla
CPU (in binario). I computer sono delle macchine digitali quindi necessitano di un codice in binario per
gestire le informazioni.

Al giorno d’oggi il programma assembler (caricato in CPU) effettua questa operazione.

All’inizio si utilizzavano delle schede o nastri forati che rappresentavano gli 0 e gli 1.

Il programmatore scrive l’istruzione in un codice ben definito senza scriverla direttamente in binario (molto
più semplice e veloce). Si può inoltre etichettare gli slot di memoria (rende più facile richiamare e leggere lo
slot di memoria). In questo modo il linguaggio è molto più leggibile che il binario.

La tipologia di linguaggio dipende dal tipo di CPU gestita.

N.B. C’è una corrispondenza “uno a uno” tra il codice assembly al codice binario. (Si può andare dal binario
all’assembly indifferentemente). Ciò rende possibile trovare gli errori (si ha un’univoca interpretazione di
un’informazione quindi si può trovare la parte di programma con degli errori).
L’operazione di traduzione da assembly al binario viene fatta prima del decode (si fa a parte così si evitano
gli errori in fase di codifica; si può seguire il linguaggio assembly e l’esecuzione del programma per
debuggare).

L’assembly è stato un grosso passo avanti rispetto al codice binario.

Dato che le istruzioni dipendono fortemente dalla CPU, se devo scrivere il programma per due differenti
devo scrivere (a volte) tutto il programma. Non è proponibile per i programmi gestire ogni CPU con le
diverse istruzioni.

È inoltre difficile fare il debug dato il grande numero di istruzioni che servono per fare un’operazione anche
molto semplice.

Come si risolvono questi problemi?


Sono stati inventati i linguaggi a alto livello (si ha un’astrazione verso l’alto: si scrive il codice e poi qualcun
altro si occuperà di tradurre il programma per le diverse CPU): Fortran (54), Cobol (59 per le banche), Basic
(64), Pascal (70), C (73), C++ (83), Java (95).

Le istruzioni quindi sono indipendenti dalla tipologia di CPU che si ha a disposizione.

Traduzione fra linguaggio ad alto livello a basso


Per prima cosa si utilizza un compilatore. La compilazione avviene in due passi: prima si crea un file oggetto
(traduzione in file binario del codice sorgente). A questo file manca, per funzionare il collegamento alle
librerie (nostre o del programma), che vanno integrate all’interno del codice. Il compito di collegare le
librerie è del linker. Il codice in binario è specifico della CPU per il quale il compilatore ha fatto la traduzione
(il file oggetto creato è giusto solo per la CPU per il quale è stato creato). Per arrivare a una compilazione
corretta per una CPU diversa bisogna partire dal codice sorgente.

Intepreter
In alcuni casi si ha una precompilazione per generare un codice intermedio (può essere una semplice
ottimizzazione o effettivamente un codice precompilato che è una sorta di codice binario che può essere
eseguito in determinate maniere). In un secondo momento si utilizza un interpreter che effettua la
traduzione delle informazioni in codice binario leggibile dalla CPU.

Differenze fra compiler e interpreter


Compiler Interpreter
Legge tutto il programma e lo traduce tutto Traduce il programma riga per riga
assieme in linguaggio macchina
Ha bisogno di un tempo più lungo per analizzare il Ha bisogno di meno tempo per analizzare il codice
codice
Tempo di esecuzione minore Tempo di esecuzione maggiore
Genera un file oggetto Genera un file oggetto intermedio
Il file oggetto necessita delle librerie (occupa più Più efficiente
spazio)
Genera messaggi di errore dopo aver letto tutto il Genera messaggi di errore subito dopo averli
programma trovati (in questo caso si stoppa)
Debugging difficile Debugging facile
Il programma viene compilato solo una volta Il programma viene compilato ogni volra
Usato da C, C++ Usato da Python, RUby
Efficienza
L’efficienza ha un ruolo fondamentale in applicazioni nella quale le performance.

Per aumentare le performance:

 Rimuovere il codice non necessario e ridondante


 Utilizzare al meglio la memoria e usare la memoria non volatile
 Utilizzare componenti riutilizzabili dove possibile
 Ottimizzare l’accesso ai dati
 Assicurare la velocità più alta per l’esecuzione del programma

Portabilità
La portabilità è la possibilità di utilizzare un software in diversi ambienti.

La portabilità è la chiave per abbattere i costi di sviluppo, perché software con le stesse funzionalità
possono essere prodotti per diverse piattaforme.