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