Instantiating a Strata Device

Name

Instantiating -- including the driver in an eCos target

Synopsis

#include <cyg/io/strata_dev.h>
      

int cyg_strata_init_nop(struct cyg_flash_dev* device);

int cyg_strata_init_check_devid_XX(struct cyg_flash_dev* device);

int cyg_strata_init_cfi_XX(struct cyg_flash_dev* device);

int cyg_strata_erase_XX(struct cyg_flash_dev* device, cyg_flashaddr_t addr);

int cyg_strata_program_XX(struct cyg_flash_dev* device, cyg_flashaddr_t addr, const void* data, size_t len);

int cyg_strata_bufprogram_XX(struct cyg_flash_dev* device, cyg_flashaddr_t addr, const void* data, size_t len);

int cyg_strata_lock_j3_XX(struct cyg_flash_dev* device, const cyg_flashaddr_t addr);

int cyg_strata_unlock_j3_XX(struct cyg_flash_dev* device, const cyg_flashaddr_t addr);

int cyg_strata_lock_k3_XX(struct cyg_flash_dev* device, const cyg_flashaddr_t addr);

int cyg_strata_unlock_k3_XX(struct cyg_flash_dev* device, const cyg_flashaddr_t addr);

Description

The Strata family contains a number of different devices, all supporting the same basic set of operations but with various common or uncommon extensions. The range includes:

28FxxxB3 Boot Block

These support 8 8K boot blocks as well as the usual 64K blocks. There is no buffered write capability. The only locking mechanism available involves manipulating voltages on certain pins.

28FxxxC3

These also have boot blocks. There is no buffered write capability. Individual blocks can be locked and unlocked in software.

28FxxxJ3

These are uniform devices where all blocks are 128K. Buffered writes are supported. Blocks can be locked individually, but the only unlock operation is a global unlock-all.

28FxxxK3

These are also uniform devices with 128K blocks. Buffered writes are supported. Individual blocks can be locked and unlocked in software.

Each of these comes in a range of sizes and bus widths. There are also platform-specific issues such as how many devices are actually present on the board and where they are mapped in the address space. The Strata driver package cannot know all this information. Instead it is the responsibility of another package, usually the platform HAL, to instantiate some flash device structures. Two pieces of information are especially important: the bus configuration and the boot block layout.

Flash devices are typically 8-bits, 16-bits, or 32-bits wide (64-bit devices are not yet in common use). Most 16-bit devices will also support 8-bit accesses, but not all. Similarly 32-bit devices can be accessed 16-bits at a time or 8-bits at a time. A board will have one or more of these devices on the bus. For example there may be a single 16-bit device on a 16-bit bus, or two 16-bit devices on a 32-bit bus. The processor's bus logic determines which combinations are possible, and usually there will be a trade off between cost and performance. For example two 16-bit devices in parallel can provide twice the memory bandwidth of a single device. The driver supports the following combinations:

8

A single 8-bit flash device on an 8-bit bus.

16

A single 16-bit flash device on a 16-bit bus.

32

A single 32-bit flash device on an 32-bit bus.

88

Two parallel 8-bit devices on an 16-bit bus.

8888

Four parallel 8-bit devices on a 32-bit bus.

1616

Two parallel 16-bit devices on a 32-bit bus, with one device providing the bottom two bytes of each 32-bit datum and the other device providing the upper two bytes.

16as8

A single 16-bit flash device connected to an 8-bit bus.

These configuration all require slightly different code to manipulate the hardware. The Strata driver package provides separate functions for each configuration, for example cyg_strata_erase_16 and cyg_strata_program_1616.

Caution

At the time of writing not all the configurations have been tested.

The second piece of information is the boot block layout. Flash devices are subdivided into blocks (also known as sectors, both terms are in common use). Some operations such as erase work on a whole block at a time, and for most applications a block is the smallest unit that gets updated. A typical block size is 64K. It is inefficient to use an entire 64K block for small bits of configuration data and similar information, so some flash devices also support a number of smaller boot blocks. A typical 2MB flash device could have eight 8K blocks and 31 full-size 64K blocks. The boot blocks may appear at the bottom or the top of the device. So-called uniform devices do not have boot blocks, just full-size ones. The driver needs to know the boot block layout. With modern devices it can work this out at run-time, but often it is better to provide the information statically.

Example

Flash support is usually specific to each platform. Even if two platforms happen to use the same flash device there are likely to be differences such as the location in the address map. Hence there is little possibility of re-using the platform-specific code, and this code is generally placed in the platform HAL rather than in a separate package. Typically this involves a separate file and a corresponding compile property in the platform HAL's CDL:

cdl_package CYGPKG_HAL_M68K_KIKOO {
    …
    compile -library=libextras.a kikoo_flash.c
    …
}
    

The contents of this file will not be accessed directly, only indirectly via the generic flash API, so normally it would be removed by link-time garbage collection. To avoid this the object file has to go into libextras.a.

The actual file kikoo_flash.c will look something like:

#include <pkgconf/system.h>
#ifdef CYGPKG_DEVS_FLASH_STRATA_V2

#include <cyg/io/flash.h>
#include <cyg/io/strata_dev.h>

static const CYG_FLASH_FUNS(hal_kikoo_flash_strata_funs,
    &cyg_strata_init_check_devid_16,
    &cyg_flash_devfn_query_nop,
    &cyg_strata_erase_16,
    &cyg_strata_bufprogram_16,
    (int (*)(struct cyg_flash_dev*, const cyg_flashaddr_t, void*, size_t))0,
    &cyg_strata_lock_j3_16,
    &cyg_strata_unlock_j3_16);

static const cyg_strata_dev hal_kikoo_flash_priv = {
    .manufacturer_code = CYG_FLASH_STRATA_MANUFACTURER_INTEL,
    .device_code = 0x0017,
    .bufsize    = 16,
    .block_info = {
        { 0x00020000, 64 } // 64 * 128K blocks
    }
};

CYG_FLASH_DRIVER(hal_kikoo_flash,
                 &hal_kikoo_flash_strata_funs,
                 0,
                 0x60000000,
                 0x601FFFFF,
                 1,
                 hal_kikoo_flash_priv.block_info,
                 &hal_kikoo_flash_priv
);
#endif
    

The bulk of the file is protected by an ifdef for the Strata flash driver. That driver will only be active if the generic flash support is enabled. Without that support there will be no way of accessing the device so there is no point in instantiating the device. The rest of the file is split into three definitions. The first supplies the functions which will be used to perform the actual flash accesses, using a macro provided by the generic flash code in cyg/io/flash_dev.h. The relevant ones have an _16 suffix, indicating that on this board there is a single 16-bit flash device on a 16-bit bus. The second definition provides information specific to Strata flash devices. The third provides the cyg_flash_dev structure needed by the generic flash code, which contains pointers to the previous two.

Functions

All eCos flash device drivers must implement a standard interface, defined by the generic flash code CYGPKG_IO_FLASH. This interface includes a table of 7 function pointers for various operations: initialization, query, erase, program, read, locking and unlocking. The query operation is optional and the generic flash support provides a dummy implementation cyg_flash_devfn_query_nop. Strata flash devices are always directly accessible so there is no need for a separate read function. The remaining functions are more complicated.

Usually the table can be declared const. In a ROM startup application this avoids both ROM and RAM copies of the table, saving a small amount of memory. const should not be used if the table may be modified by a platform-specific initialization routine.

Initialization

There is a choice of three main initialization functions. The simplest is cyg_flash_devfn_init_nop, which does nothing. It can be used if the cyg_strata_dev and cyg_flash_dev structures are fully initialized statically and the flash will just work without special effort. This is useful if it is guaranteed that the board will always be manufactured using the same flash chip, since the nop function involves the smallest code size and run-time overheads.

The next step up is cyg_strata_init_check_devid_XX, where XX will be replaced by the suffix appropriate for the bus configuration. It is still necessary to provide all the device information statically, including the devid field in the cyg_strata_dev structure. However this initialization function will attempt to query the flash device and check that the provided manufacturer and device codes matches the actual hardware. If there is a mismatch the device will be marked uninitialized and subsequent attempts to manipulate the flash will fail.

If the board may end up being manufactured with any of a number of different flash chips then the driver can perform run-time initialization, using a cyg_strata_init_cfi_XX function. This queries the flash device as per the Common Flash Memory Interface Specification, supported by all current devices (although not necessarily by older devices). The block_info field in the cyg_strata_dev structure and the end and num_block_infos fields in the cyg_flash_dev structure will be filled in. It is still necessary to supply the start field statically since otherwise the driver will not know how to access the flash device. The main disadvantage of using CFI is that it will increase the code size.

