Driving FlexNVM and EEEPROM on K64Fx512 Micros

Document created by CSMG Sarma on Oct 10, 2018Last modified by CSMG Sarma on Oct 10, 2018
Version 3Show Document
  • View in full screen mode

Freescale K64MX512 comes with a 512KB Program Flash or P-Flash and 128KB of FlexNVM. While NVM in FlexNVM makes it look like the data is lost during a power cycle, but FlexNVM actually is a Flash backed by a small chunk of RAM. So while you are writing to Flash, you are accessing it as if its a RAM. Refer to Application Note AN4282 from Freescale to know more about how FlexNVM and Enhanced EEPROM works. While MQX claims to have a FlashX driver for FlexNVM for K64Mx512, the driver requires few changes to get it running on K64 micros. This note is to update the FlashX Driver to support K64Mx512 from the BSP references.

 

I am using KDS with MQX4.2 for K64Mx512 micros. But similar steps can be used to bring up the FlashX driver for FlexNVM on other IDEs or Micros. With FlashX's simple and easy to use file handling scheme, its easy to use this note to port to a custom application.

 

The variables and files to focus are:

  • _flashx_kinetisX_block_map: the array of structures provides the description of HW flash configuration. This is defined in flash_ftfe.c. My setup looks as follows:
const FLASHX_BLOCK_INFO_STRUCT _flashx_kinetisX_block_map[] = 
{
{ BSP_INTERNAL_FLASH_SIZE / BSP_INTERNAL_FLASH_SECTOR_SIZE / 2, (uint32_t) BSP_INTERNAL_FLASH_BASE, BSP_INTERNAL_FLASH_SECTOR_SIZE },
{ BSP_INTERNAL_FLASH_SIZE / BSP_INTERNAL_FLASH_SECTOR_SIZE / 2, (uint32_t) BSP_INTERNAL_FLASH_BASE, BSP_INTERNAL_FLASH_SECTOR_SIZE },
{ BSP_INTERNAL_FLEXNVM_SIZE / BSP_FLEXNVM_SECTOR_SIZE, (_mem_size) BSP_INTERNAL_FLEXNVM_BASE, BSP_FLEXNVM_SECTOR_SIZE }, // D-Flash blocks or FlexNVM block
// { BSP_INTERNAL_FLASH_SIZE / BSP_INTERNAL_FLASH_SECTOR_SIZE, (uint32_t) __FLASHX_START_ADDR, BSP_INTERNAL_FLASH_SECTOR_SIZE }, // D-Flash blocks This is not needed on a device with FlexNVM
{ 1, (uint32_t) BSP_INTERNAL_FLEXRAM_BASE, BSP_INTERNAL_FLEXRAM_SIZE },
{ 0, 0, 0 }
};

 

  • _bsp_flashx_file_blocks in init_flashx.c: This array of structures provides the description of files. My setting looks as follows:
const FLASHX_FILE_BLOCK _bsp_flashx_file_blocks[] = 
{
{ "p-flash", BSP_INTERNAL_FLASH_BASE , BSP_INTERNAL_FLASH_BASE + BSP_INTERNAL_FLASH_SIZE - 1 },
{ "dflash", BSP_INTERNAL_FLEXNVM_BASE, (uint32_t) (BSP_INTERNAL_FLEXNVM_BASE+ BSP_INTERNAL_FLEXNVM_SIZE - 1) },
{ "EEEPROM" , (uint32_t) BSP_INTERNAL_FLEXRAM_BASE, (uint32_t) BSP_INTERNAL_FLEXRAM_BASE + BSP_INTERNAL_FLEXRAM_SIZE - 1 },
{ NULL , 0, 0 }
};

 

  • The macros are defined as follows in twrk64f120m.h:
#define BSP_INTERNAL_FLASH_BASE 0x00000000
#define BSP_INTERNAL_FLASH_SIZE 0x00100000
#define BSP_INTERNAL_FLASH_SECTOR_SIZE 0x1000
#define BSP_INTERNAL_FLEXRAM_BASE 0x14000000
#define BSP_INTERNAL_FLEXRAM_SIZE 0x00001000
#define BSP_INTERNAL_FLEXNVM_BASE (0x10000000)
#define BSP_FLEXNVM_SECTOR_SIZE (0x400)
#define BSP_INTERNAL_FLEXNVM_SIZE (0x00010000) //64KB

 

  • write_addr in ftfe_flash_write_sector() in flash_ftfe.c: This is a 32 bit address field. While writing to FlexNVM, a 32-bit to 24-bit bit address conversion is needed. This is done in the ftfe_flash_write_sector() as follows:
