STM32F4xx and core coupled memory: comments? - Page 1
Forum section: electronics / general

author: rhodges
author_rank: Frequent Contributor
timestamp: February 13, 2021, 09:46:31 pm
content: I am playing with the STM32F407-VE right now. I fixed an error in the ST linker script - it stated 192K of RAM in one block, where the manual states 128K in the main block and 64K of core coupled memory (CCM) at a totally different location. So now I have two memory areas.The CCM memory has a separate bus and won't contend with DMA (CCM is only usable by the CPU). So here are my thoughts.64K is more than enough for my stack, and data, so why not put those in the CCM area? I can still use the 128K main RAM for large data blocks, and of course, DMA.I don't expect to be using heavy DMA soon, but it seems like a good plan to start. DMA won't affect interrupt latency. And if I goof up on DMA pointers, it won't harm my program memory.Thanks for any comments!-Richard

author: ataradov
author_rank: Super Contributor
timestamp: February 13, 2021, 10:42:12 pm
content: There is nothing wrong with this approach. However don't expect a significant performance boost. With zero wait state memory, even if the access has to go though the bus, it is still pretty fast.In any scenario where moving things to TCM makes or breaks a project, you should probably be looking at a higher performing MCU. There are good exceptions to this, of course.

author: hans
author_rank: Super Contributor
timestamp: February 14, 2021, 08:46:14 am
content: I think there is nothing wrong with assigning TCM to the processor stack, (heap) and variables. You don't have to tell the C compiler all about the capabilities of the chip if you don't want the C program to use any of the 128K SRAM. Just design your program with 64KB TCM in mind so that the DMA controller has as much uncontested access to SRAM.However, I do somewhat agree with ataradov in that if you need to do this, you're probably converging outside the performance scope of this chip. Behaviour that has to run real-time (e.g. because that's why it's fed by DMA in the first place) will favor predictable memory access timing and access arbitration. If the DMA controller is pushing so much data around that each individual stream can potentially stall on each other, you may get some timing jitter or kind-of 'intermodulation' products of both stream transfer periods.That is perhaps not important for peripherals that have built in FIFO's and rely on burst performance, but in my experience ST has very little peripheral FIFO's because the chip has got so many DMA streams.. (real PITA)Also, the main 128K SRAM is also split into 2 modules of 112K and 16K. In theory that allows multiple streams to be accessed (e.g. ethernet and USB HS with GP DMA streams) to either SRAM regions.I'm actually surprised that ST's documentation doesn't state clearly the memory mapped regions of all user memory (e.g. SRAM, FLASH, FSMC). Table 3 and 4 in the user manual does describe some memory regions for boot options, but it does not state where TCM is within the memory address region..

author: iMo
author_rank: Super Contributor
timestamp: February 14, 2021, 09:14:22 am
content: Afaik F407 can work with 192kB of internal sram off C as a single block.. At least I had no issues with it in past.. I had to mess with the linker script while I executed the code off the internal sram or off the external sram (or to have the heap in the external sram).

author: Siwastaja
author_rank: Super Contributor
timestamp: February 14, 2021, 12:57:46 pm
content: Putting stack in core-coupled RAM is what I tend to do by default when this RAM is available. It's the simplest thing you can just do and expect some performance improvement and a bit more predictability to DMA access timing. While at it, put globals and statics that are written and read by CPU only there as well.Don't expect massive performance boost, though. In ARM, there is acceptable number of registers and compilers optimize quite well so that routines usually do not need to use that much stack.Clever use of buses with DMA is more significant. For example, I had a project where I ran out of performance with two ADCs each doing 16 bit DMA accesses to different parts in memory, then two different interrupt handlers reading out 8 previous values, each from the different ADC, and average them. As soon as I changed the ADCs to run in synchronized Dual Mode, generating 32-bit DMA accesses where the half-words are from the different ADCs, and the two ISRs sharing the work doing just four (not eight anymore) 32-bit loads and averaging with SIMD instructions, the performance was more than enough with good safety margin. This required use of some inline assembly and dedicating one general-purpose register for intermediate math preventing compiler for using it (gcc: -ffixed-r11) but was definitely worth it.