A final option is to use a platform-specific initialization function. This may be useful if the board may be manufactured with one of a small number of different flash devices and the platform HAL needs to adapt to this. The Strata driver provides a utility function to read the device id, cyg_strata_read_devid_XX:

static int
kikoo_flash_init(struct cyg_flash_dev* dev)
{
    int manufacturer_code, device_code;
    cyg_strata_read_devid_1616(dev, &manufacturer_code, &device_code);
    if (manufacturer_code != CYG_FLASH_STRATA_MANUFACTURER_STMICRO) {
        return CYG_FLASH_ERR_DRV_WRONG_PART;
    }
    switch(device_code) {
        case 0x0042 :
          …
        case 0x0084 :
          …
        default:
          return CYG_FLASH_ERR_DRV_WRONG_PART;
    }
}
      

There are many other possible uses for a platform-specific initialization function. For example initial prototype boards might have only supported 8-bit access to a 16-bit flash device rather than 16-bit access, but this was fixed in the next revision. The platform-specific initialization function could figure out which model board it is running on and replace the default 16as8 functions with 16 ones.

Erase and Program

The Strata driver provides erase and program functions appropriate for the various bus configurations. On most targets these can be used directly. On some targets it may be necessary to do some extra work before and after the erase and program operations. For example if the hardware has an MMU then the part of the address map containing the flash may have been set to read-only, in an attempt to catch spurious memory accesses. Erasing or programming the flash requires write-access, so the MMU settings have to be changed temporarily. For another example some flash device may require a higher voltage to be applied during an erase or program operation. or a higher voltage may be desirable to make the operation proceed faster. A typical platform-specific erase function would look like this:

static int
kikoo_flash_erase(struct cyg_flash_dev* dev, cyg_flashaddr_t addr)
{
    int result;
    …  // Set up the hardware for an erase
    result = cyg_strata_erase_32(dev, addr);
    …  // Revert the hardware change
    return result;
}
      

There are two versions of the program function. cyg_strata_bufprogram_xx uses the buffered write capability of some strata chips. This allows the flash chip to perform the writes in parallel, thus greatly improving performance. It requires that the bufsize field of the cyg_strata_dev structure is set correctly to the number of words in the write buffer. The usual value for this is 16, corresponding to a 32-byte write buffer. The alternative cyg_strata_program_xx writes the data one word at a time so is significantly slower. It should be used only with strata chips that do not support buffered writes, for example the b3 and c3 series.

There are two configuration options which affect the erase and program functions, and which a platform HAL may wish to change: CYGNUM_DEVS_FLASH_STRATA_V2_ERASE_TIMEOUT and CYGNUM_DEVS_FLASH_STRATA_V2_PROGRAM_TIMEOUT. The erase and program operations both involve polling for completion, and these timeout impose an upper bound on the polling loop. Normally these operations should never take anywhere close to the timeout period, and hence a timeout probably indicates a catastrophic failure that should really be handled by a watchdog reset. A reset is particularly appropriate because there will be no clean way of aborting the flash operation. The main reason for the timeouts is to help with debugging when porting to new hardware. If there is a valid reason why a particular platform needs different timeouts then the platform HAL's CDL can require appropriate values for these options.

Locking

Current Strata devices implement locking in three different ways, requiring different sets of functions:

28FxxxB3

There is no software locking support. The cyg_flash_devfn_lock_nop and cyg_flash_devfn_unlock_nop functions should be used.

28FxxxC3, 28FxxxK3

These support locking and unlocking individual blocks. The cyg_strata_lock_k3_XX and cyg_strata_unlock_k3_XX functions should be used. All blocks are locked following power-up or reset, so the unlock function must be used before any erase or program operation. Theoretically the lock function is optional and cyg_flash_devfn_lock_nop can be used instead, saving a small amount of code space.

28FxxxJ3

Individual blocks can be locked using cyg_strata_lock_j3_XX, albeit using a slightly different algorithm from the C3 and K3 series. However the only unlock support is a global unlock of all blocks. Hence the only way to unlock a single block is to check the locked status of every block, unlock them all, and relock the ones that should still be locked. This time-consuming operation is implemented by cyg_strata_unlock_j3_XX. Worse, unlocking all blocks can take approximately a second. During this time the flash is unusable so normally interrupts have to be disabled, affecting real-time responsiveness. There is no way of suspending this operation.

