I'm totally new to rust, and advanced beginner in embedded programming. I understood the concept of eXecute-in-Place for external flash: a bootloader
configures the MCU to access the external flash (SPI peripheral),
configures the external flash chip,
sets up memory-mapped mode,
passes the execution to the entry point of my program on the external flash
So far I'm believed capable to implement that in rust.
But how can I get my application into the external flash? Memory-mapped mode is everywhere reported as read-only (here, here).
I have no other memory to hold the whole application - no internal Flash or RAM big enough, no SD card attached.
So, I think, there's a need for some MCU-local application to receive the binary application and write it through the SPI interface into the external flash memory. Is this some kind of bridge between the JTAG/SWD interface and the external flash? Is there something like this already available in rust? Or is there any other common way of flashing external memory "through" the target controller and how fits that into my desktop rust toolchain? (I use VScode, rust-analyzer extension and probe-rs, if that matters.)
you don't need "big" RAM, as long your RAM is enough to hold one page of flash data (and a handful of CPU instructions), the debug probe can just download a small program to the RAM, and transfer the data page by page.
typical QSPI nor flash chips (unlike massive NAND flash devices), the page size is not that big at all, typically . in fact, although you have to erase the flash in sectors (usually multiple pages depending on the chip), most of them can be written one word a time, or even one byte a time, it's just you typically write a whole page using bulk transfer.
the XiP is mode normally configured by the SDK or bootloader, and when you flash your firmware, the device should not enter XiP mode.
but you don't need to do this low level thing most of the time, debug probes (jlink, stlink, etc, or in this case, probe-rs) will do it transparently for you. for example, probe-rs support most commonly used SoCs out of the box, you just tell it the chip model. even if you have a chip not supported, you can write a custom flashing program. see the list (with link to a flashing algorithm template project) here:
Ok, thank You very much. A little bit too deep already for me.
I understand about pages and sectors, my page size is 256 B, sector size is 4096 B.
Also, I know how to tell probe-rs my target (I wrote some small rust programs and can run them fine from the internal flash) and I see my target on probe-rs - targets. But there are only internal memory regions configured. No SPI configurations, neither MCU-side, nor memory-chip-side.
My external SPI flash has no memory region until it is configured in memory-mapped mode, and then it is read-only afaik.
So, I doubt, that probe-rs can magically program an external SPI flash. In case, it can, good news for me, but can You explain that concept any further? For instance, what entity is driving the SPI flash, i.e. sending write commands to it, when I send my binary via probe-rs?
Thanks a lot and hopefully I didn't miss something obvious. But that's why I'm asking more on the conceptual side of the whole mechanism.
it indeed can, depending on the chip models. for example, the rp2040, the microcontroller on the raspberry pi pico kit, can only use external flash: there's no internal flash at all, only a small boot rom which is mask in the factory and not re-programmable (at least not documented). probe-rs works just fine for rp2040, you just need to put the code at the XiP address range in the linker script.
the probe will first download a small stub program (probe-rs call it flash algorithm, j-link call it "RAMCode") into the ram, this program is target specific, and when it runs, it will transfer a chunk of data from a pre-configured ram buffer to the flash and halt. the probe then write the actual data to the buffer, and jump to the stub program, and repeat this process until all the data is transffered.
as for how the device is configured, it's just a bunch of peripheral registers, the probe can do this itself, or it can delegate to the stub program for it.
this process is very flexible and can be used for both internal and external flash memories.
Great news, I'm glad that what I didn't think was possible is true.
So, one question stays left: who/where/what configures my external SPI memory? Like, how many data lines, SPI mode, etc. Is there any minimal configuration that works with every memory chip? (I suppose for instance, single SPI, SDR,...)
Ok, in https://www.keil.com/appnotes/files/apnt_333.pdf on page 2 I also found a nice picture, how this is supposed to work. But, still, also with the now-learned term "flash-algorithm" I cannot find any flash algorithm for probe-rs, that supports SPI. Did I miss something? Where are the sources of the "magic" that enables probe-rs to flash to SPI memory?
if your hardware is not supported by probe-rs built in targets, the first step to try is to find a supported CMSIS pack that is close to your own hardwre configuration.
for example, probe-rs's built in target for nrf52840 contains only the internal 1MiB flash and the UICR, but not the XiP range:
yet the pack hosted on ARM's website does include the XiP range (note the 128MiB IROM1) which is mapped from an external QSPI flash:
in this case, you can simply download the CMSIS pack and extract the .flm files, which are just ELF files.
in rare cases, you cannot find a flash algorithm from the chip vendor, or your hardware is not compatible with the reference design, you can write your own flash algorithms. just use the project template, just fill in the routines to init, to erase a sector and erase all, and to write a page.
I don't think so. but it should not be a problem in practice. most chips should be compatible to common/standard interfaces, and SoC vendors should provide some way to flash programs already, as CMSIS pack, or maybe as a bootloader, or dfu firmware, etc.
I think we have a complete misunderstanding here. You talk about targets all the time? I have no question regarding the internal flash or RAM of any target processor - as said, I already wrote und run programs on my target with probe-rs. My question is about external SPI flash, connected to the target.
So, again: is there a flash algorithm for probe-rs that gets loaded into RAM, executed there and can handle external SPI memory, flash it? How can I use that flash algorithm? How can I tell it, what flash chip is connected to the target or the parameters of the memory and the SPI connection?
Searching all over, and probe-rs and SPI leads to no senseful answers anywere.
Can You confirm, that You really understood the question? It's not about flashing a target, it's about flashing memory connected to the target via SPI.
if you have studied the flashing process, you'll know what I mean.
the "flash algorithm" is always target dependent, it is called a "algorithm", but it is not some parameter you can set, it's actually a program that runs in the target RAM, (it is sometimes also called "flash loader").
the debug probe cannot directly access external memories, only the "flash algorithm" on the target knows how to talk to external hardware (and also internal rom memories too). different targets have different ways to communicate. some target might have special QSPI XiP controller, while some SoC might use its general SPI controller.
for example, the flash algorithm for rp2040 will uses the SSI peripheral (configured to SPI, DSPI or QSPI mode) to talk to the external rom chip, while the flash algorithm for nrf52840 has a dedicated QSPI controller for this purpose.
in other words, when you pass --chip xxxx command line flag to probe-rs, it looks up the "flash algorithm" in its built-in target list, and download this algorithm(program) to the target, only after that it starts to transfer the actual data.
if your device is not in the list, and you cannot find a compatible loader online, it's up to you to provide a working "flash algorithm".
I appreciate You willingness to help, thank You very much.
The target-dependency of the flash algorithm is totally clear, was already in my first post.
Let's take that example, nrf52840. It's similar to my target: it has an internal flash and RAM and a QSPI controller too. Can You point me to some concrete probe-rs flash algorithm for this target, that uses the QSPI interface? Because, somehow, I cannot find these. Maybe then I could understand better, how the memory-dependent parts of the configuration, that the flash algorithm has to do, come into play.
Ok, I think, I found the answer to the question of the memory-dependency.
If I use the probe-rs' target-gen tool cargo run --bin target-gen --release -- arm -f "nRF_DeviceFamilyPack" .
to stick with Your example, I get many target descriptions, as expected. And there are several different flash algorithms for each target, not "the flash algorithm". These different ones I can easily imagine to be memory-chip-dependent, and bingo, for the stm32h7xx targets - there are really plenty of flash algorithms for each target, and some are named like known SPI memory chips!
Now, their code is inside that yaml, seemingly encoded somehow, and behind that probably binary (judged by the small size).
So, this sheds light on the question, why probe-rs is totally agnostic of the interface between MCU and external memory, and why I didn't find anything meaningful with my "probe-rs SPI" searches.
The flash algorithm is brought from somewhere else, and in itself probably flash-application-independent? (Which I didn't expect.)
So, now I see, why I don't find a rust source for any flash algorithms (except the template). Probably most of them are written in C, right, and this is totally fine, even if one develops in rust? Maybe even some are closed source?
ok, I've always been using the "default" flash algorithms, as my hardware are mostly very close to the reference designs, so my previous posts just assumed you have a rare target SoC. sorry for the confusion. but looking into the docs of probe-rs, I can't find any options or configs how to select a non-"default" flash algorithm. maybe it's not implemented (yet)?
they are very short programs for the target, typically in the order of hunders of instructions long, they are extracted from the ELF binary files by target-gen. the encoding is just for the yaml format, which is text only.
it's standardized, part of the CMSIS-Pack spec, any debug probe with CMSIS-DAP support should also support the flash algorithms. so yes, it's flash application independent.
but this flashing scheme has been there for a long time, even before the CMSIS-Pack spec by ARM, different tools have their own implementations (and SDKs for custom loaders too), for example: j-link call them RAMCode, OpenOCD call them flash loaders, etc. probe-rs didn't re-invent the wheels and just use the standard CMSIS-Pack protocol.
the CMSIS-Pack flashing programs are binary blobs, and as far as I know, most vendor don't release the source code. OpenOCD does have the source code, but OpenOCD has its own driver/loader protocol which is different from the CMSIS-Pack spec. I don't knwo the details, but I think OpenOCD does the initialization by host driver code, and the loaders are only used for bulk data transfer, while the CMSIS-Pack delegate the initialization and sector erasing to the target program too.
as for example flash algorithms written in rust, I happen to know this repository for the esp32 series, but since it contains code for a lot different models in the series, the code base is a little bit complicated.
also I did a quick search in PRs of probe-rs, some PRs do have links to repositories for the flash algorithms, you may take a look. for instance, #1876 and #1852. the code doesn't necessarily use the probe-rs template, as long it implements the required ABI as defined in the CMSIS-Pack spec, target-gen can extract the code successfully.
Yeah, that was my next question too, how to select the right one. It could be easy, if for every memory range, there's only one flash algorithm, then it could be chosen by the memory range. But it isn't the case, for instance for the stm32h7xx targets, there are plenty of flash algorithms with the same address space.
Thank for the helpful links, I'll go through them.
Thanks anyway for Your help, pushed me in the right directions!
UPDATE: I found a flag default: true inside some flash algorithms in probe-rs/probe-rs/targets/STM32H7_Series.yaml at master · probe-rs/probe-rs · GitHub, and also in the template.yaml of the flash-algorithm-template of probe-rs. This seems the way to tell probe-rs which flash algorithm to take. In case, there are more than one defined, one gets an error message: Error: The flashing procedure failed [...] Trying to write flash, but found more than one suitable flash algorithims but none marked as default for NvmRegion { name: Some("FLASH"), range: 2415919104..2483027968, is_boot_memory: false, cores: ["main"], is_alias: false }. Similar, when more than one marked: ...but found more than one suitable flash loader algorithim marked as default for NvmRegion... This message is gone, when I set excatly one to default: true.