use safety_net::DrivenNet;
use safety_net::Gate;
use safety_net::GateNetlist;
use safety_net::Net;
use safety_net::Netlist;
use safety_net::assert_verilog_eq;
use safety_net::format_id;
use std::collections::HashSet;
use std::rc::Rc;
fn and_gate() -> Gate {
Gate::new_logical("AND".into(), vec!["A".into(), "B".into()], "Y".into())
}
fn full_adder() -> Gate {
Gate::new_logical_multi(
"FA".into(),
vec!["CIN".into(), "A".into(), "B".into()],
vec!["S".into(), "COUT".into()],
)
}
fn ripple_adder() -> Rc<GateNetlist> {
let netlist = Netlist::new("ripple_adder".to_string());
let bitwidth = 4;
let a = netlist.insert_input_escaped_logic_bus("a".to_string(), bitwidth);
let b = netlist.insert_input_escaped_logic_bus("b".to_string(), bitwidth);
let mut carry: DrivenNet<Gate> = netlist.insert_input("cin".into());
for (i, (a, b)) in a.into_iter().zip(b).enumerate() {
let fa = netlist
.insert_gate(full_adder(), format_id!("fa_{i}"), &[carry, a, b])
.unwrap();
fa.expose_net(&fa.get_net(0)).unwrap();
carry = fa.find_output(&"COUT".into()).unwrap();
if i == bitwidth - 1 {
fa.get_output(1).expose_with_name("cout".into()).unwrap();
}
}
netlist
}
fn get_simple_example() -> Rc<GateNetlist> {
let netlist = Netlist::new("example".to_string());
let a = netlist.insert_input("a".into());
let b = netlist.insert_input("b".into());
let instance = netlist
.insert_gate(and_gate(), "inst_0".into(), &[a, b])
.unwrap();
instance.expose_with_name("y".into());
netlist
}
#[test]
fn test_netref_printing() {
let netlist = get_simple_example();
let gate = netlist.last().unwrap();
let output = format!("{gate:?}");
assert_eq!(
output,
"{ owner: \"example\", index: 2, val: \"AND(inst_0)\" }"
);
}
#[test]
fn test_io() {
let netlist = get_simple_example();
let netlist = netlist.reclaim().unwrap();
assert_eq!(netlist.inputs().count(), 2);
assert_eq!(netlist.outputs().len(), 1);
let (output, o_net) = netlist.outputs().first().unwrap().clone();
let o_port = output.get_port();
let o_port_alt = output
.clone()
.unwrap()
.get_instance_type()
.unwrap()
.get_single_output_port()
.clone();
assert_eq!(o_port, o_port_alt);
let correct = Net::new_logic("Y".into());
assert_eq!(o_port, correct);
let correct = Net::new_logic("y".into());
assert_eq!(o_net, correct);
let correct = Net::new_logic("inst_0_Y".into());
assert_eq!(output.as_net().clone(), correct);
}
#[test]
fn test_get_input() {
let netlist = ripple_adder();
let last_fa = netlist.last().unwrap();
let d0 = last_fa.get_driver(0).unwrap();
let d1 = last_fa.get_input(0).get_driver().unwrap().unwrap();
assert_eq!(d0, d1);
let d0 = *last_fa.get_input(0).get_port().get_type();
let d1 = *last_fa
.get_input(0)
.get_driver()
.unwrap()
.as_net()
.get_type();
assert_eq!(d0, d1);
}
#[test]
#[should_panic(expected = "Attempt to grab the net of a multi-output instance")]
fn test_bad_access_1() {
let netlist = ripple_adder();
let last_fa = netlist.last().unwrap();
last_fa.as_net();
}
#[test]
#[should_panic(expected = "Attempt to grab the net of a multi-output instance")]
fn test_bad_access_2() {
let netlist = ripple_adder();
let last_fa = netlist.last().unwrap();
last_fa.as_net_mut();
}
#[test]
#[should_panic(expected = "Attempted to grab output port of a multi-output gate")]
fn test_bad_access_3() {
let netlist = ripple_adder();
let last_fa = netlist.last().unwrap();
last_fa
.get_instance_type()
.unwrap()
.get_single_output_port();
}
#[test]
#[should_panic(expected = "Input port is unlinked from netlist")]
fn test_unlinked_1() {
let netlist = ripple_adder().reclaim().unwrap();
let last_fa = netlist.last().unwrap();
last_fa.get_input(0).get_driver();
}
#[test]
#[should_panic(expected = "NetRef is unlinked from netlist")]
fn test_unlinked_2() {
let netlist = ripple_adder().reclaim().unwrap();
let last_fa = netlist.last().unwrap();
last_fa.expose_with_name("no".into());
}
#[test]
fn test_get_net_from_obj() {
let netlist = get_simple_example();
let gate = netlist.last().unwrap();
let obj = gate.get_obj();
let net = gate.get_net(0).clone();
let also_net = obj.get_net(0).clone();
let still_net = obj.get_single_net().clone();
assert_eq!(net, also_net);
assert_eq!(net, still_net);
}
#[test]
#[should_panic(expected = "RefCell already mutably borrowed")]
fn test_change_gate_incorrect() {
let netlist = get_simple_example();
let gate = netlist.last().unwrap();
let mut type_gate = gate.get_instance_type_mut().unwrap();
type_gate.set_gate_name("OR".into());
eprintln!("{netlist}");
}
#[test]
fn test_change_gate_correct() {
let netlist = get_simple_example();
let gate = netlist.last().unwrap();
{
let mut type_gate = gate.get_instance_type_mut().unwrap();
type_gate.set_gate_name("OR".into());
}
assert_verilog_eq!(
netlist.to_string(),
"module example (
a,
b,
y
);
input a;
wire a;
input b;
wire b;
output y;
wire y;
wire inst_0_Y;
OR inst_0 (
.A(a),
.B(b),
.Y(inst_0_Y)
);
assign y = inst_0_Y;
endmodule\n"
);
}
#[test]
fn test_find_net_mut() {
let netlist = get_simple_example();
let gate = netlist.last().unwrap();
{
let net = gate.find_net_mut(&"inst_0_Y".into());
assert!(net.is_some());
let mut net = net.unwrap();
net.set_identifier("changed".into());
}
assert_verilog_eq!(
netlist.to_string(),
"module example (
a,
b,
y
);
input a;
wire a;
input b;
wire b;
output y;
wire y;
wire changed;
AND inst_0 (
.A(a),
.B(b),
.Y(changed)
);
assign y = changed;
endmodule\n"
);
}
#[test]
fn test_driver_net_mut() {
let netlist = get_simple_example();
let gate = netlist.last().unwrap();
{
for d in gate.drivers().flatten() {
let mut net = d.get_net_mut(0);
net.set_identifier("changed".into());
}
assert_eq!(gate.drivers().count(), 2);
}
assert!(netlist.verify().is_err());
}
#[test]
fn test_driver_net() {
let netlist = Rc::new(ripple_adder());
let gate = netlist.last().unwrap();
assert_eq!(gate.driver_nets().flatten().count(), 3);
}
#[test]
fn test_get_driver_net() {
let netlist = ripple_adder();
let gate = netlist.last().unwrap();
let driver_net = gate.get_driver_net(0).unwrap();
assert_eq!(driver_net.get_identifier(), &"fa_2_COUT".into());
}
#[test]
fn test_gate_io() {
let netlist = get_simple_example();
let gate = netlist.last().unwrap();
assert_eq!(gate.inputs().count(), 2);
assert!(gate.is_fully_connected());
assert_eq!(gate.outputs().count(), 1);
assert!(gate.drives_a_top_output());
assert!(gate.get_output(0).is_top_level_output());
}
#[test]
fn test_implicits() {
let netlist = get_simple_example();
let gate = &netlist.last().unwrap();
let dn: DrivenNet<Gate> = gate.into();
assert_eq!(dn.unwrap(), *gate);
}
#[test]
#[should_panic(expected = "Cannot convert a multi-output netref to an output port")]
fn test_implicits_bad() {
let netlist = ripple_adder();
let gate = &netlist.last().unwrap();
let dn: DrivenNet<Gate> = gate.into();
assert_eq!(dn.unwrap(), *gate);
}
#[test]
fn test_netref_ids() {
let netlist = get_simple_example();
let gate = netlist.last().unwrap();
let id = gate.get_identifier();
assert_eq!(id, "inst_0_Y".into());
gate.set_identifier("new_id".into());
assert_verilog_eq!(
netlist.to_string(),
"module example (
a,
b,
y
);
input a;
wire a;
input b;
wire b;
output y;
wire y;
wire new_id;
AND inst_0 (
.A(a),
.B(b),
.Y(new_id)
);
assign y = new_id;
endmodule\n"
);
}
#[test]
fn test_bad_gate_creation() {
let netlist = GateNetlist::new("example".to_string());
let gate = netlist.insert_gate(and_gate(), "yo".into(), &[]);
assert!(gate.is_err());
}
#[test]
fn test_dfs_order() {
let netlist = ripple_adder();
let mut dfs = netlist
.node_dfs(netlist.last().unwrap())
.collect::<Vec<_>>();
dfs.reverse();
for (i, fa) in dfs.into_iter().filter(|i| !i.is_an_input()).enumerate() {
let instance = fa.get_instance_name();
assert!(instance.is_some());
let instance = instance.unwrap().to_string();
assert!(instance.starts_with("fa_"));
assert_eq!(instance.split('_').nth(1).unwrap(), i.to_string());
}
}
#[test]
fn test_replace_gate_bad() {
let netlist = get_simple_example();
let inputs = netlist.inputs().collect::<Vec<_>>();
let and_gate = netlist.last().unwrap();
let or_gate = Gate::new_logical("OR".into(), vec!["A".into(), "B".into()], "Y".into());
let or_gate = netlist
.insert_gate(or_gate, "inst_0".into(), &inputs)
.unwrap();
assert!(
netlist
.replace_net_uses(and_gate.into(), &or_gate.into())
.is_ok()
);
assert!(netlist.clean().is_err());
}
#[test]
fn test_retain() {
let netlist = get_simple_example();
let inputs = netlist.inputs().collect::<Vec<_>>();
let and_gate = netlist.last().unwrap().get_output(0);
let or_gate = Gate::new_logical("OR".into(), vec!["A".into(), "B".into()], "Y".into());
let or_gate = netlist
.insert_gate(or_gate, "inst_1".into(), &inputs)
.unwrap()
.get_output(0);
let mut set: HashSet<DrivenNet<Gate>> = HashSet::new();
set.insert(or_gate.clone());
assert!(netlist.replace_net_uses(and_gate, &or_gate).is_ok());
assert_eq!(set.len(), 1);
assert!(netlist.retain(&mut set).is_ok());
assert_eq!(set.len(), 1);
or_gate.remove_all_outputs();
set.insert(or_gate);
assert!(netlist.retain(&mut set).is_ok());
assert_eq!(set.len(), 0);
}
#[test]
fn test_replace_gate() {
let netlist = get_simple_example();
let inputs = netlist.inputs().collect::<Vec<_>>();
let and_gate = netlist.last().unwrap();
let or_gate = Gate::new_logical("OR".into(), vec!["A".into(), "B".into()], "Y".into());
let or_gate = netlist
.insert_gate(or_gate, "inst_0".into(), &inputs)
.unwrap();
assert!(
netlist
.replace_net_uses(and_gate.into(), &or_gate.clone().into())
.is_ok()
);
assert!(netlist.clean().is_err());
or_gate.set_instance_name("inst_1".into());
or_gate.as_net_mut().set_identifier("inst_1_Y".into());
assert!(netlist.clean().is_ok());
or_gate.set_instance_name("inst_0".into());
or_gate.as_net_mut().set_identifier("inst_0_Y".into());
assert_verilog_eq!(
netlist.to_string(),
"module example (
a,
b,
y
);
input a;
wire a;
input b;
wire b;
output y;
wire y;
wire inst_0_Y;
OR inst_0 (
.A(a),
.B(b),
.Y(inst_0_Y)
);
assign y = inst_0_Y;
endmodule\n"
);
}
#[test]
fn test_simple_example() {
let netlist = get_simple_example();
assert_eq!(netlist.get_name().clone(), "example");
assert_eq!(netlist.get_input_ports().count(), 2);
assert_eq!(netlist.get_output_ports().len(), 1);
let objects: Vec<_> = netlist.objects().collect();
assert_eq!(objects.len(), 3); netlist.set_name("new_name".to_string());
assert_eq!(netlist.get_name().clone(), "new_name");
}
#[test]
fn test_deep_clone() {
let netlist = get_simple_example();
let clone = netlist.deep_clone();
assert_eq!(netlist.to_string(), clone.to_string());
}
#[test]
#[should_panic(expected = "does not belong")]
fn test_netlist_belongs() {
let netlist = get_simple_example();
let clone = netlist.deep_clone();
let gate = netlist.last().unwrap();
let _ = clone.delete_net_uses(gate);
}