Skip to content

Commit 4825b39

Browse files
authored
flash: add program_bytes
1 parent dd06ecf commit 4825b39

File tree

4 files changed

+324
-47
lines changed

4 files changed

+324
-47
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
- Added a `is_pending` method to the `gpio::Exti` trait.
1010
- Added alarm functionality to the RTC.
1111
- Added `Rtc.is_wakeup_timer_en`.
12+
- Added `flash::flash_range`.
13+
- Added `Flash.program_bytes` for safer flash programming.
1214

1315
### Changed
1416
- Replaced `Debug` implementation with `Display` implementation for:
1517
- `subghz::FskPacketStatus`
1618
- `subghz::LoRaPacketStatus`
1719
- `subghz::Stats`
1820
- `subghz::Status`
21+
- `Flash::standard_program_generic` now checks for overflow.
1922

2023
### Fixed
2124
- Fixed an off-by-one error in `flash::Page::addr_range`.

hal/src/flash.rs

Lines changed: 240 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
//! Flash memory
2+
//!
3+
//! Quickstart:
4+
//!
5+
//! 1. [`Flash::unlock`]
6+
//! 2. [`Flash::page_erase`]
7+
//! 3. [`Flash::program_bytes`]
28
39
use crate::pac;
4-
use core::{ops::Range, ptr::write_volatile};
10+
use core::{mem::size_of, ops::Range, ptr::write_volatile, slice::ChunksExact};
511
use 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))]
207346
pub 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 {

hal/src/rtc/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Real-time clock.
1+
//! Real-time clock
22
33
mod alarm;
44

0 commit comments

Comments
 (0)