element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Raspberry Pi
  • Products
  • More
Raspberry Pi
Blog Raspberry Pico C SDK: reserve a Flash memory block for persistent storage
  • Blog
  • Forum
  • Documents
  • Quiz
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Raspberry Pi to participate - click to join for free!
Featured Articles
Announcing Pi
Technical Specifications
Raspberry Pi FAQs
Win a Pi
GPIO Pinout
Raspberry Pi Wishlist
Comparison Chart
Quiz
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 30 Aug 2023 8:02 PM Date Created
  • Views 8832 views
  • Likes 16 likes
  • Comments 33 comments
Related
Recommended
  • raspberry
  • pico_usbtmc_scpi
  • pico
  • c sdk

Raspberry Pico C SDK: reserve a Flash memory block for persistent storage

Jan Cumps
Jan Cumps
30 Aug 2023
Raspberry Pico C SDK: reserve a Flash memory block for persistent storage

When you develop firmware for a Raspberry Pico, there are cases where you want to store user data in Flash. Data that needs to survive a power-down. E.g: calibration data, user preferences, display Celsius/Fahrenheit … 
This blog shows how to reserve a section of Flash to store that data into. And to take care that it is not overwritten by newer, better (and also bigger) firmware.
image
image source:  ralphjy 

The Pico C SDK has an API for Flash read/write. You can access Flash, read content and change it. But that area (2048k on a Raspberry Pico) is by default used to store firmware.
You can use part of that Flash in your code, to persist user or device settings.
A naive approach is to define a fixed address near the end of the Flash (that is hopefully not used). Maybe you check the high-water-mark of the firmware code, and take care that you stay away from that. Because the last thing that you want is to overwrite code with your data.
But the other last thing that you want to avoid is that,  when you evolve your firmware, it overwrites your storage during firmware upload. Calibration data may get lost.

The approach that I'm suggesting here will take care that you have a dedicated block of Flash for persistent storage. 
And it will enforce its integrity. It alerts that development team when their new software version has grown so big that it would overwrite that persistent storage - destroying that valuable calibration data. It informs the development team in a very radical way: the firmware build will fail.

image

Additional bonus; your C code does not have to know the memory layout or Flash addresses. Everything is managed by the linker / loader script.

What we'll do in this post: steal 4k from the big Flash pool, and set it aside for persistent storage.

Wins:

  • the block is reserved at the end of the Flash block. When you try to build firmware that runs into this area, the firmware build will fail.
  • your C code does not have to know the memory locations and addresses. All config is done in the linker / loader script. That .ld script creates a symbol for the reserved Flash area; you can use that area in your code.
  • rigidity instead of magic addresses. The space used for persistent storage is not defined somewhere in some source or header file. It is pre-allocated. Tools can make it visible. The compiler understands it, the linker will enforce it.

Adapt the default SDK .ld file

Reserve the Memory

The Pico C SDK (as of version 1.5) has four .ld files. A default one, and a few variants (e.g.: one that loads flash code into RAM before executing). Most of our projects use the default script. You can tell CMake to use your own .ld during build:

pico_set_linker_script(PersistentStorage ${CMAKE_SOURCE_DIR}/memmap_custom.ld)
My Pico project (attached at the end of this blog!) is called PersistentStorage, and the custom script memmap_custom.ld. I used the default script as starting point. That script gives all available Flash to the linker / loader.

MEMORY
{
    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k
    RAM(rwx) : ORIGIN =  0x20000000, LENGTH = 256k
    SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
    SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
}

I will steal 4k from that Flash for the persistent storage of my firmware:

__PERSISTENT_STORAGE_LEN = 4k ;

MEMORY
{
    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k - __PERSISTENT_STORAGE_LEN
    FLASH_PERSISTENT(rw) : ORIGIN = 0x10000000 + (2048k - __PERSISTENT_STORAGE_LEN) , LENGTH = __PERSISTENT_STORAGE_LEN

You can see that I take 4k from FLASH, and create a new memory area FLASH_PERSISTENT. The script loads all our firmware in FLASH, so it will never use the new area to store code. It understands that it now has 2044k instead of 2048. The script does not give any instructions on how to load code or data in FLASH_PERSISTENT. So the binary will stay out of it. And debuggers and programmers will stay out of it too.

Make the start address available to C

Our memory layout knows where the persistent block is. It would be a shame if we'd have to also hard-code that address in our C code. Luckily, GCC can turn the address into a symbol that can be used in C.

Wins:

  • no two separate definitions that can get out of sync
  • if our code uses an unknown symbol, the build will fail. The tools protect us (again)

To make this work, we need to define a memory section, and create a symbol that represents the start of that section. If we take care that this is the first section that uses our FLASH_PERSISTENT memory block, we know that the variable will also represent the start address of our reserved area.

    .section_persisitent : {
        "ADDR_PERSISTENT" = .;  
    } > FLASH_PERSISTENT

The ADDR_PERSISTENT symbol will be available to our C code.

Validate that the C code knows the persistent block (without hardcoding)

If we declare the memory block as an external variable (that we will not declare anywhere in the code), the linker will resolve this to that physical Flash area:

flash_utils.h

Test:

#include <stdio.h>
#include "flash_utils.h"

int main()
{
    char addr[80];

    sprintf(addr, "address = %x", ds_get_address_persistent());
    return 0;
}

You see that we now have a #define that represents our block's start address. The rest of the code is there as a test case, and to make it visible in a debugger:

image

The address that we see is exactly the start location of our little protected Flash block. Confirmed by the memory map:

layout:

image

symbol available in C code:

image

The Pico Flash API can now be used to store and read data from the area. Data that you save in that block will be available after a power down.

fun fact: this design does not use any runtime resources: no code space, 0 clock ticks, no memory (except the flash area we want to reserve. 0 bits beyond that). The inline helper function does not generate machine code. If your goal is to reserve 4k of Flash, then the net resource use of the mechanism in this blog is: 4k of Flash is reserved.

Enjoy. Here is the VSCode project: PersistentStorage_20230901.zip

link to all posts

  • Sign in to reply

Top Comments

  • dougw
    dougw over 2 years ago +1
    Great blog. The idea should be useful for lots of modern MCUs, especially devices that have large FLASH onboard.
  • ggabe
    ggabe over 2 years ago in reply to Jan Cumps

    Yup, that sector would be gone in 5 days. Thumbs up for the FRAM.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 2 years ago in reply to ggabe

    an every 3 seconds write will be rough for Flash.

    According to the Pico's schematic, they use winbond flash.

    "Min. 100K Program-Erase cycles per sector"

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • DAB
    DAB over 2 years ago in reply to Jan Cumps

    Not yet, I go hit with a migraine, but I will get back to it.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • ggabe
    ggabe over 2 years ago

    I wonder how much is the reasonable wear-preventing write frequency. I’m about to either add an FRAM, or use the internal flash, for an every 3 second update. (It is to store auto zero values, so that the instrument can wake up immediately, instead of running through a 1 minute warmup cycle)

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • scottiebabe
    scottiebabe over 2 years ago in reply to Jan Cumps

    image

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
>
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube