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.
© Copyright 2024