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(feature = "g473", feature = "g474", feature = "g483", feature = "g484", feature = "l5")))]
76    /// Create a struct used to perform operations on Flash.
77    pub fn new(regs: FLASH) -> Self {
78        Self { regs }
79    }
80
81    #[cfg(any(feature = "g473", feature = "g474", feature = "g483", feature = "g484", feature = "l5"))]
82    pub fn new(regs: FLASH, dual_bank: DualBank) -> Self {
83        // Some G4 variants let you select dual or single-bank mode. via the Option bit.
84        Self { regs, dual_bank }
85    }
86
87    /// Read flash memory at a given page and offset into an 8-bit-dword buffer.
88    #[allow(unused_variables)] // bank arg on single-bank MCUs.
89    pub fn read(&self, bank: Bank, page: usize, offset: usize, buf: &mut [u8]) {
90        // H742 RM, section 4.3.8:
91        // Single read sequence
92        // The recommended simple read sequence is the following:
93        // 1. Freely perform read accesses to any AXI-mapped area.
94        // 2. The embedded Flash memory effectively executes the read operation from the read
95        // command queue buffer as soon as the non-volatile memory is ready and the previously
96        // requested operations on this specific bank have been served.
97        cfg_if! {
98            if #[cfg(any(
99                feature = "g473",
100                feature = "g474",
101                feature = "g483",
102                feature = "g484",
103                feature = "l5",
104            ))] {
105                let mut addr = page_to_address(self.dual_bank, bank, page) as *mut u32;
106            } else if #[cfg(feature = "h7")]{
107                let mut addr = page_to_address(bank, page) as *mut u32;
108            } else {
109                let mut addr = page_to_address(page) as *mut u32;
110            }
111        }
112
113        unsafe {
114            // Offset it by the start position
115            addr = unsafe { addr.add(offset) };
116            // Iterate on chunks of 32bits
117            for chunk in buf.chunks_mut(4) {
118                let word = unsafe { core::ptr::read_volatile(addr) };
119                let bytes = word.to_le_bytes();
120
121                let len = chunk.len();
122                if len < 4 {
123                    chunk[0..len].copy_from_slice(&bytes[0..len]);
124                } else {
125                    chunk[0..4].copy_from_slice(&bytes);
126                };
127
128                unsafe { addr = addr.add(1) };
129            }
130        }
131    }
132}
133
134/// Calculate the address of the start of a given page. Each page is 2,048 Kb for non-H7.
135/// For H7, sectors are 128Kb, with 8 sectors per bank.
136#[cfg(not(any(
137    feature = "g473",
138    feature = "g474",
139    feature = "g483",
140    feature = "g484",
141    feature = "h5",
142    feature = "l5",
143    feature = "h7"
144)))]
145fn page_to_address(page: usize) -> usize {
146    BANK1_START_ADDR + page * PAGE_SIZE
147}
148
149#[cfg(any(
150    feature = "g473",
151    feature = "g474",
152    feature = "g483",
153    feature = "g484",
154    feature = "h5",
155    feature = "l5",
156))]
157fn page_to_address(dual_bank: DualBank, bank: Bank, page: usize) -> usize {
158    if dual_bank == DualBank::Single {
159        BANK1_START_ADDR + page * PAGE_SIZE_SINGLE_BANK
160    } else {
161        match bank {
162            Bank::B1 => BANK1_START_ADDR + page * PAGE_SIZE_DUAL_BANK,
163            Bank::B2 => BANK2_START_ADDR + page * PAGE_SIZE_DUAL_BANK,
164        }
165    }
166}
167
168#[cfg(feature = "h7")]
169/// Calculate the address of the start of a given page. Each page is 2,048 Kb for non-H7.
170/// For H7, sectors are 128Kb, with 8 sectors per bank.
171fn page_to_address(bank: Bank, sector: usize) -> usize {
172    // Note; Named sector on H7.
173    let starting_pt = match bank {
174        Bank::B1 => BANK1_START_ADDR,
175        // todo: This isn't the same bank2 starting point for all H7 variants!
176        #[cfg(not(any(feature = "h747cm4", feature = "h747cm7")))]
177        Bank::B2 => BANK2_START_ADDR,
178    };
179
180    starting_pt + sector * SECTOR_SIZE
181}