probe_rs/architecture/xtensa/
mod.rs

1//! All the interface bits for Xtensa.
2
3use std::{sync::Arc, time::Duration};
4
5use probe_rs_target::{Architecture, CoreType, InstructionSet};
6use zerocopy::IntoBytes;
7
8use crate::{
9    CoreInformation, CoreInterface, CoreRegister, CoreStatus, Error, HaltReason, MemoryInterface,
10    architecture::xtensa::{
11        arch::{
12            CpuRegister, Register, SpecialRegister,
13            instruction::{Instruction, InstructionEncoding},
14        },
15        communication_interface::{
16            DebugCause, IBreakEn, ProgramStatus, WindowProperties, XtensaCommunicationInterface,
17        },
18        registers::{FP, PC, RA, SP, XTENSA_CORE_REGISTERS},
19        sequences::XtensaDebugSequence,
20        xdm::PowerStatus,
21    },
22    core::{
23        BreakpointCause,
24        registers::{CoreRegisters, RegisterId, RegisterValue},
25    },
26    semihosting::{SemihostingCommand, decode_semihosting_syscall},
27};
28
29pub(crate) mod arch;
30pub(crate) mod xdm;
31
32pub mod communication_interface;
33pub(crate) mod register_cache;
34pub mod registers;
35pub(crate) mod sequences;
36
37/// Xtensa core state.
38#[derive(Debug)]
39pub struct XtensaCoreState {
40    /// Whether the core is enabled.
41    enabled: bool,
42
43    /// Whether hardware breakpoints are enabled.
44    breakpoints_enabled: bool,
45
46    /// Whether each hardware breakpoint is set.
47    // 2 is the architectural upper limit. The actual count is stored in
48    // [`communication_interface::XtensaInterfaceState`]
49    breakpoint_set: [bool; 2],
50
51    /// Whether the PC was written since we last halted. Used to avoid incrementing the PC on
52    /// resume.
53    pc_written: bool,
54
55    /// The semihosting command that was decoded at the current program counter
56    semihosting_command: Option<SemihostingCommand>,
57
58    /// Whether the registers have been spilled to the stack.
59    spilled: bool,
60}
61
62impl XtensaCoreState {
63    /// Creates a new [`XtensaCoreState`].
64    pub(crate) fn new() -> Self {
65        Self {
66            enabled: false,
67            breakpoints_enabled: false,
68            breakpoint_set: [false; 2],
69            pc_written: false,
70            semihosting_command: None,
71            spilled: false,
72        }
73    }
74
75    /// Creates a bitmask of the currently set breakpoints.
76    fn breakpoint_mask(&self) -> u32 {
77        self.breakpoint_set
78            .iter()
79            .enumerate()
80            .fold(0, |acc, (i, &set)| if set { acc | (1 << i) } else { acc })
81    }
82}
83
84/// An interface to operate an Xtensa core.
85pub struct Xtensa<'probe> {
86    interface: XtensaCommunicationInterface<'probe>,
87    state: &'probe mut XtensaCoreState,
88    sequence: Arc<dyn XtensaDebugSequence>,
89}
90
91impl<'probe> Xtensa<'probe> {
92    const IBREAKA_REGS: [SpecialRegister; 2] =
93        [SpecialRegister::IBreakA0, SpecialRegister::IBreakA1];
94
95    /// Create a new Xtensa interface for a particular core.
96    pub fn new(
97        interface: XtensaCommunicationInterface<'probe>,
98        state: &'probe mut XtensaCoreState,
99        sequence: Arc<dyn XtensaDebugSequence>,
100    ) -> Result<Self, Error> {
101        let mut this = Self {
102            interface,
103            state,
104            sequence,
105        };
106
107        this.on_attach()?;
108
109        Ok(this)
110    }
111
112    fn clear_cache(&mut self) {
113        self.state.spilled = false;
114        self.interface.clear_register_cache();
115    }
116
117    fn on_attach(&mut self) -> Result<(), Error> {
118        // If the core was reset, force a reconnection.
119        let core_reset;
120        if self.state.enabled {
121            let status = self.interface.xdm.power_status({
122                let mut clear_value = PowerStatus(0);
123                clear_value.set_core_was_reset(true);
124                clear_value.set_debug_was_reset(true);
125                clear_value
126            })?;
127            core_reset = status.core_was_reset() || !status.core_domain_on();
128            let debug_reset = status.debug_was_reset() || !status.debug_domain_on();
129
130            if core_reset {
131                tracing::debug!("Core was reset");
132                *self.state = XtensaCoreState::new();
133            }
134            if debug_reset {
135                tracing::debug!("Debug was reset");
136                self.state.enabled = false;
137            }
138        } else {
139            core_reset = true;
140        }
141
142        // (Re)enter debug mode if necessary. This also checks if the core is enabled.
143        if !self.state.enabled {
144            // Enable debug module.
145            self.interface.enter_debug_mode()?;
146            self.state.enabled = true;
147
148            if core_reset {
149                // Run the connection sequence while halted.
150                let was_running = self
151                    .interface
152                    .halt_with_previous(Duration::from_millis(500))?;
153
154                self.sequence.on_connect(&mut self.interface)?;
155
156                if was_running {
157                    self.run()?;
158                }
159            }
160        }
161
162        Ok(())
163    }
164
165    fn core_info(&mut self) -> Result<CoreInformation, Error> {
166        let pc = self.read_core_reg(self.program_counter().id)?;
167
168        Ok(CoreInformation { pc: pc.try_into()? })
169    }
170
171    fn skip_breakpoint(&mut self) -> Result<(), Error> {
172        self.state.semihosting_command = None;
173        if !self.state.pc_written {
174            let debug_cause = self.debug_cause()?;
175
176            let pc_increment = if debug_cause.break_instruction() {
177                3
178            } else if debug_cause.break_n_instruction() {
179                2
180            } else {
181                0
182            };
183
184            if pc_increment > 0 {
185                // Step through the breakpoint
186                let mut pc_value = self.interface.read_register_untyped(Register::CurrentPc)?;
187                pc_value += pc_increment;
188                self.interface
189                    .write_register_untyped(Register::CurrentPc, pc_value)?;
190            } else if debug_cause.ibreak_exception() {
191                let pc_value = self.interface.read_register_untyped(Register::CurrentPc)?;
192                let bps = self.hw_breakpoints()?;
193                if let Some(bp_unit) = bps.iter().position(|bp| *bp == Some(pc_value as u64)) {
194                    // Disable the breakpoint
195                    self.clear_hw_breakpoint(bp_unit)?;
196                    // Single step
197                    let ps = self.current_ps()?;
198                    self.interface.step(1, ps.intlevel())?;
199                    // Re-enable the breakpoint
200                    self.set_hw_breakpoint(bp_unit, pc_value as u64)?;
201                }
202            }
203        }
204
205        Ok(())
206    }
207
208    /// Check if the current breakpoint is a semihosting call
209    // OpenOCD implementation: https://github.com/espressif/openocd-esp32/blob/93dd01511fd13d4a9fb322cd9b600c337becef9e/src/target/espressif/esp_xtensa_semihosting.c#L42-L103
210    fn check_for_semihosting(&mut self) -> Result<Option<SemihostingCommand>, Error> {
211        const SEMI_BREAK: u32 = const {
212            let InstructionEncoding::Narrow(bytes) = Instruction::Break(1, 14).encode();
213            bytes
214        };
215
216        // We only want to decode the semihosting command once, since answering it might change some of the registers
217        if let Some(command) = self.state.semihosting_command {
218            return Ok(Some(command));
219        }
220
221        let pc: u64 = self.read_core_reg(self.program_counter().id)?.try_into()?;
222
223        let mut actual_instruction = [0u8; 3];
224        self.read_8(pc, &mut actual_instruction)?;
225        let actual_instruction = u32::from_le_bytes([
226            actual_instruction[0],
227            actual_instruction[1],
228            actual_instruction[2],
229            0,
230        ]);
231
232        tracing::debug!("Semihosting check pc={pc:#x} instruction={actual_instruction:#010x}");
233
234        let command = if actual_instruction == SEMI_BREAK {
235            let syscall = decode_semihosting_syscall(self)?;
236            if let SemihostingCommand::Unknown(details) = syscall {
237                self.sequence
238                    .clone()
239                    .on_unknown_semihosting_command(self, details)?
240            } else {
241                Some(syscall)
242            }
243        } else {
244            None
245        };
246        self.state.semihosting_command = command;
247
248        Ok(command)
249    }
250
251    fn on_halted(&mut self) -> Result<(), Error> {
252        self.state.pc_written = false;
253        self.clear_cache();
254
255        let status = self.status()?;
256        tracing::debug!("Core halted: {:#?}", status);
257
258        if status.is_halted() {
259            self.sequence.on_halt(&mut self.interface)?;
260        }
261
262        Ok(())
263    }
264
265    fn halt_with_previous(&mut self, timeout: Duration) -> Result<bool, Error> {
266        let was_running = self.interface.halt_with_previous(timeout)?;
267        if was_running {
268            self.on_halted()?;
269        }
270
271        Ok(was_running)
272    }
273
274    fn halted_access<F, T>(&mut self, op: F) -> Result<T, Error>
275    where
276        F: FnOnce(&mut Self) -> Result<T, Error>,
277    {
278        let was_running = self.halt_with_previous(Duration::from_millis(100))?;
279
280        let result = op(self);
281
282        if was_running {
283            self.run()?;
284        }
285
286        result
287    }
288
289    fn current_ps(&mut self) -> Result<ProgramStatus, Error> {
290        // Reading ProgramStatus using `read_register` would return the value
291        // after the debug interrupt has been taken.
292        Ok(self
293            .interface
294            .read_register_untyped(Register::CurrentPs)
295            .map(ProgramStatus)?)
296    }
297
298    fn debug_cause(&mut self) -> Result<DebugCause, Error> {
299        Ok(self.interface.read_register::<DebugCause>()?)
300    }
301
302    fn spill_registers(&mut self) -> Result<(), Error> {
303        if self.state.spilled {
304            return Ok(());
305        }
306        self.state.spilled = true;
307
308        let register_file = RegisterFile::read(
309            self.interface.core_properties().window_option_properties,
310            &mut self.interface,
311        )?;
312
313        let window_reg_count = register_file.core.window_regs;
314        for reg in 0..window_reg_count {
315            let reg =
316                CpuRegister::try_from(reg).expect("Could not convert register to CpuRegister");
317            let value = register_file.read_register(reg);
318            self.interface
319                .state
320                .register_cache
321                .store(Register::Cpu(reg), value);
322        }
323
324        if self.current_ps()?.excm() {
325            // We are in an exception, possibly WindowOverflowN or WindowUnderflowN.
326            // We can't spill registers in this state.
327            return Ok(());
328        }
329        if self.current_ps()?.woe() {
330            // We should only spill registers if PS.WOE is set. According to the debug guide, we
331            // also should not spill if INTLEVEL != 0 but I don't see why.
332            register_file.spill(&mut self.interface)?;
333        }
334
335        Ok(())
336    }
337}
338
339// We can't use CoreMemoryInterface here, because we need to spill registers before reading.
340// This needs to be considerably cleaned up.
341impl MemoryInterface for Xtensa<'_> {
342    fn supports_native_64bit_access(&mut self) -> bool {
343        self.interface.supports_native_64bit_access()
344    }
345
346    fn read_word_64(&mut self, address: u64) -> Result<u64, Error> {
347        self.halted_access(|this| {
348            this.spill_registers()?;
349
350            this.interface.read_word_64(address)
351        })
352    }
353
354    fn read_word_32(&mut self, address: u64) -> Result<u32, Error> {
355        self.halted_access(|this| {
356            this.spill_registers()?;
357
358            this.interface.read_word_32(address)
359        })
360    }
361
362    fn read_word_16(&mut self, address: u64) -> Result<u16, Error> {
363        self.halted_access(|this| {
364            this.spill_registers()?;
365
366            this.interface.read_word_16(address)
367        })
368    }
369
370    fn read_word_8(&mut self, address: u64) -> Result<u8, Error> {
371        self.halted_access(|this| {
372            this.spill_registers()?;
373
374            this.interface.read_word_8(address)
375        })
376    }
377
378    fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), Error> {
379        self.read_8(address, data.as_mut_bytes())
380    }
381
382    fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), Error> {
383        self.read_8(address, data.as_mut_bytes())
384    }
385
386    fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), Error> {
387        self.read_8(address, data.as_mut_bytes())
388    }
389
390    fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), Error> {
391        self.halted_access(|this| {
392            this.spill_registers()?;
393
394            this.interface.read_8(address, data)
395        })
396    }
397
398    fn write_word_64(&mut self, address: u64, data: u64) -> Result<(), Error> {
399        self.interface.write_word_64(address, data)
400    }
401
402    fn write_word_32(&mut self, address: u64, data: u32) -> Result<(), Error> {
403        self.interface.write_word_32(address, data)
404    }
405
406    fn write_word_16(&mut self, address: u64, data: u16) -> Result<(), Error> {
407        self.interface.write_word_16(address, data)
408    }
409
410    fn write_word_8(&mut self, address: u64, data: u8) -> Result<(), Error> {
411        self.interface.write_word_8(address, data)
412    }
413
414    fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), Error> {
415        self.interface.write_64(address, data)
416    }
417
418    fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), Error> {
419        self.interface.write_32(address, data)
420    }
421
422    fn write_16(&mut self, address: u64, data: &[u16]) -> Result<(), Error> {
423        self.interface.write_16(address, data)
424    }
425
426    fn write_8(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
427        self.interface.write_8(address, data)
428    }
429
430    fn write(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
431        self.interface.write(address, data)
432    }
433
434    fn supports_8bit_transfers(&self) -> Result<bool, Error> {
435        self.interface.supports_8bit_transfers()
436    }
437
438    fn flush(&mut self) -> Result<(), Error> {
439        self.interface.flush()
440    }
441}
442
443impl CoreInterface for Xtensa<'_> {
444    fn wait_for_core_halted(&mut self, timeout: Duration) -> Result<(), Error> {
445        self.interface.wait_for_core_halted(timeout)?;
446        self.on_halted()?;
447
448        Ok(())
449    }
450
451    fn core_halted(&mut self) -> Result<bool, Error> {
452        let was_halted = self.interface.state.is_halted;
453        let is_halted = self.interface.core_halted()?;
454
455        if !was_halted && is_halted {
456            self.on_halted()?;
457        }
458
459        Ok(is_halted)
460    }
461
462    fn status(&mut self) -> Result<CoreStatus, Error> {
463        let status = if self.core_halted()? {
464            let debug_cause = self.debug_cause()?;
465
466            let mut reason = debug_cause.halt_reason();
467            if reason == HaltReason::Breakpoint(BreakpointCause::Software) {
468                // The chip initiated this halt, therefore we need to update pc_written state
469                self.state.pc_written = false;
470                // Check if the breakpoint is a semihosting call
471                if let Some(cmd) = self.check_for_semihosting()? {
472                    reason = HaltReason::Breakpoint(BreakpointCause::Semihosting(cmd));
473                }
474            }
475
476            CoreStatus::Halted(reason)
477        } else {
478            CoreStatus::Running
479        };
480
481        Ok(status)
482    }
483
484    fn halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error> {
485        self.halt_with_previous(timeout)?;
486
487        self.core_info()
488    }
489
490    fn run(&mut self) -> Result<(), Error> {
491        self.skip_breakpoint()?;
492        Ok(self.interface.resume_core()?)
493    }
494
495    fn reset(&mut self) -> Result<(), Error> {
496        self.reset_and_halt(Duration::from_millis(500))?;
497
498        self.run()
499    }
500
501    fn reset_and_halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error> {
502        self.state.semihosting_command = None;
503        self.sequence
504            .reset_system_and_halt(&mut self.interface, timeout)?;
505        self.on_halted()?;
506
507        // TODO: this may return that the core has gone away, which is fine but currently unexpected
508        self.on_attach()?;
509
510        self.core_info()
511    }
512
513    fn step(&mut self) -> Result<CoreInformation, Error> {
514        self.skip_breakpoint()?;
515
516        // Only count instructions in the current context.
517        let ps = self.current_ps()?;
518        self.interface.step(1, ps.intlevel())?;
519
520        self.on_halted()?;
521
522        self.core_info()
523    }
524
525    fn read_core_reg(&mut self, address: RegisterId) -> Result<RegisterValue, Error> {
526        self.halted_access(|this| {
527            let register = Register::try_from(address)?;
528            let value = this.interface.read_register_untyped(register)?;
529
530            Ok(RegisterValue::U32(value))
531        })
532    }
533
534    fn write_core_reg(&mut self, address: RegisterId, value: RegisterValue) -> Result<(), Error> {
535        self.halted_access(|this| {
536            let value: u32 = value.try_into()?;
537
538            if address == this.program_counter().id {
539                this.state.pc_written = true;
540            }
541
542            let register = Register::try_from(address)?;
543            this.interface.write_register_untyped(register, value)?;
544
545            Ok(())
546        })
547    }
548
549    fn available_breakpoint_units(&mut self) -> Result<u32, Error> {
550        Ok(self.interface.available_breakpoint_units())
551    }
552
553    fn hw_breakpoints(&mut self) -> Result<Vec<Option<u64>>, Error> {
554        self.halted_access(|this| {
555            let mut breakpoints = Vec::with_capacity(this.available_breakpoint_units()? as usize);
556
557            let enabled_breakpoints = this.interface.read_register::<IBreakEn>()?;
558
559            for i in 0..this.available_breakpoint_units()? as usize {
560                let is_enabled = enabled_breakpoints.0 & (1 << i) != 0;
561                let breakpoint = if is_enabled {
562                    let address = this
563                        .interface
564                        .read_register_untyped(Self::IBREAKA_REGS[i])?;
565
566                    Some(address as u64)
567                } else {
568                    None
569                };
570
571                breakpoints.push(breakpoint);
572            }
573
574            Ok(breakpoints)
575        })
576    }
577
578    fn enable_breakpoints(&mut self, state: bool) -> Result<(), Error> {
579        self.halted_access(|this| {
580            this.state.breakpoints_enabled = state;
581            let mask = if state {
582                this.state.breakpoint_mask()
583            } else {
584                0
585            };
586
587            this.interface.write_register(IBreakEn(mask))?;
588
589            Ok(())
590        })
591    }
592
593    fn set_hw_breakpoint(&mut self, unit_index: usize, addr: u64) -> Result<(), Error> {
594        self.halted_access(|this| {
595            this.state.breakpoint_set[unit_index] = true;
596            this.interface
597                .write_register_untyped(Self::IBREAKA_REGS[unit_index], addr as u32)?;
598
599            if this.state.breakpoints_enabled {
600                let mask = this.state.breakpoint_mask();
601                this.interface.write_register(IBreakEn(mask))?;
602            }
603
604            Ok(())
605        })
606    }
607
608    fn clear_hw_breakpoint(&mut self, unit_index: usize) -> Result<(), Error> {
609        self.halted_access(|this| {
610            this.state.breakpoint_set[unit_index] = false;
611
612            if this.state.breakpoints_enabled {
613                let mask = this.state.breakpoint_mask();
614                this.interface.write_register(IBreakEn(mask))?;
615            }
616
617            Ok(())
618        })
619    }
620
621    fn registers(&self) -> &'static CoreRegisters {
622        &XTENSA_CORE_REGISTERS
623    }
624
625    fn program_counter(&self) -> &'static CoreRegister {
626        &PC
627    }
628
629    fn frame_pointer(&self) -> &'static CoreRegister {
630        &FP
631    }
632
633    fn stack_pointer(&self) -> &'static CoreRegister {
634        &SP
635    }
636
637    fn return_address(&self) -> &'static CoreRegister {
638        &RA
639    }
640
641    fn hw_breakpoints_enabled(&self) -> bool {
642        self.state.breakpoints_enabled
643    }
644
645    fn architecture(&self) -> Architecture {
646        Architecture::Xtensa
647    }
648
649    fn core_type(&self) -> CoreType {
650        CoreType::Xtensa
651    }
652
653    fn instruction_set(&mut self) -> Result<InstructionSet, Error> {
654        // TODO: NX exists, too
655        Ok(InstructionSet::Xtensa)
656    }
657
658    fn fpu_support(&mut self) -> Result<bool, Error> {
659        // TODO: ESP32 and ESP32-S3 have FPU
660        Ok(false)
661    }
662
663    fn floating_point_register_count(&mut self) -> Result<usize, Error> {
664        // TODO: ESP32 and ESP32-S3 have FPU
665        Ok(0)
666    }
667
668    fn reset_catch_set(&mut self) -> Result<(), Error> {
669        Err(Error::NotImplemented("reset_catch_set"))
670    }
671
672    fn reset_catch_clear(&mut self) -> Result<(), Error> {
673        Err(Error::NotImplemented("reset_catch_clear"))
674    }
675
676    fn debug_core_stop(&mut self) -> Result<(), Error> {
677        self.interface.leave_debug_mode()?;
678        Ok(())
679    }
680}
681
682struct RegisterFile {
683    core: WindowProperties,
684    registers: Vec<u32>,
685    window_base: u8,
686    window_start: u32,
687}
688
689impl RegisterFile {
690    fn read(
691        xtensa: WindowProperties,
692        interface: &mut XtensaCommunicationInterface<'_>,
693    ) -> Result<Self, Error> {
694        let window_base_result = interface.schedule_read_register(SpecialRegister::Windowbase)?;
695        let window_start_result = interface.schedule_read_register(SpecialRegister::Windowstart)?;
696
697        let mut register_results = Vec::with_capacity(xtensa.num_aregs as usize);
698
699        let ar0 = arch::CpuRegister::A0 as u8;
700
701        // Restore registers before reading them, as reading a special
702        // register overwrote scratch registers.
703        interface.restore_registers()?;
704        // The window registers alias each other, so we need to make sure we don't read
705        // the cached values. We'll then use the registers we read here to prime the cache.
706        for ar in ar0..ar0 + xtensa.window_regs {
707            let reg = CpuRegister::try_from(ar)?;
708            interface.state.register_cache.remove(reg.into());
709        }
710
711        for _ in 0..xtensa.num_aregs / xtensa.window_regs {
712            // Read registers visible in the current window
713            for ar in ar0..ar0 + xtensa.window_regs {
714                let reg = CpuRegister::try_from(ar)?;
715                let result = interface.schedule_read_register(reg)?;
716
717                register_results.push(result);
718            }
719
720            // Rotate window to see the next `window_regs` registers
721            let rotw_arg = xtensa.window_regs / xtensa.rotw_rotates;
722            interface
723                .xdm
724                .schedule_execute_instruction(Instruction::Rotw(rotw_arg));
725        }
726
727        // Now do the actual read.
728        interface
729            .xdm
730            .execute()
731            .expect("Failed to execute read. This shouldn't happen.");
732
733        let mut register_values = register_results
734            .into_iter()
735            .map(|result| interface.read_deferred_result(result))
736            .collect::<Result<Vec<_>, _>>()?;
737
738        // WindowBase points to the first register of the current window in the register file.
739        // In essence, it selects which 16 registers are visible out of the 64 physical registers.
740        let window_base = interface.read_deferred_result(window_base_result)?;
741
742        // The WindowStart Special Register, which is also added by the option and consists of
743        // NAREG/4 bits. Each call frame, which has not been spilled, is represented by a bit in the
744        // WindowStart register. The call frame's bit is set in the position given by the current
745        // WindowBase register value.
746        // Each register window has a single bit in the WindowStart register. The window size
747        // can be calculated by the difference of bit positions in the WindowStart register.
748        // For example, if WindowBase is 6, and WindowStart is 0b0000000001011001:
749        // - The first window uses a0-a11, and executed a CALL12 or CALLX12 instruction.
750        // - The second window uses a0-a3, and executed a CALL14 instruction.
751        // - The third window uses a0-a7, and executed a CALL8 instruction.
752        // - The fourth window is free to use all 16 registers at this point.
753        // There are no more active windows.
754        // This value is used by the hardware to determine if WindowOverflow or WindowUnderflow
755        // exceptions should be raised. The exception handlers then spill or reload registers
756        // from the stack and set/clear the corresponding bit in the WindowStart register.
757        let window_start = interface.read_deferred_result(window_start_result)?;
758
759        // We have read registers relative to the current windowbase. Let's
760        // rotate the registers back so that AR0 is at index 0.
761        register_values.rotate_right((window_base * xtensa.rotw_rotates as u32) as usize);
762
763        Ok(Self {
764            core: xtensa,
765            registers: register_values,
766            window_base: window_base as u8,
767            window_start,
768        })
769    }
770
771    fn spill(&self, xtensa: &mut XtensaCommunicationInterface<'_>) -> Result<(), Error> {
772        if !self.core.has_windowed_registers {
773            return Ok(());
774        }
775
776        if self.window_start == 0 {
777            // There are no stack frames to spill.
778            return Ok(());
779        }
780
781        // Quoting the debug guide:
782        // The proper thing for the debugger to do when doing a call traceback or whenever looking
783        // at a calling function's address registers, is to examine the WINDOWSTART register bits
784        // (in combination with WINDOWBASE) to determine whether to look in the register file or on
785        // the stack for the relevant address register values.
786        // Quote ends here
787        // The above describes what we should do for minimally intrusive debugging, but I don't
788        // think we have the option to do this. Instead we spill everything that needs to be
789        // spilled, and then we can use the stack to unwind the registers. While forcefully
790        // spilling is not faithful to the true code execution, it is relatively simple to do.
791        // The debug guide states this can be noticeably slow, for example if "step to next line" is
792        // implemented by stepping one instruction at a time until the line number changes.
793        // FIXME: we can improve the above issue by only spilling before reading memory.
794
795        // Process registers. We need to roll the windows back by the window increment, and copy
796        // register values to the stack, if the relevant WindowStart bit is set. The window
797        // increment of the current window is saved in the top 2 bits of A0 (the return address).
798
799        // Find oldest window.
800        let mut window_base = self.next_window_base(self.window_base);
801
802        while let Some(window) = RegisterWindow::at_windowbase(self, window_base) {
803            window.spill(xtensa)?;
804            window_base = self.next_window_base(window_base);
805
806            if window_base == self.window_base {
807                // We are back to the original window, so we can stop.
808
809                // We are not spilling the first window. We don't have a destination for it, and we don't
810                // need to unwind it from the stack.
811                break;
812            }
813        }
814
815        Ok(())
816    }
817
818    fn is_window_start(&self, windowbase: u8) -> bool {
819        self.window_start & 1 << (windowbase % self.core.windowbase_size()) != 0
820    }
821
822    fn next_window_base(&self, window_base: u8) -> u8 {
823        let mut wb = (window_base + 1) % self.core.windowbase_size();
824        while wb != window_base {
825            if self.is_window_start(wb) {
826                break;
827            }
828            wb = (wb + 1) % self.core.windowbase_size();
829        }
830
831        wb
832    }
833
834    fn wb_offset_to_canonical(&self, idx: u8) -> u8 {
835        (idx + self.window_base * self.core.rotw_rotates) % self.core.num_aregs
836    }
837
838    fn read_register(&self, reg: CpuRegister) -> u32 {
839        let index = self.wb_offset_to_canonical(reg as u8);
840        self.registers[index as usize]
841    }
842}
843
844struct RegisterWindow<'a> {
845    window_base: u8,
846
847    /// In units of window_base bits.
848    window_size: u8,
849
850    file: &'a RegisterFile,
851}
852
853impl<'a> RegisterWindow<'a> {
854    fn at_windowbase(file: &'a RegisterFile, window_base: u8) -> Option<Self> {
855        if !file.is_window_start(window_base) {
856            return None;
857        }
858
859        let next_window_base = file.next_window_base(window_base);
860        let window_size = (next_window_base + file.core.windowbase_size() - window_base)
861            % file.core.windowbase_size();
862
863        Some(Self {
864            window_base,
865            file,
866            window_size: window_size.min(3),
867        })
868    }
869
870    /// Register spilling needs access to other frames' stack pointers.
871    fn read_register(&self, reg: CpuRegister) -> u32 {
872        let index = self.wb_offset_to_canonical(reg as u8);
873        self.file.registers[index as usize]
874    }
875
876    fn wb_offset_to_canonical(&self, idx: u8) -> u8 {
877        (idx + self.window_base * self.file.core.rotw_rotates) % self.file.core.num_aregs
878    }
879
880    fn spill(&self, interface: &mut XtensaCommunicationInterface<'_>) -> Result<(), Error> {
881        // a0-a3 goes into our stack, the rest into the stack of the caller.
882        let a0_a3 = [
883            self.read_register(CpuRegister::A0),
884            self.read_register(CpuRegister::A1),
885            self.read_register(CpuRegister::A2),
886            self.read_register(CpuRegister::A3),
887        ];
888
889        match self.window_size {
890            0 => {} // Nowhere to spill to
891
892            1 => interface.write_32(self.read_register(CpuRegister::A5) as u64 - 16, &a0_a3)?,
893
894            // Spill a4-a7
895            2 => {
896                let sp = interface.read_word_32(self.read_register(CpuRegister::A1) as u64 - 12)?;
897
898                interface.write_32(self.read_register(CpuRegister::A9) as u64 - 16, &a0_a3)?;
899
900                // Enable check at INFO level to avoid spamming the logs.
901                if tracing::enabled!(tracing::Level::INFO) {
902                    // In some cases (spilling on each halt),
903                    // this readback comes back as 0 for some reason. This assertion is temporarily
904                    // meant to help me debug this.
905                    let written =
906                        interface.read_word_32(self.read_register(CpuRegister::A9) as u64 - 12)?;
907                    assert!(
908                        written == self.read_register(CpuRegister::A1),
909                        "Failed to spill A1. Expected {:#x}, got {:#x}",
910                        self.read_register(CpuRegister::A1),
911                        written
912                    );
913                }
914
915                let regs = [
916                    self.read_register(CpuRegister::A4),
917                    self.read_register(CpuRegister::A5),
918                    self.read_register(CpuRegister::A6),
919                    self.read_register(CpuRegister::A7),
920                ];
921                interface.write_32(sp as u64 - 32, &regs)?;
922            }
923
924            // Spill a4-a11
925            3 => {
926                let sp = interface.read_word_32(self.read_register(CpuRegister::A1) as u64 - 12)?;
927                interface.write_32(self.read_register(CpuRegister::A13) as u64 - 16, &a0_a3)?;
928
929                let regs = [
930                    self.read_register(CpuRegister::A4),
931                    self.read_register(CpuRegister::A5),
932                    self.read_register(CpuRegister::A6),
933                    self.read_register(CpuRegister::A7),
934                    self.read_register(CpuRegister::A8),
935                    self.read_register(CpuRegister::A9),
936                    self.read_register(CpuRegister::A10),
937                    self.read_register(CpuRegister::A11),
938                ];
939                interface.write_32(sp as u64 - 48, &regs)?;
940            }
941
942            // There is no such thing as spilling a12-a15 - there can be only 12 active registers in
943            // a stack frame that is not the topmost, as there is no CALL16 instruction.
944            _ => unreachable!(),
945        }
946
947        Ok(())
948    }
949}