0
\$\begingroup\$

I'm working with an AVR based project and just have one curiosity.

The ATmega328P has total 2kB of SRAM. The lowest address of the stack can be 0x8FF. Now suppose the stack pointer is at 0x8FF. What would happen if I gave a RETI instruction at this point or a POP instruction multiple times?

RETI and POP both are supposed to take the value at the top of the stack and increment the stack pointer (incrementing the stack pointer would mean taking the stack down.)

I tried on Microchip Studio (earlier AVR studio.)

There it simply increments the stack pointer from 0x08FF to 0x0900 to 0x0901 an so on. This could actually be possible because the stack pointer is a 16 bit register (2 registers SPL and SPH of 8 bit each.)

Now suppose I take my SP to 0x0905 and do a PUSH, and then do a pop. In the simulator it pops the correct value (that was pushed) into my output register. Howeve , in a real ATmega328P that would not be possible (as there is no place to store the below 0x8FF address.)

How would a real microprocessor unit react to such a scenario?

The code I used was :

LDI R16, 0x88 ;load a random value
POP R0        ;increase SP to 0x900
POP R1        ;increase SP to 0x901
POP R2        ;increase SP to 0x902
POP R3        ;increase SP to 0x903
PUSH R16      ;decrease SP to 0x902
PUSH R16      ;decrease SP to 0x901
POP R4        ;increase SP to 0x902 and store 0x88 in R4

In comments I have written whatever I observed in the simulator, I want to know how would a real processor would react.

\$\endgroup\$
5
  • \$\begingroup\$ The ram is only 2KBytes. where did you get 23kb from? \$\endgroup\$
    – Kartman
    Apr 28, 2021 at 13:23
  • \$\begingroup\$ 0x8ff is the TOP of stack. The stack grows downwards ie towards 0. Beyond 0x8ff is fresh air. You can push, but what you pop won't be anything useful \$\endgroup\$
    – Kartman
    Apr 28, 2021 at 13:33
  • \$\begingroup\$ Aren't AVR using down-counting stacks or did they change to up-counting after Microchip merger? :) I think PIC are the only ones who use up-counting stacks. \$\endgroup\$
    – Lundin
    Apr 28, 2021 at 13:34
  • \$\begingroup\$ yes avr implements a down counting stack (0x08FF is the "TOP") but when it grows it decrements the stack pointer (that is what I wanted to say). @Kartman yes the mcu has 2Kb SRAM I wrote the wrong number \$\endgroup\$
    – user137273
    Apr 28, 2021 at 14:16
  • \$\begingroup\$ You're not guaranteed 16bit for SP, the datasheet says the number of bits depends on the address space - so 11bits for 328p. (The smaller MCUs don't even have SPH.) What's really in the hardware though I have no idea. But wouldn't be surprised if it just wrapped around to zero. \$\endgroup\$
    – Mat
    Apr 28, 2021 at 16:18

1 Answer 1

2
\$\begingroup\$

I don't know about AVR specifically, but in the general case, the MCU just keeps going and write out of bounds. This is a stack overflow/underflow. (Stack underflow can only happen in assembler, not in pure C.) Typically only the linker knows where your stack begins and ends: neither the compiler nor the MCU itself knows or cares.

Generally low-end MCUs only offer physical memory access, so there's no MMU to slap your fingers if touching memory that you shouldn't touch. Or if there is a MMU, it is often a simplistic one. Higher end parts would generate hardware exceptions.

The hardened embedded systems programmers therefore ensure to always map their stack so that it overflows into harmless memory. Such as addresses where nothing is allocated, or addresses where there's nothing but unprogrammed flash. A stack pointer ending up there will quite soon lead to run-away code or similar, often trying to execute op code 0xFF. From there on you can typically catch illegal op code interrupts. Or in worst case hopefully the watchdog will stop the program.

If you don't place your stack with such care, or in case you use some default memory layout provided by the manufacturer and/or monkeys, you might end up stack overflowing into your hardware register map or some .data/.bss RAM segment, which is a disaster.

Another somewhat crude technique you can use, often done by high end system compilers, is to use a "stack canary". Make a calculation of your expected worst case stack usage. Then set the whole stack to a known value at start-up (all 0x00 or all 0xFF). Then beyond your estimated worst case stack scenario, write a different value (0xAA or whatever) to various fixed locations. You can then repeatedly check these locations from the main loop to see that they still hold the fixed value - if not, then you have a stack overflow. This isn't 100% secure but catches most stack overflows.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.