author: rhodges
author_rank: Frequent Contributor
timestamp: February 14, 2021, 02:59:28 pm
content: QuoteIn the datasheet for stm32f4xx (DS8626, rev 9, August 2020):In the memory map, figure 18, page 71, it shows:"CCM data RAM" at 0x1000 0000 to 0x1000 ffff"SRAM 112K" at 0x2000 0000 to 0x2001 bfff"SRAM 16K" at 0x2001 c000 to 0x2001 ffffI think I saw that SRAM1 can be remapped, but I did not see anything about remapping CCM RAM.Thanks, everyone, for your comments.

author: ogden
author_rank: Super Contributor
timestamp: February 14, 2021, 03:37:47 pm
content: Quote from: Siwastaja on February 14, 2021, 12:57:46 pmRight. When you realize that DMA transfers are fighting with CPU for RAM bandwidth - then CCM can come to rescue. Also I agree to statement that for small volume & hi-value projects you better just use better/faster MCU.

author: Siwastaja
author_rank: Super Contributor
timestamp: February 14, 2021, 04:25:51 pm
content: Agree that it's sometimes/often easy just to buy a "better" part.Better/faster isn't always so simple, though.Shopping around for more and more powerful parts takes time as well.For example, in my example, STM32F334 was a perfect fit; very low cost, easy to use, does what it should, my familiarity with stm32 series, no higher-performance version available. The required optimization took maybe a day or two to finish and verify, but then it works and does the job very well. With the initial "dumb" code version, the performance isn't sufficient. The Right Thing To Do is to code it right, instead of starting a process to switch to a more powerful microcontroller, which would necessitate switching to some completely new product family from an unknown manufacturer, or FPGA for that matter.Or always default to overly large microcontrollers "just in case".You can't always default to just buying moar power!!1, sometimes you need to know how to utilize the easily available resources in efficient manner.For example, if you need to drive a large screw, you switch gears of your electric drill instead of saying: "I don't know how I operate this gear lever, I'm going out to buy one with a bigger motor".I operate with the mindset that programming timing and performance critical embedded applications requires expertise and is not trivial. If I don't know how to do something, I will learn how to do it.It's not always easy to detect whether you just need a "bigger part", or change how you do it.BTW with microcontrollers you just can't use the "use a bigger part" mindset because it often happens that the initial "dumb" implementations are so underperforming even the biggest part on market can't do it. So you are in this "performance of the code matters" game no matter what. Even beginners hit this performance limit very soon when they see their ISRs take too long to complete, this happens even if they start developing with 480MHz Cortex-M7.

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 16, 2021, 04:24:35 pm
content: Quote from: rhodges on February 13, 2021, 09:46:31 pmLooks like a good idea. For setting up the stack, don't you need to modify the startup code as well?

author: rhodges
author_rank: Frequent Contributor
timestamp: February 16, 2021, 08:01:19 pm
content: Quote from: SiliconWizard on February 16, 2021, 04:24:35 pmNo,the startup code does not have to set the stack. The linker script sets _estack, and the startup code puts this in the vector table. So only the linker script needs to be changed.EDIT: Core coupled memory is not enabled on reset. Read follow-up post.

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 16, 2021, 09:51:53 pm
content: Quote from: rhodges on February 16, 2021, 08:01:19 pmOh, ok. So it's just a matter of setting _estack in the linker script to a location inside CCM then (namely at the top).

author: rhodges
author_rank: Frequent Contributor
timestamp: February 22, 2021, 12:16:41 am
content: Quote from: SiliconWizard on February 16, 2021, 04:24:35 pmSorry, you are correct here. We need to enable the CCM clock.Code:RCC->AHB1ENR |= RCC_AHB1ENR_CCMDATARAMEN; /* Enable core coupled memory. */This may work as the very first line of main(). But I think it really should be in the startup code.I guess I should be grateful that Flash memory is enabled on reset.

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 22, 2021, 01:20:39 am
content: Quote from: rhodges on February 22, 2021, 12:16:41 amWell, as the startup code normally copies data from Flash to initialize all global variables (if they have initialization values) and zeroes out (bss segment) the ones that have no explicit initialization, both of those steps won't work if you don't enable the CCM clock first (and that means, before the startup code does those things.) And if those steps don't work, you're in for a number of bad surprises unless you never initialize any globals and/or never expect unitialized ones to be zeroed out.The stack would of course not be accessible either within the startup code if you only enable CCM clock within main(), which is probably less of a problem as usually the only time the stack may be written to in the startup code is when main() is called, and since it's usually rare to ever return from main() in embedded code, you probably wouldn't notice. But the startup code may actually call other functions before main(), so that could fuck things up there as well.SO yeah, I'd say definitely enable the CCM clock as one of the first steps in the startup code.

