Small Operating System Documentation

Written by Oleg Skydan
Edited by Garst R. Reese

Contents


Kernel Entry Point


StartOS

The StartOS function is the entry point of the kernel.

void StartOS(
void
);

Parameters

None.

Return Values

The funtion never returns.

Remarks

Call StartOS function to start the kernel. This function will add Idle task to the task list and switch to the task with the highest priority which is ready to go.

After calling this function all contents of the stack will be erased! The kernel rewinds the stack pointer to the initial value when you enter into kernel mode. So the kernel itself is non reentrant, that's why all interrupts are disabled in kernel mode. The system enters into kernel mode in the macros ENTERINT() and ENTER(), and returns to normal mode in macro LEAVE().

Examples

int main(void)
{ 
   //Initialize peripherals and etc.
   ....

   //Create tasks
   KCreateTask(&KeysTask);    //TID=1, prio=5
   KCreateTask(&PotsTask);    //TID=2, prio=2
   KCreateTask(&MeterTask);   //TID=3, prio=3
   KCreateTask(&LCDTask);     //TID=4, prio=1
   KCreateTask(&VFOTask);     //TID=5, prio=7
   KCreateTask(&KeyerTask);   //TID=6, prio=10

   //Set up initial events state
   KSetEvents(SPI_READY);

   //Run the kernel 
   StartOS();
}
      

Task Management

Creating Normal Tasks

First of all you should declare and implement your task.

SOS offers simple macros to make it easy to create a task.

For declaration:

DECLARE_TASK(TaskName)

For implementation:

TASK(TaskName,TaskStack,Priority){ /*your task*/ }

Note: your task should never end up. If you want to terminate a task call TerminateTask rather then return from the task function.

Next step is calling CreateTask to put your task into task list and activate it (if the task has the nesessary priority).

Note: If you want to create task before calling StartOS you should use KCreateTask instead of CreateTask

There are two functions and two macroses for creating new task:

tid_t KCreateTaskExt(void (*entry)(void),uint16_t* stack,uint8_t priority);
tid_t __attribute__((naked)) CreateTaskExt(void (*entry)(void),uint16_t* stack,uint8_t priority);

#define CreateTask(tdb)CreateTaskExt((tdb)->entry,(tdb)->stack,(tdb)->priority)
#define KCreateTask(tdb) KCreateTaskExt((tdb)->entry,(tdb)->stack,(tdb)->priority)

Creating Idle Task

Your project should have the Idle task, i.e. task that is always ready to go. The system will execute it when nothing more useful is ready to run.

The Idle task has fixed (and lowest) priority level 0.

There is a special macro for implementing Idle task:

#define IDLE(stack) TASK(Idle,stack,0)

Note: You do not need to declare Idle task. It has been already declared in the OS.h file. Also you do not need to call any CreateTask function for the Idle task. It will be created automatically after calling StartOS.

Interrupt Service Routines

You can use two types of interrupt service routines (ISRs).

The first are the ordinary ISRs you have used in your projects without using SOS. In this case you should not call any OS functions.

The second are more useful. In this case you have the SOSsyntax of an ISR:

osinterrupt(XXX_VECTOR) XXX_ISR(void)

   ENTERINT();
   //Your ISR code.
   //You can call OS functions here.
   //But remember YOU ARE IN THE KERNEL MODE HERE (see below)
   LEAVE();
}

Marco ENTERINT() will switch the system into special kernel mode. In the kernel mode interrupts are disabled and stack is switched to the kernel stack. You can use OS functions which begin with K (e.g. KSetEvents, KReshedule and etc.). These are designed specially to be called from kernel mode. (They do not switch tasks immediately, rather then correct internal OS data structures. Task switching will be made upon returning from kernel mode). LEAVE() macro switchs the system from kernel to normal mode. If necessary it also calls sheduler, which switchs tasks. Consult Remarks sections in function description on using it in interrupt handlers.


KReschedule

KReschedule sets a flag to reschedule tasks. It is defined in Kernel.h

#define KReschedule() ({ extern uint8_t Reschedule; Reschedule=1; })

Parameters

None.

Remarks

Generally you do need to call this macro, since SOS kernel will set the rescheduling flag when needed.

Examples

