Using Flash Memory in Embedded Applications

by Olaf Pfeiffer and Andrew Ayre

Introduction

Compared to the earlier implementations, today's Flash memories usually require less complex programming algorithms and they are now divided into several sectors. The benefit of having sectors is that the Flash memory is sector-erasable, meaning you can erase one sector at a time. In the past, erase commands erased the entire memory chip - therefore to keep a working copy of that data during run-time, an application required additional memory.

Since Flash memory is integrated on-chip with microcontrollers, its usage became even easier. Having Flash memory and a microcontroller on the same chip opened up the opportunity to take advantage of the "additional intelligence".

Rx2 FlashWith the 89C51Rx2 microcontrollers, NXP Semiconductors created a variety of 8051 derivatives with on-chip Flash memory that take best advantage of having a combination of microcontroller and Flash memory on one chip. Philips did this by providing an additional ROM area containing code for handling the Flash programming. The code does not only provide functions to erase or program the Flash memory, it also provides boot code - even with a completely erased Flash, the chip can still execute this boot code and accept inputs via the serial port.

Being ROM, this code area is not erasable; applications can rely on it as always being there. It can be used for recovery of a system, by downloading new code into the Flash memory via the serial port. Because of this feature, this code is also referred to as "boot loader".

In this article, we will not only take a closer look at different programming methods like ISP and IAP, we will also give guidelines on how to implement fail-safe systems using Flash memory and how Flash can be used efficiently to store variable data.

ISP vs. IAP

When it comes to re-programming Flash memory that is soldered down to a PCB (either integrated into the microcontroller or external), there are two programming methods: ISP and IAP.

ISP: In-System Programming

ISP allows for re-programming of a Flash memory device while it is soldered into the target hardware. However, the application needs to be stopped during the re-programming process. Usually, ISP requires that a service technician manually starts the re-programming procedure by halting the application and setting it into a special boot and/or programming mode. Only after programming is completed, the application can be restarted.

In the Philips 89C51Rx2 series, ISP is implemented with the boot loader. The chip is set to ISP mode either by driving pin PSEN high externally right after a hardware reset or by software. When in ISP mode, the 89C51Rx2 accepts Flash-programming commands via the serial interface.

IAP: In-Application Programming

IAP allows for re-programming of a Flash memory device while it is soldered into the target hardware and while the application code is running. With IAP it is possible to implement applications that can be re-programmed remotely without the need of a service technician to actually be present.

In general, IAP can always be realized with external Flash memory, where microcontroller and memory are separated components. This is true as long as there is some additional code memory available out of which the microcontroller can execute code, while the Flash memory is re-programmed.

With on-chip Flash, IAP is only possible if supported by the microcontroller. The Philips 89C51Rx2 parts support IAP also via the boot loader. The application code can call functions in the boot loader area by loading parameters into the registers R0, R1 and DPTR and then calling a specific address in the boot loader. To make these functions easier to use, the Embedded Systems Academy provides a C library supporting all boot loader functions. This allows erasing and programming directly from the C level, simply by calling C functions. For details, check ESAcademy's technical library at www.esacademy.com/faq/progs/.

The following is an example application that uses the C library to perform various IAP operations.

  1. #include "rx2iaplib.h"                     // IAP Library header file
  2. void isr0(void) interrupt 0 { ; }
  3. void main(void)
  4. {
  5.   unsigned int intvector;
  6.   unsigned char foo = 0x55;
  7.   iap_init(16);                            // initialize IAP Library by
  8.                                            // specifying crystal
  9.                                            // frequency rounded down
  10.                                            // to nearest integer
  11.   iap_erase_block(BLOCK_0x4000_0x7FFF);    // erase flash memory from
  12.                                            // 0x4000 to 0x7FFF
  13.   iap_program_data_byte(foo, 0x4000);      // program the value of a
  14.                                            // variable at 0x4000
  15.   intvector = iap_read_data_byte(0x0004);  // read the address of the
  16.   intvector <<= 8;                         // interrupt 0 service
  17.   intvector |= iap_read_data_byte(0x0005); // routine
  18.                                            // set security bits 1 and 2
  19.   iap_program_security_bits(SECURITY_BIT_1 | SECURITY_BIT_2);
  20.   while(1);
  21. }   


