-
Notifications
You must be signed in to change notification settings - Fork 92
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
fix(PeriphDrivers): Ensure single execution of DMA-based SPI transaction callback for all parts #1070
fix(PeriphDrivers): Ensure single execution of DMA-based SPI transaction callback for all parts #1070
Conversation
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.
Works well.
Thank you for your contribution - very nice PR description!
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.
Thanks @JeonChangmin, looks good. One minor change request below.
I wasn't sure whether stdatomic was well supported on the Arm core out of the box, but the disassembly shows it uses ldrex
and strex
to implement the atomic compare and swap loop. Thought you might find it interesting.
req_done = atomic_fetch_add(&states[i].req_done, 1);
34: f3bf 8f5b dmb ish
38: e854 af00 ldrex sl, [r4]
3c: f10a 0301 add.w r3, sl, #1
40: e844 3200 strex r2, r3, [r4]
44: 2a00 cmp r2, #0
46: d1f7 bne.n 38 <MXC_SPI_RevA1_DMACallback+0x38>
48: f3bf 8f5b dmb ish
Theoretically it's a rare condition anyways. The DMA IRQs are assigned the same priority by default in the NVIC, so instead of nesting into each other they are queued sequentially. So I think the condition would only occur if the DMA RX channel IRQ is manually assigned a lower priority value, or vice versa.
@Jake-Carter @sihyung-maxim Thank you for the feedback! If the sequential execution of the DMA handler is guaranteed, then Problem 2 might not be an issue. I thought Problem 2 was the cause because the async DMA call and wait were still unstable even after handling Problem 1. I apologize for the confusion. Thank you for your understanding. |
Do you have an example that can consistently reproduce it? |
@Jake-Carter |
@JeonChangmin ah yep, that is a somewhat common issue if the request struct goes out of scope. Thanks for your contributions! |
Description
This pull request contains three commits to ensure that the
req->completeCB
callback ofMXC_SPI_MasterTransactionDMA
is only executed once. Previously, theMXC_SPI_MasterTransactionDMA
function could trigger thereq->completeCB
callback multiple times, which is different from users' expectations (see Background & Problem).Background
The
MXC_SPI_MasterTransactionDMA
function initiates DMA requests up to two times. To executereq->completeCB
only for the last DMA callback, there is an internal DMA callback counting mechanism.Callbacks
req->completeCB: A user-provided callback that should be executed once after
MXC_SPI_MasterTransactionDMA
completes.MXC_SPI_RevA1_DMACallback: A DMA request callback used internally. The
MXC_SPI_MasterTransactionDMA
function initiates up to two DMA requests, and each call triggers theMXC_SPI_RevA1_DMACallback
function.How
MXC_SPI_MasterTransactionDMA
executes thecompleteCB
callback once?states[i].txrx_req
which indicates whether both of Tx/Rx DMA requests are needed. (ref)tx_is_complete
andrx_is_complete
which indicate whether Tx/Rx DMA requests are needed. (ref)tx/rxData != NULL && !tx/rx_is_complete
. (tx, rx)tx/rx_is_complete == true
, runMXC_SPI_RevA1_DMACallback
manually. (ref)MXC_SPI_RevA1_DMACallback
, whenstates[i].txrx_req == true
, executereq->completeCB
for secondMXC_SPI_RevA1_DMACallback
call. Ifstates[i].txrx_req == false
, always callreq->completeCB
. (ref)Problem
states[i].txrx_req
flag and the actual DMA request condition differ.req
:txData == NULL && txLen == 0 && txCnt == 0 && rxData != NULL && rxLen > 0 && rxCnt = 0
txrx_req = false
,tx_is_complete = true
,rx_is_complete = false
tx_is_complete == true
, this code will executeMXC_SPI_RevA1_DMACallback
.MXC_SPI_RevA1_DMACallback
after the DMA request finishes.2. InsideMXC_SPI_RevA1_DMACallback
, the shared variablestates[i].req_done
is used multiple times (usage 1, usage 2). This can triggerreq->completeCB
multiple times when execution of twoMXC_SPI_RevA1_DMACallback
calls overlaps.Commit Details
req->rx/txData != NULL && req->rx/txLen
for both settingtxrx_req
and initiating DMA.* Commit 2: Partially solving Problem 2 * Use the shared variablestates[i].req_done
once inMXC_SPI_RevA1_DMACallback
by utilizing a non-shared local variablereq_done
. * Potential issue: When an inturrept occurs duringreq_done = states[i].req_done++
execution,req->completeCB
may not be executed.* Commit 3: Solving potential issue of commit 2 * Use an atomic operation to fetch and increment the shared variablestates[i].req_done
, ensuring that the operation is safe from interrupt and that thereq->completeCB
callback is executed correctly.Tests
Checklist Before Requesting Review