9 years, 9 months ago.

How does this code avoid the flash read prohibition during flash programming?

I'm just curious why this code works. :) It apparently *does* work, but when I first looked at it I was skeptical because it doesn't seem to do anything to address the KL25Z restriction on simultaneous flash read/write access.

The KL25Z manual says that the flash controller will assert a bus error if it detects read activity anywhere in the flash memory map while an erase/write is going on. All of the other sample code I've seen for this elsewhere goes out of its way to deal with this restriction. In particular, everyone else says you have to explicitly set up linker instructions to put the flash programming executable code into RAM at run-time rather than letting it run from flash itself. The mbed linker doesn't appear to have any way to do this - it seems to unconditionally put all executable code into flash, with no way of instructing it otherwise.

And yet, this code seems to work just fine (as long as interrupts are disabled while it does its work - see my other post on that subject). I'm just curious how it avoids triggering the bus error. My only guess is that the whole of run_command() fits into processor cache and thus can do its work without triggering any instruction fetches on the data bus.

So, does this code reflect an intentional strategy to work around the read restriction? It might be nice to document that if so, since it would good to know what the restrictions are on the strategy. E.g., if my guess is right and processor caching is the secret sauce, it'd be good to know what the limits of that are, to help predict if changes to the code would expand it to the point it would no longer fit (and thus no longer work).

Anyway, whatever makes it work, I'm really glad you figured this out and published the code! Thanks!

Question relating to:

IAP code for Freescale platforms

1 Answer

9 years, 9 months ago.

Hey :)

I made this code just by grabbing the reference manual and making the required commands. So to be perfectly fair, I never took this into account, especially since I never have any problems with simultanious read/write. Since after starting the command it only keeps checking one register for one bit, you are correct that it might be automatically doing something with caching that command, on the other hand, I have my doubts if an M0 even has cache.

So from this it might make sense to put that last command in RAM. (I think it is possible to do it manually by memcopy'ing the function into RAM and then forcing it to run that function). You say:

Quote:

The KL25Z manual says that the flash controller will assert a bus error if it detects read activity anywhere in the flash memory map while an erase/write is going on.

The manual says:

Quote:

Reading from program flash memory space while a flash command is active (CCIF=0).

So this doesn't specifically say from the entire flash memory, but it does seem to indicate it. However a few lines earlier:

Quote:

The MCU must not read from the flash memory while commands are running (as evidenced by CCIF=0) on that block. Read data cannot be guaranteed from a flash block while any command is processing within that block.

Here it says specifically you may not read from the block being written/erased. (Which makes sense). And that we aren't doing, and is only required if you would re-program the flash writing section itself (So for example updating the bootloader).

I decided to have a quick check if it works on the K20, since I got that one too, it works on mbed compiler (well it isn't supposed to be accessible yet I think, but it works), and it is really different from the KLXX devices (for starters an M4F), so if it has some different options regarding bus errors, that one might show them. Of course they just had to change the peripheral name (possibly because it has some different options, possibly because they just hate people in general). So went for the quick and dirty option and defined some macros. After that it compiled, I ran it, and it worked fine.

So the TL;DR is: The reference manual doesn't seem to be that clear if you only aren't allowed to write from the currently used block, or the flash at all. When overwriting your own bootloader for example you definately need to move it to SRAM, but it isn't designed for that (atm). I'd be willing to check if I can move that single active command to the SRAM, but then I do need a situation where it actually shows that it is a problem.

Edit: I guess it might be related to the FMC:

Quote:

4-way, 4-set, 32-bit line size program flash memory cache for a total of sixteen 32-bit entries with invalidation control

So if that provides flash cache then that last instruction which waits until it is finished will not require actual flash reads. While an interrupt for example does require flash reads and causes a lockup. If this is correct the question is: Can I rely on this or would it be better to copy it to RAM anyway?

Accepted Answer

Thanks for taking the time to look into it so thoroughly. The manual does seem ambiguous on the rules about this. Since the code seems to work, the question of why it does work is somewhat academic; I was mostly asking to gain some confidence that it wasn't just working on a fluke and might break outside of the test setup.. (See my related post about interrupts - I did have the device start rebooting during the flash write when I had USB activity going on in the same program. Disabling interrupts during the run_command() loop fixed that.)

I actually took a stab at memcpy'ing the run_command() function into SRAM, but I couldn't get that working. I tried malloc'ing a block and memcpy'ing from run_command (there's no way in this C++ compiler to get the byte size of a function's code, as far as I can tell, so I just used a hardcoded 128 bytes, which seemed like a safe lower bound). That was all fine, but actually trying to call the code in the malloc'd block would always die - the processor just froze up at that point. I tried the same thing with a simple { return; } function with the same result. I gave up at that point, since the original code was already working.

posted by Mike R 18 Jul 2014