Flash-based Configuration Data Using The ld Linker

Here is an easy way to store and use configuration data in the flash memory of a microcontroller.

This places the configuration data at a specific absolute memory address and uses no special pragmas to enable read access.

This is a slight reworking and consolidation of what MCU on Eclipse suggests, and slightly easier than marking variables with __attribute__((section(".etwas"))) all the time, though that is useful in other cases. Also, the SEGGER J-Link debugger on my development kit was always erasing the configuration data (as I’d placed it between other code and data, at a pretty low flash address), which was the opposite of the problem MCU on Eclipse was having. So, I needed a way to protect specific flash regions from getting erased, as Kinetis Design Studio has no easy options for this.

The Problem

Let’s say you want to configure a microcontroller at runtime using some values in flash memory, and let’s say you have a C structure that looks like this:

You want the data associated with a single instance of ConfigBlock to be located somewhere specific.

The Solution

  1. Create a configuration block in the .ld linker script.
  2. Make sure the configuration block is sector-aligned, and uses a full flash sector.
  3. Put the configuration block at the end of flash memory, or beyond the highest code address that will be written.
  4. Define a linker symbol to refer to the configuration block.
  5. Declare a C symbol to refer to the configuration block.
  6. Write config values to flash memory using J-Link.
  7. Use the C symbol.
  8. Profit!

Create a configuration block in the linker script

Easy. Add a block definition to the MEMORY declaration in your linker script:

MEMORY
{
    m_config_block (R) : ORIGIN = 126K, LENGTH = 2K
}

The block is marked read-only, as flash memory is usually not memory mapped for write access.

For writing, you will have to use microcontroller-specific flash APIs provided by the manufacturer to clear + to set specific bytes in the flash memory.

This is what I want anyways, as I would only modify the configuration data during development or during the manufacturing process (i.e. when an SWD programmer is pogo-pinned onto the device).

Make sure configuration block is sector-aligned, and uses a full flash sector

The user guide for the microcontroller I’m using says the following:

Making sure the configuration block is sector-aligned and uses a full flash sector will save you from some headaches associated with the way flash memory is programmed. If you don’t align the config block, or if the config block straddles the boundary with program or data blocks, then the configuration block will likely get wiped out when the surrounding blocks are erased + written. This will violate the principle of least surprise, when time after time your config block is erased.

Using a full, sector-aligned block keeps other data out and prevents accidental overwrites / erasures.

This will not be the most efficient way to do things, if the device has very limited flash. However, limited-flash devices often have smaller sector sizes, so this should still work effectively.

Put the configuration block at the end of flash memory

When writing a program to my microcontroller, Eclipse + SEGGER J-Link seems to erase all of the flash sectors that contain code or initialized data.

In a previous version of my linker script, I had placed the configuration block between code and data, and the block straddled a flash sector boundary.

So what happened? J-Link wiped out the configuration block each time I tried to debug a newly-compiled program, because it had no sense that anything in my configuration block was data worth protecting, and it needed to wipe surrounding sectors in order to write the new program bytes.

To fix this, I pushed the configuration block to the end of flash memory, i.e. "ORIGIN = 126K, LENGTH = 2K", to get it out of harm’s way.

Because the configuration block now lives at the end of flash memory, J-Link happily ignores it completely when reflashing the chip.

Define a linker symbol to refer to the configuration block

Somewhere below the MEMORY declarations in the linker script, add the following:

/* Add symbol to provide location of the ConfigBlock data. */
configBlock = ORIGIN(m_config_block);

This tells the linker: “If the program uses a symbol called configBlock, it is placed at the memory location defined by the starting address of m_config_block“.

Declare a C symbol to refer to the configuration block

Write config values to flash memory using J-Link

Start up JLink.exe and use the mem32 and w4 commands to set configuration values into the config data block in flash memory:

SEGGER J-Link Commander V6.12a (Compiled Dec  2 2016 16:44:55)
DLL version V6.12a, compiled Dec  2 2016 16:44:26

Connecting to J-Link via USB...O.K.
Firmware: J-Link OpenSDA 2 compiled May  6 2016 11:04:17
Hardware version: V1.00
S/N: 621000000
VTref = 3.300V


