configASSERT xTaskNotifyFromISR

casan62 wrote on Thursday, August 02, 2018:

Hi,
Using NRF52832 with FreeRTOS v10.0.1

The configASSERT in the callback funtion is asserted and program stops but task has been created successfuly.
I don’t know what is the root cause for this problem as I have another similar callback, task and interrupt priority implemented to TWI that runs fine always.

Can you please give some advice on how to solve this problem ?
Any help is very appreciated.
Thanks.

ISR callback function:

/* The callback interrupt service routine for the UARTE interrupt */
void uarte_interrupt_handler(nrf_drv_uart_event_t * p_event, void* p_context)
{
    /* Tracealyzer defitions for tracing ISRs */
    traceHandle ISR_UARTE_Handle = xTraceSetISRProperties("ISR_UARTE", 7);

    vTraceStoreISRBegin(ISR_UARTE_Handle); /* Tracelyzer ISR tracing */

    BaseType_t xHigherPriorityTaskWoken =  pdFALSE;    

    configASSERT(m_uart_task);
    /* Notify the task that the p_event occurred in the task's notification value. */
    xTaskNotifyFromISR ( m_uart_task, (uint32_t) p_event->type, eSetBits, &xHigherPriorityTaskWoken );   
    /* switch context is required */
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
    vTraceStoreISREnd(xHigherPriorityTaskWoken); /* Stops ISR tracing */
}

Task function:

/* This function gets events from the UART and processes them. */
static void uart_task(void * pvParameter)
{ 
    NRF_LOG_INFO("Enter uart_task.");
    uint8_t rxbuffer;
    uint32_t err_code;
    const TickType_t xMaxBlockTime = pdMS_TO_TICKS( 500 );
    BaseType_t xResult;
    uint32_t ulNotifiedValue;
    
    if (m_uart_hook != NULL)
    {
        m_uart_hook(pvParameter);
        /* enable receiver */
        err_code = nrf_drv_uart_rx(pvParameter, &rxbuffer, 1);
        APP_ERROR_CHECK(err_code);
        NRF_LOG_INFO("UARTE receiver enabled.");
    }

    while (true)
    {
        /* Wait to be notified of an interrupt. */
        xResult = xTaskNotifyWait(pdFALSE,              /* Don't clear bits on entry. */
                                  0xFFFFFFFF,           /* Clear all bits on exit. */
                                  &ulNotifiedValue,     /* Stores the notified value. */
                                  xMaxBlockTime);
        if( xResult == pdPASS )
        {
            /* A notification was received. See which bits were set. */
            if( ( ulNotifiedValue & NRF_DRV_UART_EVT_TX_DONE ) != 0 )
            {
                /* The TX ISR has set a bit. */
                err_code = nrf_drv_uart_tx(pvParameter, &rxbuffer, 1);
                APP_ERROR_CHECK(err_code);
            }
            if( ( ulNotifiedValue & NRF_DRV_UART_EVT_RX_DONE ) != 0 )
            {
                /* The RX ISR has set a bit. */
                if ( nrf_drv_uart_rx_ready(pvParameter) )
                {
                    err_code = nrf_drv_uart_rx(pvParameter, &rxbuffer, 1);
                    APP_ERROR_CHECK(err_code);
                    /* echo data to terminal */
                    err_code = nrf_drv_uart_tx(pvParameter, &rxbuffer, 1);
                    APP_ERROR_CHECK(err_code);
                }
            }
            if( ( ulNotifiedValue & NRF_DRV_UART_EVT_ERROR ) != 0 )
            {
              NRF_LOG_INFO("Error in uart comm");
            }
        }

        /* ulTaskNotifyTake(BaseType_t xClearCountOnExit, TickType_t xTicksToWait) allows uart_task to wait in 
           the Blocked state for its notification value to be greater than zero, and either decrements (subtracts one from) 
           or clears the task’s notification value before it returns. */
        (void) ulTaskNotifyTake(pdTRUE,          /* Clear the notification value before exiting (equivalent to the binary semaphore). */
                                xMaxBlockTime ); /* Block */
    }
}

FreeRTOS config:

 configLIBRARY_LOWEST_INTERRUPT_PRIORITY         7 // (ARM highest) 0, 1, 2, 3, 4, 5, 6, 7 (ARM lowest)
 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    2        
 configKERNEL_INTERRUPT_PRIORITY                 configLIBRARY_LOWEST_INTERRUPT_PRIORITY
configMAX_SYSCALL_INTERRUPT_PRIORITY         configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY

rtel wrote on Thursday, August 02, 2018:

