ST 32F417: Any way to make an SPI SRAM to look like normal RAM? - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: April 01, 2022, 09:51:15 pm
content: What I am after is trapping an invalid address access e.g. at 0x30000000 and reading or writing 4 bytes (a complete 32 bit word, to keep things simple) from an SPI SRAM.The invalid address trap does trap the access but do you get the address you attempted to access and (if writing) the data word?It is basically a virtual memory question.Obviously it will be rather slow, limited by my 21MHz max SPI clock, but a 2 megabyte/sec "RAM" is better than nothing and would be fine for many applications.Reading thishttps://www.keil.com/appnotes/files/apnt209.pdfand specifically the MemManage trap, there appears to be a mechanism to trap (via a bus fault interrupt) areadbecause you do get the address (which is not surprising since accessing an invalid memory location under a debugger does tell you the address accessed) but if you trap awriteyou do likewise get the address but seemingly can't retrieve the data you tried to write.I find this apparent shortcoming surprising, because much older CPUs have been capable of a complete virtual memory implementation.

author: agehall
author_rank: Frequent Contributor
timestamp: April 01, 2022, 10:14:02 pm
content: You would need an MMU to do this. At least if you want to do it efficiently. You could maybe emulate this by trapping the read/write, swapping in the memory and restarting the instruction that caused the error, but I’m not sure that would be a good solution.Does it have to be SPI SRAM? The memory controller in the device supports external SRAM, just not SPI-based ones…

author: ataradov
author_rank: Super Contributor
timestamp: April 01, 2022, 10:16:07 pm
content: EDIT: Oh, yes, to get the data you have to interpret the instruction that caused the fault. It is not that hard, there is a limited number of store instructions. But making access functions and calling them from the code directly would be more efficient, of course.And even with load instructions, you still need to interpret them and return correct result in a correct register.MMFAR contains the faulting address on both reads and writes. MemManage is always precise, so it is always possible to recover from the fault, so it is absolutely possible to do what you want to do.But what makes you think you will get anywhere close to 2 megabyte/sec? If you have a command + 4 byte address + data, then you will get ~400 kbytes/second at most.

author: langwadt
author_rank: Super Contributor
timestamp: April 01, 2022, 10:45:47 pm
content: Quote from: peter-h on April 01, 2022, 09:51:15 pmI think some of the STMF4s are capable of memory mapped QSPI memory

author: DavidAlfa
author_rank: Super Contributor
timestamp: April 02, 2022, 01:00:20 am
content: Only quad/octo-spi peripherals are able to do that, not the "normal" spi.Your mcu can do that using fsmc, but it's meant only for parallel interfaces.

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 02, 2022, 01:22:27 am
content: What are you really trying to attempt/solve with this approach?

author: bson
author_rank: Unknown
timestamp: April 02, 2022, 08:45:04 am
content: I believe you get a "BusFault on data access", either precise (known address) or imprecise (address unknown due to D-space buffering enabled).See the Armv7-M Architecture Reference Manual, page B1-553.BFAR at 0xe000ed38 contains the bus fault address.It's valid if bit 7 (BFARVALID) in the BFSR is set (byte 1 in the CFSR @ 0xe000ed28). Page B3-610.Read up on the bus fault registers... you may need to disable memory buffering (at least for D, maybe I) to get a valid address.Not sure how to determine whether it's a read or a write, other than grabbing the exception address and looking at the load/store instruction.If you decide to emulate it over SPI, don't forget to update the exception address to the next instruction, but things like LDM/STM might get tricky so hopefully you have only simple single-word LD/ST without increment or decrement.

author: peter-h
author_rank: Super Contributor
timestamp: April 02, 2022, 08:58:58 am
content: QuoteGood point - unless the usage context is sequential accessing within a long buffer.It is the 16k buffers in MbedTLS which have soaked up a lot of RAM in this project.The debugger reports the faulting address so it may be possible but how would you recover the value which you tried to write, when the bus fault happened? Only by decoding the faulting instruction.QuoteJust a lack of RAM in this project. Some bits which use a lot of RAM are not that performance critical.There are no spare I/O pins and certainly any parallel type RAM is out of the question.Instruction decoding would be potentially complex, especially as you have no control over which instructions the compiler might be using. I now recall doing this in the 1980s on a Z280. I had to decode the faulting instruction, sufficiently to determine its length i.e. where the next one started. And I was supporting only a DIV by zero fault, in a recoverable way.An alternative approach,nottransparent to the running code, would be possible if the SPI RAM was used as a "cache" i.e. one had say a local 16k buffer and merely transferred 16k blocks between that, and any number of 16k buffers in the SPI RAM. That would be a trivial DMA job. But that's nothing to do with the original question.

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 02, 2022, 05:05:12 pm
content: Quote from: peter-h on April 02, 2022, 08:58:58 amI see. This MCU supports PSRAM - unfortunately, only through the FSMC controller. It doesn't support QSPI as far as I can tell. (But I don't even know whether you would have had enough IOs for QSPI anyway.)OK then, if you need extra RAM, it's one thing. But why do you need to trap invalid addresses? Can you not implement this extra RAM access through a dedicated software interface instead of making it look like regular memory access? This would be by far your easiest bet here?

