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
25pub use crate::pac::nvmctrl::ctrla::Prmselect;
26use crate::pac::nvmctrl::ctrlb::Cmdselect;
27use crate::pac::Nvmctrl;
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 let (destination_address, source_address, words, granularity) = match op {
547 NvmWrite::MainAddressSpace {
548 destination,
549 source,
550 words,
551 write_granularity,
552 } => (destination as u32, source as u32, words, write_granularity),
553 NvmWrite::Userpage(userpage) => (
554 Self::USERPAGE_ADDR as u32,
555 addr_of!(userpage.0) as u32,
556 PAGESIZE / core::mem::size_of::<u32>() as u32,
557 WriteGranularity::QuadWord,
558 ),
559 };
560
561 // Length of memory step
562 let step_size = core::mem::size_of::<u32>() as u32;
563 // Length of data in bytes
564 let length = words * step_size;
565 let write_size = granularity.size();
566
567 let read_addresses = source_address..(source_address + length);
568 let write_addresses = destination_address..(destination_address + length);
569
570 if source_address % step_size != 0 {
571 return Err(Error::Alignment);
572 }
573
574 if destination_address % step_size != 0 {
575 return Err(Error::Alignment);
576 }
577
578 match op {
579 NvmWrite::MainAddressSpace { .. } => {
580 if self.contains_non_flash_memory_area(&write_addresses) {
581 return Err(Error::NonFlash);
582 } else if self.contains_bootprotected(&write_addresses) {
583 return Err(Error::Protected);
584 } else if self.contains_smart_eeprom(&write_addresses) {
585 return Err(Error::SmartEepromArea);
586 }
587 }
588 NvmWrite::Userpage(_) => {
589 // Nothing to check
590 }
591 }
592
593 self.command_sync(Cmdselect::Pbc)?;
594 // Track whether we have unwritten data in the page buffer
595 let mut dirty = false;
596 // Zip two iterators, one counter and one with the addr word aligned
597 for (destination_address, source_address) in write_addresses
598 .step_by(step_size as usize)
599 .zip(read_addresses.step_by(step_size as usize))
600 {
601 // Write to memory, 32 bits, 1 word.
602 // The data is placed in the page buffer and ADDR is updated automatically.
603 // Memory is not written until the write page command is issued later.
604 let value = core::ptr::read_volatile(source_address as *const u32);
605 core::ptr::write_volatile(destination_address as *mut u32, value);
606 dirty = true;
607
608 // If we are about to cross a page boundary (and run out of page buffer), write
609 // to flash
610 if destination_address % write_size >= write_size - step_size {
611 // Perform a write
612 self.command_sync(granularity.command())?;
613 dirty = false;
614 }
615 }
616
617 if dirty {
618 // The dirty flag has fulfilled its role here, so we don't bother to maintain
619 // its invariant anymore. Otherwise, the compiler would warn of
620 // unused assignments. Write last page
621 self.command_sync(granularity.command())?
622 }
623
624 Ok(())
625 }
626
627 /// Erase the portion of the main address space flash memory
628 ///
629 /// Erase granularity is expressed in blocks (16 pages == 8192 bytes)
630 ///
631 /// This call will fail if area that is being erased is
632 /// - outside of the main address space flash area
633 /// - write protected (BOOTPROT)
634 /// - overlapping with SmartEEPROM flash region
635 ///
636 /// # Safety
637 ///
638 /// Erasure of the main address space flash area containing currently
639 /// executed application is unsound.
640 #[inline]
641 pub unsafe fn erase_flash(&mut self, address: *mut u32, blocks: u32) -> Result<()> {
642 // Safety: prerequisites bubbled up to the method signature
643 unsafe { self.erase(NvmErase::Flash { address, blocks }) }
644 }
645
646 /// Erase the flash memory.
647 ///
648 /// Failure modes regarding erasure of the main address space flash
649 /// area are mentioned in [`Self::erase_flash`] documentation
650 ///
651 /// # Safety
652 ///
653 /// Safety requirements regarding erasure of the main address space flash
654 /// area are mentioned in [`Self::erase_flash`] documentation
655 ///
656 /// Safety requirements regarding userpage modifications are mentioned in
657 /// [`Self::modify_userpage`] documentation
658 #[inline]
659 unsafe fn erase(&mut self, op: NvmErase) -> Result<()> {
660 let (address, length, granularity) = match op {
661 NvmErase::Flash { address, blocks } => {
662 (address as u32, blocks, EraseGranularity::Block)
663 }
664 NvmErase::Userpage => (Self::USERPAGE_ADDR as u32, 1, EraseGranularity::Page),
665 };
666
667 // Align to block/page boundary
668 // While the NVM will accept any address in the block, we need to compute the
669 // aligned address to check for boot protection.
670 let flash_address = address - address % granularity.size();
671 let range_to_erase = flash_address..(flash_address + length * granularity.size());
672
673 match op {
674 NvmErase::Flash { .. } => {
675 if self.contains_non_flash_memory_area(&range_to_erase) {
676 return Err(Error::NonFlash);
677 } else if self.contains_bootprotected(&range_to_erase) {
678 return Err(Error::Protected);
679 } else if self.contains_smart_eeprom(&range_to_erase) {
680 return Err(Error::SmartEepromArea);
681 }
682 }
683 NvmErase::Userpage => {
684 // Nothing to check
685 }
686 }
687
688 for address in range_to_erase.step_by(granularity.size() as usize) {
689 // Set target address to current block/page offset
690 self.set_address(address);
691
692 // Erase block/page, wait for completion
693 self.command_sync(granularity.command())?
694 }
695
696 Ok(())
697 }
698
699 #[inline]
700 fn contains_bootprotected(&self, input: &Range<u32>) -> bool {
701 // Calculate size that is protected for bootloader
702 // * 15 = no bootprotection, default value
703 // * 0 = max bootprotection, 15 * 8Kibyte = 120KiB
704 // * (15 - bootprot) * 8KiB = protected size
705 let bootprot = self.nvm.status().read().bootprot().bits();
706 let bp_space = 8 * 1024 * (15 - bootprot) as u32;
707
708 let boot = &(Bank::Active.address()..(Bank::Active.address() + bp_space));
709 self.is_boot_protected() && range_overlap(input, boot)
710 }
711
712 #[inline]
713 fn contains_smart_eeprom(&self, input: &Range<u32>) -> bool {
714 let smart_eeprom_allocated_blocks = self.nvm.seestat().read().sblk().bits() as u32;
715 let smart_eeprom_end = Bank::Inactive.address() + Bank::Inactive.length();
716 let smart_eeprom_start = smart_eeprom_end - smart_eeprom_allocated_blocks * BLOCKSIZE;
717 let smart_eeprom = &(smart_eeprom_start..smart_eeprom_end);
718 range_overlap(input, smart_eeprom)
719 }
720
721 #[inline]
722 fn contains_non_flash_memory_area(&self, input: &Range<u32>) -> bool {
723 input.end > retrieve_flash_size()
724 }
725
726 /// Retrieve SmartEEPROM
727 #[inline]
728 pub fn smart_eeprom(&mut self) -> smart_eeprom::Result<'_> {
729 smart_eeprom::SmartEepromMode::retrieve(self)
730 }
731}
732
733/// The outcome of [`Nvm::modify_userpage`]
734#[derive(Copy, Clone, Debug)]
735pub enum UserpageStatus {
736 /// Userpage has been updated
737 Updated,
738 /// Update has been skipped; expected value is already present.
739 Skipped,
740}
741
742enum NvmWrite<'a> {
743 /// Writes to the flash memory within the main address space
744 MainAddressSpace {
745 destination: *mut u32,
746 source: *const u32,
747 words: u32,
748 write_granularity: WriteGranularity,
749 },
750 /// Writes the userpage to the corresponding flash memory
751 Userpage(&'a Userpage),
752}
753
754enum NvmErase {
755 Flash { address: *mut u32, blocks: u32 },
756 Userpage,
757}
758
759/// Data erased per command
760#[derive(Copy, Clone, Debug)]
761enum EraseGranularity {
762 /// One block. This erase type is supported by main memory
763 Block,
764 /// One page. This erase type is supported for the AUX memory
765 Page,
766}
767
768/// Data written per command
769#[derive(Copy, Clone, Debug)]
770pub enum WriteGranularity {
771 /// Four words (16 bytes). Expected for user page writes
772 QuadWord,
773 /// One page (512 bytes)
774 Page,
775}
776
777impl EraseGranularity {
778 #[inline]
779 fn command(&self) -> Cmdselect {
780 match self {
781 Self::Block => Cmdselect::Eb,
782 Self::Page => Cmdselect::Ep,
783 }
784 }
785
786 #[inline]
787 fn size(&self) -> u32 {
788 match self {
789 Self::Block => BLOCKSIZE,
790 Self::Page => PAGESIZE,
791 }
792 }
793}
794
795impl WriteGranularity {
796 #[inline]
797 fn command(&self) -> Cmdselect {
798 match self {
799 Self::QuadWord => Cmdselect::Wqw,
800 Self::Page => Cmdselect::Wp,
801 }
802 }
803
804 #[inline]
805 fn size(&self) -> u32 {
806 match self {
807 Self::QuadWord => QUADWORDSIZE,
808 Self::Page => PAGESIZE,
809 }
810 }
811}
812
813fn range_overlap(a: &Range<u32>, b: &Range<u32>) -> bool {
814 a.start < b.end && b.start < a.end
815}
816
817/// Type alias to the userpage with a concrete underlying storage type
818pub type Userpage = RawUserpage<[u8; 512]>;
819
820bitfield! {
821 /// Raw userpage POD struct that exposes bitfields via methods
822 #[derive(Clone, PartialEq, Eq)]
823 pub struct RawUserpage([u8]);
824 impl Debug;
825 u8;
826 /// Access the `bod33_disable` field
827 pub bod33_disable, set_bod33_disable: 0;
828 /// Access the `bod33_level` field
829 pub bod33_level, set_bod33_level: 8, 1;
830 /// Access the `bod33_action` field
831 pub bod33_action, set_bod33_action: 10, 9;
832 /// Access the `bod33_hysteresis` field
833 pub bod33_hysteresis, set_bod33_hysteresis: 14, 11;
834 /// Access the `bod12_calibration_parameters` field
835 pub u16, bod12_calibration_parameters, set_bod12_calibration_parameters: 25, 15;
836 /// Access the `nvm_bootloader_size` field
837 pub nvm_bootloader_size, set_nvm_bootloader_size: 29, 26;
838 /// Access the `reserved_0` field
839 pub reserved_0, set_reserved_0: 31, 30;
840 /// Access the `see_sblk` field
841 pub see_sblk, set_see_sblk: 35, 32;
842 /// Access the `see_psz` field
843 pub see_psz, set_see_psz: 38, 36;
844 /// Access the `ram_ecc_disable` field
845 pub ram_ecc_disable, set_ram_ecc_disable: 39;
846 /// Access the `reserved_1` field
847 pub reserved_1, set_reserved_1: 47, 40;
848 /// Access the `wdt_enable` field
849 pub wdt_enable, set_wdt_enable: 48;
850 /// Access the `wdt_always_on` field
851 pub wdt_always_on, set_wdt_always_on: 49;
852 /// Access the `wdt_period` field
853 pub wdt_period, set_wdt_period: 53, 50;
854 /// Access the `wdt_window` field
855 pub wdt_window, set_wdt_window: 57, 54;
856 /// Access the `wdt_ewoffset` field
857 pub wdt_ewoffset, set_wdt_ewoffset: 61, 58;
858 /// Access the `wdt_wen` field
859 pub wdt_wen, set_wdt_wen: 62;
860 /// Access the `reserved_2` field
861 pub reserved_2, set_reserved_2: 63;
862 /// Access the `nvm_locks` field
863 pub u32, nvm_locks, set_nvm_locks: 95, 64;
864 /// Access the `userpage_0` field
865 pub u32, userpage_0, set_userpage_0: 127, 96;
866 /// Access the `reserved_3` field
867 pub u32, reserved_3, set_reserved_3: 159, 128;
868}
869
870impl<T: AsRef<[u8]>> RawUserpage<T> {
871 /// Access the general purpose user-writable section of the userpage via
872 /// slice
873 #[inline]
874 pub fn userpage1_as_slice(&self) -> &[u8] {
875 // `userpage1` starts on 160th bit (20th byte) and continues till the end of
876 // the userpage
877 &self.0.as_ref()[20..512]
878 }
879}
880impl<T: AsMut<[u8]>> RawUserpage<T> {
881 /// Access the general purpose user-writable section of the userpage via
882 /// mutable slice
883 #[inline]
884 pub fn userpage1_as_slice_mut(&mut self) -> &mut [u8] {
885 // `userpage1` starts on 160th bit (20th byte) and continues till the end of
886 // the userpage
887 &mut self.0.as_mut()[20..512]
888 }
889}
890
891bitfield! {
892 #[derive(Copy, Clone, Default)]
893 /// POD-style struct representing NVM calibration area
894 pub struct CalibrationArea(u64);
895 impl Debug;
896 u32;
897 /// Access the `ac_bias` field. Setter is not provided.
898 pub ac_bias, _: 1, 0;
899 /// Access the `adc0_biascomp` field. Setter is not provided.
900 pub adc0_biascomp, _: 4, 2;
901 /// Access the `adc0_biasrefbuf` field. Setter is not provided.
902 pub adc0_biasrefbuf, _: 7, 5;
903 /// Access the `adc0_biasr2r` field. Setter is not provided.
904 pub adc0_biasr2r, _: 10, 8;
905 /// Access the `adc1_biascomp` field. Setter is not provided.
906 pub adc1_biascomp, _: 18, 16;
907 /// Access the `adc1_biasrefbuf` field. Setter is not provided.
908 pub adc1_biasrefbuf, _: 21, 19;
909 /// Access the `adc1_biasr2r` field. Setter is not provided.
910 pub adc1_biasr2r, _: 24, 22;
911 /// Access the `usb_transn` field. Setter is not provided.
912 pub usb_transn, _: 36, 32;
913 /// Access the `usb_transp` field. Setter is not provided.
914 pub usb_transp, _: 41, 37;
915 /// Access the `usb_trim` field. Setter is not provided.
916 pub usb_trim, _: 44, 42;
917}
918
919bitfield! {
920 #[derive(Copy, Clone, Default)]
921 /// POD-style struct representing NVM calibration area for
922 /// temperature calibration
923 pub struct TemperaturesCalibrationArea(u128);
924 impl Debug;
925 u32;
926 /// Access the `tli` field. Setter is not provided.
927 pub tli, _: 7, 0;
928 /// Access the `tld` field. Setter is not provided.
929 pub tld, _: 11, 8;
930 /// Access the `thi` field. Setter is not provided.
931 pub thi, _: 19, 12;
932 /// Access the `thd` field. Setter is not provided.
933 pub thd, _: 23, 20;
934 /// Access the `vpl` field. Setter is not provided.
935 pub vpl, _: 51, 40;
936 /// Access the `vph` field. Setter is not provided.
937 pub vph, _: 63, 52;
938 /// Access the `vcl` field. Setter is not provided.
939 pub vcl, _: 75, 63;
940 /// Access the `vch` field. Setter is not provided.
941 pub vch, _: 87, 76;
942}