esp_hal/
assist_debug.rs

1//! # Debug Assistant (ASSIST_DEBUG)
2//!
3//! ## Overview
4//! Debug Assistant is an auxiliary module that features a set of functions to
5//! help locate bugs and issues during software debugging. It includes
6//! capabilities such as monitoring stack pointer (SP), monitoring memory
7//! regions, and handling interrupts related to debugging.
8//!
9//!
10//! ## Configuration
11//! While all the targets support program counter (PC) logging it's API is not
12//! exposed here. Instead the ROM bootloader will always enable it and print the
13//! last seen PC (e.g. _Saved PC:0x42002ff2_). Make sure the reset was triggered
14//! by a TIMG watchdog. Not an RTC or SWD watchdog.
15//!
16//! ## Examples
17//! Visit the [Debug Assist] example for an example of using the Debug
18//! Assistant.
19//!
20//! [Debug Assist]: https://github.com/esp-rs/esp-hal/blob/main/examples/peripheral/debug_assist/src/main.rs
21//!
22//! ## Implementation State
23//! - Bus write access logging is not available via this API
24//! - This driver has only blocking API
25
26use crate::{
27    interrupt::InterruptHandler,
28    pac,
29    peripherals::{ASSIST_DEBUG, Interrupt},
30};
31
32/// The debug assist driver instance.
33pub struct DebugAssist<'d> {
34    debug_assist: ASSIST_DEBUG<'d>,
35}
36
37impl<'d> DebugAssist<'d> {
38    /// Create a new instance in [crate::Blocking] mode.
39    pub fn new(debug_assist: ASSIST_DEBUG<'d>) -> Self {
40        // NOTE: We should enable the debug assist, however, it's always enabled in ROM
41        //       code already.
42
43        DebugAssist { debug_assist }
44    }
45
46    /// Register an interrupt handler for the Debug Assist module.
47    ///
48    /// Note that this will replace any previously registered interrupt
49    /// handlers.
50    #[instability::unstable]
51    pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
52        for core in crate::system::Cpu::other() {
53            crate::interrupt::disable(core, Interrupt::ASSIST_DEBUG);
54        }
55        unsafe { crate::interrupt::bind_interrupt(Interrupt::ASSIST_DEBUG, handler.handler()) };
56        unwrap!(crate::interrupt::enable(
57            Interrupt::ASSIST_DEBUG,
58            handler.priority()
59        ));
60    }
61
62    fn regs(&self) -> &pac::assist_debug::RegisterBlock {
63        self.debug_assist.register_block()
64    }
65}
66
67impl crate::private::Sealed for DebugAssist<'_> {}
68
69#[instability::unstable]
70impl crate::interrupt::InterruptConfigurable for DebugAssist<'_> {
71    fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
72        self.set_interrupt_handler(handler);
73    }
74}
75
76#[cfg(assist_debug_has_sp_monitor)]
77impl DebugAssist<'_> {
78    /// Enable SP monitoring on main core. When the SP exceeds the
79    /// `lower_bound` or `upper_bound` threshold, the module will record the PC
80    /// pointer and generate an interrupt.
81    pub fn internal_sp_monitor(&mut self, cpu: usize, lower_bound: u32, upper_bound: u32) {
82        let regs = self.regs().cpu(cpu);
83
84        regs.sp_min()
85            .write(|w| unsafe { w.sp_min().bits(lower_bound) });
86
87        regs.sp_max()
88            .write(|w| unsafe { w.sp_max().bits(upper_bound) });
89
90        regs.montr_ena().modify(|_, w| {
91            w.sp_spill_min_ena().set_bit();
92            w.sp_spill_max_ena().set_bit()
93        });
94
95        regs.intr_clr().write(|w| {
96            w.sp_spill_max_clr().set_bit();
97            w.sp_spill_min_clr().set_bit()
98        });
99
100        regs.intr_ena().modify(|_, w| {
101            w.sp_spill_max_intr_ena().set_bit();
102            w.sp_spill_min_intr_ena().set_bit()
103        });
104    }
105
106    fn internal_disable_sp_monitor(&mut self, cpu: usize) {
107        let regs = self.regs().cpu(cpu);
108
109        regs.intr_ena().modify(|_, w| {
110            w.sp_spill_max_intr_ena().clear_bit();
111            w.sp_spill_min_intr_ena().clear_bit()
112        });
113
114        regs.montr_ena().modify(|_, w| {
115            w.sp_spill_min_ena().clear_bit();
116            w.sp_spill_max_ena().clear_bit()
117        });
118    }
119
120    fn internal_clear_sp_monitor_interrupt(&mut self, cpu: usize) {
121        self.regs().cpu(cpu).intr_clr().write(|w| {
122            w.sp_spill_max_clr().set_bit();
123            w.sp_spill_min_clr().set_bit()
124        });
125    }
126
127    fn internal_is_sp_monitor_interrupt_set(&self, cpu: usize) -> bool {
128        let regs = self.regs().cpu(cpu);
129        let intrs = regs.intr_raw().read();
130
131        intrs.sp_spill_max_raw().bit_is_set() || intrs.sp_spill_min_raw().bit_is_set()
132    }
133
134    fn internal_sp_monitor_pc(&self, cpu: usize) -> u32 {
135        self.regs().cpu(cpu).sp_pc().read().sp_pc().bits()
136    }
137
138    /// Enable SP monitoring on main core. When the SP exceeds the
139    /// `lower_bound` or `upper_bound` threshold, the module will record the PC
140    /// pointer and generate an interrupt.
141    pub fn enable_sp_monitor(&mut self, lower_bound: u32, upper_bound: u32) {
142        self.internal_sp_monitor(0, lower_bound, upper_bound);
143    }
144
145    /// Disable SP monitoring on main core.
146    pub fn disable_sp_monitor(&mut self) {
147        self.internal_disable_sp_monitor(0)
148    }
149
150    /// Clear SP monitoring interrupt on main core.
151    pub fn clear_sp_monitor_interrupt(&mut self) {
152        self.internal_clear_sp_monitor_interrupt(0)
153    }
154
155    /// Check, if SP monitoring interrupt is set on main core.
156    pub fn is_sp_monitor_interrupt_set(&self) -> bool {
157        self.internal_is_sp_monitor_interrupt_set(0)
158    }
159
160    /// Get SP monitoring PC value on main core.
161    pub fn sp_monitor_pc(&self) -> u32 {
162        self.internal_sp_monitor_pc(0)
163    }
164}
165
166#[cfg(all(assist_debug_has_sp_monitor, multi_core))]
167impl<'d> DebugAssist<'d> {
168    /// Enable SP monitoring on secondary core. When the SP exceeds the
169    /// `lower_bound` or `upper_bound` threshold, the module will record the PC
170    /// pointer and generate an interrupt.
171    pub fn enable_core1_sp_monitor(&mut self, lower_bound: u32, upper_bound: u32) {
172        self.internal_sp_monitor(1, lower_bound, upper_bound);
173    }
174
175    /// Disable SP monitoring on secondary core.
176    pub fn disable_core1_sp_monitor(&mut self) {
177        self.internal_disable_sp_monitor(1)
178    }
179
180    /// Clear SP monitoring interrupt on secondary core.
181    pub fn clear_core1_sp_monitor_interrupt(&mut self) {
182        self.internal_clear_sp_monitor_interrupt(1)
183    }
184
185    /// Check, if SP monitoring interrupt is set on secondary core.
186    pub fn is_core1_sp_monitor_interrupt_set(&self) -> bool {
187        self.internal_is_sp_monitor_interrupt_set(1)
188    }
189
190    /// Get SP monitoring PC value on secondary core.
191    pub fn core1_sp_monitor_pc(&self) -> u32 {
192        self.internal_sp_monitor_pc(1)
193    }
194}
195
196#[cfg(assist_debug_has_region_monitor)]
197impl DebugAssist<'_> {
198    fn internal_enable_region0_monitor(
199        &mut self,
200        cpu: usize,
201        lower_bound: u32,
202        upper_bound: u32,
203        reads: bool,
204        writes: bool,
205    ) {
206        let regs = self.regs().cpu(cpu);
207
208        regs.area_dram0_0_min()
209            .write(|w| unsafe { w.area_dram0_0_min().bits(lower_bound) });
210
211        regs.area_dram0_0_max()
212            .write(|w| unsafe { w.area_dram0_0_max().bits(upper_bound) });
213
214        regs.montr_ena().modify(|_, w| {
215            w.area_dram0_0_rd_ena().bit(reads);
216            w.area_dram0_0_wr_ena().bit(writes)
217        });
218
219        regs.intr_clr().write(|w| {
220            w.area_dram0_0_rd_clr().set_bit();
221            w.area_dram0_0_wr_clr().set_bit()
222        });
223
224        regs.intr_ena().modify(|_, w| {
225            w.area_dram0_0_rd_intr_ena().set_bit();
226            w.area_dram0_0_wr_intr_ena().set_bit()
227        });
228    }
229
230    fn internal_disable_region0_monitor(&mut self, cpu: usize) {
231        let regs = self.regs().cpu(cpu);
232
233        regs.intr_ena().modify(|_, w| {
234            w.area_dram0_0_rd_intr_ena().clear_bit();
235            w.area_dram0_0_wr_intr_ena().clear_bit()
236        });
237
238        regs.montr_ena().modify(|_, w| {
239            w.area_dram0_0_rd_ena().clear_bit();
240            w.area_dram0_0_wr_ena().clear_bit()
241        });
242    }
243
244    fn internal_clear_region0_monitor_interrupt(&mut self, cpu: usize) {
245        self.regs().cpu(cpu).intr_clr().write(|w| {
246            w.area_dram0_0_rd_clr().set_bit();
247            w.area_dram0_0_wr_clr().set_bit()
248        });
249    }
250
251    fn internal_is_region0_monitor_interrupt_set(&self, cpu: usize) -> bool {
252        let regs = self.regs().cpu(cpu);
253        let intrs = regs.intr_raw().read();
254
255        intrs.area_dram0_0_rd_raw().bit_is_set() || intrs.area_dram0_0_wr_raw().bit_is_set()
256    }
257
258    fn internal_enable_region1_monitor(
259        &mut self,
260        cpu: usize,
261        lower_bound: u32,
262        upper_bound: u32,
263        reads: bool,
264        writes: bool,
265    ) {
266        let regs = self.regs().cpu(cpu);
267
268        regs.area_dram0_1_min()
269            .write(|w| unsafe { w.area_dram0_1_min().bits(lower_bound) });
270
271        regs.area_dram0_1_max()
272            .write(|w| unsafe { w.area_dram0_1_max().bits(upper_bound) });
273
274        regs.montr_ena().modify(|_, w| {
275            w.area_dram0_1_rd_ena().bit(reads);
276            w.area_dram0_1_wr_ena().bit(writes)
277        });
278
279        regs.intr_clr().write(|w| {
280            w.area_dram0_1_rd_clr().set_bit();
281            w.area_dram0_1_wr_clr().set_bit()
282        });
283
284        regs.intr_ena().modify(|_, w| {
285            w.area_dram0_1_rd_intr_ena().set_bit();
286            w.area_dram0_1_wr_intr_ena().set_bit()
287        });
288    }
289
290    fn internal_disable_region1_monitor(&mut self, cpu: usize) {
291        let regs = self.regs().cpu(cpu);
292
293        regs.intr_ena().modify(|_, w| {
294            w.area_dram0_1_rd_intr_ena().clear_bit();
295            w.area_dram0_1_wr_intr_ena().clear_bit()
296        });
297
298        regs.montr_ena().modify(|_, w| {
299            w.area_dram0_1_rd_ena().clear_bit();
300            w.area_dram0_1_wr_ena().clear_bit()
301        });
302    }
303
304    fn internal_clear_region1_monitor_interrupt(&mut self, cpu: usize) {
305        self.regs().cpu(cpu).intr_clr().write(|w| {
306            w.area_dram0_1_rd_clr().set_bit();
307            w.area_dram0_1_wr_clr().set_bit()
308        });
309    }
310
311    fn internal_is_region1_monitor_interrupt_set(&self, cpu: usize) -> bool {
312        let regs = self.regs().cpu(cpu);
313        let intrs = regs.intr_raw().read();
314
315        intrs.area_dram0_1_rd_raw().bit_is_set() || intrs.area_dram0_1_wr_raw().bit_is_set()
316    }
317
318    fn internal_region_monitor_pc(&self, cpu: usize) -> u32 {
319        self.regs().cpu(cpu).area_pc().read().area_pc().bits()
320    }
321
322    /// Enable region monitoring of read/write performed by the main CPU in a
323    /// certain memory region0. Whenever the bus reads or writes in the
324    /// specified memory region, an interrupt will be triggered. Two memory
325    /// regions (region0, region1) can be monitored at the same time.
326    pub fn enable_region0_monitor(
327        &mut self,
328        lower_bound: u32,
329        upper_bound: u32,
330        reads: bool,
331        writes: bool,
332    ) {
333        self.internal_enable_region0_monitor(0, lower_bound, upper_bound, reads, writes)
334    }
335
336    /// Disable region0 monitoring on main core.
337    pub fn disable_region0_monitor(&mut self) {
338        self.internal_disable_region0_monitor(0)
339    }
340
341    /// Clear region0 monitoring interrupt on main core.
342    pub fn clear_region0_monitor_interrupt(&mut self) {
343        self.internal_clear_region0_monitor_interrupt(0)
344    }
345
346    /// Check, if region0 monitoring interrupt is set on main core.
347    pub fn is_region0_monitor_interrupt_set(&self) -> bool {
348        self.internal_is_region0_monitor_interrupt_set(0)
349    }
350
351    /// Enable region monitoring of read/write performed by the main CPU in a
352    /// certain memory region1. Whenever the bus reads or writes in the
353    /// specified memory region, an interrupt will be triggered.
354    pub fn enable_region1_monitor(
355        &mut self,
356        lower_bound: u32,
357        upper_bound: u32,
358        reads: bool,
359        writes: bool,
360    ) {
361        self.internal_enable_region1_monitor(0, lower_bound, upper_bound, reads, writes)
362    }
363
364    /// Disable region1 monitoring on main core.
365    pub fn disable_region1_monitor(&mut self) {
366        self.internal_disable_region1_monitor(0)
367    }
368
369    /// Clear region1 monitoring interrupt on main core.
370    pub fn clear_region1_monitor_interrupt(&mut self) {
371        self.internal_clear_region1_monitor_interrupt(0)
372    }
373
374    /// Check, if region1 monitoring interrupt is set on main core.
375    pub fn is_region1_monitor_interrupt_set(&self) -> bool {
376        self.internal_is_region1_monitor_interrupt_set(0)
377    }
378
379    /// Get region monitoring PC value on main core.
380    pub fn region_monitor_pc(&self) -> u32 {
381        self.internal_region_monitor_pc(0)
382    }
383}
384
385#[cfg(all(assist_debug_has_region_monitor, multi_core))]
386impl DebugAssist<'_> {
387    /// Enable region monitoring of read/write performed by the secondary CPU in
388    /// a certain memory region0. Whenever the bus reads or writes in the
389    /// specified memory region, an interrupt will be triggered.
390    pub fn enable_core1_region0_monitor(
391        &mut self,
392        lower_bound: u32,
393        upper_bound: u32,
394        reads: bool,
395        writes: bool,
396    ) {
397        self.internal_enable_region0_monitor(1, lower_bound, upper_bound, reads, writes)
398    }
399
400    /// Disable region0 monitoring on secondary core.
401    pub fn disable_core1_region0_monitor(&mut self) {
402        self.internal_disable_region0_monitor(1)
403    }
404
405    /// Clear region0 monitoring interrupt on secondary core.
406    pub fn clear_core1_region0_monitor_interrupt(&mut self) {
407        self.internal_clear_region0_monitor_interrupt(1)
408    }
409
410    /// Check, if region0 monitoring interrupt is set on secondary core.
411    pub fn is_core1_region0_monitor_interrupt_set(&self) -> bool {
412        self.internal_is_region0_monitor_interrupt_set(1)
413    }
414
415    /// Enable region monitoring of read/write performed by the secondary CPU in
416    /// a certain memory region1. Whenever the bus reads or writes in the
417    /// specified memory region, an interrupt will be triggered.
418    pub fn enable_core1_region1_monitor(
419        &mut self,
420        lower_bound: u32,
421        upper_bound: u32,
422        reads: bool,
423        writes: bool,
424    ) {
425        self.internal_enable_region1_monitor(1, lower_bound, upper_bound, reads, writes)
426    }
427
428    /// Disable region1 monitoring on secondary core.
429    pub fn disable_core1_region1_monitor(&mut self) {
430        self.internal_disable_region1_monitor(1)
431    }
432
433    /// Clear region1 monitoring interrupt on secondary core.
434    pub fn clear_core1_region1_monitor_interrupt(&mut self) {
435        self.internal_clear_region1_monitor_interrupt(1)
436    }
437
438    /// Check, if region1 monitoring interrupt is set on secondary core.
439    pub fn is_core1_region1_monitor_interrupt_set(&self) -> bool {
440        self.internal_is_region1_monitor_interrupt_set(1)
441    }
442
443    /// Get region monitoring PC value on secondary core.
444    pub fn core1_region_monitor_pc(&self) -> u32 {
445        self.internal_region_monitor_pc(1)
446    }
447}