Boot scripts for Nitrogen (i.MX51)

Published on September 21, 2010

Archived Notice

This article has been archived and may contain broken links, photos and out-of-date information. If you have any questions, please Contact Us.

In earlier blog posts, e-mails, and the fledgling Nitrogen User's Manual we've described the setup of the U-Boot variables bootcmd and bootargs for particular use cases. Because changing these requires access to the serial console and because the overwhelming majority of our customers are booting from SD card, we're making a change to our process.For those of you familiar with our PXA series of boards, this should be old hat to you, since we've been doing this for years on that platform. We're essentially just swapping the file name of init.scr for the new convention of nitrogen_bootscript.For those of you who aren't familiar, we'll try to walk through the essentials in this post.

One command-line for all boards

From this point forward, unless we've made arrangements with you for production needs, we'll be shipping all of our devices with the same value of bootcmd:

fatload mmc 0 90008000 nitrogen_bootscript &&
source 90008000 ;
errmsg='Error running bootscript!' ;
lecho $errmsg ;
echo $errmsg ;

In English, this says to try and load the file nitrogen_bootscript from the first (FAT) partition on the SD card and run it. The source command in U-Boot will evaluate a compiled script file in memory as if it were entered at the command-line (more on compilation later).In normal use, the nitrogen_bootscript script should never return. It should load the O/S of choice.If either the script loading fails or the script runs to completion, the bootcmd will display the string Error running bootscript! on the first LCD panel and the serial port.

Why this is useful

You may be asking yourself whether we're just moving the decision about what to do at boot time from one place to another.We are. We're moving it to a place where it's easier to change.By doing this, the boot decisions can be made by removing the card, or by updating the card under either Linux or Windows CE.It's also much easier to edit the script when it's not entered through the U-Boot command line.As a final benefit, a script on the SD card can be larger than will fit into the command-line buffer in U-Boot.

Editing and compilation

I mentioned earlier that nitrogen_bootscript must be a compiled script file. More specifically, a U-Boot script is a set of text with Unix-style line-ends using the hush syntax. Refer to the U-Boot Manual for details, but if you're familiar with sh under Linux, you won't find too many surprises.The text file is compiled using the mkimage tool, which is available for a number of platforms. On Debian-based hosts, you'll find that there's a package named uboot-mkimage. mkimage takes a number of parameters, but most of them have little meaning to a script.The following short-hand should do it for you:

user@host:~$ mkimage -A arm -O linux -T script -n "Bootscript"
                      -d bootscript.txt nitrogen_bootscript
Image Name:   Bootscript
Created:      Tue Sep 21 11:26:59 2010
Image Type:   ARM Linux Script (gzip compressed)
Data Size:    1888 Bytes = 1.84 kB = 0.00 MB
Load Address: 00000000
Entry Point:  00000000
Contents:
   Image 0: 1880 Bytes = 1.84 kB = 0.00 MB

This example converts the text file bootscript.txt into the compiled binary nitrogen_bootscript.
Note that this isn't true compilation: no syntax checking occurs. The mkimage tool also doesn't change the
format of line-ends. It simply adds a header with the name, description, type and length to the file.To make it easier to do this, we've created a web-based tool that allows you to simply paste the text into a text control and receive the resulting binary.

Boot scripts in practice

The notes above cover the basics of what a boot script is and how we'll be configuring the U-Boot environment variables on the boards. The following are some snippets of code that you'll find inside the nitrogen_bootscript files that we ship.These are only a handful of examples of what you may find inside, but we've found each of them useful on at least one occasion

Displaying a splash screen

This example builds on the parts we describe in thepost about U-Boot's display support. It will attempt to load a file named logosomething.bmp from the first (FAT) partition on the SD card and display it on the screen.

if fatload mmc 0 92000000 logo*.bmp ; then
		bmp display 92000000 ;
	fi

Upgrading U-Boot itself:

This example will try to load u-boot-nitrogen-something.bin from the first (FAT) partition on the SD card and compare it against the serial EEPROM. It builds upon the commands described in the Going serial post.Note that this process may take a second or so to run if the file is actually present. You might consider a scheme to place the file onto the SD card only when an upgrade is needed and deleting it afterwards.In some environments, our customers use separate upgrade SD cards to make sure there's a person in front of a device when upgrades occur.

lecho "check U-Boot" ;
if fatload mmc 0 92000000 u-boot-nitrogen*.bin ; then
      if sf probe 1 27000000 ; then
           if sf read 0x92400000 0x400 $filesize ; then
               if cmp.b 0x92000000 0x92400000 $filesize ; then
                   lecho "..." ;
               else
                   lecho "Need U-Boot upgrade" ;
                   lecho "Program in 5 seconds" ;
                   for n in 5 4 3 2 1 0 ; do
                        lecho $n ;
                        sleep 1 ;
                   done
                   sf erase 0 0x40000 ;
                   sf write 0x92000000 0x400 $filesize
                   if sf read 0x92400000 0x400 $filesize ; then
                       if cmp.b 0x92000000 0x92400000 $filesize ; then
                           while lecho "U-Boot upgraded. Cycle power" ; do
                sleep 120
               done
                       else
                           lecho "Read verification error" ;
                       fi
                   else
                        lecho "Error re-reading EEPROM" ;
                   fi
               fi
           else
               lecho "Error reading boot loader from EEPROM" ;
           fi
      else
           lecho "Error initializing EEPROM" ;
      fi ;
