This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TMS320F28379D: Memory Fence/Memory Block to Enforce Expression Sequencing

Part Number: TMS320F28379D


Tool/software:

I am attempting to create a memory fence to ensure certain parameters are written in a specific sequence, before or after a specific point in the code.

My current solution is an asm("nop").  I also found a function call with #pragma FUNC_CANNOT_INLINE to act as a barrier.  I wanted to check if I can rely on these solutions, if there are better solutions, and understand if the compiler may at some point in the future bypass my solution and reorder my code.

I've seen several iterations of this question but have not really seen an ideal resolution.

I am writing a block of parameters and at the end of it, setting a flag.  The interrupt checks for this flag, and if set, processes the parameters and clears the flag.  The flag must be set after all the other parameters have been written.  The issue is that since all the parameters, as well as the flag, look identical to the compiler, the compiler is rearranging the writes to be most efficient, moving the flag set to somewhere in the middle of writing the block of parameters.

My original implementation where I found the issue

// Original Implementation - issue
// non-interrupt
funct_config_params(new_param1, new_param2)
{
stored_param1 = new_param1;
stored_param2 = new_param2;

ready_to_load = true;
}


funct_interrupt()
{
if(ready_to_load)
    {
    runtime_param1 = stored param1;
    runtime_param2 = stored param2;
    ready_to_load = false;
    }
}

I found that the compiler may reorder the writes and end up with assembly equivalent to this reordering

// Original - Assembly equivalent
// non-interrupt
funct_config_params(new_param1, new_param2)
{
stored_param2 = new_param2;
ready_to_load = true; // OUT OF ORDER - MUST BE LAST
stored_param1 = new_param1;
}

Which could result in the interrupt loading the previous stored value for one parameter and the new value for the other parameter.

// Current Implementation
// non-interrupt
funct_config_params(new_param1, new_param2)
{
stored_param1 = new_param1;
stored_param2 = new_param2;

asm("nop"); // seems to act as a memory fence

ready_to_load = true;
}

The asm("nop") currently forces the ready_to_load flag to be set at the very end as required in the assembly.

// Alertnate Solution
#pragma FUNC_CANNOT_INLINE
set_ready_to_load()
{
ready_to_load = true;
}

// non-interrupt
funct_config_params(new_param1, new_param2)
{
stored_param1 = new_param1;
stored_param2 = new_param2;

set_ready_to_load();
}

This function call with #pragma FUNC_CANNOT_INLINE also resolves the issue by forcing a function call.  The function call seems to always be placed at the end as needed.

While researching this issue, I found other proposed memory fences that did not seem to work:

asm(""); - This assembly block doing nothing seems to get optimized out immediately, then without the assembly acting as a block, the compiler rearranges the statements.

asm volatile(""); - same result as asm("");

asm volatile("" ::: "memory"); - this seems to be the recommended implementation of a memory fence with a gcc compiler.  The syntax seems to be sufficiently different so that it doesn't build and I haven't found an equivalent.

So, to restate the initial questions, 1) are either of the memory barriers reliable and will not be defeated by future compiler changes, and 2) are there better/more robust memory barriers available?