-
Notifications
You must be signed in to change notification settings - Fork 24
services: flash client/server #365
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
cfrantz
wants to merge
3
commits into
OpenPRoT:main
Choose a base branch
from
cfrantz:flash-service
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| # Licensed under the Apache-2.0 license | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") | ||
|
|
||
| rust_library( | ||
| name = "driver", | ||
| srcs = [ | ||
| "driver.rs", | ||
| ], | ||
| crate_name = "hal_flash_driver", | ||
| edition = "2024", | ||
| visibility = ["//visibility:public"], | ||
| deps = [ | ||
| "//util/types", | ||
| "@rust_crates//:zerocopy", | ||
| ], | ||
| ) | ||
|
|
||
| rust_library( | ||
| name = "flash", | ||
| srcs = [ | ||
| "flash.rs", | ||
| ], | ||
| crate_name = "hal_flash", | ||
| edition = "2024", | ||
| visibility = ["//visibility:public"], | ||
| deps = [ | ||
| ":driver", | ||
| "//util/io", | ||
| "//util/types", | ||
| ], | ||
| ) | ||
|
|
||
| rust_test( | ||
| name = "flash_test", | ||
| crate = ":flash", | ||
| rustc_flags = [ | ||
| "-C", | ||
| "debug-assertions", | ||
| ], | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| # Blocking Flash HAL | ||
|
|
||
| This HAL provides abstractions for interacting with flash memory. It defines traits for low-level drivers and high-level synchronous interfaces, along with a helper implementation to bridge them. | ||
|
|
||
| ## Key Abstractions | ||
|
|
||
| ### `FlashAddress` | ||
|
|
||
| A transparent wrapper around a 32-bit offset (`u32`) representing a location in flash memory. It supports basic arithmetic operations (`Add`, `AddAssign`, `BitAnd`, `BitAndAssign`) to facilitate address calculations. | ||
|
|
||
| Platform-specific extension traits (like `EarlgreyFlashAddress` in `earlgrey_util`) may use the offset bits to encode additional information (e.g., distinguishing between DATA and INFO partitions using the MSB). | ||
|
|
||
| ### `FlashDriver` Trait | ||
|
|
||
| Defines the low-level interface for flash hardware. It is designed to support both synchronous and asynchronous hardware controllers using a start-poll-complete execution model for erase and program operations: | ||
|
|
||
| 1. **Start**: Initiated via `start_erase` or `start_program`. | ||
| 2. **Poll**: Check completion status via `is_busy`, or wait for a hardware interrupt. | ||
| 3. **Complete**: Finalize the operation and retrieve any execution errors via `complete_op`. | ||
|
|
||
| Read operations (`read`) are synchronous for simplicity. | ||
|
|
||
| Drivers also define hardware-specific constraints as constants: | ||
| * `ERASABLE_SIZES_BITMAP`: A bitmap where bit `i` is set if erasing blocks of size `2^i` is supported. | ||
| * `PROGRAM_WINDOW_SIZE`: The maximum size of a single write, and the boundary alignment constraint for writes. | ||
| * `MAX_READ_SIZE`: The maximum size of a single read. | ||
| * `READ_ALIGNMENT` / `PROGRAM_ALIGNMENT`: Address and size alignment requirements. | ||
|
|
||
| ### `Flash` Trait | ||
|
|
||
| Provides a simplified, synchronous, blocking interface suitable for application use. It abstracts away the low-level start-poll-complete flow and hardware constraints (alignment, windows). | ||
|
|
||
| ### `BlockingFlash` | ||
|
|
||
| A concrete implementation of the `Flash` trait that wraps a `FlashDriver` and a `Blocking` mechanism. It implements the blocking behavior and handles driver constraints automatically: | ||
|
|
||
| ```mermaid | ||
| graph TD | ||
| App[Application] -- "Flash::program()" --> BF[BlockingFlash] | ||
| BF -- "1. start_program()" --> Driver[FlashDriver] | ||
| BF -- "2. wait_for_notification()" --> Blocking[Blocking impl] | ||
| Blocking -- "wakes up (IRQ/Poll)" --> BF | ||
| BF -- "3. complete_op()" --> Driver | ||
| ``` | ||
|
|
||
| #### Read Alignment Handling | ||
|
|
||
| If a read request is not aligned to `TDriver::READ_ALIGNMENT`, `BlockingFlash::read` will: | ||
| 1. Read the aligned block containing the unaligned start address into a temporary buffer. | ||
| 2. Copy the requested bytes from the temporary buffer. | ||
| 3. Read the remaining data in aligned chunks directly into the destination buffer. | ||
|
|
||
| #### Program Window Handling | ||
|
|
||
| Hardware flash controllers often cannot program data that crosses a write window boundary (typically 64 bytes). `BlockingFlash::program` automatically detects these boundaries and splits a large or unaligned write into multiple smaller writes that fit within the program windows. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| // Licensed under the Apache-2.0 license | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| //! Low-level flash driver interface. | ||
|
|
||
| #![no_std] | ||
|
|
||
| use core::num::NonZero; | ||
|
|
||
| use util_types::PowerOf2Usize; | ||
| use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; | ||
|
|
||
| /// Low-level flash driver interface. | ||
| /// | ||
| /// This trait defines the interface for interacting with flash hardware at a low level. | ||
| /// It supports an asynchronous-style execution model using a start-poll-complete pattern: | ||
| /// 1. Start the operation (`start_erase`, `start_program`). | ||
| /// 2. Poll status (`is_busy`) or wait for interrupt. | ||
| /// 3. Finalize and check errors (`complete_op`). | ||
| /// | ||
| /// Read operations (`read`) are assumed to be synchronous and blocking for simplicity. | ||
| pub trait FlashDriver { | ||
| /// The error type returned by driver operations. | ||
| type Error; | ||
|
|
||
| /// The default page size in bytes. | ||
| const PAGE_SIZE: usize; | ||
|
|
||
| /// The maximum size of a single program operation (write window). | ||
| /// Program operations cannot span across boundaries aligned to this size. | ||
| const PROGRAM_WINDOW_SIZE: usize; | ||
|
|
||
| /// The maximum size of a single read operation. | ||
| const MAX_READ_SIZE: usize; | ||
|
|
||
| /// The alignment required for read operations (addresses and lengths). | ||
| const READ_ALIGNMENT: usize; | ||
|
|
||
| /// The alignment required for program operations (addresses and lengths). | ||
| const PROGRAM_ALIGNMENT: usize; | ||
|
|
||
| /// Returns the total size of the flash in bytes. | ||
| fn size(&self) -> NonZero<usize>; | ||
|
|
||
| /// Returns a bitmap of supported erase block sizes. | ||
| /// | ||
| /// Each bit `i` represents a supported erase block size of `2^i` bytes. | ||
| /// For example, if bit 11 is set, then 2048-byte erases are supported. | ||
| fn erasable_sizes_bitmap(&mut self) -> Result<u32, Self::Error>; | ||
|
|
||
| /// Reads data from flash synchronously. | ||
| /// | ||
| /// # Arguments | ||
| /// * `start_addr`: The address to start reading from. Must be aligned to `READ_ALIGNMENT`. | ||
| /// * `buf`: The buffer to read data into. Must have size <= `MAX_READ_SIZE` and be aligned to `READ_ALIGNMENT`. | ||
| fn read(&mut self, start_addr: FlashAddress, buf: &mut [u8]) -> Result<(), Self::Error>; | ||
|
|
||
| /// Starts an erase operation asynchronously. | ||
| /// | ||
| /// The operation is not guaranteed to be complete until `complete_op` is called | ||
| /// and returns `Ok`. | ||
| /// | ||
| /// # Arguments | ||
| /// * `start_addr`: The start address of the block to erase. Must be aligned to the block size. | ||
| /// * `size`: The size of the block to erase. Must have its corresponding bit set in `ERASABLE_SIZES_BITMAP`. | ||
| fn start_erase( | ||
| &mut self, | ||
| start_addr: FlashAddress, | ||
| size: PowerOf2Usize, | ||
| ) -> Result<(), Self::Error>; | ||
|
|
||
| /// Starts a program operation asynchronously. | ||
| /// | ||
| /// The operation is not guaranteed to be complete until `complete_op` is called | ||
| /// and returns `Ok`. | ||
| /// | ||
| /// The programmed region must not cross a `PROGRAM_WINDOW_SIZE` boundary. | ||
| /// | ||
| /// # Arguments | ||
| /// * `start_address`: The address to start programming at. Must be aligned to `PROGRAM_ALIGNMENT`. | ||
| /// * `data`: The data to program. Must have size <= `PROGRAM_WINDOW_SIZE` and be aligned to `PROGRAM_ALIGNMENT`. | ||
| fn start_program( | ||
| &mut self, | ||
| start_address: FlashAddress, | ||
| data: &[u8], | ||
| ) -> Result<(), Self::Error>; | ||
|
|
||
| /// Returns whether the driver is currently busy with an operation. | ||
| fn is_busy(&mut self) -> bool; | ||
|
|
||
| /// Completes a pending erase or program operation and returns the result. | ||
| /// | ||
| /// This should be called after `is_busy` returns false to check for errors and | ||
| /// reset the driver state. | ||
| fn complete_op(&mut self) -> Result<(), Self::Error>; | ||
| } | ||
|
|
||
| /// Represents an address in flash memory. | ||
| /// | ||
| /// A flash address consists of an offset within the flash address space. | ||
| #[derive(Default, Clone, Copy, PartialEq, Eq, IntoBytes, Immutable, FromBytes, KnownLayout)] | ||
| #[repr(transparent)] | ||
| pub struct FlashAddress { | ||
| offset: u32, | ||
| } | ||
|
|
||
| impl core::fmt::Display for FlashAddress { | ||
| fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| write!(f, "0x{:08x}", self.offset) | ||
| } | ||
| } | ||
|
|
||
| impl core::fmt::Debug for FlashAddress { | ||
| fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| core::fmt::Display::fmt(self, f) | ||
| } | ||
| } | ||
|
|
||
| impl FlashAddress { | ||
| /// Creates a new `FlashAddress`. | ||
| pub const fn new(offset: u32) -> Self { | ||
| Self { offset } | ||
| } | ||
|
|
||
| /// Returns the offset. | ||
| pub fn offset(&self) -> u32 { | ||
| self.offset | ||
| } | ||
| } | ||
|
|
||
| impl core::ops::Add<usize> for FlashAddress { | ||
| type Output = Self; | ||
| fn add(self, other: usize) -> Self { | ||
| Self { | ||
| offset: self.offset + other as u32, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an uncheked add |
||
| } | ||
| } | ||
| } | ||
|
|
||
| impl core::ops::AddAssign<usize> for FlashAddress { | ||
| fn add_assign(&mut self, other: usize) { | ||
| self.offset += other as u32; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an uncheked add |
||
| } | ||
| } | ||
|
|
||
| impl core::ops::BitAnd<usize> for FlashAddress { | ||
| type Output = Self; | ||
| fn bitand(self, other: usize) -> Self { | ||
| Self { | ||
| offset: self.offset & other as u32, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl core::ops::BitAndAssign<usize> for FlashAddress { | ||
| fn bitand_assign(&mut self, other: usize) { | ||
| self.offset &= other as u32; | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.