We use for many years in one of our products a two-stage bootloader with success. One application is linked together with the primary bootloader (with elfloader -init option), while another one is burned into the flash at 0x10000. If a pushbutton is pressed at power on then the first application will be loaded, if it is not pressed then the second one. This is how it works:
BYTEu c;
*pPORTF_MUX = 0x0;
*pPORTG_MUX = 0x0;
*pPORTF_FER = 0x1de2;
*pPORTG_FER = 0x700;
*pPORTFIO_DIR = 0x2208;
*pPORTGIO_DIR = 0x78ff;
*pPORTFIO_INEN = 0xc011;
*pPORTGIO_INEN = 0x8000;
*pPORTFIO = 0xffff; // init output bits
*pPORTGIO = 0xffff - (OUTPUT_BITS); // init output bits
c = 1;
if ((*pPORTFIO BITAND PUSHBUTTON_MASK) == PUSHBUTTON_MASK) // switch not pressed?
{
MIDLED_ON;
sysreg_write(reg_B0, ((int) c) << 16);
asm("P0=0x0f84; P0.H=0xffb0; R0=B0; [P0]=R0; SSYNC;"); // load app from c*0x10000
}
else
{
NOP;
LOWLED_ON;
}
Now for a new product we have to extend this so, that several apps are located at 0x10000 bytes apart, and the value of a byte in the flash decides which of them should be loaded if the button is not pressed.
. . . . port initializations like before
*pSPI1_BAUD = 10; // Set SPI bit rate
*pSPI1_CTL =0x5001; // Setup SPI in main mode, 8 bits
*pPORTFIO = 0xffff; // init output bits
*pPORTGIO = 0xffff - (OUTPUT_BITS); // init output bits
#define source 0x30001
// ***** c = PEEK(source) (unsigned byte) *****************
SFLASHCS_OFF; // flash chip select activated
*pSPI1_TDBR = DF3_3READ;
while ((*pSPI1_STAT BITAND 1)==0);
c = *pSPI1_RDBR;
*pSPI1_TDBR = (source>>16) BITAND 0xff;
while ((*pSPI1_STAT BITAND 1)==0);
c = *pSPI1_RDBR;
*pSPI1_TDBR = (source>>8) BITAND 0xff;
while ((*pSPI1_STAT BITAND 1)==0);
c = *pSPI1_RDBR;
*pSPI1_TDBR = source BITAND 0xff;
while ((*pSPI1_STAT BITAND 1)==0);
c = *pSPI1_RDBR;
*pSPI1_TDBR = 0;
while ((*pSPI1_STAT BITAND 1)==0);
c = *pSPI1_RDBR;
while ((*pSPI1_STAT BITAND 1)==0);
SFLASHCS_ON; // flash chip select off
// ******** end of PEEK ***************************
if ((*pPORTFIO) BITAND PUSHBUTTON_MASK) == PUSHBUTTON_MASK) // switch not pressed?
{
MIDLED_ON;
sysreg_write(reg_B0, ((int) c) << 16);
asm("P0=0x0f84; P0.H=0xffb0; R0=B0; [P0]=R0; SSYNC;"); // load app from c*0x10000
}
else
{
NOP;
LOWLED_ON;
}
This program is definitely good, I can see on oscilloscope that the byte from the flash is read correctly, but it does not work at all, neither of the two apps will be loaded. After successfully reading the flash the control is lost and the program hangs. What is wrong?
One remark: It can be strange that I write to the RAM location 0xffb00f84 the flash address of the app to be loaded from the flash. Normally this supposed to be written to the address where P5 points to in the RAM. However, I found that the content of P5 is always 0xffb00f84, and if the bootloader program is moderately complex then the compiler saves P5 to the stack and starts to use it, so the primary bootloader cannot read the address from P5 any more.