esp_hal/
trace.rs

1//! # RISC-­V Trace Encoder (TRACE)
2//!
3//! ## Overview
4//! The high-performance CPU supports instruction trace interface through the
5//! trace encoder. The trace encoder connects to HP CPU’s instruction trace
6//! interface, compresses the information into smaller packets, and then stores
7//! the packets in internal SRAM.
8//!
9//! In complex systems, understanding program execution flow is not
10//! straightforward. This may be due to a number of factors, for example,
11//! interactions with other cores, peripherals, real-time events, poor
12//! implementations, or some combination of all of the above.
13//!
14//! It is hard to use a debugger to monitor the program execution flow of a
15//! running system in real time, as this is intrusive and might affect the
16//! running state. But providing visibility of program execution is important.
17//!
18//! That is where instruction trace comes in, which provides trace of the
19//! program execution.
20//!
21//! ## Examples
22//! ```rust, no_run
23#![doc = crate::before_snippet!()]
24//! # use esp_hal::trace::Trace;
25//! let mut buffer = [0_u8; 1024];
26//! let mut trace = Trace::new(peripherals.TRACE0);
27//! trace.start_trace(&mut buffer);
28//! // traced code
29//!
30//! // end traced code
31//! let res = trace.stop_trace()?;
32//! // transfer the trace result to the host and decode it there
33//! # Ok(())
34//! # }
35//! ```
36
37use crate::{pac::trace::RegisterBlock, peripherals::TRACE0, system::PeripheralGuard};
38
39/// Errors returned from [Trace::stop_trace]
40#[derive(Debug, Clone, Copy)]
41pub enum Error {
42    /// Attempted to stop a trace which had not been started yet
43    NotStarted,
44}
45
46/// Returned by [Trace::stop_trace]
47#[derive(Debug, Clone, Copy)]
48pub struct TraceResult {
49    /// Start index of the valid data
50    pub valid_start_index: usize,
51    /// Length of the valid data
52    pub valid_length: usize,
53}
54
55/// TRACE Encoder Instance
56pub struct Trace<'d> {
57    peripheral: TRACE0<'d>,
58    buffer: Option<&'d mut [u8]>,
59    _guard: PeripheralGuard,
60}
61
62impl<'d> Trace<'d> {
63    /// Construct a new instance
64    pub fn new(peripheral: TRACE0<'d>) -> Self {
65        let guard = PeripheralGuard::new(peripheral.peripheral());
66
67        Self {
68            peripheral,
69            buffer: None,
70            _guard: guard,
71        }
72    }
73
74    /// Start tracing, writing data into the `buffer`
75    pub fn start_trace(&mut self, buffer: &'d mut [u8]) {
76        let reg_block = self.peripheral.register_block();
77
78        reg_block
79            .mem_start_addr()
80            .modify(|_, w| unsafe { w.mem_start_addr().bits(buffer.as_ptr() as *const _ as u32) });
81        reg_block.mem_end_addr().modify(|_, w| unsafe {
82            w.mem_end_addr()
83                .bits((buffer.as_ptr() as *const _ as u32) + (buffer.len() as u32))
84        });
85        reg_block
86            .mem_addr_update()
87            .write(|w| w.mem_current_addr_update().set_bit());
88
89        // won't set bit in int-raw without enabling
90        reg_block
91            .intr_ena()
92            .modify(|_, w| w.mem_full_intr_ena().set_bit());
93
94        // for now always use looping mode
95        reg_block
96            .trigger()
97            .write(|w| w.mem_loop().set_bit().restart_ena().set_bit());
98
99        reg_block.intr_clr().write(|w| {
100            w.fifo_overflow_intr_clr()
101                .set_bit()
102                .mem_full_intr_clr()
103                .set_bit()
104        });
105
106        self.buffer.replace(buffer);
107        reg_block.trigger().write(|w| w.on().set_bit());
108    }
109
110    /// Stop tracing
111    ///
112    /// Be aware that valid data might not start at index 0 and you need to
113    /// account for wrapping when reading the data.
114    pub fn stop_trace(&mut self) -> Result<TraceResult, Error> {
115        let reg_block = self.peripheral.register_block();
116
117        reg_block
118            .trigger()
119            .write(|w| w.off().set_bit().restart_ena().clear_bit());
120
121        if self.buffer.is_none() {
122            return Err(Error::NotStarted);
123        }
124
125        let buffer = self.buffer.take().unwrap();
126
127        while !reg_block.fifo_status().read().fifo_empty().bit_is_set() {}
128
129        let overflow = reg_block.intr_raw().read().mem_full_intr_raw().bit();
130        let idx = if overflow {
131            reg_block
132                .mem_current_addr()
133                .read()
134                .mem_current_addr()
135                .bits()
136                - &buffer as *const _ as u32
137        } else {
138            0
139        };
140
141        let len = if overflow {
142            buffer.len()
143        } else {
144            reg_block
145                .mem_current_addr()
146                .read()
147                .mem_current_addr()
148                .bits() as usize
149                - buffer.as_ptr() as *const _ as usize
150                - 14 // there will be 14 zero bytes at the start
151        };
152
153        let mut valid = false;
154        let mut fourteen_zeroes = false;
155        let mut zeroes = 0;
156        let start_index = if !valid {
157            let mut i = 0;
158            loop {
159                let b = unsafe {
160                    buffer
161                        .as_ptr()
162                        .add((i + idx as usize) % buffer.len())
163                        .read_volatile()
164                };
165
166                if !valid {
167                    if b == 0 {
168                        zeroes += 1;
169                    } else {
170                        zeroes = 0;
171                    }
172
173                    if zeroes >= 14 {
174                        fourteen_zeroes = true;
175                    }
176
177                    if fourteen_zeroes && b != 0 {
178                        valid = true;
179                    }
180                }
181
182                if valid {
183                    break i;
184                }
185
186                i += 1;
187            }
188        } else {
189            0
190        };
191
192        Ok(TraceResult {
193            valid_start_index: start_index,
194            valid_length: len,
195        })
196    }
197}
198
199/// Trace peripheral instance
200#[doc(hidden)]
201pub trait Instance: crate::private::Sealed {
202    /// Get a reference to the peripheral's underlying register block
203    fn register_block(&self) -> &RegisterBlock;
204
205    /// Peripheral
206    fn peripheral(&self) -> crate::system::Peripheral;
207}
208
209impl Instance for TRACE0<'_> {
210    fn register_block(&self) -> &RegisterBlock {
211        self.register_block()
212    }
213
214    fn peripheral(&self) -> crate::system::Peripheral {
215        crate::system::Peripheral::Trace0
216    }
217}