FLASH Library The eCos FLASH Library The FLASH library is an optional part of eCos, and is only applicable to some platforms. The eCos FLASH library provides the following functionality: Identifying installed device of a FLASH family. Read, erasing and writing to FLASH blocks. Validating an address is within the FLASH. Determining the number and size of FLASH blocks. There are two APIs with the flash library. The old API is retained for backwards compatibility reasons, but should slowly be replaced with the new API which is much more flexible and does not pollute the name space as much. Notes on using the FLASH library FLASH devices cannot be read from when an erase or write operation is active. This means it is not possible to execute code from flash while an erase or write operation is active. It is possible to use the library when the executable image is resident in FLASH. The low level drivers are written such that the linker places the functions that actually manipulate the flash into RAM. However the library may not be interrupt safe. An interrupt must not cause execution of code that is resident in FLASH. This may be the image itself, or RedBoot. In some configurations of eCos, ^C on the serial port or debugging via Ethernet may cause an interrupt handler to call RedBoot. If RedBoot is resident in FLASH this will cause a crash. Similarly, if another thread invokes a virtual vector function to access RedBoot, eg to perform a diag_printf() a crash could result. Thus with a ROM based image or a ROM based Redboot it is recommended to disable interrupts while erasing or programming flash. Using both a ROMRAM or RAM images and a ROMRAM or RAM RedBoot are safe and there is no need to disable interrupts. Similarly, Danger, Will Robinson! Danger! Unlike nearly every other aspect of embedded system programming, getting it wrong with FLASH devices can render your target system useless. Most targets have a boot loader in the FLASH. Without this boot loader the target will obviously not boot. So before starting to play with this library its worth investigating a few things. How do you recover your target if you delete the boot loader? Do you have the necessary JTAG cable? Or is specialist hardware needed? Is it even possible to recover the target boards or must it be thrown into the rubbish bin? How does killing the board affect your project schedule? The Version 2 eCos FLASH API There are two APIs described here. The first is the application API which programs should use. The second API is that between the FLASH IO library and the device drivers. FLASH user API All of the functions described below are declared in the header file <cyg/io/flash.h.h> which all users of the FLASH library should include. Initializing the FLASH library The FLASH library needs to be initialized before other FLASH operations can be performed. This only needs to be done once. The following function will only do the initialization once so it's safe to call multiple times: __externC int cyg_flash_init(const cyg_flash_printf *pf); typedef int cyg_flash__printf(const char *fmt, ...); The parameter pf is a pointer to a function which is to be used for diagnostic output. Typically the function diag_printf() will be passed. Normally this function is not used by the higher layer of the library unless CYGSEM_IO_FLASH_CHATTER is enabled. Passing a NULL is not recommended, even when CYGSEM_IO_FLASH_CHATTER is disabled. The lower layers of the library may unconditionally call this function, especially when errors occur, probably resulting in a more serious error/crash!. Retrieving information about FLASH devices The following five functions return information about the FLASH. __externC int cyg_flash_get_info(cyg_uint32 devno, cyg_flash_info_t * info); __externC int cyg_flash_get_info_addr(cyg_flashaddr_t flash_base, cyg_flash_info_t * info); __externC int cyg_flash_verify_addr(const flashaddr_t address); __extern size_t cyg_flash_block_size(const cyg_flashaddr_t flash_base); typedef struct cyg_flash_block_info size_t block_size; cyg_unit32 blocks; } cyg_flash_block_info_t; typedef struct { flashaddr_t start; // First address flashaddr_t end; // Last address cyg_uint32 num_block_infos // Number of entries const cyg_flash_block_info_t *blocks_info; // Info about one block size } cyg_flash_info_t; cyg_flash_get_info() is the main function to get information about installed flash devices. Parameter devno is used to iterate over the available flash devices, starting from 0. If the devno'th device exists, the structure pointed to by info is filled in and CYG_FLASH_ERR_OK is returned, otherwise CYG_FLASH_ERR_INVALID. cyg_flash_get_info() if similar, but returns the information about the flash device at the given address. cyg_flash_block_size() returns the size of the block at the given address. cyg_flash_verify_addr() tests if the target addresses is within one of the FLASH devices, returning CYG_FLASH_ERR_OK if so. Reading from FLASH There are two methods for reading from FLASH. The first is to use the following function. __externC int cyg_flash_read(flashaddr_t *flash_base, const void *ram_base, const size_t len, flashaddr_t **err_address); flash_base is where in the flash to read from. ram_base indicates where the data read from flash should be placed into RAM. len is the number of bytes to be read from the FLASH and err_address is used to return the location in FLASH that any error occurred while reading. The second method is to simply memcpy() directly from the FLASH. This is not recommended since some types of device cannot be read in this way, eg NAND FLASH. Using the FLASH library function to read the FLASH will always work so making it easy to port code from one FLASH device to another. Erasing areas of FLASH Blocks of FLASH can be erased using the following function: __externC int cyg_flash_erase(flashaddr_t *flash_base, const size_t len, flashaddr_t **err_address); flash_base is where in the flash to erase from. len is the minimum number of bytes to erase in the FLASH and err_address is used to return the location in FLASH that any error occurred while erasing. It should be noted that FLASH devices are block oriented when erasing. It is not possible to erase a few bytes within a block, the whole block will be erased. flash_base may be anywhere within the first block to be erased and flash_base+len maybe anywhere in the last block to be erased. Programming the FLASH Programming of the flash is achieved using the following function. __externC int cyg_flash_program(const flashaddr_t *flash_base, void *ram_base, const size_t len, flashaddr_t **err_address); flash_base is where in the flash to program from. ram_base indicates where the data to be programmed into FLASH should be read from in RAM. len is the number of bytes to be program into the FLASH and err_address is used to return the location in FLASH that any error occurred while programming. Locking and unlocking blocks Some flash devices have the ability to lock and unlock blocks. A locked block cannot be erased or programmed without it first being unlocked. For devices which support this feature and when CYGHWR_IO_FLASH_BLOCK_LOCKING is enabled then the following two functions are available: __externC int cyg_flash_lock(const flashaddr_t *flash_base, const size_t len, flashaddr_t **err_address); __externC int cyg_flash_unlock(const flashaddr_t *flash_base, const size_t len, flashaddr_t **err_address); Locking FLASH Mutex's When the eCos kernel package is included in the eCos configuration, the FLASH IO library will perform mutex locking on FLASH operations. This makes the API defined here thread safe. However applications may wish to directly access the contents of the FLASH. In order for this to be thread safe it is necessary for the application to use the following two functions to inform the FLASH IO library that the FLASH devices are being used and other API calls should be blocked. __externC int cyg_flash_mutex_lock(const flashaddr_t *from, const size_t len); __externC int cyg_flash_mutex_unlock(const flashaddr_t *from, const size_t len); Return values and errors All the functions above return one of the following return values. CYG_FLASH_ERR_OK No error - operation complete CYG_FLASH_ERR_INVALID Invalid FLASH address CYG_FLASH_ERR_ERASE Error trying to erase CYG_FLASH_ERR_LOCK Error trying to lock/unlock CYG_FLASH_ERR_PROGRAM Error trying to program CYG_FLASH_ERR_PROTOCOL Generic error CYG_FLASH_ERR_PROTECT Device/region is write-protected CYG_FLASH_ERR_NOT_INIT FLASH info not yet initialized CYG_FLASH_ERR_HWR Hardware (configuration?) problem CYG_FLASH_ERR_ERASE_SUSPEND Device is in erase suspend mode CYG_FLASH_ERR_PROGRAM_SUSPEND Device is in program suspend mode CYG_FLASH_ERR_DRV_VERIFY Driver failed to verify data CYG_FLASH_ERR_DRV_TIMEOUT Driver timed out waiting for device CYG_FLASH_ERR_DRV_WRONG_PART Driver does not support device CYG_FLASH_ERR_LOW_VOLTAGE Not enough juice to complete job To turn an error code into a human readable string the following function can be used: __externC const char *cyg_flash_errmsg(const int err); FLASH device API This section describes the API between the FLASH IO library the FLASH device drivers. The FLASH device Structure This structure keeps all the information about a single driver. struct cyg_flash_dev { struct cyg_flash_dev_funs *funs; // Function pointers flashaddr_t *start; // First address flashaddr_t *end; // Last address void *priv; // Devices private data cyg_uint32 num_block_infos; // Number of entries cyg_flash_block_info_t *blocks_info; // Info about one block size // The following are only written to by the FLASH IO layer. cyg_flash_printf *pf; // Pointer to diagnostic printf cyg_bool init; // Device has been initialized #ifdef CYG_KERNEL cyg_mutex_t mutex; // Mutex for thread safeness #endif struct cyg_flash_dev *next; // Pointer to next device } struct cyg_flash_dev_funs { int (*flash_init) (struct cyg_flash_dev *dev); size_t (*flash_query) (struct cyg_flash_dev *dev, void * data, const size_t len); int (*flash_erase_block) (struct cyg_flash_dev *dev, const flashaddr_t *block_base); int (*flash_program) (struct cyg_flash_dev *dev, flashaddr_t *base, const void* data, const size_t len); int (*flash_read) (struct cyg_flash_dev *dev, const flashaddr_t *base, void* data, const size_t len); int (*flash_hwr_map_error) (struct cyg_flash_dev *dev, int err); #ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING int (*flash_block_lock) (struct cyg_flash_dev *dev, const flashaddr_t *block_base); int (*flash_block_unlock) (struct cyg_flash_dev *dev, const flashaddr_t *block_base); #endif } The FLASH IO layer will only pass requests for operations on a single block. The legacy Version 1 eCos FLASH API The library has a number of limitations: Only one family of FLASH device may be supported at once. Multiple devices of one family are supported, but they must be contiguous in memory. The library is not thread or interrupt safe under some conditions. The library currently does not use the eCos naming convention for its functions. This may change in the future but backward compatibility is likely to be kept. There are two APIs described here. The first is the application API which programs should use. The second API is that between the FLASH io library and the device drivers. FLASH user API All of the functions described below are declared in the header file <cyg/io/flash.h.h> which all users of the FLASH library should include. Initializing the FLASH library The FLASH library needs to be initialized before other FLASH operations can be performed. This only needs to be done once. The following function will only do the initialization once so it's safe to call multiple times: externC int flash_init( _printf *pf ); typedef int _printf(const char *fmt, ...); The parameter pf is a pointer to a function which is to be used for diagnostic output. Typically the function diag_printf() will be passed. Normally this function is not used by the higher layer of the library unless CYGSEM_IO_FLASH_CHATTER is enabled. Passing a NULL is not recommended, even when CYGSEM_IO_FLASH_CHATTER is disabled. The lower layers of the library may unconditionally call this function, especially when errors occur, probably resulting in a more serious error/crash!. Retrieving information about the FLASH The following four functions return information about the FLASH. externC int flash_get_block_info(int *block_size, int *blocks); externC int flash_get_limits(void *target, void **start, void **end); externC int flash_verify_addr(void *target); externC bool flash_code_overlaps(void *start, void *end); The function flash_get_block_info() returns the size and number of blocks. When the device has a mixture of block sizes, the size of the "normal" block will be returned. Please read the source code to determine exactly what this means. flash_get_limits() returns the lower and upper memory address the FLASH occupies. The target parameter is current unused. flash_verify_addr() tests if the target addresses is within the flash, returning FLASH_ERR_OK if so. Lastly, flash_code_overlaps() checks if the executing code is resident in the section of flash indicated by start and end. If this function returns true, erase and program operations within this range are very likely to cause the target to crash and burn horribly. Note the FLASH library does allow you to shoot yourself in the foot in this way. Reading from FLASH There are two methods for reading from FLASH. The first is to use the following function. externC int flash_read(void *flash_base, void *ram_base, int len, void **err_address); flash_base is where in the flash to read from. ram_base indicates where the data read from flash should be placed into RAM. len is the number of bytes to be read from the FLASH and err_address is used to return the location in FLASH that any error occurred while reading. The second method is to simply memcpy() directly from the FLASH. This is not recommended since some types of device cannot be read in this way, eg NAND FLASH. Using the FLASH library function to read the FLASH will always work so making it easy to port code from one FLASH device to another. Erasing areas of FLASH Blocks of FLASH can be erased using the following function: externC int flash_erase(void *flash_base, int len, void **err_address); flash_base is where in the flash to erase from. len is the minimum number of bytes to erase in the FLASH and err_address is used to return the location in FLASH that any error occurred while erasing. It should be noted that FLASH devices are block oriented when erasing. It is not possible to erase a few bytes within a block, the whole block will be erased. flash_base may be anywhere within the first block to be erased and flash_base+len maybe anywhere in the last block to be erased. Programming the FLASH Programming of the flash is achieved using the following function. externC int flash_program(void *flash_base, void *ram_base, int len, void **err_address); flash_base is where in the flash to program from. ram_base indicates where the data to be programmed into FLASH should be read from in RAM. len is the number of bytes to be program into the FLASH and err_address is used to return the location in FLASH that any error occurred while programming. Locking and unlocking blocks Some flash devices have the ability to lock and unlock blocks. A locked block cannot be erased or programmed without it first being unlocked. For devices which support this feature and when CYGHWR_IO_FLASH_BLOCK_LOCKING is enabled then the following two functions are available: externC int flash_lock(void *flash_base, int len, void **err_address); externC int flash_unlock(void *flash_base, int len, void **err_address); Return values and errors All the functions above, except flash_code_overlaps() return one of the following return values. FLASH_ERR_OK No error - operation complete FLASH_ERR_INVALID Invalid FLASH address FLASH_ERR_ERASE Error trying to erase FLASH_ERR_LOCK Error trying to lock/unlock FLASH_ERR_PROGRAM Error trying to program FLASH_ERR_PROTOCOL Generic error FLASH_ERR_PROTECT Device/region is write-protected FLASH_ERR_NOT_INIT FLASH info not yet initialized FLASH_ERR_HWR Hardware (configuration?) problem FLASH_ERR_ERASE_SUSPEND Device is in erase suspend mode FLASH_ERR_PROGRAM_SUSPEND Device is in program suspend mode FLASH_ERR_DRV_VERIFY Driver failed to verify data FLASH_ERR_DRV_TIMEOUT Driver timed out waiting for device FLASH_ERR_DRV_WRONG_PART Driver does not support device FLASH_ERR_LOW_VOLTAGE Not enough juice to complete job To turn an error code into a human readable string the following function can be used: externC char *flash_errmsg(int err); Notes on using the FLASH library The FLASH library evolved from the needs and environment of RedBoot rather than being a general purpose eCos component. This history explains some of the problems with the library. The library is not thread safe. Multiple simultaneous calls to its library functions will likely fail and may cause a crash. It is the callers responsibility to use the necessary mutex's if needed. FLASH device API This section describes the API between the FLASH IO library the FLASH device drivers. The flash_info structure The flash_infostructure is used by both the FLASH IO library and the device driver. struct flash_info { int block_size; // Assuming fixed size "blocks" int blocks; // Number of blocks int buffer_size; // Size of write buffer (only defined for some devices) unsigned long block_mask; void *start, *end; // Address range int init; // FLASH API initialised _printf *pf; // printf like function for diagnostics }; block_mask is used internally in the FLASH IO library. It contains a mask which can be used to turn an arbitrary address in flash to the base address of the block which contains the address. There exists one global instance of this structure with the name flash_info. All calls into the device driver makes use of this global structure to maintain state. Initializing the device driver The FLASH IO library will call the following function to initialize the device driver: externC int flash_hwr_init(void); The device driver should probe the hardware to see if the FLASH devices exist. If it does it should fill in start, end, blocks and block_size.If the FLASH contains a write buffer the size of this should be placed in buffer_size . On successful probing the function should return FLASH_ERR_OK. When things go wrong it can be assumed that pf points to a printf like function for outputting error messages. Querying the FLASH FLASH devices can be queried to return there manufacture ID, size etc. This function allows this information to be returned. int flash_query(unsigned char *data); The caller must know the size of data to be returned and provide an appropriately sized buffer pointed to be parameter data. This function is generally used by flash_hwr_init(). Erasing a block of FLASH So that the FLASH IO layer can erase a block of FLASH the following function should be provided. int flash_erase_block(volatile flash_t *block, unsigned int block_size); Programming a region of FLASH The following function must be provided so that data can be written into the FLASH. int flash_program_buf(volatile flash_t *addr, flash_t *data, int len, unsigned long block_mask, int buffer_size); The device will only be asked to program data in one block of the flash. The FLASH IO layer will break longer user requests into a smaller writes. Reading a region from FLASH Some FLASH devices are not memory mapped so it is not possible to read there contents directly. The following function read a region of FLASH. int flash_read_buf(volatile flash_t* addr, flash_t* data, int len); As with writing to the flash, the FLASH IO layer will break longer user requests for data into a number of reads which are at maximum one block in size. A device which cannot be read directy should set CYGSEM_IO_FLASH_READ_INDIRECT so that the IO layer makes use of the flash_read_buf()function. Locking and unlocking FLASH blocks Some flash devices allow blocks to be locked so that they cannot be written to. The device driver should provide the following functions to manipulate these locks. int flash_lock_block(volatile flash_t *block); int flash_unlock_block(volatile flash_t *block, int block_size, int blocks); These functions are only used if CYGHWR_IO_FLASH_BLOCK_LOCKING Mapping FLASH error codes to FLASH IO error codes The functions flash_erase_block(), flash_program_buf(), flash_read_buf(), flash_lock_block() and flash_unlock_block() return an error code which is specific to the flash device. To map this into a FLASH IO error code, the driver should provide the following function: int flash_hwr_map_error(int err); Determining if code is in FLASH Although a general function, the device driver is expected to provide the implementation of the function flash_code_overlaps(). Implementation Notes The FLASH IO layer will manipulate the caches as required. The device drivers do not need to enable/disable caches when performing operations of the FLASH. Device drivers should keep all chatter to a minimum when CYGSEM_IO_FLASH_CHATTER is not defined. All output should use the print function in the pf in flash_info and not diag_printf() Device driver functions which manipulate the state of the flash so that it cannot be read from for program execute need to ensure there code is placed into RAM. The linker will do this if the appropriate attribute is added to the function. e.g: int flash_program_buf(volatile flash_t *addr, flash_t *data, int len, unsigned long block_mask, int buffer_size) __attribute__ ((section (".2ram.flash_program_buf")));