Code size reduction (30478 of 30720 bytes)

I am running up against the size limit of 30720 byte code size limitation on the AVR328. I've made an attempt to keep the code size as small as possible (using 8 byte integers where appropriate, moving common code to functions). I'm posting my code here in case I've missed anything.

Any advise welcome.
Thanks!
Justin

http://code.google.com/p/clockthree/source/browse/trunk/libraries/ClockTHREE/examples/ClockTHREE_02/ClockTHREE_02.pde

(code too large to post here directly)

Do you have a second Arduino?

Move up to an ATMega644. 64K of flash.

using 8 byte integers where appropriate

"long long" is particularly inappropriate on an 8 bit architecture.
Use "int" (2 bytes) or "long" (4 bytes) if absolutely necessary.

I think the OP wanted to say 8 bits variables, for a very very quick look up, I can say that all this:

// MODE Constants
const uint8_t N_MODE = 11;
const uint8_t N_MAIN_MODE = 6;  // main modes are accessable through Mode mode. (sub modes are not).
const uint8_t N_SUB_MODE = 4;

const uint8_t NORMAL_MODE = 0;
const uint8_t SET_TIME_MODE = 1;
const uint8_t SET_COLOR_MODE = 2;
const uint8_t SET_ALARM_MODE = 3;
const uint8_t SERIAL_MODE = 4;
const uint8_t MODE_MODE = 5;

// Sub Modes (this cannot be accessed though Mode mode selection.)
const uint8_t SECONDS_MODE = 6;
const uint8_t ALARM_MODE = 7;
const uint8_t TEMPERATURE_MODE = 8;
const uint8_t SCROLL_MODE = 9;
const uint8_t COUNTDOWN_MODE = 10;

// Temperature unit constants
const uint8_t DEG_C = 0;
const uint8_t DEG_F = 1;

Could be changed to defines, like this:
N_MODE = 11; becomes
#define N_MODE 11

Could be changed to defines

Does that reduce the compiled size?

I never tried that train of words that is static const bla bla bla, I always use defines, so I know that I'm not using extra ram, and to put something in ram it would need to be located in flash as well, so maybe it does, maybe the gcc is smarter than I think.

I've always used #defines partly because I was certain they didn't use any memory and we had a discussion about this a while back.

Anyway I thought I'd do a quick test

//const int x = 9600;
#define x 9600

void setup() {
  Serial.begin(x);
}

void loop() {
  
}

This produces exactly the same code size either way.


Rob

Graynomad:
I've always used #defines partly because I was certain they didn't use any memory and we had a discussion about this a while back.
Anyway I thought I'd do a quick test

//const int x = 9600;

#define x 9600

void setup() {
  Serial.begin(x);
}

void loop() {
 
}




This produces exactly the same code size either way.______
Rob

Yes, same code size, but less memory in RAM.

Think about it, for the GCC to decrease the size in code you'd have to skip instructions. Defining a const variable or a define doesn't make the Serial.begin() instruction go away, does it? And that's what is taking up space in the code memory.

@bubulindo: Are you saying that the "#define" case uses less SRAM than the "[static] const" case?

I didn't see anything really obvious.

How big is your font table? You might be able to save some space by going to all upper case and deleting some symbols, but if it's a reasonable implementation that ought to be only about 5 bytes per glyph...

PS: upgrade to a recent version of optiboot and gain back an additional 1.5k of code memory.

Yes the vast majority of that size is library and setup code, but if an extra variable was being created I would expect a difference of 1 or 2. Remember we are looking at the difference between #define and const, the existence or otherwise of Serial shouldn't matter.

AFAIK the value reported by the IDE shows the text + data sections so any normal constants that get created (say a string) will go into the data section and show in the total.

This code

const int x = 9600;
//#define x 9600
int y = 12;

void setup() {
  Serial.begin(x);
  //pinMode (y, OUTPUT);
}

void loop() {
  
}

Produces

   text    data     bss     dec     hex filename
   1844      18     160    2022     7e6 output/test.elf

Regardless of whether #define or const is used.

"Serial.begin (9600)" produces the same values as well, so I think it's fair to say that no matter what method you employ the memory used (of any kind) is the same. Whether or not something is created in RAM or flash cannot be determined from this, but the two methods are the same. (Note that "y" has been optimized out.)

If I uncomment the pinMode() line we get

   text    data     bss     dec     hex filename
   2020      20     160    2200     898 output/test.elf

Text has increased as expected because we've brought in another library function, but data has risen by 2, ie the size of the int y.

