TLDR: The Jupiter Nano is based on the Microchip SAMA5D27-LD1G processor. The boot process of this processor is not very different from a typical linux processor with some quirks here and there. We discuss the boot process of this board here and prepare for doing a minimal Yocto image for this board.
This is part 2 in a series that I am writing about the Jupiter Nano board. I love this board primarily because it is a microcontroller kit AND a linux kit in a tiny form-factor! In other words, you can choose to boot linux or you can choose to use it as a microcontroller.
You can check out part 1 at the below link where I talk about the board in more detail and also explain how to build a debian linux image for it.
In the last part, I showed you how to build a debian image for the Jupiter Nano. Debian images are generally HUGE in size! Yes, you may be using a high-density SD card and hence image size may not be a problem but very often boot time is a (very) big consideration in embedded systems. In the coming few posts, I will take you through the process of how I created my (and possibly world’s) first Yocto image for the Jupiter Nano.
Not sure what Yocto is? Check out this 10-part series about Yocto.
Parts 1 to 10 – Learn Yocto on KickstartEmbedded!
But before we start building a Yocto image, we will first learn about the boot process of the Jupiter Nano when using it as a linux machine and then we will see what we need to generate a Yocto configuration for this board.
Boot process of a linux image – an overview
Although this post is about the Jupiter Nano, the below boot process is more or less true for almost any embedded linux machine with some quirks here and there for efficiency or for implementing additional features.
Below is a graphic that represents the boot process of a generic linux machine and specifically how the Jupiter Nano boots linux. Also mentioned are the roles of the various boot components until the linux kernel is loaded.
ROM Bootloader – The First Level
What is ROM Bootloader?
As soon as the required power rails are applied to the SAMA5D27-LD1G processor on the Jupiter Nano, the ROM bootloader is executed. As the name suggests, the ROM bootloader is a piece of read-only software that executes from the internal BootROM of the processor.
There is no publicly documented way to prevent this from running.
What does it do?
The primary role of this bootloader is to allow the programmer to load valid images into the boot media connected to the processor. For example, if a blank micro SD card is connected to the Jupiter Nano, you can use the ROM Bootloader to write content to it and then tell the processor to start booting from it.
ROM Bootloader passes control to the at91bootstrap. See the next section for more details about the at91bootstrap.
What are the software pre-requisites for the ROM Bootloader?
While you don’t need any software to run the ROM Bootloader, you do need a software from Microchip called the SAM-BA tool to load images into the external boot media. Of course, you can always load the images into the micro-SD card and plug it into the board every time – just don’t break your SD connector!
AT91Bootstrap – The Second Level
What is AT91Bootstrap?
AT91Bootstrap or simply the bootstrap is a free and open-source second level bootloader for the SAM microprocessors from Microchip. It is written and maintained by Microchip on Github.
The bootstrap is generally located in an external boot media (say an SD card or an SPI flash, etc.). In the case of Jupiter Nano, the bootstrap should be located in the micro SD card. Upon power-up, the ROM Bootloader looks for a valid bootstrap image in the micro SD card and if successful, loads the bootstrap for execution into the processor’s internal SRAM.
What does it do?
Once loaded into the internal SRAM, the bootstrap initializes the system clocks, configures the next boot media (this selection is done during build time) and configures the DDRAM interface (this selection is done during build time). There are some other optional features that can be enabled in the bootstrap. For example, some newer versions of the bootstrap allow you to load a secure OS called optee-os and so on.
Once the configured functionalities are done, the bootstrap looks for the next bootloader or the linux kernel image (depending on the selection during build time) in a configured external boot media, loads it to DDRAM and transfers control to it.
What are the software pre-requisites for the AT91bootstrap?
To be able to build and configure the bootstrap, you need the below pre-requisites.
- Python3 – this is used by some automation scripts within the bootstrap build system
- GNU Arm Toolchain – version >6 is preferred
- Ncurses library – this is necessary to be able to graphically configure the bootstrap. Options like which boot media to use, which IOSET to use, extra features to be enabled, etc. can be configured visually using the menuconfig window.
If you are using a linux distribution like Ubuntu as the host build machine, you can execute the below to install the dependencies.
$ sudo apt update $ sudo apt install python3 gcc-arm-linux-gnueabihf libncurses5 libncurses5-dev
U-Boot – The (Optional) Third Level
What is U-Boot?
According to Wikipedia, the U-Boot or Das U-Boot (U = Universal) is an open-source, primary boot loader used in embedded devices to package the instructions to boot the device’s operating system kernel.
It is available for a number of computer architectures including ARM, Blackfin, MicroBlaze, MIPS, etc. The U-Boot is very widely used and it is extremely common in most linux embedded systems to load the linux kernel using the U-Boot.
What does it do?
U-Boot runs a command-line interface on a console or a serial port. Using the CLI, users can load and boot an OS kernel like linux, possibly changing parameters from the default. There is a component of U-Boot called the SPL (Secondary Program Loader) which runs out of the internal SRAM of the processor and initializes the DDRAM, clocks, etc. Since the bootstrap already does that in Microchip processors, it is not strictly mandatory to use SPL.
U-Boot also provides commands to read device information, read and write flash memory, download files (kernels, boot images, etc.) from the serial port or network, manipulate device trees, and work with environment variables.
What are the software prerequisites of the U-Boot?
In our development, we will use the U-Boot maintained by Microchip called u-boot-at91 and it located here.
The software requirements of the U-Boot are very similar to those of the AT91Bootstrap. If you have already installed those, no extra steps needed except installing
openssl package as it is used for crypto operations on the host (if enabled in the configuration).
To install openssl on a linux distribution like Ubuntu, execute the below.
$ sudo apt install openssl
(Linux Kernel + Device Tree) or (FIT) – The Fourth Level
In linux based systems, the U-Boot generally loads the device tree blob and the kernel image into the DDRAM and then passes control to the linux kernel using a command like
bootz, etc. However in modern day systems, it is highly recommended to use something called FIT images for booting the kernel. FIT stands for Flattened uImage Trees.
The benefits of using a FIT are many – most important of which is the ability to have inherent integrity check using a strong hash as well as the ability to have multiple boot configurations (like a production and a rescue configuration). Particularly, the security of such a boot can be enhanced by adding RSA authentication-based boot as well.
It is also possible to select overlay multiple device trees at run-time using commands in the U-Boot CLI or the environment. All in all, FIT images are becoming popular and it is good to use them if supported.
See the below image for the major features of using zImage + device tree v/s using a FIT image.
Once the linux kernel has booted, it loads the file system. But how does the kernel know where to find the file system? Bootargs! The best way to convey this information to the linux is by providing boot arguments from U-Boot or bootargs as they are commonly referred to. Other than the location of the file system, bootargs also indicate to the linux kernel which serial port to use as console to spit out kernel prints.
Microchip maintains a version of the linux kernel for their SAM processors on GitHub.
Creating a Yocto Configuration For a New Board – A Crash Course
The process of creating a new Yocto configuration for a currently unsupported board can be daunting! The best approach to do this is to base this off an existing board’s configuration.
When going about creating your own Yocto configuration for a board, the most important steps are as below.
- Machine configuration – This configuration contains answers to questions like what kind of machine this is, the name of the device tree to use, the components to install in the boot partition, names of boot-loader configuration files, etc.
Getting this right is of utmost importance – even a small error can cause a failed or broken boot!
- Bootloader recipes – We saw above that the AT91Bootstrap (or the bootstrap) is the second level bootloader for SAMA5D27-LD1G and the U-Boot is the third level bootloader. Having working recipes for these bootloaders is essential. These recipes should point to not just the right source-tree but also the right configuration files.
- Kernel recipe(s) – Having the right linux kernel recipe for the board is absolutely essential and often the cause of a lot of grief. This is where the approach of using an existing recipe as the base is very handy!
- Image recipe(s) – More often than not, you may never need to create a new image configuration – not at the very beginning at least! It is perfectly OK to start off with a base image like a
core-image-minimaland then build on it once there is a proven and successful boot.
Not sure what a recipe is? Check out the Part 1 of my Yocto series to know more about the basics of the Yocto build system and also Part 6 where we wrote our very first recipe!
Creating a Yocto Configuration for Jupiter Nano
As we saw in Part 1 of the Yocto series, it is a layered build system. Microchip maintains a Yocto meta layer for their SAM processors called
meta-atmel on GitHub. Also, we saw above that Microchip maintains their bootloaders (bootstrap, u-boot-at91) and the linux kernel also on GitHub.
The process for creating a successful Yocto build for a simple image like
core-image-minimal would involve the below steps.
- Modifying AT91Bootstrap to add support for Jupiter Nano – creating a defconfig for micro SD card boot
- Modifying U-Boot to add support for Jupiter Nano – creating a defconfig with the changes for Jupiter Nano (machine name, DTB name, boot arguments, etc.) as well as creating source code for the board specific functionality (DDRAM initialization, clock initialization, etc.)
- Modifying Linux Kernel to add support for Jupiter Nano – creating a new device tree file for the board which enables the peripherals on the board like I2C, SPI, CAN, etc.
- Modifying the kernel and bootloader recipes inside
meta-atmelto add support for Jupiter Nano – upgrade to the latest commits done in steps 1, 2, 3 above
- Creating a new machine for the Jupiter Nano – a good example for a name is
meta-atmel. This nomenclature is in line with the names of other Microchip machines.
- Try (bit)baking the image named
core-image-minimal. If the build succeeds, you have successfully created a Yocto distribution for the Jupiter Nano!
You may need to iterate from steps 1-5 until all errors have been spotted and eliminated!
In this post, we learnt about the boot process of the Jupiter Nano when it runs a linux image. We also talked in brief about Yocto and learnt about what it would take to build a Yocto image for the Jupiter Nano.
In the coming post, we will perform the steps 1-5 listed above and build our first ever Yocto image for the Jupiter Nano.