Stack Memory in a Cortex-M Processor- Explained

STM32F407G discovery board



In this article, we explain stack memory in general and then specifically for the Cortex-M processors.

Stack memory is part of the main memory (internal or external RAM).

This means that stack memory is volatile or transient. It is reserved for the temporary storage of data. And this data is lost if power is shut off to the system.

Stack memory is mainly used with functions in a program or during interrupt/exception handling.

The stack is traced using a stack pointer (SP) register. PUSH and POP instructions affect (decrement or increment) the stack point register.

Stack memory is accessed in last in first out fashion (LIFO). This means that if the computer is executing a current program, this data from this program will be the first that will be retrieved. Thus, the most recent executing program will be retrieved when accessing data from a stack. Just imagine you were stacking dollar bills on top of each other. You put a 1 dollar bill on the bottom. On top of it, you put a five dollar bill. On top of the five dollar bill, you put a ten dollar bill. On top of the ten dollar bill, you put a 20 dollar bill. Now when you are taking out a bill, the first bill you're going to remove and the easiest bill to have access to is the 20 dollar bill, because it's on top. This is how LIFO operates. The last item to be placed into the stack is the one that will be first accessed.

The stack can be accessed using PUSH and POP instructions or any memory manipulation instructions such as load (LD) or store (STR). In ARM Cortex-Mx processors, register 13 (R13) is considered as the stack pointer register.

Stack memory can be used for the temporary storage of processor register values and the temporary storage of local variables of the executing function. During system exception or interrupt, stack memory will be used to save the context (some general-purpose registers, processor status register, and the return address) of the currently executing code.

How a stack operates is depending on the stack's operation models.

The 4 different types of stack operation models are Full Ascending Stack (FA), Full Descending Stack (FD), Empty Ascending Stack (EA), and Empty Descending Stack (ED).

ARM Cortex Mx processors use Full Descending Stacks.

In a Full Ascending Stack Operation Model, if a new data item is pushed onto the stack, the stack pointer will ascend to the next increasing memory address location and then the data will be pushed to that location. So, for example, if the stack pointer is currently pointing to the memory location, 200, and the next memory location is 201, the stack pointer will point to memory address location, 201. The new data that is pushed onto the stack will then populate the memory address location at 201.

In the Full Descending Stack, everything is the same but instead of the stack pointer going to the next increasing memory address, it goes to the next decreasing memory address. It starts at the higher memory address end and then goes down. So, in our example, if the stack pointer is currently at the memory address location, 200, the stack pointer will then decrease to 199 and the data will be inserted into this memory address location. This is a reason why using processors that incorporate full descending stacks, you initialize the stack pointer to higher memory address.

In the Empty Ascending Stack, the stack pointer will always be pointing to an empty location. If the empty location the stack pointer is pointing to is at memory location 200 and we push new data to the stack, that data will occupy the memory location at 200. The stack pointer will then ascend to the next highest location, 201, and point there, which will be empty.

In the Empty Descending Stack, the stack pointer will always be pointing to an empty location. If the mepty location the stack pointer is pointing to is at memory location 200 and we push new data to the stack, that data will occupy the memory location at 200. The stack pointer will then descend to the next lowest location, 199, and point there, which will be empty.

Knowing the stack operation model that a processor uses allows us to predictably know where a stack pointer will go to next, so that we can program the code accordingly.

Again, the ARM Cortex Mx processors use Full Descending Stack Operation model. This cannot be changed, as it is way that ARM has manufactured the stack operation model.

One important thing to know is that with a Full Descending Stack operation model, a PUSH instruction adds new data to the stack; this makes the stack pointer point to the next descending memory address location.

A POP instruction removes data from the stack. A POP instruction is used to retrieve the last data item on the stack. Thus, the stack pointer increases to the next memory address and points there. This, again, is because you removed a data item from the stack. Thus, it goes backward, which in a descending stack is upward.

Also, as explained before, the stack memory is a part of RAM. It takes a portion of the RAM memory and shares it with the heap and data. The heap is RAM for dynamic memory allocation (such as when using malloc). Data includes things such as global variables of a program.

Because the Cortex Mx processors adopt a full descending stack operation model, it is good practice to start the stack at the higher end of the memory for stack allocation. It is good practice to allocate the stack at the end of the RAM that has the highest memory address. This way, when items are pushed to the stack, it has space to descend to lower memory address.

Now we will talk specifically aboutg the stack pointers of an Cortex M processor.

Physically, the Cortex M processor has 3 stack pointers: SP (R13), MSP, and PSP.

MSP is the Main Stack Pointer.

PSP is the Process Stack Pointer.

This is shown in the diagram below of the core registers for the Cortex-M4 processor.


Cortex-M4 processor core registers


You can see the R13 (SP), MSP, and PSP.

SP always represents the current stack pointer.

We can switch between using MSP and PSP. Whichever stack pointer we are using will be copied into SP, and SP will always be the current stack pointer being used.

We will now go through a lot of key points about stack pointers for the Cortex M processor.

One is that after a processor reset, by default, MSP will be selected as the current stack pointer. That means, SP copies the contents of MSP.

The processor being in thread mode can change the current stack pointer to PSP by configuring the CONTROL register's SPSEL bit. When the processor is in thread mode, if the SPSEL bit is set to 0, the MSP will be the current stack pointer. When the processor is in thread mode, if the SPSEL bit is set to 1, the PSP will be the current stack pointer.

You can see the CONTROL register in the core registers above.

Below is the CONTROL register bit assignments for the Cortex-M4 processor.


Cortex-M4 CONTROL register bit assignments


You can see that bit 1 is the SPSEL bit, which allows us to select the PSP as the current stack pointer by setting this bit to 1.

This is good and necessary to have multiple stack pointers because there are times in programs where we want the program to execute different regions of the stack. If we set the MSP to point to one address and set the PSP to point to another address, we can effectively run different areas of memory by switching the stack pointers. So it's as if we can manipulate the stack and have certain regions run as we want them to. This important for applications like schedulers, where we manipulate the SP register to run various parts as we need it to.

Another important point is that handler mode execution will always use MSP as the current stack pointer. This means that if there is an exception, we need to change the mode back to thread mode in order to be able to switch to the PSP stack pointer.

Another point is that MSP will be initialized automatically by the processor after reset by reading the content at the memory address of, 0x000_0000.

A last important point is that if you want to use PSP as a stack pointer, make sure that you initialize PSP to a valid stack address in your code. The sequence of coding a PSP stack pointer is first to initialize the PSP to a valid stack address, then change the CONTROL register to switch to PSP by setting the SPSEL bit to 1, and then you can the PUSH and POP operations in order to use PSP to track stack memory.

So this is how stack memory works in a Cortex-M processor.



Related Resources





HTML Comment Box is loading comments...