vTaskNotifyGive works from a task NOT within an ISR (vTaskNotifyGiveFromISR)

simointe wrote on Tuesday, May 31, 2016:

(TMS570LC4, nested interrupt, C++)

I have a task waiting on a notification, I aim for a semaphore-like binary behaviour.

The stuff works fine as long as I call xTaskNotifyGive( taskHandle ) within another task (not ISR). The waiting task awake from :
ulTaskNotifyTake( pdTRUE, portMAX_DELAY );

Now I want to do the same thing from my ISR (InterruptCan1High), I call :
xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR( taskHandle, &xHigherPriorityTaskWoken );
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);

…and the waiting task never wake. Interrupt comes and comes, multiple vTaskNotifyGiveFromISR() are done.

I have try both ways to define my ISR:

__interrupt void InterruptCan1High( void )
{
…; //code above
}

or

NESTED_INTERRUPT_ENTRY_IRQ( InterruptCan1High )
{
…; //code above
}
NESTED_INTERRUPT_EXIT_IRQ( InterruptCan1High )

Nothing happen.

Regards.

Simon

rtel wrote on Tuesday, May 31, 2016:

I don’t know what NESTED_INTERRUPT_ENTRY_IRQ() is, so I should stick to
the __interrupt method of defining the interrupt handler.

Are you sure taskHandle used in the interrupt handler is the correct handle.

If you step though the call to vTaskNotifyGiveFromISR() in the debugger,
what do you see happening - does it try to unblock a task?

simointe wrote on Wednesday, June 08, 2016:

NESTED_INTERRUPT_ENTRY_IRQ() is some assembler code we defined in order to manage nesting interrupt. But I don’t use it for rtos tick interrupt and the swi interrupt cause it seems conflict with freeRtos stuff.
We have CANBUS interrupt using NO rtos stuff at all and I use these define in order it works.
I’ll test what you talk about stepping through vTaskNotifyGiveFromISR(), I’ll be right back.

#define NESTED_INTERRUPT_ENTRY_IRQ( fn )       \
      _Pragma ("FUNC_CANNOT_INLINE")           \
      static void internalIrq##fn (void)

//-----------------------------------------------------------------------------
/// \brief Define adding the necessary assembly instructions to exit a nested
///        interrupt. It needs to be a define because it can not be a function
///        call.
///        Restore LR
///        Disable IRQ (IRQ Mode)
///        Restore SPSR_irq to LR
///        Copy LR to SPSR_irq
//-----------------------------------------------------------------------------
#define NESTED_INTERRUPT_EXIT_IRQ( fn )           \
   _Pragma ("CODE_STATE(32)")                     \
   void fn (void)                                 \
   {                                              \
      {                                           \
      asm( " ADD       SP, SP, #8      " );       \
      asm( " SUB       LR, LR, #4      " );       \
      asm( " SRSFD     #0x12 !         " );       \
      asm( " CPS       #0x13           " );       \
      asm( " PUSH      {R0-R12}        " );       \
      asm( " FMRX      R5,FPEXC        " );       \
      asm( " PUSH      {R5}            " );       \
      asm( " FMRX      R5,FPSCR        " );       \
      asm( " PUSH      {R5}            " );       \
      asm( " FSTMDBD   SP!, {D0-D7}    " );       \
      asm( " MOV       R1, SP          " );       \
      asm( " AND       R1, R1, #4      " );       \
      asm( " SUB       SP, SP, R1      " );       \
      asm( " PUSH      {R1, R14}       " );       \
      }                                           \
      internalIrq##fn();                          \
      {                                           \
      asm( " POP       {R1, LR}       " );        \
      asm( " ADD       SP, SP, r1     " );        \
      asm( " FLDMIAD   SP!, {D0-D7}   " );        \
      asm( " POP       {R5}           " );        \
      asm( " FMXR      FPSCR, R5      " );        \
      asm( " POP       {R5}           " );        \
      asm( " FMXR      FPEXC, R5      " );        \
      asm( " POP       {R0-R12}       " );        \
      asm( " CPS       #0x12          " );        \
      asm( " RFEFD     SP!            " );        \
      }                                           \
   }

rtel wrote on Wednesday, June 08, 2016:

I can’t recall if the TMS570 port support interrupt nesting or not, but know the generic ARM Cortex-R port does. If the port does implement interrupt nesting it could be dangerous to have your own code on some interrupts and use FreeRTOS code on other interrupts if those two interrupts could nest with each other (don’t know that for a fact, hence I say ‘could’).

user1966 wrote on Thursday, June 09, 2016:

Hi!
(PIC24, MPLAB X, FreeRTOS 9.0.0)

I have the same issue, actually it seems that none of the FromISR calls are working, I have tried at least task notification, semaphores, queues and deferred interrupt handling.

My setup is very simple, just one simple task having only waiting point and a separate timer interrupt routine (60 ms interval) trying to wake it up. Tick is 1 ms. The code is straight from the text book. Using debugger I can see that it looks like the FromISR is working correctly but the task is not unblocked. The task and the interrupt priorities are the same (1). Maybe there is something very basic missing from my setup. Is there something specific in the configuration file that needs to be added to make it work? This is my freertos configuration .h

#define MPLAB_PIC24_PORT

#define configUSE_PREEMPTION			1
#define configUSE_IDLE_HOOK				0
#define configUSE_TICK_HOOK				0
#define configTICK_RATE_HZ				( ( portTickType ) 1000 )
#define configCPU_CLOCK_HZ				( ( unsigned long ) 16000000 )  /* Fosc / 2 */
#define configMAX_PRIORITIES			( ( unsigned portBASE_TYPE ) 4 )
#define configMINIMAL_STACK_SIZE		( 115 )
#define configTOTAL_HEAP_SIZE			( ( size_t ) (6000) )
#define configMAX_TASK_NAME_LEN			( 4 )
#define configUSE_TRACE_FACILITY		0 // For tracing
#define configUSE_16_BIT_TICKS			1
#define configIDLE_SHOULD_YIELD			1


