Svar och lösningar till övningsuppgifter

Relevanta dokument
Polling (cyklisk avfrågning) Avbrott

Lösningar till tentamen i EIT070 Datorteknik

Datorsystemteknik Föreläsning 7DAVA14

Lösningar till tentamen i EIT070 Datorteknik

Lösningar till tentamen i EIT070 Datorteknik

Övningsuppgifter i Datorteknik, EIT070

Mål. Datorteknik. Introduktion. Innehåll. Verklig situation - pappaledighet. Introduktion (forts)

Institutionen för elektro- och informationsteknologi, LTH

Datorteknik. Föreläsning 5. Realtidssystem och realtidsprogrammering. Institutionen för elektro- och informationsteknologi, LTH.

Institutionen för elektro- och informationsteknologi, LTH

Stack och subrutiner Programmeringskonventionen

Datorteknik. Föreläsning 3. Assembler, stack och subrutiner, programmeringskonventionen. Institutionen för elektro- och informationsteknologi, LTH

Institutionen för elektro- och informationsteknologi, LTH

Tentamen i EIT070 Datorteknik

Mål. Datorteknik. Innehåll. Innehåll (forts) Hur ser ett program ut? Hur skapas maskinkoden?

Datorteknik. Föreläsning 4. Polling och avbrott. Institutionen för elektro- och informationsteknologi, LTH. Mål

Institutionen för elektro- och informationsteknologi, LTH

Datorteknik. Föreläsning 2. Programmering i C och assembler MIPS instruktionsarkitektur. Institutionen för elektro- och informationsteknologi, LTH

Fö 5+6 TSEA81. Real-time kernel + Real-time OS

Mål. Datorteknik. Innehåll. Vad händer med en add-instruktion? Vad händer med en add-instruktion. Instruktioner som bitmönster i minnet

Datorsystemteknik DVGA03 Föreläsning 5

Datorsystemteknik DVGA03 Föreläsning 8

c a OP b Digitalteknik och Datorarkitektur 5hp ALU Design Principle 1 - Simplicity favors regularity add $15, $8, $11

Datorsystemteknik DVG A03 Föreläsning 3

OBS!! Detta är DEL 2 av tentan. För att få ut denna måste du ha lämnat in del 1. Om du inte fått ut del 1 bör du meddela skrivningsvakten. OBS!!

CE_O3. Nios II. Inför lab nios2time

Datorteknik. Föreläsning 6. Processorns uppbyggnad, pipelining. Institutionen för elektro- och informationsteknologi, LTH. Mål

F5: Högnivåprogrammering

F5: Högnivåprogrammering

Programmera i C Varför programmera i C när det finns språk som Simula och Pascal??

Per Holm Lågnivåprogrammering 2014/15 24 / 177. int och double = = 2, 147, 483, 647

Mål. Datorteknik. Repetition av avbrott. Innehåll. Mätning och styrning. Datorer för mätning och styrning. timer. Datorsystem A/D. Analog insignal D/A

Datorarkitektur I. Tentamen Lördag 10 April Ekonomikum, B:154, klockan 09:00 14:00. Följande gäller: Skrivningstid: Fråga

Exempelsamling Assemblerprogrammering

Datorsystemteknik D. Lösningar till tentamen i kursen EDA330 14/1 2000

Digital- och datorteknik

Övningsuppgifter STYRNING - i Mikrodatorteknik för U2 2010

Extra lab. Nu på fredag kl 8-12 Frivillig Enbart hjälp med projektuppgiften Ingen examination

Lågnivåprogrammering. Föreläsning 2 Lågnivåprogrammering. Binära tal. En enkel modell av datorns inre

LEU240 Mikrodatorsystem

TENTAMEN. Datorteknik. D1/E1/Mek1/Ö Hjälpmedel: Häfte "ARM-instruktioner", A4-format, 17 sidor. Maxpoäng:

CPU. Carry/Borrow IX. Programräknare

I denna laboration undersöker vi hur aritmetiska beräkningar utförs. Vi tittar på olika variabeltyper: 8-bitars, 16-bitars, 32-bitars och flyttal.

F2: Motorola Arkitektur. Assembler vs. Maskinkod Exekvering av instruktioner i Instruktionsformat MOVE instruktionen

Extrauppgifter för CPU12

Tentamen. Datorteknik Y, TSEA28

DatorsystemteknikDAVA14 Föreläsning 9

TENTAMEN Datorteknik (DO2005) D1/E1/Mek1/Ö1

Programmering av inbyggda system. Kodningskonventioner. Viktor Kämpe

A-del motsvarande KS1

PC-teknik, 5 p LABORATION ASSEMBLERINTRODUKTION

Tentamen i EIT070 Datorteknik

F8: Undantagshantering

Digital- och datorteknik

Arduinokurs. Kurstillfälle 4

Ansvarig lärare: Olof Andersson, Telefon (besöker skrivsalen)

CE_O1. Nios II. Enkla assembler-instruktioner.

Johan Karlsson Datavetenskap för teknisk kemi, 10p, moment 1 Datavetenskap Umeå Universitet. Tentamen

Fortsä'ning Pekare. Ulf Assarsson. Originalslides av Viktor Kämpe

Dataminne I/O Stack 0x005D 0x3D SP low byte 0x005E 0x3E SP high byte

Lista på registeruppsättningen i PIC16F877A Datablad TTL-kretsar 74-serien

Kontrollskrivning Mikrodatorteknik CDT S2-704

Grundläggande C-programmering del 2 Pekare och Arrayer. Ulf Assarsson

Tentamen den 12 januari 2017 Datorarkitektur med operativsystem, EDT621

Övning1 Datorteknik, HH vt12 - Talsystem, logik, minne, instruktioner, assembler

Tentamen. Datorteknik Y, TSEA28

Laboration 2 i Datorteknik- Assemblerprogrammering II

EDA480/EDA485 - Maskinorienterad programmering, tentamen 2006-xx-xx 1(7)

Övningsuppgifterna i kapitel F avser FLIS-processorn, vars instruktioner och motsvarande koder definieras i INSTRUKTIONSLISTA FÖR FLISP.

Systemkonstruktion LABORATION REALTIDSPROGRAMMERING

Enkla datatyper minne

Elektroteknik MF1016 föreläsning 9 MF1017 föreläsning 7 Mikrodatorteknik

TDDC77 Objektorienterad Programmering

Övning från förra gången: readword

Besvara de elektroniska frågorna (se kurshemsidan). Läs kapitel i kursbok.

Grundläggande C-programmering del 2 Pekare och Arrayer. Ulf Assarsson

Ulf Assarsson. Grundläggande C-programmering del 2 Pekare och Arrayer. Läromoment:

Användarhandledning Version 1.2

TENTAMEN. Datorteknik. D1/E1/Mek1/Ö Hjälpmedel: Häfte" ARM-instruktioner", A4-format, 17 sidor. Maxpoäng: 60p

