Skip to main content

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        crate::interrupt::bind_handler(Interrupt::ASSIST_DEBUG, handler);
56    }
57
58    fn regs(&self) -> &pac::assist_debug::RegisterBlock {
59        self.debug_assist.register_block()
60    }
61}
62
63impl crate::private::Sealed for DebugAssist<'_> {}
64
65#[instability::unstable]
66impl crate::interrupt::InterruptConfigurable for DebugAssist<'_> {
67    fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
68        self.set_interrupt_handler(handler);
69    }
70}
71
72#[cfg(assist_debug_has_sp_monitor)]
73impl DebugAssist<'_> {
74    /// Enable SP monitoring on main core. When the SP exceeds the
75    /// `lower_bound` or `upper_bound` threshold, the module will record the PC
76    /// pointer and generate an interrupt.
77    pub fn internal_sp_monitor(&mut self, cpu: usize, lower_bound: u32, upper_bound: u32) {
78        let regs = self.regs().cpu(cpu);
79
80        regs.sp_min()
81            .write(|w| unsafe { w.sp_min().bits(lower_bound) });
82
83        regs.sp_max()
84            .write(|w| unsafe { w.sp_max().bits(upper_bound) });
85
86        regs.montr_ena().modify(|_, w| {
87            w.sp_spill_min_ena().set_bit();
88            w.sp_spill_max_ena().set_bit()
89        });
90
91        regs.intr_clr().write(|w| {
92            w.sp_spill_max_clr().set_bit();
93            w.sp_spill_min_clr().set_bit()
94        });
95
96        regs.intr_ena().modify(|_, w| {
97            w.sp_spill_max_intr_ena().set_bit();
98            w.sp_spill_min_intr_ena().set_bit()
99        });
100    }
101
102    fn internal_disable_sp_monitor(&mut self, cpu: usize) {
103        let regs = self.regs().cpu(cpu);
104
105        regs.intr_ena().modify(|_, w| {
106            w.sp_spill_max_intr_ena().clear_bit();
107            w.sp_spill_min_intr_ena().clear_bit()
108        });
109
110        regs.montr_ena().modify(|_, w| {
111            w.sp_spill_min_ena().clear_bit();
112            w.sp_spill_max_ena().clear_bit()
113        });
114    }
115
116    fn internal_clear_sp_monitor_interrupt(&mut self, cpu: usize) {
117        self.regs().cpu(cpu).intr_clr().write(|w| {
118            w.sp_spill_max_clr().set_bit();
119            w.sp_spill_min_clr().set_bit()
120        });
121    }
122
123    fn internal_is_sp_monitor_interrupt_set(&self, cpu: usize) -> bool {
124        let regs = self.regs().cpu(cpu);
125        let intrs = regs.intr_raw().read();
126
127        intrs.sp_spill_max_raw().bit_is_set() || intrs.sp_spill_min_raw().bit_is_set()
128    }
129
130    fn internal_sp_monitor_pc(&self, cpu: usize) -> u32 {
131        self.regs().cpu(cpu).sp_pc().read().sp_pc().bits()
132    }
133
134    /// Enable SP monitoring on main core. When the SP exceeds the
135    /// `lower_bound` or `upper_bound` threshold, the module will record the PC
136    /// pointer and generate an interrupt.
137    pub fn enable_sp_monitor(&mut self, lower_bound: u32, upper_bound: u32) {
138        self.internal_sp_monitor(0, lower_bound, upper_bound);
139    }
140
141    /// Disable SP monitoring on main core.
142    pub fn disable_sp_monitor(&mut self) {
143        self.internal_disable_sp_monitor(0)
144    }
145
146    /// Clear SP monitoring interrupt on main core.
147    pub fn clear_sp_monitor_interrupt(&mut self) {
148        self.internal_clear_sp_monitor_interrupt(0)
149    }
150
151    /// Check, if SP monitoring interrupt is set on main core.
152    pub fn is_sp_monitor_interrupt_set(&self) -> bool {
153        self.internal_is_sp_monitor_interrupt_set(0)
154    }
155
156    /// Get SP monitoring PC value on main core.
157    pub fn sp_monitor_pc(&self) -> u32 {
158        self.internal_sp_monitor_pc(0)
159    }
160}
161
162#[cfg(all(assist_debug_has_sp_monitor, multi_core))]
163impl<'d> DebugAssist<'d> {
164    /// Enable SP monitoring on secondary core. When the SP exceeds the
165    /// `lower_bound` or `upper_bound` threshold, the module will record the PC
166    /// pointer and generate an interrupt.
167    pub fn enable_core1_sp_monitor(&mut self, lower_bound: u32, upper_bound: u32) {
168        self.internal_sp_monitor(1, lower_bound, upper_bound);
169    }
170
171    /// Disable SP monitoring on secondary core.
172    pub fn disable_core1_sp_monitor(&mut self) {
173        self.internal_disable_sp_monitor(1)
174    }
175
176    /// Clear SP monitoring interrupt on secondary core.
177    pub fn clear_core1_sp_monitor_interrupt(&mut self) {
178        self.internal_clear_sp_monitor_interrupt(1)
179    }
180
181    /// Check, if SP monitoring interrupt is set on secondary core.
182    pub fn is_core1_sp_monitor_interrupt_set(&self) -> bool {
183        self.internal_is_sp_monitor_interrupt_set(1)
184    }
185
186    /// Get SP monitoring PC value on secondary core.
187    pub fn core1_sp_monitor_pc(&self) -> u32 {
188        self.internal_sp_monitor_pc(1)
189    }
190}
191
192#[cfg(assist_debug_has_region_monitor)]
193impl DebugAssist<'_> {
194    fn internal_enable_region0_monitor(
195        &mut self,
196        cpu: usize,
197        lower_bound: u32,
198        upper_bound: u32,
199        reads: bool,
200        writes: bool,
201    ) {
202        let regs = self.regs().cpu(cpu);
203
204        regs.area_dram0_0_min()
205            .write(|w| unsafe { w.area_dram0_0_min().bits(lower_bound) });
206
207        regs.area_dram0_0_max()
208            .write(|w| unsafe { w.area_dram0_0_max().bits(upper_bound) });
209
210        regs.montr_ena().modify(|_, w| {
211            w.area_dram0_0_rd_ena().bit(reads);
212            w.area_dram0_0_wr_ena().bit(writes)
213        });
214
215        regs.intr_clr().write(|w| {
216            w.area_dram0_0_rd_clr().set_bit();
217            w.area_dram0_0_wr_clr().set_bit()
218        });
219
220        regs.intr_ena().modify(|_, w| {
221            w.area_dram0_0_rd_intr_ena().set_bit();
222            w.area_dram0_0_wr_intr_ena().set_bit()
223        });
224    }
225
226    fn internal_disable_region0_monitor(&mut self, cpu: usize) {
227        let regs = self.regs().cpu(cpu);
228
229        regs.intr_ena().modify(|_, w| {
230            w.area_dram0_0_rd_intr_ena().clear_bit();
231            w.area_dram0_0_wr_intr_ena().clear_bit()
232        });
233
234        regs.montr_ena().modify(|_, w| {
235            w.area_dram0_0_rd_ena().clear_bit();
236            w.area_dram0_0_wr_ena().clear_bit()
237        });
238    }
239
240    fn internal_clear_region0_monitor_interrupt(&mut self, cpu: usize) {
241        self.regs().cpu(cpu).intr_clr().write(|w| {
242            w.area_dram0_0_rd_clr().set_bit();
243            w.area_dram0_0_wr_clr().set_bit()
244        });
245    }
246
247    fn internal_is_region0_monitor_interrupt_set(&self, cpu: usize) -> bool {
248        let regs = self.regs().cpu(cpu);
249        let intrs = regs.intr_raw().read();
250
251        intrs.area_dram0_0_rd_raw().bit_is_set() || intrs.area_dram0_0_wr_raw().bit_is_set()
252    }
253
254    fn internal_enable_region1_monitor(
255        &mut self,
256        cpu: usize,
257        lower_bound: u32,
258        upper_bound: u32,
259        reads: bool,
260        writes: bool,
261    ) {
262        let regs = self.regs().cpu(cpu);
263
264        regs.area_dram0_1_min()
265            .write(|w| unsafe { w.area_dram0_1_min().bits(lower_bound) });
266
267        regs.area_dram0_1_max()
268            .write(|w| unsafe { w.area_dram0_1_max().bits(upper_bound) });
269
270        regs.montr_ena().modify(|_, w| {
271            w.area_dram0_1_rd_ena().bit(reads);
272            w.area_dram0_1_wr_ena().bit(writes)
273        });
274
275        regs.intr_clr().write(|w| {
276            w.area_dram0_1_rd_clr().set_bit();
277            w.area_dram0_1_wr_clr().set_bit()
278        });
279
280        regs.intr_ena().modify(|_, w| {
281            w.area_dram0_1_rd_intr_ena().set_bit();
282            w.area_dram0_1_wr_intr_ena().set_bit()
283        });
284    }
285
286    fn internal_disable_region1_monitor(&mut self, cpu: usize) {
287        let regs = self.regs().cpu(cpu);
288
289        regs.intr_ena().modify(|_, w| {
290            w.area_dram0_1_rd_intr_ena().clear_bit();
291            w.area_dram0_1_wr_intr_ena().clear_bit()
292        });
293
294        regs.montr_ena().modify(|_, w| {
295            w.area_dram0_1_rd_ena().clear_bit();
296            w.area_dram0_1_wr_ena().clear_bit()
297        });
298    }
299
300    fn internal_clear_region1_monitor_interrupt(&mut self, cpu: usize) {
301        self.regs().cpu(cpu).intr_clr().write(|w| {
302            w.area_dram0_1_rd_clr().set_bit();
303            w.area_dram0_1_wr_clr().set_bit()
304        });
305    }
306
307    fn internal_is_region1_monitor_interrupt_set(&self, cpu: usize) -> bool {
308        let regs = self.regs().cpu(cpu);
309        let intrs = regs.intr_raw().read();
310
311        intrs.area_dram0_1_rd_raw().bit_is_set() || intrs.area_dram0_1_wr_raw().bit_is_set()
312    }
313
314    fn internal_region_monitor_pc(&self, cpu: usize) -> u32 {
315        self.regs().cpu(cpu).area_pc().read().area_pc().bits()
316    }
317
318    /// Enable region monitoring of read/write performed by the main CPU in a
319    /// certain memory region0. Whenever the bus reads or writes in the
320    /// specified memory region, an interrupt will be triggered. Two memory
321    /// regions (region0, region1) can be monitored at the same time.
322    pub fn enable_region0_monitor(
323        &mut self,
324        lower_bound: u32,
325        upper_bound: u32,
326        reads: bool,
327        writes: bool,
328    ) {
329        self.internal_enable_region0_monitor(0, lower_bound, upper_bound, reads, writes)
330    }
331
332    /// Disable region0 monitoring on main core.
333    pub fn disable_region0_monitor(&mut self) {
334        self.internal_disable_region0_monitor(0)
335    }
336
337    /// Clear region0 monitoring interrupt on main core.
338    pub fn clear_region0_monitor_interrupt(&mut self) {
339        self.internal_clear_region0_monitor_interrupt(0)
340    }
341
342    /// Check, if region0 monitoring interrupt is set on main core.
343    pub fn is_region0_monitor_interrupt_set(&self) -> bool {
344        self.internal_is_region0_monitor_interrupt_set(0)
345    }
346
347    /// Enable region monitoring of read/write performed by the main CPU in a
348    /// certain memory region1. Whenever the bus reads or writes in the
349    /// specified memory region, an interrupt will be triggered.
350    pub fn enable_region1_monitor(
351        &mut self,
352        lower_bound: u32,
353        upper_bound: u32,
354        reads: bool,
355        writes: bool,
356    ) {
357        self.internal_enable_region1_monitor(0, lower_bound, upper_bound, reads, writes)
358    }
359
360    /// Disable region1 monitoring on main core.
361    pub fn disable_region1_monitor(&mut self) {
362        self.internal_disable_region1_monitor(0)
363    }
364
365    /// Clear region1 monitoring interrupt on main core.
366    pub fn clear_region1_monitor_interrupt(&mut self) {
367        self.internal_clear_region1_monitor_interrupt(0)
368    }
369
370    /// Check, if region1 monitoring interrupt is set on main core.
371    pub fn is_region1_monitor_interrupt_set(&self) -> bool {
372        self.internal_is_region1_monitor_interrupt_set(0)
373    }
374
375    /// Get region monitoring PC value on main core.
376    pub fn region_monitor_pc(&self) -> u32 {
377        self.internal_region_monitor_pc(0)
378    }
379}
380
381#[cfg(all(assist_debug_has_region_monitor, multi_core))]
382impl DebugAssist<'_> {
383    /// Enable region monitoring of read/write performed by the secondary CPU in
384    /// a certain memory region0. Whenever the bus reads or writes in the
385    /// specified memory region, an interrupt will be triggered.
386    pub fn enable_core1_region0_monitor(
387        &mut self,
388        lower_bound: u32,
389        upper_bound: u32,
390        reads: bool,
391        writes: bool,
392    ) {
393        self.internal_enable_region0_monitor(1, lower_bound, upper_bound, reads, writes)
394    }
395
396    /// Disable region0 monitoring on secondary core.
397    pub fn disable_core1_region0_monitor(&mut self) {
398        self.internal_disable_region0_monitor(1)
399    }
400
401    /// Clear region0 monitoring interrupt on secondary core.
402    pub fn clear_core1_region0_monitor_interrupt(&mut self) {
403        self.internal_clear_region0_monitor_interrupt(1)
404    }
405
406    /// Check, if region0 monitoring interrupt is set on secondary core.
407    pub fn is_core1_region0_monitor_interrupt_set(&self) -> bool {
408        self.internal_is_region0_monitor_interrupt_set(1)
409    }
410
411    /// Enable region monitoring of read/write performed by the secondary CPU in
412    /// a certain memory region1. Whenever the bus reads or writes in the
413    /// specified memory region, an interrupt will be triggered.
414    pub fn enable_core1_region1_monitor(
415        &mut self,
416        lower_bound: u32,
417        upper_bound: u32,
418        reads: bool,
419        writes: bool,
420    ) {
421        self.internal_enable_region1_monitor(1, lower_bound, upper_bound, reads, writes)
422    }
423
424    /// Disable region1 monitoring on secondary core.
425    pub fn disable_core1_region1_monitor(&mut self) {
426        self.internal_disable_region1_monitor(1)
427    }
428
429    /// Clear region1 monitoring interrupt on secondary core.
430    pub fn clear_core1_region1_monitor_interrupt(&mut self) {
431        self.internal_clear_region1_monitor_interrupt(1)
432    }
433
434    /// Check, if region1 monitoring interrupt is set on secondary core.
435    pub fn is_core1_region1_monitor_interrupt_set(&self) -> bool {
436        self.internal_is_region1_monitor_interrupt_set(1)
437    }
438
439    /// Get region monitoring PC value on secondary core.
440    pub fn core1_region_monitor_pc(&self) -> u32 {
441        self.internal_region_monitor_pc(1)
442    }
443}