sdmmc-core 0.5.0

SD/MMC core data structures and algorithms
Documentation
mod block4k;
mod block512;

pub use block4k::*;
pub use block512::*;

/// SD memory card wide bus data width (DAT0-3).
pub const WIDE_BUS_WIDTH: usize = 4;

/// Represents a SD memory card data line (DAT0-3).
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DataLine {
    Dat0 = 0,
    Dat1 = 1,
    Dat2 = 2,
    Dat3 = 3,
}

impl DataLine {
    /// Creates a new [DataLine].
    pub const fn new() -> DataLine {
        DataLine::Dat0
    }
}

impl Default for DataLine {
    fn default() -> Self {
        Self::new()
    }
}

/// Encodes a byte according to the `Wide Bus Encoding`.
///
/// Each byte is split into four two-bit groupings assigned to each [DataLine].
///
/// The encoding is as follows:
///
/// |      | Byte0      | Byte1      | Byte2     | Byte3     |
/// |------|------------|------------|-----------|-----------|
/// | DAT0 | B4B0 << 3  | B4B0 << 1  | B4B0 >> 1 | B4B0 >> 3 |
/// | DAT1 | B5B1 << 2  | B5B1 << 0  | B5B1 >> 2 | B5B1 >> 4 |
/// | DAT2 | B6B2 << 1  | B6B2 >> 1  | B6B2 >> 3 | B6B2 >> 5 |
/// | DAT3 | B7B3 >> 0  | B7B3 >> 2  | B7B3 >> 4 | B7B3 >> 6 |
///
/// This pattern cycles every four bytes of encoded data.
///
/// For details see:
///
/// `Figure 3-9, Section 3.6 in the Physical Layer Simplified Specification Version 9.10`.
#[inline(always)]
pub const fn wide_bus_encode(b: u8, byte_idx: usize, dat: DataLine) -> u8 {
    let off_lo = dat as usize;
    let off_hi = 4 + off_lo;

    wide_bus_shift(
        (b & (1 << off_hi)) | ((b & (1 << off_lo)) << 3),
        byte_idx,
        dat,
    )
}

/// Decodes a [DataLine] byte according to the `Wide Bus Encoding`.
///
/// Each [DataLine] byte includes four two-bit groupings from four bytes of the original data stream.
///
/// The encoding is as follows:
///
/// |      | Byte0 | Byte1 | Byte2 | Byte3 |
/// |------|-------|-------|-------|-------|
/// | DAT0 | B4B0  | B4B0  | B4B0  | B4B0  |
/// | DAT1 | B5B1  | B5B1  | B5B1  | B5B1  |
/// | DAT2 | B6B2  | B6B2  | B6B2  | B6B2  |
/// | DAT3 | B7B3  | B7B3  | B7B3  | B7B3  |
///
/// This pattern cycles every four bytes of encoded data.
///
/// For details see:
///
/// `Figure 3-9, Section 3.6 in the Physical Layer Simplified Specification Version 9.10`.
#[inline(always)]
pub const fn wide_bus_decode(b: u8, byte_idx: usize, dat: DataLine) -> u8 {
    // shift the two-bit grouping by the byte index of the output data stream.
    // every four bytes, the shift resets.
    let c = b >> (6 - ((byte_idx % 4) * 2));

    let shift_lo = dat as usize;
    let shift_hi = 3 + shift_lo;

    ((c & 0b10) << shift_hi) | ((c & 1) << shift_lo)
}

#[inline(always)]
const fn wide_bus_shift(b: u8, byte_idx: usize, dat: DataLine) -> u8 {
    match dat {
        // b4b0[0] | b4b0[1] | b4b0[2] | b4b0[3]
        DataLine::Dat0 => match byte_idx % 4 {
            0 => b << 3,
            1 => b << 1,
            2 => b >> 1,
            _ => b >> 3,
        },
        // b5b1[0] | b5b1[1] | b5b1[2] | b5b1[3]
        DataLine::Dat1 => match byte_idx % 4 {
            0 => b << 2,
            1 => b,
            2 => b >> 2,
            _ => b >> 4,
        },
        // b6b2[0] | b6b2[1] | b6b2[2] | b6b2[3]
        DataLine::Dat2 => match byte_idx % 4 {
            0 => b << 1,
            1 => b >> 1,
            2 => b >> 3,
            _ => b >> 5,
        },
        // b7b3[0] | b7b3[1] | b7b3[2] | b7b3[3]
        DataLine::Dat3 => b >> ((byte_idx % 4) * 2),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_wide_encode() {
        let b = [0b1010_1010, 0b0101_0101, 0b1100_1100, 0b0011_0011];
        // b4b0[0] | b4b0[1] | b4b0[2] | b4b0[3]
        let exp_dat0 = 0b00_11_00_11;
        // b5b1[0] | b5b1[1] | b5b1[2] | b5b1[3]
        let exp_dat1 = 0b11_00_00_11;
        // b6b2[0] | b6b2[1] | b6b2[2] | b6b2[3]
        let exp_dat2 = 0b00_11_11_00;
        // b7b3[0] | b7b3[1] | b7b3[2] | b7b3[3]
        let exp_dat3 = 0b11_00_11_00;

        let [dat0, dat1, dat2, dat3] = b
            .iter()
            .enumerate()
            .fold(0u32, |mut acc, (i, &c)| {
                acc |= wide_bus_encode(c, i, DataLine::Dat0) as u32;
                acc |= (wide_bus_encode(c, i, DataLine::Dat1) as u32) << 8;
                acc |= (wide_bus_encode(c, i, DataLine::Dat2) as u32) << 16;
                acc | ((wide_bus_encode(c, i, DataLine::Dat3) as u32) << 24)
            })
            .to_le_bytes();

        assert_eq!(dat0, exp_dat0);
        assert_eq!(dat1, exp_dat1);
        assert_eq!(dat2, exp_dat2);
        assert_eq!(dat3, exp_dat3);

        let wide_data = [dat0, dat1, dat2, dat3];
        let mut data = [0; 4];
        data.iter_mut().enumerate().for_each(|(i, b)| {
            let j = i.saturating_div(WIDE_BUS_WIDTH);

            *b |= wide_bus_decode(wide_data[j], i, DataLine::Dat0);
            *b |= wide_bus_decode(wide_data[j + 1], i, DataLine::Dat1);
            *b |= wide_bus_decode(wide_data[j + 2], i, DataLine::Dat2);
            *b |= wide_bus_decode(wide_data[j + 3], i, DataLine::Dat3);
        });

        assert_eq!(data, b);
    }
}