//Use this function to gain control for 
//the next task with the same priority
void __attricute__((naked)) Yield(void)
{
   ENTER();       //Enter kernel mode
   KReshedule();  //Set the rescheduling flag
   LEAVE();       //Leave kernel mode and run scheduler
}

KCreateTaskExt

The KCreateTaskExt function creates new task to execute. 

tid_t KCreateTaskExt(
void (*entry)(void),
uint16_t* stack,
uint8_t priority
);

Parameters

entry
[in] pointer to the task entry function.
stack
[in] pointer to the top of task stack.
priority
[in] task priority (0-255). 0 idle priority, 1 lowest, , 255 highest.

Return Values

If the function succeeds, the return value is a task identifier to the new task. If the function fails, the return value is K_ERR (0xFF).

Remarks

This function prevents switching tasks, so use it before starting kernel (before calling StartOS) or from the interrupt handlers (tasks will be rescheduled on the return from interrupt). For general purposes use CreateTaskExt function.

Examples


CreateTaskExt

The CreateTaskExt function creates new task to execute.

tid_t CreateTaskExt(
void (*entry)(void),
uint16_t* stack,
uint8_t priority
);

Parameters

entry
[in] pointer to the task entry function.
stack
[in] pointer to the top of task stack.
priority
[in] task priority (0-255). 0 idle priority, 1 lowest, , 255 highest.

Return Values

If the function succeeds, the return value is a task identifier to the new task. If the function fails, the return value is K_ERR (0xFF).

Remarks

This function calling can result in switching tasks, so do not use it before starting kernel (before calling StartOS) or from the interrupt handlers. For those purposes use KCreateTaskExt function.

Examples


KTerminateTask

The KTerminateTask function terminates a task. 

void KTerminateTask(
tid_t tid
);

Parameters

tid
[in] task identifier of task to terminate.

Return Values

The funtion does not return a value.

Remarks

This function prevents switching tasks, so use it in the interrupt handlers (tasks will be rescheduled on the return from interrupt). For general purposes use TerminateTask function.

Examples


TerminateTask

The TerminateTask function terminates a task. 

void TerminateTask(
tid_t tid
);

Parameters

tid
[in] task identifier of task to terminate.

Return Values

The funtion does not return a value.

Remarks

This function calling can result in switching tasks, so do not use it before starting kernel (before calling StartOS) or from the interrupt handlers. For those purposes use KTerminateTask function.

Examples

uint16_t KeyerStack[32];
TASK(KeyerTask,KeyerStack,10) 
{
   for(;;)
   {
      uint32_t e=WaitEvents(TERMINATE|DOSOMETHING,WAIT_ANY);
      if(e==TERMINATE)TerminateTask(GetTID()); //Terminate task if TERMINATE event is set
      //Do something here
      ......
   }
}      
      

KSuspendTask

The KSuspendTask function suspends execution of the task. 

void KSuspendTask(
tid_t tid
);

Parameters

tid
[in] task identifier of task to suspend.

Return Values

The funtion does not return a value.

Remarks

The execution of the task is suspended until another task calls ResumeTask or interrupt handler calls KResumeTask function.

This function prevents switching tasks, so use it in the interrupt handlers (tasks will be rescheduled on the return from interrupt). For general purposes use SuspendTask function.

Examples

int main(void)
{ 
   //Initialize peripherals and etc.
   ....

   //Create tasks
   KSuspendTask(KCreateTask(&PotsTask)); //This task will be suspended upon system startup
   ....

   //Run the kernel 
   StartOS();
}

SuspendTask

The SuspendTask function suspends execution of the task. 

void SuspendTask(
tid_t tid
);

Parameters

tid
[in] task identifier of task to suspend.

Return Values

The funtion does not return a value.

Remarks

The execution of task is suspended until another task calls ResumeTask or interrupt handler calls KResumeTask function.

This function call results in switching tasks, so do not use it before starting kernel (before calling StartOS) or from the interrupt handlers. For those purposes use KSuspendTask function.

Examples

   ....
   SuspendTask(GetTID());//Suspend self
   ....

KResumeTask

The KResumeTask function resumes execution of the task. 

void KResumeTask(
tid_t tid
);

Parameters

tid
[in] task identifier of task to resume.

Return Values

The funtion does not return a value.

Remarks

