Synchronisation
Data‐Race
Prozessor 1: berechne x = x + 2
lw $t0, 0($s0) # lade x nach $t0 addi $t0, $t0, 2 # $t0 = $t0 + 2
sw $t0, 0($s0) # speichere $t0 nach x
Gemeinsamer Speicher
Variable x Prozessor 2: berechne x = x – 1
lw $t0, 0($s0) # lade x nach $t0 addi $t0, $t0, -1 # $t0 = $t0 – 1
sw $t0, 0($s0) # speichere $t0 nach x
Es gelte zu Beginn: x=10 Gilt nach Durchlauf beider Code‐Abschnitte immer x=11?
Problem: Zugriff auf x ist nicht atomar
Prozessor 1: berechne x = x + 2
lw $t0, 0($s0) # lade x nach $t0 addi $t0, $t0, 2 # $t0 = $t0 + 2
sw $t0, 0($s0) # speichere $t0 nach x
Prozessor 2: berechne x = x – 1
lw $t0, 0($s0) # lade x nach $t0 addi $t0, $t0, -1 # $t0 = $t0 – 1
sw $t0, 0($s0) # speichere $t0 nach x
Zeit
Inhalt von x 10
Mögliche Lösung: Atomic‐Swap
Speicher
Variable lock 1.) Speicherinhalt lock in Register $t1 kopieren
2.) Alten Wert von $t1 nach lock kopieren
swap $t1, lock
Beispiel
$t1 lock
Vor Ausführung von swap 1 0
Nach Ausführung von swap 0 1
MIPS‐ISA hat kein swap, dennoch gibt es andere ISAs die so einen Befehl haben.
Also, zunächst ein Beispiel, wie man mittels swap synchronisieren kann.
swap ist hierbei atomar, d.h. während des swap wird jeglicher Speicherzugriff anderer Prozesse verzögert bis swap vollständig ausgeführt wurde!
Mögliche Lösung: Atomic‐Swap
Prozessor 1: berechne x = x + 2
addi $t1, $zero, 1 # setze $t1 auf 1
loop: swap $t1, lock # tausche $t1 und lock bne $t1, $zero, loop # nochmal wenn $t1!=0 lw $t0, 0($s0) # lade x nach $t0
addi $t0, $t0, 2 # $t0 = $t0 + 2
sw $t0, 0($s0) # speichere $t0 nach x swap $t1, lock # gib lock wieder frei
Gemeinsamer Speicher
Variable x
Variable lock (initial=0) Prozessor 2: berechne x = x – 1
addi $t1, $zero, 1 # setze $t1 auf 1
loop: swap $t1, lock # tausche $t1 und lock bne $t1, $zero, loop # nochmal wenn $t1!=0 lw $t0, 0($s0) # lade x nach $t0
addi $t0, $t0, -1 # $t0 = $t0 – 1
sw $t0, 0($s0) # speichere $t0 nach x swap $t1, lock # gib lock wieder frei
Mögliche Lösung: Atomic‐Swap
Prozessor 1: berechne x = x + 2
addi $t1, $zero, 1 # setze $t1 auf 1
loop: swap $t1, lock # tausche $t1 und lock bne $t1, $zero, loop # nochmal wenn $t1!=0 lw $t0, 0($s0) # lade x nach $t0
addi $t0, $t0, 2 # $t0 = $t0 + 2
sw $t0, 0($s0) # speichere $t0 nach x swap $t1, lock # gib lock wieder frei Prozessor 2: berechne x = x – 1
addi $t1, $zero, 1 # setze $t1 auf 1
loop: swap $t1, lock # tausche $t1 und lock bne $t1, $zero, loop # nochmal wenn $t1!=0 lw $t0, 0($s0) # lade x nach $t0
addi $t0, $t0, -1 # $t0 = $t0 – 1
sw $t0, 0($s0) # speichere $t0 nach x swap $t1, lock # gib lock wieder frei
Zeit x 10 lock
0
Weitere Lösung: Load Linked und Store Conditional
MIPS‐ISA hat ein Load‐Linked (ll) und Store‐Conditional (sc).
Also, wie kann man mit ll und sc synchronisieren?
Speicher
Variable lock Lade den Inhalt der Speicherstelle 0($s1) in das
Register $t1
ll $t1, 0($s1) # load linked
sc $t0, 0($s1) # store conditional 1. Wenn seit dem letztem load linked keiner
auf den Speicherblock zugegriffen hat , dann Speichere den Inhalt von Register $t0 auf die Speicherstelle 0($s1) und setze $t0 auf 1.
2. Sonst lasse den Speicherblock unberührt und setze $t0 auf 0.
Weitere Lösung: Load Linked und Store Conditional
Prozessor 1: berechne x = x + 2
loop: ll $t0, 0($s0) # $t0 = x
addi $t0, $t0, 2 # $t0 = $t0 + 2 sc $t0, 0($s0) # x = $t0
beq $t0, $zero, loop # nochmal bei failure
Gemeinsamer Speicher
Variable x
Prozessor 2: berechne x = x – 1
loop: ll $t0, 0($s0) # $t0 = x
addi $t0, $t0, -1 # $t0 = $t0 – 1 sc $t0, 0($s0) # x = $t0
beq $t0, $zero, loop # nochmal bei failure
Weitere Lösung: Load Linked und Store Conditional
Prozessor 1: berechne x = x + 2
loop: ll $t0, 0($s0) # $t0 = x
addi $t0, $t0, 2 # $t0 = $t0 + 2 sc $t0, 0($s0) # x = $t0
beq $t0, $zero, loop # nochmal bei failure
Prozessor 2: berechne x = x – 1
loop: ll $t0, 0($s0) # $t0 = x
addi $t0, $t0, -1 # $t0 = $t0 – 1 sc $t0, 0($s0) # x = $t0
beq $t0, $zero, loop # nochmal bei failure
Zeit x 10
Zusammenfassung der neuen Befehle
Instruktuion Beispiel Bedeutung
ll ll $s1, 0($s0) Lade den Inhalt von Adresse 0($s0) in
$s1 und starte eine atomare Read‐
Modify‐Write‐Operation.
sc sc $t0, 0($s0) Speichere den Inhalt von $t0 auf Adresse 0($s0), wenn seit dem
letzten ll nicht von einem anderen Prozess auf den Speicherblock
zugegriffen wurde, der das
adressierte Word enthält. Setze $t0 auf 1 in diesem Fall.
Ansonsten überschreibe den
Speicherbereich nicht und setze $t0 auf 0.
Quiz für den Quizmaster
Realisiere swap Register, Adresse mit ll und sc.
Erinnerung:
swap tauscht Speicher‐
inhalt und Registerinhalt atomar aus.
Das Register sei $s0 Die Adresse sei 0($s1) Das temporäre
Register sei $t0
Exceptions
Motivation: Behandlung von Overflows
Was war nochmal ein Overflow? Beispiel mit 8‐Bit‐Zahlen:
01011010 (= 90) + 01100111 (=103) --- 11000001 (=-63)
Die bisher behandelten ganzzahligen Arithmetik‐Instruktionen (z.B.
add, addi und sub ) können Overflow erzeugen.
Was wenn so ein Overflow auftritt? Einfach ignorieren?
Für jeden Overflow sollte eine Ausnahmebehandlungsroutine aufgerufen werden, die dann entscheidet was zu tun ist.
Anschließend kann der normale Code wieder ausgeführt werden.
Eine solche Ausnahmebehandlung wird über Exceptions realisiert.
Beispiele für Exceptions
Von außen ausgelöste Exceptions nennt man auch Interrupts
Ereignistyp Ausgelöst durch
den Prozessor?
Interrupt
Anfrage eines I/O Gerätes nein X
System‐Call ja
Arithmetischer Overflow ja Verwendung einer undefinierten
Instruktion
ja
Hardwarefehler ja/nein (X)
... ...
Behandlung von Exceptions
Genereller Ablauf:
Exception‐Handler
Aktuell laufendes Programm
Speicher
(1) Exception
(2) Sichere $pc (3) Springe zum
Exception‐Handler (4) Behandle die
Exception (5) springe ggf.
wieder zurück.
Rücksprung mit gesichertem $pc möglich.
Woher weis die CPU wo der Exception‐
Handler liegt?
Behandlung von Exceptions
Möglichkeit 1: Interrupt‐Vektor‐Tabelle
Speichere Adresse der aktuellen Programmausführung in einem
speziellen Register EPC.
Wähle aus der Interrupt‐Vektor‐
Tabelle die Adresse des Handlers für diesen Exception‐Typ und
springe dort hin.
Exception‐
Typ
Adresse des Exception‐
Handlers Undefinded
Instruction
0x8000 0000 Arithmetic
Overflow
0x8000 0180
... ...
Handler‐Routine springt nach Exception‐Behandlung ggf. zurück in den normalen Code, d.h. an die Programminstruktion auf die EPC
zeigt.
Interrupt‐Vektor‐Tabelle
Behandlung von Exceptions
Möglichkeit 2: Cause‐Register (das ist die MIPS‐Variante)
Speichere Adresse der aktuellen Programmausführung in einem
speziellen Register EPC.
Speichere den Exception‐Typ in einem speziellen Cause‐Register.
Springe an die Adresse des einen Exception‐Handlers.
Der Exception‐Handler behandelt den im Cause‐Register
beschriebenen Exception‐Typ.
Routine springt nach Exception‐
Behandlung ggf. zurück in den normalen Code, d.h. an die Programminstruktion auf die
EPC zeigt.
Nummer Exception‐Typ (Grund) 0 Interrupt (Hardware)
4 Address‐Error (load or fetch) 5 Address‐Error (store)
6 Bus‐Error (fetch) 7 Bus‐Error (store)
8 System‐Call
9 Break‐Point
10 Reserved Instruction
11 Coprocessor Unimplemented 12 Arithmetic Overflow
13 Trap
15 Floating‐Point‐Exception
MIPS Exception‐Codes
PC
MIPS Hardware‐Realisierung von Exceptions?
CPU Coprocessor 1 (FPU)
Coprocessor 0 (Traps and Memory)
$0 . . .
$31 Arithmetic
Unit
Multiply Divide
$0 . . .
$31
Arithmetic Unit Registers Registers
Lo Hi
BadVadr ($8) Status ($12) Cause ($13) EPC ($14) Registers
Memory
Es gibt einen weiteren Coprozessor
Beispiel: Aufruf des Exception‐Handlers
Coprocessor 0 (Traps and Memory) Registers
# Es gelte $s2 = 0x7fffffff
0x40000014 : add $s1,$s2,$s2 # Overflow!
0x40000018 : ...
...
# Exception-Handler beginnt immer hier 0x80000180 : ...
0x80000184 : ...
BadVadr ($8) Status ($12) Cause ($13) EPC ($14)
$pc vor Exception:
6 2 Exception‐Code für Arithmetic Overflow ist 12.
$pc nach Exception:
Beispiel: Handling der Exception
Coprocessor 0 (Traps and Memory)
40000014 Registers
# Es gelte $s2 = 0x7fffffff
0x40000014 : add $s1,$s2,$s2 # Overflow!
0x40000018 : ...
...
# Ein fauler Exception-Handler
0x80000180 : addi $s2,$zero,0# Problem gelöst 0x80000184 : eret # Rücksprung
BadVadr ($8) Status ($12) Cause ($13) EPC ($14)
$pc zur Behandlung:
$pc nach Behandlung:
Weitere Exceptions während des Handlings?
Coprocessor 0 (Traps and Memory)
40000014 Registers
# Es gelte $s2 = 0x7fffffff
0x40000014 : add $s1,$s2,$s2 # Overflow!
0x40000018 : ...
...
# Ein fauler Exception-Handler
0x80000180 : addi $s2,$zero,0# Problem gelöst 0x80000184 : eret # Rücksprung
BadVadr ($8) Status ($12) Cause ($13) EPC ($14)
$pc zur Behandlung:
$pc nach Behandlung:
Möglichkeiten, z.B.:
• Exception‐Handler erzeugt selber eine Exception
• Anfrage eines IO‐Gerätes
Exceptions während des Handlings abgeschaltet
Coprocessor 0 (Traps and Memory) Registers
# Es gelte $s2 = 0x7fffffff
0x40000014 : add $s1,$s2,$s2 # Overflow!
0x40000018 : ...
...
# Ein fauler Exception-Handler
0x80000180 : addi $s2,$zero,0# Problem gelöst 0x80000184 : eret # Rücksprung
BadVadr ($8) Status ($12) Cause ($13) EPC ($14) 15 8 1 0
Exception‐Level‐Bit:
0 = Exceptions werden berücksichtigt
1 = Exceptions werden nicht berücksichtigt
Wird bei Sprung in den Exception‐Handler immer gesetzt.
Bei Aufruf von eretwird das Bit wieder gelöscht
Status erlaubt auch das Maskieren von Interrupts
Coprocessor 0 (Traps and Memory) Registers
# Es gelte $s2 = 0x7fffffff
0x40000014 : add $s1,$s2,$s2 # Overflow!
0x40000018 : ...
...
# Ein fauler Exception-Handler
0x80000180 : addi $s2,$zero,0# Problem gelöst 0x80000184 : eret # Rücksprung
BadVadr ($8) Status ($12) Cause ($13) EPC ($14) 15 8 1 0
Interrupt‐Maske
Die Bits einer Interrupt‐Maske bestimmen welche Interrupt‐
Level Exceptions erzeugen dür‐
fen und welche ignoriert wer‐
den.
Jeder mögliche Interrupt ist einem Interrupt‐Level zugeord‐
net.
Mit Bit 0 des Status‐
Registers können Interrupts generell ein‐ und ausgeschaltet werden.
Pending‐Interrupts
Coprocessor 0 (Traps and Memory)
# Es gelte $s2 = 0x7fffffff
0x40000014 : add $s1,$s2,$s2 # Overflow!
0x40000018 : ...
...
# Exception-Handler beginnt immer hier 0x80000180 : ...
0x80000184 : ...
BadVadr ($8) Status ($12) Cause ($13) EPC ($14) 15 8 6 2
Exception‐Code
Registers Pending
Interrupts
Alle ankommenden Interrupts (auch ausmaskierte) setzen im Cause‐Register das
Pending‐Flag ihres Interrupt‐
Levels.
Wird das Masken‐Bit (oder das generelle Interrupt‐Bit) später wieder aktiviert, löst das
Pending‐Bit dann den Interrupt auf der CPU aus.
Zugriff auf die Coprocesor‐0‐Register
Coprocessor 0 (Traps and Memory)
BadVadr ($8) Status ($12) Cause ($13) EPC ($14) Registers
Erinnerung: Für den FPU‐Coprozessor (Coprozessor 1) hatten wir:
mfc1 : laden von FP‐Coprozessor‐Register in CPU‐Register mtc1 : laden von CPU‐Register in FP‐Coprozessor‐Register Analoge Instruktionen für den Coprozessor 0:
mfc0 : laden von Coprozessor0‐Register in CPU‐Register mtc0 : laden von CPU‐Register in Coprozessor0‐Register Beispiele:
mfc0 $s0, $13 # $s0=Coprozessor-Register 13 mtc0 $13, $s0 # Coprozessor-Register 13=$s0
Beispiel für Coprocessor 0 Register‐Zugriff
Coprocessor 0 (Traps and Memory) Registers
# Es gelte $s2 = 0x7fffffff
0x40000014 : add $s1,$s2,$s2 # Overflow!
0x40000018 : ...
...
# Ein etwas besserer Exception-Handler
# Exception auslösende Instruktion einfach überspringen 0x80000180 : mfc0 $k0,$14 # $k0 = EPC 0x80000184 : addi $k0,$k0,4 # $k0 = EPC+4 0x80000188 : mtc0 $14,$k0 # EPC=EPC+4 0x8000018c : eret # Rücksprung
BadVadr ($8) Status ($12) Cause ($13) EPC ($14)
$pc zur Behandlung:
$pc nach Behandlung:
Beachte: Register‐Satz‐
Konvention: $k0 und $k1 sind für OS‐Funktionen reserviert. Behandeln von Exceptions ist eine OS‐
Funktion.
Traps
Trap – eine Instruktion, die eine Bedingung testet und eine
Exception vom Typ 13 (Trap) auslöst, wenn die Bedingung erfüllt ist.
Trap‐Instruktionen am Beispiel:
teq $s0,$s1 # Trap‐Exception, wenn $s0 = $s1 teqi $s0,42 # Trap‐Exception, wenn $s0 = 42 tne $s0,$s1 # Trap‐Exception, wenn $s0 != $s1 tnei $s0,42 # Trap‐Exception, wenn $s0 != 42 tge $s0,$s1 # Trap‐Exception, wenn $s0 >= $s1
tgeu $s0,$s1 # Trap‐Exception, wenn $s0 >= $s1 (unsigned) tgei $s0,$42 # Trap‐Exception, wenn $s0 >= 42
tgeiu $s0,42 # Trap‐Exception, wenn $s0 >= 42 (unsigned) tlt $s0,$s1 # Trap‐Exception, wenn $s0 < $s1
tltu $s0,$s1 # Trap‐Exception, wenn $s0 < $s1 (unsigned) tlti $s0,$42 # Trap‐Exception, wenn $s0 < 42
tltiu $s0,42 # Trap‐Exception, wenn $s0 < 42 (unsigned)
System‐Call – Mechanismus zum Aufrufen von Betriebssystem‐
funktionen.
Anwendung der Instruktion syscall am Beispiel:
addi $v0, $zero, 1 # Lade System‐Call‐Code in $v0
# hier System‐Call‐Code 1 für den
# Systemcall print_int addi $a0, $zero, 42 # Lade das Argument für den
# System‐Call nach $a0.
# Hier soll die Zahl 42
# ausgegeben werden.
syscall # Rufe den System‐Call auf
# dies führt zu einer Exception
# vom Typ 8 (System‐Call)
System‐Calls
In SPIM/MARS verfügbare System‐Calls
Zusammenfassung der neuen Befehle
Instruktuion Bedeutung
eret Springe aus Exception‐Handler zurück. Setzt Exception‐Level‐Bit im Status‐Register wieder auf 0.
teq, teqi, tne, tnei, tge, tgeu, tgei, tgeiu, tlt, tltu, tlti, tltiu
Löse Trap‐Exception aus, wenn die Bedingung erfüllt ist.
Beispiel: teq $s0, $s1 löst einen Trap aus, wenn $s0 = $s1 gilt.
syscall Rufe System‐Call mit der in $v0 gespeicherten Nummer auf. Parameter des System‐Calls
werden in $a0 und $a1 übergeben.
Beispiel: syscall gibt eine 42 auf dem
Bildschirm aus, wenn $v0 = 1 (print_int) und
$a0 = 42.
Unsigned Arithmetik‐Instruktionen
Zu den bisher behandelten Arithmetik‐Instruktionen gibt es auch noch unsigned Varianten, die keine Overflow‐Exception erzeugen.
Beispiel:
# Es gelte $s2 = 0x7fffffff addu $s1,$s2,$s2 # erzeugt *keine*
# Overflow-Exception!
Signed und ihre Unsigend‐Varianten
Signed
Instruktion
kann
Overflow erzeugen
Unsigned Vertreter
kann
Overflow erzeugen
add ja addu nein
addi ja addiu nein
div nein divu nein
mult nein multu nein
mul nein
madd nein maddu nein
msub nein msubu nein
sub ja subu nein
Pseudoinstruktionen, Direktiven und Makros
Motivation für Pseudoinstruktionen
Wir hatten häufiger schon
addi $s1,$zero,wert # $s1=wert
Eine Instruktion li (Load‐Immediate) wäre doch nachvollziebarer li $s1,wert # $s1=wert
MIPS als ISA aus dem RISC‐Lager versucht aber den Instruktion‐Set möglichst klein zu halten. Damit ist so was wie ein li in der ISA nicht eingebaut. Kann man ja mit einem addi und dem $zero
Register ausdrücken.
Dennoch gibt es in MIPS oben genannte Instruktion. Wo kommt die her?
Das ist eine sogenannte Pseudoinstruktion, die der Assembler in Instruktionen der MIPS ISA übersetzt.
Umsetzung von Pseudoinstruktionen
Wie würde folgende move Instruktion vom Assembler umgesetzt?
move $s1,$s2 # Pseudoinstruktion $s1=$s2
Wie würde folgende blt Instruktion vom Assembler umgesetzt?
blt $s1,$s2, Label # Springe nach Label,
# wenn $s1<$s2 gilt
Beachte: Registerkonvention. Pseudoinstruktionen die ein Register zum Zwischenspeichern von Ergebnissen brauchen, benutzen dazu das Register $at (Assembler‐Temporary)