Tentamen. Datorteknik och realtidssystem



Relevanta dokument
Tentamen. Datorteknik och realtidssystem

Tentamen i Realtidsprogrammering

Effektpedal för elgitarr

Tentamen Datorteknik och realtidssystem, TSEA81 Datum Lokal

Synkronisering. Föreläsning 8

Tentamen TEN1 HI

Provmoment: Ladokkod: Tentamen ges för: Tentamen TE111B El3. Namn: Personnummer: Tentamensdatum: Tid: 14:00-18:00.

Tentamen. Datorteknik och realtidssystem

Tentamen för kursen Objektorienterad programvaruutveckling GU (DIT010)

Tentamen. Datorteknik och realtidssystem

Tentamen EDA698 Realtidssystem (Helsingborg)

Datorsystem Laboration 2: Minnesmappade bussar

Introduktion till arv

Föreläsning 6: Introduktion av listor

Programmeringsteknik med C och Matlab

Föreläsning 4: Poster

Synkronisering - Semaforen. Om att vänta men inte i onödan

Tentamen i. för D1 m fl, även distanskursen. fredag 13 januari 2012

Att använda pekare i. C-kod

Översikt. Installation av EasyPHP 1. Ladda ner från Jag använder Release Installera EasyPHP.

Objektorienterad programmering D2

Tentamen. 2D4135 vt 2005 Objektorienterad programmering, design och analys med Java Lördagen den 28 maj 2005 kl

Lösningar till tentauppgifterna sätts ut på kurssidan på nätet idag kl 19. Omtentamen i Programmering C, 5p, fristående, kväll,

Övningsuppgift. Bankkonton. Steg 2. Författare: Mats Loock Kurs: Inledande programmering med C# Kurskod:1DV402

Skizz till en enkel databas

Tentamen OOP

Synkronisering. Ordning och reda

Grundläggande programmering med C# 7,5 högskolepoäng

Summering av fält 1.1. Synkronisering - Semaforen. Summering av fält 1.3. Summering av fält 1.2. Summering av fält 2.3 (INTE GK)

Så här fungerar registreringen i Malmö stads Odlingsslottskö

Tentamen Datorteknik och realtidssystem, TSEA81 Datum Lokal

Tentamen i TDIU16 Process- och operativsystemprogrammering

Vem är vem på kursen. Objektorienterad programvaruutveckling GU (DIT011) Kursbok Cay Horstmann: Big Java 3rd edition.

Trådar. Aktiva objekt

Användarmanual Pagero Connect 2.0

Tentamen i Matematisk statistik Kurskod S0001M

Försättsblad till skriftlig tentamen vid Linköpings Universitet

Tentaupplägg denna gång

Tentamen, EDA501 Programmering M L TM W K V

Tentaupplägg denna gång

Tentamen i. för D1 m fl, även distanskursen. lördag 19 januari 2013

Föreläsning 3.1: Datastrukturer, en översikt

Tentamen i Objektorienterad programmering

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

Inlämningsuppgift 4 NUM131

Kapitel 1 Ansluta Router till Internet

Försättsblad till skriftlig tentamen vid Linköpings Universitet

Sätt att skriva ut binärträd

HI1024 Programmering, grundkurs TEN

Manual för Kollektomat

Föreläsning 11. Giriga algoritmer

Chapter 4: Writing Classes/ Att skriva egna klasser.

Föreläsning 2: Avlusning och antilustekniker

Tentamen i Programmering grundkurs och Programmering C

Objektorienterad Programkonstruktion, DD1346 FACIT. Tentamen , kl

Lösningar till uppgifterna sätts ut på kurssidan på nätet idag kl Omtentamen i Programmering C, 5p, A1, D1, E1, Fri, Pr1, Te/Ek1,

9-1 Koordinatsystem och funktioner. Namn:

Kundportal. Kundportal - Användarhandledning

Kom igång med ArcGIS Online - Snabba steg för att börja arbeta

Omtentamen (del 1, 6 högskolepoäng) i Programkonstruktion och datastrukturer (1DL201)

Uppgift (poäng) 1 (2) 2 (3) 3 (4) 4 (4) 5 (3) 6 (4) 7 (6) 8 (6) 9 (8) Summa

Växtviskaren EITF11 Digitala projekt VT15, I12

Summering av fält. Synkronisering. Summering av fält. Bounded Buffer. Bounded Buffer (get) Bounded Buffer (put)

Senaste version kan hämtas från Internet i PDF 1 format

Projektarbete 2: Interaktiv prototyp

Att köpa ny dator SeniorNet Lidingö Januari-2016

Systemkonstruktion SERIEKOMMUNIKATION

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

Algoritmanalys. Genomsnittligen behövs n/2 jämförelser vilket är proportionellt mot n, vi säger att vi har en O(n) algoritm.

International Olympiad in Informatics July 2011, Pattaya City, Thailand Tävlingsuppgifter Dag 2 Svenska 1.3. Papegojor

Felsökning av mjukvara

Operativsystem ID1200/06 (ID2200/06 6hp) Tentamen :00-18:00

Övningar Dag 2 En första klass

