Important update: Arm Announces End of Life Timeline for Mbed. This site will be archived in July 2026. Read the full announcement.
Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
Option to Offset Stack Pointer for IAP
In the meantime I would suggest a small wrapper that would save the top 32 bytes before calling IAP code and restore them afterwards.
#if defined(TARGET_LPC1768) #define RAM_TOP 0x10008000 #define SAVELEN 32 #elif defined(TARGET_LPC11U24) #define RAM_TOP 0x10002000 #define SAVELEN 32 #else #error Unsupported target #endif void IAPwrapper(unsigned int [] x, unsigned int [] y) { char buffer[SAVELEN*2]; // reserve some extra space to make sure we don't overlap with what we're trying to save char *saveloc = (char*)(RAM_TOP - SAVELEN); memcpy(buffer, saveloc, SAVELEN); iap_entry(x, y); memcpy(saveloc, buffer, SAVELEN); }
Thanks for the reply. I tried using your code, but I'm getting some strange results... If I try to read from the EEPROM, nothing is returned. But if I remove the RAM saving code it works fine. Do you have any ideas as to why this would fail?
EDIT: I figured it out, my read buffer is being stored at 0x10001FEC, which is within the top 32 bytes of RAM... In light of this, I don't think saving and restoring the top 32 bytes of RAM is a good solution...
You could also have a 'fake' main function which eats up 32 bytes of the stack and then calls your real main function. I just gave this a try with the following sources.
reserve.cpp
#include <stdio.h> #include "main.h" int main() { char reserve[32]; printf("reserve @ 0x%08X-0x%08X\n", reserve, reserve+sizeof(reserve)-1); return realMain(reserve); }
main.h
#ifndef _MAIN_H_ #define _MAIN_H_ int realMain(char* reserve); #endif
main.cpp
#include "mbed.h" DigitalOut myled(LED1); int realMain(char* reserve) { char testBuffer; printf("testBuffer @ 0x%08X\n", &testBuffer); while(1) { myled = 1; wait(0.2); myled = 0; wait(0.2); } }
Notes:
- I pass a pointer to the 32-byte
reserve
buffer into realMain() so that the optimizer can't get rid of this reserve buffer since it doesn't know that the realMain() function in the other module doesn't actually do anything with this buffer. - I placed the two 'main' functions in different modules so that the compiler wouldn't perform any inlining. The inlining itself wouldn't be too bad but then the optimizer would probably notice that the
reserve
array in main() was never actually used and it would be dropped.
This generated the following output when run:
reserve @ 0x10007FD8-0x10007FF7 testBuffer @ 0x10007FD0
I hope that helps,
Adam
The really hacky solution would be to open the .bin produced for your project in a hex editor and modify the first 32-bit word to contain a value 32 less than it does currently. Then copy the modified version to your mbed. This first word is what the CPU initializes the stack pointer to before entering the reset handler. This would accomplish what you want with the online compiler.
Adam Green wrote:
The really hacky solution would be to open the .bin produced for your project in a hex editor and modify the first 32-bit word to contain a value 32 less than it does currently. Then copy the modified version to your mbed. This first word is what the CPU initializes the stack pointer to before entering the reset handler. This would accomplish what you want with the online compiler.
Hmm... Actually, I consider that to be less hacky than the other method. I might have to try that!
Neil Thiessen wrote:
Adam Green wrote:
The really hacky solution would be to open the .bin produced for your project in a hex editor and modify the first 32-bit word to contain a value 32 less than it does currently. Then copy the modified version to your mbed. This first word is what the CPU initializes the stack pointer to before entering the reset handler. This would accomplish what you want with the online compiler.
Hmm... Actually, I consider that to be less hacky than the other method. I might have to try that!
I hate to say it, but this doesn't seem to work either... The correctly offset stack pointer should be 10001FE0, I confirmed this by inspecting binaries produced with LPCXpresso. But when I tried moving it, the read buffer was still being allocated at 0x10001FEC. I tried moving the stack pointer as far as 0x10001E00, but the read buffer is still being allocated at 0x10001FEC. Is there something wrong with the code I'm using to test this?
char ee_write_str[] = "Boo!"; char ee_read_str[sizeof(ee_write_str)]; printf("\tBuffer address: 0x%X\n", &ee_read_str);
EDIT: So let me get this straight, is the vector table defined in startup_LPC11xx.s being copied to the start of the flash at compile time (and hence being edited by me)? Or is it moving the stack pointer back to 0x10002000 even though I've changed it?
Finally, I've got some code that works! However, I needed to import mbed-src and edit startup_LPC11xx.s, so still not a pretty solution. Here's the edited file, I changed __initial_sp to 0x10001FE0, which is 0x10002000 - 32 bytes. I verified this by printing the value of the main stack pointer using __get_MSP(), and the location of the read buffer. They're now moved out of the top 32 bytes. Still, it's too bad that mbed-src is required to do this, it's such a simple change. Hopefully the mbed team will see this and apply the change to the main source!
;/***************************************************************************** ; * @file: startup_LPC11xx.s ; * @purpose: CMSIS Cortex-M0 Core Device Startup File ; * for the NXP LPC11xx Device Series ; * @version: V1.0 ; * @date: 25. Nov. 2008 ; *------- <<< Use Configuration Wizard in Context Menu >>> ------------------ ; * ; * Copyright (C) 2008 ARM Limited. All rights reserved. ; * ARM Limited (ARM) is supplying this software for use with Cortex-M0 ; * processor based microcontrollers. This file can be freely distributed ; * within development tools that are supporting such ARM based processors. ; * ; * THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTIES, WHETHER EXPRESS, IMPLIED ; * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF ; * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. ; * ARM SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR ; * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. ; * ; *****************************************************************************/ Stack_Size EQU 0x00000400 AREA STACK, NOINIT, READWRITE, ALIGN=3 EXPORT __initial_sp Stack_Mem SPACE Stack_Size __initial_sp EQU 0x10001FE0 ; Top of RAM (minus 32 bytes for IAP) from LPC11U Heap_Size EQU 0x00000000 AREA HEAP, NOINIT, READWRITE, ALIGN=3 EXPORT __heap_base EXPORT __heap_limit __heap_base Heap_Mem SPACE Heap_Size __heap_limit PRESERVE8 THUMB ; Vector Table Mapped to Address 0 at Reset AREA RESET, DATA, READONLY EXPORT __Vectors __Vectors DCD __initial_sp ; Top of Stack DCD Reset_Handler ; Reset Handler DCD NMI_Handler ; NMI Handler DCD HardFault_Handler ; Hard Fault Handler DCD MemManage_Handler ; MPU Fault Handler DCD BusFault_Handler ; Bus Fault Handler DCD UsageFault_Handler ; Usage Fault Handler DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD 0 ; Reserved DCD SVC_Handler ; SVCall Handler DCD DebugMon_Handler ; Debug Monitor Handler DCD 0 ; Reserved DCD PendSV_Handler ; PendSV Handler DCD SysTick_Handler ; SysTick Handler ; External Interrupts ; for LPC11Uxx (With USB) DCD FLEX_INT0_IRQHandler ; All GPIO pin can be routed to FLEX_INTx DCD FLEX_INT1_IRQHandler DCD FLEX_INT2_IRQHandler DCD FLEX_INT3_IRQHandler DCD FLEX_INT4_IRQHandler DCD FLEX_INT5_IRQHandler DCD FLEX_INT6_IRQHandler DCD FLEX_INT7_IRQHandler DCD GINT0_IRQHandler DCD GINT1_IRQHandler ; PIO0 (0:7) DCD Reserved_IRQHandler ; Reserved DCD Reserved_IRQHandler DCD Reserved_IRQHandler DCD Reserved_IRQHandler DCD SSP1_IRQHandler ; SSP1 DCD I2C_IRQHandler ; I2C DCD TIMER16_0_IRQHandler ; 16-bit Timer0 DCD TIMER16_1_IRQHandler ; 16-bit Timer1 DCD TIMER32_0_IRQHandler ; 32-bit Timer0 DCD TIMER32_1_IRQHandler ; 32-bit Timer1 DCD SSP0_IRQHandler ; SSP0 DCD UART_IRQHandler ; UART DCD USB_IRQHandler ; USB IRQ DCD USB_FIQHandler ; USB FIQ DCD ADC_IRQHandler ; A/D Converter DCD WDT_IRQHandler ; Watchdog timer DCD BOD_IRQHandler ; Brown Out Detect DCD FMC_IRQHandler ; IP2111 Flash Memory Controller DCD Reserved_IRQHandler ; Reserved DCD Reserved_IRQHandler ; Reserved DCD USBWakeup_IRQHandler ; USB wake up DCD Reserved_IRQHandler ; Reserved ;; 48 vector entries. We pad to 128 to fill the 0x0 - 0x1FF REMAP address space DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill DCD 0xFFFFFFFF ; Datafill IF :LNOT::DEF:NO_CRP AREA |.ARM.__at_0x02FC|, CODE, READONLY CRP_Key DCD 0xFFFFFFFF ENDIF AREA |.text|, CODE, READONLY ; Reset Handler Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT SystemInit IMPORT __main LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0 ENDP ; Dummy Exception Handlers (infinite loops which can be modified) ; now, under COMMON NMI.c and NMI.h, a real NMI handler is created if NMI is enabled ; for particular peripheral. ;NMI_Handler PROC ; EXPORT NMI_Handler [WEAK] ; B . ; ENDP HardFault_Handler\ PROC EXPORT HardFault_Handler [WEAK] B . ENDP MemManage_Handler\ PROC EXPORT MemManage_Handler [WEAK] B . ENDP BusFault_Handler\ PROC EXPORT BusFault_Handler [WEAK] B . ENDP UsageFault_Handler\ PROC EXPORT UsageFault_Handler [WEAK] B . ENDP SVC_Handler PROC EXPORT SVC_Handler [WEAK] B . ENDP DebugMon_Handler\ PROC EXPORT DebugMon_Handler [WEAK] B . ENDP PendSV_Handler PROC EXPORT PendSV_Handler [WEAK] B . ENDP SysTick_Handler PROC EXPORT SysTick_Handler [WEAK] B . ENDP Reserved_IRQHandler PROC EXPORT Reserved_IRQHandler [WEAK] B . ENDP Default_Handler PROC ; for LPC11Uxx (With USB) EXPORT NMI_Handler [WEAK] EXPORT FLEX_INT0_IRQHandler [WEAK] EXPORT FLEX_INT1_IRQHandler [WEAK] EXPORT FLEX_INT2_IRQHandler [WEAK] EXPORT FLEX_INT3_IRQHandler [WEAK] EXPORT FLEX_INT4_IRQHandler [WEAK] EXPORT FLEX_INT5_IRQHandler [WEAK] EXPORT FLEX_INT6_IRQHandler [WEAK] EXPORT FLEX_INT7_IRQHandler [WEAK] EXPORT GINT0_IRQHandler [WEAK] EXPORT GINT1_IRQHandler [WEAK] EXPORT SSP1_IRQHandler [WEAK] EXPORT I2C_IRQHandler [WEAK] EXPORT TIMER16_0_IRQHandler [WEAK] EXPORT TIMER16_1_IRQHandler [WEAK] EXPORT TIMER32_0_IRQHandler [WEAK] EXPORT TIMER32_1_IRQHandler [WEAK] EXPORT SSP0_IRQHandler [WEAK] EXPORT UART_IRQHandler [WEAK] EXPORT USB_IRQHandler [WEAK] EXPORT USB_FIQHandler [WEAK] EXPORT ADC_IRQHandler [WEAK] EXPORT WDT_IRQHandler [WEAK] EXPORT BOD_IRQHandler [WEAK] EXPORT FMC_IRQHandler [WEAK] EXPORT USBWakeup_IRQHandler [WEAK] NMI_Handler FLEX_INT0_IRQHandler FLEX_INT1_IRQHandler FLEX_INT2_IRQHandler FLEX_INT3_IRQHandler FLEX_INT4_IRQHandler FLEX_INT5_IRQHandler FLEX_INT6_IRQHandler FLEX_INT7_IRQHandler GINT0_IRQHandler GINT1_IRQHandler SSP1_IRQHandler I2C_IRQHandler TIMER16_0_IRQHandler TIMER16_1_IRQHandler TIMER32_0_IRQHandler TIMER32_1_IRQHandler SSP0_IRQHandler UART_IRQHandler USB_IRQHandler USB_FIQHandler ADC_IRQHandler WDT_IRQHandler BOD_IRQHandler FMC_IRQHandler USBWakeup_IRQHandler B . ENDP ALIGN END
Neil Thiessen wrote:
EDIT: So let me get this straight, is the vector table defined in startup_LPC11xx.s being copied to the start of the flash at compile time (and hence being edited by me)? Or is it moving the stack pointer back to 0x10002000 even though I've changed it?
I think the answer to both of those questions is yes. The original contents of the vector table would be the compiled version of startup_LPC11xx.s and the linker would place it at the beginning of your binary. If you are modifying the first word in the final binary and you are still getting the same stack pointer then I would guess that there is code somewhere in the startup process which resets it. I know this doesn't happen with the LPC1768 since I was able to successfully modify its initial stack pointer this way. I just tried it on my LPC11U24 and I got the same results as you. MicroLib must reset the stack pointer back to the exported __initial_sp
value. I guess this kind of makes sense as it would allow it to recover space on the stack from non-returning calls made along its way to executing your main()
function.
Adam Green wrote:
Neil Thiessen wrote:
EDIT: So let me get this straight, is the vector table defined in startup_LPC11xx.s being copied to the start of the flash at compile time (and hence being edited by me)? Or is it moving the stack pointer back to 0x10002000 even though I've changed it?
I think the answer to both of those questions is yes. The original contents of the vector table would be the compiled version of startup_LPC11xx.s and the linker would place it at the beginning of your binary. If you are modifying the first word in the final binary and you are still getting the same stack pointer then I would guess that there is code somewhere in the startup process which resets it. I know this doesn't happen with the LPC1768 since I was able to successfully modify its initial stack pointer this way. I just tried it on my LPC11U24 and I got the same results as you. MicroLib must reset the stack pointer back to the exported __initial_sp
value. I guess this kind of makes sense as it would allow it to recover space on the stack from non-returning calls made along its way to executing your main()
function.
According to Keil's documentation at http://www.keil.com/support/man/docs/armlib/armlib_bajccfbi.htm, microlib does indeed use the __intial_sp symbol for something. The good news is with access to mbed-src, __intial_sp is easily modified!
I would be interested in an easy way of doing this as well. Resorting to mbed-src is not really ideal :)
I just started thinking about this issue with my LPC1347. It has RAM ending at 0x10002000 and RAM1 starting at 0x20000000.
I plan to use a modification of Igor Skochinsky's idea: copying 32 top bytes of RAM directly to RAM1. At the moment when the IAP runs in my firmware, RAM1 is unused. And then reverse operation after IAP.
Using this method because I also need to compile same code in GCC (no 'at' attribute). I need to find an elegant way of doing it.
#define RAM1PTR (*((volatile unsigned long *) 0x20000000)) RAM1PTR = 0xDEADBEEF;
... please tell me if you know straight away why this would be a dumb idea.
I'd like to be able to use the IAP routines on an LPC11U35 (very similar to the LPC11U24) to write to the flash, but the flash-related IAP routines make use of the top 32 bytes of RAM, which is typically used by the stack. I came from using code_red Red Suite, and it had an option to offset the stack pointer to prevent the stack from being corrupted by the IAP routines which is described here: http://support.code-red-tech.com/CodeRedWiki/ReserveIAPRam
Is there any chance we could get a similar option on the online compiler (or even just a permanent offset in the linker file)? I realize Tedd OKANO has written an IAP library, but it doesn't account for the stack issue. I've performed my own experiments in Red Suite, and I can confirm this range is modified by the IAP routines during a prepare, erase, prepare, write cycle.