Yahoo Groups archive

AVR-Chat

Index last updated: 2026-04-28 22:41 UTC

Thread

WinAVR / GCC question re Stack

WinAVR / GCC question re Stack

2009-03-24 by David VanHorn

How can I assign a variable to a specific point in SRAM?

What I'm trying to do, is implement a guard against stack growing down
into the data.
Ideally, I'd allocate some variable to a point X bytes below the top
of SRAM, and above the top of allocated memory, and watch it for any
changes after init.

Or, are there better methods?

-- 

"The very powerful and the very stupid have one thing in common. Instead of
altering their views to fit the facts, they alter the facts to fit their
views... which can be very uncomfortable if you happen to be one of the
facts that needs altering." Doctor Who, Face of Evil

Re: WinAVR / GCC question re Stack

2009-03-24 by Don Kinzer

--- In AVR-Chat@yahoogroups.com, David VanHorn <microbrix@...> wrote:
> How can I assign a variable to a specific point in SRAM?
The simplest way to accomplish this is to use a pointer and initialize the pointer to the address of interest.

// assign the sentinel address
uint8_t *sentinel = (uint8_t *)0x234;

// set the sentinel value
*sentinel = 0xcc;

// check the sentinel value
if (*sentinel != 0xcc)
{
  // stack overrun occurred
}

Another alternative is to initialize all of the RAM between the end of statically allocated data and the end of RAM to a given value.  After this is done, you can scan upward from the end of statically allocated data to find the first byte that is not the initialization value, thus giving you the "high water mark" for the stack at that time.  Some example code for initializing the stack area is shown below.  This code assumes standard stack location and would need to be modified if you're using some non-standard setup.

#include <inttypes.h>
#include <avr/io.h>

#define INIT2 __attribute__((section(".init2")))
#define USED __attribute__((used))
#define NAKED __attribute__((naked))

extern uint8_t _end;
extern uint8_t __stack;

void myStackInit(void) USED NAKED INIT2;
void myStackInit(void)
{
  uint8_t *p = &_end;
  while (p != &__stack)
    *p++ = 0xcc;
}

Re: [AVR-Chat] WinAVR / GCC question re Stack

2009-03-24 by David Kelly

On Tue, Mar 24, 2009 at 12:40:32PM -0400, David VanHorn wrote:
> How can I assign a variable to a specific point in SRAM?

Declare a pointer and set its value to the address you want to read.

Study the AVR register definitions for syntax to use so that your
pointer doesn't consume RAM for storing the address of your point of
interest. 

In avr/sfr_defs.h we find this which should tell you how to do it:

#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))

-- 
David Kelly N4HHE, dkelly@HiWAAY.net
========================================================================
Whom computers would destroy, they must first drive mad.

Re: [AVR-Chat] Re: WinAVR / GCC question re Stack

2009-03-24 by David VanHorn

> // assign the sentinel address
> uint8_t *sentinel = (uint8_t *)0x234;
>
> // set the sentinel value
> *sentinel = 0xcc;
>
> // check the sentinel value
> if (*sentinel != 0xcc)
> {
>  // stack overrun occurred
> }

Ok, that does what I wanted, thanks.  :)

That brings up another question though..

I'd been using lcd_puts to send constant strings to the display, and I
added another one to indicate a stack problem.  But now I'm chewing up
more ram..

How do I use strings in program space, instead of having the system
copy them into ram, on boot, and eat up that ram permanently?





-- 
There is no computer problem which cannot be solved by proper
application of a sufficiently large hammer.

Re: WinAVR / GCC question re Stack

2009-03-24 by Don Kinzer

--- In AVR-Chat@yahoogroups.com, David VanHorn <microbrix@...> wrote:
> How do I use strings in program space, instead of having the system
> copy them into ram, on boot, and eat up that ram permanently?
See the file avr/pgmspace.h.  Also, there is a tutorial on the subject:
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=38003

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

Re: WinAVR / GCC question re Stack

2009-03-25 by Graham Davies

--- In AVR-Chat@yahoogroups.com, David VanHorn <microbrix@...> wrote:
>
> ... pgm_read_byte_near is returning
> values that aren't anything like
> what's in the string ...
>  LCD_Data_A = pgm_read_byte_near(BootString[i]);
>  lcd_putc(LCD_Data_A);

You're passing to pgm_read_byte_near() character values taken from data memory at the address of BootString in program memory.  I would expect some kind of warning from the compiler about the implicit cast from the char value to the pointer expected as the argument.  Do you have all the warnings turned on?

