Going serial (eeprom)

Published on September 2, 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.

The i.MX51 has a very flexible bootstrap scheme, driven by an internal ROM and fuses. It can boot directly from USB, serial (RS-232), serial EEPROM, NAND flash and SD cards. Early versions of the Boundary Devices Nitrogen boards shipped with their fuses configured to boot from SD card. Because they're easily removable, this made sense and prevented the possibility of bricking your board, but we're way beyond that now. Consequently. we have recently switched to shipping all of our devices to boot from serial EEPROM instead. In this note, I'll walk through the boot loader versions needed to make this happen and also walk through some of the tools available to you for updating the boot loader and for making the transition.

Getting the sources

To begin with, the serial EEPROM code was enabled in the boundary20100817  branch of the u-boot-nitrogen tree. You can build it in the normal fashion (this example presumes the use of the CodeSourcery arm2010q1 compiler). user@host:~/u-boot-nitrogen $ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- nitrogen_config user@host:~/u-boot-nitrogen $ make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- all It will build a normal u-boot.bin file as with prior releases, but you should normally use a new output: ubl_ecspi.bin instead. This file contains the U-Boot image and a first-level loader which runs from internal RAM on the i.MX5x. Because both images are created, the resulting image has support for __both__ SD card and serial EEPROM so you can use the same image on both old and new boards, but you'll need to update the first-level loader on your SD cards to match. In this release, we've placed the first level loader into the U-Boot tree in the imx51_utils/ sub-directory. The sdmmc has also been re-named to mx51_sdmmc, paving the way for upcoming i.MX53 boards. To (re)program the first-level loader on an SD card, use the following sequence: user@host:~/u-boot-nitrogen/mx51_utils $ sudo dd if=mx51_sdmmc of=/dev/mmcblk0 bs=512 seek=2

Checking your hardware and kicking the tires

