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.

Concerto IPC

Two questions about IPC and possible race conditions:

1) Do we need any Data memory barriers at all in order to make sure that all writes are visible to the other core when setting an IPC flag. For example, consider the code:

HWREG( MTOCIPC_BASE + IPC_O_MTOCIPCCOM )  = 0xAA;  // Set a command "0xAA" in the "M3 To C28 IPC Command Register"

HWREG( MTOCIPC_BASE + IPC_O_MTOCIPCSET ) |= IPC_FLAG1;  // Notify C28x about the new command by setting dedicated flag

Do I need to place  __asm(" dmb\n"); in between these two code lines to ensure 0xAA is visible before the flag is set?

 

2) My second question is wheter setting and clearing of IPC flags is guaranteed to be atomic and interrupt safe on both cores. For example, if an ARM cortex interrupt/process is in the process of setting one flag and then preempted by a higher priority ARM cortex interrupt which sets another IPC flag, can this produce unexpected results?

 

  • Hi Christian!

    Did you go carefully through Chapter 1.12.5 of TRM already?

    Regards,

    Igor

  • Hi Igor,

    Yes I have looked carefully at Chapter 1.12.5, and the potential need for a data memory barrier is not mentioned there. I therefore assume the answer to my first question is no, however it would be nice if someone could confirm this?

    Since the the IPC flag "set-registers" always reads back as zero, I also think the answer to my second question is no (i.e., no unexpected results can be produced as a result of pre-emptive interrupts owning separate flags). Again it would be nice if someone could add some details here.

    Best regards,

    Christian 

  • Christian,

    1. The data is "visible" as soon as you write it. But the C28 doesn't know the M3 wrote 0xAA until you tell it so.

    2. That depends on the priority of the interrupt that is disturbing the current IPC flag. 

    Thanks

    Noah

  • Thanks Noah,

    2. I am assuming an interrupt having strictly higher priority so that a preemption can actually happen. As I understand you, it seems that the setting/clearing of any IPC flag must be done with higher priority interrupts (temporarily) turned off. Is that correct?

    Best regards,

    Christian

  • Christian,

    That is correct. You can also enable interrupt nesting if the order of actions is not important.

    Thanks

    Noah

  • Thanks again Noah,

    I am a bit surprised (but was anxious) that the following code-line does not act atomic (allthough it always reads back as zero):

    HWREG( MTOCIPC_BASE + IPC_O_MTOCIPCSET ) |= IPC_FLAG1

    And that unexpected results hence can be produced if this code line is preempted by an interrupted involving, for example, the following line:

    HWREG( MTOCIPC_BASE + IPC_O_MTOCIPCSET ) |= IPC_FLAG32

    Thank you for confirming this for me. I will now have to protect such code lines by temporarily turning off interrupts.

     

    Could you also please explain what an unexpected result might look like? (For example, is it the case that the code that gets interrupted by a strictly higher priority interrupt might not get its flag set, if it is preempted whilst writing to the shared set-register.)

    Christian

  • Hi all,

    I feel my orginal question 2) tothis post has not fully been answered yet. I mean the 32 IPC flags are physically one register, that is, one 32-bit word. If it was a normal memory word, then one would have to protect write accesses between different threads to avoid race condition and ensure atomicity, even if, say, each flag had been dedicated to only one thread. My question is simply, if protection (e.g., turning off interrupts) is still needed considering that the IPC_O_MTOCIPCSET  is a special hardware register that always reads back as zero. 

    Best regards,

    Christian

  • Christian,

    If multiple code sections (threads) are going to access this register, and if they have no means of communicating as to whether they are about to access this register, it is needed  to disable interrupts before accessing the register.

    Thanks

    Noah

  • Thank you Noah,

    I was worried we were misunderstanding each other.

    The only means of communication between the different interrupts/threads I had in mind was that each thread/interrupt has ownership of one flag/bit in the register and never attempts to set a flag belonging to another thread/interrupt.

    I haven't seen any race conditions with my current project, but I will now go ahead and temporarily mask off interrupts in the relevant sections.

    Best regards,

    Christian

  • I realise I am late to this thread (by 3 years), but here goes.

    Addressing question 1:
    To this particular question, the answer is no. There is no need to include a DMB instruction between the writes to the IPC registrs. This is because the Cortex M3 does not reorder memory accesses and all external memory accesses operate over the same bus, so are properly ordered. That said, the register pointers should be declared volatile so the compiler doesn't reorder the memory accesses.

    As an aside, if you wanted your ARM code to be portable to the ARMv7-A or ARMv7-R profiles you would need to include the barrier instructions. This is to do with the ARMv7 architecture allowing reordering of memory accesses at the hardware level, e.g. the code can look correctly ordered at the machine code level yet the processor could reorder the memory access opcodes within the pipeline. The ARMv7-M (M3) and ARMv7E-M (M4/M7) profiles do not reorder memory accesses, so in many situations memory barriers are not required. That said, there are situations where memory barriers are required on the M3. See infocenter.arm.com/.../index.html, specifically infocenter.arm.com/.../index.jsp for more information.

    Addressing question 2:
    The IPCSET, IPCCLR, and IPCACK registers are multi-master write-only registers:
    - reads always return zeros for all bits
    - writes only take effect if the bit is being written as 1, and writing a 0 to a bit has no effect, e.g. writing 0x00000001 only writes a 1 to bit 0, and doesn't write anything to bits 1-31.
    Due to the above, there is no need to perform a read-modify-write with a thread lock (i.e. interrupts turned off) since the reads always return zeros and the writes are atomic.

    As an aside, writing a 1 to a bit to IPCSET when that bit is set in IPCFLG will leave the bit in IPCFLG unchanged and will not result in an interrupt if the bit is 0,1,2 or 3. Likewise for IPCCLR and a clear bit in IPCFLG. To force a bit change / interrupt, the bit must be cleared by writing to it in IPCCLR and then setting it by writing to it in IPCSET.

    Hope this is of use,

    Iain
  • Thank you Iain,

    This made a lot of sense.

    However, since your reply to question 2 disagrees with that made by Noah and TI above, it would be nice if someone from TI please could confirm this.

    It would enable me to get rid of code like the following which I still have running here:
    const unsigned long priorityMask = enterCriticalRegion();
    // Set the IPC flag
    HWREG(MTOCIPC_BASE + IPC_O_MTOCIPCSET) |= m_ipcFlag;
    leaveCriticalRegion( priorityMask );


    Best regards,
    Christian
  • Christian,

    Iain is correct. The IPCSET registers are write-only, so there's no need to disable interrupts to ensure the integrity of the register.

    I would recommend not using |= for the write. Just use a plain assignment operator:

    HWREG(MTOCIPC_BASE + IPC_O_MTOCIPCSET) = m_ipcFlag;

    This will save you a few cycles without changing the basic functionality.