Appendix B: RTOS Notes

Jump to: navigation, search

Table of Contents


RTOS Application Note for uC/OS

uC/OS is a simple preemptive kernel. It does not have any way to perform dynamic memory allocation. It does contain counting semaphores, message queues and mail boxes as well a preemptive scheduling. With your CD-ROM you will have received various ports of uC/OS for different processors. If your processor is not included in the ports of uC/OS that Elmic Systems provides, then you might want to visit the uC/OS web site at http://www.ucos-ii.com/


Predefined Settings in <trsystem.h>

There are processor specific settings for uC/OS in <trsystem.h>. They will setup uC/OS the same way that it was used in the Elmic test facilities for various processors.

  • TM_KERNEL_UCOS_X86 is used for the Intel 80x86 family in real mode
  • TM_KERNEL_UCOS_PPC is used for the PowerPC (specifically the MPC860 for Motorola)
  • TM_KERNEL_UCOS_CPU32 is used for the 32 Bit 68K family of processors from Motorola.

If you define one of the above in <trsystem.h>, the processor, inline assembly, task, and memory allocation definitions will be made for you. You can modify the uC/OS processor specific area of <trsystem.h> to suite your applications needs.


Initialization

uC/OS does not require any special initialization therefore the tfKernelInitialize() and tfKernelUnInitialize() should be stub functions.

Memory Allocation and Free

Because uC/OS does not contain any method for performing dynamic memory allocation, the Treck Simple Heap must be used.

In <trsystem.h> be sure to define the following

#define TM_USE_SHEAP
#define TM_SHEAP_SIZE size

Where size is the size in bytes of the simple heap area. Most simple applications will need about 32K bytes of RAM. To define a 32K SHEAP you simply do:

#define TM_SHEAP_SIZE 32L*1024L


Note Note: The size value is defined as a long constant.


Because the simple heap is used, there is no need to create the functions tfKernelMalloc() and tfKernelFree().


Critical Section Handling

uC/OS does have critical section handling via two macros:

  • OS_ENTER_CRITICAL
  • OS_EXIT_CRITICAL

For tfKernelSetCritical() you simply call OS_ENTER_CRITICAL.

For tfKernelReleaseCritical() you simply call OS_EXIT_CRITICAL.

It is always best to use inline assembly for critical section handling by setting the macros tm_kernel_set_critical and tm_kernel_release_critical for your processor in <trsystem.h>. You will find preexisting critical section macros for specific processors and compiler combinations in <trmacro.h>.


Error Logging and Warning Information Logging

uC/OS does not have any error reporting mechanism. Since this is the case, tfKernelError() and tfKernelWarning() should be written to write out to a display, serial port or a predefined area of memory that can be examined via a debugger. In the case of tfKernelError(), it should cause a CPU reset after displaying the error message.


Task Suspend and Resume

uC/OS does implement counting semaphores, so all we need to do here is create a mapping between Treck semaphores and uC/OS semaphores. uC/OS semaphores are pointers to an OS_EVENT type and should be stored in the genVoidParmPtr portion of the ttUserGenericUnion data type. To create a counting semaphore, we simply call OSSemCreate(0). This creates a counting semaphore that is initialized to zero. All counting semaphores that Treck uses must be initialized to zero. To pend on a counting semaphore, we simply call OSSemPend() with a semaphore that was created. To post on a counting semaphore, we simply call OSSemPost() with a semaphore that was created. tfKernelTaskYield() does not need to do anything with uC/OS because uC/OS is fully preemptive and every task has a unique priority. This function should just be a stub function.


ISR Interface

uC/OS does not have anyway to install Interrupt Service Routines. We have put a routine in the uC/OS ports that we supply called OSInstallISR(). This function is NOT supplied with uC/OS. This function is very processor specific. If you are not using a Treck port of uC/OS, but are using a Treck device driver, then you should add this function for your processor.


Note Note: All ISR routines that call Treck or uC/OS must call OSIntEnter() upon entry and OSIntExit() upon leaving.


Since uC/OS does not have event flags and it is okay to post on a counting semaphore from an ISR, the functions tfKernelCreateEvent(), tfKernelPendEvent(), and tfKernelIsrPostEvent() use uOS counting semaphores. You can use the same code as you used in tfKernelCreateCountSem(), tfKernelPendCountSem(), and tfKernelPostCountSem().

Hooking up the Timer

The best way to hook a timer to Treck from uC/OS is to create a separate timer task. This task simply calls tfTimerUpdate(), tfTimerExecute() then OSTimeDelay(1). The OSTimeDelay(1) causes us to delay for one RTOS clock tick. This task should be the highest priority of any task calling the Treck stack.


Task Usage

