Help with understanding the linker and project setup

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Folks,

I'm using AVR Studio 6.2 and I am working on a few projects that are getting a bit more complex than the "hello world" projects, so I want to configure the project to minimize code space (and not just optimize for size). My confusion relates to how "smart" the linker is. As an example, in my current Aquarium Controller project, I have several "libraries" that I need to use with this:
-ds1307_lib (RTC related code)
-ds18b20_lib (temp sensor code)
-i2c_lcd_lib (high level driver for LCD)
-twimaster (low level driver used by i2c_lcd_lib)
-USART_lib (driver for serial comms)

Most of these libraries consist of a single .c and .h file that contains all the functions/data/defines/etc for each library.

How is the best way to organize the project in AVR Studio? I can think of four ways to do this:

1) One solution in the project, but using the "add existing file" function with , with all the .c and .h files in a common directory (or split into src and header directories...).

2) One solution and one project, but using the "add existing file" function with "add as link" so the files remain in a common location for all projects using these libraries.

3) One solution and multiple projects, one executable project and several library projects.

4) One solution and one project, bus using the "Add Library" function under the solution.

Which method generally gives the best code size? The first method is what I started with and it led to code fragmentation when I worked on different projects over time, so I really don't like that. The last method, using the "Add Library" function is one method that I have absolutely no experience with. How does it differ from the other methods?

What are your thoughts? I know that most of the folks on here work on far more complex projects, so hopefully you have already found the answer I need! ;)

Thanks!

 

Clint

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh, I've also read were it is better to have each function in it's own file so the compiler can only include the functionality needed for a specific project. That sounds like a HUGE pain in the tail in configuring the project properties... Am I wrong here?

 

Clint

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You don't need to edit the project properties.
I have a project with about 20 modules, the .c and .h files are all in the directory src. It's only a pain to put in the declarations of external variables and functions. I also used a template for the .c and .h files, this helps to make them tidier, you don't forget to write bits of them, and it makes them more readable.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Oh, I've also read were it is better to have each function in it's own file so the compiler can only include the functionality needed for a specific project.

The compiler/linker has options for linking in only the functions you actually use even if you have several functions in the same source file. They are AFAIK set by default in AS6.

Compiler: -ffunction-sections

Linker: -Wl,--gc-sections

(If you want to port the code to some other compiler than GCC, it might or might not have "smart linking".)

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

RRRoamer wrote:
Oh, I've also read were it is better to have each function in it's (sic!)own file so the compiler can only include the functionality needed for a specific project. That sounds like a HUGE pain in the tail in configuring the project properties... Am I wrong here?

That would be a pain if one had a separate file for each function! My project contains well over a hundred functions. As mentioned before, if a function is not called, the linker doesn't link in its code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

That would be a pain if one had a separate file for each function!

In the 1990's I worked on projects/products where this was more or less the rule. Several thousands of files.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Re the Solution/Project question - I'd generally (for AVR) have one project within one solution. This does lead to "odd" directory layouts where you have something like projects\realtimeclock\realtimeclock\Debug or whatever with two levels named the same because the Solution and the project have the same name - but just learn to live with this.

I think I'd only split a solution into multiple projects if I really were going to implement some .a libraries. I might have Soltuion="complex project" then within one project called "math lib", another called "graphics lib" and a third called "main code". The first two would generate .a libs that the third was dependent on. This is the way I work professionally (no AVR code) where we have one solution with about 80 projects (most "owned" by different engineers) and something like 70 of them are functionality libraries, about 7 are dlls and 3 are actual projects that compile and link to .exe's

But for AVR the only time I've ever used more than one project in a solution was when I worked on a bootloader and a test application. I had the two projects in a single solution as ultimately both .hex/.bin were destined to go into the single AVR.

When it comes to doing what you are doing then for something like "ds1307" I'd probably develop that as a two project solution - the library code in one project and a test app in the other. I'd keep the library code in an obvious place on disk like avr/libaries/ds1307 or something.

