diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index c9c4d2153..49d3e91d5 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -825,6 +825,41 @@ void Circuit::do_purge_nets (bool keep_pins) } } +static bool can_purge_device (const db::Device &device) +{ + if (! device.device_class ()) { + return false; + } + + const std::vector &tdefs = device.device_class ()->terminal_definitions (); + if (tdefs.size () <= 1) { + return false; + } + + const db::Net *net = device.net_for_terminal (tdefs.front ().id ()); + for (auto t = tdefs.begin () + 1; t != tdefs.end (); ++t) { + if (net != device.net_for_terminal (t->id ())) { + return false; + } + } + + return true; +} + +void Circuit::purge_devices () +{ + std::vector devices_to_be_purged; + for (device_iterator d = begin_devices (); d != end_devices (); ++d) { + if (can_purge_device (*d)) { + devices_to_be_purged.push_back (d.operator-> ()); + } + } + + for (auto d = devices_to_be_purged.begin (); d != devices_to_be_purged.end (); ++d) { + remove_device (*d); + } +} + /** * @brief Sanity check for device to be removed */ diff --git a/src/db/db/dbCircuit.h b/src/db/db/dbCircuit.h index abcb74274..9ebc5f83d 100644 --- a/src/db/db/dbCircuit.h +++ b/src/db/db/dbCircuit.h @@ -732,6 +732,14 @@ class DB_PUBLIC Circuit */ void purge_nets_keep_pins (); + /** + * @brief Purges invalid devices + * + * This method will purge all invalid devices, i.e. those + * whose terminals are all connected to the same net. + */ + void purge_devices (); + /** * @brief Combine devices * diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index e5e801863..3c038eb77 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -619,6 +619,13 @@ void Netlist::purge_nets () } } +void Netlist::purge_devices () +{ + for (bottom_up_circuit_iterator c = begin_bottom_up (); c != end_bottom_up (); ++c) { + c->purge_devices (); + } +} + void Netlist::make_top_level_pins () { size_t ntop = top_circuit_count (); @@ -684,6 +691,8 @@ void Netlist::simplify () { make_top_level_pins (); purge (); + + // combine devices are purge nets that are created in that step combine_devices (); purge_nets (); } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 0bff649e1..d21115838 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -508,6 +508,14 @@ class DB_PUBLIC Netlist */ void purge_nets (); + /** + * @brief Purges invalid devices + * + * This method will purge all invalid devices, i.e. those + * whose terminals are all connected to the same net. + */ + void purge_devices (); + /** * @brief Creates pins for top-level circuits * diff --git a/src/db/db/dbNetlistDeviceClasses.cc b/src/db/db/dbNetlistDeviceClasses.cc index 2a1fa9573..5252557d0 100644 --- a/src/db/db/dbNetlistDeviceClasses.cc +++ b/src/db/db/dbNetlistDeviceClasses.cc @@ -116,7 +116,7 @@ class ResistorDeviceCombiner { double va = a->parameter_value (0); double vb = b->parameter_value (0); - a->set_parameter_value (0, va + vb < 1e-10 ? 0.0 : va * vb / (va + vb)); + a->set_parameter_value (0, va + vb < 1e-30 ? 0.0 : va * vb / (va + vb)); // parallel width is sum of both, length is the one that gives the same value of resistance // R = 1/(1/R1 + 1/R2) @@ -204,7 +204,7 @@ class CapacitorDeviceCombiner { double va = a->parameter_value (0); double vb = b->parameter_value (0); - a->set_parameter_value (0, va + vb < 1e-10 ? 0.0 : va * vb / (va + vb)); + a->set_parameter_value (0, va + vb < 1e-30 ? 0.0 : va * vb / (va + vb)); // TODO: does this implementation make sense? double aa = a->parameter_value (1); @@ -259,7 +259,7 @@ class InductorDeviceCombiner { double va = a->parameter_value (0); double vb = b->parameter_value (0); - a->set_parameter_value (0, va + vb < 1e-10 ? 0.0 : va * vb / (va + vb)); + a->set_parameter_value (0, va + vb < 1e-30 ? 0.0 : va * vb / (va + vb)); } void serial (Device *a, Device *b) const diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 1573631dc..2687fec45 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -1809,6 +1809,12 @@ Class decl_dbCircuit (decl_dbNetlistObject, "db", "Circuit", "For example, serial or parallel resistors can be combined into " "a single resistor.\n" ) + + gsi::method ("purge_devices", &db::Circuit::purge_devices, + "@brief Purges invalid devices.\n" + "Purges devices which are considered invalid. Such devices are for example those whose terminals are all connected to a single net.\n" + "\n" + "This method has been added in version 0.29.7." + ) + gsi::method ("purge_nets", &db::Circuit::purge_nets, "@brief Purges floating nets.\n" "Floating nets are nets with no device or subcircuit attached to. Such floating " @@ -2176,6 +2182,12 @@ Class decl_dbNetlist ("db", "Netlist", "Floating nets can be created as effect of reconnections of devices or pins. " "This method will eliminate all nets that make less than two connections." ) + + gsi::method ("purge_devices", &db::Netlist::purge_devices, + "@brief Purges invalid devices.\n" + "Purges devices which are considered invalid. Such devices are for example those whose terminals are all connected to a single net.\n" + "\n" + "This method has been added in version 0.29.7." + ) + gsi::method ("simplify", &db::Netlist::simplify, "@brief Convenience method that combines the simplification.\n" "This method is a convenience method that runs \\make_top_level_pins, \\purge, \\combine_devices and \\purge_nets." diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 6cb051c86..5a992d7f0 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -1792,3 +1792,253 @@ TEST(26_JoinNets) "end;\n" ); } + +// Issue #1832 - small caps are not combined properly +TEST(27_CombineSmallC) +{ + db::Netlist nl; + + db::Circuit *circuit = new db::Circuit (); + circuit->set_name ("TOP"); + nl.add_circuit (circuit); + + db::DeviceClass *device = new db::DeviceClassCapacitor (); + device->set_name ("model_name"); + nl.add_device_class (device); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + + auto p1 = circuit->add_pin ("p1"); + auto p2 = circuit->add_pin ("p2"); + + circuit->connect_pin (p1.id (), n1); + circuit->connect_pin (p2.id (), n3); + + db::Device *c1 = new db::Device (device, "c1"); + circuit->add_device (c1); + c1->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 1e-15); + + db::Device *c2 = new db::Device (device, "c2"); + circuit->add_device (c2); + c2->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 2e-15); + + db::Device *c3 = new db::Device (device, "c3"); + circuit->add_device (c3); + c3->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 3e-15); + + c1->connect_terminal (db::DeviceClassCapacitor::terminal_id_A, n1); + c1->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n2); + + c2->connect_terminal (db::DeviceClassCapacitor::terminal_id_A, n2); + c2->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n3); + + c3->connect_terminal (db::DeviceClassCapacitor::terminal_id_A, n1); + c3->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n3); + + EXPECT_EQ (nl.to_string (), + "circuit TOP (p1=n1,p2=n3);\n" + " device model_name c1 (A=n1,B=n2) (C=1e-15,A=0,P=0);\n" + " device model_name c2 (A=n2,B=n3) (C=2e-15,A=0,P=0);\n" + " device model_name c3 (A=n1,B=n3) (C=3e-15,A=0,P=0);\n" + "end;\n" + ); + + nl.combine_devices (); + + EXPECT_EQ (nl.to_string (), + "circuit TOP (p1=n1,p2=n3);\n" + " device model_name c1 (A=n1,B=n3) (C=3.66666666667e-15,A=0,P=0);\n" + "end;\n" + ); +} + +TEST(27_CombineSmallR) +{ + db::Netlist nl; + + db::Circuit *circuit = new db::Circuit (); + circuit->set_name ("TOP"); + nl.add_circuit (circuit); + + db::DeviceClass *device = new db::DeviceClassResistor (); + device->set_name ("model_name"); + nl.add_device_class (device); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + + auto p1 = circuit->add_pin ("p1"); + auto p2 = circuit->add_pin ("p2"); + + circuit->connect_pin (p1.id (), n1); + circuit->connect_pin (p2.id (), n3); + + db::Device *c1 = new db::Device (device, "c1"); + circuit->add_device (c1); + c1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1e-15); + + db::Device *c2 = new db::Device (device, "c2"); + circuit->add_device (c2); + c2->set_parameter_value (db::DeviceClassResistor::param_id_R, 2e-15); + + db::Device *c3 = new db::Device (device, "c3"); + circuit->add_device (c3); + c3->set_parameter_value (db::DeviceClassResistor::param_id_R, 3e-15); + + c1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + c1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + + c2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + c2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + + c3->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + c3->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + + EXPECT_EQ (nl.to_string (), + "circuit TOP (p1=n1,p2=n3);\n" + " device model_name c1 (A=n1,B=n2) (R=1e-15,L=0,W=0,A=0,P=0);\n" + " device model_name c2 (A=n2,B=n3) (R=2e-15,L=0,W=0,A=0,P=0);\n" + " device model_name c3 (A=n1,B=n3) (R=3e-15,L=0,W=0,A=0,P=0);\n" + "end;\n" + ); + + nl.combine_devices (); + + EXPECT_EQ (nl.to_string (), + "circuit TOP (p1=n1,p2=n3);\n" + " device model_name c1 (A=n1,B=n3) (R=1.5e-15,L=0,W=0,A=0,P=0);\n" + "end;\n" + ); +} + +TEST(27_CombineSmallL) +{ + db::Netlist nl; + + db::Circuit *circuit = new db::Circuit (); + circuit->set_name ("TOP"); + nl.add_circuit (circuit); + + db::DeviceClass *device = new db::DeviceClassInductor (); + device->set_name ("model_name"); + nl.add_device_class (device); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + + auto p1 = circuit->add_pin ("p1"); + auto p2 = circuit->add_pin ("p2"); + + circuit->connect_pin (p1.id (), n1); + circuit->connect_pin (p2.id (), n3); + + db::Device *c1 = new db::Device (device, "c1"); + circuit->add_device (c1); + c1->set_parameter_value (db::DeviceClassInductor::param_id_L, 1e-15); + + db::Device *c2 = new db::Device (device, "c2"); + circuit->add_device (c2); + c2->set_parameter_value (db::DeviceClassInductor::param_id_L, 2e-15); + + db::Device *c3 = new db::Device (device, "c3"); + circuit->add_device (c3); + c3->set_parameter_value (db::DeviceClassInductor::param_id_L, 3e-15); + + c1->connect_terminal (db::DeviceClassInductor::terminal_id_A, n1); + c1->connect_terminal (db::DeviceClassInductor::terminal_id_B, n2); + + c2->connect_terminal (db::DeviceClassInductor::terminal_id_A, n2); + c2->connect_terminal (db::DeviceClassInductor::terminal_id_B, n3); + + c3->connect_terminal (db::DeviceClassInductor::terminal_id_A, n1); + c3->connect_terminal (db::DeviceClassInductor::terminal_id_B, n3); + + EXPECT_EQ (nl.to_string (), + "circuit TOP (p1=n1,p2=n3);\n" + " device model_name c1 (A=n1,B=n2) (L=1e-15);\n" + " device model_name c2 (A=n2,B=n3) (L=2e-15);\n" + " device model_name c3 (A=n1,B=n3) (L=3e-15);\n" + "end;\n" + ); + + nl.combine_devices (); + + EXPECT_EQ (nl.to_string (), + "circuit TOP (p1=n1,p2=n3);\n" + " device model_name c1 (A=n1,B=n3) (L=1.5e-15);\n" + "end;\n" + ); +} + +TEST(28_EliminateShortedDevices) +{ + db::Netlist nl; + + db::Circuit *circuit = new db::Circuit (); + circuit->set_name ("TOP"); + nl.add_circuit (circuit); + + db::DeviceClass *device = new db::DeviceClassCapacitor (); + device->set_name ("model_name"); + nl.add_device_class (device); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + + auto p1 = circuit->add_pin ("p1"); + auto p2 = circuit->add_pin ("p2"); + + circuit->connect_pin (p1.id (), n1); + circuit->connect_pin (p2.id (), n2); + + db::Device *c1 = new db::Device (device, "c1"); + circuit->add_device (c1); + c1->set_parameter_value (db::DeviceClassInductor::param_id_L, 1e-15); + + db::Device *c2 = new db::Device (device, "c2"); + circuit->add_device (c2); + c2->set_parameter_value (db::DeviceClassInductor::param_id_L, 2e-15); + + c1->connect_terminal (db::DeviceClassInductor::terminal_id_A, n1); + c1->connect_terminal (db::DeviceClassInductor::terminal_id_B, n1); + + c2->connect_terminal (db::DeviceClassInductor::terminal_id_A, n1); + c2->connect_terminal (db::DeviceClassInductor::terminal_id_B, n2); + + EXPECT_EQ (nl.to_string (), + "circuit TOP (p1=n1,p2=n2);\n" + " device model_name c1 (A=n1,B=n1) (C=1e-15,A=0,P=0);\n" + " device model_name c2 (A=n1,B=n2) (C=2e-15,A=0,P=0);\n" + "end;\n" + ); + + nl.purge_devices (); + + EXPECT_EQ (nl.to_string (), + "circuit TOP (p1=n1,p2=n2);\n" + " device model_name c2 (A=n1,B=n2) (C=2e-15,A=0,P=0);\n" + "end;\n" + ); +} diff --git a/src/lvs/unit_tests/lvsTests.cc b/src/lvs/unit_tests/lvsTests.cc index 001df63da..d34b0fb15 100644 --- a/src/lvs/unit_tests/lvsTests.cc +++ b/src/lvs/unit_tests/lvsTests.cc @@ -121,6 +121,12 @@ TEST(12_private) run_test (_this, "test_12.lvs", "test_12b.cir.gz", "test_12.gds.gz", true); } +TEST(121_private) +{ + // test_is_long_runner (); + run_test (_this, "test_121.lvs", "test_121.cir.gz", "test_121.gds.gz", true); +} + TEST(13_private) { // test_is_long_runner ();