Software Hardware
8.03 OS_CPU.H
OS_CPU.H
contains processor a nd implementation specific#defines
constants, macros, andtypedefs
. The general layout ofOS_CPU.H
is shown in listing 8.1:#ifdef OS_CPU_GLOBALS
#define OS_CPU_EXT
#else
#define OS_CPU_EXT extern
#endif
/*
*********************************************************************************************************
* DATA TYPES
* (Compiler Specific)
*********************************************************************************************************
*/
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U; /* Unsigned 8 bit quantity */ (1) typedef signed char INT8S; /* Signed 8 bit quantity */
typedef unsigned int INT16U; /* Unsigned 16 bit quantity */
typedef signed int INT16S; /* Signed 16 bit quantity */
typedef unsigned long INT32U; /* Unsigned 32 bit quantity */
typedef signed long INT32S; /* Signed 32 bit quantity */
typedef float FP32; /* Single precision floating point */ (2) typedef double FP64; /* Double precision floating point */
typedef unsigned int OS_STK; /* Each stack entry is 16-bit wide */
/*
*********************************************************************************************************
* Processor Specifics
*********************************************************************************************************
*/
#define OS_ENTER_CRITICAL() ??? /* Disable interrupts */ (3)
#define OS_EXIT_CRITICAL() ??? /* Enable interrupts */
#define OS_STK_GROWTH 1 /* Define stack growth: 1 = Down, 0 = Up */ (4)
#define OS_TASK_SW() ??? (5)
Listing 8.1, Contents of OS_CPU.H
8.03.01 OS_CPU.H, Compiler Specific Data Types
Because different microprocessors have different word length, the port of µC/OS-II includes a series of type definitions that ensures portability. Specifically, µC/OS-II’s code never makes use of C’s
short
,int
and,long
data types because they are inherently non-portable. Instead, I defined integer data types that are both portable and intuitive L8.1(1). Also, for convenience, I have included floating-point data types L8.1(2) even though µC/OS-II doesn’t make use of floating-point.The
INT16U
data type, for example, always represents a 16-bit unsigned integer. µC/OS-II and your application code can now assume that the range of values for variables declared with this type is from 0 to 65535. A µC/OS-II port to a 32-bit processor could mean that anINT16U
is actually declared as anunsigned short
instead of anunsigned int
. Where µC/OS-II is concerned, however, it still deals with anINT16U
.You must tell µC/OS-II the data type of a task’s stack. This is done by declaring the proper C data type for
OS_STK
.If stack elements on your processor are 32-bit and your compiler documentation specify that an
int
is 32-bit then, you would declareOS_STK
as being of typeunsigned int
. All task stacks MUST be declared usingOS_STK
as its data type.
All you have to do is to consult the compiler’s manual and find the standard C data types that corresponds to the types expected by µC/OS-II.
8.03.02 OS_CPU.H, OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL()
µC/OS-II like all real-time kernels need to disable interrupts in order to access critical sections of code, and re-enable interrupts when done. This allows µC/OS-II to protect critical code from being entered simultaneously from either multiple tasks or Interrupt Service Routines (ISRs). The interrupt disable time is one of the most important specification that a real-time kernel vendor can provide because it affects the responsiveness of your system to real-time events.
µC/OS-II tries to keep the interrupt disable time to a minimum but, with µC/OS-II, interrupt disable time is largely dependent on the processor architecture, and the quality of the code generated by the compiler. Every processor generally provide instructions to disable/enable interrupts a nd your C compiler must have a mechanism to perform these operations directly from C. Some compilers will allow you to insert in-line assembly language statements in your C source code. This makes it quite easy to insert processor instructions to enable and disable interrupts. Other compilers will actually contain language extensions to enable and disable interrupts directly from C. To hide the implementation method chosen by the compiler manufacturer, µC/OS-II defines two macros to disable and enable interrupts:
OS_ENTER_CRITICAL()
andOS_EXIT_CRITICAL()
, respectively L8.1(3).µC/OS-II’s critical sections are wrapped with
OS_ENTER_CRITICAL()
andOS_EXIT_CRITICAL()
as shown below:
µC/OS-II Service Function {
OS_ENTER_CRITICAL();
/* µC/OS-II critical code section */
OS_EXIT_CRITICAL();
}
Method #1:
The first and simplest way to implement these two macros is to invoke the processor instruction to disable interrupts for
OS_ENTER_CRITICAL()
and the enable interrupts instruction forOS_EXIT_CRITICAL()
. There is, however, a little problem with this scenario. If you called the µC/OS-II function with interrupts disabled then, upon return from µC/OS-II, interrupts would be enabled! If you had interrupts disabled, you may have wanted them to be dis abled upon return from the µC/OS-II function. In this case, the above implementation would not be adequate.Method #2:
The second way to implement
OS_ENTER_CRITICAL()
is to save the interrupt disable status onto the stack and then, disable interrupts.OS_EXIT_CRITICAL()
would simply be implemented by restoring the interrupt status from the stack. Using this scheme, if you called a µC/OS-II service with either interrupts enabled or disabled then, the status would be preserved across the call. If you call a µC/OS-II service with interrupts disabled, you are potentially extending the interrupt latency of your application. Your application can useOS_ENTER_CRITICAL()
andOS_EXIT_CRITICAL()
to also protect critical sections of code. Be careful, however, be cause your application will ‘crash’if you have interrupts disabled before calling a service such asOSTimeDly()
. This will happen because the task will be suspended until time expires but, because interrupts are disabled, you would never service the tick interrupt! Obviously, all the PEND calls are also subject to this problem so, be careful. As a general rule, you should always call µC/OS-II services with interrupts enabled!The question is thus, which one is better? Well, that all depends on what yo u are willing to sacrifice. If you don’t care in your application whether interrupts are enabled after calling a µC/OS-II service then, you should opt for the first method because of performance. If you want to preserve the interrupt disable status across µC/OS-II service calls then obviously the second method is for you.
Just to give you an example, disabling interrupts on an Intel 80186 is done by executing the
STI
instructions and enabling interrupts is done by executing theCLI
instruction. You can thus implement the macros as follows:#define OS_ENTER_CRITICAL() asm CLI
#define OS_EXIT_CRITICAL() asm STI
Both the
CLI
andSTI
instructions execute in less that 2 clock cycles each on this processor (i.e. total of 4 cycles). To preserve the interrupt status you would need to implement the macros as follows:#define OS_ENTER_CRITICAL() asm PUSHF; CLI
#define OS_EXIT_CRITICAL() asm POPF
In this case,
OS_ENTER_CRITICAL()
would consume 12 clock cycles whileOS_EXIT_CRITICAL()
would use up another 8 clock cycles (i.e. a total of 20 cycles). Preserving the state of the interrupt disable status would thus take 16 clock cycles longer than simply disabling/enabling interrupts (at least on the 80186). Obviously, if you have a faster processor s uch as an Intel Pentium-II then, the difference would be minimal.
8.03.03 OS_CPU.H, OS_STK_GROWTH
The stack on most microprocessors and microcontrollers grows from high-memory to low-memory. There are, however, some processors that work the other way around. µC/OS-II has been designed to be able to handle either flavor. This is accomplished by specifying to µC/OS-II which way the stack grows through the configuration constant
OS_STK_GROWTH
L8.1(4) as shown below:Set
OS_STK_GROWTH
to 0 for Low to High memory stack growth.Set
OS_STK_GROWTH
to 1 for High to Low memory stack growth.8.03.04 OS_CPU.H, OS_TASK_SW()
OS_TASK_SW()
L8.1(5) is a macro that is invoked when µC/OS-II switches from a low-priority task to the highest-priority task.OS_TASK_SW()
is always called from task level code. Another mechanism,OSIntExit()
, is used to perform a context switch when an ISR makes a higher priority task ready for execution.A context switch simply consist of saving the processor registers on the stack of the task being suspended, and restoring the registers of the higher-priority task from its stack.
In µC/OS-II, the stack frame for a ready task always looks as if an interrupt has just occurred and all processor registers were saved onto it. In other words , all that µC/OS-II has to do to run a ready task is to restore all processor registers from the task’s stack and execute a return from interrupt. To switch context, you should implement
OS_TASK_SW()
sothat you simulate an interrupt. Most processors provide either software interrupt or
TRAP
instructions to accomplish this. The ISR or trap handler (also called the ‘exception handler’) MUST vector to the assembly language functionOSCtxSw()
(see section 8.04.02).For example, a port for an Intel or AMD 80x86 processor would use an
INT
instruction. The interrupt handler would need to vector to OSCtxSw(). A port for the Motorola 68HC11 processor would most likely use theSWI
instruction.Again, the
SWI
handler would beOSCtxSw()
. Finally, a port for a Motorola 680x0/CPU32 processor would probably use one of the 16TRAP
instructions. Of course, the selectedTRAP
handler would be none other thanOSCtxSw()
.There are some processors like the Zilog Z80 that do not provide a software interrupt mechanism. In this case, you would need to simulate the stack frame as closely to an interrupt stack frame as you can. In this case,
OS_TASK_SW()
would simply callOSCtxSw()
instead of vector to it. The Z80 is a processor that has been ported to µC/OS and thus would be portable to µC/OS-II.8.04 OS_CPU_A.ASM
A µC/OS-II port requires that you write four fairly simple assembly language functions: