Adding variable causes program to stop working at runtime

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

Hello all, first I just wanted to ask which compiler is best? Just kidding! I actually have a bit of an odd problem that I was hoping I could get some opinions on. I've been working on a project for several months using a mega2560 as my development target (board by arduino). This project involves talking to several different devices over the UARTs and I2c bus. The project has grown in complexity and everything has been going pretty well until today.

 

Today I have started adding in some more functionality and now I find that my program won't run on the hardware anymore. After some debug I have found that merely declaring & initializing a a few new 8-bit variables (global or local scope) makes the difference between the program running or not. For example I can add/remove a dummy variable from the code and recompile; and the application will then run, or not, on the hardware depending on its presence. I also found that I can add/subtract characters from a string and affect the same behavior. 

 

I'm a little bit stumped as to why that should be. I have tried to rule out a few things but first some specifics. I'm programming in C, compiled using the 8-bit GCC toolchain from Atmel Studio 6.2 (latest version available through the extension manager). I also should mention that I'm using the FreeRTOS kernel for the mega that felipu has been maintaining over on Sourceforge. 

 

My first thought was that it might be that I have filled up the entire flash memory, but this is definitely not the case. I can change the optimization level of the compiler too -O0 (from -Os) and the size of the binary grows by 30-40%; but the behavior remains exactly the same. (avrdude tells me that it is writing ~48000 bytes to flash at the higher optimization level and ~67000 bytes at the lower, either way well below the 256k on the chip.. and programming through atmel studio yields identical behavior to avrdude).

 

I also thought it might be a RAM issue; but I don't think this is the case. I have done two things toward this end. First I have observed that declaring global variables will cause the behavior the same as local variables within a function called by a freeRTOS task; so it is not a stack overrun inside one of my freeRTOS processes. And in conjunction I lowered the heap size available for freeRTOS (from 4608 to 4096 bytes) to free up RAM in general, which of course did not fix anything either. My program is not a memory hog anyway, I estimate that I'm using 1.5k-2k of the 8k on the system for my code, plus whatever the overhead for FreeRTOS is.

 

Finally I have scoured the program for programming errors involving incorrect memory access, such as forgetting to dereference a pointer etc. I can't find anything. (Side note; as per the faq at the top of this board, no I have not tried to simulate operation)

 

Does anyone have any advice or nuggets of wisdom that might help? This is a program that's been working very well for several weeks at least and I don't understand why simply adding variables without even manipulating them is causing this problem. Anyway thanks for your time!

This topic has a solution.
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Task stack size?
you might be over running arrays
Try and narrow down what task and look at the map file to see how things move around when you add/subtract variables.

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

Well, you've checked for everything I can think of...

 

You're storing any static strings in __flash so they're not eating up ram?

 

What's the output of the compiler say? Any clues there?

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

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

Kartman wrote:

Task stack size?
you might be over running arrays
Try and narrow down what task and look at the map file to see how things move around when you add/subtract variables.

 

Hi kartman, thanks for the response. But shouldn't the fact that this is occurring by merely initializing a global variable, not adding something to a function within any specific task, rule out a task stack overrun? (To answer your question the task sizes are all different depending on their function, and I monitor the tasks regularly to check that their high water marks is not coming close to their stack sizes).

 

Also I have been very careful to only use the length-protected functions ie snprintf() instead of sprintf() when dealing with strings -- which are the only arrays in my program. Or am i missing something?

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

Torby wrote:

Well, you've checked for everything I can think of...

 

You're storing any static strings in __flash so they're not eating up ram?

 

Hmm this could be part of the problem. In general I have string literals defined in the code within functions.. For example:

 

void display_error( void )

{

    uart1_printf("You have chosen an incorrect menu option. Choose again\r\n");

}

 

main()

{

    if( input != valid_choice )

        display_error();

}

 

My reasoning is that the error string would only be loaded into ram when the display_error() function call occurs, and then removed from ram. Is there a better way to do this?

 

Torby wrote:

What's the output of the compiler say? Any clues there?

 

The compiler gives a few warnings that I think should be unrelated. They have always been there & aren't new to anything I've done today. One of them is where I cast a void pointer to a local 16-bit variable inside a freeRTOS timer callback function (it doesn't like that change in data width). The other is the following, which is associated with the freeRTOS timer daemon:

 

../freeRTOS820/include/timers.h:1062:12: note: expected 'PendedFunction_t' but argument is of type 'void (*)(void *)'
 BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken );

 

Thanks for the response. 

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

y reasoning is that the error string would only be loaded into ram when the display_error() function call occurs,

I don't thin so, check your RAM in the simulator or debugger to see if it is loaded.

Is there a better way to do this?

Use fprintf_P

fprintf_P (com1, PSTR ("Hello there!This is DXIO-62 USART 1\n"));

 

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My reasoning is that the error string would only be loaded into ram when the display_error() function call occurs, and then removed from ram

Guess again:

#include <avr/io.h>

void uart1_printf(char *);

void display_error( void )
{
    uart1_printf("You have chosen an incorrect menu option. Choose again\r\n");
}

main()
{
	if (PINA == 0x55) display_error();
}

leads to:

__SREG__ = 0x3f
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__CCP__ = 0x34
__tmp_reg__ = 0
__zero_reg__ = 1
	.data
.LC0:
	.string	"You have chosen an incorrect menu option. Choose again\r\n"
	.text
.global	display_error
	.type	display_error, @function
display_error:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	ldi r24,lo8(.LC0)
	ldi r25,hi8(.LC0)
	call uart1_printf
/* epilogue start */
	ret
	.size	display_error, .-display_error
.global	main
	.type	main, @function
main:
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
	in r24,57-32
	cpi r24,lo8(85)
	brne .L4
	call display_error
.L4:
	ret
	.size	main, .-main
.global __do_copy_data

Notice how the text of that string is placed into .data. That is a fixed area in flash that is copied out to a fixed area in RAM by the CRT. So all your strings will be occupying precious RAM.

 

Look at using either PROGMEM/PSTR or __flash for your "const char" strings and then they will remain in flash alone and be accessed using LPM when required.

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

Yea. I thought so too: It'd allocate ram from the stack, put the string there, then deallocate it when the function exits. Doesn't work that way.

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

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

put the string there

In that scenario "what?" would put the string there? If I used:

void foo(void) {
    char footext[] = "hello";
    send(footext);
}

void bar(void) {
    char bartext[] = "hello";
    send(bartext);
}

then each such function would have it's own LPM copying code or at least need code to set up to make a call to a central LPM copying routine.

 

So instead the compiler just bolts them all onto .data and lets the main _do_copy_data in the CRT do ALL the copying at once at the very start of the program. But this does mean that all such strings are out in the RAM and using it all up for the entire duration of the program.

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

This is a great info, thanks for the responses. This begs the question: how do i decide whether to allow such static strings to be resident in memory? In other words what is the overhead cost of retrieving a short string from flash? I can imagine a situation where a short string which is accessed very often would have a great overall resource cost residing on flash and being repeatedly retrieving. On the other hand a long string, or one seldom accessed, is obviously going to be better off in flash. But where to draw the line?

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

The "modern" technique is __flash which is part of the compiler.

void foo(void) {
    const __flash char footext[] = "hello";
    send(footext);
}

In this scenario send() would take a "const __flash char *" pointer or possibly a "const __memx  char *". When send does something like:

void send(const __flash char * str) {
    while (*str) {
        uart_send(*str++);
    }
}

the accesses to *str would be LPM (Load from Program Memory) rather than LDS (Load from Data Space). It's true that an LPM is 3 cycles while LDS is 2 so there is a "cost" but the addition is close to insignificant.

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

This is all great, thanks clawson for the concrete example. I now have a follow-up question. Is there an elegant way to pass the data from a string stored in program space to a function written to use/act on "standard" strings stored in data space? I'm trying to figure out whether I will need to have two copies of my serial drivers -- the existing set that transmits data from data space, plus a new set that retrieves data from program space first & then transmits. It would be easier if there was an instruction or some trick that would allow me to mix and match. Or am I stuck with something like the following:

 

#include <pgmspace.h>

 

void uart_send_from_flash( const __flash char * flashstr )

{

  char[ strlen_PF( flashstr ) + 1] tempdatastr;

  strcpy_PF( tempdatastr, flashstr );

  uart_send( tempdatastr );

}

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

Is there an elegant way to pass the data from a string stored in program space to a function written to use/act on "standard" strings stored in data space?

Remember I mentioned __memx as well as __flash above? Guess what that is for? ;-)

 

__memx or rather __memx * allows for pointing to either flash or RAM. So you can make dual routines that take either RAM or flash pointers. Allied to that is __builtin_avr_flash_segment(). You pass it a __memx pointer and it returns -1 if the pointer is to RAM.

 

See:

 

https://gcc.gnu.org/onlinedocs/g...

 

and also:

 

https://gcc.gnu.org/onlinedocs/g...

 

Note that for library functions you still need to call the _P (or _PF) variant when a __flash pointer is being used and the none _p version otherwise. So something like:
 

void mystrcpy(char * dst, const __memx char * src) {
   if (__builtin_avr_flash_segment(src) == -1) { // RAM?
      strcpy(dst, src);
   }
   else {
     strcpy_P(dst, src);
   }
}

 

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

Thanks so much all, this fixed my problem. I must have eaten up all the RAM. In addition to the static string constants mistakenly residing in RAM (my menus), my program is peppered with short string literals typed in twice as function arguments. Like this: 

 

uart1_printf( "string literal typed in\n", strlen( "string literal typed in\n") );

 

(My serial driver takes a pointer to the data to be transmitted + the length of the data). 

 

I imagine that this makes 2 copies of the same string literal, loads them both into RAM, and leaves them there forever. A little knowledge + some faulty assumptions can lead to a lot of harm!

 

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

I must have eaten up all the RAM.

 Early in the thread you were asked "what did the compiler say".  Didn't the build output give you any clue about memory usage?  (But indeed, some of the fragments indicated local buffers...)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

It did not, at least as far as I can tell. At least there is nothing that said "you have exceeded the RAM on your device". I just did a recompile to confirm this and read through all the warnings. I assume (maybe incorrectly?) that such a warning would come from the linker, or from make itself after the linker has run. But I don't see anything there, other than what I already mentioned above. 

 

Here is the final output at the end of the make process:

 

Size after:
my_program.elf  :
section            size      addr
.data              3344   8389120
.text             44724         0
.bss               4952   8392464
.stab               276         0
.stabstr             87         0
.comment             92         0
.debug_aranges     1112         0
.debug_info       57269         0
.debug_abbrev     10518         0
.debug_line       12688         0
.debug_frame       8116         0
.debug_str        11969         0
.debug_loc        46250         0
.debug_ranges      1400         0
Total            202797

 

Is the first line (.data) the RAM usage? If so perhaps this, plus the size of my freeRTOS heap ( 4608 bytes, for a combined 7952 bytes ) could have given me a clue about the problem.  However this guess that .data might correspond to RAM is something I have gleaned from this discussion thread... Is the guess correct?

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

Your RAM usage is .data + .bss and then the additional run time requirement for stack including autos. 

 

You should try avr-size - C

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

I have a follow-up question. (Perhaps this belongs in a new thread, but it is at least semi-related.) When I construct bitmasks in my code for bit shifting operations, are the constants also stored perpetually in RAM as opposed to being read from the flash when the function is called? 

 

For example I when I process incoming data packets from one of my serial interfaces, I call a function that unpacks multiple 10-bit "signals" packed into a 32-bit word. 

 

my_16bit_uint1 = (uint16_t)  (incoming_32bits & 0x000003FFUL);                       // unpack first 10 bits 

my_16bit_uint2 = (uint16_t)  ( (incoming_32bits & 0x000FFC00UL) >> 10 );       // unpack second 10 bits

my_16bit_uint3 = (uint16_t)  ( (incoming_32bits & 0x3FF00000UL) >> 20 );       // unpack third 10 bits

 

And so on. Are the three 32-bit constants in the example above taking up 12 bytes of RAM at all times, or only when the function call containing these lines of code is underway?

Last Edited: Thu. Mar 12, 2015 - 11:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Answer:  Neither.  Look at the generated code for a test program with the fragment above.  Likely the "mask" values are buried in registers via LDI.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

The masks there only live in flash:

void foo( uint32_t incoming_32bits) {
	my_16bit_uint1 = (uint16_t)  ( incoming_32bits & 0x000003FFUL);                       // unpack first 10 bits 
  7c:	9b 01       	movw	r18, r22
  7e:	33 70       	andi	r19, 0x03	; 3
  80:	30 93 65 00 	sts	0x0065, r19
  84:	20 93 64 00 	sts	0x0064, r18
	my_16bit_uint2 = (uint16_t)  ( (incoming_32bits & 0x000FFC00UL) >> 10UL );       // unpack second 10 bits
  88:	9b 01       	movw	r18, r22
  8a:	ac 01       	movw	r20, r24
  8c:	20 70       	andi	r18, 0x00	; 0
  8e:	3c 7f       	andi	r19, 0xFC	; 252
  90:	4f 70       	andi	r20, 0x0F	; 15
  92:	50 70       	andi	r21, 0x00	; 0
  94:	ea e0       	ldi	r30, 0x0A	; 10
  96:	56 95       	lsr	r21
  98:	47 95       	ror	r20
  9a:	37 95       	ror	r19
  9c:	27 95       	ror	r18
  9e:	ea 95       	dec	r30
  a0:	d1 f7       	brne	.-12     	; 0x96 <foo+0x1a>
  a2:	30 93 63 00 	sts	0x0063, r19
  a6:	20 93 62 00 	sts	0x0062, r18
	my_16bit_uint3 = (uint16_t)  ( (incoming_32bits & 0x3FF00000UL) >> 20UL );       // unpack third 10 bits
  aa:	60 70       	andi	r22, 0x00	; 0
  ac:	70 70       	andi	r23, 0x00	; 0
  ae:	80 7f       	andi	r24, 0xF0	; 240
  b0:	9f 73       	andi	r25, 0x3F	; 63
  b2:	24 e1       	ldi	r18, 0x14	; 20
  b4:	96 95       	lsr	r25
  b6:	87 95       	ror	r24
  b8:	77 95       	ror	r23
  ba:	67 95       	ror	r22
  bc:	2a 95       	dec	r18
  be:	d1 f7       	brne	.-12     	; 0xb4 <foo+0x38>
  c0:	70 93 61 00 	sts	0x0061, r23
  c4:	60 93 60 00 	sts	0x0060, r22
}
  c8:	08 95       	ret