author: DavidAlfa
author_rank: Super Contributor
timestamp: April 02, 2022, 06:00:46 pm
content: This seems the situation where start making buffers everywhere and run out of memory.There's nothing you could allocate and destroy on demand? That would save huge ammounts of memory.Might be slower but still much faster than spi psram.

author: bson
author_rank: Unknown
timestamp: April 02, 2022, 07:17:10 pm
content: Quote from: SiliconWizard on April 02, 2022, 05:05:12 pmCode reuse? Especially if it's library code or shared with other projects. Keeps from going down the rathole of messing it up with a bunch of data access wrappers and stuff. Emulation is perfectly reasonable if it's either rare, or performance doesn't matter.Here's a thought... in the busfault exception handler, if there's an imprecise fault, disable the D-cache and return. This will re-execute the instruction with the cache disabled, faulting again and this time giving you the bus address. Then re-enable the cache, decode the instruction and advance the exception return address. Finally fill in the register on the stack (load from address) or do something with it (store to address). You probably also need to decode the data width and handle alignment.You can also back it with a cache buffer in SRAM.

author: peter-h
author_rank: Super Contributor
timestamp: April 02, 2022, 08:57:17 pm
content: Yes a big chunk of this issue is library code.For example the ST supplied ETH libs contain a number of buffers - about 10k. Can't get rid of these. And so it goes on. The code I/we wrote uses minimal RAM. I reckon the stuff I wrote over the past year and a half uses less than 10k. Sure, I use stack vars inside functions, and use the CCM freely during boot-up. All the usual stuff one does when writing in asmMbedTLS uses about 50k and lots of people complain about this. If one is implementing only e.g. a TLS client then one can reduce this quite a lot.It would have been good to get say 128k bytes of RAM this way (e.g. N01S818HA) but I can see it won't be simple if one has to do a lot of instruction decoding. An ESP32 can use these directly.I am actually surprised that nobody has already done this, because in many applications these chips are short of RAM. And SPI1 can run at 42MHz.