Pulsmätare med varningsindikatorer

Åtkomst och användarhandledning

Programmeringsuppgifter 1

(Lösningsförslag finns sist i denna fil.)

Dagens föreläsning. Repetition. Repetition - Programmering i C. Repetition - Vad C består av. Repetition Ett första C-program

KUNGLIGA TEKNISKA HÖGSKOLAN. Linefollower. Med LEGO Mindstorms och NXC. Paul Coada Introduktion i datateknik II1310

1 Funktioner och procedurell abstraktion

Seriehantering. [En enkel guide för hur du som serieadministratör använder SVEMO TA.]

CE_O6. Parallell in/utmatning (I/O). Förberedelser till laboration nios2io.

Lär dig sökmöjligheterna i Disgen 8

LOTS ANSÖKAN TÄVLINGSTILLSTÅND

Användar Guide. är ett varumärke av Google Inc.

Tentamen Datorteknik Y, TSEA28 Datum

HÖGSKOLAN I KALMAR Institutionen för teknik Erik Loxbo LABORATION I PLC-TEKNIK SEKVENSSTYRNING AV TRANSPORTBAND SIMATIC S7 - GRAPH

Instruktion för att slutföra registreringen

Du som har har tv-mottagare ska betala radio- och tv-avgift

Försättsblad till skriftlig tentamen vid Linköpings Universitet

Tentamen Datorteknik D del 2, TSEA49

Objektsamlingar i Java

TENTAMEN. Kurs: Objektorienterad programmeringsmetodik 5DV133 Ansvarig lärare: Anders Broberg. VT-13 Datum: Tid: kl

Bankkonto - övning. Övning 2 Skriv en metod, geträntan, som returnerar räntan.

AVR 3 - datorteknik. Avbrott. Digitala system 15 hp. Förberedelser

Tentamen DE12, IMIT12, SYST12, ITEK11 (även öppen för övriga)

LINKÖPINGS TEKNISKA HÖGSKOLA Institutionen för Ekonomisk och Industriell Utveckling Ou Tang

Att öva på och förstå ett program med flera samverkande klasser.

Programmeringsolympiaden 2012 Kvalificering

Presentkort. I grunder/inställningar/blankettval är det blankett nr 1 som används till presentkortsfaktura.

Transkript:

Tentamen Datorteknik och realtidssystem, TSEA81 Datum 2014-01-13 Lokal KÅRA Tid 8-12 Kurskod TSEA81 Provkod TEN1 Kursnamn Datorteknik och realtidssystem Institution ISY Antal frågor 4 Antal sidor (inklusive denna sida) 17 Kursansvarig Andreas Ehliar Lärare som besöker skrivsalen Andreas Ehliar Telefon under skrivtiden Besöker skrivsalen Cirka 9 och 11 Kursadministratör Anita Kilander Tillåtna hjälpmedel Inga Poäng Betyg 50-60 5 Betygsgränser 40-49 4 30-39 3 0-29 U Important information Alla svar ska ha en motivation om inget annat anges. Om du svarar med programkod räknas kommentarer i programkoden som kommentarer. Svar som ej är motiverade kan leda till poängavdrag Om inget annat anges ska du anta att schemaläggningsmetoden som används är priority based, preemptive, scheduling Om du är osäker på det exakta namnet för en viss funktion, skriv en kommentar om vad funktionen gör så kommer vi troligtvis att förstå vad du menar. (Detsamma gäller syntaxen för programspråket C.) Lycka till! 1

Fråga 1: (24p) Systembeskrivning Du har fått i uppgift att förbättra ett inbyggt system som (bland annat) är baserat på två stycken enklare 8-bitars processorer där processor 1 kör p1 task och processor 2 kör p2 task. Dessa tasks är ansvariga för att systemet uppfyller följande specifikationer: p1 task samplar värden från port A i 500 Hz, samt skickar ut en filtrerad version av dessa värden på port B i 500 Hz efter en kort fördröjning. Dvs, periodtiden är 2 ms. (Dock finns det möjlighet till tillfälliga variationer i periodtiden, se nedan.) I p1 task är det viktigt att intervallet mellan anrop till get value from porta() håller sig mellan 1.95 ms och 2.05 ms så länge intervallet i medel håller sig på 2.0 ms. I p1 task är det också viktigt att intervallet mellan anrop till set portb value() håller sig mellan 1.95 ms och 2.05 ms. Tiden mellan det att p1 task läser in ett värde samt att det filtrerade värdet skickas ut får vara max 3 ms. p2 task fungerar på liknande sätt. Skillnaden här är att samplingsfrekvensen är 200 Hz (periodtid 5ms), samt att tiden mellan anrop till get value from portc() respektive set portd value() får variera mellan 4.95 ms och 5.05 ms även om medeltiden på detta intervall ska vara 5 ms räknat över en längre tid. Slutligen får det gå max 8 ms från det att get value from portc() anropas tills dess att set portb value() anropas med det filtrerade värdet. (Till skillnad från i laborationerna, där tidupplösningen i SimpleOS är 20 ms, så är SimpleOS i denna uppgift konfigurerad så att tidupplösningen på si wait until time är 1 ms.) Utvalda delar av källkoden Källkoden för dessa två tasks finns nedan. De är identiska förutom följande detaljer: De använder inte samma parametrar till sina FIR-filter De använder inte samma in och ut-portar De väntar olika många millisekunder i sina anrop till si time add n ms. void p1_task(void) /* Initiera parametrar som krävs för FIR-filtret */ const int LENGTH = 25; int buffer[length]; int h[length]; clear_buffer(buffer, LENGTH); initialize_constants(h,length, "filter_coefficients.txt"); int next_val = 0; si_time next_time; si_get_current_time(&next_time); while(1) sleep_until(&next_time, 2); // Vänta på nästa sample /* Sampla ett värde från port A, skicka ut ett * förberäknat värde på port B samt beräkna nästa * värde som ska skickas till port B */ int in_val = get_value_from_porta(); set_portb_value(next_val); next_val = fir_filter(in_val, buffer, h, LENGTH); 2

