atsamd_hal/peripherals/nvm/
mod.rs

1//! # Non-volatile Memory Controller
2//!
3//! This module allows users to interact with non-volatile memory controller.
4//!
5//! Nvmctrl is an intermediary between memory buses and physical non-volatile
6//! memory. It provides means of managing a flash memory content, its properties
7//! (cache, wait states, bootloader blocks protection), power management and
8//! address remapping if necessary (in case bank mechanism is used). It also
9//! provides an indirection mechanism to achieve non-volatile RAM-like memory
10//! within last sectors of a physical flash (More in [`smart_eeprom`] module).
11//!
12//! NVM supports splitting flash into two sections (opt-in feature) called
13//! banks. Bank considered active is mapped to _virtual_ address `0x0`, meaning
14//! it contains currently executed application. Through NVM command & control
15//! interface, banks can be swapped and MCU reset, so the firmware from the
16//! other bank will run after restart.
17//!
18//! Module features:
19//! - Erase & write over non-volatile memory in a device.
20//! - Swap banks
21#![warn(missing_docs)]
22
23pub mod smart_eeprom;
24
25use crate::pac::Nvmctrl;
26pub use crate::pac::nvmctrl::ctrla::Prmselect;
27use crate::pac::nvmctrl::ctrlb::Cmdselect;
28use core::num::NonZeroU32;
29use core::ops::Range;
30use core::ptr::addr_of;
31
32use bitfield::bitfield;
33
34/// Retrieve a total NVM size using HW registers
35#[inline(always)]
36pub fn retrieve_flash_size() -> u32 {
37    static mut FLASHSIZE: Option<NonZeroU32> = None;
38    // Safety: Lazy initialization of a static variable - interactions with
39    // `Option<NonZeroU32>` should be atomic
40    unsafe {
41        match FLASHSIZE {
42            Some(x) => x.into(),
43            None => {
44                let nvm = &*Nvmctrl::ptr();
45                let nvm_params = nvm.param().read();
46                if !nvm_params.psz().is_512() {
47                    unreachable!("NVM page size is always expected to be 512 bytes");
48                }
49                let nvm_pages = nvm_params.nvmp().bits() as u32;
50                let flash_size = nvm_pages * 512;
51                // Safety: `flash_size` will never be 0
52                FLASHSIZE = Some(NonZeroU32::new_unchecked(flash_size));
53                flash_size
54            }
55        }
56    }
57}
58
59/// Retrieve a bank size using HW registers
60#[inline(always)]
61pub fn retrieve_bank_size() -> u32 {
62    retrieve_flash_size() / 2
63}
64
65/// Size of a page in bytes
66pub const PAGESIZE: u32 = 512;
67
68/// Size of one block
69pub const BLOCKSIZE: u32 = 512 * 16;
70
71/// Size of a quad word
72pub const QUADWORDSIZE: u32 = 16;
73
74/// Non-volatile memory controller
75pub struct Nvm {
76    /// PAC peripheral
77    nvm: Nvmctrl,
78}
79
80/// Errors generated by the NVM peripheral
81#[derive(Debug, Clone, Copy, Eq, PartialEq)]
82#[cfg_attr(feature = "defmt", derive(defmt::Format))]
83pub enum PeripheralError {
84    /// NVM error
85    NvmError,
86    /// Single ECC error
87    EccSingleError,
88    /// Dual ECC error
89    EccDualError,
90    /// Locked error
91    LockError,
92    /// Programming error
93    ProgrammingError,
94    /// Address error
95    AddressError,
96}
97
98/// Driver errors
99#[non_exhaustive]
100#[derive(Debug, Clone, Copy, Eq, PartialEq)]
101#[cfg_attr(feature = "defmt", derive(defmt::Format))]
102pub enum Error {
103    /// Address range outside of flash
104    NonFlash,
105    /// Target sector is protected
106    Protected,
107    /// Memory region is used by SmartEEPROM
108    SmartEepromArea,
109    /// Errors generated by hardware
110    Peripheral(PeripheralError),
111    /// The DSU failed in some way
112    Dsu(super::dsu::Error),
113    /// An alignment requirement was not fulfilled
114    Alignment,
115}
116
117/// Physical flash banks
118#[derive(PartialEq, Debug)]
119pub enum PhysicalBank {
120    /// Flash bank A
121    A,
122    /// Flash bank B
123    B,
124}
125
126#[derive(PartialEq, Debug, Copy, Clone)]
127/// Flash banks identified by which one we boot from.
128///
129/// Memory layout:
130/// ```text
131/// [  Active bank  | Inactive bank ]
132/// ^               ^               ^
133/// 0x0000_0000     flash_size/2    flash_size
134/// ```
135pub enum Bank {
136    /// Bank that is mapped to 0x0000_0000
137    ///
138    /// Active bank occupies first half of the flash memory.
139    Active,
140    /// Bank that is not mapped to 0x0000_0000
141    ///
142    /// Inactive bank occupies second half of the flash memory.
143    Inactive,
144}
145
146impl Bank {
147    /// Provides the address of the bank
148    #[inline]
149    pub fn address(&self) -> u32 {
150        match self {
151            Bank::Active => 0,
152            Bank::Inactive => retrieve_bank_size(),
153        }
154    }
155
156    /// Provides bank length in bytes
157    #[inline]
158    pub fn length(&self) -> u32 {
159        retrieve_bank_size()
160    }
161}
162
163/// NVM result type
164pub type Result<T> = core::result::Result<T, Error>;
165
166impl Nvm {
167    /// Pointer to the userpage region of the flash memory
168    ///
169    /// Note: *Never* call `core::ptr::read_volatile` on this pointer, yields
170    /// very poor codegen (around 9 KiB bytes of code)
171    pub const USERPAGE_ADDR: *const [u8; 512] = 0x0080_4000 as _;
172
173    /// Create a new NVM controller or handle failure from DSU
174    #[inline]
175    pub fn new(nvm: Nvmctrl) -> Self {
176        Self { nvm }
177    }
178
179    /// Raw access to the registers.
180    ///
181    /// # Safety
182    ///
183    /// The abstraction assumes that it has exclusive ownership of the
184    /// registers. Direct access can break such assumptions.
185    pub unsafe fn registers(&self) -> &Nvmctrl {
186        &self.nvm
187    }
188
189    /// Swap the flash banks. The processor will be reset, after which the
190    /// inactive bank will become the active bank.
191    ///
192    /// # Safety
193    ///
194    /// Ensure there is a working, memory safe program in place in the inactive
195    /// bank before calling.
196    #[inline]
197    pub unsafe fn bank_swap(&mut self) -> ! {
198        let _ = self.command_sync(Cmdselect::Bkswrst);
199        // The reset will happen atomically with the rest of the command, so getting
200        // here is an error.
201        unreachable!();
202    }
203
204    /// Set the power reduction mode
205    #[inline]
206    pub fn power_reduction_mode(&mut self, prm: Prmselect) {
207        self.nvm.ctrla().modify(|_, w| w.prm().variant(prm));
208    }
209
210    /// Check if the flash is boot protected
211    #[inline]
212    pub fn is_boot_protected(&self) -> bool {
213        !self.nvm.status().read().bpdis().bit()
214    }
215
216    /// Get first bank
217    #[inline]
218    pub fn first_bank(&self) -> PhysicalBank {
219        if self.nvm.status().read().afirst().bit() {
220            PhysicalBank::A
221        } else {
222            PhysicalBank::B
223        }
224    }
225
226    /// Set address for reading/writing
227    #[inline]
228    fn set_address(&mut self, address: u32) {
229        unsafe {
230            self.nvm
231                .addr()
232                .write(|w| w.addr().bits(address & 0x00ff_ffff));
233        }
234    }
235
236    /// Execute a command, wait until it is done and check error states
237    #[inline]
238    fn command_sync(&mut self, command: Cmdselect) -> Result<()> {
239        // Wait until STATUS.READY
240        while !self.nvm.status().read().ready().bit() {}
241
242        self.nvm
243            .ctrlb()
244            .write(|w| w.cmdex().key().cmd().variant(command));
245
246        // Wait until INTFLAG.DONE
247        while !self.nvm.intflag().read().done().bit() {}
248        // Clear INTFLAG.DONE
249        self.nvm.intflag().write(|w| w.done().set_bit());
250
251        self.manage_error_states()
252    }
253
254    /// Read the peripheral state to check error flags and clear the up
255    /// afterwards
256    #[inline]
257    fn manage_error_states(&mut self) -> Result<()> {
258        let read_intflag = self.nvm.intflag().read();
259        // Check ADDRE and LOCKE first as it is more specific than PROGE
260        let state = if read_intflag.addre().bit_is_set() {
261            Err(Error::Peripheral(PeripheralError::AddressError))
262        } else if read_intflag.locke().bit_is_set() {
263            Err(Error::Peripheral(PeripheralError::LockError))
264        } else if read_intflag.proge().bit_is_set() {
265            Err(Error::Peripheral(PeripheralError::ProgrammingError))
266        } else {
267            Ok(())
268        };
269
270        // Clear error flags
271        self.nvm
272            .intflag()
273            .write(|w| w.addre().set_bit().locke().set_bit().proge().set_bit());
274
275        state
276    }
277
278    /// Read the user page from the flash memory
279    #[inline]
280    pub fn read_userpage(&self) -> Userpage {
281        let mut userpage = RawUserpage([0_u8; 512]);
282        // Safety:
283        // - Nvm is a singleton because it is constructed using the PAC singleton
284        //   Nvmctrl.
285        // - You need Nvm or Nvmctrl to modify the memory, so the &self singleton is
286        //   enough to prevent concurrent modification.
287        // - Underlying [u8; 512] has no reserved bit patterns.
288        // - The pointer is aligned.
289        // Note: userpage is accessed through the iterator in order to avoid poor
290        // codegen for `read_volatile` call on the array pointer.
291        userpage
292            .0
293            .iter_mut()
294            .zip((0..512).map(|i| unsafe {
295                Self::USERPAGE_ADDR
296                    .cast::<u8>()
297                    .wrapping_offset(i)
298                    .read_volatile()
299            }))
300            .for_each(|(l, r)| *l = r);
301        userpage
302    }
303
304    /// Modify the NVM User Page (aka User Row/UROW)
305    ///
306    /// User is expected to provide a closure that modifies the user page
307    /// according to the user's needs.
308    ///
309    /// This method will read the current user page, call the closure on it,
310    /// *erase the page in the flash memory* and *write it* back again.
311    ///
312    /// Erasure and flashing is skipped if the userpage stays the same after
313    /// calling the closure on it.
314    ///
315    /// # Safety
316    ///
317    /// Even though factory calibration settings are not modifiable via setters
318    /// they can be still mutated via raw access to the `userpage.0` field.
319    ///
320    /// Power loss between the erase and the write will result in *data loss*.
321    ///
322    /// Thus, users are advised to *backup factory calibration settings* before
323    /// mutating the user page!
324    ///
325    /// If these settings are erased, device might stop behaving correctly!
326    #[inline]
327    pub unsafe fn modify_userpage(
328        &mut self,
329        f: impl FnOnce(&mut Userpage),
330    ) -> Result<UserpageStatus> {
331        let original = self.read_userpage();
332        let mut modified = original.clone();
333
334        f(&mut modified);
335
336        if original != modified {
337            unsafe { self.erase(NvmErase::Userpage)? };
338            unsafe { self.write(NvmWrite::Userpage(&modified))? };
339
340            Ok(UserpageStatus::Updated)
341        } else {
342            Ok(UserpageStatus::Skipped)
343        }
344    }
345
346    /// Read the calibration area
347    #[inline]
348    pub fn calibration_area(&self) -> CalibrationArea {
349        let mut buffer = 0_u64;
350        let base_addr: *const u8 = 0x0080_0080 as *const u8;
351
352        for i in 0..6 {
353            buffer |=
354                unsafe { core::ptr::read_volatile(base_addr.offset(i as isize)) as u64 } << (i * 8);
355        }
356
357        CalibrationArea(buffer)
358    }
359
360    /// Read the calibration area for temperatures
361    #[inline]
362    pub fn temperatures_calibration_area(&self) -> TemperaturesCalibrationArea {
363        let mut buffer = 0_u128;
364        let base_addr: *const u8 = 0x0080_0100 as *const u8;
365
366        for i in 0..11 {
367            buffer |= unsafe { core::ptr::read_volatile(base_addr.offset(i as isize)) as u128 }
368                << (i * 8);
369        }
370
371        TemperaturesCalibrationArea(buffer)
372    }
373
374    /// Enable security bit
375    ///
376    /// It locks the chip from external access for code security. Consult the
377    /// datasheet for more details.
378    ///
379    /// In order to disable it, chip erase command must be issued through the
380    /// debugger.
381    #[inline]
382    pub fn enable_security_bit(&mut self) -> Result<()> {
383        self.command_sync(Cmdselect::Ssb)
384    }
385
386    /// Enable the chip erase lock
387    ///
388    /// It disables the chip erase capability.
389    ///
390    /// # Safety
391    ///
392    /// Together with [`Self::enable_security_bit`], it completely locks the MCU
393    /// down from any external interaction via debugger, thus effectively
394    /// *bricking the device*. Flashed firmware *must provide a way* to
395    /// execute [`Self::disable_chip_erase_lock`] method in order to enable
396    /// the debugger access again.
397    #[inline]
398    pub unsafe fn enable_chip_erase_lock(&mut self) -> Result<()> {
399        self.command_sync(Cmdselect::Celck)
400    }
401
402    /// Disable the chip erase lock
403    ///
404    /// It enables the chip erase capability through the debugger.
405    #[inline]
406    pub fn disable_chip_erase_lock(&mut self) -> Result<()> {
407        self.command_sync(Cmdselect::Ceulck)
408    }
409
410    /// Enable/disable boot protection
411    ///
412    /// Userpage's NVM BOOT field defines a memory region that is supposed to be
413    /// protected. `Nvmctrl.STATUS.BOOTPROT` is a readonly HW register populated
414    /// on reset with a value from a userpage. By default, 0
415    #[inline]
416    pub fn boot_protection(&mut self, protect: bool) -> Result<()> {
417        // Check if requested state differs from current state
418        if self.is_boot_protected() != protect {
419            // Requires both command and key so the command is allowed to execute
420            if protect {
421                // Issue Clear boot protection disable (enable bootprotection)
422                self.command_sync(Cmdselect::Cbpdis)
423            } else {
424                // Issue Set boot protection disable (disable bootprotection)
425                self.command_sync(Cmdselect::Sbpdis)
426            }
427        } else {
428            Ok(())
429        }
430    }
431
432    /// Enable/disable region lock
433    ///
434    /// Flash memory is split into 32 regions. The 32 bits of the `mask`
435    /// determine if each region should be locked (if its bit is 0) and prevent
436    /// writing and erasing pages, or unlocked (if its bit is 1) and allow
437    /// writing and erasing pages.
438    ///
439    /// Less significant bits represent lower addresses, more significant bits
440    /// represent higher addresses.
441    ///
442    /// For example mask `0xFFFF_0000` implies that active bank should be locked
443    /// while inactive bank should be unlocked.
444    #[inline]
445    pub fn region_lock(&mut self, mask: u32) -> Result<()> {
446        const REGIONS_COUNT: u32 = 32;
447        const FLASH_START: u32 = 0;
448        let flash_end = retrieve_flash_size();
449        let region_size = (flash_end - FLASH_START) / REGIONS_COUNT;
450        for (i, address) in (FLASH_START..flash_end)
451            .step_by(region_size as usize)
452            .enumerate()
453        {
454            self.set_address(address);
455            let protect = mask & (1 << i) == 0;
456            self.command_sync(if protect {
457                Cmdselect::Lr
458            } else {
459                Cmdselect::Ur
460            })?;
461        }
462        Ok(())
463    }
464
465    /// Write to the main address space flash memory from a slice
466    ///
467    /// This call will fail if area that is being written to is
468    /// - outside of the main address space flash area
469    /// - write protected (BOOTPROT)
470    /// - overlapping with SmartEEPROM flash region
471    ///
472    /// `destination` has to be 4 bytes aligned.
473    ///
474    /// # Safety
475    ///
476    /// Writes to the main address space flash area containing currently
477    /// executed application are unsound.
478    #[inline]
479    pub unsafe fn write_flash_from_slice(
480        &mut self,
481        destination: *mut u32,
482        source_slice: &[u32],
483        write_granularity: WriteGranularity,
484    ) -> Result<()> {
485        let source = source_slice.as_ptr();
486        let words = source_slice.len() as u32;
487
488        // Safety: prerequisites bubbled up to the method signature
489        unsafe {
490            self.write(NvmWrite::MainAddressSpace {
491                destination,
492                source,
493                words,
494                write_granularity,
495            })
496        }
497    }
498
499    /// Write to the main address space flash memory
500    ///
501    /// This call will fail if area that is being written to is
502    /// - outside of the main address space flash area
503    /// - write protected (BOOTPROT)
504    /// - overlapping with SmartEEPROM flash region
505    ///
506    /// `destination` has to be 4 bytes aligned.
507    /// `source` has to be 4 bytes aligned.
508    ///
509    /// # Safety
510    ///
511    /// Writes to the main address space flash area containing currently
512    /// executed application are unsound.
513    #[inline]
514    pub unsafe fn write_flash(
515        &mut self,
516        destination: *mut u32,
517        source: *const u32,
518        words: u32,
519        write_granularity: WriteGranularity,
520    ) -> Result<()> {
521        // Safety: prerequisites bubbled up to the method signature
522        unsafe {
523            self.write(NvmWrite::MainAddressSpace {
524                destination,
525                source,
526                words,
527                write_granularity,
528            })
529        }
530    }
531
532    /// Write to the flash memory
533    ///
534    /// Failure modes regarding writes to the main address space flash
535    /// area are mentioned in [`Self::write_flash`] documentation
536    ///
537    /// # Safety
538    ///
539    /// Safety requirements regarding writes to the main address space flash
540    /// area are mentioned in [`Self::write_flash`] documentation
541    ///
542    /// Safety requirements regarding userpage modifications are mentioned in
543    /// [`Self::modify_userpage`] documentation
544    #[inline]
545    unsafe fn write(&mut self, op: NvmWrite) -> Result<()> {
546        unsafe {
547            let (destination_address, source_address, words, granularity) = match op {
548                NvmWrite::MainAddressSpace {
549                    destination,
550                    source,
551                    words,
552                    write_granularity,
553                } => (destination as u32, source as u32, words, write_granularity),
554                NvmWrite::Userpage(userpage) => (
555                    Self::USERPAGE_ADDR as u32,
556                    addr_of!(userpage.0) as u32,
557                    PAGESIZE / core::mem::size_of::<u32>() as u32,
558                    WriteGranularity::QuadWord,
559                ),
560            };
561
562            // Length of memory step
563            let step_size = core::mem::size_of::<u32>() as u32;
564            // Length of data in bytes
565            let length = words * step_size;
566            let write_size = granularity.size();
567
568            let read_addresses = source_address..(source_address + length);
569            let write_addresses = destination_address..(destination_address + length);
570
571            if source_address % step_size != 0 {
572                return Err(Error::Alignment);
573            }
574
575            if destination_address % step_size != 0 {
576                return Err(Error::Alignment);
577            }
578
579            match op {
580                NvmWrite::MainAddressSpace { .. } => {
581                    if self.contains_non_flash_memory_area(&write_addresses) {
582                        return Err(Error::NonFlash);
583                    } else if self.contains_bootprotected(&write_addresses) {
584                        return Err(Error::Protected);
585                    } else if self.contains_smart_eeprom(&write_addresses) {
586                        return Err(Error::SmartEepromArea);
587                    }
588                }
589                NvmWrite::Userpage(_) => {
590                    // Nothing to check
591                }
592            }
593
594            self.command_sync(Cmdselect::Pbc)?;
595            // Track whether we have unwritten data in the page buffer
596            let mut dirty = false;
597            // Zip two iterators, one counter and one with the addr word aligned
598            for (destination_address, source_address) in write_addresses
599                .step_by(step_size as usize)
600                .zip(read_addresses.step_by(step_size as usize))
601            {
602                // Write to memory, 32 bits, 1 word.
603                // The data is placed in the page buffer and ADDR is updated automatically.
604                // Memory is not written until the write page command is issued later.
605                let value = core::ptr::read_volatile(source_address as *const u32);
606                core::ptr::write_volatile(destination_address as *mut u32, value);
607                dirty = true;
608
609                // If we are about to cross a page boundary (and run out of page buffer), write
610                // to flash
611                if destination_address % write_size >= write_size - step_size {
612                    // Perform a write
613                    self.command_sync(granularity.command())?;
614                    dirty = false;
615                }
616            }
617
618            if dirty {
619                // The dirty flag has fulfilled its role here, so we don't bother to maintain
620                // its invariant anymore. Otherwise, the compiler would warn of
621                // unused assignments. Write last page
622                self.command_sync(granularity.command())?
623            }
624
625            Ok(())
626        }
627    }
628
629    /// Erase the portion of the main address space flash memory
630    ///
631    /// Erase granularity is expressed in blocks (16 pages == 8192 bytes)
632    ///
633    /// This call will fail if area that is being erased is
634    /// - outside of the main address space flash area
635    /// - write protected (BOOTPROT)
636    /// - overlapping with SmartEEPROM flash region
637    ///
638    /// # Safety
639    ///
640    /// Erasure of the main address space flash area containing currently
641    /// executed application is unsound.
642    #[inline]
643    pub unsafe fn erase_flash(&mut self, address: *mut u32, blocks: u32) -> Result<()> {
644        // Safety: prerequisites bubbled up to the method signature
645        unsafe { self.erase(NvmErase::Flash { address, blocks }) }
646    }
647
648    /// Erase the flash memory.
649    ///
650    /// Failure modes regarding erasure of the main address space flash
651    /// area are mentioned in [`Self::erase_flash`] documentation
652    ///
653    /// # Safety
654    ///
655    /// Safety requirements regarding erasure of the main address space flash
656    /// area are mentioned in [`Self::erase_flash`] documentation
657    ///
658    /// Safety requirements regarding userpage modifications are mentioned in
659    /// [`Self::modify_userpage`] documentation
660    #[inline]
661    unsafe fn erase(&mut self, op: NvmErase) -> Result<()> {
662        let (address, length, granularity) = match op {
663            NvmErase::Flash { address, blocks } => {
664                (address as u32, blocks, EraseGranularity::Block)
665            }
666            NvmErase::Userpage => (Self::USERPAGE_ADDR as u32, 1, EraseGranularity::Page),
667        };
668
669        // Align to block/page boundary
670        // While the NVM will accept any address in the block, we need to compute the
671        // aligned address to check for boot protection.
672        let flash_address = address - address % granularity.size();
673        let range_to_erase = flash_address..(flash_address + length * granularity.size());
674
675        match op {
676            NvmErase::Flash { .. } => {
677                if self.contains_non_flash_memory_area(&range_to_erase) {
678                    return Err(Error::NonFlash);
679                } else if self.contains_bootprotected(&range_to_erase) {
680                    return Err(Error::Protected);
681                } else if self.contains_smart_eeprom(&range_to_erase) {
682                    return Err(Error::SmartEepromArea);
683                }
684            }
685            NvmErase::Userpage => {
686                // Nothing to check
687            }
688        }
689
690        for address in range_to_erase.step_by(granularity.size() as usize) {
691            // Set target address to current block/page offset
692            self.set_address(address);
693
694            // Erase block/page, wait for completion
695            self.command_sync(granularity.command())?
696        }
697
698        Ok(())
699    }
700
701    #[inline]
702    fn contains_bootprotected(&self, input: &Range<u32>) -> bool {
703        // Calculate size that is protected for bootloader
704        //   * 15 = no bootprotection, default value
705        //   * 0 = max bootprotection, 15 * 8Kibyte = 120KiB
706        //   * (15 - bootprot) * 8KiB = protected size
707        let bootprot = self.nvm.status().read().bootprot().bits();
708        let bp_space = 8 * 1024 * (15 - bootprot) as u32;
709
710        let boot = &(Bank::Active.address()..(Bank::Active.address() + bp_space));
711        self.is_boot_protected() && range_overlap(input, boot)
712    }
713
714    #[inline]
715    fn contains_smart_eeprom(&self, input: &Range<u32>) -> bool {
716        let smart_eeprom_allocated_blocks = self.nvm.seestat().read().sblk().bits() as u32;
717        let smart_eeprom_end = Bank::Inactive.address() + Bank::Inactive.length();
718        let smart_eeprom_start = smart_eeprom_end - smart_eeprom_allocated_blocks * BLOCKSIZE;
719        let smart_eeprom = &(smart_eeprom_start..smart_eeprom_end);
720        range_overlap(input, smart_eeprom)
721    }
722
723    #[inline]
724    fn contains_non_flash_memory_area(&self, input: &Range<u32>) -> bool {
725        input.end > retrieve_flash_size()
726    }
727
728    /// Retrieve SmartEEPROM
729    #[inline]
730    pub fn smart_eeprom(&mut self) -> smart_eeprom::Result<'_> {
731        smart_eeprom::SmartEepromMode::retrieve(self)
732    }
733}
734
735/// The outcome of [`Nvm::modify_userpage`]
736#[derive(Copy, Clone, Debug)]
737pub enum UserpageStatus {
738    /// Userpage has been updated
739    Updated,
740    /// Update has been skipped; expected value is already present.
741    Skipped,
742}
743
744enum NvmWrite<'a> {
745    /// Writes to the flash memory within the main address space
746    MainAddressSpace {
747        destination: *mut u32,
748        source: *const u32,
749        words: u32,
750        write_granularity: WriteGranularity,
751    },
752    /// Writes the userpage to the corresponding flash memory
753    Userpage(&'a Userpage),
754}
755
756enum NvmErase {
757    Flash { address: *mut u32, blocks: u32 },
758    Userpage,
759}
760
761/// Data erased per command
762#[derive(Copy, Clone, Debug)]
763enum EraseGranularity {
764    /// One block. This erase type is supported by main memory
765    Block,
766    /// One page. This erase type is supported for the AUX memory
767    Page,
768}
769
770/// Data written per command
771#[derive(Copy, Clone, Debug)]
772pub enum WriteGranularity {
773    /// Four words (16 bytes). Expected for user page writes
774    QuadWord,
775    /// One page (512 bytes)
776    Page,
777}
778
779impl EraseGranularity {
780    #[inline]
781    fn command(&self) -> Cmdselect {
782        match self {
783            Self::Block => Cmdselect::Eb,
784            Self::Page => Cmdselect::Ep,
785        }
786    }
787
788    #[inline]
789    fn size(&self) -> u32 {
790        match self {
791            Self::Block => BLOCKSIZE,
792            Self::Page => PAGESIZE,
793        }
794    }
795}
796
797impl WriteGranularity {
798    #[inline]
799    fn command(&self) -> Cmdselect {
800        match self {
801            Self::QuadWord => Cmdselect::Wqw,
802            Self::Page => Cmdselect::Wp,
803        }
804    }
805
806    #[inline]
807    fn size(&self) -> u32 {
808        match self {
809            Self::QuadWord => QUADWORDSIZE,
810            Self::Page => PAGESIZE,
811        }
812    }
813}
814
815fn range_overlap(a: &Range<u32>, b: &Range<u32>) -> bool {
816    a.start < b.end && b.start < a.end
817}
818
819/// Type alias to the userpage with a concrete underlying storage type
820pub type Userpage = RawUserpage<[u8; 512]>;
821
822bitfield! {
823    /// Raw userpage POD struct that exposes bitfields via methods
824    #[derive(Clone, PartialEq, Eq)]
825    pub struct RawUserpage([u8]);
826    impl Debug;
827    u8;
828    /// Access the `bod33_disable` field
829    pub bod33_disable, set_bod33_disable: 0;
830    /// Access the `bod33_level` field
831    pub bod33_level, set_bod33_level: 8, 1;
832    /// Access the `bod33_action` field
833    pub bod33_action, set_bod33_action: 10, 9;
834    /// Access the `bod33_hysteresis` field
835    pub bod33_hysteresis, set_bod33_hysteresis: 14, 11;
836    /// Access the `bod12_calibration_parameters` field
837    pub u16, bod12_calibration_parameters, set_bod12_calibration_parameters: 25, 15;
838    /// Access the `nvm_bootloader_size` field
839    pub nvm_bootloader_size, set_nvm_bootloader_size: 29, 26;
840    /// Access the `reserved_0` field
841    pub reserved_0, set_reserved_0: 31, 30;
842    /// Access the `see_sblk` field
843    pub see_sblk, set_see_sblk: 35, 32;
844    /// Access the `see_psz` field
845    pub see_psz, set_see_psz: 38, 36;
846    /// Access the `ram_ecc_disable` field
847    pub ram_ecc_disable, set_ram_ecc_disable: 39;
848    /// Access the `reserved_1` field
849    pub reserved_1, set_reserved_1: 47, 40;
850    /// Access the `wdt_enable` field
851    pub wdt_enable, set_wdt_enable: 48;
852    /// Access the `wdt_always_on` field
853    pub wdt_always_on, set_wdt_always_on: 49;
854    /// Access the `wdt_period` field
855    pub wdt_period, set_wdt_period: 53, 50;
856    /// Access the `wdt_window` field
857    pub wdt_window, set_wdt_window: 57, 54;
858    /// Access the `wdt_ewoffset` field
859    pub wdt_ewoffset, set_wdt_ewoffset: 61, 58;
860    /// Access the `wdt_wen` field
861    pub wdt_wen, set_wdt_wen: 62;
862    /// Access the `reserved_2` field
863    pub reserved_2, set_reserved_2: 63;
864    /// Access the `nvm_locks` field
865    pub u32, nvm_locks, set_nvm_locks: 95, 64;
866    /// Access the `userpage_0` field
867    pub u32, userpage_0, set_userpage_0: 127, 96;
868    /// Access the `reserved_3` field
869    pub u32, reserved_3, set_reserved_3: 159, 128;
870}
871
872impl<T: AsRef<[u8]>> RawUserpage<T> {
873    /// Access the general purpose user-writable section of the userpage via
874    /// slice
875    #[inline]
876    pub fn userpage1_as_slice(&self) -> &[u8] {
877        // `userpage1` starts on 160th bit (20th byte) and continues till the end of
878        // the userpage
879        &self.0.as_ref()[20..512]
880    }
881}
882impl<T: AsMut<[u8]>> RawUserpage<T> {
883    /// Access the general purpose user-writable section of the userpage via
884    /// mutable slice
885    #[inline]
886    pub fn userpage1_as_slice_mut(&mut self) -> &mut [u8] {
887        // `userpage1` starts on 160th bit (20th byte) and continues till the end of
888        // the userpage
889        &mut self.0.as_mut()[20..512]
890    }
891}
892
893bitfield! {
894    #[derive(Copy, Clone, Default)]
895    /// POD-style struct representing NVM calibration area
896    pub struct CalibrationArea(u64);
897    impl Debug;
898    u32;
899    /// Access the `ac_bias` field. Setter is not provided.
900    pub ac_bias, _: 1, 0;
901    /// Access the `adc0_biascomp` field. Setter is not provided.
902    pub adc0_biascomp, _: 4, 2;
903    /// Access the `adc0_biasrefbuf` field. Setter is not provided.
904    pub adc0_biasrefbuf, _: 7, 5;
905    /// Access the `adc0_biasr2r` field. Setter is not provided.
906    pub adc0_biasr2r, _: 10, 8;
907    /// Access the `adc1_biascomp` field. Setter is not provided.
908    pub adc1_biascomp, _: 18, 16;
909    /// Access the `adc1_biasrefbuf` field. Setter is not provided.
910    pub adc1_biasrefbuf, _: 21, 19;
911    /// Access the `adc1_biasr2r` field. Setter is not provided.
912    pub adc1_biasr2r, _: 24, 22;
913    /// Access the `usb_transn` field. Setter is not provided.
914    pub usb_transn, _: 36, 32;
915    /// Access the `usb_transp` field. Setter is not provided.
916    pub usb_transp, _: 41, 37;
917    /// Access the `usb_trim` field. Setter is not provided.
918    pub usb_trim, _: 44, 42;
919}
920
921bitfield! {
922    #[derive(Copy, Clone, Default)]
923    /// POD-style struct representing NVM calibration area for
924    /// temperature calibration
925    pub struct TemperaturesCalibrationArea(u128);
926    impl Debug;
927    u32;
928    /// Access the `tli` field. Setter is not provided.
929    pub tli, _: 7, 0;
930    /// Access the `tld` field. Setter is not provided.
931    pub tld, _: 11, 8;
932    /// Access the `thi` field. Setter is not provided.
933    pub thi, _: 19, 12;
934    /// Access the `thd` field. Setter is not provided.
935    pub thd, _: 23, 20;
936    /// Access the `vpl` field. Setter is not provided.
937    pub vpl, _: 51, 40;
938    /// Access the `vph` field. Setter is not provided.
939    pub vph, _: 63, 52;
940    /// Access the `vcl` field. Setter is not provided.
941    pub vcl, _: 75, 63;
942    /// Access the `vch` field. Setter is not provided.
943    pub vch, _: 87, 76;
944}