-
Notifications
You must be signed in to change notification settings - Fork 520
[trackerm/p2] Fix USART/DMA deadlock #2603
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
28b319d
to
c359f83
Compare
while (GDMA_GetDstAddr(rxDmaInitStruct_.GDMA_Index, rxDmaInitStruct_.GDMA_ChNum) < expectedDstAddr) { | ||
// XXX: spin around, this should be pretty fast | ||
while (GDMA_GetDstAddr(rxDmaInitStruct_.GDMA_Index, rxDmaInitStruct_.GDMA_ChNum) < expectedDstAddr && | ||
(GDMA_BASE->ChEnReg & (1 << rxDmaInitStruct_.GDMA_ChNum))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't it lose bytes? for example, if we re goinng to consum 3 more bytes according to RX_BYTE_CNT
, but here we just ignore that if the channel is closed, will the 3 bytes get missed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assumption is that the bytes have already been transferred.
We enter this loop with knowing that the UART reported RX_BYTE_CNT
should be processed by the DMA controller. For unknown reasons, sometimes GDMA_GetDstAddr()
does not increment by this count. In those cases we look at this bit.
According to the datasheet, this bit is cleared by the DMA controller when the transfer is complete. So if this is complete we assume that the data must be present, regardless of if GDMA_GetDstAddr()
indicates the correct address:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add some comments then, in case of forgetting why we're doing in this way over time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's definitely add some comments here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make sure to add a test app or a new test case for serial_loopback2
to make sure that we are not losing any data.
while (GDMA_GetDstAddr(rxDmaInitStruct_.GDMA_Index, rxDmaInitStruct_.GDMA_ChNum) < expectedDstAddr) { | ||
// XXX: spin around, this should be pretty fast | ||
while (GDMA_GetDstAddr(rxDmaInitStruct_.GDMA_Index, rxDmaInitStruct_.GDMA_ChNum) < expectedDstAddr && | ||
(GDMA_BASE->ChEnReg & (1 << rxDmaInitStruct_.GDMA_ChNum))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's definitely add some comments here.
Problem
TrackerM devices eventually deadlock after running for a few hours. The issue was traced to the cellular USART RX DMA transfer. On some transfers, the DMA controller does not report the final block of bytes was transferred from the USART to memory. As a result, we wait in a loop that will never complete.
Solution
The solution is to workaround the DMA controller behavior by checking if the RX DMA transfer channel becomes disabled while waiting for the transfer to complete. According to the data sheet in these circumstances the DMA transfer has actually completed and the data should be available. It should be safe to continue with the assumption that we have not lost any data from the modem. So far this appears to be true, but will need additional testing to explicitly confirm all bytes are accounted for.
Steps to Test
Run tracker edge app with this branch
or run
serial_loopback2
withSERIAL_06_p2_serial2_stress_test
testloopback.cpp.txt
Example App
I modified
serial_loopback2
to test a DMA transfer with 32 byte usart buffers (ie DMA block sizes).I intentionally send 30 bytes of data, which is just less than a block size.
I can see that the final 2 bytes are not transferred until we call
flushDmaRxFiFo()
HOWEVER, I havent confirmed that when we dont see
GDMA_GetDstAddr()
increment as expected that when we break that the bytes will be there. Working on that next.....References
Log output of loopback test sending 30 bytes and reading it out via DMA, byte by byte and logging full RX buffer each time
See actual useful datasheet for DMA controller documentation
Infineon-xmc4100_xmc4200_rm_v1.6_2016-UM-v01_06-EN.pdf
Completeness