void p2_task(void) /* Initiera parametrar som krävs för FIR-filtret */ const int LENGTH = 50; int buffer[length]; int h[length]; clear_buffer(buffer, LENGTH); initialize_constants(h,length, "filter2_coefficients.txt"); int next_val = 0; si_time next_time; si_get_current_time(&next_time); while(1) /* Vänta på nästa sample */ sleep_until(&next_time, 5); /* Sampla ett värde från port C, skicka ut ett * förberäknat värde på port D samt beräkna nästa * värde som ska skickas till port D */ int in_val = get_value_from_portc(); set_portd_value(next_val); next_val = fir_filter(in_val, buffer, h, LENGTH); // Hjälpfunktionen sleep_until() används för att räkna upp tiden // angiven i t med specificerat antal millisekunder samt sova till // denna tidpunkt void sleep_until(si_time *t, int ms) si_time_add_n_ms(t, ms); si_wait_until_time(t); // Du behöver troligtvis inte veta hur funktionen fir_filter() fungerar // för att lösa uppgiften, men om du känner att du behöver veta detta så // är den inkluderad här nedan: int fir_filter(int in_val, int *x, int *h, unsigned int length) unsigned int i; long sum = 0; // Falta x med h, samt skifta x ett steg så att vi är redo // för nästa anrop till fir_filter() for(i=0; i < length - 1; i++) x[i] = x[i+1]; sum = sum + x[i] * h[i]; // Använd samt spara ner det senaste inkomna värdet i x sum = sum + in_val * h[length-1]; x[length-1] = in_val; return sum; 3

Tidsåtgång för olika delar av programmet En approximativ kostnad för att köra funktionen fir filter (på en välkänd 8-bitars processor) är cirka 40 klockcykler för varje iteration (dvs 40*length). För enkelhetens skull antar vi att övriga delar av programmet inte tar någon tid att köra, inklusive byte mellan tasks. (Undantaget funktioner som måste vänta i stil med si wait until time(), si sem wait(), och så vidare.) Under dessa parametrar kommer alltså anropet till fir filter() i p1 task ta 1000 klockcykler och anropet till fir filter() i p2 task ta 2000 klockcykler. Frågeställning (a) (2p) Givet koden samt begränsningarna ovan, vilka är den minsta klockfrekvensen som processor 1 respektive processor 2 kan köras i för att kraven listade i systembeskrivningen ovan kommer att uppfyllas? (Det är fullt acceptabelt att svara i potensform eller bråkform.) (b) (6p) För att minska produktionskostnaden för detta system är tanken att p1 task och p2 task ska köras på samma processor. Följande main-funktion används för att starta systemet: int main(void) si_kernel_init(); // Initiera SimpleOS /* skapa p1 och p2 (p1 schemaläggs med högst prioritet) */ si_task_create(p1_task, &P1_Task_Stack[STACK_SIZE-1], 10); si_task_create(p2_task, &P2_Task_Stack[STACK_SIZE-1], 20); si_kernel_start(); // Och starta kärnan /* will never be here! */ return 0; Till en början var det tänkt att denna processor skulle köras i 2 MHz. Det visar sig dock att p2 task (som körs med lägst prioritet) ej längre uppfyller specifikationen som ges i systembeskrivningen ovan. Förklara varför p2 task ej uppfyller specifikationerna samt räkna ut vilken klockfrekvens som processorn skulle behöva ha för att kraven ska uppfyllas, givet att inga modifikationer till källkoden får göras. Tips: För full poäng bör du rita ett diagram som över tid visar vad som händer i systemet (c) (8p) Tyvärr går det inte att få tag i en processor av rätt modell som klarar av att köra i den klockfrekvens som du bör ha räknat ut i deluppgift b. Du måste nu skriva om programmet så att kraven i systemspecifikationerna är lättare att uppnå. Ett möjligt sätt att lösa problemet är att ersätta p1 task och p2 task med en ny task som löser både p1 task och p2 tasks uppgifter. Du får ej ändra i funktionen fir filter(). Skriv denna task, samt räkna ut vilken ungefärlig klockfrekvens processorn nu måste köras i för att systemspecifikationerna ska uppfyllas. void combined_task(void) /* Initiera parametrar som krävs för FIR-filtret som hör till P1*/ const int LENGTH1 = 50; int buffer1[length]; int h1[length]; clear_buffer(buffer1, LENGTH1); initialize_constants(h1,length1, "filter_coefficients.txt"); int next_val1 = 0; /* Initiera parametrar som krävs för FIR-filtret som hör till P2*/ const int LENGTH2 = 50; int buffer2[length]; 4

