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

Implement DMA based on embedded-dma traits #153

Merged
merged 49 commits into from
Feb 13, 2021
Merged
Changes from 1 commit
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
876249d
Add DMA and examples for DMA1/DMA2
richardeoin Oct 17, 2020
b111bee
Mark AXISRAM as NOLOAD (as well as SRAM3/4)
richardeoin Oct 17, 2020
0cca09e
Add BDMA, work in progress
richardeoin Oct 18, 2020
e74deb2
Experiment with implementing TargetAddress on a HAL structure
richardeoin Oct 18, 2020
b178f3a
Experiment with DMA methods for I2C, add example with I2C4 and BDMA
richardeoin Oct 18, 2020
a1a719c
Tidy clippy warnings
richardeoin Oct 18, 2020
a8d58b5
Refactor I2C to expose infallible methods for parts of transcations
richardeoin Oct 21, 2020
91de4ee
Tidy more clippy lints
richardeoin Oct 21, 2020
a86c5b6
Add DMBs before and after the stream read/writes normal memory
richardeoin Oct 22, 2020
5ce5fc9
Refactor Stream trait into two parts, add config struct for BDMA
richardeoin Oct 23, 2020
7d5d843
Remove unnecessary trait bound
richardeoin Oct 23, 2020
af88ee0
Implement TargetAddress directly on the PAC/HAL struct itself
richardeoin Oct 27, 2020
d226891
Fix a few typos in the sai module's comments.
antoinevg Oct 31, 2020
a1b9a27
Add enable_dma() method to SAI peripheral
antoinevg Oct 31, 2020
5d2b2a8
Implement peripheral_target_address macro for SAI1-4
antoinevg Oct 31, 2020
e2db677
Add a second iteration of the SAI passthrough example which uses the …
antoinevg Oct 31, 2020
7ddb8a6
Merge pull request #162 from antoinevg/sai_dma_passthru_example_v0.2
richardeoin Nov 1, 2020
8d0605e
Updating DMA macro for SPI types
ryan-summers Nov 3, 2020
0c37c70
Formatting
ryan-summers Nov 3, 2020
f14dd61
Merge pull request #164 from quartiq/rs/spi-dma-types
richardeoin Nov 3, 2020
0575235
rustfmt
richardeoin Nov 3, 2020
624c87e
Add BDMA2 support for RM0455 parts
richardeoin Nov 3, 2020
e694353
Merge remote-tracking branch 'st/master' into feature/dma-rtic-example
ryan-summers Nov 7, 2020
6c738b8
Adding SPI DMA example using RTIC
ryan-summers Nov 7, 2020
d8cb6fa
Updating sample after testing
ryan-summers Nov 8, 2020
62f7fb7
Cleaning up example
ryan-summers Nov 11, 2020
002123c
Fixing clippy
ryan-summers Nov 11, 2020
0bfeeca
Merge pull request #167 from quartiq/feature/dma-rtic-example
richardeoin Nov 11, 2020
d77e835
dma: streamline double buffer address handling
jordens Nov 27, 2020
5585210
dma: StaticWriteBuffer
jordens Nov 27, 2020
301e16b
dma: buf array
jordens Nov 27, 2020
4425323
dma: streamline double buffer
jordens Nov 27, 2020
be1573f
dma: usize for addresses
jordens Nov 28, 2020
2448dba
dma: rewrite next_transfer_with and next_transfer
jordens Dec 1, 2020
f31e7ff
dma: fence() and some cleanups/comments
jordens Dec 2, 2020
0f22d94
dma: update bdma
jordens Dec 2, 2020
131340e
dma: spell
jordens Dec 2, 2020
e70a787
Adding return of remaining transfers
ryan-summers Dec 7, 2020
5f97920
dma: trait docs fixes
jordens Dec 9, 2020
8483294
Updating docs, updating next_transfer_with
ryan-summers Dec 15, 2020
25ee0f3
Merge pull request #173 from quartiq/feature/number-of-transfers
ryan-summers Jan 4, 2021
2954e70
Panic if the transfer length exceeds the hardware capability
richardeoin Dec 19, 2020
7b62474
Merge remote-tracking branch 'quartiq/feature/dma-buffer-swap-logic' …
richardeoin Jan 5, 2021
3da22d4
Add example usage of `next_transfer_with` to the spi-dma example
richardeoin Jan 5, 2021
db8e3a1
[mdma] preparation for MDMA
richardeoin Jan 7, 2021
b40f13e
Implement DMA for constant source buffers
richardeoin Jan 7, 2021
2809fef
Add allow for clippy type_complexity with 5th type parameter
richardeoin Jan 7, 2021
1ac12e1
Merge pull request #178 from richardeoin/dma-const-buf1
richardeoin Jan 13, 2021
2b8a04c
Merge pull request #183 from stm32-rs/master
richardeoin Jan 31, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
dma: fence() and some cleanups/comments
  • Loading branch information
