Convert to ARM!


Back to A-R-T main page.


A MAJOR PORTING PROJECT - uC/OS (from 80186 to ARM)


By Mike Gorgonzola.

Marco Graziano, Jaime Smith, and Geary Choppoff converted the uC/OS real time kernel to ARM as an exercise to validate the ARM and the development environment under real time constraints, and to get an idea as to the ARM's relative performance. This effort led to the radical rewriting of the DEMON. uC/OS was originally ported to the MC68HC11, but in Jean Labrosse's book "uC/OS" the source code supplied is for the 80186. To use uC/OS, it is compiled separately and then linked into the user application to add real time functionality without have to re-invent it for each project. The current ARM uC/OS implementation should not be thought of as the ultimate version, but as a work in progress. The reader is urged to get the uC/OS book and study both the 80186 code and the ARM code available from the Resource Catalog or from VLSI.

uC/OS supports a small range of basic elements which are necessary for the operation of a real time environment. These are: Tasks, Semaphores, Messages, and Queues. Tasks are defined in a preset number of Task Control Blocks (TCBs) and the other three have a common data structure called an Event Control Block (Events) of which there is also pre-defined number. Interrupts are special tasks that are initiated by the hardware. They may or may not interact with other tasks. A VERY brief description of uC/OS features is in the uC/OS sidebar.

What To DO?

Like many programs of this type, uC/OS is a mixture of assembler and C. Of course the assembler portion needs to be reinvented. The "C" part of the code is also susceptible to change because of the severe architectural differences between the parts. The first thing to do to port a program from the 8086 (of which the 80186 is a family member) is to compare some of the salient features of the two CPUs. Note - converting uC/OS to the 80386 could just as painful, judging from Ed Nisley's gyrations over protected mode.

ITEM 80186 ARM
Word Size 16/8 Bits 32/8 Bits
Address Range
64KB offset +
1MB Segment
4Gbyte Linear
No. of Regs 8 + 4 seg regs 16 + 15 overlapping regs
Supervisor Modes None Yes and FIQ, IRQ, Abort too
Instruction size 1 to 7 bytes 4 bytes

Data structures are the first to be converted as they define the details of the programs that modify them. Because of the difficulty the ARM has with 16 bit variables, it is often better to lengthen them to 32 bits rather than go through the pain of jockying packed 16 bit numbers. All ARM pointers are also converted to 32 bits. ( 8086 far pointers have the same number of bits.) The first data structure to be converted is the task stack image. The 80186 task image was built to take advantage of a combination of POP ES, POPA, and IRET instructions. The ARM's task image uses the structure from a Load Multiple instruction in which most of the registers are restored in one instruction.

uC/OS Task Image that stored on the Process Stack

Description 80186(large mem) size ARM size Description
DATA AREA Offset of Data 16 R15-PC 32 Code Pointer
Segment of Data 16 R14-LR 32 R0-R15
CODE AREA Offset of Code 16 R12 32 restored by
Segment of Code 16 R11 32 LDMFD SP!,{R0-R12,LR,PC}
Status Word PSW 16 R10 32
CS:IP Restored IP by IRET(PC) 16 R9 32
CS 16 R8 32
Registers are Restored by POPA AX 16 R7 32
CX 16 R6 32
DX 16 R5 32
BX 16 R4 32
SP 16 R3 32
BP 16 R2 32
SI 16 R1 32
DI 16 R0 32 Data Pointer
POP ES ESES16 PSR->R13 32 stk ptr

Note: *OSTCBStkPtr points to the bottom of either task structure.

Total 16 items 17 items

TCB structure.

Going along with the ARM preference for 32 bit quanities the TCB is modified for the ARM version. This is not the only possible version, as 4 byte quatities could have been packed into a word..
ITEM 80186l size ARM size DESCRIPTION
OSTCBStkPtr far * 16+16 Pointer 32 Pointer to task stack image
OSTCBStat UBYTE 8 uint 32 Task Status
OSTCBPrio UBYTE 8 uint 32 Task Priority
OSTCBDly UWORD 16 uint 32 Timeout for delay or wait
OSTCBX UBYTE 8 uint 32 Priority byte bit position
OSTCBY UBYTE 8 uint 32 Priority group bit position
OSTCBBitX UBYTE 8 uint 32 Precalculated bit mask
OSTCBBitY UBYTE 8 uint 32 Precalculated bit mask
*OSTCBEventPtr Pointer 16 Pointer 32 Pointer to Event Control Block
*OSTCBNext Pointer 16 Pointer 32 Pointer to next TCB
*OSTCBPrev Pointer 16 Pointer 32 Pointer to previous TCB

How these structures are used can best be illustrated by the uC/OS call OSStart() which starts the multitasking kernel by starting the highest priority task. Listing 3 gives the C code followed by the 80186 assembler support and the ARM assembler support codes.

Although the simple ARM instructions make code easier to read, the fact that the ARM does have processor modes makes some uC/OS functions more complicated. Whenever a uC/OS function requires access to private data structures that must be completed without interrupt, the C code calls OS_ENTER_CRITICAL() and after the sensitive code is finished OS_EXIT_CRITICAL is called. In the 80186, these are simply translated to CLI for clear interrupt enable and STI for set interrupt enable. On the other hand the ARM operating in user mode cannot change the interrupt enables directly, but does this through an operating system call. Thus SWI 0x14 - disable interrupts and SWI 0x13 - enable interrupts are used.
ITEM 80186 ARM DESCRIPTION
OS_ENTER_CRITICAL() CLI SWI 0x14 Disables Interrupts
OS_EXIT_CRITICAL() STI SWI 0x13 Enable Interupts

The processs of going through the SWI call takes many cycles, as the ARM must save user registers, back up R14 to find the SWI code, run through a look up table, and then do the request. Many uC/OS functions call OS_ENTER_CRITICAL() and OS_EXIT_CRITICAL() and place in between their user code to accomplish the function. (see listing 4 on delaying a task) Thus the processor may go through a mode transition 4 times for each call. You can probably sense that the next step in porting uC/OS would be to do a major rewrite, placing all OS functions in Supervisor Mode and operating them from SWI calls. The interrupt enable (for IRQ) is automatically turned off after a SWI instruction, obviating the need for theOS_ENTER and OS_EXIT_CRITICAL functions. This is why I call uC/OS for ARM a work in progress.

Back to A-R-T main page.