The C library, combined with the features of the boot loader provided in ROM, makes it easy to re-program the start address of the boot loader currently used. This actually allows installing a customized boot loader instead of the one included in ROM. Such an application specific boot loader could ensure that essential initialization code gets executed, even if the chip starts in boot mode.

  1. void callnewbootloader(void)
  2. {
  3.   iap_erase_boot_vector_status_byte();
  4.   iap_program_boot_vector(0xE8);       // new bootloader at 0xE800
  5.   iap_program_status_byte(0xFF);       // execute bootloader on reset
  6.   reset_device();
  7. }

Self-Recovery

For any system using Flash memory, the worst-case scenario includes a system failure, crash or power outage, while the Flash is being erased or re-programmed.

For some applications, a recovery via ISP might be acceptable. A service technician capable of doing the recovery manually might be at hand. However, it's easy to imagine that in truly remote systems (for example marine buoys), a more sophisticated self-recovery mechanism, not involving any person, is more desirable.

As an example, let's assume we have a network of buoys covering an ocean and collecting data. Each buoy can send and receive data via radio. After a year, the project team working on the data realizes that they can optimize their research with a few modifications to the code executed in the controllers in the buoys.

When uploading new code, the system should be stable enough to recover by itself, even if the system has a reset (or power failure) just right after erasing the Flash memory. Sending a maintenance crew to the buoy might take weeks and cost a significant amount of money.

SectorsTo ensure the recovery, it's vital to any application that the code starting at the reset vector (starting at location 0) never gets erased or re-programmed. Furthermore, all the code essential to the application must also be in code areas that never get erased. As a minimum, this must include all the code handling the IAP part of the program. In our case, that also includes all the radio communication routines.

In addition, routines are needed that can detect and decide if the code present in the re-programmable part of the application actually contains valid code. A jump or call to these program areas should surely only be executed after ensuring that real code is at the expected location.

One way to implement this is using checksums. If each code segment in an area that can be modified / re-programmed by the application has a predefined checksum (which can be achieved by adding fill-bytes that ensure a certain checksum), then the code testing routine only needs to calculate and match the checksum to decide if a piece of code is valid or not. Depending on the level of security needed, several checksums can be implemented.

For our example with the buoys, the entire re-programming procedure could work as follows: The controller receives the radio command to erase the Flash memory with the data collection routines. It does execute the command, but never touches the reset sector. It now receives the new code via radio and programs it into the Flash memory.

In the worst case, the system has a malfunction right now, before the programming is complete. Let's assume the system restarts after a while: the code in the reset sector is still present and executes. It recognizes a checksum failure in the code sector handling the data collection. Using the radio link it transmits this status and waits to receive new re-programming commands.

Storing Variables in Flash

Many applications require variables whose content does not change after a power-down. One common implementation is the configuration of consumer electronics: for example a radio remembering the last used station, volume and other tone settings. In many applications, this kind of information is stored in EEPROMs.

However, assuming we have Flash memory in our application anyway, isn't there a way of easily storing this information in Flash?

Well, define "easy"… - just be aware of some of the limitations!

Flash erase cycles are long - really long - it can take several seconds to erase a Flash sector. Also as the number of guaranteed erase / re-write cycles is usually limited (typically around 10,000 or up to 100,000), we cannot afford to erase an entire sector just because one variable changed.

The approach is to "sacrifice" an entire sector for variable storage. In this sector, variables are stored in a table. If a variable changes, it does not get overwritten, instead the old value is discarded and a new entry into the table gets generated.

To speed up variable access, the application code will typically work with pointers. If you have a pointer to the variable, read access is very simple (just read from the location the pointer points to). A write access takes a little more: it would involve things like

  1. Generating a new entry at the end of the table
  2. Marking it as "current"
  3. Marking the old table entry as "expired"
  4. Returning a pointer to the new location of the variable

After a power-down / power-up cycle, the entries in the table need to provide enough information for an initializing routine to locate all current variables in that table.

One of the additional challenges of such a table-based implementation is the so-called "garbage collection". This is the process of cleaning up and re-initializing the table. Once the table is full, all current variables would need to be saved, the table / sector erased and the variables written back to the table.

As this process involves erasing an entire sector and parsing through a full-table, it can take several seconds to complete. In order to avoid any delays during regular operation, a system using Flash variables needs to plan ahead and execute the garbage collection before the end of the table is reached (for example as part of the boot-up or shut-down procedures).

Another challenge is a fail-safe implementation. For a fail-safe system, recovery needs to be possible, even if a system crashes or loses power while performing a garbage collection. This can only be implemented, if two Flash sectors are used. The garbage collection would proceed as follows:

  1. Erase the currently unused sector
  2. Move all current variables to the new sector
  3. Mark the new sector as "current" and the old one as "expired"
    (for example by making the first byte a special status byte)

