Riassunto gerarchico delle nozioni del corso 2006-2007 di
Architetture di Processori e Sistemi Integrati (APSI) del Prof.
Salvatore Carta - Università degli Studi di Cagliari, Facoltà di
Scienze Matematiche Fisiche Naturali, Dipartimento di Matematica ed
Informatica, Corso di Laurea Specialistica in Tecnologie
Informatiche
- processore
- macchina sequenziale che esegue delle istruzioni residenti in memoria allo scopo di manipolare dei dati a loro volta residenti in memoria
- è l'esecutore di istruzioni del proprio Instruction Set (IS) una volta che queste sono state convertite in formato binario (codice macchina)
- istruzioni macchina
- consentono di eseguire un qualunque algoritmo specificato in un linguaggio ad alto livello
- la trasformazione da una rappresentazione all'altra viene eseguita dai software di supporto, il compilatore e l'assembler
- ISA - Instruction Set Architecture
- definisce l'interfaccia tra hardware e software
- specifica l'insieme delle caratteristiche del processore visibili al programmatore
- specifica la funzionalità del processore in termini di:
- dati gestiti
- registri interni destinati a contenere i dati da manipolare
- istruzioni che prelevano dalla memoria e manipolano questi dati
- istruzioni di "salto"
- modalità di funzionamento speciali
- istruzioni speciali
- fetch delle istruzioni
- per poter essere eseguita dal processore ciascuna istruzione deve essere caricata in un registro interno del processore (fetch dell'istruzione)
- all'istante 0 dell'esecuzione le istruzioni che codificano il programma da eseguire risiedono nella memoria istruzioni
- il flusso di esecuzione delle istruzioni viene definito dalla dislocazione in memoria delle istruzioni stesse e dalla tecnica di fetch utilizzata
- il flusso normale di esecuzione (che non fa uso delle istruzioni dedicate che si occupano di modificare tale flusso) prevede l'esecuzione sequenziale delle istruzioni secondo l'ordine di salvataggio in memoria
- instruction register (IR)
- generalmente è il registro destinato a contenere l'istruzione corrente
- program counter (PC)
- registro interno del processore che contiene l'indirizzo della locazione di memoria all'interno della quale è contenuta la prossima istruzione da eseguire
- l'esecuzione di una istruzione che non prevede la modifica del flusso di esecuzione fa incrementare automaticamente il PC in modo che la prossima istruzione caricata sull'IR, e quindi eseguita, sia la successiva a quella attualmente eseguita (sequenza di esecuzione normale)
- la modifica del flusso di esecuzione sequenziale viene gestita
tramite le istruzioni di salto.
- supponendo che la prima istruzione sia memorizzata alla locazione di memoria 0, che ciascuina istruzione occupi una sola locazione, e che nessuna delle prime 10 istruzioni sia una istruzione di salto, la sequenza di esecuzione sarebbe la seguente: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...
- supponendo che la istruzione 3 sia una istruzione di salto (jump 7) la sequenza di esecuzione sarebbe: 0, 1, 2, 3, 7, 8, 9, ...
- tipologie di istruzioni
- le principali tipologie di istruzioni sono 3, a cui vanno
aggiunte istruzioni di tipo eterogeneo non classificabili in
maniera sintetica
- istruzioni di spostamento dati:
- si occupano di spostare i dati dalla memoria ai registri interni del processore e viceversa
- esistono inoltre istruzioni che spostano i dati fra registri interni e più raramente istruzioni che spostano i dati fra locazioni di memoria
- istruzioni di manipolazione dati:
- si occupano di prelevare i dati da locazioni (di solito dai registri interni), di utilizzarli come operandi sorgente per delle operazioni logico/aritmetiche e di salvare il risultato in altre locazioni (di solito dai registri interni)
- istruzioni di modifica del flusso di esecuzione (salto):
- si occupano di modificare il contenuto del PC in modo da modificare il flusso di esecuzione definito dall'incremento automatico del PC stesso
- istruzioni di spostamento dati:
- le principali tipologie di istruzioni sono 3, a cui vanno
aggiunte istruzioni di tipo eterogeneo non classificabili in
maniera sintetica
- microarchitettura del processore
- rappresenta l'organizzazione interna del processore
- processori con diverse microarchitetture possono condividere lo stesso ISA
- elementi base
- program counter (PC)
- instruction register (IR)
- arithmetic and logic unit (ALU)
- registri interni (GPR, SPR, FPR, ACC)
- multiplexers
- special function units (SFU)
- logica di controllo
- PROCESSORE MU0
- ISA
- istruzioni
- 16 bit
- 4 bit opcode
- 12 bit indirizzo
- 16 bit
- dati
- 16 bit
- modi di indirizzamento
- indirizzamento diretto da memoria
- un solo indirizzo specificato
- registri
- registro accumulatore acc (implicito)
- program counter PC (implicito)
- tipi di dato
- half word signed (16 bit)
- instruction set
- istruz. opcode descrizione
- lda s 0 (0000) acc = mem[s]
- sto s 1 (0001) mem[s] = acc
- add s 2 (0010) acc = acc + mem[s]
- sub s 3 (0011) acc = acc - mem[s]
- jmp s 4 (0100) PC = s
- jge s 5 (0101) if (acc >= 0) PC = s
- jne s 6 (0110) if (acc != 0) PC = s
- stp 7 (0111) stop
- cicli (loop)
- l'ISA del processore MU0 prevede la presenza del solo registro accumulatore, e supporta esclusivamente l'indirizzamento immediato (l'indirizzo della locazione di memoria cui accedere è cablato nel codice) di conseguenza la gestione dell'accesso agli elementi di un vettore all'interno di un loop può essere codificato solo con l'unrolling del loop stesso (a meno di non scrivere codice automodificante)
- istruzioni
- esempio programma
- specifica C
- C=A+B;
- registri
- A @ M[0x100]
- B @ M[0x101]
- C @ M[0x102]
- codice assembly
- lda 0x100 ; inizializza l'accumulatore col dato A
- add 0x101 ; addiziona all'accumulatore il dato B
- sto 0x102 ; memorizza il risultato
- linguaggio macchina
- @000 0100 // op=0x0 (load), imm=0x100
- @001 2101 // op=0x2 (add), imm=0x101
- @002 1102 // op=0x1 (store), imm=0x102
- @003 7000 // op=0x7 (stop), imm=0x000
- specifica C
- ISA
- PROCESSORI MIPS
- generalità
- sono una famiglia di processori
- la prima versione dell'ISA MIPS è una delle prime architetture di tipo RISC (Reduced Instruction Set Compnent) ed è stata sviluppata da John L. Hennessy.
- l'ISA si è evoluta in innumerevoli versioni ciascuna delle quali è stata implementata da diverse microarchitetture
- le più evolute versioni di processori MIPS sono attualmente
integrate in dispositivi elettronici embedded di largo utilizzo:
- consolle di gioco (playstation), stampanti laser, decoder tv satellitare, router, navigatori satellitari, macchine fotografiche digitali, ...
- ISA - generalità
- l'architettura dei processori della famiglia MIPS è di tipo
load/store:
- non è possibile operare direttamente sui dati residenti in memoria ma è necessario prima copiarli nei registri interni tramite istruzioni dedicate
- è prevista la presenza di un moltiplicare integrato per dati a 32 bit con risultato a 64 bit
- i registri interni visibili del MIPS sono:
- 32 registri general purpose a 32 bit, il registro zero contiene 0 e non può essere modificato
- due registri a 32 bit, HI e LO, per memorizzare rispettivamente i 32 bit più significativi e i 32 bit meno significativi del risultato di moltiplicazione
- un registro Program Counter (PC) a 32 bit, che contiene l'indirizzo dell'istruzione da eseguire
- un registro Interrupt Address Register (IAR)
- registri per elaborazione floating point (FPR)
- tipologie di istruzioni MIPS R2000
- le principali tipologie di istruzioni sono 3, a cui vanno
aggiunte istruzioni di tipo eterogeneo non classificabili in
maniera sintetica
- istruzioni di spostamento dati:
- si occupano di spostare i dati dalla memoria ai registri interni del processore e viceversa
- esistono inoltre istruzioni che spostano i dati fra registri interni e più raramente istruzioni che spostano i dati fra locazioni di memoria
- istruzioni di manipolazione dati:
- si occupano di prelevare i dati da locazioni (di solito dai registri interni), di utilizzarli come operandi sorgente per delle operazioni logico/aritmetiche e di salvare il risultato in altre locazioni (di solito dai registri interni)
- istruzioni di modifica del flusso di esecuzione (salto):
- si occupano di modificare il contenuto del PC in modo da modificare il flusso di esecuzione definito dall'incremento automatico del PC stesso
- istruzioni di spostamento dati:
- le principali tipologie di istruzioni sono 3, a cui vanno
aggiunte istruzioni di tipo eterogeneo non classificabili in
maniera sintetica
- formato delle istruzioni
- le istruzioni sono a lunghezza fissa (32 bit), in tre possibili
formati
- Immediate (I type)
- 6 bit opcode
- 5 bit rs
- 5 bit rt
- 16 bit immediate
- Reg (R type)
- 6 bit opcode
- 5 bit rs
- 5 bit rt
- 5 bit rd
- 5 bit shamt
- 6 bit function
- Jump (J type)
- 6 bit opcode
- 26 bit target
- Immediate (I type)
- le istruzioni sono a lunghezza fissa (32 bit), in tre possibili
formati
- l'architettura dei processori della famiglia MIPS è di tipo
load/store:
- generalità
- eMIPS - MIPS ISA ridotto
- microarchitetture semplificate che implementano un sottoinsieme minimo delle istruzioni
- non è implementata la moltiplicazione, non vi sono quindi istruzioni di scambio fra registri interni
- l'IS semplificato contiene almeno una istruzione per ciascuna
delle categorie rimanenti:
- istruzioni di lettura e scrittura dalla memoria (2 su 8)
- istruzioni aritmetico-logiche (5 su 40)
- istruzioni di scambio di dati tra registri interni (0 su 4)
- istruzioni di modifica del flusso di esecuzione
- salto incondizionato (0 su 4)
- salto condizionato (1 su 17)
- instruction set
- load word (lw): carica su un registro interno un valore dalla memoria
- store word (sw): scrive in memoria il valore contenuto in un registro
- add : somma del valore di due registri
- subtract (sub): sottrazione tra il valore di due registri
- and: and logico bit a bit del contenuto di due registri
- or: or logico bit a bit del contenuto di due registri
- set on less than (slt): scrive 1 sul registro destinazione se il primo operando è minore del secondo, zero altrimenti
- branch on equal (beq): salto condizionato all'uguaglianza dei valori contenuti nei 2 registri confrontati
- gestione della memoria
- l'ISA MIPS completo gestisce dati di 6 tipi diversi: a 8, 16, 32 bit in rappresentazione naturale o complemento a 2
- il sistema di memoria deve poter gestire tutti i tipi di dato esistenti, fornendo i dati in uscita nella maniera appropriata, a seconda del valore assunto dai bit di controllo associati (optype)
- essendo il dato minimo a 8 bit, la locazione di memoria elementare deve essere di 8 bit
- per non escludere la possibilità che al processore sia associato un sistema di memoria con memoria dati ed istruzioni non separate (memoria unica, modello di Von Neumann) è necessario che anche la memoria istruzioni sia indirizzabile un byte alla volta, nonostante le istruzioni siano sempre di 4 byte
- ciò implica che l'aggiornamento del PC sia di 4 in 4
- esempio programma
- specifica C
- integer a,b,c; // dati a 32 bit
- if (A!=B) {C = A + B;} else {C = A - B;}
- specifica aggiuntiva 1 - memoria dati (t=0)
- @0100 A
- @0104 B
- specifica aggiuntiva 2 - memoria dati (t=fine simulaz)
- @0200 C
- eMIPS assembly
- lw $1,0x0100($0)
- lw $2,0x0104($0)
- beq $1,$2,else_tag
- if_tag:
- add $3,$2,$1
- beq $0,$0,store
- else_tag:
- sub $3,$2,$1
- store:
- sw $3,0x0200($0)
- specifica C
- cicli (loop)
- l'esecuzione ciclica (loop) viene gestita tramite l'utilizzo di
un registro detto indice che:
- viene inizializzato all'esterno del loop in funzione del numero di iterazioni da eseguire
- viene aggiornato ad ogni iterazione del loop
- viene testato ad ogni iterazione del loop, il test che verifica la fine del ciclo ne interrompe l'esecuzione
- l'accesso agli elementi di un vettore con indice dipendente
dall'indice del loop può essere gestita tramite un registro detto
puntatore che:
- viene inizializzato all'esterno del loop
- viene utilizzato ad ogni iterazione del loop per comporre l'indirizzo della locazione corrente
- viene aggiornato ad ogni iterazione del loop
- l'aggiornamento dipende dal numero di locazioni di memoria occupate da ciascun elemento del vettore
- è anche possibile utilizzare un solo registro che accorpi le
funzionalità di registro indice per per il test di fine ciclo e di
registro puntatore
- in questo caso l'inizializzazione e l'aggiornamento del registro, nonchè il test di fine ciclo, vanno gestiti in funzione del fatto che la funzionalità di registro puntatore richiede l'accesso a dati che possono occupare più locazioni di memoria
- esempio
- specifica C
- integer acc; // dati a 32 bit
- integer a[16];
- acc = 0;
- for( i=0; i<16;i++) {acc=acc+a[i];}
- specifica aggiuntiva 1 - memoria dati (t=0)
- @100 a[0]
- @104 a[1]
- ...
- @13c a[15]
- @400 0x1F
- @404 0x4
- specifica aggiuntiva 2- memoria dati (t=fine simulaz)
- @0200 acc
- codifica assembly 1
- ;utilizzo dei registri
- ;$1 ha la funzione di registro accumulatore
- ;$2 ha la funzione di indice del loop
- ;$3 ha la funzione di puntatore all'elemento corrente del vettore
- ;$4 memorizza la costante usata per aggiornare il registro indice
- ;$5 memorizza la costante usata per aggiornare il registro puntatore
- ;$6 memorizza la costante usata per il test di fine ciclo
- ;$7 ha la funzione di buffer temporaneo
- ;la sequenza utilizzata è crescente, dall'elemento 1 all'elemento 15
- ;l'elemento 0 è utilizzato per inizializzare l'accumulatore
- lw $1,0x100($0) ; inizializza il registro accumulatore $1 col dato a[0]
- add $2,$0,$0 ; inizializza il reg indice $2 a 0
- add $3,$0,$0 ; inizializza il reg puntatore $3 a 0
- lw $4,0x400($0) ; inizializza il reg $4 utilizzato per aggiornare il registro indice
- lw $5,0x404($0) ; inizializza il reg $5 utilizzato per aggiornare il reg puntatore
- lw $6,0x408($0) ; inizializza il registro utilizzato per il test di fine ciclo a 15=0xf
- loop:
- add $2,$2,$4 ; aggiorna il registro indice sommando 1
- add $3,$3,$5 ; aggiorna il registro puntatore sommando 4
- lw $7, 0x100($3) ; carica il dato a[i] sul registro buffer $7, la seq è 1,2,..,15.
- add $1,$1,$7 ; aggiorna l'accumulatore sommando il buffer
- beq $2,$6,store ; fa il test del registro indice, se $2=$6 interrompe il ciclo
- beq $0,$0,loop ; salta all'inizio del ciclo
- store:
- sw $1,0x200($0) ; scrive il risultato in memoria
- ;la coppia di istruzioni seguente provoca lo stop della simulazione
- lw $10, 0x500($0) ;
- sw $0, 0x0($10) ;
- ;utilizzo dei registri
- codifica assembly 2
- ;utilizzo dei registri
- ;$1 ha la funzione di registro accumulatore
- ;$2 ha la funzione di indice del loop e anche di puntatore all'elemento corrente del vettore
- ;$3 ha la funzione di buffer temporaneo
- ;$4 memorizza la costante usata per aggiornare il registro indice-puntatore
- ;la sequenza utilizzata è stavolta decrescente, dall'elemento 15 all'elemento 1
- ;l'elemento 0 è utilizzato per inizializzare l'accumulatore
- lw $1,0x100($0) ; inizializza l'accumulatore col dato a[0]
- lw $2,0x400($0) ; inizializza il reg indice/puntatore $2
- lw $4,0x404($0) ; inizializza il reg utilizzato per aggiornare il reg indice/puntatore
- loop:
- lw $3, 0x100($2) ; carica il dato su $3, la seq è 15,14,..,1.
- sub $2,$2,$4 ; aggiorna il registro indice sottraendo 4
- add $1,$1,$3 ; aggiorna l'accumulatore
- beq $2,$0,store ; fa il test del registro indice, se $2=0 interrompe il ciclo
- beq $0,$0,loop ; salta all'inizio del ciclo
- store:
- sw $1,0x200($0) ; scrive il risultato in memoria
- ;la coppia di istruzioni seguente provoca lo stop della simulazione
- lw $10, 0x500($0);
- sw $0, 0x0($10);
- ;utilizzo dei registri
- specifica C
- conversione a linguaggio macchina (esempio)
- lw $1,0x100($0)
- I-type instruction:
- opcode ( 6 bit) = 6h'23 = 100011
- rs ( 5 bit) = $0 = 00000
- rt ( 5 bit) = $1 = 00001
- immediate (16 bit) = 16h'100 = 0000000100000000
- binary: 1000 1100 0000 0001 0000 0001 0000 0000
- hex: 8 c 0 1 0 1 0 0
- l'esecuzione ciclica (loop) viene gestita tramite l'utilizzo di
un registro detto indice che:
- TMI - TEMPO MEDIO DI ISTRUZIONE
- figura di merito utilizzata per la valutazione delle prestazioni di un processore
- TMI = CPI · CC
- CPI = Clock Per Instruction = numero medio di cicli necessari per l'esecuzione di una istruzione
- CC = Clock Cycle = durata del ciclo di clock
- ARCHITETTURE DI PROCESSORI
- eMIPS-sc (architettura singolo ciclo IS ridotto)
- architettura di memoria
- è di Harvard: vi sono 2 memorie separate, una per i dati ed una per le istruzioni
- [instructions memory]⇔[eMIPS-sc]⇔[data memory]
- fetch - caricamento delle istruzioni
- è la lettura dell'istruzione dalla memoria
- è la prima operazione da eseguire all'inizio di ciascuna istruzione
- il registro program counter (PC) contiene l'indirizzo da cui leggere l'istruzione
- la fase di fetch si conclude con l'incremento del PC in modo che contenga l'indirizzo dell'istruzione successiva da leggere e quindi eseguire
- il flusso di esecuzione normale del programma è sequenziale
- il PC viene incrementato di 4 anzichè di 1 perchè si suppone sempre di indirizzare una memoria indirizzabile a singoli byte, mentre le istruzioni sono di 4 byte ciascuna
- decode
- prevede che i campi rs ed rt indirizzino il file dei registri e che il campo immediato sia decodificato
- tutti i possibili operandi sono pronti per la fase di esecuzione
- execute
- istruzione load (lw)
- copia un dato residente in memoria in uno dei registri del register file
- l'indirizzo di accesso alla memoria è la somma dell'immediato e del contenuto del registro rs
- il registro rt è il registro destinazione
- istruzione store (sw)
- copia il dato contenuto di uno dei registri del register file in una locazione di memoria
- l'indirizzo di accesso alla memoria è la somma dell'immediato e del contenuto del registro rs
- il registro rt contiene l'indirizzo di memoria sul quale scrivere il dato
- istruzione load (lw)
- istruzioni aritmetico-logiche
- la ALU compie l'operazione specificata dal campo function
- i 2 operandi sono specificati dal contenuto dei campi rs ed rt
- il risultato va in ingresso al registro specificato dal contenuto del campo rd
- istruzione branch equal
- modifica condizionalmente il flusso sequenziale di esecuzione scrivendo il contenuto del campo immediate sul PC nel caso si verifichi la condizione di uguaglianza fra i 2 registri puntati dai campi rs ed rt
- la ALU genera un segnale di controllo che segnala se il risultato di una operazione è nullo (segnale zero)
- si esegue la sottrazione tra i contenuti dei registri rs e rt
- il segnale zero controlla un mux che permette di condividere l'ingresso del PC tra PC+4 e l'indirizzo di salto
- l'indirizzo di salto si ottiene sommando PC+4 all'immediato (offset)
- datapath
- possiamo ottenere il datapath dall'unione dei pezzi di datapath
dedicati alle singole classi di istruzioni basandoci sul massimo
riutilizzo delle risorse:
- fetch (IF) - decode (ID) - execute (EX) - memory access (MEM) - write back (WB)
- la maggior parte delle risorse possono infatti essere condivise da più classi di istruzioni
- quando una porta può ricevere segnali diversi, a seconda
dell'istruzione è richiesta la presenza di un multiplexer per
condividere la porta
- il campo rs dell'istruzione va sempre nell'ingresso RaddrA del register file
- il campo rt dell'istruzione va sempre nell'ingresso RaddrB del register file
- in ingresso a Waddr può andare o il campo rt o il campo rd a seconda del valore dell'opcode
- il primo operando della ALU è sempre la prima uscita del register file
- il secondo operando della ALU può essere la seconda uscita del register file o l'immediato esteso a 32 bit a seconda del valore dell'opcode
- l'ingresso DataIn del register file può essere il dato in lettura dalla memoria oppure il risultato della ALU
- possiamo ottenere il datapath dall'unione dei pezzi di datapath
dedicati alle singole classi di istruzioni basandoci sul massimo
riutilizzo delle risorse:
- controllo
- è implementato tramite una rete combinatoria che prende in ingresso il campo opcode dell'istruzione e produce gli 8 segnali di controllo, 2 che vanno alla alla memoria dati esterna, e 6 che controllano i registri, i mux e gli altri moduli combinatori del datapath
- svantaggi dell'architettura a singolo ciclo
- anche se l'implementazione a singolo ciclo di clock funziona perfettamente ed è molto semplice, non è usata perché rispetto ad altri tipi di implementazione è inefficiente (elevato TMI)
- la durata del ciclo di clock (CC) dipende dal tempo che il
segnale più lento impiega a percorrere il datapath
- tutte le istruzioni potenzialmente più veloci vengono rallentate per uniformarsi all'istruzione più lenta
- l'istruzione più lenta è quella di load perché deve attraversare il maggior numero di unità funzionali, compreso il sottosistema di memoria
- il ciclo di clock deve durare a sufficienza affinchè ciascun segnale in uscita da un registro si sia propagato completamente fino all'ingresso del registro di destinazione che lo deve memorizzare
- architettura di memoria
- eMIPS-mc (architettura multiciclo IS ridotto)
- sacrifica il CPI (=5) per poter ottimizzare il CC
- suddivide l'esecuzione di ciascuna istruzione in fasi più semplici ed esegue ciascuna fase in un ciclo di clock
- microarchitettura sequenziale
- il datapath è suddiviso in più parti ciascuna delle quali è percorsa dall'istruzione in un ciclo di clock
- in un ciclo di clock una parte del datapath preleva i dati da uno o più registri, li elabora attraverso blocchi di logica combinatoria e memorizza i risultati in altri registri
- le uscite dei registri vengono utilizzati nel ciclo successivo di clock nella porzione successiva del datapath
- la suddivisione del datapath rispecchia la suddivisione
dell'esecuzione dell'istruzione in operazioni elementari ciascuna
eseguita in un ciclo di clock
- fetch (IF) - decode (ID) - execute (EX) - memory access (MEM) - write back (WB)
- in un ciclo di clock ogni unità funzionale può compiere una sola operazione: per esempio, si può effettuare un solo accesso alla memoria, la ALU può eseguire una sola operazione e si può eseguire un solo accesso al register file (due letture e una scrittura)
- MIPS-mc (architettura multiciclo IS completo - integer)
- microarchitettura sequenziale completa
- possiamo costruire il datapath per la versione multiciclo sulla base dell' osservazione che alcune unità funzionali che nella versione a singolo ciclo erano state replicate, qui possono essere condivise perché non vengono mai usate per due scopi diversi nello stesso ciclo di clock
- è possibile usare una sola memoria sia per i dati che per le istruzioni dal momento che l'istruzione è sempre caricata nella fase di IF mentre la lettura o scrittura dei dati avviene sempre nel quarto ciclo di clock (MEM) (architettura di Von Neumann)
- è possibile usare la ALU anche per calcolare PC+4, che avviene sempre nella fase di IF, e per calcolare l'indirizzo di salto condizionato, che avviene sempre nella fase di ID, mentre tutte le altre operazioni sono eseguite dalla ALU nel terzo ciclo di clock (EX).
- queste condivisioni sono possibili solo con l'utilizzo di multiplexer
- ciclo 1: fetch (IF)
- viene caricata sul registro IR l'istruzione contenuta nell'indirizzo della memoria istruzioni contenuto nel PC
- nello stesso tempo il PC viene aggiornato con il suo stesso valore incrementato di 4
- ciclo 2: decode (ID)
- si leggono dal register file i valori dei registri puntati dai campi rs e rt dell'istruzione e vengono memorizzati rispettivamente nei registri A e B
- nel frattempo i 16 bit meno significativi dell'istruzione vengono estesi a 32 bit e moltiplicati per 4
- il risultato di queste due operazioni non necessita di essere memorizzato in un registro al termine del ciclo di clock perché l'uscita dell'ir non cambierà fino al prossimo fetch
- viene eseguita la somma dell'immediato esteso a 32 bit e
moltiplicato per 4 e di PC+4, il risultato viene memorizzato sul
registro ALUout
- questa operazione serve nel caso in cui l'istruzione in esecuzione sia beq per calcolare l'indirizzo di salto condizionato
- in tutti gli altri casi il risultato dell'operazione sarà ignorato
- questa fase è uguale per tutte le operazioni perché sarà nella
fase successiva che si scarteranno i valori che non servono
- esempio: in una istruzione di load il contenuto del registro B non serve, quindi verrà scartato
- ciclo 3: execute (EX)
- dipende dal tipo di istruzione che si sta eseguendo:
- memory reference
- la ALU esegue la somma del contenuto del registro a e dell'immediato esteso a 32 bit (non shiftato)
- il risultato, che è l'indirizzo di lettura o scrittura in memoria, viene memorizzato nel registro ALUout
- register
- la ALU esegue l'operazione specificata dal campo function sui contenuti dei registri A e B
- il risultato viene memorizzato nel registro ALUout
- beq (branch equal)
- la ALU esegue la sottrazione tra i valori dei registri A e B
- l'uscita "zero" della ALU va in ingresso alla logica combinatoria che genera, in funzione dei segnali di controllo PCWriteCond e PCWrite, il segnale che abilita la scrittura del pc.
- il PC viene sempre aggiornato con PC+4 alla fine del ciclo IF; in questa fase verrà nuovamente aggiornato con l'uscita del registro ALUout scritto nel ciclo precedente se l'istruzione in esecuzione è beq e la condizione di salto è verificata
- memory reference
- dipende dal tipo di istruzione che si sta eseguendo:
- ciclo 4: memory access (MEM)
- questa fase è presente solo per le istruzioni di lettura e scrittura in memoria e per le istruzioni RegType
- il comportamento cambia a seconda dell'istruzione
- load
- la memoria fornisce il dato presente nella locazione puntata dall'indirizzo calcolato nel ciclo precedente e salvato nel registro ALUout
- tale dato viene memorizzato nel registro MDR (Memory Data Register)
- store
- il dato contenuto nel registro B viene scritto in memoria alla locazione puntata dall'indirizzo calcolato nel ciclo precedente e memorizzato nel registro ALUout
- register
- il contenuto del registro ALUout, scritto nel ciclo precedente con il risultato della ALU, deve essere memorizzato nel registro del register file puntato dal campo rd dell'istruzione, ancora memorizzata nell'IR
- load
- ciclo 5: write back (WB)
- quest'ultima fase è compiuta solo dall'istruzione di load
- il contenuto del registro MDR viene memorizzato nel registro del register file puntato dal campo rt dell'istruzione, che è ancora memorizzata nell'IR
- implementazione del controllo
- il controllo può essere implementato con una semplice macchina a stati (FSM)
- microarchitettura sequenziale completa
- MIPS-pipe (architettura pipelined IS completo - integer)
- pipeling
- permette di incrementare le prestazioni (TMI) tenendo pressoche inalterata la frequenza di clock (CC) rispetto al caso multiciclo e riducendo il CPI a valori prossimi ad 1
- è una tecnologia fondamentale per incrementare le prestazioni dei processori
- generalità
- il pipelining non migliora la latency del singolo task, ma il troughput dell'intera operazione
- il troughput è limitato dallo stadio di pipeline più lento
- permette l'esecuzione contemporanea di più task
- il potenziale incremento delle prestazioni è pari al numero di stadi di pipeline
- lo sbilanciamento della lunghezza degli stadi di pipeline riduce il miglioramento delle prestazioni
- il tempo necessario a riempire e svuotare la pipeline riduce il miglioramento delle prestazioni
- microarchitettura MIPS pipelined
- è possibile applicare il pipelining al datapath delle microarchitetture MIPS viste finora con pochissime modifiche al datapath
- ciascuna fase di esecuzione dell'istruzione diventa uno stadio della pipeline
- l'architettura di memoria diventa di Harvard (memorie separate per dati ed istruzioni)
- eeMIPS-sc
- versione semplificata a singolo ciclo senza la gestione dell'istruzione jump
- esecuzione pipelined delle istruzioni: utilizzo delle risorse
- al ciclo 0 nessuna istruzione è in esecuzione
- al ciclo 1 viene eseguita la fase di fetch della istruzione 1
- al ciclo 2 viene eseguita la fase di decode dell'istruzione 1 e la fase di fetch della istruzione 2
- al ciclo 3 viene eseguita la fase di execute dell'istruzione 1, la fase di decode della istruzione 2 e la fase di fetch della istruzione 3
- si prosegue finché la pipeline non si riempie
- considerazioni sul datapath
- essendo la frequenza di clock della macchina pipelined all'incirca uguale a quella della macchina sequenziale è necessario l'utilizzo di un sistema di memoria cinque volte più veloce
- ogni stadio della pipeline è attivo in ogni ciclo di clock e tutte le operazioni presenti in ciascun pipe stage vengono completate in un ciclo di clock
- i valori passati da uno stadio al successivo vengono memorizzati in registri temporanei noti come pipeline registers, posti tra ogni stadio della pipeline
- considerazioni sul controllo
- il controllo si occupa di determinare il valore dei bit di controllo dei multiplexers presenti nel datapath e di settare le abilitazioni in lettura/scrittura degli elementi di memoria, quali pc, file dei registri e memoria dati
- non viene utilizzata una FSM classica che per ogni ciclo genera
i bit opportuni
- tutti i bit vengono generati in funzione dell'opcode nello stadio di ID, e quindi propagati tramite i registri di pipeline in modo da applicarli opportunamente nello stadio in cui sono necessari
- i registri di pipeline trasmettono da uno stadio al successivo non solo i dati ma anche il controllo
- incremento delle prestazioni ideale
- l'incremento delle prestazioni ottenibile nel passaggio da un datapath sequenziale ad un datapath pipelined può essere quantificato calcolando il rapporto fra il tempo medio di esecuzione delle istruzioni nei 2 casi
- nel caso pipelined CPI = 1 in condizioni ideali
- nel caso sequenziale CPI è compreso tra 3 e 5 (poniamo CPI = 4)
- considerando che i 2 datapath sono pressochè identici si puo supporre che la durata minima del tempo di ciclo sia pressochè la stessa, pertanto, in condizioni di funzionamento ideale della pipeline, l'incremento delle prestazioni è pari approssimativamente a 4
- delta = TMIseq / TMIpip = (CPIseq * CCseq) / (CPIpip * CCpip) = CPIseq / CPIpip = 4
- pipeling hazards
- il funzionamento ideale non è ottenibile per le problematiche legate agli hazards della pipeline
- i pipelining hazards impediscono l'esecuzione di una delle fasi di una istruzione durante il ciclo di clock assegnatole
- esitono 3 tipologie di hazards:
- structural hazards
- si verifica ogni qualvolta l'hardware non è dotato di risorse sufficienti per poter soddisfare contemporaneamente una particolare combinazione di istruzioni
- nel caso si utilizzi un'architettura di Von Neumann, con un'unica memoria per dati ed istruzioni, si presenta un hazard strutturale ogni volta che un'istruzione di tipo load o store tenta di accedere alla memoria nella sua fase di MEM
- il problema può essere risolto ritardando di un ciclo
l'esecuzione della fase di fetch dell'istruzione in conflitto con
l'istruzione di load o store
- viene inserita all'interno della pipeline una bolla (bubble, stallo, interlock)
- lo stadio di IF si comporta come se avesse eseguito il fetch di un'istruzione NOP, che si propaga attraversando tutta la pipeline
- il fetch dell'istruzione 3 viene eseguito nel ciclo successivo
- il problema è risolto al costo di un ciclo di ritardo per ogni istruzione di load o store
- si risolvono con l'aggiunta di nuove risorse hardware
- data hazards
- l'istruzione dipende dal risultato di un'istruzione precedente che è ancora nella pipeline
- il pipelining cambia la temporizzazione relativa delle istruzioni eseguendole in maniera interlacciata nel tempo (overlap)
- i data hazards si verificano quando la pipeline modifica l'ordine di lettura/scrittura degli operandi dall'ordine che avrebbero se le esecuzioni fossero eseguite in maniera totalmente sequenziale su un processore sequenziale
- esempio
- add $1,$2,$3 ; scrive $1 nello stadio WB
- sub $4,$1,$3 ; legge $1 nello stadio ID: HAZARD
- si gestiscono con tecniche di forwarding e scheduling
- tipologie
- RAW: Read After Write
- questo è l'unico che si verifica nella pipeline semplificata del MIPS
- l'istruzione j tenta di leggere un operando prima che
l'istruzione i lo scriva
- i: add $1,$2,$3
- j: sub $4,$1,$3
- è causato da una "dipendenza" (nella nomenclatura dei compilatori)
- si verifica per il riuso dello stesso registro
- nel caso del MIPS si può risolvere senza modificare il datapath inserendo stalli (bolle, interlock) nella pipeline al posto delle 2 o 3 istruzioni successive
- WAR: Write After Read
- l'istruzione j scrive un operando prima che l'istruzione i lo
legga
- i: sub r4,r1,r3
- j: add r1,r2,r3
- k: mul r6,r1,r7
- causato da una "anti-dipendenza" (nella nomenclatura dei compilatori)
- si verifica per il riuso dello stesso registro
- si verifica solo se i è abbastanza "più lenta" di j
- non si può verificare nella pipeline MIPS perchè:
- tutte le istruzioni sono eseguite in 5 stadi
- la lettura è sempre nello stadio 2
- la scrittura è sempre nello stadio 5
- l'istruzione j scrive un operando prima che l'istruzione i lo
legga
- WAW: Write After Write
- l'istruzione j scrive un operando prima che lo scriva
l'istruzione i
- i: sub r1,r4,r3
- j: add r1,r2,r3
- k: mul r6,r1,r7
- causato da una "dipendenza di output " (nella nomenclatura dei compilatori)
- si verifica per il riuso dello stesso registro
- si verifica solo se i è abbastanza "più lenta" di j
- non si può verificare nel MIPS perchè:
- tutte le istruzioni sono eseguite in 5 stadi
- la scrittura è sempre nello stadio 5
- l'istruzione j scrive un operando prima che lo scriva
l'istruzione i
- RAW: Read After Write
- control hazards
- pipelining dei salti condizionati
- sono provocati dalle istruzioni di salto condizionato
- prima che controllo sia in grado di decidere se il salto deve essere eseguito o meno e che l'indirizzo di salto sia stato calcolato, 3 istruzioni sono già entrate nella pipeline
- il metodo più semplice di risolvere il problema è inserire stalli nella pipeline da quando viene individuata l'istruzione di salto condizionato, fino a che non è stato calcolato il nuovo valore del PC (stadio di MEM)
- si può anche risolvere tramite inserimento di NOP a livello di compilatore
- una semplice soluzione come l'introduzione di 3 cicli di stallo per ogni salto condizionato riduce enormemente le prestazioni
- si gestiscono con tecniche di Branch Prediction e Delayed Branch
- structural hazards
- tecniche di risoluzione degli hazards
- incremento delle prestazioni reale
- l'incremento delle prestazioni ottenibile nel passaggio da un datapath sequenziale ad un datapath pipelined va riquantificato considerando che il valore di CPI è condizionato dalla presenza degli hazard
- in condizioni di funzionamento ideale della pipeline, l'incremento delle prestazioni (delta) si riduce in maniera considerevole, soprattutto nel caso gli hazard vengano risolti solo utilizzando tecniche software
- delta = 4 / (1 + [numero medio di cicli di stallo della pipe per istruzione])
- RAW: forwarding (by-pass)
- è possibile gestire in maniera hardware gli hazard RAW tramite una tecnica nota come forwarding
- il risultatato della istruzione che dovrà essere scritto nel regfile nello stadio di WB è già disponibile nel datapath alla fine della istruzione di EX
- si può fare in modo di sfruttare questo fatto bypassando i registri di pipeline e rendendo questo risultato immediatamente disponibile all'ingresso della ALU, in caso di necessità
- il risultato della ALU viene riportato indietro dal registro EX/MEM in ingresso alla ALU
- se l'hardware dedicato riscontra che una precedente operazione
di ALU ha scritto (o dovrà scrivere) un registro che sarà un
operando sorgente per l'istruzione corrente, la logica di controllo
seleziona come input il risultato di forward al posto dell'uscita
dal register file
- il risultato della add, che normalmente dovrebbe essere scritto in r1 nel ciclo 5, è disponibile fin dal ciclo 4 per essere mandato in input alla ALU nella fase di EX dell'istruzione sub (ciclo 4)
- nel ciclo successivo il risultato può essere mandato in input alla ALU nella fase di EX della istruzione and (ciclo 5)
- nel ciclo ancora successivo il risultato della add può essere mandato in input alla ALU nella fase di EX dell'istruzione or (ciclo 6)
- implementazione
- si verifica un RAW hazard quando un'istruzione che ha come operando un registro sia preceduta di 1, 2 o 3 cicli da un'istruzione che scrive su quello stesso registro
- tutti i forwarding avvengono tra l'uscita dell'ALU o della memoria dati e l'ingresso dell'ALU
- è necessario memorizzare i risultati in uscita dalla ALU e dalla memoria dati per tutti i cicli di clock necessari, nonchè predisporre gli opportuni percorsi di forwarding e aumentare le dimensioni dei mux in ingresso alla ALU e dell'unità zero detect
- il datapath viene modificato con l'inserimento di 3 livelli di registri di forwarding, la modifica dei mux in ingresso alla ALU e l'inserimento degli opportuni percorsi di forwarding
- controllo
- nella pipeline del MIPS tutti gli hazards possono essere individuati durante la fase ID, è quindi possibile determinare il tipo di forwarding necessario e, di conseguenza, settare opportunamente il controllo
- per identificare la verifica di un hazard RAW e settare
opportunamente i percorsi di forwarding è sufficiente una logica
combinatoria che confronti:
- i campi dell'IR che selezionano i registri sorgente dell'istruzione di cui si fa il fetch
- il campo dei registri di pipeline che conservano il valore dell'IR che seleziona registro destinazione di tutte le istruzioni che stanno avanzando nella pipeline
- data hazards RAW non completamente risolvibili col forwarding
- si verificano nel caso si volesse utilizzare un dato caricato su uno dei registri interni tramite load nell'istruzione immediatamente successiva
- l'informazione non è ancora disponibile nel datapath al momento in cui sarebbe necessaria.
- il risultato del load può essere bypassato alla and e alla or,
ma non alla sub
- lw $1,0($2) ; non scriverà il risultato sul registro r1 prima della fine della fase MEM
- sub $4,$1,$6 ; necessita del nuovo valore del registro r1 all'inizio della fase MEM dell'istruzione precedente
- soluzione hardware
- affinché si verifichi un RAW hazard che interessi una load e che richieda l'interlock della pipeline, è necessario che l'istruzione di load si trovi nello stadio EX della pipeline mentre, l'istruzione che richiede il dato fornito dalla load, si trova nello stadio ID
- stallando l'istruzione di sub (e le successive) per un ciclo viene letto il dato in maniera corretta a scapito di una limitata perdita di prestazioni
- una volta identificato un hazard (utilizzando la logica di
confronto vista precedentemente) l'unità di controllo deve stallare
la pipeline ed impedire che le istruzioni nello stadio IF e ID
avanzino
- ciò si può ottenere facendo in modo che il contenuto dei pipeline registers ID_EX, IF_ID e del PC, rimanga invariato
- soluzione software (tramite compilatore)
- si esegue una ottimizzazione del codice (scheduling software)
- l'utilizzo dello scheduling sw riduce sensibilmente la percentuale di stalli dovuti a questo tipo di hazard
- è possibile evitare la perdita di prestazioni che sarebbe comunque inevitabile con la sola modifica dell'hardware in caso di istruzione di load
- modificando la sequenza di esecuzione delle istruzioni, in molti casi, è possibile fare si che l'hazard non si verifichi affatto
- esempio:
- a = b + c; // questo assegnamento può causare uno stallo
- d = e - f; // allo stesso modo questo.
- supponiamo che a, b, c, d ,e, ed f siano in memoria.
- codice non ottimizzato:
- lw rb,b
- lw rc,c
- add ra,rb,rc
- sw a,ra
- lw re,e
- lw rf,f
- sub rd,re,rf
- sw d,rd
- codice ottimizzato:
- lw rb,b
- lw rc,c
- lw re,e
- add ra,rb,rc
- lw rf,f
- sw a,ra
- sub rd,re,rf
- sw d,rd
- risoluzione di control hazards
- supponendo che i salti siano il 30% la risoluzione degli hazard
sul controllo con l'inserimento di NOP provocherebbe un quasi
dimezzamento delle prestazioni
- CPIideale = 1, salti condizionati = 30% , 3 cicli di stallo
- CPIreale = 1.9
- la risoluzione degli hazard sul controllo tramite tecniche puramente software provoca una riduzione delle prestazioni inaccettabile, è necessario modificare l'architettura
- la soluzione è in 2 parti:
- anticipare il test che decide l'effettuazione del salto
- anticipare il calcolo dell'indirizzo di destinazione
- modifiche al datapath
- la riduzione degli stalli necessari da 3 ad 1 è ottenibile con l'anticipo del calcolo dell'indirizzo di salto e del test del salto
- le istruzione di salto condizionato richiedono il test di un registro, è possibile completare questo test alla fine dello stadio di ID spostando il modulo zero in questo stadio
- è inoltre necessario avere l'indirizzo del salto nello stadio di ID
- è sufficiente aggiungere un nuovo adder nello stadio ID per calcolare il nuovo PC, non posso usare la ALU perchè il risultato arriverebbe 1 ciclo dopo
- queste modifiche riducono il numero di cicli di stallo necessari da 3 ad 1
- gestione del ciclo di stallo rimanente
- per la gestione del ciclo rimanente vi sono 4 possibilità:
- inserire comunque un ciclo di stallo per ogni istruzione di salto condizionato: ho sempre un ciclo di stallo
- assumere sempre che il salto non sarà eseguito (predict branch not taken). in pratica non inserisco alcuna NOP (in tutti i casi in cui il salto non viene eseguito risparmio uno stallo)
- assumere sempre che il salto sarà eseguito (predict branch taken) (nel MIPS non ho alcuno vantaggio)
- delayed branch
- non inserendo ulteriori modifiche hardware oltre quelle viste per la riduzione dei cicli di stallo da 3 a 1, il salto avviene (quando il test è superato) con un ciclo di ritardo, quindi dopo l'esecuzione dell'istruzione successiva al salto, che viene definita delay slot
- anziché insere una NOP, è possibile modificare la sequenza originale del programma spostando nel delay slot una istruzione la cui esecuzione fuori sequenza non modifica il risultato finale del programma
- in molti casi è possibile, ad esempio, eseguire l'istruzione precedente a quella di salto, successivamente al salto, senza modificare il risultato complessivo della esecuzione
- nel caso non sia possibile inserire istruzioni utili, il delay slot viene riempito con una NOP
- esempio
- sequenza normale
- istruzione n-2
- istruzione n-1
- istruzione di salto n
- nop
- istruzione n+1
- istruzione n+2
- istruzione n+3
- sequenza modificata
- istruzione n-2
- istruzione di salto n
- istruzione n-1: delay slot
- istruzione n+1
- istruzione n+2
- istruzione n+3
- sequenza normale
- la scelta che consente la minore riduzione delle prestazioni è il predict branch not taken
- esistono altre tecniche più efficenti per pipelines più complesse che non vedremo
- per la gestione del ciclo rimanente vi sono 4 possibilità:
- supponendo che i salti siano il 30% la risoluzione degli hazard
sul controllo con l'inserimento di NOP provocherebbe un quasi
dimezzamento delle prestazioni
- incremento delle prestazioni reale
- eccezioni nel MIPS
- le eccezioni (o interrupt) previsti nella specifica originale
del DLX sono di 4 tipi:
- IF: (page fault, misaligned address, memory protection
violation)
- derivanti da mancato accesso alla memoria istruzioni per mancanza della istruzione richiesta nella cache, indirizzo disallineato (non multiplo di 4) o accesso ad un'area non consentita
- si verificano nello stadio/fase IF
- ID: (undefined or illegal opcode)
- derivanti dal fatto che l'istruzione della quale si è fatto il fetch non è un'istruzione consentita
- si verificano nello stadio/fase ID
- EX (arithmetic exception)
- derivanti dal verificarsi di un'eccezione aritmetica (overflow, divisione per zero)
- si verificano nello stadio/fase di EX
- MEM: (page fault, misaligned address, memory protection
violation)
- derivanti dal mancato accesso alla memoria dati per mancanza della locazione richiesta nella cache, indirizzo disallineato (non multiplo di 4) o accesso ad un'area non consentita
- si verificano nello stadio/fase di MEM
- IF: (page fault, misaligned address, memory protection
violation)
- la trattazione degli interrupts, non prevista nella specifica originale del MIPS, non varia in maniera significativa
- gestione delle eccezioni
- la gestione hardware dell'eccezione è ovviamente più complessa rispetto al caso del MIPS sequenziale
- appena l'eccezione viene rilevata e viene inibita la scrittura dell'istruzione che ha causato l'eccezione e di quelle che la seguono nella pipeline
- le istruzione più vecchie rispetto a quella che ha causato l'eccezione vengono completate normalmente
- la routine di gestione dell'eccezione come prima cosa salva il PC dell'istruzione che ha causato l'eccezione, dalla quale riprenderà l'esecuzione della normale sequenza del programma
- la problematica principale relativa alla gestione delle eccezioni è legata al fatto che nella pipeline le istruzioni vengono eseguite in maniera interlacciata nel tempo, e un'eccezione di una istruzione successiva si può presentare in un ciclo precedente rispetto ad una di una istruzione precedente
- le eccezioni vengono gestite solo quando l'istruzione a cui appartengono raggiunge lo stadio di WB, in modo da garantire la sequenza corretta fra istruzioni diverse
- per garantire la corretta sequenza per le possibili eccezioni multiple di una stessa istruzione a ciascuno stadio della pipeline è associato un Exception Status Vector che tiene conto delle eventuali eccezioni presentatisi nella storia della istruzione attraverso la pipeline
- più eccezioni si possono presentare contemporaneamente o in maniera non sequenziale
- appena viene rilevata un'eccezione tutte le scritture sono inibite
- lo status vector serve a garantire la sequenza corretta per le possibili eccezioni multiple di una stessa istruzione
- le eccezioni (o interrupt) previsti nella specifica originale
del DLX sono di 4 tipi:
- istruzioni a latency elevata nel MIPS
- utilizzo di più unità funzionali speciali (SFU)
- risulta impraticabile pensare di gestire le operazioni complesse (es. le operazioni sui dati floating point (fp)) e quelle semplici in maniera identica, significherebbe o incrementare a dismisura il clock cycle o eseguire tutte le istruzioni in un elevatissimo numero di cicli o entrambe le cose
- rimangono invariati i primi 2 (IF, ID) e gli ultimi 2 (MEM, WB) stadi della pipeline
- lo stadio di esecuzione (EX) riservato agli interi viene affiancato da altre unità funzionali speciali (SFU) dedicate ciascuna ad una classe diversa di operazioni
- le SFU possono essere a loro volta composte da più stadi e possono avere un troughput maggiore o uguale di 1 ed una latency maggiore di zero o molto maggiore di uno
- utilizzo di più unità funzionali speciali (SFU)
- pipeling
- eMIPS-sc (architettura singolo ciclo IS ridotto)
- ARM PROCESSORS FAMILY
- introduzione all'ISA ARM
- processori ARM
- sono largamente i più diffusi per sistemi integrati di tipo embedded (telefonia cellulare, sistemi wireless, pda, ecc)
- l'ISA è principalmente di tipo RISC con qualche concessione al CISC che non pregiudica l'efficienza della pipeline (vedremo che una delle pipelines utilizzate è pressochè identica a quella del MIPS)
- evoluzione
- il primo processore ARM (advanced RISC machine) fu sviluppato a cambridge presso la Acorn Computers Limited (successivamente ARM Limited), fra l'ottobre 1983 e l'aprile 1985, sulla base degli studi di Patterson e Ditzel che portarono alla realizzazione del primo processore RISC, il berkeley RISC I
- l'architettura originale basata su 3 stadi di pipeline è rimasta sostanzialmente invariata fino alla versione 7 compresa (1995)
- per ottenere un incremento di prestazioni nelle versioni 8 e 9 si è passati ad una pipeline a 5 stadi (simile a quella del MIPS)
- per un ulteriore incremento di prestazioni si è passati a pipeline più complesse a 6 stadi e più nelle versioni 10 e 11
- registri interni
- user mode
- 15 GPR (General Purpose Register)
- PC (program Counter)
- CPSR (Current Program Status Register)
- formato (bit)
- 31: N - Negative
- 30: Z - Zero
- 29: C - Carry
- 28: V - oVerflow
- 27-08: unused
- 07-06: IF - interrupt enables
- 05: T - control instruction set (T = 1 = istruzioni 16 bit Thumb; T = 0 = istruzioni 32 bit ARM standard)
- 04-00: mode - control processor mode
- formato (bit)
- system modes
- gli altri 16 registri del file register più altri 5 status registers sono usati per le modalità di funzionamento speciali (gestione di interrupts e chiamate dal SO)
- user mode
- eccezioni
- i processori ARM supportano un vasta varietà di eccezioni (interrupts, traps e chiamate da so)
- gestione delle eccezioni
- lo stato corrente viene salvato copiando il PC su r14_exc e il CPSR su SPSR_exc (exc indica il tipo di eccezione, che può essere fiq, svc, abort, irq, undefined)
- la modalità operativa del processore passa da user mode all'appropriato exception mode
- il valore del PC viene forzato ad un valore compreso tra 0016 e 1c16, corrispondente alla specifica tipologia di eccezione
- l'istruzione corrispondente alla locazione alla quale viene
forzato il PC di solito contiene un branch alla routine di
exception handling
- l'exception handler utilizza r13_exc, che è normalmente inizializzato per puntare ad uno stack in memoria utilizzato per salvare il contenuto dei registri
- al ritorno dall'eccezione viene ripristinata la modalità user
- organizzazione della memoria
- array lineare di bytes numerati da 0 a 232-1
- si può selezionare la modalità big endian o little endian
- tipi di dato
- bytes (8 bits)
- half-words (16 bits) - allineati su 4 byte 2-byte boundaries (con byte iniziale di indirizzo pari)
- words (32 bits) - allineati su 4byte (con byte iniziale di indirizzo multiplo di 4)
- instruction set
- architettura load-store
- gli operandi sono sempre contenuti nei GPRs
- le uniche istruzioni che coinvolgono la memoria sono quelle di load/store
- tipologie di istruzioni
- data processing (DPI)
- utilizzano e modificano il contenuto dei registri
- classificazione
- operazioni aritmetiche
- operazioni logiche bit a bit
- operazioni di spostamento dati fra registri
- operazioni di comparazione
- operandi: a 32-bit
- tre tipologie di operandi:
- contenuto di registri
- il secondo operando può essere una costante (immediate)
- un operando proveniente da un registro può essere shiftato
- risultato a 32-bit salvato in un registro
- le operazioni di long multiply fanno eccezione, infatti producono risultati a 64bit
- bit di condizione del CPSR
- ciascuna istruzione di data processing (dpi) può settare i bit
di condizione (N, Z, V, e C) del CPSR
- per tutte le DPI eccetto la comparazione occorre fare una richiesta specifica
- a livello assembly questo viene implementato agiungendo una 's' al nome dell'istruzione
- le operazioni aritmetiche settano tutti i flag N, Z, V, e C)
- le operazioni logiche e di spostamento dati settano N e Z
- i valori di V e C vengono preservati
- ciascuna istruzione di data processing (dpi) può settare i bit
di condizione (N, Z, V, e C) del CPSR
- moltiplicazioni
- vengono scritti sul registro destinazione i 32 bit meno significativi, gli altri bit vengono ignorati
- non è supportato che il secondo operando sia un immediato
- il registro destinazione non può coincidere con il primo registro sorgente
- se l'istruzione è di tipo 's', il bit V viene preservato e il bit C diviene privo di significato
- moltiplicazione per una costante
- viene implementata spesso tramite add e shift
- data transfer
- copiano il contenuto di locazioni di memoria in registri (load) o copiano il contenuto di registri in locazioni della memoria (store)
- istruzioni di load e store di registri singoli
- trasferimento di un dato (byte, half-word, word) tra un registro e la memoria
- istruzioni di load e store di registri multipli
- rende possibile il trasfermento di grandi quantità di dati
- utilizzate per la chiamata ed il ritorno da funzione, per salvare/ripristinare lo stato dei registri, e per copiare blocchi di dati
- istruzioni di swap di registri singoli
- rende possibile lo scambio del contenuto di un registro con il contenuto di una locazione di memoria
- usato per implementare semafori
- trasferimento multiplo di dati registri-memoria
- tutti i registri possono essere copiati tramite una singola istruzione
- l'ordine dei registri non ha importanza
- control flow
- branch
- l'esecuzione condizionale è usata per evitare l'utilizzo di istruzioni di salto nel caso si debba eventualmente evitare l'esecuzione di un limitato numero di istruzioni
- branch-and-link
- salva l'indirizzo di ritorno in modo da ripristinare la corretta sequenza d'esecuzione
- chiamate del sistema operativo (interrupt software)
- branch
- data processing (DPI)
- caratteristiche delle istruzioni
- istruzioni di data processing a 3 operandi
- esecuzione condizionale di ciascuna istruzione
- istruzioni di load/store multiplo di registri
- capacità di eseguire un'operazione di shift e una operazione di tipo ALU tramite singola istruzione eseguibile in singolo ciclo (troughput)
- possibilità di ampliare l'instruction set utilizzando le operazioni del coprocessore
- modalità di funzionamento thumb che consente una rappresentazione compressa su 16 bit delle istruzioni
- architettura load-store
- processori ARM
- pipelines utilizzate dai processori ARM
- pipeline a 3 stadi (ARM 1-7)
- componenti
- register file
- 2 porte in lettura, 1 porta in scrittura più 1 porta in lettura e 1 in scrittura riservate per r15 (PC)
- barrel shifter
- esegue lo shift o il rotate di un operando di un numero di bit a scelta
- ALU - esegue le operazioni logiche ed aritmetiche
- memory address register + incrementer
- memory data registers
- instruction decoder e logica dicontrollo associata
- register file
- fetch
- l'istruzione viene prelevata dalla memoria e caricata nella pipeline
- decode
- l'istruzione viene decodificata e i segnali di controllo del datapath vengono settati per il ciclo successivo
- in questo ciclo l'istruzione utilizza la logica di decodifica ma non il datapath
- execute
- l'istruzione utilizza il datapath: vengono letti gli operandi dal file register, uno degli operandi viene eventualmente shiftato, la ALU calcola il risultato che viene scritto (write back) sul file register
- componenti
- pipeline a 5 stadi (ARM 8,9, strongARM)
- architettura di Harvard
- la pipeline è simile a quella MIPS, le differenze riguardano:
- PC contenuto nel file dei registri, il che implica 2 porte in scrittura e 3 in lettura, al posto di 1 e 2
- barrel shifter
- moltiplicatore
- modulo per la gestione di pre e post indirizzamento autoincrementante che consenta il load e store di più dati nella stessa istruzione
- stadi
- fetch
- decode
- l'istruzione viene decodificata
- vengono letti i 3 operandi
- execute
- un operando viene shiftato e la ALU genera il risultato, oppure viene calcolato un indirizzo
- buffer/data (memory)
- accesso alla memoria dati (load, store)
- write-back
- scrivo il risultato sul reg file
- confronto tra pipelines a 3 ed a 5 stadi
- l'architettura a 3 stadi esegue in sequenza la decompressione delle istruzioni thumb e quindi la decodifica
- nella pipeline a 5 stadi non c'è tempo sufficiente e la decodifica dei 2 tipi di istruzioni avviene in parallelo
- lo stadio di memory della pipeline a 5 stadi non ha uno stadio equivalente nell'altra pipe, poichè l'accesso in memoria viene eseguito con un ciclo di execute addizionale che provoca uno svuotamento della pipeline
- l'incremento di prestazioni richiesto al sottosistema di memoria è notevole
- pipeline a 6 stadi (ARM10)
- consente frequenze di clock ancora più elevate (+50%)
- ottimizzazione degli stadi di fetch, memory ed execute, duplicazione dello stadio di decode
- pipeline a 3 stadi (ARM 1-7)
- ARM cores
- un sistema VLSI, può essere commercializzato in 3 diverse
forme:
- chip
- core hardware (silicon ip)
- core software (soft ip)
- nello sviluppo di System On Chip (SoC) è indispensabile avvalersi di moduli IP (cores) sviluppati da terzi
- i processori e le CPU della famiglia ARM sono sempre commercializzati sotto forma di cores (a parte lo strongARM)
- ARM7TDMI
- è attualmente il core ARM più diffuso
- è lo standard de-facto per applicazioni di telefonia mobile digitale
- viene commercializzato sotto forma di macrocella customizzata per la tecnologia richiesta dall'acquirente
- supporta il subset di istruzioni a 16 bit thumb, on-chip debug, embeddedICE hardware, moltiplicazione a 64 bit
- è stato fabbricato in numerose tecnologie CMOS diverse, con frequenze di clock fino a 100 mhz (66 typ) e alimentazioni fino a 0.9 v (3.3 typ)
- ARM7TDMI-S
- è la versione soft-ip dell'ARM7TDMI
- viene commercializzata come progetto verilog o VHDL e il porting sulla tecnologia target viene eseguito dall'acquirente tramite sintesi
- il processo di sintesi prevede la possibiltà di introdurre modifiche architetturali quali l'omissione del modulo embeddedICE e/o la sostituzione del moltiplicatore a 64 bit con uno a 32 bit, con una conseguente riduzione della funzionalità ma anche di dimensioni e consumi
- la versione completa è il 50% più estesa e consuma il 50% in più rispetto alla versione hard a parità di tecnologia
- ARM9TDMI
- è il core ARM per applicazioni che richiedano prestazioni tanto elevate da non poter essere soddisfatte dall'architettura 7
- viene commercializzata sotto forma di macrocella customizzata per la tecnologia richiesta dall'acquirente
- ha le stesse funzionalita dell'ARM7TDMI, con la pipeline del processore che passa da 3 a 5 stadi e un embeddedICE hardware più evoluto
- è stato fabbricato in tecnologie CMOS a 0.35, 0.25 e 0.18 micron, con alimentazioni fino da 3.3 a 1.2 V
- necessita di 2 cache separate (istruzioni e dati) per poter sfruttare la propria potenza elaborativa
- ARM9TDMI-S
- è la versione soft-ip dell'ARM9TDMI, viene commercializzata come progetto verilog o VHDL e il porting sulla tecnologia target viene eseguito dall'acquirente tramite sintesi
- implementa una versione più estesa dell'instruction set rispetto alla versione hard, con l'inclusione di istruzioni per il signal processing
- è il 30% più esteso rispetto alla versione hard a parità di tecnologia
- cores più evoluti
- oltre a core che includono semplicemente il processore, la ARM commercializza anche core che includono quei moduli che spesso è indispensabile integrare sul chip: memorie cache, Memory Management Units (MMU) ed (eventualmente) bus controllers per la gestione di periferiche di I/O
- tali moduli per poter raggiungere il massimo delle prestazioni potenziali del sistema devono essere fortemente ottimizzati per il processore ospite
- ha quindi senso per una società che intenda realizzare un System on Chip lasciare al fornitore del processore l'onere di sviluppare anche questi moduli
- ARM710T
- core hardware commercializzato dalla ARM Limited
- il core comprende i seguenti moduli
- pipeline a 3 stadi + circuiteria di debug e embeddedICE (ARM7TDMI)
- una cache unificata per i dati e le istruzioni di 8kbyte di tipo 4-way associative, write back
- MMU
- coprocessore
- controller per il bus delle periferiche di I/O (AMBA)
- ne esistono altre 2 versioni con caratteristiche simili (ARM720T e ARM740T)
- strongARM
- CPU stand-alone sviluppata dalla ARM Limited e da Digital Equipment Corporation
- la versione attualmente in commercio, la SA-110, è prodotta da Intel, che ha acquisito la digital nel 1998
- il chip comprende i seguenti moduli
- pipeline a 5 stadi con register forwarding
- esecuzione in singolo ciclo di tutte le istruzioni a parte la moltiplicazione a 64 bit ed i trasferimenti multipli di registri
- una cache per le istruzioni di 16kbyte di tipo 32-way associative
- unq cache per i dati di 16kbyte di tipo 32-way associative, write back
- MMU con gestione di TLB separata per istruzioni e dati
- tra i processori aventi lo stesso livello di prestazioni, dimensioni e consumi a parità di tecnologia sono fra i più ridotti
- ARM940T
- core hardware commercializzato dalla ARM Limited
- il core comprende i seguenti moduli:
- pipeline a 5 stadi + circuiteria di debug e embeddedICE (ARM9TDMI)
- una cache per le istruzioni di 16kbyte di tipo 16-way associative
- una cache per i dati di 16kbyte di tipo 16-way associative, write back
- MMU unificata per dati e istruzioni
- interfaccia per la gestione di un coprocessore esterno
- controller per il bus delle periferiche di I/O (AMBA)
- ne esiste un'altra versione con caratteristiche simili, l'ARM920T, che supporta WindowsCE
- un sistema VLSI, può essere commercializzato in 3 diverse
forme:
- embedded ARM-based systems
- OneC VWS22100 GSM chip
- introduzione all'ISA ARM
- ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-BASED
- compilazione c
- l'utilizzo di programmi scritti in linguaggi ad alto livello (C/C++) sul processore di un sistema embedded richiede la specifica di una serie di informazioni supplementari (dimensione della memoria, partizionamento e mappa della memoria, routine di boot, routine di interrupt, ecc) che non possono essere definite a livello di software C stesso, ma vanno definite a livello assembly-sistema operativo
- si rende necessaria l'integrazione di specifiche scritte a livelli diversi: C ed Assembly
- si rende inoltre necessario la conoscenza della strategia usata dal compilatore C utilizzato per la gestione a basso livello della memoria
- utilizzo della memoria (versione semplificata):
- il compilatore utilizza la memoria dividendola in due parti:
- stack
- porzione di memoria gestita automaticamente dal processore
- inizia a partire dalle locazioni con indirizzo più alto
- ha una dimensione variabile in funzione dello spazio di cui ha bisogno il programma
- si divide in
- main data
- a partire dagli indirizzi di memoria più alti troviamo la funzione main
- il compilatore alloca ricorsivamente lo spazio per le procedure allocando e disallocando zone di memoria
- functions data
- ogni procedura ha a disposizione una zona di memoria privata definita frame di attivazione
- la memoria di tipo dinamico viene allocata a partire dagli indirizzi bassi e si espande verso quelli alti, a partire da un certo indirizzo in poi
- main data
- heap
- zona di memoria gestita dinamicamente con le funzioni malloc e calloc
- inizia dalle locazioni di memoria più basse e si riempie verso locazioni di indirizzo più elevato
- stack
- Memoria Allocata Manualmente (MAM)
- si allocano i dati in memoria in maniera esplicita tramite i puntatori (senza malloc e calloc)
- si utilizza una zona di memoria per il salvataggio dei dati relativi alle routine di interrupt o ad altre routine speciali
- si può allocare o fra lo heap (sotto lo heap) e la memoria istruzioni (sopra la memoria istruzioni) oppure sopra lo stack
- in un sistema di Von Neumann, la parte della memoria di indirizzi più bassi, da zero in poi, viene utilizzata per le istruzioni
- per sistemi a memoria condivisa fra dati ed istruzioni la parte più bassa della memoria è dedicata alle istruzioni
- il compilatore utilizza la memoria dividendola in due parti:
- meccanismo di chiamata di procedura
- un opportuno sottoinsieme delle istruzioni dell'IS MIPS viene utilizzato per supportare la chiamata di una procedura (funzione), ed una volta terminata la sua esecuzione, il ritorno alla istruzione successiva alla chiamata
- ogni volta che deve essere convertita in assembly una chiamata a funzione, viene utilizzata una istruzione che forza il salto all'indirizzo della prima istruzione della procedura, e simultaneamente salva quello dell'istruzione successiva nel registro $31; tale istruzione è: jal label_procedura
- ogni procedura deve terminare con una istruzione che forza il valore del PC all'indirizzo della istruzione successiva a quella di chiamata, cioè all'indirizzo salvato in $31;tale istruzione è jr $31
- per gestire la possibilità che una funzione ne chiami a sua volta un'altra, sovrascrivendo l'indirizzo di ritorno in $31, è necessario che questo venga salvato in memoria
- è necessaria una convenzione che definisca come salvare in memoria l'indirizzo di ritorno, i parametri passati alla procedura, i dati locali, ecc
- gestione dello stack
- quando viene eseguita una funzione viene riservata ad essa una porzione dello stack detta frame
- la dimensione del frame dipende dal numero di dati relativi alla funzione che è necessario scrivere in memoria e può essere anche nulla se non ci sono dati da memorizzare.
- le informazioni da memorizzare per ogni frame sono:
- i parametri della funzione invocata oltre il quarto (i primi quattro parametri sono salvati nei registri da $4 a $7)
- indirizzo di ritorno
- dati locali della funzione
- registri temporanei che verranno sovrascritti durante l'esecuzione della funzione
- il frame viene gestito attraverso due registri che sono
utilizzati come puntatori:
- lo stack pointer ($sp)
- punta all'indirizzo della locazione più bassa del frame
- serve da riferimento per accedere alle locazioni del frame
- per esempio una scrittura su una locazione del frame verrà specificata con sw $31,20($sp)
- frame pointer ($fp)
- punta alla locazione più alta
- lo stack pointer ($sp)
- mentre $sp è indispensabile, $fp può in certi casi non essere utilizzato a seconda del compilatore, del livello di ottimizzazione utilizzato e del programma in esecuzione
- le informazioni sul numero di dati da salvare in memoria sono
forniti dal compilatore come commenti nel programma assembly in
uscita
- vars
- indica il numero di locazioni utilizzate per memorizzare le variabili locali
- se non sono dichiarati arrays questo numero è nullo
- regs
- indica il numero di registri che devono essere salvati in memoria
- in genere non ce ne sono se non viene invocata una funzione
- args
- indica il numero di locazioni destinate ai parametri di una funzione invocata
- se non viene invocata nessuna funzione questo numero è zero ma se viene invocata una funzione questo numero è sempre maggiore o uguale a 16, anche se la funzione invocata non ha parametri
- vars
- all'inizio dell'esecuzione del programma lo stack pointer punta all'indirizzo successivo a quello più alto in memoria, perché lo stack deve occupare la memoria a partire dagli indirizzi più alti
- le prime istruzioni prodotte dal compilatore riguardano la gestione automatica di memoria e registri
- ogni funzione, se ha bisogno di dati da memorizzare, inizia con l'istruzione che fa scendere lo stack pointer in modo da allocare lo spazio sufficiente
- seguono eventualmente i salvataggi dei registri che saranno sovrascritti
- la chiamata a funzione inizia sempre con un istruzione jal che porta il processore ad eseguire la prima istruzione della funzione chiamata
- a questo punto viene creato un altro frame e così via
- alla fine di ogni funzione l'eventuale valore restituito viene scritto sul registro $2 o $3
- vengono ripristinati i registri sovrascritti con i valori salvati in memoria e viene deallocato il frame riportando lo stack pointer alla posizione che aveva prima della chiamata a funzione
- con l'istruzione jr $ra si torna alla funzione chiamante
- specifica C e boot file
- anche il main viene gestito come una funzione qualunque
- nel caso di sistema operativo, vi saranno un assembler ed un linker che si occupano di gestire la chiamata al main in maniera trasparente per il programmatore
- nel nostro caso vogliamo che tutto sia gestito in maniera esplicita, non vi è un SO e neppure un vero linker
- la chiamata del main viene eseguita tramite una specifica assembly separata codificata in un file detto file di boot
- il file di boot file verrà unito alla traduzione assembly della specifica C dando vita ad una specifica assembly completa, su singolo file, che può essere tradotta in linguaggio macchina (cioè nel file di codifica delle locazioni di memoria dedicate alle istruzioni)
- file di boot
- il file di boot che utilizzeremo inizialmente si occupa esclusivamente delle cose essenziali
- la prima istruzione salta sempre alla routine di inizializzazione del sistema, che in questo caso consiste solo nell'inizializzazione dello stack pointer
- una volta inizializzato il sistema viene eseguita la chiamata del main
- quando si vuole che lo stack allocato automaticamente dal compilatore si estenda a partire dalle locazioni di indirizzo massimo della memoria, il valore di inizializzazione dello stack pointer coincide con la dimensione della memoria
- debug di software embedded
- il debug di software per sistemi embedded presenta delle difficoltà aggiuntive rispetto al debug di sw (software) per piattaforma PC
- è spesso impossibile definire le condizioni al contorno del sw in maniera puramente sw (gestione delle condizioni di interrupt, della mappa della memoria che è legata alla tipologia del sistema, ecc)
- è indispensabile un sistema di sviluppo di tipo hw-sw che consenta al sw embedded di interagire in tempo reale con l'hw di contorno al processore
- nel caso di sistemi embedded di tipo SoB (System on Board) la fase finale dello sviluppo sw è basate su sistemi di In-Circuit Emulation (ICE)
- nel caso di sistemi embedded di tipo SoC (System on Chip) si usa l'evoluzione dei sistemi ICE, si parla di sistemi embeddedICE
- i sistemi ICE sono basati su un dispositivo hardware
(emulatore) che implementa la funzionalità del processore (e può
quindi venire inserito nel sistema) ma in aggiunta manda in output
tutta una serie di informazioni sul suo funzionamento interno
(valore dei registri, valori sui bus e sulle porte di I/O, ecc.)
- l'emulatore è collegato ad una stazione di debug (un PC o una workstation) che può memorizzare tutte le informazioni utili per il debug
- il programmatore può monitorare le informazioni relative all'esecuzione, in modo da rilevare e correggere eventuali malfunzionamenti
- breakpoints
- sono inseribili a livello di codice c
- consentono di stoppare la simulazione e di visualizzare:
- il valore assunto da segnali, bus, porte di moduli, registri, ecc (controllo a livello hw)
- il contenuto dei registri interni del processore, delle locazioni mappate in memoria, dei segnali di interrupt, e dei segnali di I/O (controllo a livello fw)
- il valore assunto dalle variabili, dalle locazioni mappate in memoria, dai segnali di interrupt, e dai segnali di I/O (controllo a livello sw)
- disponendo di un modello del processore a basso livello ed avendo la possibilità di visualizzare qualunque segnale, siamo simo infatti in grado di monitorare qualunque grandezza in qualunque condizione o insieme di condizioni ci interessi
- utilizzando sistemi ICE reali le possibilità sono più limitate
- in questi casi comunque si tratta di debuggare solo l'applicazione ed al limite le periferiche, mai il processore stesso
- compilazione c
- BUS MEMORY MAPPING PERIFERICHE
- periferiche di interfaccia con l'esterno: registri di I/O
- un sistema di elaborazione di tipo embedded è dotato della possibilità di interfacciarsi con l'ambiente esterno tramite delle porte (canali) di comunicazione diretta
- fra le periferiche base vi sono quindi quelle di input/output
- periferiche di I/O di semplicità estrema: registri digitali di
input e di output
- ciascun registro può essere collegato con degli A/D o D/A converter
- il registro funge da interfaccia con l'esterno per il processore, in modo che questo sia in grado di prelevare/spedire dati comunicando con l'esterno
- memory mapping
- la comunicazione tra processore e periferica è implementata (completamente o in parte) tramite accessi in lettura/scrittura a locazioni di memoria
- le operazioni di lettura/scrittura vengono completate tramite istruzioni standard di load o store
- a ciascun registro interno di ogni periferica è associato un
indirizzo di memoria
- lo spazio di memoria dedicato alle periferiche deve essere costituito da indirizzi non utilizzati per la memoria
- lettura
- ogni qualvolta il processore necessita di acquisire un dato dall'esterno esegue una istruzione di load
- l'indirizzo è quello del registro della periferica che si vuole leggere
- il dato viene copiato su uno dei registri interni per poter essere subito utilizzato o copiato in memoria
- scrittura
- ogni qualvolta il processore necessita di mandare un dato all'esterno esegue una istruzione di store
- l'indirizzo è quello del registro della periferica sulla quale si vuole scrivere
- il dato viene copiato da uno dei registri interni sul registro
- implementazione hardware del bus
- approcci base:
- soluzione basata su multiplexers
- è adottata dal bus ad elevate prestazioni dei sistemi ARM (AMBA AHB)
- soluzione basata su three-state
- soluzione basata su multiplexers
- multiplexed shared-bus - master singolo
- scrittura sulle periferiche
- ciascuna periferica scrivibile ha la propria porta dati in ingresso collegata al bus di uscita del processore ed è inoltre collegata alla porta indirizzi
- è dotata di un decoder che abilita la scrittura su uno dei propri registri interni solo nel caso sul bus indirizzi si presenti l'indirizzo associato a quel registro e che il segnale di scrittura sia attivo
- lettura dalle periferiche
- le periferiche da cui leggere hanno la porta dati in uscita collegata ad uno degli ingressi di un multiplexer
- il multiplexer è collegato in uscita con la porta dati in ingresso del processore
- il multiplexer è controllato da un decoder che decodifica il bus indirizzi abilitando in uscita l'ingresso associato all'indirizzo decodificato
- ciascuna periferica è collegata alla porta indirizzi del processore
- è dotata di un decoder interno che manda in uscita il contenuto del proprio registro interno avente indirizzo uguale all'indirizzo in transito sul bus apposito
- scrittura sulle periferiche
- multiplexed shared-bus - master multipli
- sottosistema di scrittura
- ciascuna periferica scrivibile ha la propria porta dati in ingresso e la porta indirizzi collegate ad un mux che le connette al bus di uscita del e la porta indirizzi del processore che in quel momento utilizza il bus
- il controllo del bus viene assegnato da un arbitro che assegna il bus ad un solo processore controllando i mux
- l'handshake coi processori avviene tramite meccanismo di reck-ack
- sottosistema di scrittura
- approcci base:
- periferiche di interfaccia con l'esterno: registri di I/O
- INTRODUZIONE AI SO EMBEDDED
- architetture software
- in relazione al funzionamento reale delle applicazioni su sistemi embedded il comportamento delle applicazioni viste finora non era realistico
- l'architettura software prevedeva infatti l'esecuzione di un certo numero di task dopodichè l'applicazione si considerava conclusa
- nelle applicazioni reali il sistema funziona a partire dall'accensione fino al venir meno dell'alimentazione o fino al reset
- round robin (RR)
- è la più semplice architettura software possibile
- consiste in un loop infinito che si occupa di interrogare a turno (polling) ciascuna delle periferiche di I/O e di eseguire le routine associate ogni qualvolta sia necessario
- rispetto alle altre architetture ha il vantaggio di essere la più semplice, presenta però limiti notevoli
- funziona bene nei sistemi che verifichino contemporaneamente le
seguenti condizioni:
- poche porte di I/O
- elaborazioni non particolarmente lunghe
- vincoli di tempi di risposta in input/output non stringenti
- nel caso uno degli ingressi richieda dei tempi di risposta inferiori al tempo di risposta del sistema nel caso peggiore, il sistema non funziona
- è sufficiente che uno solo dei task abbia una durata elevata affinchè il sistema diventi praticamente inservibile, pur continuando a funzionare
- la struttura del sw non è robusta: può essere sufficiente l'inserimento di una nuova periferica da gestire per modificare la risposta temporale complessiva in maniera inaccettabile
- round robin con interrupts (RR+INT)
- presenta un incremento di complessità ma offre vantaggi sostanziali
- la struttura dell'architettura prevede che:
- le routine di interrupt (Interrupt Service Routin - ISR) si occupino di servire le incombenze urgenti legate alle periferiche di I/O settando dei flag ogni qualvolta viene acquisito/spedito un dato
- il loop principale interroga i flag associati alle routine di interrupt ed esegue le elaborazioni associate solo quando necessario
- viene introdotta una priorità nella gestione dei task
- la gestione di dati sotto interrupt pone il problema della
gestione di dati condivisi fra routine di interrupt e applicazione
principale
- shared data problem
- viene definita in questo modo la problematica legata ai malfunzionamenti causati dall'accesso asincrono ai dati di programma principale e routine di interrupt
- esempio
- supponiamo di utilizzare un sistema embedded per acquisire dei valori fisici di temperatura da dei sensori posti in diversi punti di un reattore nucleare
- segnalare un allarme se le temperature acquisite non sono uguali
- la prima attività viene eseguita sotto interrupt la seconda nella routine principale
- supponiamo di gestire 2 soli sensori e omettiamo per semplicità anche la gestione dei flag
- si verifica una condizione di allarme ingiustificata nel caso
si presentino entrambe le seguenti condizioni:
- si verifica un interrupt fra l'esecuzione delle 2 istruzioni che assegnano i valori di temperatura alle 2 variabili temp0 e temp1
- le temperature rilevate dai 2 sensori, pur rimanendo uguali fra loro, sono cambiate rispetto alle ultime rilevate
- nota che le routine di interrupt possono interrompere il programma fra 2 delle istruzioni assembly che implementano il test di uguaglianza
- il problema si verifica quando l'interrupt e il programma principale condividono dei dati, e il programma principale utilizza questi dati in maniera non atomica
- il pezzo di codice la cui interruzione può generare il problema viene definito critical section
- per risolvere il problema è possibile sfruttare la possibilità di disabilitare gli interrupt
- lo svantaggio principale della architettura rr+int (oltre al
fatto che risulta più complessa della rr semplice) è che tutti i
task del programma principale vengono gestiti con la stessa
priorità
- supponiamo di avere 3 task (a, b e c) gestiti nel programma
principale eseguiti nell'ordine a,b,c
- nel caso si verifichino tutti e 3 gli interrupt all'inizio dell'esecuzione della routine principale prima di iniziare l'esecuzione di a, il tempo di attesa prima di poter eseguire il task c è pari alla somma dei tempi di esecuzione dei task a e b, più i tempi di esecuzione delle routine di interrupt
- ciò potrebbe essere non accettabile per le specifiche di temporizazione del task c
- in questo caso si potrebbe spostare la sezione di codice che definisce il task c sotto interrupt
- come effetto collaterale ottengo però che il tempo di risposta massimo per gli altri 2 task (a e b) si incrementa di una quantità pari al tempo di esecuzione del task c, e ciò potrebbe essere a sua volta non accettabile
- un altro svantaggio fondamentale è la poca stabilità del codice
- modificando il codice di un task si influenza il tempo di risposta degli altri
- nel caso in cui la sequenza di esecuzione dei task nel programma principale esegua in successione tutti i task, il tempo di risposta nel caso peggiore per un singolo task è pari alla somma dei tempi di esecuzione di ciascuno degli altri task più la somma dei tempi di esecuzione di tutte le routine di interrupt
- un'altra alternativa potrebbe essere usare una sequenza di
esecuzione dei task nel programma principale che privilegi il task
c:
- c,a,c,b, c,a,c,b, c,a,c,b,
- questa soluzione ridurrebbe il tempo di attesa minimo per c, al massimo fra il tempo di esecuzione dei task a e b, al costo di un aumento del tempo massimo di risposta per a e b, che verrebbe aumentato di un termine c
- supponiamo di avere 3 task (a, b e c) gestiti nel programma
principale eseguiti nell'ordine a,b,c
- shared data problem
- utilizzando una architetture di tipo rr+int si può tentare di gestire le specifiche di temporizzazione sui task tramite ottimizzazioni del codice o spostamenti di sezioni di codice fra routine interrupt e programma principale, ma si va incontro a pesanti effetti collaterali e il codice risultante rimane poco stabile
- riducendo il tempo di risposta del caso peggiore per un task, si incrementa quello per tutti gli altri task
- function queue scheduling
- questa architettura prevede che ciascuna routine di interrupt comnichi al programma principale l'avvenuta esigenza di eseguire il task di elaborazione associato, non settando un flag, ma inserendo (push) un puntatore al task associato alla routine di interrupt in una coda di puntatori a funzioni
- il programma principale legge dalla coda il puntatore ad una funzione ed esegue il task stesso
- il trucco sta nel fatto che nessuna regola mi dice che devo per forza leggere dalla coda le funzioni nell'ordine in cui sono state inserite
- posso quindi privilegiare (in maniera ordinata e sistematica) i task con una priorità maggiore
- posso addirittura modificare la priorità a tempo di esecuzione adattandomi ad eventuali modifiche delle condizioni al contorno in maniera dinamica
- il tempo di risposta di caso peggiore per il task con priorità più elevata è pari al tempo di esecuzione del task più lento più la somma dei tempi di esecuzione delle routine di interrupt (come per rr+int)
- permane, anche se ridotto, il problema della poca stabilità del
codice
- modificando il codice di un task si influenza il tempo di risposta degli altri
- real time operating system (RTOS)
- questa architettura (come le altre viste finora) prevede che:
- le routine di interrupt si occupino delle operazioni più urgenti (quelle legate all'I/O)
- le routine di interrupt segnalino l'avvenuta necessità di eseguire il task associato alla routine di interrupt
- le differenze rispetto ai casi precedenti sono le seguenti:
- la comunicazione fra routine di interrupt e task del programma principale associato viene gestita dal sistema operativo
- la sequenza di esecuzione dei task è gestita dal sistema
operativo, non ci sono loop o code o altro gestiti in maniera
esplicita dal programmatore
- il codice del RTOS si occupa dello scheduling dei task, gestendo le priorità di esecuzione nella maniera più appropriata
- il RTOS può sospendere l'esecuzione di un task per eseguirne un altro
- le prime 2 differenze forniscono dei vantaggi legati soprattutto alla modalità di programmazione
- la terza differenza (l'interrompibilità dei task da parte non
solo delle routine di interrupt, ma anche da parte di altri task),
è sostanziale:
- un'architettura software basata su RTOS da la possibilita di gestire una priorità di esecuzione sistematica non solo delle routine di interrupt, ma anche dei task del programma principale
- il tempo di risposta di caso peggiore per il task a priorità più elevata si riduce alla somma dei tempi di esecuzione delle routine di interrupt
- l'architettura sw è robusta
- modificando il codice di un task non influenzo il tempo di risposta di quelli a priorità superiore
- i RTOS sono reperibili sul mercato
- in questo modo è possibile ridurre i tempi di sviluppo a costi ragionevoli o addirittura nulli (codice freeware), soprattutto in caso di utilizzo dello stesso RTOS per applicazioni diverse
- lo svantaggio principale legato all'utilizzo di un RTOS è legato al fatto che l'OS stesso utilizza una certa quantità di tempo di elaborazione
- questa architettura (come le altre viste finora) prevede che:
- architetture software
- MICROARCHITETTURE DI PROCESSORI REALI
- classi di processori
- general purpose - alte prestazioni
- pentiums, alpha, sparc
- utilizzati per eseguire software general pourpose
- usano OS pesanti: unix, nt
- workstations, PC
- processori embedded e cores
- ARM, MIPS, 486sx, hitachi sh7000, nec v800
- spesso eseguono continuamente un solo programma
- usano os più leggeri e che lavorano in tempo reale
- supporto per DSP
- telefoni cellulari, consumer electronics (es. CD players)
- microcontrollori
- a basso costo
- utilizzano word di dimensioni ridotte - comunemente 8 bit
- sono di gran lunga i processori prodotti in qantità maggiore
- automobili, termostati, condizionatori, televisori ...
- general purpose - alte prestazioni
- architetture DSP
- Digital Signal Processing (DSP)
- applicazione di operazioni matematiche a segnali acquisiti in maniera digitale
- i segnali vengono rappresentati digitalmente attraverso sequenze di campioni
- i segnali digitali vengono ottenuti dai segnali fisici attraverso trasduttori (es. microfoni) e Analog-to-Digital Converters (ADC)
- i segnali digitali vengono riconvertiti in segnali fisici attraverso Digital-to-Analog Converters (DAC)
- Digital Signal Processor (DSP)
- sistema elettronico per l'elaborazione dei segnali
- compiti:
- la maggior parte degli algoritmi DSP richiede:
- calcoli numerici fortemente ripetitivi
- attenzione alla precisione numerica dei risultati
- elevate prestazioni del sottosistema di memoria, soprattutto per supportare l'accesso ai vettori
- esecuzione real-time
- i DSP devono svolgere queste funzioni in maniera efficiente,
minimizzando allo stesso tempo:
- costo
- potenza
- uso della memoria
- tempo di sviluppo
- la maggior parte degli algoritmi DSP richiede:
- importanza dei DSP
- la tecnologia DSP è al centro di molti prodotti elettronici in commercio
- applicazioni DSP-intensive rappresentano il collo di bottiglia di molti computer systems
- le richieste computazionali di questo tipo di applicazioni crescono continuamente
- in molte applicazioni embedded i processori general pourpose non possono competere con i DSP
- DSP vs. general purpose CPU
- i DSP sono di norma realizzati per seguire pochi programmi
- questo consente spesso di utilizzare sistemi operativi molto semplici, senza memoria virtuale ...
- i DSP spesso devono rispondere a stringenti vincoli real-time
- questo significa che è necessario calcolare tutto quanto può succedere all'interno del time slot
- la gestione delle eccezioni rappresenta un aspetto molto critico
- i DSP di solito elaborano flussi continui di dati
- i DSP sono di norma realizzati per seguire pochi programmi
- applicazioni tipiche DSP
- speech and audio compression
- filtering
- modulation and demodulation
- error correction coding and decoding
- servo control
- audio processing (es. surround sound, noise reduction, equalization, sample rate conversion)
- signaling (es. DTMF detection)
- speech recognition
- signal synthesis (es. music, speech synthesis)
- requisiti processori DSP
- datapath
- supporto per aritmetica in virgola fissa
- istruzioni di MAC (Multiply ACcumulate)
- presenza di registri speciali
- architettura di memoria
- architettura di Harvard
- presenza di diverse memorie dati
- modalità di indirizzamento
- generatori di indirizzi
- indirizzamento bit-reversed
- buffer circolari
- controllo
- zero overhead loops
- datapath
- algoritmi DSP
- piuttosto che includere costoso hardware floating-point è più utile disporre di semplici e veloci istruzioni ad-hoc, che consentano di accelerare l'esecuzione di parti critiche delle applicazioni
- le operazioni MAC tendono ad essere dominanti all'interno di
applicazioni DSP(~50% codice critico)
- è utile implementare un datapath ottimizzato per l'esecuzione di MAC
- esempio: filtro FIR
- il filtro FIR (Finite Impulse Response) implementa in campo
digitale l'operazione di convoluzione
- y(z) = SUM(i=0,N-1){Ci·x(z-i)}
- le prestazioni del sottosistema di memoria devono essere
elevate:
- architettura di Harvard
- utilizzo di più RAM in parallelo
- bus mulipli
- i vincoli di tipo real-time impongono di utilizzare le cache
con parsimonia, sono fonte di non-determinismo:
- quasi mai cache dati
- raramente cache istruzioni
- come spesso accade quando si parla di DSP non è solo importante la prestazione massima del sistema ma soprattutto che siano garantite le prestazioni minime di cui si ha necessità
- il filtro FIR (Finite Impulse Response) implementa in campo
digitale l'operazione di convoluzione
- proprietà sistemi DSP
- modalità di indirizzamento
- la necessità di attingere dati dalla memoria in maniera continua, spesso in assenza di cache, porta all'utilizzo di modalità di indirizzamento molto articolate, in grado di gestire strutture dati disposte in memoria in una varietà di forme
- spesso si utilizzano anche circuiti ad-hoc per la generazione di indirizzi
- da quanto detto emerge come quella DSP non sia una tecnologia che riguarda solamente l'architettura di processori, ma coinvolge anche aspetti legati alle proprietà dell'instruction set e del sistema nel suo complesso
- per molti versi l'instruction set ARM è un instruction set DSP, per le complesse modalità di indirizzamento di cui dispone per esempio, mentre quello MIPS lo è molto meno
- la microarchitettura di processori per DSP sarà tipicamente derivata da quella RISC classica, come nel caso ARM, ma nulla vieta l'utilizzo di microarchitetture più avanzate come quelle che vedremo nel seguito, con i correttivi opportuni
- modalità di indirizzamento
- Digital Signal Processing (DSP)
- microarchitetture di processori avanzati
- Instruction Level Parallelism (ILP)
- rappresenta una delle strade perseguibili per ottenere unincremento delle prestazioni (velocità) dei processori
- sorgenti di parallelismo (in senso ampio all'interno di sistemi
a microprocessore):
- a livello di bit
- datapath più larghi (8, 16, 32, 64, ...)
- word level (SIMD)
- processori vettoriali
- instruction set con estensioni multimediali
- a livello di istruzione
- pipelining
- rappresenta la forma più semplice di instruction level parallelism
- parecchie istruzioni possono trovarsi in esecuzione contemporaneamente
- presenta dei problemi:
- control hazards
- costringono a scartare istruzioni di cui è già stato fatto fetch e decode
- data hazards
- quando l'istruzione sucessiva non può essere eseguita la pipeline viene stallata
- rigida sequenzialità
- è presente uno speciale 'slot' anche per cose che non vengono sempre utilizzate (MEM)
- hazard strutturali vengono evitati per 'costruzione'
- control hazards
- la tecnica del forwarding dei dati consente di risolvere parzialmente alcuni di questi problemi
- superscalar
- VLIW e EPIC
- pipelining
- a livello di task e applicazioni
- programmazione esplicitamente parallela
- threads multipli
- applicazioni multiple
- a livello di bit
- scheduling dinamico delle istruzioni
- le istruzioni possono essere eseguite fuori ordine (out of order) mantenendo corretto il risultato finale
- rompe la rigidità di una pipeline classica
- diventa possibile continuare a fare fetch e decode di nuove istruzioni anche se una delle istruzioni precedenti non può essere eseguita
- è possibile mantenere il write-back in attesa, nel caso esistano hazards strutturali, senza rallentare l'esecuzione
- reservation stations
- input
- Fetch and Decode Unit e Register File
- (1) descrittori delle istruzioni decodificate (2a) operandi noti da RF o (2b) 'tags' di sorgenti e operandi
- da tutte le unità di esecuzione
- (1)'tags' delle operazioni eseguite e (2) risultati corrispondenti
- Fetch and Decode Unit e Register File
- output
- all'unità di esecuzione corrispondente
- (1) descrizioni delle istruzioni pronte ad essere eseguite (2) tag e (3) operandi corrispondenti
- all'unità di esecuzione corrispondente
- ogni reservation station controlla che tutti gli operandi siano disponibili e che le unità di esecuzione siano libere per poter dare il via all'esecuzione
- maggiore è l'estensione dell'instruction window contenuta all'interno della/e reservation station, maggiori sono le possibilità di trovare istruzioni indipendenti, più istruzioni possono essere eseguite in parallelo
- gli operandi non ancora disponibili sono identificati dalla posizione all'interno della reservation station dell'istruzione/i che li deve originare
- i risultati appena prodotti dalle unità funzionali vengono resi disponibili agli ingressi della/e reservation stations attraverso bus dedicati
- l'operazione di writeback dei risultati all'interno del register file non fa più parte del percorso critico di esecuzione
- questo nuovo paradigma di esecuzione complica la gestione delle
eccezioni
- le contromisure attuabili dipendono dal tipo di supporto che si intende garantire per le eccezioni, se si vogliono cioè supportare eccezioni precise o imprecise
- il supporto per eccezioni precise esiste ogni volta che si garantisce che nel momento in cui il processore serve una eccezione lo stato del sistema sia già stato modificato in maniera coerente a quanto prescritto dalle istruzioni precedenti a quella interrotta dall'eccezione stessa
- nei casi in cui il vincolo precedente non viene rispettato si parla di eccezioni imprecise
- nel contesto di processori che utilizzano lo scheduling
dinamico delle istruzioni, in cui dunque l'esecuzione delle
istruzioni può essere 'out of order', per garantire una semantica
'precise exceptions' è necessario introdurre all'interno della
pipeline delle strutture che eseguano il 'commit' delle istruzioni
secondo l'ordine originario del programma
- in sostanza la strategia adottata consiste nel disaccoppiare la modifica dello stato (memorie e register file) dall'esecuzione delle istruzioni
- l'utilizzo di questa tecnica consente in pratica al processore di fare quello che vuole al suo interno, l'importante è che all'esterno dia la sensazione di una esecuzione sequenziale
- diventa necessario introdurre una nuova fase detta di COMMIT, RETIRE o GRADUATE
- input
- reorder buffer
- input
- Fetch and Decode Unit e Register File
- (1) fetched-operation tags in ordine originale, (2) registro destinazione o indirizzo e (3) PC
- da tutte le unità di esecuzione
- (1)'tags' delle operazioni eseguite e (2) risultati corrispondenti
- Fetch and Decode Unit e Register File
- output
- register file e memorie
- per ognuna delle istruzioni nell'ordine di esecuzione originario:(1) registro destinazione o indirizzo e (2) valore da scrivere
- register file e memorie
- l'aggiornamento del reorder buffer avviene in maniera sincronizzata con quello delle reservation stations
- input
- processori superscalari
- il Pentium 4 è un processore superscalare
- esecuzione superscalare
- più istruzioni possono iniziare la fase di esecuzione nello stesso momento
- ad ogni ciclo si può fare il fetch di più istruzioni
- questo non presenta grosse difficoltà a patto che esista il supporto da parte della cache istruzioni
- occorre determinare se esistono data e control dependencies tra
queste istruzioni
- si può utilizzare lo scheduling dinamico visto in precedenza
- l'uso delle reservation stations, che spesso prende il nome di tecnica di "shelving", consente di ottenere un incremento dell'ILP andando essenzialmente a compensare le situazioni di data hazards attraverso l'esecuzione "out-of-order" delle istruzioni
- i data hazards sono gli hazards di tipo RAW, detti anche "true dependencies"
- all'interno di un processore superscalare l'esecuzione può però essere bloccata anche dall'occorrenza di hazards del tipo WAW e WAR, in questo caso lo shelving non è sufficiente ad evitare lo stallo del processore
- dipendenze a catena
- esempio
- add ...,r2,... ;
- mul r2,...,... ;
- il problema che potenzialmente una sequenza come questa può rappresentare si capisce provando ad immaginare cosa potrebbe accadere nel caso in cui la add venisse rallentata da una istruzione che la precede
- in questo caso anche l'esecuzione della mul dovrebbe essere bloccata, perché in caso contrario la mul andrebbe a scrivere su r2, prima che la add l'abbia ancora letto
- è essenziale che le istruzioni successive alla mul vengano "messe al corrente" del cambiamento avvenuto
- la tecnica utilizzata per ovviare a questo tipo di probemi è il REGISTER RENAMING
- esempio
- nei processori superscalari, quello che ciascuna delle unità
funzionali fa in ogni ciclo viene deciso al momento dell'esecuzione
dell'istruzione in hardware
- la presenza del dynamic scheduler, necessaria all'interno dei
processori superscalari, risulta molto gravosa in quanto richiede
una notevole quantità d'area
- nel PowerPC 750 l'instruction sequencer occupa approssimativamente il 70% dell'area occupata da tutte le unità funzionali
- il tempo di ciclo è limitato in maniera sostanziale dalla logica di scheduling
- il lavoro di test di questa logica è molto complesso
- la complessità della logica di scheduling è nell'ordine del quadrato dell'issue rate
- la presenza del dynamic scheduler, necessaria all'interno dei
processori superscalari, risulta molto gravosa in quanto richiede
una notevole quantità d'area
- processori VLIW
- lo scheduling delle istruzioni viene fatto preventivamente via software (staticamente)
- un processore VLIW non ha bisogno della logica di scheduling perché la sua unità di fetch preleva dalla memoria un gruppo di istruzioni (che normalmente prende il nome di bundle o long instruction) già pronte per essere eseguite in parallelo
- il compilatore garantisce che non esistano dipendenze fra le istruzioni che vengono eseguite all'interno dello stesso bundle
- la maniera in cui le istruzioni vengono caricate in memoria cambia radicalmente rispetto a quella utilizzata all'interno dei processori superscalari, che poi è la medesima utilizzata all'interno dei processori RISC e sequenziali in genere
- i processori VLIW per esprimere il massimo delle loro potenzialità hanno bisogno di essere supportati da strumenti software di compilazione del codice molto avanzati, di gran lunga più complessi di quelli utilizzati in processori più tradizionali
- tutto il processo di estrazione dell'ILP viene infatti condotto completamente via software, per cui è indispensabile fare uso di tecniche di analisi molto sofisticate, che vanno ad aggiungersi alle fasi di compilazione standard
- le strutture di controllo interne al processore risultano molto semplificate rispetto a quelle necessarie in un processore superscalare
- per queste ragioni quando si parla di processori VLIW si dice
spesso che si tratta di "dumb processor for a smart compiler"
- tanto più il compilatore è sofisticato tanto più semplice potrà essere la struttura interna del processore
- una delle ragioni principali che ha ritardato la comparsa sul mercato di processori VLIW è stata proprio la difficoltà di avere a disposizione compilatori sufficientemente sofisticati
- i processori VLIW hanno una interfaccia tra hardware e software disposta in maniera diversa rispetto a quanto accade nei processori superscalari, in particolare i processori VLIW espongono in maniera maggiore la microarchitettura all'ambiente software
- problemi di compatibilità binaria
- una conseguenza negativa della proprietà vista in precedenza è che spesso una modifica alla architettura interna del processore è sufficiente a rendere i programmi che venivano eseguiti sulla versione precedente non più eseguibili nella nuova
- questo problema non esisteva in precedenza per i processori superscalari perché l'assegnazione delle singole istruzioni alle varie unità funzionali avveniva in hardware, per cui aggiungere una nuova unità funzionale al processore non crea problemi a patto che venga modificata in maniera coerente la logica di scheduling
- allo stato attuale non esistono soluzioni soddisfacenti per risolvere questo problema
- tra le soluzioni avanzate dalla comunità scientifica sta ricevendo sempre maggiori consensi quella della "software binary translation", che consiste in un software dedicato avente lo scopo di convertire dinamicamente, al momento dell'esecuzione, istruzioni appartenenti ad un instruction set in istruzioni dell'instruction set nativo del processore
- compressione del codice nella gerarchia di memoria
- non sempre il compilatore è in grado di garantire che tutte le
unità funzionali siano utilizzate al massimo in ogni ciclo
- in questi casi il compilatore stesso inserisce delle NOP nel file binario, stando a significare che in un particolare ciclo quella determinata unità funzionale dovrà rimanere inutilizzata
- nei casi in cui l'applicazione software non offra molto parallelismo, il numero di NOP da inserire nel codice può essere anche molto elevato, producendo un notevole spreco di risorse di memoria
- per risolvere questo problema sono state studiate delle tecniche che permettono di comprimere l'immagine dell'applicazione in memoria, permettendo di evitare l'introduzione di NOP
- i NOP veri e propri vengono invece generati via hardware nella fase di issue delle istruzioni
- non sempre il compilatore è in grado di garantire che tutte le
unità funzionali siano utilizzate al massimo in ogni ciclo
- Instruction Level Parallelism (ILP)
- classi di processori