int h2[length]; clear_buffer(buffer2, LENGTH2); initialize_constants(h2,length1, "filter2_coefficients.txt"); int next_val2 = 0; si_time next_time; si_get_current_time(&next_time); while(1) // Din uppgift är att visa vad som ska stå här! För // att minska på skrivandet kan du förkorta ner // funktionsnamn/funktionsanrop (så länge resultatet // blir begripligt) // Exempel: in_val2 = portc istället för get_value_from_portc(), // portd = next_val2 istället för set_portd_value(next_val2) // etc (d) (8p) Ett annat sätt att lösa uppgiften är att dela upp p1 task i två olika tasks: s1 task (som samplar indata från porta samt skickar utdata till port b) f1 task (som anropar fir filter() med lämpliga parametrar på den indata som s1 task läst in, samt skickar tillbaka utdata ifrån fir filter till s1 task) Visa vilka ändringar i programmet som behöver göras för att detta ska fungera. Glöm ej bort att beskriva vilken priortet som s1 task, f1 task samt p2 task ska ha! Tips: Använd symmetrisk synkronisering för att kommunicera mellan s1 task och f1 task. Räkna även ut vilken klockfrekvens som processorn behöver köras i för att specifikationerna givna i systembeskrivningen ovan ska uppfyllas. 5

Fråga 2: (16p) Du har fått i uppdrag att granska, samt om nödvändigt förbättra, stabiliteten i en programvara som används i bolaget Säker Finans ABför att hantera insättningar i insättningsautomater, uttag ifrån bankomater, kreditkortsautomater, samt en enkel internetbank. Till att börja med var detta program skrivet för att köras i endast en task, men detta ledde ibland till långa svarstider i samband med att exempelvis en bankomat tog för lång tid på sig att kommunicera med centraldatorn. För att bättra på svarstiderna har det beslutats att varje bankomat, insättningsautomat, kreditkortsläsare, samt användare som är inloggad i internetbanken ska få en egen task. 1 struct account 2 int64_t balance; // Belopp på kontot (i öre) 3 int64_t reserved; // Reserverat belopp (i öre) 4 int32_t interest; // Kontots ränta (Alltid noll) 5 uint64_t customerid; // Kontoinnehavarens personnummer 6 // Följande medlemsvariabler används när kontoinnehavaren vill 7 // göra ett inköp online 8 int online_active; // Satt till ett om en butik vill dra pengar vid onlineköp 9 char *online_store; // Namn på den butik som vill dra pengar online 10 int64_t online_amount; // Belopp butiken vill dra via kreditkort (i öre) 11 ; 12 13 #define MAX_ACCOUNTS 1000 14 struct account accounts[max_accounts]; 15 16 int withdraw_sanitycheck(unsigned int accountnumber, int64_t amount, uint64_t customerid) 17 18 if(amount < 0) 19 return 0; 20 21 22 if(accounts[accountnumber].customerid!= customerid) 23 // Försökte ta ut pengar från ett konto du ej har tillgång till 24 return 0; 25 26 return 1; 27 28 29 // Ta ut pengar från angivet konto, returnera ett om det lyckades, noll annars. 30 int withdraw(unsigned int accountnumber, int64_t amount, uint64_t customerid) 31 32 if(!withdraw_sanitycheck(accountnumber, amount, customerid)) 33 return 0; // Något gick snett 34 35 36 int64_t balance = accounts[accountnumber].balance; 37 38 if(balance - amount < 0) 39 // Täckning saknas 40 return 0; 41 42 43 accounts[accountnumber].balance = balance - amount; 44 return 1; // Transaktionen lyckades 45 6

