Linking in C embedded project

Thread Starter

ToonooT

Joined Aug 8, 2013
11
Hi all !

I'm working on a big embedded project including RTOS, USB stack etc.
I'm working with Renesas microcontroller and E2studio IDE.

To architecture this software and to ensure a good maintainability, I would like to use libraries.

No problem to compile a lib and link it in the main project.
My problem is :
I would like to create 2 or more level of libraries. For example :
1- Low Level Driver Libraries(eg, one for UART, one for PWM driver etc.)
2- Abstraction layer library. The idea is to link this lib with Low Level ones and provide unique function in READ,WRITE style with id for each media.
3 - Applicative Layer / Main project

My question is :
How can i link Abstraction Library to others low level libraries in a way that I can change low level ones without recompile Abstraction layer lib ?

Not sure it is very clear but it's not esy to explain.

Thanks in advance for your answer.
 

joeyd999

Joined Jun 6, 2011
5,287
How can i link Abstraction Library to others low level libraries in a way that I can change low level ones without recompile Abstraction layer lib ?
This would require dynamic linking. Can both your compiler/linker *and* operating system support this?
 

MrChips

Joined Oct 2, 2009
30,824
In situations like this I would use the #define preprocessor command.

At the top of the program or header file I use something like:

#define LIBRARY_A

Then in your program you can use conditional compilation:
Rich (BB code):
#ifdef LIBRARY_A
 ...
 ...

#else
 ...
 ...

#endif
Hope this is helps.
 

joeyd999

Joined Jun 6, 2011
5,287
In situations like this I would use the #define preprocessor command.

At the top of the program or header file I use something like:

#define LIBRARY_A

Then in your program you can use conditional compilation:
Rich (BB code):
#ifdef LIBRARY_A
 ...
 ...

#else
 ...
 ...

#endif
Hope this is helps.
How does this avoid recompilation of the abstraction layer?
 

joeyd999

Joined Jun 6, 2011
5,287
You could use system calls, like the old ms-dos. Your mid/high level code simply calls one static function at a known, and always permanent, address, and supplies an index. The index tells the syscall function exactly what device you wish to address, and which function you wish to call.

The higher level code only needs to know the single static address of the syscall, and the numeric indexes of the available functions. These values never change in future builds.
 

Thread Starter

ToonooT

Joined Aug 8, 2013
11
Thanks for your answers.

joeyd999, my RTOS didn't support dynamic linking...
About system calls, did you used it in the past ?

Tell me if I'm wrong, what I undertand is :
- When I build my low level library, I have to set functions to a specific adresses right ?
- Thus I can reference this adresses in upper libraries ?

What's the detailed method for it ?

Is there other way to do so in a more "clean" way ?
The other method which makes Abstraction layer library dependent from driver library is to link it the projet ... Not the goal but easier to set up !
 

joeyd999

Joined Jun 6, 2011
5,287
About system calls, did you used it in the past ?
I use a similar concept in my PIC SPI library. Essentially, I pass an argument to the one and only entry address of the low level spi driver which tells it what spi device to address, what command to send to it.

While it's not exactly the same as a syscall, it does provide considerable advantage. As multiple parts of the code may try to send and receive multiple commands/data to multiple spi devices, each of which may have different clock polarity and timing, allowing the a single driver to manage all these things from one single entry point keeps me sane.

- When I build my low level library, I have to set functions to a specific adresses right ?
It can be 1 function, or multiple functions. The old DOS used int 21 for all syscalls (an interrupt, fundamentally, is the same as a function call, though the interrupt in that case caused a privilege escalation which was required by the OS). Prior to the call, register AH was set to the proper value for the function required, and other registers held other misc. arguments. See this page for example (references DR-DOS).

You can use more than one function, but each address, once included in the library, must stay statically in place from here till the end of time. It's like retiring a player's jersey before he ever plays his first game. After a dozen or so such functions, things are going to get hard to manage going forward.

- Thus I can reference this adresses in upper libraries ?
Yes, through the library header file, just like normal. Beware, though, that the linker must have a way of a) either placing the syscall function in a known place in memory, or b) providing the calling program the proper address of the function (during link time). Fortunately, case 'b' is what a linker is supposed to do, by default!


What's the detailed method for it ?
How much money you got?

Is there other way to do so in a more "clean" way ?
I'm sure there are clever guys that could figure 20 different ways to do this. Some will be cleaner than others. Nothing will be "cleaner" than a complete rebuild of the project when a library file changes (regardless of level).
 
Last edited:

Thread Starter

ToonooT

Joined Aug 8, 2013
11
joeyd999 said:
How much money you got?
Love money :p

Could it be possible to get this SPI library and the projet in which you apply this method ?

