stack initialization / addressing mode rant

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

Hi!

While coding assembly I noticed stack initialization code and wondered if that is really necessary. If at boot SP=0 and the stack used pre-decrement for push/call, then the data would go to RAMEND and no stack initialization were necessary.

However, the stack uses post-decrement and pre-increment [1] and thus requires initialization (if one doesn't want the beginning of RAM used as stack).

Well, I thought, maybe that's because this addressing mode is used with other instructions. But rather it looks like it's exclusively used for the stack, while pre-decrement and post-increment is used with ST, LD, LDM, SPM.

Why does Atmel use post-decrement and pre-increment for the stack?

[1]
AVR Instruction Set (http://www.atmel.com/dyn/resourc...):

"The Stack Pointer uses a post-decrement scheme during CALL."
"The Stack Pointer uses a pre-increment scheme during RET."

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

Quote:

Why does Atmel use post-decrement and pre-increment for the stack?

So that SP points unambiguously at the next free space on the stack.

Cheers,

Joey

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

joeyAVR wrote:
Quote:

Why does Atmel use post-decrement and pre-increment for the stack?

So that SP points unambiguously at the next free space on the stack.

How is that better than pointing unambigously at the lowest data on the stack? Other than requiring stack initialization.

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

Quote:

How is that better than pointing unambigously at the lowest data on the stack? Other than requiring stack initialization.

What does it matter? It's like endianism - a hidden detail that no one need ever normally worry about. The only person concerned about the exact operation of SP is going to be those trying to decode a stack frame - I don't imagine it happens that often? (and as long as you read the book and do what it says your code will work anyway).

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

darsie wrote:
If at boot SP=0 and the stack used pre-decrement for push/call, then the data would go to RAMEND and no stack initialization were necessary.
Wrong. The stack pointer would decrement from 0 to 0xffff, and not to RAMEND.

Stefan Ernst

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

A few responses:

1) Name a modern AVR model that doesn't have the SP pointing to RAMEND at reset.

Quote:

If at boot SP=0 and the stack used pre-decrement for push/call, then the data would go to RAMEND and no stack initialization were necessary.

2) Subtract 2 (say) from 0. What makes you think that would be RAMEND?

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

clawson wrote:
Quote:

How is that better than pointing unambigously at the lowest data on the stack? Other than requiring stack initialization.
What does it matter?

Sometimes people struggle for every word. 4 words could be saved [1] if the stack were 0 at boot and would pre-decrement to store the first data at RAMEND. Saves a few clock cycles, too.

[1]

00000068 <__ctors_end>:
  68:   11 24           eor     r1, r1
  6a:   1f be           out     0x3f, r1        ; 63
  6c:   cf ef           ldi     r28, 0xFF       ; 255
  6e:   d4 e0           ldi     r29, 0x04       ; 4
  70:   de bf           out     0x3e, r29       ; 62
  72:   cd bf           out     0x3d, r28       ; 61
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh, an inspection of navel fluff? I get it.

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

sternst wrote:
darsie wrote:
If at boot SP=0 and the stack used pre-decrement for push/call, then the data would go to RAMEND and no stack initialization were necessary.
Wrong. The stack pointer would decrement from 0 to 0xffff, and not to RAMEND.

The ATmega168 datasheet says:

Quote:
The AVR Stack Pointer is implemented as two 8-bit registers in the I/O space. The number of
bits actually used is implementation dependent. Note that the data space in some implementations
of the AVR architecture is so small that only SPL is needed. In this case, the SPH Register
will not be present.

It appears to me, that only as many bits are implemented as required to address the RAM. An 8 bit SP would then decrement from 0 to 0xFF. Yet, if it were any larger it would still address the right locations, IMO.

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

theusch wrote:
A few responses:

1) Name a modern AVR model that doesn't have the SP pointing to RAMEND at reset.

Well, I haven't verified it, but I guess gcc-avr (gcc-as) put's the initialization code there because the ATmega168 resets SP to 0 on boot.

The ATmega168 datasheet says:

