MCF52221 firmware upgrade/bootloader alternative...

cancel
Showing results for 
Search instead for 
Did you mean: 

MCF52221 firmware upgrade/bootloader alternative...

2,857 Views
RichTestardi
Senior Contributor II
Hi all,
 
I finally added the ability for my target boards to "upgrade" their own firmware (over USB) and it turned out so simple I thought I'd share it...
 
The only real restriction for this method is that you can't use more than half your flash for code storage (I already was using half for code and half for data, and I can afford to lose the data during an upgrade)...  My code is in the low half of flash, and my data is in the high half.
 
Basically, I download (over USB) the upgraded .S19 binary file into the high half of flash, as a temporary staging area.  Then I copy my "upgrade" routine from the low half of flash (where I am still running) to RAM, and then I just call it -- and don't forget to disable interrupts!   The "upgrade" routine copies the high half of flash back to the low half (where my code ordinarily lives), and then resets the CPU -- it never returns.
 
The only real trick here is that I write the "upgrade" routine carefully so it is inherently position independent, inlining any subroutines by hand rather than calling them -- it's all just C.  (You can disassemble the file and see that the only "relocations" in that routine are IPSBAR, which we don't want relocated differently anyway!)  This allows me to compile the "upgrade" routine to run in flash, but then just memcpy() it to RAM and call it from there instead.
 
I did not have to deal with any position independent code (my code is always linked to run in the low half of flash), and I don't even *have* a bootloader (a small piece of code that is never updated)...  I can update every single byte of my code on every upgrade...
 
My "upgrade" routine, looks like this:
 
