-
Notifications
You must be signed in to change notification settings - Fork 13.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Spi0command #6674
Spi0command #6674
Conversation
By preloading code into the flash cache we can take control over when SPI Flash reads will occur when code is executing. This can be useful where the timing of a section of code is extremely critical and we don't want random pauses to pull code in from the SPI flash chip. It can also be useful for code that accesses/uses SPI0 which is connected to the flash chip. Non interrupt handler code that is infrequently called but might otherwise require being in valuable IRAM - such as bit-banging I/O code or some code run at bootup can avoid being permanently in IRAM. Macros are provided to make precaching one or more blocks of code in any function easy.
With certain alignments/lengths of code it was possible to not read enough into the flash cache. This commit makes the length calculation clearer and adds an extra cache line to ensure we precache enough code.
The rom code does not support some flash functions, or have a generic way of sending custom commands to the flash chip. In particular XMC flash chips have a third status register, and the ROM only supports two. There are also certain requirements for using SPI0 such as waiting for the flash to be idle and not allowing your code to trigger a flash cache miss while using SPI0.
We needed to assess the SPI registers as base+offset to avoid referring to the registers using constant addresses as these addresses were loaded from flash and had the potential to trigger a flash cache miss. For similar reasons functions need to be called via function pointers stored in RAM. Also avoid constants in FLASH, use a copy stored in RAM. As a side effect we can now select which controller to access as a parameter.
@ChocolateFrogsNuts I see you take as arg an spiIfNum which can be only 0 or 1. I assume that is to select between normal spi and hspi. If that is correct, what is the meaning of the "0" in the function names? It confused me at first, I thought it indicated that the code was limited to only one of the two. EDIT: I think I understand now: the internals allow choosing either of the two, but SPI0Command hardcodes the arg to 0. I assume you did this because you have only tested with bus 0? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One last thing: this is experimental, I think it makes sense to put the code in a namespace that reflects as much, e. g. namespace experimental. That also aligns with usage I intend to put in play elsewhere for code that isn't meant to be used by users, the main reason being that backwards compatibility is not guaranteed in minor/sub releases for these functions.
Partly yes, it's only been tested with bus 0. Using the parameter also prevents the compiler from deciding the base address of the controller can be optimised as a constant. It was actually optimising the SPI register access back to being via constants loaded from flash (exactly what I was trying to avoid) until I made the parameter volatile (it worked out it was only ever set to 0 and optimised all the decision making and base+offset calculations away). |
I'll expand on the comments where possible over the next couple of days (some of the SPI registers don't actually have meaningful names/info beyond what I've called the variables) |
Added a number of comments to better explain the code and improved the formatting. Also renamed some variables for consistency.
Nice work.
I don't suppose you can somehow test this with bus 1? It's ok if not, but in that case please add a comment above SPI0Command that bus 0 is hardcoded because bus 1 isn't tested. I'm approving now. Please let me know once you're done with any additional comments that you can add (the more the merrier, remembering this stuff in the future is unlikely). I'll merge at that point. |
BTW, doesn't this supersede #6628 ? If so, maybe close that one and keep this one? |
6628 is required by this, and this branch was created from the branch for 6628 - which is why that code is showing in this PR. Anyway, merging #6628 then this should work or just merge this - your choice. EDIT: oh and I'm done with the comments now I think. |
About the xmc code, where do you intend to call it? I ask, because the features here are in the core, which isn't accessible from the bootloader, so if you intend to call from the bootloader, the code here has to be moved. |
I'm intending to call it from the core, most likely from user_init() in core_esp8266_main.cpp Do we even have a bootloader that runs when not using OTA updatable flash? Even if we do I'm thinking it needs to be kept to the bare minimum required to get the main firmware running. |
Yes, eboot. On bootup it checks if a new image is present and if so it copies it from the empty area to the start of the flash. This happens before the app is launched, that's why I asked: there is flash access before app bootup. |
Had a look at eboot - At this stage I think it is not included in non-OTA firmwares... but will look further until I'm sure. Also the issues with the XMC chips don't seem to show up during the initial firmware load from flash to the esp - possibly due to lower flash clock speed or some other factor such as WiFi not drawing power yet and/or no writes to flash. Basically I see no reason to call it from the boot loader as early in the app is sufficient, and there is a possibility the bootloader is not guaranteed to be part of the bootup process (still looking into that) |
Further to above: Thus it seems pointless to further complicate eboot by trying to move the spi0command/XMC specific code there as it would also make spi0command inaccessible to the app (ie it becomes less generically useful). |
eboot copies the new image found in the empty space over to address 0x0. That is the part that worries me, because per your approach, the flash driver would still be at 75% during that read/write process. For correct flash access, I would expect either a slower flash clock or the driver at 100% during eboot. |
The 75% drive conditions would exist for the actual load of eboot itself. The only way I can see to avoid that would be to set a slower flash speed initially, then upgrade it after the drive has been set. EDIT: I've never seen it struggle if wifi RF is at reduced power and there are no flash writes happening - conditions which hold up until the app gets into running user code. |
Does reading work at 75% with rf etc? |
ahh yes I see your point for OTA updates now... eboot works differently to the espressif bootloader in that it copies the new code, rather than executing it at its new address. OTA update is unlikely to involve a power cycle, just a soft reset. The drive output setting is maintained across this reset, so the flash chip will be running 100% drive during eboot. |
Reading seems to be 100% ok unless the RF is at full power or there are flash writes. |
Be very careful of the assumptions you're making there. Does the flash setting survive a hard reboot of the board via rst pin? Off the top of my head, I can think of two scenarios where eboot could be faced with having to update after a hard pin reset: receiving the ota image and immediately doing a deepsleep, which on wakeup is a pulse to the reset pin, and some combinations of development with the esp connected to a laptop (e. g. direct flashing to the empty area), where esptool could reset via rst pin. Also, consider a power outage or brownout etc after receiving the image. Unlikely, but wouldn't that potentially brick the esp? |
It definitely survives the reset after a usb upload, with reset by esptool... can't remember if I also tested with the reset button on the board, but I will do that (though I'm sure it's connected to the same rst line used by esptool). As far as I know the only thing to reset the drive setting to default is loss of power to the flash chip.
Deepsleep and the direct flashing scenario should be the same as rst then - no loss of power to the flash chip, so 100% drive is still enabled.
Potentially yes. At worst it would require a usb connection to either flash the new firmware directly and clear the eboot command from the RTC eeprom, or to have esptool enable 100% drive in the flash - the reset afterwards would then start eboot with 100% flash drive and copy the new firmware successfully. So we are down to a power outage between writing the eboot command to RTC and erasing it after completing the copy being a problem. Possibly ensuring the flash speed is reduced during the eboot copy would be better (and perhaps something that could be applied as a safety measure for all chips?) It should only take a couple of lines of code in eboot.... |
So maybe something like this in eboot? if(flashid == xmc && flashspeed >= 40mhz)
{
oldflashspeed = flashspeed;
flashspeed = 26Mhz;
}
... //do bin copy
flashspeed = oldflashspeed; ? |
pretty much what I was thinking... it looks like it's just save/set/restore a single register. |
I understand your thinking, and I'm of two minds there. I think at the minimum the worst case time delta for the copy operation would need to measured (so a bin near 1MB) before deciding. |
memcpy is not guaranteed to be safe (IRAM_ATTR or ROM) like I thought. As a bonus the for loop is guaranteed to do 32-bit wide transfers, unlike memcpy.
Test code based on the code in eboot, copying 1MB from address 0 of flash to 3MB into a 4MB flash at 40MHz SPI = 13937ms. Got some test code temporarily switching the the SPI frequency while doing the coyp above, but there's a slight hitch... switching 80->40 works, 40->20 works, 26->20 works, but for some reason 80->20 does not (wdt timeout during the copy). Fortunately 80->26 works, so we can work with that... Worst bit is testing can be difficult - sometimes it works perfectly with 75% drive at 80MHz flash, other times not.... but so far it always works if either the flash speed is reduced below 40 mhz or the drive is 100%. |
@ChocolateFrogsNuts I merged #6628 to get some exposure while we discuss this further. Could you please resolve the conflicts here? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Except for that enum, I think this can be merged as is. It is marked experimental, so we don't need to maintain compat, and that allows making changes in the future if needed. Once a final decision is made as to how to fully support xmc flash and similar, we can move it out of the namespace. In the meantime, it can help if anyone needs it.
Adds a function to execute generic SPI commands on SPI0.
This needs to be handled carefully as SPI0 is used to access the flash chip - so
precautions need to be taken to ensure the code doing it is precached() before
accessing the SPI controller.
This is part 2 of the solution to #6559