博客首页 | 排行榜 |

设计我最赞的博客

个人档案
博文分类
引导Blackfin(A Bootloader For Blackfin)  2008-09-15 10:31

David designed a two-stage bootloader that allows application firmware to be updated in the field to support bug fixes and additional features for specific end-user applications. It also adds capabilities to the native boot processing of the Blackfin chip. Although some details are specific to the Blackfin family of DSPs, some general features may be helpful on other CPUs.

Not long ago, I was working on an inertial measurement unit (IMU) that was based on the highly integrated ADIS16350 inertial sensor from Analog Devices that Tom Cantrell wrote about in his column in Issue 208("Thanks for the MEMS," 2007)。 This is a six-axis MEMS sensor (three axes of angular rate and three axes of acceleration)in a compact and rugged package. My client wanted to marry an Analog Devices Blackfin DSP chip to it in order to create a self-contained inertial measurement solution.

After a couple of design iterations,we came up with the board shown in Photo 1, which holds the Blackfin processor and various interface and power-supply components. A group of finished units in their boxes is shown in Photo 2. A key aspect of the implementation was that the firmware would need to be updated in the field,after the unit had left the controlled environment of the factory, in order to support both bug fixes to the basic functionality and additional features for specific end-user applications.




This article is about the two-stage bootloader that we developed that meets all our requirements and adds some capabilities to the native boot processing of the Blackfin chip. While much of this discussion will be specific to the Blackfin family of DSP chips,some aspects of it are more general and can be ported to other processors.

BOOTING A BLACKFIN
Booting the application code is a multistage process. When the Blackfin DSP chip receives a hardware reset, it begins executing code at address 0xEF000000, which is the beginning of its on-chip Boot ROM. The Boot ROM examines the external Boot Mode pins, which indicate the source of the application code, which can be an external 8-bit parallel PROM, an external SPI PROM (Blackfin is a SPI master), or another processor connected to the SPI (Blackfin is a SPI slave)。 In our application, the mode pins indicate that the boot device is an external SPI flash memory EEPROM, so the Boot ROM begins loading and processing blocks from that device. Figure 1 shows the key features of the Blackfin that come into play.
点击查看Figure 1

The structure of a loader file consists of a series of blocks of various types. Each block contains a header,and the header contains a target address, a length, and several flags. The flags include IGNORE, FINAL,ZEROFILL, and INIT. The block will also contain data bytes following the header, as long as the length is nonzero and the ZEROFILL flag is not set.

The Boot ROM firmware processes blocks from the external SPI EEPROM one at a time, stopping only when it gets to a block that has its FINAL flag set. After processing that block, the Boot ROM jumps to the address contained in the Blackfin‘s reset event vector, which should point to the cold-start entry point of the newly loaded code. The Boot ROM initializes this register to a default value, but it is possible to modify this register (and other aspects of the state of the Blackfin)

through the use of INIT blocks.

A block that has its IGNORE flag set may or may not contain data bytes (it usually does), but the Boot ROM skips over such blocks altogether-it simply adds the length of the block to its current SPI EEPROM address value and looks for the next block. Such blocks can be used to hold information for a second-stage bootloader program or for the application itself.

Any block that does not have the IGNORE flag set is either a data block or a ZEROFILL block. In the first case,the data associated with the block is copied to the specified target address for the specified length. In the second case, there is no data associated with the block in the EEPROM, but the specified target memory address is filled with bytes of zero for the specified length.


A data block that has its INIT flag set is called an "INIT block," and it must contain Blackfin code that begins execution at its load address. When the Boot ROM encounters such a block, it loads the data bytes into memory starting at the specified target address and then executes a subroutine call to that same address. The INIT block code must finish with a return instruction in order to allow the Boot ROM to continue processing blocks.

BUILDING THE SOFTWARE
Blackfin software is built in the usual way: compile/assemble, link,and load. The result of compiling or assembling source files is a set of object files. The linker is used to combine object files into a single executable image, which is stored in a file that has a .DXE extension. The loader converts one or more .DXE files into a single loader file (。LDR extension) that can be stored in a boot device, and contains the blocks that the Boot ROM will process. Among other things, the loader omits information in the .DXE file that isn‘t needed, such as debugging symbols and other metadata.

The loader includes the important feature that it can combine multiple。DXE files into a single loader file. It does this by inserting an IGNORE block at the beginning of each one. This IGNORE block includes a 4-byte data field that contains the total length of the entire set of blocks that represent that particular .DXE file. If a second-stage bootloader or other software wants to skip over the .DXE image, it can simply read the IGNORE block and then add its value to the EEPROM address pointer, which will then cause it to point to the next item in the EEPROM following the .DXE image.

