Chapter 7: ESP32 Memory Architecture Overview

Chapter Objectives

By the end of this chapter, you will be able to:

  • Understand the different types of memory present in ESP32-series microcontrollers (ROM, SRAM, Flash, RTC Memory, PSRAM).
  • Describe the general memory map and how different memory regions are addressed.
  • Explain how program code (.text), read-only data (.rodata), initialized data (.data), and uninitialized data (.bss) are typically stored and managed.
  • Grasp the concepts of heap memory for dynamic allocation and stack memory for function calls and local variables.
  • Differentiate between IRAM (Instruction RAM) and DRAM (Data RAM) and their use cases.
  • Recognize the role of external SPI Flash for persistent storage and how it’s mapped into the address space.
  • Understand what PSRAM (Pseudo-Static RAM) is and when it might be used to extend available RAM.
  • Be aware of memory limitations and their potential impact on application design and performance.
  • Identify key differences in memory architecture and capacities across various ESP32 variants.
  • Use ESP-IDF tools like idf.py size to analyze the memory footprint of an application.

Introduction

In embedded systems, memory is a precious resource. Unlike desktop computers with gigabytes of RAM, microcontrollers like the ESP32 operate with significantly more constrained memory capacities. A thorough understanding of the ESP32’s memory architecture is therefore not just academic; it’s a practical necessity for developing robust, efficient, and reliable applications. Knowing where your code and data reside, how much space they occupy, and the characteristics of different memory types will empower you to make informed design decisions, optimize performance, and troubleshoot common memory-related issues.

This chapter will take you on a tour of the ESP32’s memory landscape. We’ll explore the various types of memory available, from the fast internal SRAM to the capacious external flash, and discuss how they are organized and utilized by the ESP-IDF framework. By understanding these fundamentals, you’ll be better equipped to manage memory effectively in your ESP32 projects.

Theory

The ESP32-series SoCs feature a complex memory system comprising several distinct types of memory, each with its own characteristics and purpose.

Overview of Memory Types

Memory Type Purpose Key Characteristics Typical Use
ROM (Read-Only Memory) Stores first-stage bootloader and core Espressif libraries.
  • Non-volatile
  • Not writable by users
  • Content fixed during chip manufacturing
Initial boot sequence, fundamental hardware routines.
Internal SRAM (Static RAM) Primary on-chip RAM for fast program execution and data storage.
  • Volatile
  • Fast access
  • Divided into IRAM (Instruction) and DRAM (Data)
  • IRAM: Performance-critical code execution.
  • DRAM: Global variables, stack, heap, general data.
RTC Memory (Real-Time Clock) Small SRAM that can retain data during deep-sleep (if RTC domain powered).
  • Low power
  • Small capacity
  • RTC FAST (CPU accessible) & RTC SLOW (ULP focused)
ULP co-processor programs/data, data persistence across deep-sleep.
eFuse (Embedded Flash) One-Time Programmable (OTP) non-volatile memory for critical system parameters.
  • Non-volatile
  • OTP (bits usually 0 -> 1 only)
  • Very small capacity
MAC addresses, security keys, chip revision, permanent configuration.
External SPI Flash Primary non-volatile storage for application firmware and user data.
  • Non-volatile
  • Large capacity (MBs)
  • Block-erasable
  • Slower than SRAM; accessed via cache
Application code (XIP), NVS data, file systems (SPIFFS, FAT), large assets.
PSRAM (Pseudo-Static RAM) / External SPI RAM Optional external RAM to expand available data memory.
  • Volatile
  • Accessed via SPI (slower than internal SRAM)
  • Memory-mapped via cache
  • Not on all variants/modules
