Possible runtime crash trying to put an object in the SDRAM

I’m trying to figure out how to use the SDRAM to create a large buffer (or to hold a large object instance).

I have a simple program that just writes a counter to the OLED and says “hello world” on the USB console once it connects. This is working fine, until…

At the top of my main cpp file, I try to create an object on the SDRAM like this:

#define MAX_DELAY static_cast<size_t>(48000 * 1.f)
CircularBuffer<float, MAX_DELAY> DSY_SDRAM_BSS AudioBuffer;

Once I add that line, the program does compile without error, and it does flash to the Daisy (and the memory during programing shows the SDRAM is now being allocated 0.26%), but as soon as I reset the Daisy, it does nothing. It does not show up as a device for serial monitoring and the OLED is blank.

If I comment out the above line, it shows the “Hello World” on the serial monitor, and the OLED works again.

CircularBuffer is the name of a class that is included. I am not using the CircularBuffer at all. I’m just trying to instantiate it in the SDRAM. I can only guess that the Daisy is immediately crashing on boot.

Relevant parts of the CircularBuffer class:

template <typename T_Type, size_t BufferSize>
class CircularBuffer{

private:
T_Type Buffer[BufferSize];

I’ve looked at libDaisy: Getting Started - External SDRAM and I also tried to use the PluckEcho.cpp and DelayLine.h files as an example. I’m not sure what’s going wrong.

I’ve done a bit more experimenting.

In the global scope, I made an array instead of trying to create an object.

float DSY_SDRAM_BSS Buffer[44100 * 100] = {0};

This code runs without crashing. It claims to actually be an array of 4,410,000 floats.

size_t BufferSize = (sizeof Buffer / sizeof Buffer[0]);
std::string BufferString = "BufferSize: "+std::to_string(BufferSize);
char*       cstr = &BufferString[0];
hw.PrintLine(cstr);

Output on USB debug console:

BufferSize: 4410000

But it didn’t show any usage of the SDRAM after the build was complete.

Memory region         Used Size  Region Size  %age Used    
         FLASH:           100640 B       128 KB     76.78%
         DTCMRAM:     0 GB       128 KB      0.00%
         SRAM:             65884 B       512 KB     12.57%
         RAM_D2:         16968 B       288 KB      5.75%
         RAM_D3:          0 GB        64 KB      0.00%
         ITCMRAM:       0 GB        64 KB      0.00%
         SDRAM:           0 GB        64 MB      0.00%
         QSPIFLASH:    0 GB         8 MB      0.00%

The previous attempt (with an object) was using SDRAM. Should I be seeing SDRAM usage on the build and program output when using the SDRAM?

I’m not sure I’m using the SDRAM at all though, because if I do this:

float Buffer[44100 * 100] = {0};

instead of this:

float DSY_SDRAM_BSS Buffer[44100 * 100] = {0};

It still does the same thing. And if I make it even bigger:

float Buffer[44100 * 500] = {0};

It claims that it is now an array of 22,050,000 floats. And it still claims that it is using 76.78% of the flash memory when building, and none of the SDRAM.

It doesn’t seem to be allocating any memory at all for this array.

OK, I’m learning some new things.

I tried to force it to use the array by putting this in the main function:

for(auto& x : Buffer){
   x = 0.2;
}

Now I can’t flash it because:

SRAM:    88265884 B       512 KB    16835.38%

Which is what I expected before, but I guess it doesn’t actually reserve any memory unless it sees that you’re trying to use it?

Now, with that for loop in place, if I use:

float DSY_SDRAM_BSS Buffer[44100 * 500] = {0};

Instead of:

float Buffer[44100 * 500] = {0};

I now get:

SDRAM:    88200000 B        64 MB    131.43%

Which shows that it is trying to use the SDRAM now.

I scaled that down to something more reasonable (48k, stereo, 60 seconds):

float DSY_SDRAM_BSS Buffer[48000 * 2 * 60] = {0};

And it seems to be working.

So continuing on, trying to get an object into the SDRAM:

CircularBuffer<float, (48000 * 2 * 60)> DSY_SDRAM_BSS AudioBuffer;

Instead of:

float DSY_SDRAM_BSS Buffer[48000 * 2 * 60] = {0};

We can see the SDRAM is being used:

SDRAM:    23040008 B        64 MB     34.33%

And once again, it compiles and flashes, but nothing runs on reboot, and I can’t connect via USB console.

Something about trying to put an object into SDRAM is causing the Daisy not to boot. Why can I put an array of floats into the SDRAM but not an object?

Yeah so there’s a few things going on here. The main one is that the SDRAM sections can only be bss at the moment, (which means an uninitialized section (technically it should also be zero filled, but that doesn’t actually happen for the SDRAM)). With that in mind, you cannot place an object there or provide an initializer list.

The reason is that the SDRAM can’t be accessed before the hardware’s Init call, or a hardfault will occur on an illegal bus access. So as long as you have a buffer in SDRAM that’s uninitialized, you can access it no problem after the hardware class’s Init call.

If you really want an object there, you can do a little trick:

using Buffer = CircularBuffer<float, (48000 * 2 * 60)>;
uint8_t DSY_SDRAM_BSS object_heap[sizeof(Buffer)];

void main() {
  Buffer* buffer = new((void*) object_heap) Buffer();
}

And then you’d access the buffer object via the buffer pointer.

2 Likes

Thank you.

What is the best practice? Should I make an array of floats on the SDRAM like before:

float DSY_SDRAM_BSS Buffer[48000 * 2 * 60]

And then create an instance of the object on the flash, and then pass the SDRAM “chunk” into the object as a pointer in an init() function called inside main()?

Yeah that would be ideal. Putting an object in SDRAM like I showed would incur a small runtime cost in creation and pointer dereferencing.

I think I understand the concept, but I am stuck in pointer hell trying to figure out where all the *'s and &'s go to make this work:

This is what I am trying:

Global scope declarations:

// Allocate for an array of floats in the SDRAM
float DSY_SDRAM_BSS gSDRAM_AudioBuffer[48000 * 2 * 60];

// Create an object called AudioBuffer in the main flash memory.
// We will pass a pointer to gSDRAM_AudioBuffer into AudioBuffer in the main function.
CircularBuffer<float, (48000 * 2 * 60)> AudioBuffer;

Main function:

patch.Init();

// Pass the address of the SDRAM buffer to the CircularBuffer object's init() function
AudioBuffer.Init(&gSDRAM_AudioBuffer);

Class:

template <typename T_BufferType, unsigned int BufferSize>
class CircularBuffer{

private:

// Buffer is a pointer to an array of n floats. (where n = BufferSize from template).
// The array must be created on the SDRAM in the global scope and passed into init by pointer.
T_BufferType (*Buffer)[BufferSize];

unsigned int WritePointer = 0;


public:

CircularBuffer() {}
~CircularBuffer() {}

// Call Init() with a pointer to the SDRAM chunk that is allocated outside this object.
void Init(T_BufferType* SDRAM_Pointer){
  
   // Save the pointer inside this object so we can access it from other methods.
   Buffer = *SDRAM_Pointer;
  
   // Write zeros to the whole array.
   for(T_BufferType& x : Buffer){
      x = 0.0;
   }
}


// Write the buffer value at the current write index:
void BufferWrite(float NewValue){
   Buffer[WritePointer] = NewValue;
}

}; // end class

Now the compiler is having an issue with the BufferWrite method. It says, “error: incompatible types in assignment of ‘float’ to ‘float [5760000]’ Buffer[WritePointer] = NewValue;”

So it thinks that I’m trying to overwrite the entire array called Buffer with a single float value, rather than write to a single array member value at the index specified by WritePointer. Which means that there’s probably a * in the wrong place.

I believe you need to dereference your array pointer:
(*Buffer)[WritePointer] = NewValue;