Why Do We Need a Bootloader in an Embedded Device

Why do we need a bootloader in an embedded device?


A boot loader is a computer program that loads the main operating system or runtime environment for the computer after completion of the self-tests.

^ From Wikipedia Article

So basically bootloader is doing just what you wanted - copying data from flash into operating memory. It's really that simple.

If you want to know more about boostrapping the OS, I highly recommend you read the linked article. Boot phase consists, apart from tests, also of checking peripherals and some other things. Skipping them makes sense only on very simple embedded devices, and that's why their bootloaders are even simpler:

Some embedded systems do not require a noticeable boot sequence to begin functioning and when turned on may simply run operational programs that are stored in ROM.

The same source

Bootloader Working

When any hardware system is designed, the designer must consider where the executable code will be located. The answer depends on the microcontroller, the included memory types, and the system requirements. So the answer varies from system to system. Some systems execute code located in RAM. Other systems execute code located in flash. You didn't tell us enough about your system to know what it is designed to do.

A system might be designed to execute code from RAM because RAM access times are faster than flash so code can execute faster. A system might be designed to execute code from flash because flash is plentiful and RAM may not be. A system might be designed to execute code from flash so that it boots more quickly. These are just some examples and there are other considerations as well.

RAM is volatile so it does not retain code through a power cycle. If the system executes code located in RAM then a bootloader is required to obtain and write the code to RAM at powerup. Flash is non-volatile so execution can start right away at powerup and a bootloader is not necessary (but may still be useful).

Regarding Q3, the answer is yes. If the system is running from RAM then the .text will be located in RAM (but not until after the bootloader has copied it to there). If the system is running from flash then the .text section will be located in flash. The .bss section is variables and will be in RAM regardless of where the .text section is.

What is the difference between a Bootrom vs bootloader on ARM systems

Here's how I understand the terms.

Bootrom

Bootrom (or Boot ROM) is a small piece of mask ROM or write-protected flash embedded inside the processor chip. It contains the very first code which is executed by the processor on power-on or reset. Depending on the configuration of some strap pins or internal fuses it may decide from where to load the next part of the code to be executed and how or whether to verify it for correctness or validity. Sometimes it may contain additional functionality, possibly usable by user code during or after booting. Some examples:

  • iPhone boot ROM. Embedded in the mask ROM and can't be modified. Loads the next stage boot loader from flash or USB (in DFU mode) and verifies its signature using built-in RSA implementation. Also provides accelerated decryption functions for the next stage bootloader.

  • TI's OMAP4 boot ROM. Can load user code from flash (NOR, NAND, OneNAND), external memory, SD/MMC, USB or UART. Boot order and options are set by strap (SYSBOOT) pins. Provides some functionality for later stages (cache/TLB management etc.)

  • NXP's LPCxxxx series Boot ROM. Placed in a hidden portion of the internal flash which is mapped at 0 on power-on. Implements CRP (code read protection), ISP (In-System Programming) which allows to upload and flash new code over UART. If a valid user code is in flash (needs to have proper checksum), maps it to 0 and jumps to it. A part of bootrom remains mapped to provide IAP (In-Application Programming) and some other services.

Bootloader

Bootloader is responsible for finding and loading the final OS or firmware which is supposed to run on the chip. One main difference from bootrom is that it's usually in writable flash and can be replaced or upgraded.

Sometimes bootrom can perform the job of the bootloader. For example, OMAP's bootrom is complex enough (it can parse FAT32!) that you can probably have it load and start a Linux kernel directly.

However, in many cases a separate bootloader is used, either because the bootrom is not capable enough (or absent), or because extra flexibility is needed. It can be very simple (load kernel from a fixed flash location in RAM and jump to it), or can be much more complicated. For example, U-Boot is a like a mini-OS by itself - it has a console, some commands, allows you break the boot process and e.g. modify the kernel command line arguments or even load the kernel from a different location (SD/MMC or USB), run some tests and so on.

Bootloaders are usually used when you have a more or less complex OS which may need some set up before it can be started. Smaller microcontrollers like NXP's LPC series usually use a monolithic firmware so they can get by without it (however, there may be custom bootloaders for them too).

On the very simplest chips there may be no boot ROM or boot loader at all - they just try to fetch and execute instructions from a fixed startup address. In fact, most x86 chips to this day work like this - they just start executing code at FFFFFFF0 with the expectation that the chipset has mapped the BIOS flash chip there. Here, you can say that BIOS is the bootloader (though it also provides services to the OS, similar to bootrom).

