Rich Testardi

MCF52221 firmware upgrade/bootloader alternative...

Discussion created by Rich Testardi on May 7, 2008
Latest reply on May 6, 2010 by Marcos Bolanos
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

Outcomes