Skip to main content

riscv_etrace/packet/
encoder.rs

1// Copyright (C) 2024 - 2026 FZI Forschungszentrum Informatik
2// SPDX-License-Identifier: Apache-2.0
3//! Packet encoder
4
5use core::num::NonZeroUsize;
6use core::ops;
7
8use super::error::Error;
9use super::truncate::TruncateNum;
10use super::width::Widths;
11
12/// Am encoder for packets and/or [payloads][super::payload]
13pub struct Encoder<'d, U> {
14    data: &'d mut [u8],
15    bit_pos: usize,
16    bytes_committed: usize,
17    field_widths: Widths,
18    unit: U,
19    hart_index_width: u8,
20    timestamp_width: u8,
21    trace_type_width: u8,
22    compress: bool,
23}
24
25impl<'d, U> Encoder<'d, U> {
26    /// Create a new encoder
27    pub(super) fn new(
28        field_widths: Widths,
29        unit: U,
30        hart_index_width: u8,
31        timestamp_width: u8,
32        trace_type_width: u8,
33        compress: bool,
34    ) -> Self {
35        Self {
36            data: &mut [],
37            bit_pos: 0,
38            bytes_committed: 0,
39            field_widths,
40            unit,
41            hart_index_width,
42            timestamp_width,
43            trace_type_width,
44            compress,
45        }
46    }
47
48    /// Reset the inner data to the given byte slice
49    pub fn reset(&mut self, data: &'d mut [u8]) {
50        self.data = data;
51        self.bit_pos = 0;
52        self.bytes_committed = 0;
53    }
54
55    /// Retrieve the number of bytes in the buffer that are not committed
56    pub fn uncommitted(&self) -> usize {
57        self.data.len() - self.bytes_committed
58    }
59
60    /// Encode one entity
61    pub fn encode(&mut self, data: &impl Encode<'d, U>) -> Result<(), Error> {
62        data.encode(self)
63    }
64
65    /// Retrieve this encoder's unit
66    pub fn unit(&self) -> &U {
67        &self.unit
68    }
69
70    /// Retrieve the payload field widths
71    pub(super) fn widths(&self) -> &Widths {
72        &self.field_widths
73    }
74
75    /// Retrieve the hart index width
76    pub(super) fn hart_index_width(&self) -> u8 {
77        self.hart_index_width
78    }
79
80    /// Retrieve the width of the timestamp used in packet headers
81    pub(super) fn timestamp_width(&self) -> u8 {
82        self.timestamp_width
83    }
84
85    /// Retrieve the trace type width
86    pub(super) fn trace_type_width(&self) -> u8 {
87        self.trace_type_width
88    }
89
90    /// Extract a mutable chunk from the beginning of the uncommitted region
91    ///
92    /// Returns a chunk of fixed size from the beginning of the uncommitted
93    /// region and resets the encoder to the remaining buffer after that chunk
94    /// on success. On failure, the encoder is left with an empty buffer.
95    pub(super) fn first_uncommitted_chunk<const N: usize>(
96        &mut self,
97    ) -> Result<&'d mut [u8; N], Error> {
98        let (chunk, data) = core::mem::take(&mut self.data)
99            .split_at_mut_checked(self.bytes_committed)
100            .and_then(|(_, d)| d.split_first_chunk_mut())
101            .ok_or_else(|| {
102                self.reset(&mut []);
103                Error::BufferTooSmall
104            })?;
105        self.reset(data);
106        Ok(chunk)
107    }
108
109    /// Write a single bit
110    pub(super) fn write_bit(&mut self, bit: bool) -> Result<(), Error> {
111        let byte_pos = self.bit_pos >> 3;
112        let byte = self.get_byte(byte_pos)?;
113        let mask = 0xff << (self.bit_pos & 0x7);
114        let byte = if bit { byte | mask } else { byte & !mask };
115        self.write_byte(byte, byte_pos)?;
116        self.bit_pos += 1;
117        Ok(())
118    }
119
120    /// Write a single differential bit
121    ///
122    /// If [`true`], a negation of the current last bit will be written.
123    pub(super) fn write_differential_bit(&mut self, bit: bool) -> Result<(), Error> {
124        let byte_pos = self.bit_pos >> 3;
125        let mut byte = self.get_byte(byte_pos)?;
126        if bit {
127            byte ^= 0xff << (self.bit_pos & 0x7);
128        }
129        self.write_byte(byte, byte_pos)?;
130        self.bit_pos += 1;
131        Ok(())
132    }
133
134    /// Write an integer field
135    ///
136    /// # Safety
137    ///
138    /// May panic if `bit_count` is higher then the bit width of the target
139    /// integer.
140    pub(super) fn write_bits<T>(&mut self, bits: T, bit_count: u8) -> Result<(), Error>
141    where
142        T: Copy
143            + ops::Shl<usize, Output = T>
144            + ops::Shr<usize, Output = T>
145            + ops::BitOrAssign<T>
146            + TruncateNum,
147    {
148        let Some(bit_count) = NonZeroUsize::new(bit_count.into()) else {
149            return Ok(());
150        };
151
152        let bit_pos = self.bit_pos & 0x07;
153        let mut byte_pos = self.bit_pos >> 3;
154
155        let mut byte = self.get_byte(byte_pos)?;
156        byte &= (1 << bit_pos) - 1;
157        byte |= bits.lsb() << bit_pos;
158
159        let mut bits_written = 8 - bit_pos;
160        while bits_written < bit_count.get() {
161            self.write_byte(byte, byte_pos)?;
162            byte_pos += 1;
163            byte = (bits >> bits_written).lsb();
164            bits_written += 8;
165        }
166
167        if let Some(upper) = NonZeroUsize::new(bits_written - bit_count.get()) {
168            let mask = !(0xff >> upper.get());
169            if (bits >> (bit_count.get() - 1)).lsb() & 1 != 0 {
170                byte |= mask;
171            } else {
172                byte &= !mask;
173            }
174        }
175
176        self.write_byte(byte, byte_pos)?;
177        self.bit_pos += bit_count.get();
178        Ok(())
179    }
180
181    /// Get the byte at the given byte position
182    ///
183    /// If the position is past the boundary of committed bytes, the result of
184    /// expanding the committed sequence will be returned.
185    fn get_byte(&mut self, byte_pos: usize) -> Result<u8, Error> {
186        if byte_pos < self.bytes_committed {
187            return self
188                .data
189                .get(byte_pos)
190                .copied()
191                .ok_or(Error::BufferTooSmall);
192        }
193
194        let last_committed = self.bytes_committed.saturating_sub(1);
195        let last_committed = self.data.get(last_committed).ok_or(Error::BufferTooSmall)?;
196        if last_committed & 0x80 != 0 {
197            Ok(0xff)
198        } else {
199            Ok(0x00)
200        }
201    }
202
203    /// Write a byte at the specified byte position
204    ///
205    /// The committed bytes will be expanded if necessary.
206    fn write_byte(&mut self, byte: u8, byte_pos: usize) -> Result<(), Error> {
207        let data: &mut [u8] = self.data;
208        let split = data
209            .split_at_mut_checked(byte_pos)
210            .map(|(d, t)| (d, t.first_mut()));
211        let (data, target) = if let Some(split) = split {
212            split
213        } else {
214            (data, None)
215        };
216
217        if let Some((extend, fill)) = data
218            .split_at_mut_checked(self.bytes_committed)
219            .and_then(|(c, f)| c.last().map(|e| (e & 0x80 != 0, f)))
220        {
221            if self.compress && matches!((byte, extend), (0x00, false) | (0xff, true)) {
222                return Ok(());
223            }
224            fill.fill(if extend { 0xff } else { 0x00 });
225        }
226
227        *target.ok_or(Error::BufferTooSmall)? = byte;
228        self.bytes_committed = byte_pos + 1;
229        Ok(())
230    }
231}
232
233/// Encodable item
234///
235/// Items implementing this trait may be encoded using an [`Encoder`].
236pub trait Encode<'d, U>: Sized {
237    /// Encode this item
238    fn encode(&self, encoder: &mut Encoder<'d, U>) -> Result<(), Error>;
239}
240
241#[cfg(feature = "either")]
242impl<'d, L: Encode<'d, U>, R: Encode<'d, U>, U> Encode<'d, U> for either::Either<L, R> {
243    fn encode(&self, encoder: &mut Encoder<'d, U>) -> Result<(), Error> {
244        either::for_both!(self, e => e.encode(encoder))
245    }
246}