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