(a) (4p) Förklara för dina kollegor på Säker Finans AB varför funktionen withdraw() i programmet ovan ej kommer att fungera om det körs på en dator som har flera processorer som tillåter två tasks att köra samtidigt på olika processorer (eller processorkärnor). Du kan anta att de har de förkunskaper som krävs för TSEA81, men att de inte har läst TSEA81 (eller motsvarande). (b) (4p) Rätta till buggarna i kodlistningen ovan genom en lämplig synkroniseringsmekanism som kopplas till det aktuella bankkontot. (Synkroniseringen ska ske på ett så granulärt sätt som möjligt, det vill säga, en operation som pågår mot ett visst bankkonto ska inte kunna hindra en annan task att starta en operation mot ett annat bankkonto.) Ange också om du behöver göra något speciellt i den rutin som initierar fälten i struct account. (c) (8p) Förutom funktionen withdraw() ovan finns det också en funktion online withdraw() i systemet. Denna funktion anropas i samband med ett onlineköp på Internet för att låta kontoinnehavaren godkänna överföringen i internetbanken. I samband med att användaren loggar in på internetbanken startas online banking task() och körs tills användaren loggar ut (du behöver dock inte implementera något som har med utloggningen att göra i denna uppgift, vi kan anta att processen avslutas på något annat sätt). 1 int online_withdraw(unsigned int accountnumber, int64_t amount, 2 uint64_t customerid, char *store) 3 4 if(!withdraw_sanitycheck(accountnumber, amount, customerid)) 5 return 0; // Något gick snett 6 7 8 if(accounts[accountnumber].online_active) 9 // Vi tillåter enbart en butik i taget att försöka dra 10 // pengar ifrån ett och samma konto 11 return 0; 12 13 14 // Reservera beloppet 15 accounts[accountnumber].balance = accounts[accountnumber].balance - amount; 16 accounts[accountnumber].reserved = accounts[accountnumber].reserved + amount; 17 18 accounts[accountnumber].online_store = store; 19 accounts[accountnumber].online_amount = amount; 20 accounts[accountnumber].online_active = 1; 21 22 if(ask_user_to_authenticate_transaction(accountnumber) == 0) 23 // Användaren nekade denna överföring, återställ 24 // kontot som det var tidigare 25 accounts[accountnumber].balance = accounts[accountnumber].balance + amoun 26 27 28 29 // Ta bort reservationen, oavsett om användaren godkände eller nekade överföringe 30 accounts[accountnumber].reserved = accounts[accountnumber].reserved - amount; 31 32 accounts[accountnumber].online_active = 0; 33 34 return 1; 35 36 // Forts på nästa sida 7

37 // Denna rutin anropas av online_withdraw() för att kommunicera med 38 // online_banking_task(). 39 int ask_user_to_authenticate_transaction(unsigned int accountnumber) 40 41 // Denna funktion ska du implementera. 42 43 // Returnera 1 om användaren ville godkänna transaktionen 44 // Returnera 0 om användaren inte ville godkänna transaktionen 45 46 47 48 // Denna task startas när användaren loggat in på internetbanken med 49 // ett visst kontonummer och ska vänta på att en online-transaktion 50 // påbörjas och då fråga användaren huruvida denna transaktion ska 51 // godkännas. 52 53 void online_banking_task(void) 54 55 unsigned int accountnumber = get_accountnumber(); 56 while(1) 57 // Du ska implementera den här delen av denna task 58 59 // Du behöver dock inte bekymra dig om hur den avslutas vid 60 // utloggning (någon annan del av systemet sköter detta) 61 62 63 // För att fråga användaren om en viss transaktion ska godkännas 64 // kan du använda denna funktion som ser till så att användaren får 65 // se en dialogruta som dels anger vilken butik som vill dra pengar 66 // ifrån detta konto och dels hur mycket pengar som ska dras. 67 // 68 // Om användaren godkänner transaktionen returneras 1, annars 0 69 int ask_user_to_acknowledge(unsigned int accountnumber, 70 const char *store_name, 71 int64_t amount); Din uppgift är att rätta till eventuella buggar i dessa funktioner, samt implementera funktionerna ask user to authenticate transaction() och online banking task(). En monitor ska användas för att sköta kommunikationen däremellan. (Du behöver inte skriva av hela online withdraw()-funktionen, det räcker att du i din tenta skriver exempelvis Mellan rad 48 och 49 ska följande rader läggas in: ) 8

Fråga 3: (8p) (a) (4p) Diskutera varför det är en dålig ide att styra åtkomsten till gemensamma variabler via en semafor om ett av de ställen som behöver komma åt variablerna är en interrupt-rutin. Nedan följer ett utdrag ur en hypotetisk applikation som visar ett icke-fungerande sätt att synkronisera åtkomst till gemensamma variabler: // Detta är en interrupt-hanterare void gpio_interrupt(void) int val = get_gpio_port(); si_sem_wait(&param.mutex); param.val = val; si_sem_signal(&param.mutex); // Detta är en helt vanlig funktion som aldrig anropas ifrån ett // interrupt: int calculate_xyzzy(void) si_sem_wait(&param.mutex); int retval = param.val * 58974 / 92 + 95; si_sem_signal(&param.mutex); return retval; (b) (4p) Visa hur rutinen ovan kan skrivas om så att ett lämpligare sätt att används för att synkronisera åtkomsten till de delade variablerna i structen param. Fråga 4: (12p) Systembeskrivning Följande kod är tänkt att användas i en inspektionsutrustning som används för att inspektera kretskort. Detta system består bland annat av en motoriserad släde som kan flytta en kamera i X och Y-led så att foton kan tas av kretskortet ifrån olika positioner. Slädens maxfart är 1000 mm / s i X respektive Y-led. Det finns två tasks i systemet som du behöver känna till i denna uppgift: photo task(): Denna task är ansvarig för att kommunicera med kameran samt att regelbundet ta foton av kretskortet under kameran. Varje foto komprimeras och märks med aktuell position för att senare lagras undan i en photo list för senare analys. Denna task ska köras med uppdateringsfrekvensen 10 Hz. Felet hos positionsangivelsen som sparas ner ska vara max 2mm i X respektive Y-led. position task(): Denna task läser av positionen med frekvensen 100 Hz och uppdaterar structen camera med nuvarande position. Denna task körs med högst prioritet i systemet. struct camera_info si_semaphore mutex; // Skyddar åtkomsten till olika fält i int x; // Nuvarande x-position (i enheten mm) int y; // Nuvarande y-position (i enheten mm) struct photo_list photos; ; static struct camera_info camera; 9

