Skip to main content

fst_reader/
types.rs

1// Copyright 2023 The Regents of the University of California
2// Copyright 2024-2026 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    // SystemVerilog
106    SvArray = 22,
107    //
108    AttributeBegin = 252,
109    AttributeEnd = 253,
110    //
111    VcdScope = 254,
112    VcdUpScope = 255,
113}
114
115#[repr(u8)]
116#[derive(Debug, TryFromPrimitive, PartialEq, Copy, Clone)]
117#[cfg_attr(test, derive(Arbitrary))]
118pub enum FstVarType {
119    // VCD
120    Event = 0,
121    Integer = 1,
122    Parameter = 2,
123    Real = 3,
124    RealParameter = 4,
125    Reg = 5,
126    Supply0 = 6,
127    Supply1 = 7,
128    Time = 8,
129    Tri = 9,
130    TriAnd = 10,
131    TriOr = 11,
132    TriReg = 12,
133    Tri0 = 13,
134    Tri1 = 14,
135    Wand = 15, // or WAnd ?
136    Wire = 16,
137    Wor = 17, // or WOr?
138    Port = 18,
139    SparseArray = 19,
140    RealTime = 20,
141    GenericString = 21,
142    // System Verilog
143    Bit = 22,
144    Logic = 23,
145    Int = 24,
146    ShortInt = 25,
147    LongInt = 26,
148    Byte = 27,
149    Enum = 28,
150    ShortReal = 29,
151}
152
153impl FstVarType {
154    /// Returns `true` iff this is a Real, RealParameter, RealTime or ShortReal.
155    pub fn is_real(&self) -> bool {
156        matches!(
157            self,
158            FstVarType::Real
159                | FstVarType::RealParameter
160                | FstVarType::RealTime
161                | FstVarType::ShortReal
162        )
163    }
164}
165
166#[repr(u8)]
167#[derive(Debug, TryFromPrimitive, Clone, Copy, PartialEq)]
168#[cfg_attr(test, derive(Arbitrary))]
169pub enum FstVarDirection {
170    Implicit = 0,
171    Input = 1,
172    Output = 2,
173    InOut = 3,
174    Buffer = 4,
175    Linkage = 5,
176}
177
178#[repr(u8)]
179#[derive(Debug, TryFromPrimitive, Clone, Copy, PartialEq)]
180#[cfg_attr(test, derive(Arbitrary))]
181pub enum FstVhdlVarType {
182    None = 0,
183    Signal = 1,
184    Variable = 2,
185    Constant = 3,
186    File = 4,
187    Memory = 5,
188}
189
190#[repr(u8)]
191#[derive(Debug, TryFromPrimitive, Clone, Copy, PartialEq)]
192#[cfg_attr(test, derive(Arbitrary))]
193pub enum FstVhdlDataType {
194    None = 0,
195    Boolean = 1,
196    Bit = 2,
197    Vector = 3,
198    ULogic = 4,
199    ULogicVector = 5,
200    Logic = 6,
201    LogicVector = 7,
202    Unsigned = 8,
203    Signed = 9,
204    Integer = 10,
205    Real = 11,
206    Natural = 12,
207    Positive = 13,
208    Time = 14,
209    Character = 15,
210    String = 16,
211}
212
213#[repr(u8)]
214#[derive(Debug, TryFromPrimitive, PartialEq)]
215pub enum AttributeType {
216    Misc = 0,
217    Array = 1,
218    Enum = 2,
219    Pack = 3,
220}
221
222#[repr(u8)]
223#[derive(Debug, TryFromPrimitive, Clone, Copy, PartialEq)]
224#[cfg_attr(test, derive(Arbitrary))]
225pub enum FstPackType {
226    None = 0,
227    Unpacked = 1,
228    Packed = 2,
229    TaggedPacked = 3,
230}
231
232#[repr(u8)]
233#[derive(Debug, TryFromPrimitive, Clone, Copy, PartialEq)]
234#[cfg_attr(test, derive(Arbitrary))]
235pub enum FstEnumType {
236    Integer = 0,
237    Bit = 1,
238    Logic = 2,
239    Int = 3,
240    ShortInt = 4,
241    LongInt = 5,
242    Byte = 6,
243    UnsignedInteger = 7,
244    UnsignedBit = 8,
245    UnsignedLogic = 9,
246    UnsignedInt = 10,
247    UnsignedShortInt = 11,
248    UnsignedLongInt = 12,
249    UnsignedByte = 13,
250    Reg = 14,
251    Time = 15,
252}
253
254#[repr(u8)]
255#[derive(Debug, TryFromPrimitive, Clone, Copy, PartialEq)]
256#[cfg_attr(test, derive(Arbitrary))]
257pub enum FstArrayType {
258    None = 0,
259    Unpacked = 1,
260    Packed = 2,
261    Sparse = 3,
262}
263
264#[repr(u8)]
265#[derive(Debug, TryFromPrimitive, PartialEq)]
266pub enum MiscType {
267    Comment = 0,
268    EnvVar = 1,
269    SupVar = 2,
270    PathName = 3,
271    SourceStem = 4,
272    SourceInstantiationStem = 5,
273    ValueList = 6,
274    EnumTable = 7,
275    Unknown = 8,
276}
277
278pub(crate) const DOUBLE_ENDIAN_TEST: f64 = std::f64::consts::E;
279
280#[derive(Debug, PartialEq)]
281#[cfg_attr(test, derive(Arbitrary))]
282#[allow(dead_code)]
283pub(crate) struct Header {
284    pub(crate) start_time: u64,
285    pub(crate) end_time: u64,
286    pub(crate) memory_used_by_writer: u64,
287    pub(crate) scope_count: u64,
288    pub(crate) var_count: u64,
289    pub(crate) max_var_id_code: u64, // aka maxhandle
290    pub(crate) vc_section_count: u64,
291    pub(crate) timescale_exponent: i8,
292    pub(crate) version: String,
293    pub(crate) date: String,
294    pub(crate) file_type: FileType,
295    pub(crate) time_zero: u64,
296}
297
298#[derive(Debug, PartialEq)]
299#[cfg_attr(test, derive(Arbitrary))]
300pub(crate) enum SignalInfo {
301    BitVec(NonZeroU32),
302    Real,
303}
304
305impl SignalInfo {
306    pub(crate) fn from_file_format(value: u32) -> Self {
307        if value == 0 {
308            SignalInfo::Real
309        } else if value != u32::MAX {
310            SignalInfo::BitVec(NonZeroU32::new(value + 1).unwrap())
311        } else {
312            SignalInfo::BitVec(NonZeroU32::new(1).unwrap())
313        }
314    }
315
316    #[cfg(test)]
317    pub(crate) fn to_file_format(&self) -> u32 {
318        match self {
319            SignalInfo::BitVec(value) => match value.get() {
320                1 => u32::MAX,
321                other => other - 1,
322            },
323            SignalInfo::Real => 0,
324        }
325    }
326
327    #[inline]
328    pub(crate) fn len(&self) -> u32 {
329        match self {
330            SignalInfo::BitVec(value) => value.get() - 1,
331            SignalInfo::Real => 8,
332        }
333    }
334
335    #[inline]
336    pub(crate) fn is_real(&self) -> bool {
337        match self {
338            SignalInfo::BitVec(_) => false,
339            SignalInfo::Real => true,
340        }
341    }
342}
343
344#[derive(Debug, PartialEq)]
345#[cfg_attr(test, derive(Arbitrary))]
346pub(crate) struct BlackoutData {
347    pub(crate) time: u64,
348    pub(crate) contains_activity: bool,
349}
350
351#[derive(Debug, Clone)]
352pub(crate) struct DataSectionInfo {
353    pub(crate) file_offset: u64, // points to section length
354    pub(crate) start_time: u64,
355    pub(crate) end_time: u64,
356    pub(crate) kind: DataSectionKind,
357    /// the number of bytes needed to store all uncompressed value change data in the block
358    #[allow(dead_code)]
359    pub(crate) mem_required_for_traversal: u64,
360}
361
362#[derive(Debug, PartialEq)]
363#[cfg_attr(test, derive(Arbitrary))]
364pub enum FstHierarchyEntry {
365    Scope {
366        tpe: FstScopeType,
367        name: String,
368        component: String,
369    },
370    UpScope,
371    Var {
372        tpe: FstVarType,
373        direction: FstVarDirection,
374        name: String,
375        length: u32,
376        handle: FstSignalHandle,
377        is_alias: bool,
378    },
379    PathName {
380        /// this id is used by other attributes to refer to the path
381        id: u64,
382        name: String,
383    },
384    SourceStem {
385        is_instantiation: bool,
386        path_id: u64,
387        line: u64,
388    },
389    Comment {
390        string: String,
391    },
392    EnumTable {
393        name: String,
394        handle: u64,
395        mapping: Vec<(String, String)>,
396    },
397    EnumTableRef {
398        handle: u64,
399    },
400    VhdlVarInfo {
401        type_name: String,
402        var_type: FstVhdlVarType,
403        data_type: FstVhdlDataType,
404    },
405    Array {
406        name: String,
407        array_type: FstArrayType,
408        left: i32,
409        right: i32,
410    },
411    Pack {
412        name: String,
413        pack_type: FstPackType,
414        value: u64,
415    },
416    SVEnum {
417        name: String,
418        enum_type: FstEnumType,
419        value: u64,
420    },
421    AttributeEnd,
422}
423
424#[derive(Debug, Copy, Clone)]
425#[cfg_attr(test, derive(Arbitrary))]
426pub(crate) enum HierarchyCompression {
427    Uncompressed,
428    ZLib,
429    Lz4,
430    Lz4Duo,
431}
432
433type BitMaskWord = u64;
434
435pub(crate) struct BitMask {
436    inner: Vec<BitMaskWord>,
437}
438
439impl BitMask {
440    pub(crate) fn repeat(value: bool, size: usize) -> Self {
441        let word = if value { BitMaskWord::MAX } else { 0 };
442        let word_count = size.div_ceil(BitMaskWord::BITS as usize);
443        Self {
444            inner: vec![word; word_count],
445        }
446    }
447
448    pub(crate) fn set(&mut self, index: usize, value: bool) {
449        let (word_idx, bit_idx) = Self::word_and_bit_index(index);
450        if value {
451            self.inner[word_idx] |= (1 as BitMaskWord) << bit_idx;
452        } else {
453            self.inner[word_idx] &= !((1 as BitMaskWord) << bit_idx);
454        }
455    }
456
457    fn word_and_bit_index(index: usize) -> (usize, usize) {
458        let word_idx = index / BitMaskWord::BITS as usize;
459        let bit_idx = index - word_idx * BitMaskWord::BITS as usize;
460        (word_idx, bit_idx)
461    }
462
463    pub(crate) fn is_set(&self, index: usize) -> bool {
464        let (word_idx, bit_idx) = Self::word_and_bit_index(index);
465        (self.inner[word_idx] >> bit_idx) & 1 == 1
466    }
467}
468
469pub(crate) struct DataFilter {
470    pub(crate) start: u64,
471    pub(crate) end: u64,
472    pub(crate) signals: BitMask,
473}
474
475#[derive(Debug, Clone, Copy, PartialEq)]
476pub(crate) enum DataSectionKind {
477    Standard,
478    DynamicAlias,
479    DynamicAlias2,
480}
481
482#[derive(Debug, Clone, Copy)]
483pub(crate) enum ValueChangePackType {
484    Lz4,
485    FastLz,
486    Zlib,
487}
488
489impl ValueChangePackType {
490    pub(crate) fn from_u8(value: u8) -> Self {
491        match value {
492            b'4' => ValueChangePackType::Lz4,
493            b'F' => ValueChangePackType::FastLz,
494            _ => ValueChangePackType::Zlib,
495        }
496    }
497}
498
499impl DataSectionKind {
500    pub(crate) fn from_block_type(tpe: &BlockType) -> Option<Self> {
501        match tpe {
502            BlockType::VcData => Some(DataSectionKind::Standard),
503            BlockType::VcDataDynamicAlias => Some(DataSectionKind::DynamicAlias),
504            BlockType::VcDataDynamicAlias2 => Some(DataSectionKind::DynamicAlias2),
505            _ => None,
506        }
507    }
508}
509
510#[cfg(test)]
511mod tests {
512    use super::*;
513
514    #[test]
515    fn test_sizes() {
516        // 1-bit to distinguish between real and bitvec + length
517        assert_eq!(std::mem::size_of::<SignalInfo>(), 4);
518    }
519}