probe_rs/architecture/xtensa/
mod.rsuse std::{sync::Arc, time::Duration};
use probe_rs_target::{Architecture, CoreType, InstructionSet};
use crate::{
architecture::xtensa::{
arch::{
instruction::{Instruction, InstructionEncoding},
Register, SpecialRegister,
},
communication_interface::{DebugCause, IBreakEn, XtensaCommunicationInterface},
registers::{FP, PC, RA, SP, XTENSA_CORE_REGSISTERS},
sequences::XtensaDebugSequence,
},
core::{
registers::{CoreRegisters, RegisterId, RegisterValue},
BreakpointCause,
},
memory::CoreMemoryInterface,
semihosting::decode_semihosting_syscall,
semihosting::SemihostingCommand,
CoreInformation, CoreInterface, CoreRegister, CoreStatus, Error, HaltReason, MemoryInterface,
};
pub(crate) mod arch;
pub(crate) mod xdm;
pub mod communication_interface;
pub(crate) mod registers;
pub(crate) mod sequences;
#[derive(Debug)]
pub struct XtensaCoreState {
enabled: bool,
breakpoints_enabled: bool,
breakpoint_set: [bool; 2],
pc_written: bool,
semihosting_command: Option<SemihostingCommand>,
}
impl XtensaCoreState {
pub(crate) fn new() -> Self {
Self {
enabled: false,
breakpoints_enabled: false,
breakpoint_set: [false; 2],
pc_written: false,
semihosting_command: None,
}
}
fn breakpoint_mask(&self) -> u32 {
self.breakpoint_set
.iter()
.enumerate()
.fold(0, |acc, (i, &set)| if set { acc | (1 << i) } else { acc })
}
}
pub struct Xtensa<'probe> {
interface: XtensaCommunicationInterface<'probe>,
state: &'probe mut XtensaCoreState,
sequence: Arc<dyn XtensaDebugSequence>,
}
impl<'probe> Xtensa<'probe> {
const IBREAKA_REGS: [SpecialRegister; 2] =
[SpecialRegister::IBreakA0, SpecialRegister::IBreakA1];
pub fn new(
interface: XtensaCommunicationInterface<'probe>,
state: &'probe mut XtensaCoreState,
sequence: Arc<dyn XtensaDebugSequence>,
) -> Result<Self, Error> {
let mut this = Self {
interface,
state,
sequence,
};
this.on_attach()?;
Ok(this)
}
fn on_attach(&mut self) -> Result<(), Error> {
let core_reset;
if self.state.enabled {
core_reset = self.interface.xdm.core_was_reset()?;
let debug_reset = self.interface.xdm.debug_was_reset()?;
if core_reset {
tracing::debug!("Core was reset");
*self.state = XtensaCoreState::new();
}
if debug_reset {
tracing::debug!("Debug was reset");
self.state.enabled = false;
}
} else {
core_reset = true;
}
if !self.state.enabled {
self.interface.enter_debug_mode()?;
self.state.enabled = true;
if core_reset {
let was_running = self
.interface
.halt_with_previous(Duration::from_millis(500))?;
self.sequence.on_connect(&mut self.interface)?;
if was_running {
self.run()?;
}
}
}
Ok(())
}
fn core_info(&mut self) -> Result<CoreInformation, Error> {
let pc = self.read_core_reg(self.program_counter().id)?;
Ok(CoreInformation { pc: pc.try_into()? })
}
fn skip_breakpoint_instruction(&mut self) -> Result<(), Error> {
self.state.semihosting_command = None;
if !self.state.pc_written {
let debug_cause = self.interface.read_register::<DebugCause>()?;
let pc_increment = if debug_cause.break_instruction() {
3
} else if debug_cause.break_n_instruction() {
2
} else {
0
};
if pc_increment > 0 {
let pc = self.program_counter().id;
let mut pc_value = self.read_core_reg(pc)?;
pc_value.increment_address(pc_increment)?;
self.write_core_reg(pc, pc_value)?;
}
}
Ok(())
}
fn check_for_semihosting(&mut self) -> Result<Option<SemihostingCommand>, Error> {
const SEMI_BREAK: u32 = const {
let InstructionEncoding::Narrow(bytes) = Instruction::Break(1, 14).encode();
bytes
};
if let Some(command) = self.state.semihosting_command {
return Ok(Some(command));
}
let pc: u64 = self.read_core_reg(self.program_counter().id)?.try_into()?;
let mut actual_instruction = [0u8; 3];
self.read_8(pc, &mut actual_instruction)?;
let actual_instruction = u32::from_le_bytes([
actual_instruction[0],
actual_instruction[1],
actual_instruction[2],
0,
]);
tracing::debug!("Semihosting check pc={pc:#x} instruction={actual_instruction:#010x}");
let command = if actual_instruction == SEMI_BREAK {
Some(decode_semihosting_syscall(self)?)
} else {
None
};
self.state.semihosting_command = command;
Ok(command)
}
fn on_halted(&mut self) -> Result<(), Error> {
self.state.pc_written = false;
let status = self.status()?;
tracing::debug!("Core halted: {:#?}", status);
Ok(())
}
}
impl CoreMemoryInterface for Xtensa<'_> {
type ErrorType = Error;
fn memory(&self) -> &dyn MemoryInterface<Self::ErrorType> {
&self.interface
}
fn memory_mut(&mut self) -> &mut dyn MemoryInterface<Self::ErrorType> {
&mut self.interface
}
}
impl CoreInterface for Xtensa<'_> {
fn wait_for_core_halted(&mut self, timeout: Duration) -> Result<(), Error> {
self.interface.wait_for_core_halted(timeout)?;
self.on_halted()?;
Ok(())
}
fn core_halted(&mut self) -> Result<bool, Error> {
Ok(self.interface.core_halted()?)
}
fn status(&mut self) -> Result<CoreStatus, Error> {
let status = if self.core_halted()? {
let debug_cause = self.interface.read_register::<DebugCause>()?;
let mut reason = debug_cause.halt_reason();
if reason == HaltReason::Breakpoint(BreakpointCause::Software) {
self.state.pc_written = false;
if let Some(cmd) = self.check_for_semihosting()? {
reason = HaltReason::Breakpoint(BreakpointCause::Semihosting(cmd));
}
}
CoreStatus::Halted(reason)
} else {
CoreStatus::Running
};
Ok(status)
}
fn halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error> {
self.interface.halt(timeout)?;
self.on_halted()?;
self.core_info()
}
fn run(&mut self) -> Result<(), Error> {
self.skip_breakpoint_instruction()?;
if self.state.pc_written {
self.interface.clear_register_cache();
}
Ok(self.interface.resume_core()?)
}
fn reset(&mut self) -> Result<(), Error> {
self.reset_and_halt(Duration::from_millis(500))?;
self.run()
}
fn reset_and_halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error> {
self.state.semihosting_command = None;
self.sequence
.reset_system_and_halt(&mut self.interface, timeout)?;
self.on_halted()?;
self.on_attach()?;
self.core_info()
}
fn step(&mut self) -> Result<CoreInformation, Error> {
self.skip_breakpoint_instruction()?;
self.interface.step()?;
self.on_halted()?;
self.core_info()
}
fn read_core_reg(&mut self, address: RegisterId) -> Result<RegisterValue, Error> {
let register = Register::try_from(address)?;
let value = self.interface.read_register_untyped(register)?;
Ok(RegisterValue::U32(value))
}
fn write_core_reg(&mut self, address: RegisterId, value: RegisterValue) -> Result<(), Error> {
let value: u32 = value.try_into()?;
if address == self.program_counter().id {
self.state.pc_written = true;
}
let register = Register::try_from(address)?;
self.interface.write_register_untyped(register, value)?;
Ok(())
}
fn available_breakpoint_units(&mut self) -> Result<u32, Error> {
Ok(self.interface.available_breakpoint_units())
}
fn hw_breakpoints(&mut self) -> Result<Vec<Option<u64>>, Error> {
let mut breakpoints = Vec::with_capacity(self.available_breakpoint_units()? as usize);
let enabled_breakpoints = self.interface.read_register::<IBreakEn>()?;
for i in 0..self.available_breakpoint_units()? as usize {
let is_enabled = enabled_breakpoints.0 & (1 << i) != 0;
let breakpoint = if is_enabled {
let address = self
.interface
.read_register_untyped(Self::IBREAKA_REGS[i])?;
Some(address as u64)
} else {
None
};
breakpoints.push(breakpoint);
}
Ok(breakpoints)
}
fn enable_breakpoints(&mut self, state: bool) -> Result<(), Error> {
self.state.breakpoints_enabled = state;
let mask = if state {
self.state.breakpoint_mask()
} else {
0
};
self.interface.write_register(IBreakEn(mask))?;
Ok(())
}
fn set_hw_breakpoint(&mut self, unit_index: usize, addr: u64) -> Result<(), Error> {
self.state.breakpoint_set[unit_index] = true;
self.interface
.write_register_untyped(Self::IBREAKA_REGS[unit_index], addr as u32)?;
if self.state.breakpoints_enabled {
let mask = self.state.breakpoint_mask();
self.interface.write_register(IBreakEn(mask))?;
}
Ok(())
}
fn clear_hw_breakpoint(&mut self, unit_index: usize) -> Result<(), Error> {
self.state.breakpoint_set[unit_index] = false;
if self.state.breakpoints_enabled {
let mask = self.state.breakpoint_mask();
self.interface.write_register(IBreakEn(mask))?;
}
Ok(())
}
fn registers(&self) -> &'static CoreRegisters {
&XTENSA_CORE_REGSISTERS
}
fn program_counter(&self) -> &'static CoreRegister {
&PC
}
fn frame_pointer(&self) -> &'static CoreRegister {
&FP
}
fn stack_pointer(&self) -> &'static CoreRegister {
&SP
}
fn return_address(&self) -> &'static CoreRegister {
&RA
}
fn hw_breakpoints_enabled(&self) -> bool {
self.state.breakpoints_enabled
}
fn architecture(&self) -> Architecture {
Architecture::Xtensa
}
fn core_type(&self) -> CoreType {
CoreType::Xtensa
}
fn instruction_set(&mut self) -> Result<InstructionSet, Error> {
Ok(InstructionSet::Xtensa)
}
fn fpu_support(&mut self) -> Result<bool, Error> {
Ok(false)
}
fn floating_point_register_count(&mut self) -> Result<usize, Error> {
Ok(0)
}
fn reset_catch_set(&mut self) -> Result<(), Error> {
Err(Error::NotImplemented("reset_catch_set"))
}
fn reset_catch_clear(&mut self) -> Result<(), Error> {
Err(Error::NotImplemented("reset_catch_clear"))
}
fn debug_core_stop(&mut self) -> Result<(), Error> {
self.interface.leave_debug_mode()?;
Ok(())
}
}