Semaphore intermittently not taken in task context when given in ISR context on nRF51822
A semaphore that is given when the UART has completed transmission is not always taken by the task when it should be. The IDLE task is running when it should not be. If a timer expires, then the task will take the semaphore. This anomaly most often occurs when the transmit packet length is small (2-4 bytes).
Am I using the semaphore incorrectly? Is it possible there is an issue in the port specific implementation that was provided by Nordic?
Semaphore intermittently not taken in task context when given in ISR context on nRF51822
Details:
FreeRTOS Version 10.0.0
~~~
Uartt pUart = (Uart_t)pvTimerGetTimerID(Handle); TPHIGH(4, true); PRINTVALUE(true, pUart->tx.busy); PRINTVALUE(true, pUart->tx.confirmedBusy); pUart->errorTimeout = true; TPLOW(4, true); } static void UartEventIsr(nrfdrvuarteventt * pEvent, void * pContext) { Uartt *pUart = (Uartt *)pContext; switch( pEvent->type ) { case NRFDRVUARTEVTTXDONE: TPHIGH(1, true); GIVESEMAPHORE(pUart->tx.idle); uartObject.tx.busy = false; TPHIGH(3, true); TP_LOW(1, true); break; // other cases not shown } } ~~~
define configUSE_PREEMPTION 1
define configIDLESHOULDYIELD 1
define configUSETIMESLICING 1
~~~- IAR 8.20 doesn’t indicate that any stacks are overflowing and the overflow callback isn’t occurring. “configASSERT” is enabled.
- The task blocking on the UART transmit complete semaphore is the highest priority task after TmrSrv and soft device task.
- The code requires the nRF51 development kit to run. However, the firmware currently isn’t in a state that I can share it.
- I have included a Saleae logic capture and a Precipio Tracealyzer log of the issue.
- The semaphore/mutex macros determine whether or not to call the interrupt safe version.
Uartt pUart = (Uart_t)pvTimerGetTimerID(Handle); TPHIGH(4, true); PRINTVALUE(true, pUart->tx.busy); PRINTVALUE(true, pUart->tx.confirmedBusy); pUart->errorTimeout = true; TPLOW(4, true); } static void UartEventIsr(nrfdrvuarteventt * pEvent, void * pContext) { Uartt *pUart = (Uartt *)pContext; switch( pEvent->type ) { case NRFDRVUARTEVTTXDONE: TPHIGH(1, true); GIVESEMAPHORE(pUart->tx.idle); uartObject.tx.busy = false; TPHIGH(3, true); TP_LOW(1, true); break; // other cases not shown } } ~~~
Semaphore intermittently not taken in task context when given in ISR context on nRF51822
Precipio Tracealyzer Log (line 525).
Semaphore intermittently not taken in task context when given in ISR context on nRF51822
Saleae
Semaphore intermittently not taken in task context when given in ISR context on nRF51822
Does GIVESEMAPHORE() also call portYIELDFROM_ISR()?
Semaphore intermittently not taken in task context when given in ISR context on nRF51822
Yes.
~~~
define GIVESEMAPHOREFROM_ISR(s) do {
BaseTypet macroYieldRequest = pdFALSE; BaseTypet macroStatus = xSemaphoreGiveFromISR((s), ¯oYieldRequest); ASSERTWARNING( macroStatus == pdTRUE ); portYIELDFROM_ISR(macroYieldRequest); } while(0)define GIVESEMAPHORETHREAD(s) do {
BaseTypet macroStatus = xSemaphoreGive(s); ASSERTWARNING( macroStatus == pdTRUE ); } while(0)define GIVE_SEMAPHORE(s) do {
if( INTERRUPTCONTEXT() ) { GIVESEMAPHOREFROMISR(s); } else { GIVESEMAPHORETHREAD(s); } } while(0)define TAKESEMAPHOREFROM_ISR(s) do {
BaseTypet macroYieldRequest = pdFALSE; BaseTypet macroStatus = xSemaphoreTakeFromISR((s), ¯oYieldRequest); ASSERTWARNING( macroStatus == pdTRUE ); portYIELDFROM_ISR(macroYieldRequest); } while(0)define TAKESEMAPHORETHREAD(s) do {
BaseTypet macroStatus = xSemaphoreTake((s), portMAXDELAY); ASSERT_WARNING( macroStatus == pdTRUE ); } while(0)define TAKE_SEMAPHORE(s) do {
if( INTERRUPTCONTEXT() ) { ASSERTREBOOT(FORCED); } else { TAKESEMAPHORETHREAD(s); } } while(0) ~~~Semaphore intermittently not taken in task context when given in ISR context on nRF51822
What type of semaphore is it? More specifically, is it a Mutex type, as
they cannot be used form an ISR.
Semaphore intermittently not taken in task context when given in ISR context on nRF51822
It is a binary semaphore.
The mutex isn’t needed, but I added it when testing to ensure only one thing could be in the write function at a time.
~~~
void Uart_Initialize(void)
{
memset(&uartObject, 0, sizeof(uartObject));
uartObject.tx.idle =
xSemaphoreCreateBinary();
ASSERT_WARNING( uartObject.tx.idle != NULL );
uartObject.tx.mutex =
xSemaphoreCreateMutex();
ASSERT_WARNING( uartObject.tx.mutex != NULL );
uartObject.pUartInstance = &UART_INSTANCE;
uartObject.errorTimerHandle =
xTimerCreate(“error timer”, MSTOTICKS(50), ONESHOTTIMER, &uartObject,
ErrorTimerCallback); InitializeUartDriver(&uartObject, NRFUARTBAUDRATE_115200); PRINT(true, “Uart Initializedrn”); } ~~~
xTimerCreate(“error timer”, MSTOTICKS(50), ONESHOTTIMER, &uartObject,
ErrorTimerCallback); InitializeUartDriver(&uartObject, NRFUARTBAUDRATE_115200); PRINT(true, “Uart Initializedrn”); } ~~~
Semaphore intermittently not taken in task context when given in ISR context on nRF51822
Normally this sort of problem is because the interrupt priorities are
set wrong – but you say you are using FreeRTOS V10 with configASSERT()
defined – V10 will catch nearly all (if not all) interrupt priority
issues, so I’m discounting that as a cause in this case. All the same,
can you try setting the priority of the UART interrupt to the lowest
possible to see if it makes any difference.
Semaphore intermittently not taken in task context when given in ISR context on nRF51822
I set it to APPIRQPRIORITY_THREAD and the anomaly still occurs. I am baffled about what I did wrong.
This is a M0 part. I don’t have anything defined for portASSERTIFINTERRUPTPRIORITYINVALID(). Does that seem correct?
~~~
if __CORTEX_M == (0x00U)
define PRIOSD_HIGH 0
define PRIOAPP_HIGH 1
define PRIOAPP_MID 1
define PRIOSD_LOW 2
define PRIOAPP_LOW 3
define PRIOAPP_LOWEST 3
define PRIOTHREAD 4
elif __CORTEX_M == (0x04U)
define PRIOSD_HIGH 0
define PRIOSD_MID 1
define PRIOAPP_HIGH 2
define PRIOAPP_MID 3
define PRIOSD_LOW 4
define PRIOSD_LOWEST 5
define PRIOAPP_LOW 6
define PRIOAPP_LOWEST 7
define PRIOTHREAD 15
else
#error "No platform defined"
endif
//lint -save -e113 -e452 /**@brief The interrupt priorities available to the application while the SoftDevice is active. */ typedef enum {ifndef SOFTDEVICE_PRESENT
APP_IRQ_PRIORITY_HIGHEST = _PRIO_SD_HIGH,
else
APP_IRQ_PRIORITY_HIGHEST = _PRIO_APP_HIGH,
endif
APP_IRQ_PRIORITY_HIGH = _PRIO_APP_HIGH,
ifndef SOFTDEVICE_PRESENT
APP_IRQ_PRIORITY_MID = _PRIO_SD_LOW,
else
APP_IRQ_PRIORITY_MID = _PRIO_APP_MID,
endif
APP_IRQ_PRIORITY_LOW = _PRIO_APP_LOW,
APP_IRQ_PRIORITY_LOWEST = _PRIO_APP_LOWEST,
APP_IRQ_PRIORITY_THREAD = _PRIO_THREAD /**< "Interrupt level" when running in Thread Mode. */
} appirqpriority_t;
//lint -restore
~~~
Semaphore intermittently not taken in task context when given in ISR context on nRF51822
Ah – ok sorry – I thought it was an M4 part so you can ignore my
comments about interrupt priorities.
Can you please send me the port.c file you are using so I can compare it
to the ‘official’ one so I can see if there are any changes. You can
send it to r dot barry at freertos dot org. Thanks.
Semaphore intermittently not taken in task context when given in ISR context on nRF51822
The Nordic SoftDevice can block the tick interrupt from occurring. This can result in lost ticks (the RTC getting out of sync with the tick counter). The original port is in the folder nrf51_sdk12.3.0.
Nordic tech support provided me with changes to handle lost ticks.
My design doesn’t use tickless idle because I couldn’t get it to work properly. The anomaly occurs with either version of the systick file.
My design doesn’t use tickless idle because I couldn’t get it to work properly. The anomaly occurs with either version of the systick file.
Semaphore intermittently not taken in task context when given in ISR context on nRF51822
last post retracted