PROGMEM use on Teensy 3.0

bongo

Member
Context: I am working on using the adafruit plasma code for a 16x16 physical array of RGB LEDs using LPD8806 strips. [See https://github.com/adafruit/RGB-matrix-Panel/tree/master/examples/plasma_16x32 for the original.]

Here's my more general technical question about using PROGMEM on the Teensy 3.0. I want to store and read from a fairly large array held in PROGMEM: 4096 int16_t elements. When I try to upload my code to the Teensy 3.0, it crashes and the Teensy no longer shows up under the Arduino IDE Tools>Serial Port menu. I can reboot the Teensy and successfully load a different sketch, but this particular sketch consistently causes a crash. I wonder if it has something to do with the amount of data being written to PROGMEM? Or maybe that the Teensy 3.0 is not AVR-based?

My sketch uses:
#include <avr/pgmspace.h>​
to make use of the PROGMEM and pgm_read_xxx functions.

I initialize the array with:
static int16_t sinetab[4096] PROGMEM = {lots of values here}​

I read from the array with:
(int16_t)pgm_read_word(sinetab + offset)​
I am using read_word rather than read_byte because my array is storing 16-bit integers.

I've attached my entire sketch.

Thanks in advance for any help you might be able to offer.

Bongo
 

Attachments

  • ZigTable_Plasma3_20130413.ino.zip
    10.6 KB · Views: 389
On Teensy 3.0, all you have to do is use "const" in the variable declaration. Or at least that's all you're supposed to do. Everything that's "const" gets put into flash without consuming any RAM.

PROGMEM, pgm_read_byte() and the other AVR names are defined only so code designed for AVR can compile. But they do absolutely nothing on Teensy 3.0.

I'd recommend adding const to that huge array. If you're not planning to compile on Teensy 2.0 or other AVR boards, you might as well remove the PROGMEM stuff. Accessing the array normally is much nicer and more readable code that using pgm_read_byte().
 
Thank you Paul. As always, I appreciate that you take the time to help those of us stumbling along (I took a handful of comp sci and elect engr courses over 30 years ago, and am trying to teach myself how to work with boards like the teensy).

I will use 'const' as you suggest.

bongo
 
On Teensy 3.0, all you have to do is use "const" in the variable declaration. Or at least that's all you're supposed to do. Everything that's "const" gets put into flash without consuming any RAM.

Apparently this includes literal constants.

Code:
void setup() {
  Serial.begin(1200);
  delay(1000);
  Serial.print(
  "\n"
    "ACT I\n"
    "SCENE I. A desert place.\n"
    "\n"
    "    Thunder and lightning. Enter three Witches \n"
    "\n"
    "First Witch\n"
    "\n"
    "    When shall we three meet again\n"
    "    In thunder, lightning, or in rain?\n"
    "\n"

// and so on for the entire play

    "\n"
    "    Flourish. Exeunt\n"
    );
}

void loop() {
}

which compiles and fits into flash on Teensy 3.0

Code:
Binary sketch size: 119,956 bytes (of a 131,072 byte maximum)
Estimated memory use: 3,616 bytes (of a 16,384 byte maximum)

(As seen here, modified to only print once).
 

Attachments

  • macbeth.ino
    146.1 KB · Views: 360
Yes, string literals are formally const char[] but conventionally char [].
You can confirm that strings are const by considering which of these two lines is correct:
"thin"[3] = 'k';
int i = 0; while("thing"[i++]) ;

It used to be much easier. In the 1980's I wrote a C compiler for the 8-bit processors (Intel 8080 and Motorola 6809). I noticed
the "readonly" keyword was reserved so I asked Dennis Ritchie if my use of it (storing constants in EPROM) was reasonable. He confirmed
this and we discussed whether it should be a type declarator or a storage class declarator. The latter is what I implemented and is much
simpler conceptually and practically and what the use of "const" discussed here is all about. The C++ and C standards committees were ambitious when replacing readonly by const so there are now various subtle situations where you will need to consult books or the spec. for clarifications. C# has readonly and const. Ouch!
 
Now 30-some years later, this has come full-circle with the proposed introduction of named address spaces into the C language. Of course, the compiler used on Arduino for AVR doesn't support this (it's an older version), but eventually we can look forward to proper, well designed compiler support for AVR instead of the ugly hack that is PROGMEM.
 
Yes, I won't miss PROGMEM. In the early days of C there was a lot of resistance to such architecture-specific features because
everyone was working so hard to port software and learning how to write portable software. The named address spaces solution seems
to be somewhat portable and might even work in some time-efficiency scenarios such as locking data in caches.
 
The C++ and C standards committees were ambitious when replacing readonly by const so there are now various subtle situations where you will need to consult books or the spec. for clarifications. C# has readonly and const. Ouch!
The ANSI-C committee (X3J11) did debate whether const/volatile were to be storage classes or type modifiers. The problem with storage modifiers is it falls down in terms of pointers. Consider:

Code:
readonly int *p = &q;