author: Jope
author_rank: Regular Contributor
timestamp: February 22, 2021, 03:32:09 am
content: Quote from: rhodges on February 22, 2021, 12:16:41 amYou don't need to enable it. The reset value of CCMDATARAMEN is '1'.

author: rhodges
author_rank: Frequent Contributor
timestamp: February 22, 2021, 03:57:36 pm
content: Quote from: Jope on February 22, 2021, 03:32:09 amOkay, I took another look at the datasheet. In RM0090 rev 18 7.3.10 page 242, it states the reset value of AHB1ENR is 0010 0000. You are right about that.Damn! I was wrong twice in the same thread. Now I know what I did wrong. In my setup code, I wrote the enable bits for the GPIO clocks as a direct write, where it should have been an OR operation.Well, I hope admitting my mistake is helpful to someone else.

author: SiliconWizard
author_rank: Super Contributor
timestamp: February 22, 2021, 04:08:22 pm
content: Yeah, thanks for the thread, that raised some questions and now it looks like they are all answered.It also showed that you can disable the CCM clock (but in restrospect, that it be disabled upon reset would have been weird). And actually, on those MCUs you can selectively enable and disable the clock for MANY structures. Disabling clocks for everything you don't use can save significant power.

author: Siwastaja
author_rank: Super Contributor
timestamp: February 22, 2021, 04:31:21 pm
content: Beware, some MCUs also in STM32 family boot some memory clocks disabled so always check. Startup code is the logical place to enable them, before .bss/.data loops.

author: Jope
author_rank: Regular Contributor
timestamp: February 22, 2021, 05:31:43 pm
content: Quote from: rhodges on February 22, 2021, 03:57:36 pmThere's nothing wrong with being wrong. I have always used the CCM as stack and before I read this thread I actually didn't even know that there is a clock enable for it.

author: techman-001
author_rank: Frequent Contributor
timestamp: February 22, 2021, 08:26:35 pm
content: Quote from: rhodges on February 22, 2021, 03:57:36 pmIt's curious, the datasheet you quote does mention the CCMDATARAMEN bitfield (bit 20) in RCC_AHB1ENR but it's omitted in all the SVD files I could find for the STM32F4xx series.Code:$00100000 bin.33222222222211111111111098765432109876543210987654321000000000000100000000000000000000<field><name>DMA1EN</name><description>DMA1 clock enable</description><bitOffset>21</bitOffset><bitWidth>1</bitWidth></field><field><name>BKPSRAMEN</name><description>Backup SRAM interface clockenable</description><bitOffset>18</bitOffset><bitWidth>1</bitWidth></field>Which also means it's missing from my Forth transforms:Code:: RCC_AHB1ENR_DMA1EN ( -- x addr ) 21 bit RCC_AHB1ENR ; \ RCC_AHB1ENR_DMA1EN, DMA1 clock enable: RCC_AHB1ENR_BKPSRAMEN ( -- x addr ) 18 bit RCC_AHB1ENR ; \ RCC_AHB1ENR_BKPSRAMEN, Backup SRAM interface clock enable

author: Jope
author_rank: Regular Contributor
timestamp: February 22, 2021, 11:55:57 pm
content: Quote from: techman-001 on February 22, 2021, 08:26:35 pmThe currentMDK5 software packfor the STM32F4 contains 14 SVD files, but only three contain the CCMDATARAMEN bit.

