The flash ROMboot loader plays a crucial role in any embedded device that uses nonvolatile reprogrammable flash ROM for its program code. The boot loader is the first code that the CPU executes after a system reset and must therefore initialize the device. It must also be able to reprogram the rest of the flash ROM, containing the main application code, in the likely event that the rest of the flash ROM becomes corrupted. Because of its key role, the boot loader usually occupies special boot blocks in the flash ROM, which have hardware protection against accidental erasure and corruption. This ensures that the boot loader doesn't change through the lifetime of the shipped product. Let's look at the specific actions the flash ROM boot loader must perform.
Initializing the hardware
Immediately after a system reset, the boot loader should perform basic hardware initialization. This might include enabling access to RAM, setting up clocks and PLLs, and configuring other key peripherals. It's a good idea to keep the initialization performed from the boot block to the bare essentials, leaving the rest of the initialization to the upgradeable application code.
Checking the checksums
After the hardware initialization, you’re ready to run the application code. But at this point in the boot process, you can't trust most of the contents of the flash ROM because of the corruption possibility. So the boot loader should compute a checksum or CRC of the application code and compare it with a checksum word stored in the application code. You'll need to use (or write) a program to compute and store a checksum in the flash ROM image files you distribute for just this purpose. Make sure that you store the checksum at a fixed offset in the application code where the boot loader can always find it.
Receiving new application code
If the checksum test reveals a corrupt ROM, the boot loader should enter the flash reprogramming routines within the boot block. There, it should wait to receive a new flash ROM binary image through a suitable port on your device. Good choices are an RS-232 serial port, USB port, or Ethernet port, as a customer equipped with a standard PC will be able to run a program (supplied by you) to send new ROM images to your device. Many embedded devices are leaning toward supporting only complex interfaces such as USB or Ethernet+TCP/IP protocols. But these are not the easiest to implement in your boot loader. Go for the simplest possible implementation: Use polled instead of interrupt-driven code and use the simpler UDP protocol instead of TCP.
Executing flash routines from RAM
Most flash ROMs can't be read while being written. (Devices with "read-while-write" capability are the exception.) So you'll need to copy the flash reprogramming routines into RAM (or for simplicity, copy the entire boot loader into RAM) and jump to it before attempting to unlock, erase, and reprogram the flash ROM blocks containing the application code.
Receiving a new flash ROM image
If you have enough spare RAM (remember that most of the system RAM is "spare" at this point in the boot process), the flash reprogramming routine should receive all the new flash ROM images into RAM before starting to reprogram the flash ROM device. That way, it can verify the checksum of the new ROM image before unlocking, erasing, and reprogramming the device.
Don't include the boot block itself in the new flash ROM image, since you won’t be reprogramming that part of the flash ROM. If you can’t receive the full image into RAM in one effort, you'll need to erase and reprogram blocks as the data is streamed to your boot loader. Always remember to revalidate the checksums after programming the flash ROM to ensure that everything worked correctly. The simplest way is to instruct the customer to power-cycle the embedded device after the software upgrade to restart the boot process.
Jumping from the boot loader into the application
If the flash ROM boot loader has verified that the application code is okay, it should transfer control to the application by jumping to a fixed location in the application. This location should have the startof a secondary boot loader, which performs the rest of the system initialization and prepares the high-level run-time environment. Because this secondary boot loader is in the application area of the flash ROM, you're free to upgrade it in future software revisions.
Copying the application from ROM to RAM
If your application code runs from RAM, at some point the boot process must copy the application code from flash ROM into RAM before beginning to execute from RAM. If possible, you should arrange for the secondary boot loader to do this copying—not the boot loader in the boot block. This can help you out in future revisions when you might need to change your RAM allocation strategy. For example, a future revision of your software might want to copy only part of the flash ROM into RAM, so you should avoid locking this code into the protected boot block. Finally, don't forget to include some means of invoking the boot loader when the flash ROM isn't corrupted, so that you can perform software upgrades on a normally working device.