fst_reader/
types.rs

1// Copyright 2023 The Regents of the University of California
2// Copyright 2024 Cornell University
3// released under BSD 3-Clause License
4// author: Kevin Laeufer <laeufer@cornell.edu>
5// Contains FST in-memory types.
6
7use num_enum::TryFromPrimitive;
8#[cfg(test)]
9use proptest_derive::Arbitrary;
10use std::fmt::Formatter;
11use std::num::NonZeroU32;
12
13// The FST standard seems to limit names to 512 characters,
14// but there are exampels of files with longer names, e.g., from Verilator.
15// Choose 64 KiB, which should hopefully never have to be changed.
16pub(crate) const HIERARCHY_NAME_MAX_SIZE: usize = 65536;
17pub(crate) const HIERARCHY_ATTRIBUTE_MAX_SIZE: usize = 65536 + 4096;
18
19#[derive(Debug, PartialEq)]
20#[cfg_attr(test, derive(Arbitrary))]
21pub struct FstSignalHandle(NonZeroU32);
22
23impl FstSignalHandle {
24    pub(crate) fn new(value: u32) -> Self {
25        FstSignalHandle(NonZeroU32::new(value).unwrap())
26    }
27    pub fn from_index(index: usize) -> Self {
28        FstSignalHandle(NonZeroU32::new((index as u32) + 1).unwrap())
29    }
30    pub fn get_index(&self) -> usize {
31        (self.0.get() - 1) as usize
32    }
33
34    #[cfg(test)]
35    pub(crate) fn get_raw(&self) -> u32 {
36        self.0.get()
37    }
38}
39
40impl std::fmt::Display for FstSignalHandle {
41    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
42        write!(f, "H{}", self.0)
43    }
44}
45
46#[derive(Debug, PartialEq, Clone, Copy)]
47pub(crate) enum FloatingPointEndian {
48    Little,
49    Big,
50}
51
52#[repr(u8)]
53#[derive(Debug, TryFromPrimitive, Clone, Copy, PartialEq)]
54#[cfg_attr(test, derive(Arbitrary))]
55pub enum FileType {
56    Verilog = 0,
57    Vhdl = 1,
58    VerilogVhdl = 2,
59}
60
61#[repr(u8)]
62#[derive(Debug, TryFromPrimitive, PartialEq)]
63pub enum BlockType {
64    Header = 0,
65    VcData = 1,
66    Blackout = 2,
67    Geometry = 3,
68    Hierarchy = 4,
69    VcDataDynamicAlias = 5,
70    HierarchyLZ4 = 6,
71    HierarchyLZ4Duo = 7,
72    VcDataDynamicAlias2 = 8,
73    GZipWrapper = 254,
74    Skip = 255,
75}
76
77#[repr(u8)]
78#[derive(Debug, TryFromPrimitive, Clone, Copy, PartialEq)]
79#[cfg_attr(test, derive(Arbitrary))]
80pub enum FstScopeType {
81    // VCD
82    Module = 0,
83    Task = 1,
84    Function = 2,
85    Begin = 3,
86    Fork = 4,
87    Generate = 5,
88    Struct = 6,
89    Union = 7,
90    Class = 8,
91    Interface = 9,
92    Package = 10,
93    Program = 11,
94    // VHDL
95    VhdlArchitecture = 12,
96    VhdlProcedure = 13,
97    VhdlFunction = 14,
98    VhdlRecord = 15,
99    VhdlProcess = 16,
100    VhdlBlock = 17,
101    VhdlForGenerate = 18,
102    VhdlIfGenerate = 19,
103    VhdlGenerate = 20,
104    VhdlPackage = 21,
105    //
106    AttributeBegin = 252,
107    AttributeEnd = 253,
108    //
109    VcdScope = 254,
110    VcdUpScope = 255,
111}
112
113#[repr(u8)]
114#[derive(Debug, TryFromPrimitive, PartialEq, Copy, Clone)]
115#[cfg_attr(test, derive(Arbitrary))]
116pub enum FstVarType {
117    // VCD
118    Event = 0,
119    Integer = 1,
120    Parameter = 2,
121    Real = 3,
122    RealParameter = 4,
123    Reg = 5,
124    Supply0 = 6,
125    Supply1 = 7,
126    Time = 8,
127    Tri = 9,
128    TriAnd = 10,
129    TriOr = 11,
130    TriReg = 12,
131    Tri0 = 13,
132    Tri1 = 14,
133    Wand = 15, // or WAnd ?
134    Wire = 16,
135    Wor = 17, // or WOr?
136    Port = 18,
137    SparseArray = 19,
138    RealTime = 20,
139    GenericString = 21,
140    // System Verilog
141    Bit = 22,
142    Logic = 23,
143    Int = 24,
144    ShortInt = 25,
145    LongInt = 26,
146    Byte = 27,
147    Enum = 28,
148    ShortReal = 29,
149}
150
151impl FstVarType {
152    /// Returns `true` iff this is a Real, RealParameter, RealTime or ShortReal.
153    pub fn is_real(&self) -> bool {
154        matches!(
155            self,
156            FstVarType::Real
157                | FstVarType::RealParameter
158                | FstVarType::RealTime
159                | FstVarType::ShortReal
160        )
161    }
162}
163
164#[repr(u8)]
165#[derive(Debug, TryFromPrimitive, Clone, Copy, PartialEq)]
166#[cfg_attr(test, derive(Arbitrary))]
167pub enum FstVarDirection {
168    Implicit = 0,
169    Input = 1,
170    Output = 2,
171    InOut = 3,
172    Buffer = 4,
173    Linkage = 5,
174}
175
176#[repr(u8)]
177#[derive(Debug, TryFromPrimitive, Clone, Copy, PartialEq)]
178#[cfg_attr(test, derive(Arbitrary))]
179pub enum FstVhdlVarType {
180    None = 0,
181    Signal = 1,
182    Variable = 2,
183    Constant = 3,
184    File = 4,
185    Memory = 5,
186}
187
188#[repr(u8)]
189#[derive(Debug, TryFromPrimitive, Clone, Copy, PartialEq)]
190#[cfg_attr(test, derive(Arbitrary))]
191pub enum FstVhdlDataType {
192    None = 0,
193    Boolean = 1,
194    Bit = 2,
195    Vector = 3,
196    ULogic = 4,
197    ULogicVector = 5,
198    Logic = 6,
199    LogicVector = 7,
200    Unsigned = 8,
201    Signed = 9,
202    Integer = 10,
203    Real = 11,
204    Natural = 12,
205    Positive = 13,
206    Time = 14,
207    Character = 15,
208    String = 16,
209}
210
211#[repr(u8)]
212#[derive(Debug, TryFromPrimitive, PartialEq)]
213pub enum AttributeType {
214    Misc = 0,
215    Array = 1,
216    Enum = 2,
217    Pack = 3,
218}
219
220#[repr(u8)]
221#[derive(Debug, TryFromPrimitive, PartialEq)]
222pub enum MiscType {
223    Comment = 0,
224    EnvVar = 1,
225    SupVar = 2,
226    PathName = 3,
227    SourceStem = 4,
228    SourceInstantiationStem = 5,
229    ValueList = 6,
230    EnumTable = 7,
231    Unknown = 8,
232}
233
234pub(crate) const DOUBLE_ENDIAN_TEST: f64 = std::f64::consts::E;
235
236#[derive(Debug, PartialEq)]
237#[cfg_attr(test, derive(Arbitrary))]
238#[allow(dead_code)]
239pub(crate) struct Header {
240    pub(crate) start_time: u64,
241    pub(crate) end_time: u64,
242    pub(crate) memory_used_by_writer: u64,
243    pub(crate) scope_count: u64,
244    pub(crate) var_count: u64,
245    pub(crate) max_var_id_code: u64, // aka maxhandle
246    pub(crate) vc_section_count: u64,
247    pub(crate) timescale_exponent: i8,
248    pub(crate) version: String,
249    pub(crate) date: String,
250    pub(crate) file_type: FileType,
251    pub(crate) time_zero: u64,
252}
253
254#[derive(Debug, PartialEq)]
255#[cfg_attr(test, derive(Arbitrary))]
256pub(crate) enum SignalInfo {
257    BitVec(NonZeroU32),
258    Real,
259}
260
261impl SignalInfo {
262    pub(crate) fn from_file_format(value: u32) -> Self {
263        if value == 0 {
264            SignalInfo::Real
265        } else if value != u32::MAX {
266            SignalInfo::BitVec(NonZeroU32::new(value + 1).unwrap())
267        } else {
268            SignalInfo::BitVec(NonZeroU32::new(1).unwrap())
269        }
270    }
271
272    #[cfg(test)]
273    pub(crate) fn to_file_format(&self) -> u32 {
274        match self {
275            SignalInfo::BitVec(value) => match value.get() {
276                1 => u32::MAX,
277                other => other - 1,
278            },
279            SignalInfo::Real => 0,
280        }
281    }
282
283    #[inline]
284    pub(crate) fn len(&self) -> u32 {
285        match self {
286            SignalInfo::BitVec(value) => value.get() - 1,
287            SignalInfo::Real => 8,
288        }
289    }
290
291    #[inline]
292    pub(crate) fn is_real(&self) -> bool {
293        match self {
294            SignalInfo::BitVec(_) => false,
295            SignalInfo::Real => true,
296        }
297    }
298}
299
300#[derive(Debug, PartialEq)]
301#[cfg_attr(test, derive(Arbitrary))]
302pub(crate) struct BlackoutData {
303    pub(crate) time: u64,
304    pub(crate) contains_activity: bool,
305}
306
307#[derive(Debug, Clone)]
308pub(crate) struct DataSectionInfo {
309    pub(crate) file_offset: u64, // points to section length
310    pub(crate) start_time: u64,
311    pub(crate) end_time: u64,
312    pub(crate) kind: DataSectionKind,
313    /// the number of bytes needed to store all uncompressed value change data in the block
314    #[allow(dead_code)]
315    pub(crate) mem_required_for_traversal: u64,
316}
317
318#[derive(Debug, PartialEq)]
319#[cfg_attr(test, derive(Arbitrary))]
320pub enum FstHierarchyEntry {
321    Scope {
322        tpe: FstScopeType,
323        name: String,
324        component: String,
325    },
326    UpScope,
327    Var {
328        tpe: FstVarType,
329        direction: FstVarDirection,
330        name: String,
331        length: u32,
332        handle: FstSignalHandle,
333        is_alias: bool,
334    },
335    PathName {
336        /// this id is used by other attributes to refer to the path
337        id: u64,
338        name: String,
339    },
340    SourceStem {
341        is_instantiation: bool,
342        path_id: u64,
343        line: u64,
344    },
345    Comment {
346        string: String,
347    },
348    EnumTable {
349        name: String,
350        handle: u64,
351        mapping: Vec<(String, String)>,
352    },
353    EnumTableRef {
354        handle: u64,
355    },
356    VhdlVarInfo {
357        type_name: String,
358        var_type: FstVhdlVarType,
359        data_type: FstVhdlDataType,
360    },
361    AttributeEnd,
362}
363
364#[derive(Debug, Copy, Clone)]
365#[cfg_attr(test, derive(Arbitrary))]
366pub(crate) enum HierarchyCompression {
367    Uncompressed,
368    ZLib,
369    Lz4,
370    Lz4Duo,
371}
372
373type BitMaskWord = u64;
374
375pub(crate) struct BitMask {
376    inner: Vec<BitMaskWord>,
377}
378
379impl BitMask {
380    pub(crate) fn repeat(value: bool, size: usize) -> Self {
381        let word = if value { BitMaskWord::MAX } else { 0 };
382        let word_count = size.div_ceil(BitMaskWord::BITS as usize);
383        Self {
384            inner: vec![word; word_count],
385        }
386    }
387
388    pub(crate) fn set(&mut self, index: usize, value: bool) {
389        let (word_idx, bit_idx) = Self::word_and_bit_index(index);
390        if value {
391            self.inner[word_idx] |= (1 as BitMaskWord) << bit_idx;
392        } else {
393            self.inner[word_idx] &= !((1 as BitMaskWord) << bit_idx);
394        }
395    }
396
397    fn word_and_bit_index(index: usize) -> (usize, usize) {
398        let word_idx = index / BitMaskWord::BITS as usize;
399        let bit_idx = index - word_idx * BitMaskWord::BITS as usize;
400        (word_idx, bit_idx)
401    }
402
403    pub(crate) fn is_set(&self, index: usize) -> bool {
404        let (word_idx, bit_idx) = Self::word_and_bit_index(index);
405        (self.inner[word_idx] >> bit_idx) & 1 == 1
406    }
407}
408
409pub(crate) struct DataFilter {
410    pub(crate) start: u64,
411    pub(crate) end: u64,
412    pub(crate) signals: BitMask,
413}
414
415#[derive(Debug, Clone, Copy, PartialEq)]
416pub(crate) enum DataSectionKind {
417    Standard,
418    DynamicAlias,
419    DynamicAlias2,
420}
421
422#[derive(Debug, Clone, Copy)]
423pub(crate) enum ValueChangePackType {
424    Lz4,
425    FastLz,
426    Zlib,
427}
428
429impl ValueChangePackType {
430    pub(crate) fn from_u8(value: u8) -> Self {
431        match value {
432            b'4' => ValueChangePackType::Lz4,
433            b'F' => ValueChangePackType::FastLz,
434            _ => ValueChangePackType::Zlib,
435        }
436    }
437}
438
439impl DataSectionKind {
440    pub(crate) fn from_block_type(tpe: &BlockType) -> Option<Self> {
441        match tpe {
442            BlockType::VcData => Some(DataSectionKind::Standard),
443            BlockType::VcDataDynamicAlias => Some(DataSectionKind::DynamicAlias),
444            BlockType::VcDataDynamicAlias2 => Some(DataSectionKind::DynamicAlias2),
445            _ => None,
446        }
447    }
448}
449
450#[cfg(test)]
451mod tests {
452    use super::*;
453
454    #[test]
455    fn test_sizes() {
456        // 1-bit to distinguish between real and bitvec + length
457        assert_eq!(std::mem::size_of::<SignalInfo>(), 4);
458    }
459}