Unlike the C3 and K3 chips, on a J3 blocks are not automatically locked following power-up or reset. Hence lock and unlock support is optional, and cyg_flash_devfn_lock_nop and cyg_flash_devfn_unlock_nop can be used.

If real locking functions are used then the platform HAL's CDL script should implement the CDL interface CYGHWR_IO_FLASH_BLOCK_LOCKING. Otherwise the generic flash package may believe that none of the flash drivers in the system provide locking functionality and disable the interface functions.

Device-Specific Structure

The cyg_strata_dev structure provides information specific to Strata flash devices, as opposed to the more generic flash information which goes into the cyg_flash_dev structure. There are only two fields: devid and block_info.

manufacturer_code and device_code are needed only if the driver's initialization function is set to cyg_strata_init_check_devid_XX. That function will extract the actual device info from the flash chip and compare it with these fields. If there is a mismatch then subsequent operations on the device will fail. Definitions of CYG_FLASH_STRATA_MANUFACTURER_INTEL and CYG_FLASH_STRATA_MANUFACTURER_STMICRO are provided for convenience.

The bufsize field is needed only if a buffered program function cyg_strata_bufprogram_XX is used. It should give the size of the buffer in words. Typically Strata devices have a 32-byte buffer, so when attached to an 8-bit bus bufsize should be 32 and when attached to a 16-bit bus it should be 16.

The block_info field consists of one or more pairs of the block size in bytes and the number of blocks of that size. The order must match the actual hardware device since the flash code will use the table to determine the start and end locations of each block. The table can be initialized in one of three ways:

  1. If the driver initialization function is set to cyg_strata_init_nop or cyg_strata_init_check_devid_XX then the block information should be provided statically. This is appropriate if the board will also be manufactured using the same flash chip.

  2. If cyg_strata_init_cfi_XX is used then this will fill in the block info table. Hence there is no need for static initialization.

  3. If a platform-specific initialization function is used then either this should fill in the block info table, or the info should be provided statically.

The size of the block_info table is determined by the configuration option CYGNUM_DEVS_FLASH_STRATA_V2_ERASE_REGIONS. This has a default value of 2, which should suffice for nearly all Strata flash devices. If more entries are needed then the platform HAL's CDL script should require a larger value.

If the cyg_strata_dev structure is statically initialized then it can be const. This saves a small amount of memory in ROM startup applications. If the structure may be updated at run-time, either by cyg_strata_init_cfi_XX or by a platform-specific initialization routine, then it cannot be const.

Flash Structure

Internally the flash code works in terms of cyg_flash_dev structures, and the platform HAL should define one of these. The structure should be placed in the cyg_flashdev table. The following fields need to be provided:

funs

This should point at the table of functions.

start

The base address of the flash in the address map. On some board the flash may be mapped into memory several times, for example it may appear in both cached and uncached parts of the address space. The start field should correspond to the cached address.

end

The address of the last byte in the flash. It can either be statically initialized, or cyg_strata_init_cfi_XX will calculate its value at run-time.

num_block_infos

This should be the number of entries in the block_info table. It can either be statically initialized or it will be filled in by cyg_strata_init_cfi_XX.

block_info

The table with the block information is held in the cyg_strata_dev structure, so this field should just point into that structure.

priv

This field is reserved for use by the device driver. For the Strata driver it should point at the appropriate cyg_strata_dev structure.

The cyg_flash_dev structure contains a number of other fields which are manipulated only by the generic flash code. Some of these fields will be updated at run-time so the structure cannot be declared const.

Multiple Devices

A board may have several flash devices in parallel, for example two 16-bit devices on a 32-bit bus. It may also have several such banks to increase the total amount of flash. If each device provides 2MB, there could be one bank of 2 parallel flash devices at 0xFF800000 and another bank at 0xFFC00000, giving a total of 8MB. This setup can be described in several ways. One approach is to define two cyg_flash_dev structures. The table of function pointers can usually be shared, as can the cyg_strata_dev structure. Another approach is to define a single cyg_flash_dev structure but with a larger block_info table, covering the blocks in both banks of devices. The second approach makes more efficient use of memory.