jordens committed Dec 2, 2020
commit f31e7ff761e88351630eadb698027cddcd2db7c1
105 changes: 54 additions & 51 deletions src/dma/mod.rs
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ use core::{
mem,
ops::Not,
ptr,
sync::atomic::{compiler_fence, Ordering},
sync::atomic::{fence, Ordering},
};
use embedded_dma::StaticWriteBuffer;

@@ -421,16 +421,9 @@ where
where
F: FnOnce(&mut PERIPHERAL),
{
// "Preceding reads and writes cannot be moved past subsequent writes"
compiler_fence(Ordering::Release);

// Ensure that all transfers to normal memory complete before
// subsequent memory transfers.
//
// The memory buffer is almost certainly in normal memory, so we ensure
// that all transfers there complete before proceeding to enable
// the stream
cortex_m::asm::dmb();
// Preserve the instruction and bus ordering of preceding buffer access
// to the subsequent access by the DMA peripheral due to enabling it.
fence(Ordering::SeqCst);

unsafe {
self.stream.enable();
@@ -465,22 +458,25 @@ where
/// * This must be called immediately after a transfer complete
/// event to ensure no repeated transfers into/out of the same buffer.
/// * A `NotReady` error will be returned if this method is called
/// before the end of a transfer and the closure won't be executed.
/// before a transfer is completed and the closure won't be executed.
/// * A `SmallBuffer` error will be returned if the size of the buffer
/// returned by the closure does not match the current transfer size.
/// * The DMA may run into the poison address and throw an error if
/// any of following conditions happen:
/// * `SmallBuffer` error.
/// * The closure `f` takes too long to return and a buffer overrun occurs.
/// * The DMA may overrun and access the poison address causing a bus error
/// and disabling of the stream if any of following conditions happen:
/// * `SmallBuffer` error
/// * The closure `f` takes too long to return
/// * If the buffer address poisoning itself fails because the DMA has overrun,
/// the closure will still be called and the buffer address is updated but
/// the DMA stream will error (TEIF) and disable itself.
pub fn next_transfer_with<F, T>(&mut self, func: F) -> Result<T, DMAError>
where
F: FnOnce(BUF, CurrentBuffer) -> (BUF, T),
{
let (double, inactive) = match STREAM::get_inactive_buffer() {
let (single_buffer, inactive) = match STREAM::get_inactive_buffer() {
None => {
// Single buffer mode
self.stream.disable();
(false, CurrentBuffer::Buffer0)
(true, CurrentBuffer::Buffer0)
}
Some(inactive) => {
// Double buffer mode
@@ -489,25 +485,26 @@ where
return Err(DMAError::NotReady);
}
// Poison the peripheral's inactive memory address to get a memory
// error instead of potentially silent corruption if the DMA peripheral
// wins the race to the inactive buffer.
// NOTE(safety): Memory corruption will still occur in the "incactive"
// buffer if an DMA overrun occurs between reading the CT bit and poisoning
// the address.
// error instead of potentially silent corruption.
// If DMA wins the race (overrun) to the inactive buffer
// between reading the CT bit and poisoning the inactive address, this
// write will fail and lead to a transfer error (TEIF) and disable
// the stream.
// If DMA wins the race by the time we write the new valid addressi
// (below), it gets a bus error and errors/stops.
unsafe {
self.stream.set_memory_address(inactive, 0xffff_ffffusize);
}
(true, inactive)
(false, inactive)
}
};

// Protect the instruction sequence of preceding DMA disable/inactivity
// verification/poisoning and subsequent (old completed) buffer content
// access.
compiler_fence(Ordering::SeqCst);
// Also protect the corresponding data access sequence.
// NOTE: The data cache also needs to be flushed (if used).
cortex_m::asm::dmb();
// Cortex-M7: Also protect the corresponding data access sequence.
// NOTE: The data cache also needs to be flushed (if enabled).
fence(Ordering::SeqCst);

// This buffer is inactive now and can be accessed.
// NOTE(panic): We always hold ownership in lieu of the DMA peripheral.
@@ -523,27 +520,30 @@ where
self.buf[inactive as usize].replace(buf);

// Protect the instruction sequence of preceding (new) buffer content access
// and subsequent DMA enable/address update.
compiler_fence(Ordering::SeqCst);
// Also protect the corresponding data access sequence.
// NOTE: The data cache also needs to be flushed (if used).
cortex_m::asm::dmb();
// and subsequent DMA enable/address update. See the matching fence() above.
fence(Ordering::SeqCst);

if !double {
if single_buffer {
// Set length before the writing the new valid address.
self.stream.set_number_of_transfers(buf_len as u16);
} else if buf_len != usize::from(self.transfer_length) {
// We can't change the transfer length while double buffering
return Err(DMAError::SmallBuffer);
}

// NOTE(double buffer mode):
// Up to here, if the DMA starts accessing the poisoned inactive buffer (overrun)
// this will lead to a bus error and disable DMA.
// This write can not fail since the DMA is not accessing the corresponding buffer
// yet or has hit the poison address and errored disabling itself.
unsafe {
self.stream.set_memory_address(inactive, buf_ptr as usize);
}

// Acknowledge the TCIF.
self.stream.clear_transfer_complete_interrupt();

if !double {
if single_buffer {
unsafe {
self.stream.enable();
}
@@ -555,31 +555,37 @@ where
/// Changes the buffer and restarts or continues a double buffer
/// transfer. This must be called immediately after a transfer complete
/// event. Returns the old buffer together with its `CurrentBuffer`. If an
/// error occurs, this method will return the new buffer with the error.
/// error occurs, this method will return the old or new buffer with the error.
///
/// This method will clear the transfer complete flag on entry, it will also
/// clear it again if an overrun occurs during its execution. Moreover, if
/// This method will clear the transfer complete flag. Moreover, if
/// an overrun occurs, the stream will be disabled and the transfer error
/// flag will be set. This method can be called before the end of an ongoing
/// transfer only if not using double buffering, in that case, the current
/// transfer will be canceled and a new one will be started. A `NotReady`
/// error will be returned if this method is called before the end of a
/// transfer while double buffering.
/// error together with the new buffer will be returned if this method is called
/// before the end of a transfer while double buffering. A `SmallBuffer` error
/// together with the old buffer will be returned if the new buffer size does not
/// match the ongoing transfer size.
pub fn next_transfer(
&mut self,
new_buf: BUF,
) -> Result<(BUF, CurrentBuffer), DMAError> {
self.next_transfer_with(|old, current| (new_buf, (old, current)))
let mut buf = new_buf;
let current = self.next_transfer_with(|mut old, current| {
core::mem::swap(&mut old, &mut buf);
(old, current)
})?;
// TODO: return buf on Err
Ok((buf, current))
}

/// Stops the stream and returns the underlying resources.
pub fn free(mut self) -> (STREAM, PERIPHERAL, BUF, Option<BUF>) {
self.stream.disable();
compiler_fence(Ordering::SeqCst);

// Ensure that the transfer to device memory that disables the stream is
// complete before subsequent memory transfers
cortex_m::asm::dmb();
// Protect the instruction and bus sequence of the preceding disable and
// the subsequent buffer access.
fence(Ordering::SeqCst);

self.stream.clear_interrupts();

@@ -650,11 +656,8 @@ where
fn drop(&mut self) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In circular transfers, we may not be actually using the transfer to get data, but rather to just manage state. In these cases, we don't actually need to keep the Transfer object around and may want to just let it drop out of scope silently. As-is, this automatically disables the circular transfer, which may not be desirable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An example is using a circular transfer to write a single word into the SPI TXFIFO to trigger a transaction. The transfer may be triggered by a timer overflow, which results in a periodic SPI read. The actual transfer and TX contents are don't care, but the transfer should continue on infinitely and there's no value of keeping the Transfer around since it is never modified.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've proposed one way of addressing this in #178

self.stream.disable();

// "No re-ordering of reads and writes across this point is allowed"
compiler_fence(Ordering::SeqCst);

// Ensure that the transfer to device memory that disables the stream is
// complete before subsequent memory transfers
cortex_m::asm::dmb();
// Protect the instruction and bus sequence of the preceding disable and
// the subsequent buffer access.
fence(Ordering::SeqCst);
}
}