LPC11C14 IAP 57 command bug CAN

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LPC11C14 IAP 57 command bug CAN

3,297 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Opticalworm on Tue Jan 31 13:12:55 MST 2012
Hello Everyone, 
for the last few days now, I've been trying to figure out why my LPC11C14 kept crashing everytime I try to re-invoke the CAN bootloader (ISP) via IAP57. (bare in mind that my code were compiled at the time in uVision)
Eventually I decide to try my hand at compiling the same code in LPCXpresso and to my supprise, it worked worked fine. (Also bare in mind that I used virtually the same code.)

Test Code (in LPCXpresso):

So after a few days of of testing, hours of reading datasheets, and comparing the RAM on output from both IDEs; I belive that I've found where the bug comes is at and why it has a better chance in working LPCXpresso over uVision.

[B][SIZE=3]In short[/SIZE][/B]
The bug is due to the fact that the IAP57 command doesn't reset the stack during its initialization phase and instead it uses the user current stack pointer (which could be any where in RAM at the point). Flash magic (Via CAN) assumes the ISP bootloader wont use the RAM rigion bellow 0x1000_1E60 [= (0x1000_2000 -(256 +128 +32))] while it writes to the rest of the, supposedly free, RAM. as you can see Flash magic could write to the part of the RAM ISP is using and cause the device to fail.

this was the bug that cause by LPC11C14 to crash.

[B][SIZE=4][COLOR=black]The bit I needs help on[/COLOR][/SIZE][/B]
Now that I know that the stack need resetting, I'm having trouble in actually doing it.

for example I've tried adding the following command into InvokeBootloader() just before calling the IAP57 (the stack pointer I select should place the stack 32 byte bellow the top of the stack.)
     __set_MSP(0x10001FE0); 
but when I call iap_entry() the LPC11C14 brings up a hard fault.
Any Idea clue what I'm doing wrong?

 
void InvokeBootloader(void) {     
uint32_t command[5];    
 uint32_t result[4];     
 
__disable_irq();      /* make sure 32-bit Timer 1 is turned on before calling ISP */   

 LPC_SYSCON->SYSAHBCLKCTRL |= 0x00400;     /* make sure GPIO clock is turned on before calling ISP */     
LPC_SYSCON->SYSAHBCLKCTRL |= 0x00040;     /* make sure IO configuration clock is turned on before calling ISP */  
LPC_SYSCON->SYSAHBCLKCTRL |= 0x10000;     /* make sure AHB clock divider is 1:1 */     
LPC_SYSCON->SYSAHBCLKDIV = 1;     // Disable the PLL     LPC_SYSCON->MAINCLKSEL = 0; // Enable the IRC oscillator     LPC_SYSCON->MAINCLKUEN    = 0x01;               /* Update MCLK Clock Source */     
LPC_SYSCON->MAINCLKUEN    = 0x00;               /* Toggle Update Register   */     
LPC_SYSCON->MAINCLKUEN    = 0x01;     while (!(LPC_SYSCON->MAINCLKUEN & 0x01));       /* Wait Until Updated       */ 
     
command[0] = IAP_REINVOKE_ISP;    // Reset the stack pointer before calling IAP-57    

__set_MSP(0x10001FE0);          //reset stack pointer

iap_entry(command, result); 
}  
[SIZE=3][COLOR=black]Oh yeah this is the quick outline of the Device and Tools I'm using [/COLOR][/SIZE][COLOR=black][SIZE=3]
[/SIZE][/COLOR]• LPC11C14 BootLoader Vr: 7.1
• LPC11C14 Device ID: 0x1440102B
• IDE: KEIL uVision, LPCXpresso
• Debbuger: ULINK2, LPC-LINK
• ISP Programmer: Flash magic with [PEAK PCAN USB]

-Ron
0 Kudos
Reply
10 Replies

3,047 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by coox on Tue Dec 11 04:35:42 MST 2012
I have a problem with this code, using LPC11u24

If I use
__set_MSP(*((uint32_t *)0x1FFF0000)); /* inline asm */ 
USB ISP mode doesn't start

But if I use
__set_MSP(0x10001FE0); /* inline asm */ 
It works.

What's the reason for such behaviour?
0 Kudos
Reply

3,047 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by coox on Tue Dec 11 04:31:57 MST 2012
I have a problem, using this code on LPC11u24.

If I use

__set_MSP(*((uint32_t *)0x1FFF0000)); /* inline asm */ 
0 Kudos
Reply

3,047 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by NXP_USA on Tue Feb 14 16:40:39 MST 2012
Yes, you're right...we will fix the app note.
I have it declared as global so it is not read from the stack.
Thanks for your help.
0 Kudos
Reply

3,047 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Opticalworm on Tue Feb 14 02:37:13 MST 2012