Large buffers, extended heap for memory-intensive applications.
  1. ROM (Read-Only Memory):
    • Purpose: Contains the first-stage bootloader, and on some ESP32 variants, certain core library functions (e.g., some floating-point routines, cryptography functions) provided by Espressif. This code is permanently burned into the chip during manufacturing.
    • Characteristics: Non-volatile (contents persist without power), not writable by user applications. Its content cannot be changed after chip fabrication.
  2. Internal SRAM (Static RAM):This is the primary on-chip RAM used for program execution and data storage. It’s fast but volatile (contents are lost when power is removed). Internal SRAM is typically divided into:
    • IRAM (Instruction RAM): A portion of SRAM primarily used for executing performance-critical parts of the application code. Code running from IRAM executes faster than code fetched from external flash via the cache.
    • DRAM (Data RAM): Another portion of SRAM used for storing data, including global variables (.data, .bss sections), the system stack, FreeRTOS task stacks, and the heap (for dynamic memory allocation).
    • Shared RAM: Some RAM regions might be shareable or configurable for either instruction or data use, depending on the specific ESP32 variant and configuration.
  3. RTC (Real-Time Clock) Memory:
    • Purpose: A small amount of SRAM that can retain its contents even when the main CPUs are in deep-sleep mode, provided the RTC domain remains powered. It’s used by:
      • The ULP (Ultra-Low-Power) co-processor for its programs and data.
      • The main application to store small amounts of data that need to persist across deep-sleep cycles.
    • Types:
      • RTC FAST Memory: Can be accessed by the main CPUs like regular SRAM when the system is awake. Code from this region can be executed.
      • RTC SLOW Memory: Primarily for ULP co-processor access and data storage. Access by main CPUs might be slower or have limitations.
    • Characteristics: Low power consumption, relatively small capacity.
  4. eFuse (Embedded Flash):
    • Purpose: A very small, one-time programmable (OTP) non-volatile memory. Used to store critical system parameters like MAC addresses, security keys for flash encryption and secure boot, chip revision information, and other permanent configuration bits.
    • Characteristics: Non-volatile, OTP (bits can typically only be changed from 0 to 1, not back). Not for general application data storage.
  5. External SPI Flash:
    • Purpose: The primary non-volatile storage for the application firmware, NVS (Non-Volatile Storage) library data, file systems (like SPIFFS or FAT), and potentially large data assets. ESP32 SoCs do not have large internal flash for firmware; they rely on an external SPI flash chip connected via SPI interface.
    • Characteristics: Non-volatile, block-erasable, relatively slower access compared to internal SRAM but much larger capacity (typically MBs).
    • Memory Mapping: A portion of the external flash is memory-mapped into the ESP32’s address space via a cache, allowing code to be executed directly from flash (XIP – Execute In Place) and data to be read as if it were in memory.
  6. PSRAM (Pseudo-Static RAM) / External SPI RAM:
    • Purpose: An optional external RAM chip that can be connected via an SPI interface to significantly expand the available data memory. Useful for applications requiring large buffers, heaps, or complex data structures that don’t fit into internal SRAM.
    • Characteristics: Volatile, accessed via SPI, generally slower than internal SRAM but faster than accessing external SPI flash for data. It’s also memory-mapped via a cache.
    • Availability: Not all ESP32 variants or modules support PSRAM, or they might have different maximum capacities.

Memory Map & Organization

The ESP32’s CPUs access memory through a 32-bit address space. Different physical memory types (internal SRAM, ROM, flash cache, PSRAM cache, peripherals) are mapped to specific address ranges within this space. The exact memory map varies between ESP32 variants, so consulting the Technical Reference Manual (TRM) for your specific chip is crucial for precise details.

Code and Data Storage in Firmware

