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
151#[repr(u8)]
152#[derive(Debug, TryFromPrimitive, Clone, Copy, PartialEq)]
153#[cfg_attr(test, derive(Arbitrary))]
154pub enum FstVarDirection {
155    Implicit = 0,
156    Input = 1,
157    Output = 2,
158    InOut = 3,
159    Buffer = 4,
160    Linkage = 5,
161}
162
163#[repr(u8)]
164#[derive(Debug, TryFromPrimitive, Clone, Copy, PartialEq)]
165#[cfg_attr(test, derive(Arbitrary))]
166pub enum FstVhdlVarType {
167    None = 0,
168    Signal = 1,
169    Variable = 2,
170    Constant = 3,
171    File = 4,
172    Memory = 5,
173}
174
175#[repr(u8)]
176#[derive(Debug, TryFromPrimitive, Clone, Copy, PartialEq)]
177#[cfg_attr(test, derive(Arbitrary))]
178pub enum FstVhdlDataType {
179    None = 0,
180    Boolean = 1,
181    Bit = 2,
182    Vector = 3,
183    ULogic = 4,
184    ULogicVector = 5,
185    Logic = 6,
186    LogicVector = 7,
187    Unsigned = 8,
188    Signed = 9,
189    Integer = 10,
190    Real = 11,
191    Natural = 12,
192    Positive = 13,
193    Time = 14,
194    Character = 15,
195    String = 16,
196}
197
198#[repr(u8)]
199#[derive(Debug, TryFromPrimitive, PartialEq)]
200pub enum AttributeType {
201    Misc = 0,
202    Array = 1,
203    Enum = 2,
204    Pack = 3,
205}
206
207#[repr(u8)]
208#[derive(Debug, TryFromPrimitive, PartialEq)]
209pub enum MiscType {
210    Comment = 0,
211    EnvVar = 1,
212    SupVar = 2,
213    PathName = 3,
214    SourceStem = 4,
215    SourceInstantiationStem = 5,
216    ValueList = 6,
217    EnumTable = 7,
218    Unknown = 8,
219}
220
221pub(crate) const DOUBLE_ENDIAN_TEST: f64 = std::f64::consts::E;
222
223#[derive(Debug, PartialEq)]
224#[cfg_attr(test, derive(Arbitrary))]
225#[allow(dead_code)]
226pub(crate) struct Header {
227    pub(crate) start_time: u64,
228    pub(crate) end_time: u64,
229    pub(crate) memory_used_by_writer: u64,
230    pub(crate) scope_count: u64,
231    pub(crate) var_count: u64,
232    pub(crate) max_var_id_code: u64, // aka maxhandle
233    pub(crate) vc_section_count: u64,
234    pub(crate) timescale_exponent: i8,
235    pub(crate) version: String,
236    pub(crate) date: String,
237    pub(crate) file_type: FileType,
238    pub(crate) time_zero: u64,
239}
240
241#[derive(Debug, PartialEq)]
242#[cfg_attr(test, derive(Arbitrary))]
243pub(crate) enum SignalInfo {
244    BitVec(NonZeroU32),
245    Real,
246}
247
248impl SignalInfo {
249    pub(crate) fn from_file_format(value: u32) -> Self {
250        if value == 0 {
251            SignalInfo::Real
252        } else if value != u32::MAX {
253            SignalInfo::BitVec(NonZeroU32::new(value + 1).unwrap())
254        } else {
255            SignalInfo::BitVec(NonZeroU32::new(1).unwrap())
256        }
257    }
258
259    #[cfg(test)]
260    pub(crate) fn to_file_format(&self) -> u32 {
261        match self {
262            SignalInfo::BitVec(value) => match value.get() {
263                1 => u32::MAX,
264                other => other - 1,
265            },
266            SignalInfo::Real => 0,
267        }
268    }
269
270    #[inline]
271    pub(crate) fn len(&self) -> u32 {
272        match self {
273            SignalInfo::BitVec(value) => value.get() - 1,
274            SignalInfo::Real => 8,
275        }
276    }
277
278    #[inline]
279    pub(crate) fn is_real(&self) -> bool {
280        match self {
281            SignalInfo::BitVec(_) => false,
282            SignalInfo::Real => true,
283        }
284    }
285}
286
287#[derive(Debug, PartialEq)]
288#[cfg_attr(test, derive(Arbitrary))]
289pub(crate) struct BlackoutData {
290    pub(crate) time: u64,
291    pub(crate) contains_activity: bool,
292}
293
294#[derive(Debug, Clone)]
295pub(crate) struct DataSectionInfo {
296    pub(crate) file_offset: u64, // points to section length
297    pub(crate) start_time: u64,
298    pub(crate) end_time: u64,
299    pub(crate) kind: DataSectionKind,
300    /// the number of bytes needed to store all uncompressed value change data in the block
301    #[allow(dead_code)]
302    pub(crate) mem_required_for_traversal: u64,
303}
304
305#[derive(Debug, PartialEq)]
306#[cfg_attr(test, derive(Arbitrary))]
307pub enum FstHierarchyEntry {
308    Scope {
309        tpe: FstScopeType,
310        name: String,
311        component: String,
312    },
313    UpScope,
314    Var {
315        tpe: FstVarType,
316        direction: FstVarDirection,
317        name: String,
318        length: u32,
319        handle: FstSignalHandle,
320        is_alias: bool,
321    },
322    PathName {
323        /// this id is used by other attributes to refer to the path
324        id: u64,
325        name: String,
326    },
327    SourceStem {
328        is_instantiation: bool,
329        path_id: u64,
330        line: u64,
331    },
332    Comment {
333        string: String,
334    },
335    EnumTable {
336        name: String,
337        handle: u64,
338        mapping: Vec<(String, String)>,
339    },
340    EnumTableRef {
341        handle: u64,
342    },
343    VhdlVarInfo {
344        type_name: String,
345        var_type: FstVhdlVarType,
346        data_type: FstVhdlDataType,
347    },
348    AttributeEnd,
349}
350
351#[derive(Debug, Copy, Clone)]
352#[cfg_attr(test, derive(Arbitrary))]
353pub(crate) enum HierarchyCompression {
354    Uncompressed,
355    ZLib,
356    Lz4,
357    Lz4Duo,
358}
359
360type BitMaskWord = u64;
361
362pub(crate) struct BitMask {
363    inner: Vec<BitMaskWord>,
364}
365
366impl BitMask {
367    pub(crate) fn repeat(value: bool, size: usize) -> Self {
368        let word = if value { BitMaskWord::MAX } else { 0 };
369        let word_count = size.div_ceil(BitMaskWord::BITS as usize);
370        Self {
371            inner: vec![word; word_count],
372        }
373    }
374
375    pub(crate) fn set(&mut self, index: usize, value: bool) {
376        let (word_idx, bit_idx) = Self::word_and_bit_index(index);
377        if value {
378            self.inner[word_idx] |= (1 as BitMaskWord) << bit_idx;
379        } else {
380            self.inner[word_idx] &= !((1 as BitMaskWord) << bit_idx);
381        }
382    }
383
384    fn word_and_bit_index(index: usize) -> (usize, usize) {
385        let word_idx = index / BitMaskWord::BITS as usize;
386        let bit_idx = index - word_idx * BitMaskWord::BITS as usize;
387        (word_idx, bit_idx)
388    }
389
390    pub(crate) fn is_set(&self, index: usize) -> bool {
391        let (word_idx, bit_idx) = Self::word_and_bit_index(index);
392        (self.inner[word_idx] >> bit_idx) & 1 == 1
393    }
394}
395
396pub(crate) struct DataFilter {
397    pub(crate) start: u64,
398    pub(crate) end: u64,
399    pub(crate) signals: BitMask,
400}
401
402#[derive(Debug, Clone, Copy, PartialEq)]
403pub(crate) enum DataSectionKind {
404    Standard,
405    DynamicAlias,
406    DynamicAlias2,
407}
408
409#[derive(Debug, Clone, Copy)]
410pub(crate) enum ValueChangePackType {
411    Lz4,
412    FastLz,
413    Zlib,
414}
415
416impl ValueChangePackType {
417    pub(crate) fn from_u8(value: u8) -> Self {
418        match value {
419            b'4' => ValueChangePackType::Lz4,
420            b'F' => ValueChangePackType::FastLz,
421            _ => ValueChangePackType::Zlib,
422        }
423    }
424}
425
426impl DataSectionKind {
427    pub(crate) fn from_block_type(tpe: &BlockType) -> Option<Self> {
428        match tpe {
429            BlockType::VcData => Some(DataSectionKind::Standard),
430            BlockType::VcDataDynamicAlias => Some(DataSectionKind::DynamicAlias),
431            BlockType::VcDataDynamicAlias2 => Some(DataSectionKind::DynamicAlias2),
432            _ => None,
433        }
434    }
435}
436
437#[cfg(test)]
438mod tests {
439    use super::*;
440
441    #[test]
442    fn test_sizes() {
443        // 1-bit to distinguish between real and bitvec + length
444        assert_eq!(std::mem::size_of::<SignalInfo>(), 4);
445    }
446}