STM32 create RAM section

2

I work with STM32H7 microcontroller and GNU/GCC, in my code, I am using only DTCM RAM but I want to store some buffers in another memory which is accessible by DMA.
I am totally new to linker script, do I need to edit startup code ?
Here is my linker script, I added some code in SECTIONS

/* Memories definition */
MEMORY
{
  DTCMRAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 128K
  ITCMRAM    (xrw)    : ORIGIN = 0x00000000,   LENGTH = 64K
  RAM_D1    (xrw)    : ORIGIN = 0x24000000,   LENGTH = 512K
  RAM_D2    (xrw)    : ORIGIN = 0x30000000,   LENGTH = 288K
  RAM_D3    (xrw)    : ORIGIN = 0x38000000,   LENGTH = 64K
  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 2048K
}

/* Sections */
SECTIONS
{
   ...
   
   .ram1block 0x24000000 :
  {
    KEEP(*(.ram1section))
  } > RAM_D1

   ...
}

I want to use this attribute :

uint8_t __attribute__(( section(".ram1section") )) ads_buf[READBACK_LENGTH];

Am I doing this right ?
Do you have any topics or advice ? I am new to these scripts and a bit lost

EDIT : Apparently there is a simpler solution :
In linker file

SECTIONS
{
   ads_buf=0x24000000;
   ...
}

In source

extern uint8_t ads_buf[READBACK_LENGTH];

Can anyone confirm this solution is valid ?

c
stm32
gnu
ram
linker-scripts
asked on Stack Overflow Jan 5, 2021 by GabrielT • edited Jan 5, 2021 by GabrielT

2 Answers

4

Your "simpler" solution means you'll have to assign addresses for all objects in that memory section manually, taking extra care to avoid overlap. This is precisely the thing linkers were invented to avoid, so I'd advise not to go that way.

You don't need to specify the address again for the output section, just for the memory range. Whether you need to modify the startup code depends on whether you need that section initialized at startup.

No initialization

If you don't need initialization because you'll e.g. fill the buffer with incoming data before reading it, modify the linker script as follows:

   .ram1block (NOLOAD) :
  {
    KEEP(*(.ram1section))
  } > RAM_D1

And you're done.

Trivial initialization

If you do need it initialized trivially, e.g. all zeroes, add address symbols as follows:

   .ram1blockBss (NOLOAD) :
  {
    . = ALIGN(4);
    _BeginRam1Bss = .;
    KEEP(*(.ram1sectionBss))
    . = ALIGN(4);
    _EndRam1Bss = .;

  } > RAM_D1

Aligning the address makes initialization easier and faster. Note that I renamed the input section to ram1sectionBss to denote that this section is zero-initialized. Add something like this to the startup-code to zero-initialize the memory:

    ldr r0, =_BeginRam1Bss
    ldr r1, =_EndRam1Bss
    ldr r2, =0

    b 2f
1:  str r2, [r0], #4
2:  cmp r0, r1
    blo 1b

This initializes the whole block with zeroes efficiently, so that C variables defined as

uint8_t __attribute__(( section(".ram1sectionBss") )) ads_buf[READBACK_LENGTH];

will be zero-initialized.

Value initialization

If you need to initialize the stuff in your memory with distinct values, modify the linker section definition as follows:

   .ram1blockData (NOLOAD) :
  {
    . = ALIGN(4);
    _BeginRam1Data = .;
    KEEP(*(.ram1sectionData))
    . = ALIGN(4);
    _EndRam1Data = .;

  } > RAM_D1 AT> FLASH
  _InitRam1Data = LOADADDR (.ram1blockData);

Also append a . = ALIGN(4); into the last section before .ram1blockData that goes into FLASH to make sure the load address in flash is aligned too.

Then add the following to the startup code:

    ldr r0, =_BeginRam1Data
    ldr r1, =_EndRam1Data
    ldr r2, =_InitRam1Data

    b 2f
1:  ldr r3, [r2], #4
    str r3, [r0], #4
2:  cmp r0, r1
    blo 1b

If you then define your C variable as

uint8_t __attribute__(( section(".ram1sectionData") )) ads_buf[READBACK_LENGTH] = { 1, 2, 3, 4 };

it will be initialized correctly. Note again the renamed section (ram1sectionData to denote that the data is initialized).

Trivial + value initialization

If you need both zero-initialized and value-initialized data in your memory block, simply put both section definitions in the linker script and both assembly blocks in the startup code, to make C definitions like this work:

uint8_t __attribute__(( section(".ram1sectionBss") )) ads_buf1[READBACK_LENGTH];
uint8_t __attribute__(( section(".ram1sectionData") )) ads_buf2[READBACK_LENGTH] = { 1, 2, 3, 4 };

PS: Note the identifiers like _BeginRam1Bss that begin with underscore + uppercase letter, which are reserved C identifiers. This means you can't accidentally use them in C code which would collide with the linker script. Linker-Script + Startup-Code are part of the implementation and should provide a conformant runtime for the regular C code, without "taking up" non-reserved identifiers that should be usable by the C code.

answered on Stack Overflow Jan 5, 2021 by Erlkoenig • edited Jan 5, 2021 by Erlkoenig
1

No, it is not a valid solution. Why? - because it only declares the symbol. Linker is not aware that there is something in the memory at this address. It can place another data there. A very, very bad idea.

answered on Stack Overflow Jan 5, 2021 by 0___________

User contributions licensed under CC BY-SA 3.0