STM32 CCM Allocator
CCM Memory
The STM32 F2, F3, and F4 families have a special block of SRAM available called CCM (Core Coupled Memory). This memory has the drawback that it cannot be used for STM32 DMA operations.
By default, the CCM memory is lumped in with the rest of memory when the NuttX
heaps are created. But this can be a problem because it will be a toss of the
coin if non-DMA-able CCM memory or other DMA-able memory gets returned when
malloc()
is called. That usually does not matter but it certainly does make
a difference if you are allocating memory that will be used for DMA! In that
case, getting CCM memory for your DMA buffer will cause a failure.
CONFIG_STM32_CCMEXCLUDE
There is a configuration option called CONFIG_STM32_CCMEXCLUDE
that can be
used to exclude CCM memory from the heap. That solves the problem of getting
CCM memory when you want to allocate a DMA buffer. But then what do you do
with the CCM memory? Do you let it go unused?
CCM Allocator
In order to make use of the CCM memory, a CCM memory allocator is available. This memory allocator is automatically enabled when the following options are set:
CONFIG_STM32_CCMEXCLUDE
CCM memory is excluded from the normal heap, andCONFIG_MM_MULTIHEAP
Support for multiple heaps is enabled.
Under those conditions, the CCM memory allocator is enabled and the allocator
interfaces prototyped in the arch/arm/src/stm32/stm32_ccm.h
are available.
NOTE: These interfaces are, technically, not prototyped since they are really provided via C pre-processor macros.
NOTE: In order to use the CCM memory allocator functions, you must first call
ccm_initialize()
somewhere in your early boot-up logic.
With these interfaces you have a (nearly) standard way to manage memory from a heap that consists of the the CCM SRAM. And, since the CCM memory is no longer a part of the normal heap, all allocated I/O buffers will be DMA-able (unless you have included other non-DMA-able memory regions in the stack).
CCM Stacks
One particular problem that has been reported by Petteri Aimonen requires some additional work-arounds. The STM32 SPI driver supports DMA and with SPI it is sometimes necessary to do some very small transfers for which there is no real gain from using DMA. In this case, Petteri has devised a clever way to both 1) make use of the CMM memory and 2) to force fallback to non-DMA transfers for these small stack transfers.
Here is what Petteri has done:
First, he has modified
arch/arm/src/common/up_createstack.c
andup_releasestack.c
so that stacks are allocated from CCM memory. That allocation is something like the following:void *result = ccm_zalloc(size); if (!result) { /* Fall back to main heap */ result = zalloc(size); }
With the matching:
if (((uint32_t)p & 0xF0000000) == 0x10000000) { ccm_free(p); } else { free(p); }
Then Petteri added special DMA support enabled with
CONFIG_STM32_DMACAPABLE
. That option enables an option in all of the DMA logic called:bool stm32_dmacapable(uint32_t maddr);
That will return true if it is possible to do DMA from the address and false if not.
Finally, Petteri added logic to the STM32 SPI driver that use
stm32_dmacapable()
: If the address is not DMA capable, then the SPI driver will fall back to non-DMA operation.With Petteri’s changes all of the large I/O buffers will be allocated from DMA-able memory. All stacks will be allocated from non-DMA-able CCM memory (provided that there is space). Small SPI DMA buffers on the non-DMA-able stack will be detected by
stm32_dmacapable()
and in that case, the STM32 SPI driver will fall back and use non-DMA-transfers.From all reports this works quite well.