Type "connect" to establish a target connection, '?' for help
J-Link>
Unknown command. '?' for help.
J-Link>connect
Please specify device / core. : LPC11E67
Type '?' for selection dialog
Device>
Please specify target interface:
  J) JTAG (Default)
  S) SWD
TIF>S
Specify target interface speed [kHz]. : 4000 kHz
Speed>
Device "LPC11E67" selected.


Found SWD-DP with ID 0x0BC11477
Found SWD-DP with ID 0x0BC11477
AP-IDR: 0x04770031, Type: AHB-AP
Found Cortex-M0 r0p1, Little endian.
FPUnit: 2 code (BP) slots and 0 literal slots
CoreSight components:
ROMTbl 0 @ F0002000
ROMTbl 0 [0]: FFFFE000, CID: B105900D, PID: 001BB932 MTB-M0+
ROMTbl 0 [1]: FFFFF000, CID: B105900D, PID: 0008E000 MTBDWT
ROMTbl 0 [2]: F00FD000, CID: B105100D, PID: 000BB4C0 ROM Table
ROMTbl 1 @ E00FF000
ROMTbl 1 [0]: FFF0F000, CID: B105E00D, PID: 000BB008 SCS
ROMTbl 1 [1]: FFF02000, CID: B105E00D, PID: 000BB00A DWT
ROMTbl 1 [2]: FFF03000, CID: B105E00D, PID: 000BB00B FPB
Cortex-M0 identified.
J-Link>mem32 0x1f800,4
0001F800 = FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF
J-Link>w4 0x1f800,0x0102aa55
Writing 0102AA55 -> 0001F800
J-Link>q
J-Link: Flash download: Flash programming performed for 1 range (2048 bytes)
J-Link: Flash download: Total time needed: 0.256s (Prepare: 0.166s, Compare: 0.009s, Erase: 0.014s, Program: 0.049s, Verify: 0.008s, Restore: 0.007s)

Use the C symbol

Using the C symbol causes it to be linked to the address provided by the same-named linker symbol.

In other words, the following code:

Will print:

ld-provided configBlock: 1F800
    deviceType: 0102aa55

All data from the ConfigBlock structure is being read from flash memory starting at address 0x1F800.

The deviceType value can now be set permanently in flash, and used across Debug sessions!

Another Example

linker.ld Script

MEMORY
{
    FLASH (rx) : ORIGIN = 0x1C000,    LENGTH = 0x63000
    CONFIG (r) : ORIGIN = 0x7f000,    LENGTH = 0x1000
    RAM  (rwx) : ORIGIN = 0x20002ef8, LENGTH = 0xd108
}

SECTIONS
{
    /* Factory configuration data */
    .config :
    {
        . = ALIGN(4);
        SERIAL_NUMBER = .;
        . += 16;
        PROVIDE(SOMETHING_ELSE = .);
    } > CONFIG
}

Using PROVIDE in the linker script causes the linker to emit the SOMETHING_ELSE symbol only when it is actually used in the C program.

C file

nm output

20003a74 D __data_end__
20002ef8 D __data_start__
         w __deregister_frame_info
...
0007f000 B SERIAL_NUMBER
00068334 T sniprintf
0005cc04 T snprintf
0007f010 B SOMETHING_ELSE

Notes

The above process seems to work pretty well using Kinetis Design Studio 3.2.0 + the following SEGGER J-Link versions:

SEGGER J-Link Commander V6.12a (Compiled Dec  2 2016 16:44:55)
DLL version V6.12a, compiled Dec  2 2016 16:44:26

Connecting to J-Link via USB...O.K.
Firmware: J-Link OpenSDA 2 compiled May  6 2016 11:04:17

As always, Your Mileage May Vary, if you’re using a different kind of chip or programmer setup (or Eclipse may always just wipe out the entire flash…)

More Notes

The above process will not work if you are using the mbed USB Mass Storage Device method of programming your device.

i.e. If you copy and paste a .hex file to an mbed-enabled board that exposes a USB Mass Storage Device, it will wipe all of flash.

You must use the loadfile command in JLink.exe, which will carefully avoid overwriting parts of flash that haven’t changed and should ignore the CONFIG block entirely (as there is no data at those addresses in the .hex file)

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.