Esercizio 25pipeline MIPS con forwarding, stalli e bypass
In questa pagina 6
Testo (esempio di compitino di Architettura degli Elaboratori, UniPD, a.a. 2015-16, esercizio 8; stesso tipo di esercizio in tutti gli esempi di compitino dal 2008 al 2017). Sia data la seguente sequenza di istruzioni assembler, con i dati immediati in esadecimale:
ADD $9, $7, $5
LW $1, 7($9)
SUB $9, $1, $8
SW $3, 73($1)
SUBI $9, $3, 4
SW $7, 78($9)
SW $9, A($7)Si consideri la pipeline MIPS a 5 stadi vista a lezione, con possibilità di data-forwarding e con possibilità di scrittura e successiva lettura dei registri in uno stesso ciclo di clock. Mostrare come evolve la pipeline durante l'esecuzione del codice, spiegando nel dettaglio i motivi di un eventuale stallo o dell'utilizzo di un particolare circuito di bypass.
Teoria: PipelineIdea della catena di montaggio; pipeline a 5 stadi IF, ID, EX, MEM, WB; tempo di ciclo, tempo per n istruzioni in una pipeline a k stadi e speedup con esempi svolti; registri di pipeline; scrittura e lettura dei registri nello stesso ciclo; limiti (stadi sbilanciati, hazard).Pipeline →, Hazard nella pipelineHazard strutturali, sui dati e sul controllo; dipendenze RAW, WAR e WAW; stalli e bolle con diagrammi; data forwarding (bypass) e caso load-use; riordino delle istruzioni da parte del compilatore e dell'hardware; costo dei salti.Hazard nella pipeline →. Senza forwarding: Esercizio 24 · pipeline MIPS senza forwarding, numero di cicli.
Il modello della pipeline
- Stadi: IF (prelievo), ID (decodifica e lettura dei registri), EX (ALU o calcolo dell'indirizzo), MEM (accesso alla memoria dati), WB (scrittura del registro). Il banco registri si scrive nella prima metà del ciclo e si legge nella seconda: se il WB di una istruzione e l'ID di un'altra cadono nello stesso ciclo, la seconda legge già il valore nuovo.
- Forwarding (bypass): il risultato viene portato direttamente all'ingresso della ALU (stadio EX) dell'istruzione che lo usa:
- dall'uscita della ALU di un'istruzione aritmetica, tramite il registro
EX/MEM.ALUOutput, all'EX dell'istruzione subito dopo (distanza 1); - dal registro
MEM/WB(ALUOutputper le aritmetiche,LMD, il dato letto dalla memoria, per unaLW) all'EX dell'istruzione a distanza 2.
- dall'uscita della ALU di un'istruzione aritmetica, tramite il registro
- Una
LWproduce il dato solo alla fine del suo MEM: l'istruzione successiva che lo usa in EX deve aspettare 1 ciclo (load-use), anche con il forwarding. - Come nelle soluzioni dei temi, non è previsto un bypass verso lo stadio MEM: il dato che una
SWscrive in memoria (il primo registro,rt) si legge solo dal banco registri nello stadio ID, quindi laSWdeve avere l'ID nello stesso ciclo del WB della produttrice, o dopo. Il registro base dell'indirizzo, invece, serve in EX e può arrivare dal forwarding.
Dipendenze sui dati (RAW)
| # | istruzione | scrive | legge | dipendenza e soluzione |
|---|---|---|---|---|
| 1 | ADD $9, $7, $5 |
$9 |
$7, $5 |
|
| 2 | LW $1, 7($9) |
$1 |
$9 |
$9 da 1, distanza 1: bypass EX/MEM.ALUOutput → ingresso ALU (EX), nessuno stallo |
| 3 | SUB $9, $1, $8 |
$9 |
$1, $8 |
$1 da 2, distanza 1 con una load: il dato è pronto a fine MEM → 1 stallo, poi bypass MEM/WB.LMD → ALU |
| 4 | SW $3, 73($1) |
memoria | $3, $1 |
$1 da 2: quando la SW arriva in EX il WB di LW è già avvenuto → lettura dal banco registri in ID, nessun bypass, nessuno stallo |
| 5 | SUBI $9, $3, 4 |
$9 |
$3 |
|
| 6 | SW $7, 78($9) |
memoria | $7, $9 |
base $9 da 5, distanza 1: bypass EX/MEM.ALUOutput → ALU, nessuno stallo |
| 7 | SW $9, A($7) |
memoria | $9, $7 |
dato $9 da 5, distanza 2: non c'è bypass verso MEM → l'ID deve cadere nel ciclo del WB di SUBI o dopo → 1 stallo |
(SUB e SUBI scrivono entrambi $9: è una dipendenza WAW, senza effetti in una pipeline in ordine; ogni lettura di $9 si riferisce alla scrittura più recente che la precede.)
Evoluzione della pipeline
| istruzione | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ADD $9, $7, $5 |
IF | ID | EX | MEM | WB | ||||||||
LW $1, 7($9) |
IF | ID | EX | MEM | WB | ||||||||
SUB $9, $1, $8 |
IF | ID | ID | EX | MEM | WB | |||||||
SW $3, 73($1) |
IF | IF | ID | EX | MEM | WB | |||||||
SUBI $9, $3, 4 |
IF | ID | EX | MEM | WB | ||||||||
SW $7, 78($9) |
IF | ID | EX | MEM | WB | ||||||||
SW $9, A($7) |
IF | ID | ID | EX | MEM | WB |
Commento ai cicli.
- Ciclo 4.
LWè in EX con$9ottenuto dall'uscita della ALU diADD(registroEX/MEM): bypassEX.ALUOutputdiADD→ ingresso superiore della ALU diLW. - Cicli 4-5: stallo.
SUBha bisogno di$1, cheLWproduce al ciclo 5 (fine del suo MEM).SUBresta in ID per un ciclo (il secondo ID) e laSWdietro resta in IF. Al ciclo 6SUBva in EX e riceve$1dal registroMEM/WB.LMD: bypassMEM.LMDdiLW→ ALU diSUB. - Ciclo 6.
SW $3, 73($1)ha l'ID nel ciclo del WB diLW(6): legge$1dal banco registri, senza bypass. - Ciclo 9.
SW $7, 78($9)è in EX con$9daSUBI(che è in MEM): bypassEX.ALUOutputdiSUBI→ ALU dellaSW. - Cicli 9-10: stallo. L'ultima
SWdeve scrivere in memoria$9, scritto daSUBI, che fa il WB al ciclo 10. Il suo ID (secondo ID) cade nel ciclo 10, laSWlegge$9dal banco registri e va in EX al ciclo 11.
Totale: istruzioni 2 stalli = 13 cicli (l'ultimo WB è al ciclo 13).
Un'altra istanza (esempio di compito a.a. 2014-15)
SUB $2,$7,$5; LW $1,7($2); ADD $2,$1,$8; SW $3,73($1); SUBI $2,$3,4; ADDI $7,$3,8; ADD $1,$7,$2. Con lo stesso modello: 12 cicli, 1 solo stallo (il load-use di ADD $2,$1,$8), e i bypass sono quelli indicati nella soluzione allegata al testo: EX.ALUOutput di SUB → ALU di LW; MEM.LMD di LW → ALU di ADD; MEM.ALUOutput di SUBI → secondo ingresso della ALU dell'ultima ADD; EX.ALUOutput di ADDI → primo ingresso della ALU dell'ultima ADD.
Verifica con un programma
import re
def pipeline(codice):
"""Pipeline MIPS a 5 stadi con forwarding verso EX, nessun bypass verso MEM per il dato di una store.
Restituisce (cicli totali, stalli per istruzione)."""
ins = []
for riga in codice:
op, resto = riga.split(None, 1)
a = [x.strip() for x in resto.split(",")]
if op in ("LW", "SW"):
base = re.search(r"\((\$\d+)\)", a[1]).group(1)
ins.append((op, a[0] if op == "LW" else None, [base], [a[0]] if op == "SW" else []))
else:
ins.append((op, a[0], [x for x in a[1:] if x.startswith("$")], []))
ex, idc, stalli = [], [], []
for i, (op, scrive, per_ex, per_mem) in enumerate(ins):
if i == 0:
ifc, id_i = 1, 2
else:
ifc = idc[i - 1]
id_i = max(ifc + 1, ex[i - 1])
e = id_i + 1
for r in per_ex + per_mem:
for p in range(i - 1, -1, -1):
if ins[p][1] == r:
if r in per_mem: # dato della store: banco registri, dopo il WB
e = max(e, ex[p] + 2 + 1)
else: # operando della ALU: forwarding
e = max(e, ex[p] + (2 if ins[p][0] == "LW" else 1))
break
idc.append(id_i); ex.append(e); stalli.append(e - id_i - 1)
return ex[-1] + 2, stalli
codice = ["ADD $9, $7, $5", "LW $1, 7($9)", "SUB $9, $1, $8", "SW $3, 73($1)",
"SUBI $9, $3, 4", "SW $7, 78($9)", "SW $9, A($7)"]
print(pipeline(codice)) # (13, [0, 0, 1, 0, 0, 0, 1])
altro = ["SUB $2, $7, $5", "LW $1, 7($2)", "ADD $2, $1, $8", "SW $3, 73($1)",
"SUBI $2, $3, 4", "ADDI $7, $3, 8", "ADD $1, $7, $2"]
print(pipeline(altro)) # (12, [0, 0, 1, 0, 0, 0, 0])Errori comuni
- Dimenticare lo stallo del load-use: con la
LWil forwarding non basta, resta 1 ciclo. - Dimenticare che una
SWha due registri letti con ruoli diversi: la base (necessaria in EX, può avere il bypass) e il dato da scrivere (letto in ID, senza bypass nel modello del corso). - Usare come produttrice la scrittura sbagliata di
$9(non la più recente). - Indicare il bypass dal registro sbagliato: distanza 1 →
EX/MEM, distanza 2 →MEM/WB, e per le load il dato è nelLMD, non nell'ALUOutput. - Dimenticare che durante lo stallo l'istruzione resta in ID e quella dopo resta in IF.
Versione ripasso
Esempio di compitino a.a. 2015-16, esercizio 8: sette istruzioni MIPS, pipeline a 5 stadi con forwarding, scrittura e lettura dei registri nello stesso ciclo. Teoria: PipelineIdea della catena di montaggio; pipeline a 5 stadi IF, ID, EX, MEM, WB; tempo di ciclo, tempo per n istruzioni in una pipeline a k stadi e speedup con esempi svolti; registri di pipeline; scrittura e lettura dei registri nello stesso ciclo; limiti (stadi sbilanciati, hazard).Pipeline →, Hazard nella pipelineHazard strutturali, sui dati e sul controllo; dipendenze RAW, WAR e WAW; stalli e bolle con diagrammi; data forwarding (bypass) e caso load-use; riordino delle istruzioni da parte del compilatore e dell'hardware; costo dei salti.Hazard nella pipeline →.
Modello: bypass verso l'ingresso della ALU (stadio EX): distanza 1 da EX/MEM.ALUOutput, distanza 2 da MEM/WB (ALUOutput o, per una LW, LMD). Load-use: 1 stallo anche con forwarding. Nessun bypass verso MEM: il dato scritto da una SW si legge solo in ID (dopo il WB della produttrice); la base passa dal forwarding.
Esito (ADD $9,$7,$5; LW $1,7($9); SUB $9,$1,$8; SW $3,73($1); SUBI $9,$3,4; SW $7,78($9); SW $9,A($7)):
LW←ADD: bypassEX/MEM.ALUOutput, nessuno stallo;SUB←LW: 1 stallo (load-use), bypassMEM/WB.LMD;SW $3,73($1):$1letto dal banco registri (WB diLWgià avvenuto), nessuno stallo;SW $7,78($9)←SUBI: bypassEX/MEM.ALUOutputsulla base;SW $9,A($7): il dato$9daSUBI(WB al ciclo 10) si legge in ID → 1 stallo.
Totale cicli. Altra istanza (a.a. 2014-15): 12 cicli, 1 stallo.
Errori comuni: load-use dimenticato; dato e base di una SW confusi; produttrice sbagliata (WAW su $9); bypass dal registro sbagliato; ID/IF non fermati durante lo stallo.