Memory size limit and workarounds?

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

Hi, I'm having a small problem that I think is stemming form a program/data memory limit somewhere. I am using an ATmega48.

 

The problem basically originated when I tried to use large arrays for storing ADC readings (one for storing around 100 readings for a filter, and one for storing a few thousand of those filtered readings to keep a short history). After adding these arrays the flashed code seemingly did nothing. There were no warnings from avr-gcc or avrdude about any of this, however.

 

The following code is a boiled down version of the problem I was encountering. Based on riktronics' LED-blinker sample with one big array added.

#define F_CPU 8000000

#include <avr/io.h>
#include <util/delay.h>
#include <avr/power.h>
#include <stdint.h>

uint8_t bigArray[785]; // LED won't blink at 786

int main()
{
 DDRB |= (1 << PB1); // make PB1 an output
 
 while(1)
 {
 PORTB |= (1 << PB1); // turn on led at PB1
 _delay_ms(1000); // delay for a second
 PORTB &= ~(1 << PB1); //turn off led at PB1
 _delay_ms(1000); // delay for a second

 }

 return 0; // the program executed successfully
}

The command to avr-gcc is:

avr-gcc -Wall -Os -DF_CPU=8000000 -mmcu=atmega48 -c main.c -o main.o

This compiled .o is 852 bytes on disk. avrdude claims "130 bytes of flash verified" after the fact.

 

So, my question is basically: where can I tell exactly how much memory I'm using in program/data and what type of limit I'm hitting? What workarounds are there? (I've tried searching the datasheet but wasn't exactly sure where my code was being placed and how much space it would end up occupying.)

 

Thank you!

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

Mega48 only has 512 bytes of SRAM.

 

uint8_t bigArray[785]; // LED won't blink at 786

That won't even fit the memory you have. Surely you must be getting build errors! You would if you use the Atmel Studio 7.x IDE, I'll bet. The IDE will also tell you exactly how many flash bytes are used. Code begins at the bottom of code space, just above the interrupt vector table. As I understand it, the reset vector always points to the beginning of executable code space.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Oct 30, 2017 - 08:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

An ATmega48 only has 512 bytes of sram...

Although I believe in the code you provide, the array would be optimized away.

 

Edit:

For the code you provided, AS7 shows 134 bytes flash, 0 bytes data memory (sram).

Normal AVR programs will load at 0x0000 where the actual code is preceded by the vector table and the C startup code, as seen in the .lss file:

00000000 <__vectors>:
   0:	19 c0       	rjmp	.+50     	; 0x34 <__ctors_end>
   2:	28 c0       	rjmp	.+80     	; 0x54 <__bad_interrupt>
   4:	27 c0       	rjmp	.+78     	; 0x54 <__bad_interrupt>
   6:	26 c0       	rjmp	.+76     	; 0x54 <__bad_interrupt>
   8:	25 c0       	rjmp	.+74     	; 0x54 <__bad_interrupt>
   a:	24 c0       	rjmp	.+72     	; 0x54 <__bad_interrupt>
   c:	23 c0       	rjmp	.+70     	; 0x54 <__bad_interrupt>
   e:	22 c0       	rjmp	.+68     	; 0x54 <__bad_interrupt>
  10:	21 c0       	rjmp	.+66     	; 0x54 <__bad_interrupt>
  12:	20 c0       	rjmp	.+64     	; 0x54 <__bad_interrupt>
  14:	1f c0       	rjmp	.+62     	; 0x54 <__bad_interrupt>
  16:	1e c0       	rjmp	.+60     	; 0x54 <__bad_interrupt>
  18:	1d c0       	rjmp	.+58     	; 0x54 <__bad_interrupt>
  1a:	1c c0       	rjmp	.+56     	; 0x54 <__bad_interrupt>
  1c:	1b c0       	rjmp	.+54     	; 0x54 <__bad_interrupt>
  1e:	1a c0       	rjmp	.+52     	; 0x54 <__bad_interrupt>
  20:	19 c0       	rjmp	.+50     	; 0x54 <__bad_interrupt>
  22:	18 c0       	rjmp	.+48     	; 0x54 <__bad_interrupt>
  24:	17 c0       	rjmp	.+46     	; 0x54 <__bad_interrupt>
  26:	16 c0       	rjmp	.+44     	; 0x54 <__bad_interrupt>
  28:	15 c0       	rjmp	.+42     	; 0x54 <__bad_interrupt>
  2a:	14 c0       	rjmp	.+40     	; 0x54 <__bad_interrupt>
  2c:	13 c0       	rjmp	.+38     	; 0x54 <__bad_interrupt>
  2e:	12 c0       	rjmp	.+36     	; 0x54 <__bad_interrupt>
  30:	11 c0       	rjmp	.+34     	; 0x54 <__bad_interrupt>
  32:	10 c0       	rjmp	.+32     	; 0x54 <__bad_interrupt>