This function prevents switching tasks, so use it in the interrupt handlers (tasks will be rescheduled on the return from interrupt). For general purposes use ResumeTask function.

Examples


ResumeTask

The ResumeTask function resumes execution of the task. 

void ResumeTask(
tid_t tid
);

Parameters

tid
[in] task identifier of task to resume.

Return Values

The funtion does not return a value.

Remarks

This function call can result in switching tasks, so do not use it before starting the kernel (before calling StartOS) or from the interrupt handlers. For those purposes use KResumeTask function.

Examples

   ....
   ResumeTask(TASK1_TID); //Resume TASK1 task
   ....

KSetTaskPriority

The KSetTaskPriority function sets the priority value for the specified task. 

void KSetTaskPriority(
tid_t tid,
uint8_t priority

);

Parameters

tid
[in] task identifier of task to terminate.
priority
[in] task priority (0-255). 0 idle priority, 1 lowest, , 255 highest.

Return Values

The funtion does not return a value.

Remarks

This function prevents task switching, so use it in the interrupt handlers (tasks will be rescheduled on the return from interrupt). For general purposes use SetTaskPriority function.

Examples


SetTaskPriority

The SetTaskPriority function sets the priority value for the specified task. 

void SetTaskPriority(
tid_t tid,
uint8_t priority

);

Parameters

tid
[in] task identifier of task to terminate.
priority
[in] task priority (0-255). 0 idle priority, 1 lowest, , 255 highest.

Return Values

The funtion does not return a value.

Remarks

This function call can result in switching tasks, so do not use it before starting kernel (before calling StartOS) or from the interrupt handlers. For those purposes use KSetTaskPriority function.

Examples

   uint8_t p=GetTaskPriority(GetTID()); //Get current task priority
   SetTaskPriority(GetTID(),10);        //Rise task priority
   ....                                 //Execute importand part of code
   SetTaskPriority(GetTID(),p);         //Restore priority

GetTaskPriority

The GetTaskPriority function retrieves the priority value for the specified task.

uint8_t GetTaskPriority(
tid_t tid
);

Parameters

tid
[in] task identifier.

Return Values

The return value is the task's priority level. (0 idle priority, 1 lowest, , 255 highest.)

Remarks

This function is safe to call from interrupt handlers.

Examples

See SetTaskPriority for sample code.

GetTID

The GetTID function returns the Task IDentifier.

tid_t GetTID(
);

Return Values

The return value is the Task IDentifier of the current task.

Remarks

GetTID is actually a macro with zero overhead.

Examples

See SetTaskPriority for sample code.

Critical sections

A critical section is a synchronization object whose state is set to signaled when it is not owned by any task, and nonsignaled when it is owned. Critical section object can be owned by only one task at a time, which makes it useful for protecting a shared resource from simultaneous access. Threads will obtain ownership of the critical section according to their priority (e.g. when the critical section is released and there are several tasks waiting for its freeing, the ownership will be granted to the highest priority task).

The task is responsible for allocating the memory used by a critical section (fortunatily sizeof(cs_t)==1  :).

A task uses the EnterCS function to request ownership of a critical section. It uses the LeaveCS function to release ownership of a critical section. If the critical section object is currently owned by another thread, EnterCS waits indefinitely for ownership.

Once a thread owns a critical section, it can make additional calls to EnterCS without blocking its execution. This prevents a thread from deadlocking itself while waiting for a critical section that it already owns. To release its ownership, the thread must call LeaveCS once for each time that it entered the critical section.

You can have unlimited number of the critical sections (actually it is limited by the available memory), but due to RAM economy critical sections occupied only 1 byte of the RAM. I think that 15 normal and 1 idle task will be sufficient for the most msp430 projects, so critical sections can be applyed only to first 16 tasks and may have 16 levels in depth.


EnterCS

The EnterCS function waits for ownership of the specified critical section object. The function returns when the calling thread is granted ownership.

void EnterCS(
cs_t* cs
);

Parameters

cs
[in/out] Pointer to the critical section object..

Return Values

The funtion does not return a value.

Remarks

This function call can result in switching tasks, so do not use it before starting kernel (before calling StartOS) or from the interrupt handlers. Due to RAM economy I suggest that 15 normal and 1 idle task will be sufficient for msp430 projects, so critical sections can be applyed only to first 16 task and may have 16 levels in depth.