You need to pass to pgm_read_byte_near() the address of the character you want to read, thus:
LCD_Data_A = pgm_read_byte_near( &(BootString[i]) );

Graham.

Re: WinAVR / GCC question re Stack

2009-03-25 by Don Kinzer

--- In AVR-Chat@yahoogroups.com, David VanHorn <microbrix@...> wrote:
> lcd_putc(pgm_read_byte_near(&(BootString[i])));
> what was I passing it without the &?
The ampersand is the "address of" operator.  Without it, the value being passed was the byte in RAM whose address happens to be the same as the address of the i-th element of the array in Flash.

You may not get a warning for this in C but you probably would in C++ which is much more type-strict.

By the way, some would prefer to write the statement above slightly differently, e.g.
lcd_putc(pgm_read_byte_near(BootString + i));

This works correctly because the name of an array evaluates to the address of the array and adding an index to the address is done in terms of the array element size.  Consequently "BootString + i" is exactly equivalent to "&BootString[i]".

Similarly, "array[i]" is exactly equivalent to "*(array + i)".  Here, the asterisk serves as the "indirection" operator.

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

Re: [AVR-Chat] Re: WinAVR / GCC question re Stack

2009-03-25 by David VanHorn

This almost seems to work, but the string is stored in pgm space, but
pgm_read_byte_near is returning values that aren't anything like
what's in the string.

const char BootString[] PROGMEM =   " Signon Msg  ";

for (i=0; 16 > i; i++)	{
 LCD_Data_A = pgm_read_byte_near(BootString[i]);
 lcd_putc(LCD_Data_A);
}

-- 
There is no computer problem which cannot be solved by proper
application of a sufficiently large hammer.

Re: [AVR-Chat] Re: WinAVR / GCC question re Stack

2009-03-25 by David VanHorn

> You're passing to pgm_read_byte_near() character values taken from data memory at the address of BootString in program memory.  I would expect some kind of warning from the compiler about the implicit cast from the char value to the pointer expected as the argument.  Do you have all the warnings turned on?

Well.. I thought so..  :)

Casting is something that I'm not up on yet.


> You need to pass to pgm_read_byte_near() the address of the character you want to read, thus:
> LCD_Data_A = pgm_read_byte_near( &(BootString[i]) );

Ah.. So the final form, since I was using LCD_Data_A as an
intermediate to see what was being passed, is:

for (i=0; 16 > i; i++)	{
  lcd_putc(pgm_read_byte_near(&(BootString[i])));
}

So since this is the address of the char, what was I passing it without the &?

> Graham.
>
>
>
>
> ------------------------------------
>
> Yahoo! Groups Links
>
>
>
>



-- 
There is no computer problem which cannot be solved by proper
application of a sufficiently large hammer.

Re: WinAVR / GCC question re Stack

2009-03-25 by Graham Davies

--- In AVR-Chat@yahoogroups.com, David VanHorn <microbrix@...> wrote:

> ... what was I passing it without the &?

You were passing BootString[i], which is the value of the i'th element of the array BootString.  Since you've put BootString in program memory, the address of the array is an address in program memory.  But, since the C language doesn't know about different address spaces (not even I/O addresses as distinct from memory addresses), it treats the address as if it were in data memory.  So, you're getting a value from a piece of memory that doesn't exist.

The "&" operator is "take the address of".  So, &(BootString[i]) is
the address of the i'th element of the array BootString.  Now, once again, this will be an address in program memory.  But, the function (or maybe macro) you're passing it to understands that and jumps through a hoop or two to make sure the data is retrieved from program memory and not data memory (which is why the function exists).

> Casting is something that I'm not up on yet.

Casting is taking a variable of one type and treating it as if it were of a different type.  You do this in assembler all the time.  A strength of C is that the compiler is supposed to protect you from doing it when you don't mean to, as in the above case.  An "implicit" cast is one that you don't write but that happens anyway to match up the types.  These can be benign (such as treating an 8-bit integer as a 16-bit integer when necessary), wasteful and confusing (such as treating an 8-bit integer as a 16-bit integer when required by the language definition but not strictly necessary), or dangerous and probably wrong, as in your case.

With decades of high-level language programming experience behind me, I have a policy of avoiding implicit casts.  In other words, I write my casts out in the code.  Then, I can see what the compiler is going to do because I'm telling it exactly.  These are called "explicit" casts.  You might want to consider this.

