2
\$\begingroup\$

I am using AtmelStudio to program an ATtiny85, which has 512 bytes of SRAM data memory.

My code includes a large lookup-table (array) which is 512 bytes:

const uint16_t lut[256] = { ... };

The project has no other global or static data, so when I compile it, AtmelStudio reports:

"C:\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\bin\avr-size.exe" "mcp4821_attiny85.elf"
           text    data     bss     dec     hex filename
            242     512       0     754     2f2 mcp4821_attiny85.elf
    Done executing task "RunCompilerTask".
    Using "RunOutputFileVerifyTask" task from assembly "C:\Atmel\Studio\7.0\Extensions\Application\AvrGCC.dll".
    Task "RunOutputFileVerifyTask"
                Program Memory Usage    :   754 bytes   9.2 % Full
                Data Memory Usage       :   512 bytes   100.0 % Full
    Done executing task "RunOutputFileVerifyTask".

As shown, AtmelStudio reports that I have exactly filled my available data memory space. If I make the lut array one element larger, the compiler alerts me of the memory overflow. Conversely, if I make the array one smaller, the compiler reports that I have room to breathe (of two bytes).

However, when I used the code to program the chip, I found that there was indeed a memory corruption, and I received bad data when reading the last 16 bytes of the array.

For testing, the LUT is just a sine wave - and the microcontroller is writing the LUT to a DAC. The glitches in the image below reveal the erroneous data from the bad memory accesses (sorry for the poor image quality):

image showing glitches from bad data

I resolved this problem by following the great advice found here: Memory management problems with ATTiny85

Changing

const uint16_t lut[256] = { ... };

to

#include <avr/pgmspace.h>
const PROGMEM uint16_t lut[256] = { ... };

and using

pgm_read_word()

to access the array elements. But I'm not sure why this is necessary.

My questions are:

  • why doesn't the array fit into the data memory even though AtmelStudio reports no error.
  • and most importantly, why does AtmelStudio report no error?

I suspect the answer to both is related to stack space, but I'm not sure. Thanks.

\$\endgroup\$
  • 1
    \$\begingroup\$ Looks like C. A C compiler almost always requires stack space. Excepting oddball cases like some PIC controllers where they stack return addresses in a tiny 4-entry hardware stack or where the C compiler does hand-stands and tail-spins to avoid the stack (as in some 8051 compilers), the return stack and parameter passing is performed via the stack (in RAM.) Therefore, it is very likely your corruption is for exactly the reason you state. Given your information about where (end of RAM) and the size of the corruption. I think you nailed it. The only issue is that the linker should have said so. \$\endgroup\$ – jonk Jul 7 '18 at 4:35
  • \$\begingroup\$ @jonk copy all that and thanks. It would have saved me hours had your last sentence been true. \$\endgroup\$ – Blair Fonville Jul 7 '18 at 14:50
2
\$\begingroup\$

Even if you define your variables as const, the compiler will still put them into the AVR's RAM. Only if you define your variables with the PROGMEM attribute will it keep the variables only in flash and read them from there.

Your array fits right into the RAM, completely filling it. But as you already suspected, running your main or other functions will use part of the RAM for the stack, probably overwriting your LUT. This is called stack collision and can not be predicted by AtmelStudio, as it has no way of knowing which functions you will call and how often.

If you really need all 512 RAM Bytes and don't want to load from flash, you could write the code to drive the DAC from the LUT in assembler code without using a stack.

For illustration

stack space

graphic borrowed from here

\$\endgroup\$
  • 1
    \$\begingroup\$ Stack collision cannot be entirely predicted, but a well designed linker script would normally be written to make some sane reservations and error at the situation in this question where compile time allocations leave no room for a stack at all. Perhaps given the extremely tight RAM constraint the linker script shipped for this specific part doesn't have those checks. \$\endgroup\$ – Chris Stratton Jul 7 '18 at 15:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.