Skip to content

Commit 8dfd511

Browse files
authored
Merge pull request #142 from bbrown1867/support-non-blocking-qspi-dma
Make `qspi.rs` DMA APIs Non-Blocking
2 parents 1e478dc + 0671321 commit 8dfd511

File tree

3 files changed

+159
-124
lines changed

3 files changed

+159
-124
lines changed

examples/stm32f7disco-qspi-flash/main.rs

Lines changed: 65 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
extern crate panic_semihosting;
1111

12+
use core::pin::Pin;
1213
use cortex_m_rt::entry;
1314
use cortex_m_semihosting::hprintln;
1415
use stm32f7xx_hal::{
@@ -57,7 +58,7 @@ fn main() -> ! {
5758
}
5859

5960
fn memory_example_polling(mt25q: &mut mt25q::Mt25q) {
60-
// Create a set of buffers for a memory at address `ADDR` of size `LEN` bytes
61+
// Create a set of buffers in RAM that will mirror flash memory at address `ADDR` of size `LEN`
6162
const ADDR: u32 = 0x7003;
6263
const LEN: usize = 1035;
6364
let mut read_buffer: [u8; LEN] = [0; LEN];
@@ -66,7 +67,10 @@ fn memory_example_polling(mt25q: &mut mt25q::Mt25q) {
6667
write_buffer[i] = i as u8;
6768
}
6869

69-
// Test erase + read
70+
///////////////////////
71+
// Test erase + read //
72+
///////////////////////
73+
7074
let (num_erase, addr_erase) = mt25q.erase(ADDR, LEN);
7175
assert!(LEN <= num_erase as usize);
7276
assert!(addr_erase <= ADDR);
@@ -76,7 +80,10 @@ fn memory_example_polling(mt25q: &mut mt25q::Mt25q) {
7680
assert!(read_buffer[i] == 0xFF);
7781
}
7882

79-
// Test write + read
83+
///////////////////////
84+
// Test write + read //
85+
///////////////////////
86+
8087
mt25q.write(ADDR, &mut write_buffer, LEN);
8188
mt25q.read(&mut read_buffer, ADDR, LEN);
8289
for i in 0..LEN {
@@ -98,36 +105,72 @@ fn memory_example_dma(
98105
dma: &Handle<DMA2, state::Enabled>,
99106
stream: Stream7<DMA2>,
100107
) {
101-
// Create a set of buffers for a memory at address `ADDR` of size `LEN` bytes
108+
// Create a buffer in RAM that will mirror flash memory at address `ADDR` of size `LEN`
102109
const ADDR: u32 = 0x7000;
103-
const LEN: usize = 4096;
104-
let mut read_buffer: [u8; LEN] = [0; LEN];
105-
let mut write_buffer: [u8; LEN] = [0; LEN];
106-
for i in 0..LEN {
107-
write_buffer[i] = i as u8;
110+
const LEN: usize = mt25q::SUBSECTOR_SIZE as usize;
111+
static mut READ_BUFFER: [u8; LEN] = [0; LEN];
112+
113+
// Temporary working memory
114+
const PAGE_SIZE: usize = mt25q::PAGE_SIZE as usize;
115+
static mut PAGE_BUFFER: [u8; PAGE_SIZE] = [0; PAGE_SIZE];
116+
117+
// Set the page buffer to some random data
118+
unsafe {
119+
for i in 0..PAGE_SIZE {
120+
PAGE_BUFFER[i] = i as u8;
121+
}
108122
}
109123

110-
// Test erase + read
124+
// Create pinned versions for DMA transfers
125+
let mut stream = stream;
126+
let mut read_buffer = unsafe { Pin::new(&mut READ_BUFFER) };
127+
let mut page_buffer = unsafe { Pin::new(&mut PAGE_BUFFER) };
128+
129+
///////////////////////
130+
// Test erase + read //
131+
///////////////////////
132+
111133
let (num_erase, addr_erase) = mt25q.erase(ADDR, LEN);
112134
assert!(LEN <= num_erase as usize);
113135
assert!(addr_erase <= ADDR);
114136

115-
let stream = mt25q.read_dma(&mut read_buffer, ADDR, LEN, dma, stream);
137+
let read_resources = mt25q.read_dma(read_buffer, ADDR, LEN, dma, stream);
116138
for i in 0..LEN {
117-
assert!(read_buffer[i] == 0xFF);
139+
assert!(read_resources.buffer[i] == 0xFF);
140+
}
141+
142+
stream = read_resources.stream;
143+
read_buffer = read_resources.buffer;
144+
145+
///////////////////////
146+
// Test write + read //
147+
///////////////////////
148+
149+
// For writing with DMA, caller must break the writes down into flash memory pages.
150+
// Note that since ADDR is page aligned and LEN is a multiple of the page size some
151+
// page boundry math is ignored here. See the polling write function in the `mt25q`
152+
// driver for a more advanced example dealing with unaligned page boundries.
153+
let mut curr_addr: u32 = ADDR;
154+
let mut bytes_written: usize = 0;
155+
while bytes_written < LEN {
156+
let write_resources = mt25q.write_page_dma(curr_addr, page_buffer, PAGE_SIZE, dma, stream);
157+
stream = write_resources.stream;
158+
page_buffer = write_resources.buffer;
159+
bytes_written += PAGE_SIZE;
160+
curr_addr += PAGE_SIZE as u32;
118161
}
119162

120-
// Test write + read
121-
let stream = mt25q.write_dma(ADDR, &mut write_buffer, LEN, dma, stream);
122-
mt25q.read_dma(&mut read_buffer, ADDR, LEN, dma, stream);
163+
mt25q.read_dma(read_buffer, ADDR, LEN, dma, stream);
123164
for i in 0..LEN {
124-
if write_buffer[i] != read_buffer[i] {
125-
panic!(
126-
"Error: Mismatch at address {:X}. Expected {:X} but read {:X}",
127-
ADDR + i as u32,
128-
write_buffer[i],
129-
read_buffer[i]
130-
);
165+
unsafe {
166+
if PAGE_BUFFER[i % PAGE_SIZE] != READ_BUFFER[i] {
167+
panic!(
168+
"Error: Mismatch at address {:X}. Expected {:X} but read {:X}",
169+
ADDR + i as u32,
170+
PAGE_BUFFER[i % PAGE_SIZE],
171+
READ_BUFFER[i]
172+
);
173+
}
131174
}
132175
}
133176

examples/stm32f7disco-qspi-flash/mt25q.rs

Lines changed: 66 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
//! Basic driver for the MT25QL128ABA. Demonstrates how a flash driver can be
2-
//! written using the QSPI driver in the HAL.
1+
//! Basic driver for the MT25QL128ABA. Demonstrates how the QSPI HAL
2+
//! driver can be used to create a driver for a QSPI flash memory chip.
33
4+
use as_slice::AsSlice;
5+
use core::ops::Deref;
6+
use core::pin::Pin;
47
use stm32f7xx_hal::{
5-
dma::{Handle, Stream7},
8+
dma::{Handle, Stream7, TransferResources},
69
gpio::{GpioExt, Speed},
710
pac::{DMA2, GPIOB, GPIOD, GPIOE, QUADSPI, RCC},
8-
qspi::{Qspi, QspiTransaction, QspiWidth},
11+
qspi::{Qspi, QspiTransaction, QspiWidth, RxTx},
912
state,
1013
};
1114

@@ -19,8 +22,8 @@ const ID_MANF: u8 = 0x20;
1922
const ID_MEMT: u8 = 0xBA;
2023
const ID_MEMC: u8 = 0x18;
2124
const MAX_ADDR: u32 = 0x00FF_FFFF;
22-
const SUBSECTOR_SIZE: u32 = 4096;
23-
const PAGE_SIZE: u32 = 256;
25+
pub const SUBSECTOR_SIZE: u32 = 4096;
26+
pub const PAGE_SIZE: u32 = 256;
2427

2528
pub struct Mt25q {
2629
driver: Qspi,
@@ -86,22 +89,29 @@ impl Mt25q {
8689
};
8790

8891
let mut id = [0, 0, 0];
89-
self.driver.polling_read(&mut id, transaction).unwrap();
92+
self.driver.read(&mut id, transaction).unwrap();
9093

9194
if id[0] != ID_MANF || id[1] != ID_MEMT || id[2] != ID_MEMC {
9295
panic!("Error: Device ID mismatch!");
9396
}
9497
}
9598

9699
/// Blocking DMA read.
97-
pub fn read_dma(
100+
///
101+
/// NOTE: This function could be easily modified for non-blocking by returning
102+
/// the handle to the ongoing DMA `Transfer`.
103+
pub fn read_dma<B>(
98104
&mut self,
99-
dst: &[u8],
105+
dst: Pin<B>,
100106
src: u32,
101107
len: usize,
102108
dma: &Handle<DMA2, state::Enabled>,
103109
stream: Stream7<DMA2>,
104-
) -> Stream7<DMA2> {
110+
) -> TransferResources<RxTx<QUADSPI>, B>
111+
where
112+
B: Deref + 'static,
113+
B::Target: AsSlice<Element = u8>,
114+
{
105115
assert!(len > 0);
106116
assert!(src + (len as u32) <= MAX_ADDR);
107117

@@ -115,61 +125,63 @@ impl Mt25q {
115125
data_len: Some(len),
116126
};
117127

118-
self.driver.dma_read(dst, transaction, dma, stream).unwrap()
128+
// Start the DMA read
129+
let rx_transfer = self.driver.read_all(dst, transaction, dma, stream).unwrap();
130+
131+
// Wait for DMA read to finish
132+
rx_transfer.wait(&dma).unwrap()
119133
}
120134

121-
/// Blocking DMA write.
122-
pub fn write_dma(
135+
/// Blocking DMA page write.
136+
///
137+
/// NOTE: This function could be easily modified for non-blocking by returning
138+
/// the handle to the ongoing DMA `Transfer`. However for this flash chip it
139+
/// would not be very useful, since writes are limited by page size. The only
140+
/// way to acheive non-blocking writes would be to use interrupts to reload DMA
141+
/// for each page.
142+
pub fn write_page_dma<B>(
123143
&mut self,
124144
dst: u32,
125-
src: &[u8],
145+
src: Pin<B>,
126146
len: usize,
127147
dma: &Handle<DMA2, state::Enabled>,
128148
stream: Stream7<DMA2>,
129-
) -> Stream7<DMA2> {
149+
) -> TransferResources<RxTx<QUADSPI>, B>
150+
where
151+
B: Deref + 'static,
152+
B::Target: AsSlice<Element = u8>,
153+
{
130154
assert!(len > 0);
131155
assert!(dst + (len as u32) <= MAX_ADDR);
132156

133-
let mut outer_idx: usize = 0;
134-
let mut curr_addr: u32 = dst;
135-
let mut curr_len: usize = len;
136-
let mut curr_stream = stream;
137-
138157
// Constraints for writes: (1) Must be <= 256 bytes, (2) must not cross a page boundry
139-
while curr_len > 0 {
140-
self.write_enable();
158+
assert!(len as u32 <= PAGE_SIZE);
159+
assert!(dst % PAGE_SIZE == 0);
141160

142-
let start_page = curr_addr - (curr_addr % PAGE_SIZE);
143-
let end_page = start_page + PAGE_SIZE;
144-
let size: usize = if curr_addr + (curr_len as u32) > end_page {
145-
(end_page - curr_addr) as usize
146-
} else {
147-
curr_len
148-
};
161+
self.write_enable();
149162

150-
let transaction = QspiTransaction {
151-
iwidth: QspiWidth::SING,
152-
awidth: QspiWidth::SING,
153-
dwidth: QspiWidth::QUAD,
154-
instruction: CMD_MEM_PROGRAM,
155-
address: Some(curr_addr & MAX_ADDR),
156-
dummy: 0,
157-
data_len: Some(size),
158-
};
163+
let transaction = QspiTransaction {
164+
iwidth: QspiWidth::SING,
165+
awidth: QspiWidth::SING,
166+
dwidth: QspiWidth::QUAD,
167+
instruction: CMD_MEM_PROGRAM,
168+
address: Some(dst & MAX_ADDR),
169+
dummy: 0,
170+
data_len: Some(len),
171+
};
159172

160-
let buf = unsafe { core::slice::from_raw_parts(&src[outer_idx] as *const u8, size) };
161-
curr_stream = self
162-
.driver
163-
.dma_write(buf, transaction, dma, curr_stream)
164-
.unwrap();
165-
self.poll_status();
173+
// Start the DMA write
174+
let tx_transfer = self
175+
.driver
176+
.write_all(src, transaction, dma, stream)
177+
.unwrap();
166178

167-
outer_idx += size;
168-
curr_addr += size as u32;
169-
curr_len -= size;
170-
}
179+
// Wait for DMA write to finish
180+
let resources = tx_transfer.wait(&dma).unwrap();
181+
182+
self.poll_status();
171183

172-
curr_stream
184+
resources
173185
}
174186

175187
/// Blocking polling read.
@@ -187,7 +199,7 @@ impl Mt25q {
187199
data_len: Some(len),
188200
};
189201

190-
self.driver.polling_read(dst, transaction).unwrap();
202+
self.driver.read(dst, transaction).unwrap();
191203
}
192204

193205
/// Blocking polling write.
@@ -222,7 +234,7 @@ impl Mt25q {
222234
};
223235

224236
let buf = unsafe { core::slice::from_raw_parts(&src[outer_idx] as *const u8, size) };
225-
self.driver.polling_write(buf, transaction).unwrap();
237+
self.driver.write(buf, transaction).unwrap();
226238
self.poll_status();
227239

228240
outer_idx += size;
@@ -257,7 +269,7 @@ impl Mt25q {
257269
};
258270

259271
let mut dummy = [0];
260-
self.driver.polling_read(&mut dummy, transaction).unwrap();
272+
self.driver.read(&mut dummy, transaction).unwrap();
261273

262274
num_erased_bytes += SUBSECTOR_SIZE;
263275
addr += SUBSECTOR_SIZE;
@@ -282,9 +294,7 @@ impl Mt25q {
282294

283295
let mut status = [0];
284296
while status[0] & 0x80 == 0 {
285-
self.driver
286-
.polling_read(&mut status, transaction.clone())
287-
.unwrap();
297+
self.driver.read(&mut status, transaction.clone()).unwrap();
288298
}
289299
}
290300

@@ -301,6 +311,6 @@ impl Mt25q {
301311
};
302312

303313
let mut dummy = [0];
304-
self.driver.polling_read(&mut dummy, transaction).unwrap()
314+
self.driver.read(&mut dummy, transaction).unwrap()
305315
}
306316
}

0 commit comments

Comments
 (0)