RZ-G/RZ-G2L Flash Programming

From Renesas.info
Jump to navigation Jump to search

RZ/G2L boot modes and typical boot storage media

RZ/G2L boot modes are summarized in the table below.

Selection of RZ/G2L boot modes
MD_BOOT2 to MD_BOOT0 Boot Mode Interface Module Connected Device
0 0 0 Boot mode 0 SDHI0 eSD (3.3 V at startup)
0 0 1 Boot mode 1 SDHI0 1.8-V eMMC
0 1 0 Boot mode 2 SDHI0 3.3-V eMMC
0 1 1 Boot mode 3 SPIBSC 1.8-V Single Quad or Octal serial flash memory
1 0 0 Boot mode 4 SPIBSC 3.3-V Single or Quad serial flash memory
1 0 1 Boot mode 5 SCIF0 Downloading through SCIF

When the eMMC is delivered from the manufactures is normally divided into several areas as shown below.

image.png

In most of the Linux applications covered by the RZ/G2L, when it comes to non-volatile memory there are two main use cases:

  • QSPI and eMMC. QSPI is normally used for firmware / boot loaders (i.e. ATF and u-boot) whereas eMMC for kernel and rootfs.

image QSPI and eMMC.png

  • eMMC only. In this case one of the two eMMC boot partitions is used for boot loaders and the user partition is then used for rootfs and kernel.

image eMMC only.png

Very often there is only one ext partition in the user space and Linux kernel and device tree are stored in it (e.g. /boot folder).

image rfs + linux kernel2.png

There might be other special cases but we can confidently state that these cover the great majority. We can also reasonably say that being the RZ/G2L a low-end device, there is the tendency to remove the QSPI to save money on the BOM.

At the end of the production line of course non-volatile memories are virgin (with a notable exception explained later) and need to be programmed. Usually firmware and bootloaders are relatively small (less than 1MB in total), kernel is instead in the tens of megabyte range and rootfs can go up to several hundreds of megabytes. To transfer this amount of data a fast interface is indeed required.

Flash Programming options

RZ/G2L cannot boot from USB, so at the end-of-line there are a few real viable options:

  • Boot from SCIF (UART)
  • Use JTAG
  • It is worth to mention that, in general, if a QSPI FLASH is present on the custom board, it can also be bought preprogrammed
  • Use a temporary QSPI flash card

In any of the cases above the goal is to have U-Boot up and running, then there are plenty of options to program the rootfs: ethernet, SD card, USB as host, USB with Mass Storage gadget, fastboot or even boot Linux kernel + initramfs.

With option 1, you can download Flash Writer and program the bootloaders, Arm Trusted Firmware (ATF) and U-Boot, into QSPI / eMMC boot partition, then reset and run U-Boot.

With option 2, you can download ATF and U-Boot via JTAG and then run the boot loaders without having to initially program them. Once U-Boot runs, you can program both bootloaders and rootfs.

With option 3, QSPI has already bootloaders pre-programmed, so U-Boot is already available.

With option 4, a QSPI flash card is somehow attached to the board that needs to be programmed. The QSPI flash contains ATF and U-Boot and potentially also the root file system to be programmed, depending on the size of both. More details are available here.

Unattended Flash Programming

All the options above may not require any human intervention and can be to a certain extent automatized.

In the rest of this page follow some examples on how to implement a fully atomized flash programming. More in particular we will focus on option 1, where:

  1. ATF and U-Boot are programmed using the SCIF.
  2. At the reboot U-Boot loads and boots kernel and the initramfs from USB.
  3. Then a custom init script is used of program the root file system stored on the USB thumb to the eMMC.
  4. After eMMC programming U-Boot is replaced, mainly to have a new set of environment variables to boot the final kernel / rootfs.

It is assumed that all binaries to be programmed are already available, for example as result of the porting of the BSP. This is the list of items that are needed:

  • ATF binaries (e.g. bl2_bp.bin, bl31.bin);
  • u-boot source code to generate temporary and definitive binaries (u-boot.bin);
  • Kernel image and device tree (e.g. Image, r9a07g044l2-smarc-smarc-rzg2l.dtb);
  • A system image file including including root file system (e.g sd_card.img.gz) that can be easily created using this script.

However, there are other things to be done beforehand: step 0.

Step 0 - Environment set-up

Temporary u-boot