Examples

cs_t lcdcs=0; //Allocate critical section

void LCDDrawChar(char ch)
{
   EnterCS(&lcdcs);  //Enter critical section
   ....              //Draw char ch on the LCD (non reentrant piece of code)
   LeaveCS(&lcdcs);  //Leave critical section
}

uint16_t Task1Stack[50];
TASK(Task1,Task1Stack,1)
{
   for(;;)
   {
      ....
      LCDDrawChar('1');
      ....
   }
}

uint16_t Task2Stack[50];
TASK(Task2,Task2Stack,1)
{
   for(;;)
   {
      ....
      LCDDrawChar('2');
      ....
   }
}

LeaveCS

The LeaveCS function releases ownership of the specified critical section object. 

void LeaveCS(
cs_t* cs
);

Parameters

cs
[in/out] Pointer to the critical section object..

Return Values

The funtion does not return a value.

Remarks

This function call can result in switching tasks, so do not use it before starting kernel (before calling StartOS) or from the interrupt handlers. Due to RAM economy I suggest that 15 normal and 1 idle task will be sufficient for msp430 projects, so critical sections can be applyed only to first 16 task and may have 16 levels in depth.

Examples

See EnterCS for sample code.

Events

An event object is a synchronization object whose state can be explicitly set to signaled by use of the KSetEvent or SetEvent function.

The event is useful in sending a signal to a task indicating that a particular event has occurred. For example, an interrupt handler may notify task that data is ready to be processed. It sets a specified event to the signaled state when the interrupt occurs A single task can specify different events, then use WaitEvents function to wait for the state of any one (or all) of the events to be signaled. Also you can use CheckEvents to check particular event(s) state.

Due to RAM limitations SOS incoorporates only 32 events. (Note: timer module will occupy up to 7 events when used.) All events are global objects.

Each event is represented as 32 bit mask (with one bit set). It is very convient, because you can combine different events by simple "OR-ing" them:

WaitEvents(EVENT_1|EVENT_2,WAIT_ALL); //Waits for BOTH events
WaitEvents(EVENT_1|EVENT_2,WAIT_ANY); //Waits for ANY event

Usually events ones allocated are used from program start to the end. That is why SOS does not incoorporates dynamic events creation and deleting. You should arrange all events manually (i.e. assign unique bit number to each event in your program). This may looks like:

#define EVENT_1 0x00000080
#define EVENT_2 0x00000100

Note:The timer module occupys up to 7 lower bits when used.

There are the two types of events in SOS:
Event type Description
Manual reset event An event whose state remains signaled until it is explicitly reset to nonsignaled by the ResetEvents function. While it is signaled, any number of waiting tasks, can be released.
Auto reset event An event whose state remains signaled until a single waiting task is released, then the system automatically sets the state to nonsignaled. If no tasks are waiting, the event's state remains signaled.


KSetEvents

The KSetEvents function sets the state of the specified event(s) to signaled. 

void KSetEvents(
uint32_t mask
);

Parameters

mask
[in] Event(s) mask whoose state will be set to signaled.

Return Values

The funtion does not return a value.

Remarks

This function prevents switching tasks, so use it in the interrupt handlers (tasks will be resheduled on the return from interrupt). For general purposes use SetEvent function.

Examples

osinterrupt(PORT2_VECTOR) Port2IntHandler(void)
{
   ENTERINT();
   P2IFG=0;
   KSetEvents(VFO_CHANGED|REDRAW_MULTY_CLR); //Inform tasks about event
   LEAVE();
}

SetEvents

The SetEvents function sets the state of the specified event(s) to signaled. 

void SetEvents(
uint32_t mask
);

Parameters

mask
[in] Event(s) mask whoose state will be set to signaled.

Return Values

The funtion does not return a value.

Remarks

This function call can result in switching tasks, so do not use it before starting kernel (before calling StartOS) or from the interrupt handlers. Use KSetEvent function in the interrupt handlers.

Examples

   switch(key)
   {
      ....      
      case KMETER: //Meter
         meter=(meter+1)%3;
         SetEvents(REDRAW_LINE3);  //Inform display updating task, that we need to redraw part of the display
         break;
      ....
   }

ResetEvents, KResetEvents