When you build an ESP-IDF project, your application code and data are organized into several sections, which are then placed into appropriate memory regions by the linker:

  • .text (Code Section): Contains the compiled machine instructions of your program.
    • Typically stored in external SPI flash and executed via the instruction cache (XIP).
    • Performance-critical functions can be explicitly placed in IRAM using the IRAM_ATTR attribute for faster execution.
  • .rodata (Read-Only Data Section): Contains constants, string literals, and other read-only data.
    • Typically stored in external SPI flash and accessed via the data cache.
    • Functions or data that need to be accessed when the flash cache is disabled (e.g., during flash write operations) might be placed in IRAM/DRAM using attributes.
  • .data (Initialized Data Section): Contains global and static variables that are initialized with non-zero values.
    • Their initial values are stored in external SPI flash.
    • At startup, before app_main is called, these values are copied from flash to DRAM.
  • .bss (Block Started by Symbol Section): Contains uninitialized (or zero-initialized) global and static variables.
    • Does not occupy space in the flash image (except for metadata about its size).
    • At startup, the memory region for .bss in DRAM is cleared to zeros.
  • Linker Scripts (.ld files): These files, provided by ESP-IDF and customizable, instruct the linker on how to arrange these sections into the available memory regions (IRAM, DRAM, Flash-mapped regions).
Section Name Content Typical Storage Location (Initial) Typical Runtime Location / Access Notes
.text Compiled machine instructions (program code). External SPI Flash Executed from Flash via Instruction Cache (XIP). Performance-critical parts can be moved to IRAM using IRAM_ATTR. Largest part of most firmware images.
.rodata Read-only data: constants, string literals. External SPI Flash Accessed from Flash via Data Cache. Can be moved to IRAM/DRAM if needed when cache is disabled. Contents cannot be modified at runtime.
.data Initialized global and static variables (with non-zero initial values). Initial values stored in External SPI Flash. Copied to DRAM at system startup. Occupies space in both Flash (for initial values) and RAM (at runtime).
.bss Uninitialized (or zero-initialized) global and static variables. Metadata about its size stored in Flash. Does not store actual zeros in Flash. Memory block in DRAM is cleared to zero at system startup. Consumes RAM at runtime but minimal Flash space.

Heap and Stack

  • Stack:
    • Purpose: Used for storing local variables within functions, function arguments, return addresses during function calls, and other temporary data related to function execution context.
    • Management: Each FreeRTOS task (including the main task that runs app_main) has its own dedicated stack. Stack memory is allocated when a task is created and deallocated when the task is deleted. It typically resides in DRAM.
    • Growth: Stacks usually grow downwards in memory.
    • Stack Overflow: If a task’s stack usage exceeds its allocated size, a stack overflow occurs, often leading to crashes or unpredictable behavior. This is a common bug in embedded systems.
  • Heap:
    • Purpose: A region of memory (typically in DRAM, but can also include PSRAM) used for dynamic memory allocation at runtime using functions like malloc(), calloc(), realloc(), and free().
    • Management: Managed by a heap allocator. ESP-IDF provides a heap implementation capable of managing multiple memory regions with different capabilities.
    • Fragmentation: Over time, repeated allocation and deallocation of different-sized blocks can lead to heap fragmentation, where there might be enough total free memory but not enough contiguous memory for a large allocation request.
    • heap_caps API: ESP-IDF provides the heap_caps_... family of functions (e.g., heap_caps_malloc()) that allow allocating memory with specific capabilities (e.g., DMA-capable, executable, 32-bit accessible, PSRAM).
Feature Stack Memory Heap Memory
Purpose Local variables, function arguments, return addresses, task execution context. Dynamic memory allocation at runtime for data whose size is not known at compile time or needs to persist beyond a single function call.
Allocation & Deallocation Automatic: Managed by the compiler. Memory is allocated when entering a scope (e.g., function call) and deallocated when exiting. Manual: Managed by the programmer using functions like malloc(), calloc(), realloc(), and free() (or heap_caps_... variants).
Size Fixed size per task, defined at task creation (e.g., via menuconfig for app_main or xTaskCreate parameters). Variable size, limited by the total available heap space (DRAM + PSRAM if configured).
Location Typically in DRAM. Each FreeRTOS task has its own stack. Typically in DRAM. Can also utilize PSRAM if available and allocated using MALLOC_CAP_SPIRAM.
Speed Very fast access due to its contiguous nature and CPU register usage (stack pointer). Slightly slower than stack access due to allocator overhead and potential for non-contiguous blocks.
Growth Direction Usually grows downwards in memory address space. Grows as needed within the heap region(s); specific direction depends on allocator implementation.
Key Issues Stack Overflow: Occurs if stack usage exceeds allocated size. Leads to crashes or data corruption.
  • Heap Exhaustion: Running out of heap memory.
  • Memory Leaks: Allocated memory is not freed when no longer needed.
  • Fragmentation: Heap becomes divided into small, non-contiguous free blocks, preventing larger allocations even if total free space is sufficient.
