| # Flash Service |
| |
| The Flash Service provides a centralized interface for userspace applications to interact with on-chip and external flash memory. This is achieved via an IPC-based client-server architecture. |
| |
| ## Overview |
| |
| Applications interact with flash through the `Flash` trait, typically using the `FlashIpcClient` implementation. All operations are blocking from the perspective of the caller. |
| |
| ### Key Features |
| - **Partition Support**: Access to both primary Data partitions and auxiliary Info partitions. |
| - **Flexible Erase**: Support for multiple erase granularities (e.g., page vs. block) as reported by the hardware. |
| - **Unified Addressing**: A logical `FlashAddress` system that abstracts hardware-specific bank and page layouts. |
| |
| ## Usage |
| |
| To use the flash service, initialize a `FlashIpcClient` with a handle to the flash service: |
| |
| ```rust |
| use hal_flash::{Flash, FlashAddress}; |
| use services_flash_client::FlashIpcClient; |
| use util_ipc::IpcHandle; |
| |
| // 1. Connect to the flash service |
| let mut flash = FlashIpcClient::new(IpcHandle::new(FLASH_SERVICE_HANDLE))?; |
| |
| // 2. Retrieve device geometry |
| let (total_size, page_size, erasable_bitmap) = flash.geometry(); |
| |
| // 3. Erase a block (using the default page size) |
| let addr = FlashAddress::new(0x1000); |
| flash.erase(addr, page_size)?; |
| |
| // 4. Program data |
| flash.program(addr, b"Hello, Flash!")?; |
| |
| // 5. Read data back |
| let mut buf = [0u8; 13]; |
| flash.read(addr, &mut buf)?; |
| ``` |
| |
| ## The `Flash` Trait |
| |
| The primary interface for flash operations: |
| |
| - `geometry() -> (NonZero<usize>, PowerOf2Usize, u32)`: Returns the total capacity, the default/smallest page size, and a bitmap of all supported erase block sizes. |
| - `read(addr, buf)`: Reads data from the specified address. |
| - `erase(addr, size)`: Erases a block of the specified size. The size must be one of the values supported in the `erasable_bitmap`. |
| - `program(addr, data)`: Writes data to the specified address. Flash must be erased before programming. |
| |
| ### Understanding `erasable_bitmap` |
| The `erasable_bitmap` is a `u32` where each set bit `i` indicates that an erase block size of `2^i` bytes is supported. |
| - Bit 11 set (`0x800`) -> 2048-byte erase supported. |
| - Bit 16 set (`0x10000`) -> 64KB erase supported. |
| |
| ## Addressing |
| |
| Flash memory is addressed using the `FlashAddress` type, which wraps a single 32-bit `offset`. |
| |
| On platforms like Earlgrey, the most significant bit (MSB) of this offset is used to distinguish between different partitions: |
| - **DATA partition**: MSB is 0 (offset < 0x80000000). |
| - **INFO partition**: MSB is 1 (offset >= 0x80000000). |
| |
| The `EarlgreyFlashAddress` trait (from `earlgrey_util`) provides helper methods to construct and inspect addresses: |
| - `FlashAddress::data(offset)`: Accesses the main data partition. |
| - `FlashAddress::info(bank, page, offset)`: Accesses specific info pages. |
| |
| ## Implementation Details |
| |
| The service is built on several layers of abstraction: |
| |
| ### IPC Layer |
| - **`FlashIpcServer`**: Wraps a hardware-backed `Flash` implementation and dispatches IPC requests. |
| - **`FlashIpcClient`**: Implements the `Flash` trait by proxying calls to the server. |
| |
| ### Hardware Abstraction |
| - **`FlashDriver` Trait**: Defines the low-level, often asynchronous, interface for hardware drivers. |
| - **`BlockingFlash`**: A wrapper that converts a `FlashDriver` into a synchronous `Flash` implementation using a provided blocking mechanism. |
| |
| ### Component Diagram |
| |
| ```mermaid |
| graph TD |
| Client[Userspace Application] -- "Flash Trait" --> IPC_Client[FlashIpcClient] |
| IPC_Client -- "IPC" --> IPC_Server[FlashIpcServer] |
| IPC_Server -- "Flash Trait" --> BlockingFlash[BlockingFlash] |
| BlockingFlash -- "FlashDriver Trait" --> HardwareDriver[e.g., EmbeddedFlash] |
| HardwareDriver --> HW[Flash Controller] |
| ``` |