Skip to main content
ST Community
ST Employee
July 23, 2018

DMA is not working on STM32H7 devices

  • July 23, 2018
  • 24 replies
  • 180121 views

The problem is related to two things: memory layout on STM32H7 and internal data cache (D-Cache) of the Cortex-M7 core. 

In summary these can be the possible issues:

  • Memory placed in DTCM RAM for D1/D2 peripherals. Unfortunately, this memory is used as default in some projects including examples.
  • Memory not placed in D3 SRAM4 for D3 peripherals.
  • D-Cache enabled for DMA buffers, different content in cache and in SRAM memory.
  • Starting the DMA just after writing the data to TX buffer, without placing __DSB() instruction between.

For Ethernet related problems, please see separate FAQ: FAQ: Ethernet not working on STM32H7x3 

1. Explanation: memory layout

The STM32H7 device consists of three bus matrix domains (D1, D2 and D3) as seen on the picture below. The D1 and D2 are connected through bus bridges, both can also access data in D3 domain. However, there is no connection from D3 domain to D1 or D2 domain. In some devices (STM32H7A3/7B3 and STM32H7B0), we can find only two domains, where D1 and D2 domains are merged into one domain which is the CD Domain, and D3 is nominated as SRD Domain.

The DMA1 and DMA2 controllers are located in D2 domain and can access almost all memories, with the exception of ITCM and DTCM RAM (located at 0x20000000). These controllers are used in most cases.

The BDMA controller is located in the D3 domain and can access only SRAM4 and backup SRAM in the D3 domain.

The MDMA controller is located in D1 domain and can access all memories, including ITCM and DTCM. This controller is primarily used for handling D1 peripherals and memory-to-memory transfers.

698.png

From performance perspective, it is better to place DMA buffers inside the D2 domain (SRAM1, SRAM2 and SRAM3), since the D2-to-D1 bridge can introduce additional delay.

2. Explanation: handling DMA buffers with D-Cache enabled

The Cortex-M7 contains two internal caches: I-Cache for loading instructions and D-Cache for data. The D-Cache can affect the functionality of DMA transfers because it holds the new data in the internal cache and does not write it to the SRAM memory. However, the DMA controller loads the data from SRAM memory, not from the D-Cache.

If the DMA transfer starts immediately after writing data to the tx_buffer in the code, the tx_buffer data might still reside in the write buffer inside the CPU while the DMA has already started. The solution is to set the tx_buffer as a device type to force the CPU to order memory operations or to add the __DSB() instruction before starting the DMA.

There are several ways to manage DMA buffers with D-Cache:

  • Disable the D-cache globally. This is the simplest solution, but it is not an effective one, as you can lose a significant portion of performance. However, it can be useful for debugging to analyze whether the problem is related to the D-cache.
  • Disable the D-cache for a portion of the memory by configuring the memory protection unit (MPU). However, the MPU regions have specific alignment restrictions, and it is necessary to place the DMA buffers in designated parts of the memory. Each toolchain (GCC, IAR, KEIL) must be configured differently.
    • Note that MPU regions can overlap, and the higher region number has priority. Together with subregion disable bits, this feature can soften the alignment and size restrictions.
    • Note that Device and Strongly Ordered memory types do not allow unaligned access to memory.
  • Configure a part of the memory as write-through. This configuration can only be used for TX DMA. Note that on some revisions (r1p1 and older, excluding r0p0) of the Cortex-M7 core, there is an erratum concerning the write-through configuration. This issue affects only STM32H74x and STM32H75x devices from the STM32H7 family.
  • Use cache maintenance operations to manage data consistency. You can write data stored in the cache back to memory using the "clean" operation for a specific address range. Additionally, you can discard data stored in the cache using the "invalidate" operation.
    • The downside is that these operations work with a cache-line size of 32 bytes, so you cannot clean or invalidate a single byte from the cache. This limitation can lead to errors when the RX buffer shares the cache line with other data or the TX buffer (see the figure below).
    • Beware that with an uninitialized D-cache, the maintenance operations "clean" or "clean and invalidate" can lead to a BusFault exception. This issue is caused by uninitialized ECC (error correction code) after a power-on reset. If your project involves frequent maintenance operations and you want to temporarily disable the D-cache, you can use the SCB_InvalidateDCache function. This function cleans the cache and sets the correct ECC without enabling it.

