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::{
38    pac::trace::RegisterBlock,
39    peripheral::{Peripheral, PeripheralRef},
40    system::PeripheralGuard,
41};
42
43/// Errors returned from [Trace::stop_trace]
44#[derive(Debug, Clone, Copy)]
45pub enum Error {
46    /// Attempted to stop a trace which had not been started yet
47    NotStarted,
48}
49
50/// Returned by [Trace::stop_trace]
51#[derive(Debug, Clone, Copy)]
52pub struct TraceResult {
53    /// Start index of the valid data
54    pub valid_start_index: usize,
55    /// Length of the valid data
56    pub valid_length: usize,
57}
58
59/// TRACE Encoder Instance
60pub struct Trace<'d, T>
61where
62    T: Instance,
63{
64    peripheral: PeripheralRef<'d, T>,
65    buffer: Option<&'d mut [u8]>,
66    _guard: PeripheralGuard,
67}
68
69impl<'d, T> Trace<'d, T>
70where
71    T: Instance,
72{
73    /// Construct a new instance
74    pub fn new(peripheral: impl Peripheral<P = T> + 'd) -> Self {
75        crate::into_ref!(peripheral);
76        let guard = PeripheralGuard::new(peripheral.peripheral());
77
78        Self {
79            peripheral,
80            buffer: None,
81            _guard: guard,
82        }
83    }
84
85    /// Start tracing, writing data into the `buffer`
86    pub fn start_trace(&mut self, buffer: &'d mut [u8]) {
87        let reg_block = self.peripheral.register_block();
88
89        reg_block
90            .mem_start_addr()
91            .modify(|_, w| unsafe { w.mem_start_addr().bits(buffer.as_ptr() as *const _ as u32) });
92        reg_block.mem_end_addr().modify(|_, w| unsafe {
93            w.mem_end_addr()
94                .bits((buffer.as_ptr() as *const _ as u32) + (buffer.len() as u32))
95        });
96        reg_block
97            .mem_addr_update()
98            .write(|w| w.mem_current_addr_update().set_bit());
99
100        // won't set bit in int-raw without enabling
101        reg_block
102            .intr_ena()
103            .modify(|_, w| w.mem_full_intr_ena().set_bit());
104
105        // for now always use looping mode
106        reg_block
107            .trigger()
108            .write(|w| w.mem_loop().set_bit().restart_ena().set_bit());
109
110        reg_block.intr_clr().write(|w| {
111            w.fifo_overflow_intr_clr()
112                .set_bit()
113                .mem_full_intr_clr()
114                .set_bit()
115        });
116
117        self.buffer.replace(buffer);
118        reg_block.trigger().write(|w| w.on().set_bit());
119    }
120
121    /// Stop tracing
122    ///
123    /// Be aware that valid data might not start at index 0 and you need to
124    /// account for wrapping when reading the data.
125    pub fn stop_trace(&mut self) -> Result<TraceResult, Error> {
126        let reg_block = self.peripheral.register_block();
127
128        reg_block
129            .trigger()
130            .write(|w| w.off().set_bit().restart_ena().clear_bit());
131
132        if self.buffer.is_none() {
133            return Err(Error::NotStarted);
134        }
135
136        let buffer = self.buffer.take().unwrap();
137
138        while !reg_block.fifo_status().read().fifo_empty().bit_is_set() {}
139
140        let overflow = reg_block.intr_raw().read().mem_full_intr_raw().bit();
141        let idx = if overflow {
142            reg_block
143                .mem_current_addr()
144                .read()
145                .mem_current_addr()
146                .bits()
147                - &buffer as *const _ as u32
148        } else {
149            0
150        };
151
152        let len = if overflow {
153            buffer.len()
154        } else {
155            reg_block
156                .mem_current_addr()
157                .read()
158                .mem_current_addr()
159                .bits() as usize
160                - buffer.as_ptr() as *const _ as usize
161                - 14 // there will be 14 zero bytes at the start
162        };
163
164        let mut valid = false;
165        let mut fourteen_zeroes = false;
166        let mut zeroes = 0;
167        let start_index = if !valid {
168            let mut i = 0;
169            loop {
170                let b = unsafe {
171                    buffer
172                        .as_ptr()
173                        .add((i + idx as usize) % buffer.len())
174                        .read_volatile()
175                };
176
177                if !valid {
178                    if b == 0 {
179                        zeroes += 1;
180                    } else {
181                        zeroes = 0;
182                    }
183
184                    if zeroes >= 14 {
185                        fourteen_zeroes = true;
186                    }
187
188                    if fourteen_zeroes && b != 0 {
189                        valid = true;
190                    }
191                }
192
193                if valid {
194                    break i;
195                }
196
197                i += 1;
198            }
199        } else {
200            0
201        };
202
203        Ok(TraceResult {
204            valid_start_index: start_index,
205            valid_length: len,
206        })
207    }
208}
209
210/// Trace peripheral instance
211#[doc(hidden)]
212pub trait Instance: crate::private::Sealed {
213    /// Get a reference to the peripheral's underlying register block
214    fn register_block(&self) -> &RegisterBlock;
215
216    /// Peripheral
217    fn peripheral(&self) -> crate::system::Peripheral;
218}
219
220impl Instance for crate::peripherals::TRACE0 {
221    fn register_block(&self) -> &RegisterBlock {
222        self.register_block()
223    }
224
225    fn peripheral(&self) -> crate::system::Peripheral {
226        crate::system::Peripheral::Trace0
227    }
228}