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