author: hans
author_rank: Super Contributor
timestamp: April 02, 2022, 09:08:00 pm
content: IIRC STM32 Eth library uses 5 ethernet frame buffers. Perhaps something that could be decreased if you don't need the throughput or burst length? Could save significant amount of space.------@bson: What about the PC accuracy of the imprecise fault? It could be some cycles before the memory pipeline faults for the storing of data (because, eventually, the destination store address has become known). Meanwhile the CPU pipeline has gone over to manipulate data in registers (potentially even the same data register, introducing an inverse data hazard, I suppose write-before-read, read-before-write or write-before-write)Perhaps I'm zooming too much in from a CPU pipeline design, in which there is no single PC active, but rather a PC "in flight" for each pipeline stage (instead of in a programmer's model, where there is only a single PC).Or hopefully the ARM CPU flushes (instead of stalls) the whole pipeline when a fault occurs (as the register writeback probably occurs even 1 cycle later than the memory fault), so that this hazard is actually a non-issue or even resolved..But running the CPU with write buffering would decrease overall performance, obviously.However, I think the overall performance of this implementation would be rather slow anyway.. Just imagine the instruction decoding and storing the result to the correct data register. Sounds like quite a few instructions, which perhaps could be processed during SPI transactions, to figure out what to do in the end.But if it's all that's needed - obviously performance concerns are a non-issue then.

author: DavidAlfa
author_rank: Super Contributor
timestamp: April 02, 2022, 09:38:24 pm
content: With SPI @ 42MHz, you get at best 5Mbytes/s (Add addressing overhead, etc), instead 640MB/s from the internal 32-bit sram @168MHz.In that moment, better you get something else. It's like racing a Ferrari with a broken gas pedal.Has 500hp, but you can only use 50. What's the point?

author: peter-h
author_rank: Super Contributor
timestamp: April 03, 2022, 08:39:03 am
content: The point is that if you have a 100k lump of library code, C source but practically impossible to do anything with, which wants 100k of RAM, but isn't hugely performance critical, you could solve it with this. Also nearly all the RAM will be in buffers which will be relatively very rarely accessed. The normal variables will still be in the usual places.You could not use DMA with this method, however.BTW, does the 32F4 fault onDMAaccessing invalid memory?

author: DavidAlfa
author_rank: Super Contributor
timestamp: April 03, 2022, 09:08:04 am
content: I think you'll get Tranfer Error flag (TEIF), check "Error management" in DMA section RM.With your current needs, you'd better try to migrate to higher series (ex. 429) which have FMC and can be connected to SDRAM or either get a higher pin count 417 which allows connecting a small PSRAM.If still taking the SPI SRAM way, it would be that hard.- Create typedef structures for each function requirements- Partition the sram in different sections, declaring a base address for each function.- Declare a scratchpad to store chunks of the SRAM (Read-modify-write model)- Making Sram read/write handlers for- Reading/writing complete structures or SRAM partitions- Reading/writing single values- Then each function would need to refresh the scratchpad when entering, then access it using a struct pointer.

author: hans
author_rank: Super Contributor
timestamp: April 03, 2022, 10:28:44 am
content: I was interested to see how hard it is implement to a rudimentary virtual memory trap... Good news is my CPU pipeline worries of yesterday were unfounded.I had todisable write buffering(only works on Cortex-M3/M4 by the way - the M7 has a different store mechanism). This inevitably slows down all store instructions, as they have become synchronous. This however did resolve the IMPRECISERR to PRECISERR busfaults, which occured on stores.Attached vmem.c and main.cpp(GCC O3):Code:uint32_t variable = 0;uint32_t bm_mem(volatile uint32_t* ptr) {uint32_t cyc0_vmem = DWT->CYCCNT;uint32_t cnt = 0;for (auto i = 0; i < 100000; i++) {*ptr = cnt * 2;cnt = *ptr + 1;}uint32_t cyc1_vmem = DWT->CYCCNT;return cyc1_vmem - cyc0_vmem;}int main(void) {DWT->CTRL |= 1; // enable CYCCNTvolatile uint32_t* ACTLR = reinterpret_cast<volatile uint32_t*>(0xE000E008);*ACTLR |= 1<<1; // set DISDEFWBUF, https://interrupt.memfault.com/blog/cortex-m-fault-debugvolatile uint32_t* ptr_vmem = reinterpret_cast<volatile uint32_t*>(0x30000000);volatile uint32_t* ptr_sp = reinterpret_cast<volatile uint32_t*>(&variable);volatile uint32_t t_vmem = bm_mem(ptr_vmem);volatile uint32_t t_sp = bm_mem(ptr_sp);for(;;);}It seems to work.. but I only tested the rudimentary 16-bit ldr/str instructions with immediate offsets (and also admittedly, only for offset 0, but the BFAR should give the correct address) and on r3 (not sure if r4-r7 work). The opcode for register offset is also in there, but untested. No post inc/decrement also... (I think that's a 32-bit Thumb opcode).In terms of performance.. this is without actual external memory.. 100k load/stores:t_sp = 1400003 (14 cycles/iteration)t_vmem = 31500003 (315 cycles/iteration)t_sp decreases to 12cycles/iteration if I run it before bm_mem(ptr_vmem);So it's about 26-27x slower (incl. toy calculations) on this 16-bit Thumb opcode. Probably slower if also 32-bit Thumb opcodes are handled since they require more decoding and operations.Just this example is IMO nottoo bad.. (168MHz/27=6.2MHz, and if the use-case is really sporadic, then it's OK). The problem is that probably a SPI access will totally kill any speed that's left.E.g. 14 cycles for 1R 1W take 83ns @ 168MHz. Blow it up to 315 cycles (1875ns) + 2x SPI 40-bit transactions @ 40MHz (2000-3000ns).. I suppose a slowdown in the order of 100x is not unfeasible. Question if that is *good enough*. The code I post is only proof of concept..Also don't know how comfortable I would be running an application with application code in HardFault_Handler though.(For a more permanent solution, the F446 has QSPI memory interface with memory mapped mode, but unfortunately no integrated ethernet)edit:Had a thought about speed..Normally, memory load/store should take 1 cycle. It's done twice in the 12 cycle benchmark, so 10 cycles overhead for the loop and toy calculation. With 4bytes/cycle, 168MHz, that's 672MB/s throughput.The 315 cycles for the memory trap routine then means an overhead of almost 150 cycles per load/store instruction. So by that definition it's already 150x slower (about 1us @ 168MHz).If a single SPI memory access takes 1us (40-bit @ 40MHz), then that would double access time.Or be approx 320x slower than regular SRAM.. or about 2.1MB/s.FSMC can easily reach dozens of MB/s (16-bit 60MHz=120MB/s). QuadSPI can also reach several dozen MB/s (4-bit SDR 50MHz = 25MB/s). So in that respect QuadSPI is already quite a big slowdown..

author: DC1MC
author_rank: Super Contributor
timestamp: April 03, 2022, 10:33:52 am
content: Is time to implement virtual memory/paging or, if no MMU is available, the DOS solution: overlays, I still have flashbacks from Borland's TurboX compilers.

author: peter-h
author_rank: Super Contributor
timestamp: April 03, 2022, 11:07:16 am
content: That's awesome work, HansThank you.QuoteCan it not be emulated transparently, so in effect the invalid address access generates an interrupt which emulates a real memory access?This looks like an amazing project. The ESP32 designers thought it was worthwhile enough to implement it in hardware, which is probably quite an easy state machine (or microcode).Re the suggestions on paging, yes, I was doing that in the Z80/Z180 days, but there you had say 1MB physical RAM and you selected a 4k bank (typically the top 4k of the 64k address space) with an 8-bit bank # register. One never actually copied data.IAR implemented this in their Z180 compiler, "large model", but for code only, and you could not have a function bigger than the bank size.This cannot be done in a 32F4 because you don't have the address lines available, and if you did (because you aren't using all the pins for GPIO, ETH, USB, etc) then this is all moot. What might work ok is if you had multiple RTOS threads and every time the RTOS switches to Thread X it invokes a DMA transfer to save/restore the memory contents from the SPI RAM. You would need to hook into the RTOS...

author: DavidAlfa
author_rank: Super Contributor
timestamp: April 03, 2022, 11:56:12 am
content: What about upgrading to a 427/429/469? You get 256-384KB of RAM.You can get quad-spi starting with the 446.I still suck at handling pointers, addressing and casting, made some terrible stuff as concept.I gave up on the details, also ain't my job, feel free to fix the addressing/packing/alingment and all kind of illegal stuff you might findhttps://onlinegdb.com/HEQk6RVPE

author: Siwastaja
author_rank: Super Contributor
timestamp: April 03, 2022, 01:36:05 pm
content: Generic solution will be crappy enough (i.e.: too slow; all new projects would just use enough memory to begin with!) to not see any use beside this single time. Hence, don't invest time in generic solution.Instead, find the biggest/simplest non timing critical memory buffer, which is large enough to matter, but only accessed in a few places, and just modify / write custom access code for that, replacing those few accesses with whatever SPI access code.

author: peter-h
author_rank: Super Contributor
timestamp: April 03, 2022, 05:11:25 pm
content: QuoteI had a quick look. 32F417 to 32F437 is an extra 64k. That would actually do it.The Q is software compatibility. The 100QFPpinoutappears identical, so such a change, with no PCB change, appears possible.Surprisingly, google doesn't find anything in the way of a "design change guide". It would take many hours just to compare the 300 page data sheets, let alone compare the RMs. I am sure many have been up this path.But this is just for a little extra RAM. A virtual memory implementation would give you far more.

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 03, 2022, 05:28:26 pm
content: I would expect a rather high level of compatibility, as long as the 437 has the peripherals you need, which shouldn't take too long to check. That sure looks like the easiest path IMO while not requiring to jump through hoops. Did you use STM32's HAL? Or something else?

author: peter-h
author_rank: Super Contributor
timestamp: April 03, 2022, 06:33:33 pm
content: Someone else (working 1 day a week) set up the Cube IDE project ~ 3 years ago. I took it over almost full-time 1.5 years ago and had to re-learn C, plus a huge curve on the 32F417, Cube, etc.HAL functions were used quite a lot but I replaced a lot of them. They work but sometimes they are way too slow. They are still used for the alternate pin function (AFx) setups. The AF mapping must change, due to more peripherals. I rarely use HAL funcs now - too horrible.I have hard-coded memory regions in a few places but that's ok.This is off topic for here but I guess one has to re-setup the whole Cube project with libs for the new CPU. That is likely to open a whole new can of worms (code testing).The biggest gotchas are likely to be in subtle changes e.g. DMA config register behaviour. The 437 (i.e. going for the same 1MB FLASH space) is likely to have a different set of silicon bugs, too.Lots and lots of code will not work anymore, and looking at how often I have spent literally days on one tiny piece of code (e.g. shutting down ETH - see other thread - to save power for the SPI FLASH data save function), often involving testing and verification with a scope, I don't fancy it. The virtual memory invalid address trap solution, or even an RTOS thread based virtual memory scheme where a whole bank is copied to/from SPI SRAM, would be an isolated solution which would avoid re-testing everything else. Plus I now have a nice stock of the 32F417 which took a year to find

author: nctnico
author_rank: Super Contributor
timestamp: April 03, 2022, 06:47:18 pm
content: Isn't there some kind of document to support moving vertically through a line of microcontrollers from ST? The chips may not even be different at all. Just some fuses to set the various options when the chips are being tested / packaged. IMHO the whole point of having a range of related microcontrollers is that you can move through these vertically while remaining code compatibility.

author: peter-h
author_rank: Super Contributor
timestamp: April 03, 2022, 07:32:51 pm
content: I've not found one.Fuse-programming different features and creating a huge range of chips that way is standard for marketing differentiation, and always has been (while causing huge problems with sourcing, because you end up running around trying to find one part among 50 very slightly different ones) but if the 417 was no more than a reconfigured 437, that would be a whole new ballgameI asked here:https://community.st.com/s/question/0D53W00001TGJ54SAH/are-there-design-change-guides-eg-32f417-to-32f437

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 03, 2022, 07:52:59 pm
content: Quote from: peter-h on April 03, 2022, 06:33:33 pmYep I see. This is clearly not your ideal situation, as your project looks like a stack of ad-hoc stuff for a number of reasons, so porting it to another target, even one that is pretty close, is going to be tough.I'd highly suggest trying to design something in a more "portable" way in the future, that's always what I try to do. But this time you are stuck with this.Unfortunately, I do not know low-level ARM well enough to help here. I'd have no problem doing the same with RISC-V. Keep in mind that if you manage to do this, the access will be pretty slow. I know you said that would be ok, but make sure of that.An alternative approach would be to define a few smaller intermediate buffers in main RAM, and pass those to your library functions. Then you can hand-implement some simple caching scheme to update from/to those buffers with your external RAM. That would require some hand management of memory, and would probably depend on how the libraries you use handle memory to begin with. But caching memory like this would also be more efficient than intercepting each access in an exception. Now if all libraries you use require fixed, static memory buffers of a "large" size, then it's not going to be workable this way.

author: wek
author_rank: Frequent Contributor
timestamp: April 03, 2022, 08:08:54 pm
content: Quote from: peter-h on April 03, 2022, 05:11:25 pmAN4547QuoteThe RM is identical, RM0090.The '42x/43x are almost perfectly upwards compatible to '40x/41x. I actually do use this combination. The only factual exception I know of, is the TEMP/VBAT arrangement in ADC, both a different MUX input and different VBAT divider. And then there's an annoyance, as FSMC is renamed to FMC, and it has a difference in some very nuanced details of its behaviour (have ranted on STM32 forum on this, am lazy to look it up at the moment).QuoteNo, they are two different dices.JW

author: peter-h
author_rank: Super Contributor
timestamp: April 03, 2022, 08:25:21 pm
content: I got a little worried about this in AN4547 but fortunately it is a typoIt should be 192k, not 128k

author: hans
author_rank: Super Contributor
timestamp: April 03, 2022, 08:51:34 pm
content: AFAIK the STM32F42x has a bunch of new peripherals like LCD controller and DMA2D, but also justmoreof the same peripherals. So perhaps the pinmux is slightly different. Backporting a F42x project to F40x sounds tougher if you're unlucky.Quote from: peter-h on April 03, 2022, 11:07:16 amYes it can.. that's what the code is basically already doing. Stepping over the ptr accesses doesn't trigger any breakpoint or hint that it's handled by a HardFault handler. In principle, some hardfaults are recoverable, so perhaps that's what the ARM CPU designers had in mind when they wrote that.The issue is that this has a very dramatic speed vs robustness trade off. If you want to add more sanity checks to the routine, then that will always cost more cycles. For example, the code I posted naively assumes that all hardfaults are the memory redirects. What if a genuine fault is generated (e.g. div by zero)? More checks would be needed to check the relevant hardfault/usage registers (and if it's not a memory redirect, then write debug trace, lock up, reset the program, etc.). More checks = bigger slowdown, even on top of what it already is.Also the code I posted naively assumes all accesses are 32-bit wide. It sounds like that was fine, but what if it's not? Also, unaligned/aligned memory access is something to think about (Cortex M3/M4/M7 supports this, but M0+ does not, and AFAIK on RISC-V it's not mandatory). For a SPI ram that's byte addressable it's probably fine, but the way I addresses the 32-bit vmem array, it won't be... Not all register indices are tested. Unfortunately due the way how exception entry/leave works, the r0-r3 registers are in a different place than the r4-r11 registers. I hope they are in the right place, with the right arithmetic, and with the right handling of the stack push/pop.. It's all quite fiddly IMO, but I was just happy to get somethingworkingthis morning to give an impression how fast/slow it isIn terms of "generating" an interrupt: you could use a full software-triggered interrupt for that like a SVcall, however, that may be in use by some RTOS's. In this case you may also be fine by using a C function callback instead. After all, the hardfault context is already an interrupt..QuoteThat sounds like a complex solution to modify the RTOS to change it's behaviour for this special "task". I assume you'd swap out the application RAM data with the 'proprietary' protocol data, and then proceed to run the protocol code like normal.Then if that task is done, swap the application data back in.I imagine you would also need to block the application tasks while you're swapping things around, which doesn't sound trivial nor optimal.Also the swapping is a write/read transaction for a pretty large block of memory. Say you would be (re)storing 4K of memory: that's 32768 bits to transfer twice, taking 1.64ms. Hoping also that any processor time for that time is continuous, as switching back is very costly.A context switch on this STM chip is probably in the order of single-digit microseconds

author: peter-h
author_rank: Super Contributor
timestamp: April 04, 2022, 07:00:28 am
content: Decoding instructions is probably not that slow once one goes about it fully. It tends to be table driven, using an index. Many emulators have been written over the years and the performance loss tends to be around 10x. That's what PHP/Java doesOne can make it a lot less by coding the OS and its API calls to run natively.I think it would work ok because it would get used only for accessing large buffers, so perhaps only one line of code out of hundreds would get the hit.Then somebody could get more clever and do a proper VM implementation, which have existed since the 1960s, where you swap memory to disk. In this case you would have a 2 megabyte/sec disk which is far faster than the old days. In 1991 I worked on a Sun Sparcstation which had state of the art SCSI disks doing 1 megabyte/sec

author: hans
author_rank: Super Contributor
timestamp: April 04, 2022, 07:35:28 am
content: Oh for sure a table driven approach is better.. then it only needs a 32 and 128 entry function pointer table to decode the opcode group/instruction type. Those 640 bytes of LUT is totally worth it for performance.The trouble I was talking about, was 32-bit instructions like conditional execution, post inc/dec (then you also need to read and modify the address register/immediate), etc.I think in particular conditional execution is harder, since in ARM and Thumb instructions it's handled differently. AFAIK 16-bit Thumb uses the If-Then-Else instruction to encode conditionality of upcoming opcodes. I think ARM instruction set uses conditional flags at the front of the instruction, which would be easier to decode on the fly. In other words, in Thumb mode the CPU has state and I don't know if the HardFault handler can access that state easily (I suppose it need to be able to, otherwise context switching is hard).Nonetheless, more fiddly-ness...

author: peter-h
author_rank: Super Contributor
timestamp: April 04, 2022, 08:12:41 am
content: Isn't STM 32F all thumb?

author: hans
author_rank: Super Contributor
timestamp: April 04, 2022, 02:49:44 pm
content: Yes.. ARMv7 uses Thumb2.. The first 5 bits of the opcode indicate whether it's 16 or 32-bit instruction.Older ARM MCUs like ARM7TDMI used an older Thumb instruction set, alongside ARM instructions, which required interworking code to switch.Nonetheless, conditional execution may require some attention. Maybe some of the opcode decoding can be optimized with some kind of Just-In-Time "emulation", where instructions decode results are cached in a hashmap (with PC as key), so preceding instructions only need to be analyzed once... but that also requires (a lot) more RAM to do so.

author: abyrvalg
author_rank: Frequent Contributor
timestamp: April 04, 2022, 11:50:23 pm
content: Conditional execution shouldn’t be a problem, if an instruction triggers a fault - the condition was true. But there are many other kinds of fun - LDRD/STRD, LDM/STM with 4 modes of pre/post increment/decrement and reglists, and the whole zooo of DSP/FPU instructions loading/storing their own sets of registers.Writing code to handle all this to get a solution orders of magnitude slower than normal instead of adapting the code to use SPI RAM directly looks like masochism. If the OP needs help implementing this mess anyway, why don’t just ask for help adapting the library instead?

author: peter-h
author_rank: Super Contributor
timestamp: April 05, 2022, 09:58:26 am
content: I don't think it is quite as bad, because you need to support only the instructions which are going to be used to access this memory area, and all other fault reasons can be trapped collectively (and should not happen in a running system because of they do, it crashes / watchdog trips).In many/most situations where somebody wants to do this, the performance hit would be small. Admittedly if somebody does a strstr() on the said memory area, and ends up searching 100k bytes, that isn't so good. But you can check the source if it does that.

author: abyrvalg
author_rank: Frequent Contributor
timestamp: April 05, 2022, 11:50:46 am
content: I haven’t seen much DSP/FPU Cortex-M code in the wild, but integer code actively uses all flavors of load/store (i.e. just a single optimized memcpy/memset does the bulk part with STMIA and finishes the tail with STR, STRH, STRB). Are you going to do some code coverage analysis to find out exact instructions accessing your virtual buffer? What if that changes after next compilation? Or after a compiler upgrade?What’s the typical pattern of calling the library that needs more memory? Implementing an overlay can be trivial in cases like calling a few top level functions of mem-hungry lib - just wrap those functions and add swapping to/from external RAM before/after the real call.

author: bson
author_rank: Unknown
timestamp: April 06, 2022, 02:12:44 am
content: Quote from: hans on April 03, 2022, 10:28:44 amNice!Try my suggestion as well: if you get an IMPRECISERR busfault, disable buffering and return from the exception. This will execute the faulting instruction again, this time without buffering. Then proceed as normal and reenable buffering when done.

author: ataradov
author_rank: Super Contributor
timestamp: April 06, 2022, 02:18:14 am
content: Quote from: bson on April 06, 2022, 02:12:44 amYou can't do this. Once you get an imprecise exception, the only valid way to recover from it is a reset.The whole point of it being imprecise is that your code runs past the faulting place, sometimes quite a few instructions. In case of cache issues, it may be a lot of instructions. Imprecise exception is not even necessarily caused by an instruction.

author: hans
author_rank: Super Contributor
timestamp: April 06, 2022, 08:25:40 am
content: I agree. The PC points to instructions that are 2 ahead in my observations, but obviously it depends on the pipeline and if any other stalls occur. If there is a hazard like a write-before-write (instead of after, since PC has incremented already), then we would reexecute the same opcode with different register contents.For example:Code:STR R3,  # R7 = virtual memory addressADD R3, R3, 1 # increment R3SLL R3, R3, 1 # R3 << 1NOP # ..etc..If the STR triggers an IMPRECISERR, then the PC may have changed an arbitrary amount. We could disable buffering and retry, but in the meantime the contents of R3 has changed.@abyrvalg: Yes you're right, I was thinking too much from a RTL design again, and not the software perspective. It doesn't make sense that the trap would get triggered if the conditional code says skip execution.In terms of DSP code: modern GCC with O3 isnotincapable of producing DSP/SIMD code by itself. I've seen it happen with certain algorithms where it would eventually resort over to vector instructions. It can sometimes also do that for memcpy/memset procedures. I was very surprised..In terms of code profiling to support the right instructions.. sounds a very dangerous path to walk. I think including all types of instructions should be included for robustness. In the proof-of-concept, the 32-bit word LDR/STR are checked using:Code:bool isLDR16 = (opc7 == 0b0101100 || opc5 == 0b01101);bool isSTR16 = (opc7 == 0b0101000 || opc5 == 0b01100);These are 16-bit Thumb opcodes for LDR/STR 32-bit word with register and immediate address offsets, respectively.The halfword, byte and signed variants are easily supported by looking up the other opcode groups. If the opcode handling is implemented using a LUT, then that should all be possible with minimal expansion of run-time.I may actually look into implementing that later this week. Sounds like a fun addition. I'm personally very interested to how far such a software implementation can be pushed, even if it's only out of academic interest (and less for a practical high-performance one).

author: wek
author_rank: Frequent Contributor
timestamp: April 06, 2022, 01:19:58 pm
content: Quote from: hans on April 06, 2022, 08:25:40 amBut if it does not skip, you still would have to manually advance the if-then status bits. And it would be exceptional fun, given it's saved in discontinuous set of bits in EPSR.The pre/post-incremented address registers would be loads of fun, too.And then there are the unpriviledged loads/stores, the exclusives, double and multiple (already mentioned); and if one would want to be extra pedantic/complete, then also stacks/unstacks and code/vector fetches. I may have forgotten some of the more pervert ones.The way how ARM envisaged this whole thing is, that in the exception you are supposed to remove the *cause* for the exception, and then by returning the processor automatically replays the - now not offending anymore - instruction.JW

author: peter-h
author_rank: Super Contributor
timestamp: April 06, 2022, 03:36:44 pm
content: Getting back to basics, how is virtual memory implemented on say 80386+ ? Don't they set up an address trap?

author: nctnico
author_rank: Super Contributor
timestamp: April 06, 2022, 04:06:56 pm
content: Quote from: peter-h on April 06, 2022, 03:36:44 pmThey do but the result is that the virtual memory area gets mapped to real memory. The 32F417 seems to have an MPU that may be used to implement something similar.

author: abyrvalg
author_rank: Frequent Contributor
timestamp: April 06, 2022, 04:35:49 pm
content: Checked F417's MPU already - no fun, it doesn't support remapping, only trapping physical address acceses.The idea of 80386's & friends VM is to havedifferentvirtual addresses backed bysamephysical addresses (trap access to non-mapped VA, swap some other physical page content to disk, detach that page from old VA, attach to new VA, resume the sw and let it access new VA).

author: nctnico
author_rank: Super Contributor
timestamp: April 06, 2022, 05:04:38 pm
content: Quote from: abyrvalg on April 06, 2022, 04:35:49 pmYes, but I would theorise that you can replace the contents of the physical address space with something else before exiting from the trap. I think this should allow to use one piece of memory for multiple purposes where an OS takes care of setting up the MPU depending on the task it is executing. It will take a large amount of work to implement though.Basically:- task switch -> protect memory area- trap -> swap memory contents, unprotect and continue

author: peter-h
author_rank: Super Contributor
timestamp: April 06, 2022, 08:47:46 pm
content: I don't think there is any way on the 32F417 to access memory which does not physically exist on the chip.But I never examined that part of it.

author: nctnico
author_rank: Super Contributor
timestamp: April 06, 2022, 08:50:54 pm
content: Quote from: peter-h on April 06, 2022, 08:47:46 pmTrue. But you can reserve a piece of memory for use by multiple processes and swap it out when accessed using the MPU. But it requires some form of management because it must be very clear which process 'owns' the memory at any given time. It could be an interesting way to have a better simultaneous usage of memory in a microcontroller with the SPI ram as an overflow to still have enough memory for the worst case situation.

author: peter-h
author_rank: Super Contributor
timestamp: April 06, 2022, 09:12:31 pm
content: OK; that was my drift when I said about hooking into the RTOS and, when it switchestoa thread, you copy a load of data from the SPI RAM to some RAM buffer, and then write it back when it switchesawayfrom that thread.But as someone pointed out, this is not exactly quick, unless you are switching tasks relatively slowly. It takes "ages" to move say 64k via 21MHz SPI.I have used this method with a simple RTOS I wrote, with bank switching, which is "instant" because it is just a value in a register. Each of about 10 threads had its own 32k RAM, on a Z80-family chip.

author: peter-h
author_rank: Super Contributor
timestamp: May 06, 2022, 10:15:03 am
content: The funny thing is that I have just done a little PCB for another job relating to this project and was going to put an SPI SRAM chip on it, to save birdsnesting it later on.Except... there aren't any :-) Well there are tiny stocks of 128kbyte ones e.g.https://uk.farnell.com/on-semiconductor/n01s830bat22i/serial-sram-1mb-spi-tssop-8/dp/2627903. Nothing much bigger. Either they are used in huge numbers and are hoarded like so much else (which I doubt, because very few CPUs can use them directly) or they are going obsolete.

author: woofy
author_rank: Frequent Contributor
timestamp: May 06, 2022, 11:10:39 am
content: Mouser has stocks of 4mbit chips.https://www.mouser.co.uk/ProductDetail/ISSI/IS62WVS5128FBLL-20NLI-TR?qs=byeeYqUIh0MVWOLDS41N2g%3D%3D

author: DavidAlfa
author_rank: Super Contributor
timestamp: May 06, 2022, 12:52:19 pm
content: Or take the chinese way, these chips exploded after the ESP32 launch, they're everywhere and cheap.Check the 64Mbit LY68L6400SLIT in Aliexpress: 8Megabytes in SOIC-8, 133MHz, 84MHz crossing page boundaries.Bit-banging it in Quad IO might be faster than HW SPI, also you you run them in parallel, working with 8bit data.

author: peter-h
author_rank: Super Contributor
timestamp: May 06, 2022, 01:01:42 pm
content: That one looks amazing. What's the catch? The political risk of a key part made in China?

author: woofy
author_rank: Frequent Contributor
timestamp: May 06, 2022, 01:20:00 pm
content: There is also the Vilsion Tech VTI7064MSME, less performance and more expensive though.LCSC has both is stock - over 14k of the Lyontek chip.https://www.lcsc.com/product-detail/SRAM_Vilsion-Tech-VTI7064MSME_C139966.htmlhttps://www.lcsc.com/product-detail/SRAM_Lyontek-Inc-LY68L6400SLIT_C261881.html

author: ataradov
author_rank: Super Contributor
timestamp: May 06, 2022, 04:22:25 pm
content: Quote from: peter-h on May 06, 2022, 01:01:42 pmThere is no catch, memory is cheap. You are just being gouged by "reputable" companies because they can. At least for now.Same story with connectors or anything mechanical.

author: SiliconWizard
author_rank: Super Contributor
timestamp: May 06, 2022, 05:40:57 pm
content: Quote from: peter-h on May 06, 2022, 01:01:42 pmIf you were based in the US, there might be a "political" risk at some point. You never know what kind of sanctions they could decide if things go sour. In the UK, the risk is probably a lot less likely. But who knows, these days...Other than this, the potential risks are well known: possibly poor documentation (not necessarily, but likely), hard to get tech support, unknown lifetime, etc.Price itself is not a "catch". We all know why China can sell products at much lower prices than we can. No surprise there.

author: peter-h
author_rank: Super Contributor
timestamp: May 06, 2022, 06:47:14 pm
content: The doc on these really is incredibly brief, compared to US parts. But I am sure it is sufficient.

author: Doctorandus_P
author_rank: Super Contributor
timestamp: May 06, 2022, 10:13:31 pm
content: Maybe you can do something with overlays.They were invented for DOS (I think) when it turned out that 640k was not really enough.Warren Gay has dedicated a chapter about overlays for STM32 in his book: Beginning STM32 Developing with FreeRTOS, libopencm3 and GCCISBN-13 (pbk): 978-1-4842-3623-9ISBN-13 (electronic): 978-1-4842-3624-6https://doi.org/10.1007/978-1-4842-3624-6Library of Congress Control Number: 2018945101

author: peter-h
author_rank: Super Contributor
timestamp: May 07, 2022, 06:08:29 am
content: One could certainly move data in and out of a serial SPI RAM chip, as the RTOS switches tasks, but it isn't all that quick (milliseconds to copy over say 64k). Better than nothing, if you need lots of RAM per task.In this case I might be using the SPI SRAM for linear data storage, where FLASH would wear out much too fast.

author: ralphrmartin
author_rank: Frequent Contributor
timestamp: May 07, 2022, 08:54:22 pm
content: Quote from: Doctorandus_P on May 06, 2022, 10:13:31 pmI certainly remember using them on a PDP 11 in about 1975 before DOS was a thing. 64k (not 640k) was not enough.

author: peter-h
author_rank: Super Contributor
timestamp: May 08, 2022, 07:02:05 am
content: Overlays, where you bank in block of RAM from say a 1MB RAM array, into a small block, say 16k, which is within the CPU's address space, have been around since for ever. I used to make printer and plotter sharing products and we had up to 4MB, addressable as a 4k "overlay" on a CPU with a 16 bit address space (Z80, Z180, Z280). That 4MB box sold for £2500Good days... It even had a custom designed token ring LAN to interconnect them. I wrote the whole thing in asm.The above was for data. And same has been possible for code, using a "large model" compiler as mentioned before (we used IAR). This worked well, with the limitation that no C function could be bigger than the overlay size (4k in that project). We did an IBM coax/twinax protocol converter for driving non-IBM printers. Before the internetBut all the above require explicit programming (and some linker tricks). The original idea in this thread was totransparentlytrap an access to a nonexistent RAM location (the ARM32 has loads of address space to play with) and map it onto an SPI RAM. The ESP32 CPU does this, and it's pretty easy to do in a CPU, in hardware.

author: peter-h
author_rank: Super Contributor
timestamp: June 12, 2022, 09:47:08 am
content: Just to update this thread, I have got one of the SPI PSRAMs workinghttps://www.eevblog.com/forum/microcontrollers/lyontek-ly68l6400-8-megabyte-spi-sram-am-i-reading-the-data-sheet-right/msg4199662/#msg4199662However I will also try one of the smaller "real SRAMs" because they don't have the silly CS=0 timing limitation.