From 45b35f3aae0cd92b41fad552178d42496007ee45 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 29 Dec 2018 22:18:58 +0100 Subject: [PATCH] WIP: to_string for netlist, tests, some bugfixes on device combination. --- src/db/db/dbNetlist.cc | 144 ++++- src/db/db/dbNetlist.h | 16 +- src/db/db/dbNetlistDeviceClasses.cc | 5 + src/db/db/dbNetlistDeviceClasses.h | 5 + src/db/db/gsiDeclDbNetlist.cc | 21 + .../unit_tests/dbNetlistDeviceClassesTests.cc | 555 ++++++++++++++++++ src/db/unit_tests/dbNetlistExtractorTests.cc | 97 +-- src/db/unit_tests/unit_tests.pro | 3 +- testdata/ruby/dbNetlist.rb | 5 + 9 files changed, 746 insertions(+), 105 deletions(-) create mode 100644 src/db/unit_tests/dbNetlistDeviceClassesTests.cc diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index c48ce4b40..3c378a4b0 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -380,6 +380,12 @@ Net::Net () // .. nothing yet .. } +Net::Net (const std::string &name) + : m_cluster_id (0), mp_circuit (0) +{ + m_name = name; +} + Net::Net (const Net &other) : m_cluster_id (0), mp_circuit (0) { @@ -861,7 +867,7 @@ void Circuit::purge_nets () */ static void check_device_before_remove (db::Circuit *c, const db::Device *d) { - if (d->device_class () != 0) { + if (d->device_class () == 0) { throw tl::Exception (tl::to_string (tr ("Internal error: No device class after removing device in device combination")) + ": name=" + d->name () + ", circuit=" + c->name ()); } const std::vector &pd = d->device_class ()->terminal_definitions (); @@ -872,11 +878,13 @@ static void check_device_before_remove (db::Circuit *c, const db::Device *d) } } -void Circuit::combine_parallel_devices (const db::DeviceClass &cls) +bool Circuit::combine_parallel_devices (const db::DeviceClass &cls) { typedef std::vector key_type; std::map > combination_candidates; + bool any = false; + // identify the candidates for combination - all devices sharing the same nets // are candidates for combination in parallel mode for (device_iterator d = begin_devices (); d != end_devices (); ++d) { @@ -904,12 +912,13 @@ void Circuit::combine_parallel_devices (const db::DeviceClass &cls) for (std::map >::iterator cc = combination_candidates.begin (); cc != combination_candidates.end (); ++cc) { std::vector &cl = cc->second; - for (size_t i = 0; i != cl.size () - 1; ++i) { - for (size_t j = i + 1; j != cl.size (); ) { + for (size_t i = 0; i < cl.size () - 1; ++i) { + for (size_t j = i + 1; j < cl.size (); ) { if (cls.combine_devices (cl [i], cl [j])) { check_device_before_remove (this, cl [j]); // sanity check delete cl [j]; cl.erase (cl.begin () + j); + any = true; } else { ++j; } @@ -917,6 +926,8 @@ void Circuit::combine_parallel_devices (const db::DeviceClass &cls) } } + + return any; } static std::pair attached_two_devices (db::Net &net, const db::DeviceClass &cls) @@ -955,8 +966,10 @@ static bool same_or_swapped (const std::pair &p1, const std::pair &p return (p1.first == p2.first && p1.second == p2.second) || (p1.first == p2.second && p1.second == p2.first); } -void Circuit::combine_serial_devices (const db::DeviceClass &cls) +bool Circuit::combine_serial_devices(const db::DeviceClass &cls) { + bool any = false; + for (net_iterator n = begin_nets (); n != end_nets (); ++n) { std::pair dd = attached_two_devices (*n, cls); @@ -993,11 +1006,14 @@ void Circuit::combine_serial_devices (const db::DeviceClass &cls) if (cls.combine_devices (dd.first, dd.second)) { check_device_before_remove (this, dd.second); // sanity check delete dd.second; + any = true; } } } + + return any; } void Circuit::combine_devices () @@ -1005,12 +1021,27 @@ void Circuit::combine_devices () tl_assert (netlist () != 0); for (Netlist::device_class_iterator dc = netlist ()->begin_device_classes (); dc != netlist ()->end_device_classes (); ++dc) { - if (dc->supports_parallel_combination ()) { - combine_parallel_devices (*dc); - } - if (dc->supports_serial_combination ()) { - combine_serial_devices (*dc); + + // repeat the combination step unless no combination happens - this is required to take care of combinations that arise after + // other combinations have been realized. + bool any = true; + while (any) { + + any = false; + + if (dc->supports_parallel_combination ()) { + if (combine_parallel_devices (*dc)) { + any = true; + } + } + if (dc->supports_serial_combination ()) { + if (combine_serial_devices (*dc)) { + any = true; + } + } + } + } } @@ -1500,4 +1531,97 @@ void Netlist::combine_devices () } } +static std::string net2string (const db::Net *net) +{ + return net ? tl::to_word_or_quoted_string (net->expanded_name ()) : "(null)"; +} + +static std::string device2string (const db::Device &device) +{ + if (device.name ().empty ()) { + return "$" + tl::to_string (device.id ()); + } else { + return tl::to_word_or_quoted_string (device.name ()); + } +} + +static std::string subcircuit2string (const db::SubCircuit &subcircuit) +{ + if (subcircuit.name ().empty ()) { + return "$" + tl::to_string (subcircuit.id ()); + } else { + return tl::to_word_or_quoted_string (subcircuit.name ()); + } +} + +static std::string pin2string (const db::Pin &pin) +{ + if (pin.name ().empty ()) { + // the pin ID is zero-based and essentially the index, so we add 1 to make it compliant with the other IDs + return "$" + tl::to_string (pin.id () + 1); + } else { + return tl::to_word_or_quoted_string (pin.name ()); + } +} + +std::string Netlist::to_string () const +{ + std::string res; + for (db::Netlist::const_circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { + + std::string ps; + for (db::Circuit::const_pin_iterator p = c->begin_pins (); p != c->end_pins (); ++p) { + if (! ps.empty ()) { + ps += ","; + } + ps += pin2string (*p) + "=" + net2string (c->net_for_pin (p->id ())); + } + + res += std::string ("Circuit ") + c->name () + " (" + ps + "):\n"; + +#if 0 // for debugging + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + res += " N" + net_name (n.operator-> ()) + " pins=" + tl::to_string (n->pin_count ()) + " terminals=" + tl::to_string (n->terminal_count ()) + "\n"; + } +#endif + + for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) { + std::string ts; + const std::vector &td = d->device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + if (t != td.begin ()) { + ts += ","; + } + ts += t->name () + "=" + net2string (d->net_for_terminal (t->id ())); + } + std::string ps; + const std::vector &pd = d->device_class ()->parameter_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (p != pd.begin ()) { + ps += ","; + } + ps += p->name () + "=" + tl::to_string (d->parameter_value (p->id ())); + } + res += std::string (" D") + d->device_class ()->name () + " " + device2string (*d) + " (" + ts + ") [" + ps + "]\n"; + } + + for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) { + std::string ps; + const db::SubCircuit &subcircuit = *sc; + const db::Circuit *circuit = sc->circuit_ref (); + for (db::Circuit::const_pin_iterator p = circuit->begin_pins (); p != circuit->end_pins (); ++p) { + if (p != circuit->begin_pins ()) { + ps += ","; + } + const db::Pin &pin = *p; + ps += pin2string (pin) + "=" + net2string (subcircuit.net_for_pin (pin.id ())); + } + res += std::string (" X") + circuit->name () + " " + subcircuit2string (*sc) + " (" + ps + ")\n"; + } + + } + + return res; +} + } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index da817218b..6079bc9a7 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -301,6 +301,11 @@ public: */ Net (); + /** + * @brief Creates a empty net with the give name + */ + Net (const std::string &name); + /** * @brief Copy constructor */ @@ -1374,8 +1379,8 @@ private: void translate_circuits (const std::map &map); void translate_device_classes (const std::map &map); void set_netlist (Netlist *netlist); - void combine_parallel_devices (const db::DeviceClass &cls); - void combine_serial_devices (const db::DeviceClass &cls); + bool combine_parallel_devices (const db::DeviceClass &cls); + bool combine_serial_devices (const db::DeviceClass &cls); void validate_device_id_table (); void invalidate_device_id_table (); @@ -1806,6 +1811,13 @@ public: */ void clear (); + /** + * @brief Returns a string representation of the netlist + * + * This method is basically intended to testing. + */ + std::string to_string () const; + /** * @brief Starts a sequence of operations during which topology updates are not desired * diff --git a/src/db/db/dbNetlistDeviceClasses.cc b/src/db/db/dbNetlistDeviceClasses.cc index f97cca187..29213cd8a 100644 --- a/src/db/db/dbNetlistDeviceClasses.cc +++ b/src/db/db/dbNetlistDeviceClasses.cc @@ -68,6 +68,11 @@ bool DeviceClassTwoTerminalDevice::combine_devices (Device *a, Device *b) const // ------------------------------------------------------------------------------------ // DeviceClassResistor implementation +DB_PUBLIC size_t DeviceClassResistor::param_id_R = 0; + +DB_PUBLIC size_t DeviceClassResistor::terminal_id_A = 0; +DB_PUBLIC size_t DeviceClassResistor::terminal_id_B = 1; + DeviceClassResistor::DeviceClassResistor () { add_terminal_definition (db::DeviceTerminalDefinition ("A", "Terminal A")); diff --git a/src/db/db/dbNetlistDeviceClasses.h b/src/db/db/dbNetlistDeviceClasses.h index 949c6b6b0..b40b1a985 100644 --- a/src/db/db/dbNetlistDeviceClasses.h +++ b/src/db/db/dbNetlistDeviceClasses.h @@ -60,6 +60,11 @@ public: return new DeviceClassResistor (*this); } + static size_t param_id_R; + + static size_t terminal_id_A; + static size_t terminal_id_B; + virtual void parallel (Device *a, Device *b) const; virtual void serial (Device *a, Device *b) const; }; diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index ea9b8c71c..51e6180dc 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -105,6 +105,8 @@ Class decl_dbDevice ("db", "Device", "Devices connect to nets through the \\Device#connect_terminal method. " "Device terminals can be disconnected using \\Device#disconnect_terminal.\n" "\n" + "Device objects are created inside a circuit with \\Circuit#create_device.\n" + "\n" "This class has been added in version 0.26." ); @@ -177,6 +179,8 @@ Class decl_dbSubCircuit ("db", "SubCircuit", "Subcircuits connect to nets through the \\SubCircuit#connect_pin method. " "SubCircuit pins can be disconnected using \\SubCircuit#disconnect_pin.\n" "\n" + "Subcircuit objects are created inside a circuit with \\Circuit#create_subcircuit.\n" + "\n" "This class has been added in version 0.26." ); @@ -286,6 +290,8 @@ Class decl_dbNet ("db", "Net", "pin or subcircuits of outgoing pins of the circuit the net lives in. " "Terminals are connections made to specific terminals of devices.\n" "\n" + "Net objects are created inside a circuit with \\Circuit#create_net.\n" + "\n" "To connect a net to an outgoing pin of a circuit, use \\Circuit#connect_pin, to " "disconnect a net from an outgoing pin use \\Circuit#disconnect_pin. " "To connect a net to a pin of a subcircuit, use \\SubCircuit#connect_pin, to " @@ -840,6 +846,10 @@ Class decl_dbNetlist ("db", "Netlist", gsi::iterator ("each_device_class", (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::begin_device_classes, (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::end_device_classes, "@brief Iterates over the device classes of the netlist" ) + + gsi::method ("to_s", &db::Netlist::to_string, + "@brief Converts the netlist to a string representation.\n" + "This method is intended for test purposes mainly." + ) + gsi::method ("combine_devices", &db::Netlist::combine_devices, "@brief Combines devices where possible\n" "This method will combine devices that can be combined according " @@ -847,6 +857,17 @@ Class decl_dbNetlist ("db", "Netlist", "For example, serial or parallel resistors can be combined into " "a single resistor.\n" ) + + gsi::method ("make_top_level_pins", &db::Netlist::make_top_level_pins, + "@brief Creates pins for top-level circuits.\n" + "This method will turn all named nets of top-level circuits (such that are not " + "referenced by subcircuits) into pins. This method can be used before purge to " + "avoid that purge will remove nets which are directly connecting to subcircuits." + ) + + gsi::method ("purge", &db::Netlist::purge, + "@brief Purge unused nets, circuits and subcircuits.\n" + "This method will purge all nets which return \\floating == true. Circuits which don't have any " + "nets (or only floating ones) and removed. Their subcircuits are disconnected." + ) + gsi::method ("purge_nets", &db::Netlist::purge_nets, "@brief Purges floating nets.\n" "Floating nets can be created as effect of reconnections of devices or pins. " diff --git a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc new file mode 100644 index 000000000..9670944b8 --- /dev/null +++ b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc @@ -0,0 +1,555 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2018 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "dbNetlistDeviceClasses.h" +#include "dbNetlist.h" +#include "tlUnitTest.h" + +#include +#include + +TEST(1_SerialResistors) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n1,B=n2) [R=1]\n" + " D r2 (A=n2,B=n3) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n1,B=n3) [R=4]\n" + ); +}; + +TEST(1_SerialResistors1Swapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n1,B=n2) [R=1]\n" + " D r2 (A=n3,B=n2) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n1,B=n3) [R=4]\n" + ); +}; + +TEST(1_SerialResistors1OtherSwapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n2,B=n1) [R=1]\n" + " D r2 (A=n2,B=n3) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n3,B=n1) [R=4]\n" + ); +}; + +TEST(1_SerialResistors2Swapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n2,B=n1) [R=1]\n" + " D r2 (A=n3,B=n2) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3):\n" + " D r1 (A=n3,B=n1) [R=4]\n" + ); +}; + +TEST(1_SerialResistorsNoCombination) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + db::Pin pin_c = circuit->add_pin (db::Pin ("C")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_c.id (), n2); // prevents combination + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + circuit->connect_pin (pin_b.id (), n3); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3,C=n2):\n" + " D r1 (A=n1,B=n2) [R=1]\n" + " D r2 (A=n2,B=n3) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n3,C=n2):\n" + " D r1 (A=n1,B=n2) [R=1]\n" + " D r2 (A=n2,B=n3) [R=3]\n" + ); +}; + +TEST(1_ParallelResistors) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 2.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n1,B=n2) [R=2]\n" + " D r2 (A=n1,B=n2) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n1,B=n2) [R=1.2]\n" + ); +}; + +TEST(1_ParallelResistors1Swapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 2.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n2,B=n1) [R=2]\n" + " D r2 (A=n1,B=n2) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n2,B=n1) [R=1.2]\n" + ); +}; + +TEST(1_ParallelResistors1OtherSwapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 2.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n1,B=n2) [R=2]\n" + " D r2 (A=n2,B=n1) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n1,B=n2) [R=1.2]\n" + ); +}; + +TEST(1_ParallelResistors2Swapped) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 2.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + circuit->connect_pin (pin_b.id (), n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n2,B=n1) [R=2]\n" + " D r2 (A=n2,B=n1) [R=3]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n2):\n" + " D r1 (A=n2,B=n1) [R=1.2]\n" + ); +}; + +TEST(1_ComplexRegistorCombination) +{ + db::DeviceClassResistor *res = new db::DeviceClassResistor (); + + db::Netlist nl; + nl.add_device_class (res); + + /** + * (n2) + * +--[ r1=1.0 ]--+--[ r2=1.0 ]--+ + * | | + * --x (n1) (n3) x--[ r4=0.8 ]--+-- + * | | (n4) + * +----------[ r3=3.0 ]---------+ + */ + + db::Device *r1 = new db::Device (res, "r1"); + r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r2 = new db::Device (res, "r2"); + r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0); + db::Device *r3 = new db::Device (res, "r3"); + r3->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0); + db::Device *r4 = new db::Device (res, "r4"); + r4->set_parameter_value (db::DeviceClassResistor::param_id_R, 0.8); + + db::Circuit *circuit = new db::Circuit (); + nl.add_circuit (circuit); + + db::Pin pin_a = circuit->add_pin (db::Pin ("A")); + db::Pin pin_b = circuit->add_pin (db::Pin ("B")); + + circuit->add_device (r1); + circuit->add_device (r2); + circuit->add_device (r3); + circuit->add_device (r4); + + db::Net *n1 = new db::Net ("n1"); + circuit->add_net (n1); + circuit->connect_pin (pin_a.id (), n1); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + r3->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1); + + db::Net *n2 = new db::Net ("n2"); + circuit->add_net (n2); + r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); + + db::Net *n3 = new db::Net ("n3"); + circuit->add_net (n3); + r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + r3->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); + r4->connect_terminal (db::DeviceClassResistor::terminal_id_A, n3); + + db::Net *n4 = new db::Net ("n4"); + circuit->add_net (n4); + circuit->connect_pin (pin_b.id (), n4); + r4->connect_terminal (db::DeviceClassResistor::terminal_id_B, n4); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n4):\n" + " D r1 (A=n1,B=n2) [R=1]\n" + " D r2 (A=n2,B=n3) [R=1]\n" + " D r3 (A=n1,B=n3) [R=3]\n" + " D r4 (A=n3,B=n4) [R=0.8]\n" + ); + + nl.combine_devices (); + nl.purge (); + + EXPECT_EQ (nl.to_string (), + "Circuit (A=n1,B=n4):\n" + " D r4 (A=n1,B=n4) [R=2]\n" + ); +}; + diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index c148408a3..52dd66406 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -49,11 +49,6 @@ static unsigned int layer_of (const db::Region ®ion) return db::DeepLayer (region).layer (); } -static std::string net_name (const db::Net *net) -{ - return net ? net->expanded_name () : "(null)"; -} - static std::string device_name (const db::Device &device) { if (device.name ().empty ()) { @@ -63,26 +58,6 @@ static std::string device_name (const db::Device &device) } } -static std::string subcircuit_name (const db::SubCircuit &subcircuit) -{ - if (subcircuit.name ().empty ()) { - return "$" + tl::to_string (subcircuit.id ()); - } else { - return subcircuit.name (); - } -} - -static std::string pin_name (const db::Pin &pin) -{ - if (pin.name ().empty ()) { - // the pin ID is zero-based and essentially the index, so we add 1 to make it compliant with the other IDs - return "$" + tl::to_string (pin.id () + 1); - } else { - return pin.name (); - } -} - - class MOSFETExtractor : public db::NetlistDeviceExtractor { @@ -229,9 +204,7 @@ static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_la return lid; } -// @@@ TODO: move this somewhere else - -static void dump_nets (const db::Netlist &nl, const db::hier_clusters &clusters, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) +static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters &clusters, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) { for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) { @@ -248,7 +221,7 @@ static void dump_nets (const db::Netlist &nl, const db::hier_clustersname () + "_" + net_name (n.operator-> ()); + std::string nn = "NET_" + c->name () + "_" + n->expanded_name (); db::Cell &net_cell = ly.cell (ly.add_cell (nn.c_str ())); cell.insert (db::CellInstArray (db::CellInst (net_cell.cell_index ()), db::Trans ())); @@ -266,66 +239,6 @@ static void dump_nets (const db::Netlist &nl, const db::hier_clustersbegin_pins (); p != c->end_pins (); ++p) { - if (! ps.empty ()) { - ps += ","; - } - ps += pin_name (*p) + "=" + net_name (c->net_for_pin (p->id ())); - } - - res += std::string ("Circuit ") + c->name () + " (" + ps + "):\n"; - -#if 0 // for debugging - for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - res += " N" + net_name (n.operator-> ()) + " pins=" + tl::to_string (n->pin_count ()) + " terminals=" + tl::to_string (n->terminal_count ()) + "\n"; - } -#endif - - for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) { - std::string ts; - const std::vector &td = d->device_class ()->terminal_definitions (); - for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { - if (t != td.begin ()) { - ts += ","; - } - ts += t->name () + "=" + net_name (d->net_for_terminal (t->id ())); - } - std::string ps; - const std::vector &pd = d->device_class ()->parameter_definitions (); - for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { - if (p != pd.begin ()) { - ps += ","; - } - ps += p->name () + "=" + tl::to_string (d->parameter_value (p->id ())); - } - res += std::string (" D") + d->device_class ()->name () + " " + device_name (*d) + " (" + ts + ") [" + ps + "]\n"; - } - - for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) { - std::string ps; - const db::SubCircuit &subcircuit = *sc; - const db::Circuit *circuit = sc->circuit_ref (); - for (db::Circuit::const_pin_iterator p = circuit->begin_pins (); p != circuit->end_pins (); ++p) { - if (p != circuit->begin_pins ()) { - ps += ","; - } - const db::Pin &pin = *p; - ps += pin_name (pin) + "=" + net_name (subcircuit.net_for_pin (pin.id ())); - } - res += std::string (" X") + circuit->name () + " " + subcircuit_name (*sc) + " (" + ps + ")\n"; - } - - } - - return res; -} - TEST(1_DeviceAndNetExtraction) { db::Layout ly; @@ -475,10 +388,10 @@ TEST(1_DeviceAndNetExtraction) // write nets to layout db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ()); - dump_nets (nl, net_ex.clusters (), ly, dump_map, cm); + dump_nets_to_layout (nl, net_ex.clusters (), ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (netlist2string (nl), + EXPECT_EQ (nl.to_string (), "Circuit RINGO ():\n" " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n" " XINV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD)\n" @@ -510,7 +423,7 @@ TEST(1_DeviceAndNetExtraction) nl.purge (); // compare netlist as string - EXPECT_EQ (netlist2string (nl), + EXPECT_EQ (nl.to_string (), "Circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD):\n" " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n" " XINV2 $2 (IN=FB,$2=(null),OUT=$I19,$4=VSS,$5=VDD)\n" diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index a6f455bac..693acf642 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -62,7 +62,8 @@ SOURCES = \ dbNetlistPropertyTests.cc \ dbNetlistTests.cc \ dbNetlistExtractorTests.cc \ - dbNetlistDeviceExtractorTests.cc + dbNetlistDeviceExtractorTests.cc \ + dbNetlistDeviceClassesTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 3c42392f3..9f94ff9ad 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -561,6 +561,11 @@ class DBNetlist_TestClass < TestBase assert_equal(d1.parameter(0), -0.5) assert_equal(d1.parameter(1), 42) + assert_equal(nl.to_s, <