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