Description
Hi all,
I'd like to list a few issues for those trying to write libraries on top of the abstractions provided by this crate. I'll only focus on nor_flash::ReadNorFlash
and nor_flash::NorFlash
since those are the only traits I need and thus have looked at. I'd be happy to propose an alternative to those traits with a demonstration as to how to use them (i.e. write a generic library on top of it) as well as how to implement them (i.e. how a chip can provide the trait). But before I start this effort, I would like to know if there are any strong reasons as to why those choices have been made.
Some term definition to be sure we understand ourselves:
- I'll call user the component using the trait (i.e. calling the trait functions on an object implementing the trait). I'll consider those users to be abstract (i.e. they are generic over the object and will usually take something like
<T: NorFlash>
as argument somewhere). - I'll call implementation the component implementing the trait for a given object (i.e. this is most probably some HAL or something).
Why read(&mut self)
instead of read(&self)
?
I couldn't find anything about it in the documentation and code. Because of this design, the user can't share the object for read-only operations which is quite counter-intuitive. One would need interior mutability to work around it. But then why shouldn't the implementation do this instead of the user?
Note that this is related to direct reads as having read(&mut self)
prevents reading until the slice of the previous read is dropped.
Do we need READ_SIZE
?
There's already some discussion in #19. I would argue the same way as for read(&mut self)
and say that the implementation should take care of that, not the user. Some helper functions could be provided by this crate to help implementations translate from a coarse read to a fine read. I can write this helper as part of the demonstration.
Do we need write()
to be WRITE_SIZE
-aligned?
(Note that we still need to expose WRITE_SIZE
.)
Similar to the point above, the implementation could take care of this. And it's a user error to write more than WRITE_COUNT
times to a WRITE_SIZE
-aligned region (see taxonomy below), because the implementation can't possibly track/enforce this for all types of flash without shadowing the whole flash.
Flash taxonomy
This is not a usability issue per se, but I believe this to be critical to design those traits. I'm thinking about a markdown in this crate that could look like:
Technology:
- Nor(
ERASE_SIZE
): The flash can be written by flipping bits from 1 to 0. To flip back from 0 to 1, the flash must be erased. The user can choose which bits are flipped from 1 to 0 during write, while erasing flips all bits back to 1 in anERASE_SIZE
-aligned region. - Add other technologies here.
Read:
- Direct: The flash is memory-mapped and can be read directly from there without any additional mechanism.
- Indirect: The flash is not mapped to memory or needs specific mechanism to read safely.
NorWrite (Nor only):
- Row(
MIN_WRITE_SIZE
,WRITE_SIZE
,WRITE_COUNT
): The user can only writeWRITE_COUNT
times to the sameWRITE_SIZE
-aligned region.MIN_WRITE_SIZE
dividesWRITE_SIZE
andWRITE_SIZE
dividesERASE_SIZE
. The user only needs to set to 0 the bits they want to set to 0. Other bits could be set to 1 and they will preserve their existing value. The flash can't write less thanMIN_WRITE_SIZE
. - Add other ways to write to a Nor flash here.
Add other specifications here.
Examples (all units are in bytes):
nRF52840
: Technology::Nor(4096), Read::Direct, NorWrite::Row(4, 4, 2)nRF52832
: Technology::Nor(4096), Read::Direct, NorWrite::Row(4, 512, 181)STM32L432KC
: Technology::Nor(2048), Read::Direct, NorWrite::Row(8, 8, 1)