diff --git a/hal/blocking/src/gpio_port.rs b/hal/blocking/src/gpio_port.rs new file mode 100644 index 0000000..d8dee8b --- /dev/null +++ b/hal/blocking/src/gpio_port.rs @@ -0,0 +1,173 @@ +// Licensed under the Apache-2.0 license + +/// This represents a common set of GPIO operation errors. Implementations are +/// free to define more specific or additional error types. However, by providing +/// a mapping to these common errors, generic code can still react to them. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[non_exhaustive] +pub enum GpioErrorKind { + /// The specified GPIO port does not exist + InvalidPort, + /// The specified pin(s) do not exist on this port + InvalidPin, + /// The requested configuration is not supported + UnsupportedConfiguration, + /// The pins cannot be configured as requested (e.g., reserved pins) + ConfigurationFailed, + /// Cannot change pins currently used by another peripheral + PinInUse, + /// The interrupt requested cannot be configured + InterruptConfigurationFailed, + /// The requested operation is not allowed in the current state + PermissionDenied, + /// Hardware failure during operation + HardwareFailure, + /// Operation timed out + Timeout, + /// The pin is not configured for the requested operation + /// (e.g., reading output value from input pin) + InvalidMode, +} + +/// Trait for GPIO errors +pub trait GpioError: core::fmt::Debug { + /// Convert error to a generic error kind + /// + /// By using this method, errors freely defined by GPIO implementations + /// can be converted to a set of generic errors upon which generic + /// code can act. + fn kind(&self) -> GpioErrorKind; +} + +impl GpioError for core::convert::Infallible { + fn kind(&self) -> GpioErrorKind { + match *self {} + } +} + +/// Edge sensitivity for interrupt configuration +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum EdgeSensitivity { + /// Trigger on rising edge + RisingEdge, + /// Trigger on falling edge + FallingEdge, + /// Trigger on both rising and falling edges + BothEdges, + /// Trigger on high level + HighLevel, + /// Trigger on low level + LowLevel, +} + +/// Operations for interrupt control +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum InterruptOperation { + /// Enable interrupts + Enable, + /// Disable interrupts + Disable, + /// Clear pending interrupts + Clear, + /// Check if interrupts are pending + IsPending, +} + +/// Trait for types that define an error type for GPIO operations +pub trait GpioErrorType { + /// Error type for GPIO operations + type Error: GpioError; +} + +/// Type used to represent pin masks +pub trait PinMask: Copy + core::fmt::Debug { + /// Create an empty mask (no pins selected) + fn empty() -> Self; + + /// Create a mask with all pins selected + fn all() -> Self; + + /// Check if the mask is empty + fn is_empty(&self) -> bool; + + /// Check if the mask contains the specified pins + fn contains(&self, other: Self) -> bool; + + /// Merge two masks + fn union(&self, other: Self) -> Self; + + /// Get intersection of two masks + fn intersection(&self, other: Self) -> Self; + + /// Toggle pins in mask + fn toggle(&self) -> Self; +} + +/// Base trait for GPIO port operations with integrated error handling +pub trait GpioPort: GpioErrorType { + /// Configuration type for GPIO pins + type Config; + + /// Mask type for pin identification + type Mask: PinMask; + + /// Configure GPIO pins with specified configuration + fn configure(&mut self, pins: Self::Mask, config: Self::Config) -> Result<(), Self::Error>; + + /// Set and clear pins atomically using set and reset masks + fn set_reset( + &mut self, + set_mask: Self::Mask, + reset_mask: Self::Mask, + ) -> Result<(), Self::Error>; + + /// Read current state of input pins + fn read_input(&self) -> Result; + + /// Toggle specified output pins + fn toggle(&mut self, pins: Self::Mask) -> Result<(), Self::Error>; +} + +/// Trait for GPIO interrupt capabilities with integrated error handling +pub trait GpioInterrupt: GpioErrorType { + /// Mask type for pin identification + type Mask: PinMask; + + /// Configure interrupt sensitivity for specified pins + fn irq_configure( + &mut self, + mask: Self::Mask, + sensitivity: EdgeSensitivity, + ) -> Result<(), Self::Error>; + + /// Control interrupt operations (enable, disable, etc.) + fn irq_control( + &mut self, + mask: Self::Mask, + operation: InterruptOperation, + ) -> Result; + + /// Register a callback for interrupt handling + fn register_interrupt_handler( + &mut self, + mask: Self::Mask, + handler: F, + ) -> Result<(), Self::Error> + where + F: FnMut(Self::Mask) + Send + 'static; +} + +/// Trait for splitting a GPIO port into individual pins +pub trait SplitPort: GpioPort + Sized { + /// Container type returned when splitting the port + type PortPins; + + /// Split the port into a container of pins + fn split(self) -> Self::PortPins; +} + +/// Combined trait for full GPIO functionality +pub trait GpioController: GpioPort + GpioInterrupt {} + +/// Automatically implement GpioController for any type implementing both required traits +impl GpioController for T {} diff --git a/hal/blocking/src/lib.rs b/hal/blocking/src/lib.rs index fe1ce27..721a89f 100644 --- a/hal/blocking/src/lib.rs +++ b/hal/blocking/src/lib.rs @@ -1,14 +1,24 @@ // Licensed under the Apache-2.0 license -//! Blocking/synchronous HAL traits for OpenPRoT +//! Blocking (synchronous) HAL traits for OpenPRoT //! -//! This crate re-exports embedded-hal 1.0 traits for blocking hardware abstraction -//! layer operations such as SPI, I2C, GPIO, and other hardware interfaces. +//! This crate provides a blocking (synchronous) hardware abstraction layer (HAL) for +//! OpenPRoT-compliant platforms. It includes platform-specific modules such as reset +//! control and re-exports traits from `embedded-hal` 1.0 for common hardware interfaces +//! like SPI, I2C, GPIO, and delays. +//! +//! The goal is to offer a unified, safe, and no_std-compatible interface for embedded +//! development across multiple chip families. #![no_std] #![forbid(unsafe_code)] #![deny(missing_docs)] +/// Gpio port module +pub mod gpio_port; +/// Reset and clocking traits for OpenPRoT HAL +pub mod system_control; + // Re-export embedded-hal 1.0 traits pub use embedded_hal::delay::DelayNs; pub use embedded_hal::digital::{InputPin, OutputPin, StatefulOutputPin}; diff --git a/hal/blocking/src/system_control.rs b/hal/blocking/src/system_control.rs new file mode 100644 index 0000000..b1cde43 --- /dev/null +++ b/hal/blocking/src/system_control.rs @@ -0,0 +1,262 @@ +// Licensed under the Apache-2.0 license + +//! # Design Considerations +//! +//! This module defines traits and error types for abstracting system-level +//! clock and reset control operations. The design emphasizes **flexibility**, +//! **composability**, and **extensibility** to support a wide range of hardware +//! platforms and use cases. +//! +//! ## Flexibility +//! +//! - **Platform Independence**: The traits abstract hardware-specific operations, +//! allowing implementations to target different platforms without modifying +//! consumer code. +//! - **Custom Identifiers and Configurations**: Associated types like `ClockId`, +//! `ClockConfig`, and `ResetId` allow implementers to define identifiers and +//! configurations that match their hardware model. +//! - **Error Abstraction**: The `Error` trait and `ErrorKind` enum decouple +//! error handling from specific implementations, enabling generic code to +//! respond to common failure modes while supporting detailed diagnostics. +//! +//! ## Composability +//! +//! - **Unified Error Handling**: The `ErrorType` supertrait provides a consistent +//! interface for accessing error types, enabling integration with other traits +//! and systems. +//! - **Non-Exhaustive ErrorKind**: The `#[non_exhaustive]` attribute on `ErrorKind` +//! allows future expansion without breaking existing code, supporting long-term +//! composability. +//! +//! ## Extensibility +//! +//! - **Custom Error Types**: Implementers can define their own error types and +//! map them to `ErrorKind`, enabling rich, context-specific error reporting +//! while maintaining compatibility with generic consumers. +//! - **Vendor-Specific Configuration**: The `ClockConfig` type supports complex +//! configuration structures such as PLL settings, dividers, or source selectors. +//! - **Partial Implementations**: While all trait methods are required, implementers +//! can provide no-op or stub implementations for unsupported features, enabling +//! partial functionality where appropriate. +//! +//! ## Summary +//! +//! This design provides a robust foundation for building portable, maintainable, +//! and scalable hardware abstraction layers. By leveraging Rust’s trait system, +//! it enables: +//! +//! - Reuse of generic drivers across platforms. +//! - Simplified testing and mocking. +//! - Clear contracts between hardware abstraction and higher-level logic. + +use core::time::Duration; + +/// Represents common system control operation errors. +/// +/// This enumeration defines a standard set of error conditions that can occur +/// during clock and reset control operations. Implementations are free to define +/// more specific or additional error types. However, by providing a mapping to +/// these common errors, generic code can still react to them appropriately. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[non_exhaustive] +pub enum ErrorKind { + /// The specified clock identifier was not found or is invalid. + ClockNotFound, + /// Attempted to enable a clock that is already enabled. + ClockAlreadyEnabled, + /// Attempted to disable a clock that is already disabled. + ClockAlreadyDisabled, + /// The specified clock frequency is invalid or not supported. + InvalidClockFrequency, + /// Clock configuration operation failed due to invalid parameters or hardware constraints. + ClockConfigurationFailed, + /// The specified reset identifier was not found or is invalid. + InvalidResetId, + /// A hardware-level failure occurred during the operation. + HardwareFailure, + /// The operation was denied due to insufficient permissions or security restrictions. + PermissionDenied, + /// The operation timed out before completion. + Timeout, +} + +/// Trait for system control error types. +/// +/// This trait provides a standard interface for all error types used in +/// system control operations. It requires implementors to provide a mapping +/// to the common `ErrorKind` enumeration, enabling generic error handling +/// while preserving implementation-specific error details. +pub trait Error: core::fmt::Debug { + /// Convert error to a generic error kind. + /// + /// By using this method, errors freely defined by system control implementations + /// can be converted to a set of generic errors upon which generic + /// code can act. + fn kind(&self) -> ErrorKind; +} + +impl Error for core::convert::Infallible { + /// Convert error to a generic error kind. + /// + /// Since `core::convert::Infallible` represents an error that can never occur, + /// this implementation uses pattern matching on the uninhabited type to + /// ensure this method can never actually be called. + fn kind(&self) -> ErrorKind { + match *self {} + } +} + +/// Trait providing access to the associated error type. +/// +/// This trait serves as a foundation for other traits that need to define +/// error handling. By separating error type definition from specific operations, +/// it enables composition and reuse across different trait implementations. +pub trait ErrorType { + /// The error type used by this implementation. + /// + /// This associated type must implement the `Error` trait to ensure + /// it can be converted to generic error kinds for interoperability. + type Error: Error; +} + +/// Trait for clock control operations. +/// Abstracts enabling, disabling, and configuring clocks for peripherals or system components. +pub trait ClockControl: ErrorType { + /// Type for identifying a clock (e.g., peripheral ID, clock name, or register offset). + type ClockId: Clone + PartialEq; + /// Type for configuring a clock. + type ClockConfig: PartialEq; + + /// Enables a clock for the specified clock ID. + /// + /// # Arguments + /// + /// * `clock_id` - A reference to the identifier of the clock to enable. + /// + /// # Returns + /// + /// * `Result<(), Self::Error>` - Ok if the operation is successful, or an error of type `Self::Error`. + fn enable(&mut self, clock_id: &Self::ClockId) -> Result<(), Self::Error>; + + /// Disables a clock for the specified clock ID. + /// + /// # Arguments + /// + /// * `clock_id` - A reference to the identifier of the clock to disable. + /// + /// # Returns + /// + /// * `Result<(), Self::Error>` - Ok if the operation is successful, or an error of type `Self::Error`. + fn disable(&mut self, clock_id: &Self::ClockId) -> Result<(), Self::Error>; + + /// Sets the frequency of a clock (in Hz). + /// + /// # Arguments + /// + /// * `clock_id` - A reference to the identifier of the clock to set the frequency for. + /// * `frequency_hz` - The frequency to set, in Hertz. + /// + /// # Returns + /// + /// * `Result<(), Self::Error>` - Ok if the operation is successful, or an error of type `Self::Error`. + fn set_frequency( + &mut self, + clock_id: &Self::ClockId, + frequency_hz: u64, + ) -> Result<(), Self::Error>; + + /// Gets the current frequency of a clock (in Hz). + /// + /// # Arguments + /// + /// * `clock_id` - A reference to the identifier of the clock to get the frequency for. + /// + /// # Returns + /// + /// * `Result` - Ok with the current frequency in Hertz, or an error of type `Self::Error`. + fn get_frequency(&self, clock_id: &Self::ClockId) -> Result; + + /// Configures clock-specific parameters (e.g., divider, source). + /// Vendor-specific parameters can be passed via `ClockConfig`. + /// + /// # Arguments + /// + /// * `clock_id` - A reference to the identifier of the clock to configure. + /// * `config` - The configuration parameters for the clock. + /// + /// # Returns + /// + /// * `Result<(), Self::Error>` - Ok if the operation is successful, or an error of type `Self::Error`. + fn configure( + &mut self, + clock_id: &Self::ClockId, + config: Self::ClockConfig, + ) -> Result<(), Self::Error>; + + /// Retrieves the current configuration of a clock. + /// + /// # Arguments + /// + /// * `clock_id` - A reference to the identifier of the clock to get the configuration for. + /// + /// # Returns + /// + /// * `Result` - Ok with the current configuration, or an error of type `Self::Error`. + fn get_config(&self, clock_id: &Self::ClockId) -> Result; +} + +/// Trait for reset control operations. +/// Abstracts asserting and deasserting reset signals for peripherals or system components. +pub trait ResetControl: ErrorType { + /// Type for identifying a reset line (e.g., peripheral ID, reset name, or register offset). + type ResetId: Clone + PartialEq; + + /// Asserts the reset signal for the specified reset ID (holds the component in reset). + /// + /// # Arguments + /// + /// * `reset_id` - A reference to the identifier of the reset line to assert. + /// + /// # Returns + /// + /// * `Result<(), Self::Error>` - Ok if the operation is successful, or an error of type `Self::Error`. + fn reset_assert(&mut self, reset_id: &Self::ResetId) -> Result<(), Self::Error>; + + /// Deasserts the reset signal for the specified reset ID (releases the component from reset). + /// + /// # Arguments + /// + /// * `reset_id` - A reference to the identifier of the reset line to deassert. + /// + /// # Returns + /// + /// * `Result<(), Self::Error>` - Ok if the operation is successful, or an error of type `Self::Error`. + fn reset_deassert(&mut self, reset_id: &Self::ResetId) -> Result<(), Self::Error>; + + /// Performs a pulse reset (asserts then deasserts) with a specified duration (in microseconds). + /// + /// # Arguments + /// + /// * `reset_id` - A reference to the identifier of the reset line to pulse. + /// * `duration_us` - The duration of the pulse in microseconds. + /// + /// # Returns + /// + /// * `Result<(), Self::Error>` - Ok if the operation is successful, or an error of type `Self::Error`. + fn reset_pulse( + &mut self, + reset_id: &Self::ResetId, + duration: Duration, + ) -> Result<(), Self::Error>; + + /// Checks if the reset signal is currently asserted for the specified reset ID. + /// + /// # Arguments + /// + /// * `reset_id` - A reference to the identifier of the reset line to check. + /// + /// # Returns + /// + /// * `Result` - Ok with a boolean indicating if the reset is asserted, or an error of type `Self::Error`. + fn reset_is_asserted(&self, reset_id: &Self::ResetId) -> Result; +}