Skip to main content

tinyboot_ch32_hal/flash/
v0.rs

1const KEY1: u32 = 0x4567_0123;
2const KEY2: u32 = 0xCDEF_89AB;
3const FLASH: ch32_metapac::flash::Flash = ch32_metapac::FLASH;
4
5#[inline(always)]
6fn wait_busy() {
7    while FLASH.statr().read().bsy() {}
8    debug_assert!(
9        !FLASH.statr().read().wrprterr(),
10        "flash write protection error"
11    );
12    // RM: clear EOP flag (W1C) after every BUFRST, BUFLOAD, and STRT.
13    FLASH.statr().write(|w| w.set_eop(true));
14}
15
16/// Unlock flash controller (KEYR + MODEKEYR).
17pub fn unlock() {
18    FLASH.keyr().write(|w| w.set_keyr(KEY1));
19    FLASH.keyr().write(|w| w.set_keyr(KEY2));
20    FLASH.modekeyr().write(|w| w.set_modekeyr(KEY1));
21    FLASH.modekeyr().write(|w| w.set_modekeyr(KEY2));
22}
23
24/// Lock flash controller.
25#[inline(always)]
26pub fn lock() {
27    FLASH.ctlr().write(|w| {
28        w.set_lock(true);
29        w.set_flock(true);
30    });
31}
32
33/// Flash page size in bytes (erase and fast-write granularity).
34pub const PAGE_SIZE: usize = 64;
35
36/// Fast-write buffer load size in bytes.
37const BUF_LOAD_SIZE: usize = 4;
38
39// --- User flash (fast page erase/write) ---
40
41/// Erase a single 64-byte page at `addr` (RM §16.4.7).
42pub fn erase(addr: u32) {
43    // Step 4: set FTER
44    FLASH.ctlr().write(|w| w.set_page_er(true));
45    // Step 5: write page address
46    FLASH.addr().write(|w| w.set_addr(addr));
47    // Step 6: set STRT
48    FLASH.ctlr().write(|w| {
49        w.set_page_er(true);
50        w.set_strt(true);
51    });
52    // Step 7: wait BSY, clear EOP
53    wait_busy();
54    // Clear FTER
55    FLASH.ctlr().write(|_| {});
56}
57
58/// Write `data` to flash at `addr` (RM §16.4.6).
59///
60/// Must not cross a page boundary. `data` length must be a multiple
61/// of 4 bytes, `addr` must be 4-byte aligned.
62pub fn write(addr: u32, data: &[u8]) {
63    let page_base = addr & !(PAGE_SIZE as u32 - 1);
64    debug_assert!(
65        (addr as usize & (BUF_LOAD_SIZE - 1)) == 0,
66        "write: addr not word-aligned"
67    );
68    debug_assert!(
69        data.len().is_multiple_of(BUF_LOAD_SIZE),
70        "write: len not word-aligned"
71    );
72    debug_assert!(
73        addr + data.len() as u32 <= page_base + PAGE_SIZE as u32,
74        "write: crosses page boundary"
75    );
76
77    // Step 4: set FTPG alone
78    FLASH.ctlr().write(|w| w.set_page_pg(true));
79    // Step 5: set BUFRST (with FTPG still set)
80    FLASH.ctlr().write(|w| {
81        w.set_page_pg(true);
82        w.set_bufrst(true);
83    });
84    // Step 6: wait BSY, clear EOP
85    wait_busy();
86
87    // Steps 7-8: load words into buffer (repeat for each 4-byte chunk)
88    let mut buf_addr = addr;
89    let mut ptr = data.as_ptr() as *const u32;
90    for _ in 0..data.len() / BUF_LOAD_SIZE {
91        let word = unsafe { ptr.read() };
92        unsafe { core::ptr::write_volatile(buf_addr as *mut u32, word) };
93        FLASH.ctlr().write(|w| {
94            w.set_page_pg(true);
95            w.set_bufload(true);
96        });
97        wait_busy();
98        buf_addr += BUF_LOAD_SIZE as u32;
99        ptr = unsafe { ptr.add(1) };
100    }
101
102    // Step 10: write page address
103    FLASH.addr().write(|w| w.set_addr(page_base));
104    // Step 11: set STRT
105    FLASH.ctlr().write(|w| {
106        w.set_page_pg(true);
107        w.set_strt(true);
108    });
109    // Step 12: wait BSY, clear EOP
110    wait_busy();
111    // Step 14: clear FTPG
112    FLASH.ctlr().write(|_| {});
113}
114
115pub fn boot_mode() -> bool {
116    FLASH.statr().read().boot_mode()
117}
118
119pub fn set_boot_mode(mode: bool) {
120    FLASH.boot_modekeyp().write(|w| w.set_modekeyr(KEY1));
121    FLASH.boot_modekeyp().write(|w| w.set_modekeyr(KEY2));
122    FLASH.statr().write(|w| w.set_boot_mode(mode));
123}