700.png

Below are the possible MPU configurations. Green configurations are suitable for DMA buffers, blue configurations are suitable only for TX-only DMA buffers, and red configurations are forbidden. Other configurations are not suitable for DMA buffers and require cache maintenance operations:

Laurids_PETERSEN_0-1690194613355.png

3. Solution example 1: simple placement of all memory in the D1 domain

D-Cache must be disabled globally for this solution to work.

GCC (Atollic TrueStudio/System Workbench for STM32/Eclipse)

Replace DTCMRAM with RAM_D1 for section placement in linkerscript (.ld file extension), for example, like this:

.data : 
{
 ... /* Keep same */
} >RAM_D1 AT> FLASH

This should be done also for the .bss and the ._user_heap_stack sections.

In some linker scripts, the initial stack is defined separately. Therefore, you must either update it with the section or define it inside the section, as shown below:

._user_heap_stack :
{
 . = ALIGN(8);
 PROVIDE ( end = . );
 PROVIDE ( _end = . );
 . = . + _Min_Heap_Size;
 . = . + _Min_Stack_Size;
 _estack = .; /* <<<< line added */
 . = ALIGN(8);
} >RAM_D1

And remove the original _estack definition.

IAR (in project settings):

701.png

For Keil:

702.png

4. Solution example 2: placing buffers in separated memory part

D-cache must be disabled through the MPU for the specific memory region where the DMA buffer is placed. Note that the MPU region size must be a power of two. Additionally, the region's start address must have the same alignment as its size. For example, if the region is 512 bytes, the start address must be aligned to 512 bytes (the 9 least significant bits must be zero).


NOTE: IAR compiler and Keil compiler version <= 5 allow placing variables at absolute address in code using compiler specific extensions.

C code:

Define placement macro:

#if defined( __ICCARM__ )
 #define DMA_BUFFER \
 _Pragma("location=\".dma_buffer\"")
#else
 #define DMA_BUFFER \
 __attribute__((section(".dma_buffer")))
#endif

 

Specify DMA buffers in code:

DMA_BUFFER uint8_t rx_buffer[256];

GCC linkerscript (*.ld file)

Place the section in D2 RAM. You can also specify custom memory regions in the linker script file.

.dma_buffer : /* Space before ':' is critical */
{
 *(.dma_buffer)
} >RAM_D2

This does not include default value initialization. Otherwise, you must place special symbols and add your own initialization code.

IAR linker file (*.icf file)

define region D2_SRAM2_region = mem:[from 0x30020000 to 0x3003FFFF];
place in D2_SRAM2_region { section .dma_buffer};
initialize by copy { section .dma_buffer}; /* optional initialization of default values */

Keil scatter file (*.sct file)

LR_IROM1 0x08000000 0x00200000 { ; load region size_region
 ER_IROM1 0x08000000 0x00200000 { ; load address = execution address
 *.o (RESET, +First)
 *(InRoot$$Sections)
 .ANY (+RO)
 }
 RW_IRAM2 0x24000000 0x00080000 { ; RW data
 .ANY (+RW +ZI)
 }
 ; Added new region
 DMA_BUFFER 0x30040000 0x200 {
 *(.dma_buffer)
 }
}

Generation of scatter file should be disabled in Keil:

703.png

5. Solution example 3: Use Cache maintenance functions

Transmitting data:

#define TX_LENGTH (16)
uint8_t tx_buffer[TX_LENGTH];

/* Write data */
tx_buffer[0] = 0x0;
tx_buffer[1] = 0x1;

/* Clean D-cache */
/* Make sure the address is 32-byte aligned and add 32-bytes to length, in case it overlaps cacheline */
SCB_CleanDCache_by_Addr((uint32_t*)(((uint32_t)tx_buffer) & ~(uint32_t)0x1F), TX_LENGTH+32);

/* Start DMA transfer */
HAL_UART_Transmit_DMA(&huart1, tx_buffer, TX_LENGTH);

Receiving data:

#define RX_LENGTH (16)
uint8_t rx_buffer[RX_LENGTH];

