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: __enable_interrupts vs. __restore_interrupts

Part Number: TMS320F28379D

Tool/software:

Hi

My question

Please see the chapter Background below for extended context.

There are three intrinsics for handling global  en-/dis-able of interrupts

  1. __disable_interrupts
  2. __enable_interrupts
  3. __restore_interrupts

#3. sounds like the obvious candidate when you want to restore the interrupt after having disabled them.

But sifting through the docs and the forum, it appears that 90% uses #1. and #2., only calling #2. if bit 0 (INTM) in the returned value from #1. was 1.

Also driverlib only provides functions that uses #1. and #2. 

I wondered why that was the case, as the #3. (restore( seams to much more simple and straightforward to use.

Looking at the assembly instructions from those intrinsics  as documented in SPRU514Y, I see that #3. (restore) restores the entire St1 register, as opposed to #1 and #2 that calls SETC and CLRC to only affect DNGM and INTM.

So my question is:

Is there a reason why most sources uses the more cumbersome __disable_interrupts / __enable_interrupts.?

Is there an unexpected side-effect of using __restore_interrupts and its complete retore of ST1?

Background

To protect a critical part of the code from interrupts either because of timing or access to shared objects, i have used this construct.

bool irqWasEnabled = ! Interrupt_disableGlobal();
// Critical
// code
// lines
// ...
if( irqWasEnabled )
{
    Interrupt_enableGlobal();
}
// ...

The last part can be refactored into a one-liner

if( irqWasEnabled ) Interrupt_enableGlobal(); 

but that is not allowed in many coding-standards.

So i looked at other constructs the obvious solution was to make a pair of functions So I get

NoInterrupt_start();
// Critical
// code
// lines
// ...
NoInterrupt_end();
// ...

Those functions then use a file-static variable to carry the state, from _start to _end. Thus I had to look into if there could be a race condition around that variable if two locations in the code would call those functions, one being in interrupt context.

For reference the functions is implemented as

typedef unsigned int irqStatus_t;

static irqStatus_t irqStatus = False;

static void NoInterrupt_start( void )
{
    IrqWasEnabled = ! Interrupt_disableGlobal();
}

static void NoInterrupt_end( void )
{
    if( IrqWasEnabled )
    {
        Interrupt_enableGlobal();
    }
}

But during this search I found this intrinsic

 void __restore_interrupts( irqStatus_t val );

This looks like a way more simple solution, as I can then just use

irqStatus_t irqState = __disable_interrupts();
// Critical
// code
// lines
// ...
__restore_interrupts( irqState );
// ...

But as stated in the beginning of this post, I am not sure if the __restore_interrupts might have other side effects.

  • Hi Martin,

    I am looking into your question and will have a response back in the next 2-3 days.

    Best Regards,

    Delaney

  • Hi Martin,

    Apologies for the delay. The __restore_interrupts() function restores the entire ST1 register rather than just the INTM and DBGM bits in the register. The intention of the driverlib functions calling __enable_interrupts() / __disable_interrupts() is just to enable/disable interrupts and not to write any of the other status fields in ST1. 

    The risks of using __restore_interrupts() would be:

    1. You may overwrite unrelated status flags.
      Suppose the CPU modifies a status bit (like SXM or OVC) between disabling and restoring interrupts.
      When you later call __restore_interrupts(), those intermediate updates get clobbered — you’re effectively rolling back the CPU status register to an earlier moment in time, not just re-enabling interrupts.
    2. Unpredictable interactions with interrupt context.
      If this sequence occurs inside or near an ISR (or if nesting happens), you could restore a pre-interrupt ST1 value that is not consistent with the current CPU state, especially if the compiler expects certain ST1 bits to stay as they were set during interrupt entry.

    Best Regards,

    Delaney

  • Yes I can see the other bits in the register.

    What I can't get, is a full overview on a case where those bit would change during the critical section.

    During execution of the critical section, no interrupt will happen thats the purpose of those commands.

    I only see a small possibility that I get an interrupt between the PUSH and the SETC assembler instructions, that according to the manual is generated by the __disable_interrupts function.

    PUSH ST1
    SETC INTM, DBGM
    POP reg16

    But lets assume an interrupt happens right there, between Push and SETC.

    As the ST1 is part of the context save (Acording to table 3-5 in SPRU430F). 

    I would assume that ST1 is back to where is was before the interrupt, when the interrupt exits, i.e. when the PUSH ST1 happened. Thus the version on the stack is still true.

    Please let me know if this assumption is not correct.

    At the end of the critical section, where i call _restore_interrupts, it should generate these assembler instructions:

    PUSH val
    POP ST1

    I assume the POP ST1 is where the interrupts is re-enabled, and that this operation is atomic.

    Thus I see no possibility for an interrupt to happen before the ST1 is restored.

    So the only source i can see is if the code running inside the critical section somehow affects the ST1 register.

    So my question remains, are there any instructions generated by the C-compiler, apart from the intrinsics discussed here, that would affect the ST1 and thus have that overwritten by the restore?

    Also excluding inline assembler calling things like "MSETC XF" and the like.

    Please let me know if I missed something.

  • NB: OFF-Topic: I am a bit surprised that the code insertion block in this forum do not support assembler.

  • Hi Martin,

    I will get back to you with a response in the next 1-2 days.

    Best Regards,

    Delaney

  • Hi Martin,

    Apologies for the delay, I have been running behind on answering E2E's.

    So the only source i can see is if the code running inside the critical section somehow affects the ST1 register.

    The above is correct, that is the risk - code inside the critical section generated by the compiler that changes the ST1 register.

    The issue wouldn't be from getting an interrupt or race condition but rather with status bits in ST1 changing during compiler generated code. You can safely use the __restore_interrupts() function if the code in the critical section does not affect the status bits. I don't have a list of commands to avoid in particular, however I would suggest stepping through the critical section with the debugger attached and watching the ST1 C28x register in the Register View of CCS (with Continuous Refresh on) to check this. This should be deterministic since the code is compiler-generated and uninterruptable. 

    Best Regards,

    Delaney