First, note: I'm using avr-gcc, directly, rather than going through e.g. WinAVR, or Arduino...
And be sure to check that previous log! I am *not* using stdio, as that's *huge*, but it takes some effort to make certain it's not included.
--------------
Here I've created an AVR project with nothing but the following, code-wise...
#include <avr/io.h>
#include <stdint.h>
#include <inttypes.h>
int main(void)
{
while(1)
{}
}
This project compiles with the following specs, output by 'avr-size'
_BUILD/minStartingPoint.elf : section size addr .text 58 0 .data 0 8388704 .stab 1200 0 .stabstr 2993 0 .comment 17 0 Total 4268
As I understand the contest's requirements, this qualifies as 58 Bytes toward our 1kB limit.
-------
Now, what happens when we add a global-variable?
#include <avr/io.h>
#include <stdint.h>
#include <inttypes.h>
uint8_t globalVar; // = 0;
int main(void)
{
while(1)
{}
}
Now we get:
_BUILD/minStartingPoint.elf : section size addr .text 74 0 .data 0 8388704 .bss 1 8388704 .stab 1212 0 .stabstr 3010 0 .comment 17 0 Total 4314Toward the contest-requirements, I believe this qualifies as 74 Bytes toward our 1kB limit.
Note that I did not initialize the global variable... If I'd've initialized it to 0, we'd have *exactly* the same results. (Uninitialized global/static variables are always initialized to 0, per the C standard. THIS DIFFERS from *non-global*/*non-static* local-variables, which are *not* presumed to be 0 by default.)
-----------
But what happens when we initialize it to some other value?
#include <avr/io.h>
#include <stdint.h>
#include <inttypes.h>
uint8_t globalVar = 0x5a;
int main(void)
{
while(1)
{}
}
_BUILD/minStartingPoint.elf : section size addr .text 80 0 .data 2 8388704 .stab 1212 0 .stabstr 3010 0 .comment 17 0 Total 4321NOW, note... our ".data" section has increased from 0 to 2. (and our .bss section has dropped from 1 to 0).
As I understand, the ".data" section counts toward both your RAM and ROM/Flash usage.
Why both? Because the global-variable is *initialized* to the value 0x5a. The variable itself sits in RAM, but flash-memory is necessary to store the initial-value so it can be written to the RAM at boot.
As I understand, there's a bit of code hidden from us that essentially iterates through a lookup-table writing these initial-values to sequential RAM locations, which will then become your memory-locations for your global/static variables.
Note, again, this didn't happen when the global-variable was uninitialized (or initialized to 0) because there's no need for a lookup-table to store a bunch of "0"s, sequentially. Instead, there's a separate piece of hidden-from-us code that loads '0' to each sequential RAM location used by "uninitialized" globals/statics.
SO...
As I understand, per the contest-requirements, the above example counts as 80+2 = 82 Bytes toward the 1kB limit.
-------
I'm just guessing, here, but I imagine it went to *2* rather than *1* because they indicate the end of the initialization/"lookup-table" with a "null"-character = 0... So, most-likely, if you add a second initialized global-variable the .data section will be 3 Bytes.
Let's Check:
#include <avr/io.h>
#include <stdint.h>
#include <inttypes.h>
uint8_t globalVar = 0x5a;
uint8_t globalVar2 = 0xa5;
int main(void)
{
while(1)
{}
}
Well, color-me-stupid...section size addr .text 80 0 .data 2 8388704 .stab 1224 0 .stabstr 3028 0 .comment 17 0 Total 4351.... and three?
#include <avr/io.h>
#include <stdint.h>
#include <inttypes.h>
uint8_t globalVar = 0x5a;
uint8_t globalVar2 = 0xa5;
uint8_t globalVar3 = 0xef;
int main(void)
{
while(1)
{}
}
section size addr .text 80 0 .data 4 8388704 .stab 1236 0 .stabstr 3046 0 .comment 17 0 Total 4383Uh Huh...!
So, maybe the init-routine handles 16-bit words at a time... might make sense, since 'int' is 16-bits.
Anyways, that's probably irrelevent.
But, do note that the ".text" section hasn't grown at all.
--------
So, again, this last example would most-likely count toward 84 Bytes of the 1kB limit.
......
The key, here, is that when you "Flash" your chip, it will flash ".text" + ".data" bytes to the flash/program memory...
So, regardless of this contest, the end-result is that even if your .text section is less than your flash-memory space (say 8190 bytes), your project still might not "fit" in your flash-memory.
I remember this being *quite confusing* when I first ran into it... so maybe this'll help save you some trouble.
.......
As far as the other sections... The ones listed here, from avr-size, don't really count, they contain stuff like debugging information that's stored in your compiled binary-file, but not written to flash.
Oh, and if I wasn't clear, ".bss," it seems, tells you the amount of RAM used by *uninitialized* global/static variables... which doesn't count toward the amount of program-memory used.
.....
Side-Note: When working with projects with limited memory, it's probably wise to *not* use many large local variables... E.G. say you've a string...
void printHello(void) { char string[80] = "Hello"; char *charPtr = &(string[0]); while(*charPtr != '\0') { uart_putChar(*charPtr); charPtr++; } }If you have several such functions, it might make more sense to have *one* *global* string array which can be (cautiously) reused between these functions... Otherwise, your stack can fill up quite-quickly, and stack-overflows are *really* confusing when they occur.
Similarly, wise not to use Malloc, etc.
And another plus-side of doing-so is that it shows up in your ".bss" section, so you have an idea of how much memory you're using, and how much stack is available.
------
Here's another interesting aside... I just noticed when rereading this:
Did you notice that the ".text" section increased by only 6 bytes when we changed our uninitialized global variable to an initialized one? Seems fishy... I highly doubt they can fit looping through a lookup-table in only six bytes' worth of instructions...
I wonder if they only include each of the two different initialization-routines *when needed*...
#include <avr/io.h>
#include <stdint.h>
#include <inttypes.h>
uint8_t globalVar = 0x5a;
uint8_t globalVar2 = 0xa5;
uint8_t globalVar3 = 0xef;
uint8_t uninitializedGlobalVar;
int main(void)
{
while(1)
{}
}
section size addr .text 96 0 .data 4 8388704 .bss 1 8388708 .stab 1248 0 .stabstr 3076 0 .comment 17 0 Total 4442Ah hah! The only change was adding of another "uninitialized" == (initialized-to-zero), global-variable, and now the ".text" size has jumped from 80 Bytes to 96 Bytes.
So it would seem that the "zeroing" routine for "uninitialized" global/static variables occupies 16 bytes, and the "lookup-table"-initialization routine occupies 22 Bytes.
Hey, wanna save a few bytes? Can you get away with converting all your global/static variables to either initialized or "uninitialized"? Might be something in there...
----------
Note: The above tests were performed on an ATmega8515...
The last-experiment shown was since run on an ATtiny861...
If anyone wonders about the differences in functionality of different systems, take a look here. Here's the result from the above test on a different processor of the same architecture:
section size addr .text 100 0 .data 4 8388704 .bss 1 8388708 .stab 1248 0 .stabstr 3093 0 .comment 17 0 Total 4463Check that... The Tiny861 requires 4 more bytes of code-space to do the exact same thing.
Is there a lesson, here? Nah... just, remember that the instruction-set may have something to do with code-space-usage. (And, maybe, if you've designed something on a Tiny AVR that *just* exceeds 1024 Bytes, then you might be able to recompile it for a Mega AVR and save a few bytes...)
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.