I don’t know which configASSERT() is failing as you didn’t say - but check your definitions in FreeRTOSConfig.h. configKERNEL_INTERRUPT_PRIORITY and configMAX_SYSCALL_INTERRUPT_PRIORITY should use ARM style priorities, not the ‘library’ versions, which are reversed. grep for similar definitions in the FreeRTOS kernel distribution for examples. Also https://www.freertos.org/RTOS-Cortex-M3-M4.html

casan62 wrote on Thursday, August 02, 2018:

Hi Richard,

Thanks for your reply.
I already read that document in the link and as NRF52 has configPRIO_BITS = 3, I used to configure
"#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 2 and also
‘’#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 7
the assertion failed is the one in the ISR callback function above ( configASSERT(m_uart_task); ), as I said before I have implemented similar solution to TWI and works fine and I commented it out and testing with UART but it failing at that point of callback.
The task has been successfuly created and m_uart_task is not NULL or zero whenever asserted in the callback.
Any help is greatly appreciated.
Thanks,
Carlos

rtel wrote on Thursday, August 02, 2018:

Can you place a break point when the assert fails to look at the value
of the m_uart_task? It would be odd that it would assert without it
being NULL - assuming configASSERT() is defined in the normal way.

Does the interrupt execute a few times before the assert fails? Or does
it fail on the first execution. If the first, are you sure the
interrupt is not firing earlier than you anticipate - and therefore
before the task has been created?

Perhaps also post the code where m_uart_task is set (where the task is
created)?

casan62 wrote on Friday, August 03, 2018:

Hi Richard,
sure I did what you suggested.
In fact, I see m_uart_task is not NULL breaking at configASSERT in the callback function and no interrupt execution prior to send any char to uart. Please see detailed information below.

The code behaves as following steps:
0- clock and gpio initialization;
1- UART Task is created successfuly;
2- Scheduler is started successfuly;
3- UART task is executed, calling hook function and Rx UARTE initialized successfuly;
4- While not sending any char thru terminal to PC comm port, there is no trigger at IRQ handler in the debugger and callback function is not called/triggered. That is all right.
5- Now, I remove the breakpoint at IRQ Handler to avoid stop here when I send the first char in the terminal.
6- Now, I have only a break at configASSERT in ISR callback function and send only one char;
7- Break it, where m_uart_task contains the value (0x20007900), looking the memory dump in details:
m_uart_task is stored statically at address 0x2000A070 and contains 0x20007900 that points to address 0x20007874 that points to address 0x200078E4 that contains 0x00000000.
Is it clear ? Hope this can help to understand what is the fault.
As per your request, here goes code to:

UARTE initialization:
static void uarte0_init(void * p_context)
{
uint32_t err_code;
const nrf_drv_uart_config_t uart_config =
{
.pseltxd = TX_PIN_NUMBER, ///< TXD pin number.
.pselrxd = RX_PIN_NUMBER, ///< RXD pin number.
.pselcts = CTS_PIN_NUMBER, ///< CTS pin number.
.pselrts = RTS_PIN_NUMBER, ///< RTS pin number.
.p_context = p_context, ///< Context of UARTE instance passed to interrupt handler.
.hwfc = (nrf_uart_hwfc_t) NRF_UARTE_HWFC_DISABLED, ///< Flow control configuration.
.parity = (nrf_uart_parity_t) NRF_UARTE_PARITY_EXCLUDED, ///< Parity configuration.
.baudrate = (nrf_uart_baudrate_t) NRF_UARTE_BAUDRATE_115200, ///< Baudrate.
.interrupt_priority = UART_DEFAULT_CONFIG_IRQ_PRIORITY, ///< Interrupt priority.
" #ifdef UARTE_PRESENT
.use_easy_dma = true,
" #endif
};

err_code = nrf_drv_uart_init(p_context,
                             &uart_config,
                             uarte_interrupt_handler); 											/* using callback for UARTE IRQ */
APP_ERROR_CHECK(err_code);
NRF_LOG_INFO("UARTE initialized");

}

Task creation:
/**@brief Function for creating a task to retrieve UART events.

  • @param[in] hook_fn Function to run in the UART FreeRTOS task,

  •                      before entering the task loop.
    
  • @param[in] p_context Parameter for the function @p hook.
    */
    void nrf_uart_freertos_init(nrf_uart_freertos_task_hook_t hook_fn, void * p_context)
    {
    NRF_LOG_INFO(“Creating a UART task.”);

    m_uart_hook = hook_fn;

    BaseType_t xReturned = xTaskCreate(uart_task,
    “UART”,
    NRF_BLE_FREERTOS_UART_TASK_STACK,
    p_context,
    NRF_BLE_FREERTOS_UART_TASK_PRIO,
    &m_uart_task);
    if (xReturned != pdPASS)
    {
    NRF_LOG_ERROR(“UART task not created.”);
    APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
    }
    }

