Introduction
uC/OS-II_RealTime
SDCC_uC/OS-II
Test_App
Download
References


%Complete
9/10
8/10
4/10
8/10
7/10
8/10



SDCC and uC/OS-II

SDCC is a Freeware, retargettable, optimizing ANSI - C compiler that targets the Intel 8051, Maxim 80DS390, Zilog Z80 and the Motorola 68HC08 based MCUs. Work is in progress on supporting the Microchip PIC16 and PIC18 series. The entire source code for the compiler is distributed under GPL.

Some of the features include:

  • ASXXXX and ASLINK, a Freeware, retargettable assembler and linker.
  • extensive MCU specific language extensions, allowing effective use of the underlying hardware.
  • a host of standard optimizations such as global sub expression elimination, loop optimizations (loop invariant, strength reduction of induction variables and loop reversing ), constant folding and propagation, copy propagation, dead code elimination and jump tables for 'switch' statements.


Reentrancy

A function can be considered Reentrant if it can safely be called recursively or from multiple Tasks without side effects. Meaning 1 function call or Task does not effect(overwrite) the variables or parameters of another. Obviously this means you must have some type of dynamic memory because the recursive function call nesting or multiple Task calls cannot be known in advanceand you will need 1 copy of everything for each call. Typically this dynamic memory is implemented as a Stack. So although there is only 1 copy of the function code, there are N copies of all Local Variables and Parameters for N function calls and they are all put onto a LIFO stack. Three things usually effect Reentrancy. Hardware, Compiler/Compiler Options, and Source Code.

Hardware effects Reentrancy by defining how much Stack space you will have before overflow and thus an upper bound on your Reentrancy, and where the Stacks are located meaning whether MCU Stack pointers can access them directly or if copies have to be made first.

With SDCC_8031sdk_uC/OS-II the Hardware/MCU is 8032 so there are 256 bytes of Internal Stack minus (Registers Banks, data variables, idata variables, bit variables, and _bp Register). With the Test App things work out to 187 bytes of potential Internal RAM, Stack Pointer accessible Stack. Maybe 20 or so Recursive function calls possible if a few Local Variables and Parameters each routine.

A pretty strict limit on Recursive Reentrancy, but as Iteration uses Hardware more efficiently few embedded programs would Recurse anyway. Recursive algorithmns can always be implemented iteratively and vice versa and unless you are a Computer Scientist who doesn't remember his Architecture courses you would know the penalties of Recursion with existing Hardware Technology. For Multiple Task calls though Reentrancy is much better. This is because each Task has it's own Stack, so there is no possibly of one Task overwriting anothers Reentrant function Parameters or Local Vars, just of blowing out it's own Stack.

Where the Stacks are located and whether MCU Stack pointers can access them directly or if copies have to be made first is a big problem for 8051s and SDCC_8031sdk_uC/OS-II. It is because of required copying of the 8051 Internal Stack to xdata SRAM every Task Context Switch. It effects or rather is effected by Reentrancy because some Clever RTOS authors like Jean Labrosse will use the automatic memory allocation/deallocation of Reentrant variables to create temporary Data Structures without having to manually manage the memory. A good idea that works when Tasks Stacks are globablly and consistently accessible. That is 1 Task can access/modify anothers, and each Stack is always available, meaning never swapped out, but it is not so good when they are not. Which is the case with SDCC_8031sdk_uC/OS-II. Also it is not very good Software Engineering as a Task Stack is an Internal Data Structure and if somethng is going to be used by multiple independent Tasks(which are essentially separate programs) great care needs to be taken, and they are no longer Internal.



Compiler and Compiler Options effects Reentrancy by defining how Data and Stack Memory will be used.

how/where code memory, data memory, stack memory, parameters, local variables, pointers, and probably something else will be delt with. In short the Memory Model, but usually that is one specific option of many. They are obviously Compiler and Hardware Dependent and even different Compilers on the same Hardware will have different options effecting Reentrancy. E.G. The Tasking C51 Compiler has 4 memory models, small, compact, large, and reentrant. While the Keil 8051 Compiler has only small, compact, and large. SDCC is the same as Keil. All of them do Reentrancy however, and all of them do Reentrancy for each Memory Model.

The memory model options just say where to put global varaibles, local variables, parameters, stacks, code memory, and data memory that are not specifically placed through keywords in source code. That is the reentrant model for Tasking C51 will put all variables and parameters onto stacks and so they will be Reentrant, while Keil and SDCC require the keyword reentrant in function declarations