else
     lecho "No U-Boot image found on SD card" ;
fi

Note that the file named u-boot-nitrogen*.bin should be created from ubl_ecspi.bin in the U-Boot build directory, not from u-boot.bin.

Loading and launching Windows CE:

This is probably the simplest form of O/S load and launch, since there is no kernel command line under Windows CE. This example simply reads a file named NK6-nitrogensomething.nb0 from SD card and jumps to it.

if fatload mmc 0 90200000 nk6-nitrogen*.nb0 ; then
    lecho "Launching CE6" ;
    go 90200000 ;
else
    lecho "No system image" ;
fi

Changing a display setting

This is something we do a lot of at Boundary Devices. We'll program boards at production time for one panel, but connect it up to another before shipment to a customer. It may also be useful to you if you have more than one display in common use.Using a very minimal bootscript like the one below will allow you to simply change the display setting through the
lcdpanel command.The same idea of changing an environment variable and saving through a boot script is often useful for other reasons. You can use this to change bootargs or even bootcmd on devices you receive from us.

lcdpanel hitachi_hvga ;
savee ;
lecho Panel setting saved.

Another alternative is to set and save the panel conditionally in your production boot script like this:

if test $panel != hitachi_hvga ; then
	lcdp hitachi_hvga ;
	saveenv ;
fi

Setting a GPIO pin

If you're connecting GPIO outputs to a Nitrogen, you may want to set the pin state immediately at boot time. To do this, you can use the mw (memory write) command in U-Boot to change the value of the i.MX51 registers.This is totally hardware dependent, requires knowledge of the register set of the i.MX51 and the normal state of the rest of the bits in the GPIO1_DR register, but the following will set GPIO bank 1/pin 5 high. It just so happens that this is an output connected to J17 pin 1 on the Nitrogen board and a command like this may be just the thing you need.

mw.l 0x73F84000 008000b8

Loading and launching Linux:

Linux is incredibly flexible. Because of the wide variety of ways in which it can be configured to boot, it's impossible to list every option here. Instead, let's look at the three key pieces involved in booting Linux:

  • The Linux kernel
  • RAM disk
  • Kernel command-line arguments

The Linux kernel is generally loaded from a file named uImageSomething to address 0x92000000 using a fragment like the following:

if fatload mmc 0 0x92000000 uImage* ; then
      echo "Linux kernel image loaded" ;
fi

The address of the kernel is handed to U-Boot as a part of the bootm command.

bootm 0x92000000

RAM disks are generally used in booting Android, Busybox, and Ubuntu Live images. They're typically loaded at address 0x92400000, leaving 4MB for kernel image:

if fatload mmc 0 0x92400000 initrd*.u-boot ; then
     echo "RAM disk loaded" ;
fi

If used, the address of the RAM disk is passed to the bootm command as a second parameter:

bootm 0x92000000 0x92400000

The final piece of the puzzle, the kernel command-line has way more options. The Linux kernel tree has a set of common parameters, but that just scratches the surface. Each device driver may contain its own set of parameters, and user-space environments such as Android and Ubuntu also use the kernel command-line as a way for the boot loader to pass instructions about how to start up to their respective startup scripts.The kernel command line is handed to the bootm command indirectly through the use of the bootargs environment variable.The following is a short list of some common things we place into bootargs for different boot scenarios:

  • console=ttymxc0,115200 - (kernel). This option tells the kernel to use the first serial port as the default console. Without this, you'll get the kernel boot messages on the display.
  • ip=dhcp - (kernel). This option tells the kernel's networking stack to get an IP address through the DHCP protocol. It's normally used when booting over NFS.
  • root=/dev/nfs - (kernel). This option tells the kernel to use NFS for its' root filesystem.
  • nfsroot=10.0.0.1:/path/to/rootfs - (kernel). This option specifies the IP address of the NFS server and the path to the root filesystem on that server.
  • rootwait - (kernel). This option tells the kernel to wait if the boot device isn't immediately available. This is needed when not using a RAM disk to allow enumeration of SD card partitions or for an NFS connection to be established.
  • fixrtc - (kernel). Used to force a date and time into the real-time clock at boot time before a network time server is available. This is most useful when booting to an ext2 or ext3 filesystem since a ram-disk
    may try to use a timestamp to determine if the filesystem should be checked.
  • calibration - (Android). Tells Android to use a touch screen calibration file.
  • quiet - (Debian, Ubuntu). Don't display messages to the console during boot.
  • splash - (Debian, Ubuntu). Show an animated graphic on the display instead of the progress text.
  • text - (Debian, Ubuntu). Don't boot X-Windows.
  • debug=y - (Debian, Ubuntu live images). Display detailed boot progress for the live startup on the console.
  • --debug - (Debian, Ubuntu). Display detailed boot progress for the core Ubuntu startup on the console.
  • union - (Ubuntu) - If your kernel is built without aufs support, specify union=unionfs

We certainly hope these notes help you to understand the power of the boot script and the details that you may see in shipments from us. If any of them are confusing, please let us know.