Many variations are possible, for example a small slow flash device may be used for initial bootstrap and holding the configuration data, while there is also a much larger and faster device to hold a file system. Such variations are usually best described by separate cyg_flash_dev structures.

If more than one cyg_flash_dev structure is instantiated then the platform HAL's CDL script should implement the CDL interface CYGHWR_IO_FLASH_DEVICE once for every device past the first. Otherwise the generic code may default to the case of a single flash device and optimize for that.

Platform-Specific Macros

The Strata driver source code includes the header files cyg/hal/hal_arch.h and cyg/hal/hal_io.h, and hence indirectly the corresponding platform header files (if defined). Optionally these headers can define macros which are used inside the driver, thus giving the HAL limited control over how the driver works.

Cache Management

By default the strata driver assumes that the flash can be accessed uncached, and it will use the HAL CYGARC_UNCACHED_ADDRESS macro to map the cached address in the start field of the cyg_flash_dev structure into an uncached address. If for any reason this HAL macro is inappropriate for the flash then an alternative macro HAL_STRATA_UNCACHED_ADDRESS can be defined instead. However fixing the CYGARC_UNCACHED_ADDRESS macro is normally the better solution.

If there is no way of bypassing the cache then the platform HAL should implement the CDL interface CYGHWR_DEVS_FLASH_STRATA_V2_CACHED_ONLY. The flash driver will now disable and re-enable the cache as required. For example a program operation will involve the following:

    STRATA_INTSCACHE_STATE;
    STRATA_INTSCACHE_BEGIN();
    while ( ! finished ) {
        write a burst of CYGNUM_DEVS_FLASH_STRATA_V2_PROGRAM_BURST_SIZE
        STRATA_INTSCACHE_SUSPEND();
        STRATA_INTSCACHE_RESUME();
    }
    STRATA_INTSCACHE_END();
    

The default implementations of these INTSCACHE macros are as follows: STATE defines any local variables that may be needed, e.g. to save the current interrupt state; BEGIN disables interrupts, synchronizes the data caches, disables it, and invalidates the current contents; SUSPEND re-enables the data cache and then interrupts; RESUME disables interrupts and the data cache; END re-enables the cache and then interrupts. The cache is only disabled when interrupts are disabled, so there is no possibility of an interrupt handler running or a context switch occurring while the cache is disabled, potentially leaving the system running very slowly. The data cache synchronization ensures that there are no dirty cache lines, so when the cache is disabled the low-level flash write code will not see stale data in memory. The invalidate ensures that at the end of the operation higher-level code will not pick up stale cache contents instead of the newly written flash data. The SUSPEND and RESUME macros only re-enable and disable the data cache. An interrupt and possibly a context switch may occur between these macros and use the cache normally. It is assumed that any code which runs at this time will not touch the memory being used by the flash operation, so as far as the low-level program code is concerned it can just continue to use the uncached memory contents as set up by the BEGIN macro. If any code modifies the const data currently being written to a flash block or tries to read the flash block being modified then the system's behaviour is undefined. Theoretically a more robust approach is possible, synchronizing and invalidating the cache again in every RESUME. However these cache operations can be expensive and RESUME may get invoked some thousands of times for every flash block, so this alternative approach would cripple the driver's performance.

Some implementations of the HAL cache macros may not provide the exact semantics required by the flash driver. For example HAL_DCACHE_DISABLE may have an unwanted side effect, or it may do more work than is needed here. The driver will check for alternative macros HAL_STRATA_INTSCACHE_STATE, HAL_STRATA_INTSCACHE_BEGIN, HAL_STRATA_INTSCACHE_SUSPEND, HAL_STRATA_INTSCACHE_RESUME and HAL_STRATA_INTSCACHE_END, using these instead of the defaults.

Polling Support

On some platforms it may be necessary to perform some additional action in the middle of a lengthy erase or program operation. For example, consider a platform with a watchdog device that cannot be disabled: when running in a polled environment such as RedBoot the flash operation run will run to completion, and there will be no opportunity for higher-level code to reset the watchdog; if the flash operation takes long enough the watchdog will trigger. To avoid problems in such scenarios, the platform HAL can define a macro HAL_STRATA_POLL. The macro is optional: if absent then the driver will automatically substitute a no-op.

2017-02-09
Documentation license for this page: Open Publication License