General Purpose registers ALU I T H S V N Z C SREG. Antag att vi behöver skriva in talet 25 till register R18

Övning 6. Parallellport, timer

Tentamen i Digitala system - EDI610 15hp varav denna tentamen 4,5hp

Institutionen för datavetenskap 2014/15

Föreläsning 6: Introduktion av listor

NUV 647E. Digitalteknik och Datorarkitektur 5hp. 3x12 = 36 2x12 = 24 1x12 = 12 0x12 = 18

Digitalteknik och Datorarkitektur

CE_O2. Nios II. Subrutiner med mera.

Föreläsning 3-4 Innehåll

Lathund. C för inbyggda system

Datatyper och kontrollstrukturer. Skansholm: Kapitel 2) De åtta primitiva typerna. Typ Innehåll Defaultvärde Storlek

Digital Aritmetik Unsigned Integers Signed Integers"

Datorteknik 2 (AVR 2)

Datorteknik. Tomas Nordström. Föreläsning 6. För utveckling av verksamhet, produkter och livskvalitet.

Tentamen. Datorteknik Y, TSEA28

Föreläsning 3-4 Innehåll. Diskutera. Metod. Programexempel med metod

6 Lågnivåprogrammering

Alla datorprogram har en sak gemensam; alla processerar indata för att producera något slags resultat, utdata.

Grundläggande datavetenskap, 4p

Tentamen i IS1500/IS1200/2G1518 Datorteknik fredagen den 19 augusti 2011 kl

Föreläsning 10. Pekare (Pointers)

Transkript:

Svar och lösningar till övningsuppgifter Lösningarna är gjorda av: Mats Brorsson Bengt Öhman Kapitel 2 1 Vad är ett register? Ett register är en lagringsplats för data inne i processorn som i de flesta processorarkitekturer rymmer ett ord. För MIPS-arkitekturen är det fyra byte, 32 bitar. Den största skillnaden mellan register och det som kallas minnet i en processor är att minnet innehåller både instruktioner och data medan register enbart kan innehålla data. Processorn kan inte hämta instruktioner från registren. Det går också mycket fortare att hämta data från ett register än från minnet tack vare att det är mindre, är närmare kopplat till övriga delar i processorn samt att det bara krävs ett fåtal bitar för att benämna ett register eftersom det finns så få av dem. 1 lsg.fm 21 June 2002 12.09:49 sida 1 av 38

2 Vad har programräknaren för funktion? Programräknaren är ett register i processorn som innehåller minnesadressen till nästa instruktion som ska exekveras. 3 Hur många bitar används för att koda en MIPS-instruktion? 32. 4 En given dator har 268435456 byte minne. Varför har konstruktören valt ett så udda antal minnesceller i stället för, till exempel 250000000? 268435456 är ett jämt tal, i alla fall i hexadecimal eller binär representation. Det är 10000000 16 i hexadecimal representation. Det är också en jämn potens av basen 2: 2 28. Detta innebär att med 28 bitledningar kan man adressera precis 268435456 olika minnesceller och därför är det ett naturligt val för konstruktören. 5 Visa hur man kan skriva nedanstående C-sats med en, eller ett fåtal, MIPSinstruktioner: a = b + 100; Anta att a finns i register $8 och b i register $9. 2 lsg.fm 21 June 2002 12.09:49 sida 2 av 38

Assemblerkoden för detta är: addi $8,$9,100 6 Lägg till kommentarer till koden nedan och beskriv i en mening vad den gör. Register $4 används för att föra in en parameter och register $2 för att föra ut resultatet. Inparametern i $4 är ett heltal som du kan benämna n idin beskrivning. start: addi $8,$0,0 addi $9,$0,1 loop: slt $10,$4,$9 bne $10,$0,finish nop add $8,$8,$9 addi $9,$9,2 j loop nop finish: add $2,$8,$0 Programmet räknar ut summan av alla udda tal mindre än eller lika med n. Här följer programkoden med kommentarer. Jag benämner i kommentarerna $8 med oddsum och$9medoddnum. addi $8,$0,0 # Sätt oddsum till 0 addi $9,$0,1 # Sätt oddnum till 1 loop: slt $10,$4,$9 # Om n < oddnum bne $10,$0,finish # Hoppa till finish nop add $8,$8,$9 # oddsum += oddnum addi $9,$9,2 # oddnum += 2 3 lsg.fm 21 June 2002 12.09:49 sida 3 av 38

j loop nop finish: add $2,$8,$0 # Goto loop # Returnera oddsum 7 Skriv följande C-sats med så få MIPS-instruktioner som möjligt: x = y + c; Anta att x finns på adress 4000000 10 4000004 10 och att c finns i register $8. i minnet, att y finns på adress Adressernas motsvarighet i hexadecimal representation är: 4000000 10 = 0x3d0900 och 4000004 10 = 0x3d0904. Här kommer C-satsen som MIPSinstruktioner: lui $10,0x003d # Lägg övre halvan av adr i $10 ori $10,$10,0x0900 # Lägg undre halvan av adr i $10 lw $9,0($10) # Läs variabel y från minnet nop # Load delay slot add $11,$9,$8 # Utför additionen sw $11,4($10) # Skriv variabel x till minnet Eftersom additionsinstruktionen inte kan ha någon operand i minnet måste först variabel y läsas in och variabel x skrivas efter additionen. Valet av register är i denna lösning ganska godtycklig. Vilka som helst av de tillgängliga registren utom register $0 som ju alltid är noll kunde ha använts. 8 Följande program försöker kopiera ord från en plats i minnet vars adress finns i register $4 till en annan plats vars adress finns i $5. Programmet ska 4 lsg.fm 21 June 2002 12.09:49 sida 4 av 38

sluta kopiera när den stöter på värdet 0, och då ska antalet kopierade ord finnas i register $2. Värdet 0 ska kopieras, men inte räknas. L: lw $3,0($4) addi $2,$2,1 sw $3,0($5) addi $4,$4,1 addi $5,$5,1 bne $3,$0,L nop # Läs nästa ord från källan # Räkna upp antalet kopierade ord # Skriv det kopierade ordet # Uppdatera pekaren till nästa ord # Detsamma för destinationen # Hoppa till L om läst ord # inte är noll. Det finns flera fel i det här programmet. Din uppgift är att skriva om programmet så att det fungerar. Ändringarna är markerade med fet stil. L: lw $3,0($4) addi $2,$2,1 sw $3,0($5) addi $4,$4,4 addi $5,$5,4 bne $3,$0,L nop addi $2,$2,-1 # Läs nästa ord från källan # Räkna upp antalet kopierade ord # Skriv det kopierade ordet # Uppdatera pekaren till nästa ord # Detsamma för destinationen # Hoppa till L om läst ord # inte är noll. # Justera för att nollan inte # ska räknas 9 Använd appendix D eller en arkitekturhandledning för MIPS för att beskriva instruktionsformatet för varje instruktion i uppgift (med felen intakta), och skriv den hexadecimala koden för programmet. L: lw $3,0($4) addi $2,$2,1 # 0x8c830000 # 0x20420001 5 lsg.fm 21 June 2002 12.09:49 sida 5 av 38

