Skip to main content

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    // 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, PartialEq)]
224pub enum MiscType {
225    Comment = 0,
226    EnvVar = 1,
227    SupVar = 2,
228    PathName = 3,
229    SourceStem = 4,
230    SourceInstantiationStem = 5,
231    ValueList = 6,
232    EnumTable = 7,
233    Unknown = 8,
234}
235
236pub(crate) const DOUBLE_ENDIAN_TEST: f64 = std::f64::consts::E;
237
238#[derive(Debug, PartialEq)]
239#[cfg_attr(test, derive(Arbitrary))]
240#[allow(dead_code)]
241pub(crate) struct Header {
242    pub(crate) start_time: u64,
243    pub(crate) end_time: u64,
244    pub(crate) memory_used_by_writer: u64,
245    pub(crate) scope_count: u64,
246    pub(crate) var_count: u64,
247    pub(crate) max_var_id_code: u64, // aka maxhandle
248    pub(crate) vc_section_count: u64,
249    pub(crate) timescale_exponent: i8,
250    pub(crate) version: String,
251    pub(crate) date: String,
252    pub(crate) file_type: FileType,
253    pub(crate) time_zero: u64,
254}
255
256#[derive(Debug, PartialEq)]
257#[cfg_attr(test, derive(Arbitrary))]
258pub(crate) enum SignalInfo {
259    BitVec(NonZeroU32),
260    Real,
261}
262
263impl SignalInfo {
264    pub(crate) fn from_file_format(value: u32) -> Self {
265        if value == 0 {
266            SignalInfo::Real
267        } else if value != u32::MAX {
268            SignalInfo::BitVec(NonZeroU32::new(value + 1).unwrap())
269        } else {
270            SignalInfo::BitVec(NonZeroU32::new(1).unwrap())
271        }
272    }
273
274    #[cfg(test)]
275    pub(crate) fn to_file_format(&self) -> u32 {
276        match self {
277            SignalInfo::BitVec(value) => match value.get() {
278                1 => u32::MAX,
279                other => other - 1,
280            },
281            SignalInfo::Real => 0,
282        }
283    }
284
285    #[inline]
286    pub(crate) fn len(&self) -> u32 {
287        match self {
288            SignalInfo::BitVec(value) => value.get() - 1,
289            SignalInfo::Real => 8,
290        }
291    }
292
293    #[inline]
294    pub(crate) fn is_real(&self) -> bool {
295        match self {
296            SignalInfo::BitVec(_) => false,
297            SignalInfo::Real => true,
298        }
299    }
300}
301
302#[derive(Debug, PartialEq)]
303#[cfg_attr(test, derive(Arbitrary))]
304pub(crate) struct BlackoutData {
305    pub(crate) time: u64,
306    pub(crate) contains_activity: bool,
307}
308
309#[derive(Debug, Clone)]
310pub(crate) struct DataSectionInfo {
311    pub(crate) file_offset: u64, // points to section length
312    pub(crate) start_time: u64,
313    pub(crate) end_time: u64,
314    pub(crate) kind: DataSectionKind,
315    /// the number of bytes needed to store all uncompressed value change data in the block
316    #[allow(dead_code)]
317    pub(crate) mem_required_for_traversal: u64,
318}
319
320#[derive(Debug, PartialEq)]
321#[cfg_attr(test, derive(Arbitrary))]
322pub enum FstHierarchyEntry {
323    Scope {
324        tpe: FstScopeType,
325        name: String,
326        component: String,
327    },
328    UpScope,
329    Var {
330        tpe: FstVarType,
331        direction: FstVarDirection,
332        name: String,
333        length: u32,
334        handle: FstSignalHandle,
335        is_alias: bool,
336    },
337    PathName {
338        /// this id is used by other attributes to refer to the path
339        id: u64,
340        name: String,
341    },
342    SourceStem {
343        is_instantiation: bool,
344        path_id: u64,
345        line: u64,
346    },
347    Comment {
348        string: String,
349    },
350    EnumTable {
351        name: String,
352        handle: u64,
353        mapping: Vec<(String, String)>,
354    },
355    EnumTableRef {
356        handle: u64,
357    },
358    VhdlVarInfo {
359        type_name: String,
360        var_type: FstVhdlVarType,
361        data_type: FstVhdlDataType,
362    },
363    AttributeEnd,
364}
365
366#[derive(Debug, Copy, Clone)]
367#[cfg_attr(test, derive(Arbitrary))]
368pub(crate) enum HierarchyCompression {
369    Uncompressed,
370    ZLib,
371    Lz4,
372    Lz4Duo,
373}
374
375type BitMaskWord = u64;
376
377pub(crate) struct BitMask {
378    inner: Vec<BitMaskWord>,
379}
380
381impl BitMask {
382    pub(crate) fn repeat(value: bool, size: usize) -> Self {
383        let word = if value { BitMaskWord::MAX } else { 0 };
384        let word_count = size.div_ceil(BitMaskWord::BITS as usize);
385        Self {
386            inner: vec![word; word_count],
387        }
388    }
389
390    pub(crate) fn set(&mut self, index: usize, value: bool) {
391        let (word_idx, bit_idx) = Self::word_and_bit_index(index);
392        if value {
393            self.inner[word_idx] |= (1 as BitMaskWord) << bit_idx;
394        } else {
395            self.inner[word_idx] &= !((1 as BitMaskWord) << bit_idx);
396        }
397    }
398
399    fn word_and_bit_index(index: usize) -> (usize, usize) {
400        let word_idx = index / BitMaskWord::BITS as usize;
401        let bit_idx = index - word_idx * BitMaskWord::BITS as usize;
402        (word_idx, bit_idx)
403    }
404
405    pub(crate) fn is_set(&self, index: usize) -> bool {
406        let (word_idx, bit_idx) = Self::word_and_bit_index(index);
407        (self.inner[word_idx] >> bit_idx) & 1 == 1
408    }
409}
410
411pub(crate) struct DataFilter {
412    pub(crate) start: u64,
413    pub(crate) end: u64,
414    pub(crate) signals: BitMask,
415}
416
417#[derive(Debug, Clone, Copy, PartialEq)]
418pub(crate) enum DataSectionKind {
419    Standard,
420    DynamicAlias,
421    DynamicAlias2,
422}
423
424#[derive(Debug, Clone, Copy)]
425pub(crate) enum ValueChangePackType {
426    Lz4,
427    FastLz,
428    Zlib,
429}
430
431impl ValueChangePackType {
432    pub(crate) fn from_u8(value: u8) -> Self {
433        match value {
434            b'4' => ValueChangePackType::Lz4,
435            b'F' => ValueChangePackType::FastLz,
436            _ => ValueChangePackType::Zlib,
437        }
438    }
439}
440
441impl DataSectionKind {
442    pub(crate) fn from_block_type(tpe: &BlockType) -> Option<Self> {
443        match tpe {
444            BlockType::VcData => Some(DataSectionKind::Standard),
445            BlockType::VcDataDynamicAlias => Some(DataSectionKind::DynamicAlias),
446            BlockType::VcDataDynamicAlias2 => Some(DataSectionKind::DynamicAlias2),
447            _ => None,
448        }
449    }
450}
451
452#[cfg(test)]
453mod tests {
454    use super::*;
455
456    #[test]
457    fn test_sizes() {
458        // 1-bit to distinguish between real and bitvec + length
459        assert_eq!(std::mem::size_of::<SignalInfo>(), 4);
460    }
461}