This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

libUARTE bug: Unexpected RX free input parameter

We are more or less successfully using libUARTE for talking to an STM32 at 1M Baud. 
It was a long and tedious road to get there, but it looks like we finally got there

The biggest struggle was getting an idle line timeout to copy partially filled data, as our protocol uses variable sized packets. 

However, every now and again, we get this error:

libUARTE_async      : Unexpected RX free input parameter. length 2 rx_free_cnt 129 rx_buf_size 128

As you can see, we're only asking the libuarte to free 2 bytes we just copied, but internally, llibUARTE adds this to rx_free_cnt and tries to free that. 

void nrf_libuarte_async_rx_free(const nrf_libuarte_async_t * const p_libuarte, uint8_t * p_data, size_t length)
{

    p_libuarte->p_ctrl_blk->rx_free_cnt += length;
    ...
}

We see this seldomly, but more than just a handful of times. 

I have an assumption, as something similar happened to us on the STM, where we wrote our own driver with DMA, double buffering and an idle line timeout. It turned out to be a race condition where two different interrupts called the same callback function and the second interrupt interrupted the first one, and changed some of the variables under our noses. 

so we had to disable the interrupts until we finished with the Interrupt service routine, or at least the part where we handled data from outside the current scope. 

This also happened seldomly, was impossible to reproduce, but caused a lot of trouble when it did occur. 

Could this also be the case for libUARTE? After all, the callbacks to our code, (where we increment the head of our buffer to mach what DMA already copied, then call the above rx_free() function), come from either the timer interrupt, (when no bytes have been transmitted for a while), or the uart/dma interrupt (when a buffer is full, and we switch to a new one), if I understood libUARTE correctly. If this is the case, could it not be that the one interrupt interrupts the other? and if so, did you safeguard the data being handled in those interrupts?

for completeness sake, here's the code where we handle the NRF_LIBUARTE_ASYNC_EVT_RX_DATA event:

 static void libuarte_event_handler(void * context, nrf_libuarte_async_evt_t * p_evt) {
	instance_t * instance = (instance_t *) context;
    switch (p_evt->type)
    {
        ...
        case NRF_LIBUARTE_ASYNC_EVT_RX_DATA:
            if (p_evt->data.rxtx.p_data != NULL) {
            uint8_t * data = p_evt->data.rxtx.p_data;
            uint32_t len = p_evt->data.rxtx.length;
            uart_rx_complete_callback(instance, len, data);
            nrf_libuarte_async_rx_free(instance->uart.uart_instance_ptr, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);
            ...
            }
        ...
    }
}

Related