00000034 <__ctors_end>:
  34:	11 24       	eor	r1, r1
  36:	1f be       	out	0x3f, r1	; 63
  38:	cf ef       	ldi	r28, 0xFF	; 255
  3a:	d2 e0       	ldi	r29, 0x02	; 2
  3c:	de bf       	out	0x3e, r29	; 62
  3e:	cd bf       	out	0x3d, r28	; 61

00000040 <__do_clear_bss>:
  40:	21 e0       	ldi	r18, 0x01	; 1
  42:	a0 e0       	ldi	r26, 0x00	; 0
  44:	b1 e0       	ldi	r27, 0x01	; 1
  46:	01 c0       	rjmp	.+2      	; 0x4a <.do_clear_bss_start>

00000048 <.do_clear_bss_loop>:
  48:	1d 92       	st	X+, r1

0000004a <.do_clear_bss_start>:
  4a:	a0 30       	cpi	r26, 0x00	; 0
  4c:	b2 07       	cpc	r27, r18
  4e:	e1 f7       	brne	.-8      	; 0x48 <.do_clear_bss_loop>
  50:	02 d0       	rcall	.+4      	; 0x56 <main>
  52:	17 c0       	rjmp	.+46     	; 0x82 <_exit>

00000054 <__bad_interrupt>:
  54:	d5 cf       	rjmp	.-86     	; 0x0 <__vectors>

00000056 <main>:

uint8_t bigArray[785]; // LED won't blink at 786

