Skip to main content

libsurfer/
wave_container.rs

1use std::sync::Mutex;
2
3use chrono::prelude::{DateTime, Utc};
4use eyre::{Result, bail};
5use num::BigUint;
6use serde::{Deserialize, Serialize};
7use surfer_translation_types::VariableValue;
8
9use crate::cxxrtl_container::CxxrtlContainer;
10use crate::time::{TimeScale, TimeUnit};
11use crate::wellen::{BodyResult, LoadSignalsCmd, LoadSignalsResult, WellenContainer};
12
13pub type FieldRef = surfer_translation_types::FieldRef<VarId, ScopeId>;
14pub type ScopeRef = surfer_translation_types::ScopeRef<ScopeId>;
15pub type VariableRef = surfer_translation_types::VariableRef<VarId, ScopeId>;
16pub type VariableMeta = surfer_translation_types::VariableMeta<VarId, ScopeId>;
17
18/// Cache key for analog signal data: (`signal_id`, `translator_name`)
19pub type AnalogCacheKey = (SignalId, String);
20
21#[derive(Debug, Clone)]
22pub enum SimulationStatus {
23    Paused,
24    Running,
25    Finished,
26}
27
28pub struct MetaData {
29    pub date: Option<DateTime<Utc>>,
30    pub version: Option<String>,
31    pub timescale: TimeScale,
32}
33
34/// A backend-specific, numeric reference for fast access to the associated scope.
35#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
36pub enum ScopeId {
37    #[default]
38    None,
39    Wellen(wellen::ScopeRef),
40}
41
42/// A backend-specific, numeric reference for fast access to the associated variable.
43#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
44pub enum VarId {
45    #[default]
46    None,
47    Wellen(wellen::VarRef),
48}
49
50/// A backend-specific, numeric reference for fast access to the associated signal data.
51/// Used as cache key for signal data lookups.
52#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
53pub enum SignalId {
54    #[default]
55    None,
56    Wellen(wellen::SignalRef),
57}
58
59/// Backend-agnostic enum for accessing signal data.
60/// Variants provide iteration over signal changes.
61pub enum SignalAccessor {
62    Wellen(crate::wellen::WellenSignalAccessor),
63    // Future: Cxxrtl(CxxrtlSignalAccessor),
64}
65
66impl SignalAccessor {
67    /// Iterator over signal changes as (`time_u64`, value) pairs
68    #[must_use]
69    pub fn iter_changes(&self) -> Box<dyn Iterator<Item = (u64, VariableValue)> + '_> {
70        match self {
71            SignalAccessor::Wellen(accessor) => accessor.iter_changes(),
72        }
73    }
74}
75
76#[derive(Debug, Default)]
77pub struct QueryResult {
78    pub current: Option<(BigUint, VariableValue)>,
79    pub next: Option<BigUint>,
80}
81
82#[local_impl::local_impl]
83impl ScopeRefExt for ScopeRef {
84    fn empty() -> Self {
85        Self {
86            strs: vec![],
87            id: ScopeId::default(),
88        }
89    }
90
91    fn from_strs<S: ToString>(s: &[S]) -> Self {
92        Self::from_strs_with_id(s, ScopeId::default())
93    }
94
95    fn from_strs_with_id(s: &[impl ToString], id: ScopeId) -> Self {
96        let strs = s.iter().map(ToString::to_string).collect();
97        Self { strs, id }
98    }
99
100    /// Creates a `ScopeRef` from a string with each scope separated by `.`
101    fn from_hierarchy_string(s: &str) -> Self {
102        let strs = s.split('.').map(ToString::to_string).collect();
103        let id = ScopeId::default();
104        Self { strs, id }
105    }
106
107    fn with_subscope(&self, subscope: String, id: ScopeId) -> Self {
108        let mut result = self.clone();
109        result.strs.push(subscope);
110        // the result refers to a different scope, which we do not know the ID of
111        result.id = id;
112        result
113    }
114
115    fn name(&self) -> String {
116        self.strs.last().cloned().unwrap_or_default()
117    }
118
119    fn strs(&self) -> &[String] {
120        &self.strs
121    }
122
123    fn with_id(&self, id: ScopeId) -> Self {
124        let mut out = self.clone();
125        out.id = id;
126        out
127    }
128
129    fn cxxrtl_repr(&self) -> String {
130        self.strs.join(" ")
131    }
132
133    fn has_empty_strs(&self) -> bool {
134        self.strs.is_empty()
135    }
136}
137
138#[local_impl::local_impl]
139impl VariableRefExt for VariableRef {
140    fn new(path: ScopeRef, name: String) -> Self {
141        Self::new_with_id(path, name, VarId::default())
142    }
143
144    fn new_with_id(path: ScopeRef, name: String, id: VarId) -> Self {
145        Self { path, name, id }
146    }
147
148    fn from_hierarchy_string(s: &str) -> Self {
149        let components = s.split('.').map(ToString::to_string).collect::<Vec<_>>();
150
151        if components.is_empty() {
152            Self {
153                path: ScopeRef::empty(),
154                name: String::new(),
155                id: VarId::default(),
156            }
157        } else {
158            Self {
159                path: ScopeRef::from_strs(&components[..(components.len()) - 1]),
160                name: components.last().unwrap().to_string(),
161                id: VarId::default(),
162            }
163        }
164    }
165
166    fn from_hierarchy_string_with_id(s: &str, id: VarId) -> Self {
167        let components = s
168            .split('.')
169            .map(std::string::ToString::to_string)
170            .collect::<Vec<_>>();
171
172        if components.is_empty() {
173            Self {
174                path: ScopeRef::empty(),
175                name: String::new(),
176                id,
177            }
178        } else {
179            Self {
180                path: ScopeRef::from_strs(&components[..(components.len()) - 1]),
181                name: components.last().unwrap().to_string(),
182                id,
183            }
184        }
185    }
186
187    /// A human readable full path to the scope
188    fn full_path_string(&self) -> String {
189        if self.path.has_empty_strs() {
190            self.name.clone()
191        } else {
192            format!("{}.{}", self.path, self.name)
193        }
194    }
195
196    fn full_path(&self) -> Vec<String> {
197        self.path
198            .strs()
199            .iter()
200            .cloned()
201            .chain([self.name.clone()])
202            .collect()
203    }
204
205    fn from_strs(s: &[&str]) -> Self {
206        Self {
207            path: ScopeRef::from_strs(&s[..(s.len() - 1)]),
208            name: (*s.last().expect("from_strs called with an empty string")).to_string(),
209            id: VarId::default(),
210        }
211    }
212
213    fn clear_id(&mut self) {
214        self.id = VarId::default();
215    }
216
217    fn cxxrtl_repr(&self) -> String {
218        self.full_path().join(" ")
219    }
220}
221
222#[local_impl::local_impl]
223impl FieldRefExt for FieldRef {
224    fn without_fields(root: VariableRef) -> Self {
225        Self {
226            root,
227            field: vec![],
228        }
229    }
230
231    fn from_strs(root: &[&str], field: &[&str]) -> Self {
232        Self {
233            root: VariableRef::from_strs(root),
234            field: field.iter().map(ToString::to_string).collect(),
235        }
236    }
237}
238
239pub enum WaveContainer {
240    Wellen(Box<WellenContainer>),
241    /// A wave container that contains nothing. Currently, the only practical use for this is
242    /// a placehodler when serializing and deserializing wave state.
243    Empty,
244    Cxxrtl(Box<Mutex<CxxrtlContainer>>),
245}
246
247impl WaveContainer {
248    #[must_use]
249    pub fn new_waveform(hierarchy: std::sync::Arc<wellen::Hierarchy>) -> Self {
250        WaveContainer::Wellen(Box::new(WellenContainer::new(hierarchy, None)))
251    }
252
253    #[must_use]
254    pub fn new_remote_waveform(
255        server_url: &str,
256        hierarchy: std::sync::Arc<wellen::Hierarchy>,
257    ) -> Self {
258        WaveContainer::Wellen(Box::new(WellenContainer::new(
259            hierarchy,
260            Some(server_url.to_string()),
261        )))
262    }
263
264    /// Creates a new empty wave container. Should only be used as a default for serde. If
265    /// no wave container is present, the `WaveData` should be None, rather than this being
266    /// Empty
267    #[must_use]
268    pub fn __new_empty() -> Self {
269        WaveContainer::Empty
270    }
271
272    // Perform tasks that are done on the main thread each frame
273    pub fn tick(&self) {
274        match self {
275            WaveContainer::Wellen(_) => {}
276            WaveContainer::Empty => {}
277            WaveContainer::Cxxrtl(c) => c.lock().unwrap().tick(),
278        }
279    }
280
281    #[must_use]
282    pub fn wants_anti_aliasing(&self) -> bool {
283        match self {
284            WaveContainer::Wellen(_) => true,
285            WaveContainer::Empty => true,
286            // FIXME: Once we do AA on the server side, we can set this to false
287            WaveContainer::Cxxrtl(_) => true,
288        }
289    }
290
291    /// Returns true if all requested signals have been loaded.
292    /// Used for testing to make sure the GUI is at its final state before taking a
293    /// snapshot.
294    #[must_use]
295    pub fn is_fully_loaded(&self) -> bool {
296        match self {
297            WaveContainer::Wellen(f) => f.is_fully_loaded(),
298            WaveContainer::Empty => true,
299            WaveContainer::Cxxrtl(_) => true,
300        }
301    }
302
303    /// Returns the full names of all variables in the design.
304    #[must_use]
305    pub fn variable_names(&self) -> Vec<String> {
306        match self {
307            WaveContainer::Wellen(f) => f.variable_names(),
308            WaveContainer::Empty => vec![],
309            // I don't know if we can do
310            WaveContainer::Cxxrtl(_) => vec![], // FIXME: List variable names
311        }
312    }
313
314    /// Return all variables (excluding parameters) in the whole design.
315    #[must_use]
316    pub fn variables(&self) -> Vec<VariableRef> {
317        match self {
318            WaveContainer::Wellen(f) => f.variables(),
319            WaveContainer::Empty => vec![],
320            WaveContainer::Cxxrtl(_) => vec![],
321        }
322    }
323
324    /// Return all variables (excluding parameters) in a scope.
325    #[must_use]
326    pub fn variables_in_scope(&self, scope: &ScopeRef) -> Vec<VariableRef> {
327        match self {
328            WaveContainer::Wellen(f) => f.variables_in_scope(scope),
329            WaveContainer::Empty => vec![],
330            WaveContainer::Cxxrtl(c) => c.lock().unwrap().variables_in_module(scope),
331        }
332    }
333
334    /// Return all parameters in a scope.
335    #[must_use]
336    pub fn parameters_in_scope(&self, scope: &ScopeRef) -> Vec<VariableRef> {
337        match self {
338            WaveContainer::Wellen(f) => f.parameters_in_scope(scope),
339            WaveContainer::Empty => vec![],
340            // No parameters in Cxxrtl
341            WaveContainer::Cxxrtl(_) => vec![],
342        }
343    }
344
345    /// Return true if there are no variables or parameters in the scope.
346    #[must_use]
347    pub fn no_variables_in_scope(&self, scope: &ScopeRef) -> bool {
348        match self {
349            WaveContainer::Wellen(f) => f.no_variables_in_scope(scope),
350            WaveContainer::Empty => true,
351            WaveContainer::Cxxrtl(c) => c.lock().unwrap().no_variables_in_module(scope),
352        }
353    }
354
355    /// Loads multiple variables at once. This is useful when we want to add multiple variables in one go.
356    pub fn load_variables<S: AsRef<VariableRef>, T: Iterator<Item = S>>(
357        &mut self,
358        variables: T,
359    ) -> Result<Option<LoadSignalsCmd>> {
360        match self {
361            WaveContainer::Wellen(f) => f.load_variables(variables),
362            WaveContainer::Empty => bail!("Cannot load variables from empty container."),
363            WaveContainer::Cxxrtl(c) => {
364                c.get_mut().unwrap().load_variables(variables);
365                Ok(None)
366            }
367        }
368    }
369    /// Load all the parameters in the design so that the value can be displayed.
370    pub fn load_parameters(&mut self) -> Result<Option<LoadSignalsCmd>> {
371        match self {
372            WaveContainer::Wellen(f) => f.load_all_params(),
373            WaveContainer::Empty => bail!("Cannot load parameters from empty container."),
374            WaveContainer::Cxxrtl(_) => {
375                // Cxxrtl does not deal with parameters
376                Ok(None)
377            }
378        }
379    }
380
381    /// Callback for when wellen signals have been loaded. Might lead to a new load variable
382    /// command since new variables might have been requested in the meantime.
383    pub fn on_signals_loaded(&mut self, res: LoadSignalsResult) -> Result<Option<LoadSignalsCmd>> {
384        match self {
385            WaveContainer::Wellen(f) => f.on_signals_loaded(res),
386            WaveContainer::Empty => {
387                bail!("on_load_signals should only be called with the wellen backend.")
388            }
389            WaveContainer::Cxxrtl(_) => {
390                bail!("on_load_signals should only be called with the wellen backend.")
391            }
392        }
393    }
394
395    pub fn variable_meta<'a>(&'a self, variable: &'a VariableRef) -> Result<VariableMeta> {
396        match self {
397            WaveContainer::Wellen(f) => f.variable_to_meta(variable),
398            WaveContainer::Empty => bail!("Getting meta from empty wave container"),
399            WaveContainer::Cxxrtl(c) => c.lock().unwrap().variable_meta(variable),
400        }
401    }
402
403    /// Query the value of the variable at a certain time step.
404    /// Returns `None` if we do not have any values for the variable.
405    /// That generally happens if the corresponding variable is still being loaded.
406    pub fn query_variable(
407        &self,
408        variable: &VariableRef,
409        time: &BigUint,
410    ) -> Result<Option<QueryResult>> {
411        match self {
412            WaveContainer::Wellen(f) => f.query_variable(variable, time),
413            WaveContainer::Empty => bail!("Querying variable from empty wave container"),
414            WaveContainer::Cxxrtl(c) => Ok(c.lock().unwrap().query_variable(variable, time)),
415        }
416    }
417
418    pub fn signal_accessor(&self, signal_id: SignalId) -> Result<SignalAccessor> {
419        match (self, signal_id) {
420            (WaveContainer::Wellen(f), SignalId::Wellen(signal_ref)) => {
421                Ok(SignalAccessor::Wellen(f.signal_accessor(signal_ref)?))
422            }
423            _ => bail!("Invalid signal accessor combination"),
424        }
425    }
426    /// Get the `SignalId` for a variable (canonical signal identity for cache keys)
427    pub fn signal_id(&self, variable: &VariableRef) -> Result<SignalId> {
428        match self {
429            WaveContainer::Wellen(f) => Ok(SignalId::Wellen(f.signal_ref(variable)?)),
430            WaveContainer::Empty => bail!("No signal data"),
431            WaveContainer::Cxxrtl(_) => bail!("Not supported for Cxxrtl yet"),
432        }
433    }
434
435    /// Check if a signal is already loaded (data available)
436    #[must_use]
437    pub fn is_signal_loaded(&self, signal_id: &SignalId) -> bool {
438        match (self, signal_id) {
439            (WaveContainer::Wellen(f), SignalId::Wellen(signal_ref)) => {
440                f.is_signal_loaded(*signal_ref)
441            }
442            _ => false,
443        }
444    }
445
446    /// Looks up the variable _by name_ and returns a new reference with an updated `id` if the variable is found.
447    #[must_use]
448    pub fn update_variable_ref(&self, variable: &VariableRef) -> Option<VariableRef> {
449        match self {
450            WaveContainer::Wellen(f) => f.update_variable_ref(variable),
451            WaveContainer::Empty => None,
452            WaveContainer::Cxxrtl(_) => None,
453        }
454    }
455
456    /// Returns the full names of all scopes in the design.
457    #[must_use]
458    pub fn scope_names(&self) -> Vec<String> {
459        match self {
460            WaveContainer::Wellen(f) => f.scope_names(),
461            WaveContainer::Empty => vec![],
462            WaveContainer::Cxxrtl(c) => c
463                .lock()
464                .unwrap()
465                .modules()
466                .iter()
467                .map(|m| m.strs().last().cloned().unwrap_or("root".to_string()))
468                .collect(),
469        }
470    }
471
472    #[must_use]
473    pub fn metadata(&self) -> MetaData {
474        match self {
475            WaveContainer::Wellen(f) => f.metadata(),
476            WaveContainer::Empty => MetaData {
477                date: None,
478                version: None,
479                timescale: TimeScale {
480                    unit: TimeUnit::None,
481                    multiplier: None,
482                },
483            },
484            WaveContainer::Cxxrtl(_) => {
485                MetaData {
486                    date: None,
487                    version: None,
488                    timescale: TimeScale {
489                        // Cxxrtl always uses FemtoSeconds
490                        unit: TimeUnit::FemtoSeconds,
491                        multiplier: None,
492                    },
493                }
494            }
495        }
496    }
497
498    #[must_use]
499    pub fn root_scopes(&self) -> Vec<ScopeRef> {
500        match self {
501            WaveContainer::Wellen(f) => f.root_scopes(),
502            WaveContainer::Empty => vec![],
503            WaveContainer::Cxxrtl(c) => c.lock().unwrap().root_modules(),
504        }
505    }
506
507    pub fn child_scopes(&self, scope: &ScopeRef) -> Result<Vec<ScopeRef>> {
508        match self {
509            WaveContainer::Wellen(f) => f.child_scopes(scope),
510            WaveContainer::Empty => bail!("Getting child modules from empty wave container"),
511            WaveContainer::Cxxrtl(c) => Ok(c.lock().unwrap().child_scopes(scope)),
512        }
513    }
514
515    #[must_use]
516    pub fn max_timestamp(&self) -> Option<BigUint> {
517        match self {
518            WaveContainer::Wellen(f) => f.max_timestamp(),
519            WaveContainer::Empty => None,
520            WaveContainer::Cxxrtl(c) => c
521                .lock()
522                .unwrap()
523                .max_displayed_timestamp()
524                .map(|t| t.as_femtoseconds()),
525        }
526    }
527
528    #[must_use]
529    pub fn scope_exists(&self, scope: &ScopeRef) -> bool {
530        match self {
531            WaveContainer::Wellen(f) => f.scope_exists(scope),
532            WaveContainer::Empty => false,
533            WaveContainer::Cxxrtl(c) => c.lock().unwrap().module_exists(scope),
534        }
535    }
536
537    #[must_use]
538    /// True if scope is a compound variable
539    pub fn scope_is_variable(&self, scope: &ScopeRef) -> bool {
540        match self {
541            WaveContainer::Wellen(f) => f.scope_is_variable(scope),
542            WaveContainer::Empty => false,
543            WaveContainer::Cxxrtl(_) => false, // TODO: Check if scope is variable
544        }
545    }
546
547    /// Returns a human readable string with information about a scope.
548    /// The scope name itself should not be included, since it will be prepended automatically.
549    #[must_use]
550    pub fn get_scope_tooltip_data(&self, scope: &ScopeRef) -> String {
551        match self {
552            WaveContainer::Wellen(f) => f.get_scope_tooltip_data(scope),
553            WaveContainer::Empty => String::new(),
554            // FIXME: Tooltip
555            WaveContainer::Cxxrtl(_) => String::new(),
556        }
557    }
558
559    /// Returns the scope type for a given scope.
560    /// Returns `None` for backends that don't support scope types
561    #[must_use]
562    pub fn get_scope_type(&self, scope: &ScopeRef) -> Option<wellen::ScopeType> {
563        match self {
564            WaveContainer::Wellen(f) => f.get_scope_type(scope),
565            WaveContainer::Empty | WaveContainer::Cxxrtl(_) => None,
566        }
567    }
568
569    /// Returns the simulation status for this wave source if it exists. Wave sources which have no
570    /// simulation status should return None here, otherwise buttons for controlling simulation
571    /// will be shown
572    #[must_use]
573    pub fn simulation_status(&self) -> Option<SimulationStatus> {
574        match self {
575            WaveContainer::Wellen(_) => None,
576            WaveContainer::Empty => None,
577            WaveContainer::Cxxrtl(c) => c.lock().unwrap().simulation_status(),
578        }
579    }
580
581    /// If [`WaveContainer::simulation_status`] is `Some(SimulationStatus::Paused)`, attempt to unpause the
582    /// simulation otherwise does nothing
583    pub fn unpause_simulation(&self) {
584        match self {
585            WaveContainer::Wellen(_) => {}
586            WaveContainer::Empty => {}
587            WaveContainer::Cxxrtl(c) => c.lock().unwrap().unpause(),
588        }
589    }
590
591    /// See [`WaveContainer::unpause_simulation`]
592    pub fn pause_simulation(&self) {
593        match self {
594            WaveContainer::Wellen(_) => {}
595            WaveContainer::Empty => {}
596            WaveContainer::Cxxrtl(c) => c.lock().unwrap().pause(),
597        }
598    }
599
600    /// Called for `wellen` container, when the body of the waveform file has been parsed.
601    pub fn wellen_add_body(&mut self, body: BodyResult) -> Result<Option<LoadSignalsCmd>> {
602        match self {
603            WaveContainer::Wellen(inner) => inner.add_body(body),
604            _ => {
605                bail!("Should never call this function on a non wellen container!")
606            }
607        }
608    }
609
610    #[must_use]
611    pub fn body_loaded(&self) -> bool {
612        match self {
613            WaveContainer::Wellen(inner) => inner.body_loaded(),
614            WaveContainer::Empty => true,
615            WaveContainer::Cxxrtl(_) => true,
616        }
617    }
618
619    /// Returns true if this wave container supports analog rendering options in the GUI.
620    /// Currently only the wellen backend (VCD/FST/GHW) supports analog rendering.
621    #[must_use]
622    pub fn supports_analog(&self) -> bool {
623        matches!(self, WaveContainer::Wellen(_))
624    }
625}