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}