<< Previous | Index | Next >>

7. The System Identification and User Blocks

The BIOS supports a System Identification block and a User block. These blocks are placed at the top of the primary flash memory. Identification information for each device can be placed in the System ID block for access by the BIOS, flash driver, and users. This block contains specific part numbers for the flash and RAM devices installed, the product's serial number, Media Access Control (MAC) address if an Ethernet device, and so on. The earliest version of the System ID for Rabbit 4000 products is version 4, which is a mirrored images type.

When mirrored, there are two combined ID/User blocks images placed contiguously at the top of the primary flash, from the top down as follows: ID "A" + User "A" + ID "B" + User "B." Ordinarily, only one of the ID/User blocks images is valid at a time, and the valid ID/User blocks image alternates between "A" and "B" at each call to the writeUserBlock() function. If both "A" and "B" images are simultaneously marked valid, the "A" (topmost) image is taken to be correct. Version 5 ID blocks can be configured as described above and can also be configured so that the User block is mirrored and the System ID block is not. If a version 5 ID block is configured so that only the User block is mirrored, the images will be ID + User "A" + User "B".

If Dynamic C does not find a System ID block on a device, the compiler will assume that it is a BL1810 (Jackrabbit) board. It is recommended that board designers include System ID blocks in their products with unused fields zeroed out to maximize future compatibility.

The System ID block has information about the location of the User block. The User block is for storage of calibration constants and other persistent data the user wishes to keep in flash. It is strongly recommended that the User block (using writeUserBlock()) or the Flash File System be used for storage of persistent data. Writing to arbitrary flash addresses at run-time is possible using WriteFlash() or WriteFlash2(), but could lead to compatibility problems if the code were to be used on a different type of flash, such as a huge, non-uniform sector size flash.

For example, some flash types have a single sector as big as 128K bytes at the bottom. Writing to any part of the sector generally requires erasing the whole sector, so a write to store data in that sector would have to save the contents of the whole sector in RAM, modify the section to be changed, and write the whole sector back. This is obviously impractical. Although Rabbit does not currently sell products with this type of flash, there is no guarantee that future flash market conditions won't require that such flash types be used. Other board designers may have to deal with the same flash market issues. The User block is implemented in a way that preserves forward binary compatibility with a wide range of flash devices.

7.1 System ID Block Details

The BIOS will read the System ID block during startup. If the BIOS does not find an ID block, it sets all fields to zero in the data structure SysIDBlock. The user may access the information contained in the System ID block by accessing SysIDBlock.

7.1.1 Definition of SysIDBlock

The following global data structures are defined in IDBLOCK.LIB and are loaded from the flash device during BIOS startup. Users can access this struct in RAM if they need information from it. The reserved[] field will expand and/or shrink to compensate for the change in size. Items marked `**' are essential for proper functioning of the System ID block and certain features (e.g., TCP/IP needs the MAC address). Items marked `*' are desirable for future compatibility.

     
typedef struct _SysIDBlockType2 {
uint8 flashMBC; //
Memory Bank Configurations
uint8 flash2MBC;
uint8 ramMBC;
uint32 devSpecLoc;    //
Count of additional memory devices immediately
                           //   preceding this block
uint32 macrosLoc;       // Start of the macro table for additional board
                           //   configuration options.
uint32 driversLoc;      // offset to preloaded drivers start from ID block
                           //    start (positive is below ID block)
uint32 ioDescLoc;       // offset to I/O descriptions start from ID block
                           //   start (positive is below ID block)
uint32 ioPermLoc;       // offset to User mode I/O permissions start from ID
                           //   block start (positive is below ID block)
uint32 persBlockLoc;    // offset to persistent storage block area start from
                           //   ID block start (positive is below ID block)
uint16 userBlockSiz2;   // size of v. 5 "new style" mirrored User block image
uint16 idBlockCRC2;     // CRC of SysIDBlockType2 type with idBlockCRC2
                           //   member reset to zero and base CRC value of
                           //   SysIDBlock.idBlockCRC
} SysIDBlockType2;
typedef struct {
int tableVersion;      //
version number for this table layout**
int productID;         //
Rabbit part #
int vendorID;          // 1 = Rabbit
char timestamp[7];      // YY/M/D H:M:S
long flashID;           // Manufacturer ID/ Product ID, 1st flash *
int flashType;         //
Write method
int flashSize;         // in 1000h pages
int sectorSize;        // size of flash sector in bytes
int numSectors;        // number of sectors
int flashSpeed;        // in nanoseconds *
long flash2ID;          //
Manufacturer ID/ Product ID, 2nd flash *
int flash2Type;        //
Write method, 2nd flash
int flash2Size;        // in 1000h pages, 2nd flash
int sector2Size;       // byte size of 2nd flash's sectors
int num2Sectors;       //
number of sectors
int flash2Speed;       // in nanoseconds, 2nd flash *
long ramID;             //
Rabbit part #
int ramSize;           // in 1000h pages *
int ramSpeed;          //
in nanoseconds *
int cpuID;             //
CPU type ID *
long crystalFreq;       //
in Hertz *
char macAddr[6];        //
Media Access Control (MAC) address **
char serialNumber[24];  //
device serial number
char productName[30];   // NULL-terminated string
// Begin new version 5 System ID block member structure.
SysIDBlockType2 idBlock2; // idblock
// End new version 5 System ID block member structure.
char reserved[1];       // reserved for later use - size can grow
long idBlockSize;       // number of bytes in the SysIDBlock struct **
unsigned userBlockSize; //
User block size, in bytes (right below ID block)**
unsigned userBlockLoc;  //
offset in bytes of start of User block from this one**
int idBlockCRC;        //
CRC of this block (when this field is set to 0) **
char marker[6];        //
should be 0x55 0xAA 0x55 0xAA 0x55 0xAA**
} SysIDBlock;

