max7800x_hal/
flc.rs

1//! # Flash Controller (FLC)
2use crate::gcr::clocks::{Clock, SystemClock};
3
4/// Base address of the flash memory.
5pub const FLASH_BASE: u32 = 0x1000_0000;
6/// Size of the flash memory.
7pub const FLASH_SIZE: u32 = 0x0008_0000;
8/// End address of the flash memory.
9pub const FLASH_END: u32 = FLASH_BASE + FLASH_SIZE;
10/// Number of flash pages.
11pub const FLASH_PAGE_COUNT: u32 = 64;
12/// Size of a flash page.
13pub const FLASH_PAGE_SIZE: u32 = 0x2000;
14
15/// Flash controller errors.
16#[derive(Debug, PartialEq)]
17pub enum FlashError {
18    /// The target address or page to write or erase is invalid.
19    InvalidAddress,
20    /// The flash controller was busy or locked when attempting to write or erase.
21    AccessViolation,
22    /// Writing over the old data with new data would cause 0 -> 1 bit transitions.
23    /// The target address must be erased before writing new data.
24    NeedsErase,
25}
26
27/// # Flash Controller (FLC) Peripheral
28///
29/// The flash controller manages read, write, and erase accesses to the
30/// internal flash and provides the following features:
31/// - Up to 512 KiB of flash memory
32/// - 64 pages (8192 bytes per page)
33/// - 2048 words by 128 bits per page
34/// - 128-bit write granularity
35/// - Page erase and mass erase
36/// - Read and write protection
37///
38/// Example:
39/// ```
40/// let flc = Flc::new(p.flc, sys_clk);
41///
42/// // Erase page number 48
43/// unsafe { flc.erase_page(0x1006_0000).unwrap(); }
44/// // Read the value at address 0x1006_0004
45/// let data: u32 = flc.read_32(0x1006_0004).unwrap();
46/// // Should be 0xFFFFFFFF since flash defaults to all 1's
47/// assert_eq!(data, 0xFFFF_FFFF);
48///
49/// // Write a value to address 0x1006_0004
50/// flc.write_32(0x1006_0004, 0x7856_3412).unwrap();
51/// // Read the data back from flash memory
52/// let new_data: u32 = flc.read_32(0x1006_0004).unwrap();
53/// assert_eq!(new_data, 0x7856_3412);
54/// ```
55pub struct Flc {
56    flc: crate::pac::Flc,
57    sys_clk: Clock<SystemClock>,
58}
59
60impl Flc {
61    /// Construct a new flash controller peripheral.
62    pub fn new(flc: crate::pac::Flc, sys_clk: Clock<SystemClock>) -> Self {
63        let s = Self { flc, sys_clk };
64        s.config();
65        s
66    }
67
68    /// Configure the flash controller.
69    #[inline]
70    fn config(&self) {
71        // Wait until the flash controller is not busy
72        while self.is_busy() {}
73        // Set FLC divisor
74        let flc_div = self.sys_clk.frequency / 1_000_000;
75        self.flc
76            .clkdiv()
77            .modify(|_, w| unsafe { w.clkdiv().bits(flc_div as u8) });
78        // Clear stale interrupts
79        if self.flc.intr().read().af().bit_is_set() {
80            self.flc.intr().write(|w| w.af().clear_bit());
81        }
82    }
83
84    /// Check if the flash controller is busy.
85    #[inline]
86    pub fn is_busy(&self) -> bool {
87        let ctrl = self.flc.ctrl().read();
88        ctrl.pend().is_busy()
89            || ctrl.pge().bit_is_set()
90            || ctrl.me().bit_is_set()
91            || ctrl.wr().bit_is_set()
92    }
93
94    /// Check if an address is within the valid flash memory range.
95    #[inline]
96    pub fn check_address(&self, address: u32) -> Result<(), FlashError> {
97        if address < FLASH_BASE || address >= FLASH_END {
98            return Err(FlashError::InvalidAddress);
99        }
100        Ok(())
101    }
102
103    /// Check if an address is within the valid flash memory range.
104    #[inline]
105    pub fn check_page_number(&self, page_number: u32) -> Result<(), FlashError> {
106        if page_number >= FLASH_PAGE_COUNT {
107            return Err(FlashError::InvalidAddress);
108        }
109        Ok(())
110    }
111
112    /// Get the base address of a page
113    #[inline]
114    pub fn get_address(&self, page_number: u32) -> Result<u32, FlashError> {
115        self.check_page_number(page_number)?;
116
117        let address = FLASH_BASE + FLASH_PAGE_SIZE * page_number;
118
119        Ok(address)
120    }
121    
122    /// Get the page number of a flash address.
123    #[inline]
124    pub fn get_page_number(&self, address: u32) -> Result<u32, FlashError> {
125        self.check_address(address)?;
126        let page_num = (address >> 13) & (FLASH_PAGE_COUNT - 1);
127        // Check for invalid page number (redundant check)
128        if page_num >= FLASH_PAGE_COUNT {
129            return Err(FlashError::InvalidAddress);
130        }
131        Ok(page_num)
132    }
133
134    /// Set the target address for a write or erase operation.
135    #[inline]
136    fn set_address(&self, address: u32) -> Result<(), FlashError> {
137        self.check_address(address)?;
138        // Convert to physical address
139        let phys_addr = address & (FLASH_SIZE - 1);
140        // Safety: We have validated the address already
141        self.flc
142            .addr()
143            .write(|w| unsafe { w.addr().bits(phys_addr) });
144        Ok(())
145    }
146
147    /// Unlock the flash controller to allow write or erase operations.
148    #[inline]
149    fn unlock_flash(&self) {
150        self.flc.ctrl().modify(|_, w| w.unlock().unlocked());
151        while self.flc.ctrl().read().unlock().is_locked() {}
152    }
153
154    /// Lock the flash controller to prevent write or erase operations.
155    #[inline]
156    fn lock_flash(&self) {
157        self.flc.ctrl().modify(|_, w| w.unlock().locked());
158        while self.flc.ctrl().read().unlock().is_unlocked() {}
159    }
160
161    /// Commit a write operation.
162    #[cfg_attr(feature = "flashprog-linkage", link_section = ".flashprog")]
163    #[inline]
164    fn commit_write(&self) {
165        self.flc.ctrl().modify(|_, w| w.wr().start());
166        while !self.flc.ctrl().read().wr().is_complete() {}
167        while self.is_busy() {}
168    }
169
170    /// Commit a page erase operation.
171    #[cfg_attr(feature = "flashprog-linkage", link_section = ".flashprog")]
172    #[inline]
173    fn commit_erase(&self) {
174        self.flc.ctrl().modify(|_, w| w.pge().start());
175        while !self.flc.ctrl().read().pge().is_complete() {}
176        while self.is_busy() {}
177    }
178
179    /// Write a 128-bit word to flash memory. This is an internal function to
180    /// be used by all other write functions.
181    #[doc(hidden)]
182    #[cfg_attr(feature = "flashprog-linkage", link_section = ".flashprog")]
183    #[inline(never)]
184    fn _write_128(&self, address: u32, data: &[u32; 4]) -> Result<(), FlashError> {
185        // Target address must be 128-bit aligned
186        if address & 0b1111 != 0 {
187            return Err(FlashError::InvalidAddress);
188        }
189        self.check_address(address)?;
190        // Ensure that the flash controller is configured
191        self.config();
192        // Verify that only 1 -> 0 transitions are being made by reading the existing data at the target address
193        for i in 0..4 {
194            // Safety: We have checked the address already
195            let old_data = unsafe { core::ptr::read_volatile((address + i * 4) as *const u32) };
196            if (old_data & data[i as usize]) != data[i as usize] {
197                return Err(FlashError::NeedsErase);
198            }
199        }
200        self.set_address(address)?;
201        // Safety: Data can be written to all bits of the data registers
202        unsafe {
203            self.flc.data(0).write(|w| w.data().bits(data[0]));
204            self.flc.data(1).write(|w| w.data().bits(data[1]));
205            self.flc.data(2).write(|w| w.data().bits(data[2]));
206            self.flc.data(3).write(|w| w.data().bits(data[3]));
207        }
208        self.unlock_flash();
209        // Commit the write operation
210        self.commit_write();
211        self.lock_flash();
212        // Check for access violation
213        if self.flc.intr().read().af().bit_is_set() {
214            self.flc.intr().write(|w| w.af().clear_bit());
215            return Err(FlashError::AccessViolation);
216        }
217        Ok(())
218    }
219
220    /// Erases a page in flash memory.
221    #[doc(hidden)]
222    #[cfg_attr(feature = "flashprog-linkage", link_section = ".flashprog")]
223    #[inline(never)]
224    fn _erase_page(&self, address: u32) -> Result<(), FlashError> {
225        while self.is_busy() {}
226        self.set_address(address)?;
227        self.unlock_flash();
228        // Set erase page code
229        self.flc.ctrl().modify(|_, w| w.erase_code().erase_page());
230        // Commit the erase operation
231        self.commit_erase();
232        self.lock_flash();
233        // Check for access violation
234        if self.flc.intr().read().af().bit_is_set() {
235            self.flc.intr().write(|w| w.af().clear_bit());
236            return Err(FlashError::AccessViolation);
237        }
238        Ok(())
239    }
240
241    /// Writes four [`u32`] to flash memory. Uses little-endian byte order.
242    /// The lowest [`u32`] in the array is written to the lowest address in flash.
243    /// The target address must be 128-bit aligned.
244    ///
245    /// Example:
246    /// ```
247    /// let data: [u32; 4] = [0x0403_0201, 0x0807_0605, 0x0C0B_0A09, 0x100F_0E0D];
248    /// flash.write_128(0x1006_0000, &data).unwrap();
249    /// // The bytes in flash will look like:
250    /// // 10060000: 0102 0304 0506 0708 090A 0B0C 0D0E 0F10
251    /// ```
252    pub fn write_128(&self, address: u32, data: &[u32; 4]) -> Result<(), FlashError> {
253        self._write_128(address, &data)
254    }
255
256    /// Write a [`u32`] to flash memory. Uses little-endian byte order.
257    /// The target address must be 32-bit aligned.
258    ///
259    /// Note: Writes to flash memory must be done in 128-bit (16-byte) blocks.
260    /// This function will read the existing 128-bit word containing the target
261    /// address, modify the 32-bit word within the 128-bit word, and write the
262    /// modified 128-bit word back to flash memory.
263    ///
264    /// Example:
265    /// ```
266    /// let data: u32 = 0x7856_3412;
267    /// flash.write_32(0x1006_0004, data).unwrap();
268    /// // The bytes in flash will look like:
269    /// // 10060000: FFFF FFFF 1234 5678 FFFF FFFF FFFF FFFF
270    /// ```
271    pub fn write_32(&self, address: u32, data: u32) -> Result<(), FlashError> {
272        // Target address must be 32-bit aligned
273        if address & 0b11 != 0 {
274            return Err(FlashError::InvalidAddress);
275        }
276        self.check_address(address)?;
277        let addr_128 = address & !0b1111;
278        self.check_address(addr_128)?;
279        let addr_128_ptr = addr_128 as *const u32;
280        // Read existing data at the 128-bit word containing the target address
281        let mut prev_data: [u32; 4] = [0xFFFF_FFFF; 4];
282        // Safety: We have checked the address already
283        unsafe {
284            prev_data[0] = core::ptr::read_volatile(addr_128_ptr);
285            prev_data[1] = core::ptr::read_volatile(addr_128_ptr.offset(1));
286            prev_data[2] = core::ptr::read_volatile(addr_128_ptr.offset(2));
287            prev_data[3] = core::ptr::read_volatile(addr_128_ptr.offset(3));
288        }
289        // Determine index of the 32-bit word within the 128-bit word
290        let data_idx = (address & 0b1100) >> 2;
291        // Modify the 32-bit word within the 128-bit word
292        prev_data[data_idx as usize] = data;
293        // Write the modified 128-bit word to flash memory
294        self._write_128(addr_128, &prev_data)
295    }
296
297    /// Reads four [`u32`] from flash memory. Uses little-endian byte order.
298    /// The lowest [`u32`] in the array is read from the lowest address in flash.
299    /// The target address must be 128-bit aligned.
300    pub fn read_128(&self, address: u32) -> Result<[u32; 4], FlashError> {
301        // Target address must be 128-bit aligned
302        if address & 0b1111 != 0 {
303            return Err(FlashError::InvalidAddress);
304        }
305        self.check_address(address)?;
306        let addr_128_ptr = address as *const u32;
307        // Safety: We have checked the address already
308        unsafe {
309            Ok([
310                core::ptr::read_volatile(addr_128_ptr),
311                core::ptr::read_volatile(addr_128_ptr.offset(1)),
312                core::ptr::read_volatile(addr_128_ptr.offset(2)),
313                core::ptr::read_volatile(addr_128_ptr.offset(3)),
314            ])
315        }
316    }
317
318    /// Reads a [`u32`] from flash memory. Uses little-endian byte order.
319    /// The target address must be 32-bit aligned.
320    pub fn read_32(&self, address: u32) -> Result<u32, FlashError> {
321        // Target address must be 32-bit aligned
322        if address & 0b11 != 0 {
323            return Err(FlashError::InvalidAddress);
324        }
325        self.check_address(address)?;
326        let addr_32_ptr = address as *const u32;
327        // Safety: We have checked the address already
328        unsafe { Ok(core::ptr::read_volatile(addr_32_ptr)) }
329    }
330
331    /// Erases a page in flash memory.
332    ///
333    /// # Safety
334    /// Care must be taken to not erase the page containing the executing code.
335    pub unsafe fn erase_page(&self, address: u32) -> Result<(), FlashError> {
336        self._erase_page(address)
337    }
338
339    /// Protects a page in flash memory from write or erase operations.
340    /// Effective until the next external or power-on reset.
341    pub fn disable_page_write(&self, address: u32) -> Result<(), FlashError> {
342        while self.is_busy() {}
343        let page_num = self.get_page_number(address)?;
344        // Lock based on page number
345        if page_num < 32 {
346            let write_lock_bit = 1 << page_num;
347            self.flc
348                .welr0()
349                .write(|w| unsafe { w.bits(write_lock_bit) });
350            while self.flc.welr0().read().bits() & write_lock_bit == write_lock_bit {}
351        } else {
352            let write_lock_bit = 1 << (page_num - 32);
353            self.flc
354                .welr1()
355                .write(|w| unsafe { w.bits(write_lock_bit) });
356            while self.flc.welr1().read().bits() & write_lock_bit == write_lock_bit {}
357        }
358        Ok(())
359    }
360
361    /// Protects a page in flash memory from read operations.
362    /// Effective until the next external or power-on reset.
363    pub fn disable_page_read(&self, address: u32) -> Result<(), FlashError> {
364        while self.is_busy() {}
365        let page_num = self.get_page_number(address)?;
366        // Lock based on page number
367        if page_num < 32 {
368            let read_lock_bit = 1 << page_num;
369            self.flc.rlr0().write(|w| unsafe { w.bits(read_lock_bit) });
370            while self.flc.rlr0().read().bits() & read_lock_bit == read_lock_bit {}
371        } else {
372            let read_lock_bit = 1 << (page_num - 32);
373            self.flc.rlr1().write(|w| unsafe { w.bits(read_lock_bit) });
374            while self.flc.rlr1().read().bits() & read_lock_bit == read_lock_bit {}
375        }
376        Ok(())
377    }
378}