Quote: NXP_USA
You have to set stack pointer to ROM value (reset default) as below. This is done before before calling ISP, because most C expressions and function returns will fail after the stack pointer is changed.

__set_MSP(*((uint32_t *)0x1FFF0000)); /* inline asm */

We have an app note �Adding ISP to LPC1102 systems� (AN11015) which shows this.
http://www.nxp.com/documents/application_note/AN11015.pdf

Also, keep in mind that to re-invoke ISP, you have to configure PIO0_3 and drive it low to call the C_CAN ISP command handler.

There is an update required to the User Manual (update still needs to be released). I have attached an image file to reflect this. Hope this helps.
Thanks.



@NXP_USA sorry to say but the AN11015 (fig3) example is invalid.
You can't change the stack pointer and still expect to be able to pass local variable to iap_entry(). it won't work.

This is due to the fact that the local variables "uint32_t command[5], result[4]" will be assigned a relative address to the current stack point when ReinvokeISP() is called and if you change the stack point before calling iap_entry, then you could be passing anything to it.

You really need to make sure that command[] and result[] is defined as static in ReinvokeISP() or declare them is the globally scope.
 
see my working example bellow.

-ron
0 Kudos
Reply

3,045 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by NXP_USA on Mon Feb 13 18:21:36 MST 2012
You have to set stack pointer to ROM value (reset default) as below. This is done before before calling ISP, because most C expressions and function returns will fail after the stack pointer is changed.

__set_MSP(*((uint32_t *)0x1FFF0000)); /* inline asm */

We have an app note “Adding ISP to LPC1102 systems” (AN11015) which shows this.
http://www.nxp.com/documents/application_note/AN11015.pdf

Also, keep in mind that to re-invoke ISP, you have to configure PIO0_3 and drive it low to call the C_CAN ISP command handler.

There is an update required to the User Manual (update still needs to be released). I have attached an image file to reflect this. Hope this helps.
Thanks.
0 Kudos
Reply

3,045 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Opticalworm on Thu Feb 02 01:12:43 MST 2012
Would anybody from NXP confirm the IAP57 bug and my workaround? could there be a possibly of the user manual update been updated?

-Ron
0 Kudos
Reply

3,045 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Opticalworm on Wed Feb 01 08:55:53 MST 2012
I've found the last piece of the puzzle with the helpful hint from @whitecoe.

I forgot about my local varaibles (command[] and result[]).