All uC/OS tasks are different priorities. With uC/OS you should have one timer task, one receive task per interface and your application tasks. For best performance, you should create one receive task per device that calls tfWaitReceiveInterface() and tfRecvInterface(). The receive tasks should have a higher priority than the application tasks but lower than the timer task.


RTOS Application Note for AMX-86

AMX-86 is a Commercial-Off-The-Shelf (COTS) real-time operating system from Kadak Products Ltd. It contains counting semaphores and memory allocation. AMX is broken up into two variants. These are commonly referred to as the "AJ Kernel" and "CJ Kernel". AMX-86 is the AJ kernel (thus all the calls to the operating system are prefixed with "aj".


Initialization

AMX-86 does not require any special initialization therefore the tfKernelInitialize() should be a stub function.


Memory Allocation and Free

AMX-86 includes dynamic memory allocation via the calls ajmget() and ajmfre(). tfKernelMalloc() should call ajmget() to allocate a block of memory and tfKernelFree() should call ajmfre() to free a block of memory.


Critical Section Handling

AMX-86 is used with the Intel x86 processor family. For both Microsoft and Borland compilers, we have included inline assembly in <trmacro.h> to set and release critical sections. If you are using another compiler, you can define the macros tm_kernel_set_critical and tm_kernel_release_critical in <trsystem.h> to be the appropriate inline assembly for your compiler for setting and releasing critical sections.


Error Logging and Warning Information Logging

AMX-86 does not have any error reporting mechanism. Since this is the case, tfKernelError() and tfKernelWarning() should be written to write out to a display, serial port or a predefined area of memory that can be examined via a debugger. In the case of tfKernelError(), it should cause a CPU reset after displaying the error message.


Task Suspend and Resume

AMX-86 does implement counting semaphores, so all we need to do here is create a mapping between Treck semaphores and AMX-86 semaphores. AMX-86 semaphores are pointers to an AMX_ID type and should be stored in the genVoidParmPtr portion of the ttUserGenericUnion data type.

To create a counting semaphore, we simply call ajsmcre(). This creates a counting semaphore. Be sure to initialize to zero. All counting semaphores that Treck uses must be initialized to zero.

To pend on a counting semaphore, we simply call ajsmwat() with a semaphore that was created. To post on a counting semaphore, we simply call ajsmsig() with a semaphore that was created.

tfKernelTaskYield() does not need to do anything with AMX-86 because AMX-86 is fully preemptive and every task has a unique priority. This function should just be a stub function.


ISR Interface

AMX-86 has a method to install ISRs. in AMX-86 these are called Interrupt Service Procedures (ISP). Since we normally use "C" code for the ISR code, we need to setup our tfKernelInstallIsrHandler() to call ajispm() to create a wrapper ISP that will call our "C" function. Then we call ajivtw() to write the address of the wrapper that calls our "C" code to the interrupt vector table.

With AMX-86 it is okay to post on a counting semaphore from an ISR, the functions tfKernelCreateEvent(), tfKernelPendEvent(), and tfKernelIsrPostEvent() use uC/OS counting semaphores. You can use the same code as you used in tfKernelCreateCountSem(), tfKernelPendCountSem(), and tfKernelPostCountSem().

Hooking up the Timer

The best way to hook a timer to Treck from AMX-86 is to create a separate timer task. This task simply calls tfTimerUpdate(), tfTimerExecute() then ajwatm(). The ajwatm takes as it parameter the number of milliseconds to wait. This task should be the highest priority of any task calling the Treck stack.


Task Usage

All AMX-86 tasks are different priorities. With AMX-86 you should have one timer task, one receive task per interface and your application tasks. For best performance, you should create one receive task per device that calls tfWaitReceiveInterface() and tfRecvInterface(). The receive tasks should have a higher priority than the application tasks but lower than the timer task.


AMX-86 System Configuration Module

With the system configuration tool for AMX-86 you will need to define the maximum number of semaphores and tasks. The maximum number of tasks is calculated from the following formula:

MaxTasks = (ReceiveTasks) + (SendCompleteTasks) + (XmitTasks) + (TimerTask) + (ApplicationTasks)

The absolute maximum number of semaphores needed by the is calculated via the following formula:

MaxSemaphores = (NumberInterfaces * NumberTasksPerInterface) + MaxTasks.

NumberTasksPerInterface is the number of tasks that are dedicated to interface processing. The maximum this can be is 3:

NumberTasksPerInterface = (RecvTask) + (SendCompleteTask) + (XmitTask)

This is controlled via #defines in <trsystem.h>.

Maximum Stack Size should not exceed 2048. Selecting too small of a stack can lead to unpredictable behavior.

Treck does not require any AMX timers since we are using a separate Timer task.