-
Notifications
You must be signed in to change notification settings - Fork 5
Memory Mapping
Here some information about how the various Teensies utilize the available on- and off chip memory, how you can specify which part of the memory to use and how to handle sketches with high memory demand.
Here a quick write up for those, only interested in how to generate variables, constants and functions in certain parts of the available memory:
By default, static and global variables are stored in RAM code is stored in FLASH and local variables are stored on the STACK.
int i; // <- RAM
const int k = 5 // <- FLASH
void func(void) // <- FLASH
{
static float f; // <- RAM
double d; // <- STACK
// do something
}
Since reading from FLASH is a bit slower than reading from RAM you might consider moving time critical functions to RAM:
FASTRUN // <- move function to RAM
void func(void) // <- RAM
{
static float f; // <- RAM
double d; // <- STACK
// do something
}
Global, static and local variables behave like they do for the T3.x boards. However, Teensy 4.x uses external flash memory which is significantly slower than RAM. Thus, The T4.x linker moves all code to RAM per default. If you need a lot of RAM you might consider not to copy code which is not time critical to the RAM:
int i; // <- RAM
char txt2[]{"Hello RAM"}; // <- RAM
const char txt[]{"Hello FLASH"}; // <- FLASH
FLASHMEM // do not copy this function to RAM
void func(void) // <- FLASH
{
static float f; // <- RAM
double d; // <- STACK
// do something
}
The T4.x boards have a second RAM block which, per default, is used for DMA buffers and the HEAP. Optionally you can solder up to 16MB external RAM on the bottom side of the board. If you want to place some variables in the second block (DMAMEM) or on the external RAM (EXTMEM) you can use the following code:
uint8_t buffer1[100 * 1024]; // Place a 100kB buffer in RAM
DMAMEM
uint8_t buffer2[300 * 1024]; // Place a 300kB buffer in the second RAM block
EXTMEM
uint8_t buffer3[3* 1024 * 1024] // Place a 3MB buffer in the external RAM
The Teensy LC and all of the Teensy 3.x boards have a very simple memory model: There is a single block of flash and a single block of RAM memory. The size of RAM and flash depend on the board type. For all boards the flash memory starts at address 0x0000'0000. The image below shows the flash and RAM sizes and the respective start/end addresses.
Please note: the processors used on the T3.x boards distinguish between a low (< 0x1FFF'FFFF) and high (> 0x1FFF'FFFF) part of the RAM. Usually this splitting has no consequences. However, in rare cases it can generate weird alignment errors if the linker places a variable across the bank border (see here for more information on this issue).
For the T-LC and T3.x boards, both, RAM and flash memory are part of the processor and can be accessed very quickly. The maximum wait cycles to access RAM is 2 cycles (for consecutive reads the processor often can access the RAM without any wait cycle). Data from the flash can be read with maximal 4 wait cycles.
to be described
to be described
The linker will place different objects, like constants, variables, code etc. into various locations in the given memory. (For those, interested in technical details, these locations are defined in so called linker scripts. Here for example the one for the T3.2 and here the script for the T4.1)
Global variables are variables defined outside of any functions, static
variables are defined inside a function using the keyword static
. Static variables keep their values between function calls. Here some examples.
int i; // global variable (uninitalized)
double x = 42; // global variable (initalized)
void setup(){
static int k;
}
void loop(){
}
The linker places such variables at fixed locations in the RAM. These locations are determined at link-time and can not be changed at runtime.
If you want to have a closer look yourself, you can use the MemoryTool
which
consists of only two files MemoryTool.h
and MemoryTool.cpp
which you can copy to your sketch folder. You can download it from here: https://github.com/luni64/TeensyHelpers.
int i; // global variable (uninitalized)
double x = 42; // global variable (initalized)
void setup(){
static int k; // static variable, stored in the same place as global variables
while (!Serial) {} // wait until the pc connected
printMemoryInfo(i); // print info about the variables
printMemoryInfo(x);
printMemoryInfo(k);
}
void loop(){
}
For a T3.6 this prints:
i
Start address: 0x1FFF'11EC
End address: 0x1FFF'11EF
Size: 4 Bytes
Location: RAM (not initialized)
x
Start address: 0x1FFF'0738
End address: 0x1FFF'073F
Size: 8 Bytes
Location: RAM (initialized)
k
Start address: 0x1FFF'11F0
End address: 0x1FFF'11F3
Size: 4 Bytes
Location: RAM (not initialized)
Lets see what happens if we change x
to a constant:
int i;
const double x = 42; // <== constant
void setup(){
static int k;
while (!Serial) {}
printMemoryInfo(i);
printMemoryInfo(x);
printMemoryInfo(k);
}
void loop(){
}
Since one can't change a constant at runtime the linker leaves the constant in the flash as you can see in the printout below:
i
Start address: 0x1FFF'11E4
End address: 0x1FFF'11E7
Size: 4 Bytes
Location: RAM (not initialized)
x
Start address: 0x0001'08C8
End address: 0x0001'08CF
Size: 8 Bytes
Location: FLASH
k
Start address: 0x1FFF'11E8
End address: 0x1FFF'11EB
Size: 4 Bytes
Location: RAM (not initialized)
What about c-strings:
char myString[] = "This is a global c-string"; // can be changed at runtime
const char myConstantString[] = "I'm constant"; // can not be changed
void setup()
{
while(!Serial){}
char myLocalString[] = "Local c-string";
printMemoryInfo(myString);
printMemoryInfo(myConstantString);
printMemoryInfo(myLocalString);
}
void loop(){
}
Here the printout which shows that if you want to place a string in the flash, all you need to do is declaring it const.
myString
Start address: 0x1FFF'0734
End address: 0x1FFF'074D
Size: 26 Bytes
Location: RAM (initialized)
myConstantString
Start address: 0x0001'0908
End address: 0x0001'0914
Size: 13 Bytes
Location: FLASH
myLocalString
Start address: 0x2002'FFD0
End address: 0x2002'FFDE
Size: 15 Bytes
Location: STACK
Teensy is a PJRC trademark. Notes here are for reference and will typically refer to the ARM variants unless noted.