Skip to content

Commit be04db0

Browse files
Merge #237
237: DMA: Fix problems with DMASet and timer wrappers r=therealprof a=thalesfragoso The initial idea of the DMASet trait was that people could __unsafely__ implement it themselves from outside of the HAL. Unfortunately, we missed the problem with the fact that tuples are considered a foreign type. This PR drops the tuple approach but still maintains the check that DMASet provides. I also added a fix for #227. Fixes #227 This is also a possible solution to #226, but it takes a different approach, it still forces people (or the HAL) to make a new type if they want a "non-default" memory size (or address). This is more verbose than just dropping the associated type of `PeriAddress` in favor of a generic parameter, but, IMO, the verbosity can be an advantage here. Marking as a draft because I still want to make sure this works as intended and that there isn't another corner case, I also would like the opinion of @albru123 Co-authored-by: Thales Fragoso <[email protected]>
2 parents 1a8b2ce + f718a51 commit be04db0

File tree

4 files changed

+35
-28
lines changed

4 files changed

+35
-28
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
3939
- Added support for hardware-based CRC32 functionality
4040

4141
### Fixed
42+
4243
- Stability fixes related to SD card write
4344
- Fixed issue where timer generated a spurious interrupt after start
45+
- Allow implementations for DMASet from outside the crate [#237]
46+
- DMA: Make it possible to create the wrapper types for the timers [#237]
47+
- DMA: Fix some `compiler_fences` [#237]
48+
- DMA: Fix docs [#237]
49+
50+
[#237]: https://github.com/stm32-rs/stm32f4xx-hal/pull/237
4451

4552
## [v0.8.3] - 2020-06-12
4653

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ rand_core = "0.5.1"
3333
stm32f4 = "0.11"
3434
synopsys-usb-otg = { version = "0.2.0", features = ["cortex-m"], optional = true }
3535
sdio-host = { version = "0.5.0", optional = true }
36-
embedded-dma = "0.1.0"
36+
embedded-dma = "0.1.2"
3737

3838
[dependencies.bare-metal]
3939
version = "0.2.5"

src/dma/mod.rs

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -864,9 +864,8 @@ where
864864
STREAM: Stream,
865865
CHANNEL: Channel,
866866
DIR: Direction,
867-
PERIPHERAL: PeriAddress,
867+
PERIPHERAL: PeriAddress + DMASet<STREAM, CHANNEL, DIR>,
868868
BUF: StaticWriteBuffer<Word = <PERIPHERAL as PeriAddress>::MemSize>,
869-
(STREAM, CHANNEL, PERIPHERAL, DIR): DMASet,
870869
{
871870
/// Applies all fields in DmaConfig.
872871
fn apply_config(&mut self, config: config::DmaConfig) {
@@ -1008,8 +1007,9 @@ where
10081007
}
10091008

10101009
/// Changes the buffer and restarts or continues a double buffer transfer. This must be called
1011-
/// immediately after a transfer complete event. Returns the old buffer together with its
1012-
/// `CurrentBuffer`. If an error occurs, this method will return the new buffer with the error.
1010+
/// immediately after a transfer complete event if using double buffering, otherwise you might
1011+
/// lose data. Returns the old buffer together with its `CurrentBuffer`. If an error occurs,
1012+
/// this method will return the new buffer with the error.
10131013
///
10141014
/// This method will clear the transfer complete flag on entry, it will also clear it again if
10151015
/// an overrun occurs during its execution. Moreover, if an overrun occurs, the stream will be
@@ -1036,7 +1036,10 @@ where
10361036
}
10371037

10381038
if STREAM::current_buffer() == CurrentBuffer::DoubleBuffer {
1039+
// "Preceding reads and writes cannot be moved past subsequent writes"
1040+
compiler_fence(Ordering::Release);
10391041
self.stream.set_memory_address(new_buf_ptr as u32);
1042+
10401043
// Check if an overrun occurred, the buffer address won't be updated in that case
10411044
if self.stream.get_memory_address() != new_buf_ptr as u32 {
10421045
self.stream.clear_transfer_complete_interrupt();
@@ -1051,8 +1054,11 @@ where
10511054
// We always have a buffer, so unwrap can't fail
10521055
return Ok((old_buf.unwrap(), CurrentBuffer::FirstBuffer));
10531056
} else {
1057+
// "Preceding reads and writes cannot be moved past subsequent writes"
1058+
compiler_fence(Ordering::Release);
10541059
self.stream
10551060
.set_memory_double_buffer_address(new_buf_ptr as u32);
1061+
10561062
// Check if an overrun occurred, the buffer address won't be updated in that case
10571063
if self.stream.get_memory_double_buffer_address() != new_buf_ptr as u32 {
10581064
self.stream.clear_transfer_complete_interrupt();
@@ -1081,9 +1087,6 @@ where
10811087
self.stream.set_number_of_transfers(buf_len as u16);
10821088
let old_buf = self.buf.replace(new_buf);
10831089

1084-
// "Preceding reads and writes cannot be moved past subsequent writes"
1085-
compiler_fence(Ordering::Release);
1086-
10871090
unsafe {
10881091
self.stream.enable();
10891092
}
@@ -1155,15 +1158,15 @@ where
11551158
}
11561159

11571160
/// Changes the buffer and restarts or continues a double buffer transfer. This must be called
1158-
/// immediately after a transfer complete event. The closure must return `(BUF, T)` where `BUF`
1159-
/// is the new buffer to be used. This method can be called before the end of an ongoing
1160-
/// transfer only if not using double buffering, in that case, the current transfer will be
1161-
/// canceled and a new one will be started. A `NotReady` error will be returned if this method
1162-
/// is called before the end of a transfer while double buffering and the closure won't be
1163-
/// executed.
1161+
/// immediately after a transfer complete event if using double buffering, otherwise you might
1162+
/// lose data. The closure must return `(BUF, T)` where `BUF` is the new buffer to be used. This
1163+
/// method can be called before the end of an ongoing transfer only if not using double
1164+
/// buffering, in that case, the current transfer will be canceled and a new one will be
1165+
/// started. A `NotReady` error will be returned if this method is called before the end of a
1166+
/// transfer while double buffering and the closure won't be executed.
11641167
///
11651168
/// # Panics
1166-
/// This method will panic when double buffering and one or both of the following conditions
1169+
/// This method will panic when double buffering if one or both of the following conditions
11671170
/// happen:
11681171
///
11691172
/// * The new buffer's length is smaller than the one used in the `init` method.
@@ -1173,10 +1176,6 @@ where
11731176
///
11741177
/// Memory corruption might occur in the previous buffer, the one passed to the closure, if an
11751178
/// overrun occurs in double buffering mode.
1176-
///
1177-
/// # Panics
1178-
///
1179-
/// This method will panic if an overrun is detected while double buffering.
11801179
pub unsafe fn next_transfer_with<F, T>(&mut self, f: F) -> Result<T, DMAError<()>>
11811180
where
11821181
F: FnOnce(BUF, CurrentBuffer) -> (BUF, T),
@@ -1216,6 +1215,8 @@ where
12161215
}
12171216

12181217
if current_buffer == CurrentBuffer::DoubleBuffer {
1218+
// "Preceding reads and writes cannot be moved past subsequent writes"
1219+
compiler_fence(Ordering::Release);
12191220
self.stream.set_memory_address(new_buf_ptr as u32);
12201221

12211222
// Check again if an overrun occurred, the buffer address won't be updated in that
@@ -1230,8 +1231,11 @@ where
12301231
self.buf.replace(new_buf);
12311232
return Ok(r.1);
12321233
} else {
1234+
// "Preceding reads and writes cannot be moved past subsequent writes"
1235+
compiler_fence(Ordering::Release);
12331236
self.stream
12341237
.set_memory_double_buffer_address(new_buf_ptr as u32);
1238+
12351239
if self.stream.get_memory_double_buffer_address() != new_buf_ptr as u32 {
12361240
panic!("Overrun");
12371241
}
@@ -1259,11 +1263,7 @@ where
12591263
self.stream.set_number_of_transfers(buf_len as u16);
12601264
self.buf.replace(new_buf);
12611265

1262-
// "Preceding reads and writes cannot be moved past subsequent writes"
1263-
compiler_fence(Ordering::Release);
1264-
12651266
self.stream.enable();
1266-
12671267
Ok(r.1)
12681268
}
12691269
}

src/dma/traits.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ macro_rules! tim_channels {
285285
($($name:ident),+ $(,)*) => {
286286
$(
287287
/// Wrapper type that indicates which register of the contained timer to use for DMA.
288-
pub struct $name<T> (T);
288+
pub struct $name<T> (pub T);
289289

290290
impl<T> Deref for $name<T> {
291291
type Target = T;
@@ -305,19 +305,19 @@ pub trait Channel: Bits<u8> {
305305
fn new() -> Self;
306306
}
307307

308-
/// Trait to mark a set of Stream, Channel, Peripheral and Direction as correct together.
308+
/// Trait to mark a set of Stream, Channel and Direction for a Peripheral as correct together.
309309
///
310310
/// # Safety
311311
///
312312
/// Memory corruption might occur if this trait is implemented for an invalid combination.
313-
pub unsafe trait DMASet {}
313+
pub unsafe trait DMASet<STREAM, CHANNEL, DIRECTION> {}
314314

315315
tim_channels!(CCR1, CCR2, CCR3, CCR4, DMAR, ARR);
316316

317317
macro_rules! dma_map {
318-
($(($Stream:ty, $channel:ty, $Peripheral:ty, $dir:ty)),+ $(,)*) => {
318+
($(($Stream:ty, $Channel:ty, $Peripheral:ty, $Dir:ty)),+ $(,)*) => {
319319
$(
320-
unsafe impl DMASet for ($Stream, $channel, $Peripheral, $dir) {}
320+
unsafe impl DMASet<$Stream, $Channel, $Dir> for $Peripheral {}
321321
)+
322322
};
323323
}

0 commit comments

Comments
 (0)