Closed Sympatron closed 3 years ago
I don't know much about NAND flashes, but as far as I know they have a lot in common wih NOR flashes from a software perspective.
If I didn't miss anything the current NorFlash
in #6 could be changed to FlashStorage
and we could have NorFlash
and NandFlash
marker or inherited traits. Please correct me if I am wrong.
Maybe some kind of hierarchy of traits would be useful. I.e. "block erasable" like flash, and "random access"/"transparent" like EEPROM or some kind of FTL or even RAM.
@Sympatron Sorry i have been neglecting this a bit since i pushed for getting a dedicated repo for embedded-storage. I have to say, when i did that, i did not have in mind that i would be the sole maintainer of such. That stuff aside, i am now at a point where i need this, and would like to take up the discussion again, to see if we can bring some life into this repo.
I would LOVE to see something like this, that takes the pros and cons of each storage type into consideration, while still allowing for all the convenience abstractions ontop to be optional.
As i see it the main groups to target for this repo would be:
With specifically trying to get a broad adoption in all the HALs, by having them implement NorFlash for the internal flash, and eeprom for any chips with internal eeprom?
@eldruin I think you have an eeprom abstraction crate? Care to weigh in?
Personally my usecases are focused around NorFlash only, but both internally (STM32L4xx) and externally (QSPI)
I don't know much about NAND Flash, but perhaps we could just leave that part of the design work for when someone needs it? Perhaps with a help wanted issue open?
I have a driver for the 24
series of I2C EEPROM devices: eeprom24x but no abstraction beyond that.
As a trait, the interface for these is pretty simple:
pub trait Eeprom<E> {
fn read_byte(&mut self, address: u32) -> Result<u8, Error<E>>;
fn read_data(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error<E>>;
fn read_current_address(&mut self) -> Result<u8, Error<E>>;
fn write_byte(&mut self, address: u32, data: u8) -> Result<(), Error<E>>;
fn write_page(&mut self, address: u32, data: &[u8]) -> Result<(), Error<E>>; // (data up to a page)
}
Notice that other than taking care of page boundaries, there are almost no restrictions or weird behavior to model here. About NOR/NAND I do not know much.
Would it make sense to structure this crate as a series of technology specific traits:
And then a top level "just works"™ transparent ReadWriteStorage
trait, that is implemented for each technology specific trait
impl<T> ReadWriteStorage for T
where
T: NorFlash
{
// NorFlash specific implementation, doing Read-Modify-Write
// as write operations, allowing users to not have to know/think about erasing/erase granularity etc.
}
That gives driver authors and users the option of having the transparent one that just works, and supports basically any non-volatile storage (with a potential overhead, depending on the underlying technology), but still allows one to restrict to eg NorFlash
for applications/crates that utilize the way a NorFlash works, usually with a better overall performance as a result, if done correctly.
For Eeproms, i think the ReadWriteStorage
would just be shim layer? I don't think there is any special considerations around those, are there? Like the "cannot program non-erased memory", or "can only erase in strict granularities" of a NorFlash.
EDIT
I think automatically implementing ReadWriteStorage
as above on a generic might require negative trait bounds or possibly specialization? Guess we would have to settle on implementors adding both the tech-specific trait and ReadWriteStorage
for specific types.
@MathiasKoch That sounds like a good idea to me.
@MathiasKoch That is basically what I suggested. I suppose we can provide a JustWorks™ ReadWriteStorage
implementation, but unless we do something fancy and not just the naïve Read-Modify-Write it will have extremely bad performance for NOR and NAND flash as well as quite high RAM consumption. It's not that I am against this idea in general, I just think this has to be noted.
What I had in mind was a more sophisticated abstraction layer based on NorFlash
for example that does this more efficiently with less overhead and taking things like wear levelling into account. Though I imagine these implementations to be in their own crates. LittleFS for example would be a good candidate to build on top of NorFlash
.
About EEPROM: I think the traits for eeprom can indeed be simpler, because they have less restrictions to work around and ReadWriteStorage
can probably be implemented without overhead. I think (not 100% sure) some eeproms don't allow single byte access and work with pages that must be written together, but this might not be worth to model with the traits since it can be abstracted away easily.
That is basically what I suggested
Yeah, it wasn't to take any credit away, but more of a summary ;)
Regarding the rest, that sounds nice! How do we proceed from here?
Currently we have one trait
ReadWrite
that aims to be a general transparent storage with the exception, that erase is only supported for aligned addresses.I think it may be useful to have a trait that is supposed to be completely transparent and solves all challenges of the actual storage medium under the hood. This trait should not require alignment for any operation IMO.
Additionally there should be traits for specific kinds of storage that share common properties. For example NOR flashes always AND while writing and only support block aligned erase. So having a
NorFlash
trait (and others likeNandFlash
,Eeprom
, etc.) which can be used to write generic file systems optimized for specific storage types, would be extremely useful to have. This basically the same conclusion @MathiasKoch and @Dirbaio came to in #1.If I find time today, I'll have a go at
NorFlash
, but I wanted to see what you guys thought.