Option to Offset Stack Pointer for IAP

21 Aug 2013

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. /media/uploads/neilt6/iap_experiment.jpg

22 Aug 2013

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);
}
29 Aug 2013

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...

29 Aug 2013

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

30 Aug 2013

Hmm... Very hacky, but it seems doable. Thanks!

30 Aug 2013

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.

03 Sep 2013

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!

03 Sep 2013

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?

03 Sep 2013

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
04 Sep 2013

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.

04 Sep 2013

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!

04 Feb 2015

I would be interested in an easy way of doing this as well. Resorting to mbed-src is not really ideal :)

25 Jul 2015

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.