stm32l4xx_hal/
flash.rs

1//! Flash memory module
2//!
3//! Example usage of flash programming interface:
4//!
5//! ```
6//! fn program_region(mut flash: flash::Parts) -> Result<(), flash::Error> {
7//!     // Unlock the flashing module
8//!     let mut prog = flash.keyr.unlock_flash(&mut flash.sr, &mut flash.cr)?;
9//!
10//!     let page = flash::FlashPage(5);
11//!
12//!     // Perform the erase and programing operation
13//!     prog.erase_page(page)?;
14//!     let data = [
15//!         0x1111_1112_1113_1114,
16//!         0x2221_2222_2223_2224,
17//!         0x3331_3332_3333_3334,
18//!     ];
19//!     prog.write_native(page.to_address(), &data)?;
20//!
21//!     // Check result (not needed, but done for this example)
22//!     let addr = page.to_address() as *const u64;
23//!     assert!(unsafe { core::ptr::read(addr) } == data[0]);
24//!     assert!(unsafe { core::ptr::read(addr.offset(1)) } == data[1]);
25//!     assert!(unsafe { core::ptr::read(addr.offset(2)) } == data[2]);
26//!
27//!     Ok(())
28//! }
29//! ```
30
31#![deny(missing_docs)]
32
33use crate::stm32::{flash, FLASH};
34use crate::traits::flash as flash_trait;
35use core::convert::TryInto;
36use core::{mem, ops::Drop, ptr};
37pub use flash_trait::{Error, FlashPage, Read, WriteErase};
38
39/// Extension trait to constrain the FLASH peripheral
40pub trait FlashExt {
41    /// Constrains the FLASH peripheral to play nicely with the other abstractions
42    fn constrain(self) -> Parts;
43}
44
45impl FlashExt for FLASH {
46    fn constrain(self) -> Parts {
47        Parts {
48            acr: ACR {},
49            pdkeyr: PDKEYR {},
50            keyr: KEYR {},
51            optkeyr: OPTKEYR {},
52            sr: SR {},
53            cr: CR {},
54            eccr: ECCR {},
55            pcrop1sr: PCROP1SR {},
56            pcrop1er: PCROP1ER {},
57            wrp1ar: WRP1AR {},
58            wrp1br: WRP1BR {},
59        }
60    }
61}
62
63/// Constrained FLASH peripheral
64pub struct Parts {
65    /// Opaque ACR register
66    pub acr: ACR,
67    /// Opaque PDKEYR register
68    pub pdkeyr: PDKEYR,
69    /// Opaque KEYR register
70    pub keyr: KEYR,
71    /// Opaque OPTKEYR register
72    pub optkeyr: OPTKEYR,
73    /// Opaque SR register
74    pub sr: SR,
75    /// Opaque SR register
76    pub cr: CR,
77    /// Opaque ECCR register
78    pub eccr: ECCR,
79    /// Opaque PCROP1SR register
80    pub pcrop1sr: PCROP1SR,
81    /// Opaque PCROP1ER register
82    pub pcrop1er: PCROP1ER,
83    /// Opaque WRP1AR register
84    pub wrp1ar: WRP1AR,
85    /// Opaque WRP1BR register
86    pub wrp1br: WRP1BR,
87}
88
89macro_rules! generate_register {
90    ($a:ident, $b:ident, $name:expr) => {
91        #[doc = "Opaque "]
92        #[doc = $name]
93        #[doc = " register"]
94        pub struct $a;
95
96        impl $a {
97            #[allow(unused)]
98            pub(crate) fn $b(&mut self) -> &flash::$a {
99                // NOTE(unsafe) this proxy grants exclusive access to this register
100                unsafe { &(*FLASH::ptr()).$b }
101            }
102        }
103    };
104
105    ($a:ident, $b:ident) => {
106        generate_register!($a, $b, stringify!($a));
107    };
108}
109
110generate_register!(ACR, acr);
111generate_register!(PDKEYR, pdkeyr);
112generate_register!(KEYR, keyr);
113generate_register!(OPTKEYR, optkeyr);
114generate_register!(SR, sr);
115generate_register!(CR, cr);
116generate_register!(ECCR, eccr);
117generate_register!(PCROP1SR, pcrop1sr);
118generate_register!(PCROP1ER, pcrop1er);
119generate_register!(WRP1AR, wrp1ar);
120generate_register!(WRP1BR, wrp1br);
121
122const FLASH_KEY1: u32 = 0x4567_0123;
123const FLASH_KEY2: u32 = 0xCDEF_89AB;
124
125impl KEYR {
126    /// Unlock the flash registers via KEYR to access the flash programming
127    pub fn unlock_flash<'a>(
128        &'a mut self,
129        sr: &'a mut SR,
130        cr: &'a mut CR,
131    ) -> Result<FlashProgramming<'a>, Error> {
132        let keyr = self.keyr();
133        unsafe {
134            keyr.write(|w| w.bits(FLASH_KEY1));
135            keyr.write(|w| w.bits(FLASH_KEY2));
136        }
137
138        if cr.cr().read().lock().bit_is_clear() {
139            Ok(FlashProgramming { sr, cr })
140        } else {
141            Err(Error::Failure)
142        }
143    }
144}
145
146impl FlashPage {
147    /// This gives the starting address of a flash page in physical address
148    pub const fn to_address(&self) -> usize {
149        0x0800_0000 + self.0 * 2048
150    }
151}
152
153/// Flash programming interface
154pub struct FlashProgramming<'a> {
155    sr: &'a mut SR,
156    cr: &'a mut CR,
157}
158
159impl<'a> Drop for FlashProgramming<'a> {
160    fn drop(&mut self) {
161        // Lock on drop
162        self.lock();
163    }
164}
165
166impl<'a> Read for FlashProgramming<'a> {
167    type NativeType = u8;
168
169    #[inline]
170    fn read_native(&self, address: usize, array: &mut [Self::NativeType]) {
171        let mut address = address as *const Self::NativeType;
172
173        for data in array {
174            unsafe {
175                *data = ptr::read(address);
176                address = address.add(1);
177            }
178        }
179    }
180
181    #[inline]
182    fn read(&self, address: usize, buf: &mut [u8]) {
183        self.read_native(address, buf);
184    }
185}
186
187impl<'a> WriteErase for FlashProgramming<'a> {
188    type NativeType = u64;
189
190    fn status(&self) -> flash_trait::Result {
191        let sr = unsafe { &(*FLASH::ptr()).sr }.read();
192
193        if sr.bsy().bit_is_set() {
194            Err(flash_trait::Error::Busy)
195        } else if sr.pgaerr().bit_is_set() || sr.progerr().bit_is_set() || sr.wrperr().bit_is_set()
196        {
197            Err(flash_trait::Error::Illegal)
198        } else {
199            Ok(())
200        }
201    }
202
203    fn erase_page(&mut self, page: flash_trait::FlashPage) -> flash_trait::Result {
204        match page.0 {
205            0..=255 => {
206                self.cr.cr().modify(|_, w| unsafe {
207                    w.bker()
208                        .clear_bit()
209                        .pnb()
210                        .bits(page.0 as u8)
211                        .per()
212                        .set_bit()
213                });
214            }
215            256..=511 => {
216                self.cr.cr().modify(|_, w| unsafe {
217                    w.bker()
218                        .set_bit()
219                        .pnb()
220                        .bits((page.0 - 256) as u8)
221                        .per()
222                        .set_bit()
223                });
224            }
225            _ => {
226                return Err(flash_trait::Error::PageOutOfRange);
227            }
228        }
229
230        self.cr.cr().modify(|_, w| w.start().set_bit());
231
232        let res = self.wait();
233
234        self.cr.cr().modify(|_, w| w.per().clear_bit());
235
236        res
237    }
238
239    fn write_native(&mut self, address: usize, array: &[Self::NativeType]) -> flash_trait::Result {
240        // NB: The check for alignment of the address, and that the flash is erased is made by the
241        // flash controller. The `wait` function will return the proper error codes.
242        let mut address = address as *mut u32;
243
244        self.cr.cr().modify(|_, w| w.pg().set_bit());
245
246        for dword in array {
247            unsafe {
248                ptr::write_volatile(address, *dword as u32);
249                ptr::write_volatile(address.add(1), (*dword >> 32) as u32);
250
251                address = address.add(2);
252            }
253
254            self.wait()?;
255
256            if self.sr.sr().read().eop().bit_is_set() {
257                self.sr.sr().modify(|_, w| w.eop().clear_bit());
258            }
259        }
260
261        self.cr.cr().modify(|_, w| w.pg().clear_bit());
262
263        Ok(())
264    }
265
266    fn write(&mut self, address: usize, data: &[u8]) -> flash_trait::Result {
267        let address_offset = address % mem::align_of::<Self::NativeType>();
268        let unaligned_size = (mem::size_of::<Self::NativeType>() - address_offset)
269            % mem::size_of::<Self::NativeType>();
270
271        if unaligned_size > 0 {
272            let unaligned_data = &data[..unaligned_size];
273            // Handle unaligned address data, make it into a native write
274            let mut data = 0xffff_ffff_ffff_ffffu64;
275            for b in unaligned_data {
276                data = (data >> 8) | ((*b as Self::NativeType) << 56);
277            }
278
279            let unaligned_address = address - address_offset;
280            let native = &[data];
281            self.write_native(unaligned_address, native)?;
282        }
283
284        // Handle aligned address data
285        let aligned_data = &data[unaligned_size..];
286        let mut aligned_address = if unaligned_size > 0 {
287            address - address_offset + mem::size_of::<Self::NativeType>()
288        } else {
289            address
290        };
291
292        let mut chunks = aligned_data.chunks_exact(mem::size_of::<Self::NativeType>());
293
294        while let Some(exact_chunk) = chunks.next() {
295            // Write chunks
296            let native = &[Self::NativeType::from_ne_bytes(
297                exact_chunk.try_into().unwrap(),
298            )];
299            self.write_native(aligned_address, native)?;
300            aligned_address += mem::size_of::<Self::NativeType>();
301        }
302
303        let rem = chunks.remainder();
304
305        if !rem.is_empty() {
306            let mut data = 0xffff_ffff_ffff_ffffu64;
307            // Write remainder
308            for b in rem.iter().rev() {
309                data = (data << 8) | *b as Self::NativeType;
310            }
311
312            let native = &[data];
313            self.write_native(aligned_address, native)?;
314        }
315
316        Ok(())
317    }
318}
319
320impl<'a> FlashProgramming<'a> {
321    /// Lock the flash memory controller
322    fn lock(&mut self) {
323        self.cr.cr().modify(|_, w| w.lock().set_bit());
324    }
325
326    /// Wait till last flash operation is complete
327    fn wait(&mut self) -> flash_trait::Result {
328        while self.sr.sr().read().bsy().bit_is_set() {}
329
330        self.status()
331    }
332
333    /// Erase all flash pages, note that this will erase the current running program if it is not
334    /// called from a program running in RAM.
335    pub fn erase_all_pages(&mut self) -> flash_trait::Result {
336        self.cr.cr().modify(|_, w| w.mer1().set_bit());
337        self.cr.cr().modify(|_, w| w.start().set_bit());
338
339        let res = self.wait();
340
341        self.cr.cr().modify(|_, w| w.mer1().clear_bit());
342
343        res
344    }
345}