spi_flash/
erase_plan.rs

1use alloc::vec::Vec;
2use core::time::Duration;
3
4/// Erase plan of (opcode, size, base address, typical duration) to erase a range of memory.
5#[derive(Clone, Debug)]
6pub(crate) struct ErasePlan(pub Vec<(u8, usize, u32, Option<Duration>)>);
7
8impl ErasePlan {
9    pub fn new(insts: &[(usize, u8, Option<Duration>)], start: usize, length: usize) -> Self {
10        log::trace!("Creating erase plan, start={} length={}", start, length);
11        let mut plan = Vec::new();
12
13        // Sort instructions by smallest area of effect first.
14        let mut insts = insts.to_vec();
15        insts.sort();
16
17        // We compute the number of useful bytes erased for each operation,
18        // then from those with the same maximum number of useful bytes erased,
19        // we select the smallest operation, and repeat until all bytes are erased.
20        let end = start + length;
21        let mut pos = start;
22        while pos < end {
23            log::trace!("Evaluating candidates, pos={} end={}", pos, end);
24            // Current candidate, (bytes, size, opcode, base).
25            let mut candidate = (0, usize::MAX, 0, 0, None);
26            for (erase_size, opcode, duration) in insts.iter() {
27                let erase_base = pos - (pos % erase_size);
28                let erase_end = erase_base + erase_size - 1;
29                let mut bytes = erase_size - (pos - erase_base);
30                if erase_end > end {
31                    bytes -= erase_end - end + 1;
32                }
33                log::trace!(
34                    "  Candidate 0x{:02X} ({} bytes): base={} end={} bytes={}",
35                    opcode,
36                    erase_size,
37                    erase_base,
38                    erase_end,
39                    bytes
40                );
41                if bytes > candidate.0 || (bytes == candidate.0 && *erase_size < candidate.1) {
42                    candidate = (bytes, *erase_size, *opcode, erase_base, *duration);
43                }
44            }
45
46            log::trace!("Candidate selected: {:?}", candidate);
47            pos += candidate.0;
48            plan.push((candidate.2, candidate.1, candidate.3 as u32, candidate.4));
49        }
50
51        log::debug!("Erase plan: {:?}", plan);
52
53        ErasePlan(plan)
54    }
55
56    #[cfg(feature = "std")]
57    pub fn total_size(&self) -> usize {
58        self.0.iter().map(|x| x.1).sum()
59    }
60}
61
62#[test]
63fn test_erase_plan() {
64    let insts = &[(4, 1, None), (32, 2, None), (64, 3, None)];
65    // Use a single 4kB erase to erase an aligned 4kB block.
66    assert_eq!(ErasePlan::new(insts, 0, 4).0, alloc::vec![(1, 4, 0, None)]);
67    // Use a single 64kB erase to erase an aligned 64kB block.
68    assert_eq!(
69        ErasePlan::new(insts, 0, 64).0,
70        alloc::vec![(3, 64, 0, None)]
71    );
72    // Use three 64kB erases to erase an aligned 192kB block.
73    assert_eq!(
74        ErasePlan::new(insts, 0, 192).0,
75        alloc::vec![(3, 64, 0, None), (3, 64, 64, None), (3, 64, 128, None)]
76    );
77    // Use 64kB followed by 32kB to erase an aligned 70kB block.
78    assert_eq!(
79        ErasePlan::new(insts, 0, 70).0,
80        alloc::vec![(3, 64, 0, None), (2, 32, 64, None)]
81    );
82    // Use 64kB followed by 4kB to erase an aligned 66kB block.
83    assert_eq!(
84        ErasePlan::new(insts, 0, 66).0,
85        alloc::vec![(3, 64, 0, None), (1, 4, 64, None)]
86    );
87    // Use 4kB followed by 64kB to erase a misaligned 64kB block.
88    assert_eq!(
89        ErasePlan::new(insts, 62, 64).0,
90        alloc::vec![(1, 4, 60, None), (3, 64, 64, None)]
91    );
92    // Use a 4kB, 64kB, 4kB to erase a misaligned 68kB block.
93    assert_eq!(
94        ErasePlan::new(insts, 62, 68).0,
95        alloc::vec![(1, 4, 60, None), (3, 64, 64, None), (1, 4, 128, None)]
96    );
97}