I should also point out (and by the length of my replies, I'm sure you've figured out that I'm between jobs right now) that the parentheses around BootString[i] are not actually necessary.  The array post-fix operator [] has higher precedence than the address-of pre-fix operator &, so the array element will be located before the address is taken.  Again, experience has led me never to rely on the operator precedence, because why bother to remember it, and instead I explicitly write out the order in which I want operators applied using parentheses.  This makes my firmware as verbose as my posts, but at least I can tell what it's going to do.

Graham.

Re: WinAVR / GCC question re Stack

2009-03-25 by Graham Davies

--- In AVR-Chat@yahoogroups.com, "Don Kinzer" <dkinzer@...> wrote:
>
> By the way, some would prefer to
> write the statement above slightly
> differently, e.g.
> lcd_putc(pgm_read_byte_near(BootString + i));

Yes indeed, and I would try to keep those people away from someone learning the C language.  I am a strong advocate of writing the code in a way that explains what you expect it to do.  If you're locating an element in an array, I believe that you should use array notation to do it, not pointer arithmetic.  This avoids, for example, the inevitable confusion of inexperienced C programmers when they find that p += 1 may add two or four to p, depending on the size of the object it points to.

Pleeeeeeeeeease, don't let's start an argument about this.  I put the above forward only as my opinion (although not humble - sorry) and fully accept the validity of other people's opinions.

Graham.

Re: WinAVR / GCC question re Stack

2009-03-25 by Don Kinzer

--- In AVR-Chat@yahoogroups.com, "Graham Davies" <Yahoo37849@...> wrote:
>Pleeeeeeeeeease, don't let's start an argument about this.
The larger issue is that learning idiomatic expressions is an important part of the process of learning any language, whether it is a natural language or a computer language.  Granted, you don't begin with just the idioms but they do need to be introduced as the student progresses.

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net

Re: [AVR-Chat] Re: WinAVR / GCC question re Stack

2009-03-25 by David VanHorn

> Yes indeed, and I would try to keep those people away from someone learning the C language.

I resemble that remark, and I agree!  :)


I'm still working my way through all the wonderful features of C, and
definitely want to take it one step at a time.


-- 
There is no computer problem which cannot be solved by proper
application of a sufficiently large hammer.

Re: WinAVR / GCC question re Stack

2009-03-25 by Graham Davies

--- In AVR-Chat@yahoogroups.com, David VanHorn <microbrix@...> wrote:
> 
> lcd_putc(pgm_read_byte_near(&(BootString[i])));
> So this is dangerous?

Ah, no.  *Without* the & it was "dangerous" in the sense that it wasn't what you meant.  That is, it was a "coding error".  At some point I will take a look at why the compiler didn't yell at you.  Sorry for not being clear.

Graham.

Re: [AVR-Chat] Re: WinAVR / GCC question re Stack

2009-03-25 by David VanHorn

>A strength of C is that the compiler is supposed to protect you from doing it when you don't mean to, as in the above case.  An "implicit" cast is one that you don't write but that happens anyway to match up the types.  These can be benign (such as treating an 8-bit integer as a 16-bit integer when necessary), wasteful and confusing (such as treating an 8-bit integer as a 16-bit integer when required by the language definition but not strictly necessary), or dangerous and probably wrong, as in your case.

lcd_putc(pgm_read_byte_near(&(BootString[i])));
So this is dangerous?

The way I read this now, is:
Take the address of the ith element of BootString and pass that to
pgm_read_byte_near.
Pass to lcd_put_c the byte returned by pgm_read_byte_near.


> With decades of high-level language programming experience behind me, I have a policy of avoiding implicit casts.  In other words, I write my casts out in the code.  Then, I can see what the compiler is going to do because I'm telling it exactly.  These are called "explicit" casts.  You might want to consider this.

Sounds like a very good idea.

