The operating system I will describe, called TinyRealTime (TRT) was originally written by Dan Henriksson and Anton Cervin (technical report). (See also Resource−Constrained Embedded Control and Computing Systems, Dan Henriksson, 2006). I extended it, by adding a realtime trace ability, flexible soft timers, a mutex construct, a system dump and a simple command shell. The context switch time is a few hundred cycles on the 8-bit Atmel Mega1284, compiled using GCC. Full documentation is at http://people.ece.cornell.edu/land/courses/ece4760/TinyRealTime/index.html.

A realtime trace facility was added to TRT to enable following which tasks are executing and which semaphores are signaled. There are also to user defined events available. The trace facility uses one port of the MCU to dump data to an oscilloscope. A simple 3-bit DAC is used to convert task number and semaphore number to two separate voltages for display. Each task number adds about 125 mV to the output, so that when task 2, for example, is executing the voltage output is 250 mV. The semaphore number is specified when you initialize a semaphore. The task number starts at zero for the null task, then each task has a number defined by the order in which the tasks were created. Either of the events may be used as a scope trigger or to display. The image shown is from the realtime trace, which gives the task number as a voltage on the top trace and the semaphore number on the bottom trace.

With 4 tasks running, it takes about 700 cycles to perfrom a context switch by signalling a semaphore (Code to test this). The actual time spent in the scheduler ISR (timer1 compare-match) is 390 cycles, plus 70 cycles to store state, plus 70 cycles to restore state equals 530 cycles to service a timer tick. The scheduling scheme is Earliest Deadline First (EDF). Our student have used the kernel for motor controllers, wireless pedometer (http://people.ece.cornell.edu/land/courses/ece4760/FinalProjects/f2013/esc73_jsw267/esc73_jsw267/esc...), ultrasonic nagigators (http://people.ece.cornell.edu/land/courses/ece4760/FinalProjects/f2013/xs46_ebl43/xs46_ebl43/xs46_eb...) and more.

The API supplied with TRT includes core real time routines:

void trtInitKernel(uint16_t idletask_stack) Sets up the kernel data structures. The parameter is the desired starck size of the idle task. For a null idle task, a stack size of 80 should be sufficient.
void trtCreateTask(void (*fun)(void*),
uint16_t stacksize,
uint32_t release, uint32_t deadline, void *args)
Identifies a function to the kernel as a thread. The parameters specify a pointer to the function, the desired stack size, the initial release time, the initial deadline time, and an abitrary data input sturcture. The release time and deadline must be updated in each task whenever trtSleepUntil is called. The task structures are statically allocated. Be sure to configure MAXNBRTASKS in the kernel file to be big enough. When created, each task initializes 35 bytes of storage for registers, but stacksize minimum is around 40 bytes. If any task stack is too small, the system will crash!
void trtTerminate(void) Terminates the running task.
uint32_t trtCurrentTime(void) Get the current global time in timer ticks.
void trtSleepUntil(uint32_t release,
uint32_t deadline)
Puts a task to sleep by making it ineligible to run until the release time. After the release time, it will run when it has the nearest deadline. Never use this function in an ISR. The (deadline) - (release time) should be greater than than the execution time of the thread between trtSleepUntil calls so that the kernel can meet all the deadlines. If you give a slow task a release time equal to it's deadline, then it has to execute in zero time to meet deadline, and nothing else can run until the slow task completes. Experiment with this test code by changing the difference in the spoiler task while watching...
Read more »