libsurfer/
wellen.rs

1use std::collections::{HashMap, HashSet};
2use std::fmt::Write;
3
4use color_eyre::{eyre::anyhow, eyre::bail, Result};
5use derive_more::Debug;
6use log::warn;
7use num::{BigUint, ToPrimitive};
8use surfer_translation_types::{
9    VariableDirection, VariableEncoding, VariableIndex, VariableType, VariableValue,
10};
11use wellen::{
12    FileFormat, Hierarchy, ScopeType, Signal, SignalEncoding, SignalRef, SignalSource, Time,
13    TimeTable, TimeTableIdx, Timescale, TimescaleUnit, Var, VarRef, VarType,
14};
15
16use crate::time::{TimeScale, TimeUnit};
17use crate::variable_direction::VariableDirectionExt;
18use crate::variable_index::VariableIndexExt;
19use crate::variable_type::VariableTypeExt;
20use crate::wave_container::{
21    MetaData, QueryResult, ScopeId, ScopeRef, ScopeRefExt, VarId, VariableMeta, VariableRef,
22    VariableRefExt,
23};
24
25static UNIQUE_ID_COUNT: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
26
27#[derive(Debug)]
28pub struct WellenContainer {
29    #[debug(skip)]
30    hierarchy: std::sync::Arc<Hierarchy>,
31    /// the url of a remote server, None if waveforms are loaded locally
32    server: Option<String>,
33    scopes: Vec<String>,
34    vars: Vec<String>,
35    signals: HashMap<SignalRef, Signal>,
36    /// keeps track of signals that need to be loaded once the body of the waveform file has been loaded
37    signals_to_be_loaded: HashSet<SignalRef>,
38    time_table: TimeTable,
39    #[debug(skip)]
40    source: Option<SignalSource>,
41    unique_id: u64,
42    body_loaded: bool,
43}
44
45/// Returned by `load_variables` if we want to load the variables on a background thread.
46/// This struct is currently only used by wellen
47pub struct LoadSignalsCmd {
48    signals: Vec<SignalRef>,
49    from_unique_id: u64,
50    payload: LoadSignalPayload,
51}
52
53pub enum HeaderResult {
54    /// Result of locally parsing the header of a waveform file with wellen from a file.
55    LocalFile(Box<wellen::viewers::HeaderResult<std::io::BufReader<std::fs::File>>>),
56    /// Result of locally parsing the header of a waveform file with wellen from bytes.
57    LocalBytes(Box<wellen::viewers::HeaderResult<std::io::Cursor<Vec<u8>>>>),
58    /// Result of querying a remote surfer server (which has used wellen).
59    Remote(std::sync::Arc<Hierarchy>, FileFormat, String),
60}
61
62pub enum BodyResult {
63    /// Result of locally parsing the body of a waveform file with wellen.
64    Local(wellen::viewers::BodyResult),
65    /// Result of querying a remote surfer server (which has used wellen).
66    Remote(Vec<wellen::Time>, String),
67}
68
69pub enum LoadSignalPayload {
70    Local(SignalSource, std::sync::Arc<Hierarchy>),
71    Remote(String),
72}
73
74impl LoadSignalsCmd {
75    pub fn destruct(self) -> (Vec<SignalRef>, u64, LoadSignalPayload) {
76        (self.signals, self.from_unique_id, self.payload)
77    }
78}
79
80pub struct LoadSignalsResult {
81    source: Option<SignalSource>,
82    server: Option<String>,
83    signals: Vec<(SignalRef, Signal)>,
84    from_unique_id: u64,
85}
86
87impl LoadSignalsResult {
88    pub fn local(
89        source: SignalSource,
90        signals: Vec<(SignalRef, Signal)>,
91        from_unique_id: u64,
92    ) -> Self {
93        Self {
94            source: Some(source),
95            server: None,
96            signals,
97            from_unique_id,
98        }
99    }
100
101    pub fn remote(server: String, signals: Vec<(SignalRef, Signal)>, from_unique_id: u64) -> Self {
102        Self {
103            source: None,
104            server: Some(server),
105            signals,
106            from_unique_id,
107        }
108    }
109
110    pub fn len(&self) -> usize {
111        self.signals.len()
112    }
113
114    pub fn is_empty(&self) -> bool {
115        self.signals.is_empty()
116    }
117}
118
119pub fn convert_format(format: FileFormat) -> crate::WaveFormat {
120    match format {
121        FileFormat::Vcd => crate::WaveFormat::Vcd,
122        FileFormat::Fst => crate::WaveFormat::Fst,
123        FileFormat::Ghw => crate::WaveFormat::Ghw,
124        FileFormat::Unknown => unreachable!("should never get here"),
125    }
126}
127
128impl WellenContainer {
129    pub fn new(hierarchy: std::sync::Arc<Hierarchy>, server: Option<String>) -> Self {
130        // generate a list of names for all variables and scopes since they will be requested by the parser
131        let h = &hierarchy;
132        let scopes = h.iter_scopes().map(|r| r.full_name(h)).collect::<Vec<_>>();
133        let vars: Vec<String> = h.iter_vars().map(|r| r.full_name(h)).collect::<Vec<_>>();
134
135        let unique_id = UNIQUE_ID_COUNT.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
136
137        Self {
138            hierarchy,
139            server,
140            scopes,
141            vars,
142            signals: HashMap::new(),
143            signals_to_be_loaded: HashSet::new(),
144            time_table: vec![],
145            source: None,
146            unique_id,
147            body_loaded: false,
148        }
149    }
150
151    pub fn body_loaded(&self) -> bool {
152        self.body_loaded
153    }
154
155    pub fn add_body(&mut self, body: BodyResult) -> Result<Option<LoadSignalsCmd>> {
156        if self.body_loaded {
157            bail!("Did we just parse the body twice? That should not happen!");
158        }
159        match body {
160            BodyResult::Local(body) => {
161                if self.server.is_some() {
162                    bail!("We are connected to a server, but also received the result of parsing a file locally. Something is going wrong here!");
163                }
164                self.time_table = body.time_table;
165                self.source = Some(body.source);
166            }
167            BodyResult::Remote(time_table, server) => {
168                if let Some(old) = &self.server {
169                    if old != &server {
170                        bail!("Inconsistent server URLs: {old} vs. {server}")
171                    }
172                } else {
173                    bail!("Missing server URL!");
174                }
175                self.time_table = time_table;
176            }
177        }
178        self.body_loaded = true;
179
180        // we might have to load some signals that the user has already added while the
181        // body of the waveform file was being parser
182        Ok(self.load_signals(&[]))
183    }
184
185    pub fn metadata(&self) -> MetaData {
186        let timescale = self
187            .hierarchy
188            .timescale()
189            .unwrap_or(Timescale::new(1, TimescaleUnit::Unknown));
190        let date = None;
191        MetaData {
192            date,
193            version: Some(self.hierarchy.version().to_string()),
194            timescale: TimeScale {
195                unit: TimeUnit::from(timescale.unit),
196                multiplier: Some(timescale.factor),
197            },
198        }
199    }
200
201    pub fn max_timestamp(&self) -> Option<BigUint> {
202        self.time_table.last().map(|t| BigUint::from(*t))
203    }
204
205    pub fn is_fully_loaded(&self) -> bool {
206        (self.source.is_some() || self.server.is_some()) && self.signals_to_be_loaded.is_empty()
207    }
208
209    pub fn variable_names(&self) -> Vec<String> {
210        self.vars.clone()
211    }
212
213    fn lookup_scope(&self, scope: &ScopeRef) -> Option<wellen::ScopeRef> {
214        match scope.id {
215            ScopeId::Wellen(id) => Some(id),
216            ScopeId::None => self.hierarchy.lookup_scope(scope.strs()),
217        }
218    }
219
220    fn has_scope(&self, scope: &ScopeRef) -> bool {
221        match scope.id {
222            ScopeId::Wellen(_) => true,
223            ScopeId::None => self.hierarchy.lookup_scope(scope.strs()).is_some(),
224        }
225    }
226
227    pub fn variables(&self) -> Vec<VariableRef> {
228        let h = &self.hierarchy;
229        h.iter_vars()
230            .map(|r| VariableRef::from_hierarchy_string(&r.full_name(h)))
231            .collect::<Vec<_>>()
232    }
233
234    pub fn variables_in_scope(&self, scope_ref: &ScopeRef) -> Vec<VariableRef> {
235        let h = &self.hierarchy;
236        // special case of an empty scope means that we want to variables that are part of the toplevel
237        if scope_ref.has_empty_strs() {
238            h.vars()
239                .filter(|id| h[*id].var_type() != VarType::Parameter)
240                .map(|id| {
241                    VariableRef::new_with_id(
242                        scope_ref.clone(),
243                        h[id].name(h).to_string(),
244                        VarId::Wellen(id),
245                    )
246                })
247                .collect::<Vec<_>>()
248        } else {
249            let scope = match self.lookup_scope(scope_ref) {
250                Some(id) => &h[id],
251                None => {
252                    warn!("Found no scope '{scope_ref}'. Defaulting to no variables");
253                    return vec![];
254                }
255            };
256            scope
257                .vars(h)
258                .filter(|id| h[*id].var_type() != VarType::Parameter)
259                .map(|id| {
260                    VariableRef::new_with_id(
261                        scope_ref.clone(),
262                        h[id].name(h).to_string(),
263                        VarId::Wellen(id),
264                    )
265                })
266                .collect::<Vec<_>>()
267        }
268    }
269
270    pub fn parameters_in_scope(&self, scope_ref: &ScopeRef) -> Vec<VariableRef> {
271        let h = &self.hierarchy;
272        // special case of an empty scope means that we want to variables that are part of the toplevel
273        if scope_ref.strs().is_empty() {
274            h.vars()
275                .filter(|id| h[*id].var_type() == VarType::Parameter)
276                .map(|id| {
277                    VariableRef::new_with_id(
278                        scope_ref.clone(),
279                        h[id].name(h).to_string(),
280                        VarId::Wellen(id),
281                    )
282                })
283                .collect::<Vec<_>>()
284        } else {
285            let scope = match self.lookup_scope(scope_ref) {
286                Some(id) => &h[id],
287                None => {
288                    warn!("Found no scope '{scope_ref}'. Defaulting to no variables");
289                    return vec![];
290                }
291            };
292            scope
293                .vars(h)
294                .filter(|id| h[*id].var_type() == VarType::Parameter)
295                .map(|id| {
296                    VariableRef::new_with_id(
297                        scope_ref.clone(),
298                        h[id].name(h).to_string(),
299                        VarId::Wellen(id),
300                    )
301                })
302                .collect::<Vec<_>>()
303        }
304    }
305
306    pub fn no_variables_in_scope(&self, scope_ref: &ScopeRef) -> bool {
307        let h = &self.hierarchy;
308        // special case of an empty scope means that we want to variables that are part of the toplevel
309        if scope_ref.has_empty_strs() {
310            h.vars().next().is_none()
311        } else {
312            let scope = match self.lookup_scope(scope_ref) {
313                Some(id) => &h[id],
314                None => {
315                    warn!("Found no scope '{scope_ref}'. Defaulting to no variables");
316                    return true;
317                }
318            };
319            scope.vars(h).next().is_none()
320        }
321    }
322
323    pub fn update_variable_ref(&self, variable: &VariableRef) -> Option<VariableRef> {
324        // IMPORTANT: lookup by name!
325        let h = &self.hierarchy;
326
327        let (var, new_scope_ref) = if variable.path.has_empty_strs() {
328            let var = h.lookup_var(&[], &variable.name)?;
329            (var, variable.path.clone())
330        } else {
331            // first we lookup the scope in order to update the scope reference
332            let scope = h.lookup_scope(variable.path.strs())?;
333            let new_scope_ref = variable.path.with_id(ScopeId::Wellen(scope));
334
335            // now we lookup the variable
336            let var = h[scope].vars(h).find(|r| h[*r].name(h) == variable.name)?;
337            (var, new_scope_ref)
338        };
339
340        let new_variable_ref =
341            VariableRef::new_with_id(new_scope_ref, variable.name.clone(), VarId::Wellen(var));
342        Some(new_variable_ref)
343    }
344
345    pub fn get_var(&self, r: &VariableRef) -> Result<&Var> {
346        let h = &self.hierarchy;
347        self.get_var_ref(r).map(|r| &h[r])
348    }
349
350    pub fn get_enum_map(&self, v: &Var) -> HashMap<String, String> {
351        match v.enum_type(&self.hierarchy) {
352            None => HashMap::new(),
353            Some((_, mapping)) => HashMap::from_iter(
354                mapping
355                    .into_iter()
356                    .map(|(k, v)| (k.to_string(), v.to_string())),
357            ),
358        }
359    }
360
361    fn get_var_ref(&self, r: &VariableRef) -> Result<VarRef> {
362        match r.id {
363            VarId::Wellen(id) => Ok(id),
364            VarId::None => {
365                let h = &self.hierarchy;
366                let var = match h.lookup_var(r.path.strs(), &r.name) {
367                    None => bail!("Failed to find variable: {r:?}"),
368                    Some(id) => id,
369                };
370                Ok(var)
371            }
372        }
373    }
374
375    pub fn load_variables<S: AsRef<VariableRef>, T: Iterator<Item = S>>(
376        &mut self,
377        variables: T,
378    ) -> Result<Option<LoadSignalsCmd>> {
379        let h = &self.hierarchy;
380        let signal_refs = variables
381            .flat_map(|s| {
382                let r = s.as_ref();
383                self.get_var_ref(r).map(|v| h[v].signal_ref())
384            })
385            .collect::<Vec<_>>();
386        Ok(self.load_signals(&signal_refs))
387    }
388
389    pub fn load_all_params(&mut self) -> Result<Option<LoadSignalsCmd>> {
390        let h = &self.hierarchy;
391        let params = h
392            .iter_vars()
393            .filter(|r| r.var_type() == VarType::Parameter)
394            .map(|r| r.signal_ref())
395            .collect::<Vec<_>>();
396        Ok(self.load_signals(&params))
397    }
398
399    pub fn on_signals_loaded(&mut self, res: LoadSignalsResult) -> Result<Option<LoadSignalsCmd>> {
400        // check to see if this command came from our container, or from a previous file that was open
401        if res.from_unique_id == self.unique_id {
402            // return source or server
403            debug_assert!(self.source.is_none());
404            debug_assert!(self.server.is_none());
405            self.source = res.source;
406            self.server = res.server;
407            debug_assert!(self.server.is_some() || self.source.is_some());
408            // install signals
409            for (id, signal) in res.signals {
410                self.signals.insert(id, signal);
411            }
412        }
413
414        // see if there are any more signals to dispatch
415        Ok(self.load_signals(&[]))
416    }
417
418    fn load_signals(&mut self, ids: &[SignalRef]) -> Option<LoadSignalsCmd> {
419        // make sure that we do not load signals that have already been loaded
420        let filtered_ids = ids
421            .iter()
422            .filter(|id| !self.signals.contains_key(id) && !self.signals_to_be_loaded.contains(id))
423            .cloned()
424            .collect::<Vec<_>>();
425
426        // add signals to signals that need to be loaded
427        self.signals_to_be_loaded.extend(filtered_ids.iter());
428
429        if self.signals_to_be_loaded.is_empty() {
430            return None; // nothing to do here
431        }
432
433        if !self.body_loaded {
434            return None; // it only makes sense to load signals after we have loaded the body
435        }
436
437        // we remove the server name in order to ensure that we do not load the same signal twice
438        if let Some(server) = std::mem::take(&mut self.server) {
439            // load remote signals
440            let mut signals = Vec::from_iter(self.signals_to_be_loaded.drain());
441            signals.sort(); // for some determinism!
442            let cmd = LoadSignalsCmd {
443                signals,
444                payload: LoadSignalPayload::Remote(server),
445                from_unique_id: self.unique_id,
446            };
447            Some(cmd)
448        } else if let Some(source) = std::mem::take(&mut self.source) {
449            // if we have a source available, let's load all signals!
450            let mut signals = Vec::from_iter(self.signals_to_be_loaded.drain());
451            signals.sort(); // for some determinism!
452            let cmd = LoadSignalsCmd {
453                signals,
454                payload: LoadSignalPayload::Local(source, self.hierarchy.clone()),
455                from_unique_id: self.unique_id,
456            };
457            Some(cmd)
458        } else {
459            None
460        }
461    }
462
463    fn time_to_time_table_idx(&self, time: &BigUint) -> Option<TimeTableIdx> {
464        let time: Time = time.to_u64().expect("unsupported time!");
465        let table = &self.time_table;
466        if table.is_empty() || table[0] > time {
467            None
468        } else {
469            // binary search to find correct index
470            let idx = binary_search(table, time);
471            assert!(table[idx] <= time);
472            Some(idx as TimeTableIdx)
473        }
474    }
475
476    pub fn query_variable(
477        &self,
478        variable: &VariableRef,
479        time: &BigUint,
480    ) -> Result<Option<QueryResult>> {
481        let h = &self.hierarchy;
482        // find variable from string
483        let var_ref = self.get_var_ref(variable)?;
484        // map variable to variable ref
485        let signal_ref = h[var_ref].signal_ref();
486        let sig = match self.signals.get(&signal_ref) {
487            Some(sig) => sig,
488            None => {
489                // if the signal has not been loaded yet, we return an empty result
490                return Ok(None);
491            }
492        };
493        let time_table = &self.time_table;
494
495        // convert time to index
496        if let Some(idx) = self.time_to_time_table_idx(time) {
497            // get data offset
498            if let Some(offset) = sig.get_offset(idx) {
499                // which time did we actually get the value for?
500                let offset_time_idx = sig.get_time_idx_at(&offset);
501                let offset_time = time_table[offset_time_idx as usize];
502                // get the last value in a time step (since we ignore delta cycles for now)
503                let current_value = sig.get_value_at(&offset, offset.elements - 1);
504                // the next time the variable changes
505                let next_time = offset
506                    .next_index
507                    .and_then(|i| time_table.get(i.get() as usize));
508
509                let converted_value = convert_variable_value(current_value);
510                let result = QueryResult {
511                    current: Some((BigUint::from(offset_time), converted_value)),
512                    next: next_time.map(|t| BigUint::from(*t)),
513                };
514                return Ok(Some(result));
515            }
516        }
517
518        // if `get_offset` returns None, this means that there is no change at or before the requested time
519        let first_index = sig.get_first_time_idx();
520        let next_time = first_index.and_then(|i| time_table.get(i as usize));
521        let result = QueryResult {
522            current: None,
523            next: next_time.map(|t| BigUint::from(*t)),
524        };
525        Ok(Some(result))
526    }
527
528    pub fn scope_names(&self) -> Vec<String> {
529        self.scopes.clone()
530    }
531
532    pub fn root_scopes(&self) -> Vec<ScopeRef> {
533        let h = &self.hierarchy;
534        h.scopes()
535            .map(|id| ScopeRef::from_strs_with_id(&[h[id].name(h)], ScopeId::Wellen(id)))
536            .collect::<Vec<_>>()
537    }
538
539    pub fn child_scopes(&self, scope_ref: &ScopeRef) -> Result<Vec<ScopeRef>> {
540        let h = &self.hierarchy;
541        let scope = match self.lookup_scope(scope_ref) {
542            Some(id) => &h[id],
543            None => return Err(anyhow!("Failed to find scope {scope_ref:?}")),
544        };
545        Ok(scope
546            .scopes(h)
547            .map(|id| scope_ref.with_subscope(h[id].name(h).to_string(), ScopeId::Wellen(id)))
548            .collect::<Vec<_>>())
549    }
550
551    pub fn scope_exists(&self, scope: &ScopeRef) -> bool {
552        scope.has_empty_strs() | self.has_scope(scope)
553    }
554
555    pub fn get_scope_tooltip_data(&self, scope: &ScopeRef) -> String {
556        let mut out = String::new();
557        if let Some(scope_ref) = self.lookup_scope(scope) {
558            let h = &self.hierarchy;
559            let scope = &h[scope_ref];
560            writeln!(&mut out, "{}", scope_type_to_string(scope.scope_type())).unwrap();
561            if let Some((path, line)) = scope.instantiation_source_loc(h) {
562                writeln!(&mut out, "{path}:{line}").unwrap();
563            }
564            match (scope.component(h), scope.source_loc(h)) {
565                (Some(name), Some((path, line))) => {
566                    write!(&mut out, "{name} : {path}:{line}").unwrap();
567                }
568                (None, Some((path, line))) => {
569                    // check to see if instance and definition are the same
570                    let same = scope
571                        .instantiation_source_loc(h)
572                        .is_some_and(|(i_path, i_line)| path == i_path && line == i_line);
573                    if !same {
574                        write!(&mut out, "{path}:{line}").unwrap();
575                    }
576                }
577                (Some(name), None) => write!(&mut out, "{name}").unwrap(),
578                // remove possible trailing new line
579                (None, None) => {}
580            }
581        }
582        if out.ends_with('\n') {
583            out.pop().unwrap();
584        }
585        out
586    }
587
588    pub fn variable_to_meta(&self, variable: &VariableRef) -> Result<VariableMeta> {
589        let var = self.get_var(variable)?;
590        let encoding = match var.signal_encoding() {
591            SignalEncoding::String => VariableEncoding::String,
592            SignalEncoding::Real => VariableEncoding::Real,
593            SignalEncoding::BitVector(_) => VariableEncoding::BitVector,
594        };
595        Ok(VariableMeta {
596            var: variable.clone(),
597            num_bits: var.length(),
598            variable_type: Some(VariableType::from_wellen_type(var.var_type())),
599            variable_type_name: var.vhdl_type_name(&self.hierarchy).map(|s| s.to_string()),
600            index: var.index().map(VariableIndex::from_wellen_type),
601            direction: Some(VariableDirection::from_wellen_direction(var.direction())),
602            enum_map: self.get_enum_map(var),
603            encoding,
604        })
605    }
606}
607
608fn scope_type_to_string(tpe: ScopeType) -> &'static str {
609    match tpe {
610        ScopeType::Module => "module",
611        ScopeType::Task => "task",
612        ScopeType::Function => "function",
613        ScopeType::Begin => "begin",
614        ScopeType::Fork => "fork",
615        ScopeType::Generate => "generate",
616        ScopeType::Struct => "struct",
617        ScopeType::Union => "union",
618        ScopeType::Class => "class",
619        ScopeType::Interface => "interface",
620        ScopeType::Package => "package",
621        ScopeType::Program => "program",
622        ScopeType::VhdlArchitecture => "architecture",
623        ScopeType::VhdlProcedure => "procedure",
624        ScopeType::VhdlFunction => "function",
625        ScopeType::VhdlRecord => "record",
626        ScopeType::VhdlProcess => "process",
627        ScopeType::VhdlBlock => "block",
628        ScopeType::VhdlForGenerate => "for-generate",
629        ScopeType::VhdlIfGenerate => "if-generate",
630        ScopeType::VhdlGenerate => "generate",
631        ScopeType::VhdlPackage => "package",
632        ScopeType::GhwGeneric => "generic",
633        ScopeType::VhdlArray => "array",
634        ScopeType::Unknown => "unknown",
635        _ => todo!(),
636    }
637}
638
639fn convert_variable_value(value: wellen::SignalValue) -> VariableValue {
640    match value {
641        wellen::SignalValue::Binary(data, _bits) => {
642            VariableValue::BigUint(BigUint::from_bytes_be(data))
643        }
644        wellen::SignalValue::FourValue(_, _) | wellen::SignalValue::NineValue(_, _) => {
645            VariableValue::String(
646                value
647                    .to_bit_string()
648                    .expect("failed to convert value {value:?} to a string"),
649            )
650        }
651        wellen::SignalValue::String(value) => VariableValue::String(value.to_string()),
652        wellen::SignalValue::Real(value) => VariableValue::String(format!("{value}")),
653    }
654}
655
656#[local_impl::local_impl]
657impl FromVarType for VariableType {
658    fn from(signaltype: VarType) -> Self {
659        match signaltype {
660            VarType::Reg => VariableType::VCDReg,
661            VarType::Wire => VariableType::VCDWire,
662            VarType::Integer => VariableType::VCDInteger,
663            VarType::Real => VariableType::VCDReal,
664            VarType::Parameter => VariableType::VCDParameter,
665            VarType::String => VariableType::VCDString,
666            VarType::Time => VariableType::VCDTime,
667            VarType::Event => VariableType::VCDEvent,
668            VarType::Supply0 => VariableType::VCDSupply0,
669            VarType::Supply1 => VariableType::VCDSupply1,
670            VarType::Tri => VariableType::VCDTri,
671            VarType::TriAnd => VariableType::VCDTriAnd,
672            VarType::TriOr => VariableType::VCDTriOr,
673            VarType::TriReg => VariableType::VCDTriReg,
674            VarType::Tri0 => VariableType::VCDTri0,
675            VarType::Tri1 => VariableType::VCDTri1,
676            VarType::WAnd => VariableType::VCDWAnd,
677            VarType::WOr => VariableType::VCDWOr,
678            VarType::Port => VariableType::Port,
679            VarType::Bit => VariableType::Bit,
680            VarType::Logic => VariableType::Logic,
681            VarType::Int => VariableType::VCDInteger,
682            VarType::Enum => VariableType::Enum,
683            VarType::SparseArray => VariableType::SparseArray,
684            VarType::RealTime => VariableType::RealTime,
685            VarType::ShortInt => VariableType::ShortInt,
686            VarType::LongInt => VariableType::LongInt,
687            VarType::Byte => VariableType::Byte,
688            VarType::ShortReal => VariableType::ShortReal,
689            VarType::Boolean => VariableType::Boolean,
690            VarType::BitVector => VariableType::BitVector,
691            VarType::StdLogic => VariableType::StdLogic,
692            VarType::StdLogicVector => VariableType::StdLogicVector,
693            VarType::StdULogic => VariableType::StdULogic,
694            VarType::StdULogicVector => VariableType::StdULogicVector,
695        }
696    }
697}
698
699#[inline]
700fn binary_search(times: &[Time], needle: Time) -> usize {
701    let mut lower_idx = 0usize;
702    let mut upper_idx = times.len() - 1;
703    while lower_idx <= upper_idx {
704        let mid_idx = lower_idx + ((upper_idx - lower_idx) / 2);
705
706        match times[mid_idx].cmp(&needle) {
707            std::cmp::Ordering::Less => {
708                lower_idx = mid_idx + 1;
709            }
710            std::cmp::Ordering::Equal => {
711                return mid_idx;
712            }
713            std::cmp::Ordering::Greater => {
714                upper_idx = mid_idx - 1;
715            }
716        }
717    }
718    lower_idx - 1
719}
720
721#[cfg(test)]
722mod tests {
723    use super::*;
724
725    #[test]
726    fn test_signal_conversion() {
727        let inp0: &[u8] = &[128, 0, 0, 3];
728        let out0 = convert_variable_value(wellen::SignalValue::Binary(inp0, 32));
729        assert_eq!(out0, VariableValue::BigUint(BigUint::from(0x80000003u64)));
730    }
731}