void setx(struct camera_info *c, int x) si_sem_wait(&c->mutex); // Protect access to shared variable c->x = x; si_sem_signal(&c->mutex); void sety(struct camera_info *c, int y) si_sem_wait(&c->mutex); // Protect access to shared variable c->y = y; si_sem_signal(&c->mutex); void record_photo_and_add_to_list(struct camera_info *c) struct photo p; // Läs av positionen p.x = c->x; p.y = c->y; // Ta och spara ner fotot för senare analys take_photo(&p); // Denna rutin tar cirka 10 ms att köra compress_photo(&p); // Denna rutin tar cirka 80 ms att köra add_photo_to_list(&c->photos, &p); void photo_task(void) si_time next_time; si_get_current_time(&next_time); while(1) // Spara ner bilder i 10 Hz. sleep_until(&next_time, 100); si_sem_wait(&camera.mutex); // Skydda åtkomsten till delade variabler i camera /* Den här rutinen tar ett foto och lagrar detta för senare åtkomst */ record_photo_and_add_to_list(&camera); si_sem_signal(&camera.mutex); void position_task(void) si_time next_time; si_get_current_time(&next_time); while(1) int x,y; // För att hålla systemet stabilt behöver vi läsa av positionen i 100 Hz sleep_until(&next_time, 10); 10

// Hämta och spara ner positionen för användning av andra delar av systemet get_position(&x,&y); setx(x); sety(y); (a) (4p) Det finns ett fel i detta program som gör så att en av dessa två tasks kommer att ha svårt att möta de deadlines som är uppsatta (dvs att position task ska uppdatera parametrarna i cirka 100 Hz samt att photo task ska ta bilder i cirka 10 Hz). Vad är det för fel samt hur kan du rätta till detta fel? (b) (4p) Det finns ett annat fel här som gör så att åtkomst till delade variabler ej sker på korrekt sätt så att felaktiga mätvärden kommer att lagras ibland. Varför kan detta ske, samt hur fixar du till denna bugg? (c) (4p) Antag att buggen i uppgift a har upptäckts och rättats till innan systemet levererades till kund men att buggen i uppgift b ej hittades innan slutleveransen. Ett antal kunder som köpt inspektionsutrustningen har noterat att systemet tycks ge felaktiga mätvärden i vissa fall, två exempel på detta finns nedan i figur 1 och 2: Kamerans startposition Kamerans slutposition Kamerans tänkta körbana Felet uppmättes här Figur 1: Felmätning 1: Uppmätt fel i kamerans position: 20 mm i X-led och 15 mm i Y-led. Kamerans startposition Kamerans slutposition Kamerans tänkta körbana Figur 2: Felmätning 2: Uppmätt fel i kamerans position: 1 mm i X-led och 7 mm i Y-led. Utifrån informationen ovan, kan du säga till din chef att det är troligt att buggen som du hittat i deluppgift b är ansvarig för de felmätningar som kunderna har skickat in? 11

Lösningsförslag, uppgift 1 Del a Processor 1 måste köra fir filter(), som tar 1000 klockcykler på 2 ms. Givet att alla andra delar av programmet tog noll tid ger detta trivialt att den minsta klockfrekvensen för P1 är 1000/0.002s = 500kHz. På samma sätt ges frekvensen för Processor 2 som 2000/0.005s = 400kHz. Del b Det som händer från tid 0 är som följer (där I/O motsvarar skrivning/läsning ifrån portarna): Tid Process 1 Process 2 0ms sleep sleep 2ms I/O 2-2.5ms fir sleep 4ms I/O 4-4.5ms fir sleep 5ms I/O 5-6ms fir 6ms I/O sleep 6-6.5ms fir sleep 8ms I/O 8-8.5ms fir sleep 10ms I/O 10-10.5ms fir 10.5ms sleep I/O 10.5-11.5ms fir sleep 12ms I/O 12-12.5ms fir sleep 14ms I/O 14-14.5ms fir sleep 15ms I/O 15-16ms fir sleep... Och så vidare... Problemet här är att vid tiden 10.5ms har det tagit 5.5ms sedan P2 körde sin I/O medans den maximala tiden får vara 5.05ms. För att detta ska gå ihop måste P1 kunna köra sin fir-process på 0.05ms, vilket skulle motsvara en klockfrekvens på 1000/(0.05) 10 3 = 20MHz. Del c Följande variant fungerar som huvudloop: int nv1 = 0,nv2 = 0,iv1,iv2; while(1) sleep_until(&next_time, 2); iv1 = porta; 12