Therefore I submit that "#define", "const" and a literal number all create nothing in RAM (and presumably all create 2 bytes in flash as part of an LDI instruction somewhere) and are essentially the same in this respect.


Rob

It was proven that it is not.

I may be wrong, but I think the define is a compiler directive and not something that gets passed on to the microcontroller... that being said, if you #define something, when you compile, the compiler changes the #define'd for the value used by us. In other words, a define exists merely to allow us to give meaning to values in code. Using const int, would create an actual variable. I know that in the end, it doesn't matter, because both work... but that's what I learned.

One thing that may be happening is the compiler optimization settings equaling a define and a const int to the same. This is after all a memory constrained device and I don't know what optimization options the microcontroller allows or is set to.

Here's a few more discussions on the subject that point out some differences:

http://www.velocityreviews.com/forums/t283393-define-versus-const.html
http://www.gamedev.net/topic/418410-c-difference-between-define-and-const-int/

The reason why the Serial.begin(9600) takes up the same space is because it still needs to load a value (this time from Flash) into memory to pass it on to the register.

But either of them won't solve your memory program shortage. It would be nice though, to create a tutorial or a sticky post explaining this in the forum...

I may be wrong, but I think the define is a compiler directive and not something that gets passed on to the microcontroller...

Pretty much, actually it's a preprocessor thing, it doesn't even get to the compiler.

Using const int, would create an actual variable.

It seems that's not the case.

I confess I put forward almost the same argument as you did not that long ago, I can only assume that the compiler (or optimizer) is clever enough to realize what's going on and it inserts a literal. So effectively the #define and const do the same thing, just in different places.


Rob

Using const int, would create an actual variable.

I'd feel better if they were "static const int xxx = a;"
Without the "static", the compiler will (probably) produce variables as well as use constants, and it is only in the link phase that anything discovers that no other modules access the variables and they therefore can be "garbage collected."

You should be able to see the difference by disassembling the .o files as well as the .elf files.

Can someone try this?

const int x = 9600;
int *ptr;
//#define x 9600

void setup() {
   ptr = &x;
   Serial.begin(*ptr);  
}

void loop() {
  
}

What I'm trying to prove is that this will create the actual "const int" variable. Of course that the code will be bigger, but the memory footprint will include the const int variable because now there is pointer to it. (Yes, I'm assuming the compiler is not that smart)

Just looks like a very long program to me, around 2,000 lines.
I had a program (several files compared with your one file), that grew to 2,700 lines and took 27K FLASH. It had lots of strings for LCD but I bet the difference between mine and your program is includes. I didn't have that many includes.

Two suggestions I can think off:

  1. USE a 24LC512 EEPROM to store your English letters. Hope this won't slow you down too much. You'll have to try.
  2. See if you're overloading any functions. Try not to overload. Say you have a constructor chips(int) and another one chips(long). If you used both, just because you didn't bother to convert long into int, the compiler stores both constructors in FLASH.

This can get significant with Serial.print(). Say you're already using the awesome sprintf, you should NOT use serial.print(int or float or long), only use serial.print(string). Use sprintf to construct a string and print with serial.print(string). Try serial.print(string) and serial.print(float) in one program and get rid of serial.print(float)

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  Serial.print("3.14");
//  Serial.print(3.14);
}

This above program is only 1,894 on my 2009 and 3,726 if I uncomment the second line in loop!!!

liudr:

  1. USE a 24LC512 EEPROM to store your English letters. Hope this won't slow you down too much. You'll have to try.

I don't see what you mean by English letters... do you mean strings?
Another possibility is to have commands (a couple of bytes) sent to the PC instead of a describing string. And multiply your floating points to 10 or 100 (depending on the size) and use those on the communication to the PC instead of floating point.

liudr:
2) See if you're overloading any functions. Try not to overload. Say you have a constructor chips(int) and another one chips(long). If you used both, just because you didn't bother to convert long into int, the compiler stores both constructors in FLASH.

Also, great advice. :slight_smile:

What's english.h? It's not in your code. I suppose if it's a bunch of letter matrix for your persistent (?) display, you will need these matrix letters.

Thank you everyone for looking over the code. I can verify that replacing all of the

const uint8_t bla = xx;

with

#define bla xx

retained the exact code size.

@westwf The font is pretty large, 8 bytes / char. Most symbols use only 7x7 pixels so I could save 128 bytes there. I had a quick look at the optiboot. Looks like the way to go.

Thanks again!
Justin