sw $3,0($5) addi $4,$4,1 addi $5,$5,1 bne $3,$0,L nop # 0xaca30000 # 0x20840001 # 0x20a50001 # 0x1460fffa # 0x00000000 Instruktionen bne ska hoppa sex steg bakåt vilket kodas med tvåkomplementrepresentation som 0xfffa med 16 bitar. 10 Beskriv skillnaderna mellan följande instruktioner: lb, lw, lh och lui. Instruktionerna: lb, lw och lh är instruktioner som läser data från minnet och lägger det i ett register. Instruktionen lui, load upper immediate, däremot, läser inte från minnet utan tar en konstant kodad i instruktionen och lägger den som de 16 mest signifikanta bitarna. De 16 minst signifikanta bitarna sätts till noll. Instruktionerna: lb, lw och lh skiljer sig åt i storleken på det dataelement som hämtas från minnet. Instruktionen lb hämtar en byte 8 bitar, lw ett ord 32 bitar och lh ett halvord 16 bitar. 11 Instruktionen j L1 är en ovillkorlig hoppinstruktion som hoppar till adressen som specificeras av programläget L1. Beskriv hur du kan göra samma sak med instruktionen beq. Kan alla förekomster av j X,därX är en godtycklig adress, ersättas med en beq? Instruktionen j L1 kan i många fall realiseras med beq $0,$0,L1. Villkoret för detta är att det inte får finnas fler än ca 32000 instruktioner mellan beq-instruktionen och programläget L1. Adressen L1 kodas som en dif- 6 lsg.fm 21 June 2002 12.09:49 sida 6 av 38

ferens med 16 bitar i beq-instruktionen. Med 16 bitar kan man representera ett talområde mellan -32768 och 32767. Kapitel 3 1 Omvandla följande decimala tal till 32-bitars binära tal i tvåkomplementrepresentation: 512, -1023, -4000000. 512 10 = 00000000000000000000001000000000 2 = 0x00000200-1023 10 = 11111111111111111111110000000001 2 = 0xfffffc01-4000000 10 = 11111111110000101111011100000000 2 = 0xffc2f700 2 Vilka decimala tal motsvaras av följande binära tal i tvåkomplementrepresentation? 1111 1111 1111 1111 1111 1110 0000 1100 1111 1111 1111 1111 1111 1111 1111 1111 0111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1110 0000 1100 2 =-500 1111 1111 1111 1111 1111 1111 1111 1111 2 =-1 0111 1111 1111 1111 1111 1111 1111 1111 2 = 2147483647 7 lsg.fm 21 June 2002 12.09:49 sida 7 av 38

3 Vilket binära tal representeras av följande hexadecimala tal? 0x7ffffffa Vad är den decimala motsvarigheten? Det binära talet är: 0111 1111 1111 1111 1111 1111 1111 1010 Dess decimala motsvarighet är: 2147483642 4 En bitsekvens för sig själv betyder inget. Man måste ge den ett sammanhang för att tolka den korrekt. Avgör med hjälp av detta kapitel, kapitel 2 och appendix D det antal sätt som följande bitsträng kan tolkas på och beskriv dessa tolkningar: 1000 1111 1110 1111 1100 0000 0000 0000 Som instruktion: lw $15,0xc000($31) Som heltal (tvåkomplementrepresentation): -1880113152 Som positivt heltal: 2414854144 Som reellt tal i enkel precision: -2,36412 5 Anta att du har en processor med ordlängden 1 byte. Utför följande beräkningar: 00101101 + 01101111 11111111 + 11111111 00000000-11111111 11110111-11110111 8 lsg.fm 21 June 2002 12.09:49 sida 8 av 38

Blir svaret rätt eller fel? Spelar det någon roll om talen tolkas som positiva heltal eller i tvåkomplementrepresentation? Här är den första additionen. Minnessiffran skrivs över den översta linjen: 11 1111 00101101 + 01101111 10011100 De ingående talen är 45 och 111 vars summa är 156. Svaret stämmer om de talen tolkas som positiva heltal men inte om de ska vara både positiva och negativa tal i tvåkomplementrepresentation. I tvåkomplementrepresentation betyder svaret -100 vilket är uppenbarligt fel. Här är den andra additionen. 1111111 11111111 + 11111111 11111110 De ingående talen är båda 255 eller -1 om de ska tolkas som tvåkomplementtal. Svaret är 253 eller -2 om det tolkas som tvåkomplementtal. Här är det bara om man tolkar de ingående talen enligt tvåkomplementrepresentationen som resultatet är rätt. Den tredje och fjärde aritemetiska beräkningarna är subtraktioner som omvandlas till addition genom att negera den andra operanden. 00000000-11111111 00000000 + 00000001 00000001 Vi får alltså att 00000000-11111111 = 00000001. Om vi tolkar talen som enbart positiva tal är detta 0-255 = 1 vilket naturligtvis är helt fel. Om vi istället tolkar dem som tal i tvåkomplementrepresentation har vi: 0 - (-1) = 1 vilket är rätt. Den sista beräkningen är: 11110111-11110111 = 00000000. De ingående talen är 247 om de tolkas som positiva heltal eller -9 om de ska tolkas som tal i tvåkomplementrepresentation. I båda fallen är resultatet korrekt. 9 lsg.fm 21 June 2002 12.09:49 sida 9 av 38

6 Verifiera att algoritmen för multiplikation i figur 3.8 fungerar genom att skriva ett program som testar den. Här följer ett komplett program som testar multiplikationsrutinen: #include <stdio.h> int mult(int A, int B){ int P, bit, i, n; n = 32; P = 0; bit = 1; for (i = 0; i < (n-1); i++){ if (B & bit){ P = P + A; A = A << 1; bit = bit << 1; if (B & bit) P = P - A; return P; main(){ int a, b, p; a = 3; b = 43; p = mult(a,b); printf("%d * %d = %d (%d)\n", a, b, p, a*b); 10 lsg.fm 21 June 2002 12.09:49 sida 10 av 38

7 Verifiera att algoritmen för division i figur 3.10 fungerar genom att skriva ett program som testar den. Här är ett komplett program som testar divisionsalgoritmen. Observera att eftersom täljaren förväntas vara 2n bitar stor sätter vi här n till 16. #include <stdio.h> int div(int A, int B, int *r){ int Q, R, i, n = 16; int D; R = A; D = B; Q = 0; D = D << n; for (i = 0; i < n; i++){ D = D >> 1; R = R - D; if (R < 0) { R = R + D, Q = Q << 1; else { Q = Q << 1; Q = Q + 1; *r = R; return Q; main(){ int a, b; int q, r; a = 3; b = 43; 11 lsg.fm 21 June 2002 12.09:49 sida 11 av 38