/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 		0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )


/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */

#define INCLUDE_vTaskPrioritySet		1
#define INCLUDE_uxTaskPriorityGet		0
#define INCLUDE_vTaskDelete				1
#define INCLUDE_vTaskCleanUpResources	1
#define INCLUDE_vTaskSuspend			1
#define INCLUDE_vTaskDelayUntil			1
#define INCLUDE_vTaskDelay				1
#define configUSE_MUTEXES 				1
#define configKERNEL_INTERRUPT_PRIORITY	0x01

rtel wrote on Friday, June 10, 2016:

Have you followed the instructions on the documentation page for the
PIC24, and used the examples provided as a template?

The instructions from the web page (which is somewhat historic):

"Interrupt service routines that cannot cause a context switch have no
special requirements and can be written as per the compiler documentation.

Interrupt service routines that can cause a context switch must execute
with priority portKERNEL_INTERRUPT_PRIORITY, and only call taskYIELD()
at the very end of the service routine after the interrupt source has
been cleared. See the file serial.c included in the demo application for
an example."

The example provided:

void __attribute__((__interrupt__, auto_psv)) _U2TXInterrupt( void )
{
signed char cChar;
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

   /* If the transmit buffer is full we cannot get the next character.
   Another interrupt will occur the next time there is space so this does
   not matter. */
   IFS1bits.U2TXIF = serCLEAR_FLAG;
   while( !( U2STAbits.UTXBF ) )
   {
     if( xQueueReceiveFromISR( xCharsForTx, &cChar, 
&xHigherPriorityTaskWoken ) == pdTRUE )
     {
       /* Send the next character queued for Tx. */
       U2TXREG = cChar;
     }
     else
     {
       /* Queue empty, nothing to send. */
       xTxHasEnded = pdTRUE;
       break;
     }
   }

   if( xHigherPriorityTaskWoken != pdFALSE )
   {
     taskYIELD();
   }
}

I just tried this in the simulator and it seems to work.

user1966 wrote on Friday, June 10, 2016:

Thanks for your reply.

I think I have followed the instructions as such. I’m not using the PIC24 demo project, my HW is something else than the explorer board.

It’s getting a bit weird. I played with xQueueSendFromISR and if I go through the code line-by-line (using step-into in MPLABX) it works. But if I let the code run freely, it doesn’t. I followed the code and found strange behaviour in queue.c at line 1258. I assume that the scheduler works correctly and calls the xQueueGenericReceive to wake up the task. Something happens at line 1258 when it uses taskENTER_CRITICAL() macro. By using “step-into” the debugger passes the taskENTER_CRITICAL and enters the next code line after at line 1264 and eventually comes out of the xQueueReceive in the task as expected. But if I use “step-over” or set a breakpoint at line 1264, it will never reach that line (just stays running and never wakes up the task). Here’s the FreeRTOS code for taskENTER_CRITICAL()

void vPortEnterCritical( void )
{
portDISABLE_INTERRUPTS();
uxCriticalNesting++;
}

portDISABLE_INTERRUPTS() macro is something like this:

SET_CPU_IPL( configKERNEL_INTERRUPT_PRIORITY ); __asm volatile ( “NOP” )

and finally SET_CPU_IPL macro is:

`#define SET_CPU_IPL(ipl) { ``
unsigned int DISI_save;

DISI_save = DISICNT;
asm volatile (“disi #0x3FFF”);
SRbits.IPL = ipl;
__builtin_write_DISICNT( DISI_save); } (void) 0

Is there something I need to correct for the taskENTER_CRITICAL() for this port?

rtel wrote on Saturday, June 11, 2016:

Please post your ISR code.

user1966 wrote on Saturday, June 11, 2016:

Here it is. The t_counter1 and t_counter2 are UINT32 and just for debugging purposes.

/* _T1Interrupt ISR */

void attribute((interrupt, auto_psv)) _T2Interrupt( void )

{

/* Description
Interrupt service routine for Timer2 interrupt

Return values
    None

*/

/* Data structures */

BaseType_t xHigherPriorityTaskWoken;
TaskHandle_t task_handle;
UINT8 messu;

/* Code */

messu = 0xa;

t_counter1++;

xHigherPriorityTaskWoken = pdFALSE;

xQueueSendFromISR( xQueue, &messu, &xHigherPriorityTaskWoken );


// reset timer interrupt flag and return from ISR
IFS0bits.T1IF = 0;

if (xHigherPriorityTaskWoken == pdTRUE) {
    t_counter2++;
    portYIELD();
}

}

richard_damon wrote on Saturday, June 11, 2016:

One thing I see is it is installed for T2, but mesing with T1

user1966 wrote on Sunday, June 12, 2016:

Thanks Richard, that seemed to solve the problem! I was using old ISR template and didn’t check the interrupt flag setting at all (assumed it to be correct one). I was just too blind to see the solution.

simointe wrote on Monday, June 13, 2016:

Now it works, I have to admit that I don’t know exactly why.
But when the error was there, stepping through xTaskGenericNotifyFromISR() the problem was that the original state of the thread is taskNOTIFICATION_RECEIVED instead of taskWAITING_NOTIFICATION and the task was NOT unblock by the call.

xTaskGenericNotifyFromISR()..
			if( ucOriginalNotifyState == taskWAITING_NOTIFICATION )