1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use crate::docs::vcd2svg::display_metrics::DisplayMetrics;
use crate::docs::vcd2svg::timed_value::TimedValue;
use crate::docs::vcd2svg::utils::{value_to_bigint, value_to_bool};
use num_bigint::BigInt;
use std::clone::Clone;
use std::collections::HashMap;
use std::fs::File;
use std::iter::Iterator;
use std::string::ToString;
use svg::Document;
use vcd::IdCode;

type StringTrace = Vec<TimedValue<String>>;
type VectorTrace = Vec<TimedValue<BigInt>>;
type BinaryTrace = Vec<TimedValue<bool>>;

pub struct TraceCollection {
    pub signal_names: Vec<(IdCode, String)>,
    pub string_valued: HashMap<IdCode, StringTrace>,
    pub vector_valued: HashMap<IdCode, VectorTrace>,
    pub scalar_valued: HashMap<IdCode, BinaryTrace>,
}

impl TraceCollection {
    pub fn parse(signals: &[&str], mut file: File) -> anyhow::Result<Self> {
        let mut parser = vcd::Parser::new(&mut file);
        let header = parser.parse_header()?;
        let mut string_valued = HashMap::new();
        let mut vector_valued = HashMap::new();
        let mut scalar_valued = HashMap::new();
        let mut signal_names = Vec::new();
        for signal in signals {
            let path = signal.split(".").collect::<Vec<_>>();
            let sig = header
                .find_var(&path)
                .ok_or_else(|| anyhow::Error::msg(format!("cannot resolve signal {}", signal)))?;
            if sig.size == 0 {
                string_valued.insert(sig.code, StringTrace::new());
            } else if sig.size == 1 {
                scalar_valued.insert(sig.code, BinaryTrace::new());
            } else {
                vector_valued.insert(sig.code, VectorTrace::new());
            }
            signal_names.push((sig.code, signal.to_string()));
        }
        let mut timestamp = 0_u64;
        for command_result in parser {
            let command = command_result?;
            match command {
                vcd::Command::Timestamp(x) => {
                    timestamp = x;
                }
                vcd::Command::ChangeScalar(i, v) => {
                    if let Some(s) = scalar_valued.get_mut(&i) {
                        s.push(TimedValue {
                            time: timestamp,
                            value: value_to_bool(&v)?,
                        })
                    }
                }
                vcd::Command::ChangeVector(i, v) => {
                    if let Some(s) = vector_valued.get_mut(&i) {
                        s.push(TimedValue {
                            time: timestamp,
                            value: value_to_bigint(&v)?,
                        })
                    }
                }
                vcd::Command::ChangeString(i, v) => {
                    if let Some(s) = string_valued.get_mut(&i) {
                        s.push(TimedValue {
                            time: timestamp,
                            value: v.clone(),
                        })
                    }
                }
                _ => {}
            }
        }
        Ok(Self {
            signal_names,
            string_valued,
            vector_valued,
            scalar_valued,
        })
    }

    pub fn as_svg(&self, metrics: &DisplayMetrics) -> anyhow::Result<Document> {
        let document = Document::new()
            .set(
                "viewBox",
                (0, 0, metrics.canvas_width, metrics.canvas_height),
            )
            .add(metrics.background_rect());

        // Paint the timescale rectangle
        let mut document = document
            .add(metrics.signal_rect())
            .add(metrics.timescale_header_rect())
            .add(metrics.timescale_midline());

        document = metrics.timescale(document);

        for (index, details) in self.signal_names.iter().enumerate() {
            document = document
                .add(metrics.signal_label(index, &details.1))
                .add(metrics.signal_line(index));
            document = metrics.horiz_grid_line(index, document);
            if let Some(s) = self.scalar_valued.get(&details.0) {
                document = document.add(metrics.bit_signal_plot(index, s));
            } else if let Some(s) = self.vector_valued.get(&details.0) {
                document = metrics.vector_signal_plot(index, s, document);
            } else if let Some(s) = self.string_valued.get(&details.0) {
                document = metrics.vector_signal_plot(index, s, document);
            } else {
                anyhow::bail!("Unable to find signal {} in the trace...", details.1)
            }
        }
        Ok(document)
    }
}