Furthermore, the loader can optionally mark the code block of the first。DXE file as an INIT block. This means that both the first and second。DXE files in a multi-.DXE loader file will get loaded and executed in sequence by the Boot ROM.

SUPPORTING FIELD UPDATES
In order to support firmware updates in the field in a robust manner,it is necessary to have the capability of storing more than one copy of the application code in the SPI EERPOM-the one currently executing,and the newer one being installed. Until the install process is completed and verified, the DSP will execute the older version on any hardware reset. Furthermore, it is a requirement that the build process for the application is supported by the standard ADI VisualDSP++ development environment (the IDDE GUI) and that the build process for field upgrades be exactly the same as for factory-installed software. As I will show later, the capabilities of the Boot ROM and the build tools outlined previously are sufficient to achieve these requirements without resorting to custom tools in the build chain.

SUPPORTING NEWER EEPROM
There is a second issue related to the specific SPI EEPROM device used on our IMU, which is an Atmel AT45DB081D, a member of their DataFlash family. The Blackfin‘s onchip Boot ROM supports older versions of the DataFlash family, but does not correctly support this newer member(the one with the -D suffix)。 The specific issue is that the -D device now implements the 0x03 "legacy read" command, while the older devices did not. This causes the Boot ROM to identify it as a generic 24-bit addressable SPI flash rather than a DataFlash, and the Boot ROM then assumes that the device has 256 bytes per page. As a result, the Boot ROM will work correctly only if the AT45DB081D is permanently set to its 256-byte Page mode.

In order to fully support the 264-byte Page mode, a second-stage boot kernel is required. This works as long as all of the block headers for the INIT block and the boot kernel itself fit into the first 256 bytes of the DataFlash device. As long as the boot kernel comprises a single code block,it can be arbitrarily long (well, up to 65,534 bytes) and extend beyond the first DataFlash page.

TWO-STAGE BOOT PROCESS
Therefore, we needed to implement a two-stage boot process. This consists of the boot ROM executing, which loads and then executes a second-stage boot kernel. The boot kernel, in turn,loads and executes the actual application firmware.

In order to support multiple copies of the application firmware in EEPROM at the same time, it is necessary to have some storage reserved in the EEPROM to indicate which copy is the "active" copy of the firmware at any given point in time. Within the context of the standard software development toolchain,the only way to accomplish this is to include an INIT block. The INIT block contains just a"return" instruction, along with four additional bytes of space that get initialized to all zeros. When the boot kernel gets control, it examines those 4 bytes to determine the base address of the "active" loader file in the EEPROM. It will then skip the first two。DXE images in this file (the INIT block and the boot kernel itself) and load the third .DXE image, which is the application firmware.

However, there is one additional twist. When the boot kernel is executing,it will be loading the application code, so the boot kernel itself must be located in code memory not needed by the application. There are two ways to achieve this-the boot ROM could load the boot kernel at the default address and then the boot kernel could relocate itself to a different address higher up in memory before continuing. But in many ways, it's simpler to just have the boot kernel load and run at the higher memory address to begin with. This adds the requirement that our INIT block must set the reset vector to that other address-so that the Boot ROM will jump to that address when it finishes loading the boot kernel-and that the boot kernel itself must restore the default reset vector value before it loads the application code. This still leaves open the possibility that the application can have its own INIT block that sets a non-default reset vector value. But constructing a loader file with two (or more) INIT blocks, while possible, would require a non-standard development toolchain.

As a result of all of this, we‘re going to build each application image (。LDR file) as a concatenation of three separate DSP executable programs (。DXE fields)。 The first is our INIT block that performs two functions: It provides a place in the EEPROM to store a pointer to the current application image,and it sets up the environment (a nondefault reset vector value) in which the second-stage boot kernel runs (see Figure 2)。
点击Figure 2

Figure 3 shows the details of the EEPROM layout. As a result of the space taken up by various data structures within the loader file, the code of our INIT block begins at address 0x00018. The first thing located here must be an executable instruction, so we place a jump instruction here (2 bytes)that jumps past the next 4 bytes,which is where we‘re going to store our application pointer. Therefore,the pointer starts at address 0x0001A(decimal 26)。



The code following the pointer sets the Blackfin reset vector to the code memory address where the boot kernel will load and run,and then returns to the Boot ROM. The boot kernel is based on the source code for the Boot ROM, which is supplied by Analog Devices. Unnecessary features, such as support for devices other than the SPI EEPROM,were removed, while new features were added. The new features include support for Atmel DataFlash in either 256-byte Page mode or 264-byte Page mode, and the ability to select multiple loader files based on the pointer stored at offset 26 in the EEPROM. Otherwise, it performs the same basic block-loading functions as the Boot ROM itself.