int main()
{
    DDRB |= (1 << PB1); // make PB1 an output
  56:	21 9a       	sbi	0x04, 1	; 4
    
    while(1)
    {
        PORTB |= (1 << PB1); // turn on led at PB1
  58:	29 9a       	sbi	0x05, 1	; 5
	#else
		//round up by default
		__ticks_dc = (uint32_t)(ceil(fabs(__tmp)));
	#endif

	__builtin_avr_delay_cycles(__ticks_dc);
  5a:	2f ef       	ldi	r18, 0xFF	; 255
  5c:	89 e6       	ldi	r24, 0x69	; 105
  5e:	98 e1       	ldi	r25, 0x18	; 24
  60:	21 50       	subi	r18, 0x01	; 1
  62:	80 40       	sbci	r24, 0x00	; 0
  64:	90 40       	sbci	r25, 0x00	; 0
  66:	e1 f7       	brne	.-8      	; 0x60 <main+0xa>
  68:	00 c0       	rjmp	.+0      	; 0x6a <main+0x14>
  6a:	00 00       	nop
        _delay_ms(1000); // delay for a second
        PORTB &= ~(1 << PB1); //turn off led at PB1
  6c:	29 98       	cbi	0x05, 1	; 5
  6e:	2f ef       	ldi	r18, 0xFF	; 255
  70:	89 e6       	ldi	r24, 0x69	; 105
  72:	98 e1       	ldi	r25, 0x18	; 24
  74:	21 50       	subi	r18, 0x01	; 1
  76:	80 40       	sbci	r24, 0x00	; 0
  78:	90 40       	sbci	r25, 0x00	; 0
  7a:	e1 f7       	brne	.-8      	; 0x74 <main+0x1e>
  7c:	00 c0       	rjmp	.+0      	; 0x7e <main+0x28>
  7e:	00 00       	nop
  80:	eb cf       	rjmp	.-42     	; 0x58 <main+0x2>

00000082 <_exit>:
  82:	f8 94       	cli

00000084 <__stop_program>:
  84:	ff cf       	rjmp	.-2      	; 0x84 <__stop_program>

 

David (aka frog_jr)

Last Edited: Mon. Oct 30, 2017 - 09:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Empty Set wrote:
So, my question is basically: where can I tell exactly how much memory I'm using in program/data and what type of limit I'm hitting?

 

The amount of consumed flash can be positively determined at build time. You do not tell which "distribution" of the avr-gcc tool chain you use, but it likely comes with a utility called avr-size. If you run that and feed it the .elf file it will tell you the flash consumed.

 

It will also tell you how much RAM is consumed by statically allocated data. But there will be more RAM consumed at run time than that number, e.g. for the call stack and function-local variables. Determining how much RAM that is so consumed at run-time is very hard, and sometimes impossible. Most of us use a rule of thumb of some sort, and combine that with our experience. If you search around you might find advice such as "leave at least 20% RAM free for runtime allocation".

 

You allocate an array

uint8_t bigArray[785];

that is larger than the available RAM. An immediate recipe for disaster. The array needs 785 bytes, but the RAM on the mega48 is 512 bytes. As noted by frog_jr, since you never use the array it will most likely be optimized away. But as soon as you insert some code that references that array things will go really bad.

 

Depending on the complexity of your program, e.g. when it comes to function calls you might get away with an array of 400 to 450 uint8_t's, leaving the rest for some other non-complex variables and the call stack.

 

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

Last Edited: Mon. Oct 30, 2017 - 10:30 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Empty Set wrote:
(one for storing around 100 readings for a filter, and one for storing a few thousand of those filtered readings to keep a short history)

This is quite large amount of data as for 8-bit microcontroller. I can imagine a few solutions:

- use external RAM, most likely connected via SPI

- change requirements, so you don't need to store so much data

- use the microcontroller with larger RAM size

- compress data, for example if the ADC readings changed slowly, you could store only 4 bits for every one.

Data compression alone doesn't solve your problem, but if you replace ATmega48 with ATmega88 you have 1KB of RAM and if you reserve 200 bytes of RAM for the stack and global and static variables you can store more than 1600 samples.

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

MarekJ71 wrote:
This is quite large amount of data as for 8-bit microcontroller.

From the Atmel Microcontroller Product Selector, ATMega1284 has 16K RAM, and various XMegas have 32K

 

http://www.atmel.com/selector.html#(actives:!(81041,8238,8394,8362,8282,8431,8300,8358,8392,8378,8445,8236,8449,8474,8248,8264,8447,8256,8254,8286,8462,8429,8458,8466,8400,8302,8278),data:(area:'',category:'34864',pm:!((i:81041,v:!(1,2,3,4,5,6,7,8,9,10,11,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79)),(i:8238,v:!(0,19)),(i:8394,v:!(0,27)),(i:8362,v:!(2,49)),(i:8282,v:!(6)),(i:8431,v:!(25,42)),(i:8300,v:!(1,9)),(i:8358,v:!(2,75)),(i:8392,v:!(0,1)),(i:8378,v:0),(i:8445,v:!(1,4,5,6,7,9)),(i:8236,v:!(0,36)),(i:8449,v:!(1,11)),(i:8474,v:!(0)),(i:8248,v:!(0,1)),(i:8264,v:!(0,5)),(i:8447,v:!(0,1)),(i:8256,v:!(1,2,3,4)),(i:8254,v:!(3,18)),(i:8286,v:!(0,3)),(i:8462,v:!(0,10)),(i:8429,v:!(1,11)),(i:8458,v:!(0,8)),(i:8466,v:!(1,2,3,4,5)),(i:8400,v:!(0,20)),(i:8302,v:!(0,1,2)),(i:8278,v:!(0,1,2))),view:list),sc:1)

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

Hello,

 

Thanks for all the advice. I understand that I'm hitting the SRAM limit, and I might look into another solution for storing ADC readings involving less memory (maybe a running average, or cautious use of flash/eeprom). I was expecting the compiler to warn about this limit, especially with -Wall, but again it was entirely silent on the matter.

 

I ran avr-size on my compiled file and got:

text    data     bss     dec     hex filename
 130       0     785     915     393 main.elf

Seems like the memory is still 'allocated' to bss, and at runtime something odd happens when this allocation tries to happen that causes the code to not execute properly.

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

Empty Set wrote:

Hello,

 

Thanks for all the advice. I understand that I'm hitting the SRAM limit, and I might look into another solution for storing ADC readings involving less memory (maybe a running average, or cautious use of flash/eeprom). I was expecting the compiler to warn about this limit, especially with -Wall, but again it was entirely silent on the matter.

 

I ran avr-size on my compiled file and got:

text    data     bss     dec     hex filename
 130       0     785     915     393 main.elf

Seems like the memory is still 'allocated' to bss, and at runtime something odd happens when this allocation tries to happen that causes the code to not execute properly.

This is because your large array has been optimized out. The compiler and linker are smart enough to realize that it is not used. You have to use the array in a way that will prevent it from being optimized out. I think it will be enough if you declare the array as volatile.

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

Perhaps store your data in the eeprom, 1k IIRC!

 

Jim

 

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

one for storing around 100 readings for a filter

Why would you need to store 100 readings for a filter?  What kind of filter? 

When in the dark remember-the future looks brighter than ever.

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

Empty Set wrote:
I'm hitting the SRAM limit, and I might look into another solution for storing ADC readings involving less memory

Or look for another chip with more memory.

 

It sounds like you are doing a data-intensive application - so why did you choose a chip with such a tiny amount of RAM??

 

It's not like chips with lots of RAM are hard to find or expensive these days ...

 

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

The ultimate goal is to make a control decision based on a sensor's input and some timing parameters.

 

avrcandies wrote:

one for storing around 100 readings for a filter

Why would you need to store 100 readings for a filter?  What kind of filter? 

There's no strict requirement here, and I'm probably running the ADC too fast as a result. That said, I think the larger bottleneck for memory usage isn't the samples for the filter, even at +100, but for the history which needs to be around 20 seconds or so. The thought was to aggregate at ~8ms currently, which is probably much too fast for the application, but I'm trying to err on the fast side in observation of other possible sources of latency in my system and slow things down after I get past a proof-of-concept.

 

While 'erring on the fast side' I may still reduce that sample rate to 32 or 64ms which would greatly reduce data usage, since it seems there is this limitation on the micro. I could use another one too, but that would probably only be necessary if a lower-data solution can't provide the accuracy needed.

 

I'll look into this on my own and come back if I have any further issues working around this limit. Thanks for the help!

Last Edited: Tue. Oct 31, 2017 - 03:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have a look at adding a FRAM memory chip and your speed and memory issues may well be resolved.

 

JC

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

Empty Set wrote:
 it seems (sic) there is this limitation on the micro

There is no "seems" about it:  the  ATmega48 most certainly has only 512 bytes of SRAM - it is clearly stated in the product documentation.

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

Filter---There's no strict requirement here

 Are you doing averaging? If so, no need to store individual samples.  A few types like median & mode would require storage.

When in the dark remember-the future looks brighter than ever.

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

awneil wrote:

Empty Set wrote:
 it seems (sic) there is this limitation on the micro

There is no "seems" about it:  the  ATmega48 most certainly has only 512 bytes of SRAM - it is clearly stated in the product documentation.

 

I tend to use indefinite language but yes, I understand that smiley