Anyway, Thanks for your help and advice
 

joeyd999

Joined Jun 6, 2011
5,287
Why don't you take a look at the freedos project. They implement a PC/MS/DR DOS clone that I am sure must have the same int 21 service calls. And the code is GPL, which means you can download it, look at and modify the code, and play with it. Maybe it'll give you some ideas.
 

ErnieM

Joined Apr 24, 2011
8,377
I'm a bit late, you guys had a big party without me.

How can i link Abstraction Library to others low level libraries in a way that I can change low level ones without recompile Abstraction layer lib ?
When is this linking happening? Compile time or run time?

It's easy peasy to do at compile time, just build each library component on it's own. (That's easy for me to say, it's been years since I built any sort of library but I understand the concept.)

Run time is so messy I would not contemplate it.
 

ErnieM

Joined Apr 24, 2011
8,377
Example? *EVERY* library function you use in your project, all those standard library things, they all were compiled a long time ago perhaps even months or even a year!

A compiler creates objects, one object per dot-c file. Linkers link (ldo) objects to create the executable.

The trick is to write lots of little dot-c files, one function per file. That way the linker can pick and choose exactly which ones are needed; if you dump several functions into one dot-c then the linker has no choice but to bring all of them along in one shot.

Exactly how this is done is compiler specific. I don't use the one you use, so you'll have to read your manual yourself and bash this out.
 

THE_RB

Joined Feb 11, 2008
5,438
...
The trick is to write lots of little dot-c files, one function per file. That way the linker can pick and choose exactly which ones are needed; if you dump several functions into one dot-c then the linker has no choice but to bring all of them along in one shot.
...
Sorry ErnieM, this is not true at all in embedded C.

I regularly group collections of my own favorite functions into a .C file, and #include the file.

The linker will only compile and link specific functions (if that function is used).

So the OP could put all their favorite home-made math functions in one library (.C file, as C source code) then just #include that .C file to have any of those functions available.

Functions in that .C file which were not used will be totally ignored by the compiler.
 

ErnieM

Joined Apr 24, 2011
8,377
What gets built will also depend on your build settings. My IDE allows me to choose between "Build" and "Build All." "Build" will just build those dot-C files that have a date newer then the compiled object, while "Build All" starts with deleting any and all existing objects in the output folder.

So using a simple "Build" may accomplish just the thing you wish.

Edited to add:

If you "#include some_other.c" in your dot-C file (as opposed to a dot-H header file) you loose the ability to to check the date in the build setting: they all get first brought together then built together.
 
Last edited:

THE_RB

Joined Feb 11, 2008
5,438
Are we talking about different things ErnieM?

If you add a file to an embedded project, the compiler will only generate ASM and HEX for functions that are actually used.

Do you have a real-world example of where you have added a file to a C project where that file contains multiple functions, and the compiler has compiled/assembled and used PIC ROM space for functions that are never used?
 

ErnieM

Joined Apr 24, 2011
8,377
Are we talking about different things ErnieM?

If you add a file to an embedded project, the compiler will only generate ASM and HEX for functions that are actually used.

Do you have a real-world example of where you have added a file to a C project where that file contains multiple functions, and the compiler has compiled/assembled and used PIC ROM space for functions that are never used?
Untrue.

There may be an optimizing compiler that will remove such dead code but I have but worked with one.
 

THE_RB

Joined Feb 11, 2008
5,438
Huh? This is heading even further into the twilight zone.

I'm sure you have said before you used MikroC at some point?

That compiler is just one example of a compiler that will only compile functions in your code that are actually USED. If your code has 10 functions, and only 4 are called, only 4 functions will be compiled, assembled, and programmed into the PIC.

Even my C compiler for making Windows apps will only compile functions that are actually used.

It's one of the first jobs of the C preprocessor to check functions and calls, and determine which functions are used, so they can then be compiled.

Are you saying "All the compilers I have worked with will compile/assemble EVERY function that appears in my code"?

Do you check which functions appear in the ASM after compilation? Or check increase in .EXE or ROM size of the finished program after you add a function?

I always know exactly which functions are compiled and take up ROM (or .EXE file size), it's one of the things that is important to keep on top of when making small streamlined apps.
 

ErnieM

Joined Apr 24, 2011
8,377
Well RB you finally noted which specific compiler has the dead code optimization I was talking about. I wasn't about to guess which one you were referring to.

Yes, for your $200 to $250 you get dead code optimization with that compiler.

With the free Microchip compilers I use the code optimization option has expired. I'm unconcerned with that.

I've yet to find a function I've written not appear in the compilation, but that's because if I did not use a function I wouldn't have written it in the first place.
 
Top