eMOS – an embedded message passing real time OS
eMOS is a real time OS for embedded systems with a preemptive message passing kernel at its core. The message passing API provides a uniform model for supporting additional software stacks such as UART driver, File System, USB stack, TCP/IP and CAN library etc.
Design Principles
eMOS is designed for embedded microcontrollers. It can be used on low end 8 bits AVR microcontrollers with 8K of flash and 2K of RAM to 32 bits ARM devices with lots of memory. The microkernel API takes just a few K bytes and can be used by itself in minimal systems to provide basic multiasking services.
In designing the eMOS tasking model, we feel that a preemptive priority based round robin scheduler is the simplest model for users, allowing the natural “functions as tasks” style of writing programs. It places no restriction on how users may structure their code and users do not need to work around the tasking model. To minimize resource consumption, a separate kernel stack is used by the kernel calls, so that tasks would not duplicate the resource needed for by kernel calls. For the advance users, scheduling APIs provide the control needed to further minimize task scheduling and resource consumption.
For process synchronization and interprocess communication, we adopt the message passing semantics made popular by OS such as QNX. A set of 3 primitives provides a robust and fast solution to both synchronization and interprocess communication requirements. Priority inversion is handled automatically as well. For simpler needs, eMOS also provides semaphores.
As many embedded systems are used in low power situations, eMOS includes timer API and provides hooks to the idle task so that you may place the system in low power mode to conserve power, to be awaken by interrupts or timer events.
The general philosophy is to provide flexibility rather than optimizing the design for a particular niche (e.g. memory constraint devices). Thus eMOS uses dynamic memory allocation (via a best-fit always-merge allocator to lessen memory fragmentation) and places no arbitrary limits on the number of tasks or task priorities and allow dynamic task creation and deletion. This approach allows eMOS to scale up and down except to the most memory constrainted situations.
In theory, scheduling algorithm is f(n) where n is the number of tasks in the READY state and task allocation is affected by the memory allocator. However in practice, these should not be worse than other schemes in any meaningful way as the number of READY tasks are few and allocation is fast.
/* eMOS * Copyright (C) 2008 ImageCraft Creations Inc. All Right Reserved. * info@imagecraft.com http://www.imagecraft.com */ /* fucntion that returns a status: * status >= 0, function succeeded * 0 > status, function failed */ /* uKERNEL AND SCHEDULER API */ /* initialize eMOS, must be call first */ int eMOS_SysInit(void); /* start eMOS scheduler */ int eMOS_SysStart(void); /* arrange "func" to be called when the system is idle */ int eMOS_SysIdleHook(void (*func)(void)); /* return non-zero if there are runnable tasks */ int eMOS_isSysBusy(void); /* return non-zero if all other tasks are waiting */ int eMOS_isSysWait(void); /* create a task and return the process ID */ int eMOS_TaskCreate(void (*func)(void), unsigned stacksize, unsigned prio, unsigned hw_stacksize); /* give up processing time */ void eMOS_TaskYield(void); /* hibernate */ void eMOS_TaskHibernate(void); /* wake a task from hibernation, can be called from ISR */ int eMOS_TaskWakeup(void); /* kill a task */ int eMOS_TaskKill(int pid); /* temporarily disable the scheduler, any syscall will also re-enable it */ void eMOS_SchedOff(void); /* re-enable the scheduler */ void eMOS_SchedOn(void); /* MESSAGE PASSING API * if the buffer lengths do not match between the send/receive or send/reply calls, * then the shorter len is used */ /* send a message and wait until a reply */ int eMOS_MsgSend(void *sendbuf, int sendlen, void *reqbuf, int reqlen); /* (wait and) receive a message */ int eMOS_MsgReceive(void *recbuf, int reclen); /* reply to a request */ int eMOS_MsgReply(void *reqbuf, int reqlen); /* SEMAPHORE API */ /* create a semaphore */ int eMOS_SemCreate(void); /* lock a semaphore, wait if not available */ int eMOS_SemLock(int semid); /* unlock a semaphore */ int eMOS_SemUnlock(int semid); /* Timer, RTC, etc. */ /* sleep for X seconds */ void eMOS_Sleep(int secs); /* sleep for X milliseconds */ void eMOS_SleepMs(int msecs); /* set the RTC clock */ void eMOS_ClkPut(unsigned long time); /* get the RTC clock */ unsigned long eMOS_ClkGet(void); /* arrange "func" to be called for the system tick ISR */ void eMOS_SystemTickISRHook(void (*func)(void)); /* set the current time */ void eMOS_TimePut(unsigned long time); /* get the current time */ unsigned long eMOS_TimeGet(void); /* MEMORY MANAGEMENT API * Default allocator is best-fit always-merge */ /* set the system free space range */ void eMOS_FreeSpace(unsigned begin, unsigned end); /* allocate memory and zero out the content */ void *eMOS_MemAlloc(unsigned size); /* free memory */ void eMOS_MemFree(void * ptr); /* Ring Buffer UART */ /* initialize the COM port subsystem */ void eMOS_ComInit(void); /* open a COM port and set the ring buffer size */ int eMOS_ComOpen(int portn, int n); /* close a COM port */ int eMOS_ComClose(void); /* ??? */ eMOS_ComTerm(); /* read a character and wait if none is available */ int eMOS_ComRead(int com); /* write a character */ int eMOS_ComWrite(int c); /* ISR for putting a character into the output buffer */ void eMOS_ComISRPut(void); /* ISR for getting a character into the input buffer */ void eMOS_ComISRGet(void); /* read a string and wait if no input is available */ char *eMOS_ComGets(int com, char *buf, int size); /* write a string */ int eMOS_ComPuts(char *buf); /* return non-zero if input is available */ int eMOS_ComReady(int com);