The ResetEvents and KResetEvents functions set the state of the specified event(s) to nonsignaled. 

void ResetEvents(
uint32_t mask
);

void KResetEvents(
uint32_t mask
);

Parameters

mask
[in] Event(s) mask whoose state will be set to nonsignaled.

Return Values

The funtions do not return a value.

Remarks

The ResetEvent function disables interrupts during events setting, so use it in the common code. This function does not cause task switching.

The KResetEvent is actually inline assembly macros, so use it in the kernel mode code (e.g. interrupt handlers), when interrupts are already disabled.

Examples

  for(;;)
   {
      uint32_t e=WaitEvents(EVENT1|EVENT2,WAIT_ANY);
      //Process events
      ....
      //Check if we need more cycles ?
      e=More();
      ResetEvents(e); //Reset events if we have done all the necessary thing
   }

SetAutoEvents, KSetAutoEvents

The SetAutoEvents and KSetAutoEvents functions set the type of the specified event(s) to auto reset type. 

void SetAutoEvents(
uint32_t a
);

void KSetAutoEvents(
uint32_t a
);

Parameters

a
[in] Event(s) mask whoose type will be set to auto reset.

Return Values

The funtions do not return a value.

Remarks

The SetAutoEvents function disables interrupts during events type setting, so use it in the common code. This function does not cause task switching.

The KSetAutoEvents is actually inline assembly macros, so use it in the kernel mode code (e.g. interrupt handlers), when interrupts are already disabled.

If you do need to change event(s) type during program execution you can use another method to set up the desired events types. The EVENTS_MODE constant in the Config.h file specifies event type upon system startup. Set the bit if you want the corresponding event to be auto reset type, reset otherwise.

Examples


SetManualEvents, KSetManualEvents

The SetManualEvents and KSetManualEvents functions set the type of the specified event(s) to manual reset type. 

void SetManualEvents(
uint32_t m
);

void KSetManualEvents(
uint32_t m
);

Parameters

m
[in] Event(s) mask whoose type will be set to manual reset.

Return Values

The funtions do not return a value.

Remarks

The SetManualEvents function disables interrupts during events type setting, so use it in the common code. This function does not cause task switching.

The KSetManualEvents is actually inline assembly macros, so use it in the kernel mode code (e.g. interrupt handlers), when interrupts are already disabled.

If you do need to change event(s) type during program execution you can use another method to set up the desired events types. The EVENTS_MODE constant in the Config.h file specifies event type upon system startup. Set the bit if you want the corresponding event to be auto reset type, reset otherwise.

Examples

See StartOS for sample code.

WaitEvents

The WaitEvents function waits until either any one or all (according to the mode flag) of the specified events are in the signaled state.

uint32_t WaitEvents(
uint32_t mask,
uint8_t mode
);

Parameters

mask
[in] Event(s) mask whoose state will be checked.
mode
[in] Specifies the wait type. If WAIT_ALL constant is used, the function returns when the state of all events in the mask parameter is signaled. If WAIT_ANY, the function returns when the state of any one of the events is set to signaled. In the latter case, the return value indicates the event whose state caused the function to return.

Return Values

If mode parameter is WAIT_ALL then the return value the same as mask parameter.

If mode parameter is WAIT_ANY then the return value indicates the event that caused the function to return.

Remarks

WaitEvents function determines whether the wait criteria have been met. If the criteria have not been met, the calling task enters the wait state. It uses no processor time while waiting for the criteria to be met.

When mode set to WAIT_ALL, the function's wait operation is completed only when the states of all events have been set to signaled. The function does not modify the states of the specified events until the states of all eventss have been set to signaled.

The function modifies the state of the autoreset type events. Modification occurs only for the event or events whose signaled state caused the function to return.

This function calling can result in switching tasks, so do not use it before starting kernel (before calling StartOS) or from the interrupt handlers.

Examples

See ResetEvents for sample code.

TryEvents

The TryEvents function checks the state of the specified event(s).

uint32_t TryEvents(
uint32_t mask,
uint8_t mode
);

Parameters

mask
[in] Event(s) mask whoose state will be checked.
mode
[in] Specifies how the the events in the mask are combined. See WaitEvents function description for more information.

Return Values

If the specified criteria have not been met the funtion returns zero. Otherwise the behaviour is the same as WaitEvents function.

