bitsy_lang/
sim.rs

1use super::*;
2
3#[cfg(test)]
4mod tests;
5mod value;
6mod eval;
7pub mod ext;
8
9pub use value::Value;
10use ext::*;
11
12use std::collections::BTreeMap;
13use std::collections::BTreeSet;
14use std::sync::Arc;
15use std::time::Duration;
16use std::time::SystemTime;
17
18pub type NetId = usize;
19pub type CombId = usize;
20pub type ExtId = usize;
21pub type RegId = usize;
22
23#[derive(Debug)]
24pub struct RegInfo {
25    set_net_id: NetId,
26    val_net_id: NetId,
27    reset: Option<Arc<Expr>>,
28}
29
30#[derive(Debug)]
31pub struct Dependents {
32    pub combs: Vec<CombId>,
33    pub ext_ports: Vec<(ExtId, PortName)>,
34}
35
36#[derive(Debug)]
37pub struct SimCircuit {
38    pub nets: Vec<Net>, // indexed by NetId
39    pub combs: Vec<Comb>, // indexed by NetId
40    pub regs: Vec<RegInfo>, // indexed by RegId
41
42    pub dependents: Vec<Dependents>, // indexed by NetId
43
44    pub net_id_by_ext_port: BTreeMap<(ExtId, PortName), NetId>,
45
46    pub net_id_by_path: BTreeMap<Path, NetId>,
47    pub ext_id_by_path: BTreeMap<Path, ExtId>,
48}
49
50fn make_net_id_by_path(circuit: &Circuit, nets: &[Net]) -> BTreeMap<Path, NetId> {
51    /*
52        Supply a Path and get a NetId out.
53        Used extensively to build the Sim instance.
54        Otherwise, it's only used for the outward peek() and poke() calls
55        through the net_id() helper.
56
57        The definition just takes each path and finds which net contains it.
58    */
59    circuit
60        .paths()
61        .iter()
62        .map(|path| {
63            for (net_id, net) in nets.iter().enumerate() {
64                if net.contains(path.clone()) {
65                    return (path.clone(), net_id);
66                }
67            }
68            unreachable!()
69        })
70        .collect()
71}
72
73fn make_regs(circuit: &Circuit, net_id_by_path: &BTreeMap<Path, NetId>) -> Vec<RegInfo> {
74    /*
75        Straightforward resolution of the Circuit data to net data for all registers.
76    */
77    circuit
78        .regs()
79        .iter()
80        .cloned()
81        .map(|path| {
82            let set_net_id = net_id_by_path[&path.set()];
83            let val_net_id = net_id_by_path[&path.clone()];
84            let reset = circuit.reset_for_reg(path);
85
86            RegInfo {
87                set_net_id,
88                val_net_id,
89                reset,
90            }
91        })
92        .collect()
93}
94
95fn make_combs(circuit: &Circuit, net_id_by_path: &BTreeMap<Path, NetId>) -> Vec<Comb> {
96    /*
97        Created from the Wires of the Circuit.
98        Look at the WireType and decide if we need to capture the val or set terminal.
99        Converts everything to nets.
100        Don't add a comb when the two point at the same net.
101        (To avoid errors from the ad-hoc optimization).
102    */
103    circuit
104        .wires()
105        .iter()
106        .cloned()
107        .map(|(path, Wire(_loc, target, expr, wiretype))| {
108            let abs_target = path.clone().join(target);
109            let abs_expr = expr.rebase(path.clone());
110            let target_net_id = match wiretype {
111                WireType::Direct => net_id_by_path[&abs_target],
112                WireType::Latch => net_id_by_path[&abs_target.set()],
113                _ => todo!(), // Proc Wires not handled
114            };
115            (target_net_id, abs_expr.references_to_nets(&net_id_by_path), wiretype)
116        })
117        .filter(|(target_net_id, expr, _wiretype)| {
118            if let Expr::Net(_loc, _typ, net_id) = &**expr {
119                target_net_id != net_id
120            } else {
121                true
122            }
123        })
124        .map(|(target_net_id, expr, _wiretype)| {
125            Comb(target_net_id, expr)
126        })
127        .collect()
128}
129
130fn make_ext_id_by_path(
131    circuit: &Circuit,
132    net_id_by_path: &BTreeMap<Path, NetId>,
133    nets: &[Net],
134) -> BTreeMap<Path, ExtId> {
135    let mut ext_id_by_path: BTreeMap<Path, ExtId> = BTreeMap::new();
136    let mut ext_dependencies = vec![vec![]; nets.len()];
137
138    for (ext_id, path) in circuit.exts().iter().enumerate() {
139        ext_id_by_path.insert(path.clone(), ext_id);
140
141        let ext_component: &Component = &*circuit.component(path.clone()).unwrap();
142        for child in ext_component.children() {
143            if let Component::Incoming(_loc, name, _typ) = &*child {
144                let incoming_path = path.join(name.to_string().into());
145                let net_id = net_id_by_path[&incoming_path];
146                ext_dependencies[net_id].push((ext_id, name.to_string()));
147            }
148        }
149    }
150
151    ext_id_by_path
152}
153
154fn make_dependents(
155    circuit: &Circuit,
156    net_ids: &[NetId],
157    combs: &[Comb],
158    net_id_by_path: &BTreeMap<Path, NetId>,
159    ext_id_by_path: &BTreeMap<Path, ExtId>,
160) -> Vec<Dependents> {
161    net_ids.iter()
162    .map(|net_id| {
163        let combs: Vec<CombId> = combs.iter().enumerate().filter(|(_comb_id, comb)| comb.depends_on(*net_id)).map(|(comb_id, _comb)| comb_id).collect();
164        let mut ext_ports: Vec<(ExtId, PortName)> = vec![];
165        for path in circuit.exts() {
166            let ext_id = ext_id_by_path[&path];
167            let ext_component = circuit.component(path.clone()).unwrap();
168            match &*ext_component {
169                Component::Ext(_loc, _name, children) => {
170                    for child in children {
171                        match &**child {
172                            Component::Incoming(_loc, name, _typ) => {
173                                let port_path = path.join(name.clone().into());
174                                let port_net_id = net_id_by_path[&port_path];
175                                if  port_net_id == *net_id {
176                                    ext_ports.push((ext_id, name.to_string()))
177                                }
178                            },
179                            _ => (),
180                        }
181                    }
182                },
183                _ => unreachable!(),
184            }
185        }
186
187        let dependents = Dependents {
188            combs,
189            ext_ports,
190        };
191        dependents
192    })
193    .collect()
194}
195
196fn make_net_id_by_ext_port(
197    circuit: &Circuit,
198    net_id_by_path: &BTreeMap<Path, NetId>,
199    ext_id_by_path: &BTreeMap<Path, ExtId>,
200) -> BTreeMap<(ExtId, PortName), NetId> {
201    let mut net_id_by_ext_port = BTreeMap::new();
202
203    for path in circuit.exts() {
204        let ext_id = ext_id_by_path[&path];
205        let ext_component = circuit.component(path.clone()).unwrap();
206        match &*ext_component {
207            Component::Ext(_loc, _name, children) => {
208                for child in children {
209                    match &**child {
210                        Component::Outgoing(_loc, name, _typ) => {
211                            let port_path = path.join(name.clone().into());
212                            let net_id = net_id_by_path[&port_path];
213                            net_id_by_ext_port.insert((ext_id, name.clone()), net_id);
214                        },
215                        _ => (),
216                    }
217                }
218            },
219            _ => unreachable!(),
220        }
221    }
222    net_id_by_ext_port
223}
224
225impl SimCircuit {
226    pub fn new(circuit: &Circuit) -> SimCircuit {
227        let nets = nets(circuit);
228        let net_ids: Vec<NetId> = (0..nets.len()).into_iter().collect();
229        let net_id_by_path: BTreeMap<Path, NetId> = make_net_id_by_path(&circuit, &nets);
230        let regs: Vec<RegInfo> = make_regs(&circuit, &net_id_by_path);
231        let combs: Vec<Comb> = make_combs(&circuit, &net_id_by_path);
232        let ext_id_by_path = make_ext_id_by_path(&circuit, &net_id_by_path, &nets);
233
234        let dependents: Vec<Dependents> = make_dependents(
235            &circuit,
236            &net_ids,
237            &combs,
238            &net_id_by_path,
239            &ext_id_by_path,
240        );
241
242        let net_id_by_ext_port = make_net_id_by_ext_port(
243            &circuit,
244            &net_id_by_path,
245            &ext_id_by_path,
246        );
247
248        SimCircuit {
249            nets,
250            combs,
251            regs,
252
253            dependents,
254            net_id_by_ext_port,
255
256            net_id_by_path,
257            ext_id_by_path,
258        }
259    }
260
261    pub fn net_ids(&self) -> Vec<NetId> {
262        (0..self.nets.len()).into_iter().collect()
263    }
264}
265
266pub struct Sim {
267    sim_circuit: Arc<SimCircuit>,
268    net_values: Vec<Value>,
269    exts: Vec<Option<Box<dyn ExtInstance>>>, // indexed by ExtId
270    clock_ticks: u64,
271    start_time: SystemTime,
272    clock_freq_cap: Option<f64>,
273}
274
275impl Sim {
276    pub fn new(circuit: &Circuit) -> Sim {
277        Sim::new_with_exts(circuit, BTreeMap::new())
278    }
279
280    pub fn new_with_exts(circuit: &Circuit, linked_exts: BTreeMap<Path, Box<dyn ExtInstance>>) -> Sim {
281        let sim_circuit = Arc::new(SimCircuit::new(circuit));
282        let net_ids = sim_circuit.net_ids();
283        let net_values: Vec<Value> = net_ids.iter().map(|_net| Value::X).collect();
284
285        let mut ext_id_by_path: BTreeMap<Path, ExtId> = BTreeMap::new();
286        let mut exts: Vec<Option<Box<dyn ExtInstance>>> = vec![];
287
288        for (ext_id, path) in circuit.exts().iter().enumerate() {
289            ext_id_by_path.insert(path.clone(), ext_id);
290            exts.push(None);
291        }
292
293        let mut sim = Sim {
294            sim_circuit,
295            net_values,
296            exts,
297            start_time: SystemTime::now(),
298            clock_ticks: 0,
299            clock_freq_cap: None,
300        };
301        for (path, ext) in linked_exts {
302            sim = sim.ext(path, ext);
303        }
304        sim.broadcast_update_constants();
305        sim
306    }
307
308    pub fn net_values(&self) -> BTreeMap<NetId, Value> {
309        self.net_values.iter().cloned().enumerate().collect()
310    }
311
312    pub fn net(&self, net_id: NetId) -> &Net {
313        &self.sim_circuit.nets[net_id]
314    }
315
316    pub fn cap_clock_freq(mut self, freq: f64) -> Self {
317        self.clock_freq_cap = Some(freq);
318        self
319    }
320
321    fn ext<P: Into<Path>>(mut self, path: P, ext_inst: Box<dyn ExtInstance>) -> Self {
322        let path: Path = path.into();
323        if let Some(ext_id) = self.sim_circuit.ext_id_by_path.get(&path.clone()) {
324            let ext = &mut self.exts[*ext_id];
325            *ext = Some(ext_inst);
326            self
327        } else {
328            panic!("No such path for linkage: {path}")
329        }
330    }
331
332    pub(crate) fn poke_net(&mut self, net_id: NetId, value: Value) {
333        self.net_values[net_id] = value.clone();
334
335        // update dependent nets through all combs
336        let dependents = &self.sim_circuit.clone().dependents[net_id];
337        for comb_id in dependents.combs.iter() {
338            let comb = &self.sim_circuit.clone().combs[*comb_id];
339            let Comb(target_net_id, expr) = comb;
340            let value = expr.eval(&self);
341            self.poke_net(*target_net_id, value);
342        }
343
344        for (ext_id, port_name) in dependents.ext_ports.iter() {
345            if let Some(ext) = &mut self.exts[*ext_id].as_mut() {
346                for (updated_port_name, updated_value) in ext.update(port_name, value.clone()) {
347                    let net_id = self.sim_circuit.net_id_by_ext_port[&(*ext_id, updated_port_name)];
348                    self.poke_net(net_id, updated_value);
349                }
350            }
351        }
352    }
353
354    pub(crate) fn peek_net(&self, net_id: NetId) -> Value {
355        self.net_values[net_id].clone()
356    }
357
358    fn net_id(&self, path: Path) -> NetId {
359        if let Some(net_id) = self.sim_circuit.net_id_by_path.get(&path) {
360            *net_id
361        } else {
362            panic!("No net for {path}")
363        }
364    }
365
366    pub fn peek<P: Into<Path>>(&self, path: P) -> Value {
367        let net_id = self.net_id(path.into());
368        self.net_values[net_id].clone()
369    }
370
371    pub fn poke<P: Into<Path>>(&mut self, path: P, value: Value) {
372        let net_id = self.net_id(path.into());
373        self.poke_net(net_id, value);
374    }
375
376    pub fn type_of<P: Into<Path>>(&self, path: P) -> Type {
377        let net_id = self.net_id(path.into());
378        let Net(_driver, _terminals, typ) = &self.sim_circuit.nets[net_id];
379        typ.clone()
380    }
381
382    fn broadcast_update_constants(&mut self) {
383        for Comb(target_net_id, expr) in self.sim_circuit.clone().combs.iter() {
384            if expr.is_constant() {
385                let value = expr.eval(&self);
386                self.poke_net(*target_net_id, value);
387            }
388        }
389    }
390
391    pub fn clock(&mut self) {
392        self.clock_ticks += 1;
393
394        // frequency cap
395        if let Some(clock_freq_cap) = self.clock_freq_cap {
396            let mut clock_freq = self.clocks_per_second();
397            while clock_freq.is_finite() && clock_freq > clock_freq_cap {
398                clock_freq = self.clocks_per_second();
399            }
400        }
401
402//        if self.clock_ticks % 10000 == 0 {
403//            eprintln!("CPS: {:.2}", self.clocks_per_second());
404//        }
405
406        let mut updates = vec![];
407        for reginfo in &self.sim_circuit.clone().regs {
408            let value = self.peek_net(reginfo.set_net_id);
409            updates.push((reginfo.val_net_id, value));
410        }
411        for (val_net_id, value) in updates {
412            self.poke_net(val_net_id, value);
413        }
414
415        for ext in &mut self.exts {
416            if let Some(ext) = ext {
417                ext.clock();
418            } else {
419                // TODO
420                panic!("Unlinked ext")
421            }
422        }
423    }
424
425    pub fn reset(&mut self) {
426        for reginfo in &self.sim_circuit.clone().regs {
427            if let Some(reset) = &reginfo.reset {
428                self.poke_net(reginfo.val_net_id, reset.eval(&self));
429            }
430        }
431
432        for ext in &mut self.exts {
433            if let Some(ext) = ext {
434                ext.reset();
435            } else {
436                // TODO
437                panic!("Unlinked ext")
438            }
439        }
440    }
441
442    pub fn clocks_per_second(&self) -> f64 {
443        let end_time = SystemTime::now();
444        let duration: Duration = end_time.duration_since(self.start_time).unwrap();
445        1_000_000.0 * self.clock_ticks as f64 / duration.as_micros() as f64
446    }
447}
448
449#[derive(Debug, Clone)]
450pub struct Comb(NetId, Arc<Expr>);
451
452impl Comb {
453    pub fn depends_on(&self, net_id: NetId) -> bool {
454        let Comb(_net_id, expr) = self;
455        expr.depends_on_net(net_id)
456    }
457}
458
459impl std::fmt::Debug for Sim {
460    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
461        for (net_id, value) in self.net_values.iter().enumerate() {
462            let net = &self.sim_circuit.nets[net_id];
463            write!(f, "    {:>5}   ", format!("{value:?}"))?;
464            writeln!(f, "{}", net.terminals().iter().map(|t| t.to_string()).collect::<Vec<String>>().join(" "))?;
465        }
466
467        Ok(())
468    }
469}
470
471pub fn nets(circuit: &Circuit) -> Vec<Net> {
472    let mut immediate_driver_for: BTreeMap<Path, Path> = BTreeMap::new();
473
474    for (path, Wire(_loc, target, expr, wire_type)) in circuit.wires() {
475        let abs_expr = expr.rebase(path.clone());
476        let target_terminal: Path = match wire_type {
477            WireType::Direct => path.join(target).clone(),
478            WireType::Latch => path.join(target).set(),
479            WireType::Proc => path.join(target).set(),
480        };
481        if let Expr::Reference(_loc, _typ, driver) = &*abs_expr {
482            immediate_driver_for.insert(target_terminal.clone(), driver.clone());
483         }
484    }
485
486    let mut drivers: BTreeSet<Path> = BTreeSet::new();
487    for terminal in circuit.paths() {
488        drivers.insert(driver_for(terminal, &immediate_driver_for));
489    }
490
491    let mut nets: BTreeMap<Path, Net> = BTreeMap::new();
492    for driver in &drivers {
493        let component = circuit.component(driver.clone()).unwrap();
494        let typ = component.type_of().unwrap();
495        nets.insert(driver.clone(), Net::from(driver.clone(), typ));
496    }
497
498    for terminal in circuit.paths() {
499        let driver = driver_for(terminal.clone(), &immediate_driver_for);
500        let net = nets.get_mut(&driver).unwrap();
501        net.add(terminal);
502    }
503
504    let nets: Vec<Net> = nets.values().into_iter().cloned().collect();
505    nets
506}
507
508fn driver_for(terminal: Path, immediate_driver_for: &BTreeMap<Path, Path>) -> Path {
509    let mut driver: &Path = &terminal;
510    while let Some(immediate_driver) = &immediate_driver_for.get(driver) {
511        driver = immediate_driver;
512    }
513    driver.clone()
514}
515
516impl Net {
517    fn from(terminal: Path, typ: Type) -> Net {
518        Net(terminal, vec![], typ)
519    }
520
521    pub fn add(&mut self, terminal: Path) {
522        if self.0 != terminal {
523            self.1.push(terminal);
524            self.1.sort();
525            self.1.dedup();
526        }
527    }
528
529    pub fn driver(&self) -> Path {
530        self.0.clone()
531    }
532
533    pub fn drivees(&self) -> &[Path] {
534        &self.1
535    }
536
537    pub fn terminals(&self) -> Vec<Path> {
538        let mut results = vec![self.0.clone()];
539        for terminal in &self.1 {
540            results.push(terminal.clone());
541        }
542        results
543    }
544
545    pub fn contains(&self, terminal: Path) -> bool {
546        if terminal == self.0 {
547            true
548        } else {
549            self.1.contains(&terminal)
550        }
551    }
552}
553
554#[derive(Clone)]
555pub struct Net(Path, Vec<Path>, Type);
556
557impl std::fmt::Debug for Net {
558    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
559        write!(f, "Net({} {})", self.0, self.1.iter().map(|path| path.to_string()).collect::<Vec<_>>().join(" "))
560    }
561}
562
563impl Expr {
564    pub fn rebase(&self, current_path: Path) -> Arc<Expr> {
565        self.rebase_rec(current_path, &BTreeSet::new())
566    }
567
568    fn rebase_rec(&self, current_path: Path, shadowed: &BTreeSet<Path>) -> Arc<Expr> {
569        Arc::new(match self {
570            Expr::Reference(loc, typ, path) => {
571                if !shadowed.contains(path) {
572                    Expr::Reference(loc.clone(), typ.clone(), current_path.join(path.clone()))
573                } else {
574                    self.clone()
575                }
576            },
577            Expr::Net(_loc, _typ, _net_id) => panic!("rebase() only works on reference expressions."),
578            Expr::Word(_loc, _typ, _width, _value) => self.clone(),
579            Expr::Enum(_loc, _typ, _typedef, _name) => self.clone(),
580            Expr::Ctor(loc, typ, name, es) => Expr::Ctor(loc.clone(), typ.clone(), name.clone(), es.iter().map(|e| e.rebase_rec(current_path.clone(), shadowed)).collect()),
581            Expr::Struct(loc, typ, fields) => {
582                Expr::Struct(
583                    loc.clone(),
584                    typ.clone(),
585                    fields
586                        .iter()
587                        .map(|(name, e)| (name.clone(), e.rebase_rec(current_path.clone(), shadowed)))
588                        .collect(),
589                    )
590            },
591            Expr::Let(loc, typ, name, e, b) => {
592                let new_e = e.rebase_rec(current_path.clone(), shadowed);
593                let mut new_shadowed = shadowed.clone();
594                new_shadowed.insert(name.clone().into());
595                let new_b = b.rebase_rec(current_path, &new_shadowed);
596                Expr::Let(loc.clone(), typ.clone(), name.clone(), new_e, new_b)
597            },
598            Expr::Match(loc, typ, e, arms) => {
599                let new_arms = arms.iter().map(|MatchArm(pat, e)| {
600                    let mut new_shadowed = shadowed.clone();
601                    new_shadowed.extend(pat.bound_vars());
602                    MatchArm(pat.clone(), e.rebase_rec(current_path.clone(), &new_shadowed))
603
604                }).collect();
605                Expr::Match(
606                    loc.clone(),
607                    typ.clone(),
608                    e.rebase_rec(current_path.clone(), shadowed),
609                    new_arms,
610                )
611            },
612            Expr::UnOp(loc, typ, op, e) => Expr::UnOp(loc.clone(), typ.clone(), *op, e.rebase_rec(current_path, shadowed)),
613            Expr::BinOp(loc, typ, op, e1, e2) => {
614                Expr::BinOp(
615                    loc.clone(),
616                    typ.clone(),
617                    *op,
618                    e1.rebase_rec(current_path.clone(), shadowed),
619                    e2.rebase_rec(current_path, shadowed),
620                )
621            },
622            Expr::If(loc, typ, cond, e1, e2) => {
623                Expr::If(
624                    loc.clone(),
625                    typ.clone(),
626                    cond.rebase_rec(current_path.clone(), shadowed),
627                    e1.rebase_rec(current_path.clone(), shadowed),
628                    e2.rebase_rec(current_path, shadowed),
629                )
630            },
631            Expr::Mux(loc, typ, cond, e1, e2) => {
632                Expr::Mux(
633                    loc.clone(),
634                    typ.clone(),
635                    cond.rebase_rec(current_path.clone(), shadowed),
636                    e1.rebase_rec(current_path.clone(), shadowed),
637                    e2.rebase_rec(current_path, shadowed),
638                )
639            },
640            Expr::Cat(loc, typ, es) => {
641                Expr::Cat(
642                    loc.clone(),
643                    typ.clone(),
644                    es.iter().map(|e| e.rebase_rec(current_path.clone(), shadowed)).collect(),
645                )
646            },
647            Expr::Sext(loc, typ, e) => Expr::Sext(loc.clone(), typ.clone(), e.rebase_rec(current_path, shadowed)),
648            Expr::ToWord(loc, typ, e) => Expr::ToWord(loc.clone(), typ.clone(), e.rebase_rec(current_path, shadowed)),
649            Expr::Vec(loc, typ, es) => Expr::Vec(loc.clone(), typ.clone(), es.iter().map(|e| e.rebase_rec(current_path.clone(), shadowed)).collect()),
650            Expr::IdxField(loc, typ, e, field) => Expr::IdxField(loc.clone(), typ.clone(), e.rebase_rec(current_path, shadowed), field.clone()),
651            Expr::Idx(loc, typ, e, i) => Expr::Idx(loc.clone(), typ.clone(), e.rebase_rec(current_path, shadowed), *i),
652            Expr::IdxRange(loc, typ, e, j, i) => Expr::IdxRange(loc.clone(), typ.clone(), e.rebase_rec(current_path, shadowed), *j, *i),
653            Expr::Call(loc, typ, fndef, es) => {
654                Expr::Call(
655                    loc.clone(),
656                    typ.clone(),
657                    fndef.clone(),
658                    es.iter().map(|e| e.rebase_rec(current_path.clone(), shadowed)).collect(),
659                )
660            },
661            Expr::Hole(loc, typ, name) => Expr::Hole(loc.clone(), typ.clone(), name.clone()),
662        })
663    }
664
665    fn references_to_nets(&self, net_id_by_path: &BTreeMap<Path, NetId>) -> Arc<Expr> {
666        self.references_to_nets_rec(net_id_by_path, &BTreeSet::new())
667    }
668
669    fn references_to_nets_rec(&self, net_id_by_path: &BTreeMap<Path, NetId>, shadowed: &BTreeSet<Path>) -> Arc<Expr> {
670        Arc::new(match self {
671            Expr::Reference(loc, typ, path) => {
672                if !shadowed.contains(path) {
673                    Expr::Net(loc.clone(), typ.clone(), net_id_by_path[path])
674                } else {
675                    self.clone()
676                }
677            },
678            Expr::Net(_loc, _typ, _net_id) => panic!("references_to_nets() only works on reference expressions."),
679            Expr::Word(_loc, _typ, _width, _value) => self.clone(),
680            Expr::Enum(_loc, _typ, _typedef, _name) => self.clone(),
681            Expr::Ctor(loc, typ, name, es) => {
682                Expr::Ctor(
683                    loc.clone(),
684                    typ.clone(),
685                    name.clone(),
686                    es.iter().map(|e| e.references_to_nets_rec(net_id_by_path, shadowed)).collect(),
687                )
688            },
689            Expr::Struct(loc, typ, fields) => {
690                Expr::Struct(
691                    loc.clone(),
692                    typ.clone(),
693                    fields
694                        .iter()
695                        .map(|(name, e)| (name.clone(), e.references_to_nets_rec(net_id_by_path, shadowed)))
696                        .collect(),
697                    )
698            },
699            Expr::Let(loc, typ, name, e, b) => {
700                let new_e = e.references_to_nets_rec(net_id_by_path, shadowed);
701                let mut new_shadowed = shadowed.clone();
702                new_shadowed.insert(name.clone().into());
703                let new_b = b.references_to_nets_rec(net_id_by_path, &new_shadowed);
704                Expr::Let(loc.clone(), typ.clone(), name.clone(), new_e, new_b)
705            },
706            Expr::UnOp(loc, typ, op, e) => Expr::UnOp(loc.clone(), typ.clone(), *op, e.references_to_nets_rec(net_id_by_path, shadowed)),
707            Expr::BinOp(loc, typ, op, e1, e2) => {
708                Expr::BinOp(
709                    loc.clone(),
710                    typ.clone(),
711                    *op,
712                    e1.references_to_nets_rec(net_id_by_path, shadowed),
713                    e2.references_to_nets_rec(net_id_by_path, shadowed),
714                )
715            },
716            Expr::If(loc, typ, cond, e1, e2) => {
717                Expr::If(
718                    loc.clone(),
719                    typ.clone(),
720                    cond.references_to_nets_rec(net_id_by_path, shadowed),
721                    e1.references_to_nets_rec(net_id_by_path, shadowed),
722                    e2.references_to_nets_rec(net_id_by_path, shadowed),
723                )
724            },
725            Expr::Match(loc, typ, e, arms) => {
726                let new_arms = arms.iter().map(|MatchArm(pat, e)| {
727                    let mut new_shadowed = shadowed.clone();
728                    new_shadowed.extend(pat.bound_vars());
729                    MatchArm(pat.clone(), e.references_to_nets_rec(net_id_by_path, &new_shadowed))
730
731                }).collect();
732                Expr::Match(
733                    loc.clone(),
734                    typ.clone(),
735                    e.references_to_nets_rec(net_id_by_path, shadowed),
736                    new_arms,
737                )
738            },
739            Expr::Mux(loc, typ, cond, e1, e2) => {
740                Expr::Mux(
741                    loc.clone(),
742                    typ.clone(),
743                    cond.references_to_nets_rec(net_id_by_path, shadowed),
744                    e1.references_to_nets_rec(net_id_by_path, shadowed),
745                    e2.references_to_nets_rec(net_id_by_path, shadowed),
746                )
747            },
748            Expr::Cat(loc, typ, es) => {
749                Expr::Cat(
750                    loc.clone(),
751                    typ.clone(),
752                    es.iter().map(|e| e.references_to_nets_rec(net_id_by_path, shadowed)).collect(),
753                )
754            },
755            Expr::Sext(loc, typ, e) => Expr::Sext(loc.clone(), typ.clone(), e.references_to_nets_rec(net_id_by_path, shadowed)),
756            Expr::ToWord(loc, typ, e) => Expr::ToWord(loc.clone(), typ.clone(), e.references_to_nets_rec(net_id_by_path, shadowed)),
757            Expr::Vec(loc, typ, es) => Expr::Vec(loc.clone(), typ.clone(), es.iter().map(|e| e.references_to_nets_rec(net_id_by_path, shadowed)).collect()),
758            Expr::IdxField(loc, typ, e, field) => Expr::IdxField(loc.clone(), typ.clone(), e.references_to_nets_rec(net_id_by_path, shadowed), field.clone()),
759            Expr::Idx(loc, typ, e, i) => Expr::Idx(loc.clone(), typ.clone(), e.references_to_nets_rec(net_id_by_path, shadowed), *i),
760            Expr::IdxRange(loc, typ, e, j, i) => Expr::IdxRange(loc.clone(), typ.clone(), e.references_to_nets_rec(net_id_by_path, shadowed), *j, *i),
761            Expr::Call(loc, typ, fndef, es) => {
762                Expr::Call(
763                    loc.clone(),
764                    typ.clone(),
765                    fndef.clone(),
766                    es.iter().map(|e| e.references_to_nets_rec(net_id_by_path, shadowed)).collect(),
767                )
768            },
769            Expr::Hole(loc, typ, name) => Expr::Hole(loc.clone(), typ.clone(), name.clone()),
770        })
771    }
772}