Does this mean you can modify p to point to a different location, but can't modify what p points to (const int * p). Or can you not modify p, but you can modify what it points to (int *const p).


BTW, I was one of a handful of people that was on the original committee from the first meeting in 1983 until the publication of the ANSI standard in 1989, and then the replacement with the ISO standard in 1990 (ANSI is one of the American standards bodies, ISO is the world wide standards body). I originally represented Data General from 1983 through 1989. I had created most of the C front end for the Data General MV/Eclipse computers, and I switched to finishing the GCC Motorola 88000 port in the last years at DG. From 1990 through 1994, I represented the Open Source Foundation. After OSF and I parted company, I dropped out of the standards business.
:cool:
 
Last edited:
Thanks for the update on that history.
For what it is worth I think const as ANSI-C defined it is much better than a storage class modifier like readonly. Leveraging the distinction in C between
a declaration and a definition, const is able to provide a way to express a promise to never modify something and also the desire to have data stored somewhere it can't be modified.
 
I was looking through the code and noticed that PROGMEM is defined as nothing for Teensy 3, but as "__attribute__((section(".progmem")))" for Teensy 4. (avr/pgmspace.h.)

Two questions:
  1. Is PROGMEM needed again for Teensy 4?
  2. Where, for Teensy 3, is the linker told that "const" means "put in flash"? Is this a specific feature of the GCC compiler/linker toolchain or something?
 
To quickly answer your 3 questions...

Is PROGMEM needed again for Teensy 4?

Yes.

Where, for Teensy 3, is the linker told that "const" means "put in flash"?

For Teensy 3.2, in mk20dx256.ld on line 49:

https://github.com/PaulStoffregen/c...7de069633740d48f84e2/teensy3/mk20dx256.ld#L49


Is this a specific feature of the GCC compiler/linker toolchain or something?

Yes. It's called the "linker script". That's the name to know if you wish to search.
 
Just so I'm absolutely clear, on Teensy 4, do `const` things still go into flash automatically?
 
Just so I'm absolutely clear, on Teensy 4, do `const` things still go into flash automatically?
Yes and No ;)

Yes part - everything is in flash... However the NO part:

Some (Majority) of things are then copied down into the RAM1 area, either DTCM and ITCM and code is setup to access those things at those RAM1 addresses.

I am pretty sure if you simply mark the object as const it WILL copy the data down into ITCM and be accessed there.

To tell the compiler to NOT copy the stuff down requires the PROGMEM setting, which the linker script will then know that these variables are not to be copied and are accessed using the FLASH address.
 
Thanks, @KurtE. I was afraid of that answer. In theory, then, do I need to define a PROGMEM variable for every string literal if I want them to go into PROGMEM? Can I teach the linker to tag const arrays of char (or even unsigned char) as PROGMEM?

Second: Is it valid to think of PROGMEM as, “don’t copy into RAM”?
 
PROGMEM - means run it from Program Memory (Flash) in this case.
Different on old AVR boards where this implied different address space. where you then had to use other stuff to reference the variables.

Again there is lots of interesting hints on the product page in the memory section: https://www.pjrc.com/store/teensy40.html#memory
PROGMEM & F() - Variables defined with PROGMEM, and strings surrounded by F() are placed only in the flash memory. They can be accessed normally, special functions normally used on 8 bit boards are not required to read PROGMEM variables.

Like if you do: Serial.println(F("This is some text"));

This text will remain in flash.

One minor warning. The string will be created with a class name something like: __FlashStringHelper

So there are some cases where when I am trying to setup things like a string table, where I might end up casting it to something like: const char *
 
Ok. Sounds like pre-Teensy 4, don’t need F(), but for Teensy 4 I need F() again. Thanks again, @KurtE.
 
I still don't think "F()" puts literal strings in flash on Teensy 4. Am I missing something here? Here's a demo.

Code:
void setup() {
  // 8k literal string:
  Serial.printf("%p\n",
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"

      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"

      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"

      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"

      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"

      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"

      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"

      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
      "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"
  );
}

void loop() {}

When I build the program, I see these sizes and output:
Code:
Memory Usage on Teensy 4.1:
  FLASH: code:26372, data:13208, headers:8544   free for files:8078340
   RAM1: variables:13824, code:23704, padding:9064   free for local variables:477696
   RAM2: variables:12384  free for malloc/new:511904

Output: 0x200004b8

When I wrap the literal string in F(), I see these sizes and output:
Code:
Memory Usage on Teensy 4.1:
  FLASH: code:26372, data:13208, headers:8544   free for files:8078340
   RAM1: variables:13824, code:23704, padding:9064   free for local variables:477696
   RAM2: variables:12384  free for malloc/new:511904

Output: 0x200004b8

It's exactly the same. As I was experimenting, I discovered this thread and post: https://forum.pjrc.com/threads/6923...ot-in-the-ITCM?p=297654&viewfull=1#post297654

Maybe the only way to use flash-based literal strings is to use a function like that?
 
Back
Top