When I came to implement a project that used that particular library code I'd go with your option (2) and "add as link" so I only ever have one (maintainable!) copy of the ds1307 code in that avr/libaries/ds1307 place on disk and all projects that ever use ds1307 would link to it. When I discover a bug and make a modification it will have a knock-on effect back to all the projects that link to the core files (of course I'd also have them under Git/SVN revision control too in case the "fix" turns out to be bogus and I need to wind back!)

As for redundant code. The fact is that in the "good old days" the word "library" only meant one thing and was done one way: For each function you would put the code in a separate .c file though when a couple of functions could never be separated (like malloc() and free() they would go into one .c file). You would then -c compile all these functions to make .o files so you'd have strcpy.o, printf.o, malloc_free.o and so on and then you'd use "ar" the archiver to collect relevant functions together in a .a (archive) file. So sin.o, cos.o and tan.o would go into a maths library (for brevity it would be called just "m"). The general naming of libraries adds "lib" to the start and ".a" to the end so your maths library "m" would be called libm.a on disk. However to link with it you would be "-lm" which means "link with 'm'" then the system would add lib to the start and .a to the end to make libm.a the archive it tries to link with. Now maybe your code using math functions only uses sin and cos but not tan? In that case the linker would pull the contents of sin.o and cos.o from libm.a but not bother with tan.o and this is why you wrote the functions in separate files in the first place - because you wanted the linker to have a file level granularity to split what it did and didn't include from the .a

As I say you can still do this in avr-gcc if you like but several new technologies have been added to the compiler/linker that kind of surplant then need to split functions to files and make .a's

1) as Johan says there is no -ffunction-sections (also -fdata-sections) for the compiler and -gc-sections for the linker. The function-sections thing means that each function you write is placed in a separately named section so if you write:

void foo(void) {
}

instead of this just being placed in ".text" it's placed in something like ".text.foo" (or is it ".foo.text"?). When you come to link all the code the linker keeps a count on each section presented to it and if the reference count for a section remains 0 then at the end of the link it discards the non referenced sections. So if foo() is never called then the reference count on .text.foo will be 0 and it just won't be output as part of the final binary. This is at least as good as the .o in .a splitting and possibly renders it redundant.

2) whole program optimisation. The compiler driver (avr-gcc) has a -fwhole-program option. The only tricky bit is that nomally if you have main.c, ds1307.c and lcd.c then you would usually compile "avr-gcc -c main.c - o main.o", "avr-gcc -c ds1307.c - o ds1307.o", etc to compile each .c separately to create a .o file. Then you invoke avr-gcc a last time (without -c = compile only) to link all the .o files together: "avr-gcc main.o ds1307.o lcd.o -o project.elf". But with whole program optimisation you need to present the files all together in a single invocation of avr-gcc that compiles them all and links the result: "avr-gcc -fwhole-program main.c ds1307.c lcd.c -o project.elf" which can get tricky when there are 50 or 100 .c files! This does not discard code (still use -ffunction-sections and -gc-sections for that) but it does allow optimisations across the entire code to be made.

3) However whole-program has now been replaced (well almost) by a more recently added technology called Link Time Optimization (LTO). In this the linker calls a "plug-in" when it comes to link the code and that plug-in knows about AVR code and scans across it for things like repeated sequences that could be grouped and call'd and that kind of thing. There have been "teething troubles" but when this works right it's going to be a better form of optimisation than -fwhole-program (and doesn't require a single 50..100 file compilation!)

So if I were you I'd just look at using -ffunction-section and -gc-sections (which AS6 uses by default anyway) but if you want something a bit more than consider investigating -fwhole-program (though trying to persuade the AS6 IDE to do that could be "fun"!) and when it can be relied on ultimately look at -flto as a linker option.

BTW -fdata-sections does the same thing with variables as -ffunction-sections does with code. Each variable is put in a named section and, at the end of the link, if any variable section has not been referenced it is not allocated a place in the RAM/EEROM/whatever map.

One thing to note about -ffunction-sections and -gc-sections is that if you are ever trying to write some code to "test an idea" it can be infuriating that the code keeps getting discarded from the link (I get this a lot when trying to show examples of code to post here!). In that case just remove the tick on -gc-sections in the IDE options.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the very detailed explanation! Sorry for the late reply. I've been playing with hardware the last couple of weeks...

 

Clint