rust_hdl_core/
vcd_probe.rs

1use crate::atom::Atom;
2use crate::block::Block;
3use crate::probe::Probe;
4use crate::synth::VCDValue;
5use crate::type_descriptor::TypeDescriptor;
6use crate::type_descriptor::TypeKind;
7use std::collections::HashMap;
8use std::io::Write;
9
10#[derive(Clone, Debug)]
11enum VCDIDCode {
12    Singleton(vcd::IdCode),
13    Composite(Vec<Box<VCDIDCode>>),
14}
15
16pub struct VCDProbe<W: Write> {
17    vcd: vcd::Writer<W>,
18    id_map: HashMap<usize, VCDIDCode>,
19    val_map: HashMap<vcd::IdCode, VCDValue>,
20}
21
22impl<W: Write> VCDProbe<W> {
23    pub fn new(w: W) -> VCDProbe<W> {
24        Self {
25            vcd: vcd::Writer::new(w),
26            id_map: HashMap::default(),
27            val_map: HashMap::default(),
28        }
29    }
30
31    pub fn timestamp(&mut self, ts: u64) -> std::io::Result<()> {
32        self.vcd.timestamp(ts)
33    }
34}
35
36struct VCDHeader<W: Write>(VCDProbe<W>);
37
38fn register_signal<W: Write>(
39    name: &str,
40    descriptor: &TypeDescriptor,
41    vcd: &mut vcd::Writer<W>,
42) -> VCDIDCode {
43    match &descriptor.kind {
44        TypeKind::Bits(width) | TypeKind::Signed(width) => {
45            VCDIDCode::Singleton(vcd.add_wire(*width as u32, name).unwrap())
46        }
47        TypeKind::Enum(_) => VCDIDCode::Singleton(vcd.add_wire(0, name).unwrap()),
48        TypeKind::Composite(k) => {
49            let mut ret = vec![];
50            for field in k {
51                let sub_name = format!("{}${}", name, field.fieldname);
52                let code = register_signal(&sub_name, &field.kind, vcd);
53                ret.push(Box::new(code));
54            }
55            VCDIDCode::Composite(ret)
56        }
57    }
58}
59
60impl<W: Write> Probe for VCDHeader<W> {
61    fn visit_start_scope(&mut self, name: &str, _node: &dyn Block) {
62        self.0.vcd.add_module(name).unwrap();
63    }
64
65    fn visit_start_namespace(&mut self, name: &str, _node: &dyn Block) {
66        self.0.vcd.add_module(name).unwrap();
67    }
68
69    fn visit_atom(&mut self, name: &str, signal: &dyn Atom) {
70        self.0.id_map.insert(
71            signal.id(),
72            register_signal(name, &signal.descriptor(), &mut self.0.vcd),
73        );
74    }
75
76    fn visit_end_namespace(&mut self, _name: &str, _node: &dyn Block) {
77        self.0.vcd.upscope().unwrap();
78    }
79
80    fn visit_end_scope(&mut self, _name: &str, _node: &dyn Block) {
81        self.0.vcd.upscope().unwrap();
82    }
83}
84
85pub fn write_vcd_header<W: Write>(writer: W, uut: &dyn Block) -> VCDProbe<W> {
86    let mut visitor = VCDHeader(VCDProbe::new(writer));
87    visitor.0.vcd.timescale(1, vcd::TimescaleUnit::PS).unwrap();
88    uut.accept("uut", &mut visitor);
89    visitor.0.vcd.enddefinitions().unwrap();
90    visitor.0
91}
92
93struct VCDChange<W: Write>(VCDProbe<W>);
94
95impl<W: Write> Probe for VCDChange<W> {
96    fn visit_atom(&mut self, _name: &str, signal: &dyn Atom) {
97        if let Some(idc) = self.0.id_map.get(&signal.id()) {
98            do_vcd_change(
99                &mut self.0.val_map,
100                &mut self.0.vcd,
101                idc,
102                &signal.vcd(),
103                false,
104            );
105        }
106    }
107}
108
109pub fn write_vcd_change<W: Write>(vcd: VCDProbe<W>, uut: &dyn Block) -> VCDProbe<W> {
110    let mut visitor = VCDChange(vcd);
111    uut.accept("uut", &mut visitor);
112    visitor.0
113}
114
115struct VCDDump<W: Write>(VCDProbe<W>);
116
117impl<W: Write> Probe for VCDDump<W> {
118    fn visit_atom(&mut self, _name: &str, signal: &dyn Atom) {
119        if let Some(idc) = &self.0.id_map.get(&signal.id()) {
120            do_vcd_change(
121                &mut self.0.val_map,
122                &mut self.0.vcd,
123                idc,
124                &signal.vcd(),
125                true,
126            );
127        }
128    }
129}
130
131fn do_vcd_change<W: Write>(
132    val_map: &mut HashMap<vcd::IdCode, VCDValue>,
133    vcd: &mut vcd::Writer<W>,
134    idc: &VCDIDCode,
135    val: &VCDValue,
136    dump: bool,
137) {
138    match idc {
139        VCDIDCode::Singleton(idc) => {
140            if !dump {
141                if let Some(old_val) = val_map.get(idc) {
142                    if val.eq(old_val) {
143                        return;
144                    }
145                }
146            }
147            let _ = val_map.insert(*idc, val.clone());
148            match val {
149                VCDValue::Single(s) => {
150                    vcd.change_scalar(*idc, s.clone()).unwrap();
151                }
152                VCDValue::Vector(v) => {
153                    if v.len() == 1 {
154                        vcd.change_scalar(*idc, v[0]).unwrap();
155                    } else {
156                        vcd.change_vector(*idc, &v).unwrap();
157                    }
158                }
159                VCDValue::String(t) => {
160                    vcd.change_string(*idc, &t).unwrap();
161                }
162                VCDValue::Composite(_) => {
163                    panic!("Composite data received for singleton type");
164                }
165            }
166        }
167        VCDIDCode::Composite(idcs) => match val {
168            VCDValue::Composite(vals) => {
169                assert_eq!(
170                    idcs.len(),
171                    vals.len(),
172                    "Mismatch in values versus type information"
173                );
174                for n in 0..idcs.len() {
175                    do_vcd_change(val_map, vcd, &idcs[n], &vals[n], dump);
176                }
177            }
178            _ => {
179                panic!("Scalar data received for composite type");
180            }
181        },
182    }
183}
184
185pub fn write_vcd_dump<W: Write>(vcd: VCDProbe<W>, uut: &dyn Block) -> VCDProbe<W> {
186    let mut visitor = VCDDump(vcd);
187    visitor
188        .0
189        .vcd
190        .begin(vcd::SimulationCommand::Dumpvars)
191        .unwrap();
192    uut.accept("uut", &mut visitor);
193    visitor.0.vcd.end().unwrap();
194    visitor.0
195}