/* Invalidate D-cache before reception */
/* Make sure the address is 32-byte aligned and add 32-bytes to length, in case it overlaps cacheline */
SCB_InvalidateDCache_by_Addr((uint32_t*)(((uint32_t)rx_buffer) & ~(uint32_t)0x1F), RX_LENGTH+32);

/* Start DMA transfer */
HAL_UART_Receive_DMA(&huart1, rx_buffer, RX_LENGTH);
/* No access to rx_buffer should be made before DMA transfer is completed */

Please note that in case of reception, there can be a problem if the rx_buffer is not aligned to the size of the cache line (32 bytes). During the invalidate operation, other data sharing the same cache line(s) with the rx_buffer might be lost.

6. References

  • "AN4838: Managing memory protection unit (MPU) in STM32 MCUs"
  • "AN4839: Level 1 cache on STM32F7 Series and STM32H7 Series":
  • "AN4296: Overview and tips for using STM32F303/328/334/358xx CCM RAM with IAR EWARM, Keil MDK-ARM and GNU-based toolchains":
  • "AN4891: STM32H7x3 system architecture and performance software expansion for STM32Cube":

24 replies

OHaza.1
Associate III
December 21, 2021

Seems as though the latest generated versions of the .ld files in cube fix this issue.

waclawek.jan
Super User
January 24, 2022

It would be nice if this article would be adapted also for 'F7.

JW

Aleks
Associate III
April 20, 2022

Thank you for article.

Some comments:

  1. CMSIS library check size and address alignment inside CB_InvalidateDCache_by_Addr(). Align buffers should be done in linker script.
  2. Cache invalidate need to be performed before starting DMA receive and after DMA receive is completed isn't it? This is described This is described on topic and topic.

Alexander Sebastian
Visitor II
May 19, 2022

I seem to have this issue on STM32H7A3, while using ADC1 with DMA1. ADC keep running into overriding error, and searches led me to here. However, it seems that my .ld script is configured a little different, I only found RAM_EXEC used and not DTCMRAM (Tried solution 1). Also, SCB_DisableDCache(); leads to hardfault. I probably don't know what I'm doing, as until now I haven't run into need for modifying memory, so a little more detailed guidance would be appreciated. Thank you.

ST Employee
May 19, 2022

@Adam BERLINGER​ , please, can you help?

AAnt.1
Associate II
June 11, 2022

Hi!

I have read your article in past and try to dissable D-cache in Keil scatter file - it did not help for me.

Here is my problem https://community.st.com/s/question/0D53W00001atD9zSAE/stm32h7-spi-dma-not-working

I have STM32H745 nucleo. I want to use M4 core . In D2 domain i use SRAM1-SRAM3, DMA2 . I need to send data via SPI-DMA (SPI4, PE14 MOSI, DMA2_stream3, DMAUX11 ) I dont have any on MOSI pin + DMA2_IRQ_Handler don't work.

The same i have corectly did on stm32f407 - all worked good and spi dma transaction work.

Could you look my code and help me to edited SPI-DMA transaction ?

Best regards.

Adam BERLINGER
ST Employee
June 17, 2022

Hello @Aleksandrs Sevastjanovs​ ,

you can check what is the value being written to DMA_SxM0AR register in DMA controller and if that memory is accessible by DMA. Starting from 0x24000000 should be ok, while starting from 0x20000000 will cause issues.

Regarding the hardfault, this could be caused by calling function like SCB_CleanDCache (or similar), while the D-cache is not initialized/enabled. After reset there are undefined data in cache and ECC error can be generated. One solution can be to call SCB_InvalidateDCache which marks all cache data invalid.

If you are using thisSTM32CubeIDE, you can also use Fault analyzer to show what could be the hardfault cause.

I hope this helps.

Best regards,

Adam Berlinger

Adam BERLINGER
ST Employee
June 17, 2022

Hello @AAnt.1​ ,

I'm not sure what could be the issue.

The D-Cache is not configured in scatter file. It is either enabled at the startup (usually in main function), or disabled by default.

Also I would recommend starting with some example based on HAL library or CubeMX generated code. E.g. I don't think it is necessary to setup EGE bit in DMAMUX for regular DMA operation and it might cause some issues. The EGE bit is used for more advanced synchronizations between different channels.

Best regards,

Adam Berlinger