Also default pointers sizes because not having pointers matche up to memory allocations would mean either pointers to no where or unaddressable memory. Not good in either cases. This more of an issue with x86 or ARM Architectures but still an important aspect of memory use and memory effects Reentrancy.

With SDCC the small model means that all variables and parameters will into 8051 Internal Memory unless specifically declared as xdata. The compact model is strange. The large model means that all variables and parameters will be put into xdata(external to the MCU) memory. In either case the default without using the Reentrant keyword in your code is not Reentrant. just faster access and much less space for the data(8051 RAM) statics. A few hundred bytes to 64 thousand.

With SDCC Parameters and Local variables default to being put statically onto data(Internal 8051 Memory) or xdata(external SRAM) memory. The SDCC compiler option for Memory Model defines this and for large applications like RTOSs the large model usually chosen, putting everything statically onto external memory(xdata). To correct this all uC/OS-II functions are declared Reentrant putting Parameters and Local variables onto their calling Task Stack. Even with the functions declared Reentrant however particular variables can be specifically set to any memory location as statics by using xdata, data, bit, or idata declarations. Obviously this requires the same care as any shared resource, and the application programmer will have to use Semaphores or Mutex's or accept or accept variable overlaying(overwriting).

by declaring how your Parameters and Local variables will be put into to memory, that is onto a stack or as statics. CPU compilers like gcc typically put these onto Process/Thread/Task stacks and so there is 1 copy for each function call whether Recursive or from separate Tasks/Thread/Processes. Micro Controller or Embedded compilers usually make these statics to save memory and accessing time. So there is 1 copy for every function call or Task(not Reentrant). Furthermore in Embedded systems there is usually more then 1 kind of memory and so it's not only declaring functions as Reentrant to get Parameters and Local variables onto a stack, but also specifying which memory to put them into, which will effect the upper bound on recursive or simultaneous function calls as each memory will probably be a different size.

With SDCC_8031sdk_uC/OS-II the Compiler Options are(large memory model, external ram location 0xa000, code location 0x4000). Large memory model means that function parameters and variables not specifically set to either data, xdata, idata, or bit space are put into xdata external memory. This effects the few functions that are not declared Reentrant because those functions have local variables and parameters will statics in xdata(external STAM memory).

Some uC/OS-II and Application functions can be non-Reentrant because they are only called from Critical Sections and never Recurse. Critical Sections means they will never be interrupted and will always run to completion taking away the possiblity of an ISR or other Task from overwriting the static variables before the routine is completed.

Source Code effects Reentrancy when accessing global variables or shared resources. With only 1 copy of these the application programmer has to manage and synchronize access for recursive or multiple Task calls. This is the familiar for CPU compiler programmers.

Pseudo SFR Registers

SDCC uses a variable in data space(8051 Internal Ram) called "_bp" which is allocated at compile time after space is made for data, bit, and idata vars. It is the stack frame base pointer and is used to compute offsets into the stack for Reentrant delcared function parameters. Unlike other Internal Ram variables however it must be copied between Task Context Switches, or else each Task would get the stack frame base pointer of another's. Which really would not work, unless each Task made no Reentrant function calls. I'm not sure why this is used instead of push and pop

SDCC_8031sdk_uC/OS-II treats "_bp" like the 8051 Special Function Registers PSW, ACC, Dptr, and B, and copies it to Task stacks during context switches. This way it's state is saved and each Task has it's own copy.

"_bp" also makes a complication for writing Interrupts in C which is discussed in the ISR part of this document.

Register Bank Use

This uC/OS-II port only uses Register Bank 0 of the 8051 chip. It is possible with SDCC to use other Banks for ISRs with the Using keyword but it is ignored for normal functions. The benefits of not saving 8 registers is negligible on 8051 uC/OS-II ports however as the entire running Stacks must be copied every ISR anyway, which is typically much more then 8 bytes.

Interfacing C & Assembly

Interfacing C & Assembly is a requirement for every uC/OS-II port as a number of routines require assembler to manipulate processor registers and stack. For SDCC it is very straight forward

with only the registers used for parameter passing, and compilation naming convention to worry about.

The registers used for parameter passing are dptr, accumulator, and b. Dptr for the first 2 bytes,

then accumulator and b. The second parameters onwards are either allocated on the stack(for reentrant routines, or if --stack-auto is used) or in data/xdata memory(depending on the memory model).













uC/OS-II(ucos-ii,ucos,ucosii)
Copyright © 2006 Eric Enockson
Hosted by www.Geocities.ws

1