"This Stack space in the data SRAM must be defined by the program before
any subroutine calls are executed or interrupts are enabled. The Stack Pointer must be set to point above 0x0100, preferably RAMEND."

Does the ATmega168 reset SP to RAMEND on boot automatically?

theusch wrote:

Quote:

If at boot SP=0 and the stack used pre-decrement for push/call, then the data would go to RAMEND and no stack initialization were necessary.

2) Subtract 2 (say) from 0. What makes you think that would be RAMEND?

I assumed the usual binary arithmetic with the SP.

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

darsie wrote:
Sometimes people struggle for every word. 4 words could be saved [1] if the stack were 0 at boot and would pre-decrement to store the first data at RAMEND. Saves a few clock cycles, too.
If it's the savings of code you're interested in, wouldn't it be sufficient if the SP were initialized to contain whatever value results in the first pushed item going into the top addressable place in RAM? If so, then rejoice, for all of the modern AVR chips behave that way. I haven't made a study of which chips were the first to get this feature, but check your parts's spec sheet.

But a great many applications are writen in C, making a four-instruction startup initialization of SP insignificant. If those four instructions swell your application beyond the memory bounds, all you need do is find one place where an "int" variable was lazily used instead of a single-byte one, and you're home (assuming you'd be crazy enough to go to market with zero room for maintenance patches).

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

Quote:

It appears to me, that only as many bits are implemented as required to address the RAM.

OK, see my points 1) and 2) above. In the AVR model that you quoted, your rant is moot as SP is not 0 at reset. In other AVR models with external memory capabilities, 0xffff could indeed be a valid stack address but you are not likely to have SRAM there--and/or you don't normally want your stack in external SRAM anyway.

Note that conceptually there is more to it than just the initial value. All the pertinent instructions must "work together" to achieve the scheme/model that the chip designer has put together.

In any case, Atmel addressed your rant some years ago when all [AFAIK] new models set SP to RAMEND.

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

Quote:
It appears to me, that only as many bits are implemented as required to address the RAM. An 8 bit SP would then decrement from 0 to 0xFF. Yet, if it were any larger it would still address the right locations, IMO.
Let's assume the stack pointer of a ATmega8 has exactly the 11 minimum required bits, then it wraps from 0 to 0x07ff. Why do you think 0x045f (RAMEND) is accessed when the stack pointer is 0x07ff?

Stefan Ernst

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

I wonder if Studio 4 Sim2 can be believed. When you start simulating a mega168 it says SP=0x4FF, then this happens...

Attachment(s): 

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

darsie wrote:
Sometimes people struggle for every word. 4 words could be saved [1] if the stack were 0 at boot and would pre-decrement to store the first data at RAMEND. Saves a few clock cycles, too.

This gets to the real issue. If you've got the code space, initializing the stack isn't a problem. If you're so close to running out of code space that 4 words are critical, then you have a much bigger problem than initializing the stack, because even if you don't run out of code space today, you WILL run out soon, whether you have to initialize the stack or not.

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

sternst wrote:
Let's assume the stack pointer of a ATmega8 has exactly the 11 minimum required bits, then it wraps from 0 to 0x07ff. Why do you think 0x045f (RAMEND) is accessed when the stack pointer is 0x07ff?

Ohh, I see. I forgot about the other stuff mapped into the address space. Thank you.

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

theusch wrote:
In any case, Atmel addressed your rant some years ago when all [AFAIK] new models set SP to RAMEND.

That's cool. I guess avr-gcc should drop the stack initialization then.

Thanks.

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

Quote:

I guess avr-gcc should drop the stack initialization then.

Anyone who doesn't want it has -nostartfiles in their toolbox. Feel free to replace the entire CRT with cherry picked goodies you want to use.

(but we are talking about C programming here? if bytes are that tight why not go for Asm and take complete control of everything?)

PS AVR-LiBC is open source and available for anyone to submit patches. If you wanted to submit one which identified which models need SP initialisation and only provide the code for those I'm sure it would be considered by the AVR-LibC team. HOWEVER note that not every arrival at 0 is necessarily a POR with SP=default and some might like the assurance that it WAS being reset!

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

