itm_decode/lib.rs
1//! A decoder for the ITM and DWT packet protocol as specifed in the
2//! [ARMv7-M architecture reference manual, Appendix
3//! D4](https://developer.arm.com/documentation/ddi0403/ed/). Any
4//! references in this code base refers to this document.
5//!
6//! Common abbreviations:
7//!
8//! - ITM: instrumentation trace macrocell;
9//! - PC: program counter;
10//! - DWT: data watchpoint and trace unit;
11//! - MSB: most significant bit;
12//! - BE: big-endian;
13
14use bitmatch::bitmatch;
15use bitvec::prelude::*;
16use std::convert::TryInto;
17
18#[cfg(feature = "serde")]
19use serde_crate::{Deserialize, Serialize};
20
21/// Re-exports for exception types of the `cortex-m` crate for `serde`
22/// purposes.
23pub mod cortex_m {
24 /// Denotes the exception type (interrupt event) of the processor.
25 /// (Table B1-4)
26 pub use cortex_m::peripheral::scb::{Exception, VectActive};
27
28 /// Verbatim copy of used `cortex_m` enums for serde functionality.
29 /// Should not be used directly. Public because serde requires it.
30 /// See <https://serde.rs/remote-derive.html>
31 #[cfg(feature = "serde")]
32 pub mod serde {
33 use super::{Exception, VectActive};
34 use serde_crate::{Deserialize, Serialize};
35
36 #[derive(Serialize, Deserialize)]
37 #[serde(crate = "serde_crate", remote = "Exception")]
38 pub enum ExceptionDef {
39 NonMaskableInt,
40 HardFault,
41 MemoryManagement,
42 BusFault,
43 UsageFault,
44 SecureFault,
45 SVCall,
46 DebugMonitor,
47 PendSV,
48 SysTick,
49 }
50
51 #[derive(Serialize, Deserialize)]
52 #[serde(crate = "serde_crate", remote = "VectActive")]
53 pub enum VectActiveDef {
54 ThreadMode,
55 Exception(#[serde(with = "ExceptionDef")] Exception),
56 Interrupt { irqn: u8 },
57 }
58 }
59}
60
61/// The set of valid packet types that can be decoded.
62#[derive(Debug, Clone, PartialEq)]
63#[cfg_attr(
64 feature = "serde",
65 derive(Serialize, Deserialize),
66 serde(crate = "serde_crate")
67)]
68pub enum TracePacket {
69 // Synchronization packet category (Appendix D4, p. 782)
70 /// A synchronization packet is a unique pattern in the bitstream.
71 /// It is identified and used to provide the alignment of other
72 /// packet bytes in the bitstream. (Appendix D4.2.1)
73 Sync,
74
75 // Protocol packet category (Appendix D4, p. 782)
76 /// Found in the bitstream if
77 ///
78 /// - Software has written to an ITM stimulus port register when the
79 /// stimulus port output buffer is full.
80 /// - The DWT attempts to generate a hardware source packet when the
81 /// DWT output buffer is full.
82 /// - The local timestamp counter overflows.
83 ///
84 /// See (Appendix D4.2.3).
85 Overflow,
86
87 /// A delta timestamp that measures the interval since the
88 /// generation of the last local timestamp and its relation to the
89 /// corresponding ITM/DWT data packets. (Appendix D4.2.4)
90 LocalTimestamp1 {
91 /// Timestamp value.
92 ts: u64,
93
94 /// Indicates the relationship between the generation of `ts`
95 /// and the corresponding ITM or DWT data packet.
96 data_relation: TimestampDataRelation,
97 },
98
99 /// A derivative of `LocalTimestamp1` for timestamp values between
100 /// 1-6. Always synchronous to te associated ITM/DWT data. (Appendix D4.2.4)
101 LocalTimestamp2 {
102 /// Timestamp value.
103 ts: u8,
104 },
105
106 /// An absolute timestamp based on the global timestamp clock that
107 /// contain the timestamp's lower-order bits. (Appendix D4.2.5)
108 GlobalTimestamp1 {
109 /// Lower-order bits of the timestamp; bits\[25:0\].
110 ts: u64,
111
112 /// Set if higher order bits output by the last GTS2 have
113 /// changed.
114 wrap: bool,
115
116 /// Set if the system has asserted a clock change input to the
117 /// processor since the last generated global timestamp.
118 clkch: bool,
119 },
120
121 /// An absolute timestamp based on the global timestamp clock that
122 /// contain the timestamp's higher-order bits. (Appendix D4.2.5)
123 GlobalTimestamp2 {
124 /// Higher-order bits of the timestamp value; bits\[63:26\] or
125 /// bits\[47:26\] depending on implementation.
126 ts: u64,
127 },
128
129 /// A packet that provides additional information about the
130 /// identified source (one of two possible, theoretically). On
131 /// ARMv7-M this packet is only used to denote on which ITM stimulus
132 /// port a payload was written. (Appendix D4.2.6)
133 Extension {
134 /// Source port page number.
135 page: u8,
136 },
137
138 // Source packet category
139 /// Contains the payload written to the ITM stimulus ports.
140 Instrumentation {
141 /// Stimulus port number.
142 port: u8,
143
144 /// Instrumentation data written to the stimulus port. MSB, BE.
145 payload: Vec<u8>,
146 },
147
148 /// One or more event counters have wrapped. (Appendix D4.3.1)
149 EventCounterWrap {
150 /// POSTCNT wrap (see Appendix C1, p. 732).
151 cyc: bool,
152 /// FOLDCNT wrap (see Appendix C1, p. 734).
153 fold: bool,
154 /// LSUCNT wrap (see Appendix C1, p. 734).
155 lsu: bool,
156 /// SLEEPCNT wrap (see Appendix C1, p. 734).
157 sleep: bool,
158 /// EXCCNT wrap (see Appendix C1, p. 734).
159 exc: bool,
160 /// CPICNT wrap (see Appendix C1, p. 734).
161 cpi: bool,
162 },
163
164 /// The processor has entered, exit, or returned to an exception.
165 /// (Appendix D4.3.2)
166 ExceptionTrace {
167 #[cfg_attr(feature = "serde", serde(with = "cortex_m::serde::VectActiveDef"))]
168 exception: cortex_m::VectActive,
169 action: ExceptionAction,
170 },
171
172 /// Periodic PC sample. (Appendix D4.3.3)
173 PCSample {
174 /// The value of the PC. `None` if periodic PC sleep packet.
175 pc: Option<u32>,
176 },
177
178 /// A DWT comparator matched a PC value. (Appendix D4.3.4)
179 DataTracePC {
180 /// The comparator number that generated the data.
181 comparator: u8,
182
183 /// The PC value for the instruction that caused the successful
184 /// address comparison.
185 pc: u32,
186 },
187
188 /// A DWT comparator matched an address. (Appendix D4.3.4)
189 DataTraceAddress {
190 /// The comparator number that generated the data.
191 comparator: u8,
192
193 /// Data address content; bits\[15:0\]. MSB, BE.
194 data: Vec<u8>,
195 },
196
197 /// A data trace packet with a value. (Appendix D4.3.4)
198 DataTraceValue {
199 /// The comparator number that generated the data.
200 comparator: u8,
201
202 /// Whether the data was read or written.
203 access_type: MemoryAccessType,
204
205 /// The data value. MSB, BE.
206 value: Vec<u8>,
207 },
208}
209
210/// Denotes the action taken by the processor by a given exception. (Table D4-6)
211#[derive(Debug, Clone, PartialEq)]
212#[cfg_attr(
213 feature = "serde",
214 derive(Serialize, Deserialize),
215 serde(crate = "serde_crate")
216)]
217pub enum ExceptionAction {
218 /// Exception was entered.
219 Entered,
220
221 /// Exception was exited.
222 Exited,
223
224 /// Exception was returned to.
225 Returned,
226}
227
228/// Denotes the type of memory access.
229#[derive(Debug, Clone, PartialEq)]
230#[cfg_attr(
231 feature = "serde",
232 derive(Serialize, Deserialize),
233 serde(crate = "serde_crate")
234)]
235pub enum MemoryAccessType {
236 /// Memory was read.
237 Read,
238
239 /// Memory was written.
240 Write,
241}
242
243/// Indicates the relationship between the generation of the local
244/// timestamp packet and the corresponding ITM or DWT data packet.
245/// (Appendix D4.2.4)
246#[derive(Debug, Clone, PartialEq)]
247#[cfg_attr(
248 feature = "serde",
249 derive(Serialize, Deserialize),
250 serde(crate = "serde_crate")
251)]
252pub enum TimestampDataRelation {
253 /// The local timestamp value is synchronous to the corresponding
254 /// ITM or DWT data. The value in the TS field is the timestamp
255 /// counter value when the ITM or DWT packet is generated.
256 Sync,
257
258 /// The local timestamp value is delayed relative to the ITM or DWT
259 /// data. The value in the TS field is the timestamp counter value
260 /// when the Local timestamp packet is generated.
261 ///
262 /// Note: the local timestamp value corresponding to the previous
263 /// ITM or DWT packet is unknown, but must be between the previous
264 /// and the current local timestamp values.
265 UnknownDelay,
266
267 /// Output of the ITM or DWT packet corresponding to this Local
268 /// timestamp packet is delayed relative to the associated event.
269 /// The value in the TS field is the timestamp counter value when
270 /// the ITM or DWT packets is generated.
271 ///
272 /// This encoding indicates that the ITM or DWT packet was delayed
273 /// relative to other trace output packets.
274 AssocEventDelay,
275
276 /// Output of the ITM or DWT packet corresponding to this Local
277 /// timestamp packet is delayed relative to the associated event,
278 /// and this Local timestamp packet is delayed relative to the ITM
279 /// or DWT data. This is a combined condition of `UnknownDelay` and
280 /// `AssocEventDelay`.
281 UnknownAssocEventDelay,
282}
283
284/// A header or payload byte failed to be decoded.
285#[derive(Debug, Clone, PartialEq, thiserror::Error)]
286#[cfg_attr(
287 feature = "serde",
288 derive(Serialize, Deserialize),
289 serde(crate = "serde_crate")
290)]
291pub enum MalformedPacket {
292 /// Header is invalid and cannot be decoded.
293 #[error("Header is invalid and cannot be decoded: {}", format!("{:#b}", .0))]
294 InvalidHeader(u8),
295
296 /// The type discriminator ID in the hardware source packet header
297 /// is invalid or the associated payload is of wrong size.
298 #[error("Hardware source packet type discriminator ID ({disc_id}) or payload length ({}) is invalid", .payload.len())]
299 InvalidHardwarePacket {
300 /// The discriminator ID. Potentially invalid.
301 disc_id: u8,
302
303 /// Associated payload. Potentially invalid length. MSB, BE.
304 payload: Vec<u8>,
305 },
306
307 /// The type discriminator ID in the hardware source packet header
308 /// is invalid.
309 #[error("Hardware source packet discriminator ID is invalid: {disc_id}")]
310 InvalidHardwareDisc {
311 /// The discriminator ID. Potentially invalid.
312 disc_id: u8,
313
314 /// Associated payload length.
315 size: usize,
316 },
317
318 /// An exception trace packet refers to an invalid action or an
319 /// invalid exception number.
320 #[error("IRQ number {exception} and/or action {function} is invalid")]
321 InvalidExceptionTrace {
322 /// The exception number.
323 exception: u16,
324
325 /// Numerical representation of the function associated with the
326 /// exception number.
327 function: u8,
328 },
329
330 /// The payload length of a PCSample packet is invalid.
331 #[error("Payload length of PC sample is invalid: {}", .payload.len())]
332 InvalidPCSampleSize {
333 /// The payload constituting the PC value, of invalid size. MSB, BE.
334 payload: Vec<u8>,
335 },
336
337 /// The GlobalTimestamp2 packet does not contain a 48-bit or 64-bit
338 /// timestamp.
339 #[error("GlobalTimestamp2 packet does not contain a 48-bit or 64-bit timestamp")]
340 InvalidGTS2Size {
341 /// The payload constituting the timestamp, of invalid size. MSB, BE.
342 payload: Vec<u8>,
343 },
344
345 /// The number of zeroes in the Synchronization packet is less than
346 /// 47.
347 #[error(
348 "The number of zeroes in the Synchronization packet is less than expected: {0} < {}",
349 SYNC_MIN_ZEROS
350 )]
351 InvalidSync(usize),
352
353 /// A source packet (from software or hardware) contains an invalid
354 /// expected payload size.
355 #[error(
356 "A source packet (from software or hardware) contains an invalid expected payload size"
357 )]
358 InvalidSourcePayload {
359 /// The header which contains the invalid payload size.
360 header: u8,
361
362 /// The invalid payload size. See (Appendix D4.2.8, Table D4-4).
363 size: u8,
364 },
365}
366
367const SYNC_MIN_ZEROS: usize = 47;
368
369/// The decoder's possible states. The default decoder state is `Header`
370/// and will always return there after a maximum of two steps. (E.g. if
371/// the current state is `Syncing` or `HardwareSource`, the next state
372/// is `Header` again.)
373enum PacketStub {
374 /// Next zero bits will be assumed to be part of a a Synchronization
375 /// packet until a set bit is encountered.
376 Sync(usize),
377
378 /// Next bytes will be assumed to be part of an Instrumentation
379 /// packet, until `payload` contains `expected_size` bytes.
380 Instrumentation { port: u8, expected_size: usize },
381
382 /// Next bytes will be assumed to be part of a Hardware source
383 /// packet, until `payload` contains `expected_size` bytes.
384 HardwareSource { disc_id: u8, expected_size: usize },
385
386 /// Next bytes will be assumed to be part of a LocalTimestamp{1,2}
387 /// packet, until the MSB is set.
388 LocalTimestamp {
389 data_relation: TimestampDataRelation,
390 },
391
392 /// Next bytes will be assumed to be part of a GlobalTimestamp1
393 /// packet, until the MSB is set.
394 GlobalTimestamp1,
395
396 /// Next bytes will be assumed to be part of a GlobalTimestamp2
397 /// packet, until the MSB is set.
398 GlobalTimestamp2,
399}
400
401/// Combined timestamp generated from local and global timestamp
402/// packets. Field values relate to the target's global timestamp clock.
403/// See (Appendix C1, page 713).
404#[derive(Debug, Clone, PartialEq)]
405#[cfg_attr(
406 feature = "serde",
407 derive(Serialize, Deserialize),
408 serde(crate = "serde_crate")
409)]
410pub struct Timestamp {
411 /// A base timestamp upon which to apply the delta. `Some(base)` if
412 /// both a GTS1 and GTS2 packets where received.
413 pub base: Option<usize>,
414
415 /// A monotonically increasing local timestamp counter which apply
416 /// on the base timestamp. The value is the sum of all local
417 /// timestamps since the last global timestamp. `Some(delta)` if at
418 /// least one LTS1/LTS2 where received; or, if global timestamps are
419 /// enabled, if at least one LTS1/LTS2 where received since the last
420 /// global timestamp.
421 ///
422 /// Will be `None` if [DecoderOptions::only_gts] is set.
423 pub delta: Option<usize>,
424
425 /// In what manner this timestamp relate to the associated data
426 /// packets, if known.
427 pub data_relation: Option<TimestampDataRelation>,
428
429 /// An overflow packet was recieved, which may have been caused by a
430 /// local timestamp counter overflow. See (Appendix D4.2.3). The
431 /// timestamp in this structure is now potentially diverged from the
432 /// true timestamp by the maximum value of the local timestamp
433 /// counter (implementation defined), and will be considered such
434 /// until the next global timestamp.
435 pub diverged: bool,
436}
437
438impl Default for Timestamp {
439 fn default() -> Self {
440 Timestamp {
441 base: None,
442 delta: None,
443 data_relation: None,
444 diverged: false,
445 }
446 }
447}
448
449/// A context in which to record the current timestamp between calls to [Decoder::pull_with_timestamp].
450struct TimestampedContext {
451 /// Data packets associated with [TimestampedContext::ts] in this structure.
452 pub packets: Vec<TracePacket>,
453
454 /// Malformed packets associated with [TimestampedContext::ts] in this structure.
455 pub malformed_packets: Vec<MalformedPacket>,
456
457 /// The potentially received [TracePacket::GlobalTimestamp1] packet.
458 /// Used in combination with [TimestampedContext::gts2] to update
459 /// [Timestamp::base].
460 pub gts1: Option<usize>,
461
462 /// The potentially received [TracePacket::GlobalTimestamp2] packet.
463 /// Used in combination with [TimestampedContext::gts1] to update
464 /// [Timestamp::base].
465 pub gts2: Option<usize>,
466
467 /// The current timestamp.
468 pub ts: Timestamp,
469
470 /// Number of ITM packets consumed thus far.
471 pub packets_consumed: usize,
472}
473
474impl Default for TimestampedContext {
475 fn default() -> Self {
476 TimestampedContext {
477 packets: vec![],
478 malformed_packets: vec![],
479 gts1: None,
480 gts2: None,
481 ts: Timestamp::default(),
482 packets_consumed: 0,
483 }
484 }
485}
486
487pub struct DecoderOptions {
488 /// Whether to only process global timestamps in the bitstream on
489 /// [Decoder::pull_with_timestamps].
490 pub only_gts: bool,
491}
492
493impl Default for DecoderOptions {
494 fn default() -> Self {
495 Self { only_gts: false }
496 }
497}
498
499/// ITM and DWT packet protocol decoder.
500pub struct Decoder {
501 /// Decoder options
502 options: DecoderOptions,
503
504 /// The incoming bytes to the decoder.
505 incoming: BitVec,
506
507 /// Whether the decoder is in a state of synchronization.
508 sync: Option<usize>,
509
510 /// Timestamp context. Used exclusively in
511 /// [Decoder::pull_with_timestamp] for bookkeeping purposes.
512 ts_ctx: TimestampedContext,
513}
514
515/// Association between a set of [TracePacket]s and their Timestamp.
516#[derive(Debug, Clone, PartialEq)]
517#[cfg_attr(
518 feature = "serde",
519 derive(Serialize, Deserialize),
520 serde(crate = "serde_crate")
521)]
522pub struct TimestampedTracePackets {
523 /// Timestamp of [packets] and [malformed_packets].
524 pub timestamp: Timestamp,
525 pub packets: Vec<TracePacket>,
526 pub malformed_packets: Vec<MalformedPacket>,
527
528 /// Number of ITM packets consumed to create this structure.
529 pub packets_consumed: usize,
530}
531
532enum HeaderVariant {
533 Packet(TracePacket),
534 Stub(PacketStub),
535}
536
537impl Decoder {
538 pub fn new(options: DecoderOptions) -> Self {
539 Decoder {
540 options,
541 incoming: BitVec::new(),
542 sync: None,
543 ts_ctx: TimestampedContext::default(),
544 }
545 }
546
547 /// Push trace data into the decoder.
548 pub fn push(&mut self, data: &[u8]) {
549 // To optimize the performance in pull, we must reverse the
550 // input bitstream and prepend it. This is a costly operation,
551 // but is better done here than elsewhere.
552 let mut bv = BitVec::<LocalBits, _>::from_vec(data.to_vec());
553 bv.reverse();
554 bv.append(&mut self.incoming);
555 self.incoming.append(&mut bv);
556 }
557
558 /// Decode the next [TracePacket].
559 pub fn pull(&mut self) -> Result<Option<TracePacket>, MalformedPacket> {
560 if self.sync.is_some() {
561 return self.handle_sync();
562 }
563 assert!(self.sync.is_none());
564
565 if self.incoming.len() < 8 {
566 // No header to decode, nothing to do
567 return Ok(None);
568 }
569
570 self.ts_ctx.packets_consumed = self.ts_ctx.packets_consumed + 1;
571 match Self::decode_header(self.pull_byte())? {
572 HeaderVariant::Packet(p) => Ok(Some(p)),
573 HeaderVariant::Stub(s) => self.process_stub(&s),
574 }
575 }
576
577 /// Pull the next set of ITM data packets (not timestamps) from the
578 /// decoder and associates a [Timestamp]. **Assumes that local
579 /// timestamps will be found in the bitstream.**
580 ///
581 /// According to (Appendix C1.7.1, page 710-711), a local timestamp
582 /// relating to a single, or to a stream of back-to-back packets, is
583 /// generated and sent after the data packets in question in the
584 /// bitstream.
585 ///
586 /// This function thus [Decoder::pull]s packets until a local
587 /// timestamp is read (by default), and opportunely calculates an
588 /// associated [Timestamp]: local timestamps monotonically increase
589 /// an internal delta counter; upon a global timestamps the base is
590 /// updated, and the delta is reset.
591 pub fn pull_with_timestamp(&mut self) -> Option<TimestampedTracePackets> {
592 // Common functionality for LTS{1,2}
593 fn assoc_packets_with_lts(
594 packets: Vec<TracePacket>,
595 malformed_packets: Vec<MalformedPacket>,
596 ts: &mut Timestamp,
597 lts: usize,
598 data_relation: TimestampDataRelation,
599 packets_consumed: &mut usize,
600 ) -> TimestampedTracePackets {
601 if let Some(ref mut delta) = ts.delta {
602 *delta += lts as usize;
603 } else {
604 ts.delta = Some(lts);
605 }
606 ts.data_relation = Some(data_relation);
607 let ttp = TimestampedTracePackets {
608 timestamp: ts.clone(),
609 packets,
610 malformed_packets,
611 packets_consumed: *packets_consumed,
612 };
613 *packets_consumed = 0;
614 ttp
615 }
616
617 loop {
618 match self.pull() {
619 // No packets remaining
620 Ok(None) => return None,
621
622 // A local timestamp: packets received after the last
623 // local timestamp (all self.ts_ctx.packets) relate to
624 // this local timestamp. Return the packets and
625 // timestamp.
626 Ok(Some(TracePacket::LocalTimestamp1 { ts, data_relation }))
627 if !self.options.only_gts =>
628 {
629 return Some(assoc_packets_with_lts(
630 self.ts_ctx.packets.drain(..).collect(),
631 self.ts_ctx.malformed_packets.drain(..).collect(),
632 &mut self.ts_ctx.ts,
633 ts as usize,
634 data_relation,
635 &mut self.ts_ctx.packets_consumed,
636 ));
637 }
638 Ok(Some(TracePacket::LocalTimestamp2 { ts })) if !self.options.only_gts => {
639 return Some(assoc_packets_with_lts(
640 self.ts_ctx.packets.drain(..).collect(),
641 self.ts_ctx.malformed_packets.drain(..).collect(),
642 &mut self.ts_ctx.ts,
643 ts as usize,
644 TimestampDataRelation::Sync,
645 &mut self.ts_ctx.packets_consumed,
646 ));
647 }
648
649 // A global timestamp: store until we have both the
650 // upper (GTS2) and lower bits (GTS1).
651 Ok(Some(TracePacket::GlobalTimestamp1 { ts, wrap, clkch })) => {
652 self.ts_ctx.gts1 = Some(ts as usize);
653 if wrap {
654 // upper bits have changed; GTS2 incoming
655 self.ts_ctx.gts2 = None;
656 }
657 if clkch {
658 // changed input clock to ITM; full GTS incoming
659 self.ts_ctx.gts1 = None;
660 self.ts_ctx.gts2 = None;
661 }
662 }
663 Ok(Some(TracePacket::GlobalTimestamp2 { ts })) => {
664 self.ts_ctx.gts2 = Some(ts as usize)
665 }
666
667 // An overflow: the local timestamp may potentially have
668 // wrapped around, but this is not necessarily the case.
669 // We can in any case no longer generate an accurate
670 // Timestamp.
671 Ok(Some(TracePacket::Overflow)) => {
672 self.ts_ctx.ts.diverged = true;
673 self.ts_ctx.packets.push(TracePacket::Overflow);
674 }
675
676 // A packet that doesn't relate to the timestamp: stash
677 // it until the next local timestamp.
678 Ok(Some(packet)) if !self.options.only_gts => self.ts_ctx.packets.push(packet),
679
680 Err(malformed) => self.ts_ctx.malformed_packets.push(malformed),
681
682 // As above, but with local timestamps considered data: return the packet directly.
683 Ok(Some(packet)) if self.options.only_gts => {
684 return Some(TimestampedTracePackets {
685 timestamp: self.ts_ctx.ts.clone(),
686 packets: vec![packet],
687 malformed_packets: vec![],
688 packets_consumed: 1,
689 });
690 }
691 _ => unreachable!(),
692 }
693
694 // Do we have enough info two calculate a new base for the timestamp?
695 if let (Some(lower), Some(upper)) = (self.ts_ctx.gts1, self.ts_ctx.gts2) {
696 // XXX Should we move this calc into some Timestamp::from()?
697 const GTS2_TS_SHIFT: usize = 26; // see (Appendix D4.2.5).
698 self.ts_ctx.ts = Timestamp::default();
699 self.ts_ctx.ts.base = Some((upper << GTS2_TS_SHIFT) | lower);
700 self.ts_ctx.gts1 = None;
701 self.ts_ctx.gts2 = None;
702 }
703 }
704 }
705
706 /// Read zeros from the bitstream until the first bit is set. This
707 /// realigns the incoming bitstream for further processing, which
708 /// may not be 8-bit aligned.
709 fn handle_sync(&mut self) -> Result<Option<TracePacket>, MalformedPacket> {
710 if let Some(mut count) = self.sync {
711 while let Some(bit) = self.incoming.pop() {
712 if !bit && count < SYNC_MIN_ZEROS {
713 count += 1;
714 continue;
715 } else if bit && count >= SYNC_MIN_ZEROS {
716 self.sync = None;
717 return Ok(Some(TracePacket::Sync));
718 } else {
719 self.sync = None;
720 return Err(MalformedPacket::InvalidSync(count));
721 }
722 }
723 }
724
725 Ok(None)
726 }
727
728 /// Pulls a single byte from the incoming buffer.
729 fn pull_byte(&mut self) -> u8 {
730 let mut b: u8 = 0;
731 for i in 0..8 {
732 b |= (self.incoming.pop().unwrap() as u8) << i;
733 }
734
735 b
736 }
737
738 /// Pulls `cnt` bytes from the incoming buffer, if `cnt` bytes are
739 /// available.
740 fn pull_bytes(&mut self, cnt: usize) -> Option<Vec<u8>> {
741 if self.incoming.len() < cnt * 8 {
742 return None;
743 }
744
745 let mut payload = vec![];
746 for _ in 0..cnt {
747 payload.push(self.pull_byte());
748 }
749 Some(payload)
750 }
751
752 /// Pulls bytes from the incoming buffer until the continuation-bit
753 /// is not set. All [PacketStub]s follow follow this payload schema.
754 /// (e.g. Appendix D4, Fig. D4-4)
755 fn pull_payload(&mut self) -> Option<Vec<u8>> {
756 let mut iter = self.incoming.rchunks(8);
757 let mut cnt = 0;
758 loop {
759 cnt = cnt + 1;
760 match iter.next() {
761 None => return None,
762 Some(b) if b.len() < 8 => return None,
763 Some(b) => match b.first_zero() {
764 // bit 7 is not set: we have reached the end of the
765 // payload
766 //
767 // TODO replace with Option::contains when stable
768 Some(0) => break,
769 _ => continue,
770 },
771 }
772 }
773
774 Some(self.pull_bytes(cnt).unwrap())
775 }
776
777 fn process_stub(&mut self, stub: &PacketStub) -> Result<Option<TracePacket>, MalformedPacket> {
778 match stub {
779 PacketStub::Sync(count) => {
780 self.sync = Some(*count);
781 self.handle_sync()
782 }
783
784 PacketStub::HardwareSource {
785 disc_id,
786 expected_size,
787 } => {
788 if let Some(payload) = self.pull_bytes(*expected_size) {
789 Self::handle_hardware_source(*disc_id, payload).map(|p| Some(p))
790 } else {
791 Ok(None)
792 }
793 }
794 PacketStub::LocalTimestamp { data_relation } => {
795 if let Some(payload) = self.pull_payload() {
796 Ok(Some(TracePacket::LocalTimestamp1 {
797 data_relation: data_relation.clone(),
798 ts: Decoder::extract_timestamp(payload, 27),
799 }))
800 } else {
801 Ok(None)
802 }
803 }
804 PacketStub::GlobalTimestamp1 => {
805 if let Some(payload) = self.pull_payload() {
806 Ok(Some(TracePacket::GlobalTimestamp1 {
807 ts: Decoder::extract_timestamp(payload.clone(), 25),
808 clkch: (payload.last().unwrap() & (1 << 5)) >> 5 == 1,
809 wrap: (payload.last().unwrap() & (1 << 6)) >> 6 == 1,
810 }))
811 } else {
812 Ok(None)
813 }
814 }
815 PacketStub::GlobalTimestamp2 => {
816 if let Some(payload) = self.pull_payload() {
817 Ok(Some(TracePacket::GlobalTimestamp2 {
818 ts: Decoder::extract_timestamp(
819 payload.to_vec(),
820 match payload.len() {
821 4 => 47 - 26, // 48 bit timestamp
822 6 => 63 - 26, // 64 bit timestamp
823 _ => {
824 return Err(MalformedPacket::InvalidGTS2Size {
825 payload: payload.to_vec(),
826 })
827 }
828 },
829 ),
830 }))
831 } else {
832 Ok(None)
833 }
834 }
835 PacketStub::Instrumentation {
836 port,
837 expected_size,
838 } => {
839 if let Some(payload) = self.pull_bytes(*expected_size) {
840 Ok(Some(TracePacket::Instrumentation {
841 port: *port,
842 payload: payload.to_vec(),
843 }))
844 } else {
845 Ok(None)
846 }
847 }
848 }
849 }
850
851 // TODO template this for u32, u64?
852 fn extract_timestamp(payload: Vec<u8>, max_len: u64) -> u64 {
853 // Decode the first N - 1 payload bytes
854 let (rtail, head) = payload.split_at(payload.len() - 1);
855 let mut ts: u64 = 0;
856 for (i, b) in rtail.iter().enumerate() {
857 ts |= ((b & !(1 << 7)) as u64) // mask out continuation bit
858 << (7 * i);
859 }
860
861 // Mask out the timestamp's MSBs and shift them into the final
862 // value.
863 let shift = 7 - (max_len % 7);
864 let mask: u8 = 0xFFu8.wrapping_shl(shift.try_into().unwrap()) >> shift;
865 ts | (((head[0] & mask) as u64) << (7 * rtail.len()))
866 }
867
868 /// Decodes the payload of a hardware source packet.
869 #[bitmatch]
870 fn handle_hardware_source(
871 disc_id: u8,
872 payload: Vec<u8>,
873 ) -> Result<TracePacket, MalformedPacket> {
874 match disc_id {
875 0 => {
876 // event counter wrap
877
878 if payload.len() != 1 {
879 return Err(MalformedPacket::InvalidHardwarePacket { disc_id, payload });
880 }
881
882 let b = payload[0];
883 Ok(TracePacket::EventCounterWrap {
884 cyc: b & (1 << 5) != 0,
885 fold: b & (1 << 4) != 0,
886 lsu: b & (1 << 3) != 0,
887 sleep: b & (1 << 2) != 0,
888 exc: b & (1 << 1) != 0,
889 cpi: b & (1 << 0) != 0,
890 })
891 }
892 1 => {
893 // exception trace
894
895 if payload.len() != 2 {
896 return Err(MalformedPacket::InvalidHardwarePacket { disc_id, payload });
897 }
898
899 let function = (payload[1] >> 4) & 0b11;
900 let exception_number = ((payload[1] as u16 & 1) << 8) | payload[0] as u16;
901 let exception_number: u8 = if let Ok(nr) = exception_number.try_into() {
902 nr
903 } else {
904 return Err(MalformedPacket::InvalidExceptionTrace {
905 exception: exception_number,
906 function,
907 });
908 };
909
910 return Ok(TracePacket::ExceptionTrace {
911 exception: if let Some(exception) = cortex_m::VectActive::from(exception_number)
912 {
913 exception
914 } else {
915 return Err(MalformedPacket::InvalidExceptionTrace {
916 exception: exception_number.into(),
917 function,
918 });
919 },
920 action: match function {
921 0b01 => ExceptionAction::Entered,
922 0b10 => ExceptionAction::Exited,
923 0b11 => ExceptionAction::Returned,
924 _ => {
925 return Err(MalformedPacket::InvalidExceptionTrace {
926 exception: exception_number.into(),
927 function,
928 })
929 }
930 },
931 });
932 }
933 2 => {
934 // PC sample
935 match payload.len() {
936 1 if payload[0] == 0 => Ok(TracePacket::PCSample { pc: None }),
937 4 => Ok(TracePacket::PCSample {
938 pc: Some(u32::from_le_bytes(payload.try_into().unwrap())),
939 }),
940 _ => Err(MalformedPacket::InvalidPCSampleSize { payload }),
941 }
942 }
943 8..=23 => {
944 // data trace
945 #[bitmatch]
946 let "???t_tccd" = disc_id; // we have already masked out bit[2:0]
947 let comparator = c;
948
949 match (t, d, payload.len()) {
950 (0b01, 0, 4) => {
951 // PC value packet
952 Ok(TracePacket::DataTracePC {
953 comparator,
954 pc: u32::from_le_bytes(payload.try_into().unwrap()),
955 })
956 }
957 (0b01, 1, 2) => {
958 // address packet
959 Ok(TracePacket::DataTraceAddress {
960 comparator,
961 data: payload,
962 })
963 }
964 (0b10, d, _) => {
965 // data value packet
966 Ok(TracePacket::DataTraceValue {
967 comparator,
968 access_type: if d == 0 {
969 MemoryAccessType::Read
970 } else {
971 MemoryAccessType::Write
972 },
973 value: payload,
974 })
975 }
976 _ => Err(MalformedPacket::InvalidHardwarePacket { disc_id, payload }),
977 }
978 }
979 _ => unreachable!(), // we already verify the discriminator when we decode the header
980 }
981 }
982
983 /// Decodes the first byte of a packet, the header, into a complete packet or a packet stub.
984 #[bitmatch]
985 fn decode_header(header: u8) -> Result<HeaderVariant, MalformedPacket> {
986 fn translate_ss(ss: u8) -> Option<usize> {
987 // See (Appendix D4.2.8, Table D4-4)
988 Some(
989 match ss {
990 0b01 => 2,
991 0b10 => 3,
992 0b11 => 5,
993 _ => return None,
994 } - 1, // ss would include the header byte, but it has already been processed
995 )
996 }
997
998 let stub = |s| Ok(HeaderVariant::Stub(s));
999 let packet = |p| Ok(HeaderVariant::Packet(p));
1000
1001 #[bitmatch]
1002 match header {
1003 // Synchronization packet category
1004 "0000_0000" => stub(PacketStub::Sync(8)),
1005
1006 // Protocol packet category
1007 "0111_0000" => packet(TracePacket::Overflow),
1008 "11rr_0000" => {
1009 // Local timestamp, format 1 (LTS1)
1010 let tc = r; // relationship with corresponding data
1011
1012 stub(PacketStub::LocalTimestamp {
1013 data_relation: match tc {
1014 0b00 => TimestampDataRelation::Sync,
1015 0b01 => TimestampDataRelation::UnknownDelay,
1016 0b10 => TimestampDataRelation::AssocEventDelay,
1017 0b11 => TimestampDataRelation::UnknownAssocEventDelay,
1018 _ => unreachable!(),
1019 },
1020 })
1021 }
1022 "0ttt_0000" => {
1023 // Local timestamp, format 2 (LTS2)
1024 packet(TracePacket::LocalTimestamp2 { ts: t })
1025 }
1026 "1001_0100" => {
1027 // Global timestamp, format 1 (GTS1)
1028 stub(PacketStub::GlobalTimestamp1)
1029 }
1030 "1011_0100" => {
1031 // Global timestamp, format 2(GTS2)
1032 stub(PacketStub::GlobalTimestamp2)
1033 }
1034 "0ppp_1000" => {
1035 // Extension packet
1036 packet(TracePacket::Extension { page: p })
1037 }
1038
1039 // Source packet category
1040 "aaaa_a0ss" => {
1041 // Instrumentation packet
1042 stub(PacketStub::Instrumentation {
1043 port: a,
1044 expected_size: if let Some(s) = translate_ss(s) {
1045 s
1046 } else {
1047 return Err(MalformedPacket::InvalidSourcePayload { header, size: s });
1048 },
1049 })
1050 }
1051 "aaaa_a1ss" => {
1052 // Hardware source packet
1053 let disc_id = a;
1054
1055 if !(0..=2).contains(&disc_id) && !(8..=23).contains(&disc_id) {
1056 return Err(MalformedPacket::InvalidHardwareDisc {
1057 disc_id,
1058 size: s.into(),
1059 });
1060 }
1061
1062 stub(PacketStub::HardwareSource {
1063 disc_id,
1064 expected_size: if let Some(s) = translate_ss(s) {
1065 s
1066 } else {
1067 return Err(MalformedPacket::InvalidSourcePayload { header, size: s });
1068 },
1069 })
1070 }
1071 "hhhh_hhhh" => Err(MalformedPacket::InvalidHeader(h)),
1072 }
1073 }
1074}
1075
1076#[cfg(test)]
1077mod tests {
1078 use super::*;
1079
1080 #[test]
1081 fn pull_bytes() {
1082 let mut decoder = Decoder::new(DecoderOptions::default());
1083 let payload = vec![0b1000_0000, 0b1010_0000, 0b1000_0100, 0b0110_0000];
1084 decoder.push(&payload);
1085 assert_eq!(decoder.pull_bytes(3).unwrap().len(), 3);
1086 }
1087
1088 #[test]
1089 fn pull_payload() {
1090 let mut decoder = Decoder::new(DecoderOptions::default());
1091 let payload = vec![0b1000_0000, 0b1010_0000, 0b1000_0100, 0b0110_0000];
1092 #[rustfmt::skip]
1093 decoder.push(&payload);
1094 assert_eq!(decoder.pull_payload(), Some(payload));
1095 }
1096
1097 #[test]
1098 fn extract_timestamp() {
1099 #[rustfmt::skip]
1100 let ts: Vec<u8> = [
1101 0b1000_0000,
1102 0b1000_0000,
1103 0b1000_0000,
1104 0b0000_0000,
1105 ].to_vec();
1106
1107 assert_eq!(Decoder::extract_timestamp(ts, 25), 0);
1108
1109 #[rustfmt::skip]
1110 let ts: Vec<u8> = [
1111 0b1000_0001,
1112 0b1000_0111,
1113 0b1001_1111,
1114 0b0111_1111
1115 ].to_vec();
1116
1117 assert_eq!(
1118 Decoder::extract_timestamp(ts, 27),
1119 0b1111111_0011111_0000111_0000001,
1120 );
1121
1122 #[rustfmt::skip]
1123 let ts: Vec<u8> = [
1124 0b1000_0001,
1125 0b1000_0111,
1126 0b1001_1111,
1127 0b1111_1111
1128 ].to_vec();
1129
1130 assert_eq!(
1131 Decoder::extract_timestamp(ts, 25),
1132 0b11111_0011111_0000111_0000001,
1133 );
1134 }
1135}