'(ESP32 Arduino Wrover-E) How to make the following snippet allocate resources only in External RAM?

The goal is to allocate an array of classes purely in External Ram of the ESP32.

They say that the compiler will allocate big chuncks of space automatically in external ram, but this is not the case. My heap goes lower with this snippet, but the ext ram size remains the same.

#define TEST_CLASS_ARR_SIZE 100
class testClass {
    long testVar;
    public:
    testClass() {
        //Serial.println("testClass constructor");
    }
    ~testClass() {
        //Serial.println("testClass destructor");
    }
    void sayHello(){
        Serial.println("Hello!");
    }
};
testClass* testClassesArray[TEST_CLASS_ARR_SIZE];

void testExtRamAllocation(){
    // print available external and internal ram size
    Serial.printf("Free Heap Size: %d\n", ESP.getFreeHeap());
    Serial.printf("Free ExtRam Size: %d\n", ESP.getFreePsram());
    // allocate testClasses in external ram with extRam allocator and put them in testClassesArray
    for(int i = 0; i < TEST_CLASS_ARR_SIZE; i++){
        testClassesArray[i] = new testClass();
    }

    // I can use any of the calsses like this: testClassesArray[15]->sayHello();

    // print available external and internal ram size
    Serial.printf("Free Heap Size: %d\n", ESP.getFreeHeap());
    Serial.printf("Free ExtRam Size: %d\n", ESP.getFreePsram());
    // delete testClasses in external ram
    for(int i = 0; i < TEST_CLASS_ARR_SIZE; i++){
        delete testClassesArray[i];
    }
    // print available external and internal ram size
    Serial.printf("Free Heap Size: %d\n", ESP.getFreeHeap());
    Serial.printf("Free ExtRam Size: %d\n", ESP.getFreePsram());
}

void setup(){
  Serial.begin(115200);
  testExtRamAllocation();
}

void loop(){
  vTaskDelete(NULL);
}

My setup:

  • ESP32-Wrover-E ( 16mb flash, 8mb psRAM )
  • IDE: VsCode - PlatformIO

PIO configuration:

[env:esp-wrover-kit]
platform = espressif32

board       = esp-wrover-kit
framework   = arduino
board_build.filesystem = littlefs

board_build.f_cpu   = 240000000L
upload_port     = COM8
upload_speed    = 921600
monitor_speed   = 115200
monitor_filters = esp32_exception_decoder

board_upload.flash_size = 16MB
board_build.flash_mode = qio
board_build.partitions = large_spiffs_16MB.csv
board_build.f_flash    = 80000000L

build_flags =   -DBOARD_HAS_PSRAM
                -mfix-esp32-psram-cache-issue
                -mfix-esp32-psram-cache-strategy=memw
                -DCORE_DEBUG_LEVEL=0

What i have tried:

There is a custom vector ps_ram allocator available which looks like this:

#include <Arduino.h>
#include <vector>

template <class T>
struct PSallocator {
  typedef T value_type;
  PSallocator() = default;
  template <class U> constexpr PSallocator(const PSallocator<U>&) noexcept {}
  [[nodiscard]] T* allocate(std::size_t n) {
    if(n > std::size_t(-1) / sizeof(T)) throw std::bad_alloc();
    if(auto p = static_cast<T*>(ps_malloc(n*sizeof(T)))) return p;
    throw std::bad_alloc();
  }
  void deallocate(T* p, std::size_t) noexcept { std::free(p); }
};
template <class T, class U>
bool operator==(const PSallocator<T>&, const PSallocator<U>&) { return true; }
template <class T, class U>
bool operator!=(const PSallocator<T>&, const PSallocator<U>&) { return false; }

std::vector<int, PSallocator<int> > extRam;

With this i tried the following without success:

void testCustomAllocator(){
    // print available external and internal ram size
    Serial.printf("Free Heap Size: %d\n", ESP.getFreeHeap());
    Serial.printf("Free ExtRam Size: %d\n", ESP.getFreePsram());

    extRam.reserve( TEST_CLASS_ARR_SIZE * sizeof(testClass) );
    for (size_t x=0; x<TEST_CLASS_ARR_SIZE; x++) {
        testClass* p = new testClass();
        extRam.push_back( p );
    }
    // print available external and internal ram size
    Serial.printf("Free Heap Size: %d\n", ESP.getFreeHeap());
    Serial.printf("Free ExtRam Size: %d\n", ESP.getFreePsram());

    extRam.clear();
    extRam.shrink_to_fit();

    // print available external and internal ram size
    Serial.printf("Free Heap Size: %d\n", ESP.getFreeHeap());
    Serial.printf("Free ExtRam Size: %d\n", ESP.getFreePsram());
}

I got a compiler error:

src/main.cpp: In function 'void testCustomAllocator()':
src/main.cpp:117:29: error: no matching function for call to 'push_back(testClass*&)'
         extRam.push_back( p );
                             ^
In file included from c:\users\pc\.platformio\packages\toolchain-xtensa-esp32\xtensa-esp32-elf\include\c++\8.4.0\vector:64,
                 from src/main.cpp:2:
c:\users\pc\.platformio\packages\toolchain-xtensa-esp32\xtensa-esp32-elf\include\c++\8.4.0\bits\stl_vector.h:1074:7: note: candidate: 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = int; _Alloc = PSallocator<int>; std::vector<_Tp, _Alloc>::value_type = int]' 
<near match>
       push_back(const value_type& __x)
       ^~~~~~~~~
c:\users\pc\.platformio\packages\toolchain-xtensa-esp32\xtensa-esp32-elf\include\c++\8.4.0\bits\stl_vector.h:1074:7: note:   conversion of argument 1 would be ill-formed:
src/main.cpp:117:27: error: invalid conversion from 'testClass*' to 'std::vector<int, PSallocator<int> >::value_type' {aka 'int'} [-fpermissive]
         extRam.push_back( p );
                           ^
In file included from c:\users\pc\.platformio\packages\toolchain-xtensa-esp32\xtensa-esp32-elf\include\c++\8.4.0\vector:64,
                 from src/main.cpp:2:
c:\users\pc\.platformio\packages\toolchain-xtensa-esp32\xtensa-esp32-elf\include\c++\8.4.0\bits\stl_vector.h:1090:7: note: candidate: 'void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = int; _Alloc = PSallocator<int>; std::vector<_Tp, _Alloc>::value_type = int]' <near match>
       push_back(value_type&& __x)
       ^~~~~~~~~
c:\users\pc\.platformio\packages\toolchain-xtensa-esp32\xtensa-esp32-elf\include\c++\8.4.0\bits\stl_vector.h:1090:7: note:   conversion of argument 1 would be ill-formed:
src/main.cpp:117:27: error: invalid conversion from 'testClass*' to 'std::vector<int, PSallocator<int> >::value_type' {aka 'int'} [-fpermissive]
         extRam.push_back( p );
                           ^
*** [.pio\build\esp-wrover-kit\src\main.cpp.o] Error 1


Solution 1:[1]

I will answer to my own question.

The new arduino framework will put things in external ram if the allocatable memory is more than 4k. If it isn't, it will put things into internal ram, but if the internal ram is not sufficent for the allocation, it will try to allocate from ext ram.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dr.Random