functions calling ISR callback:
__STATIC_INLINE void rx_done_event(uart_control_block_t * p_uart_cb, uint8_t bytes, uint8_t * p_data)
{
nrf_drv_uart_event_t event;

event.type             = NRF_DRV_UART_EVT_RX_DONE;
event.data.rxtx.bytes  = bytes;
event.data.rxtx.p_data = p_data;
/* is non-blocking mode, callback function */
configASSERT(p_uart_cb->handler != NULL)
p_uart_cb->handler(&event, p_uart_cb->p_context); /* ISR callback function is called here */

}

__STATIC_INLINE void tx_done_event(uart_control_block_t * p_uart_cb, uint8_t bytes)
{
nrf_drv_uart_event_t event;

event.type             = NRF_DRV_UART_EVT_TX_DONE;
event.data.rxtx.bytes  = bytes;
event.data.rxtx.p_data = (uint8_t *)p_uart_cb->p_tx_buffer;

p_uart_cb->tx_buffer_length = 0;

NRF_LOG_INFO("TX done len:%d", bytes);
/* non-blocking mode, callback function */
configASSERT(p_uart_cb->handler != NULL)
p_uart_cb->handler(&event, p_uart_cb->p_context); /* ISR callback function is called here */

}

IRQ Handler:
__STATIC_INLINE void uarte_irq_handler(NRF_UARTE_Type * p_uarte, uart_control_block_t * p_uart_cb)
{
/* Tracealyzer defitions for tracing ISRs */
traceHandle UARTE_IRQ_Handle = xTraceSetISRProperties(“IRQ_UARTE”, UART_DEFAULT_CONFIG_IRQ_PRIORITY);

vTraceStoreISRBegin(UARTE_IRQ_Handle); /* Tracelyzer ISR tracing */ 
BaseType_t yield_req = pdFALSE;

/* End of transmition */
if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_ENDTX))
{
    nrf_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_ENDTX);
    if (p_uart_cb->tx_buffer_length)
    {
        tx_done_event(p_uart_cb, nrf_uarte_tx_amount_get(p_uarte));
    }
}
/* End of reception ? */
else if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_ENDRX))
{
    nrf_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_ENDRX);
    uint8_t amount = nrf_uarte_rx_amount_get(p_uarte);
    // If the transfer was stopped before completion, amount of transfered bytes
    // will not be equal to the buffer length. Interrupted transfer is ignored.
    if (amount == p_uart_cb->rx_buffer_length)
    {
        if (p_uart_cb->rx_secondary_buffer_length)
        {
            nrf_uarte_shorts_disable(p_uarte, NRF_UARTE_SHORT_ENDRX_STARTRX);
            p_uart_cb->rx_buffer_length     = p_uart_cb->rx_secondary_buffer_length;
            p_uart_cb->p_rx_buffer          = p_uart_cb->p_rx_secondary_buffer;
            p_uart_cb->rx_secondary_buffer_length = 0;
            rx_done_event(p_uart_cb, amount, p_uart_cb->p_rx_buffer);
        }
        else
        {   /* reset rx buffer length due to trunked transfer */
            p_uart_cb->rx_buffer_length = 0;
            rx_done_event(p_uart_cb, amount, p_uart_cb->p_rx_buffer);
        }
    }

    /* verify error event has occurred in the reception */
    if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_ERROR))
    {
        nrf_drv_uart_event_t event;

        nrf_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_ERROR);

        event.type                   = NRF_DRV_UART_EVT_ERROR;
        event.data.error.error_mask  = nrf_uarte_errorsrc_get_and_clear(p_uarte);
        event.data.error.rxtx.bytes  = nrf_uarte_rx_amount_get(p_uarte);
        event.data.error.rxtx.p_data = p_uart_cb->p_rx_buffer;

        //abort transfer
        p_uart_cb->rx_buffer_length           = 0;
        p_uart_cb->rx_secondary_buffer_length = 0;
        /* non-blocking mode, callback function */
        configASSERT(p_uart_cb->handler != NULL)
        p_uart_cb->handler(&event, p_uart_cb->p_context);
    } 
    /* Timeout event has occurred in the reception? */
    else if (nrf_uarte_event_check(p_uarte, NRF_UARTE_EVENT_RXTO))
    {
        nrf_uarte_event_clear(p_uarte, NRF_UARTE_EVENT_RXTO);
        if (p_uart_cb->rx_buffer_length)
        {   /* reset rx buffer length */
            p_uart_cb->rx_buffer_length = 0;
            rx_done_event(p_uart_cb, nrf_uarte_rx_amount_get(p_uarte), p_uart_cb->p_rx_buffer);
        }
    }   
} // end-of-reception
/* Switch the task if required. */
portYIELD_FROM_ISR(yield_req);
vTraceStoreISREnd(yield_req); /* Stops ISR tracing */

}

Thanks for your attention to this issue.
Have a nice day.

rtel wrote on Friday, August 03, 2018:

In fact, I see m_uart_task is not NULL breaking at configASSERT

That is very weird, how have you defined configASSERT()? Is anything
interfering with execution - for example are you using semihosting?

m_uart_task is stored statically at address 0x2000A070

So that is the address of the variable that holds the_muart_task value…

and contains
0x20007900

…and that is the value held in m_uart_task - which as you said - is
not NULL. This is in effect a pointer to the task TCB.

that points to address 0x20007874

Which is presumably the address of the TCB. I would probably expect
that to be 8-byte aligned.

The code you posted doesn’t show how m_uart_task is declared. Is it
just as follows:

TaskHandle_t m_uart_task;

? Is this a file scope or static variable - basically make sure it is
not defined on the stack, not even the stack of main(), as none of that
memory will be valid.

Regards.

casan62 wrote on Friday, August 03, 2018:

Hi Richard,

following more information.
Regards.

These definitions are held in nrf_uart_freertos.h :
"#define NRF_BLE_FREERTOS_UART_TASK_STACK 256
"#define NRF_BLE_FREERTOS_UART_TASK_PRIO configMAX_PRIORITIES - 3 /* prio 1, configMAX_PRIORITIES = 4 */

typedef void (*nrf_uart_freertos_task_hook_t)(void * p_context);

static TaskHandle_t m_uart_task; //!< Reference to UART FreeRTOS task.
static nrf_uart_freertos_task_hook_t m_uart_hook; //!< A hook function run by the UART task before entering its loop.

From the .map file:
m_uart_0 0x00025b4c 0x8 Data Lc main.o [1]
m_uart_cb 0x20009f8c 0x20 Data Lc nrf_drv_uart_freertos.o [1]
m_uart_hook 0x2000a06c 0x4 Data Lc nrf_uart_freertos.o [1]
m_uart_task 0x2000a070 0x4 Data Lc nrf_uart_freertos.o [1]

uart_task 0x00029049 0x96 Code Lc nrf_uart_freertos.o [1]
uarte0_init 0x000255cb 0x42 Code Lc main.o [1]
uarte_interrupt_handler
0x00025579 0x52 Code Gb main.o [1]
uarte_irq_handler 0x0002898b 0x118 Code Lc nrf_drv_uart_freertos.o [1]
ucHeap 0x20006a28 0x2000 Data Lc heap_1.o [1]
The memory model is heap_1.

definition in FreeRTOSConfig.h:
/* Define to trap errors during development. */
"#if defined(DEBUG_NRF) || defined(DEBUG_NRF_USER)
"#define configASSERT( x ) ASSERT(x)
"#endif // defined(DEBUG_NRF) || defined(DEBUG_NRF_USER)

from nrf_assert.h :
"#if (defined(DEBUG_NRF) || defined(DEBUG_NRF_USER))
"#define NRF_ASSERT_PRESENT 1
"#else
"#define NRF_ASSERT_PRESENT 0
"#endif

//#if defined(DEBUG_NRF) || defined(DEBUG_NRF_USER)

/*lint -emacro(506, ASSERT) / / Suppress "Constant value Boolean */
/*lint -emacro(774, ASSERT) / / Suppress “Boolean within ‘if’ always evaluates to True” */ \

/** @brief Function for checking intended for production code.
*

  • Check passes if “expr” evaluates to true. */

"#ifdef _lint
"#define ASSERT(expr)
if (expr)
{
}
else
{
while (1);
}
"#else //_lint
"#define ASSERT(expr)
if (NRF_ASSERT_PRESENT)
{
if (expr)
{
}
else
{
assert_nrf_callback((uint16_t)LINE, (uint8_t *)FILE);
}
}

"#endif

rtel wrote on Friday, August 03, 2018:

The definitions of ASSERT() would seem to show they won’t be triggered
when the task handle is non-null - I think you are going to have to step
through the code as the assert is triggered to figure out why this is
happening.

casan62 wrote on Friday, August 03, 2018:

Hi Richard,
I managed that issue moving callback function from main.c to the file that contains task handler.
That solved, even that weird situation, I will investigate further to understand what happened.
Thank you for your support.
Regards.