Building a relocatable C program that can be run out of SRAM in MQX

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

Building a relocatable C program that can be run out of SRAM in MQX

Jump to solution
3,895 Views
mykepredko
Contributor IV

HI Folks,

 

I am interested in finding if anyone has bult a ColdFire C app (as well as created the build configuration files) that can be run in SRAM under MQX?  I would be using CW10 as the development tool. 

 

There are three situations that I am interested in:

1) A larger program which would run independently in SRAM with its primary purpose being to update the entire CF's Flash with an image downloaded over a Serial or USB link. 

2) A small program that could be run out of allocated memory (using _mem_alloc and then loading the code and data segments into SRAM) with access to the system via my own library (probably use a SW interrupt interface to allow it to be relocatable).  This would mean the app's variable and stack memory would follow at the end of the application image. 

3) As a permutation of 2), loading the C app in an arbitrary location in Flash and provide it with its own SRAM, using _mem_alloc before execution. 

 

Question 1) is really the important one - I would like to provide a mechanism for updating the Flash while the application is in the field.  In this case, the program would be built as a complete application with no set code addresses, other than the interrupt table. 

 

From looking at the code output, it seems like the IAR compiler generates relocatable code (seemingly known as "PIC" = "Position Independent Code") - along with this, what would I have to do extra that is needed for an application's start up (like setting FLASHBAR & RAMBAR) for it to execute correctly? 

 

Should I post this on the CW forum?  There was a thread there with a similar request that didn't move to completion so I thought I would try here. 

 

Is there any documentation that I should have reviewed (before bothering people on the list)? 

 

Thanx,

 

myke

0 Kudos
1 Solution
1,292 Views
trailman
Contributor V

Hi,

 

One year ago, I started a projet on a MCF52259 processor running MQX3.4 and neded the same thing than you : update my application software running from the internal flash. The USB bootloader provided by Freescale was nice for a demo purpose, but was unusable for a remote equipment without any operator closed to it, and where only an internet connection to the system is available.

 

So I decided to find-out a way to update the MQX image from MQX itself. With this method, MQX can be used to download the new image from FTP or serial line (srecord or binary format) to a buffer. This can be done while the system is running.

Then an MQX command can be run to copy a small bootstrap code to internal SRAM and run it from SRAM to copy this buffer to the flash and restart the board on the new image.

 

As my MQX application code was less than half the size of the flash, I used the second half of the flash as a buffer.