Remarks

The TryEvents function checks for the current events state. If the specified criteria have been met, the function behaves exactly as WaitEvents function does, otherwise it returns zero and does nothing. This function does not cause task switching.

Examples


Timers

Timers are used to satisfy various tasks' timing requirements.

Timer module implementation in SOS is a bit tricky. Timers implemetation uses Timer_B hardware timer module. (So for msp430f14x up to seven timers available simultaneously). I used such unusual scheme because of minimal use of RAM/ROM/CPU resources goal. Timer module implements dynamically allocated timers, so you should never have any problems associated with the limited timers number. (I do not think that application that uses more then seven timers simultaneously is properly designed, except of some very specific cases).

Each time the specified interval (or time-out value) for a timer elapses, the system sets the event associated with the timer. So the timer module operation is completely independent from the SOS kernel.

A task uses the CreateTimer function to create new timer. You can create syncronization and notification timers by specifying the appropriate flags. Syncronization timers use auto-reset events, while notification - manual-reset events. You can also create "suicidal" timer; it will kill itself after the specified timeout elapsed. The timer can set the corresponding event only once or periodically (see CreateTimer function description for more information).

The task should call the KillTimer function for each created timer (except of "suicidal" timers) to allow reuse of timer resource.

All timeout-values are specified in the units dependent from the particular hardware in use (SOS assumes that Timer_B module is clocked from the 32768 Hz source). Use ms(t) macro to specify timeout-values in milliseconds.

Example


   ....
   Sleep(ms(100)); //Suspend the execution of the current task by 100 ms
   ....
         


CreateTimer

The CreateTimer function creates a timer with the specified time-out value and flags.

timer_t CreateTimer(
uint8_t tt,
uint16_t t,
uint16_t cyc,
);

Parameters

tt
[in] Timer type.
t
[in] Specifies initial timeout value.
cyc
[in] Specifies timeout value for periodical timer (if you need to set the event only once pass 0 here).

Return Values

The funtion returns a bitmask of the event associated with this timer (this value is the timer handle also). If the function fails (i.e. all timers have been already allocated) the function returns K_ERR (0xFF) value.

Remarks

You can create various timers by specifying the appropriate flags.
FlagDescription
TT_SYNC Use to create syncronization timer. It will use auto-reset event.
TT_NOTIFY Use to create notifycation timer. It will use manual-reset event.
TT_SUICIDAL The timer will kill itself after the specified timeout elapsed.
TT_PULSE The combination of TT_SYNC|TT_SUICIDAL.
If the cyc parameter is not 0 the timer will set the associated event every cyc period. Such timer can not be "suicidal".

The task should call the KillTimer function for each created timer (except of "suicidal" timers) to allow reuse of timer resource.

Examples

The Sleep function is implemented using just two SOS calls:
void Sleep(uint16_t t)
{
  WaitEvents(CreateTimer(TT_PULSE,t,0),WAIT_ALL);
}
         

SetTimer

The SetTimer function sets new timing parameter for the previously created timer, specified by timer parameter.

void SetTimer(
timer_t timer,
uint16_t t,
uint16_t cyc,
);

Parameters

timer
[in] Timer handle (the value returned by the CreateTimer function call).
t
[in] Specifies initial timeout value.
cyc
[in] Specifies timeout value for periodical timer (if you need to set the event only once pass 0 here).

Return Values

The funtion does not return a value.

Remarks

See CreateTimer function description for more information.

Examples


KillTimer

The KillTimer function destroys the specified timer.

void KillTimer(
timer_t timer
);

Parameters

timer
[in] Timer handle (the value returned by the CreateTimer function call).

Return Values

The funtion does not return a value.

Remarks

Do not use this function for "suicidal" timers.

Examples


Sleep

The Sleep function suspends the execution of the current task for the specified interval.

void Sleep(
uint16_t t
);

Parameters

t
[in] Specifies the time, in milliseconds, for which to suspend execution.

Return Values

The funtion does not return a value.

Remarks

See CreateTimer function description for Sleep source.

Examples


   ....
   Sleep(ms(100)); //Suspend the execution of the current task by 100 ms
   ....
         


Home| Contents| Previous| Next
Designed by Oleg Skydan
Send mail