From 89b90ad98592b1dff3f5004e1ec4087f2e29a792 Mon Sep 17 00:00:00 2001 From: Petr Horacek Date: Mon, 18 Sep 2023 18:10:13 +0200 Subject: [PATCH 1/2] Fix invalid read test logic in sdmmc example The read check is processing a buffer 10 blocks long. The check then reads data from the SD card 10 times to then report the speed of "Read 10 blocks at X bytes/second". The problem is that it actually reads 10*10 blocks. With this patch, read_blocks is used correctly to populate a continuous buffer. Signed-off-by: Petr Horacek --- CHANGELOG.md | 1 + examples/sdmmc.rs | 8 +++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce1f7b10..d6db015b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Update `smoltcp` dependency to `0.9.0` * MSRV increased to 1.65.0 * add `IntoAf` trait to restrict `into_alternate` [#346] +* sdmmc: Fix read speed test. ## [v0.14.0] 2023-03-22 diff --git a/examples/sdmmc.rs b/examples/sdmmc.rs index 2bfd68f8..900cc002 100644 --- a/examples/sdmmc.rs +++ b/examples/sdmmc.rs @@ -145,15 +145,13 @@ fn main() -> ! { info!(""); // Read test - let mut buffer = [0u8; 5120]; + let mut buffer = [0u8; 512 * 10]; cp.DWT.enable_cycle_counter(); let start = pac::DWT::cycle_count(); - for i in 0..10 { - // Read 10 blocks - sdmmc.read_blocks(10 * i, &mut buffer).unwrap(); - } + // Read 10 blocks + sdmmc.read_blocks(0, &mut buffer).unwrap(); let end = pac::DWT::cycle_count(); let duration = (end - start) as f32 / ccdr.clocks.c_ck().raw() as f32; From 7277293ee788f64be7c74387c9574a980ffc8a95 Mon Sep 17 00:00:00 2001 From: Petr Horacek Date: Mon, 18 Sep 2023 18:43:31 +0200 Subject: [PATCH 2/2] Introduce SDMMC write_blocks `write_block` needs to be called for every 512 bytes long block that is to be written to the SD card. Each of these calls requires the overhead of negotiating the write command. With this patch, `write_blocks` is introduced, mirroring the behavior of `read_block` and `read_blocks`. This new method helped me to improve write speed of the original 9492 kB/s to 66252 kB/s. Signed-off-by: Petr Horacek --- CHANGELOG.md | 2 +- examples/sdmmc.rs | 58 +++++++++++++++---------------------- src/sdmmc.rs | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6db015b..26857c2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ * Update `smoltcp` dependency to `0.9.0` * MSRV increased to 1.65.0 * add `IntoAf` trait to restrict `into_alternate` [#346] -* sdmmc: Fix read speed test. +* sdmmc: Introduce `write_blocks` [#453] ## [v0.14.0] 2023-03-22 diff --git a/examples/sdmmc.rs b/examples/sdmmc.rs index 900cc002..73dd3417 100644 --- a/examples/sdmmc.rs +++ b/examples/sdmmc.rs @@ -144,55 +144,45 @@ fn main() -> ! { info!("----------------------"); info!(""); - // Read test - let mut buffer = [0u8; 512 * 10]; - cp.DWT.enable_cycle_counter(); - let start = pac::DWT::cycle_count(); - - // Read 10 blocks - sdmmc.read_blocks(0, &mut buffer).unwrap(); + // Write single block test + let write_buffer = [0x34; 512]; + let start = pac::DWT::cycle_count(); + sdmmc.write_block(0, &write_buffer).unwrap(); let end = pac::DWT::cycle_count(); let duration = (end - start) as f32 / ccdr.clocks.c_ck().raw() as f32; + info!("Wrote single block at {} bytes/s", 512.0 / duration); - info!("Read 10 blocks at {} bytes/s", 5120. / duration); - info!(""); - - let write_buffer = [0x34; 512]; + // Write multiple blocks test + let write_buffer = [0x34; 512 * 16]; let start = pac::DWT::cycle_count(); + sdmmc.write_blocks(0, &write_buffer).unwrap(); + let end = pac::DWT::cycle_count(); + let duration = (end - start) as f32 / ccdr.clocks.c_ck().raw() as f32; + info!("Wrote 16 blocks at {} bytes/s", (512.0 * 16.0) / duration); - for i in 0..10 { - if let Err(err) = sdmmc.write_block(i, &write_buffer) { - info!("Failed to write block {}: {:?}", i, err); - } - } - + // Read single block test + let mut buffer = [0u8; 512]; + let start = pac::DWT::cycle_count(); + sdmmc.read_block(0, &mut buffer).unwrap(); let end = pac::DWT::cycle_count(); let duration = (end - start) as f32 / ccdr.clocks.c_ck().raw() as f32; + info!("Read single block at {} bytes/s", 512.0 / duration); - info!("Wrote 10 blocks at {} bytes/s", 5120. / duration); - info!(""); + // Read multiple blocks test + let mut buffer = [0u8; 512 * 16]; + let start = pac::DWT::cycle_count(); + sdmmc.read_blocks(0, &mut buffer).unwrap(); + let end = pac::DWT::cycle_count(); + let duration = (end - start) as f32 / ccdr.clocks.c_ck().raw() as f32; + info!("Read 16 blocks at {} bytes/s", (512.0 * 16.0) / duration); info!("Verification test..."); - // Write 10 blocks - for i in 0..10 { - if let Err(err) = sdmmc.write_block(i, &write_buffer) { - info!("Failed to write block {}: {:?}", i, err); - } else { - info!("Wrote block {}", i); - } - - // Read back - sdmmc.read_blocks(0, &mut buffer).unwrap(); - } - - // Check the read for byte in buffer.iter() { assert_eq!(*byte, 0x34); } - info!("Verified 10 blocks"); - info!(""); + info!("Verified all blocks"); info!("Done!"); diff --git a/src/sdmmc.rs b/src/sdmmc.rs index 8027ff87..847e327a 100644 --- a/src/sdmmc.rs +++ b/src/sdmmc.rs @@ -295,6 +295,7 @@ pub enum Error { Crc, DataCrcFail, RxOverFlow, + TxUnderFlow, NoCard, BadClock, InvalidConfiguration, @@ -342,6 +343,8 @@ macro_rules! err_from_datapath_sm { return Err(Error::DataCrcFail); } else if $status.rxoverr().bit() { return Err(Error::RxOverFlow); + } else if $status.txunderr().bit() { + return Err(Error::TxUnderFlow); } else if $status.dtimeout().bit() { return Err(Error::Timeout); } @@ -793,6 +796,77 @@ macro_rules! sdmmc { Err(Error::SoftwareTimeout) } + /// Write multiple blocks to card. The length of the buffer + /// must be multiple of 512. + /// + /// `address` is the block address. + pub fn write_blocks( + &mut self, + address: u32, + buffer: &[u8] + ) -> Result<(), Error> { + let _card = self.card()?; + + assert!(buffer.len() % 512 == 0, + "Buffer length must be a multiple of 512"); + let n_blocks = buffer.len() / 512; + + if !self.cmd16_illegal { + self.cmd(common_cmd::set_block_length(512))?; // CMD16 + } + + // Setup write command + self.start_datapath_transfer(512 * n_blocks as u32, 9, Dir::HostToCard); + self.cmd(common_cmd::write_multiple_blocks(address))?; // CMD25 + + let mut i = 0; + let mut status; + while { + status = self.sdmmc.star.read(); + !(status.txunderr().bit() + || status.dcrcfail().bit() + || status.dtimeout().bit() + || status.dataend().bit()) + } { + if status.txfifohe().bit() { + for _ in 0..8 { + let mut wb = [0u8; 4]; + wb.copy_from_slice(&buffer[i..i + 4]); + let word = u32::from_le_bytes(wb); + self.sdmmc.fifor.write(|w| unsafe { w.bits(word) }); + i += 4; + } + } + + if i >= buffer.len() { + break + } + } + + while { + status = self.sdmmc.star.read(); + !(status.txunderr().bit() + || status.dcrcfail().bit() + || status.dtimeout().bit() + || status.dataend().bit()) + } {} + self.cmd(common_cmd::stop_transmission())?; // CMD12 + + err_from_datapath_sm!(status); + self.clear_static_interrupt_flags(); + + let mut timeout: u32 = 0xFFFF_FFFF; + + // Try to read card status (CMD13) + while timeout > 0 { + if self.card_ready()? { + return Ok(()); + } + timeout -= 1; + } + Err(Error::SoftwareTimeout) + } + /// Query the card status (CMD13, returns R1) /// fn read_status(&self) -> Result, Error> {