How to switch back to main stack on ARM M4 with FreeRTOS

I’ve just ported a large application to FreeRTOS. The application has the facility to do firmware updates, which it does by loading a bootloader into the top of flash memory, disabling all interrupts via the NVIC, resetting the vector table to point into the bootloader, and jumping to the start of the bootloader. Since I started using FreeRTOS in the main application, this doesn’t work. I think this is because the application is now using the process stack instead of the main stack. The bootloader assumes that only one stack is used, located at the top of RAM. So how can I switch back to the main stack from a FreeRTOS task, just before jumping into the bootloader? From the ARM docs I think it can only be done in an exception return, so some sort of SVC looks a possibility; but FreeRTOS doesn’t appear to have an facility for adding user-defined SVCs. Thanks – David

How to switch back to main stack on ARM M4 with FreeRTOS

This is not a facility FreeRTOS gives you – so it is more a hardware question that a software question. You can reset the main stack pointer quite easily – see the function prvPortStartFirstTask() in the port.c file as it does just that – but switching to use the main stack pointer is a different thing and would require some reading of the Cortex-M technical reference manual. As you are going to disable interrupts anyway – could you jump to your boot code from inside an interrupt? That way you would be using the required stack. Could you just update the bootloader code so it doesn’t care which stack it is using? Are you sure it cares at the moment and the problem is not just running out of stack space? You could set the process stack pointer to the start of the main stack so you have lots of room – providing you didn’t need either stack again [as you are going to effectively restart everything with a new image].

How to switch back to main stack on ARM M4 with FreeRTOS

Hi Richard, thanks for the quick reply. Resetting the MSP isn’t an issue – the code already does that. I suspect the problem is that the PSP is in an area of RAM that is being used for other purposes by the bootloader. Maybe it will work if I change the PSP to be a few hundred bytes below the MSP. I thought about jumping to the boot code from within an interrupt, but wouldn’t that leave the processor executing in handler mode? Perhaps that would work if the handler was a very low priority handler and the bootloader never needs to use a handler with the same or lower priority. [EDIT: does the “msr basepri, r0” with r0==0 that I see in vPortSVCHandler() persuade the processor that there are no exceptions/faults active?] One of the complications is that I need users to be able to switch between RTOS and non-RTOS versions of the application, at least initially. So the bootloader must work coming from either. I am tempted to change the implementation of vPortSVCHandler so that SVC 0 behaves as now, but SVC 1 switches the stack to MSP and returns to the original call address, or possibly returns to the address passed in R1 instead. Unfortunately, even though I have a lot of previous experiences with an RTOS, I have almost no experience of ARM assembler. For the future, I think it would be good if FreeRTOS provided some way for users to hook in SVC handlers. I see from other posts that this has been requested before. I would have thought that in ports using the ARM MPU it would be even more impotant to have. btw I am really pleased that FreeRTOS is MISRA-compliant, that’s one of the reasons why I chose it.

How to switch back to main stack on ARM M4 with FreeRTOS

The in the non-MPU port the SVC handler is only used to start the scheduler, so it doesn’t really have any logic to determine which SVC was called. You could however look at the SVC handler in the MPU port (FreeRTOSSourceportableGCCARMCM4MPU) as an example as that handles three different SVC numbers.

How to switch back to main stack on ARM M4 with FreeRTOS

Thanks, I’ll take a look at the MPU port to see how to implement an SVC dispatcher. Meanwhile the problem appears to be resolved if I set SP (i.e. the PSP) to 1K below the MSP just before jumping to the bootloader.