Document the core module
diff --git a/probe-rs/src/core/communication_interface.rs b/probe-rs/src/core/communication_interface.rs index 41a9fc9..32e4df9 100644 --- a/probe-rs/src/core/communication_interface.rs +++ b/probe-rs/src/core/communication_interface.rs
@@ -3,9 +3,12 @@ DebugProbeError, Error, }; +/// A helper trait to get more specific interfaces. pub trait CommunicationInterface { + /// Flush all remaining commands if the target driver implements batching. fn flush(&mut self) -> Result<(), DebugProbeError>; + /// Tries to get the underlying [`ArmCommunicationInterface`]. fn get_arm_communication_interface( &mut self, ) -> Result<&mut ArmCommunicationInterface<Initialized>, Error>;
diff --git a/probe-rs/src/core/mod.rs b/probe-rs/src/core/mod.rs index f9e776c..e3ef0b2 100644 --- a/probe-rs/src/core/mod.rs +++ b/probe-rs/src/core/mod.rs
@@ -13,11 +13,15 @@ use anyhow::{anyhow, Result}; use std::time::Duration; +/// A core register (e.g. Stack Pointer). pub trait CoreRegister: Clone + From<u32> + Into<u32> + Sized + std::fmt::Debug { + /// The register's address. const ADDRESS: u32; + /// The register's name. const NAME: &'static str; } +/// The address of a core register. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct CoreRegisterAddress(pub u16); @@ -32,11 +36,15 @@ CoreRegisterAddress(value) } } + +/// An struct for storing the current state of a core. #[derive(Debug, Clone)] pub struct CoreInformation { + /// The current Program Counter. pub pc: u32, } +/// Describes a register with its properties. #[derive(Debug, Clone, PartialEq)] pub struct RegisterDescription { pub(crate) name: &'static str, @@ -95,54 +103,79 @@ } impl RegisterFile { + /// Returns an iterator over the descriptions of all the registers of this core. pub fn registers(&self) -> impl Iterator<Item = &RegisterDescription> { self.platform_registers.iter() } + /// The frame pointer. pub fn frame_pointer(&self) -> &RegisterDescription { self.frame_pointer } + /// The program counter. pub fn program_counter(&self) -> &RegisterDescription { self.program_counter } + /// The stack pointer. pub fn stack_pointer(&self) -> &RegisterDescription { self.stack_pointer } + /// The link register. pub fn return_address(&self) -> &RegisterDescription { self.return_address } + /// Returns the nth argument register. + /// + /// # Panics + /// + /// Panics if the register at given index does not exist. pub fn argument_register(&self, index: usize) -> &RegisterDescription { &self.argument_registers[index] } + /// Returns the nth argument register if it is exists, `None` otherwise. pub fn get_argument_register(&self, index: usize) -> Option<&RegisterDescription> { self.argument_registers.get(index) } + /// Returns the nth result register. + /// + /// # Panics + /// + /// Panics if the register at given index does not exist. pub fn result_register(&self, index: usize) -> &RegisterDescription { &self.result_registers[index] } + /// Returns the nth result register if it is exists, `None` otherwise. pub fn get_result_register(&self, index: usize) -> Option<&RegisterDescription> { self.result_registers.get(index) } + /// Returns the nth platform register. + /// + /// # Panics + /// + /// Panics if the register at given index does not exist. pub fn platform_register(&self, index: usize) -> &RegisterDescription { &self.platform_registers[index] } + /// Returns the nth platform register if it is exists, `None` otherwise. pub fn get_platform_register(&self, index: usize) -> Option<&RegisterDescription> { self.platform_registers.get(index) } + /// The main stack pointer. pub fn msp(&self) -> Option<&RegisterDescription> { self.msp } + /// The process stack pointer. pub fn psp(&self) -> Option<&RegisterDescription> { self.psp } @@ -154,10 +187,10 @@ // Bits[15:8] BASEPRI. // Bits[7:0] PRIMASK. // In each field, the valid bits are packed with leading zeros. For example, - // FAULTMASK is always a single bit, DCRDR[16], and DCRDR[23:17] is 0b0000000. - pub fn extra(&self) -> Option<&RegisterDescription> { - self.extra - } + // // FAULTMASK is always a single bit, DCRDR[16], and DCRDR[23:17] is 0b0000000. + // pub fn extra(&self) -> Option<&RegisterDescription> { + // self.extra + // } // TODO: support for floating point registers // 0b0100001 Floating-point Status and Control Register, FPSCR. @@ -168,6 +201,7 @@ // bits[6:5] are Reserved, SBZ. } +/// A generic interface to control a MCU core. pub trait CoreInterface: MemoryInterface { /// Wait until the core is halted. If the core does not halt on its own, /// a [`DebugProbeError::Timeout`](crate::DebugProbeError::Timeout) error will be returned. @@ -177,12 +211,14 @@ /// a [`DebugProbeError::Timeout`](crate::DebugProbeError::Timeout) error will be returned. fn core_halted(&mut self) -> Result<bool, error::Error>; + /// Returns the current status of the core. fn status(&mut self) -> Result<CoreStatus, error::Error>; /// Try to halt the core. This function ensures the core is actually halted, and /// returns a [`DebugProbeError::Timeout`](crate::DebugProbeError::Timeout) otherwise. fn halt(&mut self, timeout: Duration) -> Result<CoreInformation, error::Error>; + /// Continue to execute instructions. fn run(&mut self) -> Result<(), error::Error>; /// Reset the core, and then continue to execute instructions. If the core @@ -200,25 +236,33 @@ /// Steps one instruction and then enters halted state again. fn step(&mut self) -> Result<CoreInformation, error::Error>; + /// Read the value of a core register. fn read_core_reg(&mut self, address: CoreRegisterAddress) -> Result<u32, error::Error>; + /// Write the value of a core register. fn write_core_reg(&mut self, address: CoreRegisterAddress, value: u32) -> Result<()>; - fn get_available_breakpoint_units(&mut self) -> Result<u32, error::Error>; + /// Returns all the available breakpoint units of the core. + fn available_breakpoint_units(&mut self) -> Result<u32, error::Error>; /// Read the hardware breakpoints from FpComp registers, and adds them to the Result Vector. /// A value of None in any position of the Vector indicates that the position is unset/available. /// We intentionally return all breakpoints, irrespective of whether they are enabled or not. - fn get_hw_breakpoints(&mut self) -> Result<Vec<Option<u32>>, error::Error>; + fn hw_breakpoints(&mut self) -> Result<Vec<Option<u32>>, error::Error>; + /// Enables breakpoints on this core. If a breakpoint is set, it will halt as soon as it is hit. fn enable_breakpoints(&mut self, state: bool) -> Result<(), error::Error>; - fn set_hw_breakpoint(&mut self, bp_unit_index: usize, addr: u32) -> Result<(), error::Error>; + /// Sets a breakpoint at `addr`. It does so by using unit `bp_unit_index`. + fn set_hw_breakpoint(&mut self, unit_index: usize, addr: u32) -> Result<(), error::Error>; + /// Clears the breakpoint configured in unit `unit_index`. fn clear_hw_breakpoint(&mut self, unit_index: usize) -> Result<(), error::Error>; + /// Returns a list of all the registers of this core. fn registers(&self) -> &'static RegisterFile; + /// Returns `true` if hwardware breakpoints are enabled, `false` otherwise. fn hw_breakpoints_enabled(&self) -> bool; /// Get the `Architecture` of the Core. @@ -263,27 +307,37 @@ } } +/// A generic core state with caches the generic parts of the core state. #[derive(Debug)] pub struct CoreState { id: usize, } impl CoreState { + /// Creates a new core state from the core ID. pub fn new(id: usize) -> Self { Self { id } } + /// Returns the core ID. + pub fn id(&self) -> usize { self.id } } +/// The architecture specific core state. #[derive(Debug)] pub enum SpecificCoreState { + /// The state of an ARMv6-M core. Armv6m(State), + /// The state of an ARMv7-M core. Armv7m(State), + /// The state of an ARMv7-EM core. Armv7em(State), + /// The state of an ARMv8-M core. Armv8m(State), + /// The state of an RISC-V core. Riscv, } @@ -344,7 +398,7 @@ }) } - pub fn attach_riscv<'probe>( + pub(crate) fn attach_riscv<'probe>( &self, state: &'probe mut CoreState, interface: &'probe mut RiscvCommunicationInterface, @@ -362,12 +416,19 @@ } } +/// Generic core handle representing a physical core on an MCU. +/// +/// This should be considere as a temporary view of the core which locks the debug probe driver to as single consumer by borrowing it. +/// +/// As soon as you did your atomic task (e.g. halt the core, read the core state and all other debug relevant info) you should drop this object, +/// to allow potential other shareholders of the session struct to grab a core handle too. pub struct Core<'probe> { inner: Box<dyn CoreInterface + 'probe>, state: &'probe mut CoreState, } impl<'probe> Core<'probe> { + /// Create a new [`Core`]. pub fn new(core: impl CoreInterface + 'probe, state: &'probe mut CoreState) -> Core<'probe> { Self { inner: Box::new(core), @@ -375,10 +436,12 @@ } } + /// Creates a new [`CoreState`] pub fn create_state(id: usize) -> CoreState { CoreState::new(id) } + /// Returns the ID of this core. pub fn id(&self) -> usize { self.state.id } @@ -401,6 +464,7 @@ self.inner.halt(timeout) } + /// Continue to execute instructions. pub fn run(&mut self) -> Result<(), error::Error> { self.inner.run() } @@ -426,10 +490,12 @@ self.inner.step() } + /// Returns the current status of the core. pub fn status(&mut self) -> Result<CoreStatus, error::Error> { self.inner.status() } + /// Read the value of a core register. pub fn read_core_reg( &mut self, address: impl Into<CoreRegisterAddress>, @@ -437,6 +503,7 @@ self.inner.read_core_reg(address.into()) } + /// Write the value of a core register. pub fn write_core_reg( &mut self, address: CoreRegisterAddress, @@ -445,14 +512,17 @@ Ok(self.inner.write_core_reg(address, value)?) } - pub fn get_available_breakpoint_units(&mut self) -> Result<u32, error::Error> { - self.inner.get_available_breakpoint_units() + /// Returns all the available breakpoint units of the core. + pub fn available_breakpoint_units(&mut self) -> Result<u32, error::Error> { + self.inner.available_breakpoint_units() } + /// Enables breakpoints on this core. If a breakpoint is set, it will halt as soon as it is hit. fn enable_breakpoints(&mut self, state: bool) -> Result<(), error::Error> { self.inner.enable_breakpoints(state) } + /// Returns a list of all the registers of this core. pub fn registers(&self) -> &'static RegisterFile { self.inner.registers() } @@ -460,7 +530,7 @@ /// Find the index of the next available HW breakpoint comparator. fn find_free_breakpoint_comparator_index(&mut self) -> Result<usize, error::Error> { let mut next_available_hw_breakpoint = 0; - for breakpoint in self.inner.get_hw_breakpoints()? { + for breakpoint in self.inner.hw_breakpoints()? { if breakpoint.is_none() { return Ok(next_available_hw_breakpoint); } else { @@ -474,8 +544,9 @@ /// Set a hardware breakpoint /// - /// This function will try to set a hardware breakpoint. The amount - /// of hardware breakpoints which are supported is chip specific, + /// This function will try to set a hardware breakpoint att `address`. + /// + /// The amount of hardware breakpoints which are supported is chip specific, /// and can be queried using the `get_available_breakpoint_units` function. pub fn set_hw_breakpoint(&mut self, address: u32) -> Result<(), error::Error> { if !self.inner.hw_breakpoints_enabled() { @@ -485,7 +556,7 @@ // If there is a breakpoint set already, return its bp_unit_index, else find the next free index. let breakpoint_comparator_index = match self .inner - .get_hw_breakpoints()? + .hw_breakpoints()? .iter() .position(|&bp| bp == Some(address)) { @@ -505,10 +576,13 @@ Ok(()) } + /// Set a hardware breakpoint + /// + /// This function will try to clear a hardware breakpoint at `address` if there exists a breakpoint at that address. pub fn clear_hw_breakpoint(&mut self, address: u32) -> Result<(), error::Error> { let bp_position = self .inner - .get_hw_breakpoints()? + .hw_breakpoints()? .iter() .position(|bp| bp.is_some() && bp.unwrap() == address); @@ -536,57 +610,52 @@ /// regardless if they are set by probe-rs, AND regardless if they are enabled or not. /// Also used as a helper function in [`Session::drop`](crate::session::Session). pub fn clear_all_hw_breakpoints(&mut self) -> Result<(), error::Error> { - for breakpoint in (self.inner.get_hw_breakpoints()?).into_iter().flatten() { + for breakpoint in (self.inner.hw_breakpoints()?).into_iter().flatten() { self.clear_hw_breakpoint(breakpoint)? } Ok(()) } + /// Returns the architecture of the core. pub fn architecture(&self) -> Architecture { self.inner.architecture() } } -pub struct CoreList<'probe>(&'probe [CoreType]); - -impl<'probe> CoreList<'probe> { - pub fn new(cores: &'probe [CoreType]) -> Self { - Self(cores) - } -} - -impl<'probe> std::ops::Deref for CoreList<'probe> { - type Target = [CoreType]; - fn deref(&self) -> &Self::Target { - self.0 - } -} - +/// The id of a breakpoint. #[derive(Debug, Copy, Clone, PartialEq)] pub struct BreakpointId(usize); impl BreakpointId { + /// Creates a new breakpoint ID from an `usize`. pub fn new(id: usize) -> Self { BreakpointId(id) } } +/// The status of the core. #[derive(Debug, PartialEq, Copy, Clone)] pub enum CoreStatus { + /// The core is currently running. Running, + /// The core is currently halted. This also specifies the reason as a payload. Halted(HaltReason), /// This is a Cortex-M specific status, and will not be set or handled by RISCV code. LockedUp, + /// The core is currently sleeping. Sleeping, + /// The core state is currently unknown. This is always the case when the core is first created. Unknown, } impl CoreStatus { + /// Returns `true` if the core is currently halted. pub fn is_halted(&self) -> bool { matches!(self, CoreStatus::Halted(_)) } } +/// The reason why a core was halted. #[derive(Debug, PartialEq, Copy, Clone)] pub enum HaltReason { /// Multiple reasons for a halt.