7.1.2 Reading the System ID Block

To read the ID block from the flash instead of getting the information from SysIDBlock, call _readIDBlock().


_readIDBlock


     
int _readIDBlock(int flash_bitmap);

Description:

Attempts to read the system ID block from the highest flash quadrant and save it in the system ID block structure. It performs a CRC check on the block to verify that the block is valid. If an error occurs, SysIDBlock.tableVersion is set to zero.

This function supports combined System ID/User blocks sizes of sizeof(SysIDBlock) and from 4KB to 64KB, inclusive, in 4KB steps. Prior versions of Dynamic C only supported mirrored combined block sizes of sizeof(SysIDBlock), 8KB, 16KB and 24KB or unmirrored combined System ID/User blocks sizes of sizeof(SysIDBlock) and from 4KB to 32KB, inclusive, in 4KB steps.

Parameter

flash_bitmap

Bitmap of memory quadrants mapped to primary flash.

Examples:
    0x01 = quadrant 0 only
    0x03 = quadrants 0 and 1
    0x0C = quadrants 2 and 3

Return value:

 0: Successful
-1: Error reading from flash
-2: ID block missing
-3: ID block invalid (failed CRC check)

Library

IDBLOCK.LIB

7.1.2.1 Determining the Existence of the System ID Block

In Dynamic C versions prior to 7.20, and for ID block versions 1 and 2, the following sequence of events is used by _readIDBlock() to determine if an ID block is present:

  1. The 16 bytes at the top of the primary flash are read into a local buffer. (If a 256 KB flash is installed, the 16 bytes starting at address 0x3FFF0 will be read.)

  2. The last six bytes of the local buffer are checked for an alternating sequence of 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA. If this is not found, the block does not exist and an error (-2) is returned.

  3. The ID block size (=SIZE) is determined from the first 4 bytes of the 16-byte buffer.

  4. A block of bytes containing all fields from the start of the SysIDBlock struct up to but not including the reserved field is read from flash at address 0x40000-SIZE, essentially filling the SysIDBlock struct except for the reserved field (since the top 16 bytes have been read earlier).

  5. The CRC field is saved in a local variable, then set to 0x0000. A CRC check is then calculated for the entire ID block except the reserved field and compared to the saved value. If they do not match, the block is considered invalid and an error (-3) is returned. The CRC field is then restored. The reserved field is avoided in the CRC check since its size may vary, depending on the size of the ID block.

Determining the existence of a valid mirrored ID block may be slightly more complicated, requiring the above sequence of events to be repeated at several locations below the top of the primary flash. See Figure 7.2 below for complete details.

