11//! Flash memory
2+ //!
3+ //! Quickstart:
4+ //!
5+ //! 1. [`Flash::unlock`]
6+ //! 2. [`Flash::page_erase`]
7+ //! 3. [`Flash::program_bytes`]
28
39use crate :: pac;
4- use core:: { ops:: Range , ptr:: write_volatile} ;
10+ use core:: { mem :: size_of , ops:: Range , ptr:: write_volatile, slice :: ChunksExact } ;
511use num_integer:: Integer ;
612
713/// Starting address of the flash memory.
@@ -25,6 +31,36 @@ pub fn flash_end() -> usize {
2531 OFFSET + crate :: info:: flash_size ( ) as usize
2632}
2733
34+ /// Range of flash memory.
35+ ///
36+ /// This is calculated at runtime using the info registers.
37+ ///
38+ /// # Example
39+ ///
40+ /// ```no_run
41+ /// use core::ops::Range;
42+ /// use stm32wlxx_hal::flash::flash_range;
43+ ///
44+ /// // valid for the nucleo-wl55jc with 256k flash
45+ /// assert_eq!(
46+ /// flash_range(),
47+ /// Range {
48+ /// start: 0x0800_0000,
49+ /// end: 0x0804_0000
50+ /// }
51+ /// );
52+ /// assert!(flash_range().contains(&0x0800_0000));
53+ /// assert!(flash_range().contains(&0x0803_FFFF));
54+ /// assert!(!flash_range().contains(&0x0804_0000));
55+ /// ```
56+ #[ inline]
57+ pub fn flash_range ( ) -> Range < usize > {
58+ Range {
59+ start : FLASH_START ,
60+ end : FLASH_START + crate :: info:: flash_size ( ) as usize ,
61+ }
62+ }
63+
2864/// Number of flash pages.
2965///
3066/// This is calculated at runtime using the info registers.
@@ -68,7 +104,16 @@ impl Page {
68104 ///
69105 /// # Safety
70106 ///
71- /// 1. The `idx` argument must point to a valid flash page.
107+ /// 1. The `idx` argument must be a valid page number, less than the value
108+ /// returned by [`num_pages`].
109+ ///
110+ /// # Example
111+ ///
112+ /// ```
113+ /// use stm32wlxx_hal::flash::Page;
114+ ///
115+ /// let page0 = unsafe { Page::from_index_unchecked(0) };
116+ /// ```
72117 #[ inline]
73118 pub const unsafe fn from_index_unchecked ( idx : u8 ) -> Self {
74119 Page { idx }
@@ -150,9 +195,9 @@ impl Page {
150195 /// # Example
151196 ///
152197 /// ```
153- /// # let page7 = unsafe { Page::from_index_unchecked(7) };
154198 /// use stm32wlxx_hal::flash::Page;
155199 ///
200+ /// let page7 = unsafe { Page::from_index_unchecked(7) };
156201 /// assert_eq!(page7.to_index(), 7);
157202 /// ```
158203 #[ inline]
@@ -164,11 +209,12 @@ impl Page {
164209 ///
165210 /// # Example
166211 ///
167- /// ```no_run
168- /// # let page127 = unsafe { Page::from_index_unchecked(127) };
169- /// # let page0 = unsafe { Page::from_index_unchecked(0) };
212+ /// ```
170213 /// use stm32wlxx_hal::flash::Page;
171214 ///
215+ /// let page127 = unsafe { Page::from_index_unchecked(127) };
216+ /// let page0 = unsafe { Page::from_index_unchecked(0) };
217+ ///
172218 /// assert_eq!(page0.addr(), 0x0800_0000);
173219 /// assert_eq!(page127.addr(), 0x0803_F800);
174220 /// ```
@@ -180,11 +226,11 @@ impl Page {
180226 ///
181227 /// # Example
182228 ///
183- /// ```no_run
184- /// # let page0 = unsafe { Page::from_index_unchecked(0) };
229+ /// ```
185230 /// use core::ops::Range;
186231 /// use stm32wlxx_hal::flash::Page;
187232 ///
233+ /// let page0 = unsafe { Page::from_index_unchecked(0) };
188234 /// assert_eq!(
189235 /// page0.addr_range(),
190236 /// Range {
@@ -201,11 +247,104 @@ impl Page {
201247 }
202248}
203249
250+ impl From < Page > for AlignedAddr {
251+ #[ inline]
252+ fn from ( page : Page ) -> Self {
253+ AlignedAddr { addr : page. addr ( ) }
254+ }
255+ }
256+
257+ /// Error for conversions to [`AlignedAddr`].
258+ #[ derive( Debug , Copy , Clone , Eq , PartialEq ) ]
259+ #[ cfg_attr( feature = "defmt" , derive( defmt:: Format ) ) ]
260+ pub struct AlignedAddrError ( pub ( crate ) ( ) ) ;
261+
262+ impl AlignedAddrError {
263+ pub ( crate ) const fn new ( ) -> Self {
264+ Self ( ( ) )
265+ }
266+ }
267+
268+ /// A `u64` aligned flash address.
269+ ///
270+ /// An argument of [`Flash::program_bytes`].
271+ ///
272+ /// # Example
273+ ///
274+ /// Create an aligned flash address by converting from `usize`.
275+ ///
276+ /// ```no_run
277+ /// use stm32wlxx_hal::flash::AlignedAddr;
278+ ///
279+ /// let addr: AlignedAddr = AlignedAddr::try_from(0x0803_F800_usize)?;
280+ /// # Ok::<(), stm32wlxx_hal::flash::AlignedAddrError>(())
281+ /// ```
282+ #[ derive( Debug , Copy , Clone , Eq , PartialEq , Ord , PartialOrd ) ]
283+ #[ cfg_attr( feature = "defmt" , derive( defmt:: Format ) ) ]
284+ pub struct AlignedAddr {
285+ addr : usize ,
286+ }
287+
288+ impl AlignedAddr {
289+ /// Create a page address from an index without checking bounds.
290+ ///
291+ /// # Safety
292+ ///
293+ /// 1. The `addr` argument must be a multiple of 8.
294+ /// 2. The `addr` argument must be a valid flash address, within the range
295+ /// returned by [`flash_range`].
296+ ///
297+ /// # Example
298+ ///
299+ /// ```
300+ /// use stm32wlxx_hal::flash::Page;
301+ ///
302+ /// let page0 = unsafe { Page::from_index_unchecked(0) };
303+ /// ```
304+ pub const unsafe fn new_unchecked ( addr : usize ) -> Self {
305+ Self { addr }
306+ }
307+ }
308+
309+ impl From < AlignedAddr > for usize {
310+ #[ inline]
311+ fn from ( addr : AlignedAddr ) -> Self {
312+ addr. addr
313+ }
314+ }
315+
316+ impl From < AlignedAddr > for u32 {
317+ #[ inline]
318+ fn from ( addr : AlignedAddr ) -> Self {
319+ addr. addr as u32
320+ }
321+ }
322+
323+ impl TryFrom < u32 > for AlignedAddr {
324+ type Error = AlignedAddrError ;
325+
326+ fn try_from ( addr : u32 ) -> Result < Self , Self :: Error > {
327+ Self :: try_from ( addr as usize )
328+ }
329+ }
330+
331+ impl TryFrom < usize > for AlignedAddr {
332+ type Error = AlignedAddrError ;
333+
334+ fn try_from ( addr : usize ) -> Result < Self , Self :: Error > {
335+ if addr % size_of :: < u64 > ( ) != 0 || !flash_range ( ) . contains ( & addr) {
336+ Err ( AlignedAddrError :: new ( ) )
337+ } else {
338+ Ok ( AlignedAddr { addr } )
339+ }
340+ }
341+ }
342+
204343/// Flash errors.
205344#[ derive( Debug , PartialEq , Eq , Clone , Copy , Hash ) ]
206345#[ cfg_attr( feature = "defmt" , derive( defmt:: Format ) ) ]
207346pub enum Error {
208- /// Busy Error .
347+ /// Busy error .
209348 ///
210349 /// A flash programming sequence was started while the previous sequence
211350 /// was still in-progress.
@@ -215,6 +354,11 @@ pub enum Error {
215354 /// A flash programming sequence was started with a program erase suspend
216355 /// bit set.
217356 Suspend ,
357+ /// Overflow error.
358+ ///
359+ /// Returned by [`Flash::standard_program_generic`], or [`Flash::program_bytes`]
360+ /// when the target data and address would exceed the end of flash memory.
361+ Overflow ,
218362 /// Fast programming data miss error.
219363 ///
220364 /// In Fast programming mode, 32 double-words (256 bytes) must be sent to
@@ -404,6 +548,69 @@ impl<'a> Flash<'a> {
404548 ret
405549 }
406550
551+ /// Program any number of bytes.
552+ ///
553+ /// This is the safest possible method for programming.
554+ ///
555+ /// # Safety
556+ ///
557+ /// 1. Do not write to flash memory that is being used for your code.
558+ ///
559+ /// # Example
560+ ///
561+ /// ```no_run
562+ /// use stm32wlxx_hal::{
563+ /// flash::{Flash, Page},
564+ /// pac,
565+ /// };
566+ ///
567+ /// let mut dp: pac::Peripherals = pac::Peripherals::take().unwrap();
568+ ///
569+ /// let my_data: [u8; 3] = [0x14, 0x15, 0x16];
570+ ///
571+ /// let last_page: Page = Page::from_index(127).unwrap();
572+ ///
573+ /// let mut flash: Flash = Flash::unlock(&mut dp.FLASH);
574+ /// unsafe {
575+ /// flash.page_erase(last_page)?;
576+ /// flash.program_bytes(&my_data, last_page.into())?;
577+ /// }
578+ /// # Ok::<(), stm32wlxx_hal::flash::Error>(())
579+ /// ```
580+ pub unsafe fn program_bytes ( & mut self , from : & [ u8 ] , to : AlignedAddr ) -> Result < ( ) , Error > {
581+ if from. is_empty ( ) {
582+ return Ok ( ( ) ) ;
583+ }
584+
585+ if !flash_range ( ) . contains ( & usize:: from ( to) . saturating_add ( from. len ( ) ) ) {
586+ return Err ( Error :: Overflow ) ;
587+ }
588+
589+ let chunks_exact: ChunksExact < u8 > = from. chunks_exact ( 8 ) ;
590+ let remainder: & [ u8 ] = chunks_exact. remainder ( ) ;
591+ let remainder_len: usize = remainder. len ( ) ;
592+
593+ let last_u64: u64 = chunks_exact
594+ . remainder ( )
595+ . iter ( )
596+ . enumerate ( )
597+ . fold ( 0 , |acc, ( n, byte) | acc | u64:: from ( * byte) << ( 8 * n) ) ;
598+
599+ for ( n, chunk) in chunks_exact. enumerate ( ) {
600+ let chunk_u64: u64 = u64:: from_le_bytes ( chunk. try_into ( ) . unwrap ( ) ) ;
601+ let addr: usize = n * size_of :: < u64 > ( ) + usize:: from ( to) ;
602+
603+ self . standard_program ( & chunk_u64, addr as * mut u64 ) ?;
604+ }
605+
606+ if remainder_len != 0 {
607+ let last_addr: usize = ( usize:: from ( to) + from. len ( ) ) . prev_multiple_of ( & 8 ) ;
608+ self . standard_program ( & last_u64, last_addr as * mut u64 ) ?;
609+ }
610+
611+ Ok ( ( ) )
612+ }
613+
407614 /// Program a user-defined type.
408615 ///
409616 /// # Safety
@@ -418,11 +625,15 @@ impl<'a> Flash<'a> {
418625 from : * const T ,
419626 to : * mut T ,
420627 ) -> Result < ( ) , Error > {
421- let size: isize = core :: mem :: size_of :: < T > ( ) as isize ;
628+ let size: isize = size_of :: < T > ( ) as isize ;
422629 if size == 0 {
423630 return Ok ( ( ) ) ;
424631 }
425632
633+ if !flash_range ( ) . contains ( & ( size_of :: < T > ( ) + to as usize ) ) {
634+ return Err ( Error :: Overflow ) ;
635+ }
636+
426637 let sr: u32 = self . sr ( ) ;
427638 if sr & flags:: BSY != 0 {
428639 return Err ( Error :: Busy ) ;
@@ -533,6 +744,25 @@ impl<'a> Flash<'a> {
533744 /// # Safety
534745 ///
535746 /// 1. Do not erase flash memory that is being used for your code.
747+ ///
748+ /// # Example
749+ ///
750+ /// Erase the last page.
751+ ///
752+ /// ```no_run
753+ /// use stm32wlxx_hal::{
754+ /// flash::{Flash, Page},
755+ /// pac,
756+ /// };
757+ ///
758+ /// let mut dp: pac::Peripherals = pac::Peripherals::take().unwrap();
759+ ///
760+ /// let last_page: Page = Page::from_index(127).unwrap();
761+ ///
762+ /// let mut flash: Flash = Flash::unlock(&mut dp.FLASH);
763+ /// unsafe { flash.page_erase(last_page)? };
764+ /// # Ok::<(), stm32wlxx_hal::flash::Error>(())
765+ /// ```
536766 pub unsafe fn page_erase ( & mut self , page : Page ) -> Result < ( ) , Error > {
537767 let sr: u32 = self . sr ( ) ;
538768 if sr & flags:: BSY != 0 {
0 commit comments