Skip to main content

probe_rs/architecture/xtensa/
communication_interface.rs

1//! Xtensa Debug Module Communication
2
3use std::{
4    collections::HashMap,
5    ops::Range,
6    time::{Duration, Instant},
7};
8
9use zerocopy::IntoBytes;
10
11use crate::{
12    BreakpointCause, Error as ProbeRsError, HaltReason, MemoryInterface,
13    architecture::xtensa::{
14        arch::{CpuRegister, Register, SpecialRegister, instruction::Instruction},
15        register_cache::RegisterCache,
16        xdm::{DebugStatus, XdmState},
17    },
18    memory::{Operation, OperationKind},
19    probe::{DebugProbeError, JtagAccess, queue::DeferredResultIndex},
20};
21
22use super::xdm::{Error as XdmError, Xdm};
23
24/// Possible Xtensa errors
25#[derive(thiserror::Error, Debug, docsplay::Display)]
26pub enum XtensaError {
27    /// An error originating from the DebugProbe occurred.
28    DebugProbe(#[from] DebugProbeError),
29
30    /// Xtensa debug module error.
31    XdmError(#[from] XdmError),
32
33    /// The core is not enabled.
34    CoreDisabled,
35
36    /// The operation has timed out.
37    // TODO: maybe we could be a bit more specific
38    Timeout,
39
40    /// The connected target is not an Xtensa device.
41    NoXtensaTarget,
42
43    /// The requested register is not available.
44    RegisterNotAvailable,
45
46    /// The result index of a batched command is not available.
47    BatchedResultNotAvailable,
48}
49
50impl From<XtensaError> for ProbeRsError {
51    fn from(err: XtensaError) -> Self {
52        match err {
53            XtensaError::DebugProbe(e) => e.into(),
54            other => ProbeRsError::Xtensa(other),
55        }
56    }
57}
58
59/// Debug interrupt level values.
60#[derive(Clone, Copy)]
61pub enum DebugLevel {
62    /// The CPU was configured to take Debug interrupts at level 2.
63    L2 = 2,
64    /// The CPU was configured to take Debug interrupts at level 3.
65    L3 = 3,
66    /// The CPU was configured to take Debug interrupts at level 4.
67    L4 = 4,
68    /// The CPU was configured to take Debug interrupts at level 5.
69    L5 = 5,
70    /// The CPU was configured to take Debug interrupts at level 6.
71    L6 = 6,
72    /// The CPU was configured to take Debug interrupts at level 7.
73    L7 = 7,
74}
75
76impl DebugLevel {
77    /// The register that contains the current program counter value.
78    pub fn pc(self) -> SpecialRegister {
79        match self {
80            DebugLevel::L2 => SpecialRegister::Epc2,
81            DebugLevel::L3 => SpecialRegister::Epc3,
82            DebugLevel::L4 => SpecialRegister::Epc4,
83            DebugLevel::L5 => SpecialRegister::Epc5,
84            DebugLevel::L6 => SpecialRegister::Epc6,
85            DebugLevel::L7 => SpecialRegister::Epc7,
86        }
87    }
88
89    /// The register that contains the current program status value.
90    pub fn ps(self) -> SpecialRegister {
91        match self {
92            DebugLevel::L2 => SpecialRegister::Eps2,
93            DebugLevel::L3 => SpecialRegister::Eps3,
94            DebugLevel::L4 => SpecialRegister::Eps4,
95            DebugLevel::L5 => SpecialRegister::Eps5,
96            DebugLevel::L6 => SpecialRegister::Eps6,
97            DebugLevel::L7 => SpecialRegister::Eps7,
98        }
99    }
100}
101
102/// Xtensa interface state.
103#[derive(Default)]
104pub(super) struct XtensaInterfaceState {
105    /// The register cache.
106    pub(super) register_cache: RegisterCache,
107
108    /// Whether the core is halted.
109    // This roughly relates to Core Debug States (true = Running, false = [Stopped, Stepping])
110    pub(super) is_halted: bool,
111}
112
113/// Properties of a memory region.
114#[derive(Clone, Copy, Default)]
115pub struct MemoryRegionProperties {
116    /// Whether the CPU supports unaligned stores. (Hardware Alignment Option)
117    pub unaligned_store: bool,
118
119    /// Whether the CPU supports unaligned loads. (Hardware Alignment Option)
120    pub unaligned_load: bool,
121
122    /// Whether the CPU supports fast memory access in this region. (LDDR32.P/SDDR32.P instructions)
123    pub fast_memory_access: bool,
124}
125
126/// Properties of an Xtensa CPU core.
127pub struct XtensaCoreProperties {
128    /// The number of hardware breakpoints the target supports. CPU-specific configuration value.
129    pub hw_breakpoint_num: u32,
130
131    /// The interrupt level at which debug exceptions are generated. CPU-specific configuration value.
132    pub debug_level: DebugLevel,
133
134    /// Known memory ranges with special properties.
135    pub memory_ranges: HashMap<Range<u64>, MemoryRegionProperties>,
136
137    /// Configurable options in the Windowed Register Option
138    pub window_option_properties: WindowProperties,
139}
140
141impl Default for XtensaCoreProperties {
142    fn default() -> Self {
143        Self {
144            hw_breakpoint_num: 2,
145            debug_level: DebugLevel::L6,
146            memory_ranges: HashMap::new(),
147            window_option_properties: WindowProperties::lx(64),
148        }
149    }
150}
151
152impl XtensaCoreProperties {
153    /// Returns the memory range for the given address.
154    pub fn memory_properties_at(&self, address: u64) -> MemoryRegionProperties {
155        self.memory_ranges
156            .iter()
157            .find(|(range, _)| range.contains(&address))
158            .map(|(_, region)| *region)
159            .unwrap_or_default()
160    }
161
162    /// Returns the conservative memory range properties for the given address range.
163    pub fn memory_range_properties(&self, range: Range<u64>) -> MemoryRegionProperties {
164        let mut start = range.start;
165        let end = range.end;
166
167        if start == end {
168            return MemoryRegionProperties::default();
169        }
170
171        let mut properties = MemoryRegionProperties {
172            unaligned_store: true,
173            unaligned_load: true,
174            fast_memory_access: true,
175        };
176        while start < end {
177            // Find region that contains the start address.
178            let containing_region = self
179                .memory_ranges
180                .iter()
181                .find(|(range, _)| range.contains(&start));
182
183            let Some((range, region_properties)) = containing_region else {
184                // no point in continuing
185                return MemoryRegionProperties::default();
186            };
187
188            properties.unaligned_store &= region_properties.unaligned_store;
189            properties.unaligned_load &= region_properties.unaligned_load;
190            properties.fast_memory_access &= region_properties.fast_memory_access;
191
192            // Move start to the end of the region.
193            start = range.end;
194        }
195
196        properties
197    }
198}
199
200/// Properties of the windowed register file.
201#[derive(Clone, Copy, Debug)]
202pub struct WindowProperties {
203    /// Whether the CPU has windowed registers.
204    pub has_windowed_registers: bool,
205
206    /// The total number of AR registers in the register file.
207    pub num_aregs: u8,
208
209    /// The number of registers in a single window.
210    pub window_regs: u8,
211
212    /// The number of registers rotated by an LSB of the ROTW instruction.
213    pub rotw_rotates: u8,
214}
215
216impl WindowProperties {
217    /// Create a new WindowProperties instance with the given number of AR registers.
218    pub fn lx(num_aregs: u8) -> Self {
219        Self {
220            has_windowed_registers: true,
221            num_aregs,
222            window_regs: 16,
223            rotw_rotates: 4,
224        }
225    }
226
227    /// Returns the number of different valid WindowBase values.
228    pub fn windowbase_size(&self) -> u8 {
229        self.num_aregs / self.rotw_rotates
230    }
231}
232
233/// Debug module and transport state.
234#[derive(Default)]
235pub struct XtensaDebugInterfaceState {
236    interface_state: XtensaInterfaceState,
237    core_properties: XtensaCoreProperties,
238    xdm_state: XdmState,
239}
240
241/// The higher level of the XDM functionality.
242// TODO: this includes core state and CPU configuration that don't exactly belong
243// here but one layer up.
244pub struct XtensaCommunicationInterface<'probe> {
245    /// The Xtensa debug module
246    pub xdm: Xdm<'probe>,
247    pub(super) state: &'probe mut XtensaInterfaceState,
248    core_properties: &'probe mut XtensaCoreProperties,
249}
250
251impl<'probe> XtensaCommunicationInterface<'probe> {
252    /// Create the Xtensa communication interface using the underlying probe driver
253    pub fn new(
254        probe: &'probe mut dyn JtagAccess,
255        state: &'probe mut XtensaDebugInterfaceState,
256    ) -> Self {
257        let XtensaDebugInterfaceState {
258            interface_state,
259            core_properties,
260            xdm_state,
261        } = state;
262        let xdm = Xdm::new(probe, xdm_state);
263
264        Self {
265            xdm,
266            state: interface_state,
267            core_properties,
268        }
269    }
270
271    /// Access the properties of the CPU core.
272    pub fn core_properties(&mut self) -> &mut XtensaCoreProperties {
273        self.core_properties
274    }
275
276    /// Read the targets IDCODE.
277    pub fn read_idcode(&mut self) -> Result<u32, XtensaError> {
278        self.xdm.read_idcode()
279    }
280
281    /// Enter debug mode.
282    pub fn enter_debug_mode(&mut self) -> Result<(), XtensaError> {
283        self.clear_register_cache();
284        self.xdm.enter_debug_mode()?;
285
286        self.state.is_halted = self.xdm.status()?.stopped();
287
288        Ok(())
289    }
290
291    pub(crate) fn leave_debug_mode(&mut self) -> Result<(), XtensaError> {
292        if self.xdm.status()?.stopped() {
293            self.restore_registers()?;
294            self.resume_core()?;
295        }
296        self.xdm.leave_ocd_mode()?;
297
298        tracing::debug!("Left OCD mode");
299
300        Ok(())
301    }
302
303    /// Returns the number of hardware breakpoints the target supports.
304    ///
305    /// On the Xtensa architecture this is the `NIBREAK` configuration parameter.
306    pub fn available_breakpoint_units(&self) -> u32 {
307        self.core_properties.hw_breakpoint_num
308    }
309
310    /// Returns whether the core is halted.
311    pub fn core_halted(&mut self) -> Result<bool, XtensaError> {
312        if !self.state.is_halted {
313            self.state.is_halted = self.xdm.status()?.stopped();
314        }
315
316        Ok(self.state.is_halted)
317    }
318
319    /// Waits until the core is halted.
320    ///
321    /// This function lowers the interrupt level to allow halting on debug exceptions.
322    pub fn wait_for_core_halted(&mut self, timeout: Duration) -> Result<(), XtensaError> {
323        // Wait until halted state is active again.
324        let start = Instant::now();
325
326        while !self.core_halted()? {
327            if start.elapsed() >= timeout {
328                return Err(XtensaError::Timeout);
329            }
330            // Wait a bit before polling again.
331            std::thread::sleep(Duration::from_millis(1));
332        }
333
334        Ok(())
335    }
336
337    /// Halts the core.
338    pub(crate) fn halt(&mut self, timeout: Duration) -> Result<(), XtensaError> {
339        self.xdm.schedule_halt();
340        self.wait_for_core_halted(timeout)?;
341        Ok(())
342    }
343
344    /// Halts the core and returns `true` if the core was running before the halt.
345    pub(crate) fn halt_with_previous(&mut self, timeout: Duration) -> Result<bool, XtensaError> {
346        let was_running = if self.state.is_halted {
347            // Core is already halted, we don't need to do anything.
348            false
349        } else {
350            // If we have not halted the core, it may still be halted on a breakpoint, for example.
351            // Let's check status.
352            let status_idx = self.xdm.schedule_read_nexus_register::<DebugStatus>();
353            self.halt(timeout)?;
354            let before_status = DebugStatus(self.xdm.read_deferred_result(status_idx)?.into_u32());
355
356            !before_status.stopped()
357        };
358
359        Ok(was_running)
360    }
361
362    fn fast_halted_access(
363        &mut self,
364        mut op: impl FnMut(&mut Self) -> Result<(), XtensaError>,
365    ) -> Result<(), XtensaError> {
366        if self.state.is_halted {
367            // Core is already halted, we don't need to do anything.
368            return op(self);
369        }
370
371        // If we have not halted the core, it may still be halted on a breakpoint, for example.
372        // Let's check status.
373        let status_idx = self.xdm.schedule_read_nexus_register::<DebugStatus>();
374
375        // Queue up halting.
376        self.xdm.schedule_halt();
377
378        // We will need to check if we managed to halt the core.
379        let is_halted_idx = self.xdm.schedule_read_nexus_register::<DebugStatus>();
380        self.state.is_halted = true;
381
382        // Execute the operation while the core is presumed halted. If it is not, we will have
383        // various errors, but we will retry the operation.
384        let result = op(self);
385
386        // If we did not manage to halt the core at once, let's retry using the slow path.
387        let after_status = DebugStatus(self.xdm.read_deferred_result(is_halted_idx)?.into_u32());
388
389        if after_status.stopped() {
390            // If the core was running, resume it.
391            let before_status = DebugStatus(self.xdm.read_deferred_result(status_idx)?.into_u32());
392            if !before_status.stopped() {
393                self.resume_core()?;
394            }
395
396            return result;
397        }
398        self.state.is_halted = false;
399        self.halted_access(|this| op(this))
400    }
401
402    /// Executes a closure while ensuring the core is halted.
403    pub fn halted_access<R>(
404        &mut self,
405        op: impl FnOnce(&mut Self) -> Result<R, XtensaError>,
406    ) -> Result<R, XtensaError> {
407        let was_running = self.halt_with_previous(Duration::from_millis(100))?;
408
409        let result = op(self);
410
411        if was_running {
412            self.resume_core()?;
413        }
414
415        result
416    }
417
418    /// Steps the core by one instruction.
419    pub fn step(&mut self, by: u32, intlevel: u32) -> Result<(), XtensaError> {
420        // Instructions executed below icountlevel increment the ICOUNT register.
421        self.schedule_write_register(ICountLevel(intlevel + 1))?;
422
423        // An exception is generated at the beginning of an instruction that would overflow ICOUNT.
424        self.schedule_write_register(ICount(-((1 + by) as i32) as u32))?;
425
426        self.resume_core()?;
427        // TODO: instructions like WAITI should be emulated as they are not single steppable.
428        // For now it's good enough to force a halt on timeout (instead of crashing) although it can
429        // stop in a long-running interrupt handler which isn't necessarily what the user wants.
430        // Even then, WAITI should be detected and emulated.
431        match self.wait_for_core_halted(Duration::from_millis(100)) {
432            Ok(()) => {}
433            Err(XtensaError::Timeout) => self.halt(Duration::from_millis(100))?,
434            Err(e) => return Err(e),
435        }
436
437        // Avoid stopping again
438        self.schedule_write_register(ICountLevel(0))?;
439
440        Ok(())
441    }
442
443    /// Resumes program execution.
444    pub fn resume_core(&mut self) -> Result<(), XtensaError> {
445        // Any time we resume the core, we need to restore the registers so the the program
446        // doesn't crash.
447        self.restore_registers()?;
448        // We also need to clear the register cache, as the CPU will likely change the registers.
449        self.clear_register_cache();
450
451        tracing::debug!("Resuming core");
452        self.state.is_halted = false;
453        self.xdm.resume()?;
454
455        Ok(())
456    }
457
458    fn schedule_write_special_register(
459        &mut self,
460        register: SpecialRegister,
461        value: u32,
462    ) -> Result<(), XtensaError> {
463        tracing::debug!("Writing special register: {:?}", register);
464        const SCRATCH_REGISTER: CpuRegister = CpuRegister::A3;
465
466        self.ensure_register_saved(SCRATCH_REGISTER)?;
467        self.state.register_cache.mark_dirty(SCRATCH_REGISTER);
468
469        self.schedule_write_cpu_register(SCRATCH_REGISTER, value)?;
470
471        // scratch -> target special register
472        self.xdm
473            .schedule_execute_instruction(Instruction::Wsr(register, SCRATCH_REGISTER));
474
475        Ok(())
476    }
477
478    #[tracing::instrument(skip(self), level = "debug")]
479    fn schedule_write_cpu_register(
480        &mut self,
481        register: CpuRegister,
482        value: u32,
483    ) -> Result<(), XtensaError> {
484        tracing::debug!("Writing {:x} to register: {:?}", value, register);
485
486        self.xdm.schedule_write_ddr(value);
487        self.xdm
488            .schedule_execute_instruction(Instruction::Rsr(SpecialRegister::Ddr, register));
489
490        Ok(())
491    }
492
493    /// Read a register.
494    pub fn read_register<R: TypedRegister>(&mut self) -> Result<R, XtensaError> {
495        let value = self.read_register_untyped(R::register())?;
496
497        Ok(R::from_u32(value))
498    }
499
500    /// Write a register.
501    pub fn write_register<R: TypedRegister>(&mut self, reg: R) -> Result<(), XtensaError> {
502        self.write_register_untyped(R::register(), reg.as_u32())?;
503
504        Ok(())
505    }
506
507    /// Schedules writing a register.
508    pub(crate) fn schedule_write_register<R: TypedRegister>(
509        &mut self,
510        reg: R,
511    ) -> Result<(), XtensaError> {
512        self.schedule_write_register_untyped(R::register(), reg.as_u32())?;
513
514        Ok(())
515    }
516
517    /// Schedules reading a register.
518    ///
519    /// If the register is already in the cache, it will return the value from there.
520    pub(crate) fn schedule_read_register(
521        &mut self,
522        register: impl Into<Register>,
523    ) -> Result<MaybeDeferredResultIndex, XtensaError> {
524        let register = register.into();
525        if let Some(value) = self.state.register_cache.original_value_of(register) {
526            // Already read, can be accessed from the cache.
527            return Ok(value);
528        }
529
530        let reader = self.schedule_read_register_uncached(register)?;
531
532        self.state.register_cache.store_deferred(register, reader);
533        Ok(MaybeDeferredResultIndex::Deferred(register))
534    }
535
536    pub(crate) fn schedule_read_register_uncached(
537        &mut self,
538        register: impl Into<Register>,
539    ) -> Result<DeferredResultIndex, XtensaError> {
540        let register = register.into();
541
542        const SCRATCH_REGISTER: CpuRegister = CpuRegister::A3;
543        let mut cpu_register = SCRATCH_REGISTER;
544
545        // Do we need to read a special register?
546        let special_register = match register {
547            Register::Cpu(register) => {
548                cpu_register = register;
549                None
550            }
551            Register::Special(register) => Some(register),
552            Register::CurrentPc => Some(self.core_properties.debug_level.pc()),
553            Register::CurrentPs => Some(self.core_properties.debug_level.ps()),
554        };
555
556        if let Some(special_register) = special_register {
557            // If we need to read a special register, read through a scratch register.
558            self.ensure_register_saved(cpu_register)?;
559            self.state.register_cache.mark_dirty(cpu_register);
560
561            // Read special register into the scratch register.
562            self.xdm
563                .schedule_execute_instruction(Instruction::Rsr(special_register, cpu_register));
564        }
565
566        self.xdm
567            .schedule_execute_instruction(Instruction::Wsr(SpecialRegister::Ddr, cpu_register));
568
569        Ok(self.xdm.schedule_read_ddr())
570    }
571
572    /// Read a register.
573    pub fn read_register_untyped(
574        &mut self,
575        register: impl Into<Register>,
576    ) -> Result<u32, XtensaError> {
577        let reader = self.schedule_read_register(register)?;
578        self.read_deferred_result(reader)
579    }
580
581    /// Schedules writing a register.
582    ///
583    /// This function primes the register cache with the value to be written, therefore
584    /// it is not suitable for writing scratch registers.
585    pub fn schedule_write_register_untyped(
586        &mut self,
587        register: impl Into<Register>,
588        value: u32,
589    ) -> Result<(), XtensaError> {
590        let register = register.into();
591
592        self.state.register_cache.store(register, value);
593
594        match register {
595            Register::Cpu(register) => self.schedule_write_cpu_register(register, value),
596            Register::Special(register) => self.schedule_write_special_register(register, value),
597            Register::CurrentPc => {
598                self.schedule_write_special_register(self.core_properties.debug_level.pc(), value)
599            }
600            Register::CurrentPs => {
601                self.schedule_write_special_register(self.core_properties.debug_level.ps(), value)
602            }
603        }
604    }
605
606    /// Write a register.
607    pub fn write_register_untyped(
608        &mut self,
609        register: impl Into<Register>,
610        value: u32,
611    ) -> Result<(), XtensaError> {
612        self.schedule_write_register_untyped(register, value)?;
613        self.xdm.execute()
614    }
615
616    /// Ensures that a scratch register is saved in the register cache before overwriting it.
617    #[tracing::instrument(skip(self, register), fields(register), level = "debug")]
618    fn ensure_register_saved(&mut self, register: impl Into<Register>) -> Result<(), XtensaError> {
619        let register = register.into();
620
621        tracing::debug!("Saving register: {:?}", register);
622        self.schedule_read_register(register)?;
623
624        Ok(())
625    }
626
627    #[tracing::instrument(skip(self), level = "debug")]
628    pub(super) fn restore_registers(&mut self) -> Result<(), XtensaError> {
629        tracing::debug!("Restoring registers");
630
631        let filters = [
632            // First, we restore special registers, as they may need to use scratch registers.
633            |r: &Register| !r.is_cpu_register(),
634            // Next, we restore CPU registers, which include scratch registers.
635            |r: &Register| r.is_cpu_register(),
636        ];
637        for filter in filters {
638            // Clone the list of saved registers so we can iterate over it, but code may still save
639            // new registers. We can't take it otherwise the restore loop would unnecessarily save
640            // registers.
641            let dirty_regs = self
642                .state
643                .register_cache
644                .iter()
645                .filter(|(r, entry)| entry.is_dirty() && filter(r))
646                .map(|(r, _)| r)
647                .collect::<Vec<_>>();
648
649            for register in dirty_regs {
650                let restore_value = self
651                    .state
652                    .register_cache
653                    .resolved_original_value_of(register, &mut self.xdm)
654                    .unwrap_or_else(|| panic!("Saved register {register:?} is not in the cache. This is a bug, please report it."))?;
655
656                self.schedule_write_register_untyped(register, restore_value)?;
657            }
658        }
659
660        Ok(())
661    }
662
663    fn memory_access_for(&self, address: u64, len: usize) -> Box<dyn MemoryAccess> {
664        if self
665            .core_properties
666            .memory_range_properties(address..address + len as u64)
667            .fast_memory_access
668        {
669            Box::new(FastMemoryAccess::new())
670        } else {
671            Box::new(SlowMemoryAccess::new())
672        }
673    }
674
675    fn read_memory(&mut self, address: u64, dst: &mut [u8]) -> Result<(), XtensaError> {
676        tracing::debug!("Reading {} bytes from address {:08x}", dst.len(), address);
677        if dst.is_empty() {
678            return Ok(());
679        }
680
681        let mut memory_access = self.memory_access_for(address, dst.len());
682
683        memory_access.halted_access(self, &mut |this, memory_access| {
684            memory_access.save_scratch_registers(this)?;
685            this.read_memory_impl(memory_access, address, dst)
686        })
687    }
688
689    fn read_memory_impl(
690        &mut self,
691        memory_access: &mut dyn MemoryAccess,
692        address: u64,
693        mut dst: &mut [u8],
694    ) -> Result<(), XtensaError> {
695        let mut to_read = dst.len();
696
697        // Let's assume we can just do 32b reads, so let's
698        // do some pre-massaging on unaligned reads if needed.
699        let first_read = if !address.is_multiple_of(4)
700            && !self
701                .core_properties
702                .memory_range_properties(address..address + dst.len() as u64)
703                .unaligned_load
704        {
705            memory_access.load_initial_address_for_read(self, address as u32 & !0x3)?;
706            let offset = address as usize % 4;
707
708            // Avoid executing another read if we only have to read a single word
709            let first_read = if offset + to_read <= 4 {
710                memory_access.read_one(self)?
711            } else {
712                memory_access.read_one_and_continue(self)?
713            };
714
715            let bytes_to_copy = (4 - offset).min(to_read);
716
717            to_read -= bytes_to_copy;
718
719            Some((first_read, offset, bytes_to_copy))
720        } else {
721            // The read is either aligned or the core supports unaligned loads.
722            memory_access.load_initial_address_for_read(self, address as u32)?;
723            None
724        };
725
726        let mut aligned_reads = vec![];
727        if to_read > 0 {
728            let words = to_read.div_ceil(4);
729
730            for _ in 0..words - 1 {
731                aligned_reads.push(memory_access.read_one_and_continue(self)?);
732            }
733            aligned_reads.push(memory_access.read_one(self)?);
734        };
735
736        if let Some((read, offset, bytes_to_copy)) = first_read {
737            let word = self
738                .xdm
739                .read_deferred_result(read)?
740                .into_u32()
741                .to_le_bytes();
742
743            dst[..bytes_to_copy].copy_from_slice(&word[offset..][..bytes_to_copy]);
744            dst = &mut dst[bytes_to_copy..];
745        }
746
747        for read in aligned_reads {
748            let word = self
749                .xdm
750                .read_deferred_result(read)?
751                .into_u32()
752                .to_le_bytes();
753
754            let bytes = dst.len().min(4);
755
756            dst[..bytes].copy_from_slice(&word[..bytes]);
757            dst = &mut dst[bytes..];
758        }
759
760        Ok(())
761    }
762
763    pub(crate) fn write_memory(&mut self, address: u64, data: &[u8]) -> Result<(), XtensaError> {
764        tracing::debug!("Writing {} bytes to address {:08x}", data.len(), address);
765        if data.is_empty() {
766            return Ok(());
767        }
768
769        let mut memory_access = self.memory_access_for(address, data.len());
770
771        memory_access.halted_access(self, &mut |this, memory_access| {
772            memory_access.save_scratch_registers(this)?;
773            this.write_memory_impl(memory_access, address, data)
774        })
775    }
776
777    /// Executes a single memory operation when the processor is halted.
778    fn execute_single_memory_operation_halted(
779        &mut self,
780        operation: Operation<'_>,
781    ) -> Result<(), ProbeRsError> {
782        enum Op<'a> {
783            Read(&'a mut [u8]),
784            Write(&'a [u8]),
785        }
786        impl Op<'_> {
787            fn data_len(&self) -> usize {
788                match self {
789                    Op::Read(bytes) => bytes.len(),
790                    Op::Write(bytes) => bytes.len(),
791                }
792            }
793        }
794
795        let mut temp_bytes = [0; 8];
796        let op = match operation.operation {
797            OperationKind::Read(data) => Op::Read(data),
798            OperationKind::Read8(data) => Op::Read(data),
799            OperationKind::Read16(data) => Op::Read(data.as_mut_bytes()),
800            OperationKind::Read32(data) => Op::Read(data.as_mut_bytes()),
801            OperationKind::Read64(data) => Op::Read(data.as_mut_bytes()),
802            OperationKind::Write(data) => Op::Write(data),
803            OperationKind::Write8(data) => Op::Write(data),
804            OperationKind::Write16(data) => Op::Write(data.as_bytes()),
805            OperationKind::Write32(data) => Op::Write(data.as_bytes()),
806            OperationKind::Write64(data) => Op::Write(data.as_bytes()),
807            OperationKind::WriteWord8(word) => {
808                let word_bytes = size_of_val(&word);
809                let bytes = &mut temp_bytes[..word_bytes];
810                bytes.copy_from_slice(&word.to_le_bytes());
811                Op::Write(bytes)
812            }
813            OperationKind::WriteWord16(word) => {
814                let word_bytes = size_of_val(&word);
815                let bytes = &mut temp_bytes[..word_bytes];
816                bytes.copy_from_slice(&word.to_le_bytes());
817                Op::Write(bytes)
818            }
819            OperationKind::WriteWord32(word) => {
820                let word_bytes = size_of_val(&word);
821                let bytes = &mut temp_bytes[..word_bytes];
822                bytes.copy_from_slice(&word.to_le_bytes());
823                Op::Write(bytes)
824            }
825            OperationKind::WriteWord64(word) => {
826                let word_bytes = size_of_val(&word);
827                let bytes = &mut temp_bytes[..word_bytes];
828                bytes.copy_from_slice(&word.to_le_bytes());
829                Op::Write(bytes)
830            }
831        };
832
833        if op.data_len() == 0 {
834            return Ok(());
835        }
836
837        let address = operation.address;
838
839        let mut memory_access = self.memory_access_for(address, op.data_len());
840        memory_access.save_scratch_registers(self)?;
841
842        match op {
843            Op::Read(dst) => self
844                .read_memory_impl(memory_access.as_mut(), address, dst)
845                .map_err(ProbeRsError::Xtensa),
846            Op::Write(buffer) => self
847                .write_memory_impl(memory_access.as_mut(), address, buffer)
848                .map_err(ProbeRsError::Xtensa),
849        }
850    }
851
852    fn write_memory_impl(
853        &mut self,
854        memory_access: &mut dyn MemoryAccess,
855        address: u64,
856        mut buffer: &[u8],
857    ) -> Result<(), XtensaError> {
858        let mut addr = address as u32;
859
860        // We store the unaligned head of the data separately. In case the core supports unaligned
861        // load/store, we can just write the data directly.
862        let mut address_loaded = false;
863        if !addr.is_multiple_of(4)
864            && !self
865                .core_properties
866                .memory_range_properties(address..address + buffer.len() as u64)
867                .unaligned_store
868        {
869            // If the core does not support unaligned load/store, read-modify-write the first
870            // few unaligned bytes. We are calculating `unaligned_bytes` so that we are not going
871            // across a word boundary here.
872            let unaligned_bytes = (4 - (addr % 4) as usize).min(buffer.len());
873            let aligned_address = address & !0x3;
874            let offset_in_word = address as usize % 4;
875
876            // Read the aligned word
877            let mut word = [0; 4];
878            self.read_memory_impl(memory_access, aligned_address, &mut word)?;
879
880            // Replace the written bytes.
881            word[offset_in_word..][..unaligned_bytes].copy_from_slice(&buffer[..unaligned_bytes]);
882
883            // Write the word back.
884            memory_access.load_initial_address_for_write(self, aligned_address as u32)?;
885            memory_access.write_one(self, u32::from_le_bytes(word))?;
886
887            buffer = &buffer[unaligned_bytes..];
888            addr += unaligned_bytes as u32;
889
890            address_loaded = true;
891        }
892
893        // Store whole words. If the core needs aligned accesses, the above block will have
894        // already stored the first unaligned part.
895        if buffer.len() >= 4 {
896            if !address_loaded {
897                memory_access.load_initial_address_for_write(self, addr)?;
898            }
899            let mut chunks = buffer.chunks_exact(4);
900            for chunk in chunks.by_ref() {
901                let mut word = [0; 4];
902                word[..].copy_from_slice(chunk);
903                let word = u32::from_le_bytes(word);
904
905                memory_access.write_one(self, word)?;
906
907                addr += 4;
908            }
909
910            buffer = chunks.remainder();
911        }
912
913        // We store the narrow tail of the data (1-3 bytes) separately.
914        if !buffer.is_empty() {
915            // We have 1-3 bytes left to write. If the core does not support unaligned load/store,
916            // the above blocks took care of aligning `addr` so we don't have to worry about
917            // crossing a word boundary here.
918
919            // Read the aligned word
920            let mut word = [0; 4];
921            self.read_memory_impl(memory_access, addr as u64, &mut word)?;
922
923            // Replace the written bytes.
924            word[..buffer.len()].copy_from_slice(buffer);
925
926            // Write the word back. We need to set the address because the read may have changed it.
927            memory_access.load_initial_address_for_write(self, addr)?;
928            memory_access.write_one(self, u32::from_le_bytes(word))?;
929        }
930
931        // TODO: implement cache flushing on CPUs that need it.
932
933        Ok(())
934    }
935
936    /// Reset and halt the target.
937    pub fn reset_and_halt(&mut self, timeout: Duration) -> Result<(), XtensaError> {
938        self.clear_register_cache();
939        self.xdm.reset_and_halt()?;
940        self.wait_for_core_halted(timeout)?;
941
942        // TODO: this is only necessary to run code, so this might not be the best place
943        // Make sure the CPU is in a known state and is able to run code we download.
944        self.write_register({
945            let mut ps = ProgramStatus(0);
946            ps.set_intlevel(0);
947            ps.set_user_mode(true);
948            ps.set_woe(true);
949            ps
950        })?;
951
952        Ok(())
953    }
954
955    pub(crate) fn clear_register_cache(&mut self) {
956        self.state.register_cache = RegisterCache::new();
957    }
958
959    pub(crate) fn read_deferred_result(
960        &mut self,
961        result: MaybeDeferredResultIndex,
962    ) -> Result<u32, XtensaError> {
963        self.state.register_cache.resolve(result, &mut self.xdm)
964    }
965}
966
967impl MemoryInterface for XtensaCommunicationInterface<'_> {
968    fn read(&mut self, address: u64, dst: &mut [u8]) -> Result<(), crate::Error> {
969        self.read_memory(address, dst)?;
970
971        Ok(())
972    }
973
974    fn supports_native_64bit_access(&mut self) -> bool {
975        false
976    }
977
978    fn read_word_64(&mut self, address: u64) -> Result<u64, crate::Error> {
979        let mut out = [0; 8];
980        self.read(address, &mut out)?;
981
982        Ok(u64::from_le_bytes(out))
983    }
984
985    fn read_word_32(&mut self, address: u64) -> Result<u32, crate::Error> {
986        let mut out = [0; 4];
987        self.read(address, &mut out)?;
988
989        Ok(u32::from_le_bytes(out))
990    }
991
992    fn read_word_16(&mut self, address: u64) -> Result<u16, crate::Error> {
993        let mut out = [0; 2];
994        self.read(address, &mut out)?;
995
996        Ok(u16::from_le_bytes(out))
997    }
998
999    fn read_word_8(&mut self, address: u64) -> Result<u8, crate::Error> {
1000        let mut out = 0;
1001        self.read(address, std::slice::from_mut(&mut out))?;
1002        Ok(out)
1003    }
1004
1005    fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), crate::Error> {
1006        self.read_8(address, data.as_mut_bytes())
1007    }
1008
1009    fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), crate::Error> {
1010        self.read_8(address, data.as_mut_bytes())
1011    }
1012
1013    fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), crate::Error> {
1014        self.read_8(address, data.as_mut_bytes())
1015    }
1016
1017    fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), crate::Error> {
1018        self.read(address, data)
1019    }
1020
1021    fn write(&mut self, address: u64, data: &[u8]) -> Result<(), crate::Error> {
1022        self.write_memory(address, data)?;
1023
1024        Ok(())
1025    }
1026
1027    fn write_word_64(&mut self, address: u64, data: u64) -> Result<(), crate::Error> {
1028        self.write(address, &data.to_le_bytes())
1029    }
1030
1031    fn write_word_32(&mut self, address: u64, data: u32) -> Result<(), crate::Error> {
1032        self.write(address, &data.to_le_bytes())
1033    }
1034
1035    fn write_word_16(&mut self, address: u64, data: u16) -> Result<(), crate::Error> {
1036        self.write(address, &data.to_le_bytes())
1037    }
1038
1039    fn write_word_8(&mut self, address: u64, data: u8) -> Result<(), crate::Error> {
1040        self.write(address, &[data])
1041    }
1042
1043    fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), crate::Error> {
1044        self.write_8(address, data.as_bytes())
1045    }
1046
1047    fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), crate::Error> {
1048        self.write_8(address, data.as_bytes())
1049    }
1050
1051    fn write_16(&mut self, address: u64, data: &[u16]) -> Result<(), crate::Error> {
1052        self.write_8(address, data.as_bytes())
1053    }
1054
1055    fn write_8(&mut self, address: u64, data: &[u8]) -> Result<(), crate::Error> {
1056        self.write(address, data)
1057    }
1058
1059    fn supports_8bit_transfers(&self) -> Result<bool, crate::Error> {
1060        Ok(true)
1061    }
1062
1063    fn flush(&mut self) -> Result<(), crate::Error> {
1064        Ok(())
1065    }
1066
1067    fn execute_memory_operations(&mut self, operations: &mut [Operation<'_>]) {
1068        if operations.is_empty() {
1069            return;
1070        }
1071        let result = self.fast_halted_access(|this| {
1072            for operation in operations.iter_mut() {
1073                let result = this.execute_single_memory_operation_halted(operation.reborrow());
1074                let success = result.is_ok();
1075                operation.result = Some(result);
1076                if !success {
1077                    break;
1078                }
1079            }
1080            Ok(())
1081        });
1082
1083        if result.is_err()
1084            && let Some(op) = operations.get_mut(0)
1085        {
1086            op.result = Some(result.map_err(ProbeRsError::Xtensa));
1087        }
1088    }
1089}
1090
1091/// An Xtensa core register
1092pub trait TypedRegister: Copy {
1093    /// Returns the register ID.
1094    fn register() -> Register;
1095
1096    /// Creates a new register from the given value.
1097    fn from_u32(value: u32) -> Self;
1098
1099    /// Returns the register value.
1100    fn as_u32(self) -> u32;
1101}
1102
1103macro_rules! u32_register {
1104    ($name:ident, $register:expr) => {
1105        impl TypedRegister for $name {
1106            fn register() -> Register {
1107                Register::from($register)
1108            }
1109
1110            fn from_u32(value: u32) -> Self {
1111                Self(value)
1112            }
1113
1114            fn as_u32(self) -> u32 {
1115                self.0
1116            }
1117        }
1118    };
1119}
1120
1121bitfield::bitfield! {
1122    /// The `DEBUGCAUSE` register.
1123    #[derive(Copy, Clone)]
1124    pub struct DebugCause(u32);
1125    impl Debug;
1126
1127    /// Instruction counter exception
1128    pub icount_exception,    set_icount_exception   : 0;
1129
1130    /// Instruction breakpoint exception
1131    pub ibreak_exception,    set_ibreak_exception   : 1;
1132
1133    /// Data breakpoint (watchpoint) exception
1134    pub dbreak_exception,    set_dbreak_exception   : 2;
1135
1136    /// Break instruction exception
1137    pub break_instruction,   set_break_instruction  : 3;
1138
1139    /// Narrow Break instruction exception
1140    pub break_n_instruction, set_break_n_instruction: 4;
1141
1142    /// Debug interrupt exception
1143    pub debug_interrupt,     set_debug_interrupt    : 5;
1144
1145    /// Data breakpoint number
1146    pub dbreak_num,          set_dbreak_num         : 11, 8;
1147}
1148u32_register!(DebugCause, SpecialRegister::DebugCause);
1149
1150impl DebugCause {
1151    /// Returns the reason why the core is halted.
1152    pub fn halt_reason(&self) -> HaltReason {
1153        let is_icount_exception = self.icount_exception();
1154        let is_ibreak_exception = self.ibreak_exception();
1155        let is_break_instruction = self.break_instruction();
1156        let is_break_n_instruction = self.break_n_instruction();
1157        let is_dbreak_exception = self.dbreak_exception();
1158        let is_debug_interrupt = self.debug_interrupt();
1159
1160        let is_breakpoint = is_break_instruction || is_break_n_instruction;
1161
1162        let count = is_icount_exception as u8
1163            + is_ibreak_exception as u8
1164            + is_break_instruction as u8
1165            + is_break_n_instruction as u8
1166            + is_dbreak_exception as u8
1167            + is_debug_interrupt as u8;
1168
1169        if count > 1 {
1170            tracing::debug!("DebugCause: {:?}", self);
1171
1172            // We cannot identify why the chip halted,
1173            // it could be for multiple reasons.
1174
1175            // For debuggers, it's important to know if
1176            // the core halted because of a breakpoint.
1177            // Because of this, we still return breakpoint
1178            // even if other reasons are possible as well.
1179            if is_breakpoint {
1180                HaltReason::Breakpoint(BreakpointCause::Unknown)
1181            } else {
1182                HaltReason::Multiple
1183            }
1184        } else if is_icount_exception {
1185            HaltReason::Step
1186        } else if is_ibreak_exception {
1187            HaltReason::Breakpoint(BreakpointCause::Hardware)
1188        } else if is_breakpoint {
1189            HaltReason::Breakpoint(BreakpointCause::Software)
1190        } else if is_dbreak_exception {
1191            HaltReason::Watchpoint
1192        } else if is_debug_interrupt {
1193            HaltReason::Request
1194        } else {
1195            HaltReason::Unknown
1196        }
1197    }
1198}
1199
1200bitfield::bitfield! {
1201    /// The `PS` (Program Status) register.
1202    ///
1203    /// The physical register depends on the debug level.
1204    #[derive(Copy, Clone)]
1205    pub struct ProgramStatus(u32);
1206    impl Debug;
1207
1208    /// Interrupt level disable
1209    pub intlevel,  set_intlevel : 3, 0;
1210
1211    /// Exception mode
1212    pub excm,      set_excm     : 4;
1213
1214    /// User mode
1215    pub user_mode, set_user_mode: 5;
1216
1217    /// Privilege level (when using the MMU option)
1218    pub ring,      set_ring     : 7, 6;
1219
1220    /// Old window base
1221    pub owb,       set_owb      : 11, 8;
1222
1223    /// Call increment
1224    pub callinc,   set_callinc  : 17, 16;
1225
1226    /// Window overflow-detection enable
1227    pub woe,       set_woe      : 18;
1228}
1229u32_register!(ProgramStatus, Register::CurrentPs);
1230
1231/// The `IBREAKEN` (Instruction Breakpoint Enable) register.
1232#[derive(Copy, Clone, Debug)]
1233pub struct IBreakEn(pub u32);
1234u32_register!(IBreakEn, SpecialRegister::IBreakEnable);
1235
1236/// The `ICOUNT` (Instruction Counter) register.
1237#[derive(Copy, Clone, Debug)]
1238pub struct ICount(pub u32);
1239u32_register!(ICount, SpecialRegister::ICount);
1240
1241/// The `ICOUNTLEVEL` (Instruction Count Level) register.
1242#[derive(Copy, Clone, Debug)]
1243pub struct ICountLevel(pub u32);
1244u32_register!(ICountLevel, SpecialRegister::ICountLevel);
1245
1246/// The Program Counter register.
1247#[derive(Copy, Clone, Debug)]
1248pub struct ProgramCounter(pub u32);
1249u32_register!(ProgramCounter, Register::CurrentPc);
1250
1251trait MemoryAccess {
1252    fn halted_access(
1253        &mut self,
1254        interface: &mut XtensaCommunicationInterface,
1255        op: &mut dyn FnMut(
1256            &mut XtensaCommunicationInterface,
1257            &mut dyn MemoryAccess,
1258        ) -> Result<(), XtensaError>,
1259    ) -> Result<(), XtensaError>;
1260
1261    fn save_scratch_registers(
1262        &mut self,
1263        interface: &mut XtensaCommunicationInterface,
1264    ) -> Result<(), XtensaError>;
1265
1266    fn load_initial_address_for_read(
1267        &mut self,
1268        interface: &mut XtensaCommunicationInterface,
1269        address: u32,
1270    ) -> Result<(), XtensaError>;
1271    fn load_initial_address_for_write(
1272        &mut self,
1273        interface: &mut XtensaCommunicationInterface,
1274        address: u32,
1275    ) -> Result<(), XtensaError>;
1276
1277    fn read_one(
1278        &mut self,
1279        interface: &mut XtensaCommunicationInterface,
1280    ) -> Result<DeferredResultIndex, XtensaError>;
1281
1282    fn read_one_and_continue(
1283        &mut self,
1284        interface: &mut XtensaCommunicationInterface,
1285    ) -> Result<DeferredResultIndex, XtensaError>;
1286
1287    fn write_one(
1288        &mut self,
1289        interface: &mut XtensaCommunicationInterface,
1290        data: u32,
1291    ) -> Result<(), XtensaError>;
1292}
1293
1294/// Memory access using LDDR32.P and SDDR32.P instructions.
1295struct FastMemoryAccess;
1296impl FastMemoryAccess {
1297    fn new() -> Self {
1298        Self
1299    }
1300}
1301impl MemoryAccess for FastMemoryAccess {
1302    fn halted_access(
1303        &mut self,
1304        interface: &mut XtensaCommunicationInterface,
1305        op: &mut dyn FnMut(
1306            &mut XtensaCommunicationInterface,
1307            &mut dyn MemoryAccess,
1308        ) -> Result<(), XtensaError>,
1309    ) -> Result<(), XtensaError> {
1310        interface.fast_halted_access(|this| op(this, self))
1311    }
1312
1313    fn save_scratch_registers(
1314        &mut self,
1315        interface: &mut XtensaCommunicationInterface,
1316    ) -> Result<(), XtensaError> {
1317        interface.ensure_register_saved(CpuRegister::A3)
1318    }
1319
1320    fn load_initial_address_for_read(
1321        &mut self,
1322        interface: &mut XtensaCommunicationInterface,
1323        address: u32,
1324    ) -> Result<(), XtensaError> {
1325        // Write aligned address to the scratch register
1326        interface.schedule_write_cpu_register(CpuRegister::A3, address)?;
1327        interface.state.register_cache.mark_dirty(CpuRegister::A3);
1328
1329        // Read from address in the scratch register
1330        interface
1331            .xdm
1332            .schedule_execute_instruction(Instruction::Lddr32P(CpuRegister::A3));
1333
1334        Ok(())
1335    }
1336
1337    fn load_initial_address_for_write(
1338        &mut self,
1339        interface: &mut XtensaCommunicationInterface,
1340        address: u32,
1341    ) -> Result<(), XtensaError> {
1342        interface.schedule_write_cpu_register(CpuRegister::A3, address)?;
1343        interface.state.register_cache.mark_dirty(CpuRegister::A3);
1344
1345        interface
1346            .xdm
1347            .schedule_write_instruction(Instruction::Sddr32P(CpuRegister::A3));
1348
1349        Ok(())
1350    }
1351
1352    fn write_one(
1353        &mut self,
1354        interface: &mut XtensaCommunicationInterface,
1355        data: u32,
1356    ) -> Result<(), XtensaError> {
1357        interface.xdm.schedule_write_ddr_and_execute(data);
1358        Ok(())
1359    }
1360
1361    fn read_one(
1362        &mut self,
1363        interface: &mut XtensaCommunicationInterface,
1364    ) -> Result<DeferredResultIndex, XtensaError> {
1365        Ok(interface.xdm.schedule_read_ddr())
1366    }
1367
1368    fn read_one_and_continue(
1369        &mut self,
1370        interface: &mut XtensaCommunicationInterface,
1371    ) -> Result<DeferredResultIndex, XtensaError> {
1372        Ok(interface.xdm.schedule_read_ddr_and_execute())
1373    }
1374}
1375
1376/// Memory access without LDDR32.P and SDDR32.P instructions.
1377struct SlowMemoryAccess {
1378    current_address: u32,
1379    current_offset: u32,
1380    address_written: bool,
1381}
1382impl SlowMemoryAccess {
1383    fn new() -> Self {
1384        Self {
1385            current_address: 0,
1386            current_offset: 0,
1387            address_written: false,
1388        }
1389    }
1390}
1391
1392impl MemoryAccess for SlowMemoryAccess {
1393    fn halted_access(
1394        &mut self,
1395        interface: &mut XtensaCommunicationInterface,
1396        op: &mut dyn FnMut(
1397            &mut XtensaCommunicationInterface,
1398            &mut dyn MemoryAccess,
1399        ) -> Result<(), XtensaError>,
1400    ) -> Result<(), XtensaError> {
1401        interface.halted_access(|this| op(this, self))
1402    }
1403
1404    fn save_scratch_registers(
1405        &mut self,
1406        interface: &mut XtensaCommunicationInterface,
1407    ) -> Result<(), XtensaError> {
1408        interface.ensure_register_saved(CpuRegister::A3)?;
1409        interface.ensure_register_saved(CpuRegister::A4)?;
1410        Ok(())
1411    }
1412
1413    fn load_initial_address_for_read(
1414        &mut self,
1415        _interface: &mut XtensaCommunicationInterface,
1416        address: u32,
1417    ) -> Result<(), XtensaError> {
1418        self.current_address = address;
1419
1420        Ok(())
1421    }
1422
1423    fn load_initial_address_for_write(
1424        &mut self,
1425        _interface: &mut XtensaCommunicationInterface,
1426        address: u32,
1427    ) -> Result<(), XtensaError> {
1428        self.current_address = address;
1429
1430        Ok(())
1431    }
1432
1433    fn read_one(
1434        &mut self,
1435        interface: &mut XtensaCommunicationInterface,
1436    ) -> Result<DeferredResultIndex, XtensaError> {
1437        if !self.address_written {
1438            interface.schedule_write_cpu_register(CpuRegister::A3, self.current_address)?;
1439            interface.state.register_cache.mark_dirty(CpuRegister::A3);
1440            self.current_offset = 0;
1441            self.address_written = true;
1442        }
1443
1444        interface
1445            .xdm
1446            .schedule_execute_instruction(Instruction::L32I(
1447                CpuRegister::A3,
1448                CpuRegister::A4,
1449                (self.current_offset / 4) as u8,
1450            ));
1451        self.current_offset += 4;
1452
1453        interface.state.register_cache.mark_dirty(CpuRegister::A4);
1454
1455        if self.current_offset == 1024 {
1456            // The maximum offset for L32I is 1020, so we need to
1457            // increment the base address and reset the offset.
1458            self.current_address += self.current_offset;
1459            self.current_offset = 0;
1460            self.address_written = false;
1461        }
1462
1463        interface.schedule_read_register_uncached(CpuRegister::A4)
1464    }
1465
1466    fn read_one_and_continue(
1467        &mut self,
1468        interface: &mut XtensaCommunicationInterface,
1469    ) -> Result<DeferredResultIndex, XtensaError> {
1470        self.read_one(interface)
1471    }
1472
1473    fn write_one(
1474        &mut self,
1475        interface: &mut XtensaCommunicationInterface,
1476        data: u32,
1477    ) -> Result<(), XtensaError> {
1478        // Store address and data
1479        interface.schedule_write_cpu_register(CpuRegister::A3, self.current_address)?;
1480        interface.state.register_cache.mark_dirty(CpuRegister::A3);
1481
1482        interface.schedule_write_cpu_register(CpuRegister::A4, data)?;
1483        interface.state.register_cache.mark_dirty(CpuRegister::A4);
1484
1485        // Increment address
1486        self.current_address += 4;
1487
1488        // Store A4 into address A3
1489        interface
1490            .xdm
1491            .schedule_execute_instruction(Instruction::S32I(CpuRegister::A3, CpuRegister::A4, 0));
1492
1493        Ok(())
1494    }
1495}
1496
1497pub(crate) enum MaybeDeferredResultIndex {
1498    /// The result is already available.
1499    Value(u32),
1500
1501    /// The result is deferred and can be accessed via the register cache.
1502    Deferred(Register),
1503}