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:

Figure 1. S19 Parser

S19 Parser

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;
}
The S1, S2 and S3 records contains the code. With BL_ValidAppAddress() it checks if the address is within the application FLASH memory range:
/*!
* \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:

Figure 2. Processor Expert Flash Programming Component

Processor Expert Flash Programming Component

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;
}