I developped a buffer driver creating a "buffer:" device that can be used as target device when downloading with existing MQX applications such as FTP (for example : get filename buffer:smileyhappy:.

The buffer driver also does an on-the-fly srecord to binary conversion, if required, and the final binary image is stored in the second half of flash. The buffer driver uses the MQX flashx: driver to write data to the flash.

 

Then the only remaining step was to implement an "update" command to copy the second half of the flash to the first half and reset. This is done as follows

- allocate some memory in stack (SRAM) to copy a do_update() function from flash to SRAM. This can be done simply by declaring a local variable : char stackmem[size]; The do_update() function is the function doing the real copy.

- copy do_update() code from flash to SRAM (address=stackmem) using memcpy()

- declare a pointer to the function in SRAM and set it to stackmem

- call the function through this pointer.

 

Limitations on do_update() :

- must be self contained : do not call any other function. So a simple flash erase / write code must be written for this function.

- must not return once the flash has been altered bacause MQX application image has changed, so write to the required register to reset the board when done

- must not make use of some data stored in flash (= constant data). Only non constant data or stack data can be used from this function as it is stored in SRAM

- passing arguments to the function is allowed (through stack or registers)

 

This works fine. The only drawback compared to a bootloader if that the power must not be removed while do_update() is running to prevent from having a non functional system. Fortunately the update takes only 2 seconds on my system sothe risk is reduced to the minimum. I also added a checksum verification on the image before using it for the update.

 

I you are interested in that, I can give you more info or sample code.

 

View solution in original post

0 Kudos
10 Replies
1,292 Views
小勇邹
Contributor II

Is there any problem if the power is off when copying the second half of the flash to the first half . I think the system will die and can not start up again.

0 Kudos
1,292 Views
trailman
Contributor V

Hi mykepredko,

Yes, the only restriction is to have a code size lower than half the flash size; except if you have another device where you can implement the buffer and can be access simply from the standalone do_update() function.

In my case all pins of my MCF52259 are used for I/Os and I have no external memory device big enough to hold the buffer, so I used the second part of internal flash

There's no special build parameter to set (I use CodeWarrior 7.1.2 GUI to build for internal flash) : you just write the do_update() function and build it as part of your code running from flash. But you never run it from flash but copy it to RAM when needed.

Another limitation on the do_update() function I missed : interrupts must be disabled when running this function to prevent interrupt handlers from beeing run from flash while running it. You must also make sure that no other task will preempt it.

Here is the source code :

 

 

// Code for MCF52259 microcontroller

#define MAX_UPDATE_ROUTINE_SIZE 512

#define FLASH_UPDATE_TARGET_OFFSET 0x00000 // target is flash offset 0 (application code)
#define FLASH_BUFFER_OFFSET        0x40000 // buffer starts at half the flash size (0x80000)
#define FLASH_SECTOR_SIZE          0x1000  // 4KB

// MCF52259 specific
#define CFM_IPS_FLASH_ADDR         0x44000000
#define FLASH_WRITE_BASE_ADDRESS   CFM_IPS_FLASH_ADDR // for write; can also read from here
#define FLASH_READ_BASE_ADDRESS    0x00000000 // only read acces
#define CFM_REGS_BASE_ADDR         0x401d0000 // for flash management
#define RCR_REG_ADDR               0x40110000 // for reset
#define UART_TX_REG_ADDR           0x4000020C // to write characters on serial line

// This function copies the image in the buffer (second part of flash) to the first part of flash
// WARNING : This function is copied to RAM before beeing executed; must be self-contained
static int do_update_flash(uint_32 update_size) {
    uint_32 source_offset = FLASH_BUFFER_OFFSET;
    uint_32 dest_offset = FLASH_UPDATE_TARGET_OFFSET;
    uint_32 bytes_left = (update_size + 3) & ~3; // round byte count (only 32bit write to flash allowed)
    uint_32 old_offset = 0xFFFFFFFF;
    VMCF52XX_CFM_STRUCT_PTR cfm_ptr = (VMCF52XX_CFM_STRUCT_PTR)CFM_REGS_BASE_ADDR;

    // clear error flags and wait for ready state
    cfm_ptr->CFMUSTAT = (MCF52XX_CFM_CFMUSTAT_PVIOL | MCF52XX_CFM_CFMUSTAT_ACCERR);
    while (!(cfm_ptr->CFMUSTAT & MCF52XX_CFM_CFMUSTAT_CBEIF)) {};

    while (bytes_left >= 4)
    {
 if ((old_offset & ~(FLASH_SECTOR_SIZE -1)) != (dest_offset & ~(FLASH_SECTOR_SIZE -1))) {
     // Entered a new sector -> erase it before writing
     (*(volatile vuint_32 *)(FLASH_WRITE_BASE_ADDRESS+dest_offset)) = (vuint_32)-1;
     cfm_ptr->CFMCMD = MCF52XX_CFM_CFMCMD_PAGE_ERASE;
     cfm_ptr->CFMUSTAT = MCF52XX_CFM_CFMUSTAT_CBEIF;
     while (!(cfm_ptr->CFMUSTAT & MCF52XX_CFM_CFMUSTAT_CCIF)) {};
     if (cfm_ptr->CFMUSTAT & (MCF52XX_CFM_CFMUSTAT_ACCERR | MCF52XX_CFM_CFMUSTAT_PVIOL)) {
  goto end;
     }
     (*(volatile vuint_8 *)UART_TX_REG_ADDR) = '.'; // print . on serial console for feedback
 }

 // write a word
 (*(volatile vuint_32 *)(FLASH_WRITE_BASE_ADDRESS + dest_offset)) = *(volatile vuint_32 *)(FLASH_WRITE_BASE_ADDRESS + source_offset);
 cfm_ptr->CFMCMD = MCF52XX_CFM_CFMCMD_WORD_PROGRAM;
 cfm_ptr->CFMUSTAT = MCF52XX_CFM_CFMUSTAT_CBEIF;
 while (!(cfm_ptr->CFMUSTAT & MCF52XX_CFM_CFMUSTAT_CCIF)) {};
 if (cfm_ptr->CFMUSTAT & (MCF52XX_CFM_CFMUSTAT_PVIOL | MCF52XX_CFM_CFMUSTAT_ACCERR)) {
     goto end;
 }

 old_offset = dest_offset;
 dest_offset += 4;
 source_offset += 4;
 bytes_left -= 4;
    }

    // Now update is done but we can not return to flash as the code has been
    // modified and our return address is probably invalid, so reset the board
    // TODO : would be better to call an MQX function for reset, but is there such a function ?
    *(uchar *)RCR_REG_ADDR = 0x80;

 end:
    // Wait reset and do not return as the code has changed, or is no more valid in case of error
    while(1) {};

    return 0;
}
// this function is used to calculate the size of do_update_flash_end, assuming the linker keep thme together
static void do_update_flash_end (void) {}

static int_32 update_flash(void)
{
    char code_in_ram[MAX_UPDATE_ROUTINE_SIZE]; // create some space in stack where to copy the update routine
    int (*do_update_flash_ram)(uint_32) = (int(*)(uint_32))code_in_ram; // create a pointer to this routine
    uint_32 code_size = (char*)do_update_flash_end - (char*)do_update_flash; // size of the routine

    //printf("Calling RAM (code is %d bytes)\n", code_size); fflush(stdout);
    if (code_size > sizeof(code_in_ram)) {
 printf("ERROR : internal buffer too small for update routine\n");
 return -1;
    }

    _mem_copy((void*)do_update_flash, code_in_ram, code_size); // copy routine to RAM

    fflush(stdout); // make sure all messages have been displayed
    _int_disable();
    do_update_flash_ram(flash_buffer_loaded_size); // jump to routine in RAM
    _int_enable();

    printf("ERROR : unexpected return from RAM !!!\n"); fflush(stdout);

    return -1;
}

 

 

 

0 Kudos
1,292 Views
TheJar
Contributor I

I would be very happy to know if someone has already ported this code to support Kinetis k60 (twrk60n512). Or any advice to do such job myself? 

0 Kudos
1,292 Views
trailman
Contributor V

Hi myke,

 

Nice to see it was helpful.

 

I also posted the code of my simple flash buffer driver to use the second part of flash as a temporary buffer to download the new application image.

It can be used with MQX applications; for example the FTP client as follows : get image.bin buffer:

 

This is a simplified demo version of the flash buffer without on-the-fly Srecord to binary conversion, so you need to convert your Srecord (.S19) image to a binary one (.bin) before downloading it (and probably add some checksum verification)

If someone is interested, I can post the code of the full featured buffer driver with Srecord support (including checksum verification).

 

Once downloaded in the buffer you can call update_flash() (above in this post) to update your MQX application.

 

0 Kudos
1,292 Views
myke_predko
Senior Contributor III

HiTrail,

 

Sorry for taking so long to get back to you - lots going on here. 

 

Thanx for the link - I've actually been able to provide the same functionality in my code.  I really just needed a push from somebody who had been able to provide a mechanism to update the boot Flash image to give me some ideas.  I think I can replicate your concept very easily with my application. 

 

I would be interested in seeing your "Srecord" code as it would mean I can just use the .s19 image directly. 

 

Thanx again for your suggestions and examples,

 

myke

0 Kudos
1,292 Views
trailman
Contributor V

Hi myke,

 

I updated the flash buffer driver post with the code supporting the S-Record (.S19) format.

A command to download through serial line is also provided.

 

Enjoy.

 

trailman

0 Kudos
1,292 Views
mykepredko
Contributor IV

Hi Trail,

 

I've done a bit of poking around the Flash routines and I understand exactly how you've done it.  This is actually the same code as is used by MQX for writing to the Flash. 

 

Very clever to use it this way.  It's not exactly how I expected you to do it, but it's a very efficient. 

 

Thanx for the example - it was helpful.

 

myke

0 Kudos
1,293 Views
trailman
Contributor V

Hi,

 

One year ago, I started a projet on a MCF52259 processor running MQX3.4 and neded the same thing than you : update my application software running from the internal flash. The USB bootloader provided by Freescale was nice for a demo purpose, but was unusable for a remote equipment without any operator closed to it, and where only an internet connection to the system is available.

 

So I decided to find-out a way to update the MQX image from MQX itself. With this method, MQX can be used to download the new image from FTP or serial line (srecord or binary format) to a buffer. This can be done while the system is running.

Then an MQX command can be run to copy a small bootstrap code to internal SRAM and run it from SRAM to copy this buffer to the flash and restart the board on the new image.

 

As my MQX application code was less than half the size of the flash, I used the second half of the flash as a buffer.

I developped a buffer driver creating a "buffer:" device that can be used as target device when downloading with existing MQX applications such as FTP (for example : get filename buffer:smileyhappy:.

The buffer driver also does an on-the-fly srecord to binary conversion, if required, and the final binary image is stored in the second half of flash. The buffer driver uses the MQX flashx: driver to write data to the flash.

 

Then the only remaining step was to implement an "update" command to copy the second half of the flash to the first half and reset. This is done as follows

- allocate some memory in stack (SRAM) to copy a do_update() function from flash to SRAM. This can be done simply by declaring a local variable : char stackmem[size]; The do_update() function is the function doing the real copy.

- copy do_update() code from flash to SRAM (address=stackmem) using memcpy()

- declare a pointer to the function in SRAM and set it to stackmem

- call the function through this pointer.

 

Limitations on do_update() :

- must be self contained : do not call any other function. So a simple flash erase / write code must be written for this function.

- must not return once the flash has been altered bacause MQX application image has changed, so write to the required register to reset the board when done

- must not make use of some data stored in flash (= constant data). Only non constant data or stack data can be used from this function as it is stored in SRAM

- passing arguments to the function is allowed (through stack or registers)

 

This works fine. The only drawback compared to a bootloader if that the power must not be removed while do_update() is running to prevent from having a non functional system. Fortunately the update takes only 2 seconds on my system sothe risk is reduced to the minimum. I also added a checksum verification on the image before using it for the update.

 

I you are interested in that, I can give you more info or sample code.

 

0 Kudos
1,292 Views
mykepredko
Contributor IV

Hi Trail,

 

Wow, thank you.  This is exactly what I'm looking for.  It sounds like the only restriction is to make sure that the MQX image is less than half the available Flash. 

 

I would be very interested in seeing the "do_update" code as well as the build parameters that you use. 

 

Thanx muchly,

 

myke

0 Kudos
1,292 Views
Kafre
Contributor III

Hello,

 

I'm really interested in the 1st point also. I was looking for a way of executing part of my code in the internal SRAM of my m54455. I think that modifying the .clf could be the answer, but just I don't know how.

0 Kudos