ESP-IDF Management Stack size configured per task. FreeRTOS manages task context switching. ESP-IDF provides a heap allocator (heap_caps_... API) for managing memory with different capabilities.

Instruction and Data Caches

Since external SPI flash and PSRAM are significantly slower than internal SRAM, ESP32 SoCs use caches to improve performance:

  • Instruction Cache (ICache): Stores recently executed instructions fetched from external flash. If the CPU needs an instruction already in the cache (a “cache hit”), it’s retrieved much faster.
  • Data Cache (DCache): Stores recently accessed data from external flash or PSRAM.The MMU (Memory Management Unit) works in conjunction with these caches to map the external flash and PSRAM into the CPU’s address space, making them appear as directly accessible memory.

Memory Management Units (MMU)

The MMU is responsible for translating virtual addresses (used by the CPU) to physical addresses (in the actual memory devices). In ESP32, it’s heavily involved in managing the mapping of external SPI flash and PSRAM into the data and instruction address spaces, enabling XIP from flash and access to PSRAM as if it were part of the main memory map.

Practical Examples

While directly manipulating memory addresses is an advanced topic, understanding how to analyze and manage memory usage is practical for all developers.

1. Checking Memory Usage with idf.py size

As introduced in Chapter 5, idf.py size is your primary tool for understanding the static memory footprint of your application after a build.

  1. Open your hello_world project (or any other ESP-IDF project).
  2. Open an ESP-IDF Terminal.
  3. Run idf.py build (if not already built).
  4. Run idf.py size.

The output will look something like this (values will vary):

Bash
Total sizes:
 DRAM .data size:   13908 B
 DRAM .bss  size:   26152 B
Used C/C++ heap:       0 B
Used C/C++ stack:      0 B
 Flash code:   168513 B
 Flash rodata:  50280 B
Total image size:~258853 B (.bin may be padded larger)
  • DRAM .data size: Memory used by initialized global/static variables (copied from Flash to RAM at boot).
  • DRAM .bss size: Memory used by uninitialized global/static variables (zeroed in RAM at boot).
  • Flash code: Size of your executable code (.text) stored in flash.
  • Flash rodata: Size of your read-only data (constants, strings) stored in flash.
  • Total image size: Approximate total flash occupied by your application.

Running idf.py size-components or idf.py size-files gives a more granular breakdown.

2. Illustrative Linker Script Snippets

You generally don’t need to modify linker scripts (.ld files) unless you’re doing very advanced customization. However, looking at them can help understand memory mapping. These are found within ESP-IDF components or can be project-specific.

A simplified conceptual snippet from an ESP32 linker script might look like:

Makefile
/* Simplified Linker Script Snippet - For Illustration Only */
MEMORY
{
  /* All these values assume a 4MB Flash chip */
  iram0_0_seg (RX) :                 org = 0x40080000, len = 0x20000 /* Maximum 128K IRAM, (typically less for APP CPU) */
  dram0_0_seg (RW) :                 org = 0x3FFB0000, len = 0x50000 /* Maximum 320K DRAM */
  /* ... other memory regions ... */
}

