Skip to main content

daisy_embassy/
flash.rs

1//! Driver for the IS25LP064 Flash chip connected via QSPI
2//! Notes:
3//! 1. The Qspi driver in embassy_stm32 can currently only be used in Blocking mode, because Async would require MDMA, which is unsupported.
4//! 2. The Daisy bootloader (as of v6.3) Does not use QPI mode, and configuring the flash chip that way would cause problems on reset. So for compatibility's sake, we do not use it here either.
5#![allow(unused)]
6
7use crate::hal;
8use crate::pins::FlashPins;
9use embassy_stm32::{
10    Peri,
11    qspi::enums::{AddressSize, ChipSelectHighTime, FIFOThresholdLevel, MemorySize},
12};
13use hal::{
14    mode::Blocking,
15    peripherals::QUADSPI,
16    qspi::{
17        Qspi, TransferConfig,
18        enums::{DummyCycles, QspiWidth},
19    },
20};
21
22// Commands from IS25LP064 datasheet.
23const WRITE_CMD: u8 = 0x32; // PPQ
24const WRITE_ENABLE_CMD: u8 = 0x06; // WREN
25const SECTOR_ERASE_CMD: u8 = 0xD7; // SER
26const FAST_READ_QUAD_IO_CMD: u8 = 0xEB; // FRQIO
27const RESET_ENABLE_CMD: u8 = 0x66;
28const RESET_MEMORY_CMD: u8 = 0x99;
29
30const WRITE_STATUS_REGISTER_CMD: u8 = 0x01; // WRSR
31const READ_STATUS_REGISTER_CMD: u8 = 0x05; // RDSR
32const STATUS_BIT_WIP: u8 = 1 << 0;
33const STATUS_BIT_WEL: u8 = 1 << 1;
34const STATUS_BIT_BP0: u8 = 1 << 2;
35const STATUS_BIT_BP1: u8 = 1 << 3;
36const STATUS_BIT_BP2: u8 = 1 << 4;
37const STATUS_BIT_BP3: u8 = 1 << 5;
38const STATUS_BIT_QE: u8 = 1 << 6;
39const STATUS_BIT_SRWD: u8 = 1 << 7;
40
41const SET_READ_PARAMETERS_CMD: u8 = 0xC0; // SRP
42const READ_PARAMS_BIT_BL0: u8 = 1 << 0;
43const READ_PARAMS_BIT_BL1: u8 = 1 << 1;
44const READ_PARAMS_BIT_WE: u8 = 1 << 2;
45const READ_PARAMS_BIT_DC0: u8 = 1 << 3;
46const READ_PARAMS_BIT_DC1: u8 = 1 << 4;
47const READ_PARAMS_BIT_ODS0: u8 = 1 << 5;
48const READ_PARAMS_BIT_ODS1: u8 = 1 << 6;
49const READ_PARAMS_BIT_ODS2: u8 = 1 << 7;
50
51// Memory array specifications as defined in the datasheet.
52const SECTOR_SIZE: u32 = 4096;
53const PAGE_SIZE: u32 = 256;
54const MAX_ADDRESS: u32 = 0x7FFFFF;
55
56pub struct FlashBuilder<'a> {
57    pub pins: FlashPins<'a>,
58    pub qspi: Peri<'a, QUADSPI>,
59}
60
61impl<'a> FlashBuilder<'a> {
62    pub fn build(self) -> Flash<'a> {
63        let mut config = hal::qspi::Config::default();
64
65        config.memory_size = MemorySize::_8MiB;
66        config.address_size = AddressSize::_24bit;
67        config.prescaler = 1;
68        config.cs_high_time = ChipSelectHighTime::_2Cycle;
69        config.fifo_threshold = FIFOThresholdLevel::_1Bytes;
70
71        let Self { pins, qspi } = self;
72        let qspi = Qspi::new_blocking_bank1(
73            qspi, pins.IO0, pins.IO1, pins.IO2, pins.IO3, pins.SCK, pins.CS, config,
74        );
75        let mut result = Flash { qspi };
76        result.reset_memory();
77        result.reset_status_register();
78        result.reset_read_register();
79        result
80    }
81}
82
83pub struct Flash<'a> {
84    qspi: Qspi<'a, QUADSPI, Blocking>,
85}
86
87impl Flash<'_> {
88    pub fn read(&mut self, address: u32, buffer: &mut [u8]) {
89        assert!(address + buffer.len() as u32 <= MAX_ADDRESS);
90
91        let transaction = TransferConfig {
92            iwidth: QspiWidth::SING,
93            awidth: QspiWidth::QUAD,
94            dwidth: QspiWidth::QUAD,
95            instruction: FAST_READ_QUAD_IO_CMD,
96            address: Some(address),
97            dummy: DummyCycles::_8,
98        };
99        self.qspi.blocking_read(buffer, transaction);
100    }
101
102    pub fn read_uuid(&mut self) -> [u8; 16] {
103        let mut buffer = [0; 16];
104        let transaction: TransferConfig = TransferConfig {
105            iwidth: QspiWidth::SING,
106            awidth: QspiWidth::SING,
107            dwidth: QspiWidth::SING,
108            instruction: 0x4B,
109            address: Some(0x00),
110            dummy: DummyCycles::_8,
111        };
112        self.qspi.blocking_read(&mut buffer, transaction);
113        buffer
114    }
115
116    pub fn write(&mut self, mut address: u32, data: &[u8]) {
117        assert!(address <= MAX_ADDRESS);
118        assert!(!data.is_empty());
119        self.erase(address, data.len() as u32);
120
121        let mut length = data.len() as u32;
122        let mut start_cursor = 0;
123
124        //WRITE_CMD(or PPQ) allows to write up to 256 bytes, which is as much as PAGE_SIZE.
125        //Let's divide the data into chunks of page size to write to flash
126        loop {
127            // Calculate number of bytes between address and end of the page.
128            let page_remainder = PAGE_SIZE - (address & (PAGE_SIZE - 1));
129            let size = page_remainder.min(length) as usize;
130            self.enable_write();
131            let transaction = TransferConfig {
132                iwidth: QspiWidth::SING,
133                awidth: QspiWidth::SING,
134                dwidth: QspiWidth::QUAD,
135                instruction: WRITE_CMD,
136                address: Some(address),
137                dummy: DummyCycles::_0,
138            };
139
140            self.qspi
141                .blocking_write(&data[start_cursor..start_cursor + size], transaction);
142            self.wait_for_write();
143            start_cursor += size;
144
145            // Stop if this was the last needed page.
146            if length <= page_remainder {
147                break;
148            }
149            length -= page_remainder;
150
151            // Jump to the next page.
152            address += page_remainder;
153            address %= MAX_ADDRESS;
154        }
155    }
156
157    pub fn erase(&mut self, mut address: u32, mut length: u32) {
158        assert!(address <= MAX_ADDRESS);
159        assert!(length > 0);
160
161        loop {
162            // Erase the sector.
163            self.enable_write();
164            let transaction = TransferConfig {
165                iwidth: QspiWidth::SING,
166                awidth: QspiWidth::SING,
167                dwidth: QspiWidth::NONE,
168                instruction: SECTOR_ERASE_CMD,
169                address: Some(address),
170                dummy: DummyCycles::_0,
171            };
172
173            self.qspi.blocking_command(transaction);
174            self.wait_for_write();
175
176            // Calculate number of bytes between address and end of the sector.
177            let sector_remainder = SECTOR_SIZE - (address & (SECTOR_SIZE - 1));
178
179            // Stop if this was the last affected sector.
180            if length <= sector_remainder {
181                break;
182            }
183            length -= sector_remainder;
184
185            // Jump to the next sector.
186            address += sector_remainder;
187            address %= MAX_ADDRESS;
188        }
189    }
190
191    fn enable_write(&mut self) {
192        let transaction = TransferConfig {
193            iwidth: QspiWidth::SING,
194            awidth: QspiWidth::NONE,
195            dwidth: QspiWidth::NONE,
196            instruction: WRITE_ENABLE_CMD,
197            address: None,
198            dummy: DummyCycles::_0,
199        };
200        self.qspi.blocking_command(transaction);
201    }
202
203    fn wait_for_write(&mut self) {
204        loop {
205            if self.read_status() & STATUS_BIT_WIP == 0 {
206                break;
207            }
208        }
209    }
210
211    fn read_status(&mut self) -> u8 {
212        let mut status: [u8; 1] = [0xFF; 1];
213        let transaction = TransferConfig {
214            iwidth: QspiWidth::SING,
215            awidth: QspiWidth::NONE,
216            dwidth: QspiWidth::SING,
217            instruction: READ_STATUS_REGISTER_CMD,
218            address: None,
219            dummy: DummyCycles::_0,
220        };
221        self.qspi.blocking_read(&mut status, transaction);
222        status[0]
223    }
224
225    fn reset_memory(&mut self) {
226        let transaction = TransferConfig {
227            iwidth: QspiWidth::SING,
228            awidth: QspiWidth::NONE,
229            dwidth: QspiWidth::NONE,
230            instruction: RESET_ENABLE_CMD,
231            address: None,
232            dummy: DummyCycles::_0,
233        };
234        self.qspi.blocking_command(transaction);
235
236        let transaction = TransferConfig {
237            iwidth: QspiWidth::SING,
238            awidth: QspiWidth::NONE,
239            dwidth: QspiWidth::NONE,
240            instruction: RESET_MEMORY_CMD,
241            address: None,
242            dummy: DummyCycles::_0,
243        };
244        self.qspi.blocking_command(transaction);
245    }
246
247    /// Reset status registers into driver's defaults. This makes sure that the
248    /// peripheral is configured as expected.
249    fn reset_status_register(&mut self) {
250        self.enable_write();
251        let value = STATUS_BIT_QE;
252        let transaction = TransferConfig {
253            iwidth: QspiWidth::SING,
254            awidth: QspiWidth::NONE,
255            dwidth: QspiWidth::SING,
256            instruction: WRITE_STATUS_REGISTER_CMD,
257            address: None,
258            dummy: DummyCycles::_0,
259        };
260        self.qspi.blocking_write(&[value], transaction);
261        self.wait_for_write();
262    }
263
264    /// Reset read registers into driver's defaults. This makes sure that the
265    /// peripheral is configured as expected.
266    fn reset_read_register(&mut self) {
267        let value = READ_PARAMS_BIT_ODS2
268            | READ_PARAMS_BIT_ODS1
269            | READ_PARAMS_BIT_ODS0
270            | READ_PARAMS_BIT_DC1;
271        let transaction = TransferConfig {
272            iwidth: QspiWidth::SING,
273            awidth: QspiWidth::NONE,
274            dwidth: QspiWidth::SING,
275            instruction: SET_READ_PARAMETERS_CMD,
276            address: None,
277            dummy: DummyCycles::_0,
278        };
279        self.qspi.blocking_write(&[value], transaction);
280        self.wait_for_write();
281    }
282}