portb = nv1; nv1 = fir(iv1,...); sleep_until(&next_time, 2); iv1 = porta; portb = nv1; nv1 = fir(iv1,...); sleep_until(&next_time, 1); iv2 = portc; portd = nv2; nv2 = fir(iv2,...); sleep_until(&next_time, 1); iv1 = porta; portb = nv1; nv1 = fir(iv1,...); sleep_until(&next_time, 2); iv1 = porta; portb = nv1; nv1 = fir(iv1,...); sleep_until(&next_time, 2); iv1 = porta; // Det här är kärnan i lösningen, vi ser till så att vi samplar och portb = nv1; // skickar ut värden för både P1 och P2 vid precis rätt tidpunkt, iv2 = portc; // sedan har vi (relativt) gott om tid på oss att räkna ut nästa portd = nv2; // värde för P1 och P2 innan vi måste sample ett värde för P1 igen i nv1 = fir(iv1,...); // början av loopen. nv2 = fir(iv2,...); De två mest krävande delarna av denna loop är dels anropet till fir för P2 i mitten som måste köras klart på en millisekund innan vi ska sampla för P1 samt de två anropen till fir-filtret längst ner som ska köras klart på 2 millisekunder. Av dessa är den tidigare värst, då 2000 klockcykler ska rymmas på 1 millisekund vilket ger en minsta klockfrekvens på 2 MHz. Del d En möjlig lösning: int in_val1; int next_val1 = 0; // Sample task for filter 1 void s1_task(void) // next_time initieras som i p1_task while(1) sleep_until(&next_time, 2); in_val1 = porta; portb = next_val1; si_sem_signal(&p1_mutex_req); si_sem_wait(&p1_mutex_ack); 13

// Filter task for first filter void f1_task(void) // Parametrar och variabler för FIR-filtret initieras som i // p1_task while(1) si_sem_wait(&p1_mutex_req); next_val1 = fir_filter(in_val1,buffer, h, LENGTH); si_sem_signal(&p1_mutex_ack); main() modifieras sedan så att både p1 mutex req och p1 mutex ack initieras till 0. Slutligen så måste s1 task ha högst prioritet, p2 task näst högst, och f1 task lägst. Det värsta fallet i denna implementation är om s1 task blir körbar vid samma tidpunkt som p2 task blir körbar. I detta fall kommer s1 task att läsa/skriva portar och sedan göra f1 task körklar. Innan f1 task kan starta måste dock p2 task köras. p2 task startas och läser/skriver portar för att sedan köra FIR-filtret (som tar 2000 klockcykler). Sedan körs f1 task (som tar 1000 klockcykler). Närmaste deadline är 2 ms senare då s1 task vill läsa/skriva portarna igen. Det vill säga, processorn måste klara att köra 3000 klockcykler på 2 ms. Klockfrekvensen är alltså 3000/0.002 = 1.5M Hz. Kommentar till uppgiften Notera att det finns ett helt orealistiskt antagande i denna uppgift samt ett antagande som ofta inte går att göra: Allting förutom FIR-filtret tar noll tid (egentligen helt orealistiskt) FIR-Filtret tar alltid lika lång tid att köra (realistiskt på vissa processorer, orealistiskt på andra processorer) Syftet med att räkna klockcykler är att man ska se att det är en magnitud i skillnad mellan den naiva lösningen (att ta den existerande koden och köra rakt av) samt att anpassa koden (på olika sätt) för att köras på en processor istället för två olika processorer. I C-uppgiften är det upp till programmeraren att schemalägga allting manuellt. Detta är en möjlighet i de fall som all källkod för systemet finns tillgängligt samt att det är tillåtet att göra ganska stora ändringar i källkoden (dvs att flytta om alla aktiviteter ifrån två tasks till en task). I D-uppgiften tänker vi oss att enbart en av två tasks får modifieras (dessutom inte den task som hade varit smartast att modifiera i detta fall...). Vi kan antingen tänka oss att källkoden för p2 task inte är tillgänglig (eftersom den är inköpt ifrån en leverantör som enbart leverar en binär), alternativt att vi inte får ändra i källkoden på grund av exempelvis certifieringsskäl eller liknande. D-uppgiften skulle också kunna bli mer stabil om man har en schemaläggare i systemet som stödjer EDF-schemaläggning, som senaste versionen av Linux (läs på om SCHED DEADLINE om du är intresserad av detta). Lösningsförslag, uppgift 2 Del a Problemet kan uppstå om två tasks, som kör på två olika processorer, försöker köra withdraw samtidigt med samma kontonummer. Det största problemet uppstår om exempelvis följande sekvens uppkommer: 14

