How does a bootloader work?The following process tells you how a bootloader works:
When you enter the command BL Load S19 at the command prompt, it executes the function BL_LoadS19() in Bootloader.c. The following listing shows an example:
static uint8_t BL_LoadS19(CLS1_ConstStdIOType *io) {
unsigned char buf[16];
uint8_t res = ERR_OK;
/* first, erase flash */
if (BL_EraseAppFlash(io)!=ERR_OK) {
return ERR_FAILED;
}
/* load S19 file */
CLS1_SendStr((unsigned char*)"Waiting for the S19 file...", io->stdOut);
parserInfo.GetCharIterator = GetChar;
parserInfo.voidP = (void*)io;
parserInfo.S19Flash = BL_onS19Flash;
parserInfo.status = S19_FILE_STATUS_NOT_STARTED;
parserInfo.currType = 0;
parserInfo.currAddress = 0;
parserInfo.codeSize = 0;
parserInfo.codeBuf = codeBuf;
parserInfo.codeBufSize = sizeof(codeBuf);
while (AS1_GetCharsInRxBuf()>0) { /* clear any pending characters in rx buffer */
AS1_ClearRxBuf();
WAIT1_Waitms(100);
}
do {
if (S19_ParseLine(&parserInfo)!=ERR_OK) {
CLS1_SendStr((unsigned char*)"ERROR!\r\nFailed at address 0x", io->stdErr);
buf[0] = '\0';
UTIL1_strcatNum32Hex(buf, sizeof(buf), parserInfo.currAddress);
CLS1_SendStr(buf, io->stdErr);
CLS1_SendStr((unsigned char*)"\r\n", io->stdErr);
res = ERR_FAILED;
break;
} else {
CLS1_SendStr((unsigned char*)"\r\nS", io->stdOut);
buf[0] = parserInfo.currType;
buf[1] = '\0';
CLS1_SendStr(buf, io->stdOut);
CLS1_SendStr((unsigned char*)" address 0x", io->stdOut);
buf[0] = '\0';
UTIL1_strcatNum32Hex(buf, sizeof(buf), parserInfo.currAddress);
CLS1_SendStr(buf, io->stdOut);
}
if (parserInfo.currType=='7' || parserInfo.currType=='8' || parserInfo.currType=='9') {
/* end of records */
break;
}
} while (1);
if (res==ERR_OK) {
CLS1_SendStr((unsigned char*)"\r\ndone!\r\n", io->stdOut);
} else {
while (AS1_GetCharsInRxBuf()>0) {/* clear buffer */
AS1_ClearRxBuf();
WAIT1_Waitms(100);
}
CLS1_SendStr((unsigned char*)"\r\nfailed!\r\n", io->stdOut);
/* erase flash again to be sure we do not have illegal application image */
if (BL_EraseAppFlash(io)!=ERR_OK) {
res = ERR_FAILED;
}
}
return res;
}
It first fills a callback structure of type S19_ParserStruct, as the following listing shows:
typedef struct S19_ParserStruct {
uint8_t (*GetCharIterator)(uint8_t*, void*); /* character stream iterator */
void *voidP; /* void pointer passed to iterator function */
uint8_t (*S19Flash)(struct S19_ParserStruct*); /* called for each S19 line to be flashed */
/* the following fields will be used by the iterator */
S19_FileStatus status; /* current status of the parser */
uint8_t currType; /* current S19 record, e.g. 1 for S1 */
uint32_t currAddress; /* current code address of S19 record */
uint16_t codeSize; /* size of code in bytes in code buffer */
uint8_t *codeBuf; /* code bufffer */
uint16_t codeBufSize; /* total size of code buffer, in bytes */
} S19_ParserStruct;
That structure contains a callback to read from the input stream, as the following listing shows:
static uint8_t GetChar(uint8_t *data, void *q) {
CLS1_ConstStdIOType *io;
io = (CLS1_ConstStdIOType*)q;
if (!io->keyPressed()) {
#if USE_XON_XOFF
SendXONOFF(io, XON);
#endif
while(!io->keyPressed()) {
/* wait until there is something in the input buffer */
}
#if USE_XON_XOFF
SendXONOFF(io, XOFF);
#endif
}
io->stdIn(data); /* read character */
if (*data=='\0') { /* end of input? */
return ERR_RXEMPTY;
}
return ERR_OK;
}
Parsing of the S19 file is done in S19_ParesLine() which is implemented in a Processor Expert component, as the following figure shows:
This parser calls the callback BL_onS19Flash() for every S19 line:
static uint8_t BL_onS19Flash(S19_ParserStruct *info) {
uint8_t res = ERR_OK;
switch (info->currType) {
case '1':
case '2':
case '3':
if (!BL_ValidAppAddress(info->currAddress)) {
info->status = S19_FILE_INVALID_ADDRESS;
res = ERR_FAILED;
} else {
/* Write buffered data to Flash */
if (BL_Flash_Prog(info->currAddress, info->codeBuf, info->codeSize) != ERR_OK) {
info->status = S19_FILE_FLASH_FAILED;
res = ERR_FAILED;
}
}
break;
case '7':
case '8':
case '9': /* S7, S8 or S9 mark the end of the block/s-record file */
break;
case '0':
case '4':
case '5':
case '6':
default:
break;
} /* switch */
return res;
}
/*!
* \brief Determines if the address is a valid address for the application (outside the bootloader)
* \param addr Address to check
* \return TRUE if an application memory address, FALSE otherwise
*/
static bool BL_ValidAppAddress(dword addr) {
return ((addr>=MIN_APP_FLASH_ADDRESS) && (addr<=MAX_APP_FLASH_ADDRESS)); /* must be in application space */
}
On successful execution, it flashes the memory block:
/*!
* \brief Performs flash programming
* \param flash_addr Destination address for programming.
* \param data_addr Pointer to data.
* \param nofDataBytes Number of data bytes.
* \return ERR_OK if everything was ok, ERR_FAILED otherwise.
*/
static byte BL_Flash_Prog(dword flash_addr, uint8_t *data_addr, uint16_t nofDataBytes) {
/* only flash into application space. Everything else will be ignored */
if(BL_ValidAppAddress(flash_addr)) {
if (IFsh1_SetBlockFlash((IFsh1_TDataAddress)data_addr, flash_addr, nofDataBytes) != ERR_OK) {
return ERR_FAILED; /* flash programming failed */
}
}
return ERR_OK;
}
The Flash Programming is performed by the IntFLASH Processor Expert components:
You can also use this component for erasing:
/*!
* \brief Erases all unprotected pages of flash
* \return ERR_OK if everything is ok; ERR_FAILED otherwise
*/
static byte BL_EraseApplicationFlash(void) {
dword addr;
/* erase application flash pages */
for(addr=MIN_APP_FLASH_ADDRESS;addr<=MAX_APP_FLASH_ADDRESS;addr+=FLASH_PAGE_SIZE) {
if(IFsh1_EraseSector(addr) != ERR_OK) { /* Error Erasing Flash */
return ERR_FAILED;
}
}
return ERR_OK;
}