Cortex-A9 IRQ with priority over configMAX_API_CALL_INTERRUPT_PRIORITY

sterossi84 wrote on Wednesday, April 24, 2019:

Hi guys,

I’m using FreeRTOS on a Cortex A9 device (Xilinx ZYNQ).
The final application will be used in the context of electric motor control so, in order to reduce control-law interrupt latency, I’ve register the related handler with a priority that exceeds configMAX_API_CALL_INTERRUPT_PRIORITY.

Given the previous setting the interrupt is properly triggered also under FreeRTOS critical sections - exactly as it’s expected to.
I now intend to share data between application tasks and the aforementioned interrupt handler.

To avoid race condition I intend to mask core interrupt, write the data, and then re-enable core interrupts.
Something like the example reported below.

Is this possible /is there a better way to do it?
Does it interfere with FreeRTOS itself leading to system failure?

static uint32_t EM_PositionReferece = 0u;

/* run in the context of the task */
void EM_SetPositionReference(UINT32 user)
{
    /* mask CPU interrupt */
    __asm volatile ( "CPSID i");
    __asm volatile ( "DSB" );
    __asm volatile ( "ISB" );

    /* set motor-control position reference to be used in interrupt */
    EM_PositionReferece = user;
    
    /* 
     * NO FreeRTOS API usage here! 
     */
    
    /* un-mask CPU interrupt */
    __asm volatile ( "CPSIE i");
    __asm volatile ( "DSB" );
    __asm volatile ( "ISB" );
}

void EM_IRQHandler(void)
{
    ControlLaw_Exec(EM_PositionReferece );
    
    /* 
     * NO FreeRTOS API usage here! 
     */
}

As last thing, in the previous example the interrupt are fully masked and then unmasked.
Is there a way (e.g. a dedicated CPU instruction) to get interrupt masking status and simultaneously disabling them?
What I want to achieve is something like the line below

/* mask interrupt and get previous masking status */
irqMask = _MaskIRQ();

/* 
 * set motor-control variable shared with IRQ
 */

/* restore interrupt masking status read at _MaskIRQ */
_RestoreIRQ(irqMask);

Thanks in advance,

Bucky

rtel wrote on Wednesday, April 24, 2019:

I’m afraid I don’t know if there is a single asm instruction that will
do what you want (return the previous interrupt mask and disable as a
single atomic operation) - but there could well be.

As to whether the code you posted will interfere with the kernel - I
don’t think it will. Globally disabling and re-enabling interrupts the
way the first code you posted is doing is fine PROVIDED THAT you can
always guarantee that interrupts were already enabled - otherwise you
need to restore the previous interrupt state rather than just blindly
re-enabled (as you know, because that is what the second part of your
post is doing). The kernel only globally disables interrupt momentarily
when it is updating the interrupt mask (that is a requirement of the
interrupt controller), and your code cannot execute between the kernel’s
own disable/re-enable sequence. Therefore the kernel will not have
disabled interrupts when your code executes and only you know if
anything else in your code will have done.

If in doubt then you can keep a count of the disable nesting yourself,
and only re-enable when the nesting returns to 0. As an example look at
the implementation of vPortEnterCritical() and vPortExitCritical() in
the kernel’s port.c file that you are using. That keeps its own nesting
count for the interrupt priority mask level, you would need to do the
same but for the global interrupt enable instead of the interrupt mask
level.

richard_damon wrote on Wednesday, April 24, 2019:

Another option for updating the parameters ‘atomically’ (especially if you tend to possibly adjust most of them) is to have two copies of these with a flag to indicate which set is ‘active’, and your program then can update the inactive set, and when they are all updated switch the flag to make the ISR use the now updated set.