Task 1: Kör rad 36 Kör rad 43 Task2: Kör rad 36 Kör rad 43 I detta fall kommer accounts[].balance ha ändrats på ett sådant sätt att transaktionen task 1 gjorde har glömts bort. Del b Lägg till en si semaphore sem i struct account. Lägg sedan till följande rader i programmet: Rad 31.5: Rad 32.5: Rad 39.5: Rad 43.5: si_sem_wait(&accounts[accountnumber].sem); si_sem_signal(&accounts[accountnumber].sem); si_sem_signal(&accounts[accountnumber].sem); si_sem_signal(&accounts[accountnumber].sem); Del c Förutom semaforen vi lade till i del b behöver vi lägga till följande i struct account: si condvar change int userstatus Lägg sedan till följande rader i programmet för att säkerställa exklusiv åtkomst till delade variabler: Rad 3.5: si_sem_wait(&accounts[accountnumber].sem); Rad 4.5: si_sem_signal(&accounts[accountnumber].sem); Rad 10.5: si_sem_signal(&accounts[accountnumber].sem); Rad 33: si_sem_signal(&accounts[accountnumber].sem); Slutligen kan monitorn implementeras på exempelvis följande sätt: int ask_user_to_authenticate_transaction(unsigned int acctno) // userstatus = - 1 används för att signalera till banking task // att en ett online-köp pågår. accounts[acctno].userstatus = -1; si_cv_broadcast(&accounts[acctno].change); // Vänta på att banking task signalerar att användaren svarat // på online-köp-begäran while(!accounts[acctno].userstatus < 0) si_cv_wait(&accounts[acctno].change); return accounts[acctno].userstatus; void online_banking_task(void) unsigned int acctno = get_accountnumber(); while(1) si_sem_wait(&accounts[acctno].sem); // Vänta på att begäran om ett online-köp kommer in. 15

while(accounts[acctno].userstatus!= -1) si_cv_wait(&accounts[acctno].change); accounts[acctno].userstatus = ask_user_to_acknowledge(acctno, accounts[acctno].store_name, accounts[acctno].reserved); si_cv_broadcast(&accounts[acctno].change); si_sem_signal(&accounts[acctno].sem); Lösningsförslag, uppgift 3 Del a Semaforer får inte användas ifrån ett interrupt eftersom interruptet inte är en egentligen task utan körs som samma task som användes när interruptet aktiverades. Det vill säga, om si sem wait() faktiskt behöver sova kommer fel task att hamna i väntelistan istället för att interrupthanteraren hamnar i väntelistan. (I SimpleOS är dessutom semaforer implementerade genom att interrupt stängs av och sedan slås på, varav man vanligtvis inte vill göra det senare i en interruptrutin.) Del b void gpio_interrupt(void) int val = get_gpio_port(); param.val = val; int calculate_xyzzy(void) // De utkommenterade raderna visar var vi måste ha wait/signal om // vi behöver skydda param.val även mot åtkomst ifrån andra tasks // och inte bara ifrån interruptrutinen. // si_sem_wait(&param.mutex); DISABLE_INTERRUPTS; int retval = param.val * 58974 / 92 + 95; ENABLE_INTERRUPTS; // si_sem_signal(&param.mutex); return retval; Lösningsförslag, uppgift 4 Del a Problemet är att semaforen som photo task låser innan den anropar record photo and add to list även används av position task. Detta medför att semaforen är låst dels under tiden fotot tas och dels under tiden fotot komprimeras. Egentligen borde det bara vara låst under den tid som den gemensamma resursen används, dvs x och y i struct camera info. Fixad kod: // Följande rader tas bort ur photo_task(): si_sem_wait(&camera.mutex); 16

si_sem_signal(&camera.mutex); Istället läggs följande rader in i record_photo_and_add_to_list runt positionsavläsningen: si_sem_wait(&c->mutex); p.x = c->x; p.y = c->y; si_sem_signal(&c->mutex); Del b Det finns två ganska stora tveksamheter i kodlistningen, men det räcker med att diskutera och fixa ett av dessa problem för att få full pott: Följande rader stryks i position_task(): setx(x); // Oops, jag glömde visst &camera här i tentan, men jag tror sety(y); // att i princip alla förstod vad jag menade, även om jag bara // fick en kommentar om detta. Dessa ersätts av följande anrop: setxy(&camera,x,y); Där setxy ser ut på följande sätt: void setx(struct camera_info *c, int x, y) si_sem_wait(&c->mutex); // Protect access to shared variable c->x = x; c->y = y; si_sem_signal(&c->mutex); Det andra problemet är att det i programmet inte finns någon garanti för att position task och photo task körs i synk med varandra, vilket skulle göra att felet snabbt blev större än de tillåtna 2 mm. Detta går att ordna genom att exempelvis starta photo task med hjälp av asymmetrisk synkronisering ifrån position task vid var tionde iteration. (Det var dock bara ett fåtal som noterade detta.) Del c Felmätningen i figur 1 beror troligtvis inte (åtminstone inte enbart) på att setx/sety inte körs atomiskt. Detta för att det är ett relativt stort fel, både i x-riktning och y-riktning trots att kameran i detta skede rörde sig ortogonalt med en av axlarna (det vill säga, en felmätning orsakad av att setx/sety inte körs atomiskt skulle enbart ha påverkat en av axlarna i detta fall, eftersom positionen på den andra axeln inte borde ha ändrat sig nämnvärt). Buggen i figur 2 skulle däremot kunna tänkas bero på att setx/sety inte körs atomiskt eftersom kameran här rör sig i både x och y-led, samt att det enbart är en av positionsangivelserna som är fel. 17