Not all fields are filled in different versions of the ID block. The table below lists the first ID block version that filled each field and whether that field is absolutely required by Dynamic C for normal operation. (Much of the ID block data is useful, but not critical.)

Table 7-1 The System ID Block 
Offset from start of block
Size (bytes)
Description
Filled as of Version
Required
00h
2
ID block version number
1
x
02h
2
Product ID
1
x
04h
2
Vendor ID
2
06h
7
Timestamp (YY/MM/D/H/M/S)
1
0Dh
4
Flash ID
2
11h
2
Flash size (in 1000h pages)
2
13h
2
Flash sector size (in bytes)
2
15h
2
Number of sectors in flash
2
17h
2
Flash access time (nanoseconds)
4
19h
4
Flash ID, 2nd flash
2
1Dh
2
Flash size (in 1000h pages), 2nd flash
2
1Fh
2
Flash sector size, 2nd flash (in bytes)
2
21h
2
Number of sectors in 2nd flash
2
23h
2
Flash access time, in nanoseconds, for the 2nd flash
4
25h
4
RAM ID
2
29h
2
RAM size, in 1000h pages
2
2Bh
2
RAM access time, in nanoseconds
4
2Dh
2
CPU ID
3
2Fh
4
Crystal frequency (Hertz)
2
33h
6
Media Access Control (MAC) address
1
x
39h
24
Serial number (as a null-terminated string)
51h
30
Product name (as a null-terminated string)
6Fh
27
Version 5 System ID block member structure (SysIDBlockType2)
5
8Ah
N
Reserved (variable size)
SIZE - 10h
4
Size of System ID block, in bytes
1
x
SIZE - 0Ch
2
Size of User block, in bytes
1
x
SIZE - 0Ah
2
Offset, in bytes, of User block location from start of this block
1
x
SIZE - 08h
2
CRC value of System ID block (when this field = 0000h)
1
x
SIZE - 06h
6
Marker, should = 55h AAh 55h AAh 55h AAh
1
x

7.1.3 Writing the System ID Block

The WriteFlash() function does not allow writing to the System ID block. If the System ID block needs to be rewritten, a utility to do so is available for download from the Rabbit website:

     
www.rabbit.com/support/downloads/downloads_feat.shtml

or contact Rabbit's Technical Support.

7.2 User Block Details

Starting with the System ID block version 3, two contiguous copies of the combined ID/User blocks are used, or in the case of the version 5 ID block, two contiguous copies of the User block are used. Only one image contains "valid" data at any time. When data is written to a mirrored User block, the currently invalid User block image is updated first and then validated by changing its marker[5] byte from 0x00 to 0xAA. This marker is located in the user block itself in version 5 ID blocks where the mirrored user blocks are separate from the System ID Block, and in version 5 and prior ID blocks where the User block and System ID blocks are combined, the marker byte is located in the System ID Block. Next, the previously valid image is invalidated by changing its marker[5] byte from 0xAA to 0x00. Finally, the newly invalidated image is updated. In this way, there is only a short period of time in which both images are marked valid, and at no time are both data blocks marked invalid. If a power failure occurs at any time during the User block update, the BIOS will still find a valid ID block and the valid User block will contain data from the last completed update transaction. In addition to making data more secure, this redundancy allows even very large sector flash types to be used without requiring a large RAM buffer to temporarily store the contents of a sector, since sectors must be erased before they can be written.

In Dynamic C 7.20 and later, the possibility of mirrored combined ID/User blocks requires that multiple locations in flash must be checked for a valid ID block. In versions 7.20 through 7.3x, the sequence described above in Section 7.1.2.1 is used to check not only the top of the primary flash, but also 8KB, 16KB and 24KB below the top, and an error is returned only if no valid ID block is found at any of these locations. Note the implication here that mirrored combined ID/User blocks are limited to one of 8KB, 16KB, or 24KB in size. Dynamic C versions 8 and later check more locations in flash, from the top down, at each lower 4KB boundary to 64KB below the top. This allows Dynamic C 8 and up to recognize a combined ID/User blocks size that is any multiple of 4KB up to a maximum of 64KB.

If the version of the System ID block doesn't support the User block, or no System ID block is present, then the 8 KB starting 16 KB from the top of the primary flash are designated the User block area. However, to prevent errors arising from incompatible large sector configurations, this will only work if the flash type is small sector. Rabbit manufactured boards with large sector flash will have valid System ID and User blocks, so this should not be a problem on Rabbit-based boards.

