Page 1 of 1

Can't Allocate in Heap using MALLOC_CAP_RTCRAM

Posted: Sun Mar 31, 2024 1:15 pm
by gfvalvo
Hello.
I was looking to dynamically allocate a small amount of heap memory in RTC RAM using:

Code: Select all

TheStruct *ptr = reinterpret_cast<TheStruct*>(heap_caps_malloc(sizeof(TheStruct), MALLOC_CAP_RTCRAM  | MALLOC_CAP_8BIT));
However, it's returning the nullptr. Additionally, this function is returning 0:

Code: Select all

Serial.println(heap_caps_get_free_size(MALLOC_CAP_RTCRAM));
I'm testing this on an Adafruit ESP32 Feather V2.

So is the problem that the ESP32 Arduino Core (v2.0.14) does not allocate any available heap memory in RTC RAM by default? Can that be changed?

Thanks.

Re: Can't Allocate in Heap using MALLOC_CAP_RTCRAM

Posted: Sun Mar 31, 2024 4:39 pm
by lbernstone
I'm pretty sure this would not be possible in arduino, since any extra RTC_FAST memory is going to be mapped into the general heap. It might be possible to do in an IDF project where you separate the RTC memory, but I am doubtful that the ULP would be able to pick up a dynamic resizing by the cpu.

Re: Can't Allocate in Heap using MALLOC_CAP_RTCRAM

Posted: Mon Apr 01, 2024 11:59 pm
by gfvalvo
Thanks for the reply, @lbernstone.
Following the link you provided and then digging in the core's source code, I found that the macro CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP is not defined. So, RTC FAST MEM is not available for use as heap when running the Arduino Core.

I assume it must be possible using the IDF. Otherwise, neither the above macro nor the MALLOC_CAP_RTCRAM option for heap_caps_malloc() would have ever been mentioned.

Re: Can't Allocate in Heap using MALLOC_CAP_RTCRAM

Posted: Tue Apr 02, 2024 12:57 am
by boarchuz
I think there is an API to register memory blocks with the heap allocator, but I can't help being curious what your use case is for dynamically grabbing a chunk of RTC memory? What do you gain vs static?

Re: Can't Allocate in Heap using MALLOC_CAP_RTCRAM

Posted: Tue Apr 02, 2024 2:45 am
by gfvalvo
boarchuz wrote:
Tue Apr 02, 2024 12:57 am
but I can't help being curious what your use case is for dynamically grabbing a chunk of RTC memory? What do you gain vs static?
If a global-scope instance of a class is statically allocated in RTC memory, the constructor for that class will run after the ESP wakes up from deep sleep. That may not be the desired behavior. Perhaps you only want the constructor to run once at power-up but not after deep sleep.

If instead, you could create that object dynamically in RTC memory only when esp_reset_reason() indicates Power On, then you can avoid having the constructor run when it's a deep sleep reset.

This coded accomplishes the same thing, but in a sneaky way:

Code: Select all

#include "Arduino.h"

struct theStruct {
  uint8_t data;
  static uint32_t constructorCount;

  theStruct(uint8_t init) : data(init) {
    constructorCount++;
  }
};

RTC_DATA_ATTR uint32_t theStruct::constructorCount = 0;
RTC_DATA_ATTR theStruct *objPtr = nullptr;
RTC_DATA_ATTR uint8_t structMemory[sizeof(theStruct)];
RTC_DATA_ATTR uint32_t bootCount;

void setup() {
  Serial.begin(115200);
  delay(2000);
  esp_reset_reason_t resetReason = esp_reset_reason();
  if (resetReason != ESP_RST_DEEPSLEEP) {
    Serial.println("Resetting bootCount");
    bootCount = 0;
    if (objPtr != nullptr) {
      delete objPtr;
    }
    objPtr = new (reinterpret_cast<theStruct*>(structMemory)) theStruct(2);
  } else {
    objPtr->data++;
    bootCount++;
  }

  Serial.printf("bootCount = %lu\n", bootCount);
  Serial.printf("constructorCount = %lu\n", theStruct::constructorCount);
  Serial.printf("data = %hhu\n\n", objPtr->data);
  Serial.flush();

  esp_sleep_enable_timer_wakeup(5 * 1000000ULL);
  esp_deep_sleep_start();
}

void loop() {
}