use std::{
collections::HashMap,
ops::Range,
time::{Duration, Instant},
};
use zerocopy::IntoBytes;
use crate::{
BreakpointCause, Error as ProbeRsError, HaltReason, MemoryInterface,
architecture::xtensa::{
arch::{CpuRegister, Register, SpecialRegister, instruction::Instruction},
register_cache::RegisterCache,
xdm::{DebugStatus, XdmState},
},
memory::{Operation, OperationKind},
probe::{DebugProbeError, DeferredResultIndex, JtagAccess},
};
use super::xdm::{Error as XdmError, Xdm};
#[derive(thiserror::Error, Debug, docsplay::Display)]
pub enum XtensaError {
DebugProbe(#[from] DebugProbeError),
XdmError(#[from] XdmError),
CoreDisabled,
Timeout,
NoXtensaTarget,
RegisterNotAvailable,
BatchedResultNotAvailable,
}
impl From<XtensaError> for ProbeRsError {
fn from(err: XtensaError) -> Self {
match err {
XtensaError::DebugProbe(e) => e.into(),
other => ProbeRsError::Xtensa(other),
}
}
}
#[derive(Clone, Copy)]
pub enum DebugLevel {
L2 = 2,
L3 = 3,
L4 = 4,
L5 = 5,
L6 = 6,
L7 = 7,
}
impl DebugLevel {
pub fn pc(self) -> SpecialRegister {
match self {
DebugLevel::L2 => SpecialRegister::Epc2,
DebugLevel::L3 => SpecialRegister::Epc3,
DebugLevel::L4 => SpecialRegister::Epc4,
DebugLevel::L5 => SpecialRegister::Epc5,
DebugLevel::L6 => SpecialRegister::Epc6,
DebugLevel::L7 => SpecialRegister::Epc7,
}
}
pub fn ps(self) -> SpecialRegister {
match self {
DebugLevel::L2 => SpecialRegister::Eps2,
DebugLevel::L3 => SpecialRegister::Eps3,
DebugLevel::L4 => SpecialRegister::Eps4,
DebugLevel::L5 => SpecialRegister::Eps5,
DebugLevel::L6 => SpecialRegister::Eps6,
DebugLevel::L7 => SpecialRegister::Eps7,
}
}
}
#[derive(Default)]
pub(super) struct XtensaInterfaceState {
pub(super) register_cache: RegisterCache,
pub(super) is_halted: bool,
}
#[derive(Clone, Copy, Default)]
pub struct MemoryRegionProperties {
pub unaligned_store: bool,
pub unaligned_load: bool,
pub fast_memory_access: bool,
}
pub struct XtensaCoreProperties {
pub hw_breakpoint_num: u32,
pub debug_level: DebugLevel,
pub memory_ranges: HashMap<Range<u64>, MemoryRegionProperties>,
pub window_option_properties: WindowProperties,
}
impl Default for XtensaCoreProperties {
fn default() -> Self {
Self {
hw_breakpoint_num: 2,
debug_level: DebugLevel::L6,
memory_ranges: HashMap::new(),
window_option_properties: WindowProperties::lx(64),
}
}
}
impl XtensaCoreProperties {
pub fn memory_properties_at(&self, address: u64) -> MemoryRegionProperties {
self.memory_ranges
.iter()
.find(|(range, _)| range.contains(&address))
.map(|(_, region)| *region)
.unwrap_or_default()
}
pub fn memory_range_properties(&self, range: Range<u64>) -> MemoryRegionProperties {
let mut start = range.start;
let end = range.end;
if start == end {
return MemoryRegionProperties::default();
}
let mut properties = MemoryRegionProperties {
unaligned_store: true,
unaligned_load: true,
fast_memory_access: true,
};
while start < end {
let containing_region = self
.memory_ranges
.iter()
.find(|(range, _)| range.contains(&start));
let Some((range, region_properties)) = containing_region else {
return MemoryRegionProperties::default();
};
properties.unaligned_store &= region_properties.unaligned_store;
properties.unaligned_load &= region_properties.unaligned_load;
properties.fast_memory_access &= region_properties.fast_memory_access;
start = range.end;
}
properties
}
}
#[derive(Clone, Copy, Debug)]
pub struct WindowProperties {
pub has_windowed_registers: bool,
pub num_aregs: u8,
pub window_regs: u8,
pub rotw_rotates: u8,
}
impl WindowProperties {
pub fn lx(num_aregs: u8) -> Self {
Self {
has_windowed_registers: true,
num_aregs,
window_regs: 16,
rotw_rotates: 4,
}
}
pub fn windowbase_size(&self) -> u8 {
self.num_aregs / self.rotw_rotates
}
}
#[derive(Default)]
pub struct XtensaDebugInterfaceState {
interface_state: XtensaInterfaceState,
core_properties: XtensaCoreProperties,
xdm_state: XdmState,
}
pub struct XtensaCommunicationInterface<'probe> {
pub(crate) xdm: Xdm<'probe>,
pub(super) state: &'probe mut XtensaInterfaceState,
core_properties: &'probe mut XtensaCoreProperties,
}
impl<'probe> XtensaCommunicationInterface<'probe> {
pub fn new(
probe: &'probe mut dyn JtagAccess,
state: &'probe mut XtensaDebugInterfaceState,
) -> Self {
let XtensaDebugInterfaceState {
interface_state,
core_properties,
xdm_state,
} = state;
let xdm = Xdm::new(probe, xdm_state);
Self {
xdm,
state: interface_state,
core_properties,
}
}
pub fn core_properties(&mut self) -> &mut XtensaCoreProperties {
self.core_properties
}
pub fn read_idcode(&mut self) -> Result<u32, XtensaError> {
self.xdm.read_idcode()
}
pub fn enter_debug_mode(&mut self) -> Result<(), XtensaError> {
self.state.register_cache = RegisterCache::new();
self.xdm.enter_debug_mode()?;
self.state.is_halted = self.xdm.status()?.stopped();
Ok(())
}
pub(crate) fn leave_debug_mode(&mut self) -> Result<(), XtensaError> {
if self.xdm.status()?.stopped() {
self.restore_registers()?;
self.resume_core()?;
}
self.xdm.leave_ocd_mode()?;
tracing::debug!("Left OCD mode");
Ok(())
}
pub fn available_breakpoint_units(&self) -> u32 {
self.core_properties.hw_breakpoint_num
}
pub fn core_halted(&mut self) -> Result<bool, XtensaError> {
if !self.state.is_halted {
self.state.is_halted = self.xdm.status()?.stopped();
}
Ok(self.state.is_halted)
}
pub fn wait_for_core_halted(&mut self, timeout: Duration) -> Result<(), XtensaError> {
let start = Instant::now();
while !self.core_halted()? {
if start.elapsed() >= timeout {
return Err(XtensaError::Timeout);
}
std::thread::sleep(Duration::from_millis(1));
}
Ok(())
}
pub(crate) fn halt(&mut self, timeout: Duration) -> Result<(), XtensaError> {
self.xdm.schedule_halt();
self.wait_for_core_halted(timeout)?;
Ok(())
}
pub(crate) fn halt_with_previous(&mut self, timeout: Duration) -> Result<bool, XtensaError> {
let was_running = if self.state.is_halted {
false
} else {
let status_idx = self.xdm.schedule_read_nexus_register::<DebugStatus>();
self.halt(timeout)?;
let before_status = DebugStatus(self.xdm.read_deferred_result(status_idx)?.into_u32());
!before_status.stopped()
};
Ok(was_running)
}
fn fast_halted_access(
&mut self,
mut op: impl FnMut(&mut Self) -> Result<(), XtensaError>,
) -> Result<(), XtensaError> {
if self.state.is_halted {
return op(self);
}
let status_idx = self.xdm.schedule_read_nexus_register::<DebugStatus>();
self.xdm.schedule_halt();
let is_halted_idx = self.xdm.schedule_read_nexus_register::<DebugStatus>();
self.state.is_halted = true;
let result = op(self);
let after_status = DebugStatus(self.xdm.read_deferred_result(is_halted_idx)?.into_u32());
if after_status.stopped() {
let before_status = DebugStatus(self.xdm.read_deferred_result(status_idx)?.into_u32());
if !before_status.stopped() {
self.resume_core()?;
}
return result;
}
self.state.is_halted = false;
self.halted_access(|this| op(this))
}
pub fn halted_access<R>(
&mut self,
op: impl FnOnce(&mut Self) -> Result<R, XtensaError>,
) -> Result<R, XtensaError> {
let was_running = self.halt_with_previous(Duration::from_millis(100))?;
let result = op(self);
if was_running {
self.resume_core()?;
}
result
}
pub fn step(&mut self, by: u32, intlevel: u32) -> Result<(), XtensaError> {
self.schedule_write_register(ICountLevel(intlevel + 1))?;
self.schedule_write_register(ICount(-((1 + by) as i32) as u32))?;
self.resume_core()?;
match self.wait_for_core_halted(Duration::from_millis(100)) {
Ok(()) => {}
Err(XtensaError::Timeout) => self.halt(Duration::from_millis(100))?,
Err(e) => return Err(e),
}
self.schedule_write_register(ICountLevel(0))?;
Ok(())
}
pub fn resume_core(&mut self) -> Result<(), XtensaError> {
self.restore_registers()?;
self.clear_register_cache();
tracing::debug!("Resuming core");
self.state.is_halted = false;
self.xdm.resume()?;
Ok(())
}
fn schedule_read_cpu_register(&mut self, register: CpuRegister) -> DeferredResultIndex {
self.xdm
.schedule_execute_instruction(Instruction::Wsr(SpecialRegister::Ddr, register));
self.xdm.schedule_read_ddr()
}
fn schedule_read_special_register(
&mut self,
register: SpecialRegister,
) -> Result<DeferredResultIndex, XtensaError> {
self.ensure_register_saved(CpuRegister::A3)?;
self.state.register_cache.mark_dirty(CpuRegister::A3.into());
self.xdm
.schedule_execute_instruction(Instruction::Rsr(register, CpuRegister::A3));
Ok(self.schedule_read_cpu_register(CpuRegister::A3))
}
fn schedule_write_special_register(
&mut self,
register: SpecialRegister,
value: u32,
) -> Result<(), XtensaError> {
tracing::debug!("Writing special register: {:?}", register);
self.ensure_register_saved(CpuRegister::A3)?;
self.state.register_cache.mark_dirty(CpuRegister::A3.into());
self.xdm.schedule_write_ddr(value);
self.xdm
.schedule_execute_instruction(Instruction::Rsr(SpecialRegister::Ddr, CpuRegister::A3));
self.xdm
.schedule_execute_instruction(Instruction::Wsr(register, CpuRegister::A3));
Ok(())
}
#[tracing::instrument(skip(self), level = "debug")]
fn schedule_write_cpu_register(
&mut self,
register: CpuRegister,
value: u32,
) -> Result<(), XtensaError> {
tracing::debug!("Writing {:x} to register: {:?}", value, register);
self.xdm.schedule_write_ddr(value);
self.xdm
.schedule_execute_instruction(Instruction::Rsr(SpecialRegister::Ddr, register));
Ok(())
}
pub fn read_register<R: TypedRegister>(&mut self) -> Result<R, XtensaError> {
let value = self.read_register_untyped(R::register())?;
Ok(R::from_u32(value))
}
pub fn write_register<R: TypedRegister>(&mut self, reg: R) -> Result<(), XtensaError> {
self.write_register_untyped(R::register(), reg.as_u32())?;
Ok(())
}
pub(crate) fn schedule_write_register<R: TypedRegister>(
&mut self,
reg: R,
) -> Result<(), XtensaError> {
self.schedule_write_register_untyped(R::register(), reg.as_u32())?;
Ok(())
}
pub(crate) fn schedule_read_register(
&mut self,
register: impl Into<Register>,
) -> Result<MaybeDeferredResultIndex, XtensaError> {
let register = register.into();
if let Some(value) = self.state.register_cache.original_value_of(register) {
return Ok(value);
}
let reader = match register {
Register::Cpu(register) => self.schedule_read_cpu_register(register),
Register::Special(register) => self.schedule_read_special_register(register)?,
Register::CurrentPc => {
self.schedule_read_special_register(self.core_properties.debug_level.pc())?
}
Register::CurrentPs => {
self.schedule_read_special_register(self.core_properties.debug_level.ps())?
}
};
self.state.register_cache.store_deferred(register, reader);
Ok(MaybeDeferredResultIndex::Deferred(register))
}
pub fn read_register_untyped(
&mut self,
register: impl Into<Register>,
) -> Result<u32, XtensaError> {
let reader = self.schedule_read_register(register)?;
self.read_deferred_result(reader)
}
pub fn schedule_write_register_untyped(
&mut self,
register: impl Into<Register>,
value: u32,
) -> Result<(), XtensaError> {
let register = register.into();
self.state.register_cache.store(register, value);
match register {
Register::Cpu(register) => self.schedule_write_cpu_register(register, value),
Register::Special(register) => self.schedule_write_special_register(register, value),
Register::CurrentPc => {
self.schedule_write_special_register(self.core_properties.debug_level.pc(), value)
}
Register::CurrentPs => {
self.schedule_write_special_register(self.core_properties.debug_level.ps(), value)
}
}
}
pub fn write_register_untyped(
&mut self,
register: impl Into<Register>,
value: u32,
) -> Result<(), XtensaError> {
self.schedule_write_register_untyped(register, value)?;
self.xdm.execute()
}
#[tracing::instrument(skip(self, register), fields(register), level = "debug")]
fn ensure_register_saved(&mut self, register: impl Into<Register>) -> Result<(), XtensaError> {
let register = register.into();
tracing::debug!("Saving register: {:?}", register);
self.schedule_read_register(register)?;
Ok(())
}
#[tracing::instrument(skip(self), level = "debug")]
pub(super) fn restore_registers(&mut self) -> Result<(), XtensaError> {
tracing::debug!("Restoring registers");
let filters = [
|r: &Register| !r.is_cpu_register(),
|r: &Register| r.is_cpu_register(),
];
for filter in filters {
let dirty_regs = self
.state
.register_cache
.iter()
.filter(|(r, entry)| entry.is_dirty() && filter(r))
.map(|(r, _)| r)
.collect::<Vec<_>>();
for register in dirty_regs {
let restore_value = self
.state
.register_cache
.resolved_original_value_of(register, &mut self.xdm)
.unwrap_or_else(|| panic!("Saved register {register:?} is not in the cache. This is a bug, please report it."))?;
self.schedule_write_register_untyped(register, restore_value)?;
}
}
Ok(())
}
fn memory_access_for(&self, address: u64, len: usize) -> Box<dyn MemoryAccess> {
if self
.core_properties
.memory_range_properties(address..address + len as u64)
.fast_memory_access
{
Box::new(FastMemoryAccess::new())
} else {
Box::new(SlowMemoryAccess::new())
}
}
fn read_memory(&mut self, address: u64, dst: &mut [u8]) -> Result<(), XtensaError> {
tracing::debug!("Reading {} bytes from address {:08x}", dst.len(), address);
if dst.is_empty() {
return Ok(());
}
let mut memory_access = self.memory_access_for(address, dst.len());
memory_access.halted_access(self, &mut |this, memory_access| {
memory_access.save_scratch_registers(this)?;
this.read_memory_impl(memory_access, address, dst)
})
}
fn read_memory_impl(
&mut self,
memory_access: &mut dyn MemoryAccess,
address: u64,
mut dst: &mut [u8],
) -> Result<(), XtensaError> {
let mut to_read = dst.len();
let first_read = if !address.is_multiple_of(4)
&& !self
.core_properties
.memory_range_properties(address..address + dst.len() as u64)
.unaligned_load
{
memory_access.load_initial_address_for_read(self, address as u32 & !0x3)?;
let offset = address as usize % 4;
let first_read = if offset + to_read <= 4 {
memory_access.read_one(self)?
} else {
memory_access.read_one_and_continue(self)?
};
let bytes_to_copy = (4 - offset).min(to_read);
to_read -= bytes_to_copy;
Some((first_read, offset, bytes_to_copy))
} else {
memory_access.load_initial_address_for_read(self, address as u32)?;
None
};
let mut aligned_reads = vec![];
if to_read > 0 {
let words = to_read.div_ceil(4);
for _ in 0..words - 1 {
aligned_reads.push(memory_access.read_one_and_continue(self)?);
}
aligned_reads.push(memory_access.read_one(self)?);
};
if let Some((read, offset, bytes_to_copy)) = first_read {
let word = self
.xdm
.read_deferred_result(read)?
.into_u32()
.to_le_bytes();
dst[..bytes_to_copy].copy_from_slice(&word[offset..][..bytes_to_copy]);
dst = &mut dst[bytes_to_copy..];
}
for read in aligned_reads {
let word = self
.xdm
.read_deferred_result(read)?
.into_u32()
.to_le_bytes();
let bytes = dst.len().min(4);
dst[..bytes].copy_from_slice(&word[..bytes]);
dst = &mut dst[bytes..];
}
Ok(())
}
pub(crate) fn write_memory(&mut self, address: u64, data: &[u8]) -> Result<(), XtensaError> {
tracing::debug!("Writing {} bytes to address {:08x}", data.len(), address);
if data.is_empty() {
return Ok(());
}
let mut memory_access = self.memory_access_for(address, data.len());
memory_access.halted_access(self, &mut |this, memory_access| {
memory_access.save_scratch_registers(this)?;
this.write_memory_impl(memory_access, address, data)
})
}
fn execute_single_memory_operation_halted(
&mut self,
operation: Operation<'_>,
) -> Result<(), ProbeRsError> {
enum Op<'a> {
Read(&'a mut [u8]),
Write(&'a [u8]),
}
impl Op<'_> {
fn data_len(&self) -> usize {
match self {
Op::Read(bytes) => bytes.len(),
Op::Write(bytes) => bytes.len(),
}
}
}
let mut temp_bytes = [0; 8];
let op = match operation.operation {
OperationKind::Read(data) => Op::Read(data),
OperationKind::Read8(data) => Op::Read(data),
OperationKind::Read16(data) => Op::Read(data.as_mut_bytes()),
OperationKind::Read32(data) => Op::Read(data.as_mut_bytes()),
OperationKind::Read64(data) => Op::Read(data.as_mut_bytes()),
OperationKind::Write(data) => Op::Write(data),
OperationKind::Write8(data) => Op::Write(data),
OperationKind::Write16(data) => Op::Write(data.as_bytes()),
OperationKind::Write32(data) => Op::Write(data.as_bytes()),
OperationKind::Write64(data) => Op::Write(data.as_bytes()),
OperationKind::WriteWord8(word) => {
let word_bytes = size_of_val(&word);
let bytes = &mut temp_bytes[..word_bytes];
bytes.copy_from_slice(&word.to_le_bytes());
Op::Write(bytes)
}
OperationKind::WriteWord16(word) => {
let word_bytes = size_of_val(&word);
let bytes = &mut temp_bytes[..word_bytes];
bytes.copy_from_slice(&word.to_le_bytes());
Op::Write(bytes)
}
OperationKind::WriteWord32(word) => {
let word_bytes = size_of_val(&word);
let bytes = &mut temp_bytes[..word_bytes];
bytes.copy_from_slice(&word.to_le_bytes());
Op::Write(bytes)
}
OperationKind::WriteWord64(word) => {
let word_bytes = size_of_val(&word);
let bytes = &mut temp_bytes[..word_bytes];
bytes.copy_from_slice(&word.to_le_bytes());
Op::Write(bytes)
}
};
if op.data_len() == 0 {
return Ok(());
}
let address = operation.address;
let mut memory_access = self.memory_access_for(address, op.data_len());
memory_access.save_scratch_registers(self)?;
match op {
Op::Read(dst) => self
.read_memory_impl(memory_access.as_mut(), address, dst)
.map_err(ProbeRsError::Xtensa),
Op::Write(buffer) => self
.write_memory_impl(memory_access.as_mut(), address, buffer)
.map_err(ProbeRsError::Xtensa),
}
}
fn write_memory_impl(
&mut self,
memory_access: &mut dyn MemoryAccess,
address: u64,
mut buffer: &[u8],
) -> Result<(), XtensaError> {
let mut addr = address as u32;
let mut address_loaded = false;
if !addr.is_multiple_of(4)
&& !self
.core_properties
.memory_range_properties(address..address + buffer.len() as u64)
.unaligned_store
{
let unaligned_bytes = (4 - (addr % 4) as usize).min(buffer.len());
let aligned_address = address & !0x3;
let offset_in_word = address as usize % 4;
let mut word = [0; 4];
self.read_memory_impl(memory_access, aligned_address, &mut word)?;
word[offset_in_word..][..unaligned_bytes].copy_from_slice(&buffer[..unaligned_bytes]);
memory_access.load_initial_address_for_write(self, aligned_address as u32)?;
memory_access.write_one(self, u32::from_le_bytes(word))?;
buffer = &buffer[unaligned_bytes..];
addr += unaligned_bytes as u32;
address_loaded = true;
}
if buffer.len() >= 4 {
if !address_loaded {
memory_access.load_initial_address_for_write(self, addr)?;
}
let mut chunks = buffer.chunks_exact(4);
for chunk in chunks.by_ref() {
let mut word = [0; 4];
word[..].copy_from_slice(chunk);
let word = u32::from_le_bytes(word);
memory_access.write_one(self, word)?;
addr += 4;
}
buffer = chunks.remainder();
}
if !buffer.is_empty() {
let mut word = [0; 4];
self.read_memory_impl(memory_access, addr as u64, &mut word)?;
word[..buffer.len()].copy_from_slice(buffer);
memory_access.load_initial_address_for_write(self, addr)?;
memory_access.write_one(self, u32::from_le_bytes(word))?;
}
Ok(())
}
pub(crate) fn reset_and_halt(&mut self, timeout: Duration) -> Result<(), XtensaError> {
self.clear_register_cache();
self.xdm.reset_and_halt()?;
self.wait_for_core_halted(timeout)?;
self.write_register({
let mut ps = ProgramStatus(0);
ps.set_intlevel(0);
ps.set_user_mode(true);
ps.set_woe(true);
ps
})?;
self.state.register_cache = RegisterCache::new();
Ok(())
}
pub(crate) fn clear_register_cache(&mut self) {
self.state.register_cache = RegisterCache::new();
}
pub(crate) fn read_deferred_result(
&mut self,
result: MaybeDeferredResultIndex,
) -> Result<u32, XtensaError> {
self.state.register_cache.resolve(result, &mut self.xdm)
}
}
impl MemoryInterface for XtensaCommunicationInterface<'_> {
fn read(&mut self, address: u64, dst: &mut [u8]) -> Result<(), crate::Error> {
self.read_memory(address, dst)?;
Ok(())
}
fn supports_native_64bit_access(&mut self) -> bool {
false
}
fn read_word_64(&mut self, address: u64) -> Result<u64, crate::Error> {
let mut out = [0; 8];
self.read(address, &mut out)?;
Ok(u64::from_le_bytes(out))
}
fn read_word_32(&mut self, address: u64) -> Result<u32, crate::Error> {
let mut out = [0; 4];
self.read(address, &mut out)?;
Ok(u32::from_le_bytes(out))
}
fn read_word_16(&mut self, address: u64) -> Result<u16, crate::Error> {
let mut out = [0; 2];
self.read(address, &mut out)?;
Ok(u16::from_le_bytes(out))
}
fn read_word_8(&mut self, address: u64) -> Result<u8, crate::Error> {
let mut out = 0;
self.read(address, std::slice::from_mut(&mut out))?;
Ok(out)
}
fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), crate::Error> {
self.read_8(address, data.as_mut_bytes())
}
fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), crate::Error> {
self.read_8(address, data.as_mut_bytes())
}
fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), crate::Error> {
self.read_8(address, data.as_mut_bytes())
}
fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), crate::Error> {
self.read(address, data)
}
fn write(&mut self, address: u64, data: &[u8]) -> Result<(), crate::Error> {
self.write_memory(address, data)?;
Ok(())
}
fn write_word_64(&mut self, address: u64, data: u64) -> Result<(), crate::Error> {
self.write(address, &data.to_le_bytes())
}
fn write_word_32(&mut self, address: u64, data: u32) -> Result<(), crate::Error> {
self.write(address, &data.to_le_bytes())
}
fn write_word_16(&mut self, address: u64, data: u16) -> Result<(), crate::Error> {
self.write(address, &data.to_le_bytes())
}
fn write_word_8(&mut self, address: u64, data: u8) -> Result<(), crate::Error> {
self.write(address, &[data])
}
fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), crate::Error> {
self.write_8(address, data.as_bytes())
}
fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), crate::Error> {
self.write_8(address, data.as_bytes())
}
fn write_16(&mut self, address: u64, data: &[u16]) -> Result<(), crate::Error> {
self.write_8(address, data.as_bytes())
}
fn write_8(&mut self, address: u64, data: &[u8]) -> Result<(), crate::Error> {
self.write(address, data)
}
fn supports_8bit_transfers(&self) -> Result<bool, crate::Error> {
Ok(true)
}
fn flush(&mut self) -> Result<(), crate::Error> {
Ok(())
}
fn execute_memory_operations(&mut self, operations: &mut [Operation<'_>]) {
if operations.is_empty() {
return;
}
let result = self.fast_halted_access(|this| {
for operation in operations.iter_mut() {
let result = this.execute_single_memory_operation_halted(operation.reborrow());
let success = result.is_ok();
operation.result = Some(result);
if !success {
break;
}
}
Ok(())
});
if result.is_err()
&& let Some(op) = operations.get_mut(0)
{
op.result = Some(result.map_err(ProbeRsError::Xtensa));
}
}
}
pub trait TypedRegister: Copy {
fn register() -> Register;
fn from_u32(value: u32) -> Self;
fn as_u32(self) -> u32;
}
macro_rules! u32_register {
($name:ident, $register:expr) => {
impl TypedRegister for $name {
fn register() -> Register {
Register::from($register)
}
fn from_u32(value: u32) -> Self {
Self(value)
}
fn as_u32(self) -> u32 {
self.0
}
}
};
}
bitfield::bitfield! {
#[derive(Copy, Clone)]
pub struct DebugCause(u32);
impl Debug;
pub icount_exception, set_icount_exception : 0;
pub ibreak_exception, set_ibreak_exception : 1;
pub dbreak_exception, set_dbreak_exception : 2;
pub break_instruction, set_break_instruction : 3;
pub break_n_instruction, set_break_n_instruction: 4;
pub debug_interrupt, set_debug_interrupt : 5;
pub dbreak_num, set_dbreak_num : 11, 8;
}
u32_register!(DebugCause, SpecialRegister::DebugCause);
impl DebugCause {
pub fn halt_reason(&self) -> HaltReason {
let is_icount_exception = self.icount_exception();
let is_ibreak_exception = self.ibreak_exception();
let is_break_instruction = self.break_instruction();
let is_break_n_instruction = self.break_n_instruction();
let is_dbreak_exception = self.dbreak_exception();
let is_debug_interrupt = self.debug_interrupt();
let is_breakpoint = is_break_instruction || is_break_n_instruction;
let count = is_icount_exception as u8
+ is_ibreak_exception as u8
+ is_break_instruction as u8
+ is_break_n_instruction as u8
+ is_dbreak_exception as u8
+ is_debug_interrupt as u8;
if count > 1 {
tracing::debug!("DebugCause: {:?}", self);
if is_breakpoint {
HaltReason::Breakpoint(BreakpointCause::Unknown)
} else {
HaltReason::Multiple
}
} else if is_icount_exception {
HaltReason::Step
} else if is_ibreak_exception {
HaltReason::Breakpoint(BreakpointCause::Hardware)
} else if is_breakpoint {
HaltReason::Breakpoint(BreakpointCause::Software)
} else if is_dbreak_exception {
HaltReason::Watchpoint
} else if is_debug_interrupt {
HaltReason::Request
} else {
HaltReason::Unknown
}
}
}
bitfield::bitfield! {
#[derive(Copy, Clone)]
pub struct ProgramStatus(u32);
impl Debug;
pub intlevel, set_intlevel : 3, 0;
pub excm, set_excm : 4;
pub user_mode, set_user_mode: 5;
pub ring, set_ring : 7, 6;
pub owb, set_owb : 11, 8;
pub callinc, set_callinc : 17, 16;
pub woe, set_woe : 18;
}
u32_register!(ProgramStatus, Register::CurrentPs);
#[derive(Copy, Clone, Debug)]
pub struct IBreakEn(pub u32);
u32_register!(IBreakEn, SpecialRegister::IBreakEnable);
#[derive(Copy, Clone, Debug)]
pub struct ICount(pub u32);
u32_register!(ICount, SpecialRegister::ICount);
#[derive(Copy, Clone, Debug)]
pub struct ICountLevel(pub u32);
u32_register!(ICountLevel, SpecialRegister::ICountLevel);
#[derive(Copy, Clone, Debug)]
pub struct ProgramCounter(pub u32);
u32_register!(ProgramCounter, Register::CurrentPc);
trait MemoryAccess {
fn halted_access(
&mut self,
interface: &mut XtensaCommunicationInterface,
op: &mut dyn FnMut(
&mut XtensaCommunicationInterface,
&mut dyn MemoryAccess,
) -> Result<(), XtensaError>,
) -> Result<(), XtensaError>;
fn save_scratch_registers(
&mut self,
interface: &mut XtensaCommunicationInterface,
) -> Result<(), XtensaError>;
fn load_initial_address_for_read(
&mut self,
interface: &mut XtensaCommunicationInterface,
address: u32,
) -> Result<(), XtensaError>;
fn load_initial_address_for_write(
&mut self,
interface: &mut XtensaCommunicationInterface,
address: u32,
) -> Result<(), XtensaError>;
fn read_one(
&mut self,
interface: &mut XtensaCommunicationInterface,
) -> Result<DeferredResultIndex, XtensaError>;
fn read_one_and_continue(
&mut self,
interface: &mut XtensaCommunicationInterface,
) -> Result<DeferredResultIndex, XtensaError>;
fn write_one(
&mut self,
interface: &mut XtensaCommunicationInterface,
data: u32,
) -> Result<(), XtensaError>;
}
struct FastMemoryAccess;
impl FastMemoryAccess {
fn new() -> Self {
Self
}
}
impl MemoryAccess for FastMemoryAccess {
fn halted_access(
&mut self,
interface: &mut XtensaCommunicationInterface,
op: &mut dyn FnMut(
&mut XtensaCommunicationInterface,
&mut dyn MemoryAccess,
) -> Result<(), XtensaError>,
) -> Result<(), XtensaError> {
interface.fast_halted_access(|this| op(this, self))
}
fn save_scratch_registers(
&mut self,
interface: &mut XtensaCommunicationInterface,
) -> Result<(), XtensaError> {
interface.ensure_register_saved(CpuRegister::A3)?;
Ok(())
}
fn load_initial_address_for_read(
&mut self,
interface: &mut XtensaCommunicationInterface,
address: u32,
) -> Result<(), XtensaError> {
interface.schedule_write_cpu_register(CpuRegister::A3, address)?;
interface
.state
.register_cache
.mark_dirty(CpuRegister::A3.into());
interface
.xdm
.schedule_execute_instruction(Instruction::Lddr32P(CpuRegister::A3));
Ok(())
}
fn load_initial_address_for_write(
&mut self,
interface: &mut XtensaCommunicationInterface,
address: u32,
) -> Result<(), XtensaError> {
interface.schedule_write_cpu_register(CpuRegister::A3, address)?;
interface
.state
.register_cache
.mark_dirty(CpuRegister::A3.into());
interface
.xdm
.schedule_write_instruction(Instruction::Sddr32P(CpuRegister::A3));
Ok(())
}
fn write_one(
&mut self,
interface: &mut XtensaCommunicationInterface,
data: u32,
) -> Result<(), XtensaError> {
interface.xdm.schedule_write_ddr_and_execute(data);
Ok(())
}
fn read_one(
&mut self,
interface: &mut XtensaCommunicationInterface,
) -> Result<DeferredResultIndex, XtensaError> {
Ok(interface.xdm.schedule_read_ddr())
}
fn read_one_and_continue(
&mut self,
interface: &mut XtensaCommunicationInterface,
) -> Result<DeferredResultIndex, XtensaError> {
Ok(interface.xdm.schedule_read_ddr_and_execute())
}
}
struct SlowMemoryAccess {
current_address: u32,
current_offset: u32,
address_written: bool,
}
impl SlowMemoryAccess {
fn new() -> Self {
Self {
current_address: 0,
current_offset: 0,
address_written: false,
}
}
}
impl MemoryAccess for SlowMemoryAccess {
fn halted_access(
&mut self,
interface: &mut XtensaCommunicationInterface,
op: &mut dyn FnMut(
&mut XtensaCommunicationInterface,
&mut dyn MemoryAccess,
) -> Result<(), XtensaError>,
) -> Result<(), XtensaError> {
interface.halted_access(|this| op(this, self))
}
fn save_scratch_registers(
&mut self,
interface: &mut XtensaCommunicationInterface,
) -> Result<(), XtensaError> {
interface.ensure_register_saved(CpuRegister::A3)?;
interface.ensure_register_saved(CpuRegister::A4)?;
Ok(())
}
fn load_initial_address_for_read(
&mut self,
_interface: &mut XtensaCommunicationInterface,
address: u32,
) -> Result<(), XtensaError> {
self.current_address = address;
Ok(())
}
fn load_initial_address_for_write(
&mut self,
_interface: &mut XtensaCommunicationInterface,
address: u32,
) -> Result<(), XtensaError> {
self.current_address = address;
Ok(())
}
fn read_one(
&mut self,
interface: &mut XtensaCommunicationInterface,
) -> Result<DeferredResultIndex, XtensaError> {
if !self.address_written {
interface.schedule_write_cpu_register(CpuRegister::A3, self.current_address)?;
interface
.state
.register_cache
.mark_dirty(CpuRegister::A3.into());
self.current_offset = 0;
self.address_written = true;
}
interface
.xdm
.schedule_execute_instruction(Instruction::L32I(
CpuRegister::A3,
CpuRegister::A4,
(self.current_offset / 4) as u8,
));
self.current_offset += 4;
interface
.state
.register_cache
.mark_dirty(CpuRegister::A4.into());
if self.current_offset == 1024 {
self.current_address += self.current_offset;
self.current_offset = 0;
self.address_written = false;
}
Ok(interface.schedule_read_cpu_register(CpuRegister::A4))
}
fn read_one_and_continue(
&mut self,
interface: &mut XtensaCommunicationInterface,
) -> Result<DeferredResultIndex, XtensaError> {
self.read_one(interface)
}
fn write_one(
&mut self,
interface: &mut XtensaCommunicationInterface,
data: u32,
) -> Result<(), XtensaError> {
interface.schedule_write_cpu_register(CpuRegister::A3, self.current_address)?;
interface
.state
.register_cache
.mark_dirty(CpuRegister::A3.into());
interface.schedule_write_cpu_register(CpuRegister::A4, data)?;
interface
.state
.register_cache
.mark_dirty(CpuRegister::A4.into());
self.current_address += 4;
interface
.xdm
.schedule_execute_instruction(Instruction::S32I(CpuRegister::A3, CpuRegister::A4, 0));
Ok(())
}
}
pub(crate) enum MaybeDeferredResultIndex {
Value(u32),
Deferred(Register),
}