···11+use defmt::{trace, warn};
22+use eepy_sys::header::XIP_BASE;
33+use fw16_epd_bsp::hal::Sio;
44+use fw16_epd_bsp::pac;
55+use crate::core1::{ToCore0Message, ToCore1Message};
66+77+fn flash_op(f: impl FnOnce()) {
88+ let mut fifo = Sio::new(unsafe { pac::Peripherals::steal() }.SIO).fifo;
99+1010+ fifo.write_blocking(ToCore1Message::FlashWait as u32);
1111+ // Wait until core1 has acknowledged that it is now in RAM code
1212+ while fifo.read_blocking() != ToCore0Message::FlashAck as u32 {
1313+ warn!("received incorrect message");
1414+ }
1515+1616+ trace!("core1 is in safe state, performing flash operation...");
1717+ cortex_m::interrupt::free(|_cs| f());
1818+1919+ // Wake up core1
2020+ fifo.write_blocking(ToCore1Message::FlashFinished as u32);
2121+}
2222+2323+fn flush_cache_range(start_addr: u32, len: u32) {
2424+ // RP2040 Datasheet 2.6.3.2:
2525+ // "A write to the 0x10… mirror will look up the addressed location in the cache, and delete
2626+ // any matching entry found. Writing to all word-aligned locations in an address range (e.g.
2727+ // a flash sector that has just been erased and reprogrammed) therefore eliminates the
2828+ // possibility of stale cached data in this range, without suffering the effects of a
2929+ // complete cache flush."
3030+3131+ unsafe {
3232+ let start_ptr: *mut u32 = XIP_BASE.add(start_addr as usize).cast_mut().cast();
3333+ let words = len / 4;
3434+ for _ in 0..words {
3535+ start_ptr.add(words as usize).write_volatile(0);
3636+ }
3737+ }
3838+}
3939+4040+pub(crate) unsafe fn erase(start_addr: u32, len: u32) {
4141+ trace!("flash erasing start {} len {}", start_addr, len);
4242+ flash_op(|| rp2040_flash::flash::flash_range_erase(start_addr, len, true));
4343+ flush_cache_range(start_addr, len);
4444+}
4545+4646+pub(crate) unsafe fn program(start_addr: u32, data: &[u8]) {
4747+ trace!("flash programming start {} len {}", start_addr, data.len());
4848+ flash_op(|| rp2040_flash::flash::flash_range_program(start_addr, data, true));
4949+ flush_cache_range(start_addr, data.len() as u32);
5050+}
5151+5252+pub(crate) unsafe fn erase_and_program(start_addr: u32, data: &[u8]) {
5353+ trace!("flash programming and erasing start {} len {}", start_addr, data.len());
5454+ flash_op(|| rp2040_flash::flash::flash_range_erase_and_program(start_addr, data, true));
5555+ flush_cache_range(start_addr, data.len() as u32);
5656+}
+11-67
eepy/src/main.rs
···77mod launcher;
88mod usb;
99mod exception;
1010+mod tickv;
1111+mod flash;
1212+mod core1;
10131114extern crate panic_probe;
1215extern crate defmt_rtt;
···3841use eepy_sys::input_common::{Event, TouchEvent, TouchEventType};
3942use tp370pgh01::rp2040::{Rp2040PervasiveSpiDelays, IoPin};
4043use tp370pgh01::{Tp370pgh01, IMAGE_BYTES};
4444+use crate::core1::{core1_main, ToCore1Message, GLOBAL_EPD, IDLE};
4145use crate::ringbuffer::RingBuffer;
42464347const SRAM_END: *mut u8 = 0x20042000 as *mut u8;
···5357static GLOBAL_ALARM0: Mutex<RefCell<Option<Alarm0>>> = Mutex::new(RefCell::new(None));
54585559static IMAGE_BUFFER: Mutex<RefCell<[u8; IMAGE_BYTES]>> = Mutex::new(RefCell::new([0; IMAGE_BYTES]));
5656-static DO_REFRESH: AtomicBool = AtomicBool::new(false);
5757-static FAST_REFRESH: AtomicBool = AtomicBool::new(false);
5858-static REFRESHING: AtomicBool = AtomicBool::new(false);
5959-static EPD_NEEDS_HARD_RESET: AtomicBool = AtomicBool::new(true);
6060static TEMP: AtomicU8 = AtomicU8::new(20);
6161static SERIAL_NUMBER: OnceCell<[u8; 16]> = OnceCell::new();
62626363static EVENT_QUEUE: Mutex<RefCell<RingBuffer<Event>>> = Mutex::new(RefCell::new(RingBuffer::new()));
6464static TOUCH_ENABLED: AtomicBool = AtomicBool::new(false);
6565-6666-static FLASHING: AtomicBool = AtomicBool::new(false);
6767-static FLASHING_ACK: AtomicBool = AtomicBool::new(false);
6868-6969-/// Function in RAM to be executed by core1 whilst flashing programs (core1 cannot be executing code
7070-/// from flash whilst writing/erasing flash)
7171-///
7272-/// To exit this function from core0, set [FLASHING] to false and SEV
7373-#[link_section = ".data.ram_func"]
7474-fn core1_flash_wait() {
7575- cortex_m::interrupt::disable();
7676- FLASHING_ACK.store(true, Ordering::Relaxed);
7777-7878- while FLASHING.load(Ordering::Relaxed) {
7979- cortex_m::asm::wfe();
8080- }
8181-8282- FLASHING_ACK.store(false, Ordering::Relaxed);
8383- unsafe { cortex_m::interrupt::enable() };
8484-}
85658666#[entry]
8767fn main() -> ! {
···247227 core.NVIC.set_priority(interrupt::USBCTRL_IRQ, 0b11000000);
248228 }
249229230230+ let epd = Tp370pgh01::new(cs, IoPin::new(sda), sck, dc, busy, rst, timer, Rp2040PervasiveSpiDelays);
231231+ critical_section::with(|cs| GLOBAL_EPD.borrow_ref_mut(cs).replace(epd));
250232 let mut mc = Multicore::new(&mut pac.PSM, &mut pac.PPB, &mut sio.fifo);
251233 let core1 = &mut mc.cores()[1];
252252- core1.spawn(CORE1_STACK.take().unwrap(), move || {
253253- info!("core1 init");
254254-255255- let mut epd = Tp370pgh01::new(cs, IoPin::new(sda), sck, dc, busy, rst, timer, Rp2040PervasiveSpiDelays);
256256-257257- let mut prev_image = [0u8; IMAGE_BYTES];
258258- let mut image = [0u8; IMAGE_BYTES];
259259-260260- loop {
261261- cortex_m::asm::wfe();
262262-263263- if FLASHING.load(Ordering::Relaxed) {
264264- core1_flash_wait();
265265- continue;
266266- }
267267-268268- if EPD_NEEDS_HARD_RESET.swap(false, Ordering::Relaxed) {
269269- epd.hard_reset().unwrap();
270270- }
271271-272272- REFRESHING.store(true, Ordering::Relaxed);
273273-274274- if DO_REFRESH.swap(false, Ordering::Relaxed) {
275275- if FAST_REFRESH.load(Ordering::Relaxed) {
276276- prev_image.copy_from_slice(&image);
277277- critical_section::with(|cs| image.copy_from_slice(IMAGE_BUFFER.borrow_ref(cs).as_ref()));
278278- epd.soft_reset().unwrap();
279279- epd.refresh_fast(&image, &prev_image, TEMP.load(Ordering::Relaxed)).unwrap();
280280- } else {
281281- critical_section::with(|cs| image.copy_from_slice(IMAGE_BUFFER.borrow_ref(cs).as_ref()));
282282- epd.soft_reset().unwrap();
283283- epd.refresh(&image, TEMP.load(Ordering::Relaxed)).unwrap();
284284- }
285285-286286- critical_section::with(|cs| EVENT_QUEUE.borrow_ref_mut(cs).push(Event::RefreshFinished));
287287- cortex_m::asm::sev();
288288- }
289289-290290- REFRESHING.store(false, Ordering::Relaxed);
291291- }
292292- }).unwrap();
234234+ core1.spawn(CORE1_STACK.take().unwrap(), core1_main).unwrap();
235235+ sio.fifo.write_blocking(ToCore1Message::HardResetEpd as u32);
293236294237 unsafe {
295238 core.NVIC.set_priority(interrupt::IO_IRQ_BANK0, 0);
···423366 }
424367425368 // Wait until refresh has finished
426426- while REFRESHING.load(Ordering::Relaxed) {}
369369+ while !IDLE.load(Ordering::Relaxed) {}
427370428371 // Power down temp sensor
429372 if let Some(i2c) = &mut i2c {
···458401 power_pin.set_low().unwrap();
459402 }
460403461461- EPD_NEEDS_HARD_RESET.store(true, Ordering::Relaxed);
404404+ let mut fifo = Sio::new(unsafe { pac::Peripherals::steal() }.SIO).fifo;
405405+ fifo.write_blocking(ToCore1Message::HardResetEpd as u32);
462406463407 if let Some(reset) = TOUCH_RESET_PIN {
464408 cortex_m::asm::delay(1000000);
+154-66
eepy/src/syscall.rs
···3434 Some(SyscallNumber::Exec) => handle_exec(stack_values, using_psp),
3535 Some(SyscallNumber::CriticalSection) => cs::handle_cs(stack_values),
3636 Some(SyscallNumber::Flash) => flash::handle_flash(stack_values),
3737+ Some(SyscallNumber::KvStore) => kv_store::handle_kv_store(stack_values),
3738 None => panic!("illegal syscall"),
3839 }
3940}
···137138}
138139139140mod image {
140140- use core::sync::atomic::Ordering;
141141- use eepy_sys::image::{ImageSyscall, RefreshBlockMode};
141141+ use eepy_sys::image::ImageSyscall;
142142+ use fw16_epd_bsp::hal::Sio;
143143+ use fw16_epd_bsp::pac;
142144 use tp370pgh01::IMAGE_BYTES;
143143- use crate::{DO_REFRESH, FAST_REFRESH, IMAGE_BUFFER, REFRESHING};
145145+ use crate::IMAGE_BUFFER;
146146+ use crate::core1::{ToCore0Message, ToCore1Message};
144147 use super::StackFrame;
145148146149 pub(super) fn handle_image(stack_values: &mut StackFrame) {
147150 match ImageSyscall::from_repr(stack_values.r0) {
148148- Some(ImageSyscall::WriteImage) => handle_write_image(stack_values),
149151 Some(ImageSyscall::Refresh) => handle_refresh(stack_values),
152152+ Some(ImageSyscall::MaybeRefresh) => handle_maybe_refresh(stack_values),
150153 None => panic!("illegal syscall"),
151154 }
152155 }
153156154154- fn handle_write_image(stack_values: &mut StackFrame) {
155155- let image: &[u8; IMAGE_BYTES] = unsafe { &*(stack_values.r1 as *const [u8; IMAGE_BYTES]) };
156156- critical_section::with(|cs| IMAGE_BUFFER.borrow_ref_mut(cs).copy_from_slice(image));
157157- }
158158-159157 fn handle_refresh(stack_values: &mut StackFrame) {
160158 let fast_refresh = stack_values.r1 != 0;
161161- let blocking_mode = RefreshBlockMode::from_repr(stack_values.r2).expect("illegal refresh blocking mode");
159159+ let image: &[u8; IMAGE_BYTES] = unsafe { &*(stack_values.r2 as *const [u8; IMAGE_BYTES]) };
160160+ critical_section::with(|cs| IMAGE_BUFFER.borrow_ref_mut(cs).copy_from_slice(image));
162161163163- DO_REFRESH.store(true, Ordering::Relaxed);
164164- FAST_REFRESH.store(fast_refresh, Ordering::Relaxed);
165165- cortex_m::asm::sev();
162162+ let mut fifo = Sio::new(unsafe { pac::Peripherals::steal() }.SIO).fifo;
163163+ let message = if fast_refresh { ToCore1Message::RefreshFast } else { ToCore1Message::RefreshNormal };
164164+ fifo.write_blocking(message as u32);
166165167167- if matches!(blocking_mode, RefreshBlockMode::BlockAcknowledge | RefreshBlockMode::BlockFinish) {
168168- while DO_REFRESH.load(Ordering::Relaxed) {}
166166+ if fifo.read_blocking() != ToCore0Message::RefreshAck as u32 {
167167+ panic!("received incorrect message");
169168 }
169169+ }
170170+171171+ fn handle_maybe_refresh(stack_values: &mut StackFrame) {
172172+ let fast_refresh = stack_values.r1 != 0;
173173+ let image: &[u8; IMAGE_BYTES] = unsafe { &*(stack_values.r2 as *const [u8; IMAGE_BYTES]) };
174174+ critical_section::with(|cs| IMAGE_BUFFER.borrow_ref_mut(cs).copy_from_slice(image));
170175171171- if matches!(blocking_mode, RefreshBlockMode::BlockFinish) {
172172- while REFRESHING.load(Ordering::Relaxed) {}
176176+ let mut fifo = Sio::new(unsafe { pac::Peripherals::steal() }.SIO).fifo;
177177+ let message = if fast_refresh { ToCore1Message::MaybeRefreshFast } else { ToCore1Message::MaybeRefreshNormal };
178178+ if fifo.is_write_ready() {
179179+ fifo.write(message as u32);
173180 }
174181 }
175182}
···248255}
249256250257mod flash {
251251- use core::sync::atomic::Ordering;
252258 use eepy_sys::flash::FlashSyscall;
253259 use eepy_sys::header::{SLOT_SIZE, XIP_BASE};
254260 use crate::exception::StackFrame;
255255- use crate::{FLASHING, FLASHING_ACK};
261261+ use crate::flash::{erase, erase_and_program, program};
256262257263 pub(super) fn handle_flash(stack_values: &mut StackFrame) {
258264 match FlashSyscall::from_repr(stack_values.r0) {
···283289 }
284290 }
285291286286- fn begin() {
287287- // Make sure core1 is running code from RAM with interrupts disabled
288288- FLASHING.store(true, Ordering::Relaxed);
289289- cortex_m::asm::sev();
290290- // Wait until core1 has acknowledged that it is now in RAM code
291291- while !FLASHING_ACK.load(Ordering::Relaxed) {}
292292- // Disable interrupts on this core
293293- cortex_m::interrupt::disable();
294294- }
295295-296296- fn end() {
297297- // Enable interrupts
298298- unsafe { cortex_m::interrupt::enable() }
299299- // Wake up core1
300300- FLASHING.store(false, Ordering::Relaxed);
301301- cortex_m::asm::sev();
302302- }
303303-304304- fn flush_cache_range(start_addr: u32, len: u32) {
305305- // RP2040 Datasheet 2.6.3.2:
306306- // "A write to the 0x10… mirror will look up the addressed location in the cache, and delete
307307- // any matching entry found. Writing to all word-aligned locations in an address range (e.g.
308308- // a flash sector that has just been erased and reprogrammed) therefore eliminates the
309309- // possibility of stale cached data in this range, without suffering the effects of a
310310- // complete cache flush."
311311-312312- unsafe {
313313- let start_ptr: *mut u32 = XIP_BASE.add(start_addr as usize).cast_mut().cast();
314314- let words = len / 4;
315315- for _ in 0..words {
316316- start_ptr.add(words as usize).write_volatile(0);
317317- }
318318- }
319319- }
320320-321292 fn handle_erase(stack_values: &mut StackFrame) {
322293 let start_addr = stack_values.r1 as u32;
323294 let len = stack_values.r2 as u32;
···326297 panic!("unaligned flash erase");
327298 }
328299329329- begin();
330300 unsafe {
331331- rp2040_flash::flash::flash_range_erase(start_addr, len, true);
332332- flush_cache_range(start_addr, len);
301301+ erase(start_addr, len);
333302 }
334334- end();
335303 }
336304337305 fn handle_program(stack_values: &mut StackFrame) {
···342310 panic!("unaligned flash program");
343311 }
344312345345- begin();
346313 unsafe {
347347- rp2040_flash::flash::flash_range_program(start_addr, data, true);
348348- flush_cache_range(start_addr, data.len() as u32);
314314+ program(start_addr, data);
349315 }
350350- end();
351316 }
352317353318 fn handle_erase_and_program(stack_values: &mut StackFrame) {
···358323 panic!("unaligned flash erase");
359324 }
360325361361- begin();
362326 unsafe {
363363- rp2040_flash::flash::flash_range_erase_and_program(start_addr, data, true);
364364- flush_cache_range(start_addr, data.len() as u32);
327327+ erase_and_program(start_addr, data);
365328 }
366366- end();
329329+ }
330330+}
331331+332332+mod kv_store {
333333+ use core::hash::Hasher;
334334+ use siphasher::sip::SipHasher;
335335+ use tickv::{ErrorCode, TicKV};
336336+ use eepy_sys::header::{slot, SLOT_SIZE};
337337+ use eepy_sys::kv_store::{AppendKeyError, DeleteKeyError, KvStoreSyscall, PutKeyError, ReadKeyArgs, ReadKeyError, WriteKeyArgs};
338338+ use eepy_sys::SafeResult;
339339+ use crate::exception::StackFrame;
340340+ use crate::tickv::{with_tickv, EepyFlashController};
341341+342342+ pub(super) fn handle_kv_store(stack_values: &mut StackFrame) {
343343+ match KvStoreSyscall::from_repr(stack_values.r0) {
344344+ Some(KvStoreSyscall::ReadKey) => handle_kv_get(stack_values),
345345+ Some(KvStoreSyscall::AppendKey) => handle_kv_append(stack_values),
346346+ Some(KvStoreSyscall::PutKey) => handle_kv_put(stack_values),
347347+ Some(KvStoreSyscall::InvalidateKey) => handle_kv_invalidate(stack_values),
348348+ Some(KvStoreSyscall::ZeroiseKey) => handle_kv_zeroise(stack_values),
349349+ None => panic!("illegal syscall"),
350350+ }
351351+ }
352352+353353+ fn hash(stack_values: &mut StackFrame, key: &[u8]) -> u64 {
354354+ let slot_number = (stack_values.pc as usize) / SLOT_SIZE;
355355+ let slot_header = unsafe { slot(slot_number as u8) };
356356+ let program_name = unsafe { core::slice::from_raw_parts((*slot_header).name_ptr, (*slot_header).name_len) };
357357+358358+ let mut hasher = SipHasher::new();
359359+ hasher.write(program_name);
360360+ hasher.write(key);
361361+ hasher.finish()
362362+ }
363363+364364+ fn append_key_gc(tickv: &TicKV<EepyFlashController, 4096>, key: u64, value: &[u8]) -> Result<(), ErrorCode> {
365365+ let mut res = tickv.append_key(key, value).map(|_| ());
366366+ if let Err(ErrorCode::RegionFull | ErrorCode::FlashFull) = res {
367367+ res = tickv.garbage_collect().map(|_| ());
368368+ if res.is_ok() {
369369+ res = tickv.append_key(key, value).map(|_| ());
370370+ }
371371+ }
372372+ res
373373+ }
374374+375375+ fn handle_kv_get(stack_values: &mut StackFrame) {
376376+ let args = stack_values.r1 as *const ReadKeyArgs;
377377+ let key = unsafe { core::slice::from_raw_parts((*args).key_ptr, (*args).key_len) };
378378+ let buf = unsafe { core::slice::from_raw_parts_mut((*args).buf_ptr, (*args).buf_len) };
379379+380380+ let hashed_key = hash(stack_values, key);
381381+ let res = unsafe { with_tickv(|tickv| tickv.get_key(hashed_key, buf)) };
382382+383383+ let res_ptr = stack_values.r2 as *mut SafeResult<usize, ReadKeyError>;
384384+ let res: SafeResult<usize, ReadKeyError> = res
385385+ .map(|(_, read)| read)
386386+ .map_err(|e| e.try_into().unwrap())
387387+ .into();
388388+ unsafe { res_ptr.write(res) };
389389+ }
390390+391391+ fn handle_kv_append(stack_values: &mut StackFrame) {
392392+ let args = stack_values.r1 as *const WriteKeyArgs;
393393+ let key = unsafe { core::slice::from_raw_parts((*args).key_ptr, (*args).key_len) };
394394+ let value = unsafe { core::slice::from_raw_parts((*args).value_ptr, (*args).value_len) };
395395+396396+ let hashed_key = hash(stack_values, key);
397397+ let res = unsafe { with_tickv(|tickv| append_key_gc(tickv, hashed_key, value)) };
398398+399399+ let res_ptr = stack_values.r2 as *mut SafeResult<(), AppendKeyError>;
400400+ let res: SafeResult<(), AppendKeyError> = res
401401+ .map(|_| ())
402402+ .map_err(|e| e.try_into().unwrap())
403403+ .into();
404404+ unsafe { res_ptr.write(res) };
405405+ }
406406+407407+ fn handle_kv_put(stack_values: &mut StackFrame) {
408408+ let args = stack_values.r1 as *const WriteKeyArgs;
409409+ let key = unsafe { core::slice::from_raw_parts((*args).key_ptr, (*args).key_len) };
410410+ let value = unsafe { core::slice::from_raw_parts((*args).value_ptr, (*args).value_len) };
411411+412412+ let hashed_key = hash(stack_values, key);
413413+ let mut res = unsafe { with_tickv(|tickv| append_key_gc(tickv, hashed_key, value)) };
414414+415415+ // if there is already a value for this key, invalidate it and write the new value
416416+ if let Err(ErrorCode::KeyAlreadyExists) = res {
417417+ res = unsafe { with_tickv(|tickv| tickv.invalidate_key(hashed_key).map(|_| ())) };
418418+ if res.is_ok() {
419419+ res = unsafe { with_tickv(|tickv| append_key_gc(tickv, hashed_key, value)) };
420420+ }
421421+ }
422422+423423+ let res_ptr = stack_values.r2 as *mut SafeResult<(), PutKeyError>;
424424+ let res: SafeResult<(), PutKeyError> = res
425425+ .map(|_| ())
426426+ .map_err(|e| e.try_into().unwrap())
427427+ .into();
428428+ unsafe { res_ptr.write(res) };
429429+ }
430430+431431+ fn handle_kv_invalidate(stack_values: &mut StackFrame) {
432432+ let key = unsafe { core::slice::from_raw_parts(stack_values.r1 as *const u8, stack_values.r2) };
433433+ let hashed_key = hash(stack_values, key);
434434+ let res = unsafe { with_tickv(|tickv| tickv.invalidate_key(hashed_key)) };
435435+436436+ let res_ptr = stack_values.r3 as *mut SafeResult<(), DeleteKeyError>;
437437+ let res: SafeResult<(), DeleteKeyError> = res
438438+ .map(|_| ())
439439+ .map_err(|e| e.try_into().unwrap())
440440+ .into();
441441+ unsafe { res_ptr.write(res) };
442442+ }
443443+444444+ fn handle_kv_zeroise(stack_values: &mut StackFrame) {
445445+ let key = unsafe { core::slice::from_raw_parts(stack_values.r1 as *const u8, stack_values.r2) };
446446+ let hashed_key = hash(stack_values, key);
447447+ let res = unsafe { with_tickv(|tickv| tickv.zeroise_key(hashed_key)) };
448448+449449+ let res_ptr = stack_values.r3 as *mut SafeResult<(), DeleteKeyError>;
450450+ let res: SafeResult<(), DeleteKeyError> = res
451451+ .map(|_| ())
452452+ .map_err(|e| e.try_into().unwrap())
453453+ .into();
454454+ unsafe { res_ptr.write(res) };
367455 }
368456}
+99
eepy/src/tickv.rs
···11+use once_cell::unsync::OnceCell;
22+use defmt::trace;
33+use siphasher::sip::SipHasher;
44+use tickv::{ErrorCode, FlashController, TicKV, MAIN_KEY};
55+66+const TICKV_START_XIP: *const u8 = 0x10040000 as *const u8;
77+const TICKV_START_FLASH: usize = 0x40000;
88+const TICKV_SIZE: usize = 256 * 1024;
99+1010+pub(crate) struct EepyFlashController;
1111+1212+impl FlashController<4096> for EepyFlashController {
1313+ fn read_region(&self, region_number: usize, buf: &mut [u8; 4096]) -> Result<(), ErrorCode> {
1414+ trace!("kv read_region");
1515+1616+ unsafe {
1717+ let start_addr = TICKV_START_XIP.add(region_number * 4096);
1818+ let slice = core::slice::from_raw_parts(start_addr, 4096);
1919+ buf.copy_from_slice(slice);
2020+ Ok(())
2121+ }
2222+ }
2323+2424+ fn write(&self, mut address: usize, mut buf: &[u8]) -> Result<(), ErrorCode> {
2525+ trace!("kv write");
2626+2727+ // If the address is unaligned, read-modify-write the first part
2828+ if address % 256 != 0 {
2929+ let write_len = usize::min(buf.len(), 256 - (address % 256));
3030+ let block_start = address & !(256 - 1);
3131+3232+ let mut write_buf = [0u8; 256];
3333+ unsafe {
3434+ write_buf.copy_from_slice(core::slice::from_raw_parts(TICKV_START_XIP.add(block_start), 256));
3535+ }
3636+ write_buf[address % 256..address % 256 + write_len].copy_from_slice(&buf[..write_len]);
3737+3838+ unsafe {
3939+ crate::flash::program((TICKV_START_FLASH + block_start) as u32, &write_buf);
4040+ }
4141+4242+ buf = &buf[write_len..];
4343+ address += write_len;
4444+ }
4545+4646+ let blocks = buf.len() / 256;
4747+ if blocks > 0 {
4848+ unsafe {
4949+ crate::flash::program((TICKV_START_FLASH + address) as u32, &buf[..blocks * 256]);
5050+ }
5151+ }
5252+ address += blocks * 256;
5353+ buf = &buf[blocks * 256..];
5454+5555+ if buf.len() > 0 {
5656+ let mut write_buf = [0u8; 256];
5757+ write_buf[..buf.len()].copy_from_slice(buf);
5858+ unsafe {
5959+ write_buf[buf.len()..].copy_from_slice(core::slice::from_raw_parts(TICKV_START_XIP.add(address), 256 - buf.len()));
6060+ }
6161+6262+ unsafe {
6363+ crate::flash::program((TICKV_START_FLASH + address) as u32, &write_buf);
6464+ }
6565+ }
6666+6767+ Ok(())
6868+ }
6969+7070+ fn erase_region(&self, region_number: usize) -> Result<(), ErrorCode> {
7171+ trace!("kv erase_region");
7272+7373+ unsafe {
7474+ crate::flash::erase((TICKV_START_FLASH + region_number * 4096) as u32, 4096);
7575+ }
7676+ Ok(())
7777+ }
7878+}
7979+8080+static mut TICKV_BUFFER: [u8; 4096] = [0u8; 4096];
8181+static mut TICKV: OnceCell<TicKV<EepyFlashController, 4096>> = OnceCell::new();
8282+8383+/// SAFETY: this must only be called once
8484+unsafe fn init_tickv() -> TicKV<'static, EepyFlashController, 4096> {
8585+ // SAFETY: this is only called once, so we can't have aliased mutable references
8686+ #[allow(static_mut_refs)]
8787+ let read_buffer = unsafe { &mut TICKV_BUFFER };
8888+8989+ let tickv = TicKV::new(EepyFlashController, read_buffer, TICKV_SIZE);
9090+9191+ let hasher = SipHasher::new();
9292+ tickv.initialise(hasher.hash(MAIN_KEY)).expect("Failed to initialise TicKV");
9393+ tickv
9494+}
9595+9696+#[allow(static_mut_refs)]
9797+pub(crate) unsafe fn with_tickv<R>(f: impl FnOnce(&TicKV<EepyFlashController, 4096>) -> R) -> R {
9898+ f(TICKV.get_or_init(|| init_tickv()))
9999+}