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.copy_from(&data);
109 /// buff.store();
110 /// ```
111 ///
112 pub fn store_from<'a>(self, data: impl Into<&'a DataPage>) {
113 spm::erase_page(self.address);
114 spm::copy_to_buffer(data);
115 spm::write_page(self.address);
116 }
117
118 /// Fill the buffer by repeatedly calling the callback function
119 ///
120 /// # Example
121 ///
122 /// ```no_run
123 /// use avr_boot::PageBuffer;
124 ///
125 /// let address:u16 = 0x1000;
126 /// let buff = PageBuffer::new(address);
127 /// buff.fill_from_fn(|| Some(0x1234));
128 /// buff.store();
129 /// ```
130 pub fn fill_from_fn<F>(&self, f: F)
131 where
132 F: FnMut() -> Option<u16>,
133 {
134 self.fill_from_iter(iter::from_fn(f));
135 }
136
137 /// Fill the buffer by repeatedly polling an iterator.
138 ///
139 /// # Example
140 ///
141 /// ```no_run
142 /// use avr_boot::PageBuffer;
143 /// use core::iter;
144 ///
145 /// let page_address:u16 = 0x1000;
146 /// let buff = PageBuffer::new(page_address);
147 /// buff.fill_from_iter(iter::repeat(0x69));
148 /// buff.store();
149 /// ```
150 pub fn fill_from_iter(&self, i: impl IntoIterator<Item = u16>) {
151 for (word, value) in self.iter().zip(i.into_iter()) {
152 word.set(value);
153 }
154 }
155
156 /// Erase the page from program memory, then write the contents of the buffer to it
157 pub fn store(self) {
158 spm::erase_page(self.address);
159 spm::write_page(self.address);
160 }
161
162 /// Iterate the buffer as writable word cells
163 ///
164 /// # Example
165 ///
166 /// ```no_run
167 /// use avr_boot::PageBuffer;
168 ///
169 /// let address: u16 = 0x1000;
170 /// let buff = PageBuffer::new(address);
171 /// for w in buff.iter() {
172 /// w.set(0x69);
173 /// }
174 /// buff.store();
175 /// ```
176 pub fn iter(&self) -> impl Iterator<Item = BufferCell> {
177 CellIter { offset: 0 }
178 }
179}
180
181impl Drop for PageBuffer {
182 // Wait for any current spm operation to complete and
183 // re-enable the rww section (if there is one)
184 fn drop(&mut self) {
185 spm::rww_enable();
186 }
187}
188
189struct CellIter {
190 offset: u8,
191}
192
193impl Iterator for CellIter {
194 type Item = BufferCell;
195
196 fn next(&mut self) -> Option<Self::Item> {
197 let current = self.offset;
198 if current >= crate::SPM_PAGESIZE_BYTES as u8 {
199 None
200 } else {
201 self.offset += 2;
202 Some(BufferCell { offset: current })
203 }
204 }
205}
206
207/// A single 16 bit word in the page buffer. Write only.
208pub struct BufferCell {
209 offset: u8,
210}
211
212impl BufferCell {
213 /// Set the value of the word in the spm buffer
214 pub fn set(&self, w: u16) {
215 spm::fill_page(self.offset, w);
216 }
217}