Move the stack pointer

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

Hi everyone!

In my assembly course we used the ATMon bootloader. In the next course we moved to GCC, but doing so broke the bootloader.

The problem is that the bootloader uses the stack. In assembly, this is easy to get around, and in the documentation it says to init your stack pointer to RAMEND-0x20.

The problem is that GCC inits the stack pointer for me, and I can't find how to change it. Is there a way? Is it a linker command?

At first it was not an issue because I could program with my EZAVR2. I got a new laptop without a serial port, and my USB to serial converter doesn't work with it. If I could use ATMon, though, I could program through the USART and my converter should work for that.

I guess my other option would be to write my program in assembly? While possible, I don't feel like writing GPS code 'by hand'

Thanks!

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

Quote:

The problem is that GCC inits the stack pointer for me, and I can't find how to change it. Is there a way? Is it a linker command?

GCC's SP init code always sets it to RAMEND for the chosen device. Once the C linker is in control of flash and RAM layout why would you want to "second guess" it and take control? Surely (assuming C is going to position variables from the start of SRAM upwards) the very end of RAM is the "safe" place for the stack? It's as far away from the variables it might bump into that it can be.

But are you sure you have configured the compiler to build for the right AVR - a common error is either to build for the wrong device or, in the case of devices that default to working like a different AVR (mega64/128 default to act like mega103) to forget to switch them to acting like the correct AVR.

By the way the SRAM/SP usage of the bootloader has not connection with the RAM layout of the application code as almost the first thing any C application does is reset SP and re configure the RAM layout (initialise/wipe vars) anwyay.

Cliff

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

from the ATMon documentation

Quote:

Because the bootloader needs a few bytes of memory for itself, which are located at the end of SRAM, the stack pointer initialization needs to be modified in order to skip the bytes. You can either:

* In your stack initialization (ldi r16, low(RAMEND); out SPL, r16; etc...), replace "RAMEND" with "RAMEND-0x20"
* Put the downloaded m32def.inc to your project folder. This way, when you use directive .include "m32def.inc", the assembler will include the local include file, instead of the default for the assembler.

If C inits my stack pointer to RAMEND, it will collide with the bootloader, which is why I need to move it.

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

You would do it in an ".init" section. I'm not sure which one.

Edit: From the manual it is .init2. Also, .init3 is free for the user, so it might be easier to override it there rather than rewrite .init2.

Regards,
Steve A.

The Board helps those that help themselves.

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

But surely the only purpose of the bootloader keeping state info in RAM is if you were ever to re-enter it but given that 99.9% of the time a bootloader executes at power on, passes control to the app, then becomes so much wasted code and never regains control why would it need this if you never re-enter it? If you do need to re-enter the bootloader why not just jump into its cold entry point (as BOOTRST does) ?

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

Cliff,

I took a quick look into the ATmon documentation. Looks like it is not only a boot loader, but also a monitor.

Stefan Ernst

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

Koshchi wrote:
You would do it in an ".init" section. I'm not sure which one.

Edit: From the manual it is .init2. Also, .init3 is free for the user, so it might be easier to override it there rather than rewrite .init2.

Which manual? I searched and searched (but I guess I didn't know what I was looking for) and couldn't find anything

Quote:
Cliff,

I took a quick look into the ATmon documentation. Looks like it is not only a boot loader, but also a monitor.

Sorry about that, a bit of my inner newbie shines through. This is true.

I suppose I don't necessarily need the monitoring functionality. Atmon is what I learned, but it doesn't hurt to branch out into new things once in a while. Can anyone recommend anything else that would still allow me to program through the USART?

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

LeaRae wrote:
Which manual? I searched and searched (but I guess I didn't know what I was looking for) and couldn't find anything

General:
http://www.nongnu.org/avr-libc/u...

Specific:
http://www.nongnu.org/avr-libc/u...

Stefan Ernst

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

After a bit of time dealing with the variable lists and stuff, I found I could write the code "straight out". Sometimes I am frustrated by documentation :)

void atmon_sp_init(void) __attribute__((naked)) __attribute__((section(".init3")));

void atmon_sp_init(void) {

	asm volatile(
		"ldi r28, 0x3F"			"\n\t"
		"ldi r29, 0x08"			"\n\t"
		"out __SP_L__, r28"		"\n\t"
		"out __SP_H__, r29"		"\n\t"
		::
	);
}

How does this look? The listing file seems to support the fact that it will work. Is there anything that would cause to stack pointer to reset, other than on bootup?

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

It shouldn't need inline asm. I would go with something like:

void atmon_sp_init(void) __attribute__((naked)) __attribute__((section(".init3"))); 

void atmon_sp_init(void) { 
	SP -= 20;
} 

to reserve 20 bytes above the stack and below RAMEND. This leads to the pre-amble:

00000054 <__ctors_end>:
  54:	11 24       	eor	r1, r1
  56:	1f be       	out	0x3f, r1	; 63
  58:	cf e5       	ldi	r28, 0x5F	; 95
  5a:	d4 e0       	ldi	r29, 0x04	; 4
  5c:	de bf       	out	0x3e, r29	; 62
  5e:	cd bf       	out	0x3d, r28	; 61

00000060 :
#include 

void atmon_sp_init(void) __attribute__((naked)) __attribute__((section(".init3"))); 

void atmon_sp_init(void) { 
  60:	8d b7       	in	r24, 0x3d	; 61
  62:	9e b7       	in	r25, 0x3e	; 62
  64:	44 97       	sbiw	r24, 0x14	; 20
  66:	9e bf       	out	0x3e, r25	; 62
  68:	8d bf       	out	0x3d, r24	; 61
  6a:	0e 94 3b 00 	call	0x76	; 0x76 

in which (for mega16) you can see SP initially set to RAMEND at 0x45F by the standard C stuff then adjusted down (SBIW r24,0x14) by 20 bytes after this.

Cliff

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

Effectively the same, but your code is cleaner and much more portable.

Before we call the thread closed, could someone refer me to somewhere I can look into how that function prototype is formed? I copied it, but don't understand it.

__attribute__((naked)) __attribute__((section(".init3")))

Thanks

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

Well the generic documentation of __attribute__ is here:

http://gcc.gnu.org/onlinedocs/gc...

(that's 4.1.1 - look at your WinAVR install directory - you have a copy on your hard drive that's relevant to the GCC you are using)

The AVR-LibC user manual also talks about some of this:

http://www.nongnu.org/avr-libc/u...