Expand description
TicKV can be used asynchronously. This module provides documentation and
tests for using it with an async FlashController
interface.
To do this first there are special error values to return from the
FlashController
functions. These are the ReadNotReady
, WriteNotReady
and EraseNotReady
types.
// EXAMPLE ONLY: The `DefaultHasher` is subject to change
// and hence is not a good fit.
use std::collections::hash_map::DefaultHasher;
use core::hash::{Hash, Hasher};
use std::cell::{Cell, RefCell};
use tickv::{AsyncTicKV, MAIN_KEY};
use tickv::error_codes::ErrorCode;
use tickv::flash_controller::FlashController;
fn get_hashed_key(unhashed_key: &[u8]) -> u64 {
let mut hash_function = DefaultHasher::new();
unhashed_key.hash(&mut hash_function);
hash_function.finish()
}
struct FlashCtrl {
buf: RefCell<[[u8; 1024]; 64]>,
async_read_region: Cell<usize>,
async_erase_region: Cell<usize>,
}
impl FlashCtrl {
fn new() -> Self {
Self {
buf: RefCell::new([[0xFF; 1024]; 64]),
async_read_region: Cell::new(10),
async_erase_region: Cell::new(10),
}
}
}
impl FlashController<1024> for FlashCtrl {
fn read_region(
&self,
region_number: usize,
buf: &mut [u8; 1024],
) -> Result<(), ErrorCode> {
// We aren't ready yet, launch the async operation
self.async_read_region.set(region_number);
return Err(ErrorCode::ReadNotReady(region_number));
Ok(())
}
fn write(&self, address: usize, buf: &[u8]) -> Result<(), ErrorCode> {
// Save the write operation to a queue, we don't need to re-call
for (i, d) in buf.iter().enumerate() {
self.buf.borrow_mut()[address / 1024][(address % 1024) + i] = *d;
}
Ok(())
}
fn erase_region(&self, region_number: usize) -> Result<(), ErrorCode> {
if self.async_erase_region.get() != region_number {
// We aren't ready yet, launch the async operation
self.async_erase_region.set(region_number);
return Err(ErrorCode::EraseNotReady(region_number));
}
Ok(())
}
}
// Create the TicKV instance and loop until everything is done
// NOTE in an real implementation you will want to wait on
// callbacks/interrupts and make this async.
let mut read_buf: [u8; 1024] = [0; 1024];
let mut hash_function = DefaultHasher::new();
MAIN_KEY.hash(&mut hash_function);
let tickv = AsyncTicKV::<FlashCtrl, 1024>::new(FlashCtrl::new(),
&mut read_buf, 0x1000);
let mut ret = tickv.initialise(hash_function.finish());
while ret.is_err() {
// There is no actual delay here, in a real implementation wait on some event
ret = tickv.continue_operation().0;
match ret {
Err(ErrorCode::ReadNotReady(reg)) => {
tickv.set_read_buffer(&tickv.tickv.controller.buf.borrow()[reg]);
}
Ok(_) => break,
Err(ErrorCode::WriteNotReady(reg)) => break,
Err(ErrorCode::EraseNotReady(reg)) => {}
_ => unreachable!(),
}
}
// Then when calling the TicKV function check for the error. For example
// when appending a key:
// Add a key
static mut VALUE: [u8; 32] = [0x23; 32];
let ret = unsafe { tickv.append_key(get_hashed_key(b"ONE"), &mut VALUE, 32) };
match ret {
Err((_buf, ErrorCode::ReadNotReady(reg))) => {
// There is no actual delay in the test, just continue now
tickv.set_read_buffer(&tickv.tickv.controller.buf.borrow()[reg]);
tickv
.continue_operation().0
.unwrap();
}
Ok(_) => {}
_ => unreachable!(),
}
This will call into the FlashController
again where the
FlashController
implementation must return the data that is requested.
If the data isn’t ready (multiple reads might occur) then the NotReady
error types can still be used.
Structs§
- The struct storing all of the TicKV information for the async implementation.