Frank's Random Wanderings

Using the Freescale TFS Flash Driver Software – Bugs & Notes

Recently I’ve trying to use Freescale’s C90TFS Flash Driver for erasing and programming on-chip flash on their Kinetis MKL25Z128 Cortex M0+ processor. This is the same processor as used on the Freescale Freedom board. At the time of writing, the latest version of the TFS flash driver is 1.03.

The documentation states it’s been tested and verified on a pretty long list of processors and demo boards, including the Kinetis freedom board, Coldfire processors, etc. To be honest, I kinda doubt that, for a reason which will become apparent in a moment.

I added the appropriate driver files to my existing Kinetis CodeWarrior project, then wrote some simple code, based on the included NormalDemo_Flash.c code, to test it. It didn’t work.

Bug in file SSD_FTFx_Internal.h

In file user_cfg.h you must specify a #define FLASH_DERIVATIVE. This basically specifies your processor. Based on this single entry, the file SSD_FTFx_Internal.h contains a long set of if – else if to pull in an appropriate .h configuration file which itself contains a long list of definitions specific to your exact processor. The C90TFS flash library supports several different CPU cores, a bunch of different processors, etc, so this is a key part of making the library work correctly.

Unfortunately the file SSD_FTFx_Internal.h contains a bad bug.

/* Select file .h for each derivatives */
#if (FTFx_KX_256K_256K_4K_2K_2K == FLASH_DERIVATIVE)
    #include "FTFx_KX_256K_256K_4K_2K_2K.h"
#elif (FTFx_KX_128K_128K_4K_2K_2K == FLASH_DERIVATIVE)
    #include "FTFx_KX_128K_128K_4K_2K_2K.h"
#elif ((FTFx_KX_512K_0K_4K_2K_0K == FLASH_DERIVATIVE) || (FTFx_KX_256K_0K_4K_2K_0K == FLASH_DERIVATIVE))
    #include "FTFx_KX_(512_256)K_0K_4K_2K_0K.h"
#elif (FTFx_CX_256K_32K_2K_1K_1K == FLASH_DERIVATIVE)
    #include "FTFx_CX_256K_32K_2K_1K_1K.h"
#elif ((FTFx_CX_128K_32K_2K_1K_1K == FLASH_DERIVATIVE) || (FTFx_CX_64K_32K_2K_1K_1K == FLASH_DERIVATIVE)\
    || (FTFx_CX_32K_32K_2K_1K_1K == FLASH_DERIVATIVE))
    #include "FTFx_CX_(128_64_32)K_32K_2K_1K_1K.h"
#elif (FTFx_KX_512K_512K_16K_4K_4K == FLASH_DERIVATIVE)
    #include "FTFx_KX_512K_512K_16K_4K_4K.h"
#elif ((FTFx_KX_1024K_0K_16K_4K_0K == FLASH_DERIVATIVE) || (FTFx_KX_1024K_0K_4K_4K_0K == FLASH_DERIVATIVE)\
    || (FTFx_KX_2048K_0K_4K_4K_0K))
    #include "FTFx_KX_(2048_1024)K_0K_(16_4)K_4K_0K.h"
#elif ((FTFx_KX_128K_0K_2K_1K_0K == FLASH_DERIVATIVE)||(FTFx_KX_64K_0K_2K_1K_0K == FLASH_DERIVATIVE)\
    ||(FTFx_KX_32K_0K_2K_1K_0K == FLASH_DERIVATIVE))
    #include "FTFx_KX_(128_64_32)K_0K_2K_1K_0K.h"
#elif ((FT......
 
etc

In the little snippit above, the 6th line from the bottom:

    || (FTFx_KX_2048K_0K_4K_4K_0K))

should be:

    || (FTFx_KX_2048K_0K_4K_4K_0K == FLASH_DERIVATIVE))

Because of this bug, if your processor selection isn’t one of the first few in the list, you’ll end up pulling in the include file FTFx_KX_(2048_1024)K_0K_(16_4)K_4K_0K.h, which is for a Cortex-M processor with the larger 4k flash block sizes, etc. Not suitable for a MKL25Z128 with 1k flash sectors, certainly not suitable for many other processors. How this could work at all for other processors like the Coldfires, I’ve currently got no idea.

Like most bugs, once found it’s an easy fix to edit that line.

Executing Code from RAM