author: techman-001
author_rank: Frequent Contributor
timestamp: February 23, 2021, 03:41:30 am
content: Quote from: Jope on February 22, 2021, 11:55:57 pmThese three from the Keil Pack in fact:Code:-rw-r--r-- 1 tp tp 912885 27 Aug 2015 STM32F401x.svd-rw-r--r-- 1 tp tp 912719 14 Jun 2018 STM32F401xE.svd-rw-r--r-- 1 tp tp 1890267 3 Jan 2017 STM32F40x.svd-rw-r--r-- 1 tp tp 677446 14 Aug 2015 STM32F410xx.svd-rw-r--r-- 1 tp tp 913317 14 Jun 2018 STM32F411xx.svd-rw-r--r-- 1 tp tp 1641378 14 Jun 2018 STM32F412xG.svd-rw-r--r-- 1 tp tp 1732658 6 Dec 2016 STM32F413.svd-rw-r--r-- 1 tp tp 2039111 28 May 2015 STM32F41x.svd-rw-r--r-- 1 tp tp 1964272 11 Dec 2019 STM32F427x.svd-rw-r--r-- 1 tp tp 2029865 11 Dec 2019 STM32F429x.svd-rw-r--r-- 1 tp tp 2081471 11 Dec 2019 STM32F437x.svd-rw-r--r-- 1 tp tp 2217343 11 Dec 2019 STM32F439x.svd-rw-r--r-- 1 tp tp 1935245 11 Mar 2015 STM32F446x.svd-rw-r--r-- 1 tp tp 2279134 9 Sep 2015 STM32F46_79x.svd% grep CCMDATARAMEN *.svdSTM32F429x.svd: <name>CCMDATARAMEN</name>STM32F439x.svd: <name>CCMDATARAMEN</name>STM32F46_79x.svd: <name>CCMDATARAMEN</name>

author: peter-h
author_rank: Super Contributor
timestamp: May 12, 2021, 08:22:28 am
content: "I fixed an error in the ST linker script - it stated 192K of RAM in one block"Having just discovered this... this is a bug in Cube IDE. I am v1.6.1 (which is the current version) but don't know which version the linker script was generated with.

author: bson
author_rank: Unknown
timestamp: October 20, 2021, 08:39:00 pm
content: A bit late, but be aware of various pitfalls.One is that the NVIC can't fetch vectors from CCM, so if you set up a vector table there (via the NVIC VTOR register) it won't work at all. The CPU will read and write it fine, you can dump it and it checks fine from the debugger, but when you get an exception it fails seeming unexplainably. The vector fetch faults, which tries to vector... and you get stuck in an exception loop, that each time through saves an exception context on the stack - and clobbers all RAM, then wraps and clobbers peripheral registers. Forever.A related issue is that you shouldn't put the main stack in CCM either. The core will fail to correctly save exception state to a stack in CCM. The interrupt stack is fine, unless you use IPLs for potentially nested interrupts - then that will blow up as well.Because of all these undocumented pitfalls and lack of detailed specifications or documentation, I'd take their app notes at face value: it's for accelerating code. Don't keep data in it. Even if things like stacks and vector tables could benefit from uncontended data access from the CPU core. For example, what about lazy FPU register saves? There's just so much that can go wrong and produce mysterious and very difficult to debug problems.

author: wek
author_rank: Frequent Contributor
timestamp: October 21, 2021, 09:34:04 pm
content: > One is that the NVIC can't fetch vectors from CCMNice catch!Indeed, while the vector table may seem to be *data* (roughly, function pointers), they are *fetched* as instruction during exception entry, simultaneously with stacking the registers.> A related issue is that you shouldn't put the main stack in CCM either.I don't know what do you mean by "main stack", but IMO there's no reason not to put any data including stack into CCM, unless you want to *execute* that data or you want to access those data by any busmaster other than the CPU.JW

author: wek
author_rank: Frequent Contributor
timestamp: October 21, 2021, 09:43:14 pm
content: Quote from: techman-001 on February 23, 2021, 03:41:30 amThat's because not all 'F4 contain CCMRAM, only 'F405/407/415/417, 'F427/429/437/439, and 'F469/476. I.e. 'F401/410/411/412/413/423, and, quite notably, 'F446, don't.JW

author: peter-h
author_rank: Super Contributor
timestamp: October 26, 2021, 07:16:41 pm
content: "A related issue is that you shouldn't put the main stack in CCM either. The core will fail to correctly save exception state to a stack in CCM"I have not found that. I was developing with the main stack at the top of the CCM and everything ran fine. Ethernet, USB, comms, all under interrupts. 32F417."NVIC can't fetch vectors from CCM, so if you set up a vector table there (via the NVIC VTOR register) it won't work "Thanks for that; noted and documented in my project. I am not going to test it thoughI guess it isn't totally surprising.