rigless 0.2.0

Virtual hardware-in-the-loop runtime: run unmodified firmware on an emulated MCU with every peripheral, chip model and unit of virtual time in Rust, and a CPU core of its own that translates the firmware's instructions to host code
Documentation
// Copyright (c) 2026 Rigless Labs.
// SPDX-License-Identifier: MIT OR Apache-2.0
//! The trait for a chip the part maps into its address space, like a display or a memory on a bank
//! of an external memory interface.

use std::any::Any;

use crate::device::DeviceContext;
use crate::snapshot::Store;
use crate::soc::Width;
use crate::time::{EventId, VirtualNanos};

/// An integrated circuit in a [`Window`](crate::soc::Window) of the part's addresses.
///
/// Each access from the firmware or a model arrives as an offset into the window and a width, with
/// the window's `context` for the time, events and interrupt lines. It's the whole
/// [`DeviceContext`], where a chip on another bus gets a [`Timeline`](crate::chip::Timeline),
/// because a window is a slot of its own with the lines [`Window::raising`](crate::soc::Window::raising)
/// gives it, so nothing the chip raises or drives is a controller's. The chip decodes the offset
/// itself, for example a data/command select on an address bit. If the part hasn't enabled the
/// window, the access is refused before it gets here. The board attaches the chip with
/// [`Wired`](crate::mcu::Wired) and gets it back with `Wired::chip_mut`. Snapshots save it through
/// its [`Store`] impl.
pub trait Peer: Store + std::fmt::Debug + Send + Any {
    /// Reads `width` at `offset`.
    fn read(&mut self, offset: u64, width: Width, context: &mut DeviceContext<'_>) -> u64;

    /// Writes `value` as `width` at `offset`.
    fn write(&mut self, offset: u64, width: Width, value: u64, context: &mut DeviceContext<'_>);

    /// What a read at `offset` would return at `now`, without changing anything. The debugger uses
    /// it. Returns `None` by default, and should for any read with a side effect, like advancing a
    /// pointer.
    fn peek(&self, offset: u64, width: Width, now: VirtualNanos) -> Option<u64> {
        let _ = (offset, width, now);
        None
    }

    /// Handles an event this chip scheduled through its context.
    fn on_event(&mut self, event: EventId, context: &mut DeviceContext<'_>) {
        let _ = (event, context);
    }

    /// Called at each session stop to check for outside input. Does nothing by default.
    fn poll(&mut self, context: &mut DeviceContext<'_>) {
        let _ = context;
    }

    /// Called when the part resets. A chip whose reset pin is tied to the part's resets itself
    /// here. By default it keeps its state, like a chip with its own reset line.
    fn reset(&mut self) {}
}