Embedded Systems Lecture 9: Operating Systems, buffered i/o Ian McCrum Room 5B18, Tel: 90 366364 voice mail on 6th ring Email: IJ.McCrum@Ulster.ac.uk Web site: http://www.eej.ulst.ac.uk www.eej.ulster.ac.uk/~ian/modules/EEE527/files Operating Systems In a conventional PC, an Operating systems allows easy access to the resources of the computer • • • • • • • Disk drives, Optical, magnetic and solidstate (e.g SD) Printers, Serial ports, USB and RS232 Ethernet/WifI interfaces, Sound generations and playback, Screens and keyboard. Memory In a modern windowing environment it manages access to the screen, allows multiple programs to run at once, including hidden programs running in the background 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE527/files 2 Embedded Operating Systems Again, it manages the resources of the (micro)computer; memory. i/o and CPU access. • Task & Resource management • When you execute a program the OS creates a task • A task can not “hog” a resource (memory/serial port/timer) • Normally we consider multi-tasking but even single tasking systems can use an OS – e.g – to manage memory (malloc() and free() ) – To abstract hardware, (printf or a network socket connection) – This offers portability, we would not want to write a malloc function for every system that could run our code. 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE527/files 3 Multitasking • Gives the appearance of doing two (or more) things at once • A simple example would use interrupts and have a “main” program and a “interrupt routine” • A more managed system might have a timer interrupt only and give each task a 20 millisecond slice of time. (c.f co-routines) – Wasteful if a task is waiting for an i/o, e.g a ADC to complete – Better if a task takes its slice or hands it back, better if the OS handles all i/o and knows if a task can run or not. • Early windows give a task a time-slice but let the task itself relinquish control – or not! If it hung or waited forever for a human to do something. We could call this “co-operative non-preemptive multitasking. In practice tasks call system calls often and the OS intervenes • More efficient to allow the OS or a task to preempt another and get the CPU resource, maybe because data has arrived that a task was waiting for. For this to work the i/o must be buffered 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE527/files 4 Scheduling 18/11/13 From http://www.freertos.org 18/11/13 5 Scheduling • Here we only briefly mention this, c.f Meng module on RTOSs. • Co-operative is straight forward to implement • Preemptive can have different strategies; – Round Robin – Priority-Based Round Robin – Shortest Process Next – Lottery Scheduling – First Come First Served – Shortest Job First (SJF) – Shortest Remaining Time Next – Highest Response Ratio Next – Rate Monotonic Scheduling – Earliest Deadline First 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 6 Round Robin • Each time the scheduler has to decide which task to run it goes searches a queue. Imagine the queue is circular and its starting point varies – just after the last task to run. • With Priority-Based Round Robin a number of queues are used. If there are tasks in the high priority queue ready to run they get picked first. 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 7 Classification of OSes • Non Real time, a task will get to run at some time in the future • Soft Real time, a task will run in the future, nearly always before a deadline • Hard Real time, a task will run by a deadline, guaranteed! The Issue is not speed, it is predictability and deterministic responses that are needed. Often a mixture is used, non-critical tasks mop up spare cpu or resources but are pre-empted if necessary. Use of a Hardware Abstraction Layer gives a more robust system, portable and fault tolerant. 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 8 Other features of OSes • Memory management – Virtual memory helps • Interrupt management – top half/bottom half • Protection, Kernel space and User space, privilege levels • Security and auditing • Inter Task Communication is important – – – – – 18/11/13 Sempahores (counting and single) Other types of flags, mutexes and event flags Pipes, Queues, buffers and shared memory Sockets and Streams www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 9 Issues • Tasks can be “Ready” ,”Running” or “Waiting” (aka Blocked) modern OSes use Queues and Lists to manage these, and we also need Per Process Data Area blocks of memory (PPDA tables) So memory is needed • The source of the Linux Kernel has 35 different tables! • The kernel must ideally be in protected memory so user processes cannot access and corrupt these. • Interrupt latency, context switch time and jitter affect performance • All i/o must be buffered and managed. Implies interrupts all handled by the OS and tasks cannot interrupt. • Potential snags still possible – deathly embrace, deadlock must be monitored for, as well as memory “faults” when a task attempts to get at memory it does not own. • Virtual memory made this much easier. • Security is now also very important, to protect against rogue code, by accident or design! 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 10 Buffered i/o is useful • A user does not read hardware registers directly • A user accesses a buffer in memory • E.g typical serial input functions are kbhit() and getch(). For buffered i/p we do not use these • We can test to see if the buffer has data in it and we can get data from the buffer. 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 11 Incoming data – e,g UART • We arrange an interrupt to occur when data arrives • We get the incoming char and place it in the buffer (if there is room) • There is some housekeeping to do. • There are some data structures and variables needed (and a couple of ways of implementing ring buffers) 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 12 Taken from http://thesqldude.com/2012/01/31/sql-server-ring-buffers-and-the-fellowship-of-the-ring/ 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 18/11/13 7/files 13 Beware! Some implementations have head pointing one beyond the end of the data, at the next empty space, liekwise for tail so make sure you are clear which convention is in effect. 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 14 Ring buffers (assume an array) • Need a head pointer – holds last data written – Alternative implementations point it at the next empty space, • Need a tail pointer - holds oldest data yet to be read • Need to know when the buffer is full – either keep a count variable or check pointer values • When putting data into the buffer we advance the head pointer, if it is beyond the end of the buffer we point it at the beginning of the buffer. We might check first to see if there is room • When reading data we see if there is data there, we use the tail pointer, get the array contents and increment the tail pointer, if is beyonf the end of the array we reset it to the beginning of the array. 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 15 Ring buffer in C #define BUFSIZE 256 // make this a power of 2 unsigned char buffer[ BUFSIZE]; // better to use a struct to hold the array and variables // here we will just use global variables to make the code simpler // could use pointers or just ints to hold array index, ints simpler to understand int head; // holds index of last byte put into the array, used to store data after incrementing int tail; // holds index of the last data read from the array, one past the next one to get // alternative code might do this differently, // do we need to post increment or preincrement the indexes – think and check! int count; // zero means full, if equal to BUFSIZE then the array is full void initbuffer(void); int putdata(unsigned char data); // typically used by ISR to plant data in the array, return 0/-1 // should check to see if buffer full (count==BUFSIZE) or empty (count==0) int getdata (); /./ returns 0 to 255 for a character or -1 if buffer empty 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 16 Functions for ringbuffer void initbuffer(void){ tail=0; head=0; count=0; memset(buffer,0,sizeof(buffer);// optional, clear array } int putdata(unsigned char data){ if(count<BUFSIZE){ head++;if(head==BUFSIZE)head=0; // preincrement head buffer[head]=data; count++; return(0); }// could rewrite to overwrite old, here we throw away the newest data return(-1); // if buffer full return a code to the caller, not much can be done! } int getdata (){ unsigned char c; if(count>0){ // there is data to be read c=buffer[]; // go read it! tail++; if(tail==BUFSIZE)tail=0; // postincrement tail count--; // no need to check it less than zero… return(c); // gets promoted to int, and it will be positive } return(-1); // lets the caller know the buffer was empty, but they should have checked first! } 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 17 Variations on a theme • The reason for making the buffer a power of two is to allow making the code more efficient. Instead of using if statements to correct the pointers you can use the mod function (or the OR function in assembler) (head++)%256; forces head to be 0..0xFF 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 18 Variations on a theme Most prfessional C programmers avoid arrays and use pointers, this allows use of malloc and dynamic memory allocation – more portable. Also collections of objects that are related are often put in a struct. By writing functions that take a struct pointer you can use the same ring buffer code with different ring buffers – you might have half a dozen in a big system. 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 19 from http://www.embedded.com/electrical-engineer-community/industry-blog/4419407/3/The-ring-buffer at 18/11/13 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 20 from http://www.embedded.com/electrical-engineer-community/industry-blog/4419407/3/The-ring-buffer at 18/11/13 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 21 from http://www.embedded.com/electrical-engineer-community/industry-blog/4419407/3/The-ring-buffer at 18/11/13 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 22 from http://www.embedded.com/electrical-engineer-community/industry-blog/4419407/3/The-ring-buffer at 18/11/13 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 23 Other types of buffer - case study • I once worked with a system that collected sporadic data from an ADC but needed to write a block of data to a floppy disk. • We used two buffers, one was always filling and one was emptying (it sometimes took a good fraction of a second to write a sector to the disk) • We used a boolean variable flag, if it held 0 the current buffer for writing was buffer0 and if 1 then the current buffer was buffer1. • We still used head and tail pointers, rather than a count variable we used tail==head to indicate empty and head=tail-1 to indicate full. • We also conceived of having more buffers and “chaining” them together to allow a human to change a floppy • A good programmer should know about data structures and algorithms – single and double linked lists, hashing, binary trees and search and sort methods as well as compression. 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 24 Exercise • You have seen code that uses interrupts. Modify it to put numbers into a buffer every second. • Each time a pushbutton is pressed read out the number on an LED. • Use the other LEDs to indicate empty half full and overfull (blink rapidly) • Or wait until we have the uart working and use it for o/p 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 25 Bonus, outputting numbers on one LED Morse code uses a 5 value system (not binary!) A dah is three times longer than a dit. A dit delay is used between symbols in a letter A dah delay is used between letters A 5 dit time is used between letters. Digits are exactly 5 symbols long ‘1’ is dit dah dah dah dah ‘2’ is dit dit dah dah dah … ‘5’ is dit dit dit dit dit ‘6’ is dah dit dit dit dit ‘7’ is dah dah dit dit dit … ‘9’ is dah dah dah dah dit ‘0’ is dah dah dah dah dah 18/11/13 [• — — — — ] [• • — — — ] [• • • • • ] [— • • • • ] [— — • • • ] [— — — — • ] [— — — — —] www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 26 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 27 Old C PIC code… You will need to write a delay routine (maybe __delayms() is available. Also the output_high and output_low needs changed for XC32 18/11/13 www.eej.ulster.ac.uk/~ian/modules/EEE52 7/files 28