esp_hal/
trace.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
//! # RISC-­V Trace Encoder (TRACE)
//!
//! ## Overview
//! The high-performance CPU supports instruction trace interface through the
//! trace encoder. The trace encoder connects to HP CPU’s instruction trace
//! interface, compresses the information into smaller packets, and then stores
//! the packets in internal SRAM.
//!
//! In complex systems, understanding program execution flow is not
//! straightforward. This may be due to a number of factors, for example,
//! interactions with other cores, peripherals, real-time events, poor
//! implementations, or some combination of all of the above.
//!
//! It is hard to use a debugger to monitor the program execution flow of a
//! running system in real time, as this is intrusive and might affect the
//! running state. But providing visibility of program execution is important.
//!
//! That is where instruction trace comes in, which provides trace of the
//! program execution.
//!
//! ## Examples
//! ```rust, no_run
#![doc = crate::before_snippet!()]
//! # use esp_hal::trace::Trace;
//! let mut trace = Trace::new(peripherals.TRACE0);
//! let mut buffer = [0_u8; 1024];
//! trace.start_trace(&mut buffer);
//! // traced code
//!
//! // end traced code
//! let res = trace.stop_trace().unwrap();
//! // transfer the trace result to the host and decode it there
//! # }
//! ```

use crate::{
    peripheral::{Peripheral, PeripheralRef},
    peripherals::trace::RegisterBlock,
    system::PeripheralClockControl,
};

/// Errors returned from [Trace::stop_trace]
#[derive(Debug, Clone, Copy)]
pub enum Error {
    /// Attempted to stop a trace which had not been started yet
    NotStarted,
}

/// Returned by [Trace::stop_trace]
#[derive(Debug, Clone, Copy)]
pub struct TraceResult {
    /// Start index of the valid data
    pub valid_start_index: usize,
    /// Length of the valid data
    pub valid_length: usize,
}

/// TRACE Encoder Instance
pub struct Trace<'d, T> {
    peripheral: PeripheralRef<'d, T>,
    buffer: Option<&'d mut [u8]>,
}

impl<'d, T> Trace<'d, T>
where
    T: Instance,
{
    /// Construct a new instance
    pub fn new(peripheral: impl Peripheral<P = T> + 'd) -> Self {
        crate::into_ref!(peripheral);

        PeripheralClockControl::reset(crate::system::Peripheral::Trace0);
        PeripheralClockControl::enable(crate::system::Peripheral::Trace0);

        Self {
            peripheral,
            buffer: None,
        }
    }

    /// Start tracing, writing data into the `buffer`
    pub fn start_trace(&mut self, buffer: &'d mut [u8]) {
        let reg_block = self.peripheral.register_block();

        reg_block
            .mem_start_addr()
            .modify(|_, w| unsafe { w.mem_start_addr().bits(buffer.as_ptr() as *const _ as u32) });
        reg_block.mem_end_addr().modify(|_, w| unsafe {
            w.mem_end_addr()
                .bits((buffer.as_ptr() as *const _ as u32) + (buffer.len() as u32))
        });
        reg_block
            .mem_addr_update()
            .write(|w| w.mem_current_addr_update().set_bit());

        // won't set bit in int-raw without enabling
        reg_block
            .intr_ena()
            .modify(|_, w| w.mem_full_intr_ena().set_bit());

        // for now always use looping mode
        reg_block
            .trigger()
            .write(|w| w.mem_loop().set_bit().restart_ena().set_bit());

        reg_block.intr_clr().write(|w| {
            w.fifo_overflow_intr_clr()
                .set_bit()
                .mem_full_intr_clr()
                .set_bit()
        });

        self.buffer.replace(buffer);
        reg_block.trigger().write(|w| w.on().set_bit());
    }

    /// Stop tracing
    ///
    /// Be aware that valid data might not start at index 0 and you need to
    /// account for wrapping when reading the data.
    pub fn stop_trace(&mut self) -> Result<TraceResult, Error> {
        let reg_block = self.peripheral.register_block();

        reg_block
            .trigger()
            .write(|w| w.off().set_bit().restart_ena().clear_bit());

        if self.buffer.is_none() {
            return Err(Error::NotStarted);
        }

        let buffer = self.buffer.take().unwrap();

        while !reg_block.fifo_status().read().fifo_empty().bit_is_set() {}

        let overflow = reg_block.intr_raw().read().mem_full_intr_raw().bit();
        let idx = if overflow {
            reg_block
                .mem_current_addr()
                .read()
                .mem_current_addr()
                .bits()
                - &buffer as *const _ as u32
        } else {
            0
        };

        let len = if overflow {
            buffer.len()
        } else {
            reg_block
                .mem_current_addr()
                .read()
                .mem_current_addr()
                .bits() as usize
                - buffer.as_ptr() as *const _ as usize
                - 14 // there will be 14 zero bytes at the start
        };

        let mut valid = false;
        let mut fourteen_zeroes = false;
        let mut zeroes = 0;
        let start_index = if !valid {
            let mut i = 0;
            loop {
                let b = unsafe {
                    buffer
                        .as_ptr()
                        .add((i + idx as usize) % buffer.len())
                        .read_volatile()
                };

                if !valid {
                    if b == 0 {
                        zeroes += 1;
                    } else {
                        zeroes = 0;
                    }

                    if zeroes >= 14 {
                        fourteen_zeroes = true;
                    }

                    if fourteen_zeroes && b != 0 {
                        valid = true;
                    }
                }

                if valid {
                    break i;
                }

                i += 1;
            }
        } else {
            0
        };

        Ok(TraceResult {
            valid_start_index: start_index,
            valid_length: len,
        })
    }
}

/// Trace peripheral instance
pub trait Instance: crate::private::Sealed {
    /// Get a reference to the peripheral's underlying register block
    fn register_block(&self) -> &RegisterBlock;
}

impl Instance for crate::peripherals::TRACE0 {
    fn register_block(&self) -> &RegisterBlock {
        self
    }
}