After a power-down and power-up cycle, the table initialization routine would check the status byte of each Flash sector to determine which of the two sectors is the current one and then start to extract the current variables from that table.

If, however, a power failure or reset occurs between marking the new sector as current and the old sector as expired, then after a power-up there would appear to be two current Flash sectors. The solution is to identify which sector contains the smaller table and is therefore the "real" current sector. The other sector can then be marked as expired.

To recognize variables themselves and whether they are "current" or "expired", each table entry requires several status bits or bytes. Assuming the variables can be of different length, the following information needs to be stored with each table entry:

At first glance, a "current" or "expired" entry seems to be unnecessary - the initialization-variable-recovery routine could simply assume the last entry in the table as the "current" one. However, if the system failed while writing the last entry, then that should not be used at all. That's why it is important to not mark a new entry as current, unless all data bytes of that variable have been successfully programmed into the Flash memory.

To show in more detail how such a table works, the Embedded Systems Academy provides a C library with such an implementation. It allows reading and writing variables that are stored in Flash. For details, check ESAcademy's technical library at www.esacademy.com/faq/progs.

The library provides functions to store values, obtain a pointer to a value (necessary for fast and frequent accesses), determine when the table is full and a function to perform garbage collection, using fail-safe methods. The following is a short example of the start of an application using the library.

  1. #include "rx2fvlib.h"                        // include library
  2.                                              // header file
  3. void main(void)
  4. {
  5.   unsigned char code *flashbyte_ptr;         // pointer to flash
  6.                                              // variable w. status info
  7.   fv_init(16, BLOCK_0x2000_0x3FFF,           // initialize the Flash
  8.               BLOCK_0x4000_0x7FFF);          // Variables library
  9.   garbage_collector();                       // clean up the table
  10.   flashbyte_ptr = fv_read_byte(flashbyte);   // obtain pointer to the
  11.                                              // flash variable
  12.                                              // flashbyte
  13.   if (flashbyte_ptr == NULL)                 // if the pointer is null
  14.   {                                          // then the variable does
  15.                                              // not exist
  16.     fv_write_byte(0xAA, flashbyte);          // store default initial
  17.                                              // value
  18.     flashbyte_ptr = fv_read_byte(flashbyte); // obtain pointer to
  19.                                              // flash variable
  20.   }
  21.   switch(*flashbyte_ptr)                     // read status variable
  22.   {                                          // to recover system  

Flash Life-Time

Most Flash memories only guarantee a limited number of erase and re-write cycles. Typical values are guaranteed cycles of 10,000 times. Most parts will work far longer - it's just not guaranteed. So if your application counts on it - don't design it to do more than the maximum numbers of erase cycles over the entire lifetime of the product.

As long as this memory is used only for code storage it is very unlikely that an application will ever reach the maximum guaranteed erase cycles. Think about it - how often can you design new code to get uploaded?
However, if the Flash memory is used for data storage, the entire picture changes and we need to calculate the worst-case scenario.

As an example, let's pick a consumer electronics device that stores configuration settings with each power-down cycle. To optimize the code, only changes to the configuration data need to be stored.

Let's assume that the information is stored in a table as implemented by the Flash Variables Library as explained before. This implementation may be configured to use two 16k Flash sectors. If the configuration data occupies 32 bytes (including overhead bytes added by the library), then 1,000 entries can be made before both sectors have each been erased once. In total, 10 Million writes can be made, before the system reaches the maximum number of guaranteed erase cycles.

If we now assume our worst-case user is changing some configuration data (maybe including current volume setting) 100 times a day, the system could still operate for 273 years before reaching this maximum number of cycles.

Let's also do a different approach - how often can we change data if we want to achieve a product lifetime of 10 years? With the example from above, we have 10 Million writes, or divided by 10 years, one Million per year. That allows for about 2,700 writes per day or 114 per hour.

As a rule of thumb: if you just write 10 or so data bytes to the Flash Variable table a few times per minute, your application can run for 10 years, before the guaranteed maximum erase/re-write cycle time is reached.

Summary

Flash technology is constantly changing, providing faster program and erase cycles, a bigger number of guaranteed erase and re-program cycles and longer data retention. Flash technology put on-chip with microcontrollers has now reached the point, where the in-application usability greatly improved.

In the past, storing of configuration data that stays available after a power-down and power-up cycle required an additional EEPROM or other storage device. Today, this functionality can be provided by on-chip Flash, further decreasing the parts count of embedded applications: an additional external EEPROM or SRAM might not be required anymore.

Storing Variables in Flash

Many applications require variables whose content does not change after a power-down. One common implementation is the configuration of consumer electronics: for example a radio remembering the last used station, volume and other tone settings. In many applications, this kind of information is stored in EEPROMs.

However, assuming we have Flash memory in our application anyway, isn't there a way of easily storing this information in Flash?

Well, define "easy"… - just be aware of some of the limitations!

Flash erase cycles are long - really long - it can take several seconds to erase a Flash sector. Also as the number of guaranteed erase / re-write cycles is usually limited (typically around 10,000 or up to 100,000), we cannot afford to erase an entire sector just because one variable changed.

The approach is to "sacrifice" an entire sector for variable storage. In this sector, variables are stored in a table. If a variable changes, it does not get overwritten, instead the old value is discarded and a new entry into the table gets generated.

To speed up variable access, the application code will typically work with pointers. If you have a pointer to the variable, read access is very simple (just read from the location the pointer points to). A write access takes a little more: it would involve things like

  1. Generating a new entry at the end of the table
  2. Marking it as "current"
  3. Marking the old table entry as "expired"
  4. Returning a pointer to the new location of the variable

After a power-down / power-up cycle, the entries in the table need to provide enough information for an initializing routine to locate all current variables in that table.

One of the additional challenges of such a table-based implementation is the so-called "garbage collection". This is the process of cleaning up and re-initializing the table. Once the table is full, all current variables would need to be saved, the table / sector erased and the variables written back to the table.

As this process involves erasing an entire sector and parsing through a full-table, it can take several seconds to complete. In order to avoid any delays during regular operation, a system using Flash variables needs to plan ahead and execute the garbage collection before the end of the table is reached (for example as part of the boot-up or shut-down procedures).

Another challenge is a fail-safe implementation. For a fail-safe system, recovery needs to be possible, even if a system crashes or loses power while performing a garbage collection. This can only be implemented, if two Flash sectors are used. The garbage collection would proceed as follows:

  1. Erase the currently unused sector
  2. Move all current variables to the new sector
  3. Mark the new sector as "current" and the old one as "expired"
    (for example by making the first byte a special status byte)

After a power-down and power-up cycle, the table initialization routine would check the status byte of each Flash sector to determine which of the two sectors is the current one and then start to extract the current variables from that table.

If, however, a power failure or reset occurs between marking the new sector as current and the old sector as expired, then after a power-up there would appear to be two current Flash sectors. The solution is to identify which sector contains the smaller table and is therefore the "real" current sector. The other sector can then be marked as expired.

To recognize variables themselves and whether they are "current" or "expired", each table entry requires several status bits or bytes. Assuming the variables can be of different length, the following information needs to be stored with each table entry:

  • An identifier for the variable (for example this is variable "volume", "balance" or "tone")
  • Is this table entry "current" or "expired"
  • Variable length in number of bytes (assuming the length of the variables can vary)
  • The variable contents itself

At first glance, a "current" or "expired" entry seems to be unnecessary - the initialization-variable-recovery routine could simply assume the last entry in the table as the "current" one. However, if the system failed while writing the last entry, then that should not be used at all. That's why it is important to not mark a new entry as current, unless all data bytes of that variable have been successfully programmed into the Flash memory.

To show in more detail how such a table works, the Embedded Systems Academy provides a C library with such an implementation. It allows reading and writing variables that are stored in Flash. For details, check ESAcademy's technical library at www.esacademy.com/faq/progs.

The library provides functions to store values, obtain a pointer to a value (necessary for fast and frequent accesses), determine when the table is full and a function to perform garbage collection, using fail-safe methods. The following is a short example of the start of an application using the library.


#include "rx2fvlib.h"                        // include library 
// header file
void main(void)
{
unsigned char code *flashbyte_ptr; // pointer to flash
// variable w. status info

fv_init(16, BLOCK_0x2000_0x3FFF, // initialize the Flash
BLOCK_0x4000_0x7FFF); // Variables library

garbage_collector(); // clean up the table

flashbyte_ptr = fv_read_byte(flashbyte); // obtain pointer to the
// flash variable
// flashbyte
if (flashbyte_ptr == NULL) // if the pointer is null
{ // then the variable does
// not exist

fv_write_byte(0xAA, flashbyte); // store default initial
// value

flashbyte_ptr = fv_read_byte(flashbyte); // obtain pointer to
// flash variable
}

switch(*flashbyte_ptr) // read status variable
{ // to recover system