THE FIRMWARE UPDATE PROCESS
So far, we have not addressed the question of how firmware updates actually get programmed into the EEPROM in the field. This could be implemented as a function within the boot kernel itself, but this would make the boot kernel significantly larger, and would "lock in" the implementation to loading methods that are known at the time the IMU is manufactured. Remember,although every copy of the application code contains a boot kernel,only the one loaded at the factory ever gets executed.

Therefore, we chose to place the firmware that implements the fieldupdate function into the application code itself. The application uses a message-based interface via the Blackfin‘s UART port, and messages specific to the firmware update function have been added to that interface. While this gives us tremendous flexibility to add new functionality to the update function at any time, it also creates the onus that the update function be bug-free-if we ever"break" the field-update function by introducing a bug, then the unit must be disassembled so that the CPU's JTAG interface can be accessed to reprogram the EEPROM from scratch.

Any number of separate loader file images can be stored in the SPI EEPROM,but the present scheme uses just three, with predefined starting addresses of 0x00000, 0x20000, and 0x40000. The image at 0x00000 is always the first one loaded, usually when the IMU is manufactured. This can be accomplished by programming the EEPROM before it is installed on the board, or by using the Blackfin‘s JTAG interface and the loader utility built into the IDDE toolchain.

Updates applied in the field are loaded at 0x20000 or 0x40000, based on a simple "toggle" algorithm. Before beginning a firmware update, the application code examines the value stored at offset 26 (0x0001A)。 If this value is equal to 0x20000, the next update gets loaded at 0x40000. Otherwise(the current value is either 0x00000 or 0x40000), the next update gets loaded at 0x20000.

The firmware update message protocol includes several layers of error checking and recovery. Each data message is protected by a checksum,and once the entire loader file has been transmitted, a checksum over the entire image is separately calculated. If any errors are encountered,the entire process can be restarted without any risk to the system.

When a firmware update is completed,the last thing that happens is that locations 26-29 in the EEPROM are updated to point to the new image. This operation is the only step in the entire process during which a problem (e.g., a power interruption)could leave the IMU in a nonfunctional state. Once this step is complete,the next reboot of the IMU will load the new application code. Note also that regardless of the value of this pointer, the INIT block and the boot kernel that are used are always the ones starting at address 0x00000,because this is hard-coded into the Boot ROM. The only way to update the INIT block or the second-stage boot kernel is to use the JTAG interface to store a new loader file at offset 0x00000 in the EEPROM.

When the second-stage boot kernel finally executes, it examines locations 26-29 in the EEPROM in order to get a pointer to the ※current§ application image. This pointer contains the address of the beginning of the image,including the INIT block and the boot kernel itself, so at this point, the boot kernel must skip these items (using the information in the IGNORE blocks) before it can begin loading the application.

START YOUR DESIGN
The archive for this article on the Circuit Cellar FTP site includes the source code for the original Blackfin Boot ROM and our boot kernel for comparison. It also includes the INIT block. It does not include the application code for the firmware update function,but if you would like assistance in this area, feel free to get in touch with me.

We have found this system to work very well in practice, and an OEM who is using the IMU in a larger embedded system has successfully implemented the firmware update protocol in their host CPU. Whenever they need new functionality in the IMU, we can e-mail them an updated loader file and they can install it without disassembling the system in any way.

While many of the details discussed here are specific to the Blackfin bootprocess,I hope that the more general concepts will be found useful on other CPUs as well. I want to thank Robert Pinto and Enpoint, LLC, for their support and the permission to tell you about this aspect of their product.

David Tweed (dtweed@acm.org) is a hardware and real-time firmware engineering consultant who has been working with embedded processors starting in 1976 with the Intel 8008. His system design experience includes computer design from supercomputers to workstations,digital telecommunications systems, and the application of embedded microcomputers and DSPs. He is also a Circuit Cellar project editor and quiz master. When not playing with electronics and software, he pursues his hobby as an amateur musician, playing keyboards and low brass instruments in several community groups.

PROJECT FILES
To download code, go to ftp://ftp.circuitcellar.com/pub/Circuit_Cellar/2008/217.

RESOURCE
Enpoint, "GPS/Inertial Solutions,"
www.enpoint.com.

SOURCES
ADIS16350 Inertial sensor and BlackfinDSP
Analog Devices, Inc.
www.analog.com

AT45DB081D DataFlash
Atmel Corp.
www.atmel.com
类别:嵌入式开发 |
上一篇:自己动手制作运动控制游戏(Do-It-Yourself Motion-Controlled Gaming (Part 1)) | 下一篇:电子数据记录与分析(Electronic Data Logging And Analysis)
以下网友评论只代表其个人观点,不代表本网站的观点或立场