Developing a simple bootloader for an Embedded system

You can (in principle) link the object file generated from the assembler code like you would link any object from your program.

The catch is that you need to lay out the generated executable so that your startup code is in the beginning. If you use GNU ld, the way to do that is a linker script.

Primitive setup (not checked for syntax errors):

MEMORY
{
FLASH (RX) : ORIGIN = 0, LENGTH = 256K
RAM (RWX) : ORIGIN = 0x40000000, LENGTH = 4M
}

SECTIONS
{
.bootloader 0 : AT(0) { bootloader.o(.text) } >FLASH AT>FLASH
.text : { _stext = .; *(.text .text.* .rodata .rodata.*); _etext = . } >FLASH AT>FLASH
.data : { _sdata = .; *(.data .data.*); _edata = .; _sdata_load = LOADADDR(.data) } >RAM AT>FLASH
.bss (NOLOAD) { _sbss = .; *(.bss .bss.*); _ebss = . } >RAM
}

The basic idea is to give the linker a rough idea of the memory map, then assign sections from the input files to sections in the final program.

The linker keeps the distinction between the "virtual" and the "load" address for every output section, so you can tell it to generate a binary where the code is relocated for the final addresses, but the layout in the executable is different (here, I tell it to place the .data section in RAM, but append it to the .text section in flash).

Your bootloader can then use the symbols provided (_sdata, _edata, _sdata_load) to find the data section both in RAM and in flash, and copy it.

Final caveat: if your program uses static constructors, you also need a constructor table, and the bootloader needs to call the static constructors.

How does a typical embedded boot loader end?

Once a bootloader has finished it's initialization tasks, it transfers control of the system to the operating program/system. The specific instruction is typically a jump or a branch depending on the specific bootloader or architecture.

Since you specifically mention an operating system, I will reference the Embedded Linux Primer:

Note that the bootm command is the death knell for U-Boot. This is
an important concept. Unlike the BIOS in a desktop PC, most embedded
systems are architected in such a way that when the Linux kernel takes
control, the bootloader ceases to exist. The kernel claims any memory
and system resources that the bootloader previously used. The only way
to pass control back to the bootloader is to reboot the board.

And looking at the AT91 Assembler Code Startup Sequence for C Code Applications Software we can see that it uses bx which is the Branch and Exchange command of the THUMB Instruction Set:

;---------------------------------------------------------------------------
;- Branch on C code Main function (with interworking)
;---------------------------------------------------------------------------
IMPORT __main

ldr r0, =__main
bx r0

END

What are techniques for allowing safe software upgrades in embedded systems

It all depends on how critical the application is. The two basic approaches (backup and bootloader) are also combined sometimes.

Many systems have a read only bootloader (like redboot), and then two banks of flash memory (on the same chip, most often). The bootloader then has a flag to choose which bank to boot from. The flag will then change based on events like upgrades (failed or successful), and so on.

So, when upgrading, the running version copies the new load into the backup bank, checks the checksum, toggles the boot flag, and then reboots the device. The device reboots on the new bank, with the new load. After the reboot, the new load can copy itself into the backup bank.

Often there is also a watchdog timer with a hardware reset. This way, if the firmware goes insane, it fails to kick the watchdog, the hardware reset will reboot the device, and the bootloader will look for a sane load.

The Open Mesh project is a good example of this approach.

STM32 boot loader

There two main options:

SWD

Implement an SWD programming connector. Basically the pins GND, SWDIO, SWCLK and preferably 3.3V are made available. No resisters are needed. You can fit a 4 pin header, an official 10 pin SWD connector or just 4 pads (for connecting using an adapter with pogo pins).

This option requires an SWD debug adapter like ST-Link or J-Link. In addition to uploading firmware, this option supports debugging.

USART

Make the USART (RX, TX) pins plus GND and 3.3V available on the board. This option requires a USB-to-serial adapter.

It's also possible to use I2C or SPI instead, though there are no standard solutions for connecting to your board that I'm aware of.

USB isn't an option for this particular chip. It is supported on many of the more expensive STM32 chips though.

I strongly recommend the first option. It is far more versatile than the other options. And an ST-Link adapter isn't expensive.

Details regarding the bootloader capabilities and pins:

https://www.st.com/resource/en/application_note/cd00167594-stm32-microcontroller-system-memory-boot-mode-stmicroelectronics.pdf



Related Topics



Leave a reply



Submit