Skip to content

Commit a8826d3

Browse files
bors[bot]japarickorken89
committed
Merge #36
36: Memory safe DMA API r=adamgreig a=japaric This is a first draft on building memory safe DMA abstractions. r? @rust-embedded/resources cc @korken89 @jamesmunns Co-authored-by: Jorge Aparicio <jorge@japaric.io> Co-authored-by: Emil Fresk <emil.fresk@gmail.com>
2 parents 2b523c5 + 828228b commit a8826d3

File tree

14 files changed

+1473
-0
lines changed

14 files changed

+1473
-0
lines changed

ci/dma/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/target
2+
.#*
3+
**/*.rs.bk

ci/dma/Cargo.toml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
authors = ["Jorge Aparicio <jorge@japaric.io>"]
3+
edition = "2018"
4+
name = "shared"
5+
version = "0.1.0"
6+
7+
[dependencies]
8+
as-slice = "0.1.0"
9+
pin-utils = "0.1.0-alpha.4"

ci/dma/examples/eight.rs

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
//! Destructors
2+
3+
#![deny(missing_docs, warnings)]
4+
5+
use core::{
6+
hint,
7+
marker::Unpin,
8+
mem,
9+
ops::{Deref, DerefMut},
10+
pin::Pin,
11+
ptr,
12+
sync::atomic::{self, Ordering},
13+
};
14+
15+
use as_slice::{AsMutSlice, AsSlice};
16+
use shared::{Dma1Channel1, USART1_RX, USART1_TX};
17+
18+
/// A DMA transfer
19+
pub struct Transfer<B> {
20+
// NOTE: always `Some` variant
21+
inner: Option<Inner<B>>,
22+
}
23+
24+
// NOTE: previously named `Transfer<B>`
25+
struct Inner<B> {
26+
buffer: Pin<B>,
27+
serial: Serial1,
28+
}
29+
30+
impl<B> Transfer<B> {
31+
/// Blocks until the transfer is done and returns the buffer
32+
pub fn wait(mut self) -> (Pin<B>, Serial1) {
33+
while !self.is_done() {}
34+
35+
atomic::compiler_fence(Ordering::Acquire);
36+
37+
let inner = self
38+
.inner
39+
.take()
40+
.unwrap_or_else(|| unsafe { hint::unreachable_unchecked() });
41+
(inner.buffer, inner.serial)
42+
}
43+
}
44+
45+
impl<B> Drop for Transfer<B> {
46+
fn drop(&mut self) {
47+
if let Some(inner) = self.inner.as_mut() {
48+
// NOTE: this is a volatile write
49+
inner.serial.dma.stop();
50+
51+
// we need a read here to make the Acquire fence effective
52+
// we do *not* need this if `dma.stop` does a RMW operation
53+
unsafe {
54+
ptr::read_volatile(&0);
55+
}
56+
57+
// we need a fence here for the same reason we need one in `Transfer.wait`
58+
atomic::compiler_fence(Ordering::Acquire);
59+
}
60+
}
61+
}
62+
63+
impl Serial1 {
64+
/// Receives data into the given `buffer` until it's filled
65+
///
66+
/// Returns a value that represents the in-progress DMA transfer
67+
pub fn read_exact<B>(mut self, mut buffer: Pin<B>) -> Transfer<B>
68+
where
69+
B: DerefMut + 'static,
70+
B::Target: AsMutSlice<Element = u8> + Unpin,
71+
{
72+
// .. same as before ..
73+
let slice = buffer.as_mut_slice();
74+
let (ptr, len) = (slice.as_mut_ptr(), slice.len());
75+
76+
self.dma.set_source_address(USART1_RX, false);
77+
self.dma.set_destination_address(ptr as usize, true);
78+
self.dma.set_transfer_length(len);
79+
80+
atomic::compiler_fence(Ordering::Release);
81+
self.dma.start();
82+
83+
Transfer {
84+
inner: Some(Inner {
85+
buffer,
86+
serial: self,
87+
}),
88+
}
89+
}
90+
91+
/// Sends out the given `buffer`
92+
///
93+
/// Returns a value that represents the in-progress DMA transfer
94+
pub fn write_all<B>(mut self, buffer: Pin<B>) -> Transfer<B>
95+
where
96+
B: Deref + 'static,
97+
B::Target: AsSlice<Element = u8>,
98+
{
99+
// .. same as before ..
100+
let slice = buffer.as_slice();
101+
let (ptr, len) = (slice.as_ptr(), slice.len());
102+
103+
self.dma.set_destination_address(USART1_TX, false);
104+
self.dma.set_source_address(ptr as usize, true);
105+
self.dma.set_transfer_length(len);
106+
107+
atomic::compiler_fence(Ordering::Release);
108+
self.dma.start();
109+
110+
Transfer {
111+
inner: Some(Inner {
112+
buffer,
113+
serial: self,
114+
}),
115+
}
116+
}
117+
}
118+
119+
#[allow(dead_code, unused_mut, unused_variables)]
120+
fn reuse(serial: Serial1) {
121+
let buf = Pin::new(Box::new([0; 16]));
122+
123+
let t = serial.read_exact(buf); // compiler_fence(Ordering::Release) ▲
124+
125+
// ..
126+
127+
// this stops the DMA transfer and frees memory
128+
mem::drop(t); // compiler_fence(Ordering::Acquire) ▼
129+
130+
// this likely reuses the previous memory allocation
131+
let mut buf = Box::new([0; 16]);
132+
133+
// .. do stuff with `buf` ..
134+
}
135+
136+
// UNCHANGED
137+
138+
fn main() {}
139+
140+
/// A singleton that represents serial port #1
141+
pub struct Serial1 {
142+
dma: Dma1Channel1,
143+
// ..
144+
}
145+
146+
impl<B> Transfer<B> {
147+
/// Returns `true` if the DMA transfer has finished
148+
pub fn is_done(&self) -> bool {
149+
!Dma1Channel1::in_progress()
150+
}
151+
}

ci/dma/examples/five.rs

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//! Generic buffer
2+
3+
#![deny(missing_docs, warnings)]
4+
5+
use core::sync::atomic::{self, Ordering};
6+
7+
use shared::{Dma1Channel1, USART1_RX, USART1_TX};
8+
9+
// as-slice = "0.1.0"
10+
use as_slice::{AsMutSlice, AsSlice};
11+
12+
impl Serial1 {
13+
/// Receives data into the given `buffer` until it's filled
14+
///
15+
/// Returns a value that represents the in-progress DMA transfer
16+
pub fn read_exact<B>(mut self, mut buffer: B) -> Transfer<B>
17+
where
18+
B: AsMutSlice<Element = u8>,
19+
{
20+
// NOTE: added
21+
let slice = buffer.as_mut_slice();
22+
let (ptr, len) = (slice.as_mut_ptr(), slice.len());
23+
24+
self.dma.set_source_address(USART1_RX, false);
25+
26+
// NOTE: tweaked
27+
self.dma.set_destination_address(ptr as usize, true);
28+
self.dma.set_transfer_length(len);
29+
30+
atomic::compiler_fence(Ordering::Release);
31+
self.dma.start();
32+
33+
Transfer {
34+
buffer,
35+
serial: self,
36+
}
37+
}
38+
39+
/// Sends out the given `buffer`
40+
///
41+
/// Returns a value that represents the in-progress DMA transfer
42+
fn write_all<B>(mut self, buffer: B) -> Transfer<B>
43+
where
44+
B: AsSlice<Element = u8>,
45+
{
46+
// NOTE: added
47+
let slice = buffer.as_slice();
48+
let (ptr, len) = (slice.as_ptr(), slice.len());
49+
50+
self.dma.set_destination_address(USART1_TX, false);
51+
52+
// NOTE: tweaked
53+
self.dma.set_source_address(ptr as usize, true);
54+
self.dma.set_transfer_length(len);
55+
56+
atomic::compiler_fence(Ordering::Release);
57+
self.dma.start();
58+
59+
Transfer {
60+
buffer,
61+
serial: self,
62+
}
63+
}
64+
}
65+
66+
#[allow(dead_code, unused_variables)]
67+
fn reuse(serial: Serial1, msg: &'static mut [u8]) {
68+
// send a message
69+
let t1 = serial.write_all(msg);
70+
71+
// ..
72+
73+
let (msg, serial) = t1.wait(); // `msg` is now `&'static [u8]`
74+
75+
msg.reverse();
76+
77+
// now send it in reverse
78+
let t2 = serial.write_all(msg);
79+
80+
// ..
81+
82+
let (buf, serial) = t2.wait();
83+
84+
// ..
85+
}
86+
87+
#[allow(dead_code, unused_variables)]
88+
fn invalidate(serial: Serial1) {
89+
let t = start(serial);
90+
91+
bar();
92+
93+
let (buf, serial) = t.wait();
94+
}
95+
96+
#[inline(never)]
97+
fn start(serial: Serial1) -> Transfer<[u8; 16]> {
98+
// array allocated in this frame
99+
let buffer = [0; 16];
100+
101+
serial.read_exact(buffer)
102+
}
103+
104+
#[allow(unused_mut, unused_variables)]
105+
#[inline(never)]
106+
fn bar() {
107+
// stack variables
108+
let mut x = 0;
109+
let mut y = 0;
110+
111+
// use `x` and `y`
112+
}
113+
114+
// UNCHANGED
115+
116+
fn main() {}
117+
118+
/// A DMA transfer
119+
pub struct Transfer<B> {
120+
buffer: B,
121+
serial: Serial1,
122+
}
123+
124+
/// A singleton that represents serial port #1
125+
pub struct Serial1 {
126+
dma: Dma1Channel1,
127+
// ..
128+
}
129+
130+
impl<B> Transfer<B> {
131+
/// Returns `true` if the DMA transfer has finished
132+
pub fn is_done(&self) -> bool {
133+
!Dma1Channel1::in_progress()
134+
}
135+
136+
/// Blocks until the transfer is done and returns the buffer
137+
pub fn wait(self) -> (B, Serial1) {
138+
while !self.is_done() {}
139+
140+
atomic::compiler_fence(Ordering::Acquire);
141+
142+
(self.buffer, self.serial)
143+
}
144+
}

0 commit comments

Comments
 (0)