why is this in RAM1?

flok

Well-known member
Hi,

I've got this structure:

C++:
struct cmd_pair {
        const char       *const command;
        const char       *const parameters;
        const char       *const descr;
        const cmd_func_t func;
        enum { par_yes, par_no, par_optional } par_t;
};

that I instantiate like:

C++:
constexpr const cmd_pair cmd_pairs[] {
        { "help", "", "this help", cmd_help, cmd_pair::par_no },
        { "disassemble", "pc=/n=", "show current instruction", cmd_disassemble, cmd_pair::par_yes },

Now becaus eof the constexpr etc I expected it to NOT be in RAM1, yet:

Code:
20000d50 00000474 D _ZL9cmd_pairs.lto_priv.0

(output of
Code:
arm-none-eabi-nm -SC firmware.elf | grep ^200 | sort -k 2
).

I used const etc everywhere, how come it is still in RAM1?
 
Because for Teensy 4.x all data defaults to RAM1 unless you specify otherwise.

Oh right, the 'FLASHMEM' and 'DMAMEM' keywords. Odd: I expected const-data to be retrieved directly from flash. Is that technically not possible? Or too slow?
I've now put it in DMAMEM.
 
You want PROGMEM for const data in flash, FLASHMEM is for code/functions.
Since your structs are actually holding pointers to string literals, those will need to be stored separately in flash; F() is documented as intended for this but does not work, use PSTR() instead.
 
To get it all into flash memory only including all the string data, unfortunately an awkward syntax is needed with each string defined separately. If you define the strings inline, even though the cmd_pairs[] array is defined with PROGMEM, only the array with addresses of the strings goes into the flash memory.

Inline strings will get placed into RAM and the cmd_pairs[] array will get RAM locations addresses put into flash memory. Even though you're defining them inline inside a struct that has PROGMEM, your struct is storing the address of the string data because of the pointer syntax. As far as the compiler is concerned, PROGMEM only applies to the strorage of that 32 bit address. The string data itself is considered a completely different thing that gets default storage (in RAM1) so the address that gets stored in your struct happens to be the address where the string went into RAM1.

I'm not aware of any nice and convenient syntax like PSTR() which works for global scope definitions that use pointers to strings. The only way I know that really works requires each string separately defined with PROGMEM so the string data goes into flash, and then PROGMEM on your array so the array with string addresses goes into flash. You need to use PROGMEM 7 times, because to the compiler this really is 7 separate things.

The syntax would look like this:

C++:
struct cmd_pair {
        const char       *const command;
        const char       *const parameters;
        const char       *const descr;
        //const cmd_func_t func;
        enum { par_yes, par_no, par_optional } par_t;
};

PROGMEM const char str1[] = "help";
PROGMEM const char str2[] = "";
PROGMEM const char str3[] = "this help";
PROGMEM const char str4[] = "disassemble";
PROGMEM const char str5[] = "pc=/n=";
PROGMEM const char str6[] = "show current instruction";

PROGMEM const cmd_pair cmd_pairs[] {
        { str1, str2, str3, cmd_pair::par_no },
        { str4, str5, str6, cmd_pair::par_yes },
};

void setup() {
}

void loop() {
  Serial.println(cmd_pairs[1].descr);
  delay(1000);
}
 
Last edited:
However, you can use the nice and convenient initialization with inline strings and only a single PROGMEM if you define your struct to hold the actual character data rather than the address of a string located elsewhere. By declaring your struct with character arrays instead of pointer syntax, the compiler considers the string storage as part of your struct, so you can (and must) define your strings inline where a single PROGMEM applies to everything.

This will allocate more memory because you need to define a fixed size char buffer large enough to hold the longest string (assuming most of your strings are shorter than the maximum size char[] array by more than the 32 bit address the other way has to store). But flash memory is usually in plentiful supply. Maybe this nicer syntax worthwhile trade-off for consuming more flash memory?

The compiler might generate slightly smaller code to access your data, since it can just use the address of the struct rather than reading the string address and (probably) using another register and more code to read the actual data once it knows the string address.

Here's that simpler but less efficient flash usage syntax:

C++:
struct cmd_pair {
        const char command[12];
        const char parameters[12];
        const char descr[32];
        //const cmd_func_t func;
        enum { par_yes, par_no, par_optional } par_t;
};

PROGMEM const cmd_pair cmd_pairs[] {
        { "help", "", "this help", cmd_pair::par_no },
        { "disassemble", "pc=/n=", "show current instruction", cmd_pair::par_yes },
};

void setup() {
}

void loop() {
  Serial.println(cmd_pairs[1].descr);
  delay(1000);
}
 
Last edited:
Or too slow?

The flash memory has a 4 bit data path at approx 100 MHz. It's cached by a pair of 32K Cortex M7 caches (one for data access as in this case, the other for instruction fetches), so you only suffer the slow 50 Mbyte/sec speed for cache misses. The cache is 4 way set associative, so even with a moderate amount of access to other stuff, there's a pretty good chance your const data might still be cached. What performance you actually get depends heavily on how much the rest of your program puts pressure on the cache.

With the default, everything goes into RAM1 which is "tightly coupled" so all 512K is effectively as fast as the two 32K caches. You always get maximum possible performance, but the downsize is too many things compete for the limited 512K RAM1 in larger programs.
 
Back
Top