Skip to content
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

Circular DMA does not handle wrap around correctly #2021

Open
Dominaezzz opened this issue Aug 28, 2024 · 1 comment
Open

Circular DMA does not handle wrap around correctly #2021

Dominaezzz opened this issue Aug 28, 2024 · 1 comment
Labels
peripheral:dma DMA Peripheral

Comments

@Dominaezzz
Copy link
Collaborator

Someone brought this up in the matrix room but circular DMA transfers don't handle wrap around properly. Just thought I'd create an issue so other people know about it as well.

In the RX case, wrap around happens when the user doesn't read fast enough and the DMA runs out of space to write data to, causing the DMA to go through all the descriptors and coming back to the start (over and over again, since we don't enable check_owner).
In the TX case, it happens when the user doesn't write fast enough, and the DMA runs out of data to transmit. In practice, the DMA just resends old data over and over again, since we don't enable check_owner.

Once this happens, the user has to drop and restart the transfer, but the current code doesn't do this.
Each peripheral will have its quirks.

This is the code that needs updating on the RX side.

esp-hal/esp-hal/src/dma/mod.rs

Lines 1046 to 1065 in 61bb240

pub(crate) fn update(&mut self) {
if self.last_seen_handled_descriptor_ptr.is_null() {
// initially start at last descriptor (so that next will be the first
// descriptor)
self.last_seen_handled_descriptor_ptr = self.last_descr_ptr;
}
let mut current_in_descr_ptr =
unsafe { self.last_seen_handled_descriptor_ptr.read_volatile() }.next;
let mut current_in_descr = unsafe { current_in_descr_ptr.read_volatile() };
while current_in_descr.owner() == Owner::Cpu {
self.available += current_in_descr.len();
self.last_seen_handled_descriptor_ptr = current_in_descr_ptr;
current_in_descr_ptr =
unsafe { self.last_seen_handled_descriptor_ptr.read_volatile() }.next;
current_in_descr = unsafe { current_in_descr_ptr.read_volatile() };
}
}

There are a couple of ways to fix this, using the check_owner bit, using the IN_DSCR_EMPTY interrupt, or better bookkeeping.
I'm not sure which way is right, and it's likely the answer will depend on the peripheral (and use case) in question.

@Dominaezzz
Copy link
Collaborator Author

cc @Noxime

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
peripheral:dma DMA Peripheral
Projects
Status: Todo
Development

No branches or pull requests

2 participants