From c41dabdc5c6404efc807cf2b734d778fba37134b Mon Sep 17 00:00:00 2001 From: Petr Horacek Date: Mon, 18 Sep 2023 18:43:31 +0200 Subject: [PATCH] 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 | 3 +- examples/sdmmc.rs | 58 ++++++++++++++++---------------------- src/sdmmc.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6db015b..966a6520 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ * 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` +* sdmmc: Fix read speed test ## [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..6070a68d 100644 --- a/src/sdmmc.rs +++ b/src/sdmmc.rs @@ -793,6 +793,78 @@ 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 + } + } + + // TODO + 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> {