spi_memory_async/
series25.rs

1//! Driver for 25-series SPI Flash and EEPROM chips.
2
3use crate::{utils::HexSlice, Error};
4use bitflags::bitflags;
5use core::marker::PhantomData;
6pub use core::task::Poll;
7use core::{convert::TryInto, fmt};
8pub use embedded_hal_async::{
9    delay::DelayNs,
10    spi::{Operation, SpiDevice},
11};
12pub use embedded_storage_async;
13
14#[derive(defmt::Format)]
15/// 3-Byte JEDEC manufacturer and device identification.
16pub struct Identification {
17    /// Data collected
18    /// - First byte is the manufacturer's ID code from eg JEDEC Publication No. 106AJ
19    /// - The trailing bytes are a manufacturer-specific device ID.
20    bytes: [u8; 3],
21
22    /// The number of continuations that precede the main manufacturer ID
23    continuations: u8,
24}
25
26impl Identification {
27    /// Build an Identification from JEDEC ID bytes.
28    pub fn from_jedec_id(buf: &[u8]) -> Identification {
29        // Example response for Cypress part FM25V02A:
30        // 7F 7F 7F 7F 7F 7F C2 22 08  (9 bytes)
31        // 0x7F is a "continuation code", not part of the core manufacturer ID
32        // 0xC2 is the company identifier for Cypress (Ramtron)
33
34        // Find the end of the continuation bytes (0x7F)
35        let mut start_idx = 0;
36        for (i, item) in buf.iter().enumerate().take(buf.len() - 2) {
37            if *item != 0x7F {
38                start_idx = i;
39                break;
40            }
41        }
42
43        Self {
44            bytes: [buf[start_idx], buf[start_idx + 1], buf[start_idx + 2]],
45            continuations: start_idx as u8,
46        }
47    }
48
49    /// The JEDEC manufacturer code for this chip.
50    pub fn mfr_code(&self) -> u8 {
51        self.bytes[0]
52    }
53
54    /// The manufacturer-specific device ID for this chip.
55    pub fn device_id(&self) -> &[u8] {
56        self.bytes[1..].as_ref()
57    }
58
59    /// Number of continuation codes in this chip ID.
60    ///
61    /// For example the ARM Ltd identifier is `7F 7F 7F 7F 3B` (5 bytes), so
62    /// the continuation count is 4.
63    pub fn continuation_count(&self) -> u8 {
64        self.continuations
65    }
66}
67
68impl fmt::Debug for Identification {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        f.debug_tuple("Identification")
71            .field(&HexSlice(self.bytes))
72            .finish()
73    }
74}
75
76#[allow(unused)] // TODO support more features
77enum Opcode {
78    /// Read the 8-bit legacy device ID.
79    ReadDeviceId = 0xAB,
80    /// Read the 8-bit manufacturer and device IDs.
81    ReadMfDId = 0x90,
82    /// Read 16-bit manufacturer ID and 8-bit device ID.
83    ReadJedecId = 0x9F,
84    /// Set the write enable latch.
85    WriteEnable = 0x06,
86    /// Clear the write enable latch.
87    WriteDisable = 0x04,
88    /// Read the 8-bit status register.
89    ReadStatus = 0x05,
90    /// Write the 8-bit status register. Not all bits are writeable.
91    WriteStatus = 0x01,
92    Read = 0x03,
93    PageProg = 0x02, // directly writes to EEPROMs too
94    SectorErase = 0x20,
95    BlockErase = 0xD8,
96    ChipErase = 0xC7,
97}
98
99bitflags! {
100    /// Status register bits.
101    pub struct Status: u8 {
102        /// Erase or write in progress.
103        const BUSY = 1 << 0;
104        /// Status of the **W**rite **E**nable **L**atch.
105        const WEL = 1 << 1;
106        /// The 3 protection region bits.
107        const PROT = 0b00011100;
108        /// **S**tatus **R**egister **W**rite **D**isable bit.
109        const SRWD = 1 << 7;
110    }
111}
112
113/// Trait for defining the size of a flash.
114pub trait FlashParameters {
115    /// The page write size in bytes.
116    const PAGE_SIZE: usize;
117    /// The sector erase size in bytes.
118    const SECTOR_SIZE: usize;
119    /// The block erase size in bytes.
120    const BLOCK_SIZE: usize;
121    /// The total chip size in bytes.
122    const CHIP_SIZE: usize;
123}
124
125/// Driver for 25-series SPI Flash chips.
126///
127/// # Type Parameters
128///
129/// * **`SPI`**: The SPI master to which the flash chip is attached.
130/// * **`FlashParams`**: Memory size.
131/// * **`Delay`**: Delay provider.
132#[derive(Debug, defmt::Format)]
133pub struct Flash<SPI, FlashParams, Delay> {
134    spi: SPI,
135    delay: Delay,
136    poll_delay_us: u32,
137    params: PhantomData<FlashParams>,
138}
139
140impl<SPI, FlashParams, Delay> FlashParameters for Flash<SPI, FlashParams, Delay>
141where
142    FlashParams: FlashParameters,
143{
144    const PAGE_SIZE: usize = FlashParams::PAGE_SIZE;
145    const SECTOR_SIZE: usize = FlashParams::SECTOR_SIZE;
146    const BLOCK_SIZE: usize = FlashParams::BLOCK_SIZE;
147    const CHIP_SIZE: usize = FlashParams::CHIP_SIZE;
148}
149
150impl<SPI, FlashParams, Delay> Flash<SPI, FlashParams, Delay>
151where
152    SPI: SpiDevice<u8>,
153    Delay: DelayNs,
154    FlashParams: FlashParameters,
155{
156    /// Creates a new 26-series flash driver.
157    ///
158    /// # Parameters
159    ///
160    /// * **`spi`**: An SPI master. Must be configured to operate in the correct
161    ///   mode for the device.
162    /// * **`delay`**: A [`DelayNs`] implementation.
163    /// * **`poll_delay_us`**: The delay between polling the chip when waiting for an operation to complete.
164    pub async fn init(
165        spi: SPI,
166        delay: Delay,
167        poll_delay_us: u32,
168        _params: FlashParams,
169    ) -> Result<Self, Error<SPI>> {
170        let mut this = Flash {
171            spi,
172            delay,
173            poll_delay_us,
174            params: PhantomData,
175        };
176
177        // If the MCU is reset and an old operation is still ongoing, wait for it to finish.
178        this.wait_done().await?;
179
180        Ok(this)
181    }
182
183    /// Get the size of a page which can be written.
184    pub const fn page_write_size(&self) -> usize {
185        FlashParams::PAGE_SIZE
186    }
187
188    /// Get the size of a sector which can be erased.
189    pub const fn sector_erase_size(&self) -> usize {
190        FlashParams::SECTOR_SIZE
191    }
192
193    /// Get the size of a block which can be erased.
194    pub const fn block_erase_size(&self) -> usize {
195        FlashParams::BLOCK_SIZE
196    }
197
198    /// Get the size of the flash chip.
199    pub const fn chip_size(&self) -> usize {
200        FlashParams::CHIP_SIZE
201    }
202
203    async fn command_transfer(&mut self, bytes: &mut [u8]) -> Result<(), Error<SPI>> {
204        self.spi.transfer_in_place(bytes).await.map_err(Error::Spi)
205    }
206
207    async fn command_write(&mut self, bytes: &[u8]) -> Result<(), Error<SPI>> {
208        self.spi.write(bytes).await.map_err(Error::Spi)
209    }
210
211    /// Reads the JEDEC manufacturer/device identification.
212    pub async fn read_jedec_id(&mut self) -> Result<Identification, Error<SPI>> {
213        // Optimistically read 12 bytes, even though some identifiers will be shorter
214        let mut buf: [u8; 12] = [0; 12];
215        buf[0] = Opcode::ReadJedecId as u8;
216        self.command_transfer(&mut buf).await?;
217
218        // Skip buf[0] (SPI read response byte)
219        Ok(Identification::from_jedec_id(&buf[1..]))
220    }
221
222    /// Reads the status register.
223    pub async fn read_status(&mut self) -> Result<Status, Error<SPI>> {
224        let mut buf = [Opcode::ReadStatus as u8, 0];
225        self.command_transfer(&mut buf).await?;
226
227        Ok(Status::from_bits_truncate(buf[1]))
228    }
229
230    async fn write_enable(&mut self) -> Result<(), Error<SPI>> {
231        let cmd_buf = [Opcode::WriteEnable as u8];
232        self.command_write(&cmd_buf).await
233    }
234
235    pub async fn wait_done(&mut self) -> Result<(), Error<SPI>> {
236        while self.read_status().await?.contains(Status::BUSY) {
237            self.delay.delay_us(self.poll_delay_us).await;
238        }
239        Ok(())
240    }
241
242    /// Reads flash contents into `buf`, starting at `addr`.
243    ///
244    /// Note that `addr` is not fully decoded: Flash chips will typically only
245    /// look at the lowest `N` bits needed to encode their size, which means
246    /// that the contents are "mirrored" to addresses that are a multiple of the
247    /// flash size. Only 24 bits of `addr` are transferred to the device in any
248    /// case, limiting the maximum size of 25-series SPI flash chips to 16 MiB.
249    ///
250    /// # Parameters
251    ///
252    /// * `addr`: 24-bit address to start reading at.
253    /// * `buf`: Destination buffer to fill.
254    pub async fn read(&mut self, addr: u32, buf: &mut [u8]) -> Result<(), Error<SPI>> {
255        // TODO what happens if `buf` is empty?
256
257        let cmd_buf = [
258            Opcode::Read as u8,
259            (addr >> 16) as u8,
260            (addr >> 8) as u8,
261            addr as u8,
262        ];
263
264        self.spi
265            .transaction(&mut [Operation::Write(&cmd_buf), Operation::Read(buf)])
266            .await
267            .map_err(Error::Spi)
268    }
269
270    /// Erases a sector from the memory chip.
271    ///
272    /// # Parameters
273    /// * `addr`: The address to start erasing at. If the address is not on a sector boundary,
274    ///   the lower bits can be ignored in order to make it fit.
275    pub async fn erase_sector(&mut self, addr: u32) -> Result<(), Error<SPI>> {
276        self.write_enable().await?;
277
278        let cmd_buf = [
279            Opcode::SectorErase as u8,
280            (addr >> 16) as u8,
281            (addr >> 8) as u8,
282            addr as u8,
283        ];
284        self.command_write(&cmd_buf).await?;
285        self.wait_done().await
286    }
287
288    /// Erases a block from the memory chip.
289    ///
290    /// # Parameters
291    /// * `addr`: The address to start erasing at. If the address is not on a block boundary,
292    ///   the lower bits can be ignored in order to make it fit.
293    pub async fn erase_block(&mut self, addr: u32) -> Result<(), Error<SPI>> {
294        self.write_enable().await?;
295
296        let cmd_buf = [
297            Opcode::BlockErase as u8,
298            (addr >> 16) as u8,
299            (addr >> 8) as u8,
300            addr as u8,
301        ];
302        self.command_write(&cmd_buf).await?;
303        self.wait_done().await
304    }
305
306    /// Writes bytes onto the memory chip. This method is supposed to assume that the sectors
307    /// it is writing to have already been erased and should not do any erasing themselves.
308    ///
309    /// # Parameters
310    /// * `addr`: The address to write to.
311    /// * `data`: The bytes to write to `addr`, note that it will only take the lowest `PAGE_SIZE`
312    /// bytes from the slice.
313    pub async fn write_bytes(&mut self, addr: u32, data: &[u8]) -> Result<(), Error<SPI>> {
314        self.write_enable().await?;
315
316        let cmd_buf = [
317            Opcode::PageProg as u8,
318            (addr >> 16) as u8,
319            (addr >> 8) as u8,
320            addr as u8,
321        ];
322        self.spi
323            .transaction(&mut [
324                Operation::Write(&cmd_buf),
325                Operation::Write(&data[..FlashParams::PAGE_SIZE.min(data.len())]),
326            ])
327            .await
328            .map_err(Error::Spi)?;
329
330        self.wait_done().await
331    }
332
333    /// Erases the memory chip fully.
334    ///
335    /// Warning: Full erase operations can take a significant amount of time.
336    /// Check your device's datasheet for precise numbers.
337    pub async fn erase_all(&mut self) -> Result<(), Error<SPI>> {
338        self.write_enable().await?;
339        let cmd_buf = [Opcode::ChipErase as u8];
340        self.command_write(&cmd_buf).await?;
341        self.wait_done().await
342    }
343
344    /// Erases a range of sectors. The range is expressed in bytes. These bytes need to be a multiple of SECTOR_SIZE.
345    /// If the range starts at SECTOR_SIZE * 3 then the erase starts at the fourth sector.
346    /// All sectors are erased in the range [start_sector..end_sector].
347    /// The start address may not be a higher value than the end address.
348    ///
349    /// # Arguments
350    /// * `start_address` - Address of the first byte of the start of the range of sectors that need to be erased.
351    /// * `end_address` - Address of the first byte of the end of the range of sectors that need to be erased.
352    pub async fn erase_range(
353        &mut self,
354        start_address: u32,
355        end_address: u32,
356    ) -> Result<(), Error<SPI>> {
357        self.write_enable().await?;
358        let sector_size: u32 = FlashParams::SECTOR_SIZE.try_into().unwrap();
359
360        if start_address % sector_size != 0 {
361            return Err(Error::NotAligned);
362        }
363
364        if end_address % sector_size != 0 {
365            return Err(Error::NotAligned);
366        }
367
368        if start_address > end_address {
369            return Err(Error::OutOfBounds);
370        }
371
372        let start_sector = start_address / sector_size;
373        let end_sector = end_address / sector_size;
374
375        for sector in start_sector..end_sector {
376            self.erase_sector(sector).await.unwrap();
377        }
378
379        Ok(())
380    }
381}
382
383impl<SPI, FlashParams, Delay> embedded_storage::nor_flash::ErrorType
384    for Flash<SPI, FlashParams, Delay>
385where
386    SPI: SpiDevice<u8>,
387{
388    type Error = Error<SPI>;
389}
390
391impl<SPI, FlashParams, Delay> embedded_storage_async::nor_flash::ReadNorFlash
392    for Flash<SPI, FlashParams, Delay>
393where
394    SPI: SpiDevice<u8>,
395    Delay: DelayNs,
396    FlashParams: FlashParameters,
397{
398    const READ_SIZE: usize = 1;
399
400    async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
401        self.read(offset, bytes).await
402    }
403
404    fn capacity(&self) -> usize {
405        FlashParams::CHIP_SIZE.try_into().unwrap()
406    }
407}
408
409impl<SPI, FlashParams, Delay> embedded_storage_async::nor_flash::NorFlash
410    for Flash<SPI, FlashParams, Delay>
411where
412    SPI: SpiDevice<u8>,
413    Delay: DelayNs,
414    FlashParams: FlashParameters,
415{
416    const WRITE_SIZE: usize = 1;
417
418    const ERASE_SIZE: usize = FlashParams::SECTOR_SIZE;
419
420    async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
421        self.erase_range(from, to).await
422    }
423
424    async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
425        self.write_bytes(offset, bytes).await
426    }
427}
428
429#[cfg(test)]
430mod tests {
431    use super::*;
432
433    #[test]
434    fn test_decode_jedec_id() {
435        let cypress_id_bytes = [0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xC2, 0x22, 0x08];
436        let ident = Identification::from_jedec_id(&cypress_id_bytes);
437        assert_eq!(0xC2, ident.mfr_code());
438        assert_eq!(6, ident.continuation_count());
439        let device_id = ident.device_id();
440        assert_eq!(device_id[0], 0x22);
441        assert_eq!(device_id[1], 0x08);
442    }
443}