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/src/bin/debug_assist.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_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 enable_sp_monitor(&mut self, lower_bound: u32, upper_bound: u32) {
82        self.regs()
83            .core_0_sp_min()
84            .write(|w| unsafe { w.core_0_sp_min().bits(lower_bound) });
85
86        self.regs()
87            .core_0_sp_max()
88            .write(|w| unsafe { w.core_0_sp_max().bits(upper_bound) });
89
90        self.regs().core_0_montr_ena().modify(|_, w| {
91            w.core_0_sp_spill_min_ena()
92                .set_bit()
93                .core_0_sp_spill_max_ena()
94                .set_bit()
95        });
96
97        self.clear_sp_monitor_interrupt();
98
99        self.regs().core_0_intr_ena().modify(|_, w| {
100            w.core_0_sp_spill_max_intr_ena()
101                .set_bit()
102                .core_0_sp_spill_min_intr_ena()
103                .set_bit()
104        });
105    }
106
107    /// Disable SP monitoring on main core.
108    pub fn disable_sp_monitor(&mut self) {
109        self.regs().core_0_intr_ena().modify(|_, w| {
110            w.core_0_sp_spill_max_intr_ena()
111                .clear_bit()
112                .core_0_sp_spill_min_intr_ena()
113                .clear_bit()
114        });
115
116        self.regs().core_0_montr_ena().modify(|_, w| {
117            w.core_0_sp_spill_min_ena()
118                .clear_bit()
119                .core_0_sp_spill_max_ena()
120                .clear_bit()
121        });
122    }
123
124    /// Clear SP monitoring interrupt on main core.
125    pub fn clear_sp_monitor_interrupt(&mut self) {
126        self.regs().core_0_intr_clr().write(|w| {
127            w.core_0_sp_spill_max_clr()
128                .set_bit()
129                .core_0_sp_spill_min_clr()
130                .set_bit()
131        });
132    }
133
134    /// Check, if SP monitoring interrupt is set on main core.
135    pub fn is_sp_monitor_interrupt_set(&self) -> bool {
136        self.regs()
137            .core_0_intr_raw()
138            .read()
139            .core_0_sp_spill_max_raw()
140            .bit_is_set()
141            || self
142                .regs()
143                .core_0_intr_raw()
144                .read()
145                .core_0_sp_spill_min_raw()
146                .bit_is_set()
147    }
148
149    /// Get SP monitoring PC value on main core.
150    pub fn sp_monitor_pc(&self) -> u32 {
151        self.regs().core_0_sp_pc().read().core_0_sp_pc().bits()
152    }
153}
154
155#[cfg(all(assist_debug_sp_monitor, multi_core))]
156impl<'d> DebugAssist<'d> {
157    /// Enable SP monitoring on secondary core. When the SP exceeds the
158    /// `lower_bound` or `upper_bound` threshold, the module will record the PC
159    /// pointer and generate an interrupt.
160    pub fn enable_core1_sp_monitor(&mut self, lower_bound: u32, upper_bound: u32) {
161        self.regs()
162            .core_1_sp_min
163            .write(|w| w.core_1_sp_min().bits(lower_bound));
164
165        self.regs()
166            .core_1_sp_max
167            .write(|w| w.core_1_sp_max().bits(upper_bound));
168
169        self.regs().core_1_montr_ena.modify(|_, w| {
170            w.core_1_sp_spill_min_ena()
171                .set_bit()
172                .core_1_sp_spill_max_ena()
173                .set_bit()
174        });
175
176        self.clear_core1_sp_monitor_interrupt();
177
178        self.regs().core_1_intr_ena.modify(|_, w| {
179            w.core_1_sp_spill_max_intr_ena()
180                .set_bit()
181                .core_1_sp_spill_min_intr_ena()
182                .set_bit()
183        });
184    }
185
186    /// Disable SP monitoring on secondary core.
187    pub fn disable_core1_sp_monitor(&mut self) {
188        self.regs().core_1_intr_ena.modify(|_, w| {
189            w.core_1_sp_spill_max_intr_ena()
190                .clear_bit()
191                .core_1_sp_spill_min_intr_ena()
192                .clear_bit()
193        });
194
195        self.regs().core_1_montr_ena.modify(|_, w| {
196            w.core_1_sp_spill_min_ena()
197                .clear_bit()
198                .core_1_sp_spill_max_ena()
199                .clear_bit()
200        });
201    }
202
203    /// Clear SP monitoring interrupt on secondary core.
204    pub fn clear_core1_sp_monitor_interrupt(&mut self) {
205        self.regs().core_1_intr_clr.write(|w| {
206            w.core_1_sp_spill_max_clr()
207                .set_bit()
208                .core_1_sp_spill_min_clr()
209                .set_bit()
210        });
211    }
212
213    /// Check, if SP monitoring interrupt is set on secondary core.
214    pub fn is_core1_sp_monitor_interrupt_set(&self) -> bool {
215        self.regs()
216            .core_1_intr_raw
217            .read()
218            .core_1_sp_spill_max_raw()
219            .bit_is_set()
220            || self
221                .regs()
222                .core_1_intr_raw
223                .read()
224                .core_1_sp_spill_min_raw()
225                .bit_is_set()
226    }
227
228    /// Get SP monitoring PC value on secondary core.
229    pub fn core1_sp_monitor_pc(&self) -> u32 {
230        self.regs().core_1_sp_pc.read().core_1_sp_pc().bits()
231    }
232}
233
234#[cfg(assist_debug_region_monitor)]
235impl DebugAssist<'_> {
236    /// Enable region monitoring of read/write performed by the main CPU in a
237    /// certain memory region0. Whenever the bus reads or writes in the
238    /// specified memory region, an interrupt will be triggered. Two memory
239    /// regions (region0, region1) can be monitored at the same time.
240    pub fn enable_region0_monitor(
241        &mut self,
242        lower_bound: u32,
243        upper_bound: u32,
244        reads: bool,
245        writes: bool,
246    ) {
247        self.regs()
248            .core_0_area_dram0_0_min()
249            .write(|w| unsafe { w.core_0_area_dram0_0_min().bits(lower_bound) });
250
251        self.regs()
252            .core_0_area_dram0_0_max()
253            .write(|w| unsafe { w.core_0_area_dram0_0_max().bits(upper_bound) });
254
255        self.regs().core_0_montr_ena().modify(|_, w| {
256            w.core_0_area_dram0_0_rd_ena()
257                .bit(reads)
258                .core_0_area_dram0_0_wr_ena()
259                .bit(writes)
260        });
261
262        self.clear_region0_monitor_interrupt();
263
264        self.regs().core_0_intr_ena().modify(|_, w| {
265            w.core_0_area_dram0_0_rd_intr_ena()
266                .set_bit()
267                .core_0_area_dram0_0_wr_intr_ena()
268                .set_bit()
269        });
270    }
271
272    /// Disable region0 monitoring on main core.
273    pub fn disable_region0_monitor(&mut self) {
274        self.regs().core_0_intr_ena().modify(|_, w| {
275            w.core_0_area_dram0_0_rd_intr_ena()
276                .clear_bit()
277                .core_0_area_dram0_0_wr_intr_ena()
278                .clear_bit()
279        });
280
281        self.regs().core_0_montr_ena().modify(|_, w| {
282            w.core_0_area_dram0_0_rd_ena()
283                .clear_bit()
284                .core_0_area_dram0_0_wr_ena()
285                .clear_bit()
286        });
287    }
288
289    /// Clear region0 monitoring interrupt on main core.
290    pub fn clear_region0_monitor_interrupt(&mut self) {
291        self.regs().core_0_intr_clr().write(|w| {
292            w.core_0_area_dram0_0_rd_clr()
293                .set_bit()
294                .core_0_area_dram0_0_wr_clr()
295                .set_bit()
296        });
297    }
298
299    /// Check, if region0 monitoring interrupt is set on main core.
300    pub fn is_region0_monitor_interrupt_set(&self) -> bool {
301        self.regs()
302            .core_0_intr_raw()
303            .read()
304            .core_0_area_dram0_0_rd_raw()
305            .bit_is_set()
306            || self
307                .regs()
308                .core_0_intr_raw()
309                .read()
310                .core_0_area_dram0_0_wr_raw()
311                .bit_is_set()
312    }
313
314    /// Enable region monitoring of read/write performed by the main CPU in a
315    /// certain memory region1. Whenever the bus reads or writes in the
316    /// specified memory region, an interrupt will be triggered.
317    pub fn enable_region1_monitor(
318        &mut self,
319        lower_bound: u32,
320        upper_bound: u32,
321        reads: bool,
322        writes: bool,
323    ) {
324        self.regs()
325            .core_0_area_dram0_1_min()
326            .write(|w| unsafe { w.core_0_area_dram0_1_min().bits(lower_bound) });
327
328        self.regs()
329            .core_0_area_dram0_1_max()
330            .write(|w| unsafe { w.core_0_area_dram0_1_max().bits(upper_bound) });
331
332        self.regs().core_0_montr_ena().modify(|_, w| {
333            w.core_0_area_dram0_1_rd_ena()
334                .bit(reads)
335                .core_0_area_dram0_1_wr_ena()
336                .bit(writes)
337        });
338
339        self.clear_region1_monitor_interrupt();
340
341        self.regs().core_0_intr_ena().modify(|_, w| {
342            w.core_0_area_dram0_1_rd_intr_ena()
343                .set_bit()
344                .core_0_area_dram0_1_wr_intr_ena()
345                .set_bit()
346        });
347    }
348
349    /// Disable region1 monitoring on main core.
350    pub fn disable_region1_monitor(&mut self) {
351        self.regs().core_0_intr_ena().modify(|_, w| {
352            w.core_0_area_dram0_1_rd_intr_ena()
353                .clear_bit()
354                .core_0_area_dram0_1_wr_intr_ena()
355                .clear_bit()
356        });
357
358        self.regs().core_0_montr_ena().modify(|_, w| {
359            w.core_0_area_dram0_1_rd_ena()
360                .clear_bit()
361                .core_0_area_dram0_1_wr_ena()
362                .clear_bit()
363        });
364    }
365
366    /// Clear region1 monitoring interrupt on main core.
367    pub fn clear_region1_monitor_interrupt(&mut self) {
368        self.regs().core_0_intr_clr().write(|w| {
369            w.core_0_area_dram0_1_rd_clr()
370                .set_bit()
371                .core_0_area_dram0_1_wr_clr()
372                .set_bit()
373        });
374    }
375
376    /// Check, if region1 monitoring interrupt is set on main core.
377    pub fn is_region1_monitor_interrupt_set(&self) -> bool {
378        self.regs()
379            .core_0_intr_raw()
380            .read()
381            .core_0_area_dram0_1_rd_raw()
382            .bit_is_set()
383            || self
384                .regs()
385                .core_0_intr_raw()
386                .read()
387                .core_0_area_dram0_1_wr_raw()
388                .bit_is_set()
389    }
390
391    /// Get region monitoring PC value on main core.
392    pub fn region_monitor_pc(&self) -> u32 {
393        self.regs().core_0_area_pc().read().core_0_area_pc().bits()
394    }
395}
396
397#[cfg(all(assist_debug_region_monitor, multi_core))]
398impl DebugAssist<'_> {
399    /// Enable region monitoring of read/write performed by the secondary CPU in
400    /// a certain memory region0. Whenever the bus reads or writes in the
401    /// specified memory region, an interrupt will be triggered.
402    pub fn enable_core1_region0_monitor(
403        &mut self,
404        lower_bound: u32,
405        upper_bound: u32,
406        reads: bool,
407        writes: bool,
408    ) {
409        self.regs()
410            .core_1_area_dram0_0_min()
411            .write(|w| unsafe { w.core_1_area_dram0_0_min().bits(lower_bound) });
412
413        self.regs()
414            .core_1_area_dram0_0_max()
415            .write(|w| unsafe { w.core_1_area_dram0_0_max().bits(upper_bound) });
416
417        self.regs().core_1_montr_ena().modify(|_, w| {
418            w.core_1_area_dram0_0_rd_ena()
419                .bit(reads)
420                .core_1_area_dram0_0_wr_ena()
421                .bit(writes)
422        });
423
424        self.clear_core1_region0_monitor_interrupt();
425
426        self.regs().core_1_intr_ena().modify(|_, w| {
427            w.core_1_area_dram0_0_rd_intr_ena()
428                .set_bit()
429                .core_1_area_dram0_0_wr_intr_ena()
430                .set_bit()
431        });
432    }
433
434    /// Disable region0 monitoring on secondary core.
435    pub fn disable_core1_region0_monitor(&mut self) {
436        self.regs().core_1_intr_ena().modify(|_, w| {
437            w.core_1_area_dram0_0_rd_intr_ena()
438                .clear_bit()
439                .core_1_area_dram0_0_wr_intr_ena()
440                .clear_bit()
441        });
442
443        self.regs().core_1_montr_ena().modify(|_, w| {
444            w.core_1_area_dram0_0_rd_ena()
445                .clear_bit()
446                .core_1_area_dram0_0_wr_ena()
447                .clear_bit()
448        });
449    }
450
451    /// Clear region0 monitoring interrupt on secondary core.
452    pub fn clear_core1_region0_monitor_interrupt(&mut self) {
453        self.regs().core_1_intr_clr().write(|w| {
454            w.core_1_area_dram0_0_rd_clr()
455                .set_bit()
456                .core_1_area_dram0_0_wr_clr()
457                .set_bit()
458        });
459    }
460
461    /// Check, if region0 monitoring interrupt is set on secondary core.
462    pub fn is_core1_region0_monitor_interrupt_set(&self) -> bool {
463        self.regs()
464            .core_1_intr_raw()
465            .read()
466            .core_1_area_dram0_0_rd_raw()
467            .bit_is_set()
468            || self
469                .regs()
470                .core_1_intr_raw()
471                .read()
472                .core_1_area_dram0_0_wr_raw()
473                .bit_is_set()
474    }
475
476    /// Enable region monitoring of read/write performed by the secondary CPU in
477    /// a certain memory region1. Whenever the bus reads or writes in the
478    /// specified memory region, an interrupt will be triggered.
479    pub fn enable_core1_region1_monitor(
480        &mut self,
481        lower_bound: u32,
482        upper_bound: u32,
483        reads: bool,
484        writes: bool,
485    ) {
486        self.regs()
487            .core_1_area_dram0_1_min()
488            .write(|w| unsafe { w.core_1_area_dram0_1_min().bits(lower_bound) });
489
490        self.regs()
491            .core_1_area_dram0_1_max()
492            .write(|w| unsafe { w.core_1_area_dram0_1_max().bits(upper_bound) });
493
494        self.regs().core_1_montr_ena().modify(|_, w| {
495            w.core_1_area_dram0_1_rd_ena()
496                .bit(reads)
497                .core_1_area_dram0_1_wr_ena()
498                .bit(writes)
499        });
500
501        self.clear_core1_region1_monitor_interrupt();
502
503        self.regs().core_1_intr_ena().modify(|_, w| {
504            w.core_1_area_dram0_1_rd_intr_ena()
505                .set_bit()
506                .core_1_area_dram0_1_wr_intr_ena()
507                .set_bit()
508        });
509    }
510
511    /// Disable region1 monitoring on secondary core.
512    pub fn disable_core1_region1_monitor(&mut self) {
513        self.regs().core_1_intr_ena().modify(|_, w| {
514            w.core_1_area_dram0_1_rd_intr_ena()
515                .clear_bit()
516                .core_1_area_dram0_1_wr_intr_ena()
517                .clear_bit()
518        });
519
520        self.regs().core_1_montr_ena().modify(|_, w| {
521            w.core_1_area_dram0_1_rd_ena()
522                .clear_bit()
523                .core_1_area_dram0_1_wr_ena()
524                .clear_bit()
525        });
526    }
527
528    /// Clear region1 monitoring interrupt on secondary core.
529    pub fn clear_core1_region1_monitor_interrupt(&mut self) {
530        self.regs().core_1_intr_clr().write(|w| {
531            w.core_1_area_dram0_1_rd_clr()
532                .set_bit()
533                .core_1_area_dram0_1_wr_clr()
534                .set_bit()
535        });
536    }
537
538    /// Check, if region1 monitoring interrupt is set on secondary core.
539    pub fn is_core1_region1_monitor_interrupt_set(&self) -> bool {
540        self.regs()
541            .core_1_intr_raw()
542            .read()
543            .core_1_area_dram0_1_rd_raw()
544            .bit_is_set()
545            || self
546                .regs()
547                .core_1_intr_raw()
548                .read()
549                .core_1_area_dram0_1_wr_raw()
550                .bit_is_set()
551    }
552
553    /// Get region monitoring PC value on secondary core.
554    pub fn core1_region_monitor_pc(&self) -> u32 {
555        self.regs().core_1_area_pc().read().core_1_area_pc().bits()
556    }
557}