psoc 0.1.2

Rust drivers and hardware abstraction layer for Infineon PSOC microcontrollers
Documentation
//! Rust drivers and hardware abstraction layer for Infineon PSOC microcontrollers.
//!
//! The following families are currently supported:
//!
//! - PSOC 6
//! - PSOC Control C3
//!
//! The following drivers are currently supported:
//!
//! - System clock
//! - GPIO
//! - Watchdog
//!
//! # Important note about documentation
//!
//! The documentation available on docs.rs only covers a single chip
//! ([PSC3M5FDS2AFQ1](https://www.infineon.com/part/PSC3M5FDS2AFQ1), a PSOC Control C3 device).
//! Available drivers and APIs vary across devices. It is recommended to build documentation locally
//! for your specific chip by running `cargo doc --open` in your project.
//!
//! # Getting started
//!
//! To build an application from scratch, add `psoc` as a dependency in your `Cargo.toml` and
//! specify your device with a feature flag. You'll also need
//! [`cortex-m-rt`](https://crates.io/crates/cortex-m-rt), and a panic handler such as
//! [`panic-halt`](https://crates.io/crates/panic-halt) or
//! [`panic-probe`](https://crates.io/crates/panic-probe).
//!
//! ```toml
//! [dependencies]
//! psoc = { version = "0.1", features = ["device-psc3m5fds2afq1"] }
//!
//! cortex-m-rt = "0.7.5"
//! panic-halt = "1.0.0"
//! ```
//!
//!
//! Specify flashing, linker, and target options in `.cargo/config.toml`:
//!
//! ```toml
//! [target.'cfg(all(target_arch = "arm", target_os = "none"))']
//! runner = ["probe-rs", "run", "--log-format=oneline", "--speed=16000"]
//!
//! [build]
//! rustflags = [
//!     "-Clink-arg=-Tpsoc.x",
//!     "-Clink-arg=-Tdefmt.x",
//!     "-C", "link-arg=--nmagic"
//! ]
//!
//! # Pick ONE of these for your target
//! # target = "thumbv6m-none-eabi"         # Cortex-M0/M0+
//! # target = "thumbv7m-none-eabi"         # Cortex-M3
//! # target = "thumbv7em-none-eabi"        # Cortex-M4/M7 (no FPU)
//! # target = "thumbv7em-none-eabihf"      # Cortex-M4/M7 (with FPU)
//! # target = "thumbv8m.main-none-eabi"    # Cortex-M33/M55/M85 (no FPU)
//! target = "thumbv8m.main-none-eabihf"    # Cortex-M33/M55/M85 (with FPU)
//! ```
//!
//! Write a main function and initialize the device:
//!
//! ```rust
//! #![no_main]
//! #![no_std]
//!
//! use panic_halt as _;
//!
//! #[cortex_m_rt::entry]
//! fn main() -> ! {
//!     let mut device = psoc::device::Device::take();
//!     // Initialize clocks
//!     device.clock.configure(&Default::default(), None);
//!
//!     // Initialize an LED pin
//!     let led_port = device
//!         .gpio
//!         .8
//!         .p5(psoc::gpio::mode::Strong::<false>, Default::default())
//!         .initialize();
//!     let mut led_pin = led_port.5;
//!     loop {
//!         led_pin.toggle();
//!         psoc::sys::delay_microseconds(500_000);
//!     }
//! }
//! ```
//!
//! Install probe-rs, and flash with `cargo flash --release`.
//!
//! See the [example applications](https://github.com/Infineon/psoc-rs/tree/main/examples) for
//! examples on how to structure applications and BSPS, and recommended configuration for
//! improved compiler optimizations and IDE integration.
//!
//! # Cargo features
//!
//! The following feature flags are provided:
//!
//! - `device-*`: Selects a specific chip to build for. Exactly one chip must be selected.
//! - `core-*`: For multi-core devices, selects a specific core to build for. Exactly one core must
//!   be selected (see below for more information about multi-core support).
//! - `boot-image`: If targetting a multi-core device and selecting a core other than one used to
//!   boot the system, this flag adds a small bootloader to your application which starts up the
//!   selected core and launches your application. Required for running on a non-boot core (such as
//!   the Cortex-M4 on PSOC 6), unless you already have another binary running on the boot core.
//! - `defmt`: Enables support for the [`defmt`](https://crates.io/crates/defmt) formatting library.
//! - `embassy-time`: Implements an [`embassy-time`](https://crates.io/crates/embassy-time) driver
//!   using a multi-counter watchdog timer, enabling the use of the [Embassy](https://embassy.dev)
//!   framework. You must call `psoc::sys::embassy_time::init()` in your application startup to
//!   select a MCWDT instance to use and initialize the driver.
//! - `fault-handler`: Enables a default HardFault handler which prints a register dump using
//!   `defmt`.
//!
//! # Driver Model and Lifecycle
//!
//! ## Typestates
//!
//! All drivers in this crate make use of the [typestate pattern][typestate], in which drivers'
//! instance and configuration option are represented statically using the type system (in
//! accordance with the standard design patterns for embedded Rust). For instance, a C driver would
//! typically use struct fields to represent the pin and port number of a GPIO pin:
//!
//! [typestate]: https://cliffle.com/blog/rust-typestate/
//!
//! ```c
//! typedef enum {
//!     PIN_MODE_OUTPUT,
//!     // ...
//! } pin_mode_t;
//!
//! typedef struct {
//!     uint8_t port;
//!     uint8_t pin;
//! } gpio_pin_t;
//!
//! void gpio_pin_init(gpio_pin_t *pin, uint8_t port, uint8_t pin, pin_mode_t mode);
//!
//! gpio_pin_t led_pin;
//! gpio_pin_init(&led_pin, 4, 2, PIN_MODE_OUTPUT);
//! ```
//!
//! The psoc-rs GPIO driver instead uses generic parameters, with a third parameter to represent
//! the pin mode (e.g. whether it's consfigured as an input or output):
//!
//! ```
//! trait PinMode { /* ... */ }
//! struct Output;
//! impl PinMode for Output {}
//!
//! struct Pin<const PORT: u8, const PIN: u8, Mode: PinMode> {
//!     pub mode: PinMode
//! }
//!
//! let led_pin: Pin<4, 2, Output> = ...;
//! ```
//!
//! In effect, configuration constants are tracked as compile-time variables through the type
//! system rather than runtime variables stored in memory. This has several advantages:
//!
//! - Correctness: the compiler will statically catch many mistakes such as forgetting to
//!   initialize a pin, trying to write to an input pin, or passing an incorrectly-configured pin to
//!   a function or driver
//! - Performance: the Pin struct is a zero-sized type that takes up no memory and incurs no
//!   overhead to pass around the program. And since the pin to be used is known at compile time, the
//!   compiler can directly emit appropriate register accesses to interact with a pin rather than
//!   going through a layer of indirection at runtime.
//!
//! This does have the effect of making type signatures long and unwieldy. It is recommended your
//! board support package define type aliases for drivers as they are used in your design:
//!
//! ```
//! type LedPin = Pin<4, 2, Output>;
//!
//! fn blink_led(pin: &mut LedPin) { /* ... */ }
//! ```
//!
//! ## Type Erasure
//!
//! All drivers in this crate can be also used in the "traditional C" model in which instance and
//! configuration properties are carried around as runtime fields. This is useful when you want to
//! parameterize over driver instances at runtime.
//!
//! To obtain a type-erased instance of a driver, call the `into_erased` or `as_erased` function on
//! a driver instance. Note that some drivers have separate type-erased variants for sync vs. async
//! usage, because async usage of a driver requires instantiating the appropriate interrupt
//! handlers (see below).
//!
//! ## Obtaining and Configuring Drivers
//!
//! To obtain an instance of a driver, use the `device` module. This module provides a struct
//! containing unconfigured driver instances for all hardware available on your chip. An
//! unconfigured driver instance is a zero-sized type representing an uninitialized hardware block
//! and provididing configuration functions allowing you to create and initialize a driver.
//!
//! ## Multi-core devices
//!
//! Multi-core support is still somewhat TBD. Multi-core applications will be consist of a separate
//! binary per core rather than one binary with multiple "threads" (because different cores may have
//! different ISA versions, and may not have cache coherency across the entire memory space).
//!
//! Right now, we don't provide any out-of-the-box way to build applications targetting multiple
//! cores (you can wrangle the linker scripts yourself if you want to do that), but we do have boot
//! images allowing you to easily run an application on a non-boot core (such as the CM4 on PSOC 6).
//! The boot image is a small binary that runs on the boot core, starts the application on the other
//! core, and goes to sleep. The boot images are selected by selecting a non-boot core by enabling
//! the `boot-image` Cargo feature.
//!
//! ## Interrupts
//!
//! You may define interrupt handlers using the `#[interrupt]` macro from the Cortex-M crate:
//!
//! ```
//! use cortex_m::interrupt;
//!
//! #[interrupt]
//! fn IOSS_INTERRUPT_GPIO_0() {
//!     // ...
//! }
//! ```
//!
//! However, note that some optional driver features require specific interrupt handlers. In
//! particular, `async` driver functions typically require a driver-provided interrupt handler to
//! notify the runtime when an event occurs. If your application makes use of a driver feature that
//! requires an interrupt handler, a driver-provided interrupt handler will be automatically
//! generated. Note that such an interrupt handler will conflict with a user-provided handler for
//! the same interrupt, causing a linker error.
//!
//! Driver-provided interrupts are instantiated through generic monomorphization. The presence
//! of a call in your application to a driver function requiring an interrupt causes that function
//! to be monomorphized, resulting in the interrupt handler being emitted.
//!
// Copyright (c) 2026, Infineon Technologies AG or an affiliate of Infineon Technologies AG.
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing permissions and
// limitations under the License.

#![no_std]

pub use psoc_drivers::*;

#[cfg(boot_image)]
mod boot {
    const IMAGE_CONTENTS: &[u8] = include_bytes!(env!("BOOT_IMAGE"));

    #[unsafe(link_section = ".boot_image")]
    #[unsafe(no_mangle)]
    static BOOT_IMAGE: [u8; IMAGE_CONTENTS.len()] = *include_bytes!(env!("BOOT_IMAGE"));
}

/// The device structure, which contains all the peripherals available on the device.
pub mod device {
    include!(concat!(env!("OUT_DIR"), "/device.rs"));

    impl Device {
        /// Obtains the [`Device`] instance. Panics if called more than once.
        #[allow(clippy::new_without_default)]
        pub fn take() -> Self {
            use portable_atomic::{AtomicBool, Ordering};

            static INITIALIZED: AtomicBool = AtomicBool::new(false);
            if INITIALIZED.swap(true, Ordering::Relaxed) {
                panic!("Device::new() called more than once");
            }

            unsafe { Self::steal() }
        }
    }
}
pub use device::Device;