SPI FLASH Recovery/XO-1

From OLPC
< SPI FLASH Recovery
Revision as of 19:13, 16 October 2006 by Wmb@firmworks.com (talk | contribs) (Minor cleanups and additions.)
Jump to navigation Jump to search

SPI FLASH Recovery

If the SPI boot FLASH somehow gets bad data in it, so the firmware won't run, you can reload it with a special serial cable.

The cable connects to CN24 (Recovery Connector) on the Btest boards (CN20 on Atest). BtestRecovery.jpg


In addition to connecting the special cable to the Recovery Connector, during recovery you also must install jumpers in both positions of the Recovery Mode Jumper Block. It doesn't matter whether you install the jumpers horizontally or vertically; either orientation will work. If you don't have any jumpers the right size, you can wedge a metal object into the block, connecting all the pins together. A screw about 0.1" in diameter works well. Or wrap a piece of wire around the 4 posts.

Atest jumper

For the Atest board, there is no jumper block. You have to solder a wire across a couple of pads. They are just to the left of the ene chip, near the middle of the left row of pins on that part (need picture).

Connecting the Linux Host to the OLPC Board

Connect a "null modem" serial cable from your Linux host machine to the DB9 connector on the special recovery cable. Typically, the null modem cable will have DB9 female connectors on both ends. If possible, connect it to the host COM port that is associated with /dev/ttyS0 - typically COM1. If your host system doesn't have a built in COM port, you'll need to use a USB-to-serial adapter and figure out the associated device name.

Getting the Software

http://firmworks.com/linux/spirecovery.tgz contains a Linux application to perform the recovery procedure. The archive contains two files: forth and spiflash.dic . The two files work together. forth is a Linux executable file that provides I/O interfaces that let the Forth programming language work under Linux. spiflash.dic is a Forth language workspace (AKA "dictionary") that includes procedures for performing the serial line recovery protocol.

Get that archive and unpack it into a working directory that contains the boot FLASH image file that you want to install in the OLPC board (that file is typically named linuxbios.rom).

Make sure that the "forth" file has execute permissions.

chmod a+x forth

Running the Program

Start the program with

./forth spiflash.dic

It will display a page of instructions describing the various commands, then issue an "ok " prompt.

Here is an example of a typical command sequence for reflashing the entire SPI FLASH, including the EC code and the BIOS.

First make sure that all the connectors are hooked up and the jumpers installed. Connect power to the OLPC board, then do the following:

 ok connect
 Connected to EC, identified SPI FLASH as type 13 - Spansion, Winbond or ST

The 'connect' step should take no time at all. If it fails or hangs, it means that the host system can't talk to the OLPC board. Check the connectors, jumpers, etc, and make sure that you are on the right serial port on the host.

If /dev/ttyS0 is not available on your host system, you can specify a different serial port on the connect line, e.g.

 ok connect /dev/ttyS4

If you have re-issue the "connect" command, like for example you control-C out of the program and restart it, you'll need to power-cycle the OLPC board. The OLPC board will only accept incoming connect attempts after a power-up. If you have already connected to it once, you can't reconnect without power-cycling.

Next,

 ok get-file linuxbios.rom
 Read FLASH image file into memory

This too should happen immediately. Once the image is in memory, it can be used for flashing and verification.

 ok flash-all
 Erasing
 f0000
 Writing
 ff000
 Type verify-all if you want to verify the data just written.
 Verification will take about 17 minutes...

flash-all should take between 1 and 5 minutes, depending on how much unused (set to 0xff) space there is in the image.

Alternatives to flash-all

You can flash just the BIOS part of the SPI FLASH, leaving the EC code untouched, with

 ok flash-bios

Flashing the BIOS section should take between 1 and 5 minutes, depending on how much unused space there is in the image.

You can flash just the EC part of the SPI FLASH, leaving the BIOS code untouched, with

 ok flash-ec

Flashing just the EC part should take less than about 20 seconds.

In all cases, you would use the same kind of image file (e.g. linuxbios.rom), which contains a complete (EC + BIOS) ROM image. The flash-* command will use the correct section or sections of the file.

Verification

Verifying is quite a bit slower than programming, due to the characteristics of the serial protocol coupled with Linux scheduling issues. The net result is that programming goes at about 5K bytes per second (for most parts), while reading (necessary for verification) goes at 1K bytes per second.

So programming all the bytes of a 1 MiB SPI FLASH takes about 3.5 minutes, while reading or verifying them takes 17 minutes. Ouch!

Since verifying is so slow, you might consider not doing it. Most of the time the writing process will work just fine, and you can boot the system and let the running firmware do the verification at memory access speed.

Reading the SPI FLASH

To read the contents of the SPI FLASH and save it to a file:

 ok read-flash myfile.rom

This will read the entire 1 MiB FLASH, which will take about 17 minutes.

Exiting

To exit, type

 ok bye

(or ^C).

Turn off the power to the OLPC board and remove the recovery jumpers.

Serial Protocol

This section is just for the curious (there are lots of curious people on the OLPC project). You don't need to know anything in this section to accomplish the recovery task.

The way the serial protocol works is by letting you read and write, via the serial line, the EC registers than control the SPI FLASH programming. Writing to an EC register requires sending two serial line bytes - a register number and a data byte. Reading an EC register also requires two serial bytes - sending the register number and receiving the data.

For most of the SPI FLASH chip types (Spansion, Winbond, ST), writing is reasonably efficient. After a few bytes of setup, you can transit a stream of 256 consecutive data bytes with two serial bytes per FLASH data byte - a fixed register number followed by the data byte. The SST part's write procedure is a bit more complicated. (The astute reader will no doubt wonder why the EC designers did not improve the protocol so that only one serial byte per FLASH byte is required. Your erstwhile author wonders that too.)

At 115200 baud, you can transfer roughly 10K serial bytes per second, so you can write about 5K FLASH bytes per second (there is also some time for the FLASH part to actually do the writes, but that is fast compared to the serial line).

Reading is not so efficient. The way the EC works, in order to perform an SPI read cycle, you must first issue a "dummy write" cycle then read a register to get the data. The purpose of the dummy write is to generate 8 clock cycles on the SPI bus, during which the FLASH chip sends the data back. The net result is that reading a byte with the serial protocol transfer 4 serial bytes - two sends for the dummy write, and a send and receive for the data read. So reading inherently takes twice as long as writing. It would work out to 2.5 Kbytes per second, and indeed I can achieve that throughput with a host program running "standalone" without an operating system.

But in practice, it's worse than that under Linux, at least on the Linux installation that I have tested it on. For some reason, Linux throttles the serial "write, write, write, read" sequence so I get exactly one iteration of that sequence per millisecond. That works out to reading 1K bytes per second. I've tried many variations of raw serial I/O with different settings for VMIN and VTIME, both blocking and non-blocking, to no avail. Note that it's not practical to read more than one byte with a single read() call, because the "write, write, write" sequence for the next byte can't start until the "read" for the previous byte has finished.