If your hardware has serial EEPROM, you'll be able to see it using the sf probe command U-Boot > sf probe 1 spi_reset: request frequency 1000000 pre_div = 13, post_div=2, clk_src=54000000, spi_freq=964285 SF: idcode 1f 25 00 00 00 1024 KiB AT45DB081D at 0:1 is now current device U-Boot > sf probe 1 27000000 spi_reset: request frequency 27000000 pre_div = 1, post_div=0, clk_src=54000000, spi_freq=27000000 SF: idcode 1f 25 00 00 00 1024 KiB AT45DB081D at 0:1 is now current device This indicates that I have an Atmel AT45DB081D chip, which is a 1K-Byte device. The first parameter to the sf probe command is a chip number and should always be 1. The second, optional parameter is a clock frequency. In the first example above, I used the default of a __really__ __slow__ 1MHz. The second iteration is what we're currently recommending: 27MHz. Using the U-Boot help feature will show you the basics of the 'sf' command: U-Boot > help sf sf - SPI flash sub-system Usage: sf probe [bus:]cs [hz] [mode] - init flash device on given SPI bus and chip select sf read addr offset len - read `len' bytes starting at `offset' to memory at `addr' sf write addr offset len - write `len' bytes from memory at `addr' to flash at `offset' sf erase offset len - erase `len' bytes from `offset' You can use 'sf read' to read from the device. Because I have U-Boot loaded on my board, I see erased data (all 0xFF's) for the first 1K bytes and the first level loader at offset 0x400: U-Boot > sf read 0x90008000 0 0x1000 U-Boot > md 0x90008000 90008000: ffffffff ffffffff ffffffff ffffffff ................ 90008010: ffffffff ffffffff ffffffff ffffffff ................ 90008020: ffffffff ffffffff ffffffff ffffffff ................ 90008030: ffffffff ffffffff ffffffff ffffffff ................ 90008040: ffffffff ffffffff ffffffff ffffffff ................ 90008050: ffffffff ffffffff ffffffff ffffffff ................ 90008060: ffffffff ffffffff ffffffff ffffffff ................ 90008070: ffffffff ffffffff ffffffff ffffffff ................ 90008080: ffffffff ffffffff ffffffff ffffffff ................ 90008090: ffffffff ffffffff ffffffff ffffffff ................ 900080a0: ffffffff ffffffff ffffffff ffffffff ................ 900080b0: ffffffff ffffffff ffffffff ffffffff ................ 900080c0: ffffffff ffffffff ffffffff ffffffff ................ 900080d0: ffffffff ffffffff ffffffff ffffffff ................ 900080e0: ffffffff ffffffff ffffffff ffffffff ................ 900080f0: ffffffff ffffffff ffffffff ffffffff ................ U-Boot > md 0x90008400 90008400: 1ffe2428 000000b1 00000000 1ffe2414 ($...........$.. 90008410: 00000000 1ffe241c 1ffe2000 b17219e9 .....$... ....r. 90008420: 00000000 00001708 e3e0d27e e3cdd040 ........~...@... 90008430: e3a00e1d e3800003 e123f000 ee390f50 ..........#.P.9. 90008440: e3800402 ee290f50 ee110f10 e3c00a03 ....P.)......... 90008450: e3c00005 e3800b02 ee010f10 e3a0060f ................ 90008460: ee010f50 f57ff06f e3a01473 e381160f P...o...s....... 90008470: e3a02483 e382260f e3a00000 e5810040 .$...&......@... 90008480: e5810044 e5810048 e581004c e5810050 D...H...L...P... 90008490: e5820040 e5820044 e5820048 e582004c @...D...H...L... 900084a0: e5820050 e59f03a8 e5810000 e5810004 P............... 900084b0: e5820000 e5820004 eb000068 e3a0a473 ........h...s... 900084c0: e38aa8fb e38aa903 e3a06473 e38668fa ........sd...h.. 900084d0: e3866902 e3a00000 e5860228 e586022c .i......(...,... 900084e0: e3a00f71 e3800001 e5860618 e5860618 q............... 900084f0: e3a06473 e38668fd e3866901 e596006c sd...h...i..l... You can write to the flash using the sf write command as shown in the following example. Since I happen to know that the last 1k of the flash chip isn't currently used for anything, I'll program the four bytes there to contain the well-know constant 0xdeadbeef (sorry, vegetarians!): U-Boot > sf read 0x90008000 0xFFC00 0x400 U-Boot > md 0x90008000 90008000: ffffffff ffffffff ffffffff ffffffff ................ ... U-Boot > mm 0x90008000 90008000: ffffffff ? deadbeef 90008004: ffffffff ? x U-Boot > sf write 0x90008000 0xffc00 4 U-Boot > sf read 0x92000000 0xffc00 4 U-Boot > md.l 0x92000000 1 92000000: deadbeef ....


Note that as with most NOR flash, writes can only turn 1's into 0's. In the following example, I try to change 0xdeadbeef to 0xbeefdead: U-Boot > mm.l 0x90008000 90008000: deadbeef ? beefdead 90008004: ffffffff ? x U-Boot > sf write 0x90008000 0xffc00 4 U-Boot > sf read 0x92000000 0xffc00 4 U-Boot > md.l 0x92000000 1 92000000: 9ead9ead .... It's no coincidence that the constant 0x9ead9ead is 0xdeadbeef&0xbeefdead. In order to completely re-write data, you need to use the sf erase command: U-Boot > sf erase 0xffc00 4 SPI flash erase failed U-Boot > sf erase 0xff000 0x1000 The first attempt shows the next thing you need to know about using the serial EEPROM: that erases happen in pages, not in bytes, so you'll need to issue the erase commands to coincide with the erase block boundaries. The page sizes of various flash chips varies, but a good rule of thumb is to use 4k offsets (and just remember to operate in 0x1000's).

Upgrading U-Boot

Given the "sf read" and "sf write" commands, it's pretty straightforward to implement a boot script to upgrade U-Boot. Following the tradition of our PXA and Davinci boards, the following U-Boot scriptlet will allow you to upgrade a board by placing the U-Boot image onto the SD card and calling it u-boot-nitrogen-something.bin. 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 echo "Already upgraded U-Boot" ; else echo "Need U-Boot upgrade" ; echo "Program in 10 seconds" ; for n in 10 9 8 7 6 5 4 3 2 1 0 ; do echo $n ; sleep 1 ; done echo "Copy image from 0x92000000 to offset 0x400 here" ; sf erase 0 0x40000 ; sf write 0x92000000 0x400 $filesize if sf read 0x92400000 0x400 $filesize ; then if cmp.b 0x92000000 0x92400000 $filesize ; then echo "U-Boot upgraded. Cycle power" ; else echo "Read verification error" ; fi else echo "Error re-reading EEPROM" ; fi fi else echo "Error reading boot loader from EEPROM" ; fi else echo "Error initializing EEPROM" ; fi ; else echo "No U-Boot image found on SD card" ; fi In order to turn this into a U-Boot executable script, you can use the on-line tool on our internal web-site. In order to execute a compiled script, use the source command as described on the U-Boot site.

Flash layout

The following is what we're current using in the EEPROM images. [0-1k) - unused [1k-0x5F000)  - reserved for U-Boot [0x5F000-0x60000) - 4K for U-Boot environment [0x60000.. end of chip) - unused Both the first 1k and tail-end of the chip (after 384k) are unused and available for operating systems, splash images, or application storage. The tail-end is probably easier to use because it doesn't need special saving when upgrading the flash image.

Memory layout

When writing scripts for U-Boot, it's also necessary to know what memory regions are available for use. The following is a general list of addresses used by U-Boot:
  • RAM Addresses start at 0x90000000 and extend through 0xCFFFFFFF (512M boards) or 0x9FFFFFFF (256M boards)
  • U-Boot code is linked at 0x93f00400 (roughly 63M)
  • The heap is located below the code segment, typically at 0x93d00000 and grows upward toward code
  • The stack grows down from the start of the heap.
  • Display buffers are located at the very end of RAM
In general, we'll typically use RAM starting at 0x92000000 (offset 32M) for files loaded from SD card and use RAM at 0x90008000 (offset 32k) for scripts.

Read/write/erase speed

The serial EEPROM is currently clocked at 27MHz, or 2.7MBytes/second. Since our current version of U-Boot is around 180k, it will take around 70ms to load into RAM before execution. We don't yet have any numbers for write or erase times except from the EEPROM data sheets. As with all NOR flash, erase times should be much worse than write times. The following numbers are from the Atmel AT45DB081D data sheet: tPE Page Erase Time (256-/264-bytes) 13 32 13 32 ms tBE Block Erase Time (2,048-/2,112-bytes) 30 75 30 75 ms tSE Sector Erase Time (65,536/67,584) 0.7 1.3 0.7 1.3 s