mirror of
https://github.com/Steffo99/appunti-magistrali.git
synced 2024-11-24 03:04:18 +00:00
Importa Architettura dei calcolatori da Steffo99/appunti-universitari
This commit is contained in:
parent
a8b8ad3a87
commit
2e748ea187
18 changed files with 684 additions and 0 deletions
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Assemble RISV-V assembly (.s) files
|
||||||
|
riscv64-unknown-elf-as -g -o "$1.o" "$1.riscv"
|
||||||
|
# Link RISC-V object (.o) files
|
||||||
|
riscv64-unknown-elf-ld -o "$1.elf" "$1.o"
|
||||||
|
|
||||||
|
exit $?
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
riscv64-unknown-elf-objdump "$1.elf"
|
||||||
|
|
||||||
|
exit $?
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Compile using g++
|
||||||
|
riscv64-unknown-elf-g++ -o "$1.elf" "$1.cc"
|
||||||
|
|
||||||
|
exit $?
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Compile using g++
|
||||||
|
riscv64-unknown-elf-gcc -o "$1.elf" "$1.cc"
|
||||||
|
|
||||||
|
exit $?
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
riscv64-unknown-elf-gprof "$1.elf"
|
||||||
|
|
||||||
|
exit $?
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
riscv64-unknown-elf-readelf "$1.elf" -a
|
||||||
|
|
||||||
|
exit $?
|
17
2 - Architettura dei calcolatori/0 - Script forniti/run.sh
Normal file
17
2 - Architettura dei calcolatori/0 - Script forniti/run.sh
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Sintassi: ./run.sh nomefile
|
||||||
|
# Non mettete l'estensione!
|
||||||
|
# Potrebbe esserci bisogno di installare xterm prima
|
||||||
|
# Se non funziona, installatelo con
|
||||||
|
# sudo apt install xterm
|
||||||
|
|
||||||
|
echo "Cleaning phase"
|
||||||
|
rm "$1.o"
|
||||||
|
rm "$1.elf"
|
||||||
|
echo "Compilation phase"
|
||||||
|
riscv64-unknown-elf-as -g -o "$1.o" "$1.riscv"
|
||||||
|
riscv64-unknown-elf-ld -o "$1.elf" "$1.o"
|
||||||
|
echo "Debugging phase"
|
||||||
|
xterm -e "qemu-riscv64 -g 1234 \"$1.elf\"" &
|
||||||
|
xterm -e "riscv64-unknown-elf-gdb --ex=\"target remote localhost:1234\" \"$1.elf\"" &
|
||||||
|
wait
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Architettura dei Calcolatori
|
||||||
|
|
||||||
|
Docente: [**Andrea Mariongiu**](mailto:andrea.mariongiu@unimore.it)
|
||||||
|
|
||||||
|
Crediti: **9 CFU** (72 ore di lezione)
|
||||||
|
|
||||||
|
### Materiale
|
||||||
|
|
||||||
|
Libri:
|
||||||
|
- `ISBN9780128122761`
|
||||||
|
- `ISBN9788871924618`
|
||||||
|
|
||||||
|
**[Sito web](http://algo.ing.unimo.it/people/andrea/Didattica/Architetture/index.html)**
|
||||||
|
|
||||||
|
[Slides 2017](http://dolly.fim.unimore.it/2017/course/view.php?id=57)
|
||||||
|
|
||||||
|
### Esame
|
||||||
|
|
||||||
|
- Prova scritta
|
||||||
|
- Serie di domande a risposta singola, multipla e libera, simile a Programmazione 1
|
||||||
|
- Rimane valida fino alla fine della sessione seguente
|
||||||
|
- Prova orale
|
||||||
|
- Può essere sostituita con lo sviluppo di un _progettino_
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
# I principi dell'architettura dei calcolatori
|
||||||
|
|
||||||
|
## Gli otto grandi principi
|
||||||
|
|
||||||
|
- Progetta per la **Legge di Moore**
|
||||||
|
- **Astrai** per semplificare il design
|
||||||
|
- Velocizza i **casi più comuni**
|
||||||
|
- Sfrutta il **parallelismo**
|
||||||
|
- Instruction level parallelism
|
||||||
|
- In una sola CPU vengono realizzati più calcoli in parallelo
|
||||||
|
- Sfrutta le **pipeline**
|
||||||
|
- Pipelining
|
||||||
|
- Separare le azioni del processore in fasi
|
||||||
|
- Il fetch è separato dal decoding, mentre decodifico un'istruzione posso già fetchare quell'altra
|
||||||
|
- **Prevedi** le istruzioni successive
|
||||||
|
- **Gerarchizza** le memorie per velocità di accesso
|
||||||
|
- La CPU cerca sempre di accedere alle memorie più veloci
|
||||||
|
- Rendi affidabile con la **ridondanza**
|
||||||
|
|
||||||
|
### Parallelismo
|
||||||
|
|
||||||
|
> Chiedo a 100 persone di fare una moltiplicazione ciascuna invece che fare 100 moltiplicazioni io da solo
|
||||||
|
|
||||||
|
### Pipelining
|
||||||
|
|
||||||
|
![Senza](https://upload.wikimedia.org/wikipedia/commons/2/2c/Nopipeline.png)
|
||||||
|
|
||||||
|
![Con](https://upload.wikimedia.org/wikipedia/commons/2/21/Fivestagespipeline.png)
|
||||||
|
|
||||||
|
### Gerarchia delle memorie
|
||||||
|
|
||||||
|
> Hit or miss? I hope they never miss.
|
||||||
|
|
||||||
|
La CPU prova sempre ad accedere alle memorie più in alto di questa lista, effettuando un **hit** se trova i dati che le servono in una data memoria ed effettuando un **miss** se invece non trova i dati necessari e deve accedere a uno strato inferiore.
|
||||||
|
|
||||||
|
- [Cache](https://it.wikipedia.org/wiki/Cache) (piccolissima e velocissima)
|
||||||
|
- [Random Access Memory](https://it.wikipedia.org/wiki/RAM) (piccola e veloce)
|
||||||
|
- [Solid State Drive]() (medio e di media velocità)
|
||||||
|
- [Hard Disk Drive](https://it.wikipedia.org/wiki/Disco_rigido) (grosso, ma lento)
|
||||||
|
- [Tape storage](https://en.wikipedia.org/wiki/Magnetic_tape_data_storage) (enorme, ma lentissimo)
|
||||||
|
|
||||||
|
I _miss_ rallentano l'esecuzione dei programmi, in quanto causano ritardi nel ritrovamento dei dati, passando da _meno di 1 µs_ per le cache a _più di 1 ms_ per gli hard disk.
|
||||||
|
|
||||||
|
Inoltre, esistono due tipi di cache, anch'essi gerarchizzati:
|
||||||
|
|
||||||
|
- [SRAM](https://it.wikipedia.org/wiki/SRAM)
|
||||||
|
- [DRAM](https://it.wikipedia.org/wiki/DRAM)
|
|
@ -0,0 +1,41 @@
|
||||||
|
# Dentro il processore
|
||||||
|
|
||||||
|
## Come sono fatti i circuiti integrati?
|
||||||
|
|
||||||
|
Sono composti da un layer di silicio (semiconduttore), più delle maschere di conduttori (µfili metallici), isolanti (plastica e vetro) e transistor.
|
||||||
|
|
||||||
|
### Processo di produzione
|
||||||
|
|
||||||
|
- Lingotto di silicio
|
||||||
|
- Tagliato in _wafer_ da 2mm
|
||||||
|
- Creazione dei _patterned wafer_
|
||||||
|
- Testing dei _patterned wafer_
|
||||||
|
- Taglio i _tested wafers_
|
||||||
|
- Creo un package con i _tested dies_
|
||||||
|
- Testo i _packaged dies_
|
||||||
|
- Spedisco i _tested packaged dies_ ai clienti.
|
||||||
|
|
||||||
|
### Lo yield
|
||||||
|
|
||||||
|
Lo yield è quanti _tested packaged dies_ riesco a produrre da un singolo lingotto di silicio.
|
||||||
|
|
||||||
|
## Come definiamo la performance di un processore?
|
||||||
|
|
||||||
|
Ci sono varie metriche per definirla:
|
||||||
|
|
||||||
|
- **Response time**: tempo impiegato a compiere un'operazione
|
||||||
|
- **Throughput**: operazioni fatte in un'unità di tempo
|
||||||
|
|
||||||
|
### Il clock
|
||||||
|
|
||||||
|
Il clock della CPU è il timer che scandisce le operazioni della CPU.
|
||||||
|
|
||||||
|
Ha, ovviamente, un periodo e una frequenza costanti.
|
||||||
|
|
||||||
|
### Il CPU time
|
||||||
|
|
||||||
|
Il tempo impiegato da una CPU a compiere una determinata operazione è dato da `numero di cicli di clock richiesti * periodo di clock`, ed è detto **CPU time**.
|
||||||
|
|
||||||
|
### Il Cost per instruction (CPI)
|
||||||
|
|
||||||
|
Alcune istruzioni di una CPU potrebbero richiedere più di un ciclo di clock.
|
165
2 - Architettura dei calcolatori/1 - Appunti/03 - Assembly.md
Normal file
165
2 - Architettura dei calcolatori/1 - Appunti/03 - Assembly.md
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
# Assembly
|
||||||
|
|
||||||
|
Useremo il RISC-V.
|
||||||
|
Esso è little-endian, ed è indirizzato a gruppi di 8 bit.
|
||||||
|
|
||||||
|
## Registri
|
||||||
|
|
||||||
|
RISC-V ha a disposizione **32** registri da 64 bit, che vanno da `x0` a `x31`.
|
||||||
|
|
||||||
|
Un dato a 32 bit viene chiamato **word**, uno a 64 bit viene chiamato **doubleword**.
|
||||||
|
|
||||||
|
- `x0`: costante
|
||||||
|
- `x1`: indirizzo risultato
|
||||||
|
- `x2`: indirizzo della cima dello stack
|
||||||
|
- `x3`: indirizzo dell'area di memoria con variabili globali
|
||||||
|
- `x4`: indirizzo dell'area di memoria specifica del thread attivo
|
||||||
|
- `x5`: registro temporaneo
|
||||||
|
- `x6`: registro temporaneo
|
||||||
|
- `x7`: registro temporaneo
|
||||||
|
- `x8`: frame pointer
|
||||||
|
- `x9`: registro salvato, può essere copiato in memoria
|
||||||
|
- `x10`: argomento o risultato delle funzioni
|
||||||
|
- `x11`: argomento o risultato delle funzioni
|
||||||
|
- `x12`: argomento delle funzioni
|
||||||
|
- `x13`: argomento delle funzioni
|
||||||
|
- `x14`: argomento delle funzioni
|
||||||
|
- `x15`: argomento delle funzioni
|
||||||
|
- `x16`: argomento delle funzioni
|
||||||
|
- `x17`: argomento delle funzioni
|
||||||
|
- `x18`: registro salvato, può essere copiato in memoria
|
||||||
|
- `x19`: registro salvato, può essere copiato in memoria
|
||||||
|
- `x20`: registro salvato, può essere copiato in memoria
|
||||||
|
- `x21`: registro salvato, può essere copiato in memoria
|
||||||
|
- `x22`: registro salvato, può essere copiato in memoria
|
||||||
|
- `x23`: registro salvato, può essere copiato in memoria
|
||||||
|
- `x24`: registro salvato, può essere copiato in memoria
|
||||||
|
- `x25`: registro salvato, può essere copiato in memoria
|
||||||
|
- `x26`: registro salvato, può essere copiato in memoria
|
||||||
|
- `x27`: registro salvato, può essere copiato in memoria
|
||||||
|
- `x28`: registro temporaneo
|
||||||
|
- `x29`: registro temporaneo
|
||||||
|
- `x30`: registro temporaneo
|
||||||
|
- `x31`: registro temporaneo
|
||||||
|
|
||||||
|
## Operazioni
|
||||||
|
|
||||||
|
### Operazioni aritmetiche
|
||||||
|
|
||||||
|
Sono sempre formate da tre operandi: registro a cui il risultato verrà assegnato e gli argomenti dell'operazione.
|
||||||
|
|
||||||
|
- `add`: addizione
|
||||||
|
- `sub`: sottrazione
|
||||||
|
- `addi`: addizione di costanti
|
||||||
|
|
||||||
|
#### Esempio
|
||||||
|
|
||||||
|
```assembly
|
||||||
|
add a, b, c
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Equivalente Python
|
||||||
|
|
||||||
|
```python
|
||||||
|
a = b + c
|
||||||
|
```
|
||||||
|
|
||||||
|
### Operazioni di trasferimento dati
|
||||||
|
|
||||||
|
Permettono di caricre / scaricare dati dalla memoria.
|
||||||
|
|
||||||
|
### Operazioni logiche
|
||||||
|
|
||||||
|
Consentono di effettuare operazioni di logica.
|
||||||
|
|
||||||
|
- `and`
|
||||||
|
- `or`
|
||||||
|
- ...
|
||||||
|
|
||||||
|
### Operazioni di shift
|
||||||
|
|
||||||
|
Consentono di spostare i bit di un registro di alcune posizioni, moltiplicando o dividendo effettivamente per potenze di 2.
|
||||||
|
|
||||||
|
### Operazioni condizionali
|
||||||
|
|
||||||
|
Consentono di saltare ad un'altra serie di istruzioni se certe condizioni si verificano.
|
||||||
|
|
||||||
|
- `blt`: minore di, signed
|
||||||
|
- `bge`: maggiore di, signed
|
||||||
|
- `bltu`: minore di, unsigned
|
||||||
|
- `bgeu`: maggiore di, unsigned
|
||||||
|
|
||||||
|
### Operazioni non-condizionali
|
||||||
|
|
||||||
|
Consentono di saltare ad un'altra serie di istruzioni in qualunque caso.
|
||||||
|
|
||||||
|
#### Chiamare una procedura
|
||||||
|
|
||||||
|
> `jal x1, ProcedureLabel`
|
||||||
|
|
||||||
|
Salva l'indirizzo dell'istruzione successiva in x1, poi salta a ProcedureLabel.
|
||||||
|
|
||||||
|
#### Ritornare da una procedura
|
||||||
|
|
||||||
|
> `jalr x0, 0(x1)`
|
||||||
|
|
||||||
|
Ritorna all'indirizzo puntato da `x1`.
|
||||||
|
|
||||||
|
## Memoria
|
||||||
|
|
||||||
|
Per accedere a una posizione in memoria, si usa `offset(indirizzo)`.
|
||||||
|
|
||||||
|
##### Esempio
|
||||||
|
|
||||||
|
`64(x22)` accede al 64° byte dopo la posizione di memoria archiviata in `x22`.
|
||||||
|
|
||||||
|
## Segni
|
||||||
|
|
||||||
|
E' possibile usare rappresentazione standard o a complemento-a-2.
|
||||||
|
|
||||||
|
Possiamo usare la **sign extension** quando abbiamo un dato che vogliamo portare a un numero di bit maggiore di quello che è.
|
||||||
|
|
||||||
|
##### Esempio
|
||||||
|
|
||||||
|
```
|
||||||
|
[0]101 0101 --> 0000 0000 [0]101 0101
|
||||||
|
[1]100 0000 --> 1111 1111 [1]100 0000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Operazioni di sincronizzazione
|
||||||
|
|
||||||
|
- `lr`: load reserved
|
||||||
|
- `sc`: store conditional
|
||||||
|
|
||||||
|
#### Load reserved
|
||||||
|
|
||||||
|
```
|
||||||
|
lr rd, (rs1)
|
||||||
|
```
|
||||||
|
|
||||||
|
> Non ho la minima idea di come funzioni.
|
||||||
|
|
||||||
|
Controlla se `rs1` è bloccato; se sì, mette un valore != 0 in `rd`...?
|
||||||
|
|
||||||
|
> Forse... crea un lock in `rd`?
|
||||||
|
|
||||||
|
#### Store conditional
|
||||||
|
|
||||||
|
```
|
||||||
|
sc rd, rs2, (rs1)
|
||||||
|
```
|
||||||
|
|
||||||
|
> Non ho la minima idea di come funzioni.
|
||||||
|
|
||||||
|
Copia il dato da `rs2` a `rs1`, se non è bloccato, e metti `x0` in `rd`...?
|
||||||
|
|
||||||
|
> Carica un dato... se ho ancora il lock, altrimenti, metti `x0` in `rd`.
|
||||||
|
|
||||||
|
#### Unlock
|
||||||
|
|
||||||
|
```
|
||||||
|
sd x0, 0(x20)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Operazioni floating point
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Definisci delle costanti
|
||||||
|
.equ _SYS_EXIT, 93 # Syscall exit
|
||||||
|
.equ _SYS_WRITE, 64 # Syscall write
|
||||||
|
|
||||||
|
# Inizio del programma
|
||||||
|
.global _start
|
||||||
|
|
||||||
|
# Variabili statichie inizializzate a 0
|
||||||
|
.section .bss
|
||||||
|
|
||||||
|
# Dati modificabili
|
||||||
|
.section .data
|
||||||
|
|
||||||
|
# Dati in sola lettura
|
||||||
|
.section .rodata
|
||||||
|
msg: .string "Hello mondo!\n"
|
||||||
|
|
||||||
|
# Testo del programma
|
||||||
|
.section .text
|
||||||
|
_start:
|
||||||
|
# Chiamata a WRITE(stream, primocarattere, lunghezza)
|
||||||
|
li a0, 0 # Print to stdout
|
||||||
|
la a1, msg # Stampa il messaggio in msg
|
||||||
|
li a2, 13 # Stampa 13 caratteri dopo msg
|
||||||
|
li a7, _SYS_WRITE # Seleziona la syscall WRITE
|
||||||
|
ecall
|
||||||
|
|
||||||
|
li a7, _SYS_EXIT
|
||||||
|
ecall
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Che bello! Adoro l'assembly!
|
||||||
|
# Questo programma funziona, è verificato!
|
||||||
|
.global _start
|
||||||
|
|
||||||
|
# La sezione .data contiene i dati del programma
|
||||||
|
.section .data
|
||||||
|
|
||||||
|
# Creo una variabile con etichetta "ciao"
|
||||||
|
ciao: .string "0\n\0"
|
||||||
|
|
||||||
|
# La sezione .rodata contiene i dati del programma in sola lettura
|
||||||
|
.section .rodata
|
||||||
|
|
||||||
|
# La sezione .text contiene il testo del programma
|
||||||
|
.section .text
|
||||||
|
|
||||||
|
# Creo una label _start che funzionerà da entrypoint
|
||||||
|
_start:
|
||||||
|
li t1, 10 # Voglio ripetere il ciclo 10 volte
|
||||||
|
li a7, 64 # PRINT(stream, indirizzo, numerobytes) è la syscall 64
|
||||||
|
li a0, 0 # stream è il primo argomento: vogliamo scrivere su stdout (0)
|
||||||
|
la a1, ciao # indirizzo è il secondo argomento: partiamo dall'etichetta ciao
|
||||||
|
li a2, 2 # numerobytes è il terzo argomento: vogliamo stampare 2 bytes
|
||||||
|
print:
|
||||||
|
ecall # PRINT(a0, a1, a2)
|
||||||
|
lbu t0, 0(a1) # Carica il byte con il 0
|
||||||
|
addi t0, t0, 1 # Aggiungici 1
|
||||||
|
sb t0, 0(a1) # Ricaricalo in memoria
|
||||||
|
addi t1, t1, -1 # Togli 1 dal numero di cicli rimasti
|
||||||
|
bne t1, zero, print # Se rimangono dei cicli, torna indietro e falli!
|
||||||
|
end:
|
||||||
|
li a7, 93 # Tutti i programmi terminano chiamando la syscall 93, EXIT()
|
||||||
|
ecall # EXIT()
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Che bello! Adoro l'assembly!
|
||||||
|
# Funziona!
|
||||||
|
.global _start
|
||||||
|
|
||||||
|
# La sezione .data contiene i dati del programma
|
||||||
|
.section .data
|
||||||
|
|
||||||
|
# La sezione .rodata contiene i dati del programma in sola lettura
|
||||||
|
.section .rodata
|
||||||
|
|
||||||
|
# La sezione .text contiene il testo del programma
|
||||||
|
.section .text
|
||||||
|
|
||||||
|
# Creo una label _start che funzionerà da entrypoint
|
||||||
|
_start:
|
||||||
|
li s0, 5 # Scrivi il numero 5
|
||||||
|
li s3, 3 # COSTANTE: 3
|
||||||
|
|
||||||
|
# Pushing phase
|
||||||
|
pushing:
|
||||||
|
sb s0, 0(sp) # Metti nello stack s0
|
||||||
|
addi sp, sp, 1 # Aumenta di 1 lo stack pointer
|
||||||
|
addi s2, s2, 1 # Aumenta di 1 la dimensione dello stack
|
||||||
|
addi s0, s0, -1 # Diminuisci di 1 il numero da fattorializzare
|
||||||
|
bgeu s0, s3, pushing # Continua a pushare finchè non arrivi a 2
|
||||||
|
li s1, 2
|
||||||
|
|
||||||
|
# Popping phase
|
||||||
|
popping:
|
||||||
|
addi sp, sp, -1 # Diminuisci di 1 lo stack pointer
|
||||||
|
# Attenzione: diminuisco lo stack pointer PRIMA di caricare dallo stack, altrimenti caricarei il nulla
|
||||||
|
lb s0, 0(sp) # Prendi dallo stack il prossimo numero
|
||||||
|
addi s2, s2, -1 # Diminuisci di 1 la dimensione dello stack
|
||||||
|
mul s1, s1, s0 # Effettua la moltiplicazione
|
||||||
|
bne s2, zero, popping # Se ci sono altri valori nello stack, continua a moltiplicare
|
||||||
|
|
||||||
|
# Ending phase
|
||||||
|
ending:
|
||||||
|
li a7, 93
|
||||||
|
ecall
|
|
@ -0,0 +1,48 @@
|
||||||
|
# Che bello! Adoro l'assembly!
|
||||||
|
# Ommioddiofunzionawowimawizard
|
||||||
|
.global _start
|
||||||
|
|
||||||
|
# La sezione .data contiene i dati del programma
|
||||||
|
.section .data
|
||||||
|
|
||||||
|
primo: .word 1, 2, 3, 4, 5
|
||||||
|
secondo: .word 1, 2, 3, 4, 5
|
||||||
|
|
||||||
|
# La sezione .rodata contiene i dati del programma in sola lettura
|
||||||
|
.section .rodata
|
||||||
|
|
||||||
|
# La sezione .text contiene il testo del programma
|
||||||
|
.section .text
|
||||||
|
|
||||||
|
# s0: primo[s0]
|
||||||
|
# s1: secondo[s0]
|
||||||
|
# s2: monomio
|
||||||
|
# s3: indirizzo da cui prendere il valore
|
||||||
|
# s4: indice
|
||||||
|
# s5: dimensione array
|
||||||
|
# a0: somma
|
||||||
|
|
||||||
|
_start:
|
||||||
|
li s2, 0
|
||||||
|
li a0, 0
|
||||||
|
li s4, 0
|
||||||
|
li s5, 5
|
||||||
|
|
||||||
|
somma:
|
||||||
|
la s3, primo
|
||||||
|
add s3, s3, s4
|
||||||
|
lw s0, 0(s3)
|
||||||
|
|
||||||
|
la s3, secondo
|
||||||
|
add s3, s3, s4
|
||||||
|
lw s1, 0(s3)
|
||||||
|
|
||||||
|
mulw s2, s1, s0
|
||||||
|
add a0, a0, s2
|
||||||
|
addi s4, s4, 4
|
||||||
|
addi s5, s5, -1
|
||||||
|
bne zero, s5, somma
|
||||||
|
|
||||||
|
_end:
|
||||||
|
li a7, 93
|
||||||
|
ecall
|
|
@ -0,0 +1,57 @@
|
||||||
|
# Appunti
|
||||||
|
|
||||||
|
## Registri
|
||||||
|
|
||||||
|
I registri sono divisi in tre tipi: di **proprietà del chiamante**, di **proprietà del chiamato** (anche se in realtà è solo una convenzione) e... gli **altri**.
|
||||||
|
|
||||||
|
Tutti i registri sono grandi 64 bit: possono quindi contenere una _doubleword_.
|
||||||
|
|
||||||
|
### Proprietà del chiamante
|
||||||
|
|
||||||
|
Questi registri **non devono essere sovrascritti** quando viene chiamata una funzione, o se vengono sovrascritti, **devono essere ripristinati** prima che la funzione restituisca.
|
||||||
|
|
||||||
|
Essi sono:
|
||||||
|
- `s1..s11`: **Saved** registers, registri generici che possono essere usati per qualsiasi cosa.
|
||||||
|
- `sp`: **Stack Pointer**, il primo indirizzo dello stack disponibile per la scrittura. Va _decrementato_ quando si aggiunge qualcosa allo stack.
|
||||||
|
- `fp`: **Frame Pointer**, punta all'inizio dello stack.
|
||||||
|
|
||||||
|
### Proprietà del chiamato
|
||||||
|
|
||||||
|
Questi registri **possono essere sovrascritti** quando viene chiamata una funzione: bisogna salvarli in memoria prima di chiamarla!
|
||||||
|
|
||||||
|
Essi sono:
|
||||||
|
- `t0..t6`: **Temporary** registers, registri generici che possono essere usati per qualsiasi cosa.
|
||||||
|
- `ra`: **Return Address**, l'indirizzo a cui continuare l'esecuzione del programma dopo aver terminato una funzione. _Solitamente si usa solo con `jal` e `jalr`!_
|
||||||
|
- `a0..a7`: Function **Arguments**. Se all'interno di una funzione, contengono gli argomenti che sono stati passati. Se all'esterno di una funzione, contengono i valori restituiti dall'ultima funzione chiamata.
|
||||||
|
|
||||||
|
### Altri
|
||||||
|
|
||||||
|
- `zero`: **Zero**, registro che vale sempre 0, anche dopo essere stato sovrascritto da altro. Come `/dev/null` su Linux.
|
||||||
|
- `gp`: **Global Pointer**, punta a un indirizzo _[(?)](https://groups.google.com/a/groups.riscv.org/forum/#!topic/sw-dev/60IdaZj27dY)_
|
||||||
|
- `tp`: **Thread Pointer**, punta al thread-local storage _[(?)](https://groups.google.com/a/groups.riscv.org/d/msg/sw-dev/cov47bNy5gY/zLnlKkw9CQAJ)_
|
||||||
|
- `pc`: **Program Counter**, punta all'istruzione che sta venendo eseguita.
|
||||||
|
|
||||||
|
## Comandi preprocessore
|
||||||
|
|
||||||
|
Scrivendo file assembly per RISC-V, è possibile aggiungere direttive per il preprocessore.
|
||||||
|
|
||||||
|
Per ora ho scoperto queste:
|
||||||
|
|
||||||
|
- `.global INDIRIZZO`: definisce l'indirizzo a cui inizia il programma. _(Forse setta il `gp` a quell'indirizzo?)_
|
||||||
|
- `.equ NOME, VALORE`: definisce una costante con uno specifico nome. _Il nome delle costanti dovrebbe iniziare sempre con un underscore per convenzione._
|
||||||
|
- `.section [.data|.rodata|.bss|.text]`: marca l'inizio di una specifica sezione del programma. Le sezioni sono:
|
||||||
|
- `.data`: Variabili del programma
|
||||||
|
- `.rodata`: Variabili sola lettura del programma
|
||||||
|
- `[.bss](https://en.wikipedia.org/wiki/.bss)`: Variabili statiche non costanti non inizializzate a nessun valore. _A volte, vengono inizializzate a 0. Solo a volte. Dipende dall'OS._
|
||||||
|
- `.text`: Il testo del programma, con le istruzioni in assembly.
|
||||||
|
- `.TIPOVARIABILE`: crea una variabile di quel tipo e la inizializza a qualcosa.
|
||||||
|
|
||||||
|
# Link utili
|
||||||
|
|
||||||
|
- [algo.ing.unimo.it](http://algo.ing.unimo.it/people/andrea/Didattica/Architetture/index.html)
|
||||||
|
- [wikichip.org](https://en.wikichip.org/wiki/risc-v)
|
||||||
|
- [en.wikiversity.org](https://en.wikiversity.org/wiki/Computer_architecture)
|
||||||
|
- [it.wikiversity.org](https://it.wikiversity.org/wiki/Materia:Architetture_degli_elaboratori)
|
||||||
|
- [stackoverflow.com](https://stackoverflow.com/questions/tagged/riscv)
|
||||||
|
- [softwareengineering.stackexchange.com](https://softwareengineering.stackexchange.com/questions/tagged/assembly)
|
||||||
|
- [rv8.io](https://rv8.io/isa.html)
|
133
2 - Architettura dei calcolatori/1 - Appunti/09 - Memorie.md
Normal file
133
2 - Architettura dei calcolatori/1 - Appunti/09 - Memorie.md
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
# Memorie
|
||||||
|
|
||||||
|
La _memoria_ di un calcolatore solitamente è composta da molteplici strati, realizzati per velocizzare gli accessi.
|
||||||
|
|
||||||
|
Ogni strato offre lettura più veloce, ma è più costoso da realizzare e ha consumi più alti.
|
||||||
|
|
||||||
|
In ordine di velocità, sono:
|
||||||
|
|
||||||
|
- Registers
|
||||||
|
- Cache L1
|
||||||
|
- Cache L2
|
||||||
|
- Cache L`X`
|
||||||
|
- Random Access Memory
|
||||||
|
- Solid State Drive
|
||||||
|
- Hard Disk Drive
|
||||||
|
|
||||||
|
## Cache
|
||||||
|
|
||||||
|
### Cache L1
|
||||||
|
|
||||||
|
Piccola, ma miss penalty bassa
|
||||||
|
|
||||||
|
### Cache L2
|
||||||
|
|
||||||
|
Molto grande, ma miss penalty alta
|
||||||
|
|
||||||
|
### Direct mapping
|
||||||
|
|
||||||
|
Una cache _direct mapped_ associa a ogni indirizzo di RAM un **indice** uguale agli **N bit meno significativi** dell'indirizzo dell'inferiore, e un **tag** uguale agli **N bit più significativi**.
|
||||||
|
|
||||||
|
Se durante un accesso _il tag di un blocco è diverso dal tag dell'indirizzo a cui vogliamo accedere_, si ha un cache miss.
|
||||||
|
|
||||||
|
> Il dato di cui abbiamo bisogno è contenuto nell'indirizzo `0x0ABC` della RAM.
|
||||||
|
> Il suo indice sarà `0xBC`, e il suo tag sarà `0x0A`.
|
||||||
|
> Essendo la cache vuota all'inizio, viene immediatamente caricato.
|
||||||
|
>
|
||||||
|
> Voglio poi accedere all'indirizzo `0x0BBC` della RAM.
|
||||||
|
> Controlliamo il blocco con indice `0xBC`: il suo tag è `0x0A`, ma noi stiamo cercando `0x0B`! Si ha quindi un cache miss, e devo andare a prendere dalla memoria il dato che sto cercando e scriverlo sulla RAM.
|
||||||
|
|
||||||
|
Inoltre, in ogni blocco di memoria della cache è presente **un bit di validità**, che rappresenta se il dato in cache è stato inizializzato o no: parte da `0` e viene impostato a `1` quando viene caricato dalla RAM un dato nel relativo blocco.
|
||||||
|
|
||||||
|
> Un esempio potrebbe essere un processore con blocchi di memoria da 32 bit e indirizzi a 64 bit: la cache, contenente 1024 blocchi di memoria, avrà index a 12 bit, e di conseguenza il tag sarà grande 52 bit.
|
||||||
|
|
||||||
|
#### Dimensione blocchi
|
||||||
|
|
||||||
|
Fare blocchi grandi o fare blocchi piccoli ha significative differenze sulla velocità della cache:
|
||||||
|
|
||||||
|
Avere blocchi più grandi significa che ci saranno meno blocchi in tutta la cache, quindi:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
+ Riduce il miss rate per il principio di località spaziale
|
||||||
|
- Avendo una quantità minore di blocchi, aumenta la possibilità di conflitto
|
||||||
|
- La miss penalty è più alta
|
||||||
|
# La miss penalty è compensabile con tecniche come early restart o critical-word-first
|
||||||
|
```
|
||||||
|
|
||||||
|
Con blocchi più piccoli, invece
|
||||||
|
```diff
|
||||||
|
+ Miss penalty minore
|
||||||
|
+ Più blocchi significa meno conflitti
|
||||||
|
- E' possibile che abbia bisogno di fare più di una richiesta alla cache, rallentandola significativamente
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Scrittura tramite cache
|
||||||
|
|
||||||
|
Quando scrivo in memoria un dato presente nella cache, l'informazione presente nella cache diventa errata.
|
||||||
|
|
||||||
|
Si può risolvere questo problema con una politica di riscrittura:
|
||||||
|
- Write-through
|
||||||
|
- Write-back
|
||||||
|
|
||||||
|
##### Write-through
|
||||||
|
|
||||||
|
_Quando **viene scritto** su dato in cache_, aggiorna tutte le memorie che lo contengono.
|
||||||
|
|
||||||
|
```diff
|
||||||
|
+ Non necessita di ulteriore memoria di cache
|
||||||
|
+ Caricare dati è veloce
|
||||||
|
+ Se si verifica un write miss non ha per forza bisogno di portare in cache il dato da scrivere
|
||||||
|
- I write richiedono molto più tempo, soprattutto se ripetuti
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
##### Write-back
|
||||||
|
|
||||||
|
Scrivi la modifica di dato solo nella cache, e _marca il blocco come **dirty**_.
|
||||||
|
|
||||||
|
Quando un blocco dirty viene **sovrascritto**, aggiorna le memorie che lo contengono.
|
||||||
|
|
||||||
|
```diff
|
||||||
|
+ I write richiedono poco tempo, anche se ripetuti
|
||||||
|
- Caricare dati dalla RAM è più lento
|
||||||
|
- Necessita di memoria da dedicare al dirty bit
|
||||||
|
- Devo fetchare obbligatoriamente i dati da sovrascrivere
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Miglioramenti alle policy
|
||||||
|
|
||||||
|
E' possibile utilizzare un **write buffer** invece che fare attendere il tempo di scrittura alla CPU. I dati saranno scritti successivamente, ma prima che questi vengano utilizzati.
|
||||||
|
|
||||||
|
```diff
|
||||||
|
+ Il processore non ha bisogno di fermarsi per le write
|
||||||
|
- Il buffer potrebbe riempirsi, neganode i vantaggi
|
||||||
|
- Il buffer utilizza memoria che forse sarebbe stata più utile come cache
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fully associative
|
||||||
|
|
||||||
|
Ogni dato può essere messo in qualunque indirizzo della cache.
|
||||||
|
|
||||||
|
Richiede che l'indirizzo di origine venga salvato assieme al dato, e tanti comparatori.
|
||||||
|
|
||||||
|
### Set associative
|
||||||
|
|
||||||
|
Divido tutti i blocchi di cache in vie.
|
||||||
|
|
||||||
|
Ogni via può contenere `n` dati.
|
||||||
|
|
||||||
|
Un set è l'insieme dei dati che hanno lo stesso index ma sono in vie diverse.
|
||||||
|
|
||||||
|
Ogni dato può essere messo in qualunque indirizzo del set a cui appartiene.
|
||||||
|
|
||||||
|
Identifico il numero di set di appartenenza facendo `indirizzo % numerodiset`.
|
||||||
|
|
||||||
|
Una cache `1`-way Set Associative è una cache Direct Mapped, mentre una cache `numeroentries`-way Set Associative è una cache Fully Associative.
|
||||||
|
|
||||||
|
Quando non c'è spazio in un set, rimpiazzo un dato secondo la politica **Least Recently Used**, rimuovendo il dato usato meno recentemente. Posso usare anche la politica **Random**, se ho un'alta associatività.
|
||||||
|
|
||||||
|
#### Performance
|
||||||
|
|
||||||
|
Più una cache è associativa, più il miss rate sarà ridotto, ma l'associatività richiede un maggior numero di comparatori e potenzialmente più ritardi nella restituzione del dato.
|
||||||
|
|
||||||
|
Inoltre, la percentuale di miss non diminuisce linearmente con il numero di vie: dopo un certo numero di vie, i guadagni sono molto ridotti.
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Meltdown
|
||||||
|
|
||||||
|
Esempio circa comprensibile:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
int meltdown() {
|
||||||
|
bool array[2] = {false, true};
|
||||||
|
|
||||||
|
if( qualcosa_che_restituisce_false() ) {
|
||||||
|
//Questo viene predetto
|
||||||
|
//Durante le predizioni non ci sono segfault!
|
||||||
|
int protetto = *(indirizzo_bersaglio);
|
||||||
|
//Se il primo bit di protetto è 1, verrà caricato "true" in cache, altrimenti, verrà caricato "false", ANCHE SE E' SOLO UNA PREDIZIONE!
|
||||||
|
//Dopo essere stato caricato, NON VIENE RIMOSSO, anche se la predizione è sbagliata.
|
||||||
|
int caricato = array[protetto & 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if( in_cache(true) ) cout << "Il primo bit del bersaglio è 1.\n";
|
||||||
|
else cout << "Il primo bit del bersaglio è 0.\n";
|
||||||
|
}
|
||||||
|
```
|
Loading…
Reference in a new issue