Single Stack RTOS

Things to know about Single Stack RTOS

I have been working with a single stack RTOS for a few years now. I have been using thisĀ single stack RTOS due to the limited memory available in the devices I work with. The PIC and MSP430 have limited memory but it is useful to have an event driven paradigm to program to.

The single stack RTOSĀ is based on queues. These queues hold a structure that defines the task to be run. There are several queues to the RTOS. There is the run queue for tasks that are ready to run. There is a delay queue for tasks that are waiting for a specified time. And, there are event queues for tasks waiting for certain events. You get good at handling queues when working with this RTOS.

There is one caveat to the single stack RTOS. Each task must run to completion. This cleans up the single stack for the next task. There is no preemption. We have events and delays. It is amazing what you can do with just events and delays.

Through the use of queues the RTOS is able to manage the tasks in the system. The run queue is where a task is placed when it is ready to run. If that task needs to delay it calls a routine in the RTOS with a function pointer and a delay value. The RTOS places this function pointer in a struct and then places that on the delay queue. There is a function in a timer interrupt that goes through the delay queue decrementing delays. When a delay is zero that task or function pointer struct is placed on the run queue and later executed. It is that easy. The events are similar, when a task needs to wait for a specific event, a function pointer is put on the queue for that event. Typically an interrupt would declare the event and take all the waiting tasks off of the event queue and puts them on the run queue.

An interesting thing about this small RTOS is that it has run on several different processors with very little changes. I have run it on a Xilinx FPGA with a PPC hardcore processor. I have run it on an MSP430 and a PIC. It is very portable.

The prototype for the run routine is the following:

void krtosRun(void);

This routine is called periodically from the back ground. That is it isn’t call from an interrupt just the infinite loop in the back ground. It looks at the run queue and if there is something there it takes the first task off of the queue and runs it. When a task is put on the run queue it is put at the end so everyone will have a chance to run. In the run function there is an opportunity to put in an idle task. If there are no tasks ready to run an idle task can be run.

The prototype for the function to put tasks on the run queue is as follows:

void krtosPut(struct krtosBlock *myBlock);

This routine takes a task inside the krtosBlock and puts it on the run queue. It puts the task at the end so that everyone has a chance to run. This excludes any type of priority but that hasn’t seem to be a problem in my past projects.

The prototype for the delay function is the following:

void krtosPutDelay(krtosFunction krtosFuncPtr, unsigned int krtosDelayVal);

This routine takes the function pointer and the delay and creates a krtosBlock and puts that block on the delay queue. The delay queue is unsorted so each time the delays are decremented the routine that does that must go through all the blocks decrement each of their delay fields. The next time through decrementing the delay fields if there is a field that is zero that block is taken and put on the run queue.

The wait for event prototype is this:

void krtosWaitEvent(krtosFunction krtosFuncPtr, unsigned int Event);

This routine takes the function pointer, creates a krtosBlock, and puts that on the queue enumerated by the Event variable. Once the event is declared it will be taken off the wait queue and put on the run queue.

The prototype for the declare event routine is:

void krtosDeclareEvent(unsigned int Event);

This routine takes all the waiting tasks off the enumerated event queue and puts them on the run queue. If there are no tasks waiting it does nothing.

To round out the routines of the RTOS is an init routine and a decrement delay routine.

void krtosDecDelay(void);
void krtosInit(void);

The decrement delay routine is called typically from a timer interrupt. The init routine is called at the beginning of the program before anything is done with the RTOS. It simply initializes the queues and setups up the head pointers to the queues.