//! A [sans-I/O](https://sans-io.readthedocs.io/how-to-sans-io.html)
//! decoder for the ITM and DWT packet protocol as specifed in the
//! [ARMv7-M architecture reference manual, Appendix
//! D4](https://developer.arm.com/documentation/ddi0403/ed/). Any
//! references in this code base refers to this document.
//!
//! Common abbreviations:
//!
//! - ITM: instrumentation trace macrocell;
//! - PC: program counter;
//! - DWT: data watchpoint and trace unit;
//! - MSB: most significant bit;
//! - BE: big-endian;
use bitmatch::bitmatch;
use bitvec::prelude::*;
use std::convert::TryInto;
/// The set of possible packet types that may be decoded.
///
/// Specification would suggest an implementation of two enum types, but
/// that structure is here flattened to simplify the implementation.
#[derive(Debug, Clone, PartialEq)]
pub enum TracePacket {
// Synchronization packet category (Appendix D4, p. 782)
/// A synchronization packet is a unique pattern in the bitstream.
/// It is identified and used to provide the alignment of other
/// packet bytes in the bitstream. (Appendix D4.2.1)
Sync,
// Protocol packet category (Appendix D4, p. 782)
/// Found in the bitstream if
///
/// - Software has written to an ITM stimulus port register when the
/// stimulus port output buffer is full.
/// - The DWT attempts to generate a hardware source packet when the
/// DWT output buffer is full.
/// - The local timestamp counter overflows.
///
/// See (Appendix D4.2.3).
Overflow,
/// A delta timestamp that measures the interval since the
/// generation of the last local timestamp and its relation to the
/// corresponding ITM/DWT data packets. (Appendix D4.2.4)
LocalTimestamp1 {
/// Timestamp value.
ts: u64,
/// Indicates the relationship between the generation of `ts`
/// and the corresponding ITM or DWT data packet.
data_relation: TimestampDataRelation,
},
/// A derivative of `LocalTimestamp1` for timestamp values between
/// 1-6. Always synchronous to te associated ITM/DWT data. (Appendix D4.2.4)
LocalTimestamp2 {
/// Timestamp value.
ts: u8,
},
/// An absolute timestamp based on the global timestamp clock that
/// contain the timestamp's lower-order bits. (Appendix D4.2.5)
GlobalTimestamp1 {
/// Lower-order bits of the timestamp; bits\[25:0\].
ts: u64,
/// Set if higher order bits output by the last GTS2 have
/// changed.
wrap: bool,
/// Set if the system has asserted a clock change input to the
/// processor since the last generated global timestamp.
clkch: bool,
},
/// An absolute timestamp based on the global timestamp clock that
/// contain the timestamp's higher-order bits. (Appendix D4.2.5)
GlobalTimestamp2 {
/// Higher-order bits of the timestamp value; bits\[63:26\] or
/// bits\[47:26\] depending on implementation.
ts: u64,
},
/// A packet that provides additional information about the
/// identified source (one of two possible, theoretically). On
/// ARMv7-M this packet is only used to denote on which ITM stimulus
/// port a payload was written. (Appendix D4.2.6)
Extension {
/// Source port page number.
page: u8,
},
// Source packet category
/// Contains the payload written to the ITM stimulus ports.
Instrumentation {
/// Stimulus port number.
port: u8,
/// Instrumentation data written to the stimulus port. MSB, BE.
payload: Vec<u8>,
},
/// One or more event counters have wrapped. (Appendix D4.3.1)
EventCounterWrap {
/// POSTCNT wrap (see Appendix C1, p. 732).
cyc: bool,
/// FOLDCNT wrap (see Appendix C1, p. 734).
fold: bool,
/// LSUCNT wrap (see Appendix C1, p. 734).
lsu: bool,
/// SLEEPCNT wrap (see Appendix C1, p. 734).
sleep: bool,
/// EXCCNT wrap (see Appendix C1, p. 734).
exc: bool,
/// CPICNT wrap (see Appendix C1, p. 734).
cpi: bool,
},
/// The processor has entered, exit, or returned to an exception.
/// (Appendix D4.3.2)
ExceptionTrace {
exception: ExceptionType,
action: ExceptionAction,
},
/// Periodic PC sample. (Appendix D4.3.3)
PCSample {
/// The value of the PC. `None` if periodic PC sleep packet.
pc: Option<u32>,
},
/// A DWT comparator matched a PC value. (Appendix D4.3.4)
DataTracePC {
/// The comparator number that generated the data.
comparator: u8,
/// The PC value for the instruction that caused the successful
/// address comparison.
pc: u32,
},
/// A DWT comparator matched an address. (Appendix D4.3.4)
DataTraceAddress {
/// The comparator number that generated the data.
comparator: u8,
/// Data address content; bits\[15:0\]. MSB, BE.
data: Vec<u8>,
},
/// A data trace packet with a value. (Appendix D4.3.4)
DataTraceValue {
/// The comparator number that generated the data.
comparator: u8,
/// Whether the data was read or written.
access_type: MemoryAccessType,
/// The data value. MSB, BE.
value: Vec<u8>,
},
}
/// Denotes the exception action taken by the processor. (Table D4-6)
#[derive(Debug, Clone, PartialEq)]
pub enum ExceptionAction {
/// Exception was entered.
Entered,
/// Exception was exited.
Exited,
/// Exception was returned to.
Returned,
}
/// Denotes the exception type (interrupt event) of the processor.
/// (Table B1-4)
#[derive(Debug, Clone, PartialEq)]
pub enum ExceptionType {
Reset,
Nmi,
HardFault,
MemManage,
BusFault,
UsageFault,
SVCall,
DebugMonitor,
PendSV,
SysTick,
ExternalInterrupt(usize),
}
/// Denotes the type of memory access.
#[derive(Debug, Clone, PartialEq)]
pub enum MemoryAccessType {
/// Memory was read.
Read,
/// Memory was written.
Write,
}
/// Indicates the relationship between the generation of the local
/// timestamp packet and the corresponding ITM or DWT data packet.
/// (Appendix D4.2.4)
#[derive(Debug, Clone, PartialEq)]
pub enum TimestampDataRelation {
/// The local timestamp value is synchronous to the corresponding
/// ITM or DWT data. The value in the TS field is the timestamp
/// counter value when the ITM or DWT packet is generated.
Sync,
/// The local timestamp value is delayed relative to the ITM or DWT
/// data. The value in the TS field is the timestamp counter value
/// when the Local timestamp packet is generated.
///
/// Note: the local timestamp value corresponding to the previous
/// ITM or DWT packet is unknown, but must be between the previous
/// and the current local timestamp values.
UnknownDelay,
/// Output of the ITM or DWT packet corresponding to this Local
/// timestamp packet is delayed relative to the associated event.
/// The value in the TS field is the timestamp counter value when
/// the ITM or DWT packets is generated.
///
/// This encoding indicates that the ITM or DWT packet was delayed
/// relative to other trace output packets.
AssocEventDelay,
/// Output of the ITM or DWT packet corresponding to this Local
/// timestamp packet is delayed relative to the associated event,
/// and this Local timestamp packet is delayed relative to the ITM
/// or DWT data. This is a combined condition of `UnknownDelay` and
/// `AssocEventDelay`.
UnknownAssocEventDelay,
}
/// A header or payload byte failed to be decoded. The state of the
/// decoder is now in an unknown state and manual intervention is
/// required.
#[derive(Debug, Clone, PartialEq)]
pub enum DecoderError {
/// Header is invalid and cannot be decoded.
InvalidHeader(u8),
/// The type discriminator ID in the hardware source packet header
/// is invalid or the associated payload is of wrong size.
InvalidHardwarePacket {
/// The discriminator ID. Potentially invalid.
disc_id: u8,
/// Associated payload. Potentially invalid length. MSB, BE.
payload: Vec<u8>,
},
/// The type discriminator ID in the hardware source packet header
/// is invalid.
InvalidHardwareDisc {
/// The discriminator ID. Potentially invalid.
disc_id: u8,
/// Associated payload length.
size: usize,
},
/// An exception trace packet refers to an invalid action or an
/// invalid exception number.
InvalidExceptionTrace {
/// The exception number.
exception: u16,
/// Numerical representation of the function associated with the
/// exception number.
function: u8,
},
/// The payload length of a PCSample packet is invalid.
InvalidPCSampleSize {
/// The payload constituting the PC value, of invalid size. MSB, BE.
payload: Vec<u8>,
},
/// The GlobalTimestamp2 packet does not contain a 48-bit or 64-bit
/// timestamp.
InvalidGTS2Size {
/// The payload constituting the timestamp, of invalid size. MSB, BE.
payload: Vec<u8>,
},
/// The number of zeroes in the Synchronization packet is less than
/// 47.
InvalidSyncSize(usize),
}
/// ITM and DWT packet protocol decoder.
pub struct Decoder {
/// The incoming bytes to the decoder.
pub incoming: BitVec,
/// The current state of the decoder.
pub state: DecoderState,
}
/// The decoder's possible states. The default decoder state is `Header`
/// and will always return there after a maximum of two steps. (E.g. if
/// the current state is `Syncing` or `HardwareSource`, the next state
/// is `Header` again.)
#[derive(Debug, Clone, PartialEq)]
pub enum DecoderState {
/// Next byte will be decoded as a header byte.
Header,
/// Next zero bits will be assumed to be part of a a Synchronization
/// packet until a one is encountered.
Syncing(usize),
/// Next bytes will be assumed to be part of an Instrumentation
/// packet, until `payload` contains `expected_size` bytes.
Instrumentation {
port: u8,
payload: Vec<u8>,
expected_size: usize,
},
/// Next bytes will be assumed to be part of a Hardware source
/// packet, until `payload` contains `expected_size` bytes.
HardwareSource {
disc_id: u8,
payload: Vec<u8>,
expected_size: usize,
},
/// Next bytes will be assumed to be part of a LocalTimestamp{1,2}
/// packet, until the MSB is set.
LocalTimestamp {
data_relation: TimestampDataRelation,
payload: Vec<u8>,
},
/// Next bytes will be assumed to be part of a GlobalTimestamp1
/// packet, until the MSB is set.
GlobalTimestamp1 { payload: Vec<u8> },
/// Next bytes will be assumed to be part of a GlobalTimestamp2
/// packet, until the MSB is set.
GlobalTimestamp2 { payload: Vec<u8> },
}
impl Decoder {
pub fn new() -> Self {
Decoder {
incoming: BitVec::new(),
state: DecoderState::Header,
}
}
/// Feed trace data into the decoder.
pub fn feed(&mut self, data: Vec<u8>) {
self.incoming.extend(BitVec::<LocalBits, _>::from_vec(data));
}
/// Pull the next decoded ITM packet from the decoder, if any and able.
pub fn pull(&mut self) -> Result<Option<TracePacket>, DecoderError> {
loop {
match self.state {
DecoderState::Syncing(_) => return self.handle_sync(),
// Decode bytes until a packet is generated, or until we run out of bytes.
_ if self.incoming.len() >= 8 => match {
// XXX do we copy anything here?
let b = self.incoming[0..=7].load::<u8>();
self.incoming = self.incoming[8..].into();
self.process_byte(b)
} {
Ok(Some(packet)) => return Ok(Some(packet)),
Ok(None) => continue,
e => return e,
},
_ => return Ok(None),
}
}
}
/// Read zeros from the bitstream until the first bit is set. This
/// realigns the incoming bitstream for further processing, which
/// may not be 8-bit aligned.
fn handle_sync(&mut self) -> Result<Option<TracePacket>, DecoderError> {
const MIN_ZEROS: usize = 47;
if let DecoderState::Syncing(mut count) = self.state {
while let Some(bit) = {
self.incoming.rotate_left(1);
self.incoming.pop()
} {
if !bit && count < MIN_ZEROS {
count += 1;
continue;
} else if bit && count >= MIN_ZEROS {
self.state = DecoderState::Header;
return Ok(Some(TracePacket::Sync));
} else {
return Err(DecoderError::InvalidSyncSize(count));
}
}
} else {
unreachable!();
}
Ok(None)
}
/// Processes a single byte from the bitstream and changes decoder state if necessary.
fn process_byte(&mut self, b: u8) -> Result<Option<TracePacket>, DecoderError> {
let packet = match &mut self.state {
DecoderState::Header => self.decode_header(b),
DecoderState::Syncing(_count) => unreachable!(),
DecoderState::HardwareSource {
disc_id,
payload,
expected_size,
} => {
payload.push(b);
if payload.len() == *expected_size {
match Decoder::handle_hardware_source(*disc_id, payload.to_vec()) {
Ok(packet) => Ok(Some(packet)),
Err(e) => Err(e),
}
} else {
Ok(None)
}
}
DecoderState::LocalTimestamp {
data_relation,
payload,
} => {
let last_byte = (b >> 7) & 1 == 0;
payload.push(b);
if last_byte {
Ok(Some(TracePacket::LocalTimestamp1 {
data_relation: data_relation.clone(),
ts: Decoder::extract_timestamp(payload.to_vec(), 27),
}))
} else {
Ok(None)
}
}
DecoderState::GlobalTimestamp1 { payload } => {
let last_byte = (b >> 7) & 1 == 0;
payload.push(b);
if last_byte {
Ok(Some(TracePacket::GlobalTimestamp1 {
ts: Decoder::extract_timestamp(payload.to_vec(), 25),
clkch: (payload.last().unwrap() & (1 << 5)) >> 5 == 1,
wrap: (payload.last().unwrap() & (1 << 6)) >> 6 == 1,
}))
} else {
Ok(None)
}
}
DecoderState::GlobalTimestamp2 { payload } => {
let last_byte = (b >> 7) & 1 == 0;
payload.push(b);
if last_byte {
Ok(Some(TracePacket::GlobalTimestamp2 {
ts: Decoder::extract_timestamp(
payload.to_vec(),
match payload.len() {
4 => 47 - 26, // 48 bit timestamp
6 => 63 - 26, // 64 bit timestamp
_ => {
return Err(DecoderError::InvalidGTS2Size {
payload: payload.to_vec(),
})
}
},
),
}))
} else {
Ok(None)
}
}
DecoderState::Instrumentation {
port,
payload,
expected_size,
} => {
payload.push(b);
if payload.len() == *expected_size {
Ok(Some(TracePacket::Instrumentation {
port: *port,
payload: payload.to_vec(),
}))
} else {
Ok(None)
}
}
};
if let Ok(Some(_)) = packet {
self.state = DecoderState::Header;
}
packet
}
// TODO template this for u32, u64?
fn extract_timestamp(payload: Vec<u8>, max_len: u64) -> u64 {
// Decode the first N - 1 payload bytes
let (rtail, head) = payload.split_at(payload.len() - 1);
let mut ts: u64 = 0;
for (i, b) in rtail.iter().enumerate() {
ts |= ((b & !(1 << 7)) as u64) // mask out continuation bit
<< (7 * i);
}
// Mask out the timestamp's MSBs and shift them into the final
// value.
let shift = 7 - (max_len % 7);
let mask: u8 = 0xFFu8.wrapping_shl(shift.try_into().unwrap()) >> shift;
ts | (((head[0] & mask) as u64) << (7 * rtail.len()))
}
/// Decodes the payload of a hardware source packet, if able.
#[bitmatch]
fn handle_hardware_source(disc_id: u8, payload: Vec<u8>) -> Result<TracePacket, DecoderError> {
match disc_id {
0 => {
// event counter wrap
if payload.len() != 1 {
return Err(DecoderError::InvalidHardwarePacket { disc_id, payload });
}
let b = payload[0];
Ok(TracePacket::EventCounterWrap {
cyc: b & (1 << 5) != 0,
fold: b & (1 << 4) != 0,
lsu: b & (1 << 3) != 0,
sleep: b & (1 << 2) != 0,
exc: b & (1 << 1) != 0,
cpi: b & (1 << 0) != 0,
})
}
1 => {
// exception trace
if payload.len() != 2 {
return Err(DecoderError::InvalidHardwarePacket { disc_id, payload });
}
let exception_number = ((payload[1] as u16 & 1) << 8) | payload[0] as u16;
let function = (payload[1] >> 4) & 0b11;
return Ok(TracePacket::ExceptionTrace {
exception: match exception_number {
1 => ExceptionType::Reset,
2 => ExceptionType::Nmi,
3 => ExceptionType::HardFault,
4 => ExceptionType::MemManage,
5 => ExceptionType::BusFault,
6 => ExceptionType::UsageFault,
11 => ExceptionType::SVCall,
12 => ExceptionType::DebugMonitor,
14 => ExceptionType::PendSV,
15 => ExceptionType::SysTick,
n if n >= 16 => ExceptionType::ExternalInterrupt(n as usize - 16),
_ => {
return Err(DecoderError::InvalidExceptionTrace {
exception: exception_number,
function,
})
}
},
action: match function {
0b01 => ExceptionAction::Entered,
0b10 => ExceptionAction::Exited,
0b11 => ExceptionAction::Returned,
_ => {
return Err(DecoderError::InvalidExceptionTrace {
exception: exception_number,
function,
})
}
},
});
}
2 => {
// PC sample
match payload.len() {
1 if payload[0] == 0 => Ok(TracePacket::PCSample { pc: None }),
4 => Ok(TracePacket::PCSample {
pc: Some(u32::from_le_bytes(payload.try_into().unwrap())),
}),
_ => Err(DecoderError::InvalidPCSampleSize { payload }),
}
}
8..=23 => {
// data trace
#[bitmatch]
let "???t_tccd" = disc_id; // we have already masked out bit[2:0]
let comparator = c;
match (t, d, payload.len()) {
(0b01, 0, 4) => {
// PC value packet
Ok(TracePacket::DataTracePC {
comparator,
pc: u32::from_le_bytes(payload.try_into().unwrap()),
})
}
(0b01, 1, 2) => {
// address packet
Ok(TracePacket::DataTraceAddress {
comparator,
data: payload,
})
}
(0b10, d, _) => {
// data value packet
Ok(TracePacket::DataTraceValue {
comparator,
access_type: if d == 0 {
MemoryAccessType::Write
} else {
MemoryAccessType::Read
},
value: payload,
})
}
_ => Err(DecoderError::InvalidHardwarePacket { disc_id, payload }),
}
}
_ => unreachable!(), // we already verify the discriminator when we decode the header
}
}
/// Decodes the header byte of a packet, and enters the appropriate decoder state, if able.
#[bitmatch]
fn decode_header(&mut self, header: u8) -> Result<Option<TracePacket>, DecoderError> {
fn translate_ss(ss: u8) -> usize {
// See (Appendix D4.2.8, Table D4-4)
(match ss {
0b01 => 2,
0b10 => 3,
0b11 => 5,
_ => unreachable!(),
}) - 1 // ss would include the header byte, but it has already been processed
}
#[bitmatch]
match header {
// Synchronization packet category
"0000_0000" => {
self.state = DecoderState::Syncing(8);
}
// Protocol packet category
"0111_0000" => {
return Ok(Some(TracePacket::Overflow));
}
"11rr_0000" => {
// Local timestamp, format 1 (LTS1)
let tc = r; // relationship with corresponding data
self.state = DecoderState::LocalTimestamp {
data_relation: match tc {
0b00 => TimestampDataRelation::Sync,
0b01 => TimestampDataRelation::UnknownDelay,
0b10 => TimestampDataRelation::AssocEventDelay,
0b11 => TimestampDataRelation::UnknownAssocEventDelay,
_ => unreachable!(),
},
payload: vec![],
};
}
"0ttt_0000" => {
// Local timestamp, format 2 (LTS2)
return Ok(Some(TracePacket::LocalTimestamp2 { ts: t }));
}
"1001_0100" => {
// Global timestamp, format 1 (GTS1)
self.state = DecoderState::GlobalTimestamp1 { payload: vec![] };
}
"1011_0100" => {
// Global timestamp, format 2(GTS2)
self.state = DecoderState::GlobalTimestamp2 { payload: vec![] };
}
"0ppp_1000" => {
// Extension packet
return Ok(Some(TracePacket::Extension { page: p }));
}
// Source packet category
"aaaa_a0ss" => {
// Instrumentation packet
self.state = DecoderState::Instrumentation {
port: a,
payload: vec![],
expected_size: translate_ss(s),
};
}
"aaaa_a1ss" => {
// Hardware source packet
let disc_id = a;
if !(0..=2).contains(&disc_id) && !(8..=23).contains(&disc_id) {
return Err(DecoderError::InvalidHardwareDisc {
disc_id,
size: s.into(),
});
}
self.state = DecoderState::HardwareSource {
disc_id,
payload: vec![],
expected_size: translate_ss(s),
};
}
"hhhh_hhhh" => return Err(DecoderError::InvalidHeader(h)),
}
Ok(None)
}
}
impl Default for Decoder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decode_sync_packet() {
let mut trace_data: Vec<u8> = [0; 47 / 8].to_vec();
trace_data.push(1 << 7);
let mut decoder = Decoder::new();
decoder.feed(trace_data);
assert_eq!(decoder.pull(), Ok(Some(TracePacket::Sync)));
}
#[test]
fn decode_overflow_packet() {
let mut decoder = Decoder::new();
decoder.feed([0b0111_0000].to_vec());
assert_eq!(decoder.pull(), Ok(Some(TracePacket::Overflow)));
}
#[test]
fn decode_local_timestamp_packets() {
let mut decoder = Decoder::new();
#[rustfmt::skip]
decoder.feed([
// LTS1
0b1100_0000,
0b1100_1001,
0b0000_0001,
// LTS2
0b0101_0000,
].to_vec());
for packet in [
TracePacket::LocalTimestamp1 {
ts: 0b11001001,
data_relation: TimestampDataRelation::Sync,
},
TracePacket::LocalTimestamp2 { ts: 0b101 },
]
.iter()
{
assert_eq!(decoder.pull(), Ok(Some(packet.clone())));
}
}
#[test]
fn extract_timestamp() {
#[rustfmt::skip]
let ts: Vec<u8> = [
0b1000_0000,
0b1000_0000,
0b1000_0000,
0b0000_0000,
].to_vec();
assert_eq!(Decoder::extract_timestamp(ts, 25), 0);
#[rustfmt::skip]
let ts: Vec<u8> = [
0b1000_0001,
0b1000_0111,
0b1001_1111,
0b0111_1111
].to_vec();
assert_eq!(
Decoder::extract_timestamp(ts, 27),
0b1111111_0011111_0000111_0000001,
);
#[rustfmt::skip]
let ts: Vec<u8> = [
0b1000_0001,
0b1000_0111,
0b1001_1111,
0b1111_1111
].to_vec();
assert_eq!(
Decoder::extract_timestamp(ts, 25),
0b11111_0011111_0000111_0000001,
);
}
#[test]
fn decode_global_timestamp_packets() {
let mut decoder = Decoder::new();
#[rustfmt::skip]
decoder.feed([
// GTS1
0b1001_0100,
0b1000_0000,
0b1010_0000,
0b1000_0100,
0b0110_0000,
// GTS2 (48-bit)
0b1011_0100,
0b1011_1101,
0b1111_0100,
0b1001_0001,
0b0000_0001,
// GTS2 (64-bit)
0b1011_0100,
0b1011_1101,
0b1111_0100,
0b1001_0001,
0b1000_0001,
0b1111_0100,
0b0000_0111,
].to_vec());
for packet in [
TracePacket::GlobalTimestamp1 {
ts: 0b00000_0000100_0100000_0000000,
wrap: true,
clkch: true,
},
TracePacket::GlobalTimestamp2 {
ts: 0b1_0010001_1110100_0111101,
},
TracePacket::GlobalTimestamp2 {
ts: 0b111_1110100_0000001_0010001_1110100_0111101,
},
]
.iter()
{
assert_eq!(decoder.pull(), Ok(Some(packet.clone())));
}
}
#[test]
fn decode_extention_packet() {
let mut decoder = Decoder::new();
#[rustfmt::skip]
decoder.feed([
0b0111_1000,
].to_vec());
assert_eq!(
decoder.pull(),
Ok(Some(TracePacket::Extension { page: 0b111 }))
);
}
#[test]
fn decode_instrumentation_packet() {
let mut decoder = Decoder::new();
#[rustfmt::skip]
decoder.feed([
0b1000_1011,
0b0000_0011,
0b0000_1111,
0b0011_1111,
0b1111_1111,
].to_vec());
assert_eq!(
decoder.pull(),
Ok(Some(TracePacket::Instrumentation {
port: 0b1000_1,
#[rustfmt::skip]
payload: [
0b0000_0011,
0b0000_1111,
0b0011_1111,
0b1111_1111,
].to_vec(),
}))
);
}
#[test]
fn decode_eventcounterwrap_packet() {
let mut decoder = Decoder::new();
#[rustfmt::skip]
decoder.feed([
0b0000_0101,
0b0010_1010,
].to_vec());
assert_eq!(
decoder.pull(),
Ok(Some(TracePacket::EventCounterWrap {
cyc: true,
fold: false,
lsu: true,
sleep: false,
exc: true,
cpi: false,
}))
);
}
#[test]
fn decode_exceptiontrace_packet() {
let mut decoder = Decoder::new();
#[rustfmt::skip]
decoder.feed([
0b0000_1110,
0b0010_0000,
0b0011_0000
].to_vec());
assert_eq!(
decoder.pull(),
Ok(Some(TracePacket::ExceptionTrace {
exception: ExceptionType::ExternalInterrupt(16),
action: ExceptionAction::Returned,
}))
);
}
#[test]
fn decode_pcsample_packet() {
let mut decoder = Decoder::new();
#[rustfmt::skip]
decoder.feed([
// PC sample (not sleeping)
0b0001_0111,
0b0000_0011,
0b0000_1111,
0b0011_1111,
0b1111_1111,
// PC sample (sleeping)
0b0001_0101,
0b0000_0000,
].to_vec());
for packet in [
TracePacket::PCSample {
pc: Some(0b11111111_00111111_00001111_00000011),
},
TracePacket::PCSample { pc: None },
]
.iter()
{
assert_eq!(decoder.pull(), Ok(Some(packet.clone())));
}
}
#[test]
fn decode_datatracepc_packet() {
let mut decoder = Decoder::new();
#[rustfmt::skip]
decoder.feed([
0b0111_0111,
0b0000_0011,
0b0000_1111,
0b0011_1111,
0b1111_1111,
].to_vec());
assert_eq!(
decoder.pull(),
Ok(Some(TracePacket::DataTracePC {
comparator: 0b11,
pc: 0b11111111_00111111_00001111_00000011,
}))
);
}
#[test]
fn decode_datatraceaddress_packet() {
let mut decoder = Decoder::new();
#[rustfmt::skip]
decoder.feed([
0b0110_1110,
0b0000_0011,
0b0000_1111,
].to_vec());
assert_eq!(
decoder.pull(),
Ok(Some(TracePacket::DataTraceAddress {
comparator: 0b10,
#[rustfmt::skip]
data: [
0b0000_0011,
0b0000_1111,
].to_vec(),
}))
);
}
#[test]
fn decode_datatracevalue_packet() {
let mut decoder = Decoder::new();
#[rustfmt::skip]
decoder.feed([
// four-byte (word) payload
0b1010_1111,
0b0000_0011,
0b0000_1111,
0b0011_1111,
0b1111_1111,
// two-byte (halfword) payload
0b1010_1110,
0b0000_0011,
0b0000_1111,
// one-byte (byte) payload
0b1010_1101,
0b0000_0011,
].to_vec());
for packet in [
TracePacket::DataTraceValue {
comparator: 0b10,
access_type: MemoryAccessType::Read,
#[rustfmt::skip]
value: [
0b0000_0011,
0b0000_1111,
0b0011_1111,
0b1111_1111,
].to_vec(),
},
TracePacket::DataTraceValue {
comparator: 0b10,
access_type: MemoryAccessType::Read,
#[rustfmt::skip]
value: [
0b0000_0011,
0b0000_1111,
].to_vec(),
},
TracePacket::DataTraceValue {
comparator: 0b10,
access_type: MemoryAccessType::Read,
#[rustfmt::skip]
value: [
0b0000_0011,
].to_vec(),
},
]
.iter()
{
assert_eq!(decoder.pull(), Ok(Some(packet.clone())));
}
}
}