For processors like the MKL25Z128, you cannot execute code from flash at the same time as a flash sector is being erased or programmed. This has some ramifications.

First, there’s a driver function FlashCommandSequence() which must live in RAM. It’s called by the flash driver – you don’t directly call it. But you must copy it into RAM. There’s an example of how to do that in the NormalDemo_Flash.c file, but it involves creating a separate section in RAM in the linker file. If you’d prefer not to fiddle with the linker file, here’s another way.

#define SIZE_OF_FLASH_CMD_SEQ 0x50		// Assumed size (plus padding) of function FlashCommandSequence() - check map file

// Global SRAM memory where we put code to be executed from RAM. Code is copied into
// here when using the flash_write() function below.
uint8_t ram_for_command_sequence_func[SIZE_OF_FLASH_CMD_SEQ]  __attribute__ ((aligned (4)));

pFLASHCOMMANDSEQUENCE pFlashCommandSequence;
uint8_t *source;


/* copy FlashCommandSequence routine code to RAM, so later on it can execute from RAM */
source = (uint8_t*)((uint32_t)FlashCommandSequence & 0xfffffffe);
for(i=0; i < SIZE_OF_FLASH_CMD_SEQ; i++, source++)
    ram_for_command_sequence_func[i] = *source;
pFlashCommandSequence = (pFLASHCOMMANDSEQUENCE)((uint8_t *)ram_for_command_sequence_func + 1);

Hopefully this is sufficient to give you the idea of how to do it. Looking at my .map file, the size of FlashCommandSequence() was 0x30, so allowing 0x50 above gave lots of slack in case things might change a little. Then you pass the address of the function in RAM to the flash library through the sector erase and program functions, for example like so:

returnCode = FlashEraseSector(&flashSSDConfig, ADDR_DATA_FLASH_SECTOR, size, pFlashCommandSequence);

Note that in the Freescale flash library, you'll find a couple of source files for all the functions. You'll find one is pre-compiled; each function is just an array of bytes. The other is normal C source code. I initially tried using the precompiled version of FlashCommandSequence() because I thought it would be convenient to know the exact size. However that function did not work on the MKL25Z128. Perhaps it was compiled for a different CPU; I don't know. When I switched to using the C version of FlashCommandSequence(), compiling it normally as part of my project and copying it into RAM as shown above, it worked as expected.

Callbacks & Interrupts

The library functions can execute your code while they're waiting for flash erases and programs to take place. This is done by giving them a pointer to a callback function. On many processors, like the Freedom board processor, this would also need to be in RAM. Because remember on these processors you cannot execute code from flash while erasing or programming a flash sector.

For this reason, it's probably more trouble than it's worth. Set the callback function to NULL_CALLBACK so the callback function isn't used. Like so:

    FLASH_SSD_CONFIG flashSSDConfig =
    {
        FTFx_REG_BASE,          /* FTFx control register base */
        PFLASH_BLOCK_BASE,      /* base address of PFlash block */
        PBLOCK_SIZE,            /* size of PFlash block */
        DEFLASH_BLOCK_BASE,     /* base address of DFlash block */
        0x0U,                   /* size of DFlash block */
        EERAM_BLOCK_BASE,       /* base address of EERAM block */
        0x0U,                   /* size of EEE block */
        DEBUGENABLE,            /* background debug mode enable bit */
        NULL_CALLBACK           /* pointer to callback function - none for us */
    };

Similarly, you should make sure no interrupts might trip off which might execute code from flash during that time. The simplest thing to do is just to globally disable interrupts when you're erasing or programming flash, then reenable interrupts afterwards. Here are a couple of macros which work in CodeWarrior for the Kinetis to do that.

#define EnableInterrupts asm(" CPSIE i");
#define DisableInterrupts asm(" CPSID i");

A usage example:

DisableInterrupts
returnCode = FlashEraseSector(&flashSSDConfig, ADDR_DATA_FLASH_SECTOR, size, pFlashCommandSequence);
EnableInterrupts

Yes, globally turning interrupts off and on is a bit crude, but in a lot of cases it's quite good enough. Of course you can get much more fancy if you want.

Conclusion
It's taken me a while to get this flash library working reliably. Between the bug and the sometimes ambiguous documentation, it's not quite trivial. Hopefully this will help others get flash erasing and writing working. Once you know the tricks, it's pretty straightforward.

Leave a Reply

Your email address will not be published.