q = div(a, b, &r); printf("%d / %d = %d (%d), %d %% %d = %d (%d)\n", a, b, q, a/b, a, b, r, a%b); q = div(b, a, &r); printf("%d / %d = %d (%d), %d %% %d = %d (%d)\n", b, a, q, b/a, b, a, r, b%a); 8 Omvandla följande tal till flyttal i enkel precision: 9, 5/32, -5/32, 6,125 Flyttalet som motsvarar 9 är: 0x41100000 Flyttalet som motsvarar 5/32 är: 0x3e200000 Flyttalet som motsvarar -5/32 är: 0xbe200000 Flyttalet som motsvarar 6,125: 0x40c40000 9 Omvandla följande hexadecimala tal till dess decimala motsvarighet, om vi tolkar dem som flyttal i enkel precision: 0x42e48000, 0x3f880000, 0x00800000, 0xc7f00000 Flyttalet 0x42e48000 motsvarar: 114,25 Flyttalet 0x3f880000 motsvarar: 1,0625 12 lsg.fm 21 June 2002 12.09:49 sida 12 av 38

Flyttalet 0x00800000 motsvarar: 1,17549 10-38 Flyttalet 0xc7f00000 motsvarar: -122880 10 Skriv en C-funktion som kan omvandla ett binärt tal, givet som ett flyttal i enkel precision till dess decimala motsvarighet. Använd följande programskelett: float omvandla(unsigned int tal){ /* tal är givet i IEEE 754 representation */ Tips: använd följande makron som kan definieras i C: #define tecken(x) ((0x80000000 & x) >> 31) #define exponent(x) ((0x7f800000 & x) >> 19) #define signifikand(x) (0x007fffff & x) tecken(x) skiljer ut teckenbiten i x och skiftar den 31 bitar till höger så att den kommer i minst signifikant position. exponent(x) skiljer ut exponenten i x och skiftar den 19 bitar åt höger. signifikand(x) skiljer ut signifikanden i x. Eftersom signifikanden redan tar upp den minst signifikanta positionen i x behöver man inte skifta den. Inte färdigt ännu. 13 lsg.fm 21 June 2002 12.09:49 sida 13 av 38

Kapitel 4 1 Det finns ett antal syntetiska instruktioner definierade för MIPS vilka underlättar för assemblerprogrammeraren. Titta på följande instruktioner och avgör om det är en syntetisk instruktion eller en vanlig och om det är en syntetisk ska du skriva den eller de MIPS-instruktioner som den syntetiska instruktionen översätts till. Det enda register du får använda förutom de som står i instruktionen är at. Alla konstanter är i decimal notation. move t5,t3 # t5 = t3 clear t5 # t5 = 0 li t5,4567 # t5 = 4567 li t5,67000 # t5 = 67000 lw t5,65000(s0) # t5 = M[s0+65000] addi t5,t3,100000 # t5 = t3 + 100000 Samtliga instruktioner ovan är syntetiska. De första tre instruktionerna översätts till endast en annan instruktion (observera att vi även måste få använda register zero, annars går inte uppgiften att lösa): add t5,zero,t3 # t5 = t3 add t5,zero,zero # t5 = 0 addi t5,zero,4567 # t5 = 4567 De tre följande syntetiska instruktionerna kräver att vi använder ett temporärregister, eftersom de använder sifferkonstanter som är utanför intervallet 32768 till 32767 (det som går att få in i en sextonbitars konstant). lui at,1 # at = 65536 addi t5,at,1464 # t5 = 67000 lui at,1 # at = 65536 add at,at,s0 # at = 65536 + s0 lw t5,-536(at) # t5 = M[s0+65000] 14 lsg.fm 21 June 2002 12.09:49 sida 14 av 38

lui at,1 # at = 65536 ori at,at,34464 # at = 100000 add t5,t3,at # t5 = t3 + 100000 En alternativ lösning på den sista instruktionen är följande (Observera att även instruktionen addiu tolkar konstanten som ett sextonbitars heltal med tecken!): lui at,2 # at = 131072 addiu at,at,-31072 # at = 100000 add t5,t3,at # t5 = t3 + 100000 2 Skriv följande C-satser i MIPS-assembler: while (save[i] == k) i = i + j; Använd konstruktionen för while-slingor som presenterats i det här kapitlet. Anta att adressen till save finns i register a0. De övriga variablerna finns i register t0, t1 och t2. Antag att variabeln i ligger i register t0, att j ligger i register t1 samt att k ligger i register t2. Antag vidare att save är en vektor som innehåller 32- bitars heltal. Om innehållet består av exempelvis en teckensträng så blir lösningen lite annorlunda. I lösningen antar vi att direktivet.set reorder har givits, för att slippa sätta ut nop-instruktioner. L0: sll t3, t0, 2 # Lägg 4*i i t3 add t4, a0, t3 # t4 = adressen till save[i] lw t5, 0(t4) bne t2, t5, L1 # Jämför save[i] och k add t0, t0, t1 # i = i + j 15 lsg.fm 21 June 2002 12.09:49 sida 15 av 38

b L1:... L0 3 Skriv följande C-satser i MIPS-assembler: for (i = 0; i <= 100; i = i+1) a[i] = b[i] + c; Variabel i finns i register t0. Övriga variabler finns i minnet på adresserna A, B respektive C. Hur många instruktioner kommer att exekveras för dessa satser? Hur många datareferenser görs? Inte färdigt ännu. 4 Skriv en funktion i MIPS-assembler, bfind, som letar upp den första förekomsten av bokstaven b i en teckensträng (ASCII) som avslutas med tecknet NUL. Adressen till teckensträngen skickas som parameter i register a0 till funktionen och adressen till första förekomsten av bokstaven b ska returneras i register v0. Om det inte fins något b i strängen ska funktionen returnera 0. Vi antar även här att direktivet.set reorder har givits:.globl bfind.ent bfind # Deklarera bfind som funktion bfind: move v0, a0 addi t0, zero, 0x62 # Lägg 'b' i t0 L: 16 lsg.fm 21 June 2002 12.09:49 sida 16 av 38

lbu a0, 0(v0) # Ladda ett tecken beq a0, t0, L2 # Testa om a0 innehåller 'b' addi v0, v0, 1 # Öka till nästa index bne a0, zero, L # Testa om strängen är slut add v0, zero, zero # Nollställ v0 #(inget b hittades) L2: jr ra # Hoppa tillbaka.end bfind 5 Skriv en funktion i MIPS-assembler som beräknar Fibonacci-tal fib(n).i C kan man göra den här beräkningen rekursivt med följande funktion: int fib(int n) { if (n == 0) return 0; else if (n == 1) return 1; else return fib(n-1) + fib(n-2); Inte färdigt ännu. Kapitel 5 1 Anta att du har åtta in-enheter kopplade till en dator på inportar med adress 0xafc00i0 där i är numret på in-enheten. Ett giltigt värde på inporten signaleras med värdet 1 på bit i på en statusport som finns på adress 0xafc0100. 17 lsg.fm 21 June 2002 12.09:49 sida 17 av 38

Skriv en funktion i C eller MIPS-assembler som använder cyklisk avfrågning (polling) och som returnerar först när en av in-enheterna har data på inporten. Funktionen ska returnera värdet på den första inporten som har giltig data. Här presenteras en funktion, getvalue, först i MIPS-assembler och sedan i C. I assemblerrutinen antar vi som vanligt att vi har överlåtit på assemblern att själv lägga in nop-instruktioner där det behövs. Vidare antar vi att portarna är på en byte, åtta bitar, vardera..globl getvalue.ent getvalue getvalue: lui t0,0xafc addi t0,t0,0x100 L1: lbu t1,0(t0) beq t1,zero,l1 lui t2,0xafc addi t2,t2,0x10 L2: andi t3,t1,1 bne t3,zero,l3 srl t1,t1,1 addi t2,t2,0x10 b L2 L3: lbu v0,0(t2) jr ra.end getvalue #ladda adressen för statusport #läs från statusport #upprepa om ingen giltig data #t2 = adressen för den första # inporten #undersök vilken port som har # giltig indata #öka adressen med ett steg #lagra resultatet i v0 Här presenteras en version i C, som använder samma metod som ovan för att hämta värdet: 18 unsigned char getvalue() { /* status = pekare till statusporten */ unsigned char *status = 0xafc0100; /* inport = pekare på dataportarna */ unsigned char *inport = 0xafc0010; unsigned char temp; lsg.fm 21 June 2002 12.09:49 sida 18 av 38

/* vänta på data */ temp = *status; while (temp == 0) { temp = *status; /* hitta rätt inport */ while (temp & 0x01 == 0) { temp = temp >> 1; /* kolla nästa bit */ inport = inport + 0x10; /* nästa inport */ return *inport; /* returnera värdet på inporten */ 2 Anta att ett program spenderar den mesta tiden att exekvera utan att externa avbrott är tillåtna. Då och då tillåts avbrotten igen genom en rutin som sätter rätt bit i statusregistret men de stängs av lika snabbt igen, så här: enable_interrupts(); disable_interrupts(); Det kan tyckas meningslöst att bara tillåta avbrott under en så kort tidsperiod som mellan dessa anrop. Förklara varför det inte är så? Avbrotten ligger kvar ( interrupt pending ) tills de har kvitterats. På grund av detta kommer alla avbrott att inträffa så fort interrupten sätts på (dvs oavsett hur liten luckan är då avbrotten är på). Inga avbrott förloras om det inte inträffar mer än ett avbrott av varje typ mellan de gånger som avbrotten är på. Denna tekniken fungerar bra om avbrotten sätts på tillräckligt ofta samt att inte den ökade latensen på grund av de försenade avbrotten har betydelse för resultatet. 19 lsg.fm 21 June 2002 12.09:49 sida 19 av 38

3 Du ska utöka funktionaliteten hos klockprogrammet som finns beskrivet på sidorna 166-173. Antag att det finns en knapp kopplad till avbrottsingång 5. När denna knapp trycks ned ska en rutin anropas som nollställer klockans alla variabler (tick, hun, sek, min, tim) till noll. a) Skriv den rutin som nollställer klockans variabler. Denna rutin kan exempelvis se ut så här:.globl resetclk.ent resetclk resetclk: sw zero,tim # Syntetiska instruktioner sw zero,min # (använder at - registret) sw zero,sek sw zero,hun sw zero,tick jr ra.end resetclk b) Ändra den existerande avbrottsrutinen så att rätt rutin anropas när avbrott 5 kommer. Vi definierar först en mask för interrupt 5, så här: #define EXT_INT5 0x8000 /* mask för int5 i Cause */ Därefter ändrar vi lite i koden för avbrottsrutinen. Byt ut allt mellan labeln testa_int4 och internal_int med följande: testa_int4: andi t0,k0,ext_int4 beq t0,zero,testa_int5 # det var int4, anropa utskriftsrutinen 20 lsg.fm 21 June 2002 12.09:49 sida 20 av 38

jal printtime # vi är klara, hoppa till återställningskoden b restore testa_int5: andi t0,k0,ext_int5 beq t0,zero,internal_int # det var int 5, nollställ timervariablerna jal resetclk b restore internal_int: c) Ändra i huvudprogrammet så att avbrott 5 initieras korrekt. Det enda som behöver ändras är raden där vi sätter masken för vilka avbrott som ska tillåtas. Byt därför raden ori t0,t0,ext_int3 EXT_INT4 mot raden ori t0,t0,ext_int3 EXT_INT4 EXT_INT5 så tillåts även avbrott 5. 4 Avbrottsrutinen för klockan i uppgift 3 har den stora nackdelen att avbrotten är avstängda för en relativt lång tidsperiod. Skriv om avbrottsrutinen så att den i sin tur kan avbrytas av ett avbrott med högre prioritet. Avbrotten ska vara avstängda så kort tid som möjligt. 21 lsg.fm 21 June 2002 12.09:49 sida 21 av 38

När ett avbrott inträffar, så sätts IEc-biten i statusregistret till noll. För att tillåta avbrott igen så måste man sätta denna biten. Om vi inte vill att vårt eget avbrott ska kunna inträffa igen innan vi är klara, så måste detta avbrottet stängas av innan vi tillåter avbrotten igen. Detta sker genom att man manipulerar bitarna i statusregistret. Eftersom vi nu tillåter andra avbrott under tiden som vi är i avbrottshanteraren, så kan vi inte använda registren k0 och k1 lika fritt som förut de kan ju bli överskrivna när vi får ett annat avbrott. En annan sak som är viktig att hålla i minnet är att externa avbrott kommer att ligga kvar ända tills de är kvitterade. Det betyder att om vi glömmer kvittera ett avbrott så kommer vi omedelbart att få ett nytt så snart avbrotten tillåts igen. I denna lösningen antar vi att avbrotten kvitteras genom att skriva till en dataport, nämligen minnesadress 0xbfa90000 för int3, 0xbfa90010 för int4, samt 0xbfa90020 för int5. Hur avbrotten kvitteras beror väldigt mycket på hur hårdvaran är konstruerad, så metoden man får använda kan skilja ganska mycket mellan olika datasystem. Här kommer en lösning som tar dessa saker i beaktning (endast rutinen int_routine har ändrats):.globl int_routine.ent int_routine int_routine: subu sp,sp,17*4 sw ra,12*4(sp) sw v0,11*4(sp) sw v1,10*4(sp) sw t0,9*4(sp) sw t1,8*4(sp) sw t2,7*4(sp) sw t3,6*4(sp) sw t4,5*4(sp) sw t5,4*4(sp) sw t6,3*4(sp) sw t7,2*4(sp) sw t8,1*4(sp) sw t9,0*4(sp) mfc0 k0,c0_sr mfc0 k1,c0_epc sw k0,13*4(sp) # Hämta statusregistret # och EPC, och spara dessa 22 lsg.fm 21 June 2002 12.09:49 sida 22 av 38

sw k1,14*4(sp).set noat sw AT,15*4(sp).set at # at måste också sparas # Kontrollera vilket avbrott det var mfc0 k0,c0_cause andi t0,k0,excmask bne t0,zero,internal_int # Ta bort bitar för avbrott som är maskade mfc0 k1,c0_sr andi k1,0xff00 # IP- och SW-bitarna and k0,k0,k1 # Behåll bara påslagna avbrott andi t0,k0,ext_int3 beq t0,zero,testa_int4 # Det var int3, stäng av int3 och tillåt andra mfc0 t1,c0_sr andi t1,t1,~ext_int3 # Maska bort int3 mtc0 t1,c0_sr nop # Ge masken tid att aktiveras ori t1,t1,1 # Sätt IEc = 1 mtc0 t1,c0_sr # Aktivera avbrott # Från och med nu kan vi inte använda k0 och k1, # eftersom de kan användas av en annan avbrotts- # rutin. jal timer # Anropa timerrutin # Vi är färdiga med int3, kvittera detta och # hoppa till återställningskoden lui t0,0xbfa9 sw zero,0(t0) # Kvittera int3 (se texten ovan) b restore testa_int4: andi t0,k0,ext_int4 beq t0,zero,testa_int5 # Det var int4, stäng av int4 och tillåt andra mfc0 t1,c0_sr 23 lsg.fm 21 June 2002 12.09:49 sida 23 av 38

andi t1,t1,~ext_int4 mtc0 t1,c0_sr nop # Denna operation tar lite tid ori t1,t1,1 # Sätt IEc = 1 mtc0 t1,c0_sr # Aktivera avbrott jal printtime # Skriv ut tiden # Kvittera int4 lui t0,0xbfa9 sw zero,0x10(t0) # (se texten ovan) b restore testa_int5: andi t0,k0,ext_int5 beq t0,zero,spurious_int # Det var int5 mfc0 t1,c0_sr andi t1,t1,~ext_int5 mtc0 t1,c0_sr nop # Extra tid, enligt ovan ori t1,t1,1 mtc0 t1,c0_sr # Aktivera avbrott jal resetclk # Kvittera int5 lui t0,0xbfa9 sw zero,0x20(t0) b restore spurious_int: # Hit kommer vi om vi fick ett hårdvaruavbrott # som vi inte känner till. Försök med att bara # hoppa tillbaka till programmet. b restore internal_int: # Hit kommer vi om vi fick ett mjukvaruavbrott # (exempelvis arithmetic overflow). # Just nu görs inte så mycket här... b restore 24 lsg.fm 21 June 2002 12.09:49 sida 24 av 38

restore: # Återställningskod. För att kunna återställa # alla register så måste vi slå av avbrotten # igen (genom att återställa det gamla värdet # i statusregistret) lw t0,13*4(sp) mtc0 t0,c0_sr # Nu är avbrotten avstängda igen. # Återställ register och hoppa tillbaka. lw ra,12*4(sp) lw v0,11*4(sp) lw v1,10*4(sp) lw t0,9*4(sp) lw t1,8*4(sp) lw t2,7*4(sp) lw t3,6*4(sp) lw t4,5*4(sp) lw t5,4*4(sp) lw t6,3*4(sp) lw t7,2*4(sp) lw t8,1*4(sp) lw t9,0*4(sp).set noat lw AT,15*4(sp).set at lw k0,14*4(sp) # Hämta sparad EPC addiu sp,sp,17*4 # Återställ sp.set noreorder jr k0 rfe.set reorder.end int_routine 5 Instruktionen add genererar ett avbrott om resultatet inte kan representeras i ett 32-bitars tal med tvåkomplementrepresentation. Skriv en avbrottsrutin som tar hand om ett sådant avbrott och skriver ut följande: Aritmetiskt fel: x + y 25 lsg.fm 21 June 2002 12.09:49 sida 25 av 38

där x och y är argumenten till additionen. Prova avbrottsrutinen genom att skriva ett huvudprogram som medvetet genererar ett fel genom att addera två tal som är för stora. Provkör det på en MIPS-baserad dator. Här presenteras en avbrottsrutin, interrupt_routine, skriven i MIPS-assembler som utför ovanstående. Eftersom exekveringen alltid hoppar till adress 0x80000080 vid avbrott, så används även en liten rutin, så kallad stub, som läggs på den adressen. Det enda som utförs i stubrutinen är ett hopp till den riktiga avbrottsrutinen. I huvudprogrammet (kallat main) initialiseras avbrottsrutinen och samtliga avbrott slås på. Om vi får ett aritmetiskt fel så kommer programmet att obönhörligen avslutas (genom ett hopp till _exit), eftersom det är svårt att starta om programmet efter ett dylikt. Tilläggas kan att integeroperationer i C aldrig kommer att generera några avbrott, därför används inte några C-rutiner i denna lösningen. # Lägg några textsträngar i data-arean.data msg:.asciiz "Aritmetiskt fel: instruktion: %s operander: %d, %d\n" s_addi:.asciiz "addi" s_add:.asciiz "add" s_sub:.asciiz "sub" s_mult:.asciiz "mult" s_div:.asciiz "div" s_unknown:.asciiz "(okänd)" 26 # Här börjar programkoden.text.globl interrupt_routine.ent interrupt_routine interrupt_routine: subu sp,sp,32*4 #skapa plats på stacken.set noat #vi vill kunna använda at här sw $0,0*4(sp) #spara undan 31 register sw $1,1*4(sp) sw $2,2*4(sp) lsg.fm 21 June 2002 12.09:49 sida 26 av 38

sw $3,3*4(sp) sw $4,4*4(sp) sw $5,5*4(sp) sw $6,6*4(sp) sw $7,7*4(sp) sw $8,8*4(sp) sw $9,9*4(sp) sw $10,10*4(sp) sw $11,11*4(sp) sw $12,12*4(sp) sw $13,13*4(sp) sw $14,14*4(sp) sw $15,15*4(sp) sw $16,16*4(sp) sw $17,17*4(sp) sw $18,18*4(sp) sw $19,19*4(sp) sw $20,20*4(sp) sw $21,21*4(sp) sw $22,22*4(sp) sw $23,23*4(sp) sw $24,24*4(sp) sw $25,25*4(sp) sw $26,26*4(sp) sw $27,27*4(sp) sw $28,28*4(sp) addiu t1,sp,32*4 sw t1,29*4(sp) sw $30,30*4(sp) sw $31,31*4(sp).set at mfc0 k0,c0_cause andi t0,k0,0x3c addi t1,zero,12*4 bne t0,t1,intret #sp ($29) är speciell. Spara #originalvärdet på stacken. #tillåt användning av at igen #läs Cause-registret #skilj ut bit 2-5 (ExcCode) #vi är intresserade av # ExcCode = 12 (arit. fel) # Nu vet vi att avbrottet orsakades av ett # aritmetiskt fel. Hämta instruktionen som # orsakade avbrottet och analysera den. mfc0 k1,c0_epc lw t0,0(k1) #hämta instruktionen 27 lsg.fm 21 June 2002 12.09:49 sida 27 av 38

# Testa vilken typ av instruktion det var srl t1,t0,26 #skilj ut op-fältet addi t2,zero,0x08 #op=8 betyder addi bne t1,t2,l1 # Det var en addi-instruktion la a0,msg la a1,s_addi srl t1,t0,21 andi t1,t1,0x1f #t1 = rs-fältet sll t1,t1,2 add t1,sp,t1 lw a2,0(t1) #ladda innehållet i rs till a2 lh a3,2(k1) #ladda immediate-konstanten jal printf.set noreorder j _exit #avbryt programmet rfe.set reorder L1: bne t1,zero,intret #hoppa om det inte är en R-typ la a0,msg la a1,s_mult andi t1,t0,0x2f #skilj ut funct-fältet addi t2,zero,24 #funct=24 betyder mult beq t1,t2,l2 la a1,s_div addi t2,zero,26 #funct=26 betyder div beq t1,t2,l2 la a1,s_add addi t2,zero,32 #funct=32 betyder add beq t1,t2,l2 la a1,s_sub addi t2,zero,34 #funct=34 betyder sub beq t1,t2,l2 la a1,s_unknown #okänd instruktion L2: srl t1,t0,21 andi t1,t1,0x1f #t1 = rs-fältet sll t1,t1,2 add t1,sp,t1 lw a2,0(t1) #ladda innehållet i rs till a2 srl t1,t0,16 andi t1,t1,0x1f #t1 = rt-fältet 28 lsg.fm 21 June 2002 12.09:49 sida 28 av 38

sll t1,t1,2 add t1,sp,t1 lw a3,0(t1) jal printf.set noreorder j _exit rfe.set reorder intret: # Återställ de flesta registren.set noat lw $1,1*4(sp) lw $2,2*4(sp) lw $3,3*4(sp) lw $4,4*4(sp) lw $5,5*4(sp) lw $6,6*4(sp) lw $7,7*4(sp) lw $8,8*4(sp) lw $9,9*4(sp) lw $10,10*4(sp) lw $11,11*4(sp) lw $12,12*4(sp) lw $13,13*4(sp) lw $14,14*4(sp) lw $15,15*4(sp) lw $16,16*4(sp) lw $17,17*4(sp) lw $18,18*4(sp) lw $19,19*4(sp) lw $20,20*4(sp) lw $21,21*4(sp) lw $22,22*4(sp) lw $23,23*4(sp) lw $24,24*4(sp) lw $25,25*4(sp) lw $28,28*4(sp) lw $30,30*4(sp) lw $31,31*4(sp) addiu sp,sp,4*32.set at.set noreorder mfc0 k1,c0_epc #ladda innehållet i rt till a3 #skriv ut #avbryt programmet #hoppa över k0, k1 och sp #krävs för återhoppet 29 lsg.fm 21 June 2002 12.09:49 sida 29 av 38

nop jr k1 rfe #rfe i hoppluckan.set reorder.end interrupt_routine.globl main.ent main main: # Kopiera intstub till rätt ställe i minnet la s0,intstub lui s1,0x8000 addiu s1,s1,0x80 lw t0,0(s0) lw t1,4(s0) sw t0,0(s1) sw t1,4(s1) # Tillåt alla avbrott mfc0 s0,c0_sr ori s0,s0,1 #tillåt användardef. avbrott ori s0,s0,0xff00 #tillåt samtliga avbrott mtc0 s0,c0_sr # Nu är avbrotten aktiverade, prova en addition: lui t0,0x8000 addiu t0,t0,-1 #sätt t0 = maxint addiu t1,zero,20 #sätt t1 = 20 addu t2,t0,t1 #inget avbrott vid addu add t2,t0,t1 #avbrott: maxint+20 > maxint j _exit.end main #Avbryt programmet 6 Prova realtidskärnan ITS genom att skriva ett program som skapar tre trådar som turas om att skriva ut följande teckensträng: Jag är tråd X där X är identiteten på tråden i fråga. 30 lsg.fm 21 June 2002 12.09:49 sida 30 av 38

Här presenteras ett litet program i C som utför ovanstående. Varje tråd får sin egen textsträng som parameter när den startas. #include <stdio.h> #include "its.h" char *msg1 = "Jag är tråd 1"; char *msg2 = "Jag är tråd 2"; char *msg3 = "Jag är tråd 3"; int stack1[1024], stack2[1024], stack3[1024]; void func(void *data) { while (1) { printf("%s\n", (char *) data); ITS_yield(); /* lämna över till nästa tråd */ int main() { ITS_init(); ITS_create_thread(func, msg1, &stack1[1023]); ITS_create_thread(func, msg2, &stack2[1023]); ITS_create_thread(func, msg3, &stack3[1023]); while (1) { ITS_yield(); 7 Gör en tabell som visar vilken tråd av T1, T2 och T3 som kör och vilka som är blockerade över tiden 0-1000 ms. Alla tre trådar utför wait och signal på samma semafor (semaforer är beskrivna på sidan 191). Om två trådar kan göras körande samtidigt är det den med lägst nummer som får köra, för övrigt har alla trådar samma prioritet. Alla tre trådar kör från bör- 31 lsg.fm 21 June 2002 12.09:49 sida 31 av 38

jan och startvärdet på semaforen är 1. Trådarna gör följande operationer under körningen: Tid (ms) Händelse 100 T1 gör wait 200 T1 gör wait 300 T2 gör signal 400 T3 gör wait 500 T1 gör wait 600 T2 gör signal 700 T2 gör wait 800 T1 gör signal 900 T1 gör signal Under de olika tidsintervallen kommer trådarna vara i följande tillstånd: Tid (ms) T1 T2 T3 0-100 Kör Kör Kör 100-200 Kör Kör Kör 200-300 Blockerad Kör Kör 300-400 Kör Kör Kör 400-500 Kör Kör Blockerad 500-600 Blockerad Kör Blockerad 600-700 Kör Kör Blockerad 700-800 Kör Blockerad Blockerad 800-900 Kör Kör Blockerad 900-1000 Kör Kör Kör 8 Utöka funktionaliteten hos den enkla versionen av ITS enligt avsnitt 5.6 så att trådar kan ha prioritet och att en tråd får köra bara om det inte finns 32 lsg.fm 21 June 2002 12.09:49 sida 32 av 38

någon tråd med högre prioritet körklar. Trådar på samma prioritetsnivå turas om att köra på processorn. Tips: användenvektoravpekareochmedettelementförvarjeprioritetsnivå. Varje element pekar på en länkad lista med trådar på den prioritetsnivån. Använd en heltalsvariabel för att tala om vilken prioritetsnivå som är högst för tillfället så slipper man söka igenom vektorn. Här presenteras ett sätt att lösa problemet. I TCB-strukturerna läggs två extra variabler: runnable, som talar om ifall tråden kan köra eller är blockerad, samt next, som är en pekare till nästa TCB-struktur med samma prioritet (eller värdet NULL om listan är slut). Strukturen ser nu ut så här (ändringar är markerade med fet stil): typedef struct TCB_ { uint registers[nregs]; /* area for registers */ int id; /* Thread ID */ int runnable; /* runnable or blocked? */ struct TCB_ *next; /* link to next TCB */ TCB; Dessutom behövs några fler globala variabler, dels en vektor med 32 pekare till TCB-strukturer (en för varje prioritet), och dels en variabel som håller reda på prioriteten för den körande tråden. Blocket med globala variabler ser nu ut så här: TCB *threads[32]; TCB threaddata[max_threads]; uint thread_count; uint current_thread; int current_prio; Notera att variabeln threads nu är en vektor med pekare. Vektorn som innehåller själva TCB-strukturerna har blivit omnamnad till threaddata. Nu finns de datastrukturer som behövs. Det första vi får se till är att de blir initialiserade i funktionen ITS_init(). Den nya versionen av denna funktionen ser ut så här: 33 lsg.fm 21 June 2002 12.09:49 sida 33 av 38

void ITS_init() { int i; for (i = 0; i < 32; i++) { threads[i] = NULL; thread_count = 1; current_thread = 0; current_prio = 0; threaddata[0].runnable = 1; threaddata[0].next = NULL; threads[0] = &threaddata[0]; /* Set k0 to point to the current thread TCB */ set_k0((uint) &(threaddata[0])); threaddata[0].id = 0; /* Initialises the interrupt handler */ ITS_init_handler(); init_ext_int(); enable_int(ext_int3 SW_INT0); Nu måste vi också se till så att strukturerna hanteras korrekt när vi lägger till en ny tråd. För det första så måste vi lägga till en ny parameter till funktionen ITS_create_thread() så att vi vet vilken prioritet den nya tråden ska ha. Sedan måste vi se till att den nya trådens TCB hamnar i rätt lista, med mera. Den nya versionen av funktionen ser nu ut så här (endast den första halvan är med, andra halvan är likadan som innan): uint ITS_create_thread( void (*entry)(void *), void *arg, uint *stack_top, int prio) { TCB *new_tcb; disable_int(ext_int3 SW_INT0); /* Have we got space for another thread? */ if (thread_count >= MAX_THREADS) 34 lsg.fm 21 June 2002 12.09:49 sida 34 av 38

return 0; if (prio < 0 prio > 31) /* prio in range? */ return 0; new_tcb = &(threaddata[thread_count]); new_tcb->runnable = 1; new_tcb->next = threads[prio]; new_tcb->id = thread_count; threads[prio] = new_tcb; if (prio > current_prio) { /* Raise the priority */ current_prio = prio; /* Set initial stack pointer for the thread. It starts at a high address and grows to lower addresses. */ new_tcb->registers[r_sp] = (uint) stack_top; /* Mer kod, likadan som innan */ Nu återstår bara en sak, nämligen att ändra i själva schedulern, ITS_scheduler(). Att välja nästa tråd var ganska enkelt innan, men nu måste vi ta hänsyn till två saker, dels trådarnas prioritet och dels om de är körbara eller inte. Detta gör att schedulern blir mer komplicerad. Denna versionavschedulernväljerennytrådeftertvåkriterier:antingennästa körbara tråd med samma prioritet, eller den körbara tråden med högst prioritet. I denna implementationen så får trådar med högre prioritet börja köra när man har kommit till slutet av listan med trådar på aktuell prioritet. TCB *ITS_scheduler(TCB { uint cause; TCB *newthread; *tcb) /* Check interrupt source We are only interested in EXT_INT3 and EXC_SYSCALL */ cause = get_cause(); if ( cause & EXT_INT3 ) 35 lsg.fm 21 June 2002 12.09:49 sida 35 av 38

{ /* acknowledge external int 3 */ acknowledge_int(ext_int3); else if ( cause & SW_INT0 ) { /* acknowledge sw int 0 */ set_cause(0); else { return 0; /* Select next thread to run */ newthread = threaddata[current_thread].next; if (newthread == NULL) { newthread = threads[current_prio]; /* Check so it is runnable */ while (newthread->runnable == 0 && newthread!= &threaddata[current_thread]) { newthread = newthread->next; if (newthread == NULL) { newthread = threads[current_prio]; if (newthread->runnable == 0) { /* We checked every thread with this priority, */ /* so we have to go through the whole list */ /* and find a runnable task */ current_prio = 31; newthread = threads[current_prio]; while ((newthread == NULL) (newthread->runnable == 0)) { if (newthread == NULL) { current_prio = current_prio - 1; newthread = threads[current_prio]; else { newthread = newthread->next; current_thread = newthread->id; /* Return the new thread's TCB. */ 36 lsg.fm 21 June 2002 12.09:49 sida 36 av 38

return newthread; Kommentar: Denna lösningen har ett antal brister, som måste tas om hand innan man kan använda kärnan i ett riktigt system. Eftersom det varken går att ta bort en tråd som en gång är skapad, eller modifiera attributet runnable för trådarna, så kommer den eller de trådarna som har högst prioritet att få exklusiv tillgång till processorn. Detta gör att trådar med lägre prioritet svälts ut, och aldrig kommer att få köra. För att lösa dessa problem måste någon form av wait()-funktion implementeras (se uppgift 5.9 och 5.10), samt gärna ett sätt att ta bort en skapad tråd. 9 Man skulle kunna tänka sig att man realiserar funktionen för wait på semaforer (beskriven på sidan 191) enligt följande pseudokod: wait(s) { disable_interrupts(); while (S <= 0) { enable_interrupts(); yield(); disable_interrupts(); S = S - 1; enable_interrupts(); Förklara om, och varför, det här sättet att realisera wait fungerar om: a) Det finns en processor som byter tråd var 100 ms. b) Det finns två processorer som delar minne i vilket semaforen finns. Om det finns en processor, så kommer metoden att fungera. Anledningen är att när avbrotten är avstängda, så kan det inte inträffa något trådbyte, och den körande tråden har alltså exklusiv tillgång till minnesområdet. 37 lsg.fm 21 June 2002 12.09:49 sida 37 av 38

Om det däremot finns två processorer, så kommer metoden inte att fungera, eftersom den andra processorn fortfarande kör även när avbrotten är avstängda. Man kan alltså tänka sig att båda processorerna samtidigt upptäcker att S är ett, och båda samtidigt räknar ned S och returnerar. Då har båda de körande trådarna fått tillgång till semaforen, trots att endast en borde ha fått det. För flerprocessorsystem finns det i stället särskilda assemblerinstruktioner för att åstadkomma ömsesidig uteslutning, som garanterar att endast en processor kan modifiera värdet på semaforen. 38 lsg.fm 21 June 2002 12.09:49 sida 38 av 38