Post Go back to editing

Correct approach for transferring a packed struct via SPI in DMA mode

Thread Summary

The user inquires about the cache management in the 'SPIDMAMode_21569' example for ADSP-2156x EZ-KIT, specifically if the example is correct and how to handle struct transfers. The final answer confirms that buffer start addresses and lengths should be aligned to cache lines to ensure cache coherence. The user also explores using unions or non-cached memory sections for struct transfers, with the support engineer noting that both methods are approximately equivalent in terms of performance and cache management.
AI Generated Content
Category: Datasheet/Specs
Product Number: ADSP-21569

Hello,

since the demo project "SPIDMAMode_21569" contained in "ADSP-2156x_EZ-KIT-Rel1.0.1" package shows us the implementation of a DMA transfer of byte arrays between two SPI ports in an ultra-simple way, wanting to implement the transfer of an entire packed struct I was asking myself the following questions:

1) Is provided example in "SPIDMAMode_21569" correct, from a cache-management point of view?

The only reference I see regarding cache in such example is the TX/RX buffer declaration and PDMA descriptors list:

Apart from the fact that I don't fully understand the need of aligning descriptor lists to a cache line, I see that no one is concerned with invalidating the data cache or flushing it, consequently I was wondering about who's taking care of cache coherence?

2) Assuming "SPIDMAMode_21569" example is correct, how do you expect me to round the size of a struct to fill the entire cache line?

ADI_CACHE_ROUND_UP_SIZE() macro works quite well with arrays, but not with structs; unless I declare a byte array of the size of my packed struct rounding it up to fill the entire cache line, then cast that memory area to the datatype of my structure...... seems a bit convoluted, am I missing something?

Thanks in advance to anyone who wants to contribute.

Edit Notes

fixed
[edited by: SpoonMan999 at 5:39 PM (GMT -4) on 8 Apr 2025]
  • Hi.

    ADI_CACHE_ROUND_UP_SIZE() macro works quite well with arrays, but not with structs; unless I declare a byte array of the size of my packed struct rounding it up to fill the entire cache line, then cast that memory area to the datatype of my structure...... seems a bit convoluted, am I missing something?

    You can create union which combine struct and array.

    Also you can use array for transfer after that you can cast array to your struct.

    You can see it in SHARC\lib\src\drivers\Source\spi\adi_spi_2156x.c

  • 3) I lately realized that I forgot to mention a third option, which is the possibility of declaring the packed struct to be transferred via SPI in a non-cached memory section. In this case, cache coherency problem does not exist by definition, but for frequent data transfers will it be more convenient to manage cache coherence (flushing, invalidating, nothing, depending on previous points answer) or to use the latter approach?

  • Hello Daim and thanks for contributing to question (2).

    Using a union maybe could be a solution, even if the declaration looks more complicated to me than doing a simple typecast like follows.

    Declaration:

    Usage:

    If I didn't misunderstood your words, you said that I can get inspiration about how to do from the code in "adi_spi_2156x.c", could you kindly tell me at which line(s)? Searched on my own, but didn't find anything interesting.

  • Hi.

    I'm used union as below example

    typedef union tMSDT
    {
        MyStructDatatype Reg;
        uint8_t aReg[sizeof(MyStructDatatype)/sizeof(uint8_t)];
    }TMSDT;
    statical TMSDT data;
    void some_func1(uint8_t *pD)
    {
        ...
        ...
    }
    void some_func2(void)
    {
        ...
        some_func2(&data.aReg[0]);
        ...
    }

    You can see adi_spi_Open

    ADI_SPI_RESULT  adi_spi_Open(... void *pDevMemory, ...)
    {
        ...
        /* Pointer to track the memory available for allocation */
        uint8_t  *pAvailableMem = (uint8_t *)pDevMemory;
        ...
        /* Memory required for Tx channel */
        pDevice->pTxChannel  =   (ADI_SPI_CHANNEL *)pAvailableMem;
        ...
    }

    Best regards.

  • I'm also don't use cached memory sections. But I try to align variables to get maximum performance DMA channels. 

  • Hi Daim, are you using unions EXACTLY like you shown above, so, are you using unions BUT not rounding up size?

    If that's the case, what's the point of using unions? Thinking

  • In your code posted above, you're not rounding up nor aligning variables.... forgive me but it's difficult to follow your answers.

  • Would you be so kind to give me your point of view on this topic too? Thanks in advance for your kindful support. Bow tone1

  • Hi,

    Regarding "Is provided example in "SPIDMAMode_21569" correct, from a cache-management point of view?"

    >> Aligning buffer memory to cache line boundaries ensures that cache maintenance operations apply to the buffer memory alone, and not operate on unrelated data that may inadvertently share memory space on that cache line.
    This may not be an issue for most applications, but it can cause problems if adjoining, misaligned memory is also being accessed or written off-core, possibly by DMA or by another processor core.

    Cache is handled in "adi_pdma_Configuration" API were cache is invalidated to avoid conflicts. You can verify this configuration in both "adi_spi_DMAWrite" and "adi_spi_DMARead" APIs

    Regards
    Nandini C

  • Perfect  thank you, this briefly answers "YES" to my question (1).

    And what about question (3) ?

    From what you correctly pointed out, since flush operations are performed by PDMA APIs called by adi_spi_DMARead() and adi_spi_DMAWrite() functions, they will always be called regardless of whether the memory used for data exchange via SPI is cached or not.

    Consequently, there should be no benefit to using non-cached memory.