OSAL in embedded multiplatform C

OSAL in embedded multiplatform C



OS-independent programming

Some programmers work for their project. It may be running on a special hardware with a special operation system, mostly a RTOS (Real Time Operation System). The tests are executed at the target hardware - reality tests. Thus the thought is C is not C, any target system needs its own slang (dialect).

What is missing? Compilation of the sources with a proper Integrated Development Environment (IDE). On the PCs, there are some good IDEs, such as Eclipse, Microsoft Visual Studio etc. Mostly, the IDEs for a special hardware are good, but not so good like PC-platform-tools.

What is also missing? Functional testing the algorithm. It isn't a real-time test. But it may be essential.

What is the effort? The algorithm in C need to compile in a PC-environment. It should be able to test, not in real-time, maybe only in one thread or more. For this reason the OS-functionality should be able to use at the test-environment.

What is the result:

  • Well functional tested algorithms

  • Faster writing, reorganization, optimizing and gardening of sources, because the PC-platform is able to use for faster work.

  • Better sources.

The tests on the target-platform are reduced to test the real platform problems, not to test the algorithm basically.

Need of special Operation System capabilities

Most of the Operation Systems are similar in a large way of thinking. All of these support multi-threading, with mutex etc. Most of them have a file system in a adequate kind etc.

The differences are: They should be support the hardware platform with different processors, different equipment in RAM, ROM, different I/O. But the user programming shouldn't consider all of them so far. The user programming should be realized in a more abstract shell. This means the RTOS should be proper for the hardware, but it should not have to much special features for the user programming. It may be useful only for driver or interrupt-level-programming.

Conclusion: The user programming should use unified interfaces to the operation system.

1 OSAL-Level in embedded multiplatform C emC


The OSAL level is defined by a set of header files stored in source/OSAL. Some implementation firstly for Windows and Linux for several versions are contained in sourceSpecials/os_Windows_64 etc. For a given embedded platform the sources should be written in the necessary special form. The OSAL defines the interface between the platform independent user program and the system.

2 Common headers


An interface in C-language is established in header-files. Most of compiler-tool-associated header-files declare the same things, but in their own files in a maybe different form. So the risk of little differences are given.

Instead, all OS-interface-functions should be declared in the same header-files, independent of the current used operation system. The implementation of the OSAL should use this files as interface definition.

3 Platform and application specifics


In general, the concept of applstdef_emC.h: Headerfile to define target conditions is used. This file is included in the OSAL sources too. It means, for different application decisions, the os-specific files should be compiled for a concretely target too, a common library (for the platform) is not intended to use.

For example the kind of error handling is a decision of the application:

The adequate approach is used for the compiler specific compl_adaption.h file. If, for example, generated code from simulink is used in the application, the type int32_t should be defined in a simulink-compatible way. Simulink uses its own typedef int int32_T; in the rtwtypes.h. This type should be compatible (without pointer type error or warning) with the int32_t type used in the OSAL headers and sources. Therefore the compilation of the OSAL sources should be done especially under this conditions.

Especially for simple platforms some osal-operations are defined near to the processor in the compl_adaption.h file using a #define. For this case all relevant prototype definitions in the OSAL header are written with (example)

#ifndef os_getClockCnt
int32_t os_getClockCnt(void);

In this example the clock count can be gotten from a simple counter inside the CPU or from a FPGA device.

4 Errors on OSAL level


The handling of errors on OSAL level can be extensive in the application. That isn't a proper approach. There are 3 groups of error reason with subgroups:

The case 3) should be handled on application (user) level. The fault is indicated by the return value.

4.1 FatalSysError


On case 1) basicly errors should cause an abort of the application on OSAL level. For that cases the routine os_FatalSysError() should be called. This routine is defined in OSAL/os_error.h. It should be implemented in a proper form at user level, to control what to do by the user.

For windows an error message should be given on console and in a logfile and the application can be terminated. It should be possible to set a breakpoint there while step-debugging to explore the situation.

For a target platform without console such errors should be explore in debugging mode. If a debugger is not present the system should be stopped but the error cause should be able to read out.

Such fatal sys errors should be recognized in an early stage of software developing. If a software or application on a device is tested, such errors are possible in non-testet situations, as result of another error such as an hardware fault or others. Maybe that occurs in a defined situation. Then the device is defect and should be repaired. For the software developer it means: back to debugging.

4.2 THROW error


All OSAL errors which are not fatal, because the basic system can be used, and which are not working faults (category 3) can use the THROW macro with STACKTRC capability which is well defined for the emC software strategy, see ExceptionHandling. This should be written in the sources of OSAL.

Yet it depends on the definition in <applstdef_emC.h> and the appropriate sources how the system responds.

Calling level:

CATCH(Exception, exc) {
  //do alternate functionality,
  //inform about the problem in user's thinging

Middle, called level

void callAnything() {
  //maybe the error is not realized here,
  //but an additional damage does not occure.


void os_doSomething() {
  if(missingRessource) {
    THROW_s0(NoRessourceException, "missing ressource"...);
    //for non-throwing:
    //do furthermore
    return 0;  //maybe instead of a handle

For example, if the number of mutex objects are limited, os_createMutex(...) deliver a null pointer instead the mutex pointer and invoke THROW. Maybe, the application does not notice it.

The following invocation of os_lockMutex() detects that there is a null instead a mutex pointer and THROWs the exception but returns true. The application does not notice it because throw or longjump is not used.

Therefore, the data access is not guarded with a mutex. The application does not notice it but works maybe with non consistent data. In follow data are faulty. But the application runs.

On any calling level of the application the Exception is detected in a CATCH block. Then the faulty becomes obvious, and a proper counterreaction can be done.

That system is sometimes more easy and better to understand than a system with a lot of faulty checks on application level.

4.3 Error return


The return of a value which informs about an error should only be used for working faults. Such working faults have to be handled on application level in any case.