B4R Question rEEPROM RAM usage

BertI

Member
Licensed User
Longtime User
In trying to reduce RAM usage I noticed that even reading a single EEprom byte gobbles up RAM equivalent to the EEPROM size and this doesn't appear to be released afterwards. Is there anything that can be done about this? Example to test this is:
EEPROM RAM usage test:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'Public variables can be accessed from all modules.
    Public Serial1 As Serial
    Public ee As EEPROM
End Sub

Private Sub AppStart
    Serial1.Initialize(115200)
    Log("AppStart")
    
    Log("Available RAM: ",AvailableRAM)
    Log("Read EEPROM size: ", ee.Size)
    Log("Available RAM: ",AvailableRAM)
    Log("Read Byte 0: ",ee.ReadBytes(0,1)(0))
    Log("Available RAM: ",AvailableRAM)
    afterstart
End Sub


Private Sub afterstart
    Log("Available RAM AS: ",AvailableRAM)
End Sub
 

BertI

Member
Licensed User
Longtime User
I suppose I was just contemplating that if I called another sub then there might be a possibility of memory being released in the transition. So I have altered the test code to use CallSubPlus but still appears that RAM space remains 'taken'.
B4X:
#Region Project Attributes
    #AutoFlushLogs: True
    #CheckArrayBounds: True
    #StackBufferSize: 300
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'Public variables can be accessed from all modules.
    Public Serial1 As Serial
    Public ee As EEPROM
    Public wifi As ESP8266WiFi
End Sub

Private Sub AppStart
    Serial1.Initialize(115200)
    Log("AppStart")
    
    Log("Available RAM: ",AvailableRAM)
    Log("Read EEPROM size: ", ee.Size)
    Log("Available RAM: ",AvailableRAM)
    Log("Read Byte 0: ",ee.ReadBytes(0,1)(0))
    Log("Available RAM: ",AvailableRAM)
    CallSubPlus("afterstart",1000,0)
End Sub


Private Sub afterstart(tag As Byte)
    Log("Available RAM AS: ",AvailableRAM)
End Sub

Log output is:
Available RAM: 45776
Read EEPROM size: 2048
Available RAM: 45776
Read Byte 0: 0
Available RAM: 43720
Available RAM AS: 43696
 
Upvote 0

BertI

Member
Licensed User
Longtime User
It looks like it is not something which uses a temporary array on the stack (in so far as I understand these things...). But I did try the following code to see if it made a difference:
B4X:
#Region Project Attributes
    #AutoFlushLogs: True
    #CheckArrayBounds: True
    #StackBufferSize: 300
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'Public variables can be accessed from all modules.
    Public Serial1 As Serial
    Public ee As EEPROM
    Public wifi As ESP8266WiFi
End Sub

Private Sub AppStart
    Serial1.Initialize(115200)
    Log("AppStart")
    Log("Available RAM 1: ",AvailableRAM)
    CallSubPlus("afterstart",500,0)
    CallSubPlus("RAMafterdust",1500,0)
End Sub

Private Sub afterstart(tag As Byte)
    Log("Read EEPROM size: ", ee.Size)
    Log("Available RAM 2: ",AvailableRAM)
    Log("Read Byte 0: ",ee.ReadBytes(0,1)(0))
    Log("Available RAM 3: ",AvailableRAM)
End Sub

private Sub RAMafterdust(tag As Byte)
    Log("Available RAM 4: ",AvailableRAM)
End Sub

The log output was:
Available RAM 1: 49928
Read EEPROM size: 2048
Available RAM 2: 49880
Read Byte 0: 0
Available RAM 3: 47824
Available RAM 4: 47824

So as I say, looks like once its gone its gone (sounds like a promotion from a popular supermarket :) )
 
Upvote 0

candide

Active Member
Licensed User
memory allocation is in eeprom.cpp, at EEPROM::begin :
B4X:
void EEPROMClass::begin(size_t size) {
  if (size <= 0) {
    DEBUGV("EEPROMClass::begin error, size == 0\n");
    return;
  }
  if (size > SPI_FLASH_SEC_SIZE) {
    DEBUGV("EEPROMClass::begin error, %d > %d\n", size, SPI_FLASH_SEC_SIZE);
    size = SPI_FLASH_SEC_SIZE;
  }

  size = (size + 3) & (~3);

  //In case begin() is called a 2nd+ time, don't reallocate if size is the same
  if(_data && size != _size) {
    delete[] _data;
    _data = new uint8_t[size];
  } else if(!_data) {
    _data = new uint8_t[size];
 
Upvote 0

BertI

Member
Licensed User
Longtime User
Unfortunately still not conversant with C, but I do get that memory is allocated for the size that is passed by the rEEPROM call. I had modified the latter to provide 2048 EEprom bytes rather than the default 1024, so I know where that value is coming from, but the problem is that once the shadow RAM is allocated it does not get released. I have some understanding of why shadow RAM is used - because Flash bytes cannot be individually written unless bits within are to be flipped from 1 to 0 - so in general a whole flash page must be erased before even one byte can be written to it (unless you happen to be lucky to find a 'blank' byte at that location). There may be a way to avoid having to shadow the entire allocation at all times but unfortunately a bit beyond me at this time.
 
Upvote 0

BertI

Member
Licensed User
Longtime User
You can easily call it with inline C.
Ok thanks for that insight, so I tried the following modification to previous code:
B4X:
private Sub RAMafterdust(tag As Byte)
    RunNative("Endee",Null)
    Log("Available RAM 4: ",AvailableRAM)
End Sub

#if C
void Endee(B4R::Object* o){
EEPROM.end();
}
#End If
But upon compilation I get:
b4r_main.cpp:12:1: error: 'EEPROM' was not declared in this scope
EEPROM.end();
^

What am I doing wrong?
Also made me wonder, why is this call not already made in the rEEPROM code (presumably would have to be after both read or write operations)? Is it just that it wasn't considered or could it be detrimental in some way? E.g from my poor interpretation of the code I understand that the end sub does a commit as well, on the other hand looks like the commit only does a flash erase if the data in RAM is different to that in the flash. Would that be correct?
 
Upvote 0

BertI

Member
Licensed User
Longtime User
I guess I wasn't so clear on what 'scope' meant in the context of my code, but anyhow with a bit of guesswork, answering my own question on the compilation error part, I changed the inline C to:
B4X:
#if C
#include "EEPROM.h"
void Endee(B4R::Object* o){
EEPROM.end();
}
#End if
This now compiles ok and the logs show the RAM to be released:
Available RAM 2: 49880
.....
Available RAM 4: 49880

So success :). Thanks to all.

Contemplating the second part of the question... I guess being able to release the RAM can be useful if the EEPROM is just used in an area of code which will not be later encountered in the running application. Otherwise it kind of gives you a false sense of RAM security. I.e if you are already low on RAM in some part of the code and then use an EEPROM function then you may run out of space in perhaps a less deterministic manner. So maybe it's less prone to result in unexpected resets just to commit that RAM anyway ... So perhaps back to - maybe there is a more RAM efficient way to handle the pseudo EEPROM in the first place
 
Last edited:
Upvote 0
Top