Run from RAM in LPC13xx

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

Run from RAM in LPC13xx

2,744 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by elecia on Fri Apr 23 16:21:31 MST 2010
I am writing a bootloader for the LPC1343 to load code from an external device (not uart). I need it to run from internal RAM and write to the internal Flash. Something is going wrong with my stack pointer but first some details-.

   I am using the in application programming command (IAP) with the implementation following along that found in storing settings thread (http://knowledgebase.nxp.com/showthread.php?p=890).

  I copied the existing linker scripts and modified them to RamLoc8 instead of MFlash32 (modifying my project to use my new linker scripts as described in http://lpcxpresso.code-red-tech.com/LPCXpresso/node/31).

  With –Os (optimize for size), my code compiles ~6k which leaves me enough space to do the 256 byte writes (and plans to slim down my code):

    [FONT=Courier New][SIZE=1]text           data        bss         dec         hex     filename
[/SIZE][/FONT]
[FONT=Courier New][SIZE=1] 6152          12          364       6528      1980   bootloader.axf[/SIZE][/FONT]

  I recompiled CMSIS to set

  [FONT=Courier New]LPC_SYSCON->SYSMEMREMAP = SYSMEMREMAP_Val;[/FONT]

  Where SYSMEMREMAP_Val is defined as 0x00000001. So that my interrupt vectors are mapped to 0x1000000 (User RAM Mode).

  There was another parameter for ARM processors to allow this. The LPC111x relocatable vector table thread (http://knowledgebase.nxp.com/showthread.php?t=227) mentioned VTOR. The Cortex-M3 Technical Reference Manual (r1p1) talks about the Vector Table Offset Register (p 8-21, register at E000ED08, set bit 29 to get a RAM based table). I’m not sure which of these I need to do so I haven’t tried this one yet.

  Also, since I need –gc-sections, I upgraded to Xpresso v3.3.4 (per this thread: http://knowledgebase.nxp.com/showthread.php?t=283)

  So, my current problem is that my stack pointer (SP) starts out at 0x10000FEC in Reset_Handler(). My stack should be pretty tiny and my heap nonexistent. I’m not using printf, malloc or floating point numbers. From the clib, I’ve only got memcpy, memset and strlen. So, is there a way to see why my stack is either oddly huge at the start of the program or it is not set correctly? And is there already example code for running a program from RAM on an LPC13xx?

Thanks!
0 Kudos
Reply
12 Replies

2,630 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by NXP_USA on Tue May 11 15:03:32 MST 2010

Quote: elecia
I am writing a bootloader for the LPC1343 to load code from an external device (not uart). I need it to run from internal RAM and write to the internal Flash.
...
  I recompiled CMSIS to set

  [FONT=Courier New]LPC_SYSCON->SYSMEMREMAP = SYSMEMREMAP_Val;[/FONT]

  Where SYSMEMREMAP_Val is defined as 0x00000001. So that my interrupt vectors are mapped to 0x1000000 (User RAM Mode).
...



To be a bit clearer, SYSMEMREMAP=1 does not move the vectors. Instead it maps the memory from 0x10000000 through 0x100001FF to 0x0. You will be able to read and write to RAM at both 0x10000000 and at 0x00000000. 0x00000200 will read Flash.

Regarding your code size problems with squeezing everything into RAM- it might be worthwhile to avoid calling the redlib init funtion in the startup code. This may reduce code size some. You should be sure to avoid using any library functions that could need initialization like the toupper().
0 Kudos
Reply

2,630 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by elecia on Thu May 06 10:32:59 MST 2010

Quote:
Quote:
                                                                      Originally Posted by [B]elecia[/B]                     [IMG]http://knowledgebase.nxp.trimm.net/images/buttons/viewpost.gif[/IMG]                
                 [I]One more  annoying issue- the  Registers View loses the Main registers when it goes from C to assembly.  This can be fixed by opening a Memory View and switching between the  two. (Sorry, it is voodoo but it  seemed to work well enough.)[/I]
                                
I can't replicate this loosing of the main registers. Can you  provide more details of how to replicate this please?

I'm baffled, when I tried to reproduce this I couldn't. I thought it was just that I'd switched back to running from flash but as I try running from ram, it doesn't reoccur there either.

Thanks!
Thanks for your help. I hope that Code Red opts to support running from RAM in the debugger by setting up the registers. Of course, an example bootloader that can reflash the whole code space would be awesome. :) (The on chip serial bootloader and USB bootloader example are great but there is always someone wanting to do something else.)
0 Kudos
Reply

2,630 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by CodeRedSupport on Thu Apr 29 04:34:44 MST 2010

Quote: elecia
In the process, I had a lot of trouble with the  line:
       [FONT=Courier New][SIZE=2]
void  ResetISR(void) ALIAS(Reset_Handler);[/SIZE][/FONT]
 
Initially, I called my function Reset_Handler_ASM  and tried to plug it in to that alias but no amount of forward declaration would  make it work. Worse, when I deleted the line and I tried to run, I couldn’t  break point in Reset_Handler or Reset_Handler_ASM even though I set up g_pfnVectors to have  Reset_Handler_ASM. I can’t find where ResetISR has special magical properties, even in the Cortex-M3 Technical Reference Manual.  However, I eventually renamed my assembly function to ResetISR and could get to  my little assembly function.


Sorry you had problems here. [FONT=Courier New][SIZE=2]cr_startup_lpc13.c[/SIZE][/FONT] has been written in such a way that there are default implementations of the exception handlers (including reset) which can be overriden by user provided handlers, without the need to change the sources to [FONT=Courier New][SIZE=2]cr_startup_lpc13.c[/SIZE][/FONT]. Thus all you actually need to do is provide an external, global function called ResetISR.

This is also the same basic mechanism as used in your blinky example for the timer interrupt handler to be registered. You have a [FONT=Courier New][SIZE=2]TIMER32_0_IRQHandler() [/SIZE][/FONT]defined in  [FONT=Courier New][SIZE=2]timer32.c[/SIZE][/FONT], which overrides the use of the default handler set up in [FONT=Courier New][SIZE=2]cr_startup_lpc13.c[/SIZE][/FONT].

You can find more information on the weak and alias function attributes used to set all this up in the compiler documentation:

http://gcc.gnu.org/onlinedocs/gcc-4.3.3/gcc/Function-Attributes.html#Function-Attributes[INDENT][B]IMPORTANT NOTE : the startup code created by LPCXpresso 3.3.4 has changed slightly compared to the above. It no longer aliases ResetISR in the way described above. [Thus there is no Reset_Handler() anymore].  Thus if you were using startup code created with LPCXpresso 3.3.4, you would need to rename/delete the function ResetISR() inside cr_startup_lpc13.c, then define your own one externally.[/B]
[/INDENT]
Quote: elecia

One more  annoying issue- the Registers View loses the Main registers when it goes from C to assembly. This can be fixed by opening a Memory View and switching between the two. (Sorry, it is voodoo but it  seemed to work well enough.)


I can't replicate this loosing of the main registers. Can you provide more details of how to replicate this please?


Quote: elecia

Finally, the stack pointer is correct when the  program begins.

I think that there may be some LPCXpresso debugger issues here:

[LIST=1]
[*]Debugger should set  SP to the value it has loaded to .isr_vector
[*]ResetISR magic  should be documented in the cr_startup code or (better) the reset vector  should be read from .isr_vector
[/LIST]


I take your points, but I'm not 100% convinced this is the right way to go here.

Remember these parts were designed to run from flash at address 0x0 at reset. You are effectively trying to backdoor starting from RAM at 0x10000000 instead. In your "final" system, it sounds like you will have a bootloader in the flash, which loads your code into RAM at 0x10000000 and runs it from there. I would have thought that your bootloader (or the RAM code itself) will have to do the setting up of the stack pointer etc for your RAM based code - it won't have the debugger running to do this for you!

Anyway, I think we (CodeRed) need to ponder a little more on the issue of running from RAM like this. But at least you have a working solution for now.

Regards,
CodeRedSupport
0 Kudos
Reply

2,629 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by elecia on Wed Apr 28 11:31:42 MST 2010
I was too optimistic and my program began crashing  again as the stack had overwritten one part of my code. I finally fixed with  some creative reset handling.

  The problem centered around Reset_Handler executing  a push to the stack before my added code had the chance to correct the stack.  If you break in Reset_Handler and show the disassembly, you can see:

       [FONT=Courier New][SIZE=3]                [SIZE=2]push       {r7, lr}[/SIZE][/SIZE][/FONT]
 
Given this is at the very start of the function,  before the C-level function really starts, I couldn’t see any way to  solve this in C nor any way to make the debugger set the stack pointer to the value it loaded when it  loaded the code. I resorted to a short assembly that sets the stack pointer and  then calls Reset_Handler (reset.s).

  In the process, I had a lot of trouble with the  line:
       [FONT=Courier New][SIZE=2]
void  ResetISR(void) ALIAS(Reset_Handler);[/SIZE][/FONT]
 
Initially, I called my function Reset_Handler_ASM  and tried to plug it in to that alias but no amount of forward declaration would  make it work. Worse, when I deleted the line and I tried to run, I couldn’t  break point in Reset_Handler or Reset_Handler_ASM even though I set up g_pfnVectors to have  Reset_Handler_ASM. I can’t find where ResetISR has special magical properties, even in the Cortex-M3 Technical Reference Manual.  However, I eventually renamed my assembly function to ResetISR and could get to  my little assembly function.

One more  annoying issue- the Registers View loses the Main registers when it goes from C to assembly. This can be fixed by opening a Memory View and switching between the two. (Sorry, it is voodoo but it  seemed to work well enough.)
 
Finally, the stack pointer is correct when the  program begins.

I think that there may be some LPCXpresso debugger issues here:

[LIST=1]
[*]Debugger should set  SP to the value it has loaded to .isr_vector
[*]ResetISR magic  should be documented in the cr_startup code or (better) the reset vector  should be read from .isr_vector
[*]Registers View should never  lose the Main registers
[/LIST]
Please let me know if you cannot  reproduce these. I've ported my changes to blinky and attached it.
0 Kudos
Reply

2,630 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by elecia on Tue Apr 27 12:06:13 MST 2010
Thank you for your response. I agree that the bad stack pointer is tramping over the RAM. I put in the --gc-sections (I have version 3.3.4) and it reduced the size considerably:

   text       data        bss        dec        hex    filename
   2324          4         24       2352        930    blinky.axf

However, when I breakpoint at the start of Reset_Handler, the SP is 0x10000fec.  Because blinky is so small now, the bad stack pointer doesn't cause a problem, however, I don't think blinky's stack is over 0x1000 before the start of Reset_Handler.

Actaully, I don't see why the non-optimized version shouldn't work. It built to 0x1cc4, 7364 bytes. That leaves 0x334 (828) bytes for the stack which should be plenty. There is no heap.

So you said "your SP value is not being picked up", do you recommend that I force the SP to the _vStackTop (read from 0x10000000) at the start of Reset_Handler? Let's see what happens if I try that...

When I look at 0x10000000 (g_pfnVectors), the _vStackTop value is 0x10001FF0. So if I add some code to cr_startup_lpc13.c, blinky without --gc-sections builds to a whopping 8032 bytes but, more importantly, it works.
[B][COLOR=Black]
[/COLOR][/B][FONT=Courier New][COLOR=Black][SIZE=2][B]#include "LPC13xx.h" // for set stack pointer[/B]
void
//ResetISR(void)
Reset_Handler(void)
{
    unsigned long *pulSrc, *pulDest;
[/SIZE][/COLOR][/FONT][FONT=Courier New][COLOR=Black][SIZE=2]
[B]// loading code in RAM means stack pointer is  set before loading is complete[/B][/SIZE][/COLOR][/FONT][COLOR=Black][B][FONT=Courier New][SIZE=2], reset it now to the correct value:
    __set_MSP((uint32_t)g_pfnVectors[0]); [/SIZE][/FONT][/B]

....[/COLOR]

(New code is in bold.)

Thanks! With this code, my bootloader is no longer having code stomped on by the stack pointer as it runs from ram.

I'd still be happy to take any advice about if this is a good solution or a horrible hack (and if there is a better way).
0 Kudos
Reply

2,629 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by CodeRedSupport on Tue Apr 27 03:37:03 MST 2010
With regards to the stack pointer, this will normally be read  automatically by the processor from 0x0 as it comes out of reset. I  suspect that what is happening here is that because you are downloading  your code into RAM, your SP value is not being picked up (as it isn't in  flash, and as yet your RAM based code has not had its first 0x200 bytes  remapped over the top of address 0x0), and instead a value is being  picked up from elsewhere (maybe from user flash, or possibly the boot  flash).

Anyway, from experimentation with your example project what I believe is happening is that the stack is  trampling over your RAM based program code and data. If you look at the  info printed at the end of the build....

text        data         bss         dec         hex    filename
7336          4          24       7364       1cc4    blinky.axf

you can see that the code  is taking up most of the 8KB RAM on the LPC1343. This trampling of code will  definitely take place if you don't manually move the stack when you hit  the breakpoint you have set on Reset_Handler(). And even if you move it  up to 0x10001FF0, I still see corruption.

If you reduce the size  of your code, for example by enabling the --gc-sections option on the  Debug build, then the application does then run correctly.

See  this thread for some comments on --gc-sections though...

http://knowledgebase.nxp.com/showthread.php?t=325

Hope  this all helps.

Regards,
CodeRedSupport
0 Kudos
Reply

2,630 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by igorsk on Mon Apr 26 15:57:25 MST 2010
Well, I'm not sure how it works at all (my guess is that the debugger changes PC to the entry point), but in your bootloader you'll need to:
1) load SP from the first entry
2) set vectors to RAM and
3) jump to the reset vector from the second entry.
0 Kudos
Reply

2,630 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by elecia on Mon Apr 26 14:39:37 MST 2010
Looking in Reset_Handler, the linker variables are not filled in correctly.

[FONT=Courier New]extern unsigned long _etext;
extern unsigned long _data;
extern unsigned long _edata;
extern unsigned long _bss;
extern unsigned long _ebss;
[/FONT]
The addresses are correct (from Expressions), matching the map file:
"&_data" = 0x10001ca8   
"&_edata" = 0x10001cac   
"&_bss" = 0x10001cac   
"&_ebss" = 0x10001cc4   
"&_etext" = 0x10001ca8   

But the values are junk:
"_data" = 0x44aa200   
"_edata" = 0xfd28603f   
"_bss" = 0xfffffffffd28603f   
"_ebss" = 0xffffffffa29bbd53   
"_etext" = 0x44aa200   

Also, the previously attached blinky compiled to over 7k. If you put in the -Os optimization flag, it slims down to under 4k which gives the program some space to actually have a stack (and a heap, if it came to that).

I am beginning to think it is the loading of the image to RAM from LPCXpresso that may be at the source of some of my difficulties.
0 Kudos
Reply

2,630 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by elecia on Mon Apr 26 14:21:16 MST 2010
Currently, I’m loading through LPCXpresso, blindly assuming that it will do the right thing. In the attached, I've modified blinky in the same way I’ve modified my bootloader: just move everything in link files to RAM and set SYSMEMREMAP (via a horrible hack to CMIS SystemInit()). If you break at the start of Reset_Hander and look at the registers, you can see the SP is bad. 

  I think that part of this works because it gets to the Reset_Handler, the one that I loaded in RAM (based on the Debug window’s stack trace).

Since I'm not setting anything up, I suspect more is wrong than the stack pointer. Though it is odd that quite a bit of the code runs as expected until incorrect SP ends up writing over pieces of code and then it all goes bad.

In the long run, I will load the bootloader into RAM and then jump to start of execution (or possibly, set the exceptions to RAM based and then reset the code so it resets into the RAM code).

Thanks!
0 Kudos
Reply

2,630 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by igorsk on Mon Apr 26 12:58:24 MST 2010
How do you start the program in RAM? Do you load the stack pointer from the exception table?
0 Kudos
Reply

2,630 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by elecia on Mon Apr 26 11:45:02 MST 2010
Thanks, that is an interesting example for a bootloader.

However, Linking into different flash hasn't been a problem so far. So I'm still in need of guidance on debugging why my stack pointer is no good on program start or an example of executing code from RAM.

Help?
0 Kudos
Reply

2,630 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by whitecoe on Sat Apr 24 06:21:32 MST 2010
As a starting point, you might want to take a look at the USB bootloader (and the associated examples which place their executables at 64KB) in the examples for Code Red's RDB1768 board. You can find these in the lpcxpresso IDE examples directory, or download the latest versions from:

http://support.code-red-tech.com/CodeRedWiki/RDB1768cmsisExampleProjects

This is slightly different from what you want to do, in that the applications get linked to run from higher up in flash than the bootloader, rather than into RAM. But the general principles may well give you some pointers.

Hope this helps :)
0 Kudos
Reply