Hi all,
I’m trying to debug a serial driver. A binary semaphore has been used to signal data reception from the serial line interruption and the reading task. In rare conditions, my reading task is not awoken, whereas the received byte has been stocked properly in RAM.
In order to debug I have a debugger, and a “debug queue” where I put some debug events to have a RAM history that I read from the debugger.
Thanks to that, I can see that the normal condition is :
1. reading task waits on signal
2. char receive
3. interrupt : char is saved in a ring buffer
4. signal
5. reading task awoken
When the race condition arise, the last step (reading task awoken) does’nt happen. Note that the interrupts continue to save char as long as the ring buffer is not exhausted.
I tried 2 implementation of my driver, one with a counting semaphore, and one with a binary semaphore (as a signal), the same problem happen in both cases.
Could someone help me with debug methods to identify what’s happening ? For instance, it’d like to know where my thread is in the source code when it is blocked, do I have a mean to get the associated “program counter” ?
If it is of any interest I’m working with Atmel Studio and the board is an Arduino due.
Here is an extract of the code, please note that in complete code there is a write() function and a second part of the interrupt which is dedicated to send the tx buffer, and to manage/recover from HW errors, let me know if someone needs the complete code.
Note that my circular buffer library is OS-less so no critical section is hidden inside buffers calls, this is why I protect them.
~~~
:::C++
ArdUART::ArdUART(…):
…
{
…
rxSignal = xSemaphoreCreateBinary();
txSignal = xSemaphoreCreateBinary();
}
void ArdUART::read(uint8_t * const byte)
{
bool byteReceived = false;
while(!byteReceived)
{
portENTER_CRITICAL();
byteReceived = circular_popByte(&rxBuf, byte);
portEXIT_CRITICAL();
if(!byteReceived)
xSemaphoreTake(rxSignal, portMAX_DELAY);
}
//TODO a enlever pour debug
dh_publish_event("read", *byte, 0);
}
void ArdUART::IrqHandler(void)
{
UBaseType
t uxSavedInterruptStatus;
uxSavedInterruptStatus = portSETINTERRUPT
MASKFROM_ISR();
uint32_t status = baseAddr->UART_SR;
uint8_t rxByte = 0;
// Did we receive data?
if ((status & UART_SR_RXRDY) == UART_SR_RXRDY)
{
//read received char and append it to the buffer
txByte = baseAddr->UART_RHR;
if ( circular_appendByte(&rxBuf, txByte) )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
//signal the reading task that some data are available
xSemaphoreGiveFromISR(rxSignal, &xHigherPriorityTaskWoken);
//force context switch if a task with higher priority is awoken
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
//TODO debug : save event in history
dh_publish_event_fromISR("it::RX", txByte, 0);
}
//if the append failed, the buffer is overflown, save the error
else
{
nbRxBytesLost++;
lastError = ERR_OVERSHOOT_RX;
//TODO debug : save event in history
dh_publish_event_fromISR("ErrOvRx", nbRxBytesLost, 0);
}
}
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
}
~~~