/*FUNCTION*-------------------------------------------------------------------
*
* Function Name : ftfe_flash_write_sector
* Returned Value : TRUE if successful, FALSE otherwise
* Comments :
* Performs a write into flash memory.
*
*END*----------------------------------------------------------------------*/

bool ftfe_flash_write_sector
(
/* [IN] File pointer */
IO_FLASHX_STRUCT_PTR dev_ptr,
/* [IN] Source address */
char *from_ptr,
/* [IN] Destination address */
char *to_ptr,
/* [IN] Number of bytes to write */
_mem_size size
)
{
FTFE_FLASH_INTERNAL_STRUCT_PTR dev_spec_ptr = (FTFE_FLASH_INTERNAL_STRUCT_PTR) dev_ptr->DEVICE_SPECIFIC_DATA;
_mqx_uint i;
_mem_size write_addr = 0;
uint8_t command_array[4 + FTFE_PHRASE_SIZE];
char temp_data[FTFE_PHRASE_SIZE];
unsigned char byte_data_counter = 0;
uint32_t offset = ((uint32_t)to_ptr) & 0x00000007;
dev_spec_ptr = dev_ptr->DEVICE_SPECIFIC_DATA;
/* write to address mod 8 correction */
if (offset)
{
/* align pointer to writable address */
to_ptr -= offset;

/* jump over old data */
byte_data_counter = offset;
}
write_addr = (_mem_size) to_ptr;
if (write_addr & 0x10000000) // the FlexNVM 32-bit address to be translated to 24-bit address
write_addr |= 0x00800000;
/* heading space should be 0xFF */
for (i = 0; i < offset; i++)
temp_data[i] = 0xFF;
while (size)
{
/* move data to temporary char array */
while ((byte_data_counter < FTFE_PHRASE_SIZE) && size)
{
temp_data[byte_data_counter++] = *from_ptr++;
size--;
}

/* remaining space should be 0xFF */
while (byte_data_counter < FTFE_PHRASE_SIZE) {
temp_data[byte_data_counter++] = 0xFF;
}
/* prepare parameters to program the flash block */
command_array[0] = FTFE_PROGRAM_PHRASE;
command_array[1] = (uint8_t)(write_addr >> 16);
command_array[2] = (uint8_t)((write_addr >> 8) & 0xFF);
command_array[3] = (uint8_t)(write_addr & 0xFF);
command_array[4] = temp_data[3];
command_array[5] = temp_data[2];
command_array[6] = temp_data[1];
command_array[7] = temp_data[0];
command_array[8] = temp_data[7];
command_array[9] = temp_data[6];
command_array[10] = temp_data[5];
command_array[11] = temp_data[4];
/* call flash command sequence function to execute the command */
if (FTFE_OK != ftfe_flash_command_sequence(dev_spec_ptr, command_array, 4 + FTFE_PHRASE_SIZE, FALSE, (void*)write_addr, FTFE_PHRASE_SIZE))
{
return FALSE;
}

/* update destination address for next iteration */
write_addr += FTFE_PHRASE_SIZE;
/* init variables for next loop */
byte_data_counter = 0;
}

return TRUE;
}

 

  • Linker Script: It is possible that the linker script used is for K64F120M which does not have a FlexNVM. Hence its advised to confirm this and update the linker script. The MEMORY and SECTIONS contents are updated as follows for K64F512 micro. The remaining sections are removed for brevity.
    MEMORY
    {
    ....
    ....
    ....
    ....
    flex_nvm (RW): ORIGIN = 0x10000000, LENGTH = 0x00020000 /* 120KByte Flex NVM */
    flex_ram (RW): ORIGIN = 0x14000000, LENGTH = 0x00001000 /* 4KByte Flex Ram */

    ....
    ....
    ....
    }

    SECTIONS
    {
    ....
    ....
    ....
    __INTERNAL_FLASH_BASE = 0x00000000;
    __INTERNAL_FLASH_SIZE = 0x00080000; /* 512KB */
    /* for K64F512M */
    __INTERNAL_FLEXNVM_BASE = 0x10000000; /*0; */
    __INTERNAL_FLEXNVM_SIZE = 0x20000; /*128 KB; 0 */
    __INTERNAL_FLEXRAM_BASE = 0x14000000; /* This didnot exist */
    __INTERNAL_FLEXRAM_SIZE = 0x00001000; /* 4 KB */ /* This didnot exist */
    ....
    ....
    ....
    section_flexNVM :
    {
    _flex_nvm_start = .;
    KEEP(*(.flex_nvm_block))
    } > flex_nvm
    section_flexRAM :
    {
    _flex_ram_start = .;
    KEEP(*(.flex_ram_block))
    } > flex_ram
    ....
    ....
    ....
    }

With these changes, I can access the FlexRAM using byte, word(16 bit) and double word(32 bit) pointers and EEEPROM is accessible as a file. I have implemented the following wrappers to mask the complexity and make the application easy. While there is room for perfection, these are serving the purpose without any problems.

/*******************************************************************************
* flexNVM read and write sectors
*******************************************************************************/

/**
*
* @param buff
* @param offset word offset
* @param len
* @return
*
* @note Offset is 4 byte aligned with respect to 32-bit integer. i.e. for an
* offset of 100 bytes, the word offset is 25( = 100/4)
*/

uint32_t flexnvm_read(uint8_t * buff, uint32_t offset, uint32_t * len)
{
MQX_FILE_PTR flash_file;
_mqx_int res;
_int_install_unexpected_isr();
flash_file = fopen(FLASH_NAME, NULL);
if (flash_file == NULL)
{
printf("\nUnable to open file %s", FLASH_NAME);
_task_block();
}
else
{
printf("\nFlash file %s opened", FLASH_NAME);
}
/* Disable sector cache */
ioctl(flash_file, FLASH_IOCTL_ENABLE_SECTOR_CACHE, NULL);
if (offset != 0)
{
if (fseek(flash_file, offset, IO_SEEK_SET) != 0)
{
res = MQX_ERROR;
printf("\nERROR! Could not open flash file!\n");
goto READ_END;
}
}
res = read(flash_file, buff, *len);
if (res != *len)
{
printf("\nERROR! Could not read from flash. Exiting...\n");
*len = res;
res = MQX_ERROR;
goto READ_END;
}
else
{
res = MQX_OK;
}
READ_END:
fflush(flash_file);
fclose(flash_file);
return res;
}
/**
*
* @param buff
* @param offset
* @param len
* @return
* @note Offset is 4 byte aligned with respect to 32-bit integer. i.e. for an
* offset of 1024 bytes, the word offset is 256( = 1024/4)
*/

uint32_t flexnvm_write(const uint8_t * buff, uint32_t offset, uint32_t * len)
{
MQX_FILE_PTR flash_file;
_mqx_int res;
uint32_t ioctl_param;
_int_install_unexpected_isr();
flash_file = fopen(FLASH_NAME, NULL);
if (flash_file == NULL)
{
printf("\nUnable to open file %s", FLASH_NAME);
res = MQX_ERROR;
goto WRITE_END;
}
/* Disable sector cache */
ioctl(flash_file, FLASH_IOCTL_ENABLE_SECTOR_CACHE, NULL);
/** Unprotect the flash file before write. */
ioctl_param = 0;
ioctl(flash_file, FLASH_IOCTL_WRITE_PROTECT, &ioctl_param);
if (offset != 0)
{
if (fseek(flash_file, offset, IO_SEEK_SET))
{
res = MQX_ERROR;
printf("Fseek Error: flexnvm_write\n");
res = MQX_ERROR;
goto WRITE_END;
}
}
res = write(flash_file, buff, *len);
if (*len != res)
{
printf("\nERROR! Could not write to flash. Exiting...");
res = MQX_ERROR;
goto WRITE_END;
}
else
{
res = MQX_OK;
}
fflush(flash_file);
WRITE_END:
/** protecting the flash file. */
ioctl_param = 1;
ioctl(flash_file, FLASH_IOCTL_WRITE_PROTECT, &ioctl_param);
fflush(flash_file);
fclose(flash_file);
return res;
}

/*******************************************************************************
* flexRAM - EEEProm read and write sectors
*******************************************************************************/