AAnt.1
Associate II
June 19, 2022

Hi @Adam BERLINGER​ 

Hi to all! My old code:

https://community.st.com/s/question/0D53W00001atD9zSAE/stm32h7-spi-dma-not-working

I write modernized code - i could get only done function of IRQ_Handler, insite function LED switched, PD9 switched . That means - i can enter inside IRQ_handler and handler work, not stoped inside, but don't have any signal on MOSI SPI PIN .....

I want to work inside D2 domain (core M4, DMA2, SPI4 - send data trouth SPI DMA MOSI pin, like i corectly done instm32f407

In my project stm32h745 corect send data via SPI, only SPI without DMA, but i need to send via SPI_DMA )

Me modernized code

function main.c

 DMA_HandleTypeDef hdma_tx;

   SPI_HandleTypeDef       SpiHandle;

#define TBS0 11

uint8_t TBuf0[TBS0]={0,1,2,3,4,5,6,7,8,9,11};

SPI_Configuration( );

.....

 while (1)

 {

   HAL_DMA_Start_IT(&hdma_tx, *TBuf0,(uint32_t) &(SPI4->TXDR),TBS0);

}

function for SPI_DMA init

#define VIDEO_DMA            DMA2

#define DMA_STREAM              DMA2_Stream3

#define DMA_CHANNEL             DMA_Channel_3

void SPI_Configuration( void ) 

{

   GPIO_InitTypeDef        GPIO_InitStructure;     

// RCC->AHB2ENR |= (RCC_AHB2ENR_D2SRAM1EN | RCC_AHB2ENR_D2SRAM2EN | RCC_AHB2ENR_D2SRAM3EN);  // Enable the SRAM

//   __HAL_RCC_D2SRAM1_CLK_ENABLE();

//   __HAL_RCC_D2SRAM2_CLK_ENABLE();

//   __HAL_RCC_D2SRAM3_CLK_ENABLE();

   #define SPI4_FORCE_RESET()              __HAL_RCC_SPI4_FORCE_RESET()

 #define SPI4_RELEASE_RESET()            __HAL_RCC_SPI4_RELEASE_RESET()

 #define SPIx                            SPI4 

   #define SPIx_MOSI_GPIO_CLK_ENABLE()     __HAL_RCC_GPIOE_CLK_ENABLE()

   #define SPIx_CLK_ENABLE()               __HAL_RCC_SPI4_CLK_ENABLE()

 #define DMAx_CLK_ENABLE()               __HAL_RCC_DMA2_CLK_ENABLE()

   SPI4_FORCE_RESET();

   SPI4_RELEASE_RESET();

      HAL_DMA_DeInit(&hdma_tx);

   // ##-1- Enable peripherals and GPIO Clocks #################################

 // Enable GPIO TX/RX clock

 SPIx_MOSI_GPIO_CLK_ENABLE();

 // Enable SPI1 clock

 SPIx_CLK_ENABLE();

 // Enable DMA clock

 DMAx_CLK_ENABLE();

    // Common configuration for all channels

 GPIO_InitStructure.Pin = GPIO_PIN_14 | GPIO_PIN_12; // PE12 SPI SCK

   GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;

 GPIO_InitStructure.Pull = GPIO_NOPULL;

 GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

 GPIO_InitStructure.Alternate = GPIO_AF5_SPI4;

 HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);

 // ##-3- Configure the DMA

 // Configure the DMA handler for Transmission process

 hdma_tx.Instance                = DMA_STREAM; // SPI4 maybe?

 hdma_tx.Init.FIFOMode           = DMA_FIFOMODE_DISABLE;

 hdma_tx.Init.FIFOThreshold      = DMA_FIFO_THRESHOLD_FULL;

 hdma_tx.Init.MemBurst           = DMA_MBURST_SINGLE;

 hdma_tx.Init.PeriphBurst        = DMA_PBURST_SINGLE;

 hdma_tx.Init.Request            = DMA_REQUEST_SPI4_TX;

 hdma_tx.Init.Direction          = DMA_MEMORY_TO_PERIPH;

 hdma_tx.Init.PeriphInc          = DMA_PINC_DISABLE; 

 hdma_tx.Init.MemInc             = DMA_MINC_ENABLE;  

 hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;

 hdma_tx.Init.MemDataAlignment   = DMA_MDATAALIGN_BYTE;

 hdma_tx.Init.Mode               =  DMA_CIRCULAR; //DMA_NORMAL;  // ????? 

 hdma_tx.Init.Priority           = DMA_PRIORITY_HIGH; // ????

 HAL_DMA_Init(&hdma_tx);

 // ##-1- Configure the SPI peripheral

 // Set the SPI parameters

 SpiHandle.Instance              = SPI4;

 SpiHandle.Init.Mode             = SPI_MODE_MASTER;

 SpiHandle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; 

 SpiHandle.Init.Direction        = SPI_DIRECTION_2LINES_TXONLY;

 SpiHandle.Init.CLKPhase         = SPI_PHASE_2EDGE;

 SpiHandle.Init.CLKPolarity      = SPI_POLARITY_LOW;

 SpiHandle.Init.DataSize         = SPI_DATASIZE_8BIT;

 SpiHandle.Init.FirstBit         = SPI_FIRSTBIT_MSB;

 SpiHandle.Init.TIMode           = SPI_TIMODE_DISABLE;

 SpiHandle.Init.CRCCalculation   = SPI_CRCCALCULATION_DISABLE;

 SpiHandle.Init.CRCPolynomial    = 7;

 SpiHandle.Init.CRCLength        = SPI_CRC_LENGTH_8BIT;

 SpiHandle.Init.NSS              = SPI_NSS_SOFT;

 SpiHandle.Init.NSSPMode         = SPI_NSS_PULSE_DISABLE;

 SpiHandle.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE; 

 HAL_SPI_Init(&SpiHandle);

 // ##-4- Configure the NVIC for DMA

 // NVIC configuration for DMA transfer complete interrupt (SPI4_TX)

 HAL_NVIC_SetPriority(DMA_INTERRUPT, 1, 1); //DMA2_Stream3_IRQn

 HAL_NVIC_EnableIRQ(DMA_INTERRUPT);  //  DMA2_Stream3_IRQn

}

