There are some codes in the "Startup.s" for "User Initial Stack & Heap".
And, as per this URL http://www.keil.com/support/man/docs/armlib/armlib_chdegjfd.htm
There are another codes for "Sets up the heap and stack".
It makes me a little confused.
What is the relationship between these two "Stack & Heap Initialization"?
Are they duplicated works, or they have different and important meanings?
Hi,
I am working on the LPC23xx platform. Though my code works fine on this platform, I still do not know much about the whole system. I learned the booting/startup procedure of the LPC23xx with KEIL MDK-ARM is: Startup.s -> __main -> __rt_entry -> C main(). But I do not understand why it needs two "Stack & Heap Initialization" .
It makes me confused.
If you know the answer, please give me some hints. Many thanks in advance.
The startup file needs to know how much space to reserve for the heap to make sure it doesn't collide with the stack or with normal memory that should be zero-initialized.
But it isn't enough to have a block of memory available for the heap. The C runtime library must also configure their internal data structures so it can split and link memory blocks when you call malloc().
Or where there more specific questions you have about the heap?
Hi Per,
Many Thanks.
If I write my C program (MyCprg) and use some Librarys (Lib01/Lib02), with the powerful KEIL's solution (on LPC23xx). Does the Runtime Memory Map look like the below?
----RAM-------------- 0x-6FFF-FFFF | Stack-ASM-start (Defined in the "Startup.s") | | ------------------ | Stack-MyCprg-start (__rt_entry done for me) | | Stack-MyCprg-end (__rt_entry done for me) | ------------------ | Stack-Lib01-start (__rt_entry done for me) | | Stack-Lib01-end (__rt_entry done for me) | ------------------ | Stack-Lib02-start (__rt_entry done for me) | | Stack-Lib02-end (__rt_entry done for me) | ------------------ | | Stack-ASM-end (Defined in the "Startup.s") --------------------- | Heap-ASM-end (Defined in the "Startup.s") | | ------------------ | Heap-MyCprg-start (__rt_entry done for me) | | Heap-MyCprg-end (__rt_entry done for me) | ------------------ | Heap-Lib01-start (__rt_entry done for me) | | Heap-Lib01-end (__rt_entry done for me) | ------------------ | Heap-Lib02-start (__rt_entry done for me) | | Heap-Lib02-end (__rt_entry done for me) | ------------------ | | Heap-ASM-start (Defined in the "Startup.s") --------------------- 0x-4000-1000 | ZI AREA | --------------------- | RW AREA | ----RAM-------------- 0x-4000-0100 ------------------- --------------------- | Flash | ---------------------
If we ignore the other things (such like Memery Areas for Interrupt Vector Table, Hardware Addresses Remapping) then, can I say that, there are only 4 parts in the RAM Area: 1. Stack 2. Heap 3. ZI Area 4. RW Area But I still got confused, does the Library has its own ZI, RW, which are not located between 0x-4000-1000 and 0x-4000-0100?
Maybe the Runtime Memory Map should look like the below?
----RAM-------------- 0x-6FFF-FFFF ------------------------ | ZI AREA for Prg4/Lib4 ------------------------ | RW AREA for Prg4/Lib4 ------------------------ | ZI AREA for Prg3/Lib3 ------------------------ | RW AREA for Prg3/Lib3 ------------------------ | Stack-ASM-start (Defined in the "Startup.s") | | ------------------ | Stack-Prg1/Lib1-start (__rt_entry done for me) | | Stack-Prg1/Lib1-end (__rt_entry done for me) | ------------------ | Stack-Prg2/Lib2-start (__rt_entry done for me) | | Stack-Prg2/Lib2-end (__rt_entry done for me) | ------------------ | Stack-Prg3/Lib3-start (__rt_entry done for me) | | Stack-Prg3/Lib3-end (__rt_entry done for me) | ------------------ | Stack-Prg4/Lib4-start (__rt_entry done for me) | | Stack-Prg4/Lib4-end (__rt_entry done for me) | ------------------ | | Stack-ASM-end (Defined in the "Startup.s") --------------------- | Heap-ASM-end (Defined in the "Startup.s") | | ------------------ | Heap-Prg1/Lib1-start (__rt_entry done for me) | | Heap-Prg1/Lib1-end (__rt_entry done for me) | ------------------ | Heap-Prg2/Lib2-start (__rt_entry done for me) | | Heap-Prg2/Lib2-end (__rt_entry done for me) | ------------------ | Heap-Prg3/Lib3-start (__rt_entry done for me) | | Heap-Prg3/Lib3-end (__rt_entry done for me) | ------------------ | Heap-Prg4/Lib4-start (__rt_entry done for me) | | Heap-Prg4/Lib4-end (__rt_entry done for me) | ------------------ | | Heap-ASM-start (Defined in the "Startup.s") ------------------------ | ZI AREA for Prg2/Lib2 ------------------------ | RW AREA for Prg2/Lib2 ------------------------ | ZI AREA for Prg1/Lib1 ------------------------ | RW AREA for Prg1/Lib1 ----RAM----------------- ------------------- --------------------- | Flash | ---------------------
Sorry for that I am not able to express my questions precisely and clearly. (even in my first language)
Does the below statements correct?
Who will need a Heap Area? the application which needs to dynamically allocate memory during runtime.
Who will need a Stack Area? 1. CPU mode switching, each mode has its own Stack Pointer, but we have only one RAM Area. 2. When an Interrupt Service Routines is triggered, such like UART-Handler, Timer-Handler. 3. An application calls a function (Call and Return).
The Stack/Heap defined in the Startup.s are continuous memery area.
All the stacks/heaps which are needed by Hardware (CPU Mode Switching), Interrupt Service Routines (HW/SW ISR), Applications (My C programs, the Libraries) must be put into "the Stack/Heap defined in the Startup.s".
Does the below Runtime Memory Map correct?
----RAM-------------- 0x-6FFF-FFFF | Stack-ASM-start (Defined in the "Startup.s") | ------------------ | Stack-CPU mode switching-start | (Defined in the "Startup.s") | Stack-CPU mode switching-end | ------------------ | Stack-Interrupt Service Routines-start | (Where is the code, which defined this?) | Stack-Interrupt Service Routines-end | ------------------ | Stack-MyCprg-start (__rt_entry done for me) | | Stack-MyCprg-end (__rt_entry done for me) | ------------------ | Stack-Lib01-start (__rt_entry done for me) | | Stack-Lib01-end (__rt_entry done for me) | ------------------ | Stack-Lib02-start (__rt_entry done for me) | | Stack-Lib02-end (__rt_entry done for me) | ------------------ | | Stack-ASM-end (Defined in the "Startup.s") --------------------- | Heap-ASM-end (Defined in the "Startup.s") | ------------------ | Interrupt Service Routines do not need HEAP? | ------------------ | Heap-MyCprg-start (__rt_entry done for me) | | Heap-MyCprg-end (__rt_entry done for me) | ------------------ | Heap-Lib01-start (__rt_entry done for me) | | Heap-Lib01-end (__rt_entry done for me) | ------------------ | Heap-Lib02-start (__rt_entry done for me) | | Heap-Lib02-end (__rt_entry done for me) | ------------------ | Heap-ASM-start (Defined in the "Startup.s") --------------------- 0x-4000-1000
You have one heap area, that may be zero size large if you do not need any dynamic memory allocations.
Both your program and some CRTL functions may need the heap. You define the size of this heap in the startup file. The startup file then calls an initialization function for the CRTL and if any heap space has been declared, then the RTL init function will initialize a couple of internal structures to prepare the heap to be used.
For some processor architectures, you have a single stack. The ARM has separate stacks for the different interrupt handlers. So you define the sizes of these stacks in the startup file. The C runtime library do not have its own stack. There is one stack that the user application will use. When you make calls to the RTL, then this is your application, so the RTL will use the same stack.
If running with a RTOS, then you may use dynamic memory or global variables (ZI space) to declare more stacks and initialize the RTOS threads to use these extra stacks. But the startup file will not know about them. It will only know about that initial stack it has set up for use before your main() function gets called. So in an RTOS environment, you may have the startup file create a very small stack, and then let main directly create a couple of threads with new stacks. In short, you may need an small initial stack for a very short while just while kick-starting the RTOS.
Your chip can have more than one RAM region (or you may have internal RAM and external RAM). But you normally link your program to have only one ZI region and one RW region, even if the linker may decide to split one of these regions into two and place some variables in one sub-block and some variables in another sub-block. But that is just a linker optimization to make best use of multiple RAM memories. The startup code obviously have to be quite smart to support this, since initialization of RW memory may have to span non-contiguous memory. In the same way, the zero-initialization of the ZI memory may also have to be split. When you browse the map file (if turned on) you will notice that each object file will show a summary about RW and ZI space needed, but in the end the ZI regions from all object files will be placed side-by-side, and all RW variables from the different object files will be placed side-by-side.
If you have special needs, then you can define special memory regions where you place variables. You might for example want uninitialized variables that are not ZI-initialized since you may have a region that has battery backup. If this region is used for RW or ZI then the variables will be overwritten on every boot.
So in the end: - no, the RTL does not have its own stack. The used stack is for the current mode of the processor, i.e. IRQ, FIQ, user, ... and not for application contra RTL. - no, the RTL does not have its own heap. The RTL can support a single pool of memory (the heap) and any call for dynamic allocations will eat from this heap. But you (or an RTOS) may define other memory allocation schemes. But they will normally also take memory from a pool because of the function name you call, and not controlled by which source line that made the call.
Hi John,
> But I do not understand why it needs two "Stack & Heap Initialization".
What "two" initialization are you talking about? Could you be more precise? The only "duplication" I see in LPC2300.s is conditionally compiled code depending on whether you use Microlib or the normal C library. There should be one user mode (application) stack and a few exception mode stacks. __user_initial_stack_heap() configures the application stack and passes that information back to the C library. Different libraries do not need their own stacks. Neither do they need a separate heap if they rely on C library functions for heap management.
Regards Marcus http://www.doulos.com/arm/
Many Thanks to your explanations.
Though I need some time to completely understand these concepts, but I think I've already got some key points.
Hi Marcus,
Many Thanks to your help.
About the "duplication", now I understand I mistaken something.
At that moment, I learned the startup procedure of the LPC23xx with KEIL MDK-ARM is:
Startup.s -> __main -> __rt_entry -> C main().
I saw the codes in the "Startup.s" for User Initial Stack & Heap. And KEIL's Document says __rt_entry will set up the heap and stack, so I "assumed" there are another codes for "Sets up the heap and stack".
Now I guess that, __user_initial_stackheap() is equivalent to __rt_stackheap_init(). And the __user_initial_stackheap() located in Startup.s will only be executed when it is called by __rt_entry.
> Now I guess that, __user_initial_stackheap() is > equivalent to __rt_stackheap_init().
Not really. You have to reimplement (MDK has done that for you) __user_initial_stackheap() to place stack and heap regions.
As far as I understand things, __rt_stackheap_init() needs to be changed if you prefer to implement an entirely new stack/heap management.
I have never had to touch __rt_stackheap_init().