7.2.1 Boot Block Issues

The System ID and User block implementations have been designed to accommodate huge, non-uniform sector flash types, but it is necessary to use `T' type parts with such flash types so that the smaller boot block sectors at the top can be used for the blocks. `B' parts have smaller boot block sectors at the bottom.

No code is included with Dynamic C to lock boot blocks, and users should not lock boot blocks unless they are sure they will never write to the blocks after the System ID block is written. If a boot block lock is irreversible, we strongly recommend never locking it.

7.2.2 Reserved Flash Space

The macro MAX_USERBLOCK_SIZE (default 0x8000) in the BIOS tells the Dynamic C compiler how much flash at the top of the primary flash is excluded from use by the compiler for xmem functions. For any application, whether compiled to a single target board or for multiple target boards, the MAX_USERBLOCK_SIZE macro value defined in RabbitBios.c must not be lower than the amount of flash required for the System ID/User blocks on the target board with the largest requirement. Note that in the case of mirrored combined ID/User blocks (version 3 and up), the amount of flash that must be reserved is double the size of one combined ID/User block image. For example, if a target board has mirrored combined ID/User blocks and the size of one image is 16 KB (0x4000 bytes), then the minimum value defined for the MAX_USERBLOCK_SIZE macro is 32 KB (0x8000 bytes).

If the MAX_USERBLOCK_SIZE macro value is less than the actual size used for a target board's ID/User blocks, both Dynamic C and the RFU will fail to load to flash the part of an application that extends into the ID/User blocks area. This is a limitation of the pilot BIOS to allow the RFU to write a large USE_2NDFLASH_CODE bin file to a two-flash board (the pilot BIOS does the flash writing on behalf of both Dynamic C and the RFU).

All of the default MAX_USERBLOCK_SIZE reserved space is not necessarily needed by the System ID and User blocks, but reserving this much space maximizes forward binary compatibility should a product switch to any of various huge, non-uniform sector flash types. Some of these types have sectors of 8 KB, 8 KB and 16 KB at the top, and the mirrored design of the User block requires that these 3 sectors be used. If you do not need the User block and are not concerned with forward binary compatibility, the MAX_USERBLOCK_SIZE macro value could be safely lowered (protecting the sector containing the ID block) to as little as 0x4000 (16 KB), but only if the System ID block is rewritten to set the User block size to zero (i.e., no run-time flash writes can occur, such as to the User block or to a flash file system).

Reducing the MAX_USERBLOCK_SIZE macro value will only increase available xmem code space, not root code space which is generally in shorter supply. To increase available xmem code space, the following general procedure should be followed:

  1. Determine that binary forward compatibility with large sector flash types as described above is not an issue. This means that the application will only ever run on target boards equipped with small sector flash (i.e., uniform sectors of a size no larger than 4 KB).

  2. Determine the application's minimum User block size requirement. If the application does not write to the User block, this size is zero.

  3. If the target board has factory calibration constants stored in the User block, add the size reserved for these constants. Consult your hardware manual for the reserved size required.

  4. Add the size of the System ID block, which is 132 bytes for versions 2 through 4.

  5. Round this total size up to the next higher 4 KB block boundary.

  6. If using mirrored combined (version 3 or 4) ID/User blocks, double the size.

  7. Calculate the number of 4 KB blocks required for the total size.

  8. Edit the write_idblock.c utility to set the required number of 4 KB blocks, and write a new ID block onto the target board.

  9. Repeat the previous steps for every board which is to be programmed with the application(s) compiled using the updated MAX_USERBLOCK_SIZE macro value.

  10. Edit the RabbitBios.c file to update the MAX_USERBLOCK_SIZE macro value.
Note that it is especially difficult to effectively reduce the MAX_USERBLOCK_SIZE macro value below 0x4000 (16 KB) for the BL20xx or BL21xx board families, which have their combined ID/User blocks size hard-coded in the FLASHWR.LIB and IDBLOCK.LIB libraries because their stored calibration constants are in a nonstandard place. For this reason, Rabbit strongly recommends not attempting to make System ID/User block changes on these board families.

7.2.3 Reading the User Block


