avr_boot/
buffer.rs

1//! High level page buffer API
2
3use crate::{spm, Address, DataPage};
4use core::iter;
5
6/// Representation of the spm page buffer.
7///
8/// The page buffer is a special area of memory which is write-only, and only writable using the `spm` instruction.
9/// Setting a value in the buffer does not write to the program memory - that only happens when [`PageBuffer::store`] is called
10///
11/// # Example
12/// ```no_run
13/// use avr_boot::PageBuffer;
14///
15/// let address:u16 = 0x1000;
16/// let buff = PageBuffer::new(address);
17/// for w in buff.iter() {
18///     w.set(0xabcd);
19/// }
20/// buff.store();
21/// ```
22///
23/// A whole page is written in one go, so if you only want to change part of a page, you need to make sure you have
24/// loaded the rest of the page into the buffer first.
25///
26/// There is only one physical buffer in the system, so you should make sure only one of these structs ever
27/// exists at any time. This rule is not enforced.
28///
29/// The page address will be aligned downwards to the nearest starting page address
30///
31pub struct PageBuffer {
32    address: Address,
33}
34
35impl PageBuffer {
36    /// The buffer length in words, the value will change depending on the MCU compilation target
37    pub const LENGTH: usize = crate::SPM_PAGESIZE_WORDS;
38
39    /// Create a new PageBuffer with the given address.
40    ///
41    /// # Example
42    /// ```rust
43    /// use avr_boot::PageBuffer;
44    ///
45    /// let buff = PageBuffer::new(0x101fu16);
46    /// assert_eq!(0x1000u16, buff.address().into());
47    /// ```
48    /// The page address will be aligned downwards to the nearest starting page address
49    pub fn new(address: impl Into<Address>) -> PageBuffer {
50        PageBuffer {
51            address: address.into().into_page_aligned(),
52        }
53    }
54
55    /// Get the base page address to be operated on
56    ///
57    /// # Example
58    /// ```rust
59    /// use avr_boot::{PageBuffer, Address};
60    ///
61    /// let buff = PageBuffer::new(0x1000u16);
62    /// assert_eq!(Address::from(0x1000u16), buff.address());
63    /// ```
64    /// The page address will be aligned downwards to the nearest starting page address
65    pub fn address(&self) -> Address {
66        self.address
67    }
68
69    /// Fill the buffer from a slice
70    ///
71    /// # Example
72    ///
73    /// ```no_run
74    /// use avr_boot::{DataPage, PageBuffer};
75    ///
76    /// let address: u16 = 0x1000;
77    /// let data = DataPage(core::array::from_fn(|_| 0x69));
78    /// let buff = PageBuffer::new(address);
79    /// buff.copy_from(&data);
80    /// buff.store();
81    /// ```
82    ///
83    /// # Example
84    ///
85    /// ```no_run
86    /// use avr_boot::PageBuffer;
87    ///
88    /// let address: u16 = 0x1000;
89    /// let data = [0xff; avr_boot::SPM_PAGESIZE_BYTES];
90    /// let buff = PageBuffer::new(address);
91    /// buff.copy_from(&data);
92    /// buff.store();
93    /// ```
94    pub fn copy_from<'a>(&self, data: impl Into<&'a DataPage>) {
95        spm::copy_to_buffer(data);
96    }
97
98    /// Fill the buffer from a slice and store it immediately
99    ///
100    /// # Example
101    ///
102    /// ```no_run
103    /// use avr_boot::{DataPage, PageBuffer};
104    ///
105    /// let address: u16 = 0x1000;
106    /// let data = DataPage(core::array::from_fn(|_| 0x69));
107    /// let buff = PageBuffer::new(address);
108    /// buff.store_from(&data);
109    /// ```
110    ///
111    pub fn store_from<'a>(self, data: impl Into<&'a DataPage>) {
112        spm::erase_page(self.address);
113        spm::copy_to_buffer(data);
114        spm::write_page(self.address);
115    }
116
117    /// Fill the buffer by repeatedly calling the callback function
118    ///
119    /// # Example
120    ///
121    /// ```no_run
122    /// use avr_boot::PageBuffer;
123    ///
124    /// let address:u16 = 0x1000;
125    /// let buff = PageBuffer::new(address);
126    /// buff.fill_from_fn(|| Some(0x1234));
127    /// buff.store();
128    /// ```
129    pub fn fill_from_fn<F>(&self, f: F)
130    where
131        F: FnMut() -> Option<u16>,
132    {
133        self.fill_from_iter(iter::from_fn(f));
134    }
135
136    /// Fill the buffer by repeatedly polling an iterator.  
137    ///
138    /// # Example
139    ///
140    /// ```no_run
141    /// use avr_boot::PageBuffer;
142    /// use core::iter;
143    ///
144    /// let page_address:u16 = 0x1000;
145    /// let buff = PageBuffer::new(page_address);
146    /// buff.fill_from_iter(iter::repeat(0x69));
147    /// buff.store();
148    /// ```
149    pub fn fill_from_iter(&self, i: impl IntoIterator<Item = u16>) {
150        for (word, value) in self.iter().zip(i.into_iter()) {
151            word.set(value);
152        }
153    }
154
155    /// Erase the page from program memory, then write the contents of the buffer to it
156    pub fn store(self) {
157        spm::erase_page(self.address);
158        spm::write_page(self.address);
159    }
160
161    /// Iterate the buffer as writable word cells
162    ///
163    /// # Example
164    ///
165    /// ```no_run
166    /// use avr_boot::PageBuffer;
167    ///
168    /// let address: u16 = 0x1000;
169    /// let buff = PageBuffer::new(address);
170    /// for w in buff.iter() {
171    ///     w.set(0x69);
172    /// }
173    /// buff.store();
174    /// ```
175    pub fn iter(&self) -> impl Iterator<Item = BufferCell> {
176        CellIter { offset: 0 }
177    }
178}
179
180impl Drop for PageBuffer {
181    // Wait for any current spm operation to complete and
182    // re-enable the rww section (if there is one)
183    fn drop(&mut self) {
184        spm::rww_enable();
185    }
186}
187
188struct CellIter {
189    offset: u8,
190}
191
192impl Iterator for CellIter {
193    type Item = BufferCell;
194
195    fn next(&mut self) -> Option<Self::Item> {
196        let current = self.offset;
197        if current >= crate::SPM_PAGESIZE_BYTES as u8 {
198            None
199        } else {
200            self.offset += 2;
201            Some(BufferCell { offset: current })
202        }
203    }
204}
205
206/// A single 16 bit word in the page buffer. Write only.
207pub struct BufferCell {
208    offset: u8,
209}
210
211impl BufferCell {
212    /// Set the value of the word in the spm buffer
213    pub fn set(&self, w: u16) {
214        spm::fill_page(self.offset, w);
215    }
216}