* Use latest `esp32c6-lp` package and fix breaking changes * `esp32c2-hal` and `esp32c3-hal` mostly building (with no features...) * C6 and H2 sort of close I guess, idk * どうして。。。 * *Incoherent screaming* * HAHA ESP32-HAL BUILDS!!! * C2 and C3 are building! * S2 builds! * S3 builds! * Fix ESP32 (again) * ??? * Use git dependencies instead of local paths for PACs * Fix some errors after a dodgy rebase * Remove unnecessary constants for ESP32 * Fix RSA multiplication (math is hard)
201 lines
5.7 KiB
Rust
201 lines
5.7 KiB
Rust
//! # RISCV 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.
|
||
//!
|
||
//! ## Example
|
||
//! ```no_run
|
||
//! let mut trace = Trace::new(peripherals.TRACE);
|
||
//! let buffer = unsafe { &mut BUFFER[..] };
|
||
//! trace.start_trace(buffer);
|
||
//! // traced code
|
||
//! println!("Hello");
|
||
//! // 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},
|
||
system::PeripheralClockControl,
|
||
};
|
||
|
||
/// Errors returned from [Trace::stop_trace]
|
||
#[derive(Debug, Clone, Copy)]
|
||
pub enum Error {
|
||
NotStarted,
|
||
}
|
||
|
||
/// Returned by [Trace::stop_trace]
|
||
#[derive(Debug, Clone, Copy)]
|
||
pub struct TraceResult {
|
||
pub valid_start_index: usize,
|
||
pub valid_length: usize,
|
||
}
|
||
|
||
/// TRACE Encoder Instance
|
||
pub struct Trace<'d> {
|
||
peripheral: PeripheralRef<'d, crate::peripherals::TRACE>,
|
||
buffer: Option<&'d mut [u8]>,
|
||
}
|
||
|
||
impl<'d> Trace<'d> {
|
||
/// Construct a new instance
|
||
pub fn new(peripheral: impl Peripheral<P = crate::peripherals::TRACE> + 'd) -> Self {
|
||
crate::into_ref!(peripheral);
|
||
|
||
PeripheralClockControl::enable(crate::system::Peripheral::Trace);
|
||
|
||
Self {
|
||
peripheral,
|
||
buffer: None,
|
||
}
|
||
}
|
||
|
||
/// Start tracing, writing data into the `buffer`
|
||
pub fn start_trace(&mut self, buffer: &'d mut [u8]) {
|
||
self.peripheral.mem_start_addr().modify(|_, w| {
|
||
w.mem_staet_addr()
|
||
.variant(buffer.as_ptr() as *const _ as u32)
|
||
});
|
||
self.peripheral.mem_end_addr().modify(|_, w| {
|
||
w.mem_end_addr()
|
||
.variant((buffer.as_ptr() as *const _ as u32) + (buffer.len() as u32))
|
||
});
|
||
self.peripheral
|
||
.mem_addr_update()
|
||
.write(|w| w.mem_current_addr_update().set_bit());
|
||
|
||
// won't set bit in int-raw without enabling
|
||
self.peripheral
|
||
.intr_ena()
|
||
.modify(|_, w| w.mem_full_intr_ena().set_bit());
|
||
|
||
// for now always use looping mode
|
||
self.peripheral
|
||
.trigger()
|
||
.write(|w| w.mem_loop().set_bit().restart_ena().set_bit());
|
||
|
||
self.peripheral.intr_clr().write(|w| {
|
||
w.fifo_overflow_intr_clr()
|
||
.set_bit()
|
||
.mem_full_intr_clr()
|
||
.set_bit()
|
||
});
|
||
|
||
self.buffer.replace(buffer);
|
||
self.peripheral.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> {
|
||
self.peripheral
|
||
.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();
|
||
|
||
loop {
|
||
if self
|
||
.peripheral
|
||
.fifo_status()
|
||
.read()
|
||
.fifo_empty()
|
||
.bit_is_set()
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
let overflow = self.peripheral.intr_raw().read().mem_full_intr_raw().bit();
|
||
let idx = if overflow {
|
||
self.peripheral
|
||
.mem_current_addr()
|
||
.read()
|
||
.mem_current_addr()
|
||
.bits()
|
||
- &buffer as *const _ as u32
|
||
} else {
|
||
0
|
||
};
|
||
|
||
let len = if overflow {
|
||
buffer.len()
|
||
} else {
|
||
self.peripheral
|
||
.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() as *const _ as *const u8)
|
||
.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,
|
||
})
|
||
}
|
||
}
|