Code:
voidflash_upgrade(void){    uint32 *addr;    uint32 *data;    uint32 nwords;    uint32 npages;    uint32 *backdoor_addr;    // N.B. this code generates no relocations so we can run it from RAM!!!    // erase the firmware code    addr = NULL;    npages = FLASH_BYTES/2/FLASH_PAGE_SIZE;    while (npages) {        assert(MCF_CFM_CFMCLKD & MCF_CFM_CFMCLKD_DIVLD);        assert(MCF_CFM_CFMUSTAT & MCF_CFM_CFMUSTAT_CBEIF);        assert(! (MCF_CFM_CFMUSTAT & (MCF_CFM_CFMUSTAT_PVIOL|MCF_CFM_CFMUSTAT_ACCERR)));        backdoor_addr = (uint32 *)(__IPSBAR+0x04000000+(int)addr);        *backdoor_addr = 0;        MCF_CFM_CFMCMD = MCF_CFM_CFMCMD_PAGE_ERASE;        MCF_CFM_CFMUSTAT = MCF_CFM_CFMUSTAT_CBEIF;        while (! (MCF_CFM_CFMUSTAT & MCF_CFM_CFMUSTAT_CBEIF)) {            assert(! (MCF_CFM_CFMUSTAT & (MCF_CFM_CFMUSTAT_PVIOL|MCF_CFM_CFMUSTAT_ACCERR)));        }        npages--;        addr += FLASH_PAGE_SIZE/sizeof(uint32);    }    while(! (MCF_CFM_CFMUSTAT & MCF_CFM_CFMUSTAT_CCIF)) {    };        // and re-flash it from the staging area    addr = NULL;    data = (uint32 *)(FLASH_BYTES/2);    nwords = FLASH_BYTES/2/sizeof(uint32);    while (nwords) {        assert(MCF_CFM_CFMCLKD & MCF_CFM_CFMCLKD_DIVLD);        assert(MCF_CFM_CFMUSTAT & MCF_CFM_CFMUSTAT_CBEIF);        assert(! (MCF_CFM_CFMUSTAT & (MCF_CFM_CFMUSTAT_PVIOL|MCF_CFM_CFMUSTAT_ACCERR)));        backdoor_addr = (uint32 *)(__IPSBAR+0x04000000+(int)addr);        *backdoor_addr = *data;        MCF_CFM_CFMCMD = MCF_CFM_CFMCMD_WORD_PROGRAM;        MCF_CFM_CFMUSTAT = MCF_CFM_CFMUSTAT_CBEIF;        while (! (MCF_CFM_CFMUSTAT & MCF_CFM_CFMUSTAT_CBEIF)) {            assert(! (MCF_CFM_CFMUSTAT & (MCF_CFM_CFMUSTAT_PVIOL|MCF_CFM_CFMUSTAT_ACCERR)));        }        nwords--;        addr++;        data++;    }    while(! (MCF_CFM_CFMUSTAT & MCF_CFM_CFMUSTAT_CCIF)) {    };        // erase the staging area    addr = (uint32 *)(FLASH_BYTES/2);    npages = FLASH_BYTES/2/FLASH_PAGE_SIZE;    while (npages) {        assert(MCF_CFM_CFMCLKD & MCF_CFM_CFMCLKD_DIVLD);        assert(MCF_CFM_CFMUSTAT & MCF_CFM_CFMUSTAT_CBEIF);        assert(! (MCF_CFM_CFMUSTAT & (MCF_CFM_CFMUSTAT_PVIOL|MCF_CFM_CFMUSTAT_ACCERR)));        backdoor_addr = (uint32 *)(__IPSBAR+0x04000000+(int)addr);        *backdoor_addr = 0;        MCF_CFM_CFMCMD = MCF_CFM_CFMCMD_PAGE_ERASE;        MCF_CFM_CFMUSTAT = MCF_CFM_CFMUSTAT_CBEIF;        while (! (MCF_CFM_CFMUSTAT & MCF_CFM_CFMUSTAT_CBEIF)) {            assert(! (MCF_CFM_CFMUSTAT & (MCF_CFM_CFMUSTAT_PVIOL|MCF_CFM_CFMUSTAT_ACCERR)));        }        npages--;        addr += FLASH_PAGE_SIZE/sizeof(uint32);    }    while(! (MCF_CFM_CFMUSTAT & MCF_CFM_CFMUSTAT_CCIF)) {    };        // reset the CPU    MCF_RCM_RCR = MCF_RCM_RCR_SOFTRST;    asm { halt }}voidflash_upgrade_end(void){    // NULL}
 
And I copy the "upgrade" routine to RAM and execute it (after downloading the .S19 file to the staging area) with:
 
Code:
        // disable interrupts        x = splx(7);            // copy our flash upgrade routine to RAM        memcpy(RAM_CODE_PAGE, flash_upgrade, (int)flash_upgrade_end - (int)flash_upgrade);                // and run it!        fn = (void *)RAM_CODE_PAGE;        fn();                // we should not come back!        assert(0);        splx(x);
 
-- Rich
Labels (1)
0 Kudos
6 Replies

187 Views
RichTestardi
Senior Contributor II
PS it is probably obvious for the MCF52221, but I also define:
 
Code:
#define FLASH_PAGE_SIZE  2048#define FLASH_BYTES  (128*1024)

 
0 Kudos

187 Views
marcos862
Contributor I

Hi Rich !

 

Im trying to implement the bootloader that you post. Im very new in this technology (coldfire) so I have a little problems trying to understand some part of the code and I need you help, I hope you can help me with my doubts.

 

The problems are:

1.- In the RM of the MCF52223 say: "Prior to issuing any command, it is necessary to write the CFMCLKD register to divide the input clock to within the 150 KHz to 200 KHz range." In your code, there isn't anything like that.... So, I have to do it before call the flash_upgrade function??

 

2.- I don't understand how the erase page and program page command take the direction that it should be the one in the backdoor_addr, How it happens?

 

3.- The function splx and memcpy ... Where is declare or what it does?

 

4.- The "RAM_CODE_PAGE" what it is? It is a part of a linker file or what? How I declare a correct RAM_CODE_PAGE ??

 

5.- How I should declare the "fn"

 

6.- And the last one is... On the function "flash_upgrade_end" what I should do??

 

I will very thankful if you can help me to solve this doubts ! Thanks in advance for your time !!

 

PS. Sorry for my bad english.

0 Kudos

187 Views
RichTestardi
Senior Contributor II
And my assert() is a macro that does not call a function:
 
Code:
#define assert(x)  if (! (x)) { asm { halt } }

Smiley Happy
0 Kudos

187 Views
mjbcswitzerland
Specialist V
Hi Richard

It is nice to see that you have done this for the USB device (I need it soon..;-)

To increase the upload capability you can use an SPI FLASH device (such as AT45DB011B - these are very cheap) as tempory store. This will allows you then to upload about 124k program sizes. I use a very similar technique for Ethernet based firmware uploads which can be either in internal FLASH (same restriction that the new code can not be larger than about half the FLASH size) or to an external SPI FLASH device.

To ensure that this is totally reliable I first add a header to the new code including cyclic redundancy check, and a secret key to stop malicious firmware uploads. The conversion routine can optionally encrypt the code so that it can be distributed without it being readable - together with FLASH security it then means that new firmware can be ditributed in the encrypted form, loaded by anyone and is never readable (this is an important requirement for a lot of users who don't want their code getting into the hands of competitors).

The boot loader is 2k in size when working with internal FLASH and 4k with external since it need both IAP and SPI FLASH drivers (including decryption).

If you would like some more details, they are contained in the following documents:
Theory: http://www.utasker.com/docs/uTasker/uTaskerBoot_003.PDF
Guide to using: http://www.utasker.com/docs/uTasker/BM-Booloader_for_M5223X.PDF
SPI FLASH connection diagram: http://www.utasker.com/docs/uTasker/spi_flash_freescale.PDF

Keep up the good work - and if you would like to contribute to the uTasker project (open source and free for non-commercial use) just contact me. Hope you don't mind me contacting you about USB since this is my next challenge...

Regards

Mark

0 Kudos

187 Views
RichTestardi
Senior Contributor II
Hi Mark!
 
Yes, there are a number of ways the upgrade process can be improved, as you suggest, especially regarding security!  It is also susceptible to power loss during the few seconds that the "upgrade" routine is actually running, which can leave you with a brick...  But for my target market (high school labs, believe it or not, so the $6 CPU is about all I can afford to put on there! :smileyhappy:, that's acceptable, since I can fall back to the EzPort if I have to.
 
> Hope you don't mind me contacting you about USB since this is my next challenge...
 
No, not at all!  I've almost e-mailed you a half dozen times now -- I believe we have many of the same goals and dreams for this stuff!  Unfortunately, I am only able to do this as a hobby right now -- an hour here and there...  But my dream is to be able to work on it full-time someday soon!  (I'd like nothing more than to have a whole bunch of different microcontrollers, but for now I just have an M52221DEMO board and a handful of hand-build prototype cards... :smileyhappy:
 
I actually had a USB host and device driver running if that can give you any head start...  It's at: http://www.testardi.com/rich/coldfire/index.htm and you're welcome to use whatever helps.  That's not my absolute latest code, but it is the last time I supported/tested host and device mode (I've since abandoned host support).  One cool thing I did was add an FTDI emulator above the USB module, so that I could communicate from the PC to the MCF52221 using a PC Virtual COM Port (without having to write a driver for the PC), which, of course, is how I download my flash upgrades...
 
-- Rich
 
0 Kudos

187 Views
mjbcswitzerland
Specialist V
Hi Rich

>>It is also susceptible to power loss during the few seconds that the "upgrade" routine is actually running, which can leave you with a brick...

Yes, this is an even more important point when uploading over the Internet since the target can be at the other side of the world and so losing contact could be disasterous. This is where the boot loader comes in. It is never deleted and only deletes the copy of the new code once it has successfully been copied and verified. This means that it tolerates a power down at any point of the process. The uTasker boot loader (called the "Bare-Minimum" boot loader due to the fast that it is really the absolute minimum needed to do the job [it could be extended, for example, to allow SREC downloads with no previous application but then it is no longer "BM"]) has been used for about 18 months now and I don't know of there ever being a failure so this is quite reassuring. It has also proved to be something that users like to use (it makes general development on HW faster since the upload of new code takes only a few seconds - much faster and easier then using BDM - but the BDM debugger can still be left connected and used. Plus it is a nice feature to have in real products).

The use of SPI FLASH as an extension for enabling complete SW upgrades and as extension for the file system, or generally saving data, has also proved to be extremely popular. It enables the rather cheaper M52232 to be used with a $1 SO-8 SPI FLASH and you have an application of up to 124k with complete Internet enabled firmware uploads and maybe 1/2Meg of web page or other data.

Cheers

Mark

0 Kudos