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}