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