Synchronization requirements for DMA

Viewed 85

Let me tell you a story:

I have two buffers setup, to do what is called ping ponging. I have a DMA system pointing to each of the buffers.

The system is setup such that the DMA is writing data to one buffer, while an interrupt processes the data in the other buffer.

That would look something like this:

#include <atomic>
#include <array>
#include <algorithm>

using buf_t = std::array<std::uint32_t, 32>;

std::array<buf_t,2> ping_pong;

/*
 * In this example I am just going to push the data to a "peripheral"
 */
#define PORT (*(volatile std::uint32_t*)0x1234)


void handle_data(buf_t const& data)
{
    std::for_each(std::begin(data),std::end(data), [](auto x){
        PORT = x;
    });
}

void isr1(void)
{
    std::atomic_thread_fence(std::memory_order::memory_order_acquire);
    handle_data(ping_pong[0]);
}
void isr2(void)
{
    std::atomic_thread_fence(std::memory_order::memory_order_acquire);
    handle_data(ping_pong[1]);
}

Now my questions are this:

Is this defined behavior? What is preventing the compiler from getting rid of any access to ping_pong, because it cannot see the stuff that sets it?

At least with the current gcc compiler it seems to work: godbolt

1 Answers

I don't have any hard answers on hand, but from a glance, I would assume that because ping_pong is global, it has external linkage and therefore the compiler cannot optimize it out in that translation unit.

The compiler doesn't know where it'll be written from in that TU, but, when finally linking all TU's together (for applications) I would assume that object might be discarded if still not used. If this code is compiled into a library, the compiler will leave this global object intact as it cannot know if external code will use it.

==

I hacked your code to create ping_pong in main, and invoke: isrX(ping_pong[y]), which on clang, caused most of the code including ping_pong to be optimized out on clang. This seems to suggest external linkage is holding keeping ping_pong, and not that the port is volatile / writing to the port FROM data originating from the ping_pong array.

On GCC, as you have noted, the code is kept intact, which may hint that this is undefined behavior. There exists another possibility that for GCC, the compiler may detect that the modifying any source variables which result in updating the volatile PORT, will NOT cull those source variables.

Related