EmbeddedRelated.com
Forums

Flash program to RAM execution

Started by kohansey May 10, 2007
--- In l..., "kohansey" wrote:
>
> The idea behind this module is to reprogram the ARM's firmware from a
> USB drive, by our customers. If the module lives in RAM, when we ship
> then the product, then module will be lost do to power toggles, which
> is something we don't want. The module is a self-contained driver
> that "talks" to a USB controller and reads the firmware binary from a
> connected USB drive. The firmware is read into RAM and then loaded
> into flash via IAP calls. Because of the reprogramming, this module
> must execute from RAM, but to allow the customer to reprogram anytime
> they want, the module must live in flash.
>

This type of thing is trivial with GNU gcc and ld. It's almost fully
documented in the 'ld' manual (page 44 of 'Using ld' Version 2.14). I
tried it yesterday and it appeared to work just fine.

However... (Don't you just hate however's?) You have to declare a
pointer to the function and call via the pointer. Otherwise you will
have a problem with addressing.

Here's the function prototype:

int Setup1(void) __attribute__ ((naked)) __attribute__ ((section
(".xram")));

Here's the function:

int Setup1(void)
{
return 4;
}

Here's the pointer declaration:

static int (*pramfunc)(void) = Setup1;

Here's the invocation:

void Setup(void)
{
(*pramfunc)();
...
}

Finally, here's what I did to the 'ld' script. It needs further
refinement:

SECTIONS
{
.text :
{ {
*(.startup)
*(.text)
*(.rodata)
*(.rodata*)
*(.glue_7)
*(.glue_7t)
_etext = .;
} >flash

.other :
{
_other = .;
*(.xram);
_eother = .;
} >ram AT >flash

.data :
{
_data = .;
*(.data)
_edata = .;
} >ram AT >flash

.bss :
{
_bss_start = .;
*(.bss)
} >ram

. = ALIGN(4);
_bss_end = . ;
_end = .;

.dma :
{
_dma_start = .;
*(.dma)
_dma_end = . ;
} >ram_usb_dma
}

By defining the symbols '_other' and '_eother', I can copy the flash
between these locations to the beginning of RAM. In fact, the .map
file will show space for the code in RAM before the linker allocates
.data. By definition, this is the beginning of RAM.

Now, if the function had position independent code, it would be even
more fun to copy it to the stack and run from there. This would
eliminate the static allocation of RAM for a function that runs every
couple of years. I have no idea if this will work.

Or, use the overlay feature of 'ld' and see if you can put the code
into .bss or even .data on top of other data. After all, the system
will require a reboot after updating so trashing these areas is
unimportant.

In any event, there is no point in copying the code to RAM until it is
time to run. Copying certainly doesn't need to be in the startup code
where RAM is initialized.

Kind of a slow day yesterday...

Richard

An Engineer's Guide to the LPC2100 Series

Thank you, Richard for you reply. Do anyone know how to do what
Richard just described but using the RealView compiler?
Thank you, Richard for you detailed reply. I was wondering if any
knows how to do exactly what Richard just described but using the
RealView compiler?
--- In l..., "kohansey" wrote:
>
> Thank you, Richard for you detailed reply. I was wondering if any
> knows how to do exactly what Richard just described but using the
> RealView compiler?
>

Seems to me that Darcy Williams has already pointed you to the answer:
http://tech.groups.yahoo.com/group/lpc2000/message/24836

Doesn't the factory (Keil) approach work?

Richard
> The idea behind this module is to reprogram the ARM's firmware from a
> USB drive, by our customers. If the module lives in RAM, when we ship
> then the product, then module will be lost do to power toggles, which
> is something we don't want. The module is a self-contained driver
> that "talks" to a USB controller and reads the firmware binary from a
> connected USB drive. The firmware is read into RAM and then loaded
> into flash via IAP calls. Because of the reprogramming, this module
> must execute from RAM, but to allow the customer to reprogram anytime
> they want, the module must live in flash.
>

I had the same problem. In my application there is not enough RAM left to do a scatter loading of the module at startup.

I found a soloution to fix this problem (see source code attatched below). Within the module a scatter loading section named UPDATER is defined (there is no need to edit the scatter loading description file). The two linker variables UPDATER$$Base and UPDATER$$Limit represents the boundaries of this module. They are used to controll the copy process.

I'm using RealView MDK-ARM Version: 3.40.

/*
* ==> Compile this file position indipendend! <= *
* Open: Options for file 'Updater.c' -> C/C++
* Enter '--apcs=/ropi' into the field 'Misc controls'
*/
#include

#pragma push // store current pragmas
#pragma O0 // disable optimation
#pragma arm section code="UPDATER" // create new scatter section

typedef void func_type(void);

/*
* linker defined variables.
*/
extern unsigned long UPDATER$$Base; // first byte of section UPDATER
extern unsigned long UPDATER$$Limit; // first byte after section
// UPDATER

/*
* (dummy function)
* A simple function called by Update_FW.
*/
void Sub_function(void)
{
FIO0CLR |= 0x08000000; // clr pin
}
//////////////////////////////////////////////////////////////
/*
* (dummy function)
* In the real project, this function will update the flash
* with the new firmware.
*/
void Update_FW(void)
{
FIO0SET |= 0x08000000; // set pin
Sub_function();
}
//////////////////////////////////////////////////////////////
/*
* Copy the code of this module to the desired address.
* Return a function pointer to Update_FW now located in RAM.
*/
func_type* Copy(unsigned int addr)
{
unsigned int *src, *dest, *begin, *end, offset;

// boundaries of the section UPDATER
begin = (unsigned int*) &UPDATER$$Base;
end = (unsigned int*) &UPDATER$$Limit;

src = begin;
dest = (unsigned int*) addr;

// copy data from FLASH to RAM
while (src < end)
{
*dest++ = *src++;
}

// offset between the function Update_FW
// and the base address uf the section UPDATER
offset = ((unsigned int) &Update_FW) - ((unsigned int) begin);

// return pointer to the function Update_FW
// now located in RAM
return (func_type*)(addr + offset);
}
//////////////////////////////////////////////////////////////
#pragma pop // restor pragmas

On Fri, 08 May 2009 11:34:03 -0000, you wrote:

>> The idea behind this module is to reprogram the ARM's firmware from a
>> USB drive, by our customers. If the module lives in RAM, when we ship
>> then the product, then module will be lost do to power toggles, which
>> is something we don't want. The module is a self-contained driver
>> that "talks" to a USB controller and reads the firmware binary from a
>> connected USB drive. The firmware is read into RAM and then loaded
>> into flash via IAP calls. Because of the reprogramming, this module
>> must execute from RAM, but to allow the customer to reprogram anytime
>> they want, the module must live in flash.

Where the storage/comms involves a lot of code (USB/TCP etc.) another option is to add an external
serial eeprom/flash chip.
The new firmware image can be copied into it by code residing within the main application, and only
when the new image is complete and verified, it can be IAP'd using a very simple loader.

This avoids the risks of a RAM-based complex loader leaving a system in limbo due to an incomplete
update. With the above approach, the 'vulnerable' window when copying the image is typically only a
second or two.