Skip to main content

riscv_etrace/packet/
sync.rs

1// Copyright (C) 2025, 2026 FZI Forschungszentrum Informatik
2// SPDX-License-Identifier: Apache-2.0
3//! Synchronization payloads
4//!
5//! This module contains definitions of the various synchronization packets as
6//! defined in section 7.1 Format 3 packets of the specification. This includes
7//! the [`Synchronization`] type which may hold any of the subformats.
8
9use core::fmt;
10
11use crate::types::{self, Privilege, trap};
12
13use super::decoder::{Decode, Decoder};
14use super::encoder::{Encode, Encoder};
15use super::unit::{self, Unit};
16use super::{Error, util};
17
18/// Synchronization payload
19///
20/// Represents a format 3 packet.
21#[derive(Copy, Clone, Debug, Eq, PartialEq)]
22pub enum Synchronization<I = unit::ReferenceIOptions, D = unit::ReferenceDOptions> {
23    Start(Start),
24    Trap(Trap),
25    Context(Context),
26    Support(Support<I, D>),
27}
28
29impl<I, D> Synchronization<I, D> {
30    /// Check whether we got here without a branch being taken
31    ///
32    /// Returns [`false`] if the address was a branch target and [`true`] if the
33    /// branch was not taken or the previous instruction was not a branch
34    /// instruction. Returns [`None`] if the packet doesn't carry any address
35    /// information.
36    pub fn branch_not_taken(&self) -> Option<bool> {
37        match self {
38            Self::Start(start) => Some(start.branch),
39            Self::Trap(trap) => Some(trap.branch),
40            _ => None,
41        }
42    }
43
44    /// Retrieve the [`Context`] from this payload
45    ///
46    /// Returns [`None`] if the payload does not contain a context. This is the
47    /// case for [`Support`][Self::Support] payloads.
48    pub fn as_context(&self) -> Option<&Context> {
49        match self {
50            Self::Start(start) => Some(&start.ctx),
51            Self::Trap(trap) => Some(&trap.ctx),
52            Self::Context(ctx) => Some(ctx),
53            _ => None,
54        }
55    }
56
57    /// View this payload as a [`Support`]
58    ///
59    /// Returns the inner [`Support`] if this is a [`Support`][Self::Support],
60    /// [`None`] otherwise.
61    pub fn as_support(&self) -> Option<&Support<I, D>> {
62        match self {
63            Self::Support(supp) => Some(supp),
64            _ => None,
65        }
66    }
67}
68
69impl<I, D> From<Start> for Synchronization<I, D> {
70    fn from(start: Start) -> Self {
71        Self::Start(start)
72    }
73}
74
75impl<I, D> From<Trap> for Synchronization<I, D> {
76    fn from(trap: Trap) -> Self {
77        Self::Trap(trap)
78    }
79}
80
81impl<I, D> From<Context> for Synchronization<I, D> {
82    fn from(ctx: Context) -> Self {
83        Self::Context(ctx)
84    }
85}
86
87impl<I, D> From<Support<I, D>> for Synchronization<I, D> {
88    fn from(support: Support<I, D>) -> Self {
89        Self::Support(support)
90    }
91}
92
93impl<U: Unit> Decode<'_, U> for Synchronization<U::IOptions, U::DOptions> {
94    fn decode(decoder: &mut Decoder<U>) -> Result<Self, Error> {
95        match decoder.read_bits::<u8>(2)? {
96            0b00 => Start::decode(decoder).map(Into::into),
97            0b01 => Trap::decode(decoder).map(Into::into),
98            0b10 => Context::decode(decoder).map(Into::into),
99            0b11 => Support::decode(decoder).map(Into::into),
100            _ => unreachable!(),
101        }
102    }
103}
104
105impl<'d, U> Encode<'d, U> for Synchronization<U::IOptions, U::DOptions>
106where
107    U: Unit,
108    U::IOptions: Encode<'d, U>,
109    U::DOptions: Encode<'d, U>,
110{
111    fn encode(&self, encoder: &mut Encoder<'d, U>) -> Result<(), Error> {
112        match self {
113            Self::Start(start) => {
114                encoder.write_bits(0b00u8, 2)?;
115                encoder.encode(start)
116            }
117            Self::Trap(trap) => {
118                encoder.write_bits(0b01u8, 2)?;
119                encoder.encode(trap)
120            }
121            Self::Context(ctx) => {
122                encoder.write_bits(0b10u8, 2)?;
123                encoder.encode(ctx)
124            }
125            Self::Support(support) => {
126                encoder.write_bits(0b11u8, 2)?;
127                encoder.encode(support)
128            }
129        }
130    }
131}
132
133impl<I: unit::IOptions, D> fmt::Display for Synchronization<I, D> {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        match self {
136            Self::Start(s) => write!(f, "START {s}"),
137            Self::Trap(t) => write!(f, "TRAP {t}"),
138            Self::Context(c) => write!(f, "CTX {c}"),
139            Self::Support(s) => write!(f, "SUPP {s}"),
140        }
141    }
142}
143
144/// Start of trace
145///
146/// Represents a format 3, subformat 0 packet. It is sent by the encoder for the
147/// first traced instruction or when resynchronization is necessary.
148#[derive(Copy, Clone, Debug, Eq, PartialEq)]
149pub struct Start {
150    /// False, if the address is a taken branch instruction. True, if the branch
151    /// was not taken or the instruction is not a branch.
152    pub branch: bool,
153    pub ctx: Context,
154    /// Full address of the instruction.
155    pub address: u64,
156}
157
158impl<U> Decode<'_, U> for Start {
159    fn decode(decoder: &mut Decoder<U>) -> Result<Self, Error> {
160        let branch = decoder.read_bit()?;
161        let ctx = Context::decode(decoder)?;
162        let address = util::read_address(decoder)?;
163        Ok(Start {
164            branch,
165            ctx,
166            address,
167        })
168    }
169}
170
171impl<U> Encode<'_, U> for Start {
172    fn encode(&self, encoder: &mut Encoder<U>) -> Result<(), Error> {
173        encoder.write_bit(self.branch)?;
174        encoder.encode(&self.ctx)?;
175        util::write_address(encoder, self.address)
176    }
177}
178
179impl fmt::Display for Start {
180    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181        let address = self.address;
182        write!(f, "{address:#x}")?;
183        if !self.branch {
184            write!(f, ", branch taken")?;
185        }
186        write!(f, ", {}", self.ctx)
187    }
188}
189
190/// Trap packet
191///
192/// Represents a format 3, subformat 1 packet. It is sent by the encoder
193/// following an exception or interrupt.
194#[derive(Copy, Clone, Debug, Eq, PartialEq)]
195pub struct Trap {
196    /// `false`, if the address is a taken branch instruction. `true`, if the
197    /// branch was not taken or the instruction is not a branch.
198    pub branch: bool,
199    pub ctx: Context,
200    /// `true`, if the address points to the trap handler. `false`, if address
201    /// points to the EPC for an exception at the target of an updiscon, and is
202    /// undefined for other exceptions and interrupts.
203    pub thaddr: bool,
204    /// Full address of the instruction
205    pub address: u64,
206    pub info: trap::Info,
207}
208
209impl<U> Decode<'_, U> for Trap {
210    fn decode(decoder: &mut Decoder<U>) -> Result<Self, Error> {
211        let branch = decoder.read_bit()?;
212        let ctx = Context::decode(decoder)?;
213        let ecause = decoder.read_bits(decoder.widths().ecause.get())?;
214        let interrupt = decoder.read_bit()?;
215        let thaddr = decoder.read_bit()?;
216        let address = util::read_address(decoder)?;
217        let tval = if interrupt {
218            None
219        } else {
220            Some(decoder.read_bits(decoder.widths().iaddress.get())?)
221        };
222        Ok(Trap {
223            branch,
224            ctx,
225            thaddr,
226            address,
227            info: trap::Info { ecause, tval },
228        })
229    }
230}
231
232impl<U> Encode<'_, U> for Trap {
233    fn encode(&self, encoder: &mut Encoder<U>) -> Result<(), Error> {
234        encoder.write_bit(self.branch)?;
235        encoder.encode(&self.ctx)?;
236        encoder.write_bits(self.info.ecause, encoder.widths().ecause.get())?;
237        encoder.write_bit(self.info.tval.is_none())?;
238        encoder.write_bit(self.thaddr)?;
239        util::write_address(encoder, self.address)?;
240        if let Some(tval) = self.info.tval {
241            encoder.write_bits(tval, encoder.widths().iaddress.get())?;
242        }
243        Ok(())
244    }
245}
246
247impl fmt::Display for Trap {
248    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249        let info = self.info;
250        let address = self.address;
251        let addr_type = match self.thaddr {
252            true => "handler",
253            false => "EPC",
254        };
255        write!(f, "{info}, {addr_type}: {address:#x}")?;
256        if !self.branch {
257            write!(f, ", branch taken")?;
258        }
259        write!(f, ", {}", self.ctx)
260    }
261}
262
263/// Context packet
264///
265/// Represents a format 3, subformat 2 packet. It informs about a changed
266/// context. It is also used as part of other payloads.
267#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
268pub struct Context {
269    /// The privilege level of the reported instruction.
270    pub privilege: Privilege,
271    pub time: Option<u64>,
272    pub context: u64,
273}
274
275impl From<&Context> for types::Context {
276    fn from(ctx: &Context) -> Self {
277        Self {
278            privilege: ctx.privilege,
279            context: ctx.context,
280        }
281    }
282}
283
284impl From<Context> for types::Context {
285    fn from(ctx: Context) -> Self {
286        (&ctx).into()
287    }
288}
289
290impl<U> Decode<'_, U> for Context {
291    fn decode(decoder: &mut Decoder<U>) -> Result<Self, Error> {
292        let privilege = decoder
293            .read_bits::<u8>(decoder.widths().privilege.get())?
294            .try_into()
295            .map_err(Error::UnknownPrivilege)?;
296        let time = decoder
297            .widths()
298            .time
299            .map(|w| decoder.read_bits(w.get()))
300            .transpose()?;
301        let context_width = decoder.widths().context.map(Into::into).unwrap_or_default();
302        let context = decoder.read_bits(context_width)?;
303        Ok(Context {
304            privilege,
305            time,
306            context,
307        })
308    }
309}
310
311impl<U> Encode<'_, U> for Context {
312    fn encode(&self, encoder: &mut Encoder<U>) -> Result<(), Error> {
313        encoder.write_bits(u8::from(self.privilege), encoder.widths().privilege.get())?;
314        if let Some(width) = encoder.widths().time {
315            encoder.write_bits(self.time.unwrap_or_default(), width.get())?;
316        }
317        if let Some(width) = encoder.widths().context {
318            encoder.write_bits(self.context, width.get())?;
319        }
320        Ok(())
321    }
322}
323
324impl fmt::Display for Context {
325    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326        write!(f, "{} mode", self.privilege)?;
327        if let Some(time) = self.time {
328            write!(f, ", time: {time}")?;
329        }
330        write!(f, ", context: {}", self.context)
331    }
332}
333
334/// Supporting information for the decoder.
335///
336/// Represents a format 3, subformat 3 packet.
337#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
338pub struct Support<I = unit::ReferenceIOptions, D = unit::ReferenceDOptions> {
339    pub ienable: bool,
340    pub encoder_mode: EncoderMode,
341    pub qual_status: QualStatus,
342    pub ioptions: I,
343    pub denable: bool,
344    pub dloss: bool,
345    pub doptions: D,
346}
347
348impl<U: Unit> Decode<'_, U> for Support<U::IOptions, U::DOptions> {
349    fn decode(decoder: &mut Decoder<U>) -> Result<Self, Error> {
350        let ienable = decoder.read_bit()?;
351        let encoder_mode = decoder
352            .read_bits::<u8>(decoder.unit().encoder_mode_width())?
353            .try_into()
354            .map_err(Error::UnknownEncoderMode)?;
355        let qual_status = QualStatus::decode(decoder)?;
356        let ioptions = U::decode_ioptions(decoder)?;
357        let denable = decoder.read_bit()?;
358        let dloss = decoder.read_bit()?;
359        let doptions = U::decode_doptions(decoder)?;
360        Ok(Support {
361            ienable,
362            encoder_mode,
363            qual_status,
364            ioptions,
365            denable,
366            dloss,
367            doptions,
368        })
369    }
370}
371
372impl<'d, U> Encode<'d, U> for Support<U::IOptions, U::DOptions>
373where
374    U: Unit,
375    U::IOptions: Encode<'d, U>,
376    U::DOptions: Encode<'d, U>,
377{
378    fn encode(&self, encoder: &mut Encoder<'d, U>) -> Result<(), Error> {
379        encoder.write_bit(self.ienable)?;
380        encoder.write_bits(
381            u8::from(self.encoder_mode),
382            encoder.unit().encoder_mode_width(),
383        )?;
384        encoder.encode(&self.qual_status)?;
385        encoder.encode(&self.ioptions)?;
386        encoder.write_bit(self.denable)?;
387        if self.denable {
388            encoder.write_bit(self.dloss)?;
389            encoder.encode(&self.doptions)?;
390        }
391        Ok(())
392    }
393}
394
395impl<I: unit::IOptions, D> fmt::Display for Support<I, D> {
396    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397        let ienable = util::Enabled(self.ienable);
398        let mode = self.encoder_mode;
399        let qual = self.qual_status;
400        write!(f, "itrace {ienable} ({mode}) {qual}")?;
401        if let Some(mode) = self.ioptions.address_mode() {
402            write!(f, ", {mode} address mode")?;
403        }
404        if self.ioptions.implicit_return() == Some(true) {
405            write!(f, ", implicit return")?;
406        }
407        if self.ioptions.implicit_exception() == Some(true) {
408            write!(f, ", implicit exception")?;
409        }
410        if self.ioptions.branch_prediction() == Some(true) {
411            write!(f, ", branch prediction")?;
412        }
413        if self.ioptions.jump_target_cache() == Some(true) {
414            write!(f, ", jump target cache")?;
415        }
416
417        write!(f, "; dtrace {}", util::Enabled(self.denable))?;
418        if self.dloss {
419            write!(f, " dloss")?;
420        }
421
422        Ok(())
423    }
424}
425
426/// Representation of a change to the filter qualification
427#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
428pub enum QualStatus {
429    /// No change to filter qualification.
430    #[default]
431    NoChange,
432    /// Qualification ended, preceding packet sent explicitly to indicate last
433    /// qualification instruction.
434    EndedRep,
435    /// One or more instruction trace packets lost.
436    TraceLost,
437    /// Qualification ended, preceding packet would have been sent anyway due to
438    /// an updiscon, even if it wasn’t the last qualified instruction
439    EndedNtr,
440}
441
442impl<U> Decode<'_, U> for QualStatus {
443    fn decode(decoder: &mut Decoder<U>) -> Result<Self, Error> {
444        Ok(match decoder.read_bits::<u8>(2)? {
445            0b00 => QualStatus::NoChange,
446            0b01 => QualStatus::EndedRep,
447            0b10 => QualStatus::TraceLost,
448            0b11 => QualStatus::EndedNtr,
449            _ => unreachable!(),
450        })
451    }
452}
453
454impl<U> Encode<'_, U> for QualStatus {
455    fn encode(&self, encoder: &mut Encoder<U>) -> Result<(), Error> {
456        let value: u8 = match self {
457            Self::NoChange => 0b00,
458            Self::EndedRep => 0b01,
459            Self::TraceLost => 0b10,
460            Self::EndedNtr => 0b11,
461        };
462        encoder.write_bits(value, 2)
463    }
464}
465
466impl fmt::Display for QualStatus {
467    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
468        match self {
469            Self::NoChange => write!(f, "no change"),
470            Self::EndedRep => write!(f, "ended rep"),
471            Self::TraceLost => write!(f, "trace lost"),
472            Self::EndedNtr => write!(f, "ended ntr"),
473        }
474    }
475}
476
477/// Mode the encoder is operating in
478#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
479pub enum EncoderMode {
480    #[default]
481    BranchTrace,
482}
483
484impl TryFrom<u8> for EncoderMode {
485    type Error = u8;
486
487    fn try_from(num: u8) -> Result<Self, Self::Error> {
488        match num {
489            0 => Ok(Self::BranchTrace),
490            e => Err(e),
491        }
492    }
493}
494
495impl From<EncoderMode> for u8 {
496    fn from(mode: EncoderMode) -> Self {
497        match mode {
498            EncoderMode::BranchTrace => 0,
499        }
500    }
501}
502
503impl fmt::Display for EncoderMode {
504    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
505        match self {
506            Self::BranchTrace => write!(f, "branch trace"),
507        }
508    }
509}