stm32_hal2/flash/
mod.rs

1//! Read and write onboard flash memory. Erase pages (sectors on H7), write data,
2//! and read data.
3//!
4//! Before using this module, check the datasheet and/or RM for your specific
5//! STM32 variant for page \[sector\] size, and number of pages \[sectors\] available.
6//! Make sure not to write to a page your MCU doesn't have, or that includes your
7//! program's memory.
8
9use cfg_if::cfg_if;
10
11use crate::pac::FLASH;
12
13// Note that L5 code is simialr to other families like L4 and G4, but splits many options into
14// 2 sets; one for secure, one for nonsecure.
15
16const BANK1_START_ADDR: usize = 0x0800_0000;
17
18cfg_if! {
19    if #[cfg(any(feature = "l5", feature = "g473", feature = "g474", feature = "g483", feature = "g484"))] {
20        const PAGE_SIZE_SINGLE_BANK: usize = 4_096;
21        const PAGE_SIZE_DUAL_BANK: usize = 2_048;
22        const BANK2_START_ADDR: usize = 0x0804_0000;
23    } else if #[cfg(feature = "h7")]{
24        const SECTOR_SIZE: usize = 0x2_0000;
25        const BANK2_START_ADDR: usize = 0x0810_0000;
26    } else {
27        const PAGE_SIZE: usize = 2_048;
28        #[allow(dead_code)]  // bank arg on single-bank MCUs.
29        const BANK2_START_ADDR: usize = 0x0804_0000;
30    }
31}
32
33cfg_if! {
34    if #[cfg(any(feature = "l5", feature = "h5"))] {
35        mod trustzone;
36        pub use trustzone::*;
37    } else {
38        mod non_trustzone;
39        pub use non_trustzone::*;
40    }
41}
42
43#[cfg_attr(feature = "defmt", derive(defmt::Format))]
44#[derive(Debug, Clone, Copy, Eq, PartialEq)]
45/// Possible error states for flash operations.
46pub enum FlashError {
47    /// Flash controller is not done yet
48    Busy,
49    /// Error detected (by command execution, or because no command could be executed)
50    Illegal,
51    /// Set during read if ECC decoding logic detects correctable or uncorrectable error
52    EccError,
53    /// Page number is out of range
54    PageOutOfRange,
55    /// (Legal) command failed
56    Failure,
57}
58
59pub struct Flash {
60    pub regs: FLASH,
61    #[cfg(any(
62        feature = "g473",
63        feature = "g474",
64        feature = "g483",
65        feature = "g484",
66        feature = "l5"
67    ))]
68    pub dual_bank: DualBank,
69}
70
71// todo: Is H5 more like H7, or the others?
72
73/// Contains code common to both modules.
74impl Flash {
75    #[cfg(not(any(
76        feature = "g473",
77        feature = "g474",
78        feature = "g483",
79        feature = "g484",
80        feature = "l5"
81    )))]
82    /// Create a struct used to perform operations on Flash.
83    pub fn new(regs: FLASH) -> Self {
84        Self { regs }
85    }
86
87    #[cfg(any(
88        feature = "g473",
89        feature = "g474",
90        feature = "g483",
91        feature = "g484",
92        feature = "l5"
93    ))]
94    pub fn new(regs: FLASH, dual_bank: DualBank) -> Self {
95        // Some G4 variants let you select dual or single-bank mode. via the Option bit.
96        Self { regs, dual_bank }
97    }
98
99    /// Read flash memory at a given page and offset into an 8-bit-dword buffer.
100    #[allow(unused_variables)] // bank arg on single-bank MCUs.
101    pub fn read(&self, bank: Bank, page: usize, offset: usize, buf: &mut [u8]) {
102        // H742 RM, section 4.3.8:
103        // Single read sequence
104        // The recommended simple read sequence is the following:
105        // 1. Freely perform read accesses to any AXI-mapped area.
106        // 2. The embedded Flash memory effectively executes the read operation from the read
107        // command queue buffer as soon as the non-volatile memory is ready and the previously
108        // requested operations on this specific bank have been served.
109        cfg_if! {
110            if #[cfg(any(
111                feature = "g473",
112                feature = "g474",
113                feature = "g483",
114                feature = "g484",
115                feature = "l5",
116            ))] {
117                let mut addr = page_to_address(self.dual_bank, bank, page) as *mut u32;
118            } else if #[cfg(feature = "h7")]{
119                let mut addr = page_to_address(bank, page) as *mut u32;
120            } else {
121                let mut addr = page_to_address(page) as *mut u32;
122            }
123        }
124
125        unsafe {
126            // Offset it by the start position
127            addr = unsafe { addr.add(offset) };
128            // Iterate on chunks of 32bits
129            for chunk in buf.chunks_mut(4) {
130                let word = unsafe { core::ptr::read_volatile(addr) };
131                let bytes = word.to_le_bytes();
132
133                let len = chunk.len();
134                if len < 4 {
135                    chunk[0..len].copy_from_slice(&bytes[0..len]);
136                } else {
137                    chunk[0..4].copy_from_slice(&bytes);
138                };
139
140                unsafe { addr = addr.add(1) };
141            }
142        }
143    }
144}
145
146/// Calculate the address of the start of a given page. Each page is 2,048 Kb for non-H7.
147/// For H7, sectors are 128Kb, with 8 sectors per bank.
148#[cfg(not(any(
149    feature = "g473",
150    feature = "g474",
151    feature = "g483",
152    feature = "g484",
153    feature = "h5",
154    feature = "l5",
155    feature = "h7"
156)))]
157fn page_to_address(page: usize) -> usize {
158    BANK1_START_ADDR + page * PAGE_SIZE
159}
160
161#[cfg(any(
162    feature = "g473",
163    feature = "g474",
164    feature = "g483",
165    feature = "g484",
166    feature = "h5",
167    feature = "l5",
168))]
169fn page_to_address(dual_bank: DualBank, bank: Bank, page: usize) -> usize {
170    if dual_bank == DualBank::Single {
171        BANK1_START_ADDR + page * PAGE_SIZE_SINGLE_BANK
172    } else {
173        match bank {
174            Bank::B1 => BANK1_START_ADDR + page * PAGE_SIZE_DUAL_BANK,
175            Bank::B2 => BANK2_START_ADDR + page * PAGE_SIZE_DUAL_BANK,
176        }
177    }
178}
179
180#[cfg(feature = "h7")]
181/// Calculate the address of the start of a given page. Each page is 2,048 Kb for non-H7.
182/// For H7, sectors are 128Kb, with 8 sectors per bank.
183fn page_to_address(bank: Bank, sector: usize) -> usize {
184    // Note; Named sector on H7.
185    let starting_pt = match bank {
186        Bank::B1 => BANK1_START_ADDR,
187        // todo: This isn't the same bank2 starting point for all H7 variants!
188        #[cfg(not(any(feature = "h747cm4", feature = "h747cm7")))]
189        Bank::B2 => BANK2_START_ADDR,
190    };
191
192    starting_pt + sector * SECTOR_SIZE
193}