How to use the STM32f4 CCM in a Keil Project

The STM32F4 microcontrollers have 3 major blocks of memory. Each of 64KB. Two out of them are contiguous whereas the third block is non-contiguous with the other two. The third block is called the CCM. The CCM is short for Core Coupled Memory.

Why use the CCM?

The first two blocks are of 128 KB in total, and are connected to the various interconnects. They are also connected to the Instruction and Data bus. So it is possible to use the 128 KB for data and instructions. The CCM on the other hand is directly wired to the core. This means that this memory is present on the data bus coming from the core. This is precisely the reason that the DMA cannot access the CCM memory. The DMA has its own data bus which is wired to the 128KB Non CCM - SRAM. Also it is not possible to use the Non CCM SRAM for both the DMA and Core at the same time, so one may have to wait while the other uses it. This can give rise to problems. The CCM on the other hand is only used by the Core. This makes it all the more important. So if you have a program which is very DMA intensive and also needs some processing to do, it would be wise to use the CCM for the Core’s data.

How to do it

Now lets come to the “core’ topic of this article. When programming for the STM32f4, the most popular options are the free and open source GNU toolchain, which is cross platform, and the Keil MDK toolchain which is proprietary but has a limited free version.

In the GNU toolchain, memory management is done using linker files which generally have .ld extension. In Keil, which uses the MDK compiler, we need to specify “scatter” files to acheive this.

The main steps involved are

  1. Create a scatter file with the necessary address
  2. Add the __attribute__ tag to the data item(s)
  3. Check your generated MAP file for confirmation

Scatter File

Creating the scatter file

Screenshot Screenshot

Your scatter file should be in the following format. This is a basic file. It is possible to have other options too.

; ****************************************************
; *** Scatter-Loading Description File for STM32f4 ***
; ****************************************************

LR_IROM1 0x08000000 0x00100000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00100000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
  }
  RW_IRAM1 0x20000000 0x00020000  {  ; RW data
   .ANY (+RW +ZI)
  }
  RW_IRAM2 0x10000000 0x00010000  {  ; CCM data
     .ANY (CCM_DATA)
  }
}

Over here CCM_DATA is a label I will use even later in the post. You can call it anything you want. But ensure to change it at all the places.

You can also reallocate your Stack to the CCM. The scatter file for that is provided later.

Adding the attribute tag

For each data member you want to add to the CCM, you will have to add a tag as shown in the examples below.

Examples unsigned char imagearray[4] __attribute__ ((section("CCM_DATA"))) = {0x80, 0x80, 0x33, 0x80}

typedef struct _FOO {
  int a[3];
  int b[4];
  int c[5];
} FOO;
FOO ccmFoo[1024] __attribute__ ((section("CCM_DATA")));

Confirmation

Once the above is done you can build your code in Keil. Then go to your project folder and open the Project-Name.MAP file in a text editor.

Search for the variable you wanted to store in CCM (for example imagearray). You will find it under the Global Symbols heading.

imagearray   0x10000000   Data   11520   main.o (CCM_DATA)

If you find CCM_DATA and a memory address starting with 0x100… in the same row as your variable name, you’re done!!

Also you can dynamically store an array to the CCM using the standard memcpy function.

Special mention to clive1 of the STM32 forums. He has been a great help in my journey of “Discovering” the STM32F4. :)

If you would be interested in doing this on the GNU toolchain instead of Keil, heres a great post about it. http://sigalrm.blogspot.com/2013/12/using-ccm-memory-on-stm32.html

The scatter file for reallocating the STACK to the CCM

; *************************************************************
; ***     Scatter-Loading Description File for STM32F4      ***
; *************************************************************
 
LR_IROM1 0x08000000 0x00100000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00100000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
  }
  RW_IRAM1 0x20000000 0x00020000  {  ; RW data
   .ANY (+RW +ZI)
  }
  RW_IRAM2 0x10000000 0x00010000  {  ; CCM
   startup_stm32f4xx.o (STACK)
  }
}

You can also add startup_stm32f4xx.o (STACK) to the CCM block if it is already used. For example

RW_IRAM2 0x10000000 0x00010000  {  ; CCM
   .ANY (CCM_DATA)
   startup_stm32f4xx.o (STACK)
  }