clawson wrote:

(but we are talking about C programming here? if bytes are that tight why not go for Asm and take complete control of everything?)

I use avr-gcc to compile/assemble an assembly bootloader atm. I believe that's the way it is meant to be used.

I'm not low on memory actually. I was just wondering if this stack initialization code was necessary. It also initializes SR ...

Quote:

HOWEVER note that not every arrival at 0 is necessarily a POR with SP=default and some might like the assurance that it WAS being reset!

... for the same reasons, I guess.

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

Quote:

I'm not low on memory actually. I was just wondering if this stack initialization code was necessary. It also initializes SR ..

Bootloader eh? Read this:

https://www.avrfreaks.net/index.p...

It was a kind of competition to see whether an AVR109 could be done in C in 512 bytes. I'm pretty sure it can't but it raised issues such as -nostartfiles etc.

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

darsie wrote:
theusch wrote:
In any case, Atmel addressed your rant some years ago when all [AFAIK] new models set SP to RAMEND.

That's cool. I guess avr-gcc should drop the stack initialization then.

Thanks.

No, AVR-GCC absolutely should NOT drop the stack initialization.

One of the safety-nets provided by AVR-GCC is the ability to capture all interrupt events that fire on vectors which do not have an interrupt service handler associated with them. It transfers all of these events to a default handler that "pseudo-resets" the procesor.

It's only a "pseudo-reset" because in reality there is no single catch-all technique that can safely be applied in every circumstance which could be guaranteed to generate a true reset. So instead, for all these undefined interrupts, GCC inserts a JMP back to 0x00000 -- the reset vector -- with interrupts left inhibited.

Now, when the code JMPs back to 0x00000, the start-up code will run again, re-initializing everything. (...in the hopes that whatever went wrong last time will be corrected during the re-initialization.)

Part of this re-initialization process will be to actively ensure that the stack pointer has been set up correctly -- because if we happened to end up in the reset vector due to a run-away interrupt, then it would be an absolute certainty that SP will initially have an unknown value, neither RAMEND nor 0x0000, and it will need to be initialized.

The 8 bytes and four cycles needed to perform this initialization are, in most circumstances, not prohibitively onerous. In the very rare circumstances where they might mean the difference between a successful project and a failure, the programmer has the option Cliff mentioned earlier, to omit the default start-up routines and provide their own.

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

Quote:

Does the ATmega168 reset SP to RAMEND on boot automatically?

Ummm--well yeah--what does the datasheet you are fond of quoting say about that?

But hold on, here. Let's back up the rantmobile truck to the beginning of the thread beep beep beep:

Quote:

While coding assembly I noticed stack initialization code and wondered if that is really necessary.

And now it is
Quote:

I guess avr-gcc should drop the stack initialization then.

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

Quote:

And now it is

See:

https://www.avrfreaks.net/index.p...

Even .S programmers get a CRT until they go out of their way to do something about it.

In my opinion, like GCC itself, you cannot really complain about something you get given for free ;-)

I'd have thought for a bootloader the "cost" of that complete vector table, that's being provided, was far greater than one or two opcodes setting SP?

PS I wrote that reply to Jim with a view to it one day maybe becoming a Tutorial. Perhaps I should do that.

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

clawson wrote:

Bootloader eh? Read this:

https://www.avrfreaks.net/index.p...

Ahh this thread. I've read some of it. Too long to read it all.

Quote:
It was a kind of competition to see whether an AVR109 could be done in C in 512 bytes. I'm pretty sure it can't but it raised issues such as -nostartfiles etc.

More navel fluff inspection, eh? ;)

I'm dealing with a 62 byte (31 word) asm bootloader (based on Kasper Pedersens tinyloader) which is mostly finished. I'm programming the PC application to communicate with the bootloader atm.

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

Quote:

The only person concerned about the exact operation of SP is going to be those trying to decode a stack frame - I don't imagine it happens that often?

Like sad people who enjoy embedding relocatable data in their code?

rcall FUBAR
.db "Error Message",\n,0x00

Cheers,

Joey