Post Go back to editing

Buffers and memory allocation on sam board

Category: Software
Product Number: ADZS-SC589-MINI
Software Version: 3.0.2

Hello,

I have a few questions regarding memory allocation and buffers. Bear in mind I'm new to this processor and to DSP development in general. It is my understanding that if I want to access the external DDR ram on the sam board, then should use the #pragma directive. For example if I want to store a digital delay buffer in ddr ram I would write:

#pragma section("seg_sdram")
float digital_delay_buffer[DIGITAL_DELAY_BUF_SIZE];
#pragma section()
Questions:
- If I want to store in L1 or L2, what is the directive for those? Is there a file... maybe the .ldf where I can find this information? Where can I find the .ldf file and any relevant file I need.
- If I set the buffer size for a delay for example to 2^18 (is it recommended to have buffers of base-2 size?), Max delay time = BUF_SIZE / Sampling rate(48k) = 5.46 seconds, memory usage = 2^18 * 4(because float is 4 bytes per) = 1,048,576 bytes ~1MB. Because of all the space this takes up, it is recommended to use the external ram to store it. But if I have other effects in series with this that don't require a large buffer, should I be using a separate buffer? If so, how can I cascade the audio signal so that it is processed by each effect it goes through.
- Is there any documentation regarding memory allocation on the SHARC processors or even for the sam board specifically?
  • =

    Hi TeylorW,

    Thanks for reaching out! Please find my responses to your questions below:

    1. Yes, you can use the #pragma section directive to place variables in specific memory regions, such as external DDR RAM, as you have shown. Similarly, you can use the same directive to store data in L1 or L2 memory by specifying the appropriate section names, such as "seg_l1_block0". These section names are defined in the linker description file (.ldf), which controls how memory is allocated in the project. You can refer to the default .ldf for the processor, which can be found in the SHARC/ldf directory of the CCES install.

    2. Since your uses ~1MB, storing it in external SDRAM is recommended. For smaller effects in series, you should use separate, smaller buffers — ideally in L1 or L2 memory. To cascade effects, simply pass the output of one effect as the input to the next in your processing loop.

    3. To verify memory placement, you can generate a map file for your project by going to: Project Properties > C/C++ Build > Settings > CrossCore Baremetal C++ Linker > General and then enabling the "Generate symbol map" option.  This will produce a detailed memory map showing how your variables are allocated across L1, L2, and external memory. For more detailed information, the Linker and Utilities Manual (cces-LinkerUtilities-manual.pdf) from Analog Devices is a great resource on memory sections and the use of #pragma.

    Best, 

    Aradhita

  • I am sharing a document that provides additional details regarding the LDF modification for your reference. Hope it helps with your query.

    PDF

  • Thank you very much, this clears up a lot.

    Some other questions regarding the signal chain popped up as I was reading your response.

    1. For smaller effects in series, is it necessary to have separate smaller buffers within each effect module if I can just read the input and send out an output? Do I need a buffer to store temporary data if no long history is required? I guess what I'm getting at is can these smaller effects simply apply their processing on a buffer of the auido_block_size that is getting passed into them?


    2. Processing audio in blocks (using the default block size of 32 stored in "AUDIO_BLOCK_SIZE") should I have the for loop (int i=o;i<AUDIO_BLOCK_SIZE;i++) inside of callback_audio_processing so that when each effect processing function is called, it operates on one index of the block? Or should I have the for loop inside each effect processing function as I have in this example:

    void tremolo_process(Tremolo *tr, float *input, float *output, uint32_t audio_block_size) {
        for (uint32_t i = 0; i < audio_block_size; i++) {
            output[i] = input[i] * (1.0f + lfo_process(&tr->lfo)); // y[n]=x[n]*m[n]
        }
    }

    Maybe just some more general info on the audio blocks would help out.

    3.For cascading, the simplest solution that I can think of is that I should cascade the signal chain like this (assuming I should loop the block size within each function):
    float temp_buf_A[AUDIO_BLOCK_SIZE];
    float temp_buf_B[AUDIO_BLOCK_SIZE];
    // Process effect 1
    tremolo_process(&tremolo, temp_buf_A, temp_buf_B, AUDIO_BLOCK_SIZE);
    // Process effect 2
    overdrive_process(&overdrive, temp_buf_B, temp_buf_A, AUDIO_BLOCK_SIZE);
    // Process effect 3 (delay writes to its own internal SDRAM buffer)
    digital_delay_process(&delay, temp_buf_A, temp_buf_B, AUDIO_BLOCK_SIZE);
    ... and so on
    However, I notice it is done differently in the framework. In the processaudio_callback function, audio_effects_process_audio_core1 is called which applies a switch routine. How are these effects chosen? How do they cascade off of one another?

    I realize that is a heap of questions. I really appreciate your time and energy in answering them.

    Much appreciated,
    Teylor
  • Hi Teylor, 

    Answering your questions below:

    1. Yes, smaller effects can be processed directly on the passed-in audio_block_size buffer. No extra internal buffer is needed unless history is required. It's generally not necessary to allocate separate internal buffers beyond the input/output buffer passed to the module.

     2. If only a single effect is needed, a dedicated processing function can be created and added as a case in the audio_effects_process_audio_core1/core2 function. This function would operate on the entire audio buffer, keeping the structure simple and centralized. However, for multiple effects in sequence, it's more efficient to perform all processing within a single for loop inside callback_audio_processing. This avoids repeated buffer copying and function calls, reducing overhead and improving performance, especially in real-time audio systems.

     3. As for cascading effects: yes, effects can be chained together as you described. In the framework, the audio_effects_process_audio_core1 function uses a switch-case routine to select and apply different audio effects. This design is specifically intended for use with the Audio Fin board push buttons, where each button press corresponds to a different effect being applied. The switch-case structure makes it easy to map button inputs to specific processing routines. For more details, the official documentation provides a helpful overview: Introduction to the "Audio Elements" and "Audio Effects" [Analog Devices Wiki]

    Best,

    Aradhita