Can arrays in DMAMEM be initialized?

Bill Greiman

Well-known member
I have a SdFat user posting an SdFat issue. He wrote an initialized array to an SD and said the content was not correct.

I looked at his example and it appears a DMAMEM array like the example below is not initialized. It appears to have random content.

Code:
DMAMEM uint8_t test[] = {1,2,3,4,5,6,7,8,9,10};
void setup() {
 Serial.begin(9600);
 while (!Serial) {
 }
 for (size_t i = 0; i < sizeof(test); i++) {
  Serial.print(test[i]);
  Serial.print(' ');
 }
 Serial.println();
}
void loop() {}

Typical output:
64 23 114 16 242 140 4 49 61 115

The correct output is printed if I remove the DMAMEM.
1 2 3 4 5 6 7 8 9 10

What should I tell him?
 
No, they cannot be initialized. From the Teensy 4.0 and 4.1 product pages:

  • DMAMEM - Variables defined with DMAMEM are placed at the beginning of RAM2. Normally buffers and large arrays are placed here. These variables can not be initialized, your program must write their initial values, if needed.
 
@MichaelMeissner is of course correct. As noted if user code wants known values there, they must be copied/created.

The T_4.x family has everything in FLASH on startup. The PROGMEM/Flash based ResetHandler() code moves RAM1 Program and Data from Flash to their expected locations before calling setup().

There are no such provisions for RAM2/DMAMEM. The processor uses some of that RAM2 space on initial starting, and the rest is left holding whatever happens to be there. On a warm restart that is prior values beyond the processor startup security scratch space.
 
If anyone knows a way to accomplish this, I would like to make future Teensyduino able to initialize DMAMEM. But so far, all my attempts have been unsuccessful.

The main problem is, at least by my limited knowledge of gcc, our definition of PROGMEM only puts the variable into a unique section but doesn't give a unique section name where its initialization data goes.

Code:
#define DMAMEM __attribute__ ((section(".dmabuffers"), used))

Maybe now that we've upgraded to gcc 11.3.1 (in 1.58-beta) perhaps there is some attribute we can use to tell gcc which section to put the initialization data into? Or maybe this has been possible all along with gcc 5.4.1 and I just don't know how to do it?

Anyway, just to explain, the reason we don't have DMAMEM initialization is I've never been able to find a way to tell gcc to put the initialization data for these variables into its own section, which the linker script would then place into flash and the startup code would copy to RAM.
 
If anyone knows a way to accomplish this, I would like to make future Teensyduino able to initialize DMAMEM. But so far, all my attempts have been unsuccessful.

The main problem is, at least by my limited knowledge of gcc, our definition of PROGMEM only puts the variable into a unique section but doesn't give a unique section name where its initialization data goes.

Code:
#define DMAMEM __attribute__ ((section(".dmabuffers"), used))

Maybe now that we've upgraded to gcc 11.3.1 (in 1.58-beta) perhaps there is some attribute we can use to tell gcc which section to put the initialization data into? Or maybe this has been possible all along with gcc 5.4.1 and I just don't know how to do it?

Anyway, just to explain, the reason we don't have DMAMEM initialization is I've never been able to find a way to tell gcc to put the initialization data for these variables into its own section, which the linker script would then place into flash and the startup code would copy to RAM.

I haven't delved into linker scripts for maybe 20 years now, but I believe the technique is to use the AT output attribute. Here is code clipped from:

Output section LMA

Every section has a virtual address (VMA) and a load address (LMA); see section Basic Linker Script Concepts. The address expression which may appear in an output section description sets the VMA (see section Output section address).

The linker will normally set the LMA equal to the VMA. You can change that by using the AT keyword. The expression lma that follows the AT keyword specifies the load address of the section.

This feature is designed to make it easy to build a ROM image. For example, the following linker script creates three output sections: one called `.text', which starts at 0x1000, one called `.mdata', which is loaded at the end of the `.text' section even though its VMA is 0x2000, and one called `.bss' to hold uninitialized data at address 0x3000. The symbol _data is defined with the value 0x2000, which shows that the location counter holds the VMA value, not the LMA value.

Code:
SECTIONS
  {
  .text 0x1000 : { *(.text) _etext = . ; }
  .mdata 0x2000 : 
    AT ( ADDR (.text) + SIZEOF (.text) )
    { _data = . ; *(.data); _edata = . ;  }
  .bss 0x3000 :
    { _bstart = . ;  *(.bss) *(COMMON) ; _bend = . ;}
}

The run-time initialization code for use with a program generated with this linker script would include something like the following, to copy the initialized data from the ROM image to its runtime address. Notice how this code takes advantage of the symbols defined by the linker script.

Code:
extern char _etext, _data, _edata, _bstart, _bend;
char *src = &_etext;
char *dst = &_data;

/* ROM has data at end of text; copy it. */
while (dst < &_edata) {
  *dst++ = *src++;
}

/* Zero bss */
for (dst = &_bstart; dst< &_bend; dst++)
  *dst = 0;
 
Back
Top