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.

MSP432P401R: Critical section, interrupt safe

Part Number: MSP432P401R

Hi everybody.

I'm migrating from MSP430 to MSP432.

In MSP430 is necessary to add a NOP  instruction before or/after after any instruction setting or clearing interrupt enable or interrupt flag bit (section interrupt acceptance, page 60 SLAU208Q).

I look for < similar section in MSP432P4xx SimpleLink Microcontrollers Technical Reference Manual but found nothing related.

My question is: shouldI insert NOP in MSP432 like in MSP430? 

For example, I want to create a critical section for DIO P3.5 and this is may proposal. Is it necessary to insert any NOP?

uint8_t check_and_clear_P35_flag (void) 
{
    uint8_t interrupt_state;
    uint8_t copy_flag;

    interrupt_state = (P3->IE & BIT5);
    P3->IE &= ~BIT5;
    copy_flag = P35_flag;
    P35_flag = 0;
    P3->IE |= interrupt_state;
    return (copy_flag);
}

Thanks for your help.

Toni

  • This isn't quite like the MSP430 GIE. As the CPU sees it you're doing a memory write (type "Deviice") which won't be ordered with respect to SRAM. (type "Normal") [Ref TRM (SLAU356I) Table 1-5]. I suspect you need a __DSB() here.

    What you probably really want in this sample is LDREX/STREX, which are provided as intrinsics, though I haven't tried using them. [Ref Arm CC UG (SPNU151V) Table 5-3][See also ArmV7-M Architecture (DDI0403E.d) Sec A3.4]

  • Thanks Bruce for your comment. I have to explore it. Any way, I added some information to make my question more specific.

  • More accurate, I'm talking about critical section to avoid race condition when working with interrupts. That is, the interrupt handler set the variable P35_flag and the main loop calls check_and_clear_P35_flag () to react to the interrupt.

    Commonly the possible race condition is avoided disabling the interrupt.

    However, I'm not sure if I have to insert a NOP between P3->IE &= ~BIT5; and  copy_flag = P35_flag; as is told for MPS430.

    Thanks again,

    Toni

  • I don't think a NOP will help you. What you need to do is make sure the GPIO unit sees your IE update before you reference the (SRAM)  value of P35_flag. __DSB() should accomplish this.

  • Thanks Bruce, I'm learning a lot.

    I found two posts that suggest barrier instruction may be not useful. Also, in ARM Cortex-M Programming Guide to
    Memory Barrier Instructions,  Application Note 321 (https://developer.arm.com/documentation/dai0321/a/), in section "4.9 Disabling interrupts at peripherals" suggest to use a loop to check the disable of interrupt and clear the NVIC pending status. This assure that no interrupt is serviced after disabling the device interrupt

    Since my objective if to disable the interrupt only between the read and write of  P35_flag, I inserted a loop and I think this code will work:

    uint8_t check_and_clear_P35_flag (void) 
    {
        uint8_t interrupt_state;
        uint8_t copy_flag;

        interrupt_state = (P3->IE & BIT5);
        P3->IE &= ~BIT5;    
        while (P3->IE & BIT5){}
        copy_flag = P35_flag;
        P35_flag = 0;
        P3->IE |= interrupt_state;
        return (copy_flag);
    }

    I will greatly appreciate your comments.

    Thanks again,

    Toni

    P.D. The two posts related to my question.

    https://developer.arm.com/documentation/ka003795/latest

    https://community.arm.com/developer/ip-products/processors/f/cortex-m-forum/10389/dmb-dsb-isb-on-cortex-m3-m4-m7-single-core-parts

  • I'm pretty sure this would accomplish what you want, though I haven't found mention of an external write buffer in the MSP432.

    Unsolicited(1): Another possibility would be to disable all the P3 pin interrupts (NVIC_DisableIRQ(PORT3_IRQn)). This is a slightly heavier hammer, but the write to ICER (Strongly Ordered) would bypass the write buffer.

    Unsolicited(2): As I understand it the sequence you posted is intended as a sample, but for this particular case I'm not sure you need to disable the interrupt at all, rather rely on storage ordering for a single address. Extrapolating: this case is (a) ISR only writes to P35_flag and only writes the value 1 (b) main only writes the value 0 -- a restricted but very common idiom. Using something like: "copy_flag = P35_flag; if (copy_flag) P35_flag = 0;" would be interrupt-safe since the fetch would either occur before or after the ISR wrote it, but would never clear 1->0 without fetching a 1 first. 

    But this is a restricted case -- if P35_flag were actually e.g. a counter (ISR both reads and writes) it wouldn't work.

  • Thanks Bruce.

    You are right about the ISR, it only sets the P35_flag to 1. This is a common structure to move code out of the ISR, but it includes a read-modify-operation that can introduce a race condition.

    After reading in detail the available documentation, mainly ARM Cortex-M Programming Guide to
    Memory Barrier Instructions,  Application Note 321, I decided to disable the general interrupt (setting PRIMASK). It is the only way I found to be sure the critical section will run without be interrupted.

    Here is the final code:

    uint8_t check_and_clear_P35_flag (void) 
        uint8_t int_state;
        uint8_t copy_flag;


        int_state = __get_PRIMASK();
        __disable_irq();
        copy_flag = P35_flag;
        P35_flag = 0;
        if (!int_state)  {
            __enable_irq();
          }
        return (copy_flag);
    }

    Thanks again Bruce, your comments lead me to the right answer.

    Toni.