u-boot used at the first step is not the definitive one, its default environment parameters are adapted to allow the next steps to be executed automatically. The default environment parameters are configured in include/configs/BOARD_NAME.h, for example for the RZ/G2L smarc board:

#define CONFIG_EXTRA_ENV_SETTINGS \
	"bootm_size=0x10000000 \0" \
	"baudrate=115200 \0" \
	"bootargs=rw rootwait earlycon root=/dev/ram0 rdinit=/home/root/my_init_flash_prog.sh \0" \
	"bootdelay=3 \0" \
	"bootimagerd=booti 0x4A080000 0x50000000 0x48000000 \0" \
	"produsbbootargs=setenv bootargs rw rootwait earlycon root=/dev/ram0 rdinit=/home/root/my_init_flash_prog.sh \0" \
	"usbload=usb start;fatload usb 0 0x4A080000 Image;fatload usb 0 0x48000000 r9a07g044l2-smarc-smarc-rzg2l.dtb;fatload usb 0 0x50000000 core-image-minimal-smarc-rzg2l-mod.cpio.gz.u-boot;run produsbbootargs \0"

#define CONFIG_BOOTCOMMAND	"run usbload;run bootimagerd"

These parameters ensure that:

  1. After a boot delay of 3 seconds the command usbload is executed;
  2. usbload, as the name suggests, after having initialized USB, loads kernel, device tree and initramfs into DDR.
  3. boots kernel with initramfs and a custom init script that is in charge of programming kernel, device tree and the definitive root fs into eMMC.

It is important to note that u-boot has to be built with USB support, obviously. It may not be enabled by default, refer to this TBD page for more details.

u-boot binary (u-boot.bin) has to be combined with bl3x to generate fip.bin, assuming bl31.bin only:

tools/fiptool/fiptool create --align 16 --soc-fw build/g2l/release/bl31.bin --nt-fw u-boot.bin fip.bin

fiptool is a tool distributed with ATF.

Generating u-boot variables binary

Instead of building another ad-hoc version of u-boot, we can create a environment variable binary file that can be programmed to a specific location using Flash Writer. These environment parameters are the same as the previous section. To do that we can use a utility called mkenvimage. First of all we have to create a text file (u-boot-env.txt) with the environment variables as we would read them from u-boot, e.g.:

bootm_size=0x10000000
baudrate=115200
bootargs=rw rootwait earlycon root=/dev/ram0 rdinit=/home/root/my_init_flash_prog.sh
bootcmd=run usbload;run bootimagerd
bootdelay=3
bootimagerd=booti 0x4A080000 0x50000000 0x48000000
produsbbootargs=setenv bootargs rw rootwait earlycon root=/dev/ram0 rdinit=/home/root/my_init_flash_prog.sh
usbload=usb start;fatload usb 0 0x4A080000 Image;fatload usb 0 0x48000000 r9a07g044l2-smarc-smarc-rzg2l.dtb;fatload usb 0 0x50000000 core-image-minimal-smarc-rzg2l-mod.cpio.gz.u-boot;run produsbbootargs

Then we can convert this into a binary using mkenvimage:

mkenvimage u-boot-env.txt -s 0x20000 -o u-boot-env.bin

What is important to notice is the size (-s). This parameter must match what is configured in u-boot, take note also of the offset and partition where the environment variables are stored. If the command gets executed correctly, we will end up with a binary file (u-boot-env.bin) whose size is exactly what specified, in the example above exactly 128KB.u-boot-fw-utils

Once the kernel is running, instead of dealing with binaries that are somewhat not very convenient, we can use the utilities that are part of the former u-boot-fw-utils package, nowadays called libubootenv: fw_printenv and fw_setenv.

In order to get it build and be part of the generated image, the following packages shall be added in the local.conf (Yocto):

IMAGE_INSTALL_append = " \
   libubootenv \
   libubootenv-bin \
"

To use these two utilities a text configuration file (/etc/fw_env.config), including a single text line to tell the tools where to find the data:

/dev/mmcblk0boot1               -0x20000        0x20000

The meaning is quite obvious. In the first column we find the device where the data is stored (in this example it is eMMC, boot partition 2). The second column is the offset, in a similar way it is defined in the u-boot parameter, so a negative number is treated as a backward offset from the end of of the eMMC device/partition. The last column is the size.

One last step is to create a script file that is going to be used by fw_setenv to (re)set the environment variables to what they will have to look like eventually. The syntax is very simple:

key [space] value

Here the complete script final_env_variables.txt content.

Initrd script

This is the script that is going to be executed right after the kernel has finished booting.

Initramfs creation

Any root file system, including the final one that was created for the final application can be turned into a initramfs. However you have to take in account that it is convenient to have a small one. First it is loaded quicker from the thumb drive, then it has to be small enough to comfortably fit into the DRAM. In this example the initial rootfs is created using Yocto: bitbake core-image-minimal. You may want to add packages like mtd-utils and mmc-utils that are useful to program QSPI and eMMC.

Once the minimal rootfs has been created, we have to modify it to add the custom initrd script (see previous section). In general, assuming you have your rootfs in the usual tar.gz/bz2 format, first of all we have to untar/unzip it into a temp folder:

mkdir temp
sudo tar -xvf core-image-minimal-smarc-rzg2l.tar.bz2 -C temp

You want to use sudo because then all the files have root owner.

cd temp

Assuming in the same folder the initrd script, as well as bl2, fip (definitive version) and other files are available:

sudo cp ../my_init_flash_prog.sh home/root/
sudo cp ../bl2_bp.bin home/root/ 
sudo cp ../fip.bin home/root
sudo cp ../final_env_variables.txt home/root
sudo cp ../fw_env.config etc/

Let's get the initrd script executable:

sudo chmod +x home/root/my_init_flash_prog.sh

We have to create a init symbolic link into the root folder:

sudo ln -s sbin/init init

Now we are ready to pack the modified files to cpio ‘newc’ format:

sudo find . | sudo cpio --create --format='newc' > ../core-image-minimal-smarc-rzg2l-mod.cpio

Gzip it:

cd ..
gzip -v core-image-minimal-smarc-rzg2l-mod.cpio

Finally create a u-boot image:

mkimage -n "core-image-minimal-smarc-rzg2l-mod" -A arm64 -O linux -T ramdisk -C none -d core-image-minimal-smarc-rzg2l-mod.cpio.gz core-image-minimal-smarc-rzg2l-mod.cpio.gz.u-boot

If the last command fails, then you need to install u-boot tools. For example in Ubuntu:

sudo apt install u-boot-tools

USB thumb drive preparation

We can now prepare a thumb drive with the following content:

  • Kernel and device tree (Image, r9a07g044l2-smarc-smarc-rzg2l.dtb);
  • Root file system image (sd_card.img.gz);
  • Initram fs (core-image-minimal-smarc-rzg2l-mod.cpio.gz.u-boot)

Step 1 - Programming ATF and u-boot via SCIF

The main tool that can assist in performing external non-volatile memory (QSPI and eMMC) is Flash Writer. Depending on the host PC used, you can either use a bash script or a TeraTerm macro. In both cases you would need two / three files:

  • bl2.bin
  • fip.bin
  • u-boot-env.bin (optional)

The file names may vary a bit. These are either the output of Yocto or generated by combining ATF and U-Boot binaries. Actually the first one is generated by ATF directly whereas the second is the generated by the Firmware Image Package (FIP) utility by combining BL3x binaries (BL31 secure monitor, optionally BL32 OP-TEE and BL33 u-boot).

u-boot-env.bin can be created as per instructions here.

Linux script

A Linux bash script is available here. Please refer to the README for help on the usage.

⚠️ Note that this script cannot automatically write/update u-boot environment variables.

Windows TeraTerm macro

Usually dealing with this type of devices a Linux host PC is normally available. However sometimes it might be convenient to have a quick way to program the bootloaders using Windows. Especially during production, a Windows PC may be more convenient than a Linux PC.

Instead of using an ad-hoc tool developed only for the RZ/G2L the idea is to use a generic terminal utility. Tera Term is an open source tool that has some nice scripting capabilities embedded: a Tera Term script is called MACRO. The developed macro "replaces" the user and provides inputs to Flash Writer, whose binary is also downloaded automatically. There are just a couple of parameters that you would need to change (open the macro with a standard txt editor), stuff like COM port or the name of the Flash Writer binary to be used and whether the target is QSPI or eMMC. The "UNATTENDED" parameter suppresses all the user interaction apart from errors.

In general environment parameters can be saved in different non-volatile media, the most common are eMMC and QSPI. The TeraTerm macro can program the environment variable binary file in the eMMC only. Env variables can be programmed in different areas though: user, boot partition 1 or boot partition 2. Default RZ/G2L u-boot stores them in boot partition 2 (boot partition 1 is used for ATF and FIP). By default the u-boot parameter:

CONFIG_ENV_OFFSET=0xFFFE0000

stores the environment variables at a backwards offset from the end of the eMMC boot partition 2. eMMC may have different boot partition sizes, the actual value can be obtained by reading the extended CSD register 226, for example, using Flash Writer command EM_DECSD we will get something like:

[EXT_CSD Field Data]
[...]
[228:228]  BOOT_INFO                                  0x07
[226:226]  BOOT_SIZE_MULTI                            0xFC
[225:225]  ACC_SIZE                                   0x00
[...]

The actual size can be obtained by multiplying 0xFC by 128Kbytes, so 252x128x1.024=33.030.144 bytes=32.256KB=31.5MB.

On the other hand 0xFFFE0000 shall be read as -0x20000 or -128KB. So taking in account that in Flash Writer we have to configure a sector (eMMC default size = 512), in our case we have to choose in the TeraTerm macro:

33.030.144 - 131.072 (128K) = 32.899.072 that should be divided by 512 = 64.256 = 0xFB00.

It is assumed that MD_BOOTx pins are already configured for the final boot mode. However, if the target flash is virgin / erased, the boot mode selected fails over to SCIF download, allowing the Flash Writer bin to be downloaded.

When the procedure is complete, the system gets reset and the system boots using the newly programmed files.

Step 2

At this step there's not anything to do, just to check that what we've done at step 0 works consistently. If everything is ok the system goes thru the standard boot stages and u-boot load kernel and initrd from the USB thumb drive.

TeraTerm Flash Programming Kernel booting.png

Step 3

At this stage the custom initrd script is executed, programming the rootfs onto the eMMC user area. Note that at the end of the copy the ext4 partition is resized to the max allowed by the eMMC. Again, we just need to check that everything is inline with expectations.

TeraTerm Flash Programming eMMC.png

Step 4

The last step is needed to replace u-boot default environment variables to values that allow the final rootfs to be booted from eMMC. This is the version of u-boot that was created originally, anyway here below the environment variables you may want to use:

#define CONFIG_EXTRA_ENV_SETTINGS \
	"bootm_size=0x10000000 \0" \
	"prodsdbootargs=setenv bootargs rw rootwait earlycon root=/dev/mmcblk1p2 \0" \
	"prodemmcbootargs=setenv bootargs rw rootwait earlycon root=/dev/mmcblk0p2 \0" \
	"bootimage=booti 0x4A080000 - 0x48000000 \0" \
	"emmcload=fatload mmc 0:1 0x4A080000 Image;fatload mmc 0:1 0x48000000 r9a07g044l2-smarc-smarc-rzg2l.dtb;run prodemmcbootargs \0" \
	"sd1load=fatload mmc 1:1 0x4A080000 Image;fatload mmc 1:1 0x48000000 r9a07g044l2-smarc-smarc-rzg2l.dtb;run prodsdbootargs \0" \
	"bootcmd_check=if mmc dev 1; then run sd1load; else run emmcload; fi \0"

#define CONFIG_BOOTCOMMAND	"env default -a;run bootcmd_check;run bootimage"

Or in terms of fw_setenv script:

baudrate=115200
bootargs=rw rootwait earlycon root=/dev/mmcblk0p1
bootargsrd=rw rootwait earlycon root=/dev/ram0 rdinit=/home/root/my_init_flash_prog.sh
bootcmd=run bootcmd_check;run bootimage
bootcmd_check=if mmc dev 1; then run sd1load; else run emmcload; fi
bootdelay=2
bootimage=booti 0x4A080000 - 0x48000000
bootm_size=0x10000000
emmcload=fatload mmc 0:1 0x4A080000 Image;fatload mmc 0:1 0x48000000 r9a07g044l2-smarc-smarc-rzg2l.dtb;run prodemmcbootargs
loadaddr=0x58000000
prodemmcbootargs=setenv bootargs rw rootwait earlycon root=/dev/mmcblk0p2
prodsdbootargs=setenv bootargs rw rootwait earlycon root=/dev/mmcblk1p2
sd1load=fatload mmc 1:1 0x4A080000 Image;fatload mmc 1:1 0x48000000 r9a07g044l2-smarc-smarc-rzg2l.dtb;run prodsdbootargs

Use JTAG

TBD