raspberrypi / pico-sdk

BSD 3-Clause "New" or "Revised" License
3.24k stars 837 forks source link

Support MRAM flash (no erase cmd, etc.)? #1714

Open maholli opened 1 month ago

maholli commented 1 month ago

I have an RP2040 booting from MRAM (non-volatile magnetic memory), specifically the MR2xHxx family of Everspin devices using a slightly modified boot2_generic_03h.S

Loading a uf2 (micropython, in my case) appears to work repeatedly. However, issues arise when using hardware/flash functions.

I plan to chase down obvious areas, such as the lack of JEDEC erase commands, and apply my own crude patches - but I'm curious if any official pico-sdk support is planned? MRAM offers unique benefits ("unlimited" endurance, 20yr retention) over traditional NOR flash. The RP2040 and its XIP-centric design could take advantage of these benefits to make it even more appealing for industrial applications.

Mikefly123 commented 1 month ago

We are also very interested in whether or not MRAM support will officially come in someday. Specifically we're looking at the ASxxxx204 family of chips but the functionalities should be roughly equivalent.

We've run into some of the same issues as @maholli and are also rifling around to see if there is a work around. If we can get the RP2040 booting from MRAM we have the funding to book radiation testing time for both high energy proton and heavy ion to shake down the performance of a combined RP2040 / MRAM system.

maholli commented 1 month ago

Hi @Mikefly123. I spent an evening on this and made decent progress. Posting my crude hotfixes below in case it helps you.

Bottom line: I have the rp2040 booting pico-sdr examples as well as full-fledged micropython from an MRAM device (MR20H40) and utilizing the unused MRAM as a filesystem. Everything works across power cycles and the bootloader properly recognizes there is off chip flash at boot.

Key changes made (let me know if you need more detail and I will circle back when I have time):

  1. Specify #define PICO_BOOT_STAGE2_CHOOSE_GENERIC_03H 1 in your boards definition file.
  2. Clock the QSPI bus very slow while you are first ironing kinks out. I used #define PICO_FLASH_SPI_CLKDIV 8 (also defined in boards definition file).
  3. In my case, set the flash size to #define PICO_FLASH_SIZE_BYTES (500 * 1024) in order to not overwrite itself on subsequent boots
  4. Don't forget to add a pull-up (10k ish) to the chip select line if you chip needs it (likely does).
  5. Not wanting to deal with unknown bits after an erase flash command (since these MRAM chips don't have an erase cmd), I threw a crude temporary fix into https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_flash/flash.c that writes all 0xFF to the range of memory as part of the flash_range_erase function call. Obviously you would want a better solution long term, and at the very least wrap it in some precompiler checks for MRAM builds. What I have for now looks something like this:

    void __no_inline_not_in_flash_func(flash_range_erase)(uint32_t flash_offs, size_t count) {
    ...
    uint8_t eraseBuff[count];
    for (int i = 0; i < count; ++i) {
       eraseBuff[i] = 0xff;
    }
    
    // No flash accesses after this point
    __compiler_memory_barrier();
    
    connect_internal_flash();
    flash_exit_xip();
    flash_range_erase(flash_offs, count, FLASH_BLOCK_SIZE, FLASH_BLOCK_ERASE_CMD);
    flash_range_program(flash_offs, eraseBuff, count);
    ...

What I haven't done (but someone should!) is take a proper approach towards MRAM implementation to take full advantage of the speed and endurance characteristics.

Good luck and let us know how it goes!

wrong-formatt commented 2 weeks ago

Hey @maholli! After reading your hotfixes and tinkering around, we managed to boot and successfully build and upload the firmware into our RP2040, which is currently booting from the same MRAM model!

IMG_2552 We really appreciate the insight that you shared with us. If you don't mind me asking, could you please explain a little more about the erase flash command that you created and give us a few pointers on how to create a more coherent erase function? I'm especially curious about what you meant by "wrapping it in the precompiler checks for MRAM builds."

Mikefly123 commented 2 weeks ago

To add a little more context on what we managed to get working:

  1. We managed to get one of our RP2040 equipped EPS boards running our PicoSDK Firmware with an Everspin MR25H40.
  2. We also got one of our RP2040 equipped flight controller boards running the PicoSDK Firmware with an Avalanche AS3016204-0108X0PSAY. a. As a fun convenience feature the Avalanche parts has a 4x the memory of the Everspin part at 16Mb, supports QSPI, and is the same footprint as SOIC-8 footprint as COTS flash (so we don't need the chimera footprint on PyCubed!)

As an extra tidbit:

maholli commented 1 week ago

@Mikefly123 ah yes what a silly mistake on my part. 512Kbytes == 4Mbits. Thank you for catching that.

@wrong-formatt glad my notes were able to help you! But to be clear about my crude flash_range_erase mods above: that chunk of code should never ever be used for anything beyond convincing yourself the MRAM chip is functioning. What I show there is dynamically allocating a byte array (of any size) each and every time flash_range_erase is called. This is a big no no. -- Now that you know your MRAM is working, you can likely put the entire flash_range_erase function back the way it was originally and see how your end application behaves. Like I mentioned prior, I preemptively added this avoid worrying about possible filesystem checks assuming the state of "erased blocks" for checksums or alignment or something. Give it a go and report back with what you find.

wrong-formatt commented 1 week ago

I forgot to mention that I never added the flash_range_erase function since I wasn't sure what it did at the time. The only changes we made to the firmware was changing the boot stage2 to Generic03h, clocking the QSPI bus to a slower value, and changing the flash size. Even if our firmware worked with these changes without adding the entire flash_range_erase function, how would we be able to check to see if there were any filesystem checks? I just want to make sure that everything is working as intended.

wrong-formatt commented 1 week ago

Also @maholli if you don't mind me asking. I just wanted to ask about building CircuitPython (I was looking through CircuitPython directories and saw your contributions to the PyCubed directory). We are currently trying to build CircuitPython on the flight controller board running the Avalanche AS3016204-0108X0PSAY (as Michael previously stated). However, I do not think CircuitPython supports the Avalanche MRAM (looking through the nvm.toml directory, the only existing supported MRAM I could find was the Everspin MR2xh40).

I have created my own custom .toml file specifically for the Avalanche AS3016204-0108X0PSAY following the .toml template that was supplied by Adafruit. However, when I flash the firmware.uf2 file onto the board, no drive shows up. I have also adjusted the pins and configured the other files within the custom board CircuitPython files. Is there anything you would suggest in order to resolve this issue? I really appreciate the help you've given us the entire way.

maholli commented 1 week ago

@wrong-formatt that's a question for the circuitpython repo.

Nevertheless, it's been years since I wrote the circuitpython MRAM driver and they've made lots of infrastructure changes since then so I don't have any insight for you, sorry! What I can say is that in its default configuration, your Avalanche part should behave exactly like the Everspin MRAM. Maybe try building for the MR2xh40 part (despite have the Avalance part installed) and see how it goes.