probe_rs/flashing/
builder.rs

1use std::collections::BTreeMap;
2use std::fmt::{Debug, Formatter};
3use std::ops::Range;
4
5use probe_rs_target::{MemoryRange, NvmRegion, PageInfo};
6
7use super::{FlashAlgorithm, FlashError};
8
9/// The description of a page in flash.
10#[derive(Clone, PartialEq, Eq)]
11pub struct FlashPage {
12    pub(super) address: u64,
13    pub(super) data: Vec<u8>,
14}
15
16impl Debug for FlashPage {
17    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
18        f.debug_struct("FlashPage")
19            .field("address", &self.address())
20            .field("size", &self.size())
21            .finish()
22    }
23}
24
25impl FlashPage {
26    /// Creates a new empty flash page from a `PageInfo`.
27    fn new(page_info: &PageInfo, default_value: u8) -> Self {
28        Self {
29            address: page_info.base_address,
30            data: vec![default_value; page_info.size as usize],
31        }
32    }
33
34    /// Returns the start address of the page.
35    pub fn address(&self) -> u64 {
36        self.address
37    }
38
39    /// Returns the size of the page in bytes.
40    pub fn size(&self) -> u32 {
41        self.data.len() as u32
42    }
43
44    /// Returns the data slice of the page.
45    pub fn data(&self) -> &[u8] {
46        &self.data
47    }
48
49    /// Returns the mut data slice of the page.
50    pub(super) fn data_mut(&mut self) -> &mut [u8] {
51        &mut self.data
52    }
53}
54
55/// The description of a sector in flash.
56#[derive(Clone, PartialEq, Eq, Debug)]
57pub struct FlashSector {
58    pub(crate) address: u64,
59    pub(crate) size: u64,
60}
61
62impl FlashSector {
63    /// Returns the start address of the sector.
64    pub fn address(&self) -> u64 {
65        self.address
66    }
67
68    /// Returns the size of the sector in bytes.
69    pub fn size(&self) -> u64 {
70        self.size
71    }
72}
73
74/// A struct to hold all the information about one region
75/// in the flash that is erased during flashing and has to be restored to its original value afterwards.
76#[derive(Clone, PartialEq, Eq, Debug)]
77pub struct FlashFill {
78    address: u64,
79    size: u64,
80    page_index: usize,
81}
82
83impl FlashFill {
84    /// Returns the start address of the fill.
85    pub fn address(&self) -> u64 {
86        self.address
87    }
88
89    /// Returns the size of the fill in bytes.
90    pub fn size(&self) -> u64 {
91        self.size
92    }
93
94    /// Returns the corresponding page index of the fill.
95    pub fn page_index(&self) -> usize {
96        self.page_index
97    }
98}
99
100/// The built layout of the data in flash.
101#[derive(Debug, Default, Clone, PartialEq, Eq)]
102pub struct FlashLayout {
103    pub(crate) sectors: Vec<FlashSector>,
104    pub(crate) pages: Vec<FlashPage>,
105    pub(crate) fills: Vec<FlashFill>,
106    data_blocks: Vec<FlashDataBlockSpan>,
107}
108
109impl FlashLayout {
110    /// Merge another flash layout into this one.
111    pub fn merge_from(&mut self, other: FlashLayout) {
112        self.sectors.extend(other.sectors);
113        self.pages.extend(other.pages);
114        self.fills.extend(other.fills);
115        self.data_blocks.extend(other.data_blocks);
116    }
117
118    /// List of sectors which are erased during flashing.
119    pub fn sectors(&self) -> &[FlashSector] {
120        &self.sectors
121    }
122
123    /// List of pages which are programmed during flashing.
124    pub fn pages(&self) -> &[FlashPage] {
125        &self.pages
126    }
127
128    /// Get the fills of the flash layout.
129    ///
130    /// This is data which is not written during flashing, but has to be restored to its original value afterwards.
131    pub fn fills(&self) -> &[FlashFill] {
132        &self.fills
133    }
134
135    /// Get the data blocks of the flash layout.
136    ///
137    /// This is the data which is written during flashing.
138    pub fn data_blocks(&self) -> &[FlashDataBlockSpan] {
139        &self.data_blocks
140    }
141}
142
143/// A block of data that is to be written to flash.
144#[derive(Clone, Copy, Debug, PartialEq, Eq)]
145pub struct FlashDataBlockSpan {
146    address: u64,
147    size: u64,
148}
149
150impl FlashDataBlockSpan {
151    /// Get the start address of the block.
152    pub fn address(&self) -> u64 {
153        self.address
154    }
155
156    /// Returns the size of the block in bytes.
157    pub fn size(&self) -> u64 {
158        self.size
159    }
160}
161
162/// A helper structure to build a flash layout from a set of data blocks.
163#[derive(Default)]
164pub(super) struct FlashBuilder {
165    pub(super) data: BTreeMap<u64, Vec<u8>>,
166}
167
168impl FlashBuilder {
169    /// Creates a new `FlashBuilder` with empty data.
170    pub(super) fn new() -> Self {
171        Self {
172            data: BTreeMap::new(),
173        }
174    }
175
176    /// Stages a chunk of data to be programmed.
177    ///
178    /// The chunk can cross flash boundaries as long as one flash region connects to another flash region.
179    pub fn add_data(&mut self, address: u64, data: &[u8]) -> Result<(), FlashError> {
180        // Ignore zero-length stuff
181        if data.is_empty() {
182            return Ok(());
183        }
184
185        // Check the new data doesn't overlap to the right.
186        if let Some((&next_addr, next_data)) = self.data.range(address..).next()
187            && address + (data.len() as u64) > next_addr
188        {
189            return Err(FlashError::DataOverlaps {
190                added_addresses: address..address + data.len() as u64,
191                existing_addresses: next_addr..next_addr + next_data.len() as u64,
192            });
193        }
194
195        // Check the new data doesn't overlap to the left.
196        if let Some((&prev_addr, prev_data)) = self.data.range_mut(..address).next_back() {
197            let prev_end = prev_addr + (prev_data.len() as u64);
198
199            if prev_end > address {
200                return Err(FlashError::DataOverlaps {
201                    added_addresses: address..address + data.len() as u64,
202                    existing_addresses: prev_addr..prev_addr + prev_data.len() as u64,
203                });
204            }
205
206            // Optimization: If it exactly touches the left neighbor, extend it instead.
207            if prev_end == address {
208                prev_data.extend(data);
209                return Ok(());
210            }
211        }
212
213        // Add it
214        self.data.insert(address, data.to_vec());
215
216        Ok(())
217    }
218
219    /// Check whether there is staged data for a given address range.
220    pub(crate) fn has_data_in_range(&self, range: &Range<u64>) -> bool {
221        self.data_in_range(range).next().is_some()
222    }
223
224    /// Iterate staged data for a given address range.
225    ///
226    /// Data is returned in ascending address order, and is guaranteed not to overlap.
227    /// If a staged chunk is not fully contained in the range, only the contained part is
228    /// returned. ie for each returned item (addr, data), it's guaranteed that the condition
229    /// `start <= addr && addr + data.len() <= end` upholds.
230    pub(crate) fn data_in_range<'s>(
231        &'s self,
232        range: &Range<u64>,
233    ) -> impl Iterator<Item = (u64, &'s [u8])> + use<'s> {
234        let range = range.clone();
235
236        let mut adjusted_start = range.start;
237
238        // Check if the immediately preceding data overlaps with the wanted range.
239        // If so, adjust the iteration start so it is included.
240        if let Some((&prev_addr, prev_data)) = self.data.range(..range.start).next_back()
241            && prev_addr + (prev_data.len() as u64) > range.start
242        {
243            adjusted_start = prev_addr;
244        }
245
246        self.data
247            .range(adjusted_start..range.end)
248            .map(move |(&addr, data)| {
249                let mut addr = addr;
250                let mut data = &data[..];
251
252                // Cut chunk from the left if it starts before `start`.
253                if addr < range.start {
254                    data = &data[(range.start - addr) as usize..];
255                    addr = range.start;
256                }
257
258                // Cut chunk from the right if it ends before `end`.
259                if addr + (data.len()) as u64 > range.end {
260                    data = &data[..(range.end - addr) as usize];
261                }
262
263                (addr, data)
264            })
265    }
266
267    /// Layouts the contents of a flash memory according to the contents of the flash loader.
268    pub(super) fn build_sectors_and_pages(
269        &self,
270        region: &NvmRegion,
271        flash_algorithm: &FlashAlgorithm,
272        include_empty_pages: bool,
273    ) -> Result<FlashLayout, FlashError> {
274        let mut layout = FlashLayout::default();
275
276        for info in flash_algorithm.iter_sectors() {
277            let range = info.address_range();
278
279            // Ignore the sector if it's outside the NvmRegion.
280            if !region.range.contains_range(&range) {
281                continue;
282            }
283
284            let page = flash_algorithm.page_info(info.base_address).unwrap();
285            let page_range = page.address_range();
286            let sector_has_data = self.has_data_in_range(&range);
287            let page_has_data = self.has_data_in_range(&page_range);
288
289            // Ignore if neither the sector nor the page contain any data.
290            if !sector_has_data && !page_has_data {
291                continue;
292            }
293
294            layout.sectors.push(FlashSector {
295                address: info.base_address,
296                size: info.size,
297            })
298        }
299
300        for info in flash_algorithm.iter_pages() {
301            let range = info.address_range();
302
303            // Ignore the page if it's outside the NvmRegion.
304            if !region.range.contains_range(&range) {
305                continue;
306            }
307
308            let sector = flash_algorithm.sector_info(info.base_address).unwrap();
309            let sector_range = sector.address_range();
310            let sector_has_data = self.has_data_in_range(&sector_range);
311            let page_has_data = self.has_data_in_range(&range);
312
313            // If include_empty_pages, include the page if there's data in is sector, even if there's no data in the page.
314            if !page_has_data && (!include_empty_pages || !sector_has_data) {
315                continue;
316            }
317
318            let mut page =
319                FlashPage::new(&info, flash_algorithm.flash_properties.erased_byte_value);
320
321            let mut fill_start_addr = info.base_address;
322
323            // Loop over all datablocks in the page.
324            for (address, data) in self.data_in_range(&range) {
325                // Copy data into the page buffer
326                let offset = (address - info.base_address) as usize;
327                page.data[offset..offset + data.len()].copy_from_slice(data);
328
329                // Fill the hole between the previous data block (or page start if there are no blocks) and current block.
330                if address > fill_start_addr {
331                    layout.fills.push(FlashFill {
332                        address: fill_start_addr,
333                        size: address - fill_start_addr,
334                        page_index: layout.pages.len(),
335                    });
336                }
337                fill_start_addr = address + data.len() as u64;
338            }
339
340            // Fill the hole between the last data block (or page start if there are no blocks) and page end.
341            if fill_start_addr < range.end {
342                layout.fills.push(FlashFill {
343                    address: fill_start_addr,
344                    size: range.end - fill_start_addr,
345                    page_index: layout.pages.len(),
346                });
347            }
348
349            layout.pages.push(page);
350        }
351
352        for (address, data) in self.data_in_range(&region.range) {
353            layout.data_blocks.push(FlashDataBlockSpan {
354                address,
355                size: data.len() as u64,
356            });
357        }
358
359        // Return the finished flash layout.
360        Ok(layout)
361    }
362}
363
364#[cfg(test)]
365mod tests {
366    use probe_rs_target::{FlashProperties, MemoryAccess, SectorDescription};
367
368    use super::*;
369
370    fn assemble_demo_flash1() -> (NvmRegion, FlashAlgorithm) {
371        let sd = SectorDescription {
372            size: 4096,
373            address: 0,
374        };
375
376        let flash_algorithm = FlashAlgorithm {
377            flash_properties: FlashProperties {
378                address_range: 0..1 << 16,
379                page_size: 1024,
380                erased_byte_value: 255,
381                program_page_timeout: 200,
382                erase_sector_timeout: 200,
383                sectors: vec![sd],
384            },
385            ..Default::default()
386        };
387
388        let region = NvmRegion {
389            name: Some("FLASH".into()),
390            access: Some(MemoryAccess {
391                boot: true,
392                ..Default::default()
393            }),
394            range: 0..1 << 16,
395            cores: vec!["main".into()],
396            is_alias: false,
397        };
398
399        (region, flash_algorithm)
400    }
401
402    fn assemble_demo_flash2() -> (NvmRegion, FlashAlgorithm) {
403        let sd = SectorDescription {
404            size: 128,
405            address: 0,
406        };
407
408        let flash_algorithm = FlashAlgorithm {
409            flash_properties: FlashProperties {
410                address_range: 0..1 << 16,
411                page_size: 1024,
412                erased_byte_value: 255,
413                program_page_timeout: 200,
414                erase_sector_timeout: 200,
415                sectors: vec![sd],
416            },
417            ..Default::default()
418        };
419
420        let region = NvmRegion {
421            name: Some("FLASH".into()),
422            access: Some(MemoryAccess {
423                boot: true,
424                ..Default::default()
425            }),
426            range: 0..1 << 16,
427            cores: vec!["main".into()],
428            is_alias: false,
429        };
430
431        (region, flash_algorithm)
432    }
433
434    #[test]
435    fn single_byte_in_single_page() {
436        let (region, flash_algorithm) = assemble_demo_flash1();
437        let mut flash_builder = FlashBuilder::new();
438        flash_builder.add_data(0, &[42]).unwrap();
439        let flash_layout = flash_builder
440            .build_sectors_and_pages(&region, &flash_algorithm, true)
441            .unwrap();
442
443        let erased_byte_value = flash_algorithm.flash_properties.erased_byte_value;
444
445        assert_eq!(
446            flash_layout,
447            FlashLayout {
448                sectors: vec![FlashSector {
449                    address: 0x0000,
450                    size: 0x1000,
451                },],
452                pages: vec![
453                    FlashPage {
454                        address: 0x0000,
455                        data: {
456                            let mut data = vec![erased_byte_value; 1024];
457                            data[0] = 42;
458                            data
459                        },
460                    },
461                    FlashPage {
462                        address: 0x0400,
463                        data: vec![erased_byte_value; 1024],
464                    },
465                    FlashPage {
466                        address: 0x0800,
467                        data: vec![erased_byte_value; 1024],
468                    },
469                    FlashPage {
470                        address: 0x0C00,
471                        data: vec![erased_byte_value; 1024],
472                    },
473                ],
474                fills: vec![
475                    FlashFill {
476                        address: 0x0001,
477                        size: 0x03FF,
478                        page_index: 0,
479                    },
480                    FlashFill {
481                        address: 0x0400,
482                        size: 0x0400,
483                        page_index: 1,
484                    },
485                    FlashFill {
486                        address: 0x0800,
487                        size: 0x0400,
488                        page_index: 2,
489                    },
490                    FlashFill {
491                        address: 0x0C00,
492                        size: 0x0400,
493                        page_index: 3,
494                    }
495                ],
496                data_blocks: vec![FlashDataBlockSpan {
497                    address: 0,
498                    size: 1,
499                }],
500            }
501        )
502    }
503
504    #[test]
505    fn equal_bytes_full_single_page() {
506        let (region, flash_algorithm) = assemble_demo_flash1();
507        let mut flash_builder = FlashBuilder::new();
508        flash_builder.add_data(0, &[42; 1024]).unwrap();
509        let flash_layout = flash_builder
510            .build_sectors_and_pages(&region, &flash_algorithm, true)
511            .unwrap();
512
513        let erased_byte_value = flash_algorithm.flash_properties.erased_byte_value;
514
515        assert_eq!(
516            flash_layout,
517            FlashLayout {
518                sectors: vec![FlashSector {
519                    address: 0x0000,
520                    size: 0x1000,
521                },],
522                pages: vec![
523                    FlashPage {
524                        address: 0x0000,
525                        data: vec![42; 1024],
526                    },
527                    FlashPage {
528                        address: 0x0400,
529                        data: vec![erased_byte_value; 1024],
530                    },
531                    FlashPage {
532                        address: 0x0800,
533                        data: vec![erased_byte_value; 1024],
534                    },
535                    FlashPage {
536                        address: 0x0C00,
537                        data: vec![erased_byte_value; 1024],
538                    },
539                ],
540                fills: vec![
541                    FlashFill {
542                        address: 0x0400,
543                        size: 0x0400,
544                        page_index: 1,
545                    },
546                    FlashFill {
547                        address: 0x0800,
548                        size: 0x0400,
549                        page_index: 2,
550                    },
551                    FlashFill {
552                        address: 0x0C00,
553                        size: 0x0400,
554                        page_index: 3,
555                    }
556                ],
557                data_blocks: vec![FlashDataBlockSpan {
558                    address: 0,
559                    size: 1024,
560                }],
561            }
562        )
563    }
564
565    #[test]
566    fn equal_bytes_one_full_page_one_page_one_byte() {
567        let (region, flash_algorithm) = assemble_demo_flash1();
568        let mut flash_builder = FlashBuilder::new();
569        flash_builder.add_data(0, &[42; 1025]).unwrap();
570        let flash_layout = flash_builder
571            .build_sectors_and_pages(&region, &flash_algorithm, true)
572            .unwrap();
573
574        let erased_byte_value = flash_algorithm.flash_properties.erased_byte_value;
575
576        assert_eq!(
577            flash_layout,
578            FlashLayout {
579                sectors: vec![FlashSector {
580                    address: 0x0000,
581                    size: 0x1000,
582                },],
583                pages: vec![
584                    FlashPage {
585                        address: 0x0000,
586                        data: vec![42; 1024],
587                    },
588                    FlashPage {
589                        address: 0x0400,
590                        data: {
591                            let mut data = vec![erased_byte_value; 1024];
592                            data[0] = 42;
593                            data
594                        },
595                    },
596                    FlashPage {
597                        address: 0x0800,
598                        data: vec![erased_byte_value; 1024],
599                    },
600                    FlashPage {
601                        address: 0x0C00,
602                        data: vec![erased_byte_value; 1024],
603                    },
604                ],
605                fills: vec![
606                    FlashFill {
607                        address: 0x0401,
608                        size: 0x03FF,
609                        page_index: 1,
610                    },
611                    FlashFill {
612                        address: 0x0800,
613                        size: 0x0400,
614                        page_index: 2,
615                    },
616                    FlashFill {
617                        address: 0x0C00,
618                        size: 0x0400,
619                        page_index: 3,
620                    }
621                ],
622                data_blocks: vec![FlashDataBlockSpan {
623                    address: 0,
624                    size: 1025,
625                }],
626            }
627        )
628    }
629
630    #[test]
631    fn equal_bytes_one_full_page_one_page_one_byte_skip_fill() {
632        let (region, flash_algorithm) = assemble_demo_flash1();
633        let mut flash_builder = FlashBuilder::new();
634        flash_builder.add_data(0, &[42; 1025]).unwrap();
635        let flash_layout = flash_builder
636            .build_sectors_and_pages(&region, &flash_algorithm, false)
637            .unwrap();
638
639        let erased_byte_value = flash_algorithm.flash_properties.erased_byte_value;
640
641        assert_eq!(
642            flash_layout,
643            FlashLayout {
644                sectors: vec![FlashSector {
645                    address: 0x0000,
646                    size: 0x1000,
647                },],
648                pages: vec![
649                    FlashPage {
650                        address: 0x0000,
651                        data: vec![42; 1024],
652                    },
653                    FlashPage {
654                        address: 0x0400,
655                        data: {
656                            let mut data = vec![erased_byte_value; 1024];
657                            data[0] = 42;
658                            data
659                        },
660                    },
661                ],
662                fills: vec![FlashFill {
663                    address: 0x0401,
664                    size: 0x03FF,
665                    page_index: 1,
666                },],
667                data_blocks: vec![FlashDataBlockSpan {
668                    address: 0,
669                    size: 1025,
670                }],
671            }
672        )
673    }
674
675    #[test]
676    fn equal_bytes_one_page_from_offset_span_two_pages() {
677        let (region, flash_algorithm) = assemble_demo_flash1();
678        let mut flash_builder = FlashBuilder::new();
679        flash_builder.add_data(42, &[42; 1024]).unwrap();
680        let flash_layout = flash_builder
681            .build_sectors_and_pages(&region, &flash_algorithm, true)
682            .unwrap();
683
684        let erased_byte_value = flash_algorithm.flash_properties.erased_byte_value;
685
686        assert_eq!(
687            flash_layout,
688            FlashLayout {
689                sectors: vec![FlashSector {
690                    address: 0x000000,
691                    size: 0x001000,
692                },],
693                pages: vec![
694                    FlashPage {
695                        address: 0x000000,
696                        data: {
697                            let mut data = vec![42; 1024];
698                            for d in &mut data[..42] {
699                                *d = erased_byte_value;
700                            }
701                            data
702                        },
703                    },
704                    FlashPage {
705                        address: 0x000400,
706                        data: {
707                            let mut data = vec![erased_byte_value; 1024];
708                            for d in &mut data[..42] {
709                                *d = 42;
710                            }
711                            data
712                        },
713                    },
714                    FlashPage {
715                        address: 0x000800,
716                        data: vec![erased_byte_value; 1024],
717                    },
718                    FlashPage {
719                        address: 0x000C00,
720                        data: vec![erased_byte_value; 1024],
721                    },
722                ],
723                fills: vec![
724                    FlashFill {
725                        address: 0x000000,
726                        size: 0x00002A,
727                        page_index: 0,
728                    },
729                    FlashFill {
730                        address: 0x00042A,
731                        size: 0x0003D6,
732                        page_index: 1,
733                    },
734                    FlashFill {
735                        address: 0x000800,
736                        size: 0x000400,
737                        page_index: 2,
738                    },
739                    FlashFill {
740                        address: 0x000C00,
741                        size: 0x000400,
742                        page_index: 3,
743                    },
744                ],
745                data_blocks: vec![FlashDataBlockSpan {
746                    address: 42,
747                    size: 1024,
748                },],
749            }
750        )
751    }
752
753    #[test]
754    fn equal_bytes_four_and_a_half_pages_two_sectors() {
755        let (region, flash_algorithm) = assemble_demo_flash1();
756        let mut flash_builder = FlashBuilder::new();
757        flash_builder.add_data(0, &[42; 5024]).unwrap();
758        let flash_layout = flash_builder
759            .build_sectors_and_pages(&region, &flash_algorithm, true)
760            .unwrap();
761
762        let erased_byte_value = flash_algorithm.flash_properties.erased_byte_value;
763
764        assert_eq!(
765            flash_layout,
766            FlashLayout {
767                sectors: vec![
768                    FlashSector {
769                        address: 0x000000,
770                        size: 0x001000,
771                    },
772                    FlashSector {
773                        address: 0x001000,
774                        size: 0x001000,
775                    },
776                ],
777                pages: vec![
778                    FlashPage {
779                        address: 0x000000,
780                        data: vec![42; 1024],
781                    },
782                    FlashPage {
783                        address: 0x000400,
784                        data: vec![42; 1024],
785                    },
786                    FlashPage {
787                        address: 0x000800,
788                        data: vec![42; 1024],
789                    },
790                    FlashPage {
791                        address: 0x000C00,
792                        data: vec![42; 1024],
793                    },
794                    FlashPage {
795                        address: 0x001000,
796                        data: {
797                            let mut data = vec![erased_byte_value; 1024];
798                            for d in &mut data[..928] {
799                                *d = 42;
800                            }
801                            data
802                        },
803                    },
804                    FlashPage {
805                        address: 0x001400,
806                        data: vec![erased_byte_value; 1024],
807                    },
808                    FlashPage {
809                        address: 0x001800,
810                        data: vec![erased_byte_value; 1024],
811                    },
812                    FlashPage {
813                        address: 0x001C00,
814                        data: vec![erased_byte_value; 1024],
815                    },
816                ],
817                fills: vec![
818                    FlashFill {
819                        address: 0x0013A0,
820                        size: 0x000060,
821                        page_index: 4,
822                    },
823                    FlashFill {
824                        address: 0x001400,
825                        size: 0x000400,
826                        page_index: 5,
827                    },
828                    FlashFill {
829                        address: 0x001800,
830                        size: 0x000400,
831                        page_index: 6,
832                    },
833                    FlashFill {
834                        address: 0x001C00,
835                        size: 0x000400,
836                        page_index: 7,
837                    },
838                ],
839                data_blocks: vec![FlashDataBlockSpan {
840                    address: 0,
841                    size: 5024,
842                },],
843            }
844        )
845    }
846
847    #[test]
848    fn equal_bytes_in_two_data_chunks_multiple_sectors() {
849        let (region, flash_algorithm) = assemble_demo_flash1();
850        let mut flash_builder = FlashBuilder::new();
851        flash_builder.add_data(0, &[42; 5024]).unwrap();
852        flash_builder.add_data(7860, &[42; 5024]).unwrap();
853        let flash_layout = flash_builder
854            .build_sectors_and_pages(&region, &flash_algorithm, true)
855            .unwrap();
856
857        let erased_byte_value = flash_algorithm.flash_properties.erased_byte_value;
858
859        assert_eq!(
860            flash_layout,
861            FlashLayout {
862                sectors: vec![
863                    FlashSector {
864                        address: 0x000000,
865                        size: 0x001000,
866                    },
867                    FlashSector {
868                        address: 0x001000,
869                        size: 0x001000,
870                    },
871                    FlashSector {
872                        address: 0x002000,
873                        size: 0x001000,
874                    },
875                    FlashSector {
876                        address: 0x003000,
877                        size: 0x001000,
878                    },
879                ],
880                pages: vec![
881                    FlashPage {
882                        address: 0x000000,
883                        data: vec![42; 1024],
884                    },
885                    FlashPage {
886                        address: 0x000400,
887                        data: vec![42; 1024],
888                    },
889                    FlashPage {
890                        address: 0x000800,
891                        data: vec![42; 1024],
892                    },
893                    FlashPage {
894                        address: 0x000C00,
895                        data: vec![42; 1024],
896                    },
897                    FlashPage {
898                        address: 0x001000,
899                        data: {
900                            let mut data = vec![42; 1024];
901                            for d in &mut data[928..1024] {
902                                *d = erased_byte_value;
903                            }
904                            data
905                        },
906                    },
907                    FlashPage {
908                        address: 0x001400,
909                        data: vec![erased_byte_value; 1024],
910                    },
911                    FlashPage {
912                        address: 0x001800,
913                        data: vec![erased_byte_value; 1024],
914                    },
915                    FlashPage {
916                        address: 0x001C00,
917                        data: {
918                            let mut data = vec![42; 1024];
919                            for d in &mut data[..692] {
920                                *d = erased_byte_value;
921                            }
922                            data
923                        },
924                    },
925                    FlashPage {
926                        address: 0x002000,
927                        data: vec![42; 1024],
928                    },
929                    FlashPage {
930                        address: 0x002400,
931                        data: vec![42; 1024],
932                    },
933                    FlashPage {
934                        address: 0x002800,
935                        data: vec![42; 1024],
936                    },
937                    FlashPage {
938                        address: 0x002C00,
939                        data: vec![42; 1024],
940                    },
941                    FlashPage {
942                        address: 0x003000,
943                        data: {
944                            let mut data = vec![42; 1024];
945                            for d in &mut data[596..1024] {
946                                *d = erased_byte_value;
947                            }
948                            data
949                        },
950                    },
951                    FlashPage {
952                        address: 0x003400,
953                        data: vec![erased_byte_value; 1024],
954                    },
955                    FlashPage {
956                        address: 0x003800,
957                        data: vec![erased_byte_value; 1024],
958                    },
959                    FlashPage {
960                        address: 0x003C00,
961                        data: vec![erased_byte_value; 1024],
962                    },
963                ],
964                fills: vec![
965                    FlashFill {
966                        address: 0x0013A0,
967                        size: 0x000060,
968                        page_index: 4,
969                    },
970                    FlashFill {
971                        address: 0x001400,
972                        size: 0x000400,
973                        page_index: 5,
974                    },
975                    FlashFill {
976                        address: 0x001800,
977                        size: 0x000400,
978                        page_index: 6,
979                    },
980                    FlashFill {
981                        address: 0x001C00,
982                        size: 0x0002B4,
983                        page_index: 7,
984                    },
985                    FlashFill {
986                        address: 0x003254,
987                        size: 0x0001AC,
988                        page_index: 12,
989                    },
990                    FlashFill {
991                        address: 0x003400,
992                        size: 0x000400,
993                        page_index: 13,
994                    },
995                    FlashFill {
996                        address: 0x003800,
997                        size: 0x000400,
998                        page_index: 14,
999                    },
1000                    FlashFill {
1001                        address: 0x003C00,
1002                        size: 0x000400,
1003                        page_index: 15,
1004                    },
1005                ],
1006                data_blocks: vec![
1007                    FlashDataBlockSpan {
1008                        address: 0,
1009                        size: 5024,
1010                    },
1011                    FlashDataBlockSpan {
1012                        address: 7860,
1013                        size: 5024,
1014                    },
1015                ],
1016            }
1017        )
1018    }
1019
1020    #[test]
1021    fn two_data_chunks_multiple_sectors_smaller_than_page() {
1022        let (region, flash_algorithm) = assemble_demo_flash2();
1023        let mut flash_builder = FlashBuilder::new();
1024        flash_builder.add_data(0, &[42; 5024]).unwrap();
1025        flash_builder.add_data(7860, &[42; 5024]).unwrap();
1026        let flash_layout = flash_builder
1027            .build_sectors_and_pages(&region, &flash_algorithm, true)
1028            .unwrap();
1029
1030        let erased_byte_value = flash_algorithm.flash_properties.erased_byte_value;
1031
1032        assert_eq!(
1033            flash_layout,
1034            FlashLayout {
1035                sectors: {
1036                    let mut sectors = Vec::with_capacity(88);
1037                    for i in 0..40 {
1038                        sectors.push(FlashSector {
1039                            address: 128 * i as u64,
1040                            size: 0x000080,
1041                        });
1042                    }
1043
1044                    for i in 56..104 {
1045                        sectors.push(FlashSector {
1046                            address: 128 * i as u64,
1047                            size: 0x000080,
1048                        });
1049                    }
1050
1051                    sectors
1052                },
1053                pages: vec![
1054                    FlashPage {
1055                        address: 0x000000,
1056                        data: vec![42; 1024],
1057                    },
1058                    FlashPage {
1059                        address: 0x000400,
1060                        data: vec![42; 1024],
1061                    },
1062                    FlashPage {
1063                        address: 0x000800,
1064                        data: vec![42; 1024],
1065                    },
1066                    FlashPage {
1067                        address: 0x000C00,
1068                        data: vec![42; 1024],
1069                    },
1070                    FlashPage {
1071                        address: 0x001000,
1072                        data: {
1073                            let mut data = vec![42; 1024];
1074                            for d in &mut data[928..1024] {
1075                                *d = erased_byte_value;
1076                            }
1077                            data
1078                        },
1079                    },
1080                    FlashPage {
1081                        address: 0x001C00,
1082                        data: {
1083                            let mut data = vec![42; 1024];
1084                            for d in &mut data[..692] {
1085                                *d = erased_byte_value;
1086                            }
1087                            data
1088                        },
1089                    },
1090                    FlashPage {
1091                        address: 0x002000,
1092                        data: vec![42; 1024],
1093                    },
1094                    FlashPage {
1095                        address: 0x002400,
1096                        data: vec![42; 1024],
1097                    },
1098                    FlashPage {
1099                        address: 0x002800,
1100                        data: vec![42; 1024],
1101                    },
1102                    FlashPage {
1103                        address: 0x002C00,
1104                        data: vec![42; 1024],
1105                    },
1106                    FlashPage {
1107                        address: 0x003000,
1108                        data: {
1109                            let mut data = vec![42; 1024];
1110                            for d in &mut data[596..1024] {
1111                                *d = erased_byte_value;
1112                            }
1113                            data
1114                        },
1115                    },
1116                ],
1117                fills: vec![
1118                    FlashFill {
1119                        address: 0x0013A0,
1120                        size: 0x000060,
1121                        page_index: 4,
1122                    },
1123                    FlashFill {
1124                        address: 0x001C00,
1125                        size: 0x0002B4,
1126                        page_index: 5,
1127                    },
1128                    FlashFill {
1129                        address: 0x003254,
1130                        size: 0x0001AC,
1131                        page_index: 10,
1132                    }
1133                ],
1134                data_blocks: vec![
1135                    FlashDataBlockSpan {
1136                        address: 0,
1137                        size: 5024,
1138                    },
1139                    FlashDataBlockSpan {
1140                        address: 7860,
1141                        size: 5024,
1142                    },
1143                ],
1144            }
1145        )
1146    }
1147}