PIC32 taskENTER_CRITICAL question

grahamfclark wrote on Thursday, January 15, 2009:

I’m using taskENTER_CRITICAL and taskLEAVE_CRITCAL but have been having problems on PIC32.

I have two questions:-

i)             ulStatus = _CP0_GET_STATUS();                                           
            ulStatus |= ( configMAX_SYSCALL_INTERRUPT_PRIORITY << portIPL_SHIFT );   
            _CP0_SET_STATUS( ulStatus );

is used to take the CP0 status register and modify the IPL bits with  configMAX_SYSCALL_INTERRUPT_PRIORITY.

Maybe I’m wrong, but if my task is at ipl2 and MAX_SYSCALL is at ipl4 this will result in a new value of 6. As the PIC32 doc says that… “This field is the encoded (0…63) value of the current IPL.”  e.g. the task in example above  with ipl2 executing a crit sec will write ipl6 to status. No interrupt <=6 will preempt this crit sec when the intent was for interrupts <=4.

ii) It appears  taskENTER_CRITICAL and taskLEAVE_CRITCAL are non-atomic. It would appear possible for another process to preempt/interleave and affect the STATUS register bits and then return to have _CP0_SET_STATUS write incorrect bits back from the saved ulStatus. e.g. bit 0. Is this likely to cause problems?

I have been getting problems ( processor exceptions of differing types) using the above macro’s when locking a int variable prior to incrementing whereas using asm(“di”) asm(“ei”) seems to fix the problem.

Regards Graham

rtel wrote on Friday, January 16, 2009:

I’m a bit confused by the question.  A task does not run at an IPL level - interrupt priorities and task priorities are two separate things, and tasks do not run in interrupts.  Also taskENTER_CRITICAL() and taskEXIT_CRITICAL() should not be called from an interrupt service routine.

Also check that interrupts that are running with an IPL above that specified by configMAX_SYSCALL_INTERRUPT_PRIORITY do not call FreeRTOS API functions.


grahamfclark wrote on Friday, January 16, 2009:

Yes sorry I used the word ‘task’ in the loosest sense.

If an interupt is say at ipl2 and it uses taskENTER_CRITICAL and configMAX_SYSCALL_INTERRUPT_PRIORITY is set to 4 then the result as far as I can see will be 6. i.e. No interrupt other than 7 will pre-empt the interrupt. And as I mentioned, it’s non atomic, which worried me a little.

However,  I didn’t know that thsse critical tasks macros shouldn’t be used within an interrupt. I can’t see that entioned in the doc?

All my interrupts are running at or below configMAX_SYSCALL_INTERRUPT_PRIORITY and are all called using the FreeRTOS wrapper. I’m not using any xxxISR calls in the interrupts that are giving me problems.

As I mentioned I’m  using asm(“di”) and asm(“ei”), running code for days and I’ve had no problems.  Using the critical macros causes _exceptions errors within seconds. But then again, I was using them within interrupts!! 

The code is a Circular Buffer implementation that allows seperate tasks (again loose term - could be an intteruot or FreeRTOS task) to read / write, hence the lock required to update the header data.

Anyway, my current code is fine  using di/ei and is pretty efficient for a very short lock that I require.


edwards3 wrote on Friday, January 16, 2009:

…but as Richard said, interrupts must not call taskENTER_CRITICAL(). This goes for all ports.