lösningsförslag

Lösningsförslag till tentamen i Datorteknik 2015-01-07
Observera att dessa lösningsförslag kan vara felakktiga och/eller ofullständ
Uppgift 1. Maskinaritmetik
a) Flyttalet X är positivt, eftersom teckenbiten är noll. Exponenten lagras som 1000 1001 = 137; eftersom
exponenten lagras i excess-127-kod så är exponentvärdet 137 – 127 = 10.
För mantissan lagras endast siffrorna efter binärkommat, och ”1,” ska läggas till före. Då blir hela mantissan:
1,000 0000 0010 0000 0000 0000
Eftersom exponenten = 10 så ska binärkommat flyttas 10 positioner åt höger, så att talet blir
1000 0000 001,0 0000 0000 0000 binärt = 1025 decimalt.
b) Z = 4X ska ha ett 2 enheter större exponentvärde, eftersom 4X = X · 22. Resultat:
0 1000 1011 000 0000 0010 0000 0000 0000
c) Y = 9,75 decimalt = 1001,11 binärt = 1,00111 · 23. Positivt tecken ger nollställd teckenbit, Exponent 3
lagras som 127 + 3 = 130 decimalt = 1000 0010 binärt. I mantissan skalas inledande ”1,” bort. Resultat:
0 1000 0010 001 1100 0000 0000 0000 0000
d) +42 decimalt = 32 + 8 + 2 = 101010 binärt. Fyll ut till 8-bits heltal: 0010 1010
-42 fås genom att invertera alla bitar och sedan addera 1 i minst signifikanta positionen:
0010 1010 inverteras till 1101 0101, varefter 1 adderas: 1101 0101 + 1 = 1101 0110.
Svar: +42 lagras som 0010 1010, -42 lagras som 1101 0110
e) Teckenbiten är oförändrad, exponenten ändras till excess-1023-kod, mantissan fylls ut med nollor i slutet.
+10 i excess-1023-kod = 1033 decimalt = 1024 + 9 = 100 0000 1001. Svar:
0 100 0000 1001 0000 0000 0100 0...0
Uppgift 2. Assemblerprogrammering för Nios II
a) Det behövs en loop som går igenom bitarna. Den kan antingen gå 8 varv precis, eller gå tills aktuellt värde
har nått ner till 0. Det sistnämnda kräver att man före loopen nollställer de bitar som ska ignoreras.
onesinbyte:
movi
r2, 0
# nollställ resultat
andi
r4, r4, 0xff
# maska parameter
oloop: andi
r8, r4, 1
# undersök 1 bit
addi
r2, r2, r8
# addera till resultat
srli
r4, r4, 1
# skifta parameter höger
bne
r4, r0, oloop
# fortsätt om det finns bitar kvar
ret
b) Denna subrutin ska anropa onesinbyte och bara behålla den minst signifikanta biten av returvärdet.
partest:
PUSH
call
POP
andi
ret
r31
onesinbyte
r31
r2, r2, 1
#
#
#
#
spara returadress annars funkar inget
anropa
återställ returadress
maska returvärde
c) Denna subrutin ska hämta en byte, anropa partest och om resultatet var 1 även invertera en bit i den
hämtade byten och skriva det uppdaterade värdet till minnet.
parmem:
PUSH
r31
# spara returadress
PUSH
r4
# spara parameter
ldb
r4, 0(r4)
# hämta byte
call
partest
# undersök paritet
POP
r4
# återställ parameter
beq
r2, r0, pmend
# om jämn paritet så är vi klara
ldb
r8, 0(r4)
# hämta om byte för uppdatering
xori
r8, r8, 0x80
# invertera bit 7
stb
r8, 0(r4)
# skriv uppdaterat värde
pmend: POP
r31
# återställ returadress
ret
d) Denna subrutin ska ha en loop som kör minst ett varv. Loopräknare och adress måste sparas på säker plats
så att de inte skrivs över av de subrutiner som anropas.
pararray:
PUSH
r31
PUSH
r16
PUSH
r17
mov
r16, r4
mov
r17, r5
ploop: mov
r4, r17
call parmem
addi
r17, r17, 1
subi
r16, r16, 1
bne
r16, r0, ploop
POP
r17
POP
r16
POP
r31
ret
#
#
#
#
#
#
spara returadress
plats för loopräknare
plats för adress
loopräknare kopieras
adress kopieras
kopiera adress till rätt register
#
#
#
#
räkna upp adress
räkna ner loopräknare
fortsätt om det finns bytes kvar
återställ register i rätt ordning
Uppgift 3. In- och utmatning och avbrott
a) Blockerande pollning.
inputwait:
movia
r8, 0x840
#
ldwio
r2, 12(r8)
#
andi
r2, r2, 0xf
#
beq
r2, r0, inputwait
stwio
r0, 12(r8)
#
ret
#
b) Icke-blockerande pollning.
inputcheck:
movia
r8,
ldwio
r2,
beq
r2,
movi
r2,
icend: ret
c) Initialisering av avbrott.
key3init:
movia
stwio
movi
stwio
ret
r8,
r0,
r9,
r9,
startadress till parallellport
läs edge capture
maska fram de fyra minst signifikanta
# vänta vid behov
skriv för att nollstalla edgecap
returnera med rätt värde i r2
0x840
12(r8)
r0, icend
-1
#
#
#
#
startadress till parallellport
läs edge capture
hopp om viktiga edgecap-bitar noll
skriv alternativt returvärde
0x840
0(r8)
8
8(r8)
#
#
#
#
startadress till parallellport
nollstall edgecap
etta för KEY3
initialisera för avbrott
d) Avbrottsrutin.
keyisr: PUSHALL
call
andi
beq
call
kiend: POPALL
eret
inputwait
r8, r2, 8
r8, r0, kiend
key3work
#
#
#
#
#
#
#
spara alla viktiga registerinnehåll
undersök och återställ edgecap
kolla om KEY3
om inte gå till retur
om KEY3 anropa rutin som gör jobbet
återställ registerinnehåll
Exception RETurn
Uppgift 4. Cacheminnen
a) Rumslokalitet innebär att när en minnescell lästs eller skrivits, så läses troligen snart en annan minnescell
med näraliggande adress. Instruktionshämtningarna för programavsnittet i uppgiften har hög rumslokalitet,
precis som all annan ”rak kod” utan hoppinstruktioner: när en instruktion har hämtats, så hämtas helt säkert
nästa instruktion i adressordning och den ligger väldigt nära den föregående. Datareferenserna har viss
rumslokalitet: de flesta av adresserna som används finns i ett litet område nära den plats dit r8 pekar.
b) Ett direktmappat cacheminne med 7 bit radnummer och 5 bit offset använder totalt 12 bit för att välja ut
en byte i cacheminnet. Då har cacheminnet storleken 2 12 byte = 4096 byte. Eftersom offset har 5 bit så är
blockstorleken 25 byte = 32 byte. Det index som används på rad 2 är: 0111 000. Här visas hela adressen med
indexbitarna i fetstil: 0...0 1000 0000 1100 0111 0000 1100.
c) Cacheminnesparametrar enligt föregående deluppgift.
rad 2: index 0111 000, miss, cacheminnet är tomt, 0x80c700 – 0x80c71f hämtas.
rad 3: samma index, träff.
rad 4: samma index, träff.
rad 5: samma index, träff.
rad 6: samma index, träff.
rad 7: index 0111 100, miss, nytt index, 0x80c780 – 0x80c79f hämtas.
rad 8: index 0111 000, träff.
rad 9: index 1000 000, miss, nytt index, 0x80c800 – 0x80c81f hämtas.
rad 10: index 0111 000, träff.
d) Ny uppdelning av adressen: 25 bit tag, 3 bit index, 4 bit byte-offset.
rad 2: index 000, miss, 0x80c700 – 0x80c70f hämtas.
rad 3: samma index, träff.
rad 4: index 001, miss, 0x80c710 – 0x80c71f hämtas.
rad 5: index 000, träff.
rad 6: index 001, träff.
rad 7: index 000, miss, 0x80c780 – 0x80c78f hämtas och skriver över 0x80c700 – 0x80c70f.
rad 8: index 000, miss på grund av konflikt, 0x80c700 – 0x80c70f hämtas igen och skriver över föregående.
rad 9: index 000, miss, 0x80c800 – 0x80c81f hämtas och skriver över föregående.
rad 10: index 001, träff.
e) Samma adressuppdelning som föregående. Rad 2 till och med rad 6 samma, kan antas använda set 0.
rad 7: index 000, miss, 0x80c780 – 0x80c78f hämtas till set 1.
rad 8: index 000, träff i set 0.
rad 9: index 000, miss, 0x80c800 – 0x80c81f hämtas till set 2.
rad 10: index 001, träff.
Uppgift 5. Processorkonstruktion
a) Ingen data-forwarding medför extra NOP (en eller flera) vid RAW-beroende (Read After Write).
Med fyrstegspipeline och transparenta register så räcker 1 NOP. Följande platser kräver NOP:
efter rad 3 (r9), rad 4 (r10), rad 5 (r10 igen), rad 6 (r11), rad 7 (r12), rad 9 (r13).
b) Femstegspipeline medför att en load-instruktion måste efterföljas av en oberoende instruktion.
Det finns två load-instruktioner i programmet: rad 1 och rad 6.
Efter rad 1 finns redan en oberoende instruktion. Efter rad 6 behövs NOP.
c) Delayed branch medför ytterligare NOP förutom den från föregående deluppgift.
Efter rad 8 är NOP tänkbar men kan uteslutas, det påverkar inte resultatet från subrutinen om r12 skrivs över.
Efter rad 11 är NOP nödvändig, annars förstörs returvärdet i register r2.
d) Om r12 innehåller noll så tas hoppet på rad 8 och antalet utförda instruktioner är 9. Antalet klockcykler
blir 9 + 2 extra för de två hopp som tas: instruktionen på rad 6 och instruktionen på rad 11.
Uppgift 6. Trådar och synkronisering
a) Time-slice 1: P1 producerar 3 tal och lägger i Fifo. Time-slice 2: P2 producerar 3 tal men bara 2 av dem
kan läggas i Fifo. Det 3:e talet ligger kvar i en lokal variabel i P2. Time-slice 3: C3 konsumerar 4 tal, varav 1
från P2. Time-slice 4: C4 konsumerar 1 tal från P2 och väntar sedan på trådbyte under 3/4 time-slice.
Time-slice 5: P1 producerar 3 tal och lägger i Fifo. Time-slice 6: P2 lägger sitt sparade tal i Fifo och
producerar sedan 2 nya, varav 1 läggs i Fifo och det andra sparas i lokala variabeln. 1/3 av time-slice slösas
bort på att vänta på trådbyte. Time-slice 7 och 8 blir som time-slice 3 och 4.
Summering: 6 tal från P1 har konsumerats och 4 tal från P2.
b) Med yield i Wait så behöver ingen tråd slösa bort tid i väntan på trådbyte. Time-slice 4 förkortas med
75%, time-slice 6 förkortas med 33% och time-slice 8 förkortas med 75%. Övriga time-slices utnyttjas fullt.
För detaljerad motivering se lösningsförslag till föregående deluppgift.
c) Funktionen Signal kompileras till assemblerkod, där läsning, ökning och skrivning av semafor-variabeln
utgör separata instruktioner. Om Signal inte är atomär så kan ett trådbyte inträffa medan semafor-variabelns
aktuella värde finns i ett register i processorn. Skulle då en annan tråd uppdatera semafor-variabeln, så
kommer den uppdateringen att tappas bort när funktionen Signal fortsätter på det ställe den blev avbruten.
d.1) Sant. Alla processorregister som kan och får ändras av en tråd måste sparas. R8 är ett sånt register.
d.2) Falskt. Fifo-kön är en datastruktur som är gemensam för alla trådar. Den hör inte till någon enskild tråd
och ska alltså inte sparas tillsammans med någon tråd.
d.3) Sant. Varje tråd har egen stackpekare (och egen programräknare). Stackpekaren ska sparas tillsammans
med trådens övriga processorregister.
d.4) Falskt. Timer-kretsen hör till operativsystemet och inte till någon tråd.
e) Storleksordningen är mer än 50 instruktioner, men mindre än 500 instruktioner.
För att behandla det timer-avbrott som orsakar trådbytet behövs minst 10 instruktioner. För att spara och
återställa cirka 25 registerinnehåll behövs 25 store-instruktioner och 25 load-instruktioner. För att välja ut en
ny tråd behövs uppskattningsvis mer än 10 men troligen mindre än 100 instruktioner.
Summan blir större än 50 och mindre än 500 instruktioner.