1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
//! Read and write onboard flash memory. Erase pages (sectors on H7), write data,
//! and read data.
//!
//! Before using this module, check the datasheet and/or RM for your specific
//! STM32 variant for page [sector] size, and number of pages [sectors] available.
//! Make sure not to write to a page your MCU doesn't have, or that includes your
//! program's memory.

use cfg_if::cfg_if;

use crate::pac::FLASH;

// Note that L5 code is simialr to other families like L4 and G4, but splits many options into
// 2 sets; one for secure, one for nonsecure.

const BANK1_START_ADDR: usize = 0x0800_0000;

cfg_if! {
    if #[cfg(any(feature = "l5", feature = "g473", feature = "g474", feature = "g483", feature = "g484"))] {
        const PAGE_SIZE_SINGLE_BANK: usize = 4_096;
        const PAGE_SIZE_DUAL_BANK: usize = 2_048;
        const BANK2_START_ADDR: usize = 0x0804_0000;
    } else if #[cfg(feature = "h7")]{
        const SECTOR_SIZE: usize = 0x2_0000;
        const BANK2_START_ADDR: usize = 0x0810_0000;
    } else {
        const PAGE_SIZE: usize = 2_048;
        #[allow(dead_code)]  // bank arg on single-bank MCUs.
        const BANK2_START_ADDR: usize = 0x0804_0000;
    }
}

cfg_if! {
    if #[cfg(any(feature = "l5", feature = "h5"))] {
        mod trustzone;
        pub use trustzone::*;
    } else {
        mod non_trustzone;
        pub use non_trustzone::*;
    }
}

pub struct Flash {
    pub regs: FLASH,
    #[cfg(any(
        feature = "g473",
        feature = "g474",
        feature = "g483",
        feature = "g484",
        feature = "l5"
    ))]
    pub dual_bank: DualBank,
}

// todo: Is H5 more like H7, or the others?

/// Contains code common to both modules.
impl Flash {
    /// Create a struct used to perform operations on Flash.
    pub fn new(regs: FLASH) -> Self {
        cfg_if! {
            if #[cfg(any(feature = "g473", feature = "g474", feature = "g483", feature = "g484", feature = "l5"))] {
                // Some G4 variants let you select dual or single-bank mode.
                // Self { regs, dual_bank: DualBank::Single }
                Self { regs, dual_bank: DualBank::Dual } // todo: Experimenting
            } else {
                Self { regs }
            }
        }
    }

    /// Read flash memory at a given page and offset into an 8-bit-dword buffer.
    #[allow(unused_variables)] // bank arg on single-bank MCUs.
    pub fn read(&self, bank: Bank, page: usize, offset: usize, buf: &mut [u8]) {
        // H742 RM, section 4.3.8:
        // Single read sequence
        // The recommended simple read sequence is the following:
        // 1. Freely perform read accesses to any AXI-mapped area.
        // 2. The embedded Flash memory effectively executes the read operation from the read
        // command queue buffer as soon as the non-volatile memory is ready and the previously
        // requested operations on this specific bank have been served.
        cfg_if! {
            if #[cfg(any(
                feature = "g473",
                feature = "g474",
                feature = "g483",
                feature = "g484",
                feature = "l5",
            ))] {
                let mut addr = page_to_address(self.dual_bank, bank, page) as *mut u32;
            } else if #[cfg(feature = "h7")]{
                let mut addr = page_to_address(bank, page) as *mut u32;
            } else {
                let mut addr = page_to_address(page) as *mut u32;
            }
        }

        unsafe {
            // Offset it by the start position
            addr = unsafe { addr.add(offset) };
            // Iterate on chunks of 32bits
            for chunk in buf.chunks_mut(4) {
                let word = unsafe { core::ptr::read_volatile(addr) };
                let bytes = word.to_le_bytes();

                let len = chunk.len();
                if len < 4 {
                    chunk[0..len].copy_from_slice(&bytes[0..len]);
                } else {
                    chunk[0..4].copy_from_slice(&bytes);
                };

                unsafe { addr = addr.add(1) };
            }
        }
    }
}

/// Calculate the address of the start of a given page. Each page is 2,048 Kb for non-H7.
/// For H7, sectors are 128Kb, with 8 sectors per bank.
#[cfg(not(any(
    feature = "g473",
    feature = "g474",
    feature = "g483",
    feature = "g484",
    feature = "h5",
    feature = "l5",
    feature = "h7"
)))]
fn page_to_address(page: usize) -> usize {
    BANK1_START_ADDR + page * PAGE_SIZE
}

#[cfg(any(
    feature = "g473",
    feature = "g474",
    feature = "g483",
    feature = "g484",
    feature = "h5",
    feature = "l5",
))]
fn page_to_address(dual_bank: DualBank, bank: Bank, page: usize) -> usize {
    if dual_bank == DualBank::Single {
        BANK1_START_ADDR + page * PAGE_SIZE_SINGLE_BANK
    } else {
        match bank {
            Bank::B1 => BANK1_START_ADDR + page * PAGE_SIZE_DUAL_BANK,
            Bank::B2 => BANK2_START_ADDR + page * PAGE_SIZE_DUAL_BANK,
        }
    }
}

#[cfg(feature = "h7")]
/// Calculate the address of the start of a given page. Each page is 2,048 Kb for non-H7.
/// For H7, sectors are 128Kb, with 8 sectors per bank.
fn page_to_address(bank: Bank, sector: usize) -> usize {
    // Note; Named sector on H7.
    let starting_pt = match bank {
        Bank::B1 => BANK1_START_ADDR,
        // todo: This isn't the same bank2 starting point for all H7 variants!
        #[cfg(not(any(feature = "h747cm4", feature = "h747cm7")))]
        Bank::B2 => BANK2_START_ADDR,
    };

    starting_pt + sector * SECTOR_SIZE
}