readUserBlock


     
int readUserBlock(void *dest, unsigned addr, unsigned numbytes);

Description:

Reads a number of bytes from the User block on the primary flash to a buffer in root memory.

NOTE: portions of the User block may be used by the BIOS for your board to store values such as calibration constants. See the manual for your particular board for more information before overwriting any part of the User block.

Parameters

dest

Pointer to destination to copy data to.

addr

Address offset in User block to read from.

numbytes

Number of bytes to copy.

Return value

 0: Successful
-1: Invalid address or range
-2: No valid System ID block found

Library

IDBLOCK.LIB


readUserBlockArray


     
int readUserBlockArray(void *dests[], unsigned numbytes[], int numdests, unsigned addr);

Description

Reads a number of bytes from the User block on the primary flash to a set of buffers in root memory. This function is usually used as the inverse function of writeUserBlockArray().

Parameters

dests

Pointer to array of destinations to copy data to.

numbytes

Array of numbers of bytes to be written to each destination.

numdests

Number of destinations.

addr

Address offset in User block to read from.

Return value

 0: Success
-1: Invalid address or range
-2: No valid System ID block found (block version 3 or later)

Library

IDBLOCK.LIB

7.2.4 Writing the User Block


writeUserBlock


     
int writeUserBlock(unsigned addr, void *source, unsigned numbytes);

Description:

Rabbit-based boards are released with System ID blocks located on the primary flash. Version 2 and later of this ID block has a pointer to a User block that can be used for storing calibration constants, passwords, and other non-volatile data. This block is protected from normal writes to the flash device and can only be accessed through this function. This function writes a number of bytes from root memory to the User block

Backwards Compatibility:

If the version of the System ID block doesn't support the User block, or no System ID block is present, then the 8 KB starting 16 KB from the top of the primary flash are designated the User block area. However, to prevent errors arising from incompatible large sector configurations, this will only work if the flash type is small sector. Rabbit manufactured boards with large sector flash will have valid System and User ID blocks, so this should not be problem on Rabbit-based boards.

If users create boards with large sector flash, they must install System ID block version 3 or greater to use this function, or modify this function.

NOTE: Portions of the User block may be used by the BIOS for your board to store values such as calibration constants! See the manual for your particular board for more information before overwriting any part of the User block.

Parameters

addr

Address offset in User block to write to.

source

Pointer to destination to copy data from.

numbytes

Number of bytes to copy.

Return value

 0: Successful
-1: Invalid address or range
-2: No valid User block found (block version 3 or later)
-3: Flash writing error

Library

IDBLOCK.LIB


writeUserBlockArray


     
int writeUserBlockArray(unsigned addr, void* sources[], unsigned numbytes[], int numsources);

Description

Rabbit-based boards are released with System ID blocks located on the primary flash. Version 2 and later of this ID block has a pointer to a User block that can be used for storing calibration constants, passwords, and other non-volatile data. The User block is protected from normal write to the flash device and can only be accessed through this function or writeUserBlock().

This function writes a set of scattered data from root memory to the User block. If the data to be written is in contiguous bytes, using the function writeUserBlock()is sufficient. Use of writeUserBlockArray() is recommended when the data to be written is in noncontiguous bytes, as may be the case for something like network configuration data. See the Rabbit Microprocessor Designer's Handbook for more information about the System ID and User blocks.

Backwards Compatibility:

If the System ID block on the board doesn't support the User block, or no System ID block is present, then the 8K bytes starting 16K bytes from the top of the primary flash are designated User block area. This only works if the flash type is small sector. Rabbit manufactured boards with large sector flash will have valid System ID and User blocks, so is not a problem on Rabbit-based boards. If users create boards with large sector flash, they must install System ID blocks version 3 or greater to use this function, or modify this function.

Parameters

addr

Address offset in the User block to write to.

sources

Array of pointer to sources to copy data from.

numbytes

Array of number of bytes to copy for each source. The sum of the lengths in this array must not exceed 32767 bytes, or an error will be returned.

numsources

Number of data sources.

Return value

 0: Successful.
-1: Invalid address or range.
-2: No valid User block found (block version 3 or later).
-3: Flash writing error.

Library

IDBLOCK.LIB


Rabbit 4000
Designer's Handbook
<< Previous | Index | Next>> rabbit.com