Jump to content
Sign in to follow this  
Alan

mspgcc output binary sizes

Recommended Posts

Hi guys this question is a bit general (and I might be best posting it on stackoverflow etc) but thought to throw it down here first.

I have a c source file with a few functions I find myself using fairly often, pretty much stuff you would expect to find in string.h etc but tailored a bit more to my needs.

When I need one of the functions I treat it like any other source file e.g.

msp430-gcc file1.c functionLib.c -o out.elf

 

But of course the output binary ends up extortionately large even though I only wanted one small function out of many. (I'm nowhere near maxing out the chip memory yet, but reprogramming a few times when debugging gets tedious). I would have thought it should be possible for the linker(?) to pick out the functions that are needed, and don't really understand why it doesn't :S. Do I need to compile it as a stand alone library first?

Share this post


Link to post
Share on other sites

I don't think the linker can do that (with straight ELF objects or static libraries). Dynamic linking the end result is obviously not an option, but (IFF the toolchain supports it at all, which I'm rather sure it doesn't) you can try making your library a shared library, link your application against it with --as-needed, then chew on the result with something that makes a static ELF object out of your dynamic ELF object (there are tools to to this, but all of their names escape me for the moment).

Share this post


Link to post
Share on other sites

Rick's suggestion to use function and data sections with linker garbage collection is the least invasive solution and is expected to work. While responding it strikes me there may be an issue with functions intended to serve as interrupt handlers, if they aren't marked to prevent them from being discarded, but I believe that isn't an issue anymore (it was at one time).

 

The old-school way of using conditional compilation to split each function into its own object file and putting the set of object files into an archive where only the referenced ones are retained can also work and is what's currently done with libc and the C runtime infrastructure, but it's a mistake from a maintenance perspective given the linker garbage collection solution.

 

I hadn't been aware of and haven't tested --as-needed. I have no plans to support dynamic linking or position-independent code in mspgcc.

Share this post


Link to post
Share on other sites
You could try:

 

msp430-gcc -Os -g -fdata-sections -ffunction-sections -Wl,--gc-sections file1.c functionLib.c -o out.elf 

 

-rick

 

Carried out a test of this:

Stand alone made with:

CFLAGS= -Wall -g -I ../common -L/usr/msp430/lib/ldscripts/msp430g2553/ -I /usr/msp430/include
clock.elf:
$(CC) $(CFLAGS) -mmcu=msp430g2553 -o clock.elf main.c

Programming:

Writing 242 bytes to c000...

Writing 32 bytes to ffe0...

 

Stand alone code made with your recommended flags:

Programming:

Writing 84 bytes to c000...

Writing 32 bytes to ffe0...

 

 

Added my "usefulFunctions" with the same flags - no changes to main file so these functions are not called by it.

clock.elf:
$(CC) $(CFLAGS) -mmcu=msp430g2553 -o clock.elf main.c ../common/usefulFunctions.c

 

Programming:

Writing 4096 bytes to c000...

Writing 364 bytes to d000...

Writing 32 bytes to ffe0...

(SIGH)

 

With recommended flags:

CFLAGS= -Wall -g -I ../common -L/usr/msp430/lib/ldscripts/msp430g2553/ -I /usr/msp430/include
EXTRAFLAGS= -Os -fdata-sections -ffunction-sections -Wl,--gc-sections
all: clean clock.elf

clock.elf:
$(CC) $(CFLAGS) $(EXTRAFLAGS) -mmcu=msp430g2553 -o clock.elf main.c ../common/usefulFunctions.c

 

Programming:

Writing 84 bytes to c000...

Writing 32 bytes to ffe0...

 

So both files ended up the same size. Does either do as expected? No. Something important has been optimized away.

I'll look into it when I have more time, - it was basically a program using TImerA to flash the launchpads leds for minutes and seconds. So something has gone wrong with my interrupt...

Share this post


Link to post
Share on other sites
Rick's suggestion to use function and data sections with linker garbage collection is the least invasive solution and is expected to work. While responding it strikes me there may be an issue with functions intended to serve as interrupt handlers, if they aren't marked to prevent them from being discarded, but I believe that isn't an issue anymore (it was at one time).

 

The old-school way of using conditional compilation to split each function into its own object file and putting the set of object files into an archive where only the referenced ones are retained can also work and is what's currently done with libc and the C runtime infrastructure, but it's a mistake from a maintenance perspective given the linker garbage collection solution.

 

I hadn't been aware of and haven't tested --as-needed. I have no plans to support dynamic linking or position-independent code in mspgcc.

 

I think interrupt handler functions are definitely an issue. I think I might well just end up splitting my functions up, as I tend to try and avoid overcomplicated optimization as its witchcraft which generally gives me a debugging nightmare. (Probably my dodgy code in the first place mind you....) Archives are something I have only played about with once or twice so may well look into them further and see if they are an easier option that copying and pasting the relevant code into my source files. I could of course go back to using libc again, but for microcontrollers I always try and keep everything as self contained as possible... probably for no good reason.

Share this post


Link to post
Share on other sites
Rick's suggestion to use function and data sections with linker garbage collection is the least invasive solution and is expected to work. While responding it strikes me there may be an issue with functions intended to serve as interrupt handlers, if they aren't marked to prevent them from being discarded, but I believe that isn't an issue anymore (it was at one time).

 

I think interrupt handler functions are definitely an issue.

 

What makes you say this? Never mind, now I see your other post. Dunno what the problem is, "it works for me". If you track it down, please file a ticket.

 

I responded from a machine that didn't have mspgcc installed so couldn't validate what I said, but as far as I can tell testing this morning lts-20110716 and the development series both correctly place the interrupt handlers into the resulting executable. In fact, the issue that was fixed had to do with gcc discarding static functions that were registered as interrupt handlers because it didn't think they were called, which has nothing to do with linker gc and has, in fact, been fixed.

 

If you have a reason to believe otherwise, please file a bug on the mspgcc tracker on SourceForge.

 

The only reason not to use libc is if you think there's a bug (in which case you should report it), or you don't need the full functionality of a standard function and it's too large (a concern with the recent support for 64-bit integers in printf). The libc archives already have essentially the equivalent of section gc, and routines that aren't referenced will not appear in your executable. I tried adding the function/data section support to an msp430-libc build once, and the difference was insignificant.

Share this post


Link to post
Share on other sites
One possibility: if you're using an unpatched LTS-20110716, gc sections won't work. Make sure you (or whoever packaged the version you're using) is up to date with https://sourceforge.net/projects/mspgcc ... /20110716/.

 

I've often thought that it would be nice if you type: "msp430-gcc --version -v" and it would display not only the gcc version but also a list of all applied patches. Anyone else think this might be useful?

 

Maybe there Is there another way to figure out through headers or some other means which patches are applied?

 

-rick

Share this post


Link to post
Share on other sites
One possibility: if you're using an unpatched LTS-20110716, gc sections won't work. Make sure you (or whoever packaged the version you're using) is up to date with https://sourceforge.net/projects/mspgcc ... /20110716/.

 

I've often thought that it would be nice if you type: "msp430-gcc --version -v" and it would display not only the gcc version but also a list of all applied patches. Anyone else think this might be useful?

 

Yeah, me.

 

I don't think there's a good way to do it at my level, though, since the choice of which patches are to be applied is a decision made by the downstream packager, and if each patch attempted to update some common version identifier they'd conflict unless all were applied in the original order (and would conflict if the packager chose to apply patches that were not sent on to me). I'm relying on the packager to provide that information somewhere, e.g. "rpm info".

 

Or to tack on a final patch that does exactly what you suggest. Matthias Hartmann, who packages for mingw32, appends to the binary tarfile the date corresponding to when he grabbed the current patch set, though I don't know if he puts it in the installation directory path, or updates any version information that can be obtained by invoking a toolchain command.

 

Presumably people who built their own know whether they applied any patches; the problem there is letting them know when patches exist and should be applied.

 

I probably should at least put the base release number (20110716, or 20111224) into the --version output, since the development releases will all say "4.6.1" by will have significant differences. I haven't taken the time to figure out how to do that, and will try to remember to look at how it's done with RPM when I get back to mspgcc work next week.

 

You can check the internal version information with:

linux[5]$ msp430-gcc -E -dM -xc /dev/null | grep MSPGCC
#define __MSPGCC__ 20111224

Similarly the libc and msp430mcu version numbers area available through preprocessor constants. Take a look at the first few lines of test430.c. This can be useful to work around absence of new features in old code (e.g., the lack of support for large __delay_cycles() values prior to the new development series).

Share this post


Link to post
Share on other sites

I've been happily using the binary from the Ubuntu repo, so version 20110612, which I'm guessing has the interrupt function bug present or was that just in the one release?

Share this post


Link to post
Share on other sites

The bug I was thinking of was fixed in March, so 20110612 probably doesn't have that particular problem, though it might have the one I mentioned later. Badger whoever at Ubuntu's responsible to update their package to LTS-20110716 with all the available patches. The long-term support versions are supposed to be trustworthy, and will have reported bugs fixed. Ones like 20110612 and the most recent (20111224) are development snapshots and not suitable for non-experimental installations.

Share this post


Link to post
Share on other sites

I've altered my code somewhat so its in a decent state for posting mainly, and removed all references to the source containing my other functions just to try and test the flags. I'm not sure why its going wrong (its probably my fault no doubt).

Makefile:

CC=msp430-gcc

CFLAGS= -Wall -g -L/usr/msp430/lib/ldscripts/msp430g2553/ -I /usr/msp430/include
EXTRAFLAGS= -Os -fdata-sections -ffunction-sections -Wl,--gc-sections

all: clean clock.elf

clock.elf:
$(CC) $(CFLAGS) $(EXTRAFLAGS) -mmcu=msp430g2553 -o clock.elf main.c
clean:
rm *.elf

 

main.c:

/*
* A basic clock using WDT.
* Using a 6 pF C-001R crystal for ACLK.
*/

#include "msp430.h"
#include "legacymsp430.h"

#define SEC_LED BIT0
#define MIN_LED BIT6

volatile unsigned char seconds = 0;
volatile unsigned char minutes = 0;
volatile unsigned char hours   = 0;

int main(void)
{
   WDTCTL = WDTPW + WDTHOLD;

   //BCSCTL1 = CALBC1_1MHZ;           
   //DCOCTL  = CALDCO_1MHZ;

   P1OUT = 0x00;
   P2OUT = 0x00;
   P1DIR = SEC_LED + MIN_LED;

   WDTCTL = WDT_ADLY_1000;         // Setup WDT for 1000ms interval interrupt
   IE1 |= WDTIE + OFIE;                    // Enable WDT interrupt and Oscillator Fault interrupt
   BCSCTL3 = XCAP_1;                       // 6 pF            

   while(1)
   {
       if (seconds > 59)
       {
           minutes++;
           seconds = 0;
           P1OUT ^= MIN_LED;
       }

       if (minutes > 59)
       {
           hours++;
           minutes = 0;
       }

       if (hours > 23)
       {
           hours = 0;
       }

       __bis_SR_register(LPM3_bits + GIE);
   }

   return 0;
}

/* WDT Interrupt */
interrupt(WDT_VECTOR) wdt_interrupt(void)
{
   seconds++;
   P1OUT ^= SEC_LED;

   __bic_status_register_on_exit(LPM3_bits);
}

/* Non Maskable Interrupt Handler - Osc Fault */
interrupt(NMI_VECTOR) nmi_ (void)
{
   unsigned volatile int delayCtr = 0;
   while (IFG1 & OFIFG)    
   {
       IFG1 &= ~OFIFG;                 // Clear Oscillator fault flag
       P1OUT = BIT0 + BIT6;          // Light LEDs for warning 
       for (delayCtr = 0; delayCtr < 0xFF; delayCtr++);        // Delay for fault to clear
   }

   P1OUT = 0x00;                           
   IE1 |= OFIE;                                 // NMI's get cleared - re enable
}

 

With EXTRAFLAGS programs to be:

Writing 90 bytes to c000...

Writing 32 bytes to ffe0...

 

Without EXTRAFLAGS programs to be:

Writing 326 bytes to c000...

Writing 32 bytes to ffe0...

 

With the flags there is a lack of any sign of life from the board. In fact I tried lighting the both LEDs on the launchpad straight after disabling the dog, expecting them to stay lit permanently and that didn't happen, so I'm a little stumped.

Share this post


Link to post
Share on other sites
With the flags there is a lack of any sign of life from the board. In fact I tried lighting the both LEDs on the launchpad straight after disabling the dog, expecting them to stay lit permanently and that didn't happen, so I'm a little stumped.

 

I tried this code and it works with the EXTRAFLAGS. I ended up with a different size elf file than you though:

 

Erasing...
Programming...
Writing  250 bytes to c000 [section]...
Writing   32 bytes to ffe0 [section]...
$ make
rm -f *.elf
msp430-gcc -Wall -g -L/usr/msp430/lib/ldscripts/msp430g2553/ -I /usr/msp430/include -Os -fdata-sections -ffunction-sections -Wl,--gc-sections -mmcu=msp430g2553 -o clock.elf main.c
$ msp430-size clock.elf 
  text	   data	    bss	    dec	    hex	filename
   282	      0	      3	    285	    11d	clock.elf
$ 

 

I'm using the 20110706 msp430-gcc with all the patches applied. It is funny that you are getting a different size clock.elf. You might want to check your path and make sure you are getting the correct msp430-gcc.

 

Do you have a 32.768kHz XTAL soldered on your Launchpad? I ran this test with a msp430g2553 chip on a bread board, I just plopped a watch crystal in there without any capacitors and it worked. Without the crystal, both leds are pegged on and don't flash at all. I would expect that based on your NMI_RESET code.

 

-rick

Share this post


Link to post
Share on other sites
I'm using the 20110706 msp430-gcc with all the patches applied. It is funny that you are getting a different size clock.elf. You might want to check your path and make sure you are getting the correct msp430-gcc.

 

Do you have a 32.768kHz XTAL soldered on your Launchpad? I ran this test with a msp430g2553 chip on a bread board, I just plopped a watch crystal in there without any capacitors and it worked. Without the crystal, both leds are pegged on and don't flash at all. I would expect that based on your NMI_RESET code.

 

-rick

I have a crystal soldered in place, its not the one that came with it though (its a human-sized through hole) but without EXTRAFLAGS the code does what I expect - the leds toggle with minutes and seconds. I do have some doubts about the crystals accuracy but until I hook up a display this remains to be seen. I expect it is the code size difference that is causing issues - and mine seems to struggle with main itself which confuses me.

I might look into building a newer version from source - though I try and avoid this generally to prevent a headache. Normally when building from source the dependencies end up ruining my life, hence why I more than happy to grab binarys from the Ubuntu Software Centre.

Would you recommend any good guides for building this (for someone who isn't as competent with linux as they ought to be)? At the start when I was reading into it, I came across some discrepancies from peoples guides, from the multiple versions out there I think.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...