Skip to main content

libsurfer/
wellen.rs

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