Dynamic memory and SDRAM

Only after experiencing random crashes i understood that the 64MB SDRAM isn’t handled by malloc and friends. Instead it is a continuous block starting at the address 0xC0000000. Did anybody figure out a way to tell the system about this memory and have it manage that so it is available to malloc/new?
To the electro smith team: Is there a plan to do that by default?

2 Likes

You’d have to alter the implementation of malloc. If all you want is to allocate fixed amounts of some stuff dynamically and you know the size of that stuff in advance, you can use placement new() - this is a version of operator new() that constructs an object at a specific memory location (assuming that this memory location isn’t used by something else). You use it like this:

auto myObjectPtr = new(someMemoryAddress) MyObject();

Now if you use this, you most likely want to have a statically allocated memory location where you can construct your object with placement new(). You can use std::aligned_storage<MyObject>. Basically, std::aligned_storage<> is a POD type that occupies exactly the space needed for the type given in its template argument.

Sooo, let’s say you want to construct two objects at runtime and they should sit in the external SRAM, then you could do it like this:

// reserves enough memory for 2x MyClass but doesn't actually initialise them. 
// This basically says: Block this piece of memory for an object of type MyClass
// that I'll create there later on.
std::aligned_storage<MyClass> DSY_SDRAM_BSS myMemory1;
std::aligned_storage<MyClass> DSY_SDRAM_BSS myMemory2;

// call the "placement operator new()" to create objects at these memory locations.
auto myObjectPtr1 = new (std::addressof(myMemory1)) MyClass(/* init arguments, if any */);
auto myObjectPtr2 = new (std::addressof(myMemory2)) MyClass(/* init arguments, if any */);

// use the objects

// manually deconstruct them, - this is one of the very rare cases where you have to
// manually call a destructor in C++
myObjectPtr1->~MyClass();
myObjectPtr2->~MyClass();
1 Like

Thanks for the pointers! At the moment i am using that memory for sample buffers only and i’ll just cross my fingers and hope that all the book-keeping and glue fits into the memory on the chip. I’ve implemented a simple MemoryPool class that keeps track of allocations and frees. I suppose i’d be trivial to write an std::allocator that taps into that pool too.
If things become tight i will continue experimenting.
I’m new to embedded programming though working professionally as a developer for audio software. So i think i still need to wrap my head around ‘idiomatic’ usage of my c++ skills in that domain. Not a single daisy example is using dynamic memory on it’s surface.

But i wonder if the daisy libraries couldn’t come with an implementation of malloc and free that just uses the SDRAM. One of my motivations to write my post was to make the situation more discoverable to other people who run into the same issue.

Speaking of which: a #define of the memory address in the libs (maybe in src/dev_sdram.h) would be great so we don’t have to have an arbitrary address in our code.

Keep in mind, in bare metal embedded programming, dynamic memory is used way less than in applications running under an OS. Some would go so far as to say it shouldn’t be used at all in an embedded system.

2 Likes

Yes, I want to second that. If you use malloc/free on an embedded processor, you most likely are doing something wrong. It is very rare thet you actually need it.

Maybe if you let us know your application, we can suggest other ways of handling the memory. In many cases it’s a question of design patterns.

Keep in mind, in bare metal embedded programming, dynamic memory is used way less than in applications running under an OS

That’s what i meant to say in rather a convoluted fashion when talking about ‘idomatic’ programming.

What i want to achieve is to load samples (and objects describing them, e.g. root key, range, etc.) from the SD card into the memory for playback.

What apart from audio drop-outs and very limited memory speaks against using dynamic memory in embedded systems?

In resource-constrained embedded systems, the programmer should know all resource requirements up front. So dynamic allocation shouldn’t be necessary to adapt to user input or environmental variations.

Worst case, your gadget might crash, needing a reboot. Less drastic would be wasted CPU cycles.

But there’s no need to be dogmatic. You might, for instance, use dynamic memory only during startup, and not be doing any malloc/free stuff after initialization. This might be more a notational convenience, completely safe.

Bottom line, pay attention to resource requirements, and you probably don’t really need to use dynamic memory.

Sticking to static allocation allows you to see upfront if the system would be able to work under all circumstances. The linker would throw an error if it ran out of memory. With dynamic allocation, you can’t be sure. There may be a situation where the system runs out of memory - and you don’t notice it because that situation only happens once in 20 years.

Also, there’s memory fragmentation. If you regularly malloc and free differently sized blocks of memory, you may end up in a situation where you need X bytes of memory and the system has Y >> X bytes of memory free, but not in a continuous block. Then your allocation could fail. All of that depends on the way malloc and free handle the allocation and there are various algorithms for that, here’s an example.

As @tele_player stated, you don’t need to be dogmatic about it and there are situations where it may come in handy to use dynamic allocation. But if you regularly malloc and free from various places inluding interrupts and in random orders you are only asking for trouble.

For your use case of loading samples: Why don’t you write a little memory pool / sample data provider for that? You could design it such that your audio code never actually needs to store the pointer to the sample data. Instead you would access the sample data indirectly, e.g. “copy N samples to this buffer” or “call my callback function to which you pass a pointer to my sample”. It may sound convoluted, but this approach allows your “sample data provider” to spread continuous data over discontinuous chunks or move data around, even if it is currently in use. Then you can easily deal with the fragmentation and make sure that you can fully utilize the available memory, even if the user loads / unloads samples regularily.

1 Like

Thank you both for your suggestions and insights. I think i can limit memory allocation to phases of patch loading. So my idea is to have a memory pool that controls the entire chunk of SDRAM and houses sample data and meta data with the design goal of being able to completely drain that pool when another patch is loaded. This way it should be possible to avoid fragmentation and keep tabs on memory usage. I’ll keep your suggestion of not assuming continuous memory i mind during design.

If you’re using C++, you probably could implement such (sub-)patch switching using std::variant and move constructors.