uint32_t flex_eeeprom_read(uint8_t * buff, uint32_t offset, uint32_t * len)
{
MQX_FILE_PTR flexram_file;
FLEXNVM_PROG_PART_STRUCT part_param;
uint32_t * addr;
uint32_t ret = MQX_OK;
uint32_t result;
uint32_t ioctl_param;
flexram_file = fopen(FLEXEEEPROM_NAME, NULL);
if (flexram_file == NULL)
{
printf("\nUnable to open file %s", FLEXEEEPROM_NAME);
ret = MQX_ERROR;
}
result = _io_ioctl(flexram_file, FLEXNVM_IOCTL_GET_PARTITION_CODE, &part_param);
if (IO_OK != result)
{
printf("\nError when reading FlexNVM configuration.");
_task_block();
}
if (FLEXNVM_PART_CODE_NOPART == part_param.FLEXNVM_PART_CODE)
{
// if (FLEXNVM_PART_CODE_DATA0_EE128 == part_param.FLEXNVM_PART_CODE) {
printf("\n\nEnabling FlexEEPROM - partition memory.");
/* set FlexNVM partition and EEPROM size */
part_param.EE_DATA_SIZE_CODE = BSP_EE_DATA_SIZE_CODE;
part_param.FLEXNVM_PART_CODE = BSP_FLEXNVM_PART_CODE;
_io_ioctl(flexram_file, FLEXNVM_IOCTL_SET_PARTITION_CODE, &part_param);
/* switch FlexRAM to EEPROM mode */
ioctl_param = FLEXNVM_FLEXRAM_EE;
_io_ioctl(flexram_file, FLEXNVM_IOCTL_SET_FLEXRAM_FN, &ioctl_param);
}
/** go to the expected address location */
addr = (uint32_t*)(BSP_INTERNAL_FLEXRAM_BASE + offset);
memcpy((void*)buff, (const void*)addr, *len);
/** @warning memcpy errors have to be checked and verified by the caller */
fclose(flexram_file);
return ret;
}
uint32_t flex_eeeprom_write(uint32_t * buff, uint32_t offset, uint32_t * len)
{
MQX_FILE_PTR flexram_file;
FLEXNVM_PROG_PART_STRUCT part_param;
uint32_t * addr;
uint32_t ret = MQX_OK;
uint32_t result;
uint32_t i;
uint32_t ioctl_param;
uint8_t * byte_ptr;
flexram_file = fopen(FLEXEEEPROM_NAME, NULL);
if (flexram_file == NULL)
{
printf("\nUnable to open file %s", FLEXEEEPROM_NAME);
ret = MQX_ERROR;
}
result = _io_ioctl(flexram_file, FLEXNVM_IOCTL_GET_PARTITION_CODE, &part_param);
if (IO_OK != result)
{
printf("\nError when reading FlexNVM configuration.");
_task_block();
}
if (FLEXNVM_PART_CODE_NOPART == part_param.FLEXNVM_PART_CODE)
{
// if (FLEXNVM_PART_CODE_DATA0_EE128 == part_param.FLEXNVM_PART_CODE) {
printf("\n\nEnabling FlexEEPROM - partition memory.");
/* set FlexNVM partition and EEPROM size */
part_param.EE_DATA_SIZE_CODE = BSP_EE_DATA_SIZE_CODE;
part_param.FLEXNVM_PART_CODE = BSP_FLEXNVM_PART_CODE;
_io_ioctl(flexram_file, FLEXNVM_IOCTL_SET_PARTITION_CODE, &part_param);
/* switch FlexRAM to EEPROM mode */
ioctl_param = FLEXNVM_FLEXRAM_EE;
_io_ioctl(flexram_file, FLEXNVM_IOCTL_SET_FLEXRAM_FN, &ioctl_param);
}
/** go to the expected address location */
addr = (uint32_t*)(BSP_INTERNAL_FLEXRAM_BASE + offset);
result = *len;
if ((result % 4) != 0)
{//if the length of the data to be written is not 4 byte aligned,
byte_ptr = (uint8_t *)(BSP_INTERNAL_FLEXRAM_BASE + (result - (result % 4)));
}
i = 0;
do
{//32 byte writes
_io_ioctl(flexram_file, FLEXNVM_IOCTL_WAIT_EERDY, NULL); //check if flash is busy
*addr = buff[i];//32 byte writes. for efficiency and high endurance.
addr++;
*len -= 4;
i++;
} while (*len > 4);
while (*len > 0)
{//1 byte writes here.
_io_ioctl(flexram_file, FLEXNVM_IOCTL_WAIT_EERDY, NULL);
*byte_ptr = ((uint8_t *)addr)[*len];//1 byte writes. the remaining bytes. Max 3 byte accesses.
*len -= 1;
}
// memcpy((void*)buff, (const void*)addr, *len);
/** @warning memcpy errors have to be checked and verified by the caller */
fflush(flexram_file);
fclose(flexram_file);
return ret;
}

 

 

The following references were helpful:

MQX FlashX driver – how to write FlexNVM? 

https://community.nxp.com/message/482796?commentID=482796#comment-482796
https://community.nxp.com/thread/342787?commentID=993610&et=watches.email.thread#comment-993610

https://www.nxp.com/docs/en/application-note/AN4282.pdf

 

Summary:

K64Fx512M is a very good controller especially for the memory applications. Using the above or similar steps, FlexNVM can be used for any environment and similar micros too. 

 

Happy Coding!

 

Sarma

k64fx512 flexnvm k64_eeeprom the patition command for flexram and flexnvm flexnvm flashx

Attachments

    Outcomes