> I should also point out (and by the length of my replies, I'm sure you've figured out that I'm between jobs right now) that the parentheses around BootString[i] are not actually necessary.

I believe in forcing the order of operations.


-- 
There is no computer problem which cannot be solved by proper
application of a sufficiently large hammer.

Re: [AVR-Chat] Re: WinAVR / GCC question re Stack

2009-03-25 by David Kelly

On Wed, Mar 25, 2009 at 04:06:21PM -0000, Graham Davies wrote:
> --- In AVR-Chat@yahoogroups.com, David VanHorn <microbrix@...> wrote:
> 
> > Casting is something that I'm not up on yet.
> 
> Casting is taking a variable of one type and treating it as if it were
> of a different type.  You do this in assembler all the time.  A
> strength of C is that the compiler is supposed to protect you from
> doing it when you don't mean to, as in the above case.  An "implicit"
> cast is one that you don't write but that happens anyway to match up
> the types.  These can be benign (such as treating an 8-bit integer as
> a 16-bit integer when necessary), wasteful and confusing (such as
> treating an 8-bit integer as a 16-bit integer when required by the
> language definition but not strictly necessary), or dangerous and
> probably wrong, as in your case.
> 
> With decades of high-level language programming experience behind me,
> I have a policy of avoiding implicit casts.  In other words, I write
> my casts out in the code.  Then, I can see what the compiler is going
> to do because I'm telling it exactly.  These are called "explicit"
> casts.  You might want to consider this.

Casts can get ugly pretty quick, especially when using pointers. I
usually end up defining a UNION32_t typedef before a project is finished
so that I can mix types and reach inside values easier.

typedef union {
	struct {
		uint8_t	a,
			b,
			c,
			d;
	} u8;
	struct {
		uint16_t ab,
			 cd;
	} u16;
	uint32_t	u32;
	int32_t		i32;
} UNION32_t;

Then I can declare "UNION32_t tmp;" and use it as tmp.u32 or tmp.i32 or
just use parts from inside with tmp.u8.a, or whatever.

Before copying this into your own code spend some time thinking about
tmp.u16.ab and whether its spelled right and in the right location to
pick up bytes a and b, or b and a, or whether or not the first should be
c and d. Am pretty sure as shown its not right for an AVR.

-- 
David Kelly N4HHE, dkelly@HiWAAY.net
========================================================================
Whom computers would destroy, they must first drive mad.

Re: [AVR-Chat] Re: WinAVR / GCC question re Stack

2009-03-26 by Jim Wagner

On Mar 25, 2009, at 10:58 AM, David Kelly wrote:

> On Wed, Mar 25, 2009 at 04:06:21PM -0000, Graham Davies wrote:
> > --- In AVR-Chat@yahoogroups.com, David VanHorn <microbrix@...>  
> wrote:
> >
> > > Casting is something that I'm not up on yet.
> >
> > Casting is taking a variable of one type and treating it as if it  
> were
> > of a different type. You do this in assembler all the time. A
> > strength of C is that the compiler is supposed to protect you from
> > doing it when you don't mean to, as in the above case. An "implicit"
> > cast is one that you don't write but that happens anyway to match up
> > the types. These can be benign (such as treating an 8-bit integer as
> > a 16-bit integer when necessary), wasteful and confusing (such as
> > treating an 8-bit integer as a 16-bit integer when required by the
> > language definition but not strictly necessary), or dangerous and
> > probably wrong, as in your case.
> >
> > With decades of high-level language programming experience behind  
> me,
> > I have a policy of avoiding implicit casts. In other words, I write
> > my casts out in the code. Then, I can see what the compiler is going
> > to do because I'm telling it exactly. These are called "explicit"
> > casts. You might want to consider this.
>
> Casts can get ugly pretty quick, especially when using pointers. I
> usually end up defining a UNION32_t typedef before a project is  
> finished
> so that I can mix types and reach inside values easier.
>
> typedef union {
> struct {
> uint8_t	a,
> b,
> c,
> d;
> } u8;
> struct {
> uint16_t ab,
> cd;
> } u16;
> uint32_t	u32;
> int32_t	 i32;
> } UNION32_t;
>
> Then I can declare "UNION32_t tmp;" and use it as tmp.u32 or tmp.i32  
> or
> just use parts from inside with tmp.u8.a, or whatever.
>
> Before copying this into your own code spend some time thinking about
> tmp.u16.ab and whether its spelled right and in the right location to
> pick up bytes a and b, or b and a, or whether or not the first  
> should be
> c and d. Am pretty sure as shown its not right for an AVR.
>
> -- 
> David Kelly N4HHE, dkelly@HiWAAY.net
> = 
> = 
> ======================================================================
> Whom computers would destroy, they must first drive mad.
>
> 
Thanks, everyone for this great discussion. I am having many of the  
same problems that David van Horn is and this is a great help.

Jim Wagner
Oregon Research Electronics
KA7EHK

[Non-text portions of this message have been removed]

Move to quarantaine

This moves the raw source file on disk only. The archive index is not changed automatically, so you still need to run a manual refresh afterward.