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