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#[derive(Debug, Clone, Copy, Eq, PartialEq, defmt::Format)]
44/// Possible error states for flash operations.
45pub enum FlashError {
46    /// Flash controller is not done yet
47    Busy,
48    /// Error detected (by command execution, or because no command could be executed)
49    Illegal,
50    /// Set during read if ECC decoding logic detects correctable or uncorrectable error
51    EccError,
52    /// Page number is out of range
53    PageOutOfRange,
54    /// (Legal) command failed
55    Failure,
56}
57
58pub struct Flash {
59    pub regs: FLASH,
60    #[cfg(any(
61        feature = "g473",
62        feature = "g474",
63        feature = "g483",
64        feature = "g484",
65        feature = "l5"
66    ))]
67    pub dual_bank: DualBank,
68}
69
70// todo: Is H5 more like H7, or the others?
71
72/// Contains code common to both modules.
73impl Flash {
74    /// Create a struct used to perform operations on Flash.
75    pub fn new(regs: FLASH) -> Self {
76        cfg_if! {
77            if #[cfg(any(feature = "g473", feature = "g474", feature = "g483", feature = "g484", feature = "l5"))] {
78                // Some G4 variants let you select dual or single-bank mode.
79                // Self { regs, dual_bank: DualBank::Single }
80                Self { regs, dual_bank: DualBank::Dual } // todo: Experimenting
81            } else {
82                Self { regs }
83            }
84        }
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}