By the time I call iap_entry(command, result) the stack pointer would have already been modified but I stupidly try to pass command[] and result[] to iap_entry(). (which won't have a valid data).
Command[] and Result[] pointer will be pointing to a different part in ram which will, no dough, have random data. iap_entry() will most likely cause it to return from the call (when the code weren't expecting it not to do so) and causing the function that called it to fail or possibly cause the CPU to rise a hard fault due to an invalid return pointer or from the stack corruption.

so here my working solution:

void InvokeBootloader(void)
{
    // Make sure that you define command[] and  result [] as static if their locally declared or just declared them globally.
    // this is because by the time you call iap_entry(command, result) the stack pointer would have already been modified by you
    // and your code try to pass command[] and result[] to iap_entry() which won't be valid. it will try to pass data from a unexpected 
    // location in RAM where the data could be anything.
    // iap_entry will most likely return (when you were expecting it not too) and causing the function that called it to fail 
    /// or possibly causing the CPU to rise a hard fault due to an invalid return pointer or from the stack corruption.
    static uint32_t command[5];
    static uint32_t result[4];
    
    
    __disable_irq();

    // make sure 32-bit Timer 1 is turned on before calling ISP
    LPC_SYSCON->SYSAHBCLKCTRL |= 0x00400;
    // make sure GPIO clock is turned on before calling ISP
    LPC_SYSCON->SYSAHBCLKCTRL |= 0x00040;
    // make sure IO configuration clock is turned on before calling ISP
    LPC_SYSCON->SYSAHBCLKCTRL |= 0x10000;
    
    // make sure AHB clock divider is 1:1
    LPC_SYSCON->SYSAHBCLKDIV = 1;
    
    // Disable the PLL
    LPC_SYSCON->MAINCLKSEL = 0; // Enable the IRC oscillator
    LPC_SYSCON->MAINCLKUEN    = 0x01;               /* Update MCLK Clock Source */
    LPC_SYSCON->MAINCLKUEN    = 0x00;               /* Toggle Update Register   */
    LPC_SYSCON->MAINCLKUEN    = 0x01;
    while (!(LPC_SYSCON->MAINCLKUEN & 0x01));       /* Wait Until Updated       */

    command[0] = IAP_REINVOKE_ISP;

    // Reset the stack pointer to point to the top of the stack minus the 32 byte for IAP
    // for LPC11C14 where top of RAM is 0x1000_2000; MSP = (0x1000_2000 - 0x20) = 0x10001FE0
    __set_MSP(0x10001FE0);
    // the Cortex M0 needs to synchronize the the CPU cache
    __ISB();
    
    //Invoke the bootloaer
    iap_entry(command, result);
}
Here my final test project solution if anyone want to it:

/*
===============================================================================
 Name        : main.c
 Author      : 
 Version     :
 Copyright   : Copyright (C) 
 Description : main definition
===============================================================================
*/

#ifdef __USE_CMSIS
#include "LPC11xx.h"
#endif

#include <cr_section_macros.h>
#include <NXP/crp.h>

// Variable to store CRP value in. Will be placed automatically
// by the linker when "Enable Code Read Protect" selected.
// See crp.h header for more information
__CRP const unsigned int CRP_WORD = CRP_NO_CRP;

#define IAP_LOCATION 0x1FFF1FF1
typedef void (*IAP)(unsigned int command[], unsigned int result[]);
static const IAP iap_entry = (IAP) IAP_LOCATION;

#define IAP_REINVOKE_ISP            57



////////////////////////////////////////////////////////////////////////////////
///    \brief    Clears an individual bit in a variable.
///    \param    var    The variable to modify.
///    \param    bit    The bit position to clear.
////////////////////////////////////////////////////////////////////////////////
#define BitClear(var, bit)    (var &= ~((uint32_t)1 << (bit)))
////////////////////////////////////////////////////////////////////////////////
///    \brief    Sets an individual bit in a variable.
///    \param    var    The variable to modify.
///    \param    bit    The bit position to set.
////////////////////////////////////////////////////////////////////////////////
#define BitSet(var, bit) (var |= ((uint32_t)1 << (bit)))

#define LED1_GPIO    LPC_GPIO2
#define LED1_PIN    3
#define LED2_GPIO    LPC_GPIO3
#define LED2_PIN    1


////////////////////////////////////////////////////////////////////////////////
///    \brief    It will re-invoke the system Bootloader. (UART or CAN. depending what 
///    \note the type of bootloader it invoke is depeding what the value of PIO0_3 is
////////////////////////////////////////////////////////////////////////////////    
void InvokeBootloader(void)
{
    // Make sure that you define command[] and  result [] as static if their locally declared or just declared them globally.
    // this is because by the time you call iap_entry(command, result) the stack pointer would have already been modified by you
    // and and your code try to pass command[] and result[] to iap_entry()which wont be valid. it will try to pass data from a unexpected 
    // location in RAM where the data could be anything.
    // iap_entry will most likely return (when you were expection it not too) and causing the function that called it to fail 
    /// or possibly causing the CPU to rise a hard fault due to an invalid return pointer or from the stack curruption.
    static uint32_t command[5];
    static uint32_t result[4];
    
    
    __disable_irq(); // disable the system interrupt

    // There no needs to alter the system clock setting or any other peripheral reletaed
    // setting becasue it will initiliaze what needs before executing the ISP bootloader
    
    
    command[0] = IAP_REINVOKE_ISP;

    // Reset the stack pointer to point to the top of the stack minus the 32 byte for IAP
    // for LPC11C14 where top of RAM is 0x1000_2000; MSP = (0x1000_2000 - 0x20) = 0x10001FE0
    __set_MSP(0x10001FE0);
    // the Cortex M0 needs to synchronize the the CPU cache
    __ISB();
    
    //Invoke the bootloaer
    iap_entry(command, result);
}

////////////////////////////////////////////////////////////////////////////////
///    \brief    Setup LEDS
////////////////////////////////////////////////////////////////////////////////
void LEDInit(void)
{
    // Initiliase LED 1 to GPIO, no pullup
    LPC_IOCON->PIO2_3 = 0;
    // Set the GPIO to output
    BitClear(LED1_GPIO->DATA, LED1_PIN);
    BitSet(LED1_GPIO->DIR, LED1_PIN);

    // Initialise LED 2 to GPIO, no pullup
    LPC_IOCON->PIO3_1 = 0;

    // Set the GPIO to output
    BitClear(LED2_GPIO->DATA, LED2_PIN);
    BitSet(LED2_GPIO->DIR, LED2_PIN);

}


/////////////////////////////////////////////////////////////////////////
///    \brief    Initialises the system clock and pll
/////////////////////////////////////////////////////////////////////////
void Main_ClockInit (void)
{
    /* System Oscillator Setup  */
    uint32_t i;
    LPC_SYSCON->PDRUNCFG     &= ~(1 << 5);          /* Power-up System Osc      */
    LPC_SYSCON->SYSOSCCTRL    = 0x00000000;

    for (i = 0; i < 200; i++)
    {
        __NOP();
    }

    LPC_SYSCON->SYSPLLCLKSEL  = 0x00000001;   /* Select PLL Input         */
    LPC_SYSCON->SYSPLLCLKUEN  = 0x01;               /* Update Clock Source      */
    LPC_SYSCON->SYSPLLCLKUEN  = 0x00;               /* Toggle Update Register   */
    LPC_SYSCON->SYSPLLCLKUEN  = 0x01;
    while (!(LPC_SYSCON->SYSPLLCLKUEN & 0x01));     /* Wait Until Updated       */

    /* System PLL Setup         */
    LPC_SYSCON->SYSPLLCTRL    = 0x00000004;
    LPC_SYSCON->PDRUNCFG     &= ~(1 << 7);          /* Power-up SYSPLL          */
    while (!(LPC_SYSCON->SYSPLLSTAT & 0x01));          /* Wait Until PLL Locked    */


    LPC_SYSCON->MAINCLKSEL    = 0x00000003;     /* Select PLL Clock Output  */
    LPC_SYSCON->MAINCLKUEN    = 0x01;               /* Update MCLK Clock Source */
    LPC_SYSCON->MAINCLKUEN    = 0x00;               /* Toggle Update Register   */
    LPC_SYSCON->MAINCLKUEN    = 0x01;
    while (!(LPC_SYSCON->MAINCLKUEN & 0x01));       /* Wait Until Updated       */


    LPC_SYSCON->SYSAHBCLKDIV  = 0x00000001;
    LPC_SYSCON->SYSAHBCLKCTRL = 0x0003105F;
    LPC_SYSCON->SSP0CLKDIV    = 0x00000001;
    LPC_SYSCON->UARTCLKDIV    = 0x00000001;
    LPC_SYSCON->SSP1CLKDIV    = 0x00000001;

    //Update system clock
    SystemCoreClockUpdate();             /* Get Core Clock Frequency      */

}


////////////////////////////////////////////////////////////////////////////////
///    \brief    Main
////////////////////////////////////////////////////////////////////////////////
int main(void)
{
    uint32_t i = 0;
     
    Main_ClockInit();
      LEDInit();

    for(;;)
    {
        for(i = 0; i < 1000000; i++);
        BitClear(LED1_GPIO->DATA, LED1_PIN);

        for(i = 0; i < 1000000; i++);
        BitSet(LED1_GPIO->DATA, LED1_PIN);

        InvokeBootloader(); // InvokeBootloader() should never return

    };

    return 0 ;
}



-Ron
0 Kudos
Reply

3,045 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by whitecoe on Wed Feb 01 04:22:51 MST 2012
So you are hand modifying the stack pointer in a function that has local data (ie it is on the stack). Sounds like a recipe for disaster!

Try setting a breakpoint just before you change the stack pointer, then  look at the values in the registers and the actual instructions in  memory.

What I imagine is happening is that the code is trying to access that local data after you modify the stack pointer (and which the compiler doesn't really know you have changed), and that stack access for accessing the local data is being made beyond the end of the RAM - triggering a hard fault.

HTH!
0 Kudos
Reply

3,045 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Opticalworm on Wed Feb 01 04:00:40 MST 2012
@zero

Quote:
#1 I'm missing reserving ROM-RAM in your code. Is it included somewhere?

In KEIL I've set the ram range to not include the 32 byte as part of the poject setup settings. As for LPCXpresso, i've taken care of the 32 byte offset in the project property (Properties>C/C++>settings>target = 32). Sorry I should had mention that.


Quote:

#2 I never had problems with IAP57 call (perhaps because #3)

#3 To program nodes with enabled watchdog I reset the node, exclude  watchdog-init and call IAP57. So my node is probably in a safe  environment :):)


That make sence. So in your case, your code is reset by WDT some where deeep in the stack (depending on your code size and stack level) and on bootup, where the stack is right at the top of RAM (on LPCXpresso), you call the IAP57 command if the WDT cause the reset it. That why you never had the bug.
Unfortunatly, if your using KEIL, where the stack is located at the bottom of RAM, you will still have a problem (depending on the code size your sending).

Any Idea on how I can reset the stack to a know value before calling IAP57 command.

so far I've found some more information on what I should do to when editng the stack pointer. ARM Control register (See footer note)
It says that I should, after setting the stack pointer, call the ISB intruction.

for example:

    __set_MSP(0x10001FDF);  // Set stack pointer
    
    __ISB();  //ISB intruction, cache synchronization barrier



It still causing a hard fault.

-Ron
0 Kudos
Reply

3,045 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Ex-Zero on Wed Feb 01 02:22:59 MST 2012
Just a few notes:

#1 I'm missing reserving ROM-RAM in your code. Is it included somewhere?

#2 I never had problems with IAP57 call (perhaps because #3) :)

#3 To program nodes with enabled watchdog I reset the node, exclude watchdog-init and call IAP57. So my node is probably in a safe environment :):)
0 Kudos
Reply