Function for INTERRUPT and switching PIN inside

void DMA2_Stream3_IRQHandler(void)

{   

   HAL_DMA_IRQHandler(&hdma_tx);

   HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); // LD1 blinky

   HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_9); // PD9 blinky

}

If i understand, DMA start to work, because interrupt handler worked, but DMA don'nt send any signal to MOSI PE14 pin.....

Best regards.

MRebo.3
Associate
August 8, 2022

Someone was able to do this work? I have tried but didn't work....

How I can changed the .md file? Where do I need to change? (I'm using STM32CubeIDE with STM32H755Zi, I would like to do ADC work together with DMA)

File:

/*

******************************************************************************

**

** File    : LinkerScript.ld (debug in RAM dedicated)

**

** Author   : STM32CubeIDE

**

** Abstract  : Linker script for STM32H7 series

**           1024Kbytes FLASH

**           800Kbytes RAM

**

**        Set heap size, stack size and stack location according

**        to application requirements.

**

**        Set memory bank area and size if external memory is used.

**

** Target   : STMicroelectronics STM32

**

** Distribution: The file is distributed as is without any warranty

**        of any kind.

**

*****************************************************************************

** @attention

**

** Copyright (c) 2022 STMicroelectronics.

** All rights reserved.

**

** This software is licensed under terms that can be found in the LICENSE file

** in the root directory of this software component.

** If no LICENSE file comes with this software, it is provided AS-IS.

**

*****************************************************************************

*/

/* Entry Point */

ENTRY(Reset_Handler)

/* Highest address of the user mode stack */

_estack = ORIGIN(RAM_D1) + LENGTH(RAM_D1); /* end of "RAM_D1" Ram type memory */

_Min_Heap_Size = 0x200 ; /* required amount of heap */

_Min_Stack_Size = 0x400 ; /* required amount of stack */

/* Memories definition */

MEMORY

{

 RAM_D1 (xrw)  : ORIGIN = 0x24000000, LENGTH = 512K

 FLASH  (rx)  : ORIGIN = 0x08000000, LENGTH = 1024K  /* Memory is divided. Actual start is 0x8000000 and actual length is 2048K */

 DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K

 RAM_D2 (xrw)  : ORIGIN = 0x30000000, LENGTH = 288K

 RAM_D3 (xrw)  : ORIGIN = 0x38000000, LENGTH = 64K

 ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K

}

/* Sections */

SECTIONS

{

 /* The startup code into "RAM" Ram type memory */

 .isr_vector :

 {

  . = ALIGN(4);

  KEEP(*(.isr_vector)) /* Startup code */

  . = ALIGN(4);

 } >RAM_D1

 /* The program code and other data into "RAM" Ram type memory */

 .text :

 {

  . = ALIGN(4);

  *(.text)      /* .text sections (code) */

  *(.text*)     /* .text* sections (code) */

  *(.glue_7)     /* glue arm to thumb code */

  *(.glue_7t)    /* glue thumb to arm code */

  *(.eh_frame)

  *(.RamFunc)    /* .RamFunc sections */

  *(.RamFunc*)    /* .RamFunc* sections */

  KEEP (*(.init))

  KEEP (*(.fini))

  . = ALIGN(4);

  _etext = .;    /* define a global symbols at end of code */

 } >RAM_D1

 /* Constant data into "RAM" Ram type memory */

 .rodata :

 {

  . = ALIGN(4);

  *(.rodata)     /* .rodata sections (constants, strings, etc.) */

  *(.rodata*)    /* .rodata* sections (constants, strings, etc.) */

  . = ALIGN(4);

 } >RAM_D1

 .ARM.extab  : { 

  . = ALIGN(4);

  *(.ARM.extab* .gnu.linkonce.armextab.*)

  . = ALIGN(4);

 } >RAM_D1

  

 .ARM : {

  . = ALIGN(4);

  __exidx_start = .;

  *(.ARM.exidx*)

  __exidx_end = .;

  . = ALIGN(4);

 } >RAM_D1

 .preinit_array   :

 {

  . = ALIGN(4);

  PROVIDE_HIDDEN (__preinit_array_start = .);

  KEEP (*(.preinit_array*))

  PROVIDE_HIDDEN (__preinit_array_end = .);

  . = ALIGN(4);

 } >RAM_D1

  

 .init_array :

 {

  . = ALIGN(4);

  PROVIDE_HIDDEN (__init_array_start = .);

  KEEP (*(SORT(.init_array.*)))

  KEEP (*(.init_array*))

  PROVIDE_HIDDEN (__init_array_end = .);

  . = ALIGN(4);

 } >RAM_D1

  

 .fini_array :

 {

  . = ALIGN(4);

  PROVIDE_HIDDEN (__fini_array_start = .);

  KEEP (*(SORT(.fini_array.*)))

  KEEP (*(.fini_array*))

  PROVIDE_HIDDEN (__fini_array_end = .);

  . = ALIGN(4);

 } >RAM_D1

 /* Used by the startup to initialize data */

 _sidata = LOADADDR(.data);

 /* Initialized data sections into "RAM" Ram type memory */

 .data : 

 {

  . = ALIGN(4);

  _sdata = .;    /* create a global symbol at data start */

  *(.data)      /* .data sections */

  *(.data*)     /* .data* sections */

  . = ALIGN(4);

  _edata = .;    /* define a global symbol at data end */

   

 } >RAM_D1

 /* Uninitialized data section into "RAM" Ram type memory */

 . = ALIGN(4);

 .bss :

 {

  /* This is used by the startup in order to initialize the .bss section */

  _sbss = .;     /* define a global symbol at bss start */

  __bss_start__ = _sbss;

  *(.bss)

  *(.bss*)

  *(COMMON)

  . = ALIGN(4);

  _ebss = .;     /* define a global symbol at bss end */

  __bss_end__ = _ebss;

 } >RAM_D1

 /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */

 ._user_heap_stack :

 {

  . = ALIGN(8);

  PROVIDE ( end = . );

  PROVIDE ( _end = . );

  . = . + _Min_Heap_Size;

  . = . + _Min_Stack_Size;

  . = ALIGN(8);

 } >RAM_D1

 /* Remove information from the compiler libraries */

 /DISCARD/ :

 {

  libc.a ( * )

  libm.a ( * )

  libgcc.a ( * )

 }

 .ARM.attributes 0 : { *(.ARM.attributes) }

}