SECTIONS
{
  /* Read-only code (instructions) placed in IRAM */
  .iram0.text :
  {
    _iram_text_start = ABSOLUTE(.);
    *(.iram1 .iram0.text .iram0.literal .iram0.data) /* .iram0.data is a misnomer, it's for literals */
    _iram_text_end = ABSOLUTE(.);
  } > iram0_0_seg

  /* Read-write data (initialized) placed in DRAM */
  .dram0.data :
  {
    _data_start = ABSOLUTE(.);
    *(.data)
    *(.data.*)
    *(.dram1 .dram0.data)
    _data_end = ABSOLUTE(.);
  } > dram0_0_seg

  /* Read-write data (uninitialized) placed in DRAM */
  .dram0.bss (NOLOAD) :
  {
    _bss_start = ABSOLUTE(.);
    *(.bss)
    *(.bss.*)
    *(COMMON)
    _bss_end = ABSOLUTE(.);
  } > dram0_0_seg
  /* ... other sections ... */
}
  • MEMORY block: Defines available memory regions, their start addresses (org), and lengths (len).
  • SECTIONS block: Specifies how different input sections (like .text, .data, .bss from your compiled object files) are grouped and placed into the defined memory regions.

3. Using heap_caps for Specific Memory Allocation (Conceptual)

ESP-IDF provides heap_caps.h for allocating memory from regions with specific capabilities (e.g., DMA-capable, executable, PSRAM). This is more advanced but illustrates how different RAM types can be targeted.

C
#include "esp_system.h" // For esp_chip_info_t
#include "esp_heap_caps.h"
#include "esp_log.h"

static const char *TAG = "memory_example";

void memory_allocation_example(void) {
    void *dram_buffer;
    void *psram_buffer = NULL;

    // Allocate 1KB from general internal DRAM
    dram_buffer = heap_caps_malloc(1024, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
    if (dram_buffer) {
        ESP_LOGI(TAG, "Allocated 1KB from internal DRAM at %p", dram_buffer);
        heap_caps_free(dram_buffer);
    } else {
        ESP_LOGE(TAG, "Failed to allocate from internal DRAM");
    }

    // Check if PSRAM is available and try to allocate from it
    esp_chip_info_t chip_info;
    esp_chip_info(&chip_info);
    if (chip_info.features & CHIP_FEATURE_SPIRAM) { // CHIP_FEATURE_SPIRAM indicates PSRAM
        psram_buffer = heap_caps_malloc(4096, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
        if (psram_buffer) {
            ESP_LOGI(TAG, "Allocated 4KB from PSRAM at %p", psram_buffer);
            heap_caps_free(psram_buffer);
        } else {
            ESP_LOGE(TAG, "Failed to allocate from PSRAM, even though available.");
        }
    } else {
        ESP_LOGI(TAG, "PSRAM not available on this chip/module.");
    }
}
  • MALLOC_CAP_INTERNAL: Allocates from internal SRAM.
  • MALLOC_CAP_SPIRAM: Allocates from PSRAM (if available and configured).
  • This example demonstrates conditional allocation based on PSRAM availability. We will cover heap management in more detail in later chapters.

4. Observing Stack Usage (Conceptual)

FreeRTOS provides uxTaskGetStackHighWaterMark() to check how much stack space a task has used. This is invaluable for debugging stack overflows.

C
// Inside a FreeRTOS task:
UBaseType_t uxHighWaterMark;
uxHighWaterMark = uxTaskGetStackHighWaterMark( NULL ); // Pass NULL for current task
ESP_LOGI(TAG, "Stack High Water Mark: %lu bytes free", (unsigned long)uxHighWaterMark * sizeof(StackType_t));

A low high water mark indicates the task is close to running out of stack space. This will be covered in the FreeRTOS chapters.

Variant Notes

Memory architecture is one area where ESP32 variants show significant differences.

Feature ESP32 (Classic) ESP32-S2 ESP32-S3 ESP32-C3 ESP32-C6 ESP32-H2
CPU Core(s) Dual Xtensa LX6 Single Xtensa LX7 Dual Xtensa LX7 Single RISC-V Single RISC-V (HP + LP) Single RISC-V
Internal SRAM 520 KB 320 KB 512 KB (+16KB Cache RAM) 400 KB 512 KB (+16KB Cache RAM) 320 KB
RTC FAST Memory 8 KB 8 KB 8 KB 8 KB 16 KB (SoC) / up to 512KB (Module dependent) 8 KB
RTC SLOW Memory 8 KB 8 KB 8 KB 8 KB 16 KB 8 KB
PSRAM Support Yes (SPI, up to 8MB typical) Yes (SPI/QSPI, up to 128MB theory, module dependent) Yes (SPI/QSPI, up to 128MB theory, module dependent) Generally No (some rare modules might exist) Yes (SPI/QSPI, module dependent) No
ROM Size (Approx.) ~448 KB ~128 KB ~384 KB ~384 KB ~320 KB ~128 KB
Cache RAM (Instruction + Data) Varies (e.g., 32KB ICache, 32KB DCache per core) Varies (e.g., 8KB ICache, 8KB DCache) Varies (e.g., 16KB ICache, 16KB DCache per core) Varies (e.g., 8KB ICache) Varies (e.g., 16KB ICache) Varies (e.g., 4KB ICache)

Note: Exact memory figures, especially for Cache and ROM, can vary slightly based on chip revisions and documentation. Always refer to the official Technical Reference Manual (TRM) and Datasheet for your specific ESP32 variant and module for precise details. “Module dependent” means the SoC supports it, but the actual module may or may not include the PSRAM chip.

  • Internal SRAM: The total amount of on-chip SRAM varies. This SRAM is partitioned into IRAM and DRAM. The exact partitioning can sometimes be influenced by menuconfig settings.
  • PSRAM:
    • ESP32, ESP32-S2, ESP32-S3, ESP32-C6: Can support external PSRAM, greatly expanding available data memory. The specific interface (SPI vs Quad SPI) and maximum capacity can vary. Not all modules for these chips will include PSRAM.
    • ESP32-C3: Most common modules do not have PSRAM support. This is a key differentiator for memory-intensive applications.
    • ESP32-H2: Typically does not support PSRAM.
  • RTC Memory: While all have RTC SLOW memory for ULP and deep sleep data retention, the amount of RTC FAST memory (accessible by main CPUs) can differ. ESP32-S3 and ESP32-C6 often have more versatile RTC memory configurations.
  • Cache: The size and architecture of instruction and data caches differ, affecting performance when running code from flash or accessing PSRAM.
  • Memory Map: While the general concepts are similar, the exact addresses for memory regions and peripherals will differ. Always consult the Technical Reference Manual (TRM) for your specific ESP32 variant.
  • eFuse: The size and layout of eFuse bits can vary.

Impact on Development:

  • For applications needing large amounts of RAM (e.g., for graphics buffers, extensive network buffers, large datasets), variants with PSRAM support (like ESP32, S2, S3, C6) are preferred.
  • The ESP32-C3, being more cost-focused, is suitable for applications with more modest RAM requirements.
  • The amount of IRAM available can influence how much performance-critical code you can run directly from fast RAM.

Common Mistakes & Troubleshooting Tips

  1. Stack Overflows:
    • Symptom: Unexplained crashes, Guru Meditation Errors (especially “LoadProhibited”, “StoreProhibited”), or erratic behavior.
    • Fix: Increase the stack size for the crashing task via menuconfig (for app_main) or in xTaskCreate (for other tasks). Use uxTaskGetStackHighWaterMark() to monitor actual usage and optimize. Reduce local variable sizes or avoid deep recursive calls.
  2. Heap Issues (Exhaustion/Fragmentation):
    • Symptom: malloc (or heap_caps_malloc) returns NULL. Application might crash or behave unpredictably.
    • Fix: Check for memory leaks (forgetting to free allocated memory). Use esp_get_free_heap_size() and esp_get_minimum_free_heap_size() to monitor heap. Consider using PSRAM if available and internal RAM is insufficient. Optimize data structures and allocation patterns to reduce fragmentation.
  3. Incorrect Attribute Usage for IRAM/DRAM:
    • Symptom: Performance issues if critical code isn’t in IRAM, or crashes if code/data that must be in RAM (e.g., during flash operations when cache is off) is in flash.
    • Fix: Use IRAM_ATTR for functions that need to be in IRAM. Use DRAM_ATTR for data that must reside in DRAM. Be cautious, as IRAM is limited.
  4. PSRAM Misconceptions:
    • Symptom: Slower-than-expected performance when using PSRAM heavily, or issues with DMA if PSRAM memory is used directly without proper cache writeback/invalidate.
    • Fix: Understand that PSRAM is generally slower than internal RAM. Profile your application. For DMA with PSRAM, ensure data is cache-aligned and caches are managed correctly if necessary (ESP-IDF often handles this for its drivers).
  5. Forgetting CONFIG_SPIRAM_BOOT_INIT for PSRAM:
    • Symptom: PSRAM appears unavailable or allocations to MALLOC_CAP_SPIRAM fail even if hardware is present.
    • Fix: In menuconfig, ensure Component config --> ESP PSRAM --> Support for external RAM --> Initialize PSRAM during application startup is enabled if you intend to use PSRAM.

Exercises

  1. Memory Usage Analysis with Global Variables:
    1. Start with a clean hello_world project. Build it and run idf.py size. Note the DRAM .data and .bss sizes, and Flash .rodata and .text sizes.
    2. In hello_world_main.c, add the following global variable: static uint32_t large_uninitialized_array[1024]; // 4KB
    3. Rebuild and run idf.py size. How did the DRAM .bss size change? Why?
    4. Now, modify the variable to be initialized: static uint32_t large_initialized_array[1024] = {1, 2, 3}; // Other elements will be 0
    5. Rebuild and run idf.py size. How did DRAM .data and Flash .rodata (or .text, depending on how linker handles it) sizes change? Why?
    6. Finally, change it to a constant: static const uint32_t large_constant_array[1024] = {1, 2, 3};
    7. Rebuild and run idf.py size. How did Flash .rodata and DRAM usage change compared to the previous step?
  2. Exploring sdkconfig for Memory Settings:
    1. Open menuconfig for your hello_world project.
    2. Navigate to Component config --> Heap memory debugging. Enable Enable heap corruption detection (Basic (no poisoning)).
    3. Navigate to Component config --> FreeRTOS --> Kernel. Change the Tick rate (Hz).
    4. Save the configuration and exit menuconfig.
    5. Open the sdkconfig file in your project root. Find the lines corresponding to the settings you just changed (e.g., CONFIG_HEAP_CORRUPTION_DETECTION_BASIC, CONFIG_FREERTOS_HZ).
    6. What are their new values? This exercise helps you connect menuconfig options to their CONFIG_ macro names.

Summary

  • ESP32 SoCs have a diverse memory architecture including ROM (bootloader, core libs), Internal SRAM (IRAM for code, DRAM for data/heap/stack), RTC Memory (for deep sleep and ULP), eFuse (OTP system parameters), External SPI Flash (firmware, NVS), and optional PSRAM (external RAM expansion).
  • Code sections (.text, .rodata) and data sections (.data, .bss) are placed into appropriate memory regions by the linker based on linker scripts.
  • The heap provides dynamic memory allocation, while each FreeRTOS task has its own stack.
  • Caches are used to improve performance when accessing slower external flash and PSRAM.
  • Memory capacities (SRAM, PSRAM support, RTC memory) and specific features vary significantly across ESP32 variants. Always check the TRM and datasheet for your specific chip.
  • idf.py size is an essential tool for analyzing your application’s static memory footprint.
  • Effective memory management is crucial for stability and performance; watch out for stack overflows and heap exhaustion/fragmentation.

Further Reading

Understanding the memory architecture of your ESP32 is a continuous learning process. As you develop more complex applications, this foundational knowledge will become increasingly valuable.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top