Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stack and heap confusion for embedded 8051

I am trying to understand a few basics concepts regarding the memory layout for a 8051 MCU architecture. I would be grateful if anyone could give me some clarifications.

So, for a 8051 MCU we have several types of memories:

  • IRAM - (idata) - used for general purpose registers and SFRs

  • PMEG - (code) - used to store code - (FLASH)

  • XDATA

    • on chip (data) - cache memory for data (RAM) /
    • off-chip (xdata) - external memory (RAM)

Questions:

  1. So where is the stack actually located? I would assume in IRAM (idata) but it's quite small (30-7Fh)- 79 bytes

  2. What does the stack do?

    Now, on one hand I read that it stores the return addresses when we call a function (e.g. when I call a function the return address is stored on the stack and the stack pointer is incremented).

    http://www.alciro.org/alciro/microcontroladores-8051_24/subrutina-subprograma_357_en.htm

    On the other hand I read that the stack stores our local variables from a function, variables which are "deleted" once we return from that function. http://gribblelab.org/CBootcamp/7_Memory_Stack_vs_Heap.html

  3. If I use dynamic memory allocation (heap), will that memory always be reserved in off-chip RAM (xdata), or it depends on compiler/optimization?

like image 261
Calin Tamaian Avatar asked Dec 03 '25 16:12

Calin Tamaian


2 Answers

The 8051 has its origin in the 1970ies/early 80ies. As such, it has very limited ressources. The original version did (for instance) not even have XRAM, that was "patched" aside later and requires special (ans slow) accesses.

The IRAM is the "main memory". It really includes the stack (yes, there are only few bytes). The rest is used for global variables ("data" and "bss" section: initialized and uninitialized globals and statics). The XRAM might be used by a compiler for the same reason. Note that with these small MCUs you do not use many local variables (and if, only 8bit types). A clever compiler/linker (I actually used some of these) can allocate local variables statically overlapping - unless there is recursion used (very unlikely).

Most notably, programs for such systems mostly do not use a heap (i.e. dynamic memory allocation), but only statically allocated memory. At most, they might use a memory pool, which provides blocks of fixed size and does not merged blocks.

Note that the IRAM includes some special registers which can be bit-addressed by the hardware. Normally, you would use a specialized compiler which can exploit these functions. Very likely some features require special assembler functions (these might be provided in a header as C-functions just generating the corresponding machine code instruction), called intrinsics.

The different memory areas might also require compiler-extensions to be used. You might have a look at sdcc for a suitable compiler.

Note also that the 8051 has an extended harvard architecture (code and data seperated with XRAM as 3rd party).

Regarding your 2nd link: This is a very generalized article; it does not cover MCUs like the 8051 (or AVR, PIC and the like), but more generalized CPUs like x86, ARM, PowerPC, MIPS, MSP430 (which is also a smaller MCU), etc. using an external von Neumann architecture (internally most (if not all) 32+ bitters use a harvard architecture).

like image 199
too honest for this site Avatar answered Dec 06 '25 08:12

too honest for this site


I don't have direct experience with your chips, but I have worked with very constrained systems in the past. So here is what I can answer:

Question 1 and 2: The stack is more than likely set within a very early startup routine. This will set a register to tell it where the stack should start. Typically, you want this in memory that is very fast to access because compiled code loves pushing and popping memory from the stack all the time. This includes return addresses in calls, local variable declarations, and the occasional call to directly allocate stack memory (alloca).

For your 3rd question, the heap is set wherever your startup routine set it to.
There is no particular area that a heap needs to live. If you want it to live in external memory, then it can be set there. You want it in your really small/fast area, you can do that too, though that is probably a very bad idea. Again, your chip's/compiler's manual or included code should show you an overloaded call to malloc(). From here, you should be able to walk backwards to see what addresses are being passed into its memory routines.

Your IRAM is so dang small that it feels more like Instruction RAM - RAM where you would put a subroutine or two to make running code from them more efficient. 80 bytes of stack space will evaporate very quickly in a typical C function call framework. Actually, for sizes like this, you might have to hand assemble stuff to get the most out of things, but that may be beyond your scope.

If you have other questions, let me know. This is the kind of stuff I like doing :)


Update

This page has a bunch of good information on stack management for your particular chip. It appears that the stack for this chip is indeed in IRAM and is very very constrained. It also appears that assembly level coding on this chip would be the norm as this amount of RAM is quite small indeed.

Heck, this is the first system I've seen in many years that has bank switching as a way to access more RAM. I haven't done that since the Color Gameboy's Z80 chip.

like image 24
Michael Dorgan Avatar answered Dec 06 '25 08:12

Michael Dorgan