use super::*;
#[cfg(test)]
mod tests;
mod value;
mod eval;
pub mod ext;
pub use value::Value;
use ext::*;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::sync::Arc;
use std::time::Duration;
use std::time::SystemTime;
pub type NetId = usize;
pub type CombId = usize;
pub type ExtInstId = usize;
pub type ExtId = usize;
pub type RegId = usize;
#[derive(Debug)]
pub struct RegInfo {
set_net_id: NetId,
val_net_id: NetId,
reset: Option<Arc<Expr>>,
}
#[derive(Debug)]
pub struct Dependents {
pub combs: Vec<CombId>,
pub ext_inst_ports: Vec<(ExtInstId, PortName)>,
}
#[derive(Debug)]
pub struct SimCircuit {
pub nets: Vec<Net>, pub combs: Vec<Comb>, pub regs: Vec<RegInfo>, pub dependents: Vec<Dependents>, pub net_id_by_ext_port: BTreeMap<(ExtInstId, PortName), NetId>,
pub net_id_by_path: BTreeMap<Path, NetId>,
pub ext_inst_id_by_path: BTreeMap<Path, ExtInstId>,
pub path_by_ext_inst_id: BTreeMap<ExtInstId, Path>,
}
fn make_net_id_by_path(circuit: &Circuit, nets: &[Net]) -> BTreeMap<Path, NetId> {
circuit
.paths()
.iter()
.map(|path| {
for (net_id, net) in nets.iter().enumerate() {
if net.contains(path.clone()) {
return (path.clone(), net_id);
}
}
unreachable!()
})
.collect()
}
fn make_regs(circuit: &Circuit, net_id_by_path: &BTreeMap<Path, NetId>) -> Vec<RegInfo> {
circuit
.regs()
.iter()
.cloned()
.map(|path| {
let set_net_id = net_id_by_path[&path.set()];
let val_net_id = net_id_by_path[&path.clone()];
let reset = circuit.reset_for_reg(path);
RegInfo {
set_net_id,
val_net_id,
reset,
}
})
.collect()
}
fn make_combs(circuit: &Circuit, net_id_by_path: &BTreeMap<Path, NetId>) -> Vec<Comb> {
circuit
.wires()
.iter()
.cloned()
.map(|(path, Wire(_loc, target, expr, wiretype))| {
let abs_target = path.clone().join(target);
let abs_expr = expr.rebase(path.clone());
let target_net_id = match wiretype {
WireType::Direct => net_id_by_path[&abs_target],
WireType::Latch => net_id_by_path[&abs_target.set()],
_ => todo!(), };
(target_net_id, abs_expr.references_to_nets(&net_id_by_path), wiretype)
})
.filter(|(target_net_id, expr, _wiretype)| {
if let Expr::Net(_loc, _typ, net_id) = &**expr {
target_net_id != net_id
} else {
true
}
})
.map(|(target_net_id, expr, _wiretype)| {
Comb(target_net_id, expr)
})
.collect()
}
fn make_ext_inst_id_by_path(
circuit: &Circuit,
net_id_by_path: &BTreeMap<Path, NetId>,
nets: &[Net],
) -> (BTreeMap<Path, ExtInstId>, BTreeMap<ExtInstId, Path>) {
let mut ext_inst_id_by_path: BTreeMap<Path, ExtInstId> = BTreeMap::new();
let mut path_by_ext_inst_id: BTreeMap<ExtInstId, Path> = BTreeMap::new();
let mut ext_dependencies = vec![vec![]; nets.len()];
for (ext_id, (path, ext_component)) in circuit.exts().iter().enumerate() {
ext_inst_id_by_path.insert(path.clone(), ext_id);
path_by_ext_inst_id.insert(ext_id, path.clone());
for child in ext_component.children() {
if let Component::Incoming(_loc, name, _typ) = &*child {
let incoming_path = path.join(name.to_string().into());
let net_id = net_id_by_path[&incoming_path];
ext_dependencies[net_id].push((ext_id, name.to_string()));
}
}
}
(ext_inst_id_by_path, path_by_ext_inst_id)
}
fn make_dependents(
circuit: &Circuit,
net_ids: &[NetId],
combs: &[Comb],
net_id_by_path: &BTreeMap<Path, NetId>,
ext_id_by_path: &BTreeMap<Path, ExtInstId>,
) -> Vec<Dependents> {
net_ids.iter()
.map(|net_id| {
let combs: Vec<CombId> = combs.iter().enumerate().filter(|(_comb_id, comb)| comb.depends_on(*net_id)).map(|(comb_id, _comb)| comb_id).collect();
let mut ext_ports: Vec<(ExtInstId, PortName)> = vec![];
for (path, ext_component) in circuit.exts() {
let ext_id = ext_id_by_path[&path];
match &*ext_component {
Component::Ext(_loc, _name, children) => {
for child in children {
match &**child {
Component::Incoming(_loc, name, _typ) => {
let port_path = path.join(name.clone().into());
let port_net_id = net_id_by_path[&port_path];
if port_net_id == *net_id {
ext_ports.push((ext_id, name.to_string()))
}
},
_ => (),
}
}
},
_ => unreachable!(),
}
}
let dependents = Dependents {
combs,
ext_inst_ports: ext_ports,
};
dependents
})
.collect()
}
fn make_net_id_by_ext_port(
circuit: &Circuit,
net_id_by_path: &BTreeMap<Path, NetId>,
ext_inst_id_by_path: &BTreeMap<Path, ExtInstId>,
) -> BTreeMap<(ExtInstId, PortName), NetId> {
let mut net_id_by_ext_port = BTreeMap::new();
for (path, ext_component) in circuit.exts() {
let ext_inst_id = ext_inst_id_by_path[&path];
match &*ext_component {
Component::Ext(_loc, _name, children) => {
for child in children {
match &**child {
Component::Outgoing(_loc, name, _typ) => {
let port_path = path.join(name.clone().into());
let net_id = net_id_by_path[&port_path];
net_id_by_ext_port.insert((ext_inst_id, name.clone()), net_id);
},
_ => (),
}
}
},
_ => unreachable!(),
}
}
net_id_by_ext_port
}
impl SimCircuit {
pub fn new(circuit: &Circuit) -> SimCircuit {
let nets = nets(circuit);
let net_ids: Vec<NetId> = (0..nets.len()).into_iter().collect();
let net_id_by_path: BTreeMap<Path, NetId> = make_net_id_by_path(&circuit, &nets);
let regs: Vec<RegInfo> = make_regs(&circuit, &net_id_by_path);
let combs: Vec<Comb> = make_combs(&circuit, &net_id_by_path);
let (ext_inst_id_by_path, path_by_ext_inst_id) = make_ext_inst_id_by_path(&circuit, &net_id_by_path, &nets);
let dependents: Vec<Dependents> = make_dependents(
&circuit,
&net_ids,
&combs,
&net_id_by_path,
&ext_inst_id_by_path,
);
let net_id_by_ext_port = make_net_id_by_ext_port(
&circuit,
&net_id_by_path,
&ext_inst_id_by_path,
);
SimCircuit {
nets,
combs,
regs,
dependents,
net_id_by_ext_port,
net_id_by_path,
ext_inst_id_by_path,
path_by_ext_inst_id,
}
}
pub fn net_ids(&self) -> Vec<NetId> {
(0..self.nets.len()).into_iter().collect()
}
}
pub struct Sim {
sim_circuit: Arc<SimCircuit>,
net_values: Vec<Value>,
exts: Vec<Box<dyn Ext>>,
ext_id_by_ext_inst_id: BTreeMap<ExtInstId, ExtId>,
clock_ticks: u64,
start_time: SystemTime,
clock_freq_cap: Option<f64>,
}
impl Sim {
pub fn new(circuit: &Circuit, exts: Vec<Box<dyn Ext>>) -> Sim {
let sim_circuit = Arc::new(SimCircuit::new(circuit));
let net_ids = sim_circuit.net_ids();
let net_values: Vec<Value> = net_ids.iter().map(|_net| Value::X).collect();
let ext_id_by_ext_inst_id: BTreeMap<ExtInstId, ExtId> = BTreeMap::new();
let mut sim = Sim {
sim_circuit,
net_values,
exts,
ext_id_by_ext_inst_id,
start_time: SystemTime::now(),
clock_ticks: 0,
clock_freq_cap: None,
};
for (ext_inst_id, (path, ext_component)) in circuit.exts().iter().enumerate() {
let ext_name = ext_component.name();
let ext_id = sim.ext_id_by_name(&ext_name);
sim.ext_id_by_ext_inst_id.insert(ext_inst_id, ext_id);
sim.instantiate_ext(path.clone(), ext_id);
}
sim.broadcast_update_constants();
sim
}
fn ext_id_by_name(&self, name: &str) -> ExtId {
for (ext_id, ext) in self.exts.iter().enumerate() {
if ext.name() == name {
return ext_id;
}
}
panic!("No such ext: {name}")
}
pub fn net_values(&self) -> BTreeMap<NetId, Value> {
self.net_values.iter().cloned().enumerate().collect()
}
pub fn net(&self, net_id: NetId) -> &Net {
&self.sim_circuit.nets[net_id]
}
pub fn cap_clock_freq(mut self, freq: f64) -> Self {
self.clock_freq_cap = Some(freq);
self
}
fn instantiate_ext<P: Into<Path>>(&mut self, path: P, ext_id: ExtId) {
let path: Path = path.into();
let ext = &mut self.exts[ext_id];
ext.instantiate(path.clone());
}
pub(crate) fn poke_net(&mut self, net_id: NetId, value: Value) {
self.net_values[net_id] = value.clone();
let dependents = &self.sim_circuit.clone().dependents[net_id];
for comb_id in dependents.combs.iter() {
let comb = &self.sim_circuit.clone().combs[*comb_id];
let Comb(target_net_id, expr) = comb;
let value = expr.eval(&self);
self.poke_net(*target_net_id, value);
}
for (ext_inst_id, port_name) in dependents.ext_inst_ports.iter() {
let ext_id = self.ext_id_by_ext_inst_id[ext_inst_id];
let ext = &mut *self.exts[ext_id];
let path = self.sim_circuit.path_by_ext_inst_id[&ext_inst_id].clone();
for (updated_port_name, updated_value) in ext.update(path, port_name, value.clone()) {
let net_id = self.sim_circuit.net_id_by_ext_port[&(*ext_inst_id, updated_port_name)];
self.poke_net(net_id, updated_value);
}
}
}
pub(crate) fn peek_net(&self, net_id: NetId) -> Value {
self.net_values[net_id].clone()
}
fn net_id(&self, path: Path) -> NetId {
if let Some(net_id) = self.sim_circuit.net_id_by_path.get(&path) {
*net_id
} else {
panic!("No net for {path}")
}
}
pub fn peek<P: Into<Path>>(&self, path: P) -> Value {
let net_id = self.net_id(path.into());
self.net_values[net_id].clone()
}
pub fn poke<P: Into<Path>>(&mut self, path: P, value: Value) {
let net_id = self.net_id(path.into());
self.poke_net(net_id, value);
}
pub fn type_of<P: Into<Path>>(&self, path: P) -> Type {
let net_id = self.net_id(path.into());
let Net(_driver, _terminals, typ) = &self.sim_circuit.nets[net_id];
typ.clone()
}
fn broadcast_update_constants(&mut self) {
for Comb(target_net_id, expr) in self.sim_circuit.clone().combs.iter() {
if expr.is_constant() {
let value = expr.eval(&self);
self.poke_net(*target_net_id, value);
}
}
}
pub fn clock(&mut self) {
self.clock_ticks += 1;
if let Some(clock_freq_cap) = self.clock_freq_cap {
let mut clock_freq = self.clocks_per_second();
while clock_freq.is_finite() && clock_freq > clock_freq_cap {
clock_freq = self.clocks_per_second();
}
}
let mut updates = vec![];
for reginfo in &self.sim_circuit.clone().regs {
let value = self.peek_net(reginfo.set_net_id);
updates.push((reginfo.val_net_id, value));
}
for (val_net_id, value) in updates {
self.poke_net(val_net_id, value);
}
for (ext_inst_id, path) in &self.sim_circuit.path_by_ext_inst_id {
let ext_id = self.ext_id_by_ext_inst_id[ext_inst_id];
let ext = &mut self.exts[ext_id];
ext.clock(path.clone());
}
}
pub fn reset(&mut self) {
for reginfo in &self.sim_circuit.clone().regs {
if let Some(reset) = ®info.reset {
self.poke_net(reginfo.val_net_id, reset.eval(&self));
}
}
for (ext_inst_id, path) in &self.sim_circuit.path_by_ext_inst_id {
let ext_id = self.ext_id_by_ext_inst_id[ext_inst_id];
let ext = &mut self.exts[ext_id];
ext.reset(path.clone());
}
}
pub fn clocks_per_second(&self) -> f64 {
let end_time = SystemTime::now();
let duration: Duration = end_time.duration_since(self.start_time).unwrap();
1_000_000.0 * self.clock_ticks as f64 / duration.as_micros() as f64
}
}
#[derive(Debug, Clone)]
pub struct Comb(NetId, Arc<Expr>);
impl Comb {
pub fn depends_on(&self, net_id: NetId) -> bool {
let Comb(_net_id, expr) = self;
expr.depends_on_net(net_id)
}
}
impl std::fmt::Debug for Sim {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
for (net_id, value) in self.net_values.iter().enumerate() {
let net = &self.sim_circuit.nets[net_id];
write!(f, " {:>5} ", format!("{value:?}"))?;
writeln!(f, "{}", net.terminals().iter().map(|t| t.to_string()).collect::<Vec<String>>().join(" "))?;
}
Ok(())
}
}
pub fn nets(circuit: &Circuit) -> Vec<Net> {
let mut immediate_driver_for: BTreeMap<Path, Path> = BTreeMap::new();
for (path, Wire(_loc, target, expr, wire_type)) in circuit.wires() {
let abs_expr = expr.rebase(path.clone());
let target_terminal: Path = match wire_type {
WireType::Dom => path.join(target).clone(),
WireType::Direct => path.join(target).clone(),
WireType::Latch => path.join(target).set(),
WireType::Proc => path.join(target).set(),
};
if let Expr::Reference(_loc, _typ, driver) = &*abs_expr {
immediate_driver_for.insert(target_terminal.clone(), driver.clone());
}
}
let mut drivers: BTreeSet<Path> = BTreeSet::new();
for terminal in circuit.paths() {
drivers.insert(driver_for(terminal, &immediate_driver_for));
}
let mut nets: BTreeMap<Path, Net> = BTreeMap::new();
for driver in &drivers {
let component = circuit.component(driver.clone()).unwrap();
let typ = component.type_of().unwrap();
nets.insert(driver.clone(), Net::from(driver.clone(), typ));
}
for terminal in circuit.paths() {
let driver = driver_for(terminal.clone(), &immediate_driver_for);
let net = nets.get_mut(&driver).unwrap();
net.add(terminal);
}
let nets: Vec<Net> = nets.values().into_iter().cloned().collect();
nets
}
fn driver_for(terminal: Path, immediate_driver_for: &BTreeMap<Path, Path>) -> Path {
let mut driver: &Path = &terminal;
while let Some(immediate_driver) = &immediate_driver_for.get(driver) {
driver = immediate_driver;
}
driver.clone()
}
impl Net {
fn from(terminal: Path, typ: Type) -> Net {
Net(terminal, vec![], typ)
}
pub fn add(&mut self, terminal: Path) {
if self.0 != terminal {
self.1.push(terminal);
self.1.sort();
self.1.dedup();
}
}
pub fn driver(&self) -> Path {
self.0.clone()
}
pub fn drivees(&self) -> &[Path] {
&self.1
}
pub fn terminals(&self) -> Vec<Path> {
let mut results = vec![self.0.clone()];
for terminal in &self.1 {
results.push(terminal.clone());
}
results
}
pub fn contains(&self, terminal: Path) -> bool {
if terminal == self.0 {
true
} else {
self.1.contains(&terminal)
}
}
}
#[derive(Clone)]
pub struct Net(Path, Vec<Path>, Type);
impl std::fmt::Debug for Net {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "Net({} {})", self.0, self.1.iter().map(|path| path.to_string()).collect::<Vec<_>>().join(" "))
}
}
impl Expr {
pub fn rebase(&self, current_path: Path) -> Arc<Expr> {
self.rebase_rec(current_path, &BTreeSet::new())
}
fn rebase_rec(&self, current_path: Path, shadowed: &BTreeSet<Path>) -> Arc<Expr> {
Arc::new(match self {
Expr::Reference(loc, typ, path) => {
if !shadowed.contains(path) {
Expr::Reference(loc.clone(), typ.clone(), current_path.join(path.clone()))
} else {
self.clone()
}
},
Expr::Net(_loc, _typ, _net_id) => panic!("rebase() only works on reference expressions."),
Expr::Word(_loc, _typ, _width, _value) => self.clone(),
Expr::Enum(_loc, _typ, _typedef, _name) => self.clone(),
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()),
Expr::Struct(loc, typ, fields) => {
Expr::Struct(
loc.clone(),
typ.clone(),
fields
.iter()
.map(|(name, e)| (name.clone(), e.rebase_rec(current_path.clone(), shadowed)))
.collect(),
)
},
Expr::Let(loc, typ, name, ascription, e, b) => {
let new_e = e.rebase_rec(current_path.clone(), shadowed);
let mut new_shadowed = shadowed.clone();
new_shadowed.insert(name.clone().into());
let new_b = b.rebase_rec(current_path, &new_shadowed);
Expr::Let(loc.clone(), typ.clone(), name.clone(), ascription.clone(), new_e, new_b)
},
Expr::Match(loc, typ, e, arms) => {
let new_arms = arms.iter().map(|MatchArm(pat, e)| {
let mut new_shadowed = shadowed.clone();
new_shadowed.extend(pat.bound_vars().into_iter().map(|v| v.into()));
MatchArm(pat.clone(), e.rebase_rec(current_path.clone(), &new_shadowed))
}).collect();
Expr::Match(
loc.clone(),
typ.clone(),
e.rebase_rec(current_path.clone(), shadowed),
new_arms,
)
},
Expr::UnOp(loc, typ, op, e) => Expr::UnOp(loc.clone(), typ.clone(), *op, e.rebase_rec(current_path, shadowed)),
Expr::BinOp(loc, typ, op, e1, e2) => {
Expr::BinOp(
loc.clone(),
typ.clone(),
*op,
e1.rebase_rec(current_path.clone(), shadowed),
e2.rebase_rec(current_path, shadowed),
)
},
Expr::If(loc, typ, cond, e1, e2) => {
Expr::If(
loc.clone(),
typ.clone(),
cond.rebase_rec(current_path.clone(), shadowed),
e1.rebase_rec(current_path.clone(), shadowed),
e2.rebase_rec(current_path, shadowed),
)
},
Expr::Mux(loc, typ, cond, e1, e2) => {
Expr::Mux(
loc.clone(),
typ.clone(),
cond.rebase_rec(current_path.clone(), shadowed),
e1.rebase_rec(current_path.clone(), shadowed),
e2.rebase_rec(current_path, shadowed),
)
},
Expr::Cat(loc, typ, es) => {
Expr::Cat(
loc.clone(),
typ.clone(),
es.iter().map(|e| e.rebase_rec(current_path.clone(), shadowed)).collect(),
)
},
Expr::Sext(loc, typ, e) => Expr::Sext(loc.clone(), typ.clone(), e.rebase_rec(current_path, shadowed)),
Expr::Zext(loc, typ, e) => Expr::Zext(loc.clone(), typ.clone(), e.rebase_rec(current_path, shadowed)),
Expr::TryCast(loc, typ, e) => Expr::TryCast(loc.clone(), typ.clone(), e.rebase_rec(current_path, shadowed)),
Expr::ToWord(loc, typ, e) => Expr::ToWord(loc.clone(), typ.clone(), e.rebase_rec(current_path, shadowed)),
Expr::Vec(loc, typ, es) => Expr::Vec(loc.clone(), typ.clone(), es.iter().map(|e| e.rebase_rec(current_path.clone(), shadowed)).collect()),
Expr::IdxField(loc, typ, e, field) => Expr::IdxField(loc.clone(), typ.clone(), e.rebase_rec(current_path, shadowed), field.clone()),
Expr::Idx(loc, typ, e, i) => Expr::Idx(loc.clone(), typ.clone(), e.rebase_rec(current_path, shadowed), *i),
Expr::IdxRange(loc, typ, e, j, i) => Expr::IdxRange(loc.clone(), typ.clone(), e.rebase_rec(current_path, shadowed), *j, *i),
Expr::Call(loc, typ, fndef, es) => {
Expr::Call(
loc.clone(),
typ.clone(),
fndef.clone(),
es.iter().map(|e| e.rebase_rec(current_path.clone(), shadowed)).collect(),
)
},
Expr::Hole(loc, typ, name) => Expr::Hole(loc.clone(), typ.clone(), name.clone()),
})
}
fn references_to_nets(&self, net_id_by_path: &BTreeMap<Path, NetId>) -> Arc<Expr> {
self.references_to_nets_rec(net_id_by_path, &BTreeSet::new())
}
fn references_to_nets_rec(&self, net_id_by_path: &BTreeMap<Path, NetId>, shadowed: &BTreeSet<Path>) -> Arc<Expr> {
Arc::new(match self {
Expr::Reference(loc, typ, path) => {
if !shadowed.contains(path) {
Expr::Net(loc.clone(), typ.clone(), net_id_by_path[path])
} else {
self.clone()
}
},
Expr::Net(_loc, _typ, _net_id) => panic!("references_to_nets() only works on reference expressions."),
Expr::Word(_loc, _typ, _width, _value) => self.clone(),
Expr::Enum(_loc, _typ, _typedef, _name) => self.clone(),
Expr::Ctor(loc, typ, name, es) => {
Expr::Ctor(
loc.clone(),
typ.clone(),
name.clone(),
es.iter().map(|e| e.references_to_nets_rec(net_id_by_path, shadowed)).collect(),
)
},
Expr::Struct(loc, typ, fields) => {
Expr::Struct(
loc.clone(),
typ.clone(),
fields
.iter()
.map(|(name, e)| (name.clone(), e.references_to_nets_rec(net_id_by_path, shadowed)))
.collect(),
)
},
Expr::Let(loc, typ, name, ascription, e, b) => {
let new_e = e.references_to_nets_rec(net_id_by_path, shadowed);
let mut new_shadowed = shadowed.clone();
new_shadowed.insert(name.clone().into());
let new_b = b.references_to_nets_rec(net_id_by_path, &new_shadowed);
Expr::Let(loc.clone(), typ.clone(), name.clone(), ascription.clone(), new_e, new_b)
},
Expr::UnOp(loc, typ, op, e) => Expr::UnOp(loc.clone(), typ.clone(), *op, e.references_to_nets_rec(net_id_by_path, shadowed)),
Expr::BinOp(loc, typ, op, e1, e2) => {
Expr::BinOp(
loc.clone(),
typ.clone(),
*op,
e1.references_to_nets_rec(net_id_by_path, shadowed),
e2.references_to_nets_rec(net_id_by_path, shadowed),
)
},
Expr::If(loc, typ, cond, e1, e2) => {
Expr::If(
loc.clone(),
typ.clone(),
cond.references_to_nets_rec(net_id_by_path, shadowed),
e1.references_to_nets_rec(net_id_by_path, shadowed),
e2.references_to_nets_rec(net_id_by_path, shadowed),
)
},
Expr::Match(loc, typ, e, arms) => {
let new_arms = arms.iter().map(|MatchArm(pat, e)| {
let mut new_shadowed = shadowed.clone();
new_shadowed.extend(pat.bound_vars().into_iter().map(|v| v.into()));
MatchArm(pat.clone(), e.references_to_nets_rec(net_id_by_path, &new_shadowed))
}).collect();
Expr::Match(
loc.clone(),
typ.clone(),
e.references_to_nets_rec(net_id_by_path, shadowed),
new_arms,
)
},
Expr::Mux(loc, typ, cond, e1, e2) => {
Expr::Mux(
loc.clone(),
typ.clone(),
cond.references_to_nets_rec(net_id_by_path, shadowed),
e1.references_to_nets_rec(net_id_by_path, shadowed),
e2.references_to_nets_rec(net_id_by_path, shadowed),
)
},
Expr::Cat(loc, typ, es) => {
Expr::Cat(
loc.clone(),
typ.clone(),
es.iter().map(|e| e.references_to_nets_rec(net_id_by_path, shadowed)).collect(),
)
},
Expr::Sext(loc, typ, e) => Expr::Sext(loc.clone(), typ.clone(), e.references_to_nets_rec(net_id_by_path, shadowed)),
Expr::Zext(loc, typ, e) => Expr::Zext(loc.clone(), typ.clone(), e.references_to_nets_rec(net_id_by_path, shadowed)),
Expr::TryCast(loc, typ, e) => Expr::TryCast(loc.clone(), typ.clone(), e.references_to_nets_rec(net_id_by_path, shadowed)),
Expr::ToWord(loc, typ, e) => Expr::ToWord(loc.clone(), typ.clone(), e.references_to_nets_rec(net_id_by_path, shadowed)),
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()),
Expr::IdxField(loc, typ, e, field) => Expr::IdxField(loc.clone(), typ.clone(), e.references_to_nets_rec(net_id_by_path, shadowed), field.clone()),
Expr::Idx(loc, typ, e, i) => Expr::Idx(loc.clone(), typ.clone(), e.references_to_nets_rec(net_id_by_path, shadowed), *i),
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),
Expr::Call(loc, typ, fndef, es) => {
Expr::Call(
loc.clone(),
typ.clone(),
fndef.clone(),
es.iter().map(|e| e.references_to_nets_rec(net_id_by_path, shadowed)).collect(),
)
},
Expr::Hole(loc, typ, name) => Expr::Hole(loc.clone(), typ.clone(), name.clone()),
})
}
fn depends_on_net(&self, net_id: NetId) -> bool {
match self {
Expr::Reference(_loc, _typ, _path) => false,
Expr::Net(_loc, _typ, other_netid) => net_id == *other_netid,
Expr::Word(_loc, _typ, _width, _value) => false,
Expr::Enum(_loc, _typ, _typedef, _name) => false,
Expr::Ctor(_loc, _typ, _name, es) => es.iter().any(|e| e.depends_on_net(net_id)),
Expr::Struct(_loc, _typ, fields) => fields.iter().any(|(_name, e)| e.depends_on_net(net_id)),
Expr::Let(_loc, _typ, _name, _ascription, e, b) => e.depends_on_net(net_id) || b.depends_on_net(net_id),
Expr::Match(_loc, _typ, e, arms) => e.depends_on_net(net_id) || arms.iter().any(|MatchArm(_pat, arm_e)| arm_e.depends_on_net(net_id)),
Expr::UnOp(_loc, _typ, _op, e) => e.depends_on_net(net_id),
Expr::BinOp(_loc, _typ, _op, e1, e2) => e1.depends_on_net(net_id) || e2.depends_on_net(net_id),
Expr::If(_loc, _typ, cond, e1, e2) => cond.depends_on_net(net_id) || e1.depends_on_net(net_id) || e2.depends_on_net(net_id),
Expr::Mux(_loc, _typ, cond, e1, e2) => cond.depends_on_net(net_id) || e1.depends_on_net(net_id) || e2.depends_on_net(net_id),
Expr::Cat(_loc, _typ, es) => es.iter().any(|e| e.depends_on_net(net_id)),
Expr::Sext(_loc, _typ, e) => e.depends_on_net(net_id),
Expr::Zext(_loc, _typ, e) => e.depends_on_net(net_id),
Expr::TryCast(_loc, _typ, e) => e.depends_on_net(net_id),
Expr::ToWord(_loc, _typ, e) => e.depends_on_net(net_id),
Expr::Vec(_loc, _typ, es) => es.iter().any(|e| e.depends_on_net(net_id)),
Expr::IdxField(_loc, _typ, e, _field) => e.depends_on_net(net_id),
Expr::Idx(_loc, _typ, e, _i) => e.depends_on_net(net_id),
Expr::IdxRange(_loc, _typ, e, _j, _i) => e.depends_on_net(net_id),
Expr::Call(_loc, _typ, _fndef, es) => es.iter().any(|e| e.depends_on_net(net_id)),
Expr::Hole(_loc, _typ, _name) => false,
}
}
}