freertos_usart_serial_read_packet() calls:
freertos_copy_bytes_from_pdc_circular_buffer() in file
freertos_peripheral_control.c line 181 which manipulates
p_rx_buffer_details->next_byte_to_read (the read ptr) @ lines 243
and 252
configure_rx_dma(usart_index, data_removed) which
manipulates rx_pdc_parameters.ul_size (the size for dma transfer) @
lines 609, 611, 618, and 623
and the ISR Handler which calls:
configure_rx_dma(usart_index, data_added); which manipulates
rx_pdc_parameters.ul_addr (the write ptr) @ lines 704 and 711
The function, freertoscopybytesfrompdccircularbuffer(), determines
how much data is available in the dma buffer. If data is available, it
copies it from the dma buffer to the user buffer and then updates
prxbufferdetails->nextbytetoread. Then, if data was copied from
the buffer and the DMA controller is stopped, it calls
configurerxdma(usartindex, dataremoved) to restart the dma
controller to fill in the area in the dma buffer that was opened up by
the copy from the dma buffer (the read).
When the DMA transfer (started by the read above) is complete, the
Handler is called @ line 694. The Handler then manipulates
rxpdcparameters.uladdr (the write ptr) to set up the next write into
the dma buffer and calls configurerxdma(usartindex, data_added) line
717 to restart the dma engine if there is space available to fill in the
dma buffer.
This works unless a call to freertosusartserialreadpacket() is
interrupted between the update of prxbufferdetails->nextbytetoread
and the subsequent call to configurerxdma(usartindex, *dataremoved)
by a dma transfer completion interrupt which updates
rx_buffer_definition->rx_pdc_parameters.ul_addr and calls
configure_rx_dma(usart_index, *data_added). The logic inconfigurerxdma to decide whether the buffer is empty or full line 601 becomes confused and makes the wrong choice.
- By example if we start out with the buffer full (i.e. both the read and write pointers are equal and the DMA controller is stopped).
- Then a call to freertosusartserialreadpacket reads one byte which starts the dma engine with a size of 1 to fill in the byte we read. The read pointer is 1 byte ahead of the write pointer and the dma controller is running.
- Then another call to freertosusartserialreadpacket reads one byte. Because the dma controller is still running configurerxdma(usartindex, dataremoved) is not called. The read pointer is 2 bytes ahead of the write pointer.
- Then another call to freertosusartserialreadpacket read one byte occurs. The read occurs but after line 256 in freertoscopybytesfrompdccircularbuffer() and before**executing line 561 the dma controller receives the byte started in step 2 and triggers an interrupt.
- The interrupt runs and immediately restarts the dma controller via a call to configurerxdma(usartindex, dataadded) to fill in the two bytes from steps 3 and step 4.
- Before execution can resume at line 561 (other interrupt processing and such) the dma controller completes the transfer of the two bytes in step 5 and generates an interrupt.
- The interrupt handler advances the write ptr by 2 and the read and write ptr are now equal and calls configurerxdma(usartindex, dataadded). This call correctly identifies the buffer as full and stops the dma controller by setting size to 0 line 609. The read and write pointer are equal and the dma controller is stopped because the buffer is full.
- Finally execution resumes at line 561 (from step 4) and subsequent call to configurerxdma(usartindex, dataremoved). Seeing that the read and write pointers are equal and assuming the cause was the read in step 4 incorrectly determines the buffer was empty and at line 612 starts the action to refill the entire buffer destroying the valid data in the dma buffer.
completes. After the interrupt service routine completes the subsequent call to configurerxdma(usartindex, dataremoved) resumes execution it can misinterpret the state of the full buffer as empty. The update of prxbufferdetails->nextbytetoread and the call to configurerxdma(usartindex, dataremoved) cannot be interrupted and hence need to execute in the *same *critical section. Additionally line 627 of configurerxdma() incorrectly performs a sanity check to make sure the dma controller is not programmed to write beyond the dma buffer length. configASSERT((rxbufferdefinition->rxpdcparameters.ulsize + rxbufferdefinition->rxpdcparameters.ulsize) <= rxbufferdefinition->pastrxbufferendaddress); should read configASSERT((rxbufferdefinition->rxpdcparameters.uladdr + rxbufferdefinition->rxpdcparameters.ulsize) <= rxbufferdefinition->pastrxbufferendaddress);