From e361a528a9ddb0fb31d67c01f9dd02edb1ad7cfc Mon Sep 17 00:00:00 2001 From: matthias Date: Wed, 13 Mar 2019 16:14:27 +0100 Subject: [PATCH 01/54] DRC layer's flatten now returns a proper DRCLayer object, enabled flat layers to become input also for deep mode L2N --- src/db/db/dbLayoutToNetlist.cc | 2 +- src/drc/drc/built-in-macros/drc.lym | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index a0be5cfcd..6b610fefa 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -323,7 +323,7 @@ void LayoutToNetlist::register_layer (const db::Region ®ion, const std::strin if (region.empty ()) { dl = dss ().empty_layer (m_layout_index); } else { - throw tl::Exception (tl::to_string (tr ("Layer is not a deep region and cannot be registered with name: ")) + n); + dl = dss ().create_from_flat (region, true); } } else { diff --git a/src/drc/drc/built-in-macros/drc.lym b/src/drc/drc/built-in-macros/drc.lym index 9de7b23dd..94627bab0 100644 --- a/src/drc/drc/built-in-macros/drc.lym +++ b/src/drc/drc/built-in-macros/drc.lym @@ -2367,7 +2367,7 @@ CODE # to a flat collection of polygons, edges or edge pairs. def flatten - @engine._cmd(@data, :flatten) + DRC::DRCLayer::new(@engine, @engine._cmd(@data, :flatten)) end # %DRC% From e4078ca750296f0da173cd2123bc26ced5f4c211 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 18 Mar 2019 02:00:33 +0100 Subject: [PATCH 02/54] String serialization for netlists. --- src/db/db/dbCircuit.cc | 5 + src/db/db/dbCircuit.h | 10 +- src/db/db/dbDeviceClass.cc | 1 + src/db/db/dbNetlist.cc | 369 +++++++++++++++++++ src/db/db/dbNetlist.h | 15 + src/db/unit_tests/dbNetlistExtractorTests.cc | 8 + src/db/unit_tests/dbNetlistTests.cc | 21 ++ 7 files changed, 426 insertions(+), 3 deletions(-) diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index d1d193158..a8175d2dc 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -246,6 +246,11 @@ Circuit::const_child_circuit_iterator Circuit::end_parents () const return reinterpret_cast &> (mp_netlist->parent_circuits (const_cast (this))).end (); } +void Circuit::clear_pins () +{ + m_pins.clear (); +} + const Pin &Circuit::add_pin (const std::string &name) { m_pins.push_back (Pin (name)); diff --git a/src/db/db/dbCircuit.h b/src/db/db/dbCircuit.h index 276ac520b..9f0b362b7 100644 --- a/src/db/db/dbCircuit.h +++ b/src/db/db/dbCircuit.h @@ -235,10 +235,14 @@ public: } /** - * @brief Adds a pin to this circuit - * The circuit takes over ownership of the object. + * @brief Clears the pins */ - const Pin &add_pin(const std::string &name); + void clear_pins (); + + /** + * @brief Adds a pin to this circuit + */ + const Pin &add_pin (const std::string &name); /** * @brief Begin iterator for the pins of the circuit (non-const version) diff --git a/src/db/db/dbDeviceClass.cc b/src/db/db/dbDeviceClass.cc index e21ff80ea..bc0c2864b 100644 --- a/src/db/db/dbDeviceClass.cc +++ b/src/db/db/dbDeviceClass.cc @@ -44,6 +44,7 @@ DeviceClass &DeviceClass::operator= (const DeviceClass &other) { if (this != &other) { m_terminal_definitions = other.m_terminal_definitions; + m_parameter_definitions = other.m_parameter_definitions; m_name = other.m_name; m_description = other.m_description; } diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 6cc04ea90..198364bbf 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -546,4 +546,373 @@ std::string Netlist::to_string () const return res; } +std::string Netlist::to_parsable_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"; + + 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 (" device ") + tl::to_word_or_quoted_string (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 += ", "; + } + ps += net2string (subcircuit.net_for_pin (p->id ())); + } + res += std::string (" subcircuit ") + tl::to_word_or_quoted_string (circuit->name ()) + " " + subcircuit2string (*sc) + " (" + ps + ");\n"; + } + + res += std::string ("end;\n"); + + } + + return res; +} + +static db::Net *read_net (tl::Extractor &ex, db::Circuit *circuit, std::map &n2n) +{ + std::string nn; + bool has_name = false; + size_t cluster_id = 0; + + if (ex.test ("(")) { + + ex.expect ("null"); + ex.expect (")"); + + return 0; + + } else if (ex.test ("$")) { + + bool has_i = ex.test ("I"); + ex.read (cluster_id); + + nn = (has_i ? "$I" : "$") + tl::to_string (cluster_id); + + if (has_i) { + cluster_id = (std::numeric_limits::max () - cluster_id) + 1; + } + + } else { + + ex.read_word_or_quoted (nn); + + has_name = true; + + } + + std::map::const_iterator i = n2n.find (nn); + if (i == n2n.end ()) { + + db::Net *net = new db::Net (); + circuit->add_net (net); + if (has_name) { + net->set_name (nn); + } else { + net->set_cluster_id (cluster_id); + } + + n2n.insert (std::make_pair (nn, net)); + return net; + + } else { + + return i->second; + + } +} + +static void read_pins (tl::Extractor &ex, db::Circuit *circuit, std::map &n2n) +{ + size_t npins = circuit->pin_count (); + + circuit->clear_pins (); + + ex.expect ("("); + while (! ex.test (")")) { + + ex.expect_more (); + + std::string pn; + if (ex.test ("$")) { + size_t i; + ex.read (i); + } else { + ex.read_word_or_quoted (pn); + } + + ex.expect ("="); + + db::Net *net = read_net (ex, circuit, n2n); + + const db::Pin &pin = circuit->add_pin (pn); + if (net) { + net->add_pin (db::NetPinRef (pin.id ())); + } + + ex.test (","); + + } + + if (circuit->pin_count () < npins) { + ex.error (tl::to_string (tr ("Circuit defines less pins that subcircuit"))); + } +} + +static void read_device_terminals (tl::Extractor &ex, db::Device *device, std::map &n2n) +{ + ex.expect ("("); + while (! ex.test (")")) { + + ex.expect_more (); + + std::string tn; + ex.read_word_or_quoted (tn); + + size_t tid = std::numeric_limits::max (); + const std::vector &td = device->device_class ()->terminal_definitions (); + for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { + if (i->name () == tn) { + tid = i->id (); + break; + } + } + + if (tid == std::numeric_limits::max ()) { + ex.error (tl::to_string (tr ("Not a valid terminal name: ")) + tn); + } + + ex.expect ("="); + + db::Net *net = read_net (ex, device->circuit (), n2n); + if (net) { + device->connect_terminal (tid, net); + } + + ex.test (","); + + } +} + +static void read_device_parameters (tl::Extractor &ex, db::Device *device) +{ + ex.expect ("("); + while (! ex.test (")")) { + + ex.expect_more (); + + std::string pn; + ex.read_word_or_quoted (pn); + + size_t pid = std::numeric_limits::max (); + const std::vector &pd = device->device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + if (i->name () == pn) { + pid = i->id (); + break; + } + } + + if (pid == std::numeric_limits::max ()) { + ex.error (tl::to_string (tr ("Not a valid parameter name: ")) + pn); + } + + ex.expect ("="); + + double value = 0; + ex.read (value); + device->set_parameter_value (pid, value); + + ex.test (","); + + } +} + +static void read_device (tl::Extractor &ex, db::Circuit *circuit, std::map &n2n) +{ + db::Netlist *netlist = circuit->netlist (); + + std::string dcn; + ex.read_word_or_quoted (dcn); + db::DeviceClass *dc = 0; + for (db::Netlist::device_class_iterator i = netlist->begin_device_classes (); i != netlist->end_device_classes (); ++i) { + if (i->name () == dcn) { + dc = i.operator-> (); + } + } + if (! dc) { + ex.error (tl::to_string (tr ("Not a valid device class name: ")) + dcn); + } + + std::string dn; + if (ex.test ("$")) { + size_t i; + ex.read (i); + } else { + ex.read_word_or_quoted (dn); + } + + db::Device *device = new db::Device (dc, dn); + circuit->add_device (device); + + read_device_terminals (ex, device, n2n); + read_device_parameters (ex, device); +} + +static void read_subcircuit_pins (tl::Extractor &ex, db::SubCircuit *subcircuit, std::map &n2n) +{ + db::Circuit *circuit = subcircuit->circuit_ref (); + db::Circuit::pin_iterator pi = circuit->begin_pins (); + + ex.expect ("("); + while (! ex.test (")")) { + + if (pi == circuit->end_pins ()) { + // add a dummy pin + circuit->add_pin (std::string ()); + pi = circuit->end_pins (); + --pi; + } + + ex.expect_more (); + + db::Net *net = read_net (ex, circuit, n2n); + if (net) { + subcircuit->connect_pin (pi->id (), net); + } + + ex.test (","); + + ++pi; + + } + + if (pi != circuit->end_pins ()) { + // @@@ ex.error (tl::to_string (tr ("Too few pins in subcircuit call"))); + } +} + +static void read_subcircuit (tl::Extractor &ex, db::Circuit *circuit, std::map &n2n, std::map &c2n) +{ + std::string cn; + ex.read_word_or_quoted (cn); + + db::Circuit *cc = 0; + std::map::const_iterator ic = c2n.find (cn); + if (ic == c2n.end ()) { + + cc = new db::Circuit (); + circuit->netlist ()->add_circuit (cc); + cc->set_name (cn); + + c2n.insert (std::make_pair (cn, cc)); + + } else { + cc = ic->second; + } + + std::string scn; + if (ex.test ("$")) { + size_t i; + ex.read (i); + } else { + ex.read_word_or_quoted (scn); + } + + db::SubCircuit *subcircuit = new db::SubCircuit (cc, scn); + circuit->add_subcircuit (subcircuit); + + read_subcircuit_pins (ex, subcircuit, n2n); +} + +void Netlist::from_string (const std::string &s) +{ + tl::Extractor ex (s.c_str ()); + + std::map c2n; + + while (ex.test ("circuit")) { + + std::string n; + ex.read_word_or_quoted (n); + + db::Circuit *circuit = 0; + + std::map::const_iterator ic = c2n.find (n); + if (ic == c2n.end ()) { + + circuit = new db::Circuit (); + add_circuit (circuit); + circuit->set_name (n); + + c2n.insert (std::make_pair (n, circuit)); + + } else { + circuit = ic->second; + } + + std::map n2n; + read_pins (ex, circuit, n2n); + + ex.expect (";"); + + while (! ex.test ("end")) { + + ex.expect_more (); + + if (ex.test ("device")) { + + read_device (ex, circuit, n2n); + ex.expect (";"); + + } else if (ex.test ("subcircuit")) { + + read_subcircuit (ex, circuit, n2n, c2n); + ex.expect (";"); + + } else { + ex.error (tl::to_string (tr ("device or subcircuit expected"))); + } + + } + + ex.expect (";"); + + } + + ex.expect_end (); +} + } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 86bfd9298..fab9c79f9 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -94,6 +94,21 @@ public: */ std::string to_string () const; + /** + * @brief Returns a parsable string representation of the netlist + * + * This method returns a string suitable for being put into from_string. + */ + std::string to_parsable_string () const; + + /** + * @brief Reads a netlist from the string generated by to_parsable_string + * + * The device classes have to be installed so it's possible to identify the devices + * by their class. + */ + void from_string (const std::string &s); + /** * @brief Starts a sequence of operations during which topology updates are not desired * diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 4713081aa..9081ea214 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -304,6 +304,14 @@ TEST(1_DeviceAndNetExtraction) "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" ); + // use this opportunity to test serialization to and from string + db::Netlist nldup; + for (db::Netlist::device_class_iterator i = nl.begin_device_classes (); i != nl.end_device_classes (); ++i) { + nldup.add_device_class (i->clone ()); + } + nldup.from_string (nl.to_parsable_string ()); + EXPECT_EQ (nldup.to_string (), nl.to_string ()); + // doesn't do anything here, but we test that this does not destroy anything: nl.combine_devices (); diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 4d85928cd..11e54ea09 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -501,6 +501,9 @@ TEST(4_NetlistSubcircuits) dc->add_terminal_definition (db::DeviceTerminalDefinition ("B", "")); nl->add_device_class (dc); + std::auto_ptr nldup (new db::Netlist ()); + nldup->add_device_class (dc->clone ()); + db::DeviceAbstract *dm = new db::DeviceAbstract (); dm->set_device_class (dc); EXPECT_EQ (dm->device_class () == dc, true); @@ -609,6 +612,24 @@ TEST(4_NetlistSubcircuits) "D:B,+c2p2\n" ); + EXPECT_EQ (nl->to_string (), + "Circuit c1 (c1p1=n1a,c1p2=n1c):\n" + " Xc2 sc1 (c2p1=n1a,c2p2=n1b)\n" + " Xc2 sc2 (c2p1=n1b,c2p2=n1c)\n" + "Circuit c2 (c2p1=n2a,c2p2=n2b):\n" + " Ddc2 D (A=n2a,B=n2b) []\n" + ); + + nldup->from_string (nl->to_parsable_string ()); + + EXPECT_EQ (nldup->to_string (), + "Circuit c1 (c1p1=n1a,c1p2=n1c):\n" + " Xc2 sc1 (c2p1=n1a,c2p2=n1b)\n" + " Xc2 sc2 (c2p1=n1b,c2p2=n1c)\n" + "Circuit c2 (c2p1=n2a,c2p2=n2b):\n" + " Ddc2 D (A=n2a,B=n2b) []\n" + ); + EXPECT_EQ (netlist2 (*nl), "c1:c1p1=n1a,c1p2=n1c\n" " Xsc1:c2p1=n1a,c2p2=n1b\n" From d7eb9162ce26fbdc691d1c12fa5e1f32bd52a329 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 18 Mar 2019 19:28:20 +0100 Subject: [PATCH 03/54] WIP: unified to_string/to_parsable_string of db::Netlist, step 1 --- src/db/db/dbNetlist.cc | 21 +- src/db/db/dbNetlist.h | 2 +- src/db/db/gsiDeclDbNetlist.cc | 2 +- src/db/unit_tests/dbLayoutToNetlistTests.cc | 455 ++++++++++-------- .../unit_tests/dbNetlistDeviceClassesTests.cc | 436 +++++++++-------- src/db/unit_tests/dbNetlistExtractorTests.cc | 253 +++++----- src/db/unit_tests/dbNetlistTests.cc | 28 +- 7 files changed, 654 insertions(+), 543 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 198364bbf..937128f0b 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -486,7 +486,7 @@ static std::string pin2string (const db::Pin &pin) } } -std::string Netlist::to_string () const +std::string Netlist::to_string_old () const { std::string res; for (db::Netlist::const_circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { @@ -559,14 +559,14 @@ std::string Netlist::to_parsable_string () const ps += pin2string (*p) + "=" + net2string (c->net_for_pin (p->id ())); } - res += std::string ("circuit ") + c->name () + " (" + ps + ");\n"; + res += std::string ("circuit ") + tl::to_word_or_quoted_string (c->name ()) + " (" + ps + ");\n"; 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 += ","; } ts += t->name () + "=" + net2string (d->net_for_terminal (t->id ())); } @@ -587,9 +587,9 @@ std::string Netlist::to_parsable_string () const 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 += ", "; + ps += ","; } - ps += net2string (subcircuit.net_for_pin (p->id ())); + ps += pin2string (*p) + "=" + net2string (subcircuit.net_for_pin (p->id ())); } res += std::string (" subcircuit ") + tl::to_word_or_quoted_string (circuit->name ()) + " " + subcircuit2string (*sc) + " (" + ps + ");\n"; } @@ -799,11 +799,18 @@ static void read_subcircuit_pins (tl::Extractor &ex, db::SubCircuit *subcircuit, ex.expect ("("); while (! ex.test (")")) { + std::string pn; + ex.read_word_or_quoted (pn); + + ex.expect ("="); + if (pi == circuit->end_pins ()) { // add a dummy pin - circuit->add_pin (std::string ()); + circuit->add_pin (pn); pi = circuit->end_pins (); --pi; + } else if (pi->name () != pn) { + ex.error (tl::to_string (tr ("Expected pin with name: ")) + pi->name ()); } ex.expect_more (); @@ -820,7 +827,7 @@ static void read_subcircuit_pins (tl::Extractor &ex, db::SubCircuit *subcircuit, } if (pi != circuit->end_pins ()) { - // @@@ ex.error (tl::to_string (tr ("Too few pins in subcircuit call"))); + ex.error (tl::to_string (tr ("Too few pins in subcircuit call"))); } } diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index fab9c79f9..b563aa493 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -92,7 +92,7 @@ public: * * This method is basically intended to testing. */ - std::string to_string () const; + std::string to_string_old () const; /** * @brief Returns a parsable string representation of the netlist diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 0b5b4d0d8..9c8e0ac9f 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -972,7 +972,7 @@ 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, + gsi::method ("to_s", &db::Netlist::to_parsable_string, "@brief Converts the netlist to a string representation.\n" "This method is intended for test purposes mainly." ) + diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 730d31ae0..54ce41d6a 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -337,28 +337,31 @@ TEST(1_BasicExtraction) db::compare_layouts (_this, ly, au); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->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" - " XINV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VDD)\n" - " XINV2 $4 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VDD)\n" - " XINV2 $5 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VDD)\n" - " XINV2 $6 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VDD)\n" - " XINV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VDD)\n" - " XINV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VDD)\n" - " XINV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD)\n" - " XINV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD)\n" - "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" - " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" - " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" - " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" - " XTRANS $4 ($1=$4,$2=OUT,$3=$2)\n" - "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + "circuit RINGO ();\n" + " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $4 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $5 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $6 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD);\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " subcircuit TRANS $1 ($1=$2,$2=$4,$3=IN);\n" + " subcircuit TRANS $2 ($1=$2,$2=$5,$3=IN);\n" + " subcircuit TRANS $3 ($1=$5,$2=OUT,$3=$2);\n" + " subcircuit TRANS $4 ($1=$4,$2=OUT,$3=$2);\n" + "end;\n" + "circuit TRANS ($1=$1,$2=$2,$3=$3);\n" + "end;\n" ); // do some probing before purging @@ -492,23 +495,25 @@ TEST(1_BasicExtraction) l2n.netlist ()->purge (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->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" - " XINV2 $3 (IN=$I19,$2=(null),OUT=$I1,$4=VSS,$5=VDD)\n" - " XINV2 $4 (IN=$I1,$2=(null),OUT=$I2,$4=VSS,$5=VDD)\n" - " XINV2 $5 (IN=$I2,$2=(null),OUT=$I3,$4=VSS,$5=VDD)\n" - " XINV2 $6 (IN=$I3,$2=(null),OUT=$I4,$4=VSS,$5=VDD)\n" - " XINV2 $7 (IN=$I4,$2=(null),OUT=$I5,$4=VSS,$5=VDD)\n" - " XINV2 $8 (IN=$I5,$2=(null),OUT=$I6,$4=VSS,$5=VDD)\n" - " XINV2 $9 (IN=$I6,$2=(null),OUT=$I7,$4=VSS,$5=VDD)\n" - " XINV2 $10 (IN=$I7,$2=(null),OUT=$I8,$4=VSS,$5=VDD)\n" - "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" - " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + "circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD);\n" + " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $2 (IN=FB,$2=(null),OUT=$I19,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $3 (IN=$I19,$2=(null),OUT=$I1,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $4 (IN=$I1,$2=(null),OUT=$I2,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $5 (IN=$I2,$2=(null),OUT=$I3,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $6 (IN=$I3,$2=(null),OUT=$I4,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $7 (IN=$I4,$2=(null),OUT=$I5,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $8 (IN=$I5,$2=(null),OUT=$I6,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $9 (IN=$I6,$2=(null),OUT=$I7,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $10 (IN=$I7,$2=(null),OUT=$I8,$4=VSS,$5=VDD);\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + "end;\n" ); // do some probing after purging @@ -684,26 +689,30 @@ TEST(2_Probing) dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), - "Circuit RINGO ():\n" - " XINV2PAIR $1 ($1=FB,$2=VDD,$3=VSS,$4=$I3,$5=OSC)\n" - " XINV2PAIR $2 ($1=$I18,$2=VDD,$3=VSS,$4=FB,$5=$I9)\n" - " XINV2PAIR $3 ($1=$I19,$2=VDD,$3=VSS,$4=$I9,$5=$I1)\n" - " XINV2PAIR $4 ($1=$I20,$2=VDD,$3=VSS,$4=$I1,$5=$I2)\n" - " XINV2PAIR $5 ($1=$I21,$2=VDD,$3=VSS,$4=$I2,$5=$I3)\n" - "Circuit INV2PAIR ($1=$I7,$2=$I5,$3=$I4,$4=$I2,$5=$I1):\n" - " XINV2 $1 (IN=$I3,$2=$I7,OUT=$I1,$4=$I4,$5=$I5)\n" - " XINV2 $2 (IN=$I2,$2=$I6,OUT=$I3,$4=$I4,$5=$I5)\n" - "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" - " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" - " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" - " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" - " XTRANS $4 ($1=$4,$2=OUT,$3=$2)\n" - "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + "circuit RINGO ();\n" + " subcircuit INV2PAIR $1 ($1=FB,$2=VDD,$3=VSS,$4=$I3,$5=OSC);\n" + " subcircuit INV2PAIR $2 ($1=$I18,$2=VDD,$3=VSS,$4=FB,$5=$I9);\n" + " subcircuit INV2PAIR $3 ($1=$I19,$2=VDD,$3=VSS,$4=$I9,$5=$I1);\n" + " subcircuit INV2PAIR $4 ($1=$I20,$2=VDD,$3=VSS,$4=$I1,$5=$I2);\n" + " subcircuit INV2PAIR $5 ($1=$I21,$2=VDD,$3=VSS,$4=$I2,$5=$I3);\n" + "end;\n" + "circuit INV2PAIR ($1=$I7,$2=$I5,$3=$I4,$4=$I2,$5=$I1);\n" + " subcircuit INV2 $1 (IN=$I3,$2=$I7,OUT=$I1,$4=$I4,$5=$I5);\n" + " subcircuit INV2 $2 (IN=$I2,$2=$I6,OUT=$I3,$4=$I4,$5=$I5);\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " subcircuit TRANS $1 ($1=$2,$2=$4,$3=IN);\n" + " subcircuit TRANS $2 ($1=$2,$2=$5,$3=IN);\n" + " subcircuit TRANS $3 ($1=$5,$2=OUT,$3=$2);\n" + " subcircuit TRANS $4 ($1=$4,$2=OUT,$3=$2);\n" + "end;\n" + "circuit TRANS ($1=$1,$2=$2,$3=$3);\n" + "end;\n" ); // compare the collected test data @@ -736,21 +745,24 @@ TEST(2_Probing) l2n.netlist ()->purge (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), - "Circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD):\n" - " XINV2PAIR $1 ($1=FB,$2=VDD,$3=VSS,$4=$I3,$5=OSC)\n" - " XINV2PAIR $2 ($1=(null),$2=VDD,$3=VSS,$4=FB,$5=$I9)\n" - " XINV2PAIR $3 ($1=(null),$2=VDD,$3=VSS,$4=$I9,$5=$I1)\n" - " XINV2PAIR $4 ($1=(null),$2=VDD,$3=VSS,$4=$I1,$5=$I2)\n" - " XINV2PAIR $5 ($1=(null),$2=VDD,$3=VSS,$4=$I2,$5=$I3)\n" - "Circuit INV2PAIR ($1=$I7,$2=$I5,$3=$I4,$4=$I2,$5=$I1):\n" - " XINV2 $1 (IN=$I3,$2=$I7,OUT=$I1,$4=$I4,$5=$I5)\n" - " XINV2 $2 (IN=$I2,$2=(null),OUT=$I3,$4=$I4,$5=$I5)\n" - "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" - " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + "circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD);\n" + " subcircuit INV2PAIR $1 ($1=FB,$2=VDD,$3=VSS,$4=$I3,$5=OSC);\n" + " subcircuit INV2PAIR $2 ($1=(null),$2=VDD,$3=VSS,$4=FB,$5=$I9);\n" + " subcircuit INV2PAIR $3 ($1=(null),$2=VDD,$3=VSS,$4=$I9,$5=$I1);\n" + " subcircuit INV2PAIR $4 ($1=(null),$2=VDD,$3=VSS,$4=$I1,$5=$I2);\n" + " subcircuit INV2PAIR $5 ($1=(null),$2=VDD,$3=VSS,$4=$I2,$5=$I3);\n" + "end;\n" + "circuit INV2PAIR ($1=$I7,$2=$I5,$3=$I4,$4=$I2,$5=$I1);\n" + " subcircuit INV2 $1 (IN=$I3,$2=$I7,OUT=$I1,$4=$I4,$5=$I5);\n" + " subcircuit INV2 $2 (IN=$I2,$2=(null),OUT=$I3,$4=$I4,$5=$I5);\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + "end;\n" ); // do some probing after purging @@ -956,26 +968,30 @@ TEST(3_GlobalNetConnections) dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), - "Circuit RINGO ():\n" - " XINV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD)\n" - " XINV2PAIR $2 (BULK='BULK,VSS',$2=$I22,$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD)\n" - " XINV2PAIR $3 (BULK='BULK,VSS',$2=$I23,$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD)\n" - " XINV2PAIR $4 (BULK='BULK,VSS',$2=$I24,$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD)\n" - " XINV2PAIR $5 (BULK='BULK,VSS',$2=$I25,$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD)\n" - "Circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1):\n" - " XINV2 $1 ($1=$I1,IN=$I3,$3=$I7,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" - " XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" - "Circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" - " DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $4 (S=VSS,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " XTRANS $1 ($1=$3,$2=VSS,$3=IN)\n" - " XTRANS $2 ($1=$3,$2=VDD,$3=IN)\n" - " XTRANS $3 ($1=VDD,$2=OUT,$3=$3)\n" - " XTRANS $4 ($1=VSS,$2=OUT,$3=$3)\n" - "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + "circuit RINGO ();\n" + " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD);\n" + " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=$I22,$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD);\n" + " subcircuit INV2PAIR $3 (BULK='BULK,VSS',$2=$I23,$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD);\n" + " subcircuit INV2PAIR $4 (BULK='BULK,VSS',$2=$I24,$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD);\n" + " subcircuit INV2PAIR $5 (BULK='BULK,VSS',$2=$I25,$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD);\n" + "end;\n" + "circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1);\n" + " subcircuit INV2 $1 ($1=$I1,IN=$I3,$3=$I7,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK);\n" + " subcircuit INV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK);\n" + "end;\n" + "circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK);\n" + " device PMOS $1 (S=$3,G=IN,D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=$3,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$3,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=VSS,G=$3,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " subcircuit TRANS $1 ($1=$3,$2=VSS,$3=IN);\n" + " subcircuit TRANS $2 ($1=$3,$2=VDD,$3=IN);\n" + " subcircuit TRANS $3 ($1=VDD,$2=OUT,$3=$3);\n" + " subcircuit TRANS $4 ($1=VSS,$2=OUT,$3=$3);\n" + "end;\n" + "circuit TRANS ($1=$1,$2=$2,$3=$3);\n" + "end;\n" ); // compare the collected test data @@ -1008,21 +1024,24 @@ TEST(3_GlobalNetConnections) l2n.netlist ()->purge (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), - "Circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS'):\n" - " XINV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD)\n" - " XINV2PAIR $2 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD)\n" - " XINV2PAIR $3 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD)\n" - " XINV2PAIR $4 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD)\n" - " XINV2PAIR $5 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD)\n" - "Circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1):\n" - " XINV2 $1 ($1=$I1,IN=$I3,$3=(null),OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" - " XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" - "Circuit INV2 ($1=(null),IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=(null)):\n" - " DPMOS $1 (S=$3,G=IN,D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $2 (S=VDD,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $3 (S=$3,G=IN,D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $4 (S=VSS,G=$3,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + "circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS');\n" + " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD);\n" + " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD);\n" + " subcircuit INV2PAIR $3 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD);\n" + " subcircuit INV2PAIR $4 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD);\n" + " subcircuit INV2PAIR $5 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD);\n" + "end;\n" + "circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1);\n" + " subcircuit INV2 $1 ($1=$I1,IN=$I3,$3=(null),OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK);\n" + " subcircuit INV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK);\n" + "end;\n" + "circuit INV2 ($1=(null),IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=(null));\n" + " device PMOS $1 (S=$3,G=IN,D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=$3,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$3,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=VSS,G=$3,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + "end;\n" ); // do some probing after purging @@ -1234,26 +1253,30 @@ TEST(4_GlobalNetDeviceExtraction) dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), - "Circuit RINGO ():\n" - " XINV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD)\n" - " XINV2PAIR $2 (BULK='BULK,VSS',$2=$I22,$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD)\n" - " XINV2PAIR $3 (BULK='BULK,VSS',$2=$I23,$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD)\n" - " XINV2PAIR $4 (BULK='BULK,VSS',$2=$I24,$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD)\n" - " XINV2PAIR $5 (BULK='BULK,VSS',$2=$I25,$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD)\n" - "Circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1):\n" - " XINV2 $1 ($1=$I1,IN=$I3,$3=$I7,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" - " XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" - "Circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" - " DPMOS $1 (S=$3,G=IN,D=VDD,B=$1) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $2 (S=VDD,G=$3,D=OUT,B=$1) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $3 (S=$3,G=IN,D=VSS,B=BULK) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $4 (S=VSS,G=$3,D=OUT,B=BULK) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " XTRANS $1 ($1=$3,$2=VSS,$3=IN)\n" - " XTRANS $2 ($1=$3,$2=VDD,$3=IN)\n" - " XTRANS $3 ($1=VDD,$2=OUT,$3=$3)\n" - " XTRANS $4 ($1=VSS,$2=OUT,$3=$3)\n" - "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + "circuit RINGO ();\n" + " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD);\n" + " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=$I22,$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD);\n" + " subcircuit INV2PAIR $3 (BULK='BULK,VSS',$2=$I23,$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD);\n" + " subcircuit INV2PAIR $4 (BULK='BULK,VSS',$2=$I24,$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD);\n" + " subcircuit INV2PAIR $5 (BULK='BULK,VSS',$2=$I25,$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD);\n" + "end;\n" + "circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1);\n" + " subcircuit INV2 $1 ($1=$I1,IN=$I3,$3=$I7,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK);\n" + " subcircuit INV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK);\n" + "end;\n" + "circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK);\n" + " device PMOS $1 (S=$3,G=IN,D=VDD,B=$1) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=$3,D=OUT,B=$1) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$3,G=IN,D=VSS,B=BULK) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=VSS,G=$3,D=OUT,B=BULK) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " subcircuit TRANS $1 ($1=$3,$2=VSS,$3=IN);\n" + " subcircuit TRANS $2 ($1=$3,$2=VDD,$3=IN);\n" + " subcircuit TRANS $3 ($1=VDD,$2=OUT,$3=$3);\n" + " subcircuit TRANS $4 ($1=VSS,$2=OUT,$3=$3);\n" + "end;\n" + "circuit TRANS ($1=$1,$2=$2,$3=$3);\n" + "end;\n" ); // compare the collected test data @@ -1286,21 +1309,24 @@ TEST(4_GlobalNetDeviceExtraction) l2n.netlist ()->purge (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), - "Circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS'):\n" - " XINV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD)\n" - " XINV2PAIR $2 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD)\n" - " XINV2PAIR $3 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD)\n" - " XINV2PAIR $4 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD)\n" - " XINV2PAIR $5 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD)\n" - "Circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1):\n" - " XINV2 $1 ($1=$I1,IN=$I3,$3=(null),OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" - " XINV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" - "Circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" - " DPMOS $1 (S=$3,G=IN,D=VDD,B=$1) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $2 (S=VDD,G=$3,D=OUT,B=$1) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $3 (S=$3,G=IN,D=VSS,B=BULK) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $4 (S=VSS,G=$3,D=OUT,B=BULK) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + "circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS');\n" + " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD);\n" + " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD);\n" + " subcircuit INV2PAIR $3 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD);\n" + " subcircuit INV2PAIR $4 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD);\n" + " subcircuit INV2PAIR $5 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD);\n" + "end;\n" + "circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1);\n" + " subcircuit INV2 $1 ($1=$I1,IN=$I3,$3=(null),OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK);\n" + " subcircuit INV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK);\n" + "end;\n" + "circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK);\n" + " device PMOS $1 (S=$3,G=IN,D=VDD,B=$1) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=$3,D=OUT,B=$1) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$3,G=IN,D=VSS,B=BULK) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=VSS,G=$3,D=OUT,B=BULK) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + "end;\n" ); // do some probing after purging @@ -1512,26 +1538,30 @@ TEST(5_DeviceExtractionWithDeviceCombination) dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), - "Circuit RINGO ():\n" - " XINV2PAIR $1 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=FB,$5=$I7,$6=OSC,$7=VDD)\n" - " XINV2PAIR $2 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I22,$5=FB,$6=$I13,$7=VDD)\n" - " XINV2PAIR $3 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I23,$5=$I13,$6=$I5,$7=VDD)\n" - " XINV2PAIR $4 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I24,$5=$I5,$6=$I6,$7=VDD)\n" - " XINV2PAIR $5 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I25,$5=$I6,$6=$I7,$7=VDD)\n" - "Circuit INV2PAIR (BULK=BULK,$2=$I6,$3=$I5,$4=$I4,$5=$I3,$6=$I2,$7=$I1):\n" - " XINV2 $1 ($1=$I1,IN=$I3,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" - " XINV2 $2 ($1=$I1,IN=$I4,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" - "Circuit INV2 ($1=$1,IN=IN,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" - " DPMOS $1 (S=OUT,G=IN,D=VDD,B=$1) [L=0.25,W=1.75,AS=0.91875,AD=0.48125,PS=4.55,PD=2.3]\n" - " DPMOS $2 (S=VDD,G=IN,D=OUT,B=$1) [L=0.25,W=1.75,AS=0.48125,AD=0.91875,PS=2.3,PD=4.55]\n" - " DNMOS $3 (S=OUT,G=IN,D=VSS,B=BULK) [L=0.25,W=1.75,AS=0.91875,AD=0.48125,PS=4.55,PD=2.3]\n" - " DNMOS $4 (S=VSS,G=IN,D=OUT,B=BULK) [L=0.25,W=1.75,AS=0.48125,AD=0.91875,PS=2.3,PD=4.55]\n" - " XTRANS $1 ($1=OUT,$2=VSS,$3=IN)\n" - " XTRANS $2 ($1=OUT,$2=VDD,$3=IN)\n" - " XTRANS $3 ($1=OUT,$2=VSS,$3=IN)\n" - " XTRANS $4 ($1=OUT,$2=VDD,$3=IN)\n" - "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + "circuit RINGO ();\n" + " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=FB,$5=$I7,$6=OSC,$7=VDD);\n" + " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I22,$5=FB,$6=$I13,$7=VDD);\n" + " subcircuit INV2PAIR $3 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I23,$5=$I13,$6=$I5,$7=VDD);\n" + " subcircuit INV2PAIR $4 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I24,$5=$I5,$6=$I6,$7=VDD);\n" + " subcircuit INV2PAIR $5 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I25,$5=$I6,$6=$I7,$7=VDD);\n" + "end;\n" + "circuit INV2PAIR (BULK=BULK,$2=$I6,$3=$I5,$4=$I4,$5=$I3,$6=$I2,$7=$I1);\n" + " subcircuit INV2 $1 ($1=$I1,IN=$I3,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK);\n" + " subcircuit INV2 $2 ($1=$I1,IN=$I4,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK);\n" + "end;\n" + "circuit INV2 ($1=$1,IN=IN,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK);\n" + " device PMOS $1 (S=OUT,G=IN,D=VDD,B=$1) (L=0.25,W=1.75,AS=0.91875,AD=0.48125,PS=4.55,PD=2.3);\n" + " device PMOS $2 (S=VDD,G=IN,D=OUT,B=$1) (L=0.25,W=1.75,AS=0.48125,AD=0.91875,PS=2.3,PD=4.55);\n" + " device NMOS $3 (S=OUT,G=IN,D=VSS,B=BULK) (L=0.25,W=1.75,AS=0.91875,AD=0.48125,PS=4.55,PD=2.3);\n" + " device NMOS $4 (S=VSS,G=IN,D=OUT,B=BULK) (L=0.25,W=1.75,AS=0.48125,AD=0.91875,PS=2.3,PD=4.55);\n" + " subcircuit TRANS $1 ($1=OUT,$2=VSS,$3=IN);\n" + " subcircuit TRANS $2 ($1=OUT,$2=VDD,$3=IN);\n" + " subcircuit TRANS $3 ($1=OUT,$2=VSS,$3=IN);\n" + " subcircuit TRANS $4 ($1=OUT,$2=VDD,$3=IN);\n" + "end;\n" + "circuit TRANS ($1=$1,$2=$2,$3=$3);\n" + "end;\n" ); // compare the collected test data @@ -1561,19 +1591,22 @@ TEST(5_DeviceExtractionWithDeviceCombination) l2n.netlist ()->purge (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), - "Circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS'):\n" - " XINV2PAIR $1 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=FB,$5=$I7,$6=OSC,$7=VDD)\n" - " XINV2PAIR $2 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=FB,$6=$I13,$7=VDD)\n" - " XINV2PAIR $3 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=$I13,$6=$I5,$7=VDD)\n" - " XINV2PAIR $4 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=$I5,$6=$I6,$7=VDD)\n" - " XINV2PAIR $5 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=$I6,$6=$I7,$7=VDD)\n" - "Circuit INV2PAIR (BULK=BULK,$2=$I6,$3=$I5,$4=$I4,$5=$I3,$6=$I2,$7=$I1):\n" - " XINV2 $1 ($1=$I1,IN=$I3,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK)\n" - " XINV2 $2 ($1=$I1,IN=$I4,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK)\n" - "Circuit INV2 ($1=$1,IN=IN,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK):\n" - " DPMOS $1 (S=OUT,G=IN,D=VDD,B=$1) [L=0.25,W=3.5,AS=1.4,AD=1.4,PS=6.85,PD=6.85]\n" - " DNMOS $3 (S=OUT,G=IN,D=VSS,B=BULK) [L=0.25,W=3.5,AS=1.4,AD=1.4,PS=6.85,PD=6.85]\n" + EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + "circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS');\n" + " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=FB,$5=$I7,$6=OSC,$7=VDD);\n" + " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=FB,$6=$I13,$7=VDD);\n" + " subcircuit INV2PAIR $3 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=$I13,$6=$I5,$7=VDD);\n" + " subcircuit INV2PAIR $4 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=$I5,$6=$I6,$7=VDD);\n" + " subcircuit INV2PAIR $5 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=$I6,$6=$I7,$7=VDD);\n" + "end;\n" + "circuit INV2PAIR (BULK=BULK,$2=$I6,$3=$I5,$4=$I4,$5=$I3,$6=$I2,$7=$I1);\n" + " subcircuit INV2 $1 ($1=$I1,IN=$I3,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK);\n" + " subcircuit INV2 $2 ($1=$I1,IN=$I4,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK);\n" + "end;\n" + "circuit INV2 ($1=$1,IN=IN,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK);\n" + " device PMOS $1 (S=OUT,G=IN,D=VDD,B=$1) (L=0.25,W=3.5,AS=1.4,AD=1.4,PS=6.85,PD=6.85);\n" + " device NMOS $3 (S=OUT,G=IN,D=VSS,B=BULK) (L=0.25,W=3.5,AS=1.4,AD=1.4,PS=6.85,PD=6.85);\n" + "end;\n" ); // do some probing after purging @@ -1732,14 +1765,15 @@ TEST(6_MoreDeviceTypes) l2n.extract_netlist (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), - "Circuit TOP ():\n" - " DHVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) [L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4]\n" - " DHVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) [L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8]\n" - " DLVPMOS $3 (S=$10,G=A,D=$6,B=$9) [L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5]\n" - " DHVNMOS $4 (S=Z,G=$6,D=VSS,B=BULK) [L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6]\n" - " DHVNMOS $5 (S=VSS,G=A,D=$5,B=BULK) [L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2]\n" - " DLVNMOS $6 (S=VSS,G=A,D=$6,B=BULK) [L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89]\n" + EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + "circuit TOP ();\n" + " device HVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) (L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4);\n" + " device HVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) (L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8);\n" + " device LVPMOS $3 (S=$10,G=A,D=$6,B=$9) (L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5);\n" + " device HVNMOS $4 (S=Z,G=$6,D=VSS,B=BULK) (L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6);\n" + " device HVNMOS $5 (S=VSS,G=A,D=$5,B=BULK) (L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2);\n" + " device LVNMOS $6 (S=VSS,G=A,D=$6,B=BULK) (L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89);\n" + "end;\n" ); } @@ -1887,14 +1921,15 @@ TEST(7_MoreByEmptyDeviceTypes) l2n.extract_netlist (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), - "Circuit TOP ():\n" - " DLVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) [L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4]\n" - " DLVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) [L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8]\n" - " DLVPMOS $3 (S=$10,G=A,D=$6,B=$9) [L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5]\n" - " DLVNMOS $4 (S=VSS,G=A,D=$6,B=BULK) [L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89]\n" - " DLVNMOS $5 (S=Z,G=$6,D=VSS,B=BULK) [L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6]\n" - " DLVNMOS $6 (S=VSS,G=A,D=$5,B=BULK) [L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2]\n" + EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + "circuit TOP ();\n" + " device LVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) (L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4);\n" + " device LVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) (L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8);\n" + " device LVPMOS $3 (S=$10,G=A,D=$6,B=$9) (L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5);\n" + " device LVNMOS $4 (S=VSS,G=A,D=$6,B=BULK) (L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89);\n" + " device LVNMOS $5 (S=Z,G=$6,D=VSS,B=BULK) (L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6);\n" + " device LVNMOS $6 (S=VSS,G=A,D=$5,B=BULK) (L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2);\n" + "end;\n" ); } @@ -2064,14 +2099,15 @@ TEST(8_FlatExtraction) l2n.extract_netlist (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), - "Circuit TOP ():\n" - " DHVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) [L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4]\n" - " DHVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) [L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8]\n" - " DLVPMOS $3 (S=$10,G=A,D=$6,B=$9) [L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5]\n" - " DHVNMOS $4 (S=Z,G=$6,D=VSS,B=BULK) [L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6]\n" - " DHVNMOS $5 (S=VSS,G=A,D=$5,B=BULK) [L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2]\n" - " DLVNMOS $6 (S=VSS,G=A,D=$6,B=BULK) [L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89]\n" + EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + "circuit TOP ();\n" + " device HVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) (L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4);\n" + " device HVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) (L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8);\n" + " device LVPMOS $3 (S=$10,G=A,D=$6,B=$9) (L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5);\n" + " device HVNMOS $4 (S=Z,G=$6,D=VSS,B=BULK) (L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6);\n" + " device HVNMOS $5 (S=VSS,G=A,D=$5,B=BULK) (L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2);\n" + " device LVNMOS $6 (S=VSS,G=A,D=$6,B=BULK) (L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89);\n" + "end;\n" ); } @@ -2246,14 +2282,15 @@ TEST(9_FlatExtractionWithExternalDSS) l2n.extract_netlist (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), - "Circuit TOP ():\n" - " DLVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) [L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4]\n" - " DLVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) [L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8]\n" - " DLVPMOS $3 (S=$10,G=A,D=$6,B=$9) [L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5]\n" - " DLVNMOS $4 (S=VSS,G=A,D=$6,B=BULK) [L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89]\n" - " DLVNMOS $5 (S=Z,G=$6,D=VSS,B=BULK) [L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6]\n" - " DLVNMOS $6 (S=VSS,G=A,D=$5,B=BULK) [L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2]\n" + EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + "circuit TOP ();\n" + " device LVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) (L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4);\n" + " device LVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) (L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8);\n" + " device LVPMOS $3 (S=$10,G=A,D=$6,B=$9) (L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5);\n" + " device LVNMOS $4 (S=VSS,G=A,D=$6,B=BULK) (L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89);\n" + " device LVNMOS $5 (S=Z,G=$6,D=VSS,B=BULK) (L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6);\n" + " device LVNMOS $6 (S=VSS,G=A,D=$5,B=BULK) (L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2);\n" + "end;\n" ); } diff --git a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc index 095541605..a61bd37bb 100644 --- a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc @@ -64,18 +64,20 @@ TEST(1_SerialResistors) 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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n3);\n" + " device '' r1 (A=n1,B=n2) (R=1);\n" + " device '' r2 (A=n2,B=n3) (R=3);\n" + "end;\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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n3);\n" + " device '' r1 (A=n1,B=n3) (R=4);\n" + "end;\n" ); } @@ -115,18 +117,20 @@ TEST(2_SerialResistors1Swapped) 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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n3);\n" + " device '' r1 (A=n1,B=n2) (R=1);\n" + " device '' r2 (A=n3,B=n2) (R=3);\n" + "end;\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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n3);\n" + " device '' r1 (A=n1,B=n3) (R=4);\n" + "end;\n" ); } @@ -166,18 +170,20 @@ TEST(3_SerialResistors1OtherSwapped) 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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n3);\n" + " device '' r1 (A=n2,B=n1) (R=1);\n" + " device '' r2 (A=n2,B=n3) (R=3);\n" + "end;\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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n3);\n" + " device '' r1 (A=n3,B=n1) (R=4);\n" + "end;\n" ); } @@ -217,18 +223,20 @@ TEST(4_SerialResistors2Swapped) 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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n3);\n" + " device '' r1 (A=n2,B=n1) (R=1);\n" + " device '' r2 (A=n3,B=n2) (R=3);\n" + "end;\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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n3);\n" + " device '' r1 (A=n3,B=n1) (R=4);\n" + "end;\n" ); } @@ -270,19 +278,21 @@ TEST(5_SerialResistorsNoCombination) 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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n3,C=n2);\n" + " device '' r1 (A=n1,B=n2) (R=1);\n" + " device '' r2 (A=n2,B=n3) (R=3);\n" + "end;\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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n3,C=n2);\n" + " device '' r1 (A=n1,B=n2) (R=1);\n" + " device '' r2 (A=n2,B=n3) (R=3);\n" + "end;\n" ); } @@ -319,18 +329,20 @@ TEST(6_ParallelResistors) 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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2);\n" + " device '' r1 (A=n1,B=n2) (R=2);\n" + " device '' r2 (A=n1,B=n2) (R=3);\n" + "end;\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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2);\n" + " device '' r1 (A=n1,B=n2) (R=1.2);\n" + "end;\n" ); } @@ -367,18 +379,20 @@ TEST(7_ParallelResistors1Swapped) 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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2);\n" + " device '' r1 (A=n2,B=n1) (R=2);\n" + " device '' r2 (A=n1,B=n2) (R=3);\n" + "end;\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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2);\n" + " device '' r1 (A=n2,B=n1) (R=1.2);\n" + "end;\n" ); } @@ -415,18 +429,20 @@ TEST(8_ParallelResistors1OtherSwapped) 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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2);\n" + " device '' r1 (A=n1,B=n2) (R=2);\n" + " device '' r2 (A=n2,B=n1) (R=3);\n" + "end;\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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2);\n" + " device '' r1 (A=n1,B=n2) (R=1.2);\n" + "end;\n" ); } @@ -463,18 +479,20 @@ TEST(9_ParallelResistors2Swapped) 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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2);\n" + " device '' r1 (A=n2,B=n1) (R=2);\n" + " device '' r2 (A=n2,B=n1) (R=3);\n" + "end;\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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2);\n" + " device '' r1 (A=n2,B=n1) (R=1.2);\n" + "end;\n" ); } @@ -536,20 +554,22 @@ TEST(10_ComplexRegistorCombination) 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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n4);\n" + " device '' r1 (A=n1,B=n2) (R=1);\n" + " device '' r2 (A=n2,B=n3) (R=1);\n" + " device '' r3 (A=n1,B=n3) (R=3);\n" + " device '' r4 (A=n3,B=n4) (R=0.8);\n" + "end;\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" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n4);\n" + " device '' r4 (A=n1,B=n4) (R=2);\n" + "end;\n" ); } @@ -589,18 +609,20 @@ TEST(11_SerialInductors) l2->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 l1 (A=n1,B=n2) [L=1]\n" - " D l2 (A=n2,B=n3) [L=3]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n3);\n" + " device '' l1 (A=n1,B=n2) (L=1);\n" + " device '' l2 (A=n2,B=n3) (L=3);\n" + "end;\n" ); nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n3):\n" - " D l1 (A=n1,B=n3) [L=4]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n3);\n" + " device '' l1 (A=n1,B=n3) (L=4);\n" + "end;\n" ); } @@ -637,18 +659,20 @@ TEST(12_ParallelInductors) l1->connect_terminal (db::DeviceClassInductor::terminal_id_B, n2); l2->connect_terminal (db::DeviceClassInductor::terminal_id_B, n2); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2):\n" - " D l1 (A=n1,B=n2) [L=2]\n" - " D l2 (A=n1,B=n2) [L=3]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2);\n" + " device '' l1 (A=n1,B=n2) (L=2);\n" + " device '' l2 (A=n1,B=n2) (L=3);\n" + "end;\n" ); nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2):\n" - " D l1 (A=n1,B=n2) [L=1.2]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2);\n" + " device '' l1 (A=n1,B=n2) (L=1.2);\n" + "end;\n" ); } @@ -688,18 +712,20 @@ TEST(13_SerialCapacitors) c2->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n3); circuit->connect_pin (pin_b.id (), n3); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n3):\n" - " D c1 (A=n1,B=n2) [C=2]\n" - " D c2 (A=n2,B=n3) [C=3]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n3);\n" + " device '' c1 (A=n1,B=n2) (C=2);\n" + " device '' c2 (A=n2,B=n3) (C=3);\n" + "end;\n" ); nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n3):\n" - " D c1 (A=n1,B=n3) [C=1.2]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n3);\n" + " device '' c1 (A=n1,B=n3) (C=1.2);\n" + "end;\n" ); } @@ -736,18 +762,20 @@ TEST(14_ParallelCapacitors) c1->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n2); c2->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n2); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2):\n" - " D c1 (A=n1,B=n2) [C=1]\n" - " D c2 (A=n1,B=n2) [C=3]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2);\n" + " device '' c1 (A=n1,B=n2) (C=1);\n" + " device '' c2 (A=n1,B=n2) (C=3);\n" + "end;\n" ); nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2):\n" - " D c1 (A=n1,B=n2) [C=4]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2);\n" + " device '' c1 (A=n1,B=n2) (C=4);\n" + "end;\n" ); } @@ -787,10 +815,11 @@ TEST(15_SerialDiodes) d2->connect_terminal (db::DeviceClassDiode::terminal_id_C, n3); circuit->connect_pin (pin_b.id (), n3); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n3):\n" - " D d1 (A=n1,C=n2) [A=2]\n" - " D d2 (A=n2,C=n3) [A=3]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n3);\n" + " device '' d1 (A=n1,C=n2) (A=2);\n" + " device '' d2 (A=n2,C=n3) (A=3);\n" + "end;\n" ); nl.combine_devices (); @@ -798,10 +827,11 @@ TEST(15_SerialDiodes) // serial diodes are not combined! - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n3):\n" - " D d1 (A=n1,C=n2) [A=2]\n" - " D d2 (A=n2,C=n3) [A=3]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n3);\n" + " device '' d1 (A=n1,C=n2) (A=2);\n" + " device '' d2 (A=n2,C=n3) (A=3);\n" + "end;\n" ); } @@ -838,18 +868,20 @@ TEST(16_ParallelDiodes) d1->connect_terminal (db::DeviceClassDiode::terminal_id_C, n2); d2->connect_terminal (db::DeviceClassDiode::terminal_id_C, n2); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2):\n" - " D d1 (A=n1,C=n2) [A=1]\n" - " D d2 (A=n1,C=n2) [A=3]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2);\n" + " device '' d1 (A=n1,C=n2) (A=1);\n" + " device '' d2 (A=n1,C=n2) (A=3);\n" + "end;\n" ); nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2):\n" - " D d1 (A=n1,C=n2) [A=4]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2);\n" + " device '' d1 (A=n1,C=n2) (A=4);\n" + "end;\n" ); } @@ -886,10 +918,11 @@ TEST(17_AntiParallelDiodes) d1->connect_terminal (db::DeviceClassDiode::terminal_id_C, n2); d2->connect_terminal (db::DeviceClassDiode::terminal_id_A, n2); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2):\n" - " D d1 (A=n1,C=n2) [A=1]\n" - " D d2 (A=n2,C=n1) [A=3]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2);\n" + " device '' d1 (A=n1,C=n2) (A=1);\n" + " device '' d2 (A=n2,C=n1) (A=3);\n" + "end;\n" ); nl.combine_devices (); @@ -897,10 +930,11 @@ TEST(17_AntiParallelDiodes) // anti-parallel diodes are not combined - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2):\n" - " D d1 (A=n1,C=n2) [A=1]\n" - " D d2 (A=n2,C=n1) [A=3]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2);\n" + " device '' d1 (A=n1,C=n2) (A=1);\n" + " device '' d2 (A=n2,C=n1) (A=3);\n" + "end;\n" ); } @@ -954,18 +988,20 @@ TEST(20_ParallelMOS3Transistors) d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C=n3):\n" - " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" - " D d2 (S=n1,G=n3,D=n2) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C=n3);\n" + " device '' d1 (S=n1,G=n3,D=n2) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" + " device '' d2 (S=n1,G=n3,D=n2) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" + "end;\n" ); nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C=n3):\n" - " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=3,AS=5,AD=7,PS=25,PD=27]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C=n3);\n" + " device '' d1 (S=n1,G=n3,D=n2) (L=0.5,W=3,AS=5,AD=7,PS=25,PD=27);\n" + "end;\n" ); } @@ -1019,18 +1055,20 @@ TEST(21_AntiParallelMOS3Transistors) d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C=n3):\n" - " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" - " D d2 (S=n2,G=n3,D=n1) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C=n3);\n" + " device '' d1 (S=n1,G=n3,D=n2) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" + " device '' d2 (S=n2,G=n3,D=n1) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" + "end;\n" ); nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C=n3):\n" - " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=3,AS=5,AD=7,PS=25,PD=27]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C=n3);\n" + " device '' d1 (S=n1,G=n3,D=n2) (L=0.5,W=3,AS=5,AD=7,PS=25,PD=27);\n" + "end;\n" ); } @@ -1089,10 +1127,11 @@ TEST(22_ParallelMOS3TransistorsDisconnectedGates) circuit->connect_pin (pin_c2.id (), n4); d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n4); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C1=n3,C2=n4):\n" - " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" - " D d2 (S=n1,G=n4,D=n2) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C1=n3,C2=n4);\n" + " device '' d1 (S=n1,G=n3,D=n2) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" + " device '' d2 (S=n1,G=n4,D=n2) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" + "end;\n" ); nl.combine_devices (); @@ -1100,10 +1139,11 @@ TEST(22_ParallelMOS3TransistorsDisconnectedGates) // because of the disconnected gates, devices will no be joined: - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C1=n3,C2=n4):\n" - " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" - " D d2 (S=n1,G=n4,D=n2) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C1=n3,C2=n4);\n" + " device '' d1 (S=n1,G=n3,D=n2) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" + " device '' d2 (S=n1,G=n4,D=n2) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" + "end;\n" ); } @@ -1157,10 +1197,11 @@ TEST(23_ParallelMOS3TransistorsDifferentLength) d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C=n3):\n" - " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" - " D d2 (S=n1,G=n3,D=n2) [L=0.75,W=2,AS=3,AD=4,PS=13,PD=14]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C=n3);\n" + " device '' d1 (S=n1,G=n3,D=n2) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" + " device '' d2 (S=n1,G=n3,D=n2) (L=0.75,W=2,AS=3,AD=4,PS=13,PD=14);\n" + "end;\n" ); nl.combine_devices (); @@ -1168,10 +1209,11 @@ TEST(23_ParallelMOS3TransistorsDifferentLength) // because of different length, the devices will not be combined: - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C=n3):\n" - " D d1 (S=n1,G=n3,D=n2) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" - " D d2 (S=n1,G=n3,D=n2) [L=0.75,W=2,AS=3,AD=4,PS=13,PD=14]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C=n3);\n" + " device '' d1 (S=n1,G=n3,D=n2) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" + " device '' d2 (S=n1,G=n3,D=n2) (L=0.75,W=2,AS=3,AD=4,PS=13,PD=14);\n" + "end;\n" ); } @@ -1232,18 +1274,20 @@ TEST(30_ParallelMOS4Transistors) d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C=n3,D=n0):\n" - " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" - " D d2 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C=n3,D=n0);\n" + " device '' d1 (S=n1,G=n3,D=n2,B=n0) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" + " device '' d2 (S=n1,G=n3,D=n2,B=n0) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" + "end;\n" ); nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C=n3,D=n0):\n" - " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=3,AS=5,AD=7,PS=25,PD=27]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C=n3,D=n0);\n" + " device '' d1 (S=n1,G=n3,D=n2,B=n0) (L=0.5,W=3,AS=5,AD=7,PS=25,PD=27);\n" + "end;\n" ); } @@ -1304,18 +1348,20 @@ TEST(31_AntiParallelMOS4Transistors) d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C=n3,D=n0):\n" - " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" - " D d2 (S=n2,G=n3,D=n1,B=n0) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C=n3,D=n0);\n" + " device '' d1 (S=n1,G=n3,D=n2,B=n0) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" + " device '' d2 (S=n2,G=n3,D=n1,B=n0) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" + "end;\n" ); nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C=n3,D=n0):\n" - " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=3,AS=5,AD=7,PS=25,PD=27]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C=n3,D=n0);\n" + " device '' d1 (S=n1,G=n3,D=n2,B=n0) (L=0.5,W=3,AS=5,AD=7,PS=25,PD=27);\n" + "end;\n" ); } @@ -1381,10 +1427,11 @@ TEST(32_ParallelMOS4TransistorsDisconnectedGates) d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C1=n3a,C2=n3b,D=n0):\n" - " D d1 (S=n1,G=n3a,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" - " D d2 (S=n1,G=n3b,D=n2,B=n0) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C1=n3a,C2=n3b,D=n0);\n" + " device '' d1 (S=n1,G=n3a,D=n2,B=n0) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" + " device '' d2 (S=n1,G=n3b,D=n2,B=n0) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" + "end;\n" ); nl.combine_devices (); @@ -1392,10 +1439,11 @@ TEST(32_ParallelMOS4TransistorsDisconnectedGates) // not combined because gate is different: - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C1=n3a,C2=n3b,D=n0):\n" - " D d1 (S=n1,G=n3a,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" - " D d2 (S=n1,G=n3b,D=n2,B=n0) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C1=n3a,C2=n3b,D=n0);\n" + " device '' d1 (S=n1,G=n3a,D=n2,B=n0) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" + " device '' d2 (S=n1,G=n3b,D=n2,B=n0) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" + "end;\n" ); } @@ -1461,10 +1509,11 @@ TEST(33_ParallelMOS4TransistorsDisconnectedBulk) circuit->connect_pin (pin_d2.id (), n0b); d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0b); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C=n3,D1=n0a,D2=n0b):\n" - " D d1 (S=n1,G=n3,D=n2,B=n0a) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" - " D d2 (S=n1,G=n3,D=n2,B=n0b) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C=n3,D1=n0a,D2=n0b);\n" + " device '' d1 (S=n1,G=n3,D=n2,B=n0a) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" + " device '' d2 (S=n1,G=n3,D=n2,B=n0b) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" + "end;\n" ); // not combined because bulk is different: @@ -1472,10 +1521,11 @@ TEST(33_ParallelMOS4TransistorsDisconnectedBulk) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C=n3,D1=n0a,D2=n0b):\n" - " D d1 (S=n1,G=n3,D=n2,B=n0a) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" - " D d2 (S=n1,G=n3,D=n2,B=n0b) [L=0.5,W=2,AS=3,AD=4,PS=13,PD=14]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C=n3,D1=n0a,D2=n0b);\n" + " device '' d1 (S=n1,G=n3,D=n2,B=n0a) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" + " device '' d2 (S=n1,G=n3,D=n2,B=n0b) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" + "end;\n" ); } @@ -1536,10 +1586,11 @@ TEST(34_ParallelMOS4TransistorsDifferentLength) d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C=n3,D=n0):\n" - " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" - " D d2 (S=n1,G=n3,D=n2,B=n0) [L=0.75,W=2,AS=3,AD=4,PS=13,PD=14]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C=n3,D=n0);\n" + " device '' d1 (S=n1,G=n3,D=n2,B=n0) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" + " device '' d2 (S=n1,G=n3,D=n2,B=n0) (L=0.75,W=2,AS=3,AD=4,PS=13,PD=14);\n" + "end;\n" ); nl.combine_devices (); @@ -1547,10 +1598,11 @@ TEST(34_ParallelMOS4TransistorsDifferentLength) // not combined because length is different: - EXPECT_EQ (nl.to_string (), - "Circuit (A=n1,B=n2,C=n3,D=n0):\n" - " D d1 (S=n1,G=n3,D=n2,B=n0) [L=0.5,W=1,AS=2,AD=3,PS=12,PD=13]\n" - " D d2 (S=n1,G=n3,D=n2,B=n0) [L=0.75,W=2,AS=3,AD=4,PS=13,PD=14]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit '' (A=n1,B=n2,C=n3,D=n0);\n" + " device '' d1 (S=n1,G=n3,D=n2,B=n0) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" + " device '' d2 (S=n1,G=n3,D=n2,B=n0) (L=0.75,W=2,AS=3,AD=4,PS=13,PD=14);\n" + "end;\n" ); } diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 9081ea214..a1beb5a78 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -280,28 +280,31 @@ TEST(1_DeviceAndNetExtraction) dump_nets_to_layout (nl, cl, ly, dump_map, cm); // compare netlist as string - 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" - " XINV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VDD)\n" - " XINV2 $4 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VDD)\n" - " XINV2 $5 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VDD)\n" - " XINV2 $6 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VDD)\n" - " XINV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VDD)\n" - " XINV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VDD)\n" - " XINV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD)\n" - " XINV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD)\n" - "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" - " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" - " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" - " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" - " XTRANS $4 ($1=$4,$2=OUT,$3=$2)\n" - "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit RINGO ();\n" + " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $4 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $5 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $6 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD);\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " subcircuit TRANS $1 ($1=$2,$2=$4,$3=IN);\n" + " subcircuit TRANS $2 ($1=$2,$2=$5,$3=IN);\n" + " subcircuit TRANS $3 ($1=$5,$2=OUT,$3=$2);\n" + " subcircuit TRANS $4 ($1=$4,$2=OUT,$3=$2);\n" + "end;\n" + "circuit TRANS ($1=$1,$2=$2,$3=$3);\n" + "end;\n" ); // use this opportunity to test serialization to and from string @@ -310,7 +313,7 @@ TEST(1_DeviceAndNetExtraction) nldup.add_device_class (i->clone ()); } nldup.from_string (nl.to_parsable_string ()); - EXPECT_EQ (nldup.to_string (), nl.to_string ()); + EXPECT_EQ (nldup.to_parsable_string (), nl.to_parsable_string ()); // doesn't do anything here, but we test that this does not destroy anything: nl.combine_devices (); @@ -320,23 +323,25 @@ TEST(1_DeviceAndNetExtraction) nl.purge (); // compare netlist as string - 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" - " XINV2 $3 (IN=$I19,$2=(null),OUT=$I1,$4=VSS,$5=VDD)\n" - " XINV2 $4 (IN=$I1,$2=(null),OUT=$I2,$4=VSS,$5=VDD)\n" - " XINV2 $5 (IN=$I2,$2=(null),OUT=$I3,$4=VSS,$5=VDD)\n" - " XINV2 $6 (IN=$I3,$2=(null),OUT=$I4,$4=VSS,$5=VDD)\n" - " XINV2 $7 (IN=$I4,$2=(null),OUT=$I5,$4=VSS,$5=VDD)\n" - " XINV2 $8 (IN=$I5,$2=(null),OUT=$I6,$4=VSS,$5=VDD)\n" - " XINV2 $9 (IN=$I6,$2=(null),OUT=$I7,$4=VSS,$5=VDD)\n" - " XINV2 $10 (IN=$I7,$2=(null),OUT=$I8,$4=VSS,$5=VDD)\n" - "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" - " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD);\n" + " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $2 (IN=FB,$2=(null),OUT=$I19,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $3 (IN=$I19,$2=(null),OUT=$I1,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $4 (IN=$I1,$2=(null),OUT=$I2,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $5 (IN=$I2,$2=(null),OUT=$I3,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $6 (IN=$I3,$2=(null),OUT=$I4,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $7 (IN=$I4,$2=(null),OUT=$I5,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $8 (IN=$I5,$2=(null),OUT=$I6,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $9 (IN=$I6,$2=(null),OUT=$I7,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $10 (IN=$I7,$2=(null),OUT=$I8,$4=VSS,$5=VDD);\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + "end;\n" ); // compare the collected test data @@ -503,48 +508,49 @@ TEST(2_DeviceAndNetExtractionFlat) // compare netlist as string // NOTE: some of the nets are called IN,OUT but are different ones. They // happen to be the same because they share the same label. - EXPECT_EQ (nl.to_string (), - "Circuit RINGO ():\n" - " DPMOS $1 (S=$16,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $2 (S=VDD,G=$16,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DPMOS $3 (S=$14,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $4 (S=VDD,G=$14,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DPMOS $5 (S=$12,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $6 (S=VDD,G=$12,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DPMOS $7 (S='IN,FB',G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $8 (S=VDD,G='IN,FB',D='OUT,OSC') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DPMOS $9 (S=$4,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $10 (S=VDD,G=$4,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DPMOS $11 (S=$8,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $12 (S=VDD,G=$8,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DPMOS $13 (S=$2,G='IN,FB',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $14 (S=VDD,G=$2,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DPMOS $15 (S=$6,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $16 (S=VDD,G=$6,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DPMOS $17 (S=$18,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $18 (S=VDD,G=$18,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DPMOS $19 (S=$10,G='IN,OUT',D=VDD) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $20 (S=VDD,G=$10,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $21 (S='IN,FB',G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $22 (S=VSS,G='IN,FB',D='OUT,OSC') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $23 (S=$18,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $24 (S=VSS,G=$18,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $25 (S=$14,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $26 (S=VSS,G=$14,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $27 (S=$12,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $28 (S=VSS,G=$12,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $29 (S=$4,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $30 (S=VSS,G=$4,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $31 (S=$2,G='IN,FB',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $32 (S=VSS,G=$2,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $33 (S=$8,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $34 (S=VSS,G=$8,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $35 (S=$6,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $36 (S=VSS,G=$6,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $37 (S=$16,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $38 (S=VSS,G=$16,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $39 (S=$10,G='IN,OUT',D=VSS) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $40 (S=VSS,G=$10,D='IN,OUT') [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit RINGO ();\n" + " device PMOS $1 (S=$16,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=$16,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $3 (S=$14,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $4 (S=VDD,G=$14,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $5 (S=$12,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $6 (S=VDD,G=$12,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $7 (S='IN,FB',G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $8 (S=VDD,G='IN,FB',D='OUT,OSC') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $9 (S=$4,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $10 (S=VDD,G=$4,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $11 (S=$8,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $12 (S=VDD,G=$8,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $13 (S=$2,G='IN,FB',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $14 (S=VDD,G=$2,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $15 (S=$6,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $16 (S=VDD,G=$6,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $17 (S=$18,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $18 (S=VDD,G=$18,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $19 (S=$10,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $20 (S=VDD,G=$10,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $21 (S='IN,FB',G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $22 (S=VSS,G='IN,FB',D='OUT,OSC') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $23 (S=$18,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $24 (S=VSS,G=$18,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $25 (S=$14,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $26 (S=VSS,G=$14,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $27 (S=$12,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $28 (S=VSS,G=$12,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $29 (S=$4,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $30 (S=VSS,G=$4,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $31 (S=$2,G='IN,FB',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $32 (S=VSS,G=$2,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $33 (S=$8,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $34 (S=VSS,G=$8,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $35 (S=$6,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $36 (S=VSS,G=$6,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $37 (S=$16,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $38 (S=VSS,G=$16,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $39 (S=$10,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $40 (S=VSS,G=$10,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + "end;\n" ); // compare the collected test data @@ -736,28 +742,31 @@ TEST(3_DeviceAndNetExtractionWithImplicitConnections) dump_nets_to_layout (nl, cl, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (nl.to_string (), - "Circuit RINGO ():\n" - " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $3 (IN=NEXT,$2=$I43,OUT=$I5,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $4 (IN=$I3,$2=$I42,OUT=NEXT,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $5 (IN=$I5,$2=$I44,OUT=$I6,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $6 (IN=$I6,$2=$I45,OUT=$I7,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $7 (IN=$I7,$2=$I46,OUT=$I8,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $8 (IN=$I19,$2=$I39,OUT=$I1,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $9 (IN=$I1,$2=$I40,OUT=$I2,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $10 (IN=$I2,$2=$I41,OUT=$I3,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" - " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " XTRANS $1 ($1=$2,$2=$4,$3=IN)\n" - " XTRANS $2 ($1=$2,$2=$5,$3=IN)\n" - " XTRANS $3 ($1=$5,$2=OUT,$3=$2)\n" - " XTRANS $4 ($1=$4,$2=OUT,$3=$2)\n" - "Circuit TRANS ($1=$1,$2=$2,$3=$3):\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit RINGO ();\n" + " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $3 (IN=NEXT,$2=$I43,OUT=$I5,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $4 (IN=$I3,$2=$I42,OUT=NEXT,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $5 (IN=$I5,$2=$I44,OUT=$I6,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $6 (IN=$I6,$2=$I45,OUT=$I7,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $7 (IN=$I7,$2=$I46,OUT=$I8,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $8 (IN=$I19,$2=$I39,OUT=$I1,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $9 (IN=$I1,$2=$I40,OUT=$I2,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $10 (IN=$I2,$2=$I41,OUT=$I3,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " subcircuit TRANS $1 ($1=$2,$2=$4,$3=IN);\n" + " subcircuit TRANS $2 ($1=$2,$2=$5,$3=IN);\n" + " subcircuit TRANS $3 ($1=$5,$2=OUT,$3=$2);\n" + " subcircuit TRANS $4 ($1=$4,$2=OUT,$3=$2);\n" + "end;\n" + "circuit TRANS ($1=$1,$2=$2,$3=$3);\n" + "end;\n" ); // doesn't do anything here, but we test that this does not destroy anything: @@ -768,23 +777,25 @@ TEST(3_DeviceAndNetExtractionWithImplicitConnections) nl.purge (); // compare netlist as string - EXPECT_EQ (nl.to_string (), - "Circuit RINGO (FB=FB,OSC=OSC,NEXT=NEXT,'VSSZ,VSS'='VSSZ,VSS','VDDZ,VDD'='VDDZ,VDD'):\n" - " XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $2 (IN=FB,$2=(null),OUT=$I19,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $3 (IN=NEXT,$2=(null),OUT=$I5,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $4 (IN=$I3,$2=(null),OUT=NEXT,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $5 (IN=$I5,$2=(null),OUT=$I6,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $6 (IN=$I6,$2=(null),OUT=$I7,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $7 (IN=$I7,$2=(null),OUT=$I8,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $8 (IN=$I19,$2=(null),OUT=$I1,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $9 (IN=$I1,$2=(null),OUT=$I2,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - " XINV2 $10 (IN=$I2,$2=(null),OUT=$I3,$4='VSSZ,VSS',$5='VDDZ,VDD')\n" - "Circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5):\n" - " DPMOS $1 (S=$2,G=IN,D=$5) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DPMOS $2 (S=$5,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" - " DNMOS $3 (S=$2,G=IN,D=$4) [L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5]\n" - " DNMOS $4 (S=$4,G=$2,D=OUT) [L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95]\n" + EXPECT_EQ (nl.to_parsable_string (), + "circuit RINGO (FB=FB,OSC=OSC,NEXT=NEXT,'VSSZ,VSS'='VSSZ,VSS','VDDZ,VDD'='VDDZ,VDD');\n" + " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $2 (IN=FB,$2=(null),OUT=$I19,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $3 (IN=NEXT,$2=(null),OUT=$I5,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $4 (IN=$I3,$2=(null),OUT=NEXT,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $5 (IN=$I5,$2=(null),OUT=$I6,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $6 (IN=$I6,$2=(null),OUT=$I7,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $7 (IN=$I7,$2=(null),OUT=$I8,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $8 (IN=$I19,$2=(null),OUT=$I1,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $9 (IN=$I1,$2=(null),OUT=$I2,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + " subcircuit INV2 $10 (IN=$I2,$2=(null),OUT=$I3,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + "end;\n" ); // compare the collected test data diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 11e54ea09..95bf9844c 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -612,22 +612,26 @@ TEST(4_NetlistSubcircuits) "D:B,+c2p2\n" ); - EXPECT_EQ (nl->to_string (), - "Circuit c1 (c1p1=n1a,c1p2=n1c):\n" - " Xc2 sc1 (c2p1=n1a,c2p2=n1b)\n" - " Xc2 sc2 (c2p1=n1b,c2p2=n1c)\n" - "Circuit c2 (c2p1=n2a,c2p2=n2b):\n" - " Ddc2 D (A=n2a,B=n2b) []\n" + EXPECT_EQ (nl->to_parsable_string (), + "circuit c1 (c1p1=n1a,c1p2=n1c);\n" + " subcircuit c2 sc1 (c2p1=n1a,c2p2=n1b);\n" + " subcircuit c2 sc2 (c2p1=n1b,c2p2=n1c);\n" + "end;\n" + "circuit c2 (c2p1=n2a,c2p2=n2b);\n" + " device dc2 D (A=n2a,B=n2b) ();\n" + "end;\n" ); nldup->from_string (nl->to_parsable_string ()); - EXPECT_EQ (nldup->to_string (), - "Circuit c1 (c1p1=n1a,c1p2=n1c):\n" - " Xc2 sc1 (c2p1=n1a,c2p2=n1b)\n" - " Xc2 sc2 (c2p1=n1b,c2p2=n1c)\n" - "Circuit c2 (c2p1=n2a,c2p2=n2b):\n" - " Ddc2 D (A=n2a,B=n2b) []\n" + EXPECT_EQ (nldup->to_parsable_string (), + "circuit c1 (c1p1=n1a,c1p2=n1c);\n" + " subcircuit c2 sc1 (c2p1=n1a,c2p2=n1b);\n" + " subcircuit c2 sc2 (c2p1=n1b,c2p2=n1c);\n" + "end;\n" + "circuit c2 (c2p1=n2a,c2p2=n2b);\n" + " device dc2 D (A=n2a,B=n2b) ();\n" + "end;\n" ); EXPECT_EQ (netlist2 (*nl), From 2d4f23abd1c47e59046f3beab8074139788dba85 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 19 Mar 2019 00:08:47 +0100 Subject: [PATCH 04/54] Updated tests. --- src/db/db/dbNetlist.cc | 62 +----- src/db/db/dbNetlist.h | 9 +- src/db/db/gsiDeclDbNetlist.cc | 2 +- src/db/unit_tests/dbLayoutToNetlistTests.cc | 28 +-- .../unit_tests/dbNetlistDeviceClassesTests.cc | 104 +++++----- src/db/unit_tests/dbNetlistExtractorTests.cc | 14 +- src/db/unit_tests/dbNetlistTests.cc | 6 +- testdata/ruby/dbLayoutToNetlist.rb | 192 ++++++++++-------- testdata/ruby/dbNetlist.rb | 5 +- testdata/ruby/dbNetlistDeviceClasses.rb | 72 ++++--- 10 files changed, 228 insertions(+), 266 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 937128f0b..3a51a782a 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -486,67 +486,7 @@ static std::string pin2string (const db::Pin &pin) } } -std::string Netlist::to_string_old () 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 ()) + " sc_pins=" + tl::to_string (n->subcircuit_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; -} - -std::string Netlist::to_parsable_string () const +std::string Netlist::to_string () const { std::string res; for (db::Netlist::const_circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) { diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index b563aa493..aeae88923 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -87,19 +87,12 @@ public: */ void clear (); - /** - * @brief Returns a string representation of the netlist - * - * This method is basically intended to testing. - */ - std::string to_string_old () const; - /** * @brief Returns a parsable string representation of the netlist * * This method returns a string suitable for being put into from_string. */ - std::string to_parsable_string () const; + std::string to_string () const; /** * @brief Reads a netlist from the string generated by to_parsable_string diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 9c8e0ac9f..0b5b4d0d8 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -972,7 +972,7 @@ 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_parsable_string, + 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." ) + diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 54ce41d6a..35915880d 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -337,7 +337,7 @@ TEST(1_BasicExtraction) db::compare_layouts (_this, ly, au); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + EXPECT_EQ (l2n.netlist ()->to_string (), "circuit RINGO ();\n" " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" " subcircuit INV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD);\n" @@ -495,7 +495,7 @@ TEST(1_BasicExtraction) l2n.netlist ()->purge (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + EXPECT_EQ (l2n.netlist ()->to_string (), "circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD);\n" " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" " subcircuit INV2 $2 (IN=FB,$2=(null),OUT=$I19,$4=VSS,$5=VDD);\n" @@ -689,7 +689,7 @@ TEST(2_Probing) dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + EXPECT_EQ (l2n.netlist ()->to_string (), "circuit RINGO ();\n" " subcircuit INV2PAIR $1 ($1=FB,$2=VDD,$3=VSS,$4=$I3,$5=OSC);\n" " subcircuit INV2PAIR $2 ($1=$I18,$2=VDD,$3=VSS,$4=FB,$5=$I9);\n" @@ -745,7 +745,7 @@ TEST(2_Probing) l2n.netlist ()->purge (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + EXPECT_EQ (l2n.netlist ()->to_string (), "circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD);\n" " subcircuit INV2PAIR $1 ($1=FB,$2=VDD,$3=VSS,$4=$I3,$5=OSC);\n" " subcircuit INV2PAIR $2 ($1=(null),$2=VDD,$3=VSS,$4=FB,$5=$I9);\n" @@ -968,7 +968,7 @@ TEST(3_GlobalNetConnections) dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + EXPECT_EQ (l2n.netlist ()->to_string (), "circuit RINGO ();\n" " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD);\n" " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=$I22,$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD);\n" @@ -1024,7 +1024,7 @@ TEST(3_GlobalNetConnections) l2n.netlist ()->purge (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + EXPECT_EQ (l2n.netlist ()->to_string (), "circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS');\n" " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD);\n" " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD);\n" @@ -1253,7 +1253,7 @@ TEST(4_GlobalNetDeviceExtraction) dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + EXPECT_EQ (l2n.netlist ()->to_string (), "circuit RINGO ();\n" " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD);\n" " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=$I22,$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD);\n" @@ -1309,7 +1309,7 @@ TEST(4_GlobalNetDeviceExtraction) l2n.netlist ()->purge (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + EXPECT_EQ (l2n.netlist ()->to_string (), "circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS');\n" " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD);\n" " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD);\n" @@ -1538,7 +1538,7 @@ TEST(5_DeviceExtractionWithDeviceCombination) dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + EXPECT_EQ (l2n.netlist ()->to_string (), "circuit RINGO ();\n" " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=FB,$5=$I7,$6=OSC,$7=VDD);\n" " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I22,$5=FB,$6=$I13,$7=VDD);\n" @@ -1591,7 +1591,7 @@ TEST(5_DeviceExtractionWithDeviceCombination) l2n.netlist ()->purge (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + EXPECT_EQ (l2n.netlist ()->to_string (), "circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS');\n" " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=FB,$5=$I7,$6=OSC,$7=VDD);\n" " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=FB,$6=$I13,$7=VDD);\n" @@ -1765,7 +1765,7 @@ TEST(6_MoreDeviceTypes) l2n.extract_netlist (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + EXPECT_EQ (l2n.netlist ()->to_string (), "circuit TOP ();\n" " device HVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) (L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4);\n" " device HVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) (L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8);\n" @@ -1921,7 +1921,7 @@ TEST(7_MoreByEmptyDeviceTypes) l2n.extract_netlist (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + EXPECT_EQ (l2n.netlist ()->to_string (), "circuit TOP ();\n" " device LVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) (L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4);\n" " device LVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) (L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8);\n" @@ -2099,7 +2099,7 @@ TEST(8_FlatExtraction) l2n.extract_netlist (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + EXPECT_EQ (l2n.netlist ()->to_string (), "circuit TOP ();\n" " device HVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) (L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4);\n" " device HVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) (L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8);\n" @@ -2282,7 +2282,7 @@ TEST(9_FlatExtractionWithExternalDSS) l2n.extract_netlist (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_parsable_string (), + EXPECT_EQ (l2n.netlist ()->to_string (), "circuit TOP ();\n" " device LVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) (L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4);\n" " device LVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) (L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8);\n" diff --git a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc index a61bd37bb..37661487d 100644 --- a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc @@ -64,7 +64,7 @@ TEST(1_SerialResistors) r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); circuit->connect_pin (pin_b.id (), n3); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n3);\n" " device '' r1 (A=n1,B=n2) (R=1);\n" " device '' r2 (A=n2,B=n3) (R=3);\n" @@ -74,7 +74,7 @@ TEST(1_SerialResistors) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n3);\n" " device '' r1 (A=n1,B=n3) (R=4);\n" "end;\n" @@ -117,7 +117,7 @@ TEST(2_SerialResistors1Swapped) r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n3); circuit->connect_pin (pin_b.id (), n3); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n3);\n" " device '' r1 (A=n1,B=n2) (R=1);\n" " device '' r2 (A=n3,B=n2) (R=3);\n" @@ -127,7 +127,7 @@ TEST(2_SerialResistors1Swapped) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n3);\n" " device '' r1 (A=n1,B=n3) (R=4);\n" "end;\n" @@ -170,7 +170,7 @@ TEST(3_SerialResistors1OtherSwapped) r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); circuit->connect_pin (pin_b.id (), n3); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n3);\n" " device '' r1 (A=n2,B=n1) (R=1);\n" " device '' r2 (A=n2,B=n3) (R=3);\n" @@ -180,7 +180,7 @@ TEST(3_SerialResistors1OtherSwapped) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n3);\n" " device '' r1 (A=n3,B=n1) (R=4);\n" "end;\n" @@ -223,7 +223,7 @@ TEST(4_SerialResistors2Swapped) r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n3); circuit->connect_pin (pin_b.id (), n3); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n3);\n" " device '' r1 (A=n2,B=n1) (R=1);\n" " device '' r2 (A=n3,B=n2) (R=3);\n" @@ -233,7 +233,7 @@ TEST(4_SerialResistors2Swapped) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n3);\n" " device '' r1 (A=n3,B=n1) (R=4);\n" "end;\n" @@ -278,7 +278,7 @@ TEST(5_SerialResistorsNoCombination) r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); circuit->connect_pin (pin_b.id (), n3); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n3,C=n2);\n" " device '' r1 (A=n1,B=n2) (R=1);\n" " device '' r2 (A=n2,B=n3) (R=3);\n" @@ -288,7 +288,7 @@ TEST(5_SerialResistorsNoCombination) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n3,C=n2);\n" " device '' r1 (A=n1,B=n2) (R=1);\n" " device '' r2 (A=n2,B=n3) (R=3);\n" @@ -329,7 +329,7 @@ TEST(6_ParallelResistors) r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2);\n" " device '' r1 (A=n1,B=n2) (R=2);\n" " device '' r2 (A=n1,B=n2) (R=3);\n" @@ -339,7 +339,7 @@ TEST(6_ParallelResistors) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2);\n" " device '' r1 (A=n1,B=n2) (R=1.2);\n" "end;\n" @@ -379,7 +379,7 @@ TEST(7_ParallelResistors1Swapped) r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2);\n" " device '' r1 (A=n2,B=n1) (R=2);\n" " device '' r2 (A=n1,B=n2) (R=3);\n" @@ -389,7 +389,7 @@ TEST(7_ParallelResistors1Swapped) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2);\n" " device '' r1 (A=n2,B=n1) (R=1.2);\n" "end;\n" @@ -429,7 +429,7 @@ TEST(8_ParallelResistors1OtherSwapped) r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2); r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2);\n" " device '' r1 (A=n1,B=n2) (R=2);\n" " device '' r2 (A=n2,B=n1) (R=3);\n" @@ -439,7 +439,7 @@ TEST(8_ParallelResistors1OtherSwapped) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2);\n" " device '' r1 (A=n1,B=n2) (R=1.2);\n" "end;\n" @@ -479,7 +479,7 @@ TEST(9_ParallelResistors2Swapped) r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2);\n" " device '' r1 (A=n2,B=n1) (R=2);\n" " device '' r2 (A=n2,B=n1) (R=3);\n" @@ -489,7 +489,7 @@ TEST(9_ParallelResistors2Swapped) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2);\n" " device '' r1 (A=n2,B=n1) (R=1.2);\n" "end;\n" @@ -554,7 +554,7 @@ TEST(10_ComplexRegistorCombination) circuit->connect_pin (pin_b.id (), n4); r4->connect_terminal (db::DeviceClassResistor::terminal_id_B, n4); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n4);\n" " device '' r1 (A=n1,B=n2) (R=1);\n" " device '' r2 (A=n2,B=n3) (R=1);\n" @@ -566,7 +566,7 @@ TEST(10_ComplexRegistorCombination) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n4);\n" " device '' r4 (A=n1,B=n4) (R=2);\n" "end;\n" @@ -609,7 +609,7 @@ TEST(11_SerialInductors) l2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3); circuit->connect_pin (pin_b.id (), n3); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n3);\n" " device '' l1 (A=n1,B=n2) (L=1);\n" " device '' l2 (A=n2,B=n3) (L=3);\n" @@ -619,7 +619,7 @@ TEST(11_SerialInductors) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n3);\n" " device '' l1 (A=n1,B=n3) (L=4);\n" "end;\n" @@ -659,7 +659,7 @@ TEST(12_ParallelInductors) l1->connect_terminal (db::DeviceClassInductor::terminal_id_B, n2); l2->connect_terminal (db::DeviceClassInductor::terminal_id_B, n2); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2);\n" " device '' l1 (A=n1,B=n2) (L=2);\n" " device '' l2 (A=n1,B=n2) (L=3);\n" @@ -669,7 +669,7 @@ TEST(12_ParallelInductors) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2);\n" " device '' l1 (A=n1,B=n2) (L=1.2);\n" "end;\n" @@ -712,7 +712,7 @@ TEST(13_SerialCapacitors) c2->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n3); circuit->connect_pin (pin_b.id (), n3); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n3);\n" " device '' c1 (A=n1,B=n2) (C=2);\n" " device '' c2 (A=n2,B=n3) (C=3);\n" @@ -722,7 +722,7 @@ TEST(13_SerialCapacitors) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n3);\n" " device '' c1 (A=n1,B=n3) (C=1.2);\n" "end;\n" @@ -762,7 +762,7 @@ TEST(14_ParallelCapacitors) c1->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n2); c2->connect_terminal (db::DeviceClassCapacitor::terminal_id_B, n2); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2);\n" " device '' c1 (A=n1,B=n2) (C=1);\n" " device '' c2 (A=n1,B=n2) (C=3);\n" @@ -772,7 +772,7 @@ TEST(14_ParallelCapacitors) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2);\n" " device '' c1 (A=n1,B=n2) (C=4);\n" "end;\n" @@ -815,7 +815,7 @@ TEST(15_SerialDiodes) d2->connect_terminal (db::DeviceClassDiode::terminal_id_C, n3); circuit->connect_pin (pin_b.id (), n3); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n3);\n" " device '' d1 (A=n1,C=n2) (A=2);\n" " device '' d2 (A=n2,C=n3) (A=3);\n" @@ -827,7 +827,7 @@ TEST(15_SerialDiodes) // serial diodes are not combined! - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n3);\n" " device '' d1 (A=n1,C=n2) (A=2);\n" " device '' d2 (A=n2,C=n3) (A=3);\n" @@ -868,7 +868,7 @@ TEST(16_ParallelDiodes) d1->connect_terminal (db::DeviceClassDiode::terminal_id_C, n2); d2->connect_terminal (db::DeviceClassDiode::terminal_id_C, n2); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2);\n" " device '' d1 (A=n1,C=n2) (A=1);\n" " device '' d2 (A=n1,C=n2) (A=3);\n" @@ -878,7 +878,7 @@ TEST(16_ParallelDiodes) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2);\n" " device '' d1 (A=n1,C=n2) (A=4);\n" "end;\n" @@ -918,7 +918,7 @@ TEST(17_AntiParallelDiodes) d1->connect_terminal (db::DeviceClassDiode::terminal_id_C, n2); d2->connect_terminal (db::DeviceClassDiode::terminal_id_A, n2); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2);\n" " device '' d1 (A=n1,C=n2) (A=1);\n" " device '' d2 (A=n2,C=n1) (A=3);\n" @@ -930,7 +930,7 @@ TEST(17_AntiParallelDiodes) // anti-parallel diodes are not combined - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2);\n" " device '' d1 (A=n1,C=n2) (A=1);\n" " device '' d2 (A=n2,C=n1) (A=3);\n" @@ -988,7 +988,7 @@ TEST(20_ParallelMOS3Transistors) d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C=n3);\n" " device '' d1 (S=n1,G=n3,D=n2) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" " device '' d2 (S=n1,G=n3,D=n2) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" @@ -998,7 +998,7 @@ TEST(20_ParallelMOS3Transistors) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C=n3);\n" " device '' d1 (S=n1,G=n3,D=n2) (L=0.5,W=3,AS=5,AD=7,PS=25,PD=27);\n" "end;\n" @@ -1055,7 +1055,7 @@ TEST(21_AntiParallelMOS3Transistors) d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C=n3);\n" " device '' d1 (S=n1,G=n3,D=n2) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" " device '' d2 (S=n2,G=n3,D=n1) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" @@ -1065,7 +1065,7 @@ TEST(21_AntiParallelMOS3Transistors) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C=n3);\n" " device '' d1 (S=n1,G=n3,D=n2) (L=0.5,W=3,AS=5,AD=7,PS=25,PD=27);\n" "end;\n" @@ -1127,7 +1127,7 @@ TEST(22_ParallelMOS3TransistorsDisconnectedGates) circuit->connect_pin (pin_c2.id (), n4); d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n4); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C1=n3,C2=n4);\n" " device '' d1 (S=n1,G=n3,D=n2) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" " device '' d2 (S=n1,G=n4,D=n2) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" @@ -1139,7 +1139,7 @@ TEST(22_ParallelMOS3TransistorsDisconnectedGates) // because of the disconnected gates, devices will no be joined: - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C1=n3,C2=n4);\n" " device '' d1 (S=n1,G=n3,D=n2) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" " device '' d2 (S=n1,G=n4,D=n2) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" @@ -1197,7 +1197,7 @@ TEST(23_ParallelMOS3TransistorsDifferentLength) d1->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); d2->connect_terminal (db::DeviceClassMOS3Transistor::terminal_id_G, n3); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C=n3);\n" " device '' d1 (S=n1,G=n3,D=n2) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" " device '' d2 (S=n1,G=n3,D=n2) (L=0.75,W=2,AS=3,AD=4,PS=13,PD=14);\n" @@ -1209,7 +1209,7 @@ TEST(23_ParallelMOS3TransistorsDifferentLength) // because of different length, the devices will not be combined: - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C=n3);\n" " device '' d1 (S=n1,G=n3,D=n2) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" " device '' d2 (S=n1,G=n3,D=n2) (L=0.75,W=2,AS=3,AD=4,PS=13,PD=14);\n" @@ -1274,7 +1274,7 @@ TEST(30_ParallelMOS4Transistors) d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C=n3,D=n0);\n" " device '' d1 (S=n1,G=n3,D=n2,B=n0) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" " device '' d2 (S=n1,G=n3,D=n2,B=n0) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" @@ -1284,7 +1284,7 @@ TEST(30_ParallelMOS4Transistors) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C=n3,D=n0);\n" " device '' d1 (S=n1,G=n3,D=n2,B=n0) (L=0.5,W=3,AS=5,AD=7,PS=25,PD=27);\n" "end;\n" @@ -1348,7 +1348,7 @@ TEST(31_AntiParallelMOS4Transistors) d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C=n3,D=n0);\n" " device '' d1 (S=n1,G=n3,D=n2,B=n0) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" " device '' d2 (S=n2,G=n3,D=n1,B=n0) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" @@ -1358,7 +1358,7 @@ TEST(31_AntiParallelMOS4Transistors) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C=n3,D=n0);\n" " device '' d1 (S=n1,G=n3,D=n2,B=n0) (L=0.5,W=3,AS=5,AD=7,PS=25,PD=27);\n" "end;\n" @@ -1427,7 +1427,7 @@ TEST(32_ParallelMOS4TransistorsDisconnectedGates) d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C1=n3a,C2=n3b,D=n0);\n" " device '' d1 (S=n1,G=n3a,D=n2,B=n0) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" " device '' d2 (S=n1,G=n3b,D=n2,B=n0) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" @@ -1439,7 +1439,7 @@ TEST(32_ParallelMOS4TransistorsDisconnectedGates) // not combined because gate is different: - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C1=n3a,C2=n3b,D=n0);\n" " device '' d1 (S=n1,G=n3a,D=n2,B=n0) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" " device '' d2 (S=n1,G=n3b,D=n2,B=n0) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" @@ -1509,7 +1509,7 @@ TEST(33_ParallelMOS4TransistorsDisconnectedBulk) circuit->connect_pin (pin_d2.id (), n0b); d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0b); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C=n3,D1=n0a,D2=n0b);\n" " device '' d1 (S=n1,G=n3,D=n2,B=n0a) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" " device '' d2 (S=n1,G=n3,D=n2,B=n0b) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" @@ -1521,7 +1521,7 @@ TEST(33_ParallelMOS4TransistorsDisconnectedBulk) nl.combine_devices (); nl.purge (); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C=n3,D1=n0a,D2=n0b);\n" " device '' d1 (S=n1,G=n3,D=n2,B=n0a) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" " device '' d2 (S=n1,G=n3,D=n2,B=n0b) (L=0.5,W=2,AS=3,AD=4,PS=13,PD=14);\n" @@ -1586,7 +1586,7 @@ TEST(34_ParallelMOS4TransistorsDifferentLength) d1->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); d2->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, n0); - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C=n3,D=n0);\n" " device '' d1 (S=n1,G=n3,D=n2,B=n0) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" " device '' d2 (S=n1,G=n3,D=n2,B=n0) (L=0.75,W=2,AS=3,AD=4,PS=13,PD=14);\n" @@ -1598,7 +1598,7 @@ TEST(34_ParallelMOS4TransistorsDifferentLength) // not combined because length is different: - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit '' (A=n1,B=n2,C=n3,D=n0);\n" " device '' d1 (S=n1,G=n3,D=n2,B=n0) (L=0.5,W=1,AS=2,AD=3,PS=12,PD=13);\n" " device '' d2 (S=n1,G=n3,D=n2,B=n0) (L=0.75,W=2,AS=3,AD=4,PS=13,PD=14);\n" diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index a1beb5a78..52c2a5961 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -280,7 +280,7 @@ TEST(1_DeviceAndNetExtraction) dump_nets_to_layout (nl, cl, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit RINGO ();\n" " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" " subcircuit INV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD);\n" @@ -312,8 +312,8 @@ TEST(1_DeviceAndNetExtraction) for (db::Netlist::device_class_iterator i = nl.begin_device_classes (); i != nl.end_device_classes (); ++i) { nldup.add_device_class (i->clone ()); } - nldup.from_string (nl.to_parsable_string ()); - EXPECT_EQ (nldup.to_parsable_string (), nl.to_parsable_string ()); + nldup.from_string (nl.to_string ()); + EXPECT_EQ (nldup.to_string (), nl.to_string ()); // doesn't do anything here, but we test that this does not destroy anything: nl.combine_devices (); @@ -323,7 +323,7 @@ TEST(1_DeviceAndNetExtraction) nl.purge (); // compare netlist as string - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD);\n" " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" " subcircuit INV2 $2 (IN=FB,$2=(null),OUT=$I19,$4=VSS,$5=VDD);\n" @@ -508,7 +508,7 @@ TEST(2_DeviceAndNetExtractionFlat) // compare netlist as string // NOTE: some of the nets are called IN,OUT but are different ones. They // happen to be the same because they share the same label. - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit RINGO ();\n" " device PMOS $1 (S=$16,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" " device PMOS $2 (S=VDD,G=$16,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" @@ -742,7 +742,7 @@ TEST(3_DeviceAndNetExtractionWithImplicitConnections) dump_nets_to_layout (nl, cl, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit RINGO ();\n" " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" " subcircuit INV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" @@ -777,7 +777,7 @@ TEST(3_DeviceAndNetExtractionWithImplicitConnections) nl.purge (); // compare netlist as string - EXPECT_EQ (nl.to_parsable_string (), + EXPECT_EQ (nl.to_string (), "circuit RINGO (FB=FB,OSC=OSC,NEXT=NEXT,'VSSZ,VSS'='VSSZ,VSS','VDDZ,VDD'='VDDZ,VDD');\n" " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" " subcircuit INV2 $2 (IN=FB,$2=(null),OUT=$I19,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 95bf9844c..8169d05a3 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -612,7 +612,7 @@ TEST(4_NetlistSubcircuits) "D:B,+c2p2\n" ); - EXPECT_EQ (nl->to_parsable_string (), + EXPECT_EQ (nl->to_string (), "circuit c1 (c1p1=n1a,c1p2=n1c);\n" " subcircuit c2 sc1 (c2p1=n1a,c2p2=n1b);\n" " subcircuit c2 sc2 (c2p1=n1b,c2p2=n1c);\n" @@ -622,9 +622,9 @@ TEST(4_NetlistSubcircuits) "end;\n" ); - nldup->from_string (nl->to_parsable_string ()); + nldup->from_string (nl->to_string ()); - EXPECT_EQ (nldup->to_parsable_string (), + EXPECT_EQ (nldup->to_string (), "circuit c1 (c1p1=n1a,c1p2=n1c);\n" " subcircuit c2 sc1 (c2p1=n1a,c2p2=n1b);\n" " subcircuit c2 sc2 (c2p1=n1b,c2p2=n1c);\n" diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index b5e9ac6cc..17b5a85ae 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -97,23 +97,26 @@ class DBLayoutToNetlist_TestClass < TestBase l2n.extract_netlist assert_equal(l2n.netlist.to_s, < Date: Wed, 20 Mar 2019 23:00:43 +0100 Subject: [PATCH 05/54] WIP: netlist compare --- src/db/unit_tests/dbNetlistCompareTests.cc | 579 +++++++++++++++++++++ src/db/unit_tests/unit_tests.pro | 3 +- 2 files changed, 581 insertions(+), 1 deletion(-) create mode 100644 src/db/unit_tests/dbNetlistCompareTests.cc diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc new file mode 100644 index 000000000..bcd832451 --- /dev/null +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -0,0 +1,579 @@ + + +// @@@ + +#include "dbNetlist.h" +#include "dbNetlistDeviceClasses.h" +#include "dbHash.h" +#include "tlUnitTest.h" + +namespace db +{ + +struct DeviceCompare +{ + bool operator() (const db::Device *d1, const db::Device *d2) const + { + if (d1->device_class () != d2->device_class ()) { + return d1->device_class ()->name () < d1->device_class ()->name (); + } + const std::vector &dp = d1->device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = dp.begin (); i != dp.end (); ++i) { + double v1 = d1->parameter_value (i->id ()); + double v2 = d2->parameter_value (i->id ()); + if (fabs (v1 - v2) > db::epsilon) { + return v1 < v2; + } + } + return false; + } +}; + +static size_t translate_terminal_id (size_t tid, const db::Device *device) +{ + // @@@ delegate to device class + if (dynamic_cast (device->device_class ())) { + if (tid == db::DeviceClassMOS3Transistor::terminal_id_D) { + return db::DeviceClassMOS3Transistor::terminal_id_S; + } + } else if (dynamic_cast (device->device_class ())) { + if (tid == db::DeviceClassMOS4Transistor::terminal_id_D) { + return db::DeviceClassMOS4Transistor::terminal_id_S; + } + } + return tid; + // @@@ +} + +class NetDeviceGraphNode +{ +public: + struct EdgeDesc { + const db::Device *device; + size_t terminal1_id, terminal2_id; + + bool operator< (const EdgeDesc &other) const + { + DeviceCompare dc; + bool dlt = dc (device, other.device); + if (dlt || dc (other.device, device)) { + return dlt; + } + if (terminal1_id != other.terminal1_id) { + return terminal1_id < other.terminal1_id; + } + return terminal2_id < other.terminal2_id; + } + + bool operator== (const EdgeDesc &other) const + { + DeviceCompare dc; + if (dc (device, other.device) || dc (other.device, device)) { + return false; + } + if (terminal1_id != other.terminal1_id) { + return false; + } + return terminal2_id == other.terminal2_id; + } + }; + + typedef std::vector, std::pair > >::const_iterator edge_iterator; + + NetDeviceGraphNode (const db::Net *net, std::map &devmap, std::vector &device_prototypes) + : mp_net (net), m_other_net_index (std::numeric_limits::max ()) + { + std::map n2entry; + + for (db::Net::const_terminal_iterator i = net->begin_terminals (); i != net->end_terminals (); ++i) { + + const db::Device *d = i->device (); + + size_t dev_id = 0; + std::map::iterator di = devmap.find (d); + if (di != devmap.end ()) { + dev_id = di->second; + } else { + dev_id = device_prototypes.size (); + device_prototypes.push_back (d); + } + + EdgeDesc ed; + ed.device = device_prototypes [dev_id]; + ed.terminal1_id = translate_terminal_id (i->terminal_id (), d); + + const std::vector &td = d->device_class ()->terminal_definitions (); + for (std::vector::const_iterator it = td.begin (); it != td.end (); ++it) { + + if (it->id () != i->terminal_id ()) { + + EdgeDesc ed2 = ed; + ed2.terminal2_id = translate_terminal_id (it->id (), d); + const db::Net *net2 = d->net_for_terminal (it->id ()); + + std::map::const_iterator in = n2entry.find (net2); + if (in == n2entry.end ()) { + in = n2entry.insert (std::make_pair (net2, m_edges.size ())).first; + m_edges.push_back (std::make_pair (std::vector (), std::make_pair (net2, size_t (0)))); + } + + m_edges [in->second].first.push_back (ed2); + + } + + } + + } + + // "deep sorting" of the edge descriptor + for (std::vector, std::pair > >::iterator i = m_edges.begin (); i != m_edges.end (); ++i) { + std::sort (i->first.begin (), i->first.end ()); + } + + std::sort (m_edges.begin (), m_edges.end ()); + } + + const db::Net *net () const + { + return mp_net; + } + + bool has_other () const + { + return m_other_net_index != std::numeric_limits::max (); + } + + size_t other_net_index () const + { + return m_other_net_index; + } + + void set_other_net (size_t index) + { + m_other_net_index = index; + } + + void apply_net_index (const std::map &ni) + { + for (std::vector, std::pair > >::iterator i = m_edges.begin (); i != m_edges.end (); ++i) { + std::map::const_iterator j = ni.find (i->second.first); + tl_assert (j != ni.end ()); + i->second.second = j->second; + } + } + + bool operator< (const NetDeviceGraphNode &node) const + { + if (m_edges.size () != node.m_edges.size ()) { + return m_edges.size () < node.m_edges.size (); + } + for (size_t i = 0; i < m_edges.size (); ++i) { + if (m_edges [i].first != node.m_edges [i].first) { + return m_edges [i].first < node.m_edges [i].first; + } + } + return false; + } + + void swap (NetDeviceGraphNode &other) + { + std::swap (m_other_net_index, other.m_other_net_index); + std::swap (mp_net, other.mp_net); + m_edges.swap (other.m_edges); + } + + edge_iterator begin () const + { + return m_edges.begin (); + } + + edge_iterator end () const + { + return m_edges.end (); + } + +private: + const db::Net *mp_net; + size_t m_other_net_index; + std::vector, std::pair > > m_edges; +}; + +} + +namespace std +{ + void swap (db::NetDeviceGraphNode &a, db::NetDeviceGraphNode &b) + { + a.swap (b); + } +} + +namespace db +{ + +class NetDeviceGraph +{ +public: + typedef std::vector::const_iterator node_iterator; + + NetDeviceGraph () + { + // .. nothing yet .. + } + + void build (const db::Circuit *c) + { + m_device_map.clear (); + m_device_prototypes.clear (); + m_nodes.clear (); + m_net_index.clear (); + + size_t nets = 0; + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + ++nets; + } + m_nodes.reserve (nets); + + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + NetDeviceGraphNode node (n.operator-> (), m_device_map, m_device_prototypes); + m_nodes.push_back (node); + } + + std::sort (m_nodes.begin (), m_nodes.end ()); + + for (std::vector::const_iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { + m_net_index.insert (std::make_pair (i->net (), i - m_nodes.begin ())); + } + for (std::vector::iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { + i->apply_net_index (m_net_index); + } + + // ... + } + + size_t index_for_net (const db::Net *net) const + { + std::map::const_iterator j = m_net_index.find (net); + tl_assert (j != m_net_index.end ()); + return j->second; + } + + void identify (size_t net_index, size_t other_net_index) + { + m_nodes [net_index].set_other_net (other_net_index); + } + + node_iterator begin () const + { + return m_nodes.begin (); + } + + node_iterator end () const + { + return m_nodes.end (); + } + + size_t derive_node_identities (size_t net_index, NetDeviceGraph &other) + { + size_t added = 0; + + std::vector todo, more; + more.push_back (net_index); + + while (! more.empty ()) { + + todo.swap (more); + more.clear (); + + for (std::vector::const_iterator index = todo.begin (); index != todo.end (); ++index) { + + NetDeviceGraphNode *n = & m_nodes[*index]; + NetDeviceGraphNode *nother = & other.m_nodes[n->other_net_index ()]; + + // non-ambiguous paths to non-assigned nodes create a node identity on the + // end of this path + + for (NetDeviceGraphNode::edge_iterator e = n->begin (); e != n->end (); ) { + + NetDeviceGraphNode::edge_iterator ee = e; + ++ee; + + while (ee != n->end () && *ee == *e) { + ++ee; + } + + size_t count = 0; + NetDeviceGraphNode::edge_iterator ec; + for (NetDeviceGraphNode::edge_iterator i = e; i != ee; ++i) { + if (! m_nodes[i->second.second].has_other ()) { + ec = i; + ++count; + } + } + + if (count == 1) { // if non-ambiguous, non-assigned + + NetDeviceGraphNode::edge_iterator e_other = std::lower_bound (nother->begin (), nother->end (), *ec); + if (e_other != nother->end () && *e_other == *ec) { + + NetDeviceGraphNode::edge_iterator ee_other = e_other; + ++ee_other; + + while (ee_other != n->end () && *ee_other == *e_other) { + ++ee_other; + } + + size_t count_other = 0; + NetDeviceGraphNode::edge_iterator ec_other; + for (NetDeviceGraphNode::edge_iterator i = e_other; i != ee_other; ++i) { + if (! m_nodes[i->second.second].has_other ()) { + ec_other = i; + ++count_other; + } + } + + if (count_other == 1) { + identify (ec->second.second, ec_other->second.second); + other.identify (ec_other->second.second, ec->second.second); + ++added; + more.push_back (ec->second.second); + } + + } + + } + + e = ee; + + } + + } + + } + + return added; + } + +private: + std::vector m_nodes; + std::map m_device_map; + std::vector m_device_prototypes; + std::map m_net_index; +}; + + +static bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2) +{ + db::NetDeviceGraph g1, g2; + + g1.build (c1); + g2.build (c2); + + while (true) { + + size_t new_identities = 0; + for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { + if (i1->has_other ()) { + new_identities += g1.derive_node_identities (i1 - g1.begin (), g2); + } + } + + bool any_without = false; + for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end () && ! any_without; ++i1) { + any_without = i1->has_other (); + } + + if (! any_without) { + return true; + } + + bool ambiguous = false; + + if (new_identities == 0) { + + // derive new identities through topology + + db::NetDeviceGraph::node_iterator s1 = g1.end (), s2 = g2.end (); + size_t seeds = 0; + + db::NetDeviceGraph::node_iterator i1 = g1.begin (), i2 = g2.begin (); + for ( ; i1 != g1.end () && i2 != g2.end (); ) { + + if (i1->has_other ()) { + ++i1; + } else if (i2->has_other ()) { + ++i2; + } else if (*i1 < *i2) { + seeds = 0; + ++i1; + } else if (*i2 < *i1) { + seeds = 0; + ++i2; + } else { + + if (seeds == 0 || *s1 < *i1) { + + if (seeds == 1) { + // found a candidate - a single node with the same edges + g1.identify (s1 - g1.begin (), s2 - g2.begin ()); + g2.identify (s2 - g2.begin (), s1 - g1.begin ()); + ++new_identities; + } else if (seeds > 1) { + ambiguous = true; + } + + s1 = i1; s2 = i2; + seeds = 1; + + } else { + ++seeds; + } + + ++i1; + ++i2; + + } + + } + + if (seeds == 1) { + // found a candidate - a single node with the same edges + g1.identify (s1 - g1.begin (), s2 - g2.begin ()); + g2.identify (s2 - g2.begin (), s1 - g1.begin ()); + ++new_identities; + } else if (seeds > 1) { + ambiguous = true; + } + + } + + if (new_identities == 0) { + if (ambiguous) { + // @@@ + tl::error << tr ("No seed found - no non-ambiguous nets identified"); + // @@@ + } else { + // @@@ + tl::error << tr ("No seed found - no equivalent nets identified"); + // @@@ + } + return false; + } + + } +} + +} + +TEST(1) +{ + const char *nls2 = + "circuit RINGO ();\n" + " device PMOS $1 (S=$16,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=$16,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $3 (S=$14,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $4 (S=VDD,G=$14,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $5 (S=$12,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $6 (S=VDD,G=$12,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $7 (S='IN,FB',G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $8 (S=VDD,G='IN,FB',D='OUT,OSC') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $9 (S=$4,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $10 (S=VDD,G=$4,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $11 (S=$8,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $12 (S=VDD,G=$8,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $13 (S=$2,G='IN,FB',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $14 (S=VDD,G=$2,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $15 (S=$6,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $16 (S=VDD,G=$6,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $17 (S=$18,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $18 (S=VDD,G=$18,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $19 (S=$10,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $20 (S=VDD,G=$10,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $21 (S='IN,FB',G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $22 (S=VSS,G='IN,FB',D='OUT,OSC') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $23 (S=$18,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $24 (S=VSS,G=$18,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $25 (S=$14,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $26 (S=VSS,G=$14,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $27 (S=$12,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $28 (S=VSS,G=$12,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $29 (S=$4,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $30 (S=VSS,G=$4,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $31 (S=$2,G='IN,FB',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $32 (S=VSS,G=$2,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $33 (S=$8,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $34 (S=VSS,G=$8,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $35 (S=$6,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $36 (S=VSS,G=$6,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $37 (S=$16,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $38 (S=VSS,G=$16,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $39 (S=$10,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $40 (S=VSS,G=$10,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + "end;\n"; + + const char *nls1 = + "circuit RINGO ();\n" + " device PMOS $1 (S=$16,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=$16,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $3 (S=$14,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $4 (S=VDD,G=$14,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $5 (S=$12,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $6 (S=VDD,G=$12,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $7 (S='IN,FB',G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $8 (S=VDD,G='IN,FB',D='OUT,OSC') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $9 (S=$4,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $10 (S=VDD,G=$4,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $11 (S=$8,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $12 (S=VDD,G=$8,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $13 (S=$2,G='IN,FB',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $14 (S=VDD,G=$2,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $15 (S=$6,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $16 (S=VDD,G=$6,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $17 (S=$18,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $18 (S=VDD,G=$18,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $19 (S=$10,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $20 (S=VDD,G=$10,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $21 (S='IN,FB',G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $22 (S=VSS,G='IN,FB',D='OUT,OSC') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $23 (S=$18,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $24 (S=VSS,G=$18,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $25 (S=$14,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $26 (S=VSS,G=$14,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $27 (S=$12,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $28 (S=VSS,G=$12,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $29 (S=$4,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $30 (S=VSS,G=$4,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $31 (S=$2,G='IN,FB',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $32 (S=VSS,G=$2,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $33 (S=$8,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $34 (S=VSS,G=$8,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $35 (S=$6,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $36 (S=VSS,G=$6,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $37 (S=$16,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $38 (S=VSS,G=$16,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $39 (S=$10,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $40 (S=VSS,G=$10,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + "end;\n"; + + db::DeviceClass *dc; + + db::Netlist nl1, nl2; + + db::Netlist *nlp[] = { &nl1, &nl2 }; + for (int i = 0; i < 2; ++i) { + + dc = new db::DeviceClassMOS3Transistor (); + dc->set_name ("PMOS"); + nlp[i]->add_device_class (dc); + + dc = new db::DeviceClassMOS3Transistor (); + dc->set_name ("NMOS"); + nlp[i]->add_device_class (dc); + + } + + nl1.from_string (nls1); + nl2.from_string (nls2); + + db::compare_circuits (nl1.circuit_by_name ("RINGO"), nl2.circuit_by_name ("RINGO")); +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 87fbdd2a0..b5eded222 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -69,7 +69,8 @@ SOURCES = \ dbNetlistWriterTests.cc \ dbCellVariantsTests.cc \ dbDeepEdgesTests.cc \ - dbDeepEdgePairsTests.cc + dbDeepEdgePairsTests.cc \ + dbNetlistCompareTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC From e424a88c90c94ef207655698544cd693c743033e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 21 Mar 2019 22:13:23 +0100 Subject: [PATCH 06/54] WIP: netlist compare algo 1.) Can identify transistor netlists without subcircuits 2.) Ambiguities stay unresolved Next steps: assign ambiguous nets one by one and continue in case of ambiguitites. --- src/db/unit_tests/dbNetlistCompareTests.cc | 64 ++++++++++++++++------ 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index bcd832451..f7baef190 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -6,6 +6,8 @@ #include "dbNetlistDeviceClasses.h" #include "dbHash.h" #include "tlUnitTest.h" +#include "tlProgress.h" +#include "tlTimer.h" namespace db { @@ -14,9 +16,11 @@ struct DeviceCompare { bool operator() (const db::Device *d1, const db::Device *d2) const { - if (d1->device_class () != d2->device_class ()) { + // @@@ TODO: device class identity should not be defined via name + if (d1->device_class () != d2->device_class () && d1->device_class ()->name () != d2->device_class ()->name ()) { return d1->device_class ()->name () < d1->device_class ()->name (); } + const std::vector &dp = d1->device_class ()->parameter_definitions (); for (std::vector::const_iterator i = dp.begin (); i != dp.end (); ++i) { double v1 = d1->parameter_value (i->id ()); @@ -211,6 +215,15 @@ namespace std namespace db { +static std::string net2string (const db::Net *net) +{ + if (! net) { + return "(null)"; + } else { + return net->expanded_name (); + } +} + class NetDeviceGraph { public: @@ -223,6 +236,8 @@ public: void build (const db::Circuit *c) { + tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Building net graph for circuit: ")) + c->name ()); + m_device_map.clear (); m_device_prototypes.clear (); m_nodes.clear (); @@ -247,8 +262,6 @@ public: for (std::vector::iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { i->apply_net_index (m_net_index); } - - // ... } size_t index_for_net (const db::Net *net) const @@ -298,7 +311,7 @@ public: NetDeviceGraphNode::edge_iterator ee = e; ++ee; - while (ee != n->end () && *ee == *e) { + while (ee != n->end () && ee->first == e->first) { ++ee; } @@ -314,12 +327,12 @@ public: if (count == 1) { // if non-ambiguous, non-assigned NetDeviceGraphNode::edge_iterator e_other = std::lower_bound (nother->begin (), nother->end (), *ec); - if (e_other != nother->end () && *e_other == *ec) { + if (e_other != nother->end () && e_other->first == ec->first) { NetDeviceGraphNode::edge_iterator ee_other = e_other; ++ee_other; - while (ee_other != n->end () && *ee_other == *e_other) { + while (ee_other != n->end () && ee_other->first == e_other->first) { ++ee_other; } @@ -333,8 +346,7 @@ public: } if (count_other == 1) { - identify (ec->second.second, ec_other->second.second); - other.identify (ec_other->second.second, ec->second.second); + confirm_identity (*this, begin () + ec->second.second, other, other.begin () + ec_other->second.second); ++added; more.push_back (ec->second.second); } @@ -354,6 +366,15 @@ public: return added; } + static void confirm_identity (db::NetDeviceGraph &g1, db::NetDeviceGraph::node_iterator s1, db::NetDeviceGraph &g2, db::NetDeviceGraph::node_iterator s2) + { + if (tl::verbosity () >= 30) { + tl::log << tl::to_string (tr ("Net identity confirmed: ")) << net2string (s1->net ()) << " - " << net2string (s2->net ()); + } + g1.identify (s1 - g1.begin (), s2 - g2.begin ()); + g2.identify (s2 - g2.begin (), s1 - g1.begin ()); + } + private: std::vector m_nodes; std::map m_device_map; @@ -361,7 +382,6 @@ private: std::map m_net_index; }; - static bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2) { db::NetDeviceGraph g1, g2; @@ -380,7 +400,7 @@ static bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2) bool any_without = false; for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end () && ! any_without; ++i1) { - any_without = i1->has_other (); + any_without = ! i1->has_other (); } if (! any_without) { @@ -415,8 +435,7 @@ static bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2) if (seeds == 1) { // found a candidate - a single node with the same edges - g1.identify (s1 - g1.begin (), s2 - g2.begin ()); - g2.identify (s2 - g2.begin (), s1 - g1.begin ()); + db::NetDeviceGraph::confirm_identity (g1, s1, g2, s2); ++new_identities; } else if (seeds > 1) { ambiguous = true; @@ -438,8 +457,7 @@ static bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2) if (seeds == 1) { // found a candidate - a single node with the same edges - g1.identify (s1 - g1.begin (), s2 - g2.begin ()); - g2.identify (s2 - g2.begin (), s1 - g1.begin ()); + db::NetDeviceGraph::confirm_identity (g1, s1, g2, s2); ++new_identities; } else if (seeds > 1) { ambiguous = true; @@ -448,15 +466,25 @@ static bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2) } if (new_identities == 0) { + // @@@ if (ambiguous) { - // @@@ tl::error << tr ("No seed found - no non-ambiguous nets identified"); - // @@@ } else { - // @@@ tl::error << tr ("No seed found - no equivalent nets identified"); - // @@@ } + tl::error << tr ("Unassigned in netlist A:"); + for (db::NetDeviceGraph::node_iterator i = g1.begin (); i != g1.end (); ++i) { + if (! i->has_other ()) { + tl::error << " " << net2string (i->net ()); + } + } + tl::error << tr ("Unassigned in netlist B:"); + for (db::NetDeviceGraph::node_iterator i = g2.begin (); i != g2.end (); ++i) { + if (! i->has_other ()) { + tl::error << " " << net2string (i->net ()); + } + } + // @@@ return false; } From 25b7ab9dab59b628e6a7b802a441a7b81fa1ad9d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 23 Mar 2019 10:31:29 +0100 Subject: [PATCH 07/54] WIP: netlist comparer --- src/db/unit_tests/dbNetlistCompareTests.cc | 303 +++++++++++++++++++-- 1 file changed, 282 insertions(+), 21 deletions(-) diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index f7baef190..01ef3142b 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -12,6 +12,94 @@ namespace db { +class NetlistCompareLogger +{ +public: + NetlistCompareLogger () { } + virtual ~NetlistCompareLogger () { } + + /** + * @brief Begin logging for netlist a and b + */ + virtual void begin_netlist (const db::Netlist * /*a*/, const db::Netlist * /*b*/) { } + + /** + * @brief End logging for netlist a and b + */ + virtual void end_netlist (const db::Netlist * /*a*/, const db::Netlist * /*b*/) { } + + /** + * @brief Begin logging for circuit a and b + */ + virtual void begin_circuit (const db::Circuit * /*a*/, const db::Circuit * /*b*/) { } + + /** + * @brief End logging for circuit a and b + */ + virtual void end_circuit (const db::Circuit * /*a*/, const db::Circuit * /*b*/, bool /*matching*/) { } + + /** + * @brief There is a circuit mismatch + * "a" is null if there is no match for b and vice versa. + */ + virtual void circuit_mismatch (const db::Circuit * /*a*/, const db::Circuit * /*b*/) { } + + /** + * @brief Nets a and b match exactly + */ + virtual void match_nets (const db::Net * /*a*/, const db::Net * /*b*/) { } + + /** + * @brief Nets a and b are matched, but are ambiguous + * Other nets might also match with a and also with b. Matching this a and b is + * an arbitrary decision. + */ + virtual void match_ambiguous_nets (const db::Net * /*a*/, const db::Net * /*b*/) { } + + /** + * @brief Net a or b doesn't match + * "a" is null if there is no match for b and vice versa. + */ + virtual void net_mismatch (const db::Net * /*a*/, const db::Net * /*b*/) { } + + /** + * @brief Devices a and b match exactly + */ + virtual void match_devices (const db::Device * /*a*/, const db::Device * /*b*/) { } + + /** + * @brief Devices a and b are matched but have different parameters + */ + virtual void match_devices_with_different_parameters (const db::Device * /*a*/, const db::Device * /*b*/) { } + + /** + * @brief Devices a and b are matched but have different device classes + */ + virtual void match_devices_with_different_device_classes (const db::Device * /*a*/, const db::Device * /*b*/) { } + + /** + * @brief Pins a and b of the current circuit are matched + */ + virtual void match_pins (const db::Pin * /*a*/, const db::Pin * /*b*/) { } + + /** + * @brief Pin a or b doesn't match + * "a" is null if there is no match for b and vice versa. + */ + virtual void pin_mismatch (const db::Pin * /*a*/, const db::Pin * /*b*/) { } + + /** + * @brief Subcircuits a and b match exactly + */ + virtual void match_subcircuits (const db::SubCircuit * /*a*/, const db::SubCircuit * /*b*/) { } + + /** + * @brief SubCircuit a or b doesn't match + * "a" is null if there is no match for b and vice versa. + */ + virtual void subcircuit_mismatch (const db::SubCircuit * /*a*/, const db::SubCircuit * /*b*/) { } +}; + struct DeviceCompare { bool operator() (const db::Device *d1, const db::Device *d2) const @@ -215,15 +303,6 @@ namespace std namespace db { -static std::string net2string (const db::Net *net) -{ - if (! net) { - return "(null)"; - } else { - return net->expanded_name (); - } -} - class NetDeviceGraph { public: @@ -286,7 +365,7 @@ public: return m_nodes.end (); } - size_t derive_node_identities (size_t net_index, NetDeviceGraph &other) + size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, NetlistCompareLogger *logger) { size_t added = 0; @@ -346,7 +425,7 @@ public: } if (count_other == 1) { - confirm_identity (*this, begin () + ec->second.second, other, other.begin () + ec_other->second.second); + confirm_identity (*this, begin () + ec->second.second, other, other.begin () + ec_other->second.second, logger); ++added; more.push_back (ec->second.second); } @@ -366,10 +445,10 @@ public: return added; } - static void confirm_identity (db::NetDeviceGraph &g1, db::NetDeviceGraph::node_iterator s1, db::NetDeviceGraph &g2, db::NetDeviceGraph::node_iterator s2) + static void confirm_identity (db::NetDeviceGraph &g1, db::NetDeviceGraph::node_iterator s1, db::NetDeviceGraph &g2, db::NetDeviceGraph::node_iterator s2, db::NetlistCompareLogger *logger) { - if (tl::verbosity () >= 30) { - tl::log << tl::to_string (tr ("Net identity confirmed: ")) << net2string (s1->net ()) << " - " << net2string (s2->net ()); + if (logger) { + logger->match_nets (s1->net (), s2->net ()); } g1.identify (s1 - g1.begin (), s2 - g2.begin ()); g2.identify (s2 - g2.begin (), s1 - g1.begin ()); @@ -382,7 +461,75 @@ private: std::map m_net_index; }; -static bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2) +class NetlistComparer +{ +public: + NetlistComparer (NetlistCompareLogger *logger) + : mp_logger (logger) + { } + + bool compare (const db::Netlist *a, const db::Netlist *b) const + { + bool good = true; + + std::map > name2circuits; + + for (db::Netlist::const_circuit_iterator i = a->begin_circuits (); i != a->end_circuits (); ++i) { + name2circuits[i->name ()].first = i.operator-> (); + } + + for (db::Netlist::const_circuit_iterator i = b->begin_circuits (); i != b->end_circuits (); ++i) { + name2circuits[i->name ()].second = i.operator-> (); + } + + if (mp_logger) { + mp_logger->begin_netlist (a, b); + } + + for (std::map >::const_iterator i = name2circuits.begin (); i != name2circuits.end (); ++i) { + if (! i->second.first || ! i->second.second) { + good = false; + if (mp_logger) { + mp_logger->circuit_mismatch (i->second.first, i->second.second); + } + } + } + + for (db::Netlist::const_bottom_up_circuit_iterator c = a->begin_bottom_up (); c != a->end_bottom_up (); ++c) { + + std::map >::const_iterator i = name2circuits.find ((*c)->name ()); + tl_assert (i != name2circuits.end ()); + + if (i->second.first && i->second.second) { + if (mp_logger) { + mp_logger->begin_circuit (i->second.first, i->second.second); + } + bool g = compare_circuits (i->second.first, i->second.second); + if (! g) { + good = false; + } + if (mp_logger) { + mp_logger->end_circuit (i->second.first, i->second.second, g); + } + } + + } + + if (mp_logger) { + mp_logger->begin_netlist (a, b); + } + + return good; + } + +protected: + bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2) const; + + NetlistCompareLogger *mp_logger; +}; + +bool +NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2) const { db::NetDeviceGraph g1, g2; @@ -394,7 +541,7 @@ static bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2) size_t new_identities = 0; for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { if (i1->has_other ()) { - new_identities += g1.derive_node_identities (i1 - g1.begin (), g2); + new_identities += g1.derive_node_identities (i1 - g1.begin (), g2, mp_logger); } } @@ -435,7 +582,7 @@ static bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2) if (seeds == 1) { // found a candidate - a single node with the same edges - db::NetDeviceGraph::confirm_identity (g1, s1, g2, s2); + db::NetDeviceGraph::confirm_identity (g1, s1, g2, s2, mp_logger); ++new_identities; } else if (seeds > 1) { ambiguous = true; @@ -457,7 +604,7 @@ static bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2) if (seeds == 1) { // found a candidate - a single node with the same edges - db::NetDeviceGraph::confirm_identity (g1, s1, g2, s2); + db::NetDeviceGraph::confirm_identity (g1, s1, g2, s2, mp_logger); ++new_identities; } else if (seeds > 1) { ambiguous = true; @@ -475,13 +622,13 @@ static bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2) tl::error << tr ("Unassigned in netlist A:"); for (db::NetDeviceGraph::node_iterator i = g1.begin (); i != g1.end (); ++i) { if (! i->has_other ()) { - tl::error << " " << net2string (i->net ()); + tl::error << " " << i->net ()->expanded_name (); } } tl::error << tr ("Unassigned in netlist B:"); for (db::NetDeviceGraph::node_iterator i = g2.begin (); i != g2.end (); ++i) { if (! i->has_other ()) { - tl::error << " " << net2string (i->net ()); + tl::error << " " << i->net ()->expanded_name (); } } // @@@ @@ -493,6 +640,112 @@ static bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2) } +class NetlistCompareTestLogger + : public db::NetlistCompareLogger +{ +public: + NetlistCompareTestLogger () { } + + virtual void begin_circuit (const db::Circuit *a, const db::Circuit *b) + { + m_texts.push_back ("begin_circuit " + circuit2str (a) + " " + circuit2str (b)); + } + + virtual void end_circuit (const db::Circuit *a, const db::Circuit *b, bool matching) + { + m_texts.push_back ("end_circuit " + circuit2str (a) + " " + circuit2str (b) + " " + (matching ? "MATCH" : "NOMATCH")); + } + + virtual void circuit_mismatch (const db::Circuit *a, const db::Circuit *b) + { + m_texts.push_back ("circuit_mismatch " + circuit2str (a) + " " + circuit2str (b)); + } + + virtual void match_nets (const db::Net *a, const db::Net *b) + { + m_texts.push_back ("match_nets " + net2str (a) + " " + net2str (b)); + } + + virtual void match_ambiguous_nets (const db::Net *a, const db::Net *b) + { + m_texts.push_back ("match_ambiguous_nets " + net2str (a) + " " + net2str (b)); + } + + virtual void net_mismatch (const db::Net *a, const db::Net *b) + { + m_texts.push_back ("net_mismatch " + net2str (a) + " " + net2str (b)); + } + + virtual void match_devices (const db::Device *a, const db::Device *b) + { + m_texts.push_back ("match_devices " + device2str (a) + " " + device2str (b)); + } + + virtual void match_devices_with_different_parameters (const db::Device *a, const db::Device *b) + { + m_texts.push_back ("match_devices_with_different_parameters " + device2str (a) + " " + device2str (b)); + } + + virtual void match_devices_with_different_device_classes (const db::Device *a, const db::Device *b) + { + m_texts.push_back ("match_devices_with_different_device_classes " + device2str (a) + " " + device2str (b)); + } + + virtual void match_pins (const db::Pin *a, const db::Pin *b) + { + m_texts.push_back ("match_pins " + pin2str (a) + " " + pin2str (b)); + } + + virtual void pin_mismatch (const db::Pin *a, const db::Pin *b) + { + m_texts.push_back ("pin_mismatch " + pin2str (a) + " " + pin2str (b)); + } + + virtual void match_subcircuits (const db::SubCircuit *a, const db::SubCircuit *b) + { + m_texts.push_back ("match_subcircuits " + subcircuit2str (a) + " " + subcircuit2str (b)); + } + + virtual void subcircuit_mismatch (const db::SubCircuit *a, const db::SubCircuit *b) + { + m_texts.push_back ("subcircuit_mismatch " + subcircuit2str (a) + " " + subcircuit2str (b)); + } + + std::string text () const + { + return tl::join (m_texts, "\n"); + } + +private: + std::vector m_texts; + + std::string circuit2str (const db::Circuit *x) const + { + return x ? x->name () : "(null)"; + } + + std::string device2str (const db::Device *x) const + { + return x ? x->expanded_name () : "(null)"; + } + + std::string net2str (const db::Net *x) const + { + return x ? x->expanded_name () : "(null)"; + } + + std::string pin2str (const db::Pin *x) const + { + return x ? x->expanded_name () : "(null)"; + } + + std::string subcircuit2str (const db::SubCircuit *x) const + { + return x ? x->expanded_name () : "(null)"; + } +}; + + TEST(1) { const char *nls2 = @@ -603,5 +856,13 @@ TEST(1) nl1.from_string (nls1); nl2.from_string (nls2); - db::compare_circuits (nl1.circuit_by_name ("RINGO"), nl2.circuit_by_name ("RINGO")); + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "" + ); + EXPECT_EQ (good, true); } From bb2d3765b83f75a88ba4d0127f6518ef997b410d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2019 00:45:58 +0100 Subject: [PATCH 08/54] WIP: netlist compare, ambiguous net resolution, device mapping. --- src/db/unit_tests/dbNetlistCompareTests.cc | 579 ++++++++++++++++++--- 1 file changed, 511 insertions(+), 68 deletions(-) diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 01ef3142b..834120c52 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -38,6 +38,12 @@ public: */ virtual void end_circuit (const db::Circuit * /*a*/, const db::Circuit * /*b*/, bool /*matching*/) { } + /** + * @brief Circuits are skipped + * Circuits are skipped if their subcircuits could not be matched. + */ + virtual void circuit_skipped (const db::Circuit * /*a*/, const db::Circuit * /*b*/) { } + /** * @brief There is a circuit mismatch * "a" is null if there is no match for b and vice versa. @@ -77,6 +83,12 @@ public: */ virtual void match_devices_with_different_device_classes (const db::Device * /*a*/, const db::Device * /*b*/) { } + /** + * @brief Device a or b doesn't match + * "a" is null if there is no match for b and vice versa. + */ + virtual void device_mismatch (const db::Device * /*a*/, const db::Device * /*b*/) { } + /** * @brief Pins a and b of the current circuit are matched */ @@ -106,7 +118,7 @@ struct DeviceCompare { // @@@ TODO: device class identity should not be defined via name if (d1->device_class () != d2->device_class () && d1->device_class ()->name () != d2->device_class ()->name ()) { - return d1->device_class ()->name () < d1->device_class ()->name (); + return d1->device_class ()->name () < d2->device_class ()->name (); } const std::vector &dp = d1->device_class ()->parameter_definitions (); @@ -267,6 +279,19 @@ public: return false; } + bool operator== (const NetDeviceGraphNode &node) const + { + if (m_edges.size () != node.m_edges.size ()) { + return false; + } + for (size_t i = 0; i < m_edges.size (); ++i) { + if (m_edges [i].first != node.m_edges [i].first) { + return false; + } + } + return true; + } + void swap (NetDeviceGraphNode &other) { std::swap (m_other_net_index, other.m_other_net_index); @@ -343,13 +368,18 @@ public: } } - size_t index_for_net (const db::Net *net) const + size_t node_index_for_net (const db::Net *net) const { std::map::const_iterator j = m_net_index.find (net); tl_assert (j != m_net_index.end ()); return j->second; } + const db::Net *net_by_node_index (size_t net_index) const + { + return m_nodes [net_index].net (); + } + void identify (size_t net_index, size_t other_net_index) { m_nodes [net_index].set_other_net (other_net_index); @@ -445,10 +475,14 @@ public: return added; } - static void confirm_identity (db::NetDeviceGraph &g1, db::NetDeviceGraph::node_iterator s1, db::NetDeviceGraph &g2, db::NetDeviceGraph::node_iterator s2, db::NetlistCompareLogger *logger) + static void confirm_identity (db::NetDeviceGraph &g1, db::NetDeviceGraph::node_iterator s1, db::NetDeviceGraph &g2, db::NetDeviceGraph::node_iterator s2, db::NetlistCompareLogger *logger, bool ambiguous = false) { if (logger) { - logger->match_nets (s1->net (), s2->net ()); + if (ambiguous) { + logger->match_ambiguous_nets (s1->net (), s2->net ()); + } else { + logger->match_nets (s1->net (), s2->net ()); + } } g1.identify (s1 - g1.begin (), s2 - g2.begin ()); g2.identify (s2 - g2.begin (), s1 - g1.begin ()); @@ -468,6 +502,11 @@ public: : mp_logger (logger) { } + void same_nets (const db::Circuit *a, const db::Net *na, const db::Circuit *b, const db::Net *nb) + { + m_same_nets [std::make_pair (a, b)].push_back (std::make_pair (na, nb)); + } + bool compare (const db::Netlist *a, const db::Netlist *b) const { bool good = true; @@ -495,22 +534,54 @@ public: } } + std::set verified_circuits_a, verified_circuits_b; + for (db::Netlist::const_bottom_up_circuit_iterator c = a->begin_bottom_up (); c != a->end_bottom_up (); ++c) { std::map >::const_iterator i = name2circuits.find ((*c)->name ()); tl_assert (i != name2circuits.end ()); if (i->second.first && i->second.second) { - if (mp_logger) { - mp_logger->begin_circuit (i->second.first, i->second.second); + + const db::Circuit *ca = i->second.first; + const db::Circuit *cb = i->second.second; + + std::vector > empty; + const std::vector > *net_identity = ∅ + std::map, std::vector > >::const_iterator sn = m_same_nets.find (std::make_pair (ca, cb)); + if (sn != m_same_nets.end ()) { + net_identity = &sn->second; } - bool g = compare_circuits (i->second.first, i->second.second); - if (! g) { - good = false; - } - if (mp_logger) { - mp_logger->end_circuit (i->second.first, i->second.second, g); + + if (all_subcircuits_verified (ca, verified_circuits_a) && all_subcircuits_verified (cb, verified_circuits_b)) { + + if (mp_logger) { + mp_logger->begin_circuit (ca, cb); + } + + bool pin_mismatch = false; + bool g = compare_circuits (ca, cb, *net_identity, pin_mismatch); + if (! g) { + good = false; + } + + if (! pin_mismatch) { + verified_circuits_a.insert (ca); + verified_circuits_b.insert (cb); + } + + if (mp_logger) { + mp_logger->end_circuit (ca, cb, g); + } + + } else { + + if (mp_logger) { + mp_logger->circuit_skipped (ca, cb); + } + } + } } @@ -523,20 +594,59 @@ public: } protected: - bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2) const; + bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2, const std::vector > &net_identity, bool &pin_mismatch) const; + bool all_subcircuits_verified (const db::Circuit *c, const std::set &verified_circuits) const; NetlistCompareLogger *mp_logger; + std::map, std::vector > > m_same_nets; }; bool -NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2) const +NetlistComparer::all_subcircuits_verified (const db::Circuit *c, const std::set &verified_circuits) const +{ + for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) { + if (verified_circuits.find (sc->circuit_ref ()) == verified_circuits.end ()) { + return false; + } + } + return true; +} + +static std::vector > +compute_device_key (const db::Device &device, const db::NetDeviceGraph &g) +{ + std::vector > k; + + const std::vector &td = device.device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + size_t terminal_id = translate_terminal_id (t->id (), &device); + const db::Net *net = device.net_for_terminal (t->id ()); + size_t net_id = g.node_index_for_net (net); + k.push_back (std::make_pair (terminal_id, net_id)); + } + + std::sort (k.begin (), k.end ()); + + return k; +} + +bool +NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, const std::vector > &net_identity, bool &pin_mismatch) const { db::NetDeviceGraph g1, g2; g1.build (c1); g2.build (c2); - while (true) { + for (std::vector >::const_iterator p = net_identity.begin (); p != net_identity.end (); ++p) { + size_t ni1 = g1.node_index_for_net (p->first); + size_t ni2 = g2.node_index_for_net (p->second); + g1.identify (ni1, ni2); + g2.identify (ni2, ni1); + } + + bool good = true; + while (good) { size_t new_identities = 0; for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { @@ -551,18 +661,13 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2) } if (! any_without) { - return true; + break; } - bool ambiguous = false; - if (new_identities == 0) { // derive new identities through topology - db::NetDeviceGraph::node_iterator s1 = g1.end (), s2 = g2.end (); - size_t seeds = 0; - db::NetDeviceGraph::node_iterator i1 = g1.begin (), i2 = g2.begin (); for ( ; i1 != g1.end () && i2 != g2.end (); ) { @@ -571,71 +676,153 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2) } else if (i2->has_other ()) { ++i2; } else if (*i1 < *i2) { - seeds = 0; ++i1; } else if (*i2 < *i1) { - seeds = 0; ++i2; } else { - if (seeds == 0 || *s1 < *i1) { - - if (seeds == 1) { - // found a candidate - a single node with the same edges - db::NetDeviceGraph::confirm_identity (g1, s1, g2, s2, mp_logger); - ++new_identities; - } else if (seeds > 1) { - ambiguous = true; - } - - s1 = i1; s2 = i2; - seeds = 1; - - } else { - ++seeds; - } + db::NetDeviceGraph::node_iterator ii1 = i1, ii2 = i2; ++i1; ++i2; + bool ambiguous = (i1 != g1.end () && *i1 == *ii1) || (i2 != g2.end () && *i2 == *ii2); + + // found a candidate - a single node with the same edges + db::NetDeviceGraph::confirm_identity (g1, ii1, g2, ii2, mp_logger, ambiguous); + ++new_identities; + } } - if (seeds == 1) { - // found a candidate - a single node with the same edges - db::NetDeviceGraph::confirm_identity (g1, s1, g2, s2, mp_logger); - ++new_identities; - } else if (seeds > 1) { - ambiguous = true; - } - } if (new_identities == 0) { - // @@@ - if (ambiguous) { - tl::error << tr ("No seed found - no non-ambiguous nets identified"); - } else { - tl::error << tr ("No seed found - no equivalent nets identified"); - } - tl::error << tr ("Unassigned in netlist A:"); - for (db::NetDeviceGraph::node_iterator i = g1.begin (); i != g1.end (); ++i) { - if (! i->has_other ()) { - tl::error << " " << i->net ()->expanded_name (); - } - } - tl::error << tr ("Unassigned in netlist B:"); - for (db::NetDeviceGraph::node_iterator i = g2.begin (); i != g2.end (); ++i) { - if (! i->has_other ()) { - tl::error << " " << i->net ()->expanded_name (); - } - } - // @@@ - return false; + good = false; } } + + // Report missing net assignment + + for (db::NetDeviceGraph::node_iterator i = g1.begin (); i != g1.end (); ++i) { + if (! i->has_other ()) { + mp_logger->net_mismatch (i->net (), 0); + } + } + tl::error << tr ("Unassigned in netlist B:"); + for (db::NetDeviceGraph::node_iterator i = g2.begin (); i != g2.end (); ++i) { + if (! i->has_other ()) { + mp_logger->net_mismatch (0, i->net ()); + } + } + + + // Report pin assignment + + std::multimap net2pin; + for (db::Circuit::const_pin_iterator p = c2->begin_pins (); p != c2->end_pins (); ++p) { + const db::Net *net = c2->net_for_pin (p->id ()); + tl_assert (net != 0); + net2pin.insert (std::make_pair (net, p.operator-> ())); + } + + for (db::NetDeviceGraph::node_iterator i = g1.begin (); i != g1.end (); ++i) { + + const db::Net *net = i->net (); + tl_assert (net != 0); + + if (net->pin_count () == 0) { + continue; + } + + if (! i->has_other ()) { + for (db::Net::const_pin_iterator pi = net->begin_pins (); pi != net->end_pins (); ++pi) { + mp_logger->pin_mismatch (pi->pin (), 0); + pin_mismatch = true; + } + continue; + } + + const db::Net *other_net = g2.net_by_node_index (i->other_net_index ()); + + std::multimap::iterator np = net2pin.find (other_net); + for (db::Net::const_pin_iterator pi = net->begin_pins (); pi != net->end_pins (); ++pi) { + + if (np != net2pin.end () && np->first == other_net) { + + mp_logger->match_pins (pi->pin (), np->second); + + std::multimap::iterator np_delete = np; + ++np; + net2pin.erase (np_delete); + + } else { + mp_logger->pin_mismatch (pi->pin (), 0); + pin_mismatch = true; + } + + } + + } + + for (std::multimap::iterator np = net2pin.begin (); np != net2pin.end (); ++np) { + mp_logger->pin_mismatch (0, np->second); + pin_mismatch = true; + } + + // Report device assignment + + std::map >, const db::Device *> device_map; + + for (db::Circuit::const_device_iterator d = c1->begin_devices (); d != c1->end_devices (); ++d) { + + std::vector > k = compute_device_key (*d, g1); + + bool mapped = true; + for (std::vector >::iterator i = k.begin (); i != k.end () && mapped; ++i) { + if (! g1.begin () [i->second].has_other ()) { + mapped = false; + } + } + + if (! mapped) { + mp_logger->device_mismatch (d.operator-> (), 0); + } else { + // TODO: report devices which cannot be distiguished topologically? + device_map[k] = d.operator-> (); + } + + } + + for (db::Circuit::const_device_iterator d = c2->begin_devices (); d != c2->end_devices (); ++d) { + + std::vector > k = compute_device_key (*d, g2); + + bool mapped = true; + for (std::vector >::iterator i = k.begin (); i != k.end (); ++i) { + if (! g1.begin () [i->second].has_other ()) { + mapped = false; + } else { + i->second = g2.begin () [i->second].other_net_index (); + } + } + + std::sort (k.begin (), k.end ()); + + std::map >, const db::Device *>::const_iterator dm = device_map.find (k); + + if (! mapped || dm == device_map.end ()) { + mp_logger->device_mismatch (0, d.operator-> ()); + } else { + // @@@ compare parameters, device class + mp_logger->match_devices (dm->second, d.operator-> ()); + } + + } + + return good; } } @@ -656,6 +843,11 @@ public: m_texts.push_back ("end_circuit " + circuit2str (a) + " " + circuit2str (b) + " " + (matching ? "MATCH" : "NOMATCH")); } + virtual void circuit_skipped (const db::Circuit *a, const db::Circuit *b) + { + m_texts.push_back ("circuit_skipped " + circuit2str (a) + " " + circuit2str (b)); + } + virtual void circuit_mismatch (const db::Circuit *a, const db::Circuit *b) { m_texts.push_back ("circuit_mismatch " + circuit2str (a) + " " + circuit2str (b)); @@ -681,6 +873,11 @@ public: m_texts.push_back ("match_devices " + device2str (a) + " " + device2str (b)); } + virtual void device_mismatch (const db::Device *a, const db::Device *b) + { + m_texts.push_back ("device_mismatch " + device2str (a) + " " + device2str (b)); + } + virtual void match_devices_with_different_parameters (const db::Device *a, const db::Device *b) { m_texts.push_back ("match_devices_with_different_parameters " + device2str (a) + " " + device2str (b)); @@ -745,8 +942,253 @@ private: } }; +static void prep_nl (db::Netlist &nl, const char *str) +{ + db::DeviceClass *dc; -TEST(1) + dc = new db::DeviceClassMOS3Transistor (); + dc->set_name ("PMOS"); + nl.add_device_class (dc); + + dc = new db::DeviceClassMOS3Transistor (); + dc->set_name ("NMOS"); + nl.add_device_class (dc); + + nl.from_string (str); +} + +TEST(1_SimpleInverter) +{ + const char *nls1 = + "circuit INV ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $2 (S=VSS,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + const char *nls2 = + "circuit INV ($1=VDD,$2=IN,$3=VSS,$4=OUT);\n" + " device NMOS $1 (S=OUT,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit INV INV\n" + "match_nets VSS VSS\n" + "match_nets VDD VDD\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_pins $3 $2\n" + "match_pins $2 $0\n" + "match_pins $1 $3\n" + "match_pins $0 $1\n" + "match_devices $2 $1\n" + "match_devices $1 $2\n" + "end_circuit INV INV MATCH" + ); + EXPECT_EQ (good, true); +} + +TEST(2_SimpleInverterWithForcedNetAssignment) +{ + const char *nls1 = + "circuit INV ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $2 (S=VSS,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + const char *nls2 = + "circuit INV ($1=VDD,$2=IN,$3=VSS,$4=OUT);\n" + " device NMOS $1 (S=OUT,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + const db::Circuit *ca = nl1.circuit_by_name ("INV"); + const db::Circuit *cb = nl2.circuit_by_name ("INV"); + comp.same_nets (ca, ca->net_by_name ("VDD"), cb, cb->net_by_name ("VDD")); + comp.same_nets (ca, ca->net_by_name ("VSS"), cb, cb->net_by_name ("VSS")); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit INV INV\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_pins $3 $2\n" + "match_pins $2 $0\n" + "match_pins $1 $3\n" + "match_pins $0 $1\n" + "match_devices $2 $1\n" + "match_devices $1 $2\n" + "end_circuit INV INV MATCH" + ); + EXPECT_EQ (good, true); +} + +TEST(3_Buffer) +{ + const char *nls1 = + "circuit BUF ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $2 (S=VSS,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $3 (S=VDD,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=VSS,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + const char *nls2 = + "circuit BUF ($1=VDD,$2=IN,$3=VSS,$4=OUT);\n" + " device PMOS $1 (S=VDD,G=IN,D=$10) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=$10,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $3 (S=$10,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=OUT,G=$10,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit BUF BUF\n" + "match_nets VSS VSS\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_nets VDD VDD\n" + "match_nets INT $10\n" + "match_pins $3 $2\n" + "match_pins $1 $3\n" + "match_pins $0 $1\n" + "match_pins $2 $0\n" + "match_devices $1 $1\n" + "match_devices $3 $2\n" + "match_devices $2 $3\n" + "match_devices $4 $4\n" + "end_circuit BUF BUF MATCH" + ); + EXPECT_EQ (good, true); +} + +TEST(4_BufferTwoPaths) +{ + const char *nls1 = + "circuit BUF ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $2 (S=VSS,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $3 (S=VDD,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=VSS,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $5 (S=VDD,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $6 (S=VSS,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $7 (S=VDD,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $8 (S=VSS,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + const char *nls2 = + "circuit BUF ($1=VDD,$2=IN,$3=VSS,$4=OUT);\n" + " device PMOS $1 (S=VDD,G=IN,D=$10) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=$10,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $3 (S=VDD,G=IN,D=$11) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $4 (S=VDD,G=$11,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $5 (S=$10,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $6 (S=OUT,G=$10,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $7 (S=$11,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $8 (S=OUT,G=$11,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit BUF BUF\n" + "match_nets VSS VSS\n" + "match_nets OUT OUT\n" + "match_ambiguous_nets INT $10\n" + "match_nets INT2 $11\n" + "match_nets IN IN\n" + "match_nets VDD VDD\n" + "match_pins $3 $2\n" + "match_pins $1 $3\n" + "match_pins $0 $1\n" + "match_pins $2 $0\n" + "match_devices $1 $1\n" + "match_devices $3 $2\n" + "match_devices $5 $3\n" + "match_devices $7 $4\n" + "match_devices $2 $5\n" + "match_devices $4 $6\n" + "match_devices $6 $7\n" + "match_devices $8 $8\n" + "end_circuit BUF BUF MATCH" + ); + EXPECT_EQ (good, true); +} + +TEST(100_SimpleInverterWithWrongTransistorParameter) +{ +return; // @@@ doesn't work yet. + const char *nls1 = + "circuit INV ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $2 (S=VSS,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + const char *nls2 = + "circuit INV ($1=VDD,$2=IN,$3=VSS,$4=OUT);\n" + " device NMOS $1 (S=OUT,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=IN,D=OUT) (L=0.30,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + const db::Circuit *ca = nl1.circuit_by_name ("INV"); + const db::Circuit *cb = nl2.circuit_by_name ("INV"); + comp.same_nets (ca, ca->net_by_name ("VDD"), cb, cb->net_by_name ("VDD")); + comp.same_nets (ca, ca->net_by_name ("VSS"), cb, cb->net_by_name ("VSS")); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit INV INV\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_pins $3 $2\n" + "match_pins $2 $0\n" + "match_pins $1 $3\n" + "match_pins $0 $1\n" + "end_circuit INV INV MATCH" + ); + EXPECT_EQ (good, true); +} + +#if 0 +TEST(2) { const char *nls2 = "circuit RINGO ();\n" @@ -866,3 +1308,4 @@ TEST(1) ); EXPECT_EQ (good, true); } +#endif From 55052038ea8b7c398a9927f83eea2c8118871ea7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 24 Mar 2019 21:14:08 +0100 Subject: [PATCH 09/54] WIP: netlist compare --- src/db/unit_tests/dbNetlistCompareTests.cc | 215 ++++++++++++++++----- 1 file changed, 172 insertions(+), 43 deletions(-) diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 834120c52..cd5236893 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -182,7 +182,15 @@ public: } }; - typedef std::vector, std::pair > >::const_iterator edge_iterator; + struct EdgeToEdgeOnlyCompare + { + bool operator() (const std::pair, std::pair > &a, const std::vector &b) const + { + return a.first < b; + } + }; + + typedef std::vector, std::pair > >::const_iterator edge_iterator; NetDeviceGraphNode (const db::Net *net, std::map &devmap, std::vector &device_prototypes) : mp_net (net), m_other_net_index (std::numeric_limits::max ()) @@ -218,7 +226,7 @@ public: std::map::const_iterator in = n2entry.find (net2); if (in == n2entry.end ()) { in = n2entry.insert (std::make_pair (net2, m_edges.size ())).first; - m_edges.push_back (std::make_pair (std::vector (), std::make_pair (net2, size_t (0)))); + m_edges.push_back (std::make_pair (std::vector (), std::make_pair (size_t (0), net2))); } m_edges [in->second].first.push_back (ed2); @@ -228,13 +236,6 @@ public: } } - - // "deep sorting" of the edge descriptor - for (std::vector, std::pair > >::iterator i = m_edges.begin (); i != m_edges.end (); ++i) { - std::sort (i->first.begin (), i->first.end ()); - } - - std::sort (m_edges.begin (), m_edges.end ()); } const db::Net *net () const @@ -259,11 +260,18 @@ public: void apply_net_index (const std::map &ni) { - for (std::vector, std::pair > >::iterator i = m_edges.begin (); i != m_edges.end (); ++i) { - std::map::const_iterator j = ni.find (i->second.first); + for (std::vector, std::pair > >::iterator i = m_edges.begin (); i != m_edges.end (); ++i) { + std::map::const_iterator j = ni.find (i->second.second); tl_assert (j != ni.end ()); - i->second.second = j->second; + i->second.first = j->second; } + + // "deep sorting" of the edge descriptor + for (std::vector, std::pair > >::iterator i = m_edges.begin (); i != m_edges.end (); ++i) { + std::sort (i->first.begin (), i->first.end ()); + } + + std::sort (m_edges.begin (), m_edges.end ()); } bool operator< (const NetDeviceGraphNode &node) const @@ -309,10 +317,20 @@ public: return m_edges.end (); } + edge_iterator find_edge (const std::vector &edge) const + { + edge_iterator res = std::lower_bound (begin (), end (), edge, NetDeviceGraphNode::EdgeToEdgeOnlyCompare ()); + if (res->first != edge) { + return end (); + } else { + return res; + } + } + private: const db::Net *mp_net; size_t m_other_net_index; - std::vector, std::pair > > m_edges; + std::vector, std::pair > > m_edges; }; } @@ -427,7 +445,7 @@ public: size_t count = 0; NetDeviceGraphNode::edge_iterator ec; for (NetDeviceGraphNode::edge_iterator i = e; i != ee; ++i) { - if (! m_nodes[i->second.second].has_other ()) { + if (! m_nodes[i->second.first].has_other ()) { ec = i; ++count; } @@ -435,29 +453,39 @@ public: if (count == 1) { // if non-ambiguous, non-assigned - NetDeviceGraphNode::edge_iterator e_other = std::lower_bound (nother->begin (), nother->end (), *ec); - if (e_other != nother->end () && e_other->first == ec->first) { +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::log << "considering " << n->net ()->expanded_name () << " to " << ec->second.second->expanded_name (); +#endif + NetDeviceGraphNode::edge_iterator e_other = nother->find_edge (ec->first); + if (e_other != nother->end ()) { + +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::log << "candidate accepted"; +#endif NetDeviceGraphNode::edge_iterator ee_other = e_other; ++ee_other; - while (ee_other != n->end () && ee_other->first == e_other->first) { + while (ee_other != nother->end () && ee_other->first == e_other->first) { ++ee_other; } size_t count_other = 0; NetDeviceGraphNode::edge_iterator ec_other; for (NetDeviceGraphNode::edge_iterator i = e_other; i != ee_other; ++i) { - if (! m_nodes[i->second.second].has_other ()) { + if (! m_nodes[i->second.first].has_other ()) { ec_other = i; ++count_other; } } +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::log << "identity count = " << count_other; +#endif if (count_other == 1) { - confirm_identity (*this, begin () + ec->second.second, other, other.begin () + ec_other->second.second, logger); + confirm_identity (*this, begin () + ec->second.first, other, other.begin () + ec_other->second.first, logger); ++added; - more.push_back (ec->second.second); + more.push_back (ec->second.first); } } @@ -648,10 +676,23 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, bool good = true; while (good) { +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::log << "new iteration ..."; +#endif + size_t new_identities = 0; for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { if (i1->has_other ()) { - new_identities += g1.derive_node_identities (i1 - g1.begin (), g2, mp_logger); +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::log << "deriving new identities from " << i1->net ()->expanded_name (); +#endif + size_t ni = g1.derive_node_identities (i1 - g1.begin (), g2, mp_logger); + new_identities += ni; + if (ni > 0) { +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::log << ni << " new identities."; +#endif + } } } @@ -666,6 +707,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, if (new_identities == 0) { +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::log << "checking topological identity ..."; +#endif // derive new identities through topology db::NetDeviceGraph::node_iterator i1 = g1.begin (), i2 = g2.begin (); @@ -711,7 +755,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, mp_logger->net_mismatch (i->net (), 0); } } - tl::error << tr ("Unassigned in netlist B:"); + for (db::NetDeviceGraph::node_iterator i = g2.begin (); i != g2.end (); ++i) { if (! i->has_other ()) { mp_logger->net_mismatch (0, i->net ()); @@ -814,10 +858,23 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, std::map >, const db::Device *>::const_iterator dm = device_map.find (k); if (! mapped || dm == device_map.end ()) { + mp_logger->device_mismatch (0, d.operator-> ()); + } else { - // @@@ compare parameters, device class - mp_logger->match_devices (dm->second, d.operator-> ()); + + db::DeviceCompare dc; + + if (dc (dm->second, d.operator-> ()) || dc (d.operator-> (), dm->second)) { + if (dm->second->device_class ()->name () != d->device_class ()->name ()) { + mp_logger->match_devices_with_different_device_classes (dm->second, d.operator-> ()); + } else { + mp_logger->match_devices_with_different_parameters (dm->second, d.operator-> ()); + } + } else { + mp_logger->match_devices (dm->second, d.operator-> ()); + } + } } @@ -833,79 +890,87 @@ class NetlistCompareTestLogger public: NetlistCompareTestLogger () { } + void out (const std::string &text) + { + m_texts.push_back (text); +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::log << m_texts.back (); +#endif + } + virtual void begin_circuit (const db::Circuit *a, const db::Circuit *b) { - m_texts.push_back ("begin_circuit " + circuit2str (a) + " " + circuit2str (b)); + out ("begin_circuit " + circuit2str (a) + " " + circuit2str (b)); } virtual void end_circuit (const db::Circuit *a, const db::Circuit *b, bool matching) { - m_texts.push_back ("end_circuit " + circuit2str (a) + " " + circuit2str (b) + " " + (matching ? "MATCH" : "NOMATCH")); + out ("end_circuit " + circuit2str (a) + " " + circuit2str (b) + " " + (matching ? "MATCH" : "NOMATCH")); } virtual void circuit_skipped (const db::Circuit *a, const db::Circuit *b) { - m_texts.push_back ("circuit_skipped " + circuit2str (a) + " " + circuit2str (b)); + out ("circuit_skipped " + circuit2str (a) + " " + circuit2str (b)); } virtual void circuit_mismatch (const db::Circuit *a, const db::Circuit *b) { - m_texts.push_back ("circuit_mismatch " + circuit2str (a) + " " + circuit2str (b)); + out ("circuit_mismatch " + circuit2str (a) + " " + circuit2str (b)); } virtual void match_nets (const db::Net *a, const db::Net *b) { - m_texts.push_back ("match_nets " + net2str (a) + " " + net2str (b)); + out ("match_nets " + net2str (a) + " " + net2str (b)); } virtual void match_ambiguous_nets (const db::Net *a, const db::Net *b) { - m_texts.push_back ("match_ambiguous_nets " + net2str (a) + " " + net2str (b)); + out ("match_ambiguous_nets " + net2str (a) + " " + net2str (b)); } virtual void net_mismatch (const db::Net *a, const db::Net *b) { - m_texts.push_back ("net_mismatch " + net2str (a) + " " + net2str (b)); + out ("net_mismatch " + net2str (a) + " " + net2str (b)); } virtual void match_devices (const db::Device *a, const db::Device *b) { - m_texts.push_back ("match_devices " + device2str (a) + " " + device2str (b)); + out ("match_devices " + device2str (a) + " " + device2str (b)); } virtual void device_mismatch (const db::Device *a, const db::Device *b) { - m_texts.push_back ("device_mismatch " + device2str (a) + " " + device2str (b)); + out ("device_mismatch " + device2str (a) + " " + device2str (b)); } virtual void match_devices_with_different_parameters (const db::Device *a, const db::Device *b) { - m_texts.push_back ("match_devices_with_different_parameters " + device2str (a) + " " + device2str (b)); + out ("match_devices_with_different_parameters " + device2str (a) + " " + device2str (b)); } virtual void match_devices_with_different_device_classes (const db::Device *a, const db::Device *b) { - m_texts.push_back ("match_devices_with_different_device_classes " + device2str (a) + " " + device2str (b)); + out ("match_devices_with_different_device_classes " + device2str (a) + " " + device2str (b)); } virtual void match_pins (const db::Pin *a, const db::Pin *b) { - m_texts.push_back ("match_pins " + pin2str (a) + " " + pin2str (b)); + out ("match_pins " + pin2str (a) + " " + pin2str (b)); } virtual void pin_mismatch (const db::Pin *a, const db::Pin *b) { - m_texts.push_back ("pin_mismatch " + pin2str (a) + " " + pin2str (b)); + out ("pin_mismatch " + pin2str (a) + " " + pin2str (b)); } virtual void match_subcircuits (const db::SubCircuit *a, const db::SubCircuit *b) { - m_texts.push_back ("match_subcircuits " + subcircuit2str (a) + " " + subcircuit2str (b)); + out ("match_subcircuits " + subcircuit2str (a) + " " + subcircuit2str (b)); } virtual void subcircuit_mismatch (const db::SubCircuit *a, const db::SubCircuit *b) { - m_texts.push_back ("subcircuit_mismatch " + subcircuit2str (a) + " " + subcircuit2str (b)); + out ("subcircuit_mismatch " + subcircuit2str (a) + " " + subcircuit2str (b)); } std::string text () const @@ -1070,13 +1135,13 @@ TEST(3_Buffer) "begin_circuit BUF BUF\n" "match_nets VSS VSS\n" "match_nets OUT OUT\n" - "match_nets IN IN\n" "match_nets VDD VDD\n" + "match_nets IN IN\n" "match_nets INT $10\n" "match_pins $3 $2\n" "match_pins $1 $3\n" - "match_pins $0 $1\n" "match_pins $2 $0\n" + "match_pins $0 $1\n" "match_devices $1 $1\n" "match_devices $3 $2\n" "match_devices $2 $3\n" @@ -1127,12 +1192,12 @@ TEST(4_BufferTwoPaths) "match_nets OUT OUT\n" "match_ambiguous_nets INT $10\n" "match_nets INT2 $11\n" - "match_nets IN IN\n" "match_nets VDD VDD\n" + "match_nets IN IN\n" "match_pins $3 $2\n" "match_pins $1 $3\n" - "match_pins $0 $1\n" "match_pins $2 $0\n" + "match_pins $0 $1\n" "match_devices $1 $1\n" "match_devices $3 $2\n" "match_devices $5 $3\n" @@ -1146,6 +1211,70 @@ TEST(4_BufferTwoPaths) EXPECT_EQ (good, true); } +TEST(5_BufferTwoPathsDifferentParameters) +{ + const char *nls1 = + "circuit BUF ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $2 (S=VSS,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $3 (S=VDD,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=VSS,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $5 (S=VDD,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $6 (S=VSS,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $7 (S=VDD,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $8 (S=VSS,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + const char *nls2 = + "circuit BUF ($1=VDD,$2=IN,$3=VSS,$4=OUT);\n" + " device PMOS $1 (S=VDD,G=IN,D=$10) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=$10,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $3 (S=VDD,G=IN,D=$11) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $4 (S=VDD,G=$11,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $5 (S=$10,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $6 (S=OUT,G=$10,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $7 (S=$11,G=IN,D=VSS) (L=0.35,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" // NOTE: 0.35 instead of 0.25 + " device NMOS $8 (S=OUT,G=$11,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + // Forcing the power nets into equality makes the parameter error harder to detect + const db::Circuit *ca = nl1.circuit_by_name ("BUF"); + const db::Circuit *cb = nl2.circuit_by_name ("BUF"); + comp.same_nets (ca, ca->net_by_name ("VDD"), cb, cb->net_by_name ("VDD")); + comp.same_nets (ca, ca->net_by_name ("VSS"), cb, cb->net_by_name ("VSS")); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit BUF BUF\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_ambiguous_nets INT $10\n" + "match_nets INT2 $11\n" + "match_pins $3 $2\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $0 $1\n" + "match_devices $1 $1\n" + "match_devices $3 $2\n" + "match_devices $5 $3\n" + "match_devices $7 $4\n" + "match_devices $2 $5\n" + "match_devices $4 $6\n" + "match_devices_with_different_parameters $6 $7\n" + "match_devices $8 $8\n" + "end_circuit BUF BUF MATCH" + ); + EXPECT_EQ (good, true); +} + TEST(100_SimpleInverterWithWrongTransistorParameter) { return; // @@@ doesn't work yet. From 1a30a3919d12eabe211ab14ba33e644a545b81d0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 25 Mar 2019 22:14:16 +0100 Subject: [PATCH 10/54] WIP: Net compare with subcircuits. --- src/db/db/dbNetlist.cc | 18 +- src/db/unit_tests/dbNetlistCompareTests.cc | 501 ++++++++++++++------- 2 files changed, 352 insertions(+), 167 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 3a51a782a..b1b576b03 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -731,10 +731,10 @@ static void read_device (tl::Extractor &ex, db::Circuit *circuit, std::map &n2n) +static void read_subcircuit_pins (tl::Extractor &ex, db::Circuit *circuit, db::SubCircuit *subcircuit, std::map &n2n) { - db::Circuit *circuit = subcircuit->circuit_ref (); - db::Circuit::pin_iterator pi = circuit->begin_pins (); + db::Circuit *circuit_ref = subcircuit->circuit_ref (); + db::Circuit::pin_iterator pi = circuit_ref->begin_pins (); ex.expect ("("); while (! ex.test (")")) { @@ -744,12 +744,12 @@ static void read_subcircuit_pins (tl::Extractor &ex, db::SubCircuit *subcircuit, ex.expect ("="); - if (pi == circuit->end_pins ()) { + if (pi == circuit_ref->end_pins ()) { // add a dummy pin - circuit->add_pin (pn); - pi = circuit->end_pins (); + circuit_ref->add_pin (pn); + pi = circuit_ref->end_pins (); --pi; - } else if (pi->name () != pn) { + } else if (! pi->name ().empty () && pi->name () != pn) { ex.error (tl::to_string (tr ("Expected pin with name: ")) + pi->name ()); } @@ -766,7 +766,7 @@ static void read_subcircuit_pins (tl::Extractor &ex, db::SubCircuit *subcircuit, } - if (pi != circuit->end_pins ()) { + if (pi != circuit_ref->end_pins ()) { ex.error (tl::to_string (tr ("Too few pins in subcircuit call"))); } } @@ -801,7 +801,7 @@ static void read_subcircuit (tl::Extractor &ex, db::Circuit *circuit, std::mapadd_subcircuit (subcircuit); - read_subcircuit_pins (ex, subcircuit, n2n); + read_subcircuit_pins (ex, circuit, subcircuit, n2n); } void Netlist::from_string (const std::string &s) diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index cd5236893..709af8bc7 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -133,6 +133,64 @@ struct DeviceCompare } }; +struct SubCircuitCompare +{ + bool operator() (const db::SubCircuit *sc1, const db::SubCircuit *sc2) const + { + // @@@ TODO: device class identity should not be defined via name + if (sc1->circuit_ref () != sc2->circuit_ref () && sc1->circuit_ref ()->name () != sc2->circuit_ref ()->name ()) { + return sc1->circuit_ref ()->name () < sc2->circuit_ref ()->name (); + } + + // no parameters + return false; + } +}; + +class CircuitMapper +{ +public: + CircuitMapper () + : mp_other (0) + { + // .. nothing yet .. + } + + void set_other (const db::Circuit *other) + { + mp_other = other; + } + + const db::Circuit *other () const + { + return mp_other; + } + + void map_pin (size_t this_pin, size_t other_pin) + { + m_pin_map.insert (std::make_pair (this_pin, other_pin)); + m_rev_pin_map.insert (std::make_pair (other_pin, this_pin)); + } + + size_t other_pin_from_this_pin (size_t this_pin) const + { + std::map::const_iterator i = m_pin_map.find (this_pin); + tl_assert (i != m_pin_map.end ()); + return i->second; + } + + size_t this_pin_from_other_pin (size_t other_pin) const + { + std::map::const_iterator i = m_rev_pin_map.find (other_pin); + tl_assert (i != m_rev_pin_map.end ()); + return i->second; + } + +private: + const db::Circuit *mp_other; + std::map m_pin_map, m_rev_pin_map; +}; + static size_t translate_terminal_id (size_t tid, const db::Device *device) { // @@@ delegate to device class @@ -149,36 +207,97 @@ static size_t translate_terminal_id (size_t tid, const db::Device *device) // @@@ } +static size_t translate_subcircuit_pin_id (size_t pid, const db::Circuit * /*circuit*/) +{ + // @@@ not implemented yet + return pid; + // @@@ +} + class NetDeviceGraphNode { public: struct EdgeDesc { + + // @@@ TODO: there can be only devices or subcircuits, so we can + // compress this structure. const db::Device *device; size_t terminal1_id, terminal2_id; + const db::SubCircuit *subcircuit; + size_t pin1_id, pin2_id; + + EdgeDesc () + : device (0), terminal1_id (0), terminal2_id (0), + subcircuit (0), pin1_id (0), pin2_id (0) + { + // .. nothing yet .. + } bool operator< (const EdgeDesc &other) const { - DeviceCompare dc; - bool dlt = dc (device, other.device); - if (dlt || dc (other.device, device)) { - return dlt; + if ((device != 0) != (other.device != 0)) { + return (device != 0) < (other.device != 0); } - if (terminal1_id != other.terminal1_id) { - return terminal1_id < other.terminal1_id; + + if (device != 0) { + + DeviceCompare dc; + bool dlt = dc (device, other.device); + if (dlt || dc (other.device, device)) { + return dlt; + } + + if (terminal1_id != other.terminal1_id) { + return terminal1_id < other.terminal1_id; + } + return terminal2_id < other.terminal2_id; + + } else { + + SubCircuitCompare sc; + bool sclt = sc (subcircuit, other.subcircuit); + if (sclt || sc (other.subcircuit, subcircuit)) { + return sclt; + } + + if (pin1_id != other.pin1_id) { + return pin1_id < other.pin1_id; + } + return pin2_id < other.pin2_id; + } - return terminal2_id < other.terminal2_id; } bool operator== (const EdgeDesc &other) const { - DeviceCompare dc; - if (dc (device, other.device) || dc (other.device, device)) { + if ((device != 0) != (other.device != 0)) { return false; } - if (terminal1_id != other.terminal1_id) { - return false; + + if (device != 0) { + + DeviceCompare dc; + if (dc (device, other.device) || dc (other.device, device)) { + return false; + } + if (terminal1_id != other.terminal1_id) { + return false; + } + return terminal2_id == other.terminal2_id; + + } else { + + SubCircuitCompare sc; + if (sc (subcircuit, other.subcircuit) || sc (other.subcircuit, subcircuit)) { + return false; + } + + if (pin1_id != other.pin1_id) { + return false; + } + return pin2_id == other.pin2_id; + } - return terminal2_id == other.terminal2_id; } }; @@ -192,11 +311,78 @@ public: typedef std::vector, std::pair > >::const_iterator edge_iterator; - NetDeviceGraphNode (const db::Net *net, std::map &devmap, std::vector &device_prototypes) + NetDeviceGraphNode (const db::Net *net, std::map &devmap, std::vector &device_prototypes, const std::map *circuit_map) : mp_net (net), m_other_net_index (std::numeric_limits::max ()) { std::map n2entry; + for (db::Net::const_subcircuit_pin_iterator i = net->begin_subcircuit_pins (); i != net->end_subcircuit_pins (); ++i) { + + const db::SubCircuit *sc = i->subcircuit (); + size_t pin_id = i->pin ()->id (); + const db::Circuit *cr = sc->circuit_ref (); + + const CircuitMapper *cm = 0; + + if (circuit_map) { + std::map::const_iterator icm = circuit_map->find (cr); + tl_assert (icm != circuit_map->end ()); + cm = & icm->second; + } + + // NOTE: if cm is given, cr and pin_id are given in terms of the "other" circuit + + if (cm) { + cr = cm->other (); + pin_id = cm->other_pin_from_this_pin (pin_id); + } + + // we cannot afford creating edges from all to all other pins, so we just create edges to the previous and next + // pin. This may take more iterations to solve, but should be equivalent. + + db::Circuit::const_pin_iterator p = cr->begin_pins (); + for ( ; p != cr->end_pins () && p->id () != pin_id; ++p) + ; + tl_assert (p != cr->end_pins ()); + + db::Circuit::const_pin_iterator pp = p; + if (pp == cr->begin_pins ()) { + pp = cr->end_pins (); + } + --pp; + + db::Circuit::const_pin_iterator pn = p; + ++pn; + if (pn == cr->end_pins ()) { + pn = cr->begin_pins (); + } + + for (int i = 0; i < 2; ++i) { + + size_t pin2_id = (i == 0 ? pp->id () : pn->id ()); + + EdgeDesc ed; + ed.subcircuit = sc; + // NOTE: if a pin mapping is given, EdgeDesc::pin1_id and EdgeDesc::pin2_id are given + // as pin ID's of the other circuit. + ed.pin1_id = translate_subcircuit_pin_id (pin_id, cr); + ed.pin2_id = translate_subcircuit_pin_id (pin2_id, cr); + + size_t this_pin2_id = cm ? cm->this_pin_from_other_pin (pin2_id) : pin2_id; + const db::Net *net2 = sc->net_for_pin (this_pin2_id); + + std::map::const_iterator in = n2entry.find (net2); + if (in == n2entry.end ()) { + in = n2entry.insert (std::make_pair (net2, m_edges.size ())).first; + m_edges.push_back (std::make_pair (std::vector (), std::make_pair (size_t (0), net2))); + } + + m_edges [in->second].first.push_back (ed); + + } + + } + for (db::Net::const_terminal_iterator i = net->begin_terminals (); i != net->end_terminals (); ++i) { const db::Device *d = i->device (); @@ -356,7 +542,7 @@ public: // .. nothing yet .. } - void build (const db::Circuit *c) + void build (const db::Circuit *c, const std::map *circuit_and_pin_mapping) { tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Building net graph for circuit: ")) + c->name ()); @@ -372,7 +558,7 @@ public: m_nodes.reserve (nets); for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - NetDeviceGraphNode node (n.operator-> (), m_device_map, m_device_prototypes); + NetDeviceGraphNode node (n.operator-> (), m_device_map, m_device_prototypes, circuit_and_pin_mapping); m_nodes.push_back (node); } @@ -563,6 +749,7 @@ public: } std::set verified_circuits_a, verified_circuits_b; + std::map pin_mapping; for (db::Netlist::const_bottom_up_circuit_iterator c = a->begin_bottom_up (); c != a->end_bottom_up (); ++c) { @@ -588,7 +775,7 @@ public: } bool pin_mismatch = false; - bool g = compare_circuits (ca, cb, *net_identity, pin_mismatch); + bool g = compare_circuits (ca, cb, *net_identity, pin_mismatch, pin_mapping); if (! g) { good = false; } @@ -622,7 +809,7 @@ public: } protected: - bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2, const std::vector > &net_identity, bool &pin_mismatch) const; + bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2, const std::vector > &net_identity, bool &pin_mismatch, std::map &circuit_and_pin_map) const; bool all_subcircuits_verified (const db::Circuit *c, const std::set &verified_circuits) const; NetlistCompareLogger *mp_logger; @@ -658,13 +845,49 @@ compute_device_key (const db::Device &device, const db::NetDeviceGraph &g) return k; } +static std::vector > +compute_subcircuit_key (const db::SubCircuit &subcircuit, const db::NetDeviceGraph &g, const std::map *circuit_map) +{ + std::vector > k; + + const db::Circuit *cr = subcircuit.circuit_ref (); + + const CircuitMapper *cm = 0; + + if (circuit_map) { + std::map::const_iterator icm = circuit_map->find (cr); + tl_assert (icm != circuit_map->end ()); + cm = & icm->second; + cr = cm->other (); + } + + // NOTE: if cm is given, cr is given in terms of the "other" circuit + + for (db::Circuit::const_pin_iterator p = cr->begin_pins (); p != cr->end_pins (); ++p) { + + size_t this_pin_id = cm ? cm->this_pin_from_other_pin (p->id ()) : p->id (); + + size_t pin_id = translate_subcircuit_pin_id (p->id (), cr); + const db::Net *net = subcircuit.net_for_pin (this_pin_id); + size_t net_id = g.node_index_for_net (net); + k.push_back (std::make_pair (pin_id, net_id)); + + } + + std::sort (k.begin (), k.end ()); + + return k; +} + bool -NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, const std::vector > &net_identity, bool &pin_mismatch) const +NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, const std::vector > &net_identity, bool &pin_mismatch, std::map &circuit_and_pin_mapping) const { db::NetDeviceGraph g1, g2; - g1.build (c1); - g2.build (c2); + // NOTE: for normalization we map all subcircuits of c1 to c2. + // Also, pin swapping will only happen there. + g1.build (c1, &circuit_and_pin_mapping); + g2.build (c2, 0); for (std::vector >::const_iterator p = net_identity.begin (); p != net_identity.end (); ++p) { size_t ni1 = g1.node_index_for_net (p->first); @@ -748,6 +971,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } + // Report missing net assignment for (db::NetDeviceGraph::node_iterator i = g1.begin (); i != g1.end (); ++i) { @@ -772,6 +996,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, net2pin.insert (std::make_pair (net, p.operator-> ())); } + CircuitMapper &pin_mapping = circuit_and_pin_mapping [c1]; + pin_mapping.set_other (c2); + for (db::NetDeviceGraph::node_iterator i = g1.begin (); i != g1.end (); ++i) { const db::Net *net = i->net (); @@ -797,6 +1024,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, if (np != net2pin.end () && np->first == other_net) { mp_logger->match_pins (pi->pin (), np->second); + pin_mapping.map_pin (pi->pin ()->id (), np->second->id ()); std::multimap::iterator np_delete = np; ++np; @@ -879,6 +1107,65 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } + // Report subcircuit assignment + + std::map >, const db::SubCircuit *> subcircuit_map; + + for (db::Circuit::const_subcircuit_iterator sc = c1->begin_subcircuits (); sc != c1->end_subcircuits (); ++sc) { + + std::vector > k = compute_subcircuit_key (*sc, g1, &circuit_and_pin_mapping); + + bool mapped = true; + for (std::vector >::iterator i = k.begin (); i != k.end () && mapped; ++i) { + if (! g1.begin () [i->second].has_other ()) { + mapped = false; + } + } + + if (! mapped) { + mp_logger->subcircuit_mismatch (sc.operator-> (), 0); + } else { + // TODO: report devices which cannot be distiguished topologically? + subcircuit_map[k] = sc.operator-> (); + } + + } + + for (db::Circuit::const_subcircuit_iterator sc = c2->begin_subcircuits (); sc != c2->end_subcircuits (); ++sc) { + + std::vector > k = compute_subcircuit_key (*sc, g2, 0); + + bool mapped = true; + for (std::vector >::iterator i = k.begin (); i != k.end (); ++i) { + if (! g1.begin () [i->second].has_other ()) { + mapped = false; + } else { + i->second = g2.begin () [i->second].other_net_index (); + } + } + + std::sort (k.begin (), k.end ()); + + std::map >, const db::SubCircuit *>::const_iterator scm = subcircuit_map.find (k); + + if (! mapped || scm == subcircuit_map.end ()) { + + mp_logger->subcircuit_mismatch (0, sc.operator-> ()); + + } else { + + db::SubCircuitCompare scc; + + if (scc (scm->second, sc.operator-> ()) || scc (sc.operator-> (), scm->second)) { + mp_logger->subcircuit_mismatch (scm->second, sc.operator-> ()); + } else { + mp_logger->match_subcircuits (scm->second, sc.operator-> ()); + } + + } + + } + return good; } @@ -1275,19 +1562,26 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (good, true); } -TEST(100_SimpleInverterWithWrongTransistorParameter) +TEST(10_SimpleSubCircuits) { -return; // @@@ doesn't work yet. const char *nls1 = "circuit INV ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n" " device PMOS $1 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" " device NMOS $2 (S=VSS,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TOP ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n" + " subcircuit INV $1 ($1=IN,$2=INT,$3=VDD,$4=VSS);\n" + " subcircuit INV $2 ($1=INT,$2=OUT,$3=VDD,$4=VSS);\n" "end;\n"; const char *nls2 = "circuit INV ($1=VDD,$2=IN,$3=VSS,$4=OUT);\n" " device NMOS $1 (S=OUT,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $2 (S=VDD,G=IN,D=OUT) (L=0.30,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TOP ($1=OUT,$2=VDD,$3=IN,$4=VSS);\n" + " subcircuit INV $1 ($1=VDD,$2=INT,$3=VSS,$4=OUT);\n" + " subcircuit INV $2 ($1=VDD,$2=IN,$3=VSS,$4=INT);\n" "end;\n"; db::Netlist nl1, nl2; @@ -1296,145 +1590,36 @@ return; // @@@ doesn't work yet. NetlistCompareTestLogger logger; db::NetlistComparer comp (&logger); - const db::Circuit *ca = nl1.circuit_by_name ("INV"); - const db::Circuit *cb = nl2.circuit_by_name ("INV"); - comp.same_nets (ca, ca->net_by_name ("VDD"), cb, cb->net_by_name ("VDD")); - comp.same_nets (ca, ca->net_by_name ("VSS"), cb, cb->net_by_name ("VSS")); bool good = comp.compare (&nl1, &nl2); EXPECT_EQ (logger.text (), - "begin_circuit INV INV\n" - "match_nets OUT OUT\n" - "match_nets IN IN\n" - "match_pins $3 $2\n" - "match_pins $2 $0\n" - "match_pins $1 $3\n" - "match_pins $0 $1\n" - "end_circuit INV INV MATCH" + "begin_circuit INV INV\n" + "match_nets VSS VSS\n" + "match_nets VDD VDD\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_pins $3 $2\n" + "match_pins $2 $0\n" + "match_pins $1 $3\n" + "match_pins $0 $1\n" + "match_devices $2 $1\n" + "match_devices $1 $2\n" + "end_circuit INV INV MATCH\n" + "begin_circuit TOP TOP\n" + "match_nets IN IN\n" + "match_nets OUT OUT\n" + "match_nets VDD VDD\n" + "match_nets VSS VSS\n" + "match_nets INT INT\n" + "match_pins $0 $2\n" + "match_pins $1 $0\n" + "match_pins $2 $1\n" + "match_pins $3 $3\n" + "match_subcircuits $2 $1\n" + "match_subcircuits $1 $2\n" + "end_circuit TOP TOP MATCH" ); + EXPECT_EQ (good, true); } - -#if 0 -TEST(2) -{ - const char *nls2 = - "circuit RINGO ();\n" - " device PMOS $1 (S=$16,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $2 (S=VDD,G=$16,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $3 (S=$14,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $4 (S=VDD,G=$14,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $5 (S=$12,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $6 (S=VDD,G=$12,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $7 (S='IN,FB',G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $8 (S=VDD,G='IN,FB',D='OUT,OSC') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $9 (S=$4,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $10 (S=VDD,G=$4,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $11 (S=$8,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $12 (S=VDD,G=$8,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $13 (S=$2,G='IN,FB',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $14 (S=VDD,G=$2,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $15 (S=$6,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $16 (S=VDD,G=$6,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $17 (S=$18,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $18 (S=VDD,G=$18,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $19 (S=$10,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $20 (S=VDD,G=$10,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $21 (S='IN,FB',G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $22 (S=VSS,G='IN,FB',D='OUT,OSC') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $23 (S=$18,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $24 (S=VSS,G=$18,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $25 (S=$14,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $26 (S=VSS,G=$14,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $27 (S=$12,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $28 (S=VSS,G=$12,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $29 (S=$4,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $30 (S=VSS,G=$4,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $31 (S=$2,G='IN,FB',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $32 (S=VSS,G=$2,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $33 (S=$8,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $34 (S=VSS,G=$8,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $35 (S=$6,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $36 (S=VSS,G=$6,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $37 (S=$16,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $38 (S=VSS,G=$16,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $39 (S=$10,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $40 (S=VSS,G=$10,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - "end;\n"; - - const char *nls1 = - "circuit RINGO ();\n" - " device PMOS $1 (S=$16,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $2 (S=VDD,G=$16,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $3 (S=$14,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $4 (S=VDD,G=$14,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $5 (S=$12,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $6 (S=VDD,G=$12,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $7 (S='IN,FB',G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $8 (S=VDD,G='IN,FB',D='OUT,OSC') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $9 (S=$4,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $10 (S=VDD,G=$4,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $11 (S=$8,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $12 (S=VDD,G=$8,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $13 (S=$2,G='IN,FB',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $14 (S=VDD,G=$2,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $15 (S=$6,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $16 (S=VDD,G=$6,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $17 (S=$18,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $18 (S=VDD,G=$18,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $19 (S=$10,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $20 (S=VDD,G=$10,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $21 (S='IN,FB',G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $22 (S=VSS,G='IN,FB',D='OUT,OSC') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $23 (S=$18,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $24 (S=VSS,G=$18,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $25 (S=$14,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $26 (S=VSS,G=$14,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $27 (S=$12,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $28 (S=VSS,G=$12,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $29 (S=$4,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $30 (S=VSS,G=$4,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $31 (S=$2,G='IN,FB',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $32 (S=VSS,G=$2,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $33 (S=$8,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $34 (S=VSS,G=$8,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $35 (S=$6,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $36 (S=VSS,G=$6,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $37 (S=$16,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $38 (S=VSS,G=$16,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $39 (S=$10,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $40 (S=VSS,G=$10,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - "end;\n"; - - db::DeviceClass *dc; - - db::Netlist nl1, nl2; - - db::Netlist *nlp[] = { &nl1, &nl2 }; - for (int i = 0; i < 2; ++i) { - - dc = new db::DeviceClassMOS3Transistor (); - dc->set_name ("PMOS"); - nlp[i]->add_device_class (dc); - - dc = new db::DeviceClassMOS3Transistor (); - dc->set_name ("NMOS"); - nlp[i]->add_device_class (dc); - - } - - nl1.from_string (nls1); - nl2.from_string (nls2); - - NetlistCompareTestLogger logger; - db::NetlistComparer comp (&logger); - - bool good = comp.compare (&nl1, &nl2); - - EXPECT_EQ (logger.text (), - "" - ); - EXPECT_EQ (good, true); -} -#endif From fec2348d97d3955c02fcaee9606b122be7ea0c62 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 25 Mar 2019 23:26:46 +0100 Subject: [PATCH 11/54] WIP: Net compare. --- src/db/unit_tests/dbNetlistCompareTests.cc | 70 +++++++++++++++++++++- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 709af8bc7..7c6204082 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -1044,6 +1044,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, pin_mismatch = true; } + // Report device assignment std::map >, const db::Device *> device_map; @@ -1074,7 +1075,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, bool mapped = true; for (std::vector >::iterator i = k.begin (); i != k.end (); ++i) { - if (! g1.begin () [i->second].has_other ()) { + if (! g2.begin () [i->second].has_other ()) { mapped = false; } else { i->second = g2.begin () [i->second].other_net_index (); @@ -1096,17 +1097,26 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, if (dc (dm->second, d.operator-> ()) || dc (d.operator-> (), dm->second)) { if (dm->second->device_class ()->name () != d->device_class ()->name ()) { mp_logger->match_devices_with_different_device_classes (dm->second, d.operator-> ()); + good = false; } else { mp_logger->match_devices_with_different_parameters (dm->second, d.operator-> ()); + good = false; } } else { mp_logger->match_devices (dm->second, d.operator-> ()); } + device_map.erase (dm); + } } + for (std::map >, const db::Device *>::const_iterator dm = device_map.begin (); dm != device_map.end (); ++dm) { + mp_logger->device_mismatch (dm->second, 0); + } + + // Report subcircuit assignment std::map >, const db::SubCircuit *> subcircuit_map; @@ -1557,9 +1567,9 @@ TEST(5_BufferTwoPathsDifferentParameters) "match_devices $4 $6\n" "match_devices_with_different_parameters $6 $7\n" "match_devices $8 $8\n" - "end_circuit BUF BUF MATCH" + "end_circuit BUF BUF NOMATCH" ); - EXPECT_EQ (good, true); + EXPECT_EQ (good, false); } TEST(10_SimpleSubCircuits) @@ -1623,3 +1633,57 @@ TEST(10_SimpleSubCircuits) EXPECT_EQ (good, true); } + +TEST(11_MismatchingSubcircuits) +{ + const char *nls1 = + "circuit INV ($0=IN,$1=OUT,$2=VDD,$3=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $2 (S=VSS,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TOP ($0=IN,$1=OUT,$2=VDD,$3=VSS);\n" + " subcircuit INV $1 ($1=IN,$2=INT,$3=VDD,$4=VSS);\n" + " subcircuit INV $2 ($1=INT,$2=OUT,$3=VDD,$4=VSS);\n" + "end;\n"; + + const char *nls2 = + "circuit INV ($0=VDD,$1=IN,$2=VSS,$3=OUT);\n" + " device NMOS $1 (S=OUT,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + // wrong wiring: + " device PMOS $2 (S=IN,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TOP ($0=OUT,$1=VDD,$2=IN,$3=VSS);\n" + " subcircuit INV $1 ($1=VDD,$2=INT,$3=VSS,$4=OUT);\n" + " subcircuit INV $2 ($1=VDD,$2=IN,$3=VSS,$4=INT);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit INV INV\n" + "match_nets VSS VSS\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "net_mismatch VDD (null)\n" + "net_mismatch (null) VDD\n" + "match_pins $3 $2\n" + "pin_mismatch $2 (null)\n" + "match_pins $1 $3\n" + "match_pins $0 $1\n" + "pin_mismatch (null) $0\n" + "device_mismatch $1 (null)\n" + "match_devices $2 $1\n" + "device_mismatch (null) $2\n" + "end_circuit INV INV NOMATCH\n" + "circuit_skipped TOP TOP" + ); + + EXPECT_EQ (good, false); +} From 93d2341bc7b105b56839e1e7242e43fd2bcacd73 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 26 Mar 2019 00:10:10 +0100 Subject: [PATCH 12/54] WIP: netlist compare --- src/db/unit_tests/dbNetlistCompareTests.cc | 151 +++++++++++++++++++-- 1 file changed, 143 insertions(+), 8 deletions(-) diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 7c6204082..dd2fdf090 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -340,6 +340,7 @@ public: // we cannot afford creating edges from all to all other pins, so we just create edges to the previous and next // pin. This may take more iterations to solve, but should be equivalent. + // @@@ this is just pin_id + 1 or pin_id - 1!!! db::Circuit::const_pin_iterator p = cr->begin_pins (); for ( ; p != cr->end_pins () && p->id () != pin_id; ++p) ; @@ -1012,6 +1013,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (db::Net::const_pin_iterator pi = net->begin_pins (); pi != net->end_pins (); ++pi) { mp_logger->pin_mismatch (pi->pin (), 0); pin_mismatch = true; + good = false; } continue; } @@ -1033,6 +1035,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } else { mp_logger->pin_mismatch (pi->pin (), 0); pin_mismatch = true; + good = false; } } @@ -1042,12 +1045,13 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (std::multimap::iterator np = net2pin.begin (); np != net2pin.end (); ++np) { mp_logger->pin_mismatch (0, np->second); pin_mismatch = true; + good = false; } // Report device assignment - std::map >, const db::Device *> device_map; + std::multimap >, const db::Device *> device_map; for (db::Circuit::const_device_iterator d = c1->begin_devices (); d != c1->end_devices (); ++d) { @@ -1062,9 +1066,10 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, if (! mapped) { mp_logger->device_mismatch (d.operator-> (), 0); + good = false; } else { // TODO: report devices which cannot be distiguished topologically? - device_map[k] = d.operator-> (); + device_map.insert (std::make_pair (k, d.operator-> ())); } } @@ -1084,11 +1089,12 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, std::sort (k.begin (), k.end ()); - std::map >, const db::Device *>::const_iterator dm = device_map.find (k); + std::multimap >, const db::Device *>::const_iterator dm = device_map.find (k); - if (! mapped || dm == device_map.end ()) { + if (! mapped || dm == device_map.end () || dm->first != k) { mp_logger->device_mismatch (0, d.operator-> ()); + good = false; } else { @@ -1112,14 +1118,15 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } - for (std::map >, const db::Device *>::const_iterator dm = device_map.begin (); dm != device_map.end (); ++dm) { + for (std::multimap >, const db::Device *>::const_iterator dm = device_map.begin (); dm != device_map.end (); ++dm) { mp_logger->device_mismatch (dm->second, 0); + good = false; } // Report subcircuit assignment - std::map >, const db::SubCircuit *> subcircuit_map; + std::multimap >, const db::SubCircuit *> subcircuit_map; for (db::Circuit::const_subcircuit_iterator sc = c1->begin_subcircuits (); sc != c1->end_subcircuits (); ++sc) { @@ -1134,9 +1141,10 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, if (! mapped) { mp_logger->subcircuit_mismatch (sc.operator-> (), 0); + good = false; } else { // TODO: report devices which cannot be distiguished topologically? - subcircuit_map[k] = sc.operator-> (); + subcircuit_map.insert (std::make_pair (k, sc.operator-> ())); } } @@ -1156,11 +1164,12 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, std::sort (k.begin (), k.end ()); - std::map >, const db::SubCircuit *>::const_iterator scm = subcircuit_map.find (k); + std::multimap >, const db::SubCircuit *>::const_iterator scm = subcircuit_map.find (k); if (! mapped || scm == subcircuit_map.end ()) { mp_logger->subcircuit_mismatch (0, sc.operator-> ()); + good = false; } else { @@ -1168,14 +1177,22 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, if (scc (scm->second, sc.operator-> ()) || scc (sc.operator-> (), scm->second)) { mp_logger->subcircuit_mismatch (scm->second, sc.operator-> ()); + good = false; } else { mp_logger->match_subcircuits (scm->second, sc.operator-> ()); } + subcircuit_map.erase (scm); + } } + for (std::multimap >, const db::SubCircuit *>::const_iterator scm = subcircuit_map.begin (); scm != subcircuit_map.end (); ++scm) { + mp_logger->subcircuit_mismatch (scm->second, 0); + good = false; + } + return good; } @@ -1572,6 +1589,70 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (good, false); } +TEST(6_BufferTwoPathsAdditionalDevices) +{ + const char *nls1 = + "circuit BUF ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $2 (S=VSS,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $3 (S=VDD,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=VSS,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $5 (S=VDD,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $6 (S=VSS,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $7 (S=VDD,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $8 (S=VSS,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $9 (S=VSS,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + const char *nls2 = + "circuit BUF ($1=VDD,$2=IN,$3=VSS,$4=OUT);\n" + " device PMOS $1 (S=VDD,G=IN,D=$10) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=$10,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $3 (S=VDD,G=$10,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $4 (S=VDD,G=IN,D=$11) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $5 (S=VDD,G=$11,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $6 (S=$10,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $7 (S=OUT,G=$10,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $8 (S=$11,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $9 (S=OUT,G=$11,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit BUF BUF\n" + "match_nets INT $11\n" + "match_nets VSS VSS\n" + "match_nets IN IN\n" + "match_nets OUT OUT\n" + "match_nets VDD VDD\n" + "match_nets INT2 $10\n" + "match_pins $3 $2\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $0 $1\n" + "match_devices $5 $1\n" + "match_devices $7 $2\n" + "device_mismatch (null) $3\n" + "match_devices $1 $4\n" + "match_devices $3 $5\n" + "match_devices $6 $6\n" + "match_devices $8 $7\n" + "match_devices $2 $8\n" + "match_devices $4 $9\n" + "device_mismatch $9 (null)\n" + "end_circuit BUF BUF NOMATCH" + ); + EXPECT_EQ (good, false); +} + TEST(10_SimpleSubCircuits) { const char *nls1 = @@ -1687,3 +1768,57 @@ TEST(11_MismatchingSubcircuits) EXPECT_EQ (good, false); } + +TEST(12_MismatchingSubcircuitsDuplicates) +{ + const char *nls1 = + "circuit INV ($0=IN,$1=OUT,$2=VDD,$3=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $2 (S=VSS,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TOP ($0=IN,$1=OUT,$2=VDD,$3=VSS);\n" + " subcircuit INV $1 ($1=IN,$2=INT,$3=VDD,$4=VSS);\n" + " subcircuit INV $2 ($1=INT,$2=OUT,$3=VDD,$4=VSS);\n" + " subcircuit INV $3 ($1=INT,$2=OUT,$3=VDD,$4=VSS);\n" + "end;\n"; + + const char *nls2 = + "circuit INV ($0=VDD,$1=VSS,$2=IN,$3=OUT);\n" + " device NMOS $1 (S=OUT,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TOP ($0=OUT,$1=IN,$2=VDD,$3=VSS);\n" + " subcircuit INV $1 ($1=VDD,$2=VSS,$3=INT,$4=OUT);\n" + " subcircuit INV $2 ($1=VDD,$2=VSS,$3=IN,$4=INT);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit INV INV\n" + "match_nets VSS VSS\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "net_mismatch VDD (null)\n" + "net_mismatch (null) VDD\n" + "match_pins $3 $2\n" + "pin_mismatch $2 (null)\n" + "match_pins $1 $3\n" + "match_pins $0 $1\n" + "pin_mismatch (null) $0\n" + "device_mismatch $1 (null)\n" + "match_devices $2 $1\n" + "device_mismatch (null) $2\n" + "end_circuit INV INV NOMATCH\n" + "circuit_skipped TOP TOP" + ); + + EXPECT_EQ (good, false); +} From e0cb3f630397004da73456672ce2376767cd70ab Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 26 Mar 2019 20:54:49 +0100 Subject: [PATCH 13/54] WIP: netlist compare - subcircuit matching enhanced. --- src/db/unit_tests/dbNetlistCompareTests.cc | 70 +++++++++++++--------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index dd2fdf090..16be9959d 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -340,27 +340,26 @@ public: // we cannot afford creating edges from all to all other pins, so we just create edges to the previous and next // pin. This may take more iterations to solve, but should be equivalent. - // @@@ this is just pin_id + 1 or pin_id - 1!!! - db::Circuit::const_pin_iterator p = cr->begin_pins (); - for ( ; p != cr->end_pins () && p->id () != pin_id; ++p) - ; - tl_assert (p != cr->end_pins ()); + std::vector pids; + size_t pin_count = cr->pin_count (); - db::Circuit::const_pin_iterator pp = p; - if (pp == cr->begin_pins ()) { - pp = cr->end_pins (); - } - --pp; + // take the previous, next and second-next pin as targets for edges + // (using the second-next pin avoid isolation of OUT vs. IN in case + // of a pin configuration of VSS-IN-VDD-OUT like for an inverter). - db::Circuit::const_pin_iterator pn = p; - ++pn; - if (pn == cr->end_pins ()) { - pn = cr->begin_pins (); + if (pin_count >= 2) { + pids.push_back ((pin_id + pin_count - 1) % pin_count); + if (pin_count >= 3) { + pids.push_back ((pin_id + 1) % pin_count); + if (pin_count >= 4) { + pids.push_back ((pin_id + 2) % pin_count); + } + } } - for (int i = 0; i < 2; ++i) { + for (std::vector::const_iterator i = pids.begin (); i != pids.end (); ++i) { - size_t pin2_id = (i == 0 ? pp->id () : pn->id ()); + size_t pin2_id = *i; EdgeDesc ed; ed.subcircuit = sc; @@ -507,7 +506,7 @@ public: edge_iterator find_edge (const std::vector &edge) const { edge_iterator res = std::lower_bound (begin (), end (), edge, NetDeviceGraphNode::EdgeToEdgeOnlyCompare ()); - if (res->first != edge) { + if (res == end () || res->first != edge) { return end (); } else { return res; @@ -660,7 +659,7 @@ public: size_t count_other = 0; NetDeviceGraphNode::edge_iterator ec_other; for (NetDeviceGraphNode::edge_iterator i = e_other; i != ee_other; ++i) { - if (! m_nodes[i->second.first].has_other ()) { + if (! other.m_nodes[i->second.first].has_other ()) { ec_other = i; ++count_other; } @@ -1783,13 +1782,13 @@ TEST(12_MismatchingSubcircuitsDuplicates) "end;\n"; const char *nls2 = - "circuit INV ($0=VDD,$1=VSS,$2=IN,$3=OUT);\n" + "circuit INV ($0=VDD,$1=IN,$2=VSS,$3=OUT);\n" " device NMOS $1 (S=OUT,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" " device PMOS $2 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" "end;\n" "circuit TOP ($0=OUT,$1=IN,$2=VDD,$3=VSS);\n" - " subcircuit INV $1 ($1=VDD,$2=VSS,$3=INT,$4=OUT);\n" - " subcircuit INV $2 ($1=VDD,$2=VSS,$3=IN,$4=INT);\n" + " subcircuit INV $1 ($1=VDD,$2=INT,$3=VSS,$4=OUT);\n" + " subcircuit INV $2 ($1=VDD,$2=IN,$3=VSS,$4=INT);\n" "end;\n"; db::Netlist nl1, nl2; @@ -1804,20 +1803,33 @@ TEST(12_MismatchingSubcircuitsDuplicates) EXPECT_EQ (logger.text (), "begin_circuit INV INV\n" "match_nets VSS VSS\n" + "match_nets VDD VDD\n" "match_nets OUT OUT\n" "match_nets IN IN\n" - "net_mismatch VDD (null)\n" - "net_mismatch (null) VDD\n" "match_pins $3 $2\n" - "pin_mismatch $2 (null)\n" + "match_pins $2 $0\n" "match_pins $1 $3\n" "match_pins $0 $1\n" - "pin_mismatch (null) $0\n" - "device_mismatch $1 (null)\n" "match_devices $2 $1\n" - "device_mismatch (null) $2\n" - "end_circuit INV INV NOMATCH\n" - "circuit_skipped TOP TOP" + "match_devices $1 $2\n" + "end_circuit INV INV MATCH\n" + "begin_circuit TOP TOP\n" + "match_nets IN IN\n" + "match_nets VDD VDD\n" + "match_nets VSS VSS\n" + "match_nets INT INT\n" + "net_mismatch OUT (null)\n" + "net_mismatch (null) OUT\n" + "match_pins $0 $1\n" + "pin_mismatch $1 (null)\n" + "match_pins $2 $2\n" + "match_pins $3 $3\n" + "pin_mismatch (null) $0\n" + "subcircuit_mismatch $2 (null)\n" + "subcircuit_mismatch $3 (null)\n" + "subcircuit_mismatch (null) $1\n" + "match_subcircuits $1 $2\n" + "end_circuit TOP TOP NOMATCH" ); EXPECT_EQ (good, false); From 46cd80d6067bc792220893df46f6a1985972e5e9 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 26 Mar 2019 22:05:08 +0100 Subject: [PATCH 14/54] WIP: netlist compare - terminal swapping of devices. --- src/db/db/dbDeviceClass.h | 11 + src/db/db/dbNetlist.cc | 5 +- src/db/db/dbNetlistDeviceClasses.h | 25 ++ src/db/db/gsiDeclDbNetlist.cc | 21 ++ src/db/unit_tests/dbNetlistCompareTests.cc | 335 ++++++++++++++++++++- 5 files changed, 380 insertions(+), 17 deletions(-) diff --git a/src/db/db/dbDeviceClass.h b/src/db/db/dbDeviceClass.h index 8c4b34aca..c985ea7a3 100644 --- a/src/db/db/dbDeviceClass.h +++ b/src/db/db/dbDeviceClass.h @@ -403,6 +403,17 @@ public: return false; } + /** + * @brief Normalizes the terminal IDs to indicate terminal swapping + * + * This method returns a "normalized" terminal ID. For example, for MOS + * transistors where S and D can be exchanged, D will be mapped to S. + */ + virtual size_t normalize_terminal_id (size_t tid) const + { + return tid; + } + private: friend class Netlist; diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index b1b576b03..85719f6d3 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -668,7 +668,10 @@ static void read_device_terminals (tl::Extractor &ex, db::Device *device, std::m static void read_device_parameters (tl::Extractor &ex, db::Device *device) { - ex.expect ("("); + if (! ex.test ("(")) { + return; + } + while (! ex.test (")")) { ex.expect_more (); diff --git a/src/db/db/dbNetlistDeviceClasses.h b/src/db/db/dbNetlistDeviceClasses.h index c7d4b5187..7162cbfbf 100644 --- a/src/db/db/dbNetlistDeviceClasses.h +++ b/src/db/db/dbNetlistDeviceClasses.h @@ -67,6 +67,11 @@ public: virtual void parallel (Device *a, Device *b) const; virtual void serial (Device *a, Device *b) const; + + virtual size_t normalize_terminal_id (size_t) const + { + return terminal_id_A; + } }; /** @@ -92,6 +97,11 @@ public: virtual void parallel (Device *a, Device *b) const; virtual void serial (Device *a, Device *b) const; + + virtual size_t normalize_terminal_id (size_t) const + { + return terminal_id_A; + } }; /** @@ -117,6 +127,11 @@ public: virtual void parallel (Device *a, Device *b) const; virtual void serial (Device *a, Device *b) const; + + virtual size_t normalize_terminal_id (size_t) const + { + return terminal_id_A; + } }; /** @@ -176,6 +191,11 @@ public: virtual bool combine_devices (Device *a, Device *b) const; virtual bool supports_parallel_combination () const { return true; } + virtual size_t normalize_terminal_id (size_t tid) const + { + return tid == terminal_id_D ? terminal_id_S : tid; + } + protected: void combine_parameters (Device *a, Device *b) const; }; @@ -198,6 +218,11 @@ public: return new DeviceClassMOS4Transistor (*this); } + virtual size_t normalize_terminal_id (size_t tid) const + { + return tid == terminal_id_D ? terminal_id_S : tid; + } + virtual bool combine_devices (Device *a, Device *b) const; }; diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 0b5b4d0d8..94c152521 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -586,11 +586,27 @@ public: m_supports_serial_combination = f; } + void equivalent_terminal_id (size_t tid, size_t equiv_tid) + { + m_equivalent_terminal_ids.insert (std::make_pair (tid, equiv_tid)); + } + + virtual size_t normalize_terminal_id (size_t tid) const + { + std::map::const_iterator ntid = m_equivalent_terminal_ids.find (tid); + if (ntid != m_equivalent_terminal_ids.end ()) { + return ntid->second; + } else { + return tid; + } + } + gsi::Callback cb_combine_devices; private: bool m_supports_parallel_combination; bool m_supports_serial_combination; + std::map m_equivalent_terminal_ids; }; } @@ -655,6 +671,11 @@ Class decl_GenericDeviceClass (decl_dbDeviceClass, "db", "Ge "Serial device combination means that the devices are connected by internal nodes. " "If the device does not support this combination mode, this predicate can be set to false. This will make the device " "extractor skip the combination test in serial mode and improve performance somewhat." + ) + + gsi::method ("equivalent_terminal_id", &GenericDeviceClass::equivalent_terminal_id, gsi::arg ("original_id"), gsi::arg ("equivalent_id"), + "@brief Specifies a terminal to be equivalent to another.\n" + "Use this method to specify two terminals to be exchangeable. For example to make S and D of a MOS transistor equivalent, " + "call this method with S and D terminal IDs. In netlist matching, S will be translated to D and thus made equivalent to D." ), "@brief A generic device class\n" "This class allows building generic device classes. Specificially, terminals can be defined " diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 16be9959d..e5df2da52 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -193,18 +193,7 @@ private: static size_t translate_terminal_id (size_t tid, const db::Device *device) { - // @@@ delegate to device class - if (dynamic_cast (device->device_class ())) { - if (tid == db::DeviceClassMOS3Transistor::terminal_id_D) { - return db::DeviceClassMOS3Transistor::terminal_id_S; - } - } else if (dynamic_cast (device->device_class ())) { - if (tid == db::DeviceClassMOS4Transistor::terminal_id_D) { - return db::DeviceClassMOS4Transistor::terminal_id_S; - } - } - return tid; - // @@@ + return device->device_class () ? device->device_class ()->normalize_terminal_id (tid) : tid; } static size_t translate_subcircuit_pin_id (size_t pid, const db::Circuit * /*circuit*/) @@ -326,7 +315,11 @@ public: if (circuit_map) { std::map::const_iterator icm = circuit_map->find (cr); - tl_assert (icm != circuit_map->end ()); + if (icm == circuit_map->end ()) { + // this can happen if the other circuit is not present - this is allowed for single-pin + // circuits. + continue; + } cm = & icm->second; } @@ -820,10 +813,17 @@ bool NetlistComparer::all_subcircuits_verified (const db::Circuit *c, const std::set &verified_circuits) const { for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) { - if (verified_circuits.find (sc->circuit_ref ()) == verified_circuits.end ()) { + + const db::Circuit *cr = sc->circuit_ref (); + + // typical via subcircuits attach through one pin. We can safely ignore such subcircuits because they don't + // contribute graph edges. + if (cr->pin_count () > 1 && verified_circuits.find (cr) == verified_circuits.end ()) { return false; } + } + return true; } @@ -856,7 +856,11 @@ compute_subcircuit_key (const db::SubCircuit &subcircuit, const db::NetDeviceGra if (circuit_map) { std::map::const_iterator icm = circuit_map->find (cr); - tl_assert (icm != circuit_map->end ()); + if (icm == circuit_map->end ()) { + // this can happen if the other circuit does not exist - in this case the key is an invalid one which cannot + // be produced by a regular subcircuit. + return k; + } cm = & icm->second; cr = cm->other (); } @@ -1141,7 +1145,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, if (! mapped) { mp_logger->subcircuit_mismatch (sc.operator-> (), 0); good = false; - } else { + } else if (! k.empty ()) { // TODO: report devices which cannot be distiguished topologically? subcircuit_map.insert (std::make_pair (k, sc.operator-> ())); } @@ -1332,6 +1336,30 @@ static void prep_nl (db::Netlist &nl, const char *str) dc->set_name ("NMOS"); nl.add_device_class (dc); + dc = new db::DeviceClassMOS4Transistor (); + dc->set_name ("PMOS4"); + nl.add_device_class (dc); + + dc = new db::DeviceClassMOS4Transistor (); + dc->set_name ("NMOS4"); + nl.add_device_class (dc); + + dc = new db::DeviceClassResistor (); + dc->set_name ("RES"); + nl.add_device_class (dc); + + dc = new db::DeviceClassCapacitor (); + dc->set_name ("CAP"); + nl.add_device_class (dc); + + dc = new db::DeviceClassInductor (); + dc->set_name ("IND"); + nl.add_device_class (dc); + + dc = new db::DeviceClassDiode (); + dc->set_name ("DIODE"); + nl.add_device_class (dc); + nl.from_string (str); } @@ -1652,6 +1680,214 @@ TEST(6_BufferTwoPathsAdditionalDevices) EXPECT_EQ (good, false); } +TEST(7_Resistors) +{ + const char *nls1 = + "circuit TRIANGLE ($0=P1,$1=P2,$2=P3);\n" + " device RES $1 (A=P1,B=P2) (R=1.5);\n" + " device RES $2 (A=P2,B=P3) (R=2.5);\n" + " device RES $3 (A=P3,B=P1) (R=3);\n" + "end;\n"; + + const char *nls2 = + "circuit TRIANGLE ($0=P1,$1=P2,$2=P3);\n" + " device RES $2 (A=P2,B=P3) (R=2.5);\n" + " device RES $1 (A=P2,B=P1) (R=1.5);\n" + " device RES $3 (A=P3,B=P1) (R=3);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit TRIANGLE TRIANGLE\n" + "match_nets P1 P1\n" + "match_nets P3 P3\n" + "match_nets P2 P2\n" + "match_pins $1 $1\n" + "match_pins $0 $0\n" + "match_pins $2 $2\n" + "match_devices $2 $1\n" + "match_devices $1 $2\n" + "match_devices $3 $3\n" + "end_circuit TRIANGLE TRIANGLE MATCH" + ); + EXPECT_EQ (good, true); +} + +TEST(7_ResistorsParameterMismatch) +{ + const char *nls1 = + "circuit TRIANGLE ($0=P1,$1=P2,$2=P3);\n" + " device RES $1 (A=P1,B=P2) (R=1.5);\n" + " device RES $2 (A=P2,B=P3) (R=2.5);\n" + " device RES $3 (A=P3,B=P1) (R=3);\n" + "end;\n"; + + const char *nls2 = + "circuit TRIANGLE ($0=P1,$1=P2,$2=P3);\n" + " device RES $1 (A=P2,B=P1) (R=1.5);\n" + " device RES $3 (A=P3,B=P1) (R=3.5);\n" + " device RES $2 (A=P2,B=P3) (R=2.5);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit TRIANGLE TRIANGLE\n" + "match_nets P2 P2\n" + "match_nets P1 P1\n" + "match_nets P3 P3\n" + "match_pins $1 $1\n" + "match_pins $0 $0\n" + "match_pins $2 $2\n" + "match_devices $1 $1\n" + "match_devices_with_different_parameters $3 $2\n" + "match_devices $2 $3\n" + "end_circuit TRIANGLE TRIANGLE NOMATCH" + ); + EXPECT_EQ (good, false); +} + +TEST(7_ResistorsPlusOneDevice) +{ + const char *nls1 = + "circuit TRIANGLE ($0=P1,$1=P2,$2=P3);\n" + " device RES $1 (A=P1,B=P2) (R=1.5);\n" + " device RES $2 (A=P2,B=P3) (R=2.5);\n" + " device RES $3 (A=P3,B=P1) (R=3);\n" + "end;\n"; + + const char *nls2 = + "circuit TRIANGLE ($0=P1,$1=P2,$2=P3);\n" + " device RES $1 (A=P2,B=P1) (R=1.5);\n" + " device RES $3 (A=P3,B=P1) (R=3);\n" + " device CAP $4 (A=P1,B=P2) (C=1e-4);\n" + " device RES $2 (A=P2,B=P3) (R=2.5);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit TRIANGLE TRIANGLE\n" + "match_nets P3 P3\n" + "match_nets P2 P2\n" + "match_nets P1 P1\n" + "match_pins $1 $1\n" + "match_pins $0 $0\n" + "match_pins $2 $2\n" + "match_devices $1 $1\n" + "match_devices $3 $2\n" + "device_mismatch (null) $3\n" + "match_devices $2 $4\n" + "end_circuit TRIANGLE TRIANGLE NOMATCH" + ); + EXPECT_EQ (good, false); +} + +TEST(8_Diodes) +{ + const char *nls1 = + "circuit TRIANGLE ($0=P1,$1=P2,$2=P3);\n" + " device DIODE $1 (A=P1,C=P2);\n" + " device DIODE $2 (A=P1,C=P3);\n" + " device DIODE $3 (A=P3,C=P2);\n" + "end;\n"; + + const char *nls2 = + "circuit TRIANGLE ($0=P1,$1=P2,$2=P3);\n" + " device DIODE $1 (A=P3,C=P2);\n" + " device DIODE $2 (A=P1,C=P3);\n" + " device DIODE $3 (A=P1,C=P2);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit TRIANGLE TRIANGLE\n" + "match_nets P1 P1\n" + "match_nets P3 P3\n" + "match_nets P2 P2\n" + "match_pins $0 $0\n" + "match_pins $2 $2\n" + "match_pins $1 $1\n" + "match_devices $3 $1\n" + "match_devices $2 $2\n" + "match_devices $1 $3\n" + "end_circuit TRIANGLE TRIANGLE MATCH" + ); + EXPECT_EQ (good, true); +} + +TEST(8_DiodesDontMatchOnSwappedPins) +{ + const char *nls1 = + "circuit TRIANGLE ($0=P1,$1=P2,$2=P3);\n" + " device DIODE $1 (A=P1,C=P2);\n" + " device DIODE $2 (A=P2,C=P3);\n" + " device DIODE $3 (A=P3,C=P1);\n" + "end;\n"; + + const char *nls2 = + "circuit TRIANGLE ($0=P1,$1=P2,$2=P3);\n" + " device DIODE $3 (A=P3,C=P1);\n" + " device DIODE $1 (A=P2,C=P1);\n" + " device DIODE $2 (A=P2,C=P3);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit TRIANGLE TRIANGLE\n" + "match_ambiguous_nets P1 P3\n" + "match_nets P2 P1\n" + "match_nets P3 P2\n" + "match_pins $0 $2\n" + "match_pins $1 $0\n" + "match_pins $2 $1\n" + "match_devices $1 $1\n" + "device_mismatch (null) $2\n" + "match_devices $3 $3\n" + "device_mismatch $2 (null)\n" + "end_circuit TRIANGLE TRIANGLE NOMATCH" + ); + EXPECT_EQ (good, false); +} + TEST(10_SimpleSubCircuits) { const char *nls1 = @@ -1834,3 +2070,70 @@ TEST(12_MismatchingSubcircuitsDuplicates) EXPECT_EQ (good, false); } + +TEST(13_MismatchingSubcircuitsAdditionalHierarchy) +{ + const char *nls1 = + "circuit INV ($0=IN,$1=OUT,$2=VDD,$3=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $2 (S=VSS,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + // a typical via: + "circuit VIA ($0=IN);\n" + "end;\n" + "circuit TOP ($0=IN,$1=OUT,$2=VDD,$3=VSS);\n" + " subcircuit INV $1 ($0=IN,$1=INT,$2=VDD,$3=VSS);\n" + " subcircuit VIA $3 ($0=IN);\n" + " subcircuit INV $2 ($0=INT,$1=OUT,$2=VDD,$3=VSS);\n" + "end;\n"; + + const char *nls2 = + "circuit INV ($0=VDD,$1=IN,$2=VSS,$3=OUT);\n" + " device NMOS $1 (S=OUT,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TOP ($0=OUT,$1=IN,$2=VDD,$3=VSS);\n" + " subcircuit INV $1 ($0=VDD,$1=INT,$2=VSS,$3=OUT);\n" + " subcircuit INV $2 ($0=VDD,$1=IN,$2=VSS,$3=INT);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "circuit_mismatch VIA (null)\n" + "begin_circuit INV INV\n" + "match_nets VSS VSS\n" + "match_nets VDD VDD\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_pins $3 $2\n" + "match_pins $2 $0\n" + "match_pins $1 $3\n" + "match_pins $0 $1\n" + "match_devices $2 $1\n" + "match_devices $1 $2\n" + "end_circuit INV INV MATCH\n" + "begin_circuit TOP TOP\n" + "match_nets IN IN\n" + "match_nets OUT OUT\n" + "match_nets VDD VDD\n" + "match_nets VSS VSS\n" + "match_nets INT INT\n" + "match_pins $0 $1\n" + "match_pins $1 $0\n" + "match_pins $2 $2\n" + "match_pins $3 $3\n" + "match_subcircuits $3 $1\n" + "match_subcircuits $1 $2\n" + "end_circuit TOP TOP MATCH" + ); + + EXPECT_EQ (good, false); +} From b44a55d901eb65053fd9372cff0c5dfb3d733893 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 26 Mar 2019 23:38:36 +0100 Subject: [PATCH 15/54] WIP: netlist compare - pin swapping. --- src/db/unit_tests/dbNetlistCompareTests.cc | 325 +++++++++++++++++++-- 1 file changed, 307 insertions(+), 18 deletions(-) diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index e5df2da52..c7ff53446 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -147,6 +147,47 @@ struct SubCircuitCompare } }; +class CircuitPinMapper +{ +public: + CircuitPinMapper () + { + // .. nothing yet .. + } + + void map_pins (const db::Circuit *circuit, size_t pin1_id, size_t pin2_id) + { + m_pin_map [circuit].insert (std::make_pair (pin1_id, pin2_id)); + } + + void map_pins (const db::Circuit *circuit, const std::vector &pin_ids) + { + if (pin_ids.size () < 2) { + return; + } + + std::map &pm = m_pin_map [circuit]; + for (size_t i = 1; i < pin_ids.size (); ++i) { + pm.insert (std::make_pair (pin_ids [i], pin_ids [0])); + } + } + + size_t normalize_pin_id (const db::Circuit *circuit, size_t pin_id) const + { + std::map >::const_iterator pm = m_pin_map.find (circuit); + if (pm != m_pin_map.end ()) { + std::map::const_iterator ipm = pm->second.find (pin_id); + if (ipm != pm->second.end ()) { + return ipm->second; + } + } + return pin_id; + } + +private: + std::map > m_pin_map; +}; + class CircuitMapper { public: @@ -196,13 +237,6 @@ static size_t translate_terminal_id (size_t tid, const db::Device *device) return device->device_class () ? device->device_class ()->normalize_terminal_id (tid) : tid; } -static size_t translate_subcircuit_pin_id (size_t pid, const db::Circuit * /*circuit*/) -{ - // @@@ not implemented yet - return pid; - // @@@ -} - class NetDeviceGraphNode { public: @@ -300,7 +334,7 @@ public: typedef std::vector, std::pair > >::const_iterator edge_iterator; - NetDeviceGraphNode (const db::Net *net, std::map &devmap, std::vector &device_prototypes, const std::map *circuit_map) + NetDeviceGraphNode (const db::Net *net, std::map &devmap, std::vector &device_prototypes, const std::map *circuit_map, const CircuitPinMapper *pin_map) : mp_net (net), m_other_net_index (std::numeric_limits::max ()) { std::map n2entry; @@ -330,6 +364,10 @@ public: pin_id = cm->other_pin_from_this_pin (pin_id); } + if (pin_map) { + pin_id = pin_map->normalize_pin_id (cr, pin_id); + } + // we cannot afford creating edges from all to all other pins, so we just create edges to the previous and next // pin. This may take more iterations to solve, but should be equivalent. @@ -358,8 +396,8 @@ public: ed.subcircuit = sc; // NOTE: if a pin mapping is given, EdgeDesc::pin1_id and EdgeDesc::pin2_id are given // as pin ID's of the other circuit. - ed.pin1_id = translate_subcircuit_pin_id (pin_id, cr); - ed.pin2_id = translate_subcircuit_pin_id (pin2_id, cr); + ed.pin1_id = pin_id; + ed.pin2_id = pin_map ? pin_map->normalize_pin_id (cr, pin2_id) : pin2_id; size_t this_pin2_id = cm ? cm->this_pin_from_other_pin (pin2_id) : pin2_id; const db::Net *net2 = sc->net_for_pin (this_pin2_id); @@ -535,7 +573,7 @@ public: // .. nothing yet .. } - void build (const db::Circuit *c, const std::map *circuit_and_pin_mapping) + void build (const db::Circuit *c, const std::map *circuit_and_pin_mapping, const CircuitPinMapper *circuit_pin_mapper) { tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Building net graph for circuit: ")) + c->name ()); @@ -551,7 +589,7 @@ public: m_nodes.reserve (nets); for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - NetDeviceGraphNode node (n.operator-> (), m_device_map, m_device_prototypes, circuit_and_pin_mapping); + NetDeviceGraphNode node (n.operator-> (), m_device_map, m_device_prototypes, circuit_and_pin_mapping, circuit_pin_mapper); m_nodes.push_back (node); } @@ -714,6 +752,29 @@ public: m_same_nets [std::make_pair (a, b)].push_back (std::make_pair (na, nb)); } + /** + * @brief Mark two pins as equivalent (i.e. can be swapped) + * + * Only circuits from the *second* input can be given swappable pins. + * This will imply the same swappable pins on the equivalent circuit of the first input. + * To mark multiple pins as swappable, use the version that takes a list of pins. + */ + void equivalent_pins (const db::Circuit *cb, size_t pin1_id, size_t pin2_id) + { + m_circuit_pin_mapper.map_pins (cb, pin1_id, pin2_id); + } + + /** + * @brief Mark multiple pins as equivalent (i.e. can be swapped) + * + * Only circuits from the *second* input can be given swappable pins. + * This will imply the same swappable pins on the equivalent circuit of the first input. + */ + void equivalent_pins (const db::Circuit *cb, const std::vector &pin_ids) + { + m_circuit_pin_mapper.map_pins (cb, pin_ids); + } + bool compare (const db::Netlist *a, const db::Netlist *b) const { bool good = true; @@ -807,6 +868,7 @@ protected: NetlistCompareLogger *mp_logger; std::map, std::vector > > m_same_nets; + CircuitPinMapper m_circuit_pin_mapper; }; bool @@ -846,7 +908,7 @@ compute_device_key (const db::Device &device, const db::NetDeviceGraph &g) } static std::vector > -compute_subcircuit_key (const db::SubCircuit &subcircuit, const db::NetDeviceGraph &g, const std::map *circuit_map) +compute_subcircuit_key (const db::SubCircuit &subcircuit, const db::NetDeviceGraph &g, const std::map *circuit_map, const CircuitPinMapper *pin_map) { std::vector > k; @@ -870,8 +932,8 @@ compute_subcircuit_key (const db::SubCircuit &subcircuit, const db::NetDeviceGra for (db::Circuit::const_pin_iterator p = cr->begin_pins (); p != cr->end_pins (); ++p) { size_t this_pin_id = cm ? cm->this_pin_from_other_pin (p->id ()) : p->id (); + size_t pin_id = pin_map ? pin_map->normalize_pin_id (cr, p->id ()) : p->id (); - size_t pin_id = translate_subcircuit_pin_id (p->id (), cr); const db::Net *net = subcircuit.net_for_pin (this_pin_id); size_t net_id = g.node_index_for_net (net); k.push_back (std::make_pair (pin_id, net_id)); @@ -890,8 +952,8 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, // NOTE: for normalization we map all subcircuits of c1 to c2. // Also, pin swapping will only happen there. - g1.build (c1, &circuit_and_pin_mapping); - g2.build (c2, 0); + g1.build (c1, &circuit_and_pin_mapping, &m_circuit_pin_mapper); + g2.build (c2, 0, &m_circuit_pin_mapper); for (std::vector >::const_iterator p = net_identity.begin (); p != net_identity.end (); ++p) { size_t ni1 = g1.node_index_for_net (p->first); @@ -1133,7 +1195,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (db::Circuit::const_subcircuit_iterator sc = c1->begin_subcircuits (); sc != c1->end_subcircuits (); ++sc) { - std::vector > k = compute_subcircuit_key (*sc, g1, &circuit_and_pin_mapping); + std::vector > k = compute_subcircuit_key (*sc, g1, &circuit_and_pin_mapping, &m_circuit_pin_mapper); bool mapped = true; for (std::vector >::iterator i = k.begin (); i != k.end () && mapped; ++i) { @@ -1154,7 +1216,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (db::Circuit::const_subcircuit_iterator sc = c2->begin_subcircuits (); sc != c2->end_subcircuits (); ++sc) { - std::vector > k = compute_subcircuit_key (*sc, g2, 0); + std::vector > k = compute_subcircuit_key (*sc, g2, 0, &m_circuit_pin_mapper); bool mapped = true; for (std::vector >::iterator i = k.begin (); i != k.end (); ++i) { @@ -2137,3 +2199,230 @@ TEST(13_MismatchingSubcircuitsAdditionalHierarchy) EXPECT_EQ (good, false); } + +TEST(14_Subcircuit2Nand) +{ + const char *nls1 = + "circuit NAND ($0=A,$1=B,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=A,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $3 (S=VSS,G=A,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=INT,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TOP ($0=IN1,$1=IN2,$2=OUT,$3=VDD,$4=VSS);\n" + " subcircuit NAND $1 ($0=IN1,$1=IN2,$2=INT,$3=VDD,$4=VSS);\n" + " subcircuit NAND $2 ($0=IN1,$1=INT,$2=OUT,$3=VDD,$4=VSS);\n" + "end;\n"; + + const char *nls2 = + "circuit NAND ($0=A,$1=B,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=A,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $3 (S=VSS,G=A,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=INT,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TOP ($0=IN1,$1=IN2,$2=OUT,$3=VDD,$4=VSS);\n" + " subcircuit NAND $2 ($0=IN1,$1=INT,$2=OUT,$3=VDD,$4=VSS);\n" + " subcircuit NAND $1 ($0=IN1,$1=IN2,$2=INT,$3=VDD,$4=VSS);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + comp.equivalent_pins (nl2.circuit_by_name ("NAND"), 0, 1); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit NAND NAND\n" + "match_nets VSS VSS\n" + "match_nets VDD VDD\n" + "match_nets B B\n" + "match_nets INT INT\n" + "match_nets OUT OUT\n" + "match_nets A A\n" + "match_pins $4 $4\n" + "match_pins $3 $3\n" + "match_pins $1 $1\n" + "match_pins $2 $2\n" + "match_pins $0 $0\n" + "match_devices $1 $1\n" + "match_devices $2 $2\n" + "match_devices $3 $3\n" + "match_devices $4 $4\n" + "end_circuit NAND NAND MATCH\n" + "begin_circuit TOP TOP\n" + "match_nets IN2 IN2\n" + "match_nets OUT OUT\n" + "match_nets IN1 IN1\n" + "match_nets VDD VDD\n" + "match_nets VSS VSS\n" + "match_nets INT INT\n" + "match_pins $1 $1\n" + "match_pins $2 $2\n" + "match_pins $0 $0\n" + "match_pins $3 $3\n" + "match_pins $4 $4\n" + "match_subcircuits $2 $1\n" + "match_subcircuits $1 $2\n" + "end_circuit TOP TOP MATCH" + ); + + EXPECT_EQ (good, true); +} + +TEST(14_Subcircuit2NandMismatchNoSwap) +{ + const char *nls1 = + "circuit NAND ($0=A,$1=B,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=A,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $3 (S=VSS,G=A,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=INT,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TOP ($0=IN1,$1=IN2,$2=OUT,$3=VDD,$4=VSS);\n" + " subcircuit NAND $1 ($0=IN1,$1=IN2,$2=INT,$3=VDD,$4=VSS);\n" + " subcircuit NAND $2 ($0=IN1,$1=INT,$2=OUT,$3=VDD,$4=VSS);\n" + "end;\n"; + + const char *nls2 = + "circuit NAND ($0=A,$1=B,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=A,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $3 (S=VSS,G=A,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=INT,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TOP ($0=IN1,$1=IN2,$2=OUT,$3=VDD,$4=VSS);\n" + " subcircuit NAND $2 ($0=INT,$1=IN1,$2=OUT,$3=VDD,$4=VSS);\n" + " subcircuit NAND $1 ($0=IN2,$1=IN1,$2=INT,$3=VDD,$4=VSS);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + // intentionally missing: comp.equivalent_pins (nl2.circuit_by_name ("NAND"), 0, 1); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit NAND NAND\n" + "match_nets VSS VSS\n" + "match_nets VDD VDD\n" + "match_nets B B\n" + "match_nets INT INT\n" + "match_nets OUT OUT\n" + "match_nets A A\n" + "match_pins $4 $4\n" + "match_pins $3 $3\n" + "match_pins $1 $1\n" + "match_pins $2 $2\n" + "match_pins $0 $0\n" + "match_devices $1 $1\n" + "match_devices $2 $2\n" + "match_devices $3 $3\n" + "match_devices $4 $4\n" + "end_circuit NAND NAND MATCH\n" + "begin_circuit TOP TOP\n" + "match_nets OUT OUT\n" + "match_nets INT IN1\n" + "match_nets VDD VDD\n" + "match_nets VSS VSS\n" + "match_nets IN1 IN2\n" + "net_mismatch IN2 (null)\n" + "net_mismatch (null) INT\n" + "pin_mismatch $1 (null)\n" + "match_pins $2 $2\n" + "match_pins $0 $1\n" + "match_pins $3 $3\n" + "match_pins $4 $4\n" + "pin_mismatch (null) $0\n" + "subcircuit_mismatch $1 (null)\n" + "subcircuit_mismatch (null) $1\n" + "subcircuit_mismatch (null) $2\n" + "subcircuit_mismatch $2 (null)\n" + "end_circuit TOP TOP NOMATCH" + ); + + EXPECT_EQ (good, false); +} + +TEST(14_Subcircuit2MatchWithSwap) +{ + const char *nls1 = + "circuit NAND ($0=A,$1=B,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=A,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $3 (S=VSS,G=A,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=INT,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TOP ($0=IN1,$1=IN2,$2=OUT,$3=VDD,$4=VSS);\n" + " subcircuit NAND $1 ($0=IN1,$1=IN2,$2=INT,$3=VDD,$4=VSS);\n" + " subcircuit NAND $2 ($0=IN1,$1=INT,$2=OUT,$3=VDD,$4=VSS);\n" + "end;\n"; + + const char *nls2 = + "circuit NAND ($0=A,$1=B,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=A,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $3 (S=VSS,G=A,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=INT,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TOP ($0=IN1,$1=IN2,$2=OUT,$3=VDD,$4=VSS);\n" + " subcircuit NAND $2 ($0=INT,$1=IN1,$2=OUT,$3=VDD,$4=VSS);\n" + " subcircuit NAND $1 ($0=IN2,$1=IN1,$2=INT,$3=VDD,$4=VSS);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + comp.equivalent_pins (nl2.circuit_by_name ("NAND"), 0, 1); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit NAND NAND\n" + "match_nets VSS VSS\n" + "match_nets VDD VDD\n" + "match_nets B B\n" + "match_nets INT INT\n" + "match_nets OUT OUT\n" + "match_nets A A\n" + "match_pins $4 $4\n" + "match_pins $3 $3\n" + "match_pins $1 $1\n" + "match_pins $2 $2\n" + "match_pins $0 $0\n" + "match_devices $1 $1\n" + "match_devices $2 $2\n" + "match_devices $3 $3\n" + "match_devices $4 $4\n" + "end_circuit NAND NAND MATCH\n" + "begin_circuit TOP TOP\n" + "match_nets IN2 IN2\n" + "match_nets OUT OUT\n" + "match_nets VSS VSS\n" + "match_nets INT INT\n" + "match_nets VDD VDD\n" + "match_nets IN1 IN1\n" + "match_pins $1 $1\n" + "match_pins $2 $2\n" + "match_pins $0 $0\n" + "match_pins $3 $3\n" + "match_pins $4 $4\n" + "match_subcircuits $2 $1\n" + "match_subcircuits $1 $2\n" + "end_circuit TOP TOP MATCH" + ); + + EXPECT_EQ (good, true); +} + From cefd6e91cf0ae146756570089548c9090a36f850 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 27 Mar 2019 23:17:35 +0100 Subject: [PATCH 16/54] WIP: some refactoring, netlist compare. Goal: support explicit device class and circuit mapping. --- src/db/unit_tests/dbNetlistCompareTests.cc | 470 ++++++++++++++------- 1 file changed, 312 insertions(+), 158 deletions(-) diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index c7ff53446..61a2ad786 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -114,36 +114,51 @@ public: struct DeviceCompare { - bool operator() (const db::Device *d1, const db::Device *d2) const + bool operator() (const std::pair &d1, const std::pair &d2) const { - // @@@ TODO: device class identity should not be defined via name - if (d1->device_class () != d2->device_class () && d1->device_class ()->name () != d2->device_class ()->name ()) { - return d1->device_class ()->name () < d2->device_class ()->name (); + if (d1.second != d2.second) { + return d1.second < d2.second; } - const std::vector &dp = d1->device_class ()->parameter_definitions (); + const std::vector &dp = d1.first->device_class ()->parameter_definitions (); for (std::vector::const_iterator i = dp.begin (); i != dp.end (); ++i) { - double v1 = d1->parameter_value (i->id ()); - double v2 = d2->parameter_value (i->id ()); + double v1 = d1.first->parameter_value (i->id ()); + double v2 = d2.first->parameter_value (i->id ()); if (fabs (v1 - v2) > db::epsilon) { return v1 < v2; } } return false; } + + bool equals (const std::pair &d1, const std::pair &d2) const + { + if (d1.second != d2.second) { + return false; + } + + const std::vector &dp = d1.first->device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = dp.begin (); i != dp.end (); ++i) { + double v1 = d1.first->parameter_value (i->id ()); + double v2 = d2.first->parameter_value (i->id ()); + if (fabs (v1 - v2) > db::epsilon) { + return false; + } + } + return true; + } }; struct SubCircuitCompare { - bool operator() (const db::SubCircuit *sc1, const db::SubCircuit *sc2) const + bool operator() (const std::pair &sc1, const std::pair &sc2) const { - // @@@ TODO: device class identity should not be defined via name - if (sc1->circuit_ref () != sc2->circuit_ref () && sc1->circuit_ref ()->name () != sc2->circuit_ref ()->name ()) { - return sc1->circuit_ref ()->name () < sc2->circuit_ref ()->name (); - } + return sc1.second < sc2.second; + } - // no parameters - return false; + bool equals (const std::pair &sc1, const std::pair &sc2) const + { + return sc1.second == sc2.second; } }; @@ -232,6 +247,94 @@ private: std::map m_pin_map, m_rev_pin_map; }; +struct DeviceCategorizer +{ +public: + DeviceCategorizer () + : m_next_cat (0) + { + // .. nothing yet .. + } + + void same_class (const db::DeviceClass *ca, const db::DeviceClass *cb) + { + ++m_next_cat; + m_cat_by_ptr.insert (std::make_pair (ca, m_next_cat)); + m_cat_by_ptr.insert (std::make_pair (cb, m_next_cat)); + } + + size_t cat_for_device (const db::Device *device) + { + const db::DeviceClass *cls = device->device_class (); + if (! cls) { + return 0; + } + + std::map::const_iterator cp = m_cat_by_ptr.find (cls); + if (cp != m_cat_by_ptr.end ()) { + return cp->second; + } + + std::map::const_iterator c = m_cat_by_name.find (cls->name ()); + if (c != m_cat_by_name.end ()) { + return c->second; + } else { + ++m_next_cat; + m_cat_by_name.insert (std::make_pair (cls->name (), m_next_cat)); + return m_next_cat; + } + } + +public: + std::map m_cat_by_ptr; + std::map m_cat_by_name; + size_t m_next_cat; +}; + +struct CircuitCategorizer +{ +public: + CircuitCategorizer () + : m_next_cat (0) + { + // .. nothing yet .. + } + + void same_circuit (const db::Circuit *ca, const db::Circuit *cb) + { + ++m_next_cat; + m_cat_by_ptr.insert (std::make_pair (ca, m_next_cat)); + m_cat_by_ptr.insert (std::make_pair (cb, m_next_cat)); + } + + size_t cat_for_subcircuit (const db::SubCircuit *subcircuit) + { + const db::Circuit *cr = subcircuit->circuit_ref (); + if (! cr) { + return 0; + } + + std::map::const_iterator cp = m_cat_by_ptr.find (cr); + if (cp != m_cat_by_ptr.end ()) { + return cp->second; + } + + std::map::const_iterator c = m_cat_by_name.find (cr->name ()); + if (c != m_cat_by_name.end ()) { + return c->second; + } else { + ++m_next_cat; + m_cat_by_name.insert (std::make_pair (cr->name (), m_next_cat)); + return m_next_cat; + } + } + +public: + std::map m_cat_by_ptr; + std::map m_cat_by_name; + size_t m_next_cat; +}; + static size_t translate_terminal_id (size_t tid, const db::Device *device) { return device->device_class () ? device->device_class ()->normalize_terminal_id (tid) : tid; @@ -242,85 +345,113 @@ class NetDeviceGraphNode public: struct EdgeDesc { - // @@@ TODO: there can be only devices or subcircuits, so we can - // compress this structure. - const db::Device *device; - size_t terminal1_id, terminal2_id; - const db::SubCircuit *subcircuit; - size_t pin1_id, pin2_id; - - EdgeDesc () - : device (0), terminal1_id (0), terminal2_id (0), - subcircuit (0), pin1_id (0), pin2_id (0) + EdgeDesc (const db::Device *device, size_t device_category, size_t terminal1_id, size_t terminal2_id) { - // .. nothing yet .. + device_pair ().first = device; + device_pair ().second = device_category; + m_id1 = terminal1_id; + m_id2 = terminal2_id; + } + + EdgeDesc (const db::SubCircuit *subcircuit, size_t subcircuit_category, size_t pin1_id, size_t pin2_id) + { + subcircuit_pair ().first = subcircuit; + subcircuit_pair ().second = subcircuit_category; + m_id1 = std::numeric_limits::max () - pin1_id; + m_id2 = pin2_id; } bool operator< (const EdgeDesc &other) const { - if ((device != 0) != (other.device != 0)) { - return (device != 0) < (other.device != 0); - } + if (m_id1 > std::numeric_limits::max () / 2) { - if (device != 0) { - - DeviceCompare dc; - bool dlt = dc (device, other.device); - if (dlt || dc (other.device, device)) { - return dlt; + if ((subcircuit_pair ().first != 0) != (other.subcircuit_pair ().first != 0)) { + return (subcircuit_pair ().first != 0) < (other.subcircuit_pair ().first != 0); } - if (terminal1_id != other.terminal1_id) { - return terminal1_id < other.terminal1_id; + if (subcircuit_pair ().first != 0) { + SubCircuitCompare scc; + if (! scc.equals (subcircuit_pair (), other.subcircuit_pair ())) { + return scc (subcircuit_pair (), other.subcircuit_pair ()); + } } - return terminal2_id < other.terminal2_id; } else { - SubCircuitCompare sc; - bool sclt = sc (subcircuit, other.subcircuit); - if (sclt || sc (other.subcircuit, subcircuit)) { - return sclt; + if ((device_pair ().first != 0) != (other.device_pair ().first != 0)) { + return (device_pair ().first != 0) < (other.device_pair ().first != 0); } - if (pin1_id != other.pin1_id) { - return pin1_id < other.pin1_id; + if (device_pair ().first != 0) { + DeviceCompare dc; + if (! dc.equals (device_pair (), other.device_pair ())) { + return dc (device_pair (), other.device_pair ()); + } } - return pin2_id < other.pin2_id; } + + if (m_id1 != other.m_id1) { + return m_id1 < other.m_id1; + } + return m_id2 < other.m_id2; } bool operator== (const EdgeDesc &other) const { - if ((device != 0) != (other.device != 0)) { - return false; - } + if (m_id1 > std::numeric_limits::max () / 2) { - if (device != 0) { - - DeviceCompare dc; - if (dc (device, other.device) || dc (other.device, device)) { + if ((subcircuit_pair ().first != 0) != (other.subcircuit_pair ().first != 0)) { return false; } - if (terminal1_id != other.terminal1_id) { - return false; + + if (subcircuit_pair ().first != 0) { + SubCircuitCompare scc; + if (! scc.equals (subcircuit_pair (), other.subcircuit_pair ())) { + return false; + } } - return terminal2_id == other.terminal2_id; } else { - SubCircuitCompare sc; - if (sc (subcircuit, other.subcircuit) || sc (other.subcircuit, subcircuit)) { + if ((device_pair ().first != 0) != (other.device_pair ().first != 0)) { return false; } - if (pin1_id != other.pin1_id) { - return false; + if (device_pair ().first != 0) { + DeviceCompare dc; + if (! dc.equals (device_pair (), other.device_pair ())) { + return false; + } } - return pin2_id == other.pin2_id; } + + return (m_id1 == other.m_id1 && m_id2 == other.m_id2); + } + + private: + char m_ref [sizeof (std::pair)]; + size_t m_id1, m_id2; + + std::pair &device_pair () + { + return *reinterpret_cast *> ((void *) &m_ref); + } + + const std::pair &device_pair () const + { + return *reinterpret_cast *> ((const void *) &m_ref); + } + + std::pair &subcircuit_pair () + { + return *reinterpret_cast *> ((void *) &m_ref); + } + + const std::pair &subcircuit_pair () const + { + return *reinterpret_cast *> ((const void *) &m_ref); } }; @@ -334,7 +465,7 @@ public: typedef std::vector, std::pair > >::const_iterator edge_iterator; - NetDeviceGraphNode (const db::Net *net, std::map &devmap, std::vector &device_prototypes, const std::map *circuit_map, const CircuitPinMapper *pin_map) + NetDeviceGraphNode (const db::Net *net, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const std::map *circuit_map, const CircuitPinMapper *pin_map) : mp_net (net), m_other_net_index (std::numeric_limits::max ()) { std::map n2entry; @@ -392,12 +523,9 @@ public: size_t pin2_id = *i; - EdgeDesc ed; - ed.subcircuit = sc; // NOTE: if a pin mapping is given, EdgeDesc::pin1_id and EdgeDesc::pin2_id are given // as pin ID's of the other circuit. - ed.pin1_id = pin_id; - ed.pin2_id = pin_map ? pin_map->normalize_pin_id (cr, pin2_id) : pin2_id; + EdgeDesc ed (sc, circuit_categorizer.cat_for_subcircuit (sc), pin_id, pin_map ? pin_map->normalize_pin_id (cr, pin2_id) : pin2_id); size_t this_pin2_id = cm ? cm->this_pin_from_other_pin (pin2_id) : pin2_id; const db::Net *net2 = sc->net_for_pin (this_pin2_id); @@ -417,27 +545,17 @@ public: for (db::Net::const_terminal_iterator i = net->begin_terminals (); i != net->end_terminals (); ++i) { const db::Device *d = i->device (); - - size_t dev_id = 0; - std::map::iterator di = devmap.find (d); - if (di != devmap.end ()) { - dev_id = di->second; - } else { - dev_id = device_prototypes.size (); - device_prototypes.push_back (d); - } - - EdgeDesc ed; - ed.device = device_prototypes [dev_id]; - ed.terminal1_id = translate_terminal_id (i->terminal_id (), d); + size_t device_cat = device_categorizer.cat_for_device (d); + size_t terminal1_id = translate_terminal_id (i->terminal_id (), d); const std::vector &td = d->device_class ()->terminal_definitions (); for (std::vector::const_iterator it = td.begin (); it != td.end (); ++it) { if (it->id () != i->terminal_id ()) { - EdgeDesc ed2 = ed; - ed2.terminal2_id = translate_terminal_id (it->id (), d); + size_t terminal2_id = translate_terminal_id (it->id (), d); + EdgeDesc ed2 (d, device_cat, terminal1_id, terminal2_id); + const db::Net *net2 = d->net_for_terminal (it->id ()); std::map::const_iterator in = n2entry.find (net2); @@ -573,12 +691,10 @@ public: // .. nothing yet .. } - void build (const db::Circuit *c, const std::map *circuit_and_pin_mapping, const CircuitPinMapper *circuit_pin_mapper) + void build (const db::Circuit *c, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const std::map *circuit_and_pin_mapping, const CircuitPinMapper *circuit_pin_mapper) { tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Building net graph for circuit: ")) + c->name ()); - m_device_map.clear (); - m_device_prototypes.clear (); m_nodes.clear (); m_net_index.clear (); @@ -589,7 +705,7 @@ public: m_nodes.reserve (nets); for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - NetDeviceGraphNode node (n.operator-> (), m_device_map, m_device_prototypes, circuit_and_pin_mapping, circuit_pin_mapper); + NetDeviceGraphNode node (n.operator-> (), device_categorizer, circuit_categorizer, circuit_and_pin_mapping, circuit_pin_mapper); m_nodes.push_back (node); } @@ -735,8 +851,6 @@ public: private: std::vector m_nodes; - std::map m_device_map; - std::vector m_device_prototypes; std::map m_net_index; }; @@ -747,9 +861,16 @@ public: : mp_logger (logger) { } - void same_nets (const db::Circuit *a, const db::Net *na, const db::Circuit *b, const db::Net *nb) + /** + * @brief Mark two nets as identical + * + * This makes a net na in netlist a identical to the corresponding + * net nb in netlist b. + * By default nets are not identical expect through their topology. + */ + void same_nets (const db::Net *na, const db::Net *nb) { - m_same_nets [std::make_pair (a, b)].push_back (std::make_pair (na, nb)); + m_same_nets [std::make_pair (na->circuit (), nb->circuit ())].push_back (std::make_pair (na, nb)); } /** @@ -775,8 +896,36 @@ public: m_circuit_pin_mapper.map_pins (cb, pin_ids); } + /** + * @brief Mark two device classes as identical + * + * This makes a device class ca in netlist a identical to the corresponding + * device class cb in netlist b. + * By default device classes with the same name are identical. + */ + void same_device_classes (const db::DeviceClass *ca, const db::DeviceClass *cb) + { + m_device_categorizer.same_class (ca, cb); + } + + /** + * @brief Mark two circuits as identical + * + * This makes a circuit ca in netlist a identical to the corresponding + * circuit cb in netlist b. + * By default circuits with the same name are identical. + */ + void same_circuits (const db::Circuit *ca, const db::Circuit *cb) + { + m_circuit_categorizer.same_circuit (ca, cb); + } + bool compare (const db::Netlist *a, const db::Netlist *b) const { + // we need to create a copy because this method is supposed to be const. + db::CircuitCategorizer circuit_categorizer = m_circuit_categorizer; + db::DeviceCategorizer device_categorizer = m_device_categorizer; + bool good = true; std::map > name2circuits; @@ -829,7 +978,7 @@ public: } bool pin_mismatch = false; - bool g = compare_circuits (ca, cb, *net_identity, pin_mismatch, pin_mapping); + bool g = compare_circuits (ca, cb, device_categorizer, circuit_categorizer, *net_identity, pin_mismatch, pin_mapping); if (! g) { good = false; } @@ -863,12 +1012,14 @@ public: } protected: - bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2, const std::vector > &net_identity, bool &pin_mismatch, std::map &circuit_and_pin_map) const; + bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, const std::vector > &net_identity, bool &pin_mismatch, std::map &circuit_and_pin_mapping) const; bool all_subcircuits_verified (const db::Circuit *c, const std::set &verified_circuits) const; NetlistCompareLogger *mp_logger; std::map, std::vector > > m_same_nets; CircuitPinMapper m_circuit_pin_mapper; + DeviceCategorizer m_device_categorizer; + CircuitCategorizer m_circuit_categorizer; }; bool @@ -946,14 +1097,14 @@ compute_subcircuit_key (const db::SubCircuit &subcircuit, const db::NetDeviceGra } bool -NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, const std::vector > &net_identity, bool &pin_mismatch, std::map &circuit_and_pin_mapping) const +NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, const std::vector > &net_identity, bool &pin_mismatch, std::map &circuit_and_pin_mapping) const { db::NetDeviceGraph g1, g2; // NOTE: for normalization we map all subcircuits of c1 to c2. // Also, pin swapping will only happen there. - g1.build (c1, &circuit_and_pin_mapping, &m_circuit_pin_mapper); - g2.build (c2, 0, &m_circuit_pin_mapper); + g1.build (c1, device_categorizer, circuit_categorizer, &circuit_and_pin_mapping, &m_circuit_pin_mapper); + g2.build (c2, device_categorizer, circuit_categorizer, 0, &m_circuit_pin_mapper); for (std::vector >::const_iterator p = net_identity.begin (); p != net_identity.end (); ++p) { size_t ni1 = g1.node_index_for_net (p->first); @@ -1116,7 +1267,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, // Report device assignment - std::multimap >, const db::Device *> device_map; + std::multimap >, std::pair > device_map; for (db::Circuit::const_device_iterator d = c1->begin_devices (); d != c1->end_devices (); ++d) { @@ -1134,7 +1285,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, good = false; } else { // TODO: report devices which cannot be distiguished topologically? - device_map.insert (std::make_pair (k, d.operator-> ())); + device_map.insert (std::make_pair (k, std::make_pair (d.operator-> (), device_categorizer.cat_for_device (d.operator-> ())))); } } @@ -1154,7 +1305,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, std::sort (k.begin (), k.end ()); - std::multimap >, const db::Device *>::const_iterator dm = device_map.find (k); + std::multimap >, std::pair >::const_iterator dm = device_map.find (k); if (! mapped || dm == device_map.end () || dm->first != k) { @@ -1165,16 +1316,18 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCompare dc; - if (dc (dm->second, d.operator-> ()) || dc (d.operator-> (), dm->second)) { - if (dm->second->device_class ()->name () != d->device_class ()->name ()) { - mp_logger->match_devices_with_different_device_classes (dm->second, d.operator-> ()); + size_t device_cat = device_categorizer.cat_for_device (d.operator-> ()); + + if (! dc.equals (dm->second, std::make_pair (d.operator-> (), device_cat))) { + if (dm->second.second != device_cat) { + mp_logger->match_devices_with_different_device_classes (dm->second.first, d.operator-> ()); good = false; } else { - mp_logger->match_devices_with_different_parameters (dm->second, d.operator-> ()); + mp_logger->match_devices_with_different_parameters (dm->second.first, d.operator-> ()); good = false; } } else { - mp_logger->match_devices (dm->second, d.operator-> ()); + mp_logger->match_devices (dm->second.first, d.operator-> ()); } device_map.erase (dm); @@ -1183,15 +1336,15 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } - for (std::multimap >, const db::Device *>::const_iterator dm = device_map.begin (); dm != device_map.end (); ++dm) { - mp_logger->device_mismatch (dm->second, 0); + for (std::multimap >, std::pair >::const_iterator dm = device_map.begin (); dm != device_map.end (); ++dm) { + mp_logger->device_mismatch (dm->second.first, 0); good = false; } // Report subcircuit assignment - std::multimap >, const db::SubCircuit *> subcircuit_map; + std::multimap >, std::pair > subcircuit_map; for (db::Circuit::const_subcircuit_iterator sc = c1->begin_subcircuits (); sc != c1->end_subcircuits (); ++sc) { @@ -1209,7 +1362,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, good = false; } else if (! k.empty ()) { // TODO: report devices which cannot be distiguished topologically? - subcircuit_map.insert (std::make_pair (k, sc.operator-> ())); + subcircuit_map.insert (std::make_pair (k, std::make_pair (sc.operator-> (), circuit_categorizer.cat_for_subcircuit (sc.operator-> ())))); } } @@ -1229,7 +1382,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, std::sort (k.begin (), k.end ()); - std::multimap >, const db::SubCircuit *>::const_iterator scm = subcircuit_map.find (k); + std::multimap >, std::pair >::const_iterator scm = subcircuit_map.find (k); if (! mapped || scm == subcircuit_map.end ()) { @@ -1239,12 +1392,13 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } else { db::SubCircuitCompare scc; + size_t sc_cat = circuit_categorizer.cat_for_subcircuit (sc.operator-> ()); - if (scc (scm->second, sc.operator-> ()) || scc (sc.operator-> (), scm->second)) { - mp_logger->subcircuit_mismatch (scm->second, sc.operator-> ()); + if (! scc.equals (scm->second, std::make_pair (sc.operator-> (), sc_cat))) { + mp_logger->subcircuit_mismatch (scm->second.first, sc.operator-> ()); good = false; } else { - mp_logger->match_subcircuits (scm->second, sc.operator-> ()); + mp_logger->match_subcircuits (scm->second.first, sc.operator-> ()); } subcircuit_map.erase (scm); @@ -1253,8 +1407,8 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } - for (std::multimap >, const db::SubCircuit *>::const_iterator scm = subcircuit_map.begin (); scm != subcircuit_map.end (); ++scm) { - mp_logger->subcircuit_mismatch (scm->second, 0); + for (std::multimap >, std::pair >::const_iterator scm = subcircuit_map.begin (); scm != subcircuit_map.end (); ++scm) { + mp_logger->subcircuit_mismatch (scm->second.first, 0); good = false; } @@ -1450,12 +1604,12 @@ TEST(1_SimpleInverter) EXPECT_EQ (logger.text (), "begin_circuit INV INV\n" - "match_nets VSS VSS\n" "match_nets VDD VDD\n" + "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" - "match_pins $3 $2\n" "match_pins $2 $0\n" + "match_pins $3 $2\n" "match_pins $1 $3\n" "match_pins $0 $1\n" "match_devices $2 $1\n" @@ -1487,8 +1641,8 @@ TEST(2_SimpleInverterWithForcedNetAssignment) db::NetlistComparer comp (&logger); const db::Circuit *ca = nl1.circuit_by_name ("INV"); const db::Circuit *cb = nl2.circuit_by_name ("INV"); - comp.same_nets (ca, ca->net_by_name ("VDD"), cb, cb->net_by_name ("VDD")); - comp.same_nets (ca, ca->net_by_name ("VSS"), cb, cb->net_by_name ("VSS")); + comp.same_nets (ca->net_by_name ("VDD"), cb->net_by_name ("VDD")); + comp.same_nets (ca->net_by_name ("VSS"), cb->net_by_name ("VSS")); bool good = comp.compare (&nl1, &nl2); @@ -1496,8 +1650,8 @@ TEST(2_SimpleInverterWithForcedNetAssignment) "begin_circuit INV INV\n" "match_nets OUT OUT\n" "match_nets IN IN\n" - "match_pins $3 $2\n" "match_pins $2 $0\n" + "match_pins $3 $2\n" "match_pins $1 $3\n" "match_pins $0 $1\n" "match_devices $2 $1\n" @@ -1536,15 +1690,15 @@ TEST(3_Buffer) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" - "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets VDD VDD\n" "match_nets IN IN\n" + "match_nets VSS VSS\n" "match_nets INT $10\n" - "match_pins $3 $2\n" "match_pins $1 $3\n" "match_pins $2 $0\n" "match_pins $0 $1\n" + "match_pins $3 $2\n" "match_devices $1 $1\n" "match_devices $3 $2\n" "match_devices $2 $3\n" @@ -1591,16 +1745,16 @@ TEST(4_BufferTwoPaths) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" - "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_ambiguous_nets INT $10\n" "match_nets INT2 $11\n" "match_nets VDD VDD\n" "match_nets IN IN\n" - "match_pins $3 $2\n" + "match_nets VSS VSS\n" "match_pins $1 $3\n" "match_pins $2 $0\n" "match_pins $0 $1\n" + "match_pins $3 $2\n" "match_devices $1 $1\n" "match_devices $3 $2\n" "match_devices $5 $3\n" @@ -1650,8 +1804,8 @@ TEST(5_BufferTwoPathsDifferentParameters) // Forcing the power nets into equality makes the parameter error harder to detect const db::Circuit *ca = nl1.circuit_by_name ("BUF"); const db::Circuit *cb = nl2.circuit_by_name ("BUF"); - comp.same_nets (ca, ca->net_by_name ("VDD"), cb, cb->net_by_name ("VDD")); - comp.same_nets (ca, ca->net_by_name ("VSS"), cb, cb->net_by_name ("VSS")); + comp.same_nets (ca->net_by_name ("VDD"), cb->net_by_name ("VDD")); + comp.same_nets (ca->net_by_name ("VSS"), cb->net_by_name ("VSS")); bool good = comp.compare (&nl1, &nl2); @@ -1661,10 +1815,10 @@ TEST(5_BufferTwoPathsDifferentParameters) "match_nets IN IN\n" "match_ambiguous_nets INT $10\n" "match_nets INT2 $11\n" - "match_pins $3 $2\n" "match_pins $1 $3\n" "match_pins $2 $0\n" "match_pins $0 $1\n" + "match_pins $3 $2\n" "match_devices $1 $1\n" "match_devices $3 $2\n" "match_devices $5 $3\n" @@ -1718,15 +1872,15 @@ TEST(6_BufferTwoPathsAdditionalDevices) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets INT $11\n" - "match_nets VSS VSS\n" "match_nets IN IN\n" - "match_nets OUT OUT\n" "match_nets VDD VDD\n" + "match_nets OUT OUT\n" + "match_nets VSS VSS\n" "match_nets INT2 $10\n" - "match_pins $3 $2\n" "match_pins $1 $3\n" "match_pins $2 $0\n" "match_pins $0 $1\n" + "match_pins $3 $2\n" "match_devices $5 $1\n" "match_devices $7 $2\n" "device_mismatch (null) $3\n" @@ -1983,27 +2137,27 @@ TEST(10_SimpleSubCircuits) EXPECT_EQ (logger.text (), "begin_circuit INV INV\n" - "match_nets VSS VSS\n" "match_nets VDD VDD\n" + "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" - "match_pins $3 $2\n" "match_pins $2 $0\n" + "match_pins $3 $2\n" "match_pins $1 $3\n" "match_pins $0 $1\n" "match_devices $2 $1\n" "match_devices $1 $2\n" "end_circuit INV INV MATCH\n" "begin_circuit TOP TOP\n" - "match_nets IN IN\n" "match_nets OUT OUT\n" - "match_nets VDD VDD\n" + "match_nets IN IN\n" "match_nets VSS VSS\n" + "match_nets VDD VDD\n" "match_nets INT INT\n" - "match_pins $0 $2\n" "match_pins $1 $0\n" - "match_pins $2 $1\n" + "match_pins $0 $2\n" "match_pins $3 $3\n" + "match_pins $2 $1\n" "match_subcircuits $2 $1\n" "match_subcircuits $1 $2\n" "end_circuit TOP TOP MATCH" @@ -2051,8 +2205,8 @@ TEST(11_MismatchingSubcircuits) "match_nets IN IN\n" "net_mismatch VDD (null)\n" "net_mismatch (null) VDD\n" - "match_pins $3 $2\n" "pin_mismatch $2 (null)\n" + "match_pins $3 $2\n" "match_pins $1 $3\n" "match_pins $0 $1\n" "pin_mismatch (null) $0\n" @@ -2100,12 +2254,12 @@ TEST(12_MismatchingSubcircuitsDuplicates) EXPECT_EQ (logger.text (), "begin_circuit INV INV\n" - "match_nets VSS VSS\n" "match_nets VDD VDD\n" + "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" - "match_pins $3 $2\n" "match_pins $2 $0\n" + "match_pins $3 $2\n" "match_pins $1 $3\n" "match_pins $0 $1\n" "match_devices $2 $1\n" @@ -2118,10 +2272,10 @@ TEST(12_MismatchingSubcircuitsDuplicates) "match_nets INT INT\n" "net_mismatch OUT (null)\n" "net_mismatch (null) OUT\n" - "match_pins $0 $1\n" "pin_mismatch $1 (null)\n" - "match_pins $2 $2\n" + "match_pins $0 $1\n" "match_pins $3 $3\n" + "match_pins $2 $2\n" "pin_mismatch (null) $0\n" "subcircuit_mismatch $2 (null)\n" "subcircuit_mismatch $3 (null)\n" @@ -2171,27 +2325,27 @@ TEST(13_MismatchingSubcircuitsAdditionalHierarchy) EXPECT_EQ (logger.text (), "circuit_mismatch VIA (null)\n" "begin_circuit INV INV\n" - "match_nets VSS VSS\n" "match_nets VDD VDD\n" + "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" - "match_pins $3 $2\n" "match_pins $2 $0\n" + "match_pins $3 $2\n" "match_pins $1 $3\n" "match_pins $0 $1\n" "match_devices $2 $1\n" "match_devices $1 $2\n" "end_circuit INV INV MATCH\n" "begin_circuit TOP TOP\n" - "match_nets IN IN\n" "match_nets OUT OUT\n" - "match_nets VDD VDD\n" + "match_nets IN IN\n" "match_nets VSS VSS\n" + "match_nets VDD VDD\n" "match_nets INT INT\n" - "match_pins $0 $1\n" "match_pins $1 $0\n" - "match_pins $2 $2\n" + "match_pins $0 $1\n" "match_pins $3 $3\n" + "match_pins $2 $2\n" "match_subcircuits $3 $1\n" "match_subcircuits $1 $2\n" "end_circuit TOP TOP MATCH" @@ -2241,9 +2395,9 @@ TEST(14_Subcircuit2Nand) "match_nets VSS VSS\n" "match_nets VDD VDD\n" "match_nets B B\n" - "match_nets INT INT\n" "match_nets OUT OUT\n" "match_nets A A\n" + "match_nets INT INT\n" "match_pins $4 $4\n" "match_pins $3 $3\n" "match_pins $1 $1\n" @@ -2255,17 +2409,17 @@ TEST(14_Subcircuit2Nand) "match_devices $4 $4\n" "end_circuit NAND NAND MATCH\n" "begin_circuit TOP TOP\n" - "match_nets IN2 IN2\n" "match_nets OUT OUT\n" - "match_nets IN1 IN1\n" - "match_nets VDD VDD\n" + "match_nets IN2 IN2\n" "match_nets VSS VSS\n" + "match_nets VDD VDD\n" + "match_nets IN1 IN1\n" "match_nets INT INT\n" - "match_pins $1 $1\n" "match_pins $2 $2\n" - "match_pins $0 $0\n" - "match_pins $3 $3\n" + "match_pins $1 $1\n" "match_pins $4 $4\n" + "match_pins $3 $3\n" + "match_pins $0 $0\n" "match_subcircuits $2 $1\n" "match_subcircuits $1 $2\n" "end_circuit TOP TOP MATCH" @@ -2315,9 +2469,9 @@ TEST(14_Subcircuit2NandMismatchNoSwap) "match_nets VSS VSS\n" "match_nets VDD VDD\n" "match_nets B B\n" - "match_nets INT INT\n" "match_nets OUT OUT\n" "match_nets A A\n" + "match_nets INT INT\n" "match_pins $4 $4\n" "match_pins $3 $3\n" "match_pins $1 $1\n" @@ -2336,11 +2490,11 @@ TEST(14_Subcircuit2NandMismatchNoSwap) "match_nets IN1 IN2\n" "net_mismatch IN2 (null)\n" "net_mismatch (null) INT\n" - "pin_mismatch $1 (null)\n" "match_pins $2 $2\n" - "match_pins $0 $1\n" - "match_pins $3 $3\n" + "pin_mismatch $1 (null)\n" "match_pins $4 $4\n" + "match_pins $3 $3\n" + "match_pins $0 $1\n" "pin_mismatch (null) $0\n" "subcircuit_mismatch $1 (null)\n" "subcircuit_mismatch (null) $1\n" @@ -2393,9 +2547,9 @@ TEST(14_Subcircuit2MatchWithSwap) "match_nets VSS VSS\n" "match_nets VDD VDD\n" "match_nets B B\n" - "match_nets INT INT\n" "match_nets OUT OUT\n" "match_nets A A\n" + "match_nets INT INT\n" "match_pins $4 $4\n" "match_pins $3 $3\n" "match_pins $1 $1\n" @@ -2407,17 +2561,17 @@ TEST(14_Subcircuit2MatchWithSwap) "match_devices $4 $4\n" "end_circuit NAND NAND MATCH\n" "begin_circuit TOP TOP\n" - "match_nets IN2 IN2\n" "match_nets OUT OUT\n" + "match_nets IN2 IN2\n" "match_nets VSS VSS\n" "match_nets INT INT\n" "match_nets VDD VDD\n" "match_nets IN1 IN1\n" - "match_pins $1 $1\n" "match_pins $2 $2\n" - "match_pins $0 $0\n" - "match_pins $3 $3\n" + "match_pins $1 $1\n" "match_pins $4 $4\n" + "match_pins $3 $3\n" + "match_pins $0 $0\n" "match_subcircuits $2 $1\n" "match_subcircuits $1 $2\n" "end_circuit TOP TOP MATCH" From d255617051d4b95c81f4c45cc7a94a7d609c6e99 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 28 Mar 2019 18:01:22 +0100 Subject: [PATCH 17/54] WIP: netlist compare - tests for device class equivalence mapping, added Netlist#device_class_by_name --- src/db/db/dbNetlist.cc | 10 ++++ src/db/db/dbNetlist.h | 17 +++++++ src/db/db/gsiDeclDbNetlist.cc | 6 ++- src/db/unit_tests/dbNetlistCompareTests.cc | 59 ++++++++++++++++++++++ testdata/ruby/dbNetlist.rb | 3 ++ 5 files changed, 94 insertions(+), 1 deletion(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 85719f6d3..a1fdcab96 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -369,6 +369,16 @@ void Netlist::remove_circuit (Circuit *circuit) m_circuits.erase (circuit); } +DeviceClass *Netlist::device_class_by_name (const std::string &name) +{ + for (device_class_iterator d = begin_device_classes (); d != end_device_classes (); ++d) { + if (d->name () == name) { + return d.operator-> (); + } + } + return 0; +} + void Netlist::add_device_class (DeviceClass *device_class) { m_device_classes.push_back (device_class); diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index aeae88923..98d47f84b 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -281,6 +281,23 @@ public: */ void remove_device_class (DeviceClass *device_class); + /** + * @brief Gets a device class by it's name (const version) + * + * This method returns 0 if there is no class with this name. + */ + const DeviceClass *device_class_by_name (const std::string &name) const + { + return const_cast (this)->device_class_by_name (name); + } + + /** + * @brief Gets a device class by it's name (non-const version) + * + * This method returns 0 if there is no class with this name. + */ + DeviceClass *device_class_by_name (const std::string &name); + /** * @brief Begin iterator for the device classes of the netlist (non-const version) */ diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 94c152521..5dc648258 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -959,7 +959,7 @@ Class decl_dbNetlist ("db", "Netlist", ) + gsi::method ("circuit_by_name", (db::Circuit *(db::Netlist::*) (const std::string &)) &db::Netlist::circuit_by_name, gsi::arg ("name"), "@brief Gets the circuit object for a given name.\n" - "If the ID is not a valid circuit name, nil is returned." + "If the name is not a valid circuit name, nil is returned." ) + gsi::iterator ("each_circuit_top_down", (db::Netlist::top_down_circuit_iterator (db::Netlist::*) ()) &db::Netlist::begin_top_down, (db::Netlist::top_down_circuit_iterator (db::Netlist::*) ()) &db::Netlist::end_top_down, "@brief Iterates over the circuits top-down\n" @@ -990,6 +990,10 @@ Class decl_dbNetlist ("db", "Netlist", "Use this method with care as it may corrupt the internal structure of the netlist. " "Only use this method when device refers to this device class." ) + + gsi::method ("device_class_by_name", (db::DeviceClass *(db::Netlist::*) (const std::string &)) &db::Netlist::device_class_by_name, gsi::arg ("name"), + "@brief Gets the device class for a given name.\n" + "If the name is not a valid device class name, nil is returned." + ) + 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" ) + diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 61a2ad786..1bf6e0fb9 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -1511,6 +1511,11 @@ public: return tl::join (m_texts, "\n"); } + void clear () + { + m_texts.clear (); + } + private: std::vector m_texts; @@ -1552,6 +1557,14 @@ static void prep_nl (db::Netlist &nl, const char *str) dc->set_name ("NMOS"); nl.add_device_class (dc); + dc = new db::DeviceClassMOS3Transistor (); + dc->set_name ("PMOSB"); + nl.add_device_class (dc); + + dc = new db::DeviceClassMOS3Transistor (); + dc->set_name ("NMOSB"); + nl.add_device_class (dc); + dc = new db::DeviceClassMOS4Transistor (); dc->set_name ("PMOS4"); nl.add_device_class (dc); @@ -1619,6 +1632,52 @@ TEST(1_SimpleInverter) EXPECT_EQ (good, true); } +TEST(1_SimpleInverterMatchedDeviceClasses) +{ + const char *nls1 = + "circuit INV ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $2 (S=VSS,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + const char *nls2 = + "circuit INV ($1=VDD,$2=IN,$3=VSS,$4=OUT);\n" + " device NMOSB $1 (S=OUT,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOSB $2 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + comp.same_device_classes (nl1.device_class_by_name ("PMOS"), nl2.device_class_by_name ("PMOSB")); + + bool good = comp.compare (&nl1, &nl2); + EXPECT_EQ (good, false); + + logger.clear (); + comp.same_device_classes (nl1.device_class_by_name ("NMOS"), nl2.device_class_by_name ("NMOSB")); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit INV INV\n" + "match_nets VDD VDD\n" + "match_nets VSS VSS\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_pins $2 $0\n" + "match_pins $3 $2\n" + "match_pins $1 $3\n" + "match_pins $0 $1\n" + "match_devices $2 $1\n" + "match_devices $1 $2\n" + "end_circuit INV INV MATCH" + ); + EXPECT_EQ (good, true); +} + TEST(2_SimpleInverterWithForcedNetAssignment) { const char *nls1 = diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 96d5d6c51..0897f9ad2 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -105,6 +105,9 @@ class DBNetlist_TestClass < TestBase nl.add(cc) cc.name = "UVW" + assert_equal(nl.device_class_by_name("UVW") == cc, true) + assert_equal(nl.device_class_by_name("doesntexist") == nil, true) + names = [] nl.each_device_class { |i| names << i.name } assert_equal(names, [ c.name, cc.name ]) From e8d59504dd7a2218b230c51954d318b960db096d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 29 Mar 2019 00:13:13 +0100 Subject: [PATCH 18/54] WIP: netlist compare - forced matching of circuits. --- src/db/unit_tests/dbNetlistCompareTests.cc | 92 ++++++++++++++++++++-- 1 file changed, 86 insertions(+), 6 deletions(-) diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 1bf6e0fb9..7286c743d 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -277,10 +277,12 @@ public: std::map::const_iterator c = m_cat_by_name.find (cls->name ()); if (c != m_cat_by_name.end ()) { + m_cat_by_ptr.insert (std::make_pair (cls, c->second)); return c->second; } else { ++m_next_cat; m_cat_by_name.insert (std::make_pair (cls->name (), m_next_cat)); + m_cat_by_ptr.insert (std::make_pair (cls, m_next_cat)); return m_next_cat; } } @@ -312,8 +314,13 @@ public: const db::Circuit *cr = subcircuit->circuit_ref (); if (! cr) { return 0; + } else { + return cat_for_circuit (cr); } + } + size_t cat_for_circuit (const db::Circuit *cr) + { std::map::const_iterator cp = m_cat_by_ptr.find (cr); if (cp != m_cat_by_ptr.end ()) { return cp->second; @@ -321,10 +328,12 @@ public: std::map::const_iterator c = m_cat_by_name.find (cr->name ()); if (c != m_cat_by_name.end ()) { + m_cat_by_ptr.insert (std::make_pair (cr, c->second)); return c->second; } else { ++m_next_cat; m_cat_by_name.insert (std::make_pair (cr->name (), m_next_cat)); + m_cat_by_ptr.insert (std::make_pair (cr, m_next_cat)); return m_next_cat; } } @@ -928,21 +937,23 @@ public: bool good = true; - std::map > name2circuits; + std::map > cat2circuits; for (db::Netlist::const_circuit_iterator i = a->begin_circuits (); i != a->end_circuits (); ++i) { - name2circuits[i->name ()].first = i.operator-> (); + size_t cat = circuit_categorizer.cat_for_circuit (i.operator-> ()); + cat2circuits[cat].first = i.operator-> (); } for (db::Netlist::const_circuit_iterator i = b->begin_circuits (); i != b->end_circuits (); ++i) { - name2circuits[i->name ()].second = i.operator-> (); + size_t cat = circuit_categorizer.cat_for_circuit (i.operator-> ()); + cat2circuits[cat].second = i.operator-> (); } if (mp_logger) { mp_logger->begin_netlist (a, b); } - for (std::map >::const_iterator i = name2circuits.begin (); i != name2circuits.end (); ++i) { + for (std::map >::const_iterator i = cat2circuits.begin (); i != cat2circuits.end (); ++i) { if (! i->second.first || ! i->second.second) { good = false; if (mp_logger) { @@ -956,8 +967,10 @@ public: for (db::Netlist::const_bottom_up_circuit_iterator c = a->begin_bottom_up (); c != a->end_bottom_up (); ++c) { - std::map >::const_iterator i = name2circuits.find ((*c)->name ()); - tl_assert (i != name2circuits.end ()); + size_t ccat = circuit_categorizer.cat_for_circuit (*c); + + std::map >::const_iterator i = cat2circuits.find (ccat); + tl_assert (i != cat2circuits.end ()); if (i->second.first && i->second.second) { @@ -2225,6 +2238,73 @@ TEST(10_SimpleSubCircuits) EXPECT_EQ (good, true); } +TEST(10_SimpleSubCircuitsMatchedNames) +{ + const char *nls1 = + "circuit INV ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $2 (S=VSS,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TOP ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n" + " subcircuit INV $1 ($1=IN,$2=INT,$3=VDD,$4=VSS);\n" + " subcircuit INV $2 ($1=INT,$2=OUT,$3=VDD,$4=VSS);\n" + "end;\n"; + + const char *nls2 = + "circuit INVB ($1=VDD,$2=IN,$3=VSS,$4=OUT);\n" + " device NMOS $1 (S=OUT,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TOP ($1=OUT,$2=VDD,$3=IN,$4=VSS);\n" + " subcircuit INVB $1 ($1=VDD,$2=INT,$3=VSS,$4=OUT);\n" + " subcircuit INVB $2 ($1=VDD,$2=IN,$3=VSS,$4=INT);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + EXPECT_EQ (good, false); + + logger.clear (); + comp.same_circuits (nl1.circuit_by_name ("INV"), nl2.circuit_by_name ("INVB")); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit INV INVB\n" + "match_nets VDD VDD\n" + "match_nets VSS VSS\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_pins $2 $0\n" + "match_pins $3 $2\n" + "match_pins $1 $3\n" + "match_pins $0 $1\n" + "match_devices $2 $1\n" + "match_devices $1 $2\n" + "end_circuit INV INVB MATCH\n" + "begin_circuit TOP TOP\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_nets VSS VSS\n" + "match_nets VDD VDD\n" + "match_nets INT INT\n" + "match_pins $1 $0\n" + "match_pins $0 $2\n" + "match_pins $3 $3\n" + "match_pins $2 $1\n" + "match_subcircuits $2 $1\n" + "match_subcircuits $1 $2\n" + "end_circuit TOP TOP MATCH" + ); + + EXPECT_EQ (good, true); +} + TEST(11_MismatchingSubcircuits) { const char *nls1 = From f06d435b05974d9e391919d7e63bc985c3c04442 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 29 Mar 2019 00:37:45 +0100 Subject: [PATCH 19/54] WIP: netlist comparer - moved into it's own files. --- src/db/db/db.pro | 6 +- src/db/db/dbNetlistCompare.cc | 1337 ++++++++++++++++++ src/db/db/dbNetlistCompare.h | 216 +++ src/db/unit_tests/dbNetlistCompareTests.cc | 1449 +------------------- 4 files changed, 1578 insertions(+), 1430 deletions(-) create mode 100644 src/db/db/dbNetlistCompare.cc create mode 100644 src/db/db/dbNetlistCompare.h diff --git a/src/db/db/db.pro b/src/db/db/db.pro index fd68bd081..1ff2c1882 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -172,7 +172,8 @@ SOURCES = \ dbDeepEdgePairs.cc \ dbRegionUtils.cc \ dbEdgesUtils.cc \ - dbRegionProcessors.cc + dbRegionProcessors.cc \ + dbNetlistCompare.cc HEADERS = \ dbArray.h \ @@ -310,7 +311,8 @@ HEADERS = \ dbRegionUtils.h \ dbEdgesUtils.h \ dbRegionProcessors.h \ - gsiDeclDbHelpers.h + gsiDeclDbHelpers.h \ + dbNetlistCompare.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc new file mode 100644 index 000000000..200062bf5 --- /dev/null +++ b/src/db/db/dbNetlistCompare.cc @@ -0,0 +1,1337 @@ + + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "dbNetlistCompare.h" +#include "dbHash.h" +#include "tlProgress.h" +#include "tlTimer.h" + +namespace db +{ + +// -------------------------------------------------------------------------------------------------------------------- +// DeviceCompare definition and implementation + +struct DeviceCompare +{ + bool operator() (const std::pair &d1, const std::pair &d2) const + { + if (d1.second != d2.second) { + return d1.second < d2.second; + } + + const std::vector &dp = d1.first->device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = dp.begin (); i != dp.end (); ++i) { + double v1 = d1.first->parameter_value (i->id ()); + double v2 = d2.first->parameter_value (i->id ()); + if (fabs (v1 - v2) > db::epsilon) { + return v1 < v2; + } + } + return false; + } + + bool equals (const std::pair &d1, const std::pair &d2) const + { + if (d1.second != d2.second) { + return false; + } + + const std::vector &dp = d1.first->device_class ()->parameter_definitions (); + for (std::vector::const_iterator i = dp.begin (); i != dp.end (); ++i) { + double v1 = d1.first->parameter_value (i->id ()); + double v2 = d2.first->parameter_value (i->id ()); + if (fabs (v1 - v2) > db::epsilon) { + return false; + } + } + return true; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- +// SubCircuitCompare definition and implementation + +struct SubCircuitCompare +{ + bool operator() (const std::pair &sc1, const std::pair &sc2) const + { + return sc1.second < sc2.second; + } + + bool equals (const std::pair &sc1, const std::pair &sc2) const + { + return sc1.second == sc2.second; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- +// CircuitPinMapper definition and implementation + +class CircuitPinMapper +{ +public: + CircuitPinMapper () + { + // .. nothing yet .. + } + + void map_pins (const db::Circuit *circuit, size_t pin1_id, size_t pin2_id) + { + m_pin_map [circuit].insert (std::make_pair (pin1_id, pin2_id)); + } + + void map_pins (const db::Circuit *circuit, const std::vector &pin_ids) + { + if (pin_ids.size () < 2) { + return; + } + + std::map &pm = m_pin_map [circuit]; + for (size_t i = 1; i < pin_ids.size (); ++i) { + pm.insert (std::make_pair (pin_ids [i], pin_ids [0])); + } + } + + size_t normalize_pin_id (const db::Circuit *circuit, size_t pin_id) const + { + std::map >::const_iterator pm = m_pin_map.find (circuit); + if (pm != m_pin_map.end ()) { + std::map::const_iterator ipm = pm->second.find (pin_id); + if (ipm != pm->second.end ()) { + return ipm->second; + } + } + return pin_id; + } + +private: + std::map > m_pin_map; +}; + +// -------------------------------------------------------------------------------------------------------------------- +// CircuitMapper definition and implementation + +class CircuitMapper +{ +public: + CircuitMapper () + : mp_other (0) + { + // .. nothing yet .. + } + + void set_other (const db::Circuit *other) + { + mp_other = other; + } + + const db::Circuit *other () const + { + return mp_other; + } + + void map_pin (size_t this_pin, size_t other_pin) + { + m_pin_map.insert (std::make_pair (this_pin, other_pin)); + m_rev_pin_map.insert (std::make_pair (other_pin, this_pin)); + } + + size_t other_pin_from_this_pin (size_t this_pin) const + { + std::map::const_iterator i = m_pin_map.find (this_pin); + tl_assert (i != m_pin_map.end ()); + return i->second; + } + + size_t this_pin_from_other_pin (size_t other_pin) const + { + std::map::const_iterator i = m_rev_pin_map.find (other_pin); + tl_assert (i != m_rev_pin_map.end ()); + return i->second; + } + +private: + const db::Circuit *mp_other; + std::map m_pin_map, m_rev_pin_map; +}; + +// -------------------------------------------------------------------------------------------------------------------- +// DeviceCategorizer definition and implementation + +class DeviceCategorizer +{ +public: + DeviceCategorizer () + : m_next_cat (0) + { + // .. nothing yet .. + } + + void same_class (const db::DeviceClass *ca, const db::DeviceClass *cb) + { + ++m_next_cat; + m_cat_by_ptr.insert (std::make_pair (ca, m_next_cat)); + m_cat_by_ptr.insert (std::make_pair (cb, m_next_cat)); + } + + size_t cat_for_device (const db::Device *device) + { + const db::DeviceClass *cls = device->device_class (); + if (! cls) { + return 0; + } + + std::map::const_iterator cp = m_cat_by_ptr.find (cls); + if (cp != m_cat_by_ptr.end ()) { + return cp->second; + } + + std::map::const_iterator c = m_cat_by_name.find (cls->name ()); + if (c != m_cat_by_name.end ()) { + m_cat_by_ptr.insert (std::make_pair (cls, c->second)); + return c->second; + } else { + ++m_next_cat; + m_cat_by_name.insert (std::make_pair (cls->name (), m_next_cat)); + m_cat_by_ptr.insert (std::make_pair (cls, m_next_cat)); + return m_next_cat; + } + } + +public: + std::map m_cat_by_ptr; + std::map m_cat_by_name; + size_t m_next_cat; +}; + +// -------------------------------------------------------------------------------------------------------------------- +// CircuitCategorizer definition and implementation + +class CircuitCategorizer +{ +public: + CircuitCategorizer () + : m_next_cat (0) + { + // .. nothing yet .. + } + + void same_circuit (const db::Circuit *ca, const db::Circuit *cb) + { + ++m_next_cat; + m_cat_by_ptr.insert (std::make_pair (ca, m_next_cat)); + m_cat_by_ptr.insert (std::make_pair (cb, m_next_cat)); + } + + size_t cat_for_subcircuit (const db::SubCircuit *subcircuit) + { + const db::Circuit *cr = subcircuit->circuit_ref (); + if (! cr) { + return 0; + } else { + return cat_for_circuit (cr); + } + } + + size_t cat_for_circuit (const db::Circuit *cr) + { + std::map::const_iterator cp = m_cat_by_ptr.find (cr); + if (cp != m_cat_by_ptr.end ()) { + return cp->second; + } + + std::map::const_iterator c = m_cat_by_name.find (cr->name ()); + if (c != m_cat_by_name.end ()) { + m_cat_by_ptr.insert (std::make_pair (cr, c->second)); + return c->second; + } else { + ++m_next_cat; + m_cat_by_name.insert (std::make_pair (cr->name (), m_next_cat)); + m_cat_by_ptr.insert (std::make_pair (cr, m_next_cat)); + return m_next_cat; + } + } + +public: + std::map m_cat_by_ptr; + std::map m_cat_by_name; + size_t m_next_cat; +}; + +// -------------------------------------------------------------------------------------------------------------------- +// NetDeviceGraphNode definition and implementation + +static size_t translate_terminal_id (size_t tid, const db::Device *device) +{ + return device->device_class () ? device->device_class ()->normalize_terminal_id (tid) : tid; +} + +class NetDeviceGraphNode +{ +public: + struct EdgeDesc { + + EdgeDesc (const db::Device *device, size_t device_category, size_t terminal1_id, size_t terminal2_id) + { + device_pair ().first = device; + device_pair ().second = device_category; + m_id1 = terminal1_id; + m_id2 = terminal2_id; + } + + EdgeDesc (const db::SubCircuit *subcircuit, size_t subcircuit_category, size_t pin1_id, size_t pin2_id) + { + subcircuit_pair ().first = subcircuit; + subcircuit_pair ().second = subcircuit_category; + m_id1 = std::numeric_limits::max () - pin1_id; + m_id2 = pin2_id; + } + + bool operator< (const EdgeDesc &other) const + { + if (m_id1 > std::numeric_limits::max () / 2) { + + if ((subcircuit_pair ().first != 0) != (other.subcircuit_pair ().first != 0)) { + return (subcircuit_pair ().first != 0) < (other.subcircuit_pair ().first != 0); + } + + if (subcircuit_pair ().first != 0) { + SubCircuitCompare scc; + if (! scc.equals (subcircuit_pair (), other.subcircuit_pair ())) { + return scc (subcircuit_pair (), other.subcircuit_pair ()); + } + } + + } else { + + if ((device_pair ().first != 0) != (other.device_pair ().first != 0)) { + return (device_pair ().first != 0) < (other.device_pair ().first != 0); + } + + if (device_pair ().first != 0) { + DeviceCompare dc; + if (! dc.equals (device_pair (), other.device_pair ())) { + return dc (device_pair (), other.device_pair ()); + } + } + + } + + if (m_id1 != other.m_id1) { + return m_id1 < other.m_id1; + } + return m_id2 < other.m_id2; + } + + bool operator== (const EdgeDesc &other) const + { + if (m_id1 > std::numeric_limits::max () / 2) { + + if ((subcircuit_pair ().first != 0) != (other.subcircuit_pair ().first != 0)) { + return false; + } + + if (subcircuit_pair ().first != 0) { + SubCircuitCompare scc; + if (! scc.equals (subcircuit_pair (), other.subcircuit_pair ())) { + return false; + } + } + + } else { + + if ((device_pair ().first != 0) != (other.device_pair ().first != 0)) { + return false; + } + + if (device_pair ().first != 0) { + DeviceCompare dc; + if (! dc.equals (device_pair (), other.device_pair ())) { + return false; + } + } + + } + + return (m_id1 == other.m_id1 && m_id2 == other.m_id2); + } + + private: + char m_ref [sizeof (std::pair)]; + size_t m_id1, m_id2; + + std::pair &device_pair () + { + return *reinterpret_cast *> ((void *) &m_ref); + } + + const std::pair &device_pair () const + { + return *reinterpret_cast *> ((const void *) &m_ref); + } + + std::pair &subcircuit_pair () + { + return *reinterpret_cast *> ((void *) &m_ref); + } + + const std::pair &subcircuit_pair () const + { + return *reinterpret_cast *> ((const void *) &m_ref); + } + }; + + struct EdgeToEdgeOnlyCompare + { + bool operator() (const std::pair, std::pair > &a, const std::vector &b) const + { + return a.first < b; + } + }; + + typedef std::vector, std::pair > >::const_iterator edge_iterator; + + NetDeviceGraphNode (const db::Net *net, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const std::map *circuit_map, const CircuitPinMapper *pin_map) + : mp_net (net), m_other_net_index (std::numeric_limits::max ()) + { + std::map n2entry; + + for (db::Net::const_subcircuit_pin_iterator i = net->begin_subcircuit_pins (); i != net->end_subcircuit_pins (); ++i) { + + const db::SubCircuit *sc = i->subcircuit (); + size_t pin_id = i->pin ()->id (); + const db::Circuit *cr = sc->circuit_ref (); + + const CircuitMapper *cm = 0; + + if (circuit_map) { + std::map::const_iterator icm = circuit_map->find (cr); + if (icm == circuit_map->end ()) { + // this can happen if the other circuit is not present - this is allowed for single-pin + // circuits. + continue; + } + cm = & icm->second; + } + + // NOTE: if cm is given, cr and pin_id are given in terms of the "other" circuit + + if (cm) { + cr = cm->other (); + pin_id = cm->other_pin_from_this_pin (pin_id); + } + + if (pin_map) { + pin_id = pin_map->normalize_pin_id (cr, pin_id); + } + + // we cannot afford creating edges from all to all other pins, so we just create edges to the previous and next + // pin. This may take more iterations to solve, but should be equivalent. + + std::vector pids; + size_t pin_count = cr->pin_count (); + + // take the previous, next and second-next pin as targets for edges + // (using the second-next pin avoid isolation of OUT vs. IN in case + // of a pin configuration of VSS-IN-VDD-OUT like for an inverter). + + if (pin_count >= 2) { + pids.push_back ((pin_id + pin_count - 1) % pin_count); + if (pin_count >= 3) { + pids.push_back ((pin_id + 1) % pin_count); + if (pin_count >= 4) { + pids.push_back ((pin_id + 2) % pin_count); + } + } + } + + for (std::vector::const_iterator i = pids.begin (); i != pids.end (); ++i) { + + size_t pin2_id = *i; + + // NOTE: if a pin mapping is given, EdgeDesc::pin1_id and EdgeDesc::pin2_id are given + // as pin ID's of the other circuit. + EdgeDesc ed (sc, circuit_categorizer.cat_for_subcircuit (sc), pin_id, pin_map ? pin_map->normalize_pin_id (cr, pin2_id) : pin2_id); + + size_t this_pin2_id = cm ? cm->this_pin_from_other_pin (pin2_id) : pin2_id; + const db::Net *net2 = sc->net_for_pin (this_pin2_id); + + std::map::const_iterator in = n2entry.find (net2); + if (in == n2entry.end ()) { + in = n2entry.insert (std::make_pair (net2, m_edges.size ())).first; + m_edges.push_back (std::make_pair (std::vector (), std::make_pair (size_t (0), net2))); + } + + m_edges [in->second].first.push_back (ed); + + } + + } + + for (db::Net::const_terminal_iterator i = net->begin_terminals (); i != net->end_terminals (); ++i) { + + const db::Device *d = i->device (); + size_t device_cat = device_categorizer.cat_for_device (d); + size_t terminal1_id = translate_terminal_id (i->terminal_id (), d); + + const std::vector &td = d->device_class ()->terminal_definitions (); + for (std::vector::const_iterator it = td.begin (); it != td.end (); ++it) { + + if (it->id () != i->terminal_id ()) { + + size_t terminal2_id = translate_terminal_id (it->id (), d); + EdgeDesc ed2 (d, device_cat, terminal1_id, terminal2_id); + + const db::Net *net2 = d->net_for_terminal (it->id ()); + + std::map::const_iterator in = n2entry.find (net2); + if (in == n2entry.end ()) { + in = n2entry.insert (std::make_pair (net2, m_edges.size ())).first; + m_edges.push_back (std::make_pair (std::vector (), std::make_pair (size_t (0), net2))); + } + + m_edges [in->second].first.push_back (ed2); + + } + + } + + } + } + + const db::Net *net () const + { + return mp_net; + } + + bool has_other () const + { + return m_other_net_index != std::numeric_limits::max (); + } + + size_t other_net_index () const + { + return m_other_net_index; + } + + void set_other_net (size_t index) + { + m_other_net_index = index; + } + + void apply_net_index (const std::map &ni) + { + for (std::vector, std::pair > >::iterator i = m_edges.begin (); i != m_edges.end (); ++i) { + std::map::const_iterator j = ni.find (i->second.second); + tl_assert (j != ni.end ()); + i->second.first = j->second; + } + + // "deep sorting" of the edge descriptor + for (std::vector, std::pair > >::iterator i = m_edges.begin (); i != m_edges.end (); ++i) { + std::sort (i->first.begin (), i->first.end ()); + } + + std::sort (m_edges.begin (), m_edges.end ()); + } + + bool operator< (const NetDeviceGraphNode &node) const + { + if (m_edges.size () != node.m_edges.size ()) { + return m_edges.size () < node.m_edges.size (); + } + for (size_t i = 0; i < m_edges.size (); ++i) { + if (m_edges [i].first != node.m_edges [i].first) { + return m_edges [i].first < node.m_edges [i].first; + } + } + return false; + } + + bool operator== (const NetDeviceGraphNode &node) const + { + if (m_edges.size () != node.m_edges.size ()) { + return false; + } + for (size_t i = 0; i < m_edges.size (); ++i) { + if (m_edges [i].first != node.m_edges [i].first) { + return false; + } + } + return true; + } + + void swap (NetDeviceGraphNode &other) + { + std::swap (m_other_net_index, other.m_other_net_index); + std::swap (mp_net, other.mp_net); + m_edges.swap (other.m_edges); + } + + edge_iterator begin () const + { + return m_edges.begin (); + } + + edge_iterator end () const + { + return m_edges.end (); + } + + edge_iterator find_edge (const std::vector &edge) const + { + edge_iterator res = std::lower_bound (begin (), end (), edge, NetDeviceGraphNode::EdgeToEdgeOnlyCompare ()); + if (res == end () || res->first != edge) { + return end (); + } else { + return res; + } + } + +private: + const db::Net *mp_net; + size_t m_other_net_index; + std::vector, std::pair > > m_edges; +}; + +// -------------------------------------------------------------------------------------------------------------------- +// NetDeviceGraph definition and implementation + +} + +namespace std +{ + void swap (db::NetDeviceGraphNode &a, db::NetDeviceGraphNode &b) + { + a.swap (b); + } +} + +namespace db +{ + +class NetDeviceGraph +{ +public: + typedef std::vector::const_iterator node_iterator; + + NetDeviceGraph () + { + // .. nothing yet .. + } + + void build (const db::Circuit *c, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const std::map *circuit_and_pin_mapping, const CircuitPinMapper *circuit_pin_mapper) + { + tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Building net graph for circuit: ")) + c->name ()); + + m_nodes.clear (); + m_net_index.clear (); + + size_t nets = 0; + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + ++nets; + } + m_nodes.reserve (nets); + + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + NetDeviceGraphNode node (n.operator-> (), device_categorizer, circuit_categorizer, circuit_and_pin_mapping, circuit_pin_mapper); + m_nodes.push_back (node); + } + + std::sort (m_nodes.begin (), m_nodes.end ()); + + for (std::vector::const_iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { + m_net_index.insert (std::make_pair (i->net (), i - m_nodes.begin ())); + } + for (std::vector::iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { + i->apply_net_index (m_net_index); + } + } + + size_t node_index_for_net (const db::Net *net) const + { + std::map::const_iterator j = m_net_index.find (net); + tl_assert (j != m_net_index.end ()); + return j->second; + } + + const db::Net *net_by_node_index (size_t net_index) const + { + return m_nodes [net_index].net (); + } + + void identify (size_t net_index, size_t other_net_index) + { + m_nodes [net_index].set_other_net (other_net_index); + } + + node_iterator begin () const + { + return m_nodes.begin (); + } + + node_iterator end () const + { + return m_nodes.end (); + } + + size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, NetlistCompareLogger *logger) + { + size_t added = 0; + + std::vector todo, more; + more.push_back (net_index); + + while (! more.empty ()) { + + todo.swap (more); + more.clear (); + + for (std::vector::const_iterator index = todo.begin (); index != todo.end (); ++index) { + + NetDeviceGraphNode *n = & m_nodes[*index]; + NetDeviceGraphNode *nother = & other.m_nodes[n->other_net_index ()]; + + // non-ambiguous paths to non-assigned nodes create a node identity on the + // end of this path + + for (NetDeviceGraphNode::edge_iterator e = n->begin (); e != n->end (); ) { + + NetDeviceGraphNode::edge_iterator ee = e; + ++ee; + + while (ee != n->end () && ee->first == e->first) { + ++ee; + } + + size_t count = 0; + NetDeviceGraphNode::edge_iterator ec; + for (NetDeviceGraphNode::edge_iterator i = e; i != ee; ++i) { + if (! m_nodes[i->second.first].has_other ()) { + ec = i; + ++count; + } + } + + if (count == 1) { // if non-ambiguous, non-assigned + +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::log << "considering " << n->net ()->expanded_name () << " to " << ec->second.second->expanded_name (); +#endif + + NetDeviceGraphNode::edge_iterator e_other = nother->find_edge (ec->first); + if (e_other != nother->end ()) { + +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::log << "candidate accepted"; +#endif + NetDeviceGraphNode::edge_iterator ee_other = e_other; + ++ee_other; + + while (ee_other != nother->end () && ee_other->first == e_other->first) { + ++ee_other; + } + + size_t count_other = 0; + NetDeviceGraphNode::edge_iterator ec_other; + for (NetDeviceGraphNode::edge_iterator i = e_other; i != ee_other; ++i) { + if (! other.m_nodes[i->second.first].has_other ()) { + ec_other = i; + ++count_other; + } + } + +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::log << "identity count = " << count_other; +#endif + if (count_other == 1) { + confirm_identity (*this, begin () + ec->second.first, other, other.begin () + ec_other->second.first, logger); + ++added; + more.push_back (ec->second.first); + } + + } + + } + + e = ee; + + } + + } + + } + + return added; + } + + static void confirm_identity (db::NetDeviceGraph &g1, db::NetDeviceGraph::node_iterator s1, db::NetDeviceGraph &g2, db::NetDeviceGraph::node_iterator s2, db::NetlistCompareLogger *logger, bool ambiguous = false) + { + if (logger) { + if (ambiguous) { + logger->match_ambiguous_nets (s1->net (), s2->net ()); + } else { + logger->match_nets (s1->net (), s2->net ()); + } + } + g1.identify (s1 - g1.begin (), s2 - g2.begin ()); + g2.identify (s2 - g2.begin (), s1 - g1.begin ()); + } + +private: + std::vector m_nodes; + std::map m_net_index; +}; + +// -------------------------------------------------------------------------------------------------------------------- +// NetlistComparer implementation + +NetlistComparer::NetlistComparer (NetlistCompareLogger *logger) + : mp_logger (logger) +{ + mp_device_categorizer.reset (new DeviceCategorizer ()); + mp_circuit_categorizer.reset (new CircuitCategorizer ()); + mp_circuit_pin_mapper.reset (new CircuitPinMapper ()); +} + +void +NetlistComparer::same_nets (const db::Net *na, const db::Net *nb) +{ + m_same_nets [std::make_pair (na->circuit (), nb->circuit ())].push_back (std::make_pair (na, nb)); +} + +void +NetlistComparer::equivalent_pins (const db::Circuit *cb, size_t pin1_id, size_t pin2_id) +{ + mp_circuit_pin_mapper->map_pins (cb, pin1_id, pin2_id); +} + +void +NetlistComparer::equivalent_pins (const db::Circuit *cb, const std::vector &pin_ids) +{ + mp_circuit_pin_mapper->map_pins (cb, pin_ids); +} + +void +NetlistComparer::same_device_classes (const db::DeviceClass *ca, const db::DeviceClass *cb) +{ + mp_device_categorizer->same_class (ca, cb); +} + +void +NetlistComparer::same_circuits (const db::Circuit *ca, const db::Circuit *cb) +{ + mp_circuit_categorizer->same_circuit (ca, cb); +} + +bool +NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const +{ + // we need to create a copy because this method is supposed to be const. + db::CircuitCategorizer circuit_categorizer = *mp_circuit_categorizer; + db::DeviceCategorizer device_categorizer = *mp_device_categorizer; + + bool good = true; + + std::map > cat2circuits; + + for (db::Netlist::const_circuit_iterator i = a->begin_circuits (); i != a->end_circuits (); ++i) { + size_t cat = circuit_categorizer.cat_for_circuit (i.operator-> ()); + cat2circuits[cat].first = i.operator-> (); + } + + for (db::Netlist::const_circuit_iterator i = b->begin_circuits (); i != b->end_circuits (); ++i) { + size_t cat = circuit_categorizer.cat_for_circuit (i.operator-> ()); + cat2circuits[cat].second = i.operator-> (); + } + + if (mp_logger) { + mp_logger->begin_netlist (a, b); + } + + for (std::map >::const_iterator i = cat2circuits.begin (); i != cat2circuits.end (); ++i) { + if (! i->second.first || ! i->second.second) { + good = false; + if (mp_logger) { + mp_logger->circuit_mismatch (i->second.first, i->second.second); + } + } + } + + std::set verified_circuits_a, verified_circuits_b; + std::map pin_mapping; + + for (db::Netlist::const_bottom_up_circuit_iterator c = a->begin_bottom_up (); c != a->end_bottom_up (); ++c) { + + size_t ccat = circuit_categorizer.cat_for_circuit (*c); + + std::map >::const_iterator i = cat2circuits.find (ccat); + tl_assert (i != cat2circuits.end ()); + + if (i->second.first && i->second.second) { + + const db::Circuit *ca = i->second.first; + const db::Circuit *cb = i->second.second; + + std::vector > empty; + const std::vector > *net_identity = ∅ + std::map, std::vector > >::const_iterator sn = m_same_nets.find (std::make_pair (ca, cb)); + if (sn != m_same_nets.end ()) { + net_identity = &sn->second; + } + + if (all_subcircuits_verified (ca, verified_circuits_a) && all_subcircuits_verified (cb, verified_circuits_b)) { + + if (mp_logger) { + mp_logger->begin_circuit (ca, cb); + } + + bool pin_mismatch = false; + bool g = compare_circuits (ca, cb, device_categorizer, circuit_categorizer, *net_identity, pin_mismatch, pin_mapping); + if (! g) { + good = false; + } + + if (! pin_mismatch) { + verified_circuits_a.insert (ca); + verified_circuits_b.insert (cb); + } + + if (mp_logger) { + mp_logger->end_circuit (ca, cb, g); + } + + } else { + + if (mp_logger) { + mp_logger->circuit_skipped (ca, cb); + } + + } + + } + + } + + if (mp_logger) { + mp_logger->begin_netlist (a, b); + } + + return good; +} + +bool +NetlistComparer::all_subcircuits_verified (const db::Circuit *c, const std::set &verified_circuits) const +{ + for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) { + + const db::Circuit *cr = sc->circuit_ref (); + + // typical via subcircuits attach through one pin. We can safely ignore such subcircuits because they don't + // contribute graph edges. + if (cr->pin_count () > 1 && verified_circuits.find (cr) == verified_circuits.end ()) { + return false; + } + + } + + return true; +} + +static std::vector > +compute_device_key (const db::Device &device, const db::NetDeviceGraph &g) +{ + std::vector > k; + + const std::vector &td = device.device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + size_t terminal_id = translate_terminal_id (t->id (), &device); + const db::Net *net = device.net_for_terminal (t->id ()); + size_t net_id = g.node_index_for_net (net); + k.push_back (std::make_pair (terminal_id, net_id)); + } + + std::sort (k.begin (), k.end ()); + + return k; +} + +static std::vector > +compute_subcircuit_key (const db::SubCircuit &subcircuit, const db::NetDeviceGraph &g, const std::map *circuit_map, const CircuitPinMapper *pin_map) +{ + std::vector > k; + + const db::Circuit *cr = subcircuit.circuit_ref (); + + const CircuitMapper *cm = 0; + + if (circuit_map) { + std::map::const_iterator icm = circuit_map->find (cr); + if (icm == circuit_map->end ()) { + // this can happen if the other circuit does not exist - in this case the key is an invalid one which cannot + // be produced by a regular subcircuit. + return k; + } + cm = & icm->second; + cr = cm->other (); + } + + // NOTE: if cm is given, cr is given in terms of the "other" circuit + + for (db::Circuit::const_pin_iterator p = cr->begin_pins (); p != cr->end_pins (); ++p) { + + size_t this_pin_id = cm ? cm->this_pin_from_other_pin (p->id ()) : p->id (); + size_t pin_id = pin_map ? pin_map->normalize_pin_id (cr, p->id ()) : p->id (); + + const db::Net *net = subcircuit.net_for_pin (this_pin_id); + size_t net_id = g.node_index_for_net (net); + k.push_back (std::make_pair (pin_id, net_id)); + + } + + std::sort (k.begin (), k.end ()); + + return k; +} + +bool +NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, const std::vector > &net_identity, bool &pin_mismatch, std::map &circuit_and_pin_mapping) const +{ + db::NetDeviceGraph g1, g2; + + // NOTE: for normalization we map all subcircuits of c1 to c2. + // Also, pin swapping will only happen there. + g1.build (c1, device_categorizer, circuit_categorizer, &circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); + g2.build (c2, device_categorizer, circuit_categorizer, 0, mp_circuit_pin_mapper.get ()); + + for (std::vector >::const_iterator p = net_identity.begin (); p != net_identity.end (); ++p) { + size_t ni1 = g1.node_index_for_net (p->first); + size_t ni2 = g2.node_index_for_net (p->second); + g1.identify (ni1, ni2); + g2.identify (ni2, ni1); + } + + bool good = true; + while (good) { + +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::log << "new iteration ..."; +#endif + + size_t new_identities = 0; + for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { + if (i1->has_other ()) { +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::log << "deriving new identities from " << i1->net ()->expanded_name (); +#endif + size_t ni = g1.derive_node_identities (i1 - g1.begin (), g2, mp_logger); + new_identities += ni; + if (ni > 0) { +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::log << ni << " new identities."; +#endif + } + } + } + + bool any_without = false; + for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end () && ! any_without; ++i1) { + any_without = ! i1->has_other (); + } + + if (! any_without) { + break; + } + + if (new_identities == 0) { + +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::log << "checking topological identity ..."; +#endif + // derive new identities through topology + + db::NetDeviceGraph::node_iterator i1 = g1.begin (), i2 = g2.begin (); + for ( ; i1 != g1.end () && i2 != g2.end (); ) { + + if (i1->has_other ()) { + ++i1; + } else if (i2->has_other ()) { + ++i2; + } else if (*i1 < *i2) { + ++i1; + } else if (*i2 < *i1) { + ++i2; + } else { + + db::NetDeviceGraph::node_iterator ii1 = i1, ii2 = i2; + + ++i1; + ++i2; + + bool ambiguous = (i1 != g1.end () && *i1 == *ii1) || (i2 != g2.end () && *i2 == *ii2); + + // found a candidate - a single node with the same edges + db::NetDeviceGraph::confirm_identity (g1, ii1, g2, ii2, mp_logger, ambiguous); + ++new_identities; + + } + + } + + } + + if (new_identities == 0) { + good = false; + } + + } + + + // Report missing net assignment + + for (db::NetDeviceGraph::node_iterator i = g1.begin (); i != g1.end (); ++i) { + if (! i->has_other ()) { + mp_logger->net_mismatch (i->net (), 0); + } + } + + for (db::NetDeviceGraph::node_iterator i = g2.begin (); i != g2.end (); ++i) { + if (! i->has_other ()) { + mp_logger->net_mismatch (0, i->net ()); + } + } + + + // Report pin assignment + + std::multimap net2pin; + for (db::Circuit::const_pin_iterator p = c2->begin_pins (); p != c2->end_pins (); ++p) { + const db::Net *net = c2->net_for_pin (p->id ()); + tl_assert (net != 0); + net2pin.insert (std::make_pair (net, p.operator-> ())); + } + + CircuitMapper &pin_mapping = circuit_and_pin_mapping [c1]; + pin_mapping.set_other (c2); + + for (db::NetDeviceGraph::node_iterator i = g1.begin (); i != g1.end (); ++i) { + + const db::Net *net = i->net (); + tl_assert (net != 0); + + if (net->pin_count () == 0) { + continue; + } + + if (! i->has_other ()) { + for (db::Net::const_pin_iterator pi = net->begin_pins (); pi != net->end_pins (); ++pi) { + mp_logger->pin_mismatch (pi->pin (), 0); + pin_mismatch = true; + good = false; + } + continue; + } + + const db::Net *other_net = g2.net_by_node_index (i->other_net_index ()); + + std::multimap::iterator np = net2pin.find (other_net); + for (db::Net::const_pin_iterator pi = net->begin_pins (); pi != net->end_pins (); ++pi) { + + if (np != net2pin.end () && np->first == other_net) { + + mp_logger->match_pins (pi->pin (), np->second); + pin_mapping.map_pin (pi->pin ()->id (), np->second->id ()); + + std::multimap::iterator np_delete = np; + ++np; + net2pin.erase (np_delete); + + } else { + mp_logger->pin_mismatch (pi->pin (), 0); + pin_mismatch = true; + good = false; + } + + } + + } + + for (std::multimap::iterator np = net2pin.begin (); np != net2pin.end (); ++np) { + mp_logger->pin_mismatch (0, np->second); + pin_mismatch = true; + good = false; + } + + + // Report device assignment + + std::multimap >, std::pair > device_map; + + for (db::Circuit::const_device_iterator d = c1->begin_devices (); d != c1->end_devices (); ++d) { + + std::vector > k = compute_device_key (*d, g1); + + bool mapped = true; + for (std::vector >::iterator i = k.begin (); i != k.end () && mapped; ++i) { + if (! g1.begin () [i->second].has_other ()) { + mapped = false; + } + } + + if (! mapped) { + mp_logger->device_mismatch (d.operator-> (), 0); + good = false; + } else { + // TODO: report devices which cannot be distiguished topologically? + device_map.insert (std::make_pair (k, std::make_pair (d.operator-> (), device_categorizer.cat_for_device (d.operator-> ())))); + } + + } + + for (db::Circuit::const_device_iterator d = c2->begin_devices (); d != c2->end_devices (); ++d) { + + std::vector > k = compute_device_key (*d, g2); + + bool mapped = true; + for (std::vector >::iterator i = k.begin (); i != k.end (); ++i) { + if (! g2.begin () [i->second].has_other ()) { + mapped = false; + } else { + i->second = g2.begin () [i->second].other_net_index (); + } + } + + std::sort (k.begin (), k.end ()); + + std::multimap >, std::pair >::const_iterator dm = device_map.find (k); + + if (! mapped || dm == device_map.end () || dm->first != k) { + + mp_logger->device_mismatch (0, d.operator-> ()); + good = false; + + } else { + + db::DeviceCompare dc; + + size_t device_cat = device_categorizer.cat_for_device (d.operator-> ()); + + if (! dc.equals (dm->second, std::make_pair (d.operator-> (), device_cat))) { + if (dm->second.second != device_cat) { + mp_logger->match_devices_with_different_device_classes (dm->second.first, d.operator-> ()); + good = false; + } else { + mp_logger->match_devices_with_different_parameters (dm->second.first, d.operator-> ()); + good = false; + } + } else { + mp_logger->match_devices (dm->second.first, d.operator-> ()); + } + + device_map.erase (dm); + + } + + } + + for (std::multimap >, std::pair >::const_iterator dm = device_map.begin (); dm != device_map.end (); ++dm) { + mp_logger->device_mismatch (dm->second.first, 0); + good = false; + } + + + // Report subcircuit assignment + + std::multimap >, std::pair > subcircuit_map; + + for (db::Circuit::const_subcircuit_iterator sc = c1->begin_subcircuits (); sc != c1->end_subcircuits (); ++sc) { + + std::vector > k = compute_subcircuit_key (*sc, g1, &circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); + + bool mapped = true; + for (std::vector >::iterator i = k.begin (); i != k.end () && mapped; ++i) { + if (! g1.begin () [i->second].has_other ()) { + mapped = false; + } + } + + if (! mapped) { + mp_logger->subcircuit_mismatch (sc.operator-> (), 0); + good = false; + } else if (! k.empty ()) { + // TODO: report devices which cannot be distiguished topologically? + subcircuit_map.insert (std::make_pair (k, std::make_pair (sc.operator-> (), circuit_categorizer.cat_for_subcircuit (sc.operator-> ())))); + } + + } + + for (db::Circuit::const_subcircuit_iterator sc = c2->begin_subcircuits (); sc != c2->end_subcircuits (); ++sc) { + + std::vector > k = compute_subcircuit_key (*sc, g2, 0, mp_circuit_pin_mapper.get ()); + + bool mapped = true; + for (std::vector >::iterator i = k.begin (); i != k.end (); ++i) { + if (! g1.begin () [i->second].has_other ()) { + mapped = false; + } else { + i->second = g2.begin () [i->second].other_net_index (); + } + } + + std::sort (k.begin (), k.end ()); + + std::multimap >, std::pair >::const_iterator scm = subcircuit_map.find (k); + + if (! mapped || scm == subcircuit_map.end ()) { + + mp_logger->subcircuit_mismatch (0, sc.operator-> ()); + good = false; + + } else { + + db::SubCircuitCompare scc; + size_t sc_cat = circuit_categorizer.cat_for_subcircuit (sc.operator-> ()); + + if (! scc.equals (scm->second, std::make_pair (sc.operator-> (), sc_cat))) { + mp_logger->subcircuit_mismatch (scm->second.first, sc.operator-> ()); + good = false; + } else { + mp_logger->match_subcircuits (scm->second.first, sc.operator-> ()); + } + + subcircuit_map.erase (scm); + + } + + } + + for (std::multimap >, std::pair >::const_iterator scm = subcircuit_map.begin (); scm != subcircuit_map.end (); ++scm) { + mp_logger->subcircuit_mismatch (scm->second.first, 0); + good = false; + } + + return good; +} + +} diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h new file mode 100644 index 000000000..a752302d2 --- /dev/null +++ b/src/db/db/dbNetlistCompare.h @@ -0,0 +1,216 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 + +*/ + +#ifndef _HDR_dbNetlistCompare +#define _HDR_dbNetlistCompare + +#include "dbCommon.h" +#include "dbNetlist.h" + +#include +#include + +namespace db +{ + +class CircuitPinMapper; +class DeviceCategorizer; +class CircuitCategorizer; +class CircuitMapper; + +/** + * @brief A receiver for netlist compare events + */ +class DB_PUBLIC NetlistCompareLogger +{ +public: + NetlistCompareLogger () { } + virtual ~NetlistCompareLogger () { } + + /** + * @brief Begin logging for netlist a and b + */ + virtual void begin_netlist (const db::Netlist * /*a*/, const db::Netlist * /*b*/) { } + + /** + * @brief End logging for netlist a and b + */ + virtual void end_netlist (const db::Netlist * /*a*/, const db::Netlist * /*b*/) { } + + /** + * @brief Begin logging for circuit a and b + */ + virtual void begin_circuit (const db::Circuit * /*a*/, const db::Circuit * /*b*/) { } + + /** + * @brief End logging for circuit a and b + */ + virtual void end_circuit (const db::Circuit * /*a*/, const db::Circuit * /*b*/, bool /*matching*/) { } + + /** + * @brief Circuits are skipped + * Circuits are skipped if their subcircuits could not be matched. + */ + virtual void circuit_skipped (const db::Circuit * /*a*/, const db::Circuit * /*b*/) { } + + /** + * @brief There is a circuit mismatch + * "a" is null if there is no match for b and vice versa. + */ + virtual void circuit_mismatch (const db::Circuit * /*a*/, const db::Circuit * /*b*/) { } + + /** + * @brief Nets a and b match exactly + */ + virtual void match_nets (const db::Net * /*a*/, const db::Net * /*b*/) { } + + /** + * @brief Nets a and b are matched, but are ambiguous + * Other nets might also match with a and also with b. Matching this a and b is + * an arbitrary decision. + */ + virtual void match_ambiguous_nets (const db::Net * /*a*/, const db::Net * /*b*/) { } + + /** + * @brief Net a or b doesn't match + * "a" is null if there is no match for b and vice versa. + */ + virtual void net_mismatch (const db::Net * /*a*/, const db::Net * /*b*/) { } + + /** + * @brief Devices a and b match exactly + */ + virtual void match_devices (const db::Device * /*a*/, const db::Device * /*b*/) { } + + /** + * @brief Devices a and b are matched but have different parameters + */ + virtual void match_devices_with_different_parameters (const db::Device * /*a*/, const db::Device * /*b*/) { } + + /** + * @brief Devices a and b are matched but have different device classes + */ + virtual void match_devices_with_different_device_classes (const db::Device * /*a*/, const db::Device * /*b*/) { } + + /** + * @brief Device a or b doesn't match + * "a" is null if there is no match for b and vice versa. + */ + virtual void device_mismatch (const db::Device * /*a*/, const db::Device * /*b*/) { } + + /** + * @brief Pins a and b of the current circuit are matched + */ + virtual void match_pins (const db::Pin * /*a*/, const db::Pin * /*b*/) { } + + /** + * @brief Pin a or b doesn't match + * "a" is null if there is no match for b and vice versa. + */ + virtual void pin_mismatch (const db::Pin * /*a*/, const db::Pin * /*b*/) { } + + /** + * @brief Subcircuits a and b match exactly + */ + virtual void match_subcircuits (const db::SubCircuit * /*a*/, const db::SubCircuit * /*b*/) { } + + /** + * @brief SubCircuit a or b doesn't match + * "a" is null if there is no match for b and vice versa. + */ + virtual void subcircuit_mismatch (const db::SubCircuit * /*a*/, const db::SubCircuit * /*b*/) { } +}; + +/** + * @brief The netlist comparer + */ +class DB_PUBLIC NetlistComparer +{ +public: + /** + * @brief Constructor + */ + NetlistComparer (NetlistCompareLogger *logger); + + /** + * @brief Mark two nets as identical + * + * This makes a net na in netlist a identical to the corresponding + * net nb in netlist b. + * By default nets are not identical expect through their topology. + */ + void same_nets (const db::Net *na, const db::Net *nb); + + /** + * @brief Mark two pins as equivalent (i.e. can be swapped) + * + * Only circuits from the *second* input can be given swappable pins. + * This will imply the same swappable pins on the equivalent circuit of the first input. + * To mark multiple pins as swappable, use the version that takes a list of pins. + */ + void equivalent_pins (const db::Circuit *cb, size_t pin1_id, size_t pin2_id); + + /** + * @brief Mark multiple pins as equivalent (i.e. can be swapped) + * + * Only circuits from the *second* input can be given swappable pins. + * This will imply the same swappable pins on the equivalent circuit of the first input. + */ + void equivalent_pins (const db::Circuit *cb, const std::vector &pin_ids); + + /** + * @brief Mark two device classes as identical + * + * This makes a device class ca in netlist a identical to the corresponding + * device class cb in netlist b. + * By default device classes with the same name are identical. + */ + void same_device_classes (const db::DeviceClass *ca, const db::DeviceClass *cb); + + /** + * @brief Mark two circuits as identical + * + * This makes a circuit ca in netlist a identical to the corresponding + * circuit cb in netlist b. + * By default circuits with the same name are identical. + */ + void same_circuits (const db::Circuit *ca, const db::Circuit *cb); + + /** + * @brief Actually compares the two netlists + */ + bool compare (const db::Netlist *a, const db::Netlist *b) const; + +protected: + bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, const std::vector > &net_identity, bool &pin_mismatch, std::map &circuit_and_pin_mapping) const; + bool all_subcircuits_verified (const db::Circuit *c, const std::set &verified_circuits) const; + + NetlistCompareLogger *mp_logger; + std::map, std::vector > > m_same_nets; + std::auto_ptr mp_circuit_pin_mapper; + std::auto_ptr mp_device_categorizer; + std::auto_ptr mp_circuit_categorizer; +}; + +} + +#endif diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 7286c743d..e4521bf0c 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -1,1434 +1,28 @@ +/* -// @@@ + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "dbNetlist.h" -#include "dbNetlistDeviceClasses.h" -#include "dbHash.h" #include "tlUnitTest.h" -#include "tlProgress.h" -#include "tlTimer.h" - -namespace db -{ - -class NetlistCompareLogger -{ -public: - NetlistCompareLogger () { } - virtual ~NetlistCompareLogger () { } - - /** - * @brief Begin logging for netlist a and b - */ - virtual void begin_netlist (const db::Netlist * /*a*/, const db::Netlist * /*b*/) { } - - /** - * @brief End logging for netlist a and b - */ - virtual void end_netlist (const db::Netlist * /*a*/, const db::Netlist * /*b*/) { } - - /** - * @brief Begin logging for circuit a and b - */ - virtual void begin_circuit (const db::Circuit * /*a*/, const db::Circuit * /*b*/) { } - - /** - * @brief End logging for circuit a and b - */ - virtual void end_circuit (const db::Circuit * /*a*/, const db::Circuit * /*b*/, bool /*matching*/) { } - - /** - * @brief Circuits are skipped - * Circuits are skipped if their subcircuits could not be matched. - */ - virtual void circuit_skipped (const db::Circuit * /*a*/, const db::Circuit * /*b*/) { } - - /** - * @brief There is a circuit mismatch - * "a" is null if there is no match for b and vice versa. - */ - virtual void circuit_mismatch (const db::Circuit * /*a*/, const db::Circuit * /*b*/) { } - - /** - * @brief Nets a and b match exactly - */ - virtual void match_nets (const db::Net * /*a*/, const db::Net * /*b*/) { } - - /** - * @brief Nets a and b are matched, but are ambiguous - * Other nets might also match with a and also with b. Matching this a and b is - * an arbitrary decision. - */ - virtual void match_ambiguous_nets (const db::Net * /*a*/, const db::Net * /*b*/) { } - - /** - * @brief Net a or b doesn't match - * "a" is null if there is no match for b and vice versa. - */ - virtual void net_mismatch (const db::Net * /*a*/, const db::Net * /*b*/) { } - - /** - * @brief Devices a and b match exactly - */ - virtual void match_devices (const db::Device * /*a*/, const db::Device * /*b*/) { } - - /** - * @brief Devices a and b are matched but have different parameters - */ - virtual void match_devices_with_different_parameters (const db::Device * /*a*/, const db::Device * /*b*/) { } - - /** - * @brief Devices a and b are matched but have different device classes - */ - virtual void match_devices_with_different_device_classes (const db::Device * /*a*/, const db::Device * /*b*/) { } - - /** - * @brief Device a or b doesn't match - * "a" is null if there is no match for b and vice versa. - */ - virtual void device_mismatch (const db::Device * /*a*/, const db::Device * /*b*/) { } - - /** - * @brief Pins a and b of the current circuit are matched - */ - virtual void match_pins (const db::Pin * /*a*/, const db::Pin * /*b*/) { } - - /** - * @brief Pin a or b doesn't match - * "a" is null if there is no match for b and vice versa. - */ - virtual void pin_mismatch (const db::Pin * /*a*/, const db::Pin * /*b*/) { } - - /** - * @brief Subcircuits a and b match exactly - */ - virtual void match_subcircuits (const db::SubCircuit * /*a*/, const db::SubCircuit * /*b*/) { } - - /** - * @brief SubCircuit a or b doesn't match - * "a" is null if there is no match for b and vice versa. - */ - virtual void subcircuit_mismatch (const db::SubCircuit * /*a*/, const db::SubCircuit * /*b*/) { } -}; - -struct DeviceCompare -{ - bool operator() (const std::pair &d1, const std::pair &d2) const - { - if (d1.second != d2.second) { - return d1.second < d2.second; - } - - const std::vector &dp = d1.first->device_class ()->parameter_definitions (); - for (std::vector::const_iterator i = dp.begin (); i != dp.end (); ++i) { - double v1 = d1.first->parameter_value (i->id ()); - double v2 = d2.first->parameter_value (i->id ()); - if (fabs (v1 - v2) > db::epsilon) { - return v1 < v2; - } - } - return false; - } - - bool equals (const std::pair &d1, const std::pair &d2) const - { - if (d1.second != d2.second) { - return false; - } - - const std::vector &dp = d1.first->device_class ()->parameter_definitions (); - for (std::vector::const_iterator i = dp.begin (); i != dp.end (); ++i) { - double v1 = d1.first->parameter_value (i->id ()); - double v2 = d2.first->parameter_value (i->id ()); - if (fabs (v1 - v2) > db::epsilon) { - return false; - } - } - return true; - } -}; - -struct SubCircuitCompare -{ - bool operator() (const std::pair &sc1, const std::pair &sc2) const - { - return sc1.second < sc2.second; - } - - bool equals (const std::pair &sc1, const std::pair &sc2) const - { - return sc1.second == sc2.second; - } -}; - -class CircuitPinMapper -{ -public: - CircuitPinMapper () - { - // .. nothing yet .. - } - - void map_pins (const db::Circuit *circuit, size_t pin1_id, size_t pin2_id) - { - m_pin_map [circuit].insert (std::make_pair (pin1_id, pin2_id)); - } - - void map_pins (const db::Circuit *circuit, const std::vector &pin_ids) - { - if (pin_ids.size () < 2) { - return; - } - - std::map &pm = m_pin_map [circuit]; - for (size_t i = 1; i < pin_ids.size (); ++i) { - pm.insert (std::make_pair (pin_ids [i], pin_ids [0])); - } - } - - size_t normalize_pin_id (const db::Circuit *circuit, size_t pin_id) const - { - std::map >::const_iterator pm = m_pin_map.find (circuit); - if (pm != m_pin_map.end ()) { - std::map::const_iterator ipm = pm->second.find (pin_id); - if (ipm != pm->second.end ()) { - return ipm->second; - } - } - return pin_id; - } - -private: - std::map > m_pin_map; -}; - -class CircuitMapper -{ -public: - CircuitMapper () - : mp_other (0) - { - // .. nothing yet .. - } - - void set_other (const db::Circuit *other) - { - mp_other = other; - } - - const db::Circuit *other () const - { - return mp_other; - } - - void map_pin (size_t this_pin, size_t other_pin) - { - m_pin_map.insert (std::make_pair (this_pin, other_pin)); - m_rev_pin_map.insert (std::make_pair (other_pin, this_pin)); - } - - size_t other_pin_from_this_pin (size_t this_pin) const - { - std::map::const_iterator i = m_pin_map.find (this_pin); - tl_assert (i != m_pin_map.end ()); - return i->second; - } - - size_t this_pin_from_other_pin (size_t other_pin) const - { - std::map::const_iterator i = m_rev_pin_map.find (other_pin); - tl_assert (i != m_rev_pin_map.end ()); - return i->second; - } - -private: - const db::Circuit *mp_other; - std::map m_pin_map, m_rev_pin_map; -}; - -struct DeviceCategorizer -{ -public: - DeviceCategorizer () - : m_next_cat (0) - { - // .. nothing yet .. - } - - void same_class (const db::DeviceClass *ca, const db::DeviceClass *cb) - { - ++m_next_cat; - m_cat_by_ptr.insert (std::make_pair (ca, m_next_cat)); - m_cat_by_ptr.insert (std::make_pair (cb, m_next_cat)); - } - - size_t cat_for_device (const db::Device *device) - { - const db::DeviceClass *cls = device->device_class (); - if (! cls) { - return 0; - } - - std::map::const_iterator cp = m_cat_by_ptr.find (cls); - if (cp != m_cat_by_ptr.end ()) { - return cp->second; - } - - std::map::const_iterator c = m_cat_by_name.find (cls->name ()); - if (c != m_cat_by_name.end ()) { - m_cat_by_ptr.insert (std::make_pair (cls, c->second)); - return c->second; - } else { - ++m_next_cat; - m_cat_by_name.insert (std::make_pair (cls->name (), m_next_cat)); - m_cat_by_ptr.insert (std::make_pair (cls, m_next_cat)); - return m_next_cat; - } - } - -public: - std::map m_cat_by_ptr; - std::map m_cat_by_name; - size_t m_next_cat; -}; - -struct CircuitCategorizer -{ -public: - CircuitCategorizer () - : m_next_cat (0) - { - // .. nothing yet .. - } - - void same_circuit (const db::Circuit *ca, const db::Circuit *cb) - { - ++m_next_cat; - m_cat_by_ptr.insert (std::make_pair (ca, m_next_cat)); - m_cat_by_ptr.insert (std::make_pair (cb, m_next_cat)); - } - - size_t cat_for_subcircuit (const db::SubCircuit *subcircuit) - { - const db::Circuit *cr = subcircuit->circuit_ref (); - if (! cr) { - return 0; - } else { - return cat_for_circuit (cr); - } - } - - size_t cat_for_circuit (const db::Circuit *cr) - { - std::map::const_iterator cp = m_cat_by_ptr.find (cr); - if (cp != m_cat_by_ptr.end ()) { - return cp->second; - } - - std::map::const_iterator c = m_cat_by_name.find (cr->name ()); - if (c != m_cat_by_name.end ()) { - m_cat_by_ptr.insert (std::make_pair (cr, c->second)); - return c->second; - } else { - ++m_next_cat; - m_cat_by_name.insert (std::make_pair (cr->name (), m_next_cat)); - m_cat_by_ptr.insert (std::make_pair (cr, m_next_cat)); - return m_next_cat; - } - } - -public: - std::map m_cat_by_ptr; - std::map m_cat_by_name; - size_t m_next_cat; -}; - -static size_t translate_terminal_id (size_t tid, const db::Device *device) -{ - return device->device_class () ? device->device_class ()->normalize_terminal_id (tid) : tid; -} - -class NetDeviceGraphNode -{ -public: - struct EdgeDesc { - - EdgeDesc (const db::Device *device, size_t device_category, size_t terminal1_id, size_t terminal2_id) - { - device_pair ().first = device; - device_pair ().second = device_category; - m_id1 = terminal1_id; - m_id2 = terminal2_id; - } - - EdgeDesc (const db::SubCircuit *subcircuit, size_t subcircuit_category, size_t pin1_id, size_t pin2_id) - { - subcircuit_pair ().first = subcircuit; - subcircuit_pair ().second = subcircuit_category; - m_id1 = std::numeric_limits::max () - pin1_id; - m_id2 = pin2_id; - } - - bool operator< (const EdgeDesc &other) const - { - if (m_id1 > std::numeric_limits::max () / 2) { - - if ((subcircuit_pair ().first != 0) != (other.subcircuit_pair ().first != 0)) { - return (subcircuit_pair ().first != 0) < (other.subcircuit_pair ().first != 0); - } - - if (subcircuit_pair ().first != 0) { - SubCircuitCompare scc; - if (! scc.equals (subcircuit_pair (), other.subcircuit_pair ())) { - return scc (subcircuit_pair (), other.subcircuit_pair ()); - } - } - - } else { - - if ((device_pair ().first != 0) != (other.device_pair ().first != 0)) { - return (device_pair ().first != 0) < (other.device_pair ().first != 0); - } - - if (device_pair ().first != 0) { - DeviceCompare dc; - if (! dc.equals (device_pair (), other.device_pair ())) { - return dc (device_pair (), other.device_pair ()); - } - } - - } - - if (m_id1 != other.m_id1) { - return m_id1 < other.m_id1; - } - return m_id2 < other.m_id2; - } - - bool operator== (const EdgeDesc &other) const - { - if (m_id1 > std::numeric_limits::max () / 2) { - - if ((subcircuit_pair ().first != 0) != (other.subcircuit_pair ().first != 0)) { - return false; - } - - if (subcircuit_pair ().first != 0) { - SubCircuitCompare scc; - if (! scc.equals (subcircuit_pair (), other.subcircuit_pair ())) { - return false; - } - } - - } else { - - if ((device_pair ().first != 0) != (other.device_pair ().first != 0)) { - return false; - } - - if (device_pair ().first != 0) { - DeviceCompare dc; - if (! dc.equals (device_pair (), other.device_pair ())) { - return false; - } - } - - } - - return (m_id1 == other.m_id1 && m_id2 == other.m_id2); - } - - private: - char m_ref [sizeof (std::pair)]; - size_t m_id1, m_id2; - - std::pair &device_pair () - { - return *reinterpret_cast *> ((void *) &m_ref); - } - - const std::pair &device_pair () const - { - return *reinterpret_cast *> ((const void *) &m_ref); - } - - std::pair &subcircuit_pair () - { - return *reinterpret_cast *> ((void *) &m_ref); - } - - const std::pair &subcircuit_pair () const - { - return *reinterpret_cast *> ((const void *) &m_ref); - } - }; - - struct EdgeToEdgeOnlyCompare - { - bool operator() (const std::pair, std::pair > &a, const std::vector &b) const - { - return a.first < b; - } - }; - - typedef std::vector, std::pair > >::const_iterator edge_iterator; - - NetDeviceGraphNode (const db::Net *net, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const std::map *circuit_map, const CircuitPinMapper *pin_map) - : mp_net (net), m_other_net_index (std::numeric_limits::max ()) - { - std::map n2entry; - - for (db::Net::const_subcircuit_pin_iterator i = net->begin_subcircuit_pins (); i != net->end_subcircuit_pins (); ++i) { - - const db::SubCircuit *sc = i->subcircuit (); - size_t pin_id = i->pin ()->id (); - const db::Circuit *cr = sc->circuit_ref (); - - const CircuitMapper *cm = 0; - - if (circuit_map) { - std::map::const_iterator icm = circuit_map->find (cr); - if (icm == circuit_map->end ()) { - // this can happen if the other circuit is not present - this is allowed for single-pin - // circuits. - continue; - } - cm = & icm->second; - } - - // NOTE: if cm is given, cr and pin_id are given in terms of the "other" circuit - - if (cm) { - cr = cm->other (); - pin_id = cm->other_pin_from_this_pin (pin_id); - } - - if (pin_map) { - pin_id = pin_map->normalize_pin_id (cr, pin_id); - } - - // we cannot afford creating edges from all to all other pins, so we just create edges to the previous and next - // pin. This may take more iterations to solve, but should be equivalent. - - std::vector pids; - size_t pin_count = cr->pin_count (); - - // take the previous, next and second-next pin as targets for edges - // (using the second-next pin avoid isolation of OUT vs. IN in case - // of a pin configuration of VSS-IN-VDD-OUT like for an inverter). - - if (pin_count >= 2) { - pids.push_back ((pin_id + pin_count - 1) % pin_count); - if (pin_count >= 3) { - pids.push_back ((pin_id + 1) % pin_count); - if (pin_count >= 4) { - pids.push_back ((pin_id + 2) % pin_count); - } - } - } - - for (std::vector::const_iterator i = pids.begin (); i != pids.end (); ++i) { - - size_t pin2_id = *i; - - // NOTE: if a pin mapping is given, EdgeDesc::pin1_id and EdgeDesc::pin2_id are given - // as pin ID's of the other circuit. - EdgeDesc ed (sc, circuit_categorizer.cat_for_subcircuit (sc), pin_id, pin_map ? pin_map->normalize_pin_id (cr, pin2_id) : pin2_id); - - size_t this_pin2_id = cm ? cm->this_pin_from_other_pin (pin2_id) : pin2_id; - const db::Net *net2 = sc->net_for_pin (this_pin2_id); - - std::map::const_iterator in = n2entry.find (net2); - if (in == n2entry.end ()) { - in = n2entry.insert (std::make_pair (net2, m_edges.size ())).first; - m_edges.push_back (std::make_pair (std::vector (), std::make_pair (size_t (0), net2))); - } - - m_edges [in->second].first.push_back (ed); - - } - - } - - for (db::Net::const_terminal_iterator i = net->begin_terminals (); i != net->end_terminals (); ++i) { - - const db::Device *d = i->device (); - size_t device_cat = device_categorizer.cat_for_device (d); - size_t terminal1_id = translate_terminal_id (i->terminal_id (), d); - - const std::vector &td = d->device_class ()->terminal_definitions (); - for (std::vector::const_iterator it = td.begin (); it != td.end (); ++it) { - - if (it->id () != i->terminal_id ()) { - - size_t terminal2_id = translate_terminal_id (it->id (), d); - EdgeDesc ed2 (d, device_cat, terminal1_id, terminal2_id); - - const db::Net *net2 = d->net_for_terminal (it->id ()); - - std::map::const_iterator in = n2entry.find (net2); - if (in == n2entry.end ()) { - in = n2entry.insert (std::make_pair (net2, m_edges.size ())).first; - m_edges.push_back (std::make_pair (std::vector (), std::make_pair (size_t (0), net2))); - } - - m_edges [in->second].first.push_back (ed2); - - } - - } - - } - } - - const db::Net *net () const - { - return mp_net; - } - - bool has_other () const - { - return m_other_net_index != std::numeric_limits::max (); - } - - size_t other_net_index () const - { - return m_other_net_index; - } - - void set_other_net (size_t index) - { - m_other_net_index = index; - } - - void apply_net_index (const std::map &ni) - { - for (std::vector, std::pair > >::iterator i = m_edges.begin (); i != m_edges.end (); ++i) { - std::map::const_iterator j = ni.find (i->second.second); - tl_assert (j != ni.end ()); - i->second.first = j->second; - } - - // "deep sorting" of the edge descriptor - for (std::vector, std::pair > >::iterator i = m_edges.begin (); i != m_edges.end (); ++i) { - std::sort (i->first.begin (), i->first.end ()); - } - - std::sort (m_edges.begin (), m_edges.end ()); - } - - bool operator< (const NetDeviceGraphNode &node) const - { - if (m_edges.size () != node.m_edges.size ()) { - return m_edges.size () < node.m_edges.size (); - } - for (size_t i = 0; i < m_edges.size (); ++i) { - if (m_edges [i].first != node.m_edges [i].first) { - return m_edges [i].first < node.m_edges [i].first; - } - } - return false; - } - - bool operator== (const NetDeviceGraphNode &node) const - { - if (m_edges.size () != node.m_edges.size ()) { - return false; - } - for (size_t i = 0; i < m_edges.size (); ++i) { - if (m_edges [i].first != node.m_edges [i].first) { - return false; - } - } - return true; - } - - void swap (NetDeviceGraphNode &other) - { - std::swap (m_other_net_index, other.m_other_net_index); - std::swap (mp_net, other.mp_net); - m_edges.swap (other.m_edges); - } - - edge_iterator begin () const - { - return m_edges.begin (); - } - - edge_iterator end () const - { - return m_edges.end (); - } - - edge_iterator find_edge (const std::vector &edge) const - { - edge_iterator res = std::lower_bound (begin (), end (), edge, NetDeviceGraphNode::EdgeToEdgeOnlyCompare ()); - if (res == end () || res->first != edge) { - return end (); - } else { - return res; - } - } - -private: - const db::Net *mp_net; - size_t m_other_net_index; - std::vector, std::pair > > m_edges; -}; - -} - -namespace std -{ - void swap (db::NetDeviceGraphNode &a, db::NetDeviceGraphNode &b) - { - a.swap (b); - } -} - -namespace db -{ - -class NetDeviceGraph -{ -public: - typedef std::vector::const_iterator node_iterator; - - NetDeviceGraph () - { - // .. nothing yet .. - } - - void build (const db::Circuit *c, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const std::map *circuit_and_pin_mapping, const CircuitPinMapper *circuit_pin_mapper) - { - tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Building net graph for circuit: ")) + c->name ()); - - m_nodes.clear (); - m_net_index.clear (); - - size_t nets = 0; - for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - ++nets; - } - m_nodes.reserve (nets); - - for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - NetDeviceGraphNode node (n.operator-> (), device_categorizer, circuit_categorizer, circuit_and_pin_mapping, circuit_pin_mapper); - m_nodes.push_back (node); - } - - std::sort (m_nodes.begin (), m_nodes.end ()); - - for (std::vector::const_iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { - m_net_index.insert (std::make_pair (i->net (), i - m_nodes.begin ())); - } - for (std::vector::iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { - i->apply_net_index (m_net_index); - } - } - - size_t node_index_for_net (const db::Net *net) const - { - std::map::const_iterator j = m_net_index.find (net); - tl_assert (j != m_net_index.end ()); - return j->second; - } - - const db::Net *net_by_node_index (size_t net_index) const - { - return m_nodes [net_index].net (); - } - - void identify (size_t net_index, size_t other_net_index) - { - m_nodes [net_index].set_other_net (other_net_index); - } - - node_iterator begin () const - { - return m_nodes.begin (); - } - - node_iterator end () const - { - return m_nodes.end (); - } - - size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, NetlistCompareLogger *logger) - { - size_t added = 0; - - std::vector todo, more; - more.push_back (net_index); - - while (! more.empty ()) { - - todo.swap (more); - more.clear (); - - for (std::vector::const_iterator index = todo.begin (); index != todo.end (); ++index) { - - NetDeviceGraphNode *n = & m_nodes[*index]; - NetDeviceGraphNode *nother = & other.m_nodes[n->other_net_index ()]; - - // non-ambiguous paths to non-assigned nodes create a node identity on the - // end of this path - - for (NetDeviceGraphNode::edge_iterator e = n->begin (); e != n->end (); ) { - - NetDeviceGraphNode::edge_iterator ee = e; - ++ee; - - while (ee != n->end () && ee->first == e->first) { - ++ee; - } - - size_t count = 0; - NetDeviceGraphNode::edge_iterator ec; - for (NetDeviceGraphNode::edge_iterator i = e; i != ee; ++i) { - if (! m_nodes[i->second.first].has_other ()) { - ec = i; - ++count; - } - } - - if (count == 1) { // if non-ambiguous, non-assigned - -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::log << "considering " << n->net ()->expanded_name () << " to " << ec->second.second->expanded_name (); -#endif - - NetDeviceGraphNode::edge_iterator e_other = nother->find_edge (ec->first); - if (e_other != nother->end ()) { - -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::log << "candidate accepted"; -#endif - NetDeviceGraphNode::edge_iterator ee_other = e_other; - ++ee_other; - - while (ee_other != nother->end () && ee_other->first == e_other->first) { - ++ee_other; - } - - size_t count_other = 0; - NetDeviceGraphNode::edge_iterator ec_other; - for (NetDeviceGraphNode::edge_iterator i = e_other; i != ee_other; ++i) { - if (! other.m_nodes[i->second.first].has_other ()) { - ec_other = i; - ++count_other; - } - } - -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::log << "identity count = " << count_other; -#endif - if (count_other == 1) { - confirm_identity (*this, begin () + ec->second.first, other, other.begin () + ec_other->second.first, logger); - ++added; - more.push_back (ec->second.first); - } - - } - - } - - e = ee; - - } - - } - - } - - return added; - } - - static void confirm_identity (db::NetDeviceGraph &g1, db::NetDeviceGraph::node_iterator s1, db::NetDeviceGraph &g2, db::NetDeviceGraph::node_iterator s2, db::NetlistCompareLogger *logger, bool ambiguous = false) - { - if (logger) { - if (ambiguous) { - logger->match_ambiguous_nets (s1->net (), s2->net ()); - } else { - logger->match_nets (s1->net (), s2->net ()); - } - } - g1.identify (s1 - g1.begin (), s2 - g2.begin ()); - g2.identify (s2 - g2.begin (), s1 - g1.begin ()); - } - -private: - std::vector m_nodes; - std::map m_net_index; -}; - -class NetlistComparer -{ -public: - NetlistComparer (NetlistCompareLogger *logger) - : mp_logger (logger) - { } - - /** - * @brief Mark two nets as identical - * - * This makes a net na in netlist a identical to the corresponding - * net nb in netlist b. - * By default nets are not identical expect through their topology. - */ - void same_nets (const db::Net *na, const db::Net *nb) - { - m_same_nets [std::make_pair (na->circuit (), nb->circuit ())].push_back (std::make_pair (na, nb)); - } - - /** - * @brief Mark two pins as equivalent (i.e. can be swapped) - * - * Only circuits from the *second* input can be given swappable pins. - * This will imply the same swappable pins on the equivalent circuit of the first input. - * To mark multiple pins as swappable, use the version that takes a list of pins. - */ - void equivalent_pins (const db::Circuit *cb, size_t pin1_id, size_t pin2_id) - { - m_circuit_pin_mapper.map_pins (cb, pin1_id, pin2_id); - } - - /** - * @brief Mark multiple pins as equivalent (i.e. can be swapped) - * - * Only circuits from the *second* input can be given swappable pins. - * This will imply the same swappable pins on the equivalent circuit of the first input. - */ - void equivalent_pins (const db::Circuit *cb, const std::vector &pin_ids) - { - m_circuit_pin_mapper.map_pins (cb, pin_ids); - } - - /** - * @brief Mark two device classes as identical - * - * This makes a device class ca in netlist a identical to the corresponding - * device class cb in netlist b. - * By default device classes with the same name are identical. - */ - void same_device_classes (const db::DeviceClass *ca, const db::DeviceClass *cb) - { - m_device_categorizer.same_class (ca, cb); - } - - /** - * @brief Mark two circuits as identical - * - * This makes a circuit ca in netlist a identical to the corresponding - * circuit cb in netlist b. - * By default circuits with the same name are identical. - */ - void same_circuits (const db::Circuit *ca, const db::Circuit *cb) - { - m_circuit_categorizer.same_circuit (ca, cb); - } - - bool compare (const db::Netlist *a, const db::Netlist *b) const - { - // we need to create a copy because this method is supposed to be const. - db::CircuitCategorizer circuit_categorizer = m_circuit_categorizer; - db::DeviceCategorizer device_categorizer = m_device_categorizer; - - bool good = true; - - std::map > cat2circuits; - - for (db::Netlist::const_circuit_iterator i = a->begin_circuits (); i != a->end_circuits (); ++i) { - size_t cat = circuit_categorizer.cat_for_circuit (i.operator-> ()); - cat2circuits[cat].first = i.operator-> (); - } - - for (db::Netlist::const_circuit_iterator i = b->begin_circuits (); i != b->end_circuits (); ++i) { - size_t cat = circuit_categorizer.cat_for_circuit (i.operator-> ()); - cat2circuits[cat].second = i.operator-> (); - } - - if (mp_logger) { - mp_logger->begin_netlist (a, b); - } - - for (std::map >::const_iterator i = cat2circuits.begin (); i != cat2circuits.end (); ++i) { - if (! i->second.first || ! i->second.second) { - good = false; - if (mp_logger) { - mp_logger->circuit_mismatch (i->second.first, i->second.second); - } - } - } - - std::set verified_circuits_a, verified_circuits_b; - std::map pin_mapping; - - for (db::Netlist::const_bottom_up_circuit_iterator c = a->begin_bottom_up (); c != a->end_bottom_up (); ++c) { - - size_t ccat = circuit_categorizer.cat_for_circuit (*c); - - std::map >::const_iterator i = cat2circuits.find (ccat); - tl_assert (i != cat2circuits.end ()); - - if (i->second.first && i->second.second) { - - const db::Circuit *ca = i->second.first; - const db::Circuit *cb = i->second.second; - - std::vector > empty; - const std::vector > *net_identity = ∅ - std::map, std::vector > >::const_iterator sn = m_same_nets.find (std::make_pair (ca, cb)); - if (sn != m_same_nets.end ()) { - net_identity = &sn->second; - } - - if (all_subcircuits_verified (ca, verified_circuits_a) && all_subcircuits_verified (cb, verified_circuits_b)) { - - if (mp_logger) { - mp_logger->begin_circuit (ca, cb); - } - - bool pin_mismatch = false; - bool g = compare_circuits (ca, cb, device_categorizer, circuit_categorizer, *net_identity, pin_mismatch, pin_mapping); - if (! g) { - good = false; - } - - if (! pin_mismatch) { - verified_circuits_a.insert (ca); - verified_circuits_b.insert (cb); - } - - if (mp_logger) { - mp_logger->end_circuit (ca, cb, g); - } - - } else { - - if (mp_logger) { - mp_logger->circuit_skipped (ca, cb); - } - - } - - } - - } - - if (mp_logger) { - mp_logger->begin_netlist (a, b); - } - - return good; - } - -protected: - bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, const std::vector > &net_identity, bool &pin_mismatch, std::map &circuit_and_pin_mapping) const; - bool all_subcircuits_verified (const db::Circuit *c, const std::set &verified_circuits) const; - - NetlistCompareLogger *mp_logger; - std::map, std::vector > > m_same_nets; - CircuitPinMapper m_circuit_pin_mapper; - DeviceCategorizer m_device_categorizer; - CircuitCategorizer m_circuit_categorizer; -}; - -bool -NetlistComparer::all_subcircuits_verified (const db::Circuit *c, const std::set &verified_circuits) const -{ - for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) { - - const db::Circuit *cr = sc->circuit_ref (); - - // typical via subcircuits attach through one pin. We can safely ignore such subcircuits because they don't - // contribute graph edges. - if (cr->pin_count () > 1 && verified_circuits.find (cr) == verified_circuits.end ()) { - return false; - } - - } - - return true; -} - -static std::vector > -compute_device_key (const db::Device &device, const db::NetDeviceGraph &g) -{ - std::vector > k; - - const std::vector &td = device.device_class ()->terminal_definitions (); - for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { - size_t terminal_id = translate_terminal_id (t->id (), &device); - const db::Net *net = device.net_for_terminal (t->id ()); - size_t net_id = g.node_index_for_net (net); - k.push_back (std::make_pair (terminal_id, net_id)); - } - - std::sort (k.begin (), k.end ()); - - return k; -} - -static std::vector > -compute_subcircuit_key (const db::SubCircuit &subcircuit, const db::NetDeviceGraph &g, const std::map *circuit_map, const CircuitPinMapper *pin_map) -{ - std::vector > k; - - const db::Circuit *cr = subcircuit.circuit_ref (); - - const CircuitMapper *cm = 0; - - if (circuit_map) { - std::map::const_iterator icm = circuit_map->find (cr); - if (icm == circuit_map->end ()) { - // this can happen if the other circuit does not exist - in this case the key is an invalid one which cannot - // be produced by a regular subcircuit. - return k; - } - cm = & icm->second; - cr = cm->other (); - } - - // NOTE: if cm is given, cr is given in terms of the "other" circuit - - for (db::Circuit::const_pin_iterator p = cr->begin_pins (); p != cr->end_pins (); ++p) { - - size_t this_pin_id = cm ? cm->this_pin_from_other_pin (p->id ()) : p->id (); - size_t pin_id = pin_map ? pin_map->normalize_pin_id (cr, p->id ()) : p->id (); - - const db::Net *net = subcircuit.net_for_pin (this_pin_id); - size_t net_id = g.node_index_for_net (net); - k.push_back (std::make_pair (pin_id, net_id)); - - } - - std::sort (k.begin (), k.end ()); - - return k; -} - -bool -NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, const std::vector > &net_identity, bool &pin_mismatch, std::map &circuit_and_pin_mapping) const -{ - db::NetDeviceGraph g1, g2; - - // NOTE: for normalization we map all subcircuits of c1 to c2. - // Also, pin swapping will only happen there. - g1.build (c1, device_categorizer, circuit_categorizer, &circuit_and_pin_mapping, &m_circuit_pin_mapper); - g2.build (c2, device_categorizer, circuit_categorizer, 0, &m_circuit_pin_mapper); - - for (std::vector >::const_iterator p = net_identity.begin (); p != net_identity.end (); ++p) { - size_t ni1 = g1.node_index_for_net (p->first); - size_t ni2 = g2.node_index_for_net (p->second); - g1.identify (ni1, ni2); - g2.identify (ni2, ni1); - } - - bool good = true; - while (good) { - -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::log << "new iteration ..."; -#endif - - size_t new_identities = 0; - for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { - if (i1->has_other ()) { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::log << "deriving new identities from " << i1->net ()->expanded_name (); -#endif - size_t ni = g1.derive_node_identities (i1 - g1.begin (), g2, mp_logger); - new_identities += ni; - if (ni > 0) { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::log << ni << " new identities."; -#endif - } - } - } - - bool any_without = false; - for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end () && ! any_without; ++i1) { - any_without = ! i1->has_other (); - } - - if (! any_without) { - break; - } - - if (new_identities == 0) { - -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::log << "checking topological identity ..."; -#endif - // derive new identities through topology - - db::NetDeviceGraph::node_iterator i1 = g1.begin (), i2 = g2.begin (); - for ( ; i1 != g1.end () && i2 != g2.end (); ) { - - if (i1->has_other ()) { - ++i1; - } else if (i2->has_other ()) { - ++i2; - } else if (*i1 < *i2) { - ++i1; - } else if (*i2 < *i1) { - ++i2; - } else { - - db::NetDeviceGraph::node_iterator ii1 = i1, ii2 = i2; - - ++i1; - ++i2; - - bool ambiguous = (i1 != g1.end () && *i1 == *ii1) || (i2 != g2.end () && *i2 == *ii2); - - // found a candidate - a single node with the same edges - db::NetDeviceGraph::confirm_identity (g1, ii1, g2, ii2, mp_logger, ambiguous); - ++new_identities; - - } - - } - - } - - if (new_identities == 0) { - good = false; - } - - } - - - // Report missing net assignment - - for (db::NetDeviceGraph::node_iterator i = g1.begin (); i != g1.end (); ++i) { - if (! i->has_other ()) { - mp_logger->net_mismatch (i->net (), 0); - } - } - - for (db::NetDeviceGraph::node_iterator i = g2.begin (); i != g2.end (); ++i) { - if (! i->has_other ()) { - mp_logger->net_mismatch (0, i->net ()); - } - } - - - // Report pin assignment - - std::multimap net2pin; - for (db::Circuit::const_pin_iterator p = c2->begin_pins (); p != c2->end_pins (); ++p) { - const db::Net *net = c2->net_for_pin (p->id ()); - tl_assert (net != 0); - net2pin.insert (std::make_pair (net, p.operator-> ())); - } - - CircuitMapper &pin_mapping = circuit_and_pin_mapping [c1]; - pin_mapping.set_other (c2); - - for (db::NetDeviceGraph::node_iterator i = g1.begin (); i != g1.end (); ++i) { - - const db::Net *net = i->net (); - tl_assert (net != 0); - - if (net->pin_count () == 0) { - continue; - } - - if (! i->has_other ()) { - for (db::Net::const_pin_iterator pi = net->begin_pins (); pi != net->end_pins (); ++pi) { - mp_logger->pin_mismatch (pi->pin (), 0); - pin_mismatch = true; - good = false; - } - continue; - } - - const db::Net *other_net = g2.net_by_node_index (i->other_net_index ()); - - std::multimap::iterator np = net2pin.find (other_net); - for (db::Net::const_pin_iterator pi = net->begin_pins (); pi != net->end_pins (); ++pi) { - - if (np != net2pin.end () && np->first == other_net) { - - mp_logger->match_pins (pi->pin (), np->second); - pin_mapping.map_pin (pi->pin ()->id (), np->second->id ()); - - std::multimap::iterator np_delete = np; - ++np; - net2pin.erase (np_delete); - - } else { - mp_logger->pin_mismatch (pi->pin (), 0); - pin_mismatch = true; - good = false; - } - - } - - } - - for (std::multimap::iterator np = net2pin.begin (); np != net2pin.end (); ++np) { - mp_logger->pin_mismatch (0, np->second); - pin_mismatch = true; - good = false; - } - - - // Report device assignment - - std::multimap >, std::pair > device_map; - - for (db::Circuit::const_device_iterator d = c1->begin_devices (); d != c1->end_devices (); ++d) { - - std::vector > k = compute_device_key (*d, g1); - - bool mapped = true; - for (std::vector >::iterator i = k.begin (); i != k.end () && mapped; ++i) { - if (! g1.begin () [i->second].has_other ()) { - mapped = false; - } - } - - if (! mapped) { - mp_logger->device_mismatch (d.operator-> (), 0); - good = false; - } else { - // TODO: report devices which cannot be distiguished topologically? - device_map.insert (std::make_pair (k, std::make_pair (d.operator-> (), device_categorizer.cat_for_device (d.operator-> ())))); - } - - } - - for (db::Circuit::const_device_iterator d = c2->begin_devices (); d != c2->end_devices (); ++d) { - - std::vector > k = compute_device_key (*d, g2); - - bool mapped = true; - for (std::vector >::iterator i = k.begin (); i != k.end (); ++i) { - if (! g2.begin () [i->second].has_other ()) { - mapped = false; - } else { - i->second = g2.begin () [i->second].other_net_index (); - } - } - - std::sort (k.begin (), k.end ()); - - std::multimap >, std::pair >::const_iterator dm = device_map.find (k); - - if (! mapped || dm == device_map.end () || dm->first != k) { - - mp_logger->device_mismatch (0, d.operator-> ()); - good = false; - - } else { - - db::DeviceCompare dc; - - size_t device_cat = device_categorizer.cat_for_device (d.operator-> ()); - - if (! dc.equals (dm->second, std::make_pair (d.operator-> (), device_cat))) { - if (dm->second.second != device_cat) { - mp_logger->match_devices_with_different_device_classes (dm->second.first, d.operator-> ()); - good = false; - } else { - mp_logger->match_devices_with_different_parameters (dm->second.first, d.operator-> ()); - good = false; - } - } else { - mp_logger->match_devices (dm->second.first, d.operator-> ()); - } - - device_map.erase (dm); - - } - - } - - for (std::multimap >, std::pair >::const_iterator dm = device_map.begin (); dm != device_map.end (); ++dm) { - mp_logger->device_mismatch (dm->second.first, 0); - good = false; - } - - - // Report subcircuit assignment - - std::multimap >, std::pair > subcircuit_map; - - for (db::Circuit::const_subcircuit_iterator sc = c1->begin_subcircuits (); sc != c1->end_subcircuits (); ++sc) { - - std::vector > k = compute_subcircuit_key (*sc, g1, &circuit_and_pin_mapping, &m_circuit_pin_mapper); - - bool mapped = true; - for (std::vector >::iterator i = k.begin (); i != k.end () && mapped; ++i) { - if (! g1.begin () [i->second].has_other ()) { - mapped = false; - } - } - - if (! mapped) { - mp_logger->subcircuit_mismatch (sc.operator-> (), 0); - good = false; - } else if (! k.empty ()) { - // TODO: report devices which cannot be distiguished topologically? - subcircuit_map.insert (std::make_pair (k, std::make_pair (sc.operator-> (), circuit_categorizer.cat_for_subcircuit (sc.operator-> ())))); - } - - } - - for (db::Circuit::const_subcircuit_iterator sc = c2->begin_subcircuits (); sc != c2->end_subcircuits (); ++sc) { - - std::vector > k = compute_subcircuit_key (*sc, g2, 0, &m_circuit_pin_mapper); - - bool mapped = true; - for (std::vector >::iterator i = k.begin (); i != k.end (); ++i) { - if (! g1.begin () [i->second].has_other ()) { - mapped = false; - } else { - i->second = g2.begin () [i->second].other_net_index (); - } - } - - std::sort (k.begin (), k.end ()); - - std::multimap >, std::pair >::const_iterator scm = subcircuit_map.find (k); - - if (! mapped || scm == subcircuit_map.end ()) { - - mp_logger->subcircuit_mismatch (0, sc.operator-> ()); - good = false; - - } else { - - db::SubCircuitCompare scc; - size_t sc_cat = circuit_categorizer.cat_for_subcircuit (sc.operator-> ()); - - if (! scc.equals (scm->second, std::make_pair (sc.operator-> (), sc_cat))) { - mp_logger->subcircuit_mismatch (scm->second.first, sc.operator-> ()); - good = false; - } else { - mp_logger->match_subcircuits (scm->second.first, sc.operator-> ()); - } - - subcircuit_map.erase (scm); - - } - - } - - for (std::multimap >, std::pair >::const_iterator scm = subcircuit_map.begin (); scm != subcircuit_map.end (); ++scm) { - mp_logger->subcircuit_mismatch (scm->second.first, 0); - good = false; - } - - return good; -} - -} +#include "dbNetlistDeviceClasses.h" +#include "dbNetlistCompare.h" class NetlistCompareTestLogger : public db::NetlistCompareLogger @@ -2718,4 +1312,3 @@ TEST(14_Subcircuit2MatchWithSwap) EXPECT_EQ (good, true); } - From 2452c72d2de487043964ae49ce5825e244b29c54 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 30 Mar 2019 23:04:57 +0100 Subject: [PATCH 20/54] WIP: netlist compare deployed for netlist extractors Some enhancements were required: * Clusters left over from joined clusters must not be turned into nets: this leads to dummy nets. * null Nets can happen as targets of edges. Don't assert in this case but treat null nets as identical for both netlists. * Don't resolve ambiguous nets if there are options to do this non-ambiguously. * logger can be null * Added compare_netlists to dbTestSupport --- src/db/db/dbHierNetworkProcessor.cc | 7 + src/db/db/dbHierNetworkProcessor.h | 5 + src/db/db/dbNetlistCompare.cc | 133 +++++++++++----- src/db/db/dbNetlistExtractor.cc | 9 +- src/db/db/dbTestSupport.cc | 150 +++++++++++++++++++ src/db/db/dbTestSupport.h | 6 + src/db/unit_tests/dbNetlistCompareTests.cc | 75 +++++++++- src/db/unit_tests/dbNetlistExtractorTests.cc | 17 ++- 8 files changed, 355 insertions(+), 47 deletions(-) diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 45c2623e0..a1016b750 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -267,6 +267,13 @@ local_cluster::clear () m_global_nets.clear (); } +template +bool +local_cluster::empty () const +{ + return m_global_nets.empty () && m_shapes.empty (); +} + template void local_cluster::set_global_nets (const global_nets &gn) diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 6fef35daa..94fb0ec28 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -218,6 +218,11 @@ public: */ void clear (); + /** + * @brief Returns true if the cluster is empty + */ + bool empty () const; + /** * @brief Adds a shape with the given layer to the cluster */ diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 200062bf5..3905c97f5 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -415,6 +415,10 @@ public: NetDeviceGraphNode (const db::Net *net, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const std::map *circuit_map, const CircuitPinMapper *pin_map) : mp_net (net), m_other_net_index (std::numeric_limits::max ()) { + if (! net) { + return; + } + std::map n2entry; for (db::Net::const_subcircuit_pin_iterator i = net->begin_subcircuit_pins (); i != net->end_subcircuit_pins (); ++i) { @@ -648,6 +652,9 @@ public: m_nodes.clear (); m_net_index.clear (); + // create a dummy node for a null net + m_nodes.push_back (NetDeviceGraphNode (0, device_categorizer, circuit_categorizer, circuit_and_pin_mapping, circuit_pin_mapper)); + size_t nets = 0; for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { ++nets; @@ -1025,6 +1032,10 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, g1.build (c1, device_categorizer, circuit_categorizer, &circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); g2.build (c2, device_categorizer, circuit_categorizer, 0, mp_circuit_pin_mapper.get ()); + // Match dummy nodes for null nets + g1.identify (0, 0); + g2.identify (0, 0); + for (std::vector >::const_iterator p = net_identity.begin (); p != net_identity.end (); ++p) { size_t ni1 = g1.node_index_for_net (p->first); size_t ni2 = g2.node_index_for_net (p->second); @@ -1069,31 +1080,45 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, #if defined(PRINT_DEBUG_NETCOMPARE) tl::log << "checking topological identity ..."; #endif - // derive new identities through topology - db::NetDeviceGraph::node_iterator i1 = g1.begin (), i2 = g2.begin (); - for ( ; i1 != g1.end () && i2 != g2.end (); ) { + // first pass: without ambiguities, second pass: match ambiguous nets + for (int pass = 0; pass < 2 && new_identities == 0; ++pass) { - if (i1->has_other ()) { - ++i1; - } else if (i2->has_other ()) { - ++i2; - } else if (*i1 < *i2) { - ++i1; - } else if (*i2 < *i1) { - ++i2; - } else { + // derive new identities through topology - db::NetDeviceGraph::node_iterator ii1 = i1, ii2 = i2; + bool same_as_prev = false; - ++i1; - ++i2; + db::NetDeviceGraph::node_iterator i1 = g1.begin (), i2 = g2.begin (); + for ( ; i1 != g1.end () && i2 != g2.end (); ) { - bool ambiguous = (i1 != g1.end () && *i1 == *ii1) || (i2 != g2.end () && *i2 == *ii2); + bool same_as_next = false; - // found a candidate - a single node with the same edges - db::NetDeviceGraph::confirm_identity (g1, ii1, g2, ii2, mp_logger, ambiguous); - ++new_identities; + if (i1->has_other ()) { + ++i1; + } else if (i2->has_other ()) { + ++i2; + } else if (*i1 < *i2) { + ++i1; + } else if (*i2 < *i1) { + ++i2; + } else { + + db::NetDeviceGraph::node_iterator ii1 = i1, ii2 = i2; + + ++i1; + ++i2; + + same_as_next = (i1 != g1.end () && *i1 == *ii1) || (i2 != g2.end () && *i2 == *ii2); + + // found a candidate - a single node with the same edges + if (! (same_as_next || same_as_prev) || pass) { + db::NetDeviceGraph::confirm_identity (g1, ii1, g2, ii2, mp_logger, same_as_next || same_as_prev); + ++new_identities; + } + + } + + same_as_prev = same_as_next; } @@ -1111,13 +1136,13 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, // Report missing net assignment for (db::NetDeviceGraph::node_iterator i = g1.begin (); i != g1.end (); ++i) { - if (! i->has_other ()) { + if (! i->has_other () && mp_logger) { mp_logger->net_mismatch (i->net (), 0); } } for (db::NetDeviceGraph::node_iterator i = g2.begin (); i != g2.end (); ++i) { - if (! i->has_other ()) { + if (! i->has_other () && mp_logger) { mp_logger->net_mismatch (0, i->net ()); } } @@ -1138,15 +1163,15 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (db::NetDeviceGraph::node_iterator i = g1.begin (); i != g1.end (); ++i) { const db::Net *net = i->net (); - tl_assert (net != 0); - - if (net->pin_count () == 0) { + if (! net || net->pin_count () == 0) { continue; } if (! i->has_other ()) { for (db::Net::const_pin_iterator pi = net->begin_pins (); pi != net->end_pins (); ++pi) { - mp_logger->pin_mismatch (pi->pin (), 0); + if (mp_logger) { + mp_logger->pin_mismatch (pi->pin (), 0); + } pin_mismatch = true; good = false; } @@ -1160,7 +1185,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, if (np != net2pin.end () && np->first == other_net) { - mp_logger->match_pins (pi->pin (), np->second); + if (mp_logger) { + mp_logger->match_pins (pi->pin (), np->second); + } pin_mapping.map_pin (pi->pin ()->id (), np->second->id ()); std::multimap::iterator np_delete = np; @@ -1168,9 +1195,13 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, net2pin.erase (np_delete); } else { - mp_logger->pin_mismatch (pi->pin (), 0); + + if (mp_logger) { + mp_logger->pin_mismatch (pi->pin (), 0); + } pin_mismatch = true; good = false; + } } @@ -1178,7 +1209,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } for (std::multimap::iterator np = net2pin.begin (); np != net2pin.end (); ++np) { - mp_logger->pin_mismatch (0, np->second); + if (mp_logger) { + mp_logger->pin_mismatch (0, np->second); + } pin_mismatch = true; good = false; } @@ -1200,7 +1233,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } if (! mapped) { - mp_logger->device_mismatch (d.operator-> (), 0); + if (mp_logger) { + mp_logger->device_mismatch (d.operator-> (), 0); + } good = false; } else { // TODO: report devices which cannot be distiguished topologically? @@ -1228,7 +1263,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, if (! mapped || dm == device_map.end () || dm->first != k) { - mp_logger->device_mismatch (0, d.operator-> ()); + if (mp_logger) { + mp_logger->device_mismatch (0, d.operator-> ()); + } good = false; } else { @@ -1239,14 +1276,20 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, if (! dc.equals (dm->second, std::make_pair (d.operator-> (), device_cat))) { if (dm->second.second != device_cat) { - mp_logger->match_devices_with_different_device_classes (dm->second.first, d.operator-> ()); + if (mp_logger) { + mp_logger->match_devices_with_different_device_classes (dm->second.first, d.operator-> ()); + } good = false; } else { - mp_logger->match_devices_with_different_parameters (dm->second.first, d.operator-> ()); + if (mp_logger) { + mp_logger->match_devices_with_different_parameters (dm->second.first, d.operator-> ()); + } good = false; } } else { - mp_logger->match_devices (dm->second.first, d.operator-> ()); + if (mp_logger) { + mp_logger->match_devices (dm->second.first, d.operator-> ()); + } } device_map.erase (dm); @@ -1256,7 +1299,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } for (std::multimap >, std::pair >::const_iterator dm = device_map.begin (); dm != device_map.end (); ++dm) { - mp_logger->device_mismatch (dm->second.first, 0); + if (mp_logger) { + mp_logger->device_mismatch (dm->second.first, 0); + } good = false; } @@ -1277,7 +1322,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } if (! mapped) { - mp_logger->subcircuit_mismatch (sc.operator-> (), 0); + if (mp_logger) { + mp_logger->subcircuit_mismatch (sc.operator-> (), 0); + } good = false; } else if (! k.empty ()) { // TODO: report devices which cannot be distiguished topologically? @@ -1305,7 +1352,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, if (! mapped || scm == subcircuit_map.end ()) { - mp_logger->subcircuit_mismatch (0, sc.operator-> ()); + if (mp_logger) { + mp_logger->subcircuit_mismatch (0, sc.operator-> ()); + } good = false; } else { @@ -1314,10 +1363,14 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, size_t sc_cat = circuit_categorizer.cat_for_subcircuit (sc.operator-> ()); if (! scc.equals (scm->second, std::make_pair (sc.operator-> (), sc_cat))) { - mp_logger->subcircuit_mismatch (scm->second.first, sc.operator-> ()); + if (mp_logger) { + mp_logger->subcircuit_mismatch (scm->second.first, sc.operator-> ()); + } good = false; } else { - mp_logger->match_subcircuits (scm->second.first, sc.operator-> ()); + if (mp_logger) { + mp_logger->match_subcircuits (scm->second.first, sc.operator-> ()); + } } subcircuit_map.erase (scm); @@ -1327,7 +1380,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } for (std::multimap >, std::pair >::const_iterator scm = subcircuit_map.begin (); scm != subcircuit_map.end (); ++scm) { - mp_logger->subcircuit_mismatch (scm->second.first, 0); + if (mp_logger) { + mp_logger->subcircuit_mismatch (scm->second.first, 0); + } good = false; } diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index e8ba2bade..40cd6439f 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -127,11 +127,18 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo for (connected_clusters_type::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) { + const db::local_cluster &lc = clusters.cluster_by_id (*c); + if (clusters.connections_for_cluster (*c).empty () && lc.empty ()) { + // this is an entirely empty cluster so we skip it. + // Such clusters are left over when joining clusters. + continue; + } + db::Net *net = new db::Net (); net->set_cluster_id (*c); circuit->add_net (net); - const db::local_cluster::global_nets &gn = clusters.cluster_by_id (*c).get_global_nets (); + const db::local_cluster::global_nets &gn = lc.get_global_nets (); for (db::local_cluster::global_nets::const_iterator g = gn.begin (); g != gn.end (); ++g) { assign_net_name (conn.global_net_name (*g), net); } diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index b8b1af7d5..70c24b7fc 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -28,6 +28,8 @@ #include "dbCell.h" #include "dbCellInst.h" #include "dbLayoutDiff.h" +#include "dbNetlist.h" +#include "dbNetlistCompare.h" #include "tlUnitTest.h" #include "tlFileUtils.h" @@ -152,4 +154,152 @@ void compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std:: } } +class CompareLogger + : public db::NetlistCompareLogger +{ +public: + CompareLogger () + : m_new_circuit (true) { } + + void out (const std::string &text) + { + if (m_new_circuit) { + tl::log << m_circuit; + m_new_circuit = false; + } + tl::log << text; + } + + virtual void begin_netlist () + { + tl::log << "Comparing netlists:"; + } + + virtual void end_netlist () + { + tl::log << "End of difference log."; + } + + virtual void begin_circuit (const db::Circuit *a, const db::Circuit *b) + { + m_new_circuit = true; + m_circuit = circuit2str (a) + " vs. " + circuit2str (b); + } + + virtual void circuit_skipped (const db::Circuit *a, const db::Circuit *b) + { + out ("circuit_skipped " + circuit2str (a) + " " + circuit2str (b)); + } + + virtual void circuit_mismatch (const db::Circuit *a, const db::Circuit *b) + { + out ("circuit_mismatch " + circuit2str (a) + " " + circuit2str (b)); + } + + virtual void match_nets (const db::Net *a, const db::Net *b) + { + out ("match_nets " + net2str (a) + " " + net2str (b)); + } + + virtual void match_ambiguous_nets (const db::Net *a, const db::Net *b) + { + out ("match_ambiguous_nets " + net2str (a) + " " + net2str (b)); + } + + virtual void net_mismatch (const db::Net *a, const db::Net *b) + { + out ("net_mismatch " + net2str (a) + " " + net2str (b)); + } + + virtual void match_devices (const db::Device *a, const db::Device *b) + { + out ("match_devices " + device2str (a) + " " + device2str (b)); + } + + virtual void device_mismatch (const db::Device *a, const db::Device *b) + { + out ("device_mismatch " + device2str (a) + " " + device2str (b)); + } + + virtual void match_devices_with_different_parameters (const db::Device *a, const db::Device *b) + { + out ("match_devices_with_different_parameters " + device2str (a) + " " + device2str (b)); + } + + virtual void match_devices_with_different_device_classes (const db::Device *a, const db::Device *b) + { + out ("match_devices_with_different_device_classes " + device2str (a) + " " + device2str (b)); + } + + virtual void match_pins (const db::Pin *a, const db::Pin *b) + { + out ("match_pins " + pin2str (a) + " " + pin2str (b)); + } + + virtual void pin_mismatch (const db::Pin *a, const db::Pin *b) + { + out ("pin_mismatch " + pin2str (a) + " " + pin2str (b)); + } + + virtual void match_subcircuits (const db::SubCircuit *a, const db::SubCircuit *b) + { + out ("match_subcircuits " + subcircuit2str (a) + " " + subcircuit2str (b)); + } + + virtual void subcircuit_mismatch (const db::SubCircuit *a, const db::SubCircuit *b) + { + out ("subcircuit_mismatch " + subcircuit2str (a) + " " + subcircuit2str (b)); + } + +private: + bool m_new_circuit; + std::string m_circuit; + + std::string circuit2str (const db::Circuit *x) const + { + return x ? x->name () : "(null)"; + } + + std::string device2str (const db::Device *x) const + { + return x ? x->expanded_name () : "(null)"; + } + + std::string net2str (const db::Net *x) const + { + return x ? x->expanded_name () : "(null)"; + } + + std::string pin2str (const db::Pin *x) const + { + return x ? x->expanded_name () : "(null)"; + } + + std::string subcircuit2str (const db::SubCircuit *x) const + { + return x ? x->expanded_name () : "(null)"; + } +}; + +void DB_PUBLIC compare_netlist (tl::TestBase *_this, const db::Netlist &netlist, const std::string &au_nl_string) +{ + db::NetlistComparer comp (0); + + db::Netlist au_nl; + for (db::Netlist::const_device_class_iterator d = netlist.begin_device_classes (); d != netlist.end_device_classes (); ++d) { + au_nl.add_device_class (d->clone ()); + } + + au_nl.from_string (au_nl_string); + + if (! comp.compare (&netlist, &au_nl)) { + _this->raise ("Compare failed - see log for details.\n\nActual:\n" + netlist.to_string () + "\nGolden:\n" + au_nl_string); + // Compare once again - this time with logger + CompareLogger logger; + db::NetlistComparer comp (&logger); + comp.compare (&netlist, &au_nl); + } +} + + } diff --git a/src/db/db/dbTestSupport.h b/src/db/db/dbTestSupport.h index 00fb38432..6e4e34917 100644 --- a/src/db/db/dbTestSupport.h +++ b/src/db/db/dbTestSupport.h @@ -39,6 +39,7 @@ namespace db class Layout; class Cell; class LayerMap; +class Netlist; /** * @brief Specifies the normalization mode for compare_layouts @@ -73,6 +74,11 @@ void DB_PUBLIC compare_layouts (tl::TestBase *_this, const db::Layout &layout, c */ void DB_PUBLIC compare_layouts (tl::TestBase *_this, const db::Layout &layout, const std::string &au_file, const db::LayerMap &lmap, bool read_all_others, NormalizationMode norm = WriteGDS2, db::Coord tolerance = 0); +/** + * @brief Compares a netlist against a string + */ +void DB_PUBLIC compare_netlist (tl::TestBase *_this, const db::Netlist &netlist, const std::string &au_nl_string); + } #endif diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index e4521bf0c..0def3a631 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -412,11 +412,11 @@ TEST(4_BufferTwoPaths) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" - "match_ambiguous_nets INT $10\n" - "match_nets INT2 $11\n" "match_nets VDD VDD\n" "match_nets IN IN\n" "match_nets VSS VSS\n" + "match_ambiguous_nets INT $10\n" + "match_ambiguous_nets INT2 $11\n" "match_pins $1 $3\n" "match_pins $2 $0\n" "match_pins $0 $1\n" @@ -1312,3 +1312,74 @@ TEST(14_Subcircuit2MatchWithSwap) EXPECT_EQ (good, true); } + +TEST(15_EmptySubCircuitTest) +{ + const char *nls1 = + "circuit RINGO ();\n" + " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $4 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $5 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $6 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD);\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " subcircuit TRANS $1 (S=$2,G=$4,D=IN);\n" + " subcircuit TRANS $2 (S=$2,G=$5,D=IN);\n" + " subcircuit TRANS $3 (S=$5,G=OUT,D=$2);\n" + " subcircuit TRANS $4 (S=$4,G=OUT,D=$2);\n" + "end;\n" + "circuit TRANS (S=$1,G=$2,D=$3);\n" + "end;\n"; + + const char *nls2 = + "circuit RINGO ();\n" + " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $4 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $5 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $6 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD);\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " subcircuit TRANS $1 (G=$4,S=$2,D=IN);\n" + " subcircuit TRANS $2 (G=$5,S=$2,D=IN);\n" + " subcircuit TRANS $3 (G=OUT,S=$5,D=$2);\n" + " subcircuit TRANS $4 (G=OUT,S=$4,D=$2);\n" + "end;\n" + // This circuit is an abstract and it's pins are defined by the pin names + "circuit TRANS (G=$1,S=$2,D=$3);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "..." + ); + + EXPECT_EQ (good, true); +} diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 52c2a5961..3fd08616c 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -35,6 +35,7 @@ #include "dbCommonReader.h" #include "dbTestSupport.h" #include "dbCellMapping.h" +#include "dbTestSupport.h" #include "tlUnitTest.h" #include "tlString.h" @@ -280,7 +281,8 @@ TEST(1_DeviceAndNetExtraction) dump_nets_to_layout (nl, cl, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (nl.to_string (), + CHECKPOINT (); + db::compare_netlist (_this, nl, "circuit RINGO ();\n" " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" " subcircuit INV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD);\n" @@ -321,9 +323,11 @@ TEST(1_DeviceAndNetExtraction) // make pins for named nets of top-level circuits - this way they are not purged nl.make_top_level_pins (); nl.purge (); + nl.purge_nets (); // compare netlist as string - EXPECT_EQ (nl.to_string (), + CHECKPOINT (); + db::compare_netlist (_this, nl, "circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD);\n" " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" " subcircuit INV2 $2 (IN=FB,$2=(null),OUT=$I19,$4=VSS,$5=VDD);\n" @@ -508,7 +512,8 @@ TEST(2_DeviceAndNetExtractionFlat) // compare netlist as string // NOTE: some of the nets are called IN,OUT but are different ones. They // happen to be the same because they share the same label. - EXPECT_EQ (nl.to_string (), + CHECKPOINT (); + db::compare_netlist (_this, nl, "circuit RINGO ();\n" " device PMOS $1 (S=$16,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" " device PMOS $2 (S=VDD,G=$16,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" @@ -742,7 +747,8 @@ TEST(3_DeviceAndNetExtractionWithImplicitConnections) dump_nets_to_layout (nl, cl, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (nl.to_string (), + CHECKPOINT (); + db::compare_netlist (_this, nl, "circuit RINGO ();\n" " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" " subcircuit INV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" @@ -777,7 +783,8 @@ TEST(3_DeviceAndNetExtractionWithImplicitConnections) nl.purge (); // compare netlist as string - EXPECT_EQ (nl.to_string (), + CHECKPOINT (); + db::compare_netlist (_this, nl, "circuit RINGO (FB=FB,OSC=OSC,NEXT=NEXT,'VSSZ,VSS'='VSSZ,VSS','VDDZ,VDD'='VDDZ,VDD');\n" " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" " subcircuit INV2 $2 (IN=FB,$2=(null),OUT=$I19,$4='VSSZ,VSS',$5='VDDZ,VDD');\n" From b391b4510fc8d085b90399b4468e1c18dc655375 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 31 Mar 2019 09:53:51 +0200 Subject: [PATCH 21/54] WIP: can compare empty circuits now Empty circuits play a role as abstracts. They are compared by using the pin names the nets are attached to. The implementation change is: * nodes without device terminals or subcircuit pins are compared through their net properties (count and name of pins attached) * some enhancements of the net string serializer have been made to account for pin name mismatches. --- src/db/db/dbNetlist.cc | 20 +++- src/db/db/dbNetlistCompare.cc | 129 +++++++++++++++++---- src/db/unit_tests/dbNetlistCompareTests.cc | 57 ++++----- 3 files changed, 151 insertions(+), 55 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index a1fdcab96..4396c8e4d 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -606,7 +606,10 @@ static db::Net *read_net (tl::Extractor &ex, db::Circuit *circuit, std::map &n2n) { - size_t npins = circuit->pin_count (); + std::vector org_pins; + for (db::Circuit::const_pin_iterator p = circuit->begin_pins (); p != circuit->end_pins (); ++p) { + org_pins.push_back (p->name ()); + } circuit->clear_pins (); @@ -627,6 +630,10 @@ static void read_pins (tl::Extractor &ex, db::Circuit *circuit, std::mappin_count () < org_pins.size () && pn != org_pins [circuit->pin_count ()]) { + ex.error (tl::sprintf (tl::to_string (tr ("Circuit defines different name for pin than subcircuit: %s (circuit) vs. %s (subcircuit)")), pn, org_pins [circuit->pin_count ()])); + } + const db::Pin &pin = circuit->add_pin (pn); if (net) { net->add_pin (db::NetPinRef (pin.id ())); @@ -636,8 +643,10 @@ static void read_pins (tl::Extractor &ex, db::Circuit *circuit, std::mappin_count () < npins) { + if (circuit->pin_count () < org_pins.size ()) { ex.error (tl::to_string (tr ("Circuit defines less pins that subcircuit"))); + } else if (org_pins.size () > 0 && circuit->pin_count () > org_pins.size ()) { + ex.error (tl::to_string (tr ("Circuit defines more pins that subcircuit"))); } } @@ -753,7 +762,12 @@ static void read_subcircuit_pins (tl::Extractor &ex, db::Circuit *circuit, db::S while (! ex.test (")")) { std::string pn; - ex.read_word_or_quoted (pn); + if (ex.test ("$")) { + size_t i; + ex.read (i); + } else { + ex.read_word_or_quoted (pn); + } ex.expect ("="); diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 3905c97f5..0cff104fa 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -287,7 +287,7 @@ static size_t translate_terminal_id (size_t tid, const db::Device *device) return device->device_class () ? device->device_class ()->normalize_terminal_id (tid) : tid; } -class NetDeviceGraphNode +class NetGraphNode { public: struct EdgeDesc { @@ -310,7 +310,11 @@ public: bool operator< (const EdgeDesc &other) const { - if (m_id1 > std::numeric_limits::max () / 2) { + if (is_for_subcircuit () != other.is_for_subcircuit ()) { + return is_for_subcircuit () < other.is_for_subcircuit (); + } + + if (is_for_subcircuit ()) { if ((subcircuit_pair ().first != 0) != (other.subcircuit_pair ().first != 0)) { return (subcircuit_pair ().first != 0) < (other.subcircuit_pair ().first != 0); @@ -346,7 +350,11 @@ public: bool operator== (const EdgeDesc &other) const { - if (m_id1 > std::numeric_limits::max () / 2) { + if (is_for_subcircuit () != other.is_for_subcircuit ()) { + return false; + } + + if (is_for_subcircuit ()) { if ((subcircuit_pair ().first != 0) != (other.subcircuit_pair ().first != 0)) { return false; @@ -381,6 +389,11 @@ public: char m_ref [sizeof (std::pair)]; size_t m_id1, m_id2; + inline bool is_for_subcircuit () const + { + return m_id1 > std::numeric_limits::max () / 2; + } + std::pair &device_pair () { return *reinterpret_cast *> ((void *) &m_ref); @@ -412,7 +425,7 @@ public: typedef std::vector, std::pair > >::const_iterator edge_iterator; - NetDeviceGraphNode (const db::Net *net, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const std::map *circuit_map, const CircuitPinMapper *pin_map) + NetGraphNode (const db::Net *net, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const std::map *circuit_map, const CircuitPinMapper *pin_map) : mp_net (net), m_other_net_index (std::numeric_limits::max ()) { if (! net) { @@ -544,6 +557,11 @@ public: m_other_net_index = index; } + bool empty () const + { + return m_edges.empty (); + } + void apply_net_index (const std::map &ni) { for (std::vector, std::pair > >::iterator i = m_edges.begin (); i != m_edges.end (); ++i) { @@ -560,7 +578,7 @@ public: std::sort (m_edges.begin (), m_edges.end ()); } - bool operator< (const NetDeviceGraphNode &node) const + bool operator< (const NetGraphNode &node) const { if (m_edges.size () != node.m_edges.size ()) { return m_edges.size () < node.m_edges.size (); @@ -570,10 +588,14 @@ public: return m_edges [i].first < node.m_edges [i].first; } } + if (m_edges.empty ()) { + // do a more detailed analysis on the edges + return edge_less (net (), node.net ()); + } return false; } - bool operator== (const NetDeviceGraphNode &node) const + bool operator== (const NetGraphNode &node) const { if (m_edges.size () != node.m_edges.size ()) { return false; @@ -583,10 +605,14 @@ public: return false; } } + if (m_edges.empty ()) { + // do a more detailed analysis on the edges + return edge_equal (net (), node.net ()); + } return true; } - void swap (NetDeviceGraphNode &other) + void swap (NetGraphNode &other) { std::swap (m_other_net_index, other.m_other_net_index); std::swap (mp_net, other.mp_net); @@ -605,7 +631,7 @@ public: edge_iterator find_edge (const std::vector &edge) const { - edge_iterator res = std::lower_bound (begin (), end (), edge, NetDeviceGraphNode::EdgeToEdgeOnlyCompare ()); + edge_iterator res = std::lower_bound (begin (), end (), edge, NetGraphNode::EdgeToEdgeOnlyCompare ()); if (res == end () || res->first != edge) { return end (); } else { @@ -617,6 +643,59 @@ private: const db::Net *mp_net; size_t m_other_net_index; std::vector, std::pair > > m_edges; + + /** + * @brief Compares edges as "less" + * Edge comparison is based on the pins attached (name of the first pin) or net + * name if no pins are attached on both nets. + */ + static bool edge_less (const db::Net *a, const db::Net *b) + { + if ((a != 0) != (b != 0)) { + return (a != 0) < (b != 0); + } + if (a != 0) { + if (a->pin_count () != b->pin_count ()) { + return a->pin_count () < b->pin_count (); + } + if (a->pin_count () > 0) { + const std::string &pna = a->begin_pins ()->pin ()->name (); + const std::string &pnb = b->begin_pins ()->pin ()->name (); + if (! pna.empty () && ! pnb.empty ()) { + return pna < pnb; + } + } + return a->name () < b->name (); + } else { + return false; + } + } + + /** + * @brief Compares edges as "equal" + * See edge_less for the comparison details. + */ + static bool edge_equal (const db::Net *a, const db::Net *b) + { + if ((a != 0) != (b != 0)) { + return false; + } + if (a != 0) { + if (a->pin_count () != b->pin_count ()) { + return false; + } + if (a->pin_count () > 0) { + const std::string &pna = a->begin_pins ()->pin ()->name (); + const std::string &pnb = b->begin_pins ()->pin ()->name (); + if (! pna.empty () && ! pnb.empty ()) { + return pna == pnb; + } + } + return a->name () == b->name (); + } else { + return true; + } + } }; // -------------------------------------------------------------------------------------------------------------------- @@ -626,7 +705,7 @@ private: namespace std { - void swap (db::NetDeviceGraphNode &a, db::NetDeviceGraphNode &b) + void swap (db::NetGraphNode &a, db::NetGraphNode &b) { a.swap (b); } @@ -638,7 +717,7 @@ namespace db class NetDeviceGraph { public: - typedef std::vector::const_iterator node_iterator; + typedef std::vector::const_iterator node_iterator; NetDeviceGraph () { @@ -653,7 +732,7 @@ public: m_net_index.clear (); // create a dummy node for a null net - m_nodes.push_back (NetDeviceGraphNode (0, device_categorizer, circuit_categorizer, circuit_and_pin_mapping, circuit_pin_mapper)); + m_nodes.push_back (NetGraphNode (0, device_categorizer, circuit_categorizer, circuit_and_pin_mapping, circuit_pin_mapper)); size_t nets = 0; for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { @@ -662,16 +741,16 @@ public: m_nodes.reserve (nets); for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - NetDeviceGraphNode node (n.operator-> (), device_categorizer, circuit_categorizer, circuit_and_pin_mapping, circuit_pin_mapper); + NetGraphNode node (n.operator-> (), device_categorizer, circuit_categorizer, circuit_and_pin_mapping, circuit_pin_mapper); m_nodes.push_back (node); } std::sort (m_nodes.begin (), m_nodes.end ()); - for (std::vector::const_iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { + for (std::vector::const_iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { m_net_index.insert (std::make_pair (i->net (), i - m_nodes.begin ())); } - for (std::vector::iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { + for (std::vector::iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { i->apply_net_index (m_net_index); } } @@ -717,15 +796,15 @@ public: for (std::vector::const_iterator index = todo.begin (); index != todo.end (); ++index) { - NetDeviceGraphNode *n = & m_nodes[*index]; - NetDeviceGraphNode *nother = & other.m_nodes[n->other_net_index ()]; + NetGraphNode *n = & m_nodes[*index]; + NetGraphNode *nother = & other.m_nodes[n->other_net_index ()]; // non-ambiguous paths to non-assigned nodes create a node identity on the // end of this path - for (NetDeviceGraphNode::edge_iterator e = n->begin (); e != n->end (); ) { + for (NetGraphNode::edge_iterator e = n->begin (); e != n->end (); ) { - NetDeviceGraphNode::edge_iterator ee = e; + NetGraphNode::edge_iterator ee = e; ++ee; while (ee != n->end () && ee->first == e->first) { @@ -733,8 +812,8 @@ public: } size_t count = 0; - NetDeviceGraphNode::edge_iterator ec; - for (NetDeviceGraphNode::edge_iterator i = e; i != ee; ++i) { + NetGraphNode::edge_iterator ec; + for (NetGraphNode::edge_iterator i = e; i != ee; ++i) { if (! m_nodes[i->second.first].has_other ()) { ec = i; ++count; @@ -747,13 +826,13 @@ public: tl::log << "considering " << n->net ()->expanded_name () << " to " << ec->second.second->expanded_name (); #endif - NetDeviceGraphNode::edge_iterator e_other = nother->find_edge (ec->first); + NetGraphNode::edge_iterator e_other = nother->find_edge (ec->first); if (e_other != nother->end ()) { #if defined(PRINT_DEBUG_NETCOMPARE) tl::log << "candidate accepted"; #endif - NetDeviceGraphNode::edge_iterator ee_other = e_other; + NetGraphNode::edge_iterator ee_other = e_other; ++ee_other; while (ee_other != nother->end () && ee_other->first == e_other->first) { @@ -761,8 +840,8 @@ public: } size_t count_other = 0; - NetDeviceGraphNode::edge_iterator ec_other; - for (NetDeviceGraphNode::edge_iterator i = e_other; i != ee_other; ++i) { + NetGraphNode::edge_iterator ec_other; + for (NetGraphNode::edge_iterator i = e_other; i != ee_other; ++i) { if (! other.m_nodes[i->second.first].has_other ()) { ec_other = i; ++count_other; @@ -807,7 +886,7 @@ public: } private: - std::vector m_nodes; + std::vector m_nodes; std::map m_net_index; }; diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 0def3a631..8406093f5 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -1316,18 +1316,6 @@ TEST(14_Subcircuit2MatchWithSwap) TEST(15_EmptySubCircuitTest) { const char *nls1 = - "circuit RINGO ();\n" - " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $4 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $5 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $6 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD);\n" - "end;\n" "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" " device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" @@ -1342,22 +1330,10 @@ TEST(15_EmptySubCircuitTest) "end;\n"; const char *nls2 = - "circuit RINGO ();\n" - " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $3 (IN=$I19,$2=$I39,OUT=$I1,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $4 (IN=$I2,$2=$I41,OUT=$I3,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $5 (IN=$I3,$2=$I42,OUT=$I4,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $6 (IN=$I1,$2=$I40,OUT=$I2,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $7 (IN=$I4,$2=$I43,OUT=$I5,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $8 (IN=$I5,$2=$I44,OUT=$I6,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $9 (IN=$I6,$2=$I45,OUT=$I7,$4=VSS,$5=VDD);\n" - " subcircuit INV2 $10 (IN=$I7,$2=$I46,OUT=$I8,$4=VSS,$5=VDD);\n" - "end;\n" "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" " device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $3 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $1 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $3 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" " subcircuit TRANS $1 (G=$4,S=$2,D=IN);\n" " subcircuit TRANS $2 (G=$5,S=$2,D=IN);\n" @@ -1378,7 +1354,34 @@ TEST(15_EmptySubCircuitTest) bool good = comp.compare (&nl1, &nl2); EXPECT_EQ (logger.text (), - "..." + "begin_circuit TRANS TRANS\n" + "match_nets $3 $3\n" + "match_nets $2 $1\n" + "match_nets $1 $2\n" + "match_pins D D\n" + "match_pins G G\n" + "match_pins S S\n" + "end_circuit TRANS TRANS MATCH\n" + "begin_circuit INV2 INV2\n" + "match_nets IN IN\n" + "match_nets $5 $5\n" + "match_nets $4 $4\n" + "match_nets OUT OUT\n" + "match_nets $2 $2\n" + "match_pins IN IN\n" + "match_pins $4 $4\n" + "match_pins $3 $3\n" + "match_pins OUT OUT\n" + "match_pins $1 $1\n" + "match_devices $1 $1\n" + "match_devices $3 $2\n" + "match_devices $2 $3\n" + "match_devices $4 $4\n" + "match_subcircuits $1 $1\n" + "match_subcircuits $2 $2\n" + "match_subcircuits $3 $3\n" + "match_subcircuits $4 $4\n" + "end_circuit INV2 INV2 MATCH" ); EXPECT_EQ (good, true); From 06e326dfd9e4249cd2d8c926338057b57dd2aec2 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 31 Mar 2019 19:00:42 +0200 Subject: [PATCH 22/54] WIP: netlist compare - some more tests by netlist compare. Needs fixing. --- src/db/db/dbTestSupport.cc | 12 +- src/db/unit_tests/dbLayoutToNetlistTests.cc | 42 ++++-- src/db/unit_tests/dbNetlistCompareTests.cc | 130 +++++++++++++++++++ src/db/unit_tests/dbNetlistExtractorTests.cc | 66 +++++----- 4 files changed, 196 insertions(+), 54 deletions(-) diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index 70c24b7fc..b9f48536f 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -164,20 +164,20 @@ public: void out (const std::string &text) { if (m_new_circuit) { - tl::log << m_circuit; + tl::info << m_circuit; m_new_circuit = false; } - tl::log << text; + tl::info << text; } - virtual void begin_netlist () + virtual void begin_netlist (const db::Netlist * /*a*/, const db::Netlist * /*b*/) { - tl::log << "Comparing netlists:"; + tl::info << "Comparing netlists:"; } - virtual void end_netlist () + virtual void end_netlist (const db::Netlist * /*a*/, const db::Netlist * /*b*/) { - tl::log << "End of difference log."; + tl::info << "End of difference log."; } virtual void begin_circuit (const db::Circuit *a, const db::Circuit *b) diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 35915880d..7b5999a74 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -337,7 +337,8 @@ TEST(1_BasicExtraction) db::compare_layouts (_this, ly, au); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), "circuit RINGO ();\n" " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" " subcircuit INV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD);\n" @@ -495,7 +496,8 @@ TEST(1_BasicExtraction) l2n.netlist ()->purge (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), "circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD);\n" " subcircuit INV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" " subcircuit INV2 $2 (IN=FB,$2=(null),OUT=$I19,$4=VSS,$5=VDD);\n" @@ -689,7 +691,8 @@ TEST(2_Probing) dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), "circuit RINGO ();\n" " subcircuit INV2PAIR $1 ($1=FB,$2=VDD,$3=VSS,$4=$I3,$5=OSC);\n" " subcircuit INV2PAIR $2 ($1=$I18,$2=VDD,$3=VSS,$4=FB,$5=$I9);\n" @@ -745,7 +748,8 @@ TEST(2_Probing) l2n.netlist ()->purge (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), "circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD);\n" " subcircuit INV2PAIR $1 ($1=FB,$2=VDD,$3=VSS,$4=$I3,$5=OSC);\n" " subcircuit INV2PAIR $2 ($1=(null),$2=VDD,$3=VSS,$4=FB,$5=$I9);\n" @@ -968,7 +972,8 @@ TEST(3_GlobalNetConnections) dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), "circuit RINGO ();\n" " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD);\n" " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=$I22,$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD);\n" @@ -1024,7 +1029,8 @@ TEST(3_GlobalNetConnections) l2n.netlist ()->purge (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), "circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS');\n" " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD);\n" " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD);\n" @@ -1253,7 +1259,8 @@ TEST(4_GlobalNetDeviceExtraction) dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), "circuit RINGO ();\n" " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD);\n" " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=$I22,$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD);\n" @@ -1309,7 +1316,8 @@ TEST(4_GlobalNetDeviceExtraction) l2n.netlist ()->purge (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), "circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS');\n" " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD);\n" " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=(null),$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD);\n" @@ -1538,7 +1546,8 @@ TEST(5_DeviceExtractionWithDeviceCombination) dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), "circuit RINGO ();\n" " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=FB,$5=$I7,$6=OSC,$7=VDD);\n" " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=$I22,$5=FB,$6=$I13,$7=VDD);\n" @@ -1591,7 +1600,8 @@ TEST(5_DeviceExtractionWithDeviceCombination) l2n.netlist ()->purge (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), "circuit RINGO (FB=FB,OSC=OSC,VDD=VDD,'BULK,VSS'='BULK,VSS');\n" " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=FB,$5=$I7,$6=OSC,$7=VDD);\n" " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=VDD,$3='BULK,VSS',$4=(null),$5=FB,$6=$I13,$7=VDD);\n" @@ -1765,7 +1775,8 @@ TEST(6_MoreDeviceTypes) l2n.extract_netlist (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), "circuit TOP ();\n" " device HVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) (L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4);\n" " device HVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) (L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8);\n" @@ -1921,7 +1932,8 @@ TEST(7_MoreByEmptyDeviceTypes) l2n.extract_netlist (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), "circuit TOP ();\n" " device LVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) (L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4);\n" " device LVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) (L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8);\n" @@ -2099,7 +2111,8 @@ TEST(8_FlatExtraction) l2n.extract_netlist (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), "circuit TOP ();\n" " device HVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) (L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4);\n" " device HVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) (L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8);\n" @@ -2282,7 +2295,8 @@ TEST(9_FlatExtractionWithExternalDSS) l2n.extract_netlist (); // compare netlist as string - EXPECT_EQ (l2n.netlist ()->to_string (), + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), "circuit TOP ();\n" " device LVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) (L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4);\n" " device LVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) (L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8);\n" diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 8406093f5..8dade2799 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -498,6 +498,136 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (good, false); } +TEST(5_BufferTwoPathsDifferentDeviceClasses) +{ + const char *nls1 = + "circuit BUF ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $2 (S=VSS,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $3 (S=VDD,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=VSS,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $5 (S=VDD,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $6 (S=VSS,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOSB $7 (S=VDD,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOSB $8 (S=VSS,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + const char *nls2 = + "circuit BUF ($1=VDD,$2=IN,$3=VSS,$4=OUT);\n" + " device PMOS $1 (S=VDD,G=IN,D=$10) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=$10,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $3 (S=VDD,G=IN,D=$11) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $4 (S=VDD,G=$11,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $5 (S=$10,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $6 (S=OUT,G=$10,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $7 (S=$11,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOSB $8 (S=OUT,G=$11,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + // NOTE: adding this power hint makes the device class error harder to detect + const db::Circuit *ca = nl1.circuit_by_name ("BUF"); + const db::Circuit *cb = nl2.circuit_by_name ("BUF"); + comp.same_nets (ca->net_by_name ("VDD"), cb->net_by_name ("VDD")); + comp.same_nets (ca->net_by_name ("VSS"), cb->net_by_name ("VSS")); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit BUF BUF\n" + "match_nets IN IN\n" + "match_nets INT $10\n" + "match_nets OUT OUT\n" + "match_nets INT2 $11\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $0 $1\n" + "match_pins $3 $2\n" + "match_devices $1 $1\n" + "match_devices $3 $2\n" + "match_devices $5 $3\n" + "match_devices_with_different_device_classes $7 $4\n" + "match_devices $2 $5\n" + "match_devices $4 $6\n" + "match_devices $6 $7\n" + "match_devices $8 $8\n" + "end_circuit BUF BUF NOMATCH" + ); + EXPECT_EQ (good, false); +} + +TEST(6_BufferTwoPathsAdditionalResistor) +{ + const char *nls1 = + "circuit BUF ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $2 (S=VSS,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $3 (S=VDD,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=VSS,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $5 (S=VDD,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $6 (S=VSS,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOSB $7 (S=VDD,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOSB $8 (S=VSS,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + const char *nls2 = + "circuit BUF ($1=VDD,$2=IN,$3=VSS,$4=OUT);\n" + " device PMOS $1 (S=VDD,G=IN,D=$10) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=$10,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $3 (S=VDD,G=IN,D=$11) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOSB $4 (S=VDD,G=$11,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $5 (S=$10,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $6 (S=OUT,G=$10,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $7 (S=$11,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOSB $8 (S=OUT,G=$11,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device RES $9 (A=$10,B=$11) (R=42);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + // Forcing the power nets into equality makes the resistor error harder to detect + const db::Circuit *ca = nl1.circuit_by_name ("BUF"); + const db::Circuit *cb = nl2.circuit_by_name ("BUF"); + comp.same_nets (ca->net_by_name ("VDD"), cb->net_by_name ("VDD")); + comp.same_nets (ca->net_by_name ("VSS"), cb->net_by_name ("VSS")); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit BUF BUF\n" + "match_nets INT $10\n" + "match_nets OUT OUT\n" + "match_nets INT2 $11\n" + "match_nets IN IN\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $0 $1\n" + "match_pins $3 $2\n" + "match_devices $1 $1\n" + "match_devices $3 $2\n" + "match_devices $5 $3\n" + "match_devices $7 $4\n" + "match_devices $2 $5\n" + "match_devices $4 $6\n" + "match_devices $6 $7\n" + "match_devices $8 $8\n" + "device_mismatch (null) $9\n" + "end_circuit BUF BUF NOMATCH" + ); + EXPECT_EQ (good, false); +} + TEST(6_BufferTwoPathsAdditionalDevices) { const char *nls1 = diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 3fd08616c..f3a8cd1f7 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -510,51 +510,49 @@ TEST(2_DeviceAndNetExtractionFlat) dump_nets_to_layout (nl, cl, ly, dump_map, cm); // compare netlist as string - // NOTE: some of the nets are called IN,OUT but are different ones. They - // happen to be the same because they share the same label. CHECKPOINT (); db::compare_netlist (_this, nl, "circuit RINGO ();\n" - " device PMOS $1 (S=$16,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $2 (S=VDD,G=$16,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $3 (S=$14,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $4 (S=VDD,G=$14,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $5 (S=$12,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $6 (S=VDD,G=$12,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $7 (S='IN,FB',G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $1 (S=$16,G='IN,OUT$6',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=$16,D='IN,OUT$7') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $3 (S=$14,G='IN,OUT$5',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $4 (S=VDD,G=$14,D='IN,OUT$6') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $5 (S=$12,G='IN,OUT$4',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $6 (S=VDD,G=$12,D='IN,OUT$5') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $7 (S='IN,FB',G='IN,OUT$8',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" " device PMOS $8 (S=VDD,G='IN,FB',D='OUT,OSC') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" " device PMOS $9 (S=$4,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $10 (S=VDD,G=$4,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $11 (S=$8,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $12 (S=VDD,G=$8,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $10 (S=VDD,G=$4,D='IN,OUT$1') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $11 (S=$8,G='IN,OUT$2',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $12 (S=VDD,G=$8,D='IN,OUT$3') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" " device PMOS $13 (S=$2,G='IN,FB',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" " device PMOS $14 (S=VDD,G=$2,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $15 (S=$6,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $16 (S=VDD,G=$6,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $17 (S=$18,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $18 (S=VDD,G=$18,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device PMOS $19 (S=$10,G='IN,OUT',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device PMOS $20 (S=VDD,G=$10,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $21 (S='IN,FB',G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $15 (S=$6,G='IN,OUT$1',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $16 (S=VDD,G=$6,D='IN,OUT$2') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $17 (S=$18,G='IN,OUT$7',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $18 (S=VDD,G=$18,D='IN,OUT$8') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device PMOS $19 (S=$10,G='IN,OUT$3',D=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $20 (S=VDD,G=$10,D='IN,OUT$4') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $21 (S='IN,FB',G='IN,OUT$8',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" " device NMOS $22 (S=VSS,G='IN,FB',D='OUT,OSC') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $23 (S=$18,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $24 (S=VSS,G=$18,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $25 (S=$14,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $26 (S=VSS,G=$14,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $27 (S=$12,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $28 (S=VSS,G=$12,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $23 (S=$18,G='IN,OUT$7',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $24 (S=VSS,G=$18,D='IN,OUT$8') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $25 (S=$14,G='IN,OUT$5',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $26 (S=VSS,G=$14,D='IN,OUT$6') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $27 (S=$12,G='IN,OUT$4',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $28 (S=VSS,G=$12,D='IN,OUT$5') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" " device NMOS $29 (S=$4,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $30 (S=VSS,G=$4,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $30 (S=VSS,G=$4,D='IN,OUT$1') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" " device NMOS $31 (S=$2,G='IN,FB',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" " device NMOS $32 (S=VSS,G=$2,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $33 (S=$8,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $34 (S=VSS,G=$8,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $35 (S=$6,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $36 (S=VSS,G=$6,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $37 (S=$16,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $38 (S=VSS,G=$16,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" - " device NMOS $39 (S=$10,G='IN,OUT',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" - " device NMOS $40 (S=VSS,G=$10,D='IN,OUT') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $33 (S=$8,G='IN,OUT$2',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $34 (S=VSS,G=$8,D='IN,OUT$3') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $35 (S=$6,G='IN,OUT$1',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $36 (S=VSS,G=$6,D='IN,OUT$2') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $37 (S=$16,G='IN,OUT$6',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $38 (S=VSS,G=$16,D='IN,OUT$7') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $39 (S=$10,G='IN,OUT$3',D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $40 (S=VSS,G=$10,D='IN,OUT$4') (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" "end;\n" ); From 9613ad72c8fb75213d138e7bebde8e9c55c442be Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 31 Mar 2019 23:59:43 +0200 Subject: [PATCH 23/54] WIP: netlist compare - using it for more tests Issue solved: some circuit pins may not have a net - these need to be ignored. Requirement: all pins with a net must be mapped. Detached pins are not present in the mapping table. A dummy mapping table was introduced to allow dropping of pins in the second circuit too. Output of compare should not depend on memory location anymore and pin mismatch reporting should include all pins. --- src/db/db/dbNetlistCompare.cc | 169 ++++++++------ src/db/db/dbNetlistCompare.h | 2 +- src/db/unit_tests/dbLayoutToNetlistTests.cc | 8 +- src/db/unit_tests/dbNetlistCompareTests.cc | 245 ++++++++++++++------ testdata/algo/device_extract_l6.gds | Bin 4430 -> 4430 bytes 5 files changed, 279 insertions(+), 145 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 0cff104fa..c2b43d015 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -157,6 +157,16 @@ public: m_rev_pin_map.insert (std::make_pair (other_pin, this_pin)); } + bool has_other_pin_for_this_pin (size_t this_pin) const + { + return m_pin_map.find (this_pin) != m_pin_map.end (); + } + + bool has_this_pin_for_other_pin (size_t other_pin) const + { + return m_rev_pin_map.find (other_pin) != m_rev_pin_map.end (); + } + size_t other_pin_from_this_pin (size_t this_pin) const { std::map::const_iterator i = m_pin_map.find (this_pin); @@ -440,28 +450,28 @@ public: size_t pin_id = i->pin ()->id (); const db::Circuit *cr = sc->circuit_ref (); - const CircuitMapper *cm = 0; + pin_id = pin_map->normalize_pin_id (cr, pin_id); - if (circuit_map) { - std::map::const_iterator icm = circuit_map->find (cr); - if (icm == circuit_map->end ()) { - // this can happen if the other circuit is not present - this is allowed for single-pin - // circuits. - continue; - } - cm = & icm->second; + std::map::const_iterator icm = circuit_map->find (cr); + if (icm == circuit_map->end ()) { + // this can happen if the other circuit is not present - this is allowed for single-pin + // circuits. + continue; } - // NOTE: if cm is given, cr and pin_id are given in terms of the "other" circuit + const CircuitMapper *cm = & icm->second; - if (cm) { - cr = cm->other (); - pin_id = cm->other_pin_from_this_pin (pin_id); + // A pin assignment may be missing because there is no net for a pin -> skip this + + if (! cm->has_other_pin_for_this_pin (pin_id)) { + continue; } - if (pin_map) { - pin_id = pin_map->normalize_pin_id (cr, pin_id); - } + // NOTE: if cm is given, cr and pin_id are given in terms of the canonical "other" circuit. + // For c1 this is the c1->c2 mapper, for c2 this is the c2->c2 dummy mapper. + + cr = cm->other (); + pin_id = cm->other_pin_from_this_pin (pin_id); // we cannot afford creating edges from all to all other pins, so we just create edges to the previous and next // pin. This may take more iterations to solve, but should be equivalent. @@ -469,17 +479,21 @@ public: std::vector pids; size_t pin_count = cr->pin_count (); - // take the previous, next and second-next pin as targets for edges - // (using the second-next pin avoid isolation of OUT vs. IN in case - // of a pin configuration of VSS-IN-VDD-OUT like for an inverter). + // take a number if additional pins as edges: this allows identifying a pin as dependent + // from other pins hence nets are propagated. We assume that there are 4 power pins max so + // 5 additional pins should be sufficient to capture one additional non-power pin. - if (pin_count >= 2) { - pids.push_back ((pin_id + pin_count - 1) % pin_count); - if (pin_count >= 3) { - pids.push_back ((pin_id + 1) % pin_count); - if (pin_count >= 4) { - pids.push_back ((pin_id + 2) % pin_count); - } + size_t take_additional_pins = 5; + for (size_t n = 0; n < take_additional_pins; ++n) { + size_t add_pin_id = (pin_id + n + 1) % pin_count; + if (add_pin_id == pin_id) { + break; + } + if (cm->has_this_pin_for_other_pin (add_pin_id)) { + pids.push_back (add_pin_id); + } else { + // skip pins without mapping + ++take_additional_pins; } } @@ -489,9 +503,9 @@ public: // NOTE: if a pin mapping is given, EdgeDesc::pin1_id and EdgeDesc::pin2_id are given // as pin ID's of the other circuit. - EdgeDesc ed (sc, circuit_categorizer.cat_for_subcircuit (sc), pin_id, pin_map ? pin_map->normalize_pin_id (cr, pin2_id) : pin2_id); + EdgeDesc ed (sc, circuit_categorizer.cat_for_subcircuit (sc), pin_id, pin_map->normalize_pin_id (cr, pin2_id)); - size_t this_pin2_id = cm ? cm->this_pin_from_other_pin (pin2_id) : pin2_id; + size_t this_pin2_id = cm->this_pin_from_other_pin (pin2_id); const db::Net *net2 = sc->net_for_pin (this_pin2_id); std::map::const_iterator in = n2entry.find (net2); @@ -966,7 +980,7 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const } std::set verified_circuits_a, verified_circuits_b; - std::map pin_mapping; + std::map c12_pin_mapping, c22_pin_mapping; for (db::Netlist::const_bottom_up_circuit_iterator c = a->begin_bottom_up (); c != a->end_bottom_up (); ++c) { @@ -994,7 +1008,7 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const } bool pin_mismatch = false; - bool g = compare_circuits (ca, cb, device_categorizer, circuit_categorizer, *net_identity, pin_mismatch, pin_mapping); + bool g = compare_circuits (ca, cb, device_categorizer, circuit_categorizer, *net_identity, pin_mismatch, c12_pin_mapping, c22_pin_mapping); if (! g) { good = false; } @@ -1070,29 +1084,30 @@ compute_subcircuit_key (const db::SubCircuit &subcircuit, const db::NetDeviceGra const db::Circuit *cr = subcircuit.circuit_ref (); - const CircuitMapper *cm = 0; - - if (circuit_map) { - std::map::const_iterator icm = circuit_map->find (cr); - if (icm == circuit_map->end ()) { - // this can happen if the other circuit does not exist - in this case the key is an invalid one which cannot - // be produced by a regular subcircuit. - return k; - } - cm = & icm->second; - cr = cm->other (); + std::map::const_iterator icm = circuit_map->find (cr); + if (icm == circuit_map->end ()) { + // this can happen if the other circuit does not exist - in this case the key is an invalid one which cannot + // be produced by a regular subcircuit. + return k; } - // NOTE: if cm is given, cr is given in terms of the "other" circuit + const CircuitMapper *cm = & icm->second; + cr = cm->other (); + + // NOTE: cr is given in terms of the canonical "other" circuit. for (db::Circuit::const_pin_iterator p = cr->begin_pins (); p != cr->end_pins (); ++p) { - size_t this_pin_id = cm ? cm->this_pin_from_other_pin (p->id ()) : p->id (); - size_t pin_id = pin_map ? pin_map->normalize_pin_id (cr, p->id ()) : p->id (); + if (cm->has_this_pin_for_other_pin (p->id ())) { - const db::Net *net = subcircuit.net_for_pin (this_pin_id); - size_t net_id = g.node_index_for_net (net); - k.push_back (std::make_pair (pin_id, net_id)); + size_t this_pin_id = cm->this_pin_from_other_pin (p->id ()); + size_t pin_id = pin_map->normalize_pin_id (cr, p->id ()); + + const db::Net *net = subcircuit.net_for_pin (this_pin_id); + size_t net_id = g.node_index_for_net (net); + k.push_back (std::make_pair (pin_id, net_id)); + + } } @@ -1102,14 +1117,14 @@ compute_subcircuit_key (const db::SubCircuit &subcircuit, const db::NetDeviceGra } bool -NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, const std::vector > &net_identity, bool &pin_mismatch, std::map &circuit_and_pin_mapping) const +NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, const std::vector > &net_identity, bool &pin_mismatch, std::map &c12_circuit_and_pin_mapping, std::map &c22_circuit_and_pin_mapping) const { db::NetDeviceGraph g1, g2; // NOTE: for normalization we map all subcircuits of c1 to c2. // Also, pin swapping will only happen there. - g1.build (c1, device_categorizer, circuit_categorizer, &circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); - g2.build (c2, device_categorizer, circuit_categorizer, 0, mp_circuit_pin_mapper.get ()); + g1.build (c1, device_categorizer, circuit_categorizer, &c12_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); + g2.build (c2, device_categorizer, circuit_categorizer, &c22_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); // Match dummy nodes for null nets g1.identify (0, 0); @@ -1228,48 +1243,54 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, // Report pin assignment + // This step also does the pin identity mapping. - std::multimap net2pin; + std::multimap net2pin; for (db::Circuit::const_pin_iterator p = c2->begin_pins (); p != c2->end_pins (); ++p) { const db::Net *net = c2->net_for_pin (p->id ()); - tl_assert (net != 0); - net2pin.insert (std::make_pair (net, p.operator-> ())); + if (net) { + net2pin.insert (std::make_pair (g2.node_index_for_net (net), p.operator-> ())); + } } - CircuitMapper &pin_mapping = circuit_and_pin_mapping [c1]; - pin_mapping.set_other (c2); + CircuitMapper &c12_pin_mapping = c12_circuit_and_pin_mapping [c1]; + c12_pin_mapping.set_other (c2); - for (db::NetDeviceGraph::node_iterator i = g1.begin (); i != g1.end (); ++i) { + // dummy mapping: we show this circuit is used. + CircuitMapper &c22_pin_mapping = c22_circuit_and_pin_mapping [c2]; + c22_pin_mapping.set_other (c2); - const db::Net *net = i->net (); - if (! net || net->pin_count () == 0) { + for (db::Circuit::const_pin_iterator p = c1->begin_pins (); p != c1->end_pins (); ++p) { + + const db::Net *net = c1->net_for_pin (p->id ()); + if (! net) { continue; } - if (! i->has_other ()) { - for (db::Net::const_pin_iterator pi = net->begin_pins (); pi != net->end_pins (); ++pi) { - if (mp_logger) { - mp_logger->pin_mismatch (pi->pin (), 0); - } - pin_mismatch = true; - good = false; + const db::NetGraphNode &n = *(g1.begin () + g1.node_index_for_net (net)); + + if (! n.has_other ()) { + if (mp_logger) { + mp_logger->pin_mismatch (p.operator-> (), 0); } + pin_mismatch = true; + good = false; continue; } - const db::Net *other_net = g2.net_by_node_index (i->other_net_index ()); - - std::multimap::iterator np = net2pin.find (other_net); + std::multimap::iterator np = net2pin.find (n.other_net_index ()); for (db::Net::const_pin_iterator pi = net->begin_pins (); pi != net->end_pins (); ++pi) { - if (np != net2pin.end () && np->first == other_net) { + if (np != net2pin.end () && np->first == n.other_net_index ()) { if (mp_logger) { mp_logger->match_pins (pi->pin (), np->second); } - pin_mapping.map_pin (pi->pin ()->id (), np->second->id ()); + c12_pin_mapping.map_pin (pi->pin ()->id (), np->second->id ()); + // dummy mapping: we show this pin is used. + c22_pin_mapping.map_pin (np->second->id (), np->second->id ()); - std::multimap::iterator np_delete = np; + std::multimap::iterator np_delete = np; ++np; net2pin.erase (np_delete); @@ -1287,7 +1308,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } - for (std::multimap::iterator np = net2pin.begin (); np != net2pin.end (); ++np) { + for (std::multimap::iterator np = net2pin.begin (); np != net2pin.end (); ++np) { if (mp_logger) { mp_logger->pin_mismatch (0, np->second); } @@ -1391,7 +1412,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (db::Circuit::const_subcircuit_iterator sc = c1->begin_subcircuits (); sc != c1->end_subcircuits (); ++sc) { - std::vector > k = compute_subcircuit_key (*sc, g1, &circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); + std::vector > k = compute_subcircuit_key (*sc, g1, &c12_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); bool mapped = true; for (std::vector >::iterator i = k.begin (); i != k.end () && mapped; ++i) { @@ -1414,7 +1435,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (db::Circuit::const_subcircuit_iterator sc = c2->begin_subcircuits (); sc != c2->end_subcircuits (); ++sc) { - std::vector > k = compute_subcircuit_key (*sc, g2, 0, mp_circuit_pin_mapper.get ()); + std::vector > k = compute_subcircuit_key (*sc, g2, &c22_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); bool mapped = true; for (std::vector >::iterator i = k.begin (); i != k.end (); ++i) { diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h index a752302d2..b0fcf8009 100644 --- a/src/db/db/dbNetlistCompare.h +++ b/src/db/db/dbNetlistCompare.h @@ -201,7 +201,7 @@ public: bool compare (const db::Netlist *a, const db::Netlist *b) const; protected: - bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, const std::vector > &net_identity, bool &pin_mismatch, std::map &circuit_and_pin_mapping) const; + bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, const std::vector > &net_identity, bool &pin_mismatch, std::map &c12_circuit_and_pin_mapping, std::map &c22_circuit_and_pin_mapping) const; bool all_subcircuits_verified (const db::Circuit *c, const std::set &verified_circuits) const; NetlistCompareLogger *mp_logger; diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 7b5999a74..b2d8b8fe9 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -1780,7 +1780,7 @@ TEST(6_MoreDeviceTypes) "circuit TOP ();\n" " device HVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) (L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4);\n" " device HVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) (L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8);\n" - " device LVPMOS $3 (S=$10,G=A,D=$6,B=$9) (L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5);\n" + " device LVPMOS $3 (S=$10,G=A,D=$6,B=$9) (L=1.5,W=2.475,AS=4.77675,AD=3.155625,PS=8.81,PD=7.5);\n" " device HVNMOS $4 (S=Z,G=$6,D=VSS,B=BULK) (L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6);\n" " device HVNMOS $5 (S=VSS,G=A,D=$5,B=BULK) (L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2);\n" " device LVNMOS $6 (S=VSS,G=A,D=$6,B=BULK) (L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89);\n" @@ -1937,7 +1937,7 @@ TEST(7_MoreByEmptyDeviceTypes) "circuit TOP ();\n" " device LVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) (L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4);\n" " device LVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) (L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8);\n" - " device LVPMOS $3 (S=$10,G=A,D=$6,B=$9) (L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5);\n" + " device LVPMOS $3 (S=$10,G=A,D=$6,B=$9) (L=1.5,W=2.475,AS=4.77675,AD=3.155625,PS=8.81,PD=7.5);\n" " device LVNMOS $4 (S=VSS,G=A,D=$6,B=BULK) (L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89);\n" " device LVNMOS $5 (S=Z,G=$6,D=VSS,B=BULK) (L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6);\n" " device LVNMOS $6 (S=VSS,G=A,D=$5,B=BULK) (L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2);\n" @@ -2116,7 +2116,7 @@ TEST(8_FlatExtraction) "circuit TOP ();\n" " device HVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) (L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4);\n" " device HVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) (L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8);\n" - " device LVPMOS $3 (S=$10,G=A,D=$6,B=$9) (L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5);\n" + " device LVPMOS $3 (S=$10,G=A,D=$6,B=$9) (L=1.5,W=2.475,AS=4.77675,AD=3.155625,PS=8.81,PD=7.5);\n" " device HVNMOS $4 (S=Z,G=$6,D=VSS,B=BULK) (L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6);\n" " device HVNMOS $5 (S=VSS,G=A,D=$5,B=BULK) (L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2);\n" " device LVNMOS $6 (S=VSS,G=A,D=$6,B=BULK) (L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89);\n" @@ -2300,7 +2300,7 @@ TEST(9_FlatExtractionWithExternalDSS) "circuit TOP ();\n" " device LVPMOS $1 (S=Z,G=$5,D=VDD2,B=$8) (L=1.5,W=4.05,AS=5.4675,AD=2.73375,PS=10.8,PD=5.4);\n" " device LVPMOS $2 (S=VDD2,G=Z,D=$5,B=$8) (L=1.5,W=4.05,AS=2.73375,AD=5.4675,PS=5.4,PD=10.8);\n" - " device LVPMOS $3 (S=$10,G=A,D=$6,B=$9) (L=1.5,W=2.475,AS=1.11375,AD=3.155625,PS=5.85,PD=7.5);\n" + " device LVPMOS $3 (S=$10,G=A,D=$6,B=$9) (L=1.5,W=2.475,AS=4.77675,AD=3.155625,PS=8.81,PD=7.5);\n" " device LVNMOS $4 (S=VSS,G=A,D=$6,B=BULK) (L=1.2,W=1.7,AS=2.346,AD=2.1165,PS=6.16,PD=5.89);\n" " device LVNMOS $5 (S=Z,G=$6,D=VSS,B=BULK) (L=1.5,W=5.25,AS=7.0875,AD=3.54375,PS=13.2,PD=6.6);\n" " device LVNMOS $6 (S=VSS,G=A,D=$5,B=BULK) (L=1.5,W=5.25,AS=3.54375,AD=7.0875,PS=6.6,PD=13.2);\n" diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 8dade2799..7d21d100b 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -228,10 +228,10 @@ TEST(1_SimpleInverter) "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" "match_pins $2 $0\n" "match_pins $3 $2\n" - "match_pins $1 $3\n" - "match_pins $0 $1\n" "match_devices $2 $1\n" "match_devices $1 $2\n" "end_circuit INV INV MATCH" @@ -274,10 +274,10 @@ TEST(1_SimpleInverterMatchedDeviceClasses) "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" "match_pins $2 $0\n" "match_pins $3 $2\n" - "match_pins $1 $3\n" - "match_pins $0 $1\n" "match_devices $2 $1\n" "match_devices $1 $2\n" "end_circuit INV INV MATCH" @@ -316,10 +316,10 @@ TEST(2_SimpleInverterWithForcedNetAssignment) "begin_circuit INV INV\n" "match_nets OUT OUT\n" "match_nets IN IN\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" "match_pins $2 $0\n" "match_pins $3 $2\n" - "match_pins $1 $3\n" - "match_pins $0 $1\n" "match_devices $2 $1\n" "match_devices $1 $2\n" "end_circuit INV INV MATCH" @@ -361,9 +361,9 @@ TEST(3_Buffer) "match_nets IN IN\n" "match_nets VSS VSS\n" "match_nets INT $10\n" + "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" - "match_pins $0 $1\n" "match_pins $3 $2\n" "match_devices $1 $1\n" "match_devices $3 $2\n" @@ -417,9 +417,9 @@ TEST(4_BufferTwoPaths) "match_nets VSS VSS\n" "match_ambiguous_nets INT $10\n" "match_ambiguous_nets INT2 $11\n" + "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" - "match_pins $0 $1\n" "match_pins $3 $2\n" "match_devices $1 $1\n" "match_devices $3 $2\n" @@ -481,9 +481,9 @@ TEST(5_BufferTwoPathsDifferentParameters) "match_nets IN IN\n" "match_ambiguous_nets INT $10\n" "match_nets INT2 $11\n" + "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" - "match_pins $0 $1\n" "match_pins $3 $2\n" "match_devices $1 $1\n" "match_devices $3 $2\n" @@ -545,9 +545,9 @@ TEST(5_BufferTwoPathsDifferentDeviceClasses) "match_nets INT $10\n" "match_nets OUT OUT\n" "match_nets INT2 $11\n" + "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" - "match_pins $0 $1\n" "match_pins $3 $2\n" "match_devices $1 $1\n" "match_devices $3 $2\n" @@ -610,9 +610,9 @@ TEST(6_BufferTwoPathsAdditionalResistor) "match_nets OUT OUT\n" "match_nets INT2 $11\n" "match_nets IN IN\n" + "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" - "match_pins $0 $1\n" "match_pins $3 $2\n" "match_devices $1 $1\n" "match_devices $3 $2\n" @@ -673,9 +673,9 @@ TEST(6_BufferTwoPathsAdditionalDevices) "match_nets OUT OUT\n" "match_nets VSS VSS\n" "match_nets INT2 $10\n" + "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" - "match_pins $0 $1\n" "match_pins $3 $2\n" "match_devices $5 $1\n" "match_devices $7 $2\n" @@ -722,8 +722,8 @@ TEST(7_Resistors) "match_nets P1 P1\n" "match_nets P3 P3\n" "match_nets P2 P2\n" - "match_pins $1 $1\n" "match_pins $0 $0\n" + "match_pins $1 $1\n" "match_pins $2 $2\n" "match_devices $2 $1\n" "match_devices $1 $2\n" @@ -763,8 +763,8 @@ TEST(7_ResistorsParameterMismatch) "match_nets P2 P2\n" "match_nets P1 P1\n" "match_nets P3 P3\n" - "match_pins $1 $1\n" "match_pins $0 $0\n" + "match_pins $1 $1\n" "match_pins $2 $2\n" "match_devices $1 $1\n" "match_devices_with_different_parameters $3 $2\n" @@ -805,8 +805,8 @@ TEST(7_ResistorsPlusOneDevice) "match_nets P3 P3\n" "match_nets P2 P2\n" "match_nets P1 P1\n" - "match_pins $1 $1\n" "match_pins $0 $0\n" + "match_pins $1 $1\n" "match_pins $2 $2\n" "match_devices $1 $1\n" "match_devices $3 $2\n" @@ -848,8 +848,8 @@ TEST(8_Diodes) "match_nets P3 P3\n" "match_nets P2 P2\n" "match_pins $0 $0\n" - "match_pins $2 $2\n" "match_pins $1 $1\n" + "match_pins $2 $2\n" "match_devices $3 $1\n" "match_devices $2 $2\n" "match_devices $1 $3\n" @@ -937,10 +937,10 @@ TEST(10_SimpleSubCircuits) "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" "match_pins $2 $0\n" "match_pins $3 $2\n" - "match_pins $1 $3\n" - "match_pins $0 $1\n" "match_devices $2 $1\n" "match_devices $1 $2\n" "end_circuit INV INV MATCH\n" @@ -950,10 +950,10 @@ TEST(10_SimpleSubCircuits) "match_nets VSS VSS\n" "match_nets VDD VDD\n" "match_nets INT INT\n" - "match_pins $1 $0\n" "match_pins $0 $2\n" - "match_pins $3 $3\n" + "match_pins $1 $0\n" "match_pins $2 $1\n" + "match_pins $3 $3\n" "match_subcircuits $2 $1\n" "match_subcircuits $1 $2\n" "end_circuit TOP TOP MATCH" @@ -1004,10 +1004,10 @@ TEST(10_SimpleSubCircuitsMatchedNames) "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" "match_pins $2 $0\n" "match_pins $3 $2\n" - "match_pins $1 $3\n" - "match_pins $0 $1\n" "match_devices $2 $1\n" "match_devices $1 $2\n" "end_circuit INV INVB MATCH\n" @@ -1017,10 +1017,10 @@ TEST(10_SimpleSubCircuitsMatchedNames) "match_nets VSS VSS\n" "match_nets VDD VDD\n" "match_nets INT INT\n" - "match_pins $1 $0\n" "match_pins $0 $2\n" - "match_pins $3 $3\n" + "match_pins $1 $0\n" "match_pins $2 $1\n" + "match_pins $3 $3\n" "match_subcircuits $2 $1\n" "match_subcircuits $1 $2\n" "end_circuit TOP TOP MATCH" @@ -1068,10 +1068,10 @@ TEST(11_MismatchingSubcircuits) "match_nets IN IN\n" "net_mismatch VDD (null)\n" "net_mismatch (null) VDD\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" "pin_mismatch $2 (null)\n" "match_pins $3 $2\n" - "match_pins $1 $3\n" - "match_pins $0 $1\n" "pin_mismatch (null) $0\n" "device_mismatch $1 (null)\n" "match_devices $2 $1\n" @@ -1121,10 +1121,10 @@ TEST(12_MismatchingSubcircuitsDuplicates) "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" "match_pins $2 $0\n" "match_pins $3 $2\n" - "match_pins $1 $3\n" - "match_pins $0 $1\n" "match_devices $2 $1\n" "match_devices $1 $2\n" "end_circuit INV INV MATCH\n" @@ -1135,10 +1135,10 @@ TEST(12_MismatchingSubcircuitsDuplicates) "match_nets INT INT\n" "net_mismatch OUT (null)\n" "net_mismatch (null) OUT\n" - "pin_mismatch $1 (null)\n" "match_pins $0 $1\n" - "match_pins $3 $3\n" + "pin_mismatch $1 (null)\n" "match_pins $2 $2\n" + "match_pins $3 $3\n" "pin_mismatch (null) $0\n" "subcircuit_mismatch $2 (null)\n" "subcircuit_mismatch $3 (null)\n" @@ -1192,10 +1192,10 @@ TEST(13_MismatchingSubcircuitsAdditionalHierarchy) "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" "match_pins $2 $0\n" "match_pins $3 $2\n" - "match_pins $1 $3\n" - "match_pins $0 $1\n" "match_devices $2 $1\n" "match_devices $1 $2\n" "end_circuit INV INV MATCH\n" @@ -1205,10 +1205,10 @@ TEST(13_MismatchingSubcircuitsAdditionalHierarchy) "match_nets VSS VSS\n" "match_nets VDD VDD\n" "match_nets INT INT\n" - "match_pins $1 $0\n" "match_pins $0 $1\n" - "match_pins $3 $3\n" + "match_pins $1 $0\n" "match_pins $2 $2\n" + "match_pins $3 $3\n" "match_subcircuits $3 $1\n" "match_subcircuits $1 $2\n" "end_circuit TOP TOP MATCH" @@ -1261,11 +1261,11 @@ TEST(14_Subcircuit2Nand) "match_nets OUT OUT\n" "match_nets A A\n" "match_nets INT INT\n" - "match_pins $4 $4\n" - "match_pins $3 $3\n" + "match_pins $0 $0\n" "match_pins $1 $1\n" "match_pins $2 $2\n" - "match_pins $0 $0\n" + "match_pins $3 $3\n" + "match_pins $4 $4\n" "match_devices $1 $1\n" "match_devices $2 $2\n" "match_devices $3 $3\n" @@ -1276,13 +1276,13 @@ TEST(14_Subcircuit2Nand) "match_nets IN2 IN2\n" "match_nets VSS VSS\n" "match_nets VDD VDD\n" - "match_nets IN1 IN1\n" "match_nets INT INT\n" - "match_pins $2 $2\n" - "match_pins $1 $1\n" - "match_pins $4 $4\n" - "match_pins $3 $3\n" + "match_nets IN1 IN1\n" "match_pins $0 $0\n" + "match_pins $1 $1\n" + "match_pins $2 $2\n" + "match_pins $3 $3\n" + "match_pins $4 $4\n" "match_subcircuits $2 $1\n" "match_subcircuits $1 $2\n" "end_circuit TOP TOP MATCH" @@ -1335,11 +1335,11 @@ TEST(14_Subcircuit2NandMismatchNoSwap) "match_nets OUT OUT\n" "match_nets A A\n" "match_nets INT INT\n" - "match_pins $4 $4\n" - "match_pins $3 $3\n" + "match_pins $0 $0\n" "match_pins $1 $1\n" "match_pins $2 $2\n" - "match_pins $0 $0\n" + "match_pins $3 $3\n" + "match_pins $4 $4\n" "match_devices $1 $1\n" "match_devices $2 $2\n" "match_devices $3 $3\n" @@ -1347,22 +1347,22 @@ TEST(14_Subcircuit2NandMismatchNoSwap) "end_circuit NAND NAND MATCH\n" "begin_circuit TOP TOP\n" "match_nets OUT OUT\n" + "match_nets IN1 INT\n" "match_nets INT IN1\n" "match_nets VDD VDD\n" "match_nets VSS VSS\n" - "match_nets IN1 IN2\n" "net_mismatch IN2 (null)\n" - "net_mismatch (null) INT\n" - "match_pins $2 $2\n" + "net_mismatch (null) IN2\n" + "pin_mismatch $0 (null)\n" "pin_mismatch $1 (null)\n" - "match_pins $4 $4\n" + "match_pins $2 $2\n" "match_pins $3 $3\n" - "match_pins $0 $1\n" + "match_pins $4 $4\n" + "pin_mismatch (null) $1\n" "pin_mismatch (null) $0\n" "subcircuit_mismatch $1 (null)\n" - "subcircuit_mismatch (null) $1\n" + "match_subcircuits $2 $1\n" "subcircuit_mismatch (null) $2\n" - "subcircuit_mismatch $2 (null)\n" "end_circuit TOP TOP NOMATCH" ); @@ -1413,11 +1413,11 @@ TEST(14_Subcircuit2MatchWithSwap) "match_nets OUT OUT\n" "match_nets A A\n" "match_nets INT INT\n" - "match_pins $4 $4\n" - "match_pins $3 $3\n" + "match_pins $0 $0\n" "match_pins $1 $1\n" "match_pins $2 $2\n" - "match_pins $0 $0\n" + "match_pins $3 $3\n" + "match_pins $4 $4\n" "match_devices $1 $1\n" "match_devices $2 $2\n" "match_devices $3 $3\n" @@ -1427,14 +1427,14 @@ TEST(14_Subcircuit2MatchWithSwap) "match_nets OUT OUT\n" "match_nets IN2 IN2\n" "match_nets VSS VSS\n" - "match_nets INT INT\n" "match_nets VDD VDD\n" + "match_nets INT INT\n" "match_nets IN1 IN1\n" - "match_pins $2 $2\n" - "match_pins $1 $1\n" - "match_pins $4 $4\n" - "match_pins $3 $3\n" "match_pins $0 $0\n" + "match_pins $1 $1\n" + "match_pins $2 $2\n" + "match_pins $3 $3\n" + "match_pins $4 $4\n" "match_subcircuits $2 $1\n" "match_subcircuits $1 $2\n" "end_circuit TOP TOP MATCH" @@ -1488,21 +1488,21 @@ TEST(15_EmptySubCircuitTest) "match_nets $3 $3\n" "match_nets $2 $1\n" "match_nets $1 $2\n" - "match_pins D D\n" - "match_pins G G\n" "match_pins S S\n" + "match_pins G G\n" + "match_pins D D\n" "end_circuit TRANS TRANS MATCH\n" "begin_circuit INV2 INV2\n" "match_nets IN IN\n" + "match_nets OUT OUT\n" "match_nets $5 $5\n" "match_nets $4 $4\n" - "match_nets OUT OUT\n" "match_nets $2 $2\n" "match_pins IN IN\n" - "match_pins $4 $4\n" - "match_pins $3 $3\n" - "match_pins OUT OUT\n" "match_pins $1 $1\n" + "match_pins OUT OUT\n" + "match_pins $3 $3\n" + "match_pins $4 $4\n" "match_devices $1 $1\n" "match_devices $3 $2\n" "match_devices $2 $3\n" @@ -1516,3 +1516,116 @@ TEST(15_EmptySubCircuitTest) EXPECT_EQ (good, true); } + +TEST(16_UniqueSubCircuitMatching) +{ + const char *nls1 = + "circuit RINGO ();\n" + " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD);\n" + " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=$I22,$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD);\n" + " subcircuit INV2PAIR $3 (BULK='BULK,VSS',$2=$I23,$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD);\n" + " subcircuit INV2PAIR $4 (BULK='BULK,VSS',$2=$I24,$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD);\n" + " subcircuit INV2PAIR $5 (BULK='BULK,VSS',$2=$I25,$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD);\n" + "end;\n" + "circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1);\n" + " subcircuit INV2 $1 ($1=$I1,IN=$I3,$3=$I7,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK);\n" + " subcircuit INV2 $2 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK);\n" + "end;\n" + "circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK);\n" + " device PMOS4 $1 (S=$3,G=IN,D=VDD,B=$1) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS4 $2 (S=VDD,G=$3,D=OUT,B=$1) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS4 $3 (S=$3,G=IN,D=VSS,B=BULK) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS4 $4 (S=VSS,G=$3,D=OUT,B=BULK) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + "end;\n"; + const char *nls2 = + "circuit RINGO ();\n" + " subcircuit INV2PAIR $1 (BULK='BULK,VSS',$2=FB,$3=VDD,$4='BULK,VSS',$5=$I7,$6=OSC,$7=VDD);\n" + " subcircuit INV2PAIR $2 (BULK='BULK,VSS',$2=$I24,$3=VDD,$4='BULK,VSS',$5=$I5,$6=$I6,$7=VDD);\n" + " subcircuit INV2PAIR $3 (BULK='BULK,VSS',$2=$I23,$3=VDD,$4='BULK,VSS',$5=$I13,$6=$I5,$7=VDD);\n" + " subcircuit INV2PAIR $4 (BULK='BULK,VSS',$2=$I22,$3=VDD,$4='BULK,VSS',$5=FB,$6=$I13,$7=VDD);\n" + " subcircuit INV2PAIR $5 (BULK='BULK,VSS',$2=$I25,$3=VDD,$4='BULK,VSS',$5=$I6,$6=$I7,$7=VDD);\n" + "end;\n" + "circuit INV2PAIR (BULK=BULK,$2=$I8,$3=$I6,$4=$I5,$5=$I3,$6=$I2,$7=$I1);\n" + " subcircuit INV2 $1 ($1=$I1,IN=$I4,$3=$I8,OUT=$I2,VSS=$I5,VDD=$I6,BULK=BULK);\n" + " subcircuit INV2 $2 ($1=$I1,IN=$I3,$3=$I7,OUT=$I4,VSS=$I5,VDD=$I6,BULK=BULK);\n" + "end;\n" + "circuit INV2 ($1=$1,IN=IN,$3=$3,OUT=OUT,VSS=VSS,VDD=VDD,BULK=BULK);\n" + " device PMOS4 $1 (S=$3,G=IN,D=VDD,B=$1) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS4 $2 (S=$3,G=IN,D=VSS,B=BULK) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS4 $3 (S=VDD,G=$3,D=OUT,B=$1) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS4 $4 (S=VSS,G=$3,D=OUT,B=BULK) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit INV2 INV2\n" + "match_nets VDD VDD\n" + "match_nets $1 $1\n" + "match_nets VSS VSS\n" + "match_nets BULK BULK\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_nets $3 $3\n" + "match_pins $0 $0\n" + "match_pins IN IN\n" + "match_pins $2 $2\n" + "match_pins OUT OUT\n" + "match_pins VSS VSS\n" + "match_pins VDD VDD\n" + "match_pins BULK BULK\n" + "match_devices $1 $1\n" + "match_devices $3 $2\n" + "match_devices $2 $3\n" + "match_devices $4 $4\n" + "end_circuit INV2 INV2 MATCH\n" + "begin_circuit INV2PAIR INV2PAIR\n" + "match_nets $I2 $I2\n" + "match_nets $I3 $I3\n" + "match_nets BULK BULK\n" + "match_nets $I6 $I6\n" + "match_nets $I5 $I5\n" + "match_nets $I4 $I4\n" + "match_nets $I1 $I1\n" + "match_nets $I7 $I7\n" + "match_nets $I8 $I8\n" + "match_pins BULK BULK\n" + "match_pins $1 $1\n" + "match_pins $2 $2\n" + "match_pins $3 $3\n" + "match_pins $4 $4\n" + "match_pins $5 $5\n" + "match_pins $6 $6\n" + "match_subcircuits $2 $1\n" + "match_subcircuits $1 $2\n" + "end_circuit INV2PAIR INV2PAIR MATCH\n" + "begin_circuit RINGO RINGO\n" + "match_nets OSC OSC\n" + "match_nets FB FB\n" + "match_nets VDD VDD\n" + "match_nets BULK,VSS BULK,VSS\n" + "match_nets $I22 $I22\n" + "match_nets $I13 $I13\n" + "match_nets $I7 $I7\n" + "match_nets $I23 $I23\n" + "match_nets $I5 $I5\n" + "match_nets $I25 $I25\n" + "match_nets $I24 $I24\n" + "match_nets $I6 $I6\n" + "match_subcircuits $1 $1\n" + "match_subcircuits $4 $2\n" + "match_subcircuits $3 $3\n" + "match_subcircuits $2 $4\n" + "match_subcircuits $5 $5\n" + "end_circuit RINGO RINGO MATCH" + ); + + EXPECT_EQ (good, true); +} diff --git a/testdata/algo/device_extract_l6.gds b/testdata/algo/device_extract_l6.gds index 73ece855b6fadc7120d50dc3f5ce3073412e95f1..6fbeeb989217480ef9429f47f1c557c3f0922771 100644 GIT binary patch delta 201 zcmX@7bWTZ$fsKKQDS|gD`^;gA_7*qLQ)GHt9j2VaV=nAFm`2N0MRxd x7#L(35i~@6au#2l5?2ocgOMaq8ITSNW?+!3K+q8J$*=gFnD}>Y*5g-T0sw8LD delta 202 zcmX@7bWTZ$fsKKQDS|gA6izqLQ) Date: Mon, 1 Apr 2019 22:46:33 +0200 Subject: [PATCH 24/54] WIP: Simple SPICE reader. --- src/db/db/db.pro | 8 +- src/db/db/dbNetlistReader.cc | 31 ++ src/db/db/dbNetlistReader.h | 68 +++ src/db/db/dbNetlistSpiceReader.cc | 588 ++++++++++++++++++++++ src/db/db/dbNetlistSpiceReader.h | 96 ++++ src/db/db/dbNetlistWriter.h | 22 - src/db/db/gsiDeclDbNetlist.cc | 40 ++ src/db/unit_tests/dbNetlistReaderTests.cc | 125 +++++ src/db/unit_tests/unit_tests.pro | 3 +- src/tl/tl/tlString.cc | 71 ++- src/tl/tl/tlString.h | 7 + src/tl/unit_tests/tlString.cc | 27 + testdata/algo/nreader1.cir | 17 + testdata/algo/nreader2.cir | 83 +++ testdata/algo/nreader3.cir | 27 + 15 files changed, 1176 insertions(+), 37 deletions(-) create mode 100644 src/db/db/dbNetlistReader.cc create mode 100644 src/db/db/dbNetlistReader.h create mode 100644 src/db/db/dbNetlistSpiceReader.cc create mode 100644 src/db/db/dbNetlistSpiceReader.h create mode 100644 src/db/unit_tests/dbNetlistReaderTests.cc create mode 100644 testdata/algo/nreader1.cir create mode 100644 testdata/algo/nreader2.cir create mode 100644 testdata/algo/nreader3.cir diff --git a/src/db/db/db.pro b/src/db/db/db.pro index 1ff2c1882..cd1acda6e 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -173,7 +173,9 @@ SOURCES = \ dbRegionUtils.cc \ dbEdgesUtils.cc \ dbRegionProcessors.cc \ - dbNetlistCompare.cc + dbNetlistCompare.cc \ + dbNetlistReader.cc \ + dbNetlistSpiceReader.cc HEADERS = \ dbArray.h \ @@ -312,7 +314,9 @@ HEADERS = \ dbEdgesUtils.h \ dbRegionProcessors.h \ gsiDeclDbHelpers.h \ - dbNetlistCompare.h + dbNetlistCompare.h \ + dbNetlistReader.h \ + dbNetlistSpiceReader.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbNetlistReader.cc b/src/db/db/dbNetlistReader.cc new file mode 100644 index 000000000..ee4525e62 --- /dev/null +++ b/src/db/db/dbNetlistReader.cc @@ -0,0 +1,31 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "dbNetlistReader.h" + +namespace db +{ + + // .. nothing yet .. + +} diff --git a/src/db/db/dbNetlistReader.h b/src/db/db/dbNetlistReader.h new file mode 100644 index 000000000..117793fa5 --- /dev/null +++ b/src/db/db/dbNetlistReader.h @@ -0,0 +1,68 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 + +*/ + +#ifndef HDR_dbNetlistReader +#define HDR_dbNetlistReader + +#include "dbCommon.h" +#include "tlTypeTraits.h" + +#include + +namespace tl +{ + class InputStream; +} + +namespace db +{ + +class Netlist; + +/** + * @brief A common base class for netlist writers + */ +class DB_PUBLIC NetlistReader +{ +public: + NetlistReader () { } + virtual ~NetlistReader () { } + + virtual void read (tl::InputStream &stream, db::Netlist &netlist) = 0; +}; + +} + +namespace tl +{ + +template <> +struct type_traits + : public tl::type_traits +{ + typedef tl::false_tag has_default_constructor; + typedef tl::false_tag has_copy_constructor; +}; + +} + +#endif diff --git a/src/db/db/dbNetlistSpiceReader.cc b/src/db/db/dbNetlistSpiceReader.cc new file mode 100644 index 000000000..32e62e9db --- /dev/null +++ b/src/db/db/dbNetlistSpiceReader.cc @@ -0,0 +1,588 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "dbNetlistSpiceReader.h" +#include "dbNetlist.h" +#include "dbNetlistDeviceClasses.h" + +#include "tlStream.h" +#include "tlLog.h" + +#include +#include + +namespace db +{ + +NetlistSpiceReader::NetlistSpiceReader () + : mp_netlist (0), mp_stream (0) +{ + // .. nothing yet .. +} + +NetlistSpiceReader::~NetlistSpiceReader () +{ + // .. nothing yet .. +} + +void NetlistSpiceReader::read (tl::InputStream &stream, db::Netlist &netlist) +{ + mp_stream.reset (new tl::TextInputStream (stream)); + mp_netlist = &netlist; + mp_circuit = 0; + + try { + + while (! at_end ()) { + read_element (); + } + + finish (); + + } catch (tl::Exception &ex) { + + // NOTE: because we do a peek to capture the "+" line continuation character, we're + // one line ahead. + std::string fmt_msg = tl::sprintf ("%s in %s, line %d", ex.msg (), mp_stream->source (), mp_stream->line_number () - 1); + finish (); + throw tl::Exception (fmt_msg); + + } catch (...) { + + finish (); + throw; + + } +} + +void NetlistSpiceReader::finish () +{ + while (! m_streams.empty ()) { + pop_stream (); + } + + mp_stream.reset (0); + mp_netlist = 0; + mp_circuit = 0; +} + +void NetlistSpiceReader::push_stream (const std::string &path) +{ + tl::InputStream *istream = new tl::InputStream (path); + m_streams.push_back (std::make_pair (istream, mp_stream.release ())); + mp_stream.reset (new tl::TextInputStream (*istream)); +} + +void NetlistSpiceReader::pop_stream () +{ + if (! m_streams.empty ()) { + + mp_stream.reset (m_streams.back ().second); + delete m_streams.back ().first; + + m_streams.pop_back (); + + } +} + +bool NetlistSpiceReader::at_end () +{ + return mp_stream->at_end () && m_streams.empty (); +} + +std::string NetlistSpiceReader::get_line () +{ + if (! m_stored_line.empty ()) { + std::string l; + l.swap (m_stored_line); + return l; + } + + std::string l; + + do { + + while (mp_stream->at_end ()) { + if (m_streams.empty ()) { + return std::string (); + } + pop_stream (); + } + + l = mp_stream->get_line (); + while (! mp_stream->at_end () && mp_stream->peek_char () == '+') { + mp_stream->get_char (); + l += mp_stream->get_line (); + } + + tl::Extractor ex (l.c_str ()); + if (ex.test_without_case (".include")) { + + std::string path; + ex.read_word_or_quoted (path, "_.:,!+$/\\"); + + push_stream (path); + + l.clear (); + + } else if (ex.at_end () || ex.test ("*")) { + l.clear (); + } + + } while (l.empty ()); + + return l; +} + +void NetlistSpiceReader::unget_line (const std::string &l) +{ + m_stored_line = l; +} + +bool NetlistSpiceReader::read_element () +{ + std::string l = get_line (); + if (l.empty ()) { + return false; + } + + tl::Extractor ex (l.c_str ()); + + const char *res_device_class_name = "RES"; + const char *cap_device_class_name = "CAP"; + const char *ind_device_class_name = "IND"; + + if (ex.test_without_case (".")) { + + // control statement + if (ex.test_without_case ("model")) { + + // ignore model statements + + } else if (ex.test_without_case ("subckt")) { + + read_circuit (ex); + + } else if (ex.test_without_case ("ends")) { + + return true; + + } else if (ex.test_without_case ("end")) { + + // ignore end statements + + } else { + + std::string s; + ex.read_word (s); + s = tl::to_lower_case (s); + warn (tl::to_string (tr ("Control statement ignored: ")) + s); + + } + + } else if (ex.test_without_case ("r")) { + + db::DeviceClass *dev_cls = mp_netlist->device_class_by_name (res_device_class_name); + if (! dev_cls) { + dev_cls = new db::DeviceClassResistor (); + dev_cls->set_name (res_device_class_name); + mp_netlist->add_device_class (dev_cls); + } + + ensure_circuit (); + read_device (dev_cls, db::DeviceClassResistor::param_id_R, ex); + + } else if (ex.test_without_case ("c")) { + + db::DeviceClass *dev_cls = mp_netlist->device_class_by_name (cap_device_class_name); + if (! dev_cls) { + dev_cls = new db::DeviceClassCapacitor (); + dev_cls->set_name (cap_device_class_name); + mp_netlist->add_device_class (dev_cls); + } + + ensure_circuit (); + read_device (dev_cls, db::DeviceClassCapacitor::param_id_C, ex); + + } else if (ex.test_without_case ("l")) { + + db::DeviceClass *dev_cls = mp_netlist->device_class_by_name (ind_device_class_name); + if (! dev_cls) { + dev_cls = new db::DeviceClassInductor (); + dev_cls->set_name (ind_device_class_name); + mp_netlist->add_device_class (dev_cls); + } + + ensure_circuit (); + read_device (dev_cls, db::DeviceClassInductor::param_id_L, ex); + + } else if (ex.test_without_case ("m")) { + + ensure_circuit (); + read_mos4_device (ex); + + } else if (ex.test_without_case ("x")) { + + ensure_circuit (); + read_subcircuit (ex); + + } else { + + char c = *ex.skip (); + if (c) { + warn (tl::sprintf (tl::to_string (tr ("Element type '%c' ignored")), c)); + } + + } + + return false; +} + +void NetlistSpiceReader::error (const std::string &msg) +{ + throw tl::Exception (msg); +} + +void NetlistSpiceReader::warn (const std::string &msg) +{ + std::string fmt_msg = tl::sprintf ("%s in %s, line %d", msg, mp_stream->source (), mp_stream->line_number ()); + tl::warn << fmt_msg; +} + +double NetlistSpiceReader::read_atomic_value (tl::Extractor &ex) +{ + if (ex.test ("(")) { + + double v = read_dot_expr (ex); + ex.expect (")"); + return v; + + } else { + + double v = 0.0; + ex.read (v); + + double f = 1.0; + if (*ex == 't' || *ex == 'T') { + f = 1e12; + } else if (*ex == 'g' || *ex == 'G') { + f = 1e9; + } else if (*ex == 'k' || *ex == 'K') { + f = 1e3; + } else if (*ex == 'm' || *ex == 'M') { + f = 1e-3; + if (ex.test_without_case ("meg")) { + f = 1e6; + } + } else if (*ex == 'u' || *ex == 'U') { + f = 1e-6; + } else if (*ex == 'n' || *ex == 'N') { + f = 1e-9; + } else if (*ex == 'p' || *ex == 'P') { + f = 1e-12; + } else if (*ex == 'f' || *ex == 'F') { + f = 1e-15; + } else if (*ex == 'a' || *ex == 'A') { + f = 1e-18; + } + while (*ex && isalpha (*ex)) { + ++ex; + } + + v *= f; + return v; + + } +} + +double NetlistSpiceReader::read_bar_expr (tl::Extractor &ex) +{ + double v = read_atomic_value (ex); + while (true) { + if (ex.test ("+")) { + double vv = read_atomic_value (ex); + v += vv; + } else if (ex.test ("+")) { + double vv = read_atomic_value (ex); + v -= vv; + } else { + break; + } + } + return v; +} + +double NetlistSpiceReader::read_dot_expr (tl::Extractor &ex) +{ + double v = read_atomic_value (ex); + while (true) { + if (ex.test ("*")) { + double vv = read_atomic_value (ex); + v *= vv; + } else if (ex.test ("/")) { + double vv = read_atomic_value (ex); + v /= vv; + } else { + break; + } + } + return v; +} + +double NetlistSpiceReader::read_value (tl::Extractor &ex) +{ + return read_dot_expr (ex); +} + +void NetlistSpiceReader::ensure_circuit () +{ + if (! mp_circuit) { + + mp_circuit = new db::Circuit (); + // TODO: make top name configurable + mp_circuit->set_name (".TOP"); + mp_netlist->add_circuit (mp_circuit); + + } +} + +db::Net *NetlistSpiceReader::make_net (const std::string &name) +{ + db::Net *net = mp_circuit->net_by_name (name); + if (! net) { + net = new db::Net (); + net->set_name (name); + mp_circuit->add_net (net); + } + return net; +} + +void NetlistSpiceReader::read_subcircuit (tl::Extractor &ex) +{ + std::string sc_name; + ex.read_word_or_quoted (sc_name, "_.:,!+$/\\"); + + std::vector nn; + std::map pv; + + while (! ex.at_end ()) { + + std::string n; + ex.read_word_or_quoted (n, "_.:,!+$/\\"); + + if (ex.test ("=")) { + // a parameter + pv.insert (std::make_pair (tl::to_upper_case (n), read_value (ex))); + } else { + nn.push_back (n); + } + + } + + if (nn.empty ()) { + error (tl::to_string (tr ("No circuit name given for subcircuit call"))); + } + if (! pv.empty ()) { + warn (tl::to_string (tr ("Circuit parameters are not allowed currently"))); + } + + std::string nc = nn.back (); + nn.pop_back (); + + if (nn.empty ()) { + error (tl::to_string (tr ("A circuit call needs at least one net"))); + } + + db::Circuit *cc = mp_netlist->circuit_by_name (nc); + if (! cc) { + cc = new db::Circuit (); + mp_netlist->add_circuit (cc); + cc->set_name (nc); + for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { + cc->add_pin (std::string ()); + } + } else { + if (cc->pin_count () != nn.size ()) { + error (tl::sprintf (tl::to_string (tr ("Pin count mismatch between circuit definition and circuit call: %d expected, got %d")), int (cc->pin_count ()), int (nn.size ()))); + } + } + + db::SubCircuit *sc = new db::SubCircuit (cc, sc_name); + mp_circuit->add_subcircuit (sc); + + for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { + db::Net *net = make_net (*i); + sc->connect_pin (i - nn.begin (), net); + } + + ex.expect_end (); +} + +void NetlistSpiceReader::read_circuit (tl::Extractor &ex) +{ + std::string nc; + ex.read_word_or_quoted (nc, "_.:,!+$/\\"); + + std::vector nn; + std::map pv; + + while (! ex.at_end ()) { + + std::string n; + ex.read_word_or_quoted (n, "_.:,!+$/\\"); + + if (ex.test ("=")) { + // a parameter + pv.insert (std::make_pair (tl::to_upper_case (n), read_value (ex))); + } else { + nn.push_back (n); + } + + } + + if (! pv.empty ()) { + warn (tl::to_string (tr ("Circuit parameters are not allowed currently"))); + } + + db::Circuit *cc = mp_netlist->circuit_by_name (nc); + if (! cc) { + cc = new db::Circuit (); + mp_netlist->add_circuit (cc); + cc->set_name (nc); + for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { + cc->add_pin (std::string ()); + } + } else { + if (cc->pin_count () != nn.size ()) { + error (tl::sprintf (tl::to_string (tr ("Pin count mismatch between implicit (through call) and explicit circuit definition: %d expected, got %d")), int (cc->pin_count ()), int (nn.size ()))); + } + } + + std::swap (cc, mp_circuit); + + for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { + db::Net *net = make_net (*i); + mp_circuit->connect_pin (i - nn.begin (), net); + } + + while (! at_end ()) { + if (read_element ()) { + break; + } + } + + std::swap (cc, mp_circuit); + + ex.expect_end (); +} + +void NetlistSpiceReader::read_device (db::DeviceClass *dev_cls, size_t param_id, tl::Extractor &ex) +{ + std::string dn; + ex.read_word_or_quoted (dn, "_.:,!+$/\\"); + + std::vector nn; + + while (! ex.at_end () && nn.size () < 2) { + nn.push_back (std::string ()); + ex.read_word_or_quoted (nn.back (), "_.:,!+$/\\"); + } + + if (nn.size () != 2) { + error (tl::to_string (tr ("Two-terminal device needs two nets"))); + } + + double v = read_value (ex); + + db::Device *dev = new db::Device (dev_cls, dn); + mp_circuit->add_device (dev); + + for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { + db::Net *net = make_net (*i); + dev->connect_terminal (i - nn.begin (), net); + } + + dev->set_parameter_value (param_id, v); + + ex.expect_end (); +} + +void NetlistSpiceReader::read_mos4_device (tl::Extractor &ex) +{ + std::string dn; + ex.read_word_or_quoted (dn, "_.:,!+$/\\"); + + std::vector nn; + std::map pv; + + while (! ex.at_end ()) { + + std::string n; + ex.read_word_or_quoted (n, "_.:,!+$/\\"); + + if (ex.test ("=")) { + // a parameter + pv.insert (std::make_pair (tl::to_upper_case (n), read_value (ex))); + } else { + nn.push_back (n); + } + + } + + if (nn.empty ()) { + error (tl::to_string (tr ("No model name given for MOS transistor element"))); + } + + std::string mn = nn.back (); + nn.pop_back (); + + if (nn.size () != 4) { + error (tl::to_string (tr ("A MOS transistor needs four nets"))); + } + + db::DeviceClass *dev_cls = mp_netlist->device_class_by_name (mn); + if (! dev_cls) { + dev_cls = new db::DeviceClassMOS4Transistor (); + dev_cls->set_name (mn); + mp_netlist->add_device_class (dev_cls); + } + + db::Device *dev = new db::Device (dev_cls, dn); + mp_circuit->add_device (dev); + + for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { + db::Net *net = make_net (*i); + dev->connect_terminal (i - nn.begin (), net); + } + + const std::vector &pd = dev_cls->parameter_definitions (); + for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + std::map::const_iterator v = pv.find (i->name ()); + if (v != pv.end ()) { + dev->set_parameter_value (i->id (), v->second); + } + } + + ex.expect_end (); +} + +} diff --git a/src/db/db/dbNetlistSpiceReader.h b/src/db/db/dbNetlistSpiceReader.h new file mode 100644 index 000000000..08d3991a1 --- /dev/null +++ b/src/db/db/dbNetlistSpiceReader.h @@ -0,0 +1,96 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 + +*/ + +#ifndef HDR_dbNetlistSpiceReader +#define HDR_dbNetlistSpiceReader + +#include "dbCommon.h" +#include "dbNetlistReader.h" +#include "tlStream.h" + +#include +#include + +namespace db +{ + +class Netlist; +class Net; +class Circuit; +class DeviceClass; + +/** + * @brief A SPICE format reader for netlists + */ +class DB_PUBLIC NetlistSpiceReader + : public NetlistReader +{ +public: + NetlistSpiceReader (); + virtual ~NetlistSpiceReader (); + + virtual void read (tl::InputStream &stream, db::Netlist &netlist); + +private: + db::Netlist *mp_netlist; + db::Circuit *mp_circuit; + std::auto_ptr mp_stream; + std::vector > m_streams; + std::string m_stored_line; + + void push_stream (const std::string &path); + void pop_stream (); + bool at_end (); + void read_subcircuit (tl::Extractor &ex); + void read_circuit (tl::Extractor &ex); + void read_device (db::DeviceClass *dev_cls, size_t param_id, tl::Extractor &ex); + void read_mos4_device (tl::Extractor &ex); + bool read_element (); + double read_value (tl::Extractor &ex); + double read_atomic_value (tl::Extractor &ex); + double read_dot_expr (tl::Extractor &ex); + double read_bar_expr (tl::Extractor &ex); + std::string get_line (); + void unget_line (const std::string &l); + void error (const std::string &msg); + void warn (const std::string &msg); + void finish (); + db::Net *make_net (const std::string &name); + void ensure_circuit (); +}; + +} + +namespace tl +{ + +template <> +struct type_traits + : public tl::type_traits +{ + typedef tl::false_tag has_default_constructor; + typedef tl::false_tag has_copy_constructor; +}; + +} + +#endif diff --git a/src/db/db/dbNetlistWriter.h b/src/db/db/dbNetlistWriter.h index a97f1fd2c..cdbac98cd 100644 --- a/src/db/db/dbNetlistWriter.h +++ b/src/db/db/dbNetlistWriter.h @@ -1,26 +1,4 @@ -/* - - KLayout Layout Viewer - Copyright (C) 2006-2019 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 - -*/ - - /* KLayout Layout Viewer diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 5dc648258..173ea6336 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -24,6 +24,8 @@ #include "dbNetlist.h" #include "dbNetlistWriter.h" #include "dbNetlistSpiceWriter.h" +#include "dbNetlistReader.h" +#include "dbNetlistSpiceReader.h" #include "tlException.h" #include "tlInternational.h" #include "tlStream.h" @@ -943,6 +945,13 @@ static void write_netlist (const db::Netlist *nl, const std::string &file, db::N writer->write (os, *nl, description); } +static void read_netlist (db::Netlist *nl, const std::string &file, db::NetlistReader *reader) +{ + tl_assert (reader != 0); + tl::InputStream os (file); + reader->read (os, *nl); +} + Class decl_dbNetlist ("db", "Netlist", gsi::method_ext ("add", &gsi::add_circuit, gsi::arg ("circuit"), "@brief Adds the circuit to the netlist\n" @@ -1024,6 +1033,10 @@ 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_ext ("read", &read_netlist, gsi::arg ("file"), gsi::arg ("reader"), + "@brief Writes the netlist to the given file using the given reader object to parse the file\n" + "See \\NetlistSpiceReader for an example for a parser. " + ) + gsi::method_ext ("write", &write_netlist, gsi::arg ("file"), gsi::arg ("writer"), gsi::arg ("description", std::string ()), "@brief Writes the netlist to the given file using the given writer object to format the file\n" "See \\NetlistSpiceWriter for an example for a formatter. " @@ -1255,4 +1268,31 @@ Class db_NetlistSpiceWriter (db_NetlistWriter, "db", "Ne "This class has been introduced in version 0.26." ); +Class db_NetlistReader ("db", "NetlistReader", + gsi::Methods (), + "@hide\n" +); + +db::NetlistSpiceReader *new_spice_reader () +{ + return new db::NetlistSpiceReader (); +} + +Class db_NetlistSpiceReader (db_NetlistReader, "db", "NetlistSpiceReader", + gsi::constructor ("new", &new_spice_reader, + "@brief Creates a new reader.\n" + ), + "@brief Implements a netlist Reader for the SPICE format.\n" + "Use the SPICE reader like this:\n" + "\n" + "@code\n" + "writer = RBA::NetlistSpiceReader::new\n" + "netlist = RBA::Netlist::new\n" + "netlist.read(path, reader)\n" + "@endcode\n" + "\n" + "This class has been introduced in version 0.26." +); + + } diff --git a/src/db/unit_tests/dbNetlistReaderTests.cc b/src/db/unit_tests/dbNetlistReaderTests.cc new file mode 100644 index 000000000..25e50da57 --- /dev/null +++ b/src/db/unit_tests/dbNetlistReaderTests.cc @@ -0,0 +1,125 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "dbNetlistSpiceReader.h" +#include "dbNetlist.h" +#include "dbNetlistDeviceClasses.h" + +#include "tlUnitTest.h" +#include "tlStream.h" +#include "tlFileUtils.h" + +TEST(1_BasicReader) +{ + db::Netlist nl; + + std::string path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nreader1.cir"); + + db::NetlistSpiceReader reader; + tl::InputStream is (path); + reader.read (is, nl); + + EXPECT_EQ (nl.to_string (), + "circuit TOP ();\n" + " device RES $1 (A='6',B='1') (R=7650);\n" + " device RES $2 (A='3',B='1') (R=7650);\n" + " device RES $3 (A='3',B='2') (R=2670);\n" + " device MHVPMOS $4 (S='6',G='4',D='7',B='7') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=3.84e-06,PD=3.84e-06);\n" + "end;\n" + ); +} + +TEST(2_ReaderWithSubcircuits) +{ + db::Netlist nl; + + std::string path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nreader2.cir"); + + db::NetlistSpiceReader reader; + tl::InputStream is (path); + reader.read (is, nl); + + EXPECT_EQ (nl.to_string (), + "circuit RINGO ($1='11',$2='12',$3='13',$4='14',$5='15');\n" + " subcircuit ND2X1 $1 ($1='12',$2='1',$3='15',$4='12',$5='11',$6='14',$7='15');\n" + " subcircuit INVX1 $2 ($1='12',$2='2',$3='15',$4='12',$5='1',$6='15');\n" + " subcircuit INVX1 $3 ($1='12',$2='3',$3='15',$4='12',$5='2',$6='15');\n" + " subcircuit INVX1 $4 ($1='12',$2='4',$3='15',$4='12',$5='3',$6='15');\n" + " subcircuit INVX1 $5 ($1='12',$2='5',$3='15',$4='12',$5='4',$6='15');\n" + " subcircuit INVX1 $6 ($1='12',$2='6',$3='15',$4='12',$5='5',$6='15');\n" + " subcircuit INVX1 $7 ($1='12',$2='7',$3='15',$4='12',$5='6',$6='15');\n" + " subcircuit INVX1 $8 ($1='12',$2='8',$3='15',$4='12',$5='7',$6='15');\n" + " subcircuit INVX1 $9 ($1='12',$2='9',$3='15',$4='12',$5='8',$6='15');\n" + " subcircuit INVX1 $10 ($1='12',$2='10',$3='15',$4='12',$5='9',$6='15');\n" + " subcircuit INVX1 $11 ($1='12',$2='11',$3='15',$4='12',$5='10',$6='15');\n" + " subcircuit INVX1 $12 ($1='12',$2='13',$3='15',$4='12',$5='11',$6='15');\n" + "end;\n" + "circuit ND2X1 ($1='1',$2='2',$3='3',$4='4',$5='5',$6='6',$7='7');\n" + " device MLVPMOS $1 (S='2',G='6',D='1',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=3.85e-06,PD=1.95e-06);\n" + " device MLVPMOS $2 (S='1',G='5',D='2',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=1.95e-06,PD=3.85e-06);\n" + " device MLVNMOS $3 (S='3',G='6',D='8',B='7') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=2.75e-06,PD=1.4e-06);\n" + " device MLVNMOS $4 (S='8',G='5',D='2',B='7') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=1.4e-06,PD=2.75e-06);\n" + "end;\n" + "circuit INVX1 ($1='1',$2='2',$3='3',$4='4',$5='5',$6='6');\n" + " device MLVPMOS $1 (S='1',G='5',D='2',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=3.85e-06,PD=3.85e-06);\n" + " device MLVNMOS $2 (S='3',G='5',D='2',B='6') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=2.75e-06,PD=2.75e-06);\n" + "end;\n" + ); +} + +TEST(3_ReaderWithSubcircuitsAltOrder) +{ + db::Netlist nl; + + std::string path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nreader3.cir"); + + db::NetlistSpiceReader reader; + tl::InputStream is (path); + reader.read (is, nl); + + EXPECT_EQ (nl.to_string (), + "circuit INVX1 ($1='1',$2='2',$3='3',$4='4',$5='5',$6='6');\n" + " device MLVPMOS $1 (S='1',G='5',D='2',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=0,PD=0);\n" + " device MLVNMOS $2 (S='3',G='5',D='2',B='6') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + "circuit ND2X1 ($1='1',$2='2',$3='3',$4='4',$5='5',$6='6',$7='7');\n" + " device MLVPMOS $1 (S='2',G='6',D='1',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=0,PD=0);\n" + " device MLVPMOS $2 (S='1',G='5',D='2',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=0,PD=0);\n" + " device MLVNMOS $3 (S='3',G='6',D='8',B='7') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=0,PD=0);\n" + " device MLVNMOS $4 (S='8',G='5',D='2',B='7') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + "circuit RINGO ($1='11',$2='12',$3='13',$4='14',$5='15');\n" + " subcircuit ND2X1 $1 ($1='12',$2='1',$3='15',$4='12',$5='11',$6='14',$7='15');\n" + " subcircuit INVX1 $2 ($1='12',$2='2',$3='15',$4='12',$5='1',$6='15');\n" + " subcircuit INVX1 $3 ($1='12',$2='3',$3='15',$4='12',$5='2',$6='15');\n" + " subcircuit INVX1 $4 ($1='12',$2='4',$3='15',$4='12',$5='3',$6='15');\n" + " subcircuit INVX1 $5 ($1='12',$2='5',$3='15',$4='12',$5='4',$6='15');\n" + " subcircuit INVX1 $6 ($1='12',$2='6',$3='15',$4='12',$5='5',$6='15');\n" + " subcircuit INVX1 $7 ($1='12',$2='7',$3='15',$4='12',$5='6',$6='15');\n" + " subcircuit INVX1 $8 ($1='12',$2='8',$3='15',$4='12',$5='7',$6='15');\n" + " subcircuit INVX1 $9 ($1='12',$2='9',$3='15',$4='12',$5='8',$6='15');\n" + " subcircuit INVX1 $10 ($1='12',$2='10',$3='15',$4='12',$5='9',$6='15');\n" + " subcircuit INVX1 $11 ($1='12',$2='11',$3='15',$4='12',$5='10',$6='15');\n" + " subcircuit INVX1 $12 ($1='12',$2='13',$3='15',$4='12',$5='11',$6='15');\n" + "end;\n" + ); +} diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index b5eded222..39a1196b1 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -70,7 +70,8 @@ SOURCES = \ dbCellVariantsTests.cc \ dbDeepEdgesTests.cc \ dbDeepEdgePairsTests.cc \ - dbNetlistCompareTests.cc + dbNetlistCompareTests.cc \ + dbNetlistReaderTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc index ae5420eed..4c9285b99 100644 --- a/src/tl/tl/tlString.cc +++ b/src/tl/tl/tlString.cc @@ -42,7 +42,7 @@ namespace tl static std::locale c_locale ("C"); // ------------------------------------------------------------------------- -// lower and upper case for wchar_t +// lower and upper case for wchar_t and uint32_t #include "utf_casefolding.h" @@ -66,9 +66,44 @@ wchar_t wupcase (wchar_t c) } } +uint32_t utf32_downcase (uint32_t c32) +{ + if (sizeof (wchar_t) == 2 && c32 >= 0x10000) { + return c32; + } else { + return uint32_t (wdowncase (wchar_t (c32))); + } +} + +uint32_t utf32_upcase (uint32_t c32) +{ + if (sizeof (wchar_t) == 2 && c32 >= 0x10000) { + return c32; + } else { + return uint32_t (wupcase (wchar_t (c32))); + } +} + // ------------------------------------------------------------------------- // Conversion of UTF8 to wchar_t +uint32_t utf32_from_utf8 (const char *&cp, const char *cpe = 0) +{ + uint32_t c32 = (unsigned char) *cp++; + if (c32 >= 0xf0 && ((cpe && cp + 2 < cpe) || (! cpe && cp [0] && cp [1] && cp [2]))) { + c32 = ((c32 & 0x7) << 18) | ((uint32_t (cp [0]) & 0x3f) << 12) | ((uint32_t (cp [1]) & 0x3f) << 6) | (uint32_t (cp [2]) & 0x3f); + cp += 3; + } else if (c32 >= 0xe0 && ((cpe && cp + 1 < cpe) || (! cpe && cp [0] && cp [1]))) { + c32 = ((c32 & 0xf) << 12) | ((uint32_t (cp [0]) & 0x3f) << 6) | (uint32_t (cp [1]) & 0x3f); + cp += 2; + } else if (c32 >= 0xc0 && ((cpe && cp < cpe) || (! cpe && cp [0]))) { + c32 = ((c32 & 0x1f) << 6) | (uint32_t (*cp) & 0x3f); + ++cp; + } + + return c32; +} + std::wstring to_wstring (const std::string &s) { std::wstring ws; @@ -76,17 +111,7 @@ std::wstring to_wstring (const std::string &s) const char *cpe = s.c_str () + s.size (); for (const char *cp = s.c_str (); cp < cpe; ) { - uint32_t c32 = (unsigned char) *cp++; - if (c32 >= 0xf0 && cp + 2 < cpe) { - c32 = ((c32 & 0x7) << 18) | ((uint32_t (cp [0]) & 0x3f) << 12) | ((uint32_t (cp [1]) & 0x3f) << 6) | (uint32_t (cp [2]) & 0x3f); - cp += 3; - } else if (c32 >= 0xe0 && cp + 1 < cpe) { - c32 = ((c32 & 0xf) << 12) | ((uint32_t (cp [0]) & 0x3f) << 6) | (uint32_t (cp [1]) & 0x3f); - cp += 2; - } else if (c32 >= 0xc0 && cp < cpe) { - c32 = ((c32 & 0x1f) << 6) | (uint32_t (*cp) & 0x3f); - ++cp; - } + uint32_t c32 = utf32_from_utf8 (cp, cpe); if (sizeof (wchar_t) == 2 && c32 >= 0x10000) { c32 -= 0x10000; @@ -1324,6 +1349,28 @@ Extractor::test (const char *token) } } +bool +Extractor::test_without_case (const char *token) +{ + skip (); + + const char *cp = m_cp; + while (*cp && *token) { + uint32_t c = utf32_downcase (utf32_from_utf8 (cp)); + uint32_t ct = utf32_downcase (utf32_from_utf8 (token)); + if (c != ct) { + return false; + } + } + + if (! *token) { + m_cp = cp; + return true; + } else { + return false; + } +} + const char * Extractor::skip () { diff --git a/src/tl/tl/tlString.h b/src/tl/tl/tlString.h index 6f69aa1d5..f38ae77de 100644 --- a/src/tl/tl/tlString.h +++ b/src/tl/tl/tlString.h @@ -669,6 +669,13 @@ public: */ bool test (const char *token); + /** + * @brief Test for a token (a certain string) in case-insensitive mode + * + * If the token is not present, return false. + */ + bool test_without_case (const char *token); + /** * @brief Skip blanks * diff --git a/src/tl/unit_tests/tlString.cc b/src/tl/unit_tests/tlString.cc index 846e73956..5e049200a 100644 --- a/src/tl/unit_tests/tlString.cc +++ b/src/tl/unit_tests/tlString.cc @@ -388,6 +388,33 @@ TEST(8) EXPECT_EQ (x.try_read_quoted (s), true); EXPECT_EQ (s, "a_word\'!"); EXPECT_EQ (x.at_end (), true); + + x = Extractor (" foobar"); + EXPECT_EQ (x.test ("foo"), true); + EXPECT_EQ (x.test ("bar"), true); + + x = Extractor (" foo bar"); + EXPECT_EQ (x.test ("foo"), true); + EXPECT_EQ (x.test ("bar"), true); + + x = Extractor (" FOObar"); + EXPECT_EQ (x.test ("foo"), false); + EXPECT_EQ (x.test ("BAR"), false); + + x = Extractor (" FOObar"); + EXPECT_EQ (x.test_without_case ("foo"), true); + EXPECT_EQ (x.test_without_case ("BAR"), true); + + x = Extractor (" µm"); + EXPECT_EQ (x.test ("µm"), true); + + x = Extractor (" µM"); + EXPECT_EQ (x.test ("µm"), false); + EXPECT_EQ (x.test_without_case ("µm"), true); + + x = Extractor (" µm"); + EXPECT_EQ (x.test ("µM"), false); + EXPECT_EQ (x.test_without_case ("µM"), true); } TEST(9) diff --git a/testdata/algo/nreader1.cir b/testdata/algo/nreader1.cir new file mode 100644 index 000000000..8b06b1937 --- /dev/null +++ b/testdata/algo/nreader1.cir @@ -0,0 +1,17 @@ +* VDIV netlist before simplification + +* cell TOP +.SUBCKT TOP +* net 1 OUT +* net 2 GND +* net 4 IN +* net 7 VDD +* device instance $1 1.025,0.335 RES +R$1 6 1 7650 +* device instance $2 2.85,0.335 RES +R$2 3 1 7650 +* device instance $3 4.665,0.335 RES +R$3 3 2 2670 +* device instance $4 1.765,7.485 HVPMOS +M$4 6 4 7 7 MHVPMOS L=0.25U W=1.5U AS=0.63P AD=0.63P PS=3.84U PD=3.84U +.ENDS TOP diff --git a/testdata/algo/nreader2.cir b/testdata/algo/nreader2.cir new file mode 100644 index 000000000..32e7dfc09 --- /dev/null +++ b/testdata/algo/nreader2.cir @@ -0,0 +1,83 @@ +* RINGO netlist after simplification + +* cell RINGO +* pin FB +* pin VDD +* pin OUT +* pin ENABLE +* pin BULK,VSS +.SUBCKT RINGO 11 12 13 14 15 +* net 11 FB +* net 12 VDD +* net 13 OUT +* net 14 ENABLE +* net 15 BULK,VSS +* cell instance $1 r0 *1 1.8,0 +X$1 12 1 15 12 11 14 15 ND2X1 +* cell instance $2 r0 *1 4.2,0 +X$2 12 2 15 12 1 15 INVX1 +* cell instance $3 r0 *1 6,0 +X$3 12 3 15 12 2 15 INVX1 +* cell instance $4 r0 *1 7.8,0 +X$4 12 4 15 12 3 15 INVX1 +* cell instance $5 r0 *1 9.6,0 +X$5 12 5 15 12 4 15 INVX1 +* cell instance $6 r0 *1 11.4,0 +X$6 12 6 15 12 5 15 INVX1 +* cell instance $7 r0 *1 13.2,0 +X$7 12 7 15 12 6 15 INVX1 +* cell instance $8 r0 *1 15,0 +X$8 12 8 15 12 7 15 INVX1 +* cell instance $9 r0 *1 16.8,0 +X$9 12 9 15 12 8 15 INVX1 +* cell instance $10 r0 *1 18.6,0 +X$10 12 10 15 12 9 15 INVX1 +* cell instance $11 r0 *1 20.4,0 +X$11 12 11 15 12 10 15 INVX1 +* cell instance $12 r0 *1 22.2,0 +X$12 12 13 15 12 11 15 INVX1 +.ENDS RINGO + +* cell ND2X1 +* pin VDD +* pin OUT +* pin VSS +* pin +* pin B +* pin A +* pin BULK +.SUBCKT ND2X1 1 2 3 4 5 6 7 +* net 1 VDD +* net 2 OUT +* net 3 VSS +* net 5 B +* net 6 A +* net 7 BULK +* device instance $1 0.85,5.8 LVPMOS +M$1 2 6 1 4 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U +* device instance $2 1.55,5.8 LVPMOS +M$2 1 5 2 4 MLVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U +* device instance $3 0.85,2.135 LVNMOS +M$3 3 6 8 7 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U PD=1.4U +* device instance $4 1.55,2.135 LVNMOS +M$4 8 5 2 7 MLVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U +.ENDS ND2X1 + +* cell INVX1 +* pin VDD +* pin OUT +* pin VSS +* pin +* pin IN +* pin BULK +.SUBCKT INVX1 1 2 3 4 5 6 +* net 1 VDD +* net 2 OUT +* net 3 VSS +* net 5 IN +* net 6 BULK +* device instance $1 0.85,5.8 LVPMOS +M$1 1 5 2 4 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +* device instance $2 0.85,2.135 LVNMOS +M$2 3 5 2 6 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +.ENDS INVX1 diff --git a/testdata/algo/nreader3.cir b/testdata/algo/nreader3.cir new file mode 100644 index 000000000..f4dfd6cf1 --- /dev/null +++ b/testdata/algo/nreader3.cir @@ -0,0 +1,27 @@ +.subckt INVX1 1 2 3 4 5 6 + m$1 1 5 2 4 MLVPMOS w=1.5um l=0.25um + m$2 3 5 2 6 MLVNMOS w=0.95um l=0.25um +.ends + +.subckt ND2X1 1 2 3 4 5 6 7 + m$1 2 6 1 4 MLVPMOS L=0.25um W=1.5um + m$2 1 5 2 4 MLVPMOS L=0.25um W=1.5um + m$3 3 6 8 7 MLVNMOS L=0.25um W=0.95um + m$4 8 5 2 7 MLVNMOS L=0.25um W=0.95um +.ends ND2X1 + +.subckt RINGO 11 12 13 14 15 + x$1 12 1 15 12 11 14 15 ND2X1 + x$2 12 2 15 12 1 15 INVX1 + x$3 12 3 15 12 2 15 INVX1 + x$4 12 4 15 12 3 15 INVX1 + x$5 12 5 15 12 4 15 INVX1 + x$6 12 6 15 12 5 15 INVX1 + x$7 12 7 15 12 6 15 INVX1 + x$8 12 8 15 12 7 15 INVX1 + x$9 12 9 15 12 8 15 INVX1 + x$10 12 10 15 12 9 15 INVX1 + x$11 12 11 15 12 10 15 INVX1 + x$12 12 13 15 12 11 15 INVX1 +.ends RINGO + From 8e9f15669f839743d7072865d0934e38adcc193c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 2 Apr 2019 22:39:29 +0200 Subject: [PATCH 25/54] WIP: utilizing netlist compare for DRC checks as well + Some enhancements (e.g. enable pin swapping for pins without names and devices or subcircuits) --- src/db/db/dbNetlistCompare.cc | 69 ++++++++++++---- src/db/db/dbTestSupport.cc | 17 +++- src/db/db/dbTestSupport.h | 5 ++ src/db/unit_tests/dbNetlistCompareTests.cc | 82 ++++++++++++++++++- src/drc/unit_tests/drcSimpleTests.cc | 93 ++++++++-------------- 5 files changed, 184 insertions(+), 82 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index c2b43d015..3dbd96430 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -25,6 +25,7 @@ #include "dbHash.h" #include "tlProgress.h" #include "tlTimer.h" +#include "tlEquivalenceClusters.h" namespace db { @@ -98,7 +99,7 @@ public: void map_pins (const db::Circuit *circuit, size_t pin1_id, size_t pin2_id) { - m_pin_map [circuit].insert (std::make_pair (pin1_id, pin2_id)); + m_pin_map [circuit].same (pin1_id, pin2_id); } void map_pins (const db::Circuit *circuit, const std::vector &pin_ids) @@ -107,26 +108,26 @@ public: return; } - std::map &pm = m_pin_map [circuit]; + tl::equivalence_clusters &pm = m_pin_map [circuit]; for (size_t i = 1; i < pin_ids.size (); ++i) { - pm.insert (std::make_pair (pin_ids [i], pin_ids [0])); + pm.same (pin_ids [0], pin_ids [i]); } } size_t normalize_pin_id (const db::Circuit *circuit, size_t pin_id) const { - std::map >::const_iterator pm = m_pin_map.find (circuit); + std::map >::const_iterator pm = m_pin_map.find (circuit); if (pm != m_pin_map.end ()) { - std::map::const_iterator ipm = pm->second.find (pin_id); - if (ipm != pm->second.end ()) { - return ipm->second; + size_t cluster_id = pm->second.cluster_id (pin_id); + if (cluster_id > 0) { + return (*pm->second.begin_cluster (cluster_id))->first; } } return pin_id; } private: - std::map > m_pin_map; + std::map > m_pin_map; }; // -------------------------------------------------------------------------------------------------------------------- @@ -450,6 +451,7 @@ public: size_t pin_id = i->pin ()->id (); const db::Circuit *cr = sc->circuit_ref (); + size_t this_pin_id = pin_id; pin_id = pin_map->normalize_pin_id (cr, pin_id); std::map::const_iterator icm = circuit_map->find (cr); @@ -476,7 +478,6 @@ public: // we cannot afford creating edges from all to all other pins, so we just create edges to the previous and next // pin. This may take more iterations to solve, but should be equivalent. - std::vector pids; size_t pin_count = cr->pin_count (); // take a number if additional pins as edges: this allows identifying a pin as dependent @@ -484,6 +485,12 @@ public: // 5 additional pins should be sufficient to capture one additional non-power pin. size_t take_additional_pins = 5; + + std::vector pids; + pids.reserve (take_additional_pins + 1); + // this symmetrizes the pin list with respect to the before-normalization pin id: + pids.push_back (pin_id); + for (size_t n = 0; n < take_additional_pins; ++n) { size_t add_pin_id = (pin_id + n + 1) % pin_count; if (add_pin_id == pin_id) { @@ -500,12 +507,17 @@ public: for (std::vector::const_iterator i = pids.begin (); i != pids.end (); ++i) { size_t pin2_id = *i; + size_t this_pin2_id = cm->this_pin_from_other_pin (pin2_id); + + if (this_pin2_id == this_pin_id) { + // we should not go back to our original, non-normalized pin + continue; + } // NOTE: if a pin mapping is given, EdgeDesc::pin1_id and EdgeDesc::pin2_id are given // as pin ID's of the other circuit. EdgeDesc ed (sc, circuit_categorizer.cat_for_subcircuit (sc), pin_id, pin_map->normalize_pin_id (cr, pin2_id)); - size_t this_pin2_id = cm->this_pin_from_other_pin (pin2_id); const db::Net *net2 = sc->net_for_pin (this_pin2_id); std::map::const_iterator in = n2entry.find (net2); @@ -660,8 +672,7 @@ private: /** * @brief Compares edges as "less" - * Edge comparison is based on the pins attached (name of the first pin) or net - * name if no pins are attached on both nets. + * Edge comparison is based on the pins attached (name of the first pin). */ static bool edge_less (const db::Net *a, const db::Net *b) { @@ -679,7 +690,7 @@ private: return pna < pnb; } } - return a->name () < b->name (); + return false; } else { return false; } @@ -705,7 +716,7 @@ private: return pna == pnb; } } - return a->name () == b->name (); + return true; } else { return true; } @@ -1205,9 +1216,35 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, same_as_next = (i1 != g1.end () && *i1 == *ii1) || (i2 != g2.end () && *i2 == *ii2); // found a candidate - a single node with the same edges - if (! (same_as_next || same_as_prev) || pass) { - db::NetDeviceGraph::confirm_identity (g1, ii1, g2, ii2, mp_logger, same_as_next || same_as_prev); + + bool ambiguous = (same_as_next || same_as_prev); + if (! ambiguous || pass) { + + // For ambiguous nets make the pins exchangable + + if (same_as_next) { + + if (ii1->net () && i1->net ()) { + for (db::Net::const_pin_iterator pp = ii1->net ()->begin_pins (); pp != ii1->net ()->end_pins (); ++pp) { + for (db::Net::const_pin_iterator p = i1->net ()->begin_pins (); p != i1->net ()->end_pins (); ++p) { + mp_circuit_pin_mapper->map_pins (c1, pp->pin_id (), p->pin_id ()); + } + } + } + + if (ii2->net () && i2->net ()) { + for (db::Net::const_pin_iterator pp = ii2->net ()->begin_pins (); pp != ii2->net ()->end_pins (); ++pp) { + for (db::Net::const_pin_iterator p = i2->net ()->begin_pins (); p != i2->net ()->end_pins (); ++p) { + mp_circuit_pin_mapper->map_pins (c2, pp->pin_id (), p->pin_id ()); + } + } + } + + } + + db::NetDeviceGraph::confirm_identity (g1, ii1, g2, ii2, mp_logger, ambiguous); ++new_identities; + } } diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index b9f48536f..6aa938e9d 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -283,8 +283,6 @@ private: void DB_PUBLIC compare_netlist (tl::TestBase *_this, const db::Netlist &netlist, const std::string &au_nl_string) { - db::NetlistComparer comp (0); - db::Netlist au_nl; for (db::Netlist::const_device_class_iterator d = netlist.begin_device_classes (); d != netlist.end_device_classes (); ++d) { au_nl.add_device_class (d->clone ()); @@ -292,6 +290,8 @@ void DB_PUBLIC compare_netlist (tl::TestBase *_this, const db::Netlist &netlist, au_nl.from_string (au_nl_string); + db::NetlistComparer comp (0); + if (! comp.compare (&netlist, &au_nl)) { _this->raise ("Compare failed - see log for details.\n\nActual:\n" + netlist.to_string () + "\nGolden:\n" + au_nl_string); // Compare once again - this time with logger @@ -301,5 +301,18 @@ void DB_PUBLIC compare_netlist (tl::TestBase *_this, const db::Netlist &netlist, } } +void DB_PUBLIC compare_netlist (tl::TestBase *_this, const db::Netlist &netlist, const db::Netlist &netlist_au) +{ + db::NetlistComparer comp (0); + + if (! comp.compare (&netlist, &netlist_au)) { + _this->raise ("Compare failed - see log for details.\n\nActual:\n" + netlist.to_string () + "\nGolden:\n" + netlist_au.to_string ()); + // Compare once again - this time with logger + CompareLogger logger; + db::NetlistComparer comp (&logger); + comp.compare (&netlist, &netlist_au); + } +} + } diff --git a/src/db/db/dbTestSupport.h b/src/db/db/dbTestSupport.h index 6e4e34917..e079cf05b 100644 --- a/src/db/db/dbTestSupport.h +++ b/src/db/db/dbTestSupport.h @@ -79,6 +79,11 @@ void DB_PUBLIC compare_layouts (tl::TestBase *_this, const db::Layout &layout, c */ void DB_PUBLIC compare_netlist (tl::TestBase *_this, const db::Netlist &netlist, const std::string &au_nl_string); +/** + * @brief Compares a netlist against another netlist + */ +void DB_PUBLIC compare_netlist (tl::TestBase *_this, const db::Netlist &netlist, const db::Netlist &netlist_au); + } #endif diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 7d21d100b..43f6b9eba 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -1273,11 +1273,11 @@ TEST(14_Subcircuit2Nand) "end_circuit NAND NAND MATCH\n" "begin_circuit TOP TOP\n" "match_nets OUT OUT\n" - "match_nets IN2 IN2\n" "match_nets VSS VSS\n" "match_nets VDD VDD\n" - "match_nets INT INT\n" "match_nets IN1 IN1\n" + "match_nets INT INT\n" + "match_nets IN2 IN2\n" "match_pins $0 $0\n" "match_pins $1 $1\n" "match_pins $2 $2\n" @@ -1425,11 +1425,11 @@ TEST(14_Subcircuit2MatchWithSwap) "end_circuit NAND NAND MATCH\n" "begin_circuit TOP TOP\n" "match_nets OUT OUT\n" - "match_nets IN2 IN2\n" "match_nets VSS VSS\n" "match_nets VDD VDD\n" - "match_nets INT INT\n" "match_nets IN1 IN1\n" + "match_nets INT INT\n" + "match_nets IN2 IN2\n" "match_pins $0 $0\n" "match_pins $1 $1\n" "match_pins $2 $2\n" @@ -1517,6 +1517,80 @@ TEST(15_EmptySubCircuitTest) EXPECT_EQ (good, true); } +TEST(15_EmptySubCircuitWithoutPinNames) +{ + const char *nls1 = + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " subcircuit TRANS $1 ($1=$2,$2=$4,$3=IN);\n" + " subcircuit TRANS $2 ($1=$2,$2=$5,$3=IN);\n" + " subcircuit TRANS $3 ($1=$5,$2=OUT,$3=$2);\n" + " subcircuit TRANS $4 ($1=$4,$2=OUT,$3=$2);\n" + "end;\n" + "circuit TRANS ($1=$1,$2=$2,$3=$3);\n" + "end;\n"; + + const char *nls2 = + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $1 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $3 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " subcircuit TRANS $1 ($1=$4,$2=$2,$3=IN);\n" + " subcircuit TRANS $2 ($1=$5,$2=$2,$3=IN);\n" + " subcircuit TRANS $3 ($1=OUT,$2=$5,$3=$2);\n" + " subcircuit TRANS $4 ($1=OUT,$2=$4,$3=$2);\n" + "end;\n" + // This circuit is an abstract and it's pins are defined by the pin names + "circuit TRANS ($1=$1,$2=$2,$3=$3);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit TRANS TRANS\n" + "match_ambiguous_nets $1 $1\n" + "match_ambiguous_nets $2 $2\n" + "match_ambiguous_nets $3 $3\n" + "match_pins $0 $0\n" + "match_pins $1 $1\n" + "match_pins $2 $2\n" + "end_circuit TRANS TRANS MATCH\n" + "begin_circuit INV2 INV2\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_nets $4 $4\n" + "match_nets $2 $2\n" + "match_nets $5 $5\n" + "match_pins IN IN\n" + "match_pins $1 $1\n" + "match_pins OUT OUT\n" + "match_pins $3 $3\n" + "match_pins $4 $4\n" + "match_devices $1 $1\n" + "match_devices $3 $2\n" + "match_devices $2 $3\n" + "match_devices $4 $4\n" + "match_subcircuits $1 $1\n" + "match_subcircuits $2 $2\n" + "match_subcircuits $3 $3\n" + "match_subcircuits $4 $4\n" + "end_circuit INV2 INV2 MATCH" + ); + + EXPECT_EQ (good, true); +} + TEST(16_UniqueSubCircuitMatching) { const char *nls1 = diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 885495d8c..6d27bd9ae 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -23,6 +23,8 @@ #include "tlUnitTest.h" #include "dbReader.h" #include "dbTestSupport.h" +#include "dbNetlist.h" +#include "dbNetlistSpiceReader.h" #include "lymMacro.h" #include "tlFileUtils.h" @@ -343,6 +345,25 @@ TEST(8_TextsAndPolygons) db::compare_layouts (_this, layout, au, db::NoNormalization); } +static void compare_netlists (tl::TestBase *_this, const std::string &cir, const std::string &cir_au) +{ + db::Netlist nl, nl_au; + + db::NetlistSpiceReader reader; + + { + tl::InputStream is (cir); + reader.read (is, nl); + } + + { + tl::InputStream is (cir_au); + reader.read (is, nl_au); + } + + db::compare_netlist (_this, nl, nl_au); +} + TEST(9_NetlistExtraction) { std::string rs = tl::testsrc (); @@ -380,27 +401,11 @@ TEST(9_NetlistExtraction) // verify - { - tl::InputStream is (output); - tl::InputStream is_au (au); + CHECKPOINT (); + compare_netlists (_this, output, au); - if (is.read_all () != is_au.read_all ()) { - _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", - tl::absolute_file_path (output), - tl::absolute_file_path (au))); - } - } - - { - tl::InputStream is (output_simplified); - tl::InputStream is_au (au_simplified); - - if (is.read_all () != is_au.read_all ()) { - _this->raise (tl::sprintf ("Compare failed (simplified netlist) - see\n actual: %s\n golden: %s", - tl::absolute_file_path (output_simplified), - tl::absolute_file_path (au_simplified))); - } - } + CHECKPOINT (); + compare_netlists (_this, output_simplified, au_simplified); } TEST(10_NetlistExtractionFlat) @@ -440,27 +445,11 @@ TEST(10_NetlistExtractionFlat) // verify - { - tl::InputStream is (output); - tl::InputStream is_au (au); + CHECKPOINT (); + compare_netlists (_this, output, au); - if (is.read_all () != is_au.read_all ()) { - _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", - tl::absolute_file_path (output), - tl::absolute_file_path (au))); - } - } - - { - tl::InputStream is (output_simplified); - tl::InputStream is_au (au_simplified); - - if (is.read_all () != is_au.read_all ()) { - _this->raise (tl::sprintf ("Compare failed (simplified netlist) - see\n actual: %s\n golden: %s", - tl::absolute_file_path (output_simplified), - tl::absolute_file_path (au_simplified))); - } - } + CHECKPOINT (); + compare_netlists (_this, output_simplified, au_simplified); } TEST(11_CustomDevices) @@ -500,25 +489,9 @@ TEST(11_CustomDevices) // verify - { - tl::InputStream is (output); - tl::InputStream is_au (au); + CHECKPOINT (); + compare_netlists (_this, output, au); - if (is.read_all () != is_au.read_all ()) { - _this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s", - tl::absolute_file_path (output), - tl::absolute_file_path (au))); - } - } - - { - tl::InputStream is (output_simplified); - tl::InputStream is_au (au_simplified); - - if (is.read_all () != is_au.read_all ()) { - _this->raise (tl::sprintf ("Compare failed (simplified netlist) - see\n actual: %s\n golden: %s", - tl::absolute_file_path (output_simplified), - tl::absolute_file_path (au_simplified))); - } - } + CHECKPOINT (); + compare_netlists (_this, output_simplified, au_simplified); } From c5a56dbc5f47c8acb2c9b6aa21d37aeae8b33a64 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 4 Apr 2019 23:41:46 +0200 Subject: [PATCH 26/54] WIP: GSI binding for netlist comparer. --- src/db/db/db.pro | 3 +- src/db/db/dbNetlistCompare.h | 12 + src/db/db/gsiDeclDbNetlistCompare.cc | 466 +++++++++++++++++++++++++++ 3 files changed, 480 insertions(+), 1 deletion(-) create mode 100644 src/db/db/gsiDeclDbNetlistCompare.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index cd1acda6e..1556c253a 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -175,7 +175,8 @@ SOURCES = \ dbRegionProcessors.cc \ dbNetlistCompare.cc \ dbNetlistReader.cc \ - dbNetlistSpiceReader.cc + dbNetlistSpiceReader.cc \ + gsiDeclDbNetlistCompare.cc HEADERS = \ dbArray.h \ diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h index b0fcf8009..b941aef52 100644 --- a/src/db/db/dbNetlistCompare.h +++ b/src/db/db/dbNetlistCompare.h @@ -213,4 +213,16 @@ protected: } +namespace tl +{ + +template<> struct type_traits : public tl::type_traits +{ + // mark "NetlistDeviceExtractor" as having a default ctor and no copy ctor + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +} + #endif diff --git a/src/db/db/gsiDeclDbNetlistCompare.cc b/src/db/db/gsiDeclDbNetlistCompare.cc new file mode 100644 index 000000000..04d7c8f0c --- /dev/null +++ b/src/db/db/gsiDeclDbNetlistCompare.cc @@ -0,0 +1,466 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 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 "gsiDecl.h" +#include "dbNetlistCompare.h" + +namespace { + +/** + * @brief A NetlistDeviceExtractor implementation that allows reimplementation of the virtual methods + */ +class GenericNetlistCompareLogger + : public db::NetlistCompareLogger +{ +public: + GenericNetlistCompareLogger () + : db::NetlistCompareLogger () + { + // .. nothing yet .. + } + + virtual void begin_netlist (const db::Netlist *a, const db::Netlist *b) + { + if (cb_begin_netlist.can_issue ()) { + cb_begin_netlist.issue (&GenericNetlistCompareLogger::begin_netlist_fb, a, b); + } else { + db::NetlistCompareLogger::begin_netlist (a, b); + } + } + + void begin_netlist_fb (const db::Netlist *a, const db::Netlist *b) + { + db::NetlistCompareLogger::begin_netlist (a, b); + } + + virtual void end_netlist (const db::Netlist *a, const db::Netlist *b) + { + if (cb_end_netlist.can_issue ()) { + cb_end_netlist.issue (&GenericNetlistCompareLogger::end_netlist_fb, a, b); + } else { + db::NetlistCompareLogger::end_netlist (a, b); + } + } + + void end_netlist_fb (const db::Netlist *a, const db::Netlist *b) + { + db::NetlistCompareLogger::end_netlist (a, b); + } + + virtual void begin_circuit (const db::Circuit *a, const db::Circuit *b) + { + if (cb_begin_circuit.can_issue ()) { + cb_begin_circuit.issue (&GenericNetlistCompareLogger::begin_circuit_fb, a, b); + } else { + db::NetlistCompareLogger::begin_circuit (a, b); + } + } + + void begin_circuit_fb (const db::Circuit *a, const db::Circuit *b) + { + db::NetlistCompareLogger::begin_circuit (a, b); + } + + virtual void end_circuit (const db::Circuit *a, const db::Circuit *b, bool matching) + { + if (cb_end_circuit.can_issue ()) { + cb_end_circuit.issue (&GenericNetlistCompareLogger::end_circuit_fb, a, b, matching); + } else { + db::NetlistCompareLogger::end_circuit (a, b, matching); + } + } + + void end_circuit_fb (const db::Circuit *a, const db::Circuit *b, bool matching) + { + db::NetlistCompareLogger::end_circuit (a, b, matching); + } + + virtual void circuit_skipped (const db::Circuit *a, const db::Circuit *b) + { + if (cb_circuit_skipped.can_issue ()) { + cb_circuit_skipped.issue (&GenericNetlistCompareLogger::circuit_skipped_fb, a, b); + } else { + db::NetlistCompareLogger::circuit_skipped (a, b); + } + } + + void circuit_skipped_fb (const db::Circuit *a, const db::Circuit *b) + { + db::NetlistCompareLogger::circuit_skipped (a, b); + } + + virtual void circuit_mismatch (const db::Circuit *a, const db::Circuit *b) + { + if (cb_circuit_mismatch.can_issue ()) { + cb_circuit_mismatch.issue (&GenericNetlistCompareLogger::circuit_mismatch_fb, a, b); + } else { + db::NetlistCompareLogger::circuit_mismatch (a, b); + } + } + + void circuit_mismatch_fb (const db::Circuit *a, const db::Circuit *b) + { + db::NetlistCompareLogger::circuit_mismatch (a, b); + } + + virtual void match_nets (const db::Net *a, const db::Net *b) + { + if (cb_match_nets.can_issue ()) { + cb_match_nets.issue (&GenericNetlistCompareLogger::match_nets_fb, a, b); + } else { + db::NetlistCompareLogger::match_nets (a, b); + } + } + + void match_nets_fb (const db::Net *a, const db::Net *b) + { + db::NetlistCompareLogger::match_nets (a, b); + } + + virtual void match_ambiguous_nets (const db::Net *a, const db::Net *b) + { + if (cb_match_ambiguous_nets.can_issue ()) { + cb_match_ambiguous_nets.issue (&GenericNetlistCompareLogger::match_ambiguous_nets_fb, a, b); + } else { + db::NetlistCompareLogger::match_ambiguous_nets (a, b); + } + } + + void match_ambiguous_nets_fb (const db::Net *a, const db::Net *b) + { + db::NetlistCompareLogger::match_ambiguous_nets (a, b); + } + + virtual void net_mismatch (const db::Net *a, const db::Net *b) + { + if (cb_net_mismatch.can_issue ()) { + cb_net_mismatch.issue (&GenericNetlistCompareLogger::net_mismatch_fb, a, b); + } else { + db::NetlistCompareLogger::net_mismatch (a, b); + } + } + + void net_mismatch_fb (const db::Net *a, const db::Net *b) + { + db::NetlistCompareLogger::net_mismatch (a, b); + } + + virtual void match_devices (const db::Device *a, const db::Device *b) + { + if (cb_match_devices.can_issue ()) { + cb_match_devices.issue (&GenericNetlistCompareLogger::match_devices_fb, a, b); + } else { + db::NetlistCompareLogger::match_devices (a, b); + } + } + + void match_devices_fb (const db::Device *a, const db::Device *b) + { + db::NetlistCompareLogger::match_devices (a, b); + } + + virtual void match_devices_with_different_parameters (const db::Device *a, const db::Device *b) + { + if (cb_match_devices_with_different_parameters.can_issue ()) { + cb_match_devices_with_different_parameters.issue (&GenericNetlistCompareLogger::match_devices_with_different_parameters_fb, a, b); + } else { + db::NetlistCompareLogger::match_devices_with_different_parameters (a, b); + } + } + + void match_devices_with_different_parameters_fb (const db::Device *a, const db::Device *b) + { + db::NetlistCompareLogger::match_devices_with_different_parameters (a, b); + } + + virtual void match_devices_with_different_device_classes (const db::Device *a, const db::Device *b) + { + if (cb_match_devices_with_different_device_classes.can_issue ()) { + cb_match_devices_with_different_device_classes.issue (&GenericNetlistCompareLogger::match_devices_with_different_device_classes_fb, a, b); + } else { + db::NetlistCompareLogger::match_devices_with_different_device_classes (a, b); + } + } + + void match_devices_with_different_device_classes_fb (const db::Device *a, const db::Device *b) + { + db::NetlistCompareLogger::match_devices_with_different_device_classes (a, b); + } + + virtual void device_mismatch (const db::Device *a, const db::Device *b) + { + if (cb_device_mismatch.can_issue ()) { + cb_device_mismatch.issue (&GenericNetlistCompareLogger::device_mismatch_fb, a, b); + } else { + db::NetlistCompareLogger::device_mismatch (a, b); + } + } + + void device_mismatch_fb (const db::Device *a, const db::Device *b) + { + db::NetlistCompareLogger::device_mismatch (a, b); + } + + virtual void match_pins (const db::Pin *a, const db::Pin *b) + { + if (cb_match_pins.can_issue ()) { + cb_match_pins.issue (&GenericNetlistCompareLogger::match_pins_fb, a, b); + } else { + db::NetlistCompareLogger::match_pins (a, b); + } + } + + void match_pins_fb (const db::Pin *a, const db::Pin *b) + { + db::NetlistCompareLogger::match_pins (a, b); + } + + virtual void pin_mismatch (const db::Pin *a, const db::Pin *b) + { + if (cb_pin_mismatch.can_issue ()) { + cb_pin_mismatch.issue (&GenericNetlistCompareLogger::pin_mismatch_fb, a, b); + } else { + db::NetlistCompareLogger::pin_mismatch (a, b); + } + } + + void pin_mismatch_fb (const db::Pin *a, const db::Pin *b) + { + db::NetlistCompareLogger::pin_mismatch (a, b); + } + + virtual void match_subcircuits (const db::SubCircuit *a, const db::SubCircuit *b) + { + if (cb_match_subcircuits.can_issue ()) { + cb_match_subcircuits.issue (&GenericNetlistCompareLogger::match_subcircuits_fb, a, b); + } else { + db::NetlistCompareLogger::match_subcircuits (a, b); + } + } + + void match_subcircuits_fb (const db::SubCircuit *a, const db::SubCircuit *b) + { + db::NetlistCompareLogger::match_subcircuits (a, b); + } + + virtual void subcircuit_mismatch (const db::SubCircuit *a, const db::SubCircuit *b) + { + if (cb_subcircuit_mismatch.can_issue ()) { + cb_subcircuit_mismatch.issue (&GenericNetlistCompareLogger::subcircuit_mismatch_fb, a, b); + } else { + db::NetlistCompareLogger::subcircuit_mismatch (a, b); + } + } + + void subcircuit_mismatch_fb (const db::SubCircuit *a, const db::SubCircuit *b) + { + db::NetlistCompareLogger::subcircuit_mismatch (a, b); + } + + gsi::Callback cb_begin_netlist; + gsi::Callback cb_end_netlist; + gsi::Callback cb_begin_circuit; + gsi::Callback cb_end_circuit; + gsi::Callback cb_circuit_skipped; + gsi::Callback cb_match_nets; + gsi::Callback cb_net_mismatch; + gsi::Callback cb_circuit_mismatch; + gsi::Callback cb_match_ambiguous_nets; + gsi::Callback cb_match_devices; + gsi::Callback cb_match_devices_with_different_parameters; + gsi::Callback cb_match_devices_with_different_device_classes; + gsi::Callback cb_device_mismatch; + gsi::Callback cb_match_pins; + gsi::Callback cb_pin_mismatch; + gsi::Callback cb_match_subcircuits; + gsi::Callback cb_subcircuit_mismatch; +}; + +} + +namespace gsi +{ + +Class decl_GenericNetlistCompareLogger (decl_GenericNetlistCompareLogger, "db", "GenericNetlistCompareLogger", + gsi::callback ("begin_netlist", &GenericNetlistCompareLogger::begin_netlist, &GenericNetlistCompareLogger::cb_begin_netlist, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called at the beginning of the compare process.\n" + "This method is called once when the compare run begins.\n" + ) + + gsi::callback ("end_netlist", &GenericNetlistCompareLogger::end_netlist, &GenericNetlistCompareLogger::cb_end_netlist, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called at the end of the compare process.\n" + "This method is called once when the compare run ended.\n" + ) + + gsi::callback ("begin_circuit", &GenericNetlistCompareLogger::begin_circuit, &GenericNetlistCompareLogger::cb_begin_circuit, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when a new circuit is compared.\n" + "This compare procedure will run the netlist compare circuit vs. circuit in a bottom-up fashion.\n" + "Before each circuit is compared, this method is called once with the circuits that are about to be compared.\n" + "After the circuit has been compared, \\end_circuit will be called.\n" + "\n" + "In some cases, the compare algorithm will decide that circuits can't be compared. This happens if for " + "some or all subcircuits the pin assignment can't be derived. In this case, \\circuit_skipped will be called once " + "instead of \\begin_circuit and \\end_circuit.\n" + ) + + gsi::callback ("end_circuit", &GenericNetlistCompareLogger::end_circuit, &GenericNetlistCompareLogger::cb_end_circuit, gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("matching"), + "@brief This function is called at the end of the compare process.\n" + "The 'matching' argument indicates whether the circuits have been identified as identical.\n" + "See \\begin_circuit for details." + ) + + gsi::callback ("circuit_skipped", &GenericNetlistCompareLogger::circuit_skipped, &GenericNetlistCompareLogger::cb_circuit_skipped, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when circuits can't be compared.\n" + "If there is a known circuit pair, but the circuits can be compared - for example because subcircuits can't be identified - this method will be called with " + "both circuits.\n" + "\n" + "This method is called instead of \\begin_circuit and \\end_circuit." + ) + + gsi::callback ("circuit_mismatch", &GenericNetlistCompareLogger::circuit_mismatch, &GenericNetlistCompareLogger::cb_circuit_mismatch, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when circuits can't be compared.\n" + "This method is called when a circuit can't be mapped to a partner in the other netlist. In this case, " + "this method is called with the one circuit and nil for the other circuit.\n" + "\n" + "This method is called instead of \\begin_circuit and \\end_circuit." + ) + + gsi::callback ("match_nets", &GenericNetlistCompareLogger::match_nets, &GenericNetlistCompareLogger::cb_match_nets, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two nets are identified.\n" + "If two nets are identified as a corresponding pair, this method will be called with both nets.\n" + "If the nets can be paired, but this match is ambiguous, \\match_ambiguous_nets_fb will be called instead.\n" + "If nets can't be matched to a partner, \\net_mismatch will be called.\n" + ) + + gsi::callback ("match_ambiguous_nets", &GenericNetlistCompareLogger::match_ambiguous_nets, &GenericNetlistCompareLogger::cb_match_ambiguous_nets, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two nets are identified, but this choice is ambiguous.\n" + "See \\match_nets for more details." + ) + + gsi::callback ("net_mismatch", &GenericNetlistCompareLogger::net_mismatch, &GenericNetlistCompareLogger::cb_net_mismatch, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when a net can't be paired.\n" + "This method will be called, if a net cannot be identified as identical with another net. The corresponding argument " + "will identify the net and source netlist. The other argument will be nil.\n" + ) + + gsi::callback ("match_devices", &GenericNetlistCompareLogger::match_devices, &GenericNetlistCompareLogger::cb_match_devices, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two devices are identified.\n" + "If two devices are identified as a corresponding pair, this method will be called with both devices.\n" + "If the devices can be paired, but the device parameters don't match, \\match_devices_with_different_parameters will be called instead.\n" + "If the devices can be paired, but the device classes don't match, \\match_devices_with_different_device_classes will be called instead.\n" + "If devices can't be matched, \\device_mismatch will be called with the one device considered and the other device being nil." + ) + + gsi::callback ("match_devices_with_different_parameters", &GenericNetlistCompareLogger::match_devices_with_different_parameters, &GenericNetlistCompareLogger::cb_match_devices_with_different_parameters, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two devices are identified but have different parameters.\n" + "See \\match_devices for details.\n" + ) + + gsi::callback ("match_devices_with_different_device_classes", &GenericNetlistCompareLogger::match_devices_with_different_device_classes, &GenericNetlistCompareLogger::cb_match_devices_with_different_device_classes, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two devices are identified but have different device classes.\n" + "See \\match_devices for details.\n" + ) + + gsi::callback ("device_mismatch", &GenericNetlistCompareLogger::device_mismatch, &GenericNetlistCompareLogger::cb_device_mismatch, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two devices can't be paired.\n" + "This will report the device considered in a or b. The other argument is nil." + "See \\match_devices for details.\n" + ) + + gsi::callback ("match_pins", &GenericNetlistCompareLogger::match_pins, &GenericNetlistCompareLogger::cb_match_pins, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two pins are identified.\n" + "If two pins are identified as a corresponding pair, this method will be called with both pins.\n" + "If pins can't be matched, \\pin_mismatch will be called with the one pin considered and the other pin being nil." + ) + + gsi::callback ("pin_mismatch", &GenericNetlistCompareLogger::pin_mismatch, &GenericNetlistCompareLogger::cb_pin_mismatch, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two pins can't be paired.\n" + "This will report the pin considered in a or b. The other argument is nil." + "See \\match_pins for details.\n" + ) + + gsi::callback ("match_subcircuits", &GenericNetlistCompareLogger::match_subcircuits, &GenericNetlistCompareLogger::cb_match_subcircuits, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two subcircuits are identified.\n" + "If two subcircuits are identified as a corresponding pair, this method will be called with both subcircuits.\n" + "If subcircuits can't be matched, \\subcircuit_mismatch will be called with the one subcircuit considered and the other subcircuit being nil." + ) + + gsi::callback ("subcircuit_mismatch", &GenericNetlistCompareLogger::subcircuit_mismatch, &GenericNetlistCompareLogger::cb_subcircuit_mismatch, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two subcircuits can't be paired.\n" + "This will report the subcircuit considered in a or b. The other argument is nil." + "See \\match_subcircuits for details.\n" + ), + "@brief An event receiver for the netlist compare feature.\n" + "The \\NetlistComparer class will send compare events to a logger derived from this class. " + "Use this class to implement your own logger class. You can override on of it's methods to receive certain " + "kind of events." + "\n" + "This class has been introduced in version 0.26.\n" +); + +db::NetlistComparer *make_comparer (db::NetlistCompareLogger *logger) +{ + return new db::NetlistComparer (logger); +} + +Class decl_dbNetlistComparer ("db", "NetlistComparer", + gsi::constructor ("new", &make_comparer, gsi::arg ("logger", (db::NetlistCompareLogger *) 0), + "@brief Creates a new comparer object." + "The logger is a delegate or event receiver which the comparer will send compare events to. " + "See the class description for more details." + ) + + gsi::method ("same_nets", &db::NetlistComparer::same_nets, gsi::arg ("net_a"), gsi::arg ("net_b"), + "@brief Marks two nets as identical\n" + "This makes a net net_a in netlist a identical to the corresponding\n" + "net net_b in netlist b (see \\compare).\n" + "Otherwise, the algorithm will try to identify nets according to their topology. " + "This method can be used to supply hints to the compare algorithm. It will use " + "these hints to derive further identities." + ) + + gsi::method ("equivalent_pins", (void (db::NetlistComparer::*) (const db::Circuit *, size_t, size_t)) &db::NetlistComparer::equivalent_pins, gsi::arg ("circuit_b"), gsi::arg ("pin_id1"), gsi::arg ("pin_id2"), + "@brief Marks two pins of the given circuit as equivalent (i.e. they can be swapped)\n" + "Only circuits from the second input can be given swappable pins. " + "This will imply the same swappable pins on the equivalent circuit of the first input. " + "To mark multiple pins as swappable, use the version that takes a list of pins." + ) + + gsi::method ("equivalent_pins", (void (db::NetlistComparer::*) (const db::Circuit *, const std::vector &)) &db::NetlistComparer::equivalent_pins, gsi::arg ("circuit_b"), gsi::arg ("pin_ids"), + "@brief Marks several pins of the given circuit as equivalent (i.e. they can be swapped)\n" + "Only circuits from the second input can be given swappable pins. " + "This will imply the same swappable pins on the equivalent circuit of the first input. " + "This version is a generic variant of the two-pin version of this method." + ) + + gsi::method ("same_device_classes", &db::NetlistComparer::same_device_classes, gsi::arg ("dev_cls_a"), gsi::arg ("dev_cls_b"), + "@brief Marks two device classes as identical\n" + "This makes a device class dev_cls_a in netlist a identical to the corresponding\n" + "device class dev_cls_b in netlist b (see \\compare).\n" + "By default device classes with the same name are identical.\n" + ) + + gsi::method ("same_circuits", &db::NetlistComparer::same_circuits, gsi::arg ("circuit_a"), gsi::arg ("circuit_b"), + "@brief Marks two circuits as identical\n" + "This method makes a circuit circuit_a in netlist a identical to the corresponding\n" + "circuit circuit_b in netlist b (see \\compare). By default circuits with the same name are identical.\n" + ) + + gsi::method ("compare", &db::NetlistComparer::compare, gsi::arg ("netlist_a"), gsi::arg ("netlist_b"), + "@brief Compares two netlists\n" + "This method will perform the actual netlist compare. It will return true if both netlists are identical. " + "If the comparer has been configured with \\same_nets or similar methods, the objects given there must " + "be located inside 'circuit_a' and 'circuit_b' respectively." + ), + "@brief Compares two netlists\n" + "This class performs the actual comparison of two netlists.\n" + "It can be used with an event receiver to log the errors and net mismatches. " + "Event receivers are derived from class \\GenericNetlistCompareLogger." + "\n" + "The netlist comparer can be configured in different ways. Specifically hints can be given for nets, device classes or circuits. " + "Equivalence hints can be given with \\same_nets, \\same_circuits etc.\n" + "\n" + "Another configuration relates to swappable pins of subcircuits. If pins marked this way, the compare algorithm may swap them to " + "achieve net matching. Swappable pins belong to an 'equivalence group' and can be defined with \\equivalent_pins.\n" + "\n" + "This class has been introduced in version 0.26." +); + +} From 43f65e4d295c1cdec5cafee95bba2bb2bfb70d7e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 6 Apr 2019 00:18:37 +0200 Subject: [PATCH 27/54] Added tests for GSI binding of dbNetlistCompare --- src/db/db/gsiDeclDbNetlist.cc | 9 + src/db/db/gsiDeclDbNetlistCompare.cc | 7 +- src/rba/unit_tests/rba.cc | 1 + testdata/ruby/dbNetlistCompare.rb | 747 +++++++++++++++++++++++++++ 4 files changed, 760 insertions(+), 4 deletions(-) create mode 100644 testdata/ruby/dbNetlistCompare.rb diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 97ff3a824..29f927cf7 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -39,6 +39,10 @@ Class decl_dbPin ("db", "Pin", ) + gsi::method ("name", &db::Pin::name, "@brief Gets the name of the pin.\n" + ) + + gsi::method ("expanded_name", &db::Pin::expanded_name, + "@brief Gets the expanded name of the pin.\n" + "The expanded name is the name or a generic identifier made from the ID if the name is empty." ), "@brief A pin of a circuit.\n" "Pin objects are used to describe the outgoing pins of " @@ -1010,6 +1014,11 @@ Class decl_dbNetlist ("db", "Netlist", "@brief Converts the netlist to a string representation.\n" "This method is intended for test purposes mainly." ) + + gsi::method ("from_s", &db::Netlist::from_string, gsi::arg ("str"), + "@brief Reads the netlist from a string representation.\n" + "This method is intended for test purposes mainly. It turns a string returned by \\to_s back into " + "a netlist. Note that the device classes must be created before as they are not persisted inside the string." + ) + gsi::method ("combine_devices", &db::Netlist::combine_devices, "@brief Combines devices where possible\n" "This method will combine devices that can be combined according " diff --git a/src/db/db/gsiDeclDbNetlistCompare.cc b/src/db/db/gsiDeclDbNetlistCompare.cc index 04d7c8f0c..50b91ae69 100644 --- a/src/db/db/gsiDeclDbNetlistCompare.cc +++ b/src/db/db/gsiDeclDbNetlistCompare.cc @@ -296,11 +296,10 @@ public: }; } - namespace gsi { -Class decl_GenericNetlistCompareLogger (decl_GenericNetlistCompareLogger, "db", "GenericNetlistCompareLogger", +Class decl_GenericNetlistCompareLogger ("db", "GenericNetlistCompareLogger", gsi::callback ("begin_netlist", &GenericNetlistCompareLogger::begin_netlist, &GenericNetlistCompareLogger::cb_begin_netlist, gsi::arg ("a"), gsi::arg ("b"), "@brief This function is called at the beginning of the compare process.\n" "This method is called once when the compare run begins.\n" @@ -401,13 +400,13 @@ Class decl_GenericNetlistCompareLogger (decl_Generi "This class has been introduced in version 0.26.\n" ); -db::NetlistComparer *make_comparer (db::NetlistCompareLogger *logger) +static db::NetlistComparer *make_comparer (GenericNetlistCompareLogger *logger) { return new db::NetlistComparer (logger); } Class decl_dbNetlistComparer ("db", "NetlistComparer", - gsi::constructor ("new", &make_comparer, gsi::arg ("logger", (db::NetlistCompareLogger *) 0), + gsi::constructor ("new", &make_comparer, gsi::arg ("logger", (GenericNetlistCompareLogger *) 0), "@brief Creates a new comparer object." "The logger is a delegate or event receiver which the comparer will send compare events to. " "See the class description for more details." diff --git a/src/rba/unit_tests/rba.cc b/src/rba/unit_tests/rba.cc index 5c6d4175e..cdc31958b 100644 --- a/src/rba/unit_tests/rba.cc +++ b/src/rba/unit_tests/rba.cc @@ -113,6 +113,7 @@ RUBYTEST (dbMatrix, "dbMatrix.rb") RUBYTEST (dbNetlist, "dbNetlist.rb") RUBYTEST (dbNetlistDeviceClasses, "dbNetlistDeviceClasses.rb") RUBYTEST (dbNetlistWriterTests, "dbNetlistWriterTests.rb") +RUBYTEST (dbNetlistCompare, "dbNetlistCompare.rb") RUBYTEST (dbPathTest, "dbPathTest.rb") RUBYTEST (dbPCells, "dbPCells.rb") RUBYTEST (dbPointTest, "dbPointTest.rb") diff --git a/testdata/ruby/dbNetlistCompare.rb b/testdata/ruby/dbNetlistCompare.rb new file mode 100644 index 000000000..69d9f44ce --- /dev/null +++ b/testdata/ruby/dbNetlistCompare.rb @@ -0,0 +1,747 @@ +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright(C) 2006-2019 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 + +if !$:.member?(File::dirname($0)) + $:.push(File::dirname($0)) +end + +load("test_prologue.rb") + +class NetlistCompareTestLogger < RBA::GenericNetlistCompareLogger + + def initialize + @texts = [] + end + + def out(text) + @texts << text + end + + def begin_circuit(a, b) + out("begin_circuit " + circuit2str(a) + " " + circuit2str(b)) + end + + def end_circuit(a, b, matching) + out("end_circuit " + circuit2str(a) + " " + circuit2str(b) + " " + (matching ? "MATCH" : "NOMATCH")) + end + + def circuit_skipped(a, b) + out("circuit_skipped " + circuit2str(a) + " " + circuit2str(b)) + end + + def circuit_mismatch(a, b) + out("circuit_mismatch " + circuit2str(a) + " " + circuit2str(b)) + end + + def match_nets(a, b) + out("match_nets " + net2str(a) + " " + net2str(b)) + end + + def match_ambiguous_nets(a, b) + out("match_ambiguous_nets " + net2str(a) + " " + net2str(b)) + end + + def net_mismatch(a, b) + out("net_mismatch " + net2str(a) + " " + net2str(b)) + end + + def match_devices(a, b) + out("match_devices " + device2str(a) + " " + device2str(b)) + end + + def device_mismatch(a, b) + out("device_mismatch " + device2str(a) + " " + device2str(b)) + end + + def match_devices_with_different_parameters(a, b) + out("match_devices_with_different_parameters " + device2str(a) + " " + device2str(b)) + end + + def match_devices_with_different_device_classes(a, b) + out("match_devices_with_different_device_classes " + device2str(a) + " " + device2str(b)) + end + + def match_pins(a, b) + out("match_pins " + pin2str(a) + " " + pin2str(b)) + end + + def pin_mismatch(a, b) + out("pin_mismatch " + pin2str(a) + " " + pin2str(b)) + end + + def match_subcircuits(a, b) + out("match_subcircuits " + subcircuit2str(a) + " " + subcircuit2str(b)) + end + + def subcircuit_mismatch(a, b) + out("subcircuit_mismatch " + subcircuit2str(a) + " " + subcircuit2str(b)) + end + + def text + return @texts.join("\n") + "\n" + end + + def clear + @texts = [] + end + + def circuit2str(x) + return x ? x.name : "(null)" + end + + def device2str(x) + return x ? x.expanded_name : "(null)" + end + + def net2str(x) + return x ? x.expanded_name : "(null)" + end + + def pin2str(x) + return x ? x.expanded_name : "(null)" + end + + def subcircuit2str(x) + return x ? x.expanded_name : "(null)" + end + +end + +def prep_nl(nl, str) + + dc = RBA::DeviceClassMOS3Transistor::new + dc.name = "PMOS" + nl.add(dc) + + dc = RBA::DeviceClassMOS3Transistor::new + dc.name = "NMOS" + nl.add(dc) + + dc = RBA::DeviceClassMOS3Transistor::new + dc.name = "PMOSB" + nl.add(dc) + + dc = RBA::DeviceClassMOS3Transistor::new + dc.name = "NMOSB" + nl.add(dc) + + dc = RBA::DeviceClassMOS4Transistor::new + dc.name = "PMOS4" + nl.add(dc) + + dc = RBA::DeviceClassMOS4Transistor::new + dc.name = "NMOS4" + nl.add(dc) + + dc = RBA::DeviceClassResistor::new + dc.name = "RES" + nl.add(dc) + + dc = RBA::DeviceClassCapacitor::new + dc.name = "CAP" + nl.add(dc) + + dc = RBA::DeviceClassInductor::new + dc.name = "IND" + nl.add(dc) + + dc = RBA::DeviceClassDiode::new + dc.name = "DIODE" + nl.add(dc) + + nl.from_s(str) + +end + +class NetlistCompare_TestClass < TestBase + + def test_1 + + nls1 = <<"END" +circuit INV($1=IN,$2=OUT,$3=VDD,$4=VSS); + device PMOS $1(S=VDD,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $2(S=VSS,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nls2 = <<"END" +circuit INV($1=VDD,$2=IN,$3=VSS,$4=OUT); + device NMOS $1(S=OUT,G=IN,D=VSS)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $2(S=VDD,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nl1 = RBA::Netlist::new + nl2 = RBA::Netlist::new + prep_nl(nl1, nls1) + prep_nl(nl2, nls2) + + logger = NetlistCompareTestLogger::new + comp = RBA::NetlistComparer::new(logger) + + good = comp.compare(nl1, nl2) + + assert_equal(logger.text, <<"END") +begin_circuit INV INV +match_nets VDD VDD +match_nets VSS VSS +match_nets OUT OUT +match_nets IN IN +match_pins $0 $1 +match_pins $1 $3 +match_pins $2 $0 +match_pins $3 $2 +match_devices $2 $1 +match_devices $1 $2 +end_circuit INV INV MATCH +END + + assert_equal(good, true) + + end + + def test_2 + + nls1 = <<"END" +circuit INV($1=IN,$2=OUT,$3=VDD,$4=VSS); + device PMOS $1(S=VDD,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $2(S=VSS,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nls2 = <<"END" +circuit INV($1=VDD,$2=IN,$3=VSS,$4=OUT); + device NMOSB $1(S=OUT,G=IN,D=VSS)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOSB $2(S=VDD,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nl1 = RBA::Netlist::new + nl2 = RBA::Netlist::new + prep_nl(nl1, nls1) + prep_nl(nl2, nls2) + + logger = NetlistCompareTestLogger::new + comp = RBA::NetlistComparer::new(logger) + + comp.same_device_classes(nl1.device_class_by_name("PMOS"), nl2.device_class_by_name("PMOSB")) + + good = comp.compare(nl1, nl2) + assert_equal(good, false) + + logger.clear + comp.same_device_classes(nl1.device_class_by_name("NMOS"), nl2.device_class_by_name("NMOSB")) + good = comp.compare(nl1, nl2) + + assert_equal(logger.text(), <<"END") +begin_circuit INV INV +match_nets VDD VDD +match_nets VSS VSS +match_nets OUT OUT +match_nets IN IN +match_pins $0 $1 +match_pins $1 $3 +match_pins $2 $0 +match_pins $3 $2 +match_devices $2 $1 +match_devices $1 $2 +end_circuit INV INV MATCH +END + + assert_equal(good, true) + + end + + def test_3 + + nls1 = <<"END" +circuit INV($1=IN,$2=OUT,$3=VDD,$4=VSS); + device PMOS $1(S=VDD,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $2(S=VSS,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nls2 = <<"END" +circuit INV($1=VDD,$2=IN,$3=VSS,$4=OUT); + device NMOS $1(S=OUT,G=IN,D=VSS)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $2(S=VDD,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nl1 = RBA::Netlist::new + nl2 = RBA::Netlist::new + prep_nl(nl1, nls1) + prep_nl(nl2, nls2) + + logger = NetlistCompareTestLogger::new + comp = RBA::NetlistComparer::new(logger) + + ca = nl1.circuit_by_name("INV") + cb = nl2.circuit_by_name("INV") + comp.same_nets(ca.net_by_name("VDD"), cb.net_by_name("VDD")) + comp.same_nets(ca.net_by_name("VSS"), cb.net_by_name("VSS")) + + good = comp.compare(nl1, nl2) + + assert_equal(logger.text(), <<"END") +begin_circuit INV INV +match_nets OUT OUT +match_nets IN IN +match_pins $0 $1 +match_pins $1 $3 +match_pins $2 $0 +match_pins $3 $2 +match_devices $2 $1 +match_devices $1 $2 +end_circuit INV INV MATCH +END + + assert_equal(good, true) + + end + + def test_4 + + nls1 = <<"END" +circuit BUF ($1=IN,$2=OUT,$3=VDD,$4=VSS); + device PMOS $1 (S=VDD,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $2 (S=VSS,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $3 (S=VDD,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $4 (S=VSS,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $5 (S=VDD,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $6 (S=VSS,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $7 (S=VDD,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $8 (S=VSS,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nls2 = <<"END" +circuit BUF ($1=VDD,$2=IN,$3=VSS,$4=OUT); + device PMOS $1 (S=VDD,G=IN,D=$10) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $2 (S=VDD,G=$10,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $3 (S=VDD,G=IN,D=$11) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $4 (S=VDD,G=$11,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $5 (S=$10,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $6 (S=OUT,G=$10,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $7 (S=$11,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $8 (S=OUT,G=$11,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nl1 = RBA::Netlist::new + nl2 = RBA::Netlist::new + prep_nl(nl1, nls1) + prep_nl(nl2, nls2) + + logger = NetlistCompareTestLogger::new + comp = RBA::NetlistComparer::new(logger) + + good = comp.compare(nl1, nl2) + + assert_equal(logger.text, <<"END") +begin_circuit BUF BUF +match_nets OUT OUT +match_nets VDD VDD +match_nets IN IN +match_nets VSS VSS +match_ambiguous_nets INT $10 +match_ambiguous_nets INT2 $11 +match_pins $0 $1 +match_pins $1 $3 +match_pins $2 $0 +match_pins $3 $2 +match_devices $1 $1 +match_devices $3 $2 +match_devices $5 $3 +match_devices $7 $4 +match_devices $2 $5 +match_devices $4 $6 +match_devices $6 $7 +match_devices $8 $8 +end_circuit BUF BUF MATCH +END + + assert_equal(good, true) + + end + + def test_5 + + nls1 = <<"END" +circuit BUF ($1=IN,$2=OUT,$3=VDD,$4=VSS); + device PMOS $1 (S=VDD,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $2 (S=VSS,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $3 (S=VDD,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $4 (S=VSS,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $5 (S=VDD,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $6 (S=VSS,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $7 (S=VDD,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $8 (S=VSS,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nls2 = <<"END" +circuit BUF ($1=VDD,$2=IN,$3=VSS,$4=OUT); + device PMOS $1 (S=VDD,G=IN,D=$10) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $2 (S=VDD,G=$10,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $3 (S=VDD,G=IN,D=$11) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $4 (S=VDD,G=$11,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $5 (S=$10,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $6 (S=OUT,G=$10,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $7 (S=$11,G=IN,D=VSS) (L=0.35,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $8 (S=OUT,G=$11,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nl1 = RBA::Netlist::new + nl2 = RBA::Netlist::new + prep_nl(nl1, nls1) + prep_nl(nl2, nls2) + + logger = NetlistCompareTestLogger::new + comp = RBA::NetlistComparer::new(logger) + + # NOTE: adding this power hint makes the device class error harder to detect + ca = nl1.circuit_by_name("BUF") + cb = nl2.circuit_by_name("BUF") + comp.same_nets(ca.net_by_name("VDD"), cb.net_by_name("VDD")) + comp.same_nets(ca.net_by_name("VSS"), cb.net_by_name("VSS")) + + good = comp.compare(nl1, nl2) + + assert_equal(logger.text, <<"END") +begin_circuit BUF BUF +match_nets OUT OUT +match_nets IN IN +match_ambiguous_nets INT $10 +match_nets INT2 $11 +match_pins $0 $1 +match_pins $1 $3 +match_pins $2 $0 +match_pins $3 $2 +match_devices $1 $1 +match_devices $3 $2 +match_devices $5 $3 +match_devices $7 $4 +match_devices $2 $5 +match_devices $4 $6 +match_devices_with_different_parameters $6 $7 +match_devices $8 $8 +end_circuit BUF BUF NOMATCH +END + + assert_equal(good, false) + + end + + def test_6 + + nls1 = <<"END" +circuit BUF ($1=IN,$2=OUT,$3=VDD,$4=VSS); + device PMOS $1 (S=VDD,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $2 (S=VSS,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $3 (S=VDD,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $4 (S=VSS,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $5 (S=VDD,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $6 (S=VSS,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOSB $7 (S=VDD,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOSB $8 (S=VSS,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nls2 = <<"END" +circuit BUF ($1=VDD,$2=IN,$3=VSS,$4=OUT); + device PMOS $1 (S=VDD,G=IN,D=$10) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $2 (S=VDD,G=$10,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $3 (S=VDD,G=IN,D=$11) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $4 (S=VDD,G=$11,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $5 (S=$10,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $6 (S=OUT,G=$10,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $7 (S=$11,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOSB $8 (S=OUT,G=$11,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nl1 = RBA::Netlist::new + nl2 = RBA::Netlist::new + prep_nl(nl1, nls1) + prep_nl(nl2, nls2) + + logger = NetlistCompareTestLogger::new + comp = RBA::NetlistComparer::new(logger) + + # NOTE: adding this power hint makes the device class error harder to detect + ca = nl1.circuit_by_name("BUF") + cb = nl2.circuit_by_name("BUF") + comp.same_nets(ca.net_by_name("VDD"), cb.net_by_name("VDD")) + comp.same_nets(ca.net_by_name("VSS"), cb.net_by_name("VSS")) + + good = comp.compare(nl1, nl2) + + assert_equal(logger.text, <<"END") +begin_circuit BUF BUF +match_nets IN IN +match_nets INT $10 +match_nets OUT OUT +match_nets INT2 $11 +match_pins $0 $1 +match_pins $1 $3 +match_pins $2 $0 +match_pins $3 $2 +match_devices $1 $1 +match_devices $3 $2 +match_devices $5 $3 +match_devices_with_different_device_classes $7 $4 +match_devices $2 $5 +match_devices $4 $6 +match_devices $6 $7 +match_devices $8 $8 +end_circuit BUF BUF NOMATCH +END + + assert_equal(good, false) + + end + + def test_7 + + nls1 = <<"END" +circuit BUF ($1=IN,$2=OUT,$3=VDD,$4=VSS); + device PMOS $1 (S=VDD,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $2 (S=VSS,G=IN,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $3 (S=VDD,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $4 (S=VSS,G=INT,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $5 (S=VDD,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $6 (S=VSS,G=IN,D=INT2) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOSB $7 (S=VDD,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOSB $8 (S=VSS,G=INT2,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nls2 = <<"END" +circuit BUF ($1=VDD,$2=IN,$3=VSS,$4=OUT); + device PMOS $1 (S=VDD,G=IN,D=$10) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $2 (S=VDD,G=$10,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $3 (S=VDD,G=IN,D=$11) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOSB $4 (S=VDD,G=$11,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $5 (S=$10,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $6 (S=OUT,G=$10,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $7 (S=$11,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOSB $8 (S=OUT,G=$11,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device RES $9 (A=$10,B=$11) (R=42); +end; +END + + nl1 = RBA::Netlist::new + nl2 = RBA::Netlist::new + prep_nl(nl1, nls1) + prep_nl(nl2, nls2) + + logger = NetlistCompareTestLogger::new + comp = RBA::NetlistComparer::new(logger) + + # Forcing the power nets into equality makes the resistor error harder to detect + ca = nl1.circuit_by_name("BUF") + cb = nl2.circuit_by_name("BUF") + comp.same_nets(ca.net_by_name("VDD"), cb.net_by_name("VDD")) + comp.same_nets(ca.net_by_name("VSS"), cb.net_by_name("VSS")) + + good = comp.compare(nl1, nl2) + + assert_equal(logger.text, <<"END") +begin_circuit BUF BUF +match_nets INT $10 +match_nets OUT OUT +match_nets INT2 $11 +match_nets IN IN +match_pins $0 $1 +match_pins $1 $3 +match_pins $2 $0 +match_pins $3 $2 +match_devices $1 $1 +match_devices $3 $2 +match_devices $5 $3 +match_devices $7 $4 +match_devices $2 $5 +match_devices $4 $6 +match_devices $6 $7 +match_devices $8 $8 +device_mismatch (null) $9 +end_circuit BUF BUF NOMATCH +END + + assert_equal(good, false) + + end + + def test_8 + + nls1 = <<"END" +circuit INV ($1=IN,$2=OUT,$3=VDD,$4=VSS); + device PMOS $1 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $2 (S=VSS,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +circuit TOP ($1=IN,$2=OUT,$3=VDD,$4=VSS); + subcircuit INV $1 ($1=IN,$2=INT,$3=VDD,$4=VSS); + subcircuit INV $2 ($1=INT,$2=OUT,$3=VDD,$4=VSS); +end; +END + + nls2 = <<"END" +circuit INVB ($1=VDD,$2=IN,$3=VSS,$4=OUT); + device NMOS $1 (S=OUT,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $2 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +circuit TOP ($1=OUT,$2=VDD,$3=IN,$4=VSS); + subcircuit INVB $1 ($1=VDD,$2=INT,$3=VSS,$4=OUT); + subcircuit INVB $2 ($1=VDD,$2=IN,$3=VSS,$4=INT); +end; +END + + nl1 = RBA::Netlist::new + nl2 = RBA::Netlist::new + prep_nl(nl1, nls1) + prep_nl(nl2, nls2) + + logger = NetlistCompareTestLogger::new + comp = RBA::NetlistComparer::new(logger) + + good = comp.compare(nl1, nl2) + assert_equal(good, false) + + logger.clear + comp.same_circuits(nl1.circuit_by_name("INV"), nl2.circuit_by_name("INVB")) + + good = comp.compare(nl1, nl2) + + assert_equal(logger.text, <<"END") +begin_circuit INV INVB +match_nets VDD VDD +match_nets VSS VSS +match_nets OUT OUT +match_nets IN IN +match_pins $0 $1 +match_pins $1 $3 +match_pins $2 $0 +match_pins $3 $2 +match_devices $2 $1 +match_devices $1 $2 +end_circuit INV INVB MATCH +begin_circuit TOP TOP +match_nets OUT OUT +match_nets IN IN +match_nets VSS VSS +match_nets VDD VDD +match_nets INT INT +match_pins $0 $2 +match_pins $1 $0 +match_pins $2 $1 +match_pins $3 $3 +match_subcircuits $2 $1 +match_subcircuits $1 $2 +end_circuit TOP TOP MATCH +END + + assert_equal(good, true) + + end + + def test_9 + + nls1 = <<"END" +circuit NAND ($0=A,$1=B,$2=OUT,$3=VDD,$4=VSS); + device PMOS $1 (S=VDD,G=A,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $3 (S=VSS,G=A,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $4 (S=INT,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +circuit TOP ($0=IN1,$1=IN2,$2=OUT,$3=VDD,$4=VSS); + subcircuit NAND $1 ($0=IN1,$1=IN2,$2=INT,$3=VDD,$4=VSS); + subcircuit NAND $2 ($0=IN1,$1=INT,$2=OUT,$3=VDD,$4=VSS); +end; +END + + nls2 = <<"END" +circuit NAND ($0=A,$1=B,$2=OUT,$3=VDD,$4=VSS); + device PMOS $1 (S=VDD,G=A,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $3 (S=VSS,G=A,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device NMOS $4 (S=INT,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +circuit TOP ($0=IN1,$1=IN2,$2=OUT,$3=VDD,$4=VSS); + subcircuit NAND $2 ($0=IN1,$1=INT,$2=OUT,$3=VDD,$4=VSS); + subcircuit NAND $1 ($0=IN1,$1=IN2,$2=INT,$3=VDD,$4=VSS); +end; +END + + nl1 = RBA::Netlist::new + nl2 = RBA::Netlist::new + prep_nl(nl1, nls1) + prep_nl(nl2, nls2) + + logger = NetlistCompareTestLogger::new + comp = RBA::NetlistComparer::new(logger) + + comp.equivalent_pins(nl2.circuit_by_name("NAND"), 0, 1) + + good = comp.compare(nl1, nl2) + + assert_equal(logger.text, <<"END") +begin_circuit NAND NAND +match_nets VSS VSS +match_nets VDD VDD +match_nets B B +match_nets OUT OUT +match_nets A A +match_nets INT INT +match_pins $0 $0 +match_pins $1 $1 +match_pins $2 $2 +match_pins $3 $3 +match_pins $4 $4 +match_devices $1 $1 +match_devices $2 $2 +match_devices $3 $3 +match_devices $4 $4 +end_circuit NAND NAND MATCH +begin_circuit TOP TOP +match_nets OUT OUT +match_nets VSS VSS +match_nets VDD VDD +match_nets IN1 IN1 +match_nets INT INT +match_nets IN2 IN2 +match_pins $0 $0 +match_pins $1 $1 +match_pins $2 $2 +match_pins $3 $3 +match_pins $4 $4 +match_subcircuits $2 $1 +match_subcircuits $1 $2 +end_circuit TOP TOP MATCH +END + + assert_equal(good, true) + + end + +end + +load("test_epilogue.rb") + From da5680ef24205ffd04dde1910c777091e99f7c20 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 6 Apr 2019 15:19:43 +0200 Subject: [PATCH 28/54] Netlist compare: configurable device parameter compare scheme. --- src/db/db/dbDeviceClass.cc | 125 ++++++++++ src/db/db/dbDeviceClass.h | 94 +++++++- src/db/db/dbNetlistCompare.cc | 23 +- src/db/db/gsiDeclDbNetlist.cc | 126 +++++++++- src/db/db/gsiDeclDbNetlistCompare.cc | 57 +++-- src/db/unit_tests/dbNetlistCompareTests.cc | 258 +++++++++++++++++++++ testdata/ruby/dbNetlistCompare.rb | 130 +++++++++++ 7 files changed, 765 insertions(+), 48 deletions(-) diff --git a/src/db/db/dbDeviceClass.cc b/src/db/db/dbDeviceClass.cc index bc0c2864b..6b5b7f1a5 100644 --- a/src/db/db/dbDeviceClass.cc +++ b/src/db/db/dbDeviceClass.cc @@ -21,10 +21,81 @@ */ #include "dbDeviceClass.h" +#include "dbDevice.h" namespace db { +// -------------------------------------------------------------------------------- +// EqualDeviceParameters implementation + +static int compare_parameters (double pa, double pb, double absolute, double relative) +{ + double pa_min = pa - absolute; + double pa_max = pa + absolute; + + double mean = 0.5 * (fabs (pa) + fabs (pb)); + pa_min -= mean * relative; + pa_max += mean * relative; + + // NOTE: parameter values may be small (e.g. pF for caps) -> no epsilon + + if (pa_max < pb) { + return -1; + } else if (pa_min > pb) { + return 1; + } else { + return 0; + } +} + +EqualDeviceParameters::EqualDeviceParameters () +{ + // .. nothing yet .. +} + +EqualDeviceParameters::EqualDeviceParameters (size_t parameter_id) +{ + m_compare_set.push_back (std::make_pair (parameter_id, std::make_pair (0.0, 0.0))); +} + +EqualDeviceParameters::EqualDeviceParameters (size_t parameter_id, double relative, double absolute) +{ + m_compare_set.push_back (std::make_pair (parameter_id, std::make_pair (relative, absolute))); +} + +bool EqualDeviceParameters::less (const db::Device &a, const db::Device &b) const +{ + for (std::vector > >::const_iterator c = m_compare_set.begin (); c != m_compare_set.end (); ++c) { + int cmp = compare_parameters (a.parameter_value (c->first), b.parameter_value (c->first), c->second.first, c->second.second); + if (cmp != 0) { + return cmp < 0; + } + } + + return false; +} + +bool EqualDeviceParameters::equal (const db::Device &a, const db::Device &b) const +{ + for (std::vector > >::const_iterator c = m_compare_set.begin (); c != m_compare_set.end (); ++c) { + int cmp = compare_parameters (a.parameter_value (c->first), b.parameter_value (c->first), c->second.first, c->second.second); + if (cmp != 0) { + return false; + } + } + + return true; +} + +EqualDeviceParameters &EqualDeviceParameters::operator+= (const EqualDeviceParameters &other) +{ + for (std::vector > >::const_iterator c = other.m_compare_set.begin (); c != other.m_compare_set.end (); ++c) { + m_compare_set.push_back (*c); + } + return *this; +} + // -------------------------------------------------------------------------------- // DeviceClass class implementation @@ -137,4 +208,58 @@ size_t DeviceClass::terminal_id_for_name (const std::string &name) const throw tl::Exception (tl::to_string (tr ("Invalid terminal name")) + ": '" + name + "'"); } +bool DeviceClass::less (const db::Device &a, const db::Device &b) +{ + tl_assert (a.device_class () != 0); + tl_assert (b.device_class () != 0); + + const db::DeviceParameterCompareDelegate *pcd = a.device_class ()->mp_pc_delegate.get (); + if (! pcd) { + pcd = b.device_class ()->mp_pc_delegate.get (); + } + + if (pcd != 0) { + return pcd->less (a, b); + } else { + + const std::vector &pd = a.device_class ()->parameter_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + int cmp = compare_parameters (a.parameter_value (p->id ()), b.parameter_value (p->id ()), 0.0, 0.0); + if (cmp != 0) { + return cmp < 0; + } + } + + return false; + + } +} + +bool DeviceClass::equal (const db::Device &a, const db::Device &b) +{ + tl_assert (a.device_class () != 0); + tl_assert (b.device_class () != 0); + + const db::DeviceParameterCompareDelegate *pcd = a.device_class ()->mp_pc_delegate.get (); + if (! pcd) { + pcd = b.device_class ()->mp_pc_delegate.get (); + } + + if (pcd != 0) { + return pcd->equal (a, b); + } else { + + const std::vector &pd = a.device_class ()->parameter_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + int cmp = compare_parameters (a.parameter_value (p->id ()), b.parameter_value (p->id ()), 0.0, 0.0); + if (cmp != 0) { + return false; + } + } + + return true; + + } +} + } diff --git a/src/db/db/dbDeviceClass.h b/src/db/db/dbDeviceClass.h index c985ea7a3..bece69f4d 100644 --- a/src/db/db/dbDeviceClass.h +++ b/src/db/db/dbDeviceClass.h @@ -207,6 +207,53 @@ private: } }; +/** + * @brief A device parameter compare delegate + * + * Device parameter compare delegates are used to establish + * device equivalence in the context of netlist comparison. + */ +class DB_PUBLIC DeviceParameterCompareDelegate + : public gsi::ObjectBase, public tl::Object +{ +public: + DeviceParameterCompareDelegate () { } + virtual ~DeviceParameterCompareDelegate () { } + + virtual bool less (const db::Device &a, const db::Device &b) const = 0; + virtual bool equal (const db::Device &a, const db::Device &b) const = 0; +}; + +/** + * @brief A parameter compare delegate that compares several parameters either relative or absolute (or both) + * + * The reasoning behind this class is to supply a chainable compare delegate: ab = a + b + * where a and b are compare delegates for two different parameters and ab is the combined compare delegate. + */ +class DB_PUBLIC EqualDeviceParameters + : public DeviceParameterCompareDelegate +{ +public: + EqualDeviceParameters (); + EqualDeviceParameters (size_t parameter_id); + EqualDeviceParameters (size_t parameter_id, double relative, double absolute); + + virtual bool less (const db::Device &a, const db::Device &b) const; + virtual bool equal (const db::Device &a, const db::Device &b) const; + + EqualDeviceParameters &operator+= (const EqualDeviceParameters &other); + + EqualDeviceParameters operator+ (const EqualDeviceParameters &other) const + { + EqualDeviceParameters pc (*this); + pc += other; + return pc; + } + +private: + std::vector > > m_compare_set; +}; + /** * @brief A device class * @@ -228,14 +275,14 @@ public: /** * @brief Copy constructor * NOTE: do not use this copy constructor as the device class - * is intended to subclassing. + * is intended for subclassing. */ DeviceClass (const DeviceClass &other); /** * @brief Assignment * NOTE: do not use this copy constructor as the device class - * is intended to subclassing. + * is intended for subclassing. */ DeviceClass &operator= (const DeviceClass &other); @@ -365,7 +412,7 @@ public: size_t terminal_id_for_name (const std::string &name) const; /** - * @brief Clears the circuit + * @brief Clones the device class */ virtual DeviceClass *clone () const { @@ -414,6 +461,46 @@ public: return tid; } + /** + * @brief Compares the parameters of the devices a and b + * + * a and b are expected to originate from this or an equivalent device class having + * the same parameters. + * This is the "less" operation. If a parameter compare delegate is registered, this + * compare request will be forwarded to the delegate. + * + * If two devices with different device classes are compared and only one of + * the classes features a delegate, the one with the delegate is employed. + */ + static bool less (const db::Device &a, const db::Device &b); + + /** + * @brief Compares the parameters of the devices a and b + * + * a and b are expected to originate from this or an equivalent device class having + * the same parameters. + * This is the "equal" operation. If a parameter compare delegate is registered, this + * compare request will be forwarded to the delegate. + * + * If two devices with different device classes are compared and only one of + * the classes features a delegate, the one with the delegate is employed. + */ + static bool equal (const db::Device &a, const db::Device &b); + + /** + * @brief Registers a compare delegate + * + * The reasoning behind chosing a delegate is that a delegate is efficient + * also in scripts if one of the standard delegates is taken. + * + * The device class takes ownership of the delegate. + */ + virtual void set_parameter_compare_delegate (db::DeviceParameterCompareDelegate *delegate) + { + delegate->keep (); // assume transfer of ownership for scripts + mp_pc_delegate.reset (delegate); + } + private: friend class Netlist; @@ -421,6 +508,7 @@ private: std::vector m_terminal_definitions; std::vector m_parameter_definitions; db::Netlist *mp_netlist; + tl::shared_ptr mp_pc_delegate; void set_netlist (db::Netlist *nl) { diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 3dbd96430..89c21be52 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -40,16 +40,7 @@ struct DeviceCompare if (d1.second != d2.second) { return d1.second < d2.second; } - - const std::vector &dp = d1.first->device_class ()->parameter_definitions (); - for (std::vector::const_iterator i = dp.begin (); i != dp.end (); ++i) { - double v1 = d1.first->parameter_value (i->id ()); - double v2 = d2.first->parameter_value (i->id ()); - if (fabs (v1 - v2) > db::epsilon) { - return v1 < v2; - } - } - return false; + return db::DeviceClass::less (*d1.first, *d2.first); } bool equals (const std::pair &d1, const std::pair &d2) const @@ -57,16 +48,7 @@ struct DeviceCompare if (d1.second != d2.second) { return false; } - - const std::vector &dp = d1.first->device_class ()->parameter_definitions (); - for (std::vector::const_iterator i = dp.begin (); i != dp.end (); ++i) { - double v1 = d1.first->parameter_value (i->id ()); - double v2 = d2.first->parameter_value (i->id ()); - if (fabs (v1 - v2) > db::epsilon) { - return false; - } - } - return true; + return db::DeviceClass::equal (*d1.first, *d2.first); } }; @@ -1419,6 +1401,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, good = false; } else { if (mp_logger) { +dc.equals (dm->second, std::make_pair (d.operator-> (), device_cat)); // @@@ mp_logger->match_devices_with_different_parameters (dm->second.first, d.operator-> ()); } good = false; diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 29f927cf7..40a5968b9 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -473,11 +473,120 @@ Class decl_dbDeviceParameterDefinition ("db", "De "This class has been added in version 0.26." ); +namespace +{ + +/** + * @brief A DeviceParameterCompare implementation that allows reimplementation of the virtual methods + */ +class GenericDeviceParameterCompare + : public db::EqualDeviceParameters +{ +public: + GenericDeviceParameterCompare () + : db::EqualDeviceParameters () + { + // .. nothing yet .. + } + + virtual bool less (const db::Device &a, const db::Device &b) const + { + if (cb_less.can_issue ()) { + return cb_less.issue (&db::EqualDeviceParameters::less, a, b); + } else { + return db::EqualDeviceParameters::less (a, b); + } + } + + virtual bool equal (const db::Device &a, const db::Device &b) const + { + if (cb_equal.can_issue ()) { + return cb_equal.issue (&db::EqualDeviceParameters::equal, a, b); + } else { + return db::EqualDeviceParameters::equal (a, b); + } + } + + gsi::Callback cb_less, cb_equal; +}; + +} + +db::EqualDeviceParameters *make_equal_dp (size_t param_id, double absolute, double relative) +{ + return new db::EqualDeviceParameters (param_id, absolute, relative); +} + +Class decl_dbEqualDeviceParameters ("db", "EqualDeviceParameters", + gsi::constructor ("new", &make_equal_dp, gsi::arg ("param_id"), gsi::arg ("absolute", 0.0), gsi::arg ("relative", 0.0), + "@brief Creates a device parameter comparer for a single parameter.\n" + "'absolute' is the absolute deviation allowed for the parameter values. " + "'relative' is the relative deviation allowed for the parameter values (a value between 0 and 1).\n" + "\n" + "A value of 0 for both absolute and relative deviation means the parameters have to match exactly.\n" + "\n" + "If 'absolute' and 'relative' are both given, their deviations will add to the allowed difference between " + "two parameter values. The relative deviation will be applied to the mean value of both parameter values. " + "For example, when comparing parameter values of 40 and 60, a relative deviation of 0.35 means an absolute " + "deviation of 17.5 (= 0.35 * average of 40 and 60) which does not make both values match." + ) + + gsi::method ("+", &db::EqualDeviceParameters::operator+, gsi::arg ("other"), + "@brief Combines two parameters for comparison.\n" + "The '+' operator will join the parameter comparers and produce one that checks the combined parameters.\n" + ) + + gsi::method ("+=", &db::EqualDeviceParameters::operator+, gsi::arg ("other"), + "@brief Combines two parameters for comparison (in-place).\n" + "The '+=' operator will join the parameter comparers and produce one that checks the combined parameters.\n" + ), + "@brief A device parameter equality comparer.\n" + "Attach this object to a device class with \\DeviceClass#equal_parameters= to make the device " + "class use this comparer:\n" + "\n" + "@code\n" + "# 20nm tolerance for length:\n" + "equal_device_parameters = RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS4Transistor::PARAM_L, 0.02, 0.0)\n" + "# one percent tolerance for width:\n" + "equal_device_parameters += RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS4Transistor::PARAM_W, 0.0, 0.01)\n" + "# applies the compare delegate:\n" + "netlist.device_class_by_name(\"NMOS\").equal_parameters = equal_device_parameters\n" + "@/code\n" + "\n" + "You can use this class to specify fuzzy equality criteria for the comparison of device parameters in " + "netlist verification or to confine the equality of devices to certain parameters only.\n" + "\n" + "This class has been added in version 0.26." +); + +Class decl_GenericDeviceParameterCompare (decl_dbEqualDeviceParameters, "db", "GenericDeviceParameterCompare", + gsi::callback ("equal", &GenericDeviceParameterCompare::equal, &GenericDeviceParameterCompare::cb_equal, gsi::arg ("device_a"), gsi::arg ("device_b"), + "@brief Compares the parameters of two devices for equality. " + "Returns true, if the parameters of device a and b are considered equal." + ) + + gsi::callback ("less", &GenericDeviceParameterCompare::less, &GenericDeviceParameterCompare::cb_less, gsi::arg ("device_a"), gsi::arg ("device_b"), + "@brief Compares the parameters of two devices for a begin less than b. " + "Returns true, if the parameters of device a are considered less than those of device b." + ), + "@brief A class implementing the comparison of device parameters.\n" + "Reimplement this class to provide a custom device parameter compare scheme.\n" + "Attach this object to a device class with \\DeviceClass#equal_parameters= to make the device " + "class use this comparer.\n" + "\n" + "This class is intended for special cases. In most scenarios it is easier to use \\EqualDeviceParameters instead of " + "implementing a custom comparer class.\n" + "\n" + "This class has been added in version 0.26." +); + static tl::id_type id_of_device_class (const db::DeviceClass *cls) { return tl::id_of (cls); } +static void equal_parameters (db::DeviceClass *cls, db::EqualDeviceParameters *comparer) +{ + cls->set_parameter_compare_delegate (comparer); +} + Class decl_dbDeviceClass ("db", "DeviceClass", gsi::method ("name", &db::DeviceClass::name, "@brief Gets the name of the device class." @@ -533,6 +642,15 @@ Class decl_dbDeviceClass ("db", "DeviceClass", "@brief Returns the terminal ID of the terminal with the given name.\n" "An exception is thrown if there is no terminal with the given name. Use \\has_terminal to check " "whether the name is a valid terminal name." + ) + + gsi::method_ext ("equal_parameters=", &equal_parameters, gsi::arg ("comparer"), + "@brief Specifies a device parameter comparer for netlist verification.\n" + "By default, all devices are compared with all parameters. If you want to select only certain parameters " + "for comparison or use a fuzzy compare criterion, use an \\EqualDeviceParameters object and assign it " + "to the device class of one netlist. You can also chain multiple \\EqualDeviceParameters objects with the '+' operator " + "for specifying multiple parameters in the equality check.\n" + "\n" + "In special cases, you can even implement a custom compare scheme by deriving your own comparer from the \\GenericDeviceParameterCompare class." ), "@brief A class describing a specific type of device.\n" "Device class objects live in the context of a \\Netlist object. After a " @@ -1231,14 +1349,14 @@ Class db_NetlistSpiceWriter (db_NetlistWriter, "db", "Ne "@code\n" "writer = RBA::NetlistSpiceWriter::new\n" "netlist.write(path, writer)\n" - "@endcode\n" + "@/code\n" "\n" "You can give a custom description for the headline:\n" "\n" "@code\n" "writer = RBA::NetlistSpiceWriter::new\n" "netlist.write(path, writer, \"A custom description\")\n" - "@endcode\n" + "@/code\n" "\n" "To customize the output, you can use a device writer delegate.\n" "The delegate is an object of a class derived from \\NetlistSpiceWriterDelegate which " @@ -1279,7 +1397,7 @@ Class db_NetlistSpiceWriter (db_NetlistWriter, "db", "Ne "# write the netlist with delegate:\n" "writer = RBA::NetlistSpiceWriter::new(MyDelegate::new)\n" "netlist.write(path, writer)\n" - "@endcode\n" + "@/code\n" "\n" "This class has been introduced in version 0.26." ); @@ -1305,7 +1423,7 @@ Class db_NetlistSpiceReader (db_NetlistReader, "db", "Ne "writer = RBA::NetlistSpiceReader::new\n" "netlist = RBA::Netlist::new\n" "netlist.read(path, reader)\n" - "@endcode\n" + "@/code\n" "\n" "This class has been introduced in version 0.26." ); diff --git a/src/db/db/gsiDeclDbNetlistCompare.cc b/src/db/db/gsiDeclDbNetlistCompare.cc index 50b91ae69..2e7b6485e 100644 --- a/src/db/db/gsiDeclDbNetlistCompare.cc +++ b/src/db/db/gsiDeclDbNetlistCompare.cc @@ -29,7 +29,7 @@ namespace { * @brief A NetlistDeviceExtractor implementation that allows reimplementation of the virtual methods */ class GenericNetlistCompareLogger - : public db::NetlistCompareLogger + : public gsi::ObjectBase, public db::NetlistCompareLogger { public: GenericNetlistCompareLogger () @@ -296,6 +296,7 @@ public: }; } + namespace gsi { @@ -340,11 +341,13 @@ Class decl_GenericNetlistCompareLogger ("db", "Gene gsi::callback ("match_nets", &GenericNetlistCompareLogger::match_nets, &GenericNetlistCompareLogger::cb_match_nets, gsi::arg ("a"), gsi::arg ("b"), "@brief This function is called when two nets are identified.\n" "If two nets are identified as a corresponding pair, this method will be called with both nets.\n" - "If the nets can be paired, but this match is ambiguous, \\match_ambiguous_nets_fb will be called instead.\n" + "If the nets can be paired, but this match is ambiguous, \\match_ambiguous_nets will be called instead.\n" "If nets can't be matched to a partner, \\net_mismatch will be called.\n" ) + gsi::callback ("match_ambiguous_nets", &GenericNetlistCompareLogger::match_ambiguous_nets, &GenericNetlistCompareLogger::cb_match_ambiguous_nets, gsi::arg ("a"), gsi::arg ("b"), "@brief This function is called when two nets are identified, but this choice is ambiguous.\n" + "This choice is a last-resort fallback to allow continuation of the compare procedure. It is likely that this " + "compare will fail later. Looking for ambiguous nets allows deduction of the origin of this faulty decision. " "See \\match_nets for more details." ) + gsi::callback ("net_mismatch", &GenericNetlistCompareLogger::net_mismatch, &GenericNetlistCompareLogger::cb_net_mismatch, gsi::arg ("a"), gsi::arg ("b"), @@ -369,7 +372,7 @@ Class decl_GenericNetlistCompareLogger ("db", "Gene ) + gsi::callback ("device_mismatch", &GenericNetlistCompareLogger::device_mismatch, &GenericNetlistCompareLogger::cb_device_mismatch, gsi::arg ("a"), gsi::arg ("b"), "@brief This function is called when two devices can't be paired.\n" - "This will report the device considered in a or b. The other argument is nil." + "This will report the device considered in a or b. The other argument is nil. " "See \\match_devices for details.\n" ) + gsi::callback ("match_pins", &GenericNetlistCompareLogger::match_pins, &GenericNetlistCompareLogger::cb_match_pins, gsi::arg ("a"), gsi::arg ("b"), @@ -379,7 +382,7 @@ Class decl_GenericNetlistCompareLogger ("db", "Gene ) + gsi::callback ("pin_mismatch", &GenericNetlistCompareLogger::pin_mismatch, &GenericNetlistCompareLogger::cb_pin_mismatch, gsi::arg ("a"), gsi::arg ("b"), "@brief This function is called when two pins can't be paired.\n" - "This will report the pin considered in a or b. The other argument is nil." + "This will report the pin considered in a or b. The other argument is nil. " "See \\match_pins for details.\n" ) + gsi::callback ("match_subcircuits", &GenericNetlistCompareLogger::match_subcircuits, &GenericNetlistCompareLogger::cb_match_subcircuits, gsi::arg ("a"), gsi::arg ("b"), @@ -389,7 +392,7 @@ Class decl_GenericNetlistCompareLogger ("db", "Gene ) + gsi::callback ("subcircuit_mismatch", &GenericNetlistCompareLogger::subcircuit_mismatch, &GenericNetlistCompareLogger::cb_subcircuit_mismatch, gsi::arg ("a"), gsi::arg ("b"), "@brief This function is called when two subcircuits can't be paired.\n" - "This will report the subcircuit considered in a or b. The other argument is nil." + "This will report the subcircuit considered in a or b. The other argument is nil. " "See \\match_subcircuits for details.\n" ), "@brief An event receiver for the netlist compare feature.\n" @@ -400,19 +403,28 @@ Class decl_GenericNetlistCompareLogger ("db", "Gene "This class has been introduced in version 0.26.\n" ); -static db::NetlistComparer *make_comparer (GenericNetlistCompareLogger *logger) +static db::NetlistComparer *make_comparer0 () +{ + return new db::NetlistComparer (0); +} + +static db::NetlistComparer *make_comparer1 (GenericNetlistCompareLogger *logger) { return new db::NetlistComparer (logger); } Class decl_dbNetlistComparer ("db", "NetlistComparer", - gsi::constructor ("new", &make_comparer, gsi::arg ("logger", (GenericNetlistCompareLogger *) 0), - "@brief Creates a new comparer object." + gsi::constructor ("new", &make_comparer0, + "@brief Creates a new comparer object.\n" + "See the class description for more details." + ) + + gsi::constructor ("new", &make_comparer1, gsi::arg ("logger"), + "@brief Creates a new comparer object.\n" "The logger is a delegate or event receiver which the comparer will send compare events to. " "See the class description for more details." ) + gsi::method ("same_nets", &db::NetlistComparer::same_nets, gsi::arg ("net_a"), gsi::arg ("net_b"), - "@brief Marks two nets as identical\n" + "@brief Marks two nets as identical.\n" "This makes a net net_a in netlist a identical to the corresponding\n" "net net_b in netlist b (see \\compare).\n" "Otherwise, the algorithm will try to identify nets according to their topology. " @@ -420,43 +432,46 @@ Class decl_dbNetlistComparer ("db", "NetlistComparer", "these hints to derive further identities." ) + gsi::method ("equivalent_pins", (void (db::NetlistComparer::*) (const db::Circuit *, size_t, size_t)) &db::NetlistComparer::equivalent_pins, gsi::arg ("circuit_b"), gsi::arg ("pin_id1"), gsi::arg ("pin_id2"), - "@brief Marks two pins of the given circuit as equivalent (i.e. they can be swapped)\n" + "@brief Marks two pins of the given circuit as equivalent (i.e. they can be swapped).\n" "Only circuits from the second input can be given swappable pins. " "This will imply the same swappable pins on the equivalent circuit of the first input. " "To mark multiple pins as swappable, use the version that takes a list of pins." ) + gsi::method ("equivalent_pins", (void (db::NetlistComparer::*) (const db::Circuit *, const std::vector &)) &db::NetlistComparer::equivalent_pins, gsi::arg ("circuit_b"), gsi::arg ("pin_ids"), - "@brief Marks several pins of the given circuit as equivalent (i.e. they can be swapped)\n" + "@brief Marks several pins of the given circuit as equivalent (i.e. they can be swapped).\n" "Only circuits from the second input can be given swappable pins. " "This will imply the same swappable pins on the equivalent circuit of the first input. " "This version is a generic variant of the two-pin version of this method." ) + gsi::method ("same_device_classes", &db::NetlistComparer::same_device_classes, gsi::arg ("dev_cls_a"), gsi::arg ("dev_cls_b"), - "@brief Marks two device classes as identical\n" + "@brief Marks two device classes as identical.\n" "This makes a device class dev_cls_a in netlist a identical to the corresponding\n" "device class dev_cls_b in netlist b (see \\compare).\n" "By default device classes with the same name are identical.\n" ) + gsi::method ("same_circuits", &db::NetlistComparer::same_circuits, gsi::arg ("circuit_a"), gsi::arg ("circuit_b"), - "@brief Marks two circuits as identical\n" - "This method makes a circuit circuit_a in netlist a identical to the corresponding\n" - "circuit circuit_b in netlist b (see \\compare). By default circuits with the same name are identical.\n" + "@brief Marks two circuits as identical.\n" + "This method makes a circuit circuit_a in netlist a identical to the corresponding\n" + "circuit circuit_b in netlist b (see \\compare). By default circuits with the same name are identical.\n" ) + gsi::method ("compare", &db::NetlistComparer::compare, gsi::arg ("netlist_a"), gsi::arg ("netlist_b"), - "@brief Compares two netlists\n" + "@brief Compares two netlists.\n" "This method will perform the actual netlist compare. It will return true if both netlists are identical. " "If the comparer has been configured with \\same_nets or similar methods, the objects given there must " "be located inside 'circuit_a' and 'circuit_b' respectively." ), "@brief Compares two netlists\n" - "This class performs the actual comparison of two netlists.\n" - "It can be used with an event receiver to log the errors and net mismatches. " + "This class performs a comparison of two netlists.\n" + "It can be used with an event receiver (logger) to track the errors and net mismatches. " "Event receivers are derived from class \\GenericNetlistCompareLogger." "\n" - "The netlist comparer can be configured in different ways. Specifically hints can be given for nets, device classes or circuits. " - "Equivalence hints can be given with \\same_nets, \\same_circuits etc.\n" + "The netlist comparer can be configured in different ways. Specific hints can be given for nets, device classes or circuits " + "to improve efficiency and reliability of the graph equivalence deduction algorithm. " + "For example, objects can be marked as equivalent using \\same_nets, \\same_circuits etc. " + "The compare algorithm will then use these hints to derive further equivalences. This way, " + "ambiguities can be resolved.\n" "\n" - "Another configuration relates to swappable pins of subcircuits. If pins marked this way, the compare algorithm may swap them to " + "Another configuration option relates to swappable pins of subcircuits. If pins are marked this way, the compare algorithm may swap them to " "achieve net matching. Swappable pins belong to an 'equivalence group' and can be defined with \\equivalent_pins.\n" "\n" "This class has been introduced in version 0.26." diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 43f6b9eba..f873f5bcf 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -199,6 +199,106 @@ static void prep_nl (db::Netlist &nl, const char *str) nl.from_string (str); } +TEST(0_EqualDeviceParameters) +{ + db::DeviceClassMOS3Transistor dc; + + db::EqualDeviceParameters *eqp = new db::EqualDeviceParameters (); + *eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.0, 0.0); + dc.set_parameter_compare_delegate (eqp); + + db::Device d1 (&dc); + db::Device d2 (&dc); + + d1.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 40.0); + d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 40.0); + + EXPECT_EQ (dc.equal (d1, d2), true); + EXPECT_EQ (dc.equal (d2, d1), true); + EXPECT_EQ (dc.less (d1, d2), false); + EXPECT_EQ (dc.less (d2, d1), false); + + d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 41.0); + + EXPECT_EQ (dc.equal (d1, d2), false); + EXPECT_EQ (dc.equal (d2, d1), false); + EXPECT_EQ (dc.less (d1, d2), true); + EXPECT_EQ (dc.less (d2, d1), false); + + eqp = new db::EqualDeviceParameters (); + *eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.9, 0.0); + dc.set_parameter_compare_delegate (eqp); + + EXPECT_EQ (dc.equal (d1, d2), false); + EXPECT_EQ (dc.equal (d2, d1), false); + EXPECT_EQ (dc.less (d1, d2), true); + EXPECT_EQ (dc.less (d2, d1), false); + + eqp = new db::EqualDeviceParameters (); + *eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 1.0, 0.0); + dc.set_parameter_compare_delegate (eqp); + + EXPECT_EQ (dc.equal (d1, d2), true); + EXPECT_EQ (dc.equal (d2, d1), true); + EXPECT_EQ (dc.less (d1, d2), false); + EXPECT_EQ (dc.less (d2, d1), false); + + eqp = new db::EqualDeviceParameters (); + *eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 1.1, 0.0); + dc.set_parameter_compare_delegate (eqp); + + EXPECT_EQ (dc.equal (d1, d2), true); + EXPECT_EQ (dc.equal (d2, d1), true); + EXPECT_EQ (dc.less (d1, d2), false); + EXPECT_EQ (dc.less (d2, d1), false); + + eqp = new db::EqualDeviceParameters (); + *eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.5, 0.01); + dc.set_parameter_compare_delegate (eqp); + + EXPECT_EQ (dc.equal (d1, d2), false); + EXPECT_EQ (dc.equal (d2, d1), false); + EXPECT_EQ (dc.less (d1, d2), true); + EXPECT_EQ (dc.less (d2, d1), false); + + eqp = new db::EqualDeviceParameters (); + *eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.5, 0.013); + dc.set_parameter_compare_delegate (eqp); + + EXPECT_EQ (dc.equal (d1, d2), true); + EXPECT_EQ (dc.equal (d2, d1), true); + EXPECT_EQ (dc.less (d1, d2), false); + EXPECT_EQ (dc.less (d2, d1), false); + + d1.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.5); + d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.2); + + EXPECT_EQ (dc.equal (d1, d2), true); + EXPECT_EQ (dc.equal (d2, d1), true); + EXPECT_EQ (dc.less (d1, d2), false); + EXPECT_EQ (dc.less (d2, d1), false); + + eqp = new db::EqualDeviceParameters (); + *eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.5, 0.013); + *eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_W); + dc.set_parameter_compare_delegate (eqp); + + EXPECT_EQ (dc.equal (d1, d2), false); + EXPECT_EQ (dc.equal (d2, d1), false); + EXPECT_EQ (dc.less (d1, d2), false); + EXPECT_EQ (dc.less (d2, d1), true); + + eqp = new db::EqualDeviceParameters (); + *eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.5, 0.013); + *eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_W, 0.3, 1e-6); + dc.set_parameter_compare_delegate (eqp); + + EXPECT_EQ (dc.equal (d1, d2), true); + EXPECT_EQ (dc.equal (d2, d1), true); + EXPECT_EQ (dc.less (d1, d2), false); + EXPECT_EQ (dc.less (d2, d1), false); +} + TEST(1_SimpleInverter) { const char *nls1 = @@ -496,6 +596,164 @@ TEST(5_BufferTwoPathsDifferentParameters) "end_circuit BUF BUF NOMATCH" ); EXPECT_EQ (good, false); + + logger.clear (); + nl2.device_class_by_name ("NMOS")->set_parameter_compare_delegate (new db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 1.5, 0.0)); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit BUF BUF\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_ambiguous_nets INT $10\n" + "match_ambiguous_nets INT2 $11\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $3 $2\n" + "match_devices $1 $1\n" + "match_devices $3 $2\n" + "match_devices $5 $3\n" + "match_devices $7 $4\n" + "match_devices $2 $5\n" + "match_devices $4 $6\n" + "match_devices $6 $7\n" + "match_devices $8 $8\n" + "end_circuit BUF BUF MATCH" + ); + EXPECT_EQ (good, true); + + logger.clear (); + nl2.device_class_by_name ("NMOS")->set_parameter_compare_delegate (new db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.0, 0.0)); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit BUF BUF\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_ambiguous_nets INT $10\n" + "match_nets INT2 $11\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $3 $2\n" + "match_devices $1 $1\n" + "match_devices $3 $2\n" + "match_devices $5 $3\n" + "match_devices $7 $4\n" + "match_devices $2 $5\n" + "match_devices $4 $6\n" + "match_devices_with_different_parameters $6 $7\n" + "match_devices $8 $8\n" + "end_circuit BUF BUF NOMATCH" + ); + EXPECT_EQ (good, false); + + logger.clear (); + nl2.device_class_by_name ("NMOS")->set_parameter_compare_delegate (new db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.0, 0.2)); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit BUF BUF\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_ambiguous_nets INT $10\n" + "match_nets INT2 $11\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $3 $2\n" + "match_devices $1 $1\n" + "match_devices $3 $2\n" + "match_devices $5 $3\n" + "match_devices $7 $4\n" + "match_devices $2 $5\n" + "match_devices $4 $6\n" + "match_devices_with_different_parameters $6 $7\n" + "match_devices $8 $8\n" + "end_circuit BUF BUF NOMATCH" + ); + EXPECT_EQ (good, false); + + logger.clear (); + nl2.device_class_by_name ("NMOS")->set_parameter_compare_delegate (new db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.0, 0.4)); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit BUF BUF\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_ambiguous_nets INT $10\n" + "match_ambiguous_nets INT2 $11\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $3 $2\n" + "match_devices $1 $1\n" + "match_devices $3 $2\n" + "match_devices $5 $3\n" + "match_devices $7 $4\n" + "match_devices $2 $5\n" + "match_devices $4 $6\n" + "match_devices $6 $7\n" + "match_devices $8 $8\n" + "end_circuit BUF BUF MATCH" + ); + EXPECT_EQ (good, true); + + logger.clear (); + db::EqualDeviceParameters eq_dp = db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_W) + db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.2, 0.0); + nl2.device_class_by_name ("NMOS")->set_parameter_compare_delegate (new db::EqualDeviceParameters (eq_dp)); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit BUF BUF\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_ambiguous_nets INT $10\n" + "match_ambiguous_nets INT2 $11\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $3 $2\n" + "match_devices $1 $1\n" + "match_devices $3 $2\n" + "match_devices $5 $3\n" + "match_devices $7 $4\n" + "match_devices $2 $5\n" + "match_devices $4 $6\n" + "match_devices $6 $7\n" + "match_devices $8 $8\n" + "end_circuit BUF BUF MATCH" + ); + EXPECT_EQ (good, true); + + logger.clear (); + eq_dp = db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_W) + db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L); + nl2.device_class_by_name ("NMOS")->set_parameter_compare_delegate (new db::EqualDeviceParameters (eq_dp)); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit BUF BUF\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_ambiguous_nets INT $10\n" + "match_nets INT2 $11\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $3 $2\n" + "match_devices $1 $1\n" + "match_devices $3 $2\n" + "match_devices $5 $3\n" + "match_devices $7 $4\n" + "match_devices $2 $5\n" + "match_devices $4 $6\n" + "match_devices_with_different_parameters $6 $7\n" + "match_devices $8 $8\n" + "end_circuit BUF BUF NOMATCH" + ); + EXPECT_EQ (good, false); } TEST(5_BufferTwoPathsDifferentDeviceClasses) diff --git a/testdata/ruby/dbNetlistCompare.rb b/testdata/ruby/dbNetlistCompare.rb index 69d9f44ce..b9d772e3c 100644 --- a/testdata/ruby/dbNetlistCompare.rb +++ b/testdata/ruby/dbNetlistCompare.rb @@ -448,6 +448,92 @@ END assert_equal(good, false) + logger.clear + eqp = RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS3Transistor::PARAM_L, 0.2, 0.0) + nl2.device_class_by_name("NMOS").equal_parameters = eqp + good = comp.compare(nl1, nl2) + + assert_equal(logger.text, <<"END") +begin_circuit BUF BUF +match_nets OUT OUT +match_nets IN IN +match_ambiguous_nets INT $10 +match_ambiguous_nets INT2 $11 +match_pins $0 $1 +match_pins $1 $3 +match_pins $2 $0 +match_pins $3 $2 +match_devices $1 $1 +match_devices $3 $2 +match_devices $5 $3 +match_devices $7 $4 +match_devices $2 $5 +match_devices $4 $6 +match_devices $6 $7 +match_devices $8 $8 +end_circuit BUF BUF MATCH +END + + assert_equal(good, true) + + logger.clear + eqp = RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS3Transistor::PARAM_W, 0.01, 0.0) + eqp = eqp + RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS3Transistor::PARAM_L, 0.2, 0.0) + nl2.device_class_by_name("NMOS").equal_parameters = eqp + good = comp.compare(nl1, nl2) + + assert_equal(logger.text, <<"END") +begin_circuit BUF BUF +match_nets OUT OUT +match_nets IN IN +match_ambiguous_nets INT $10 +match_ambiguous_nets INT2 $11 +match_pins $0 $1 +match_pins $1 $3 +match_pins $2 $0 +match_pins $3 $2 +match_devices $1 $1 +match_devices $3 $2 +match_devices $5 $3 +match_devices $7 $4 +match_devices $2 $5 +match_devices $4 $6 +match_devices $6 $7 +match_devices $8 $8 +end_circuit BUF BUF MATCH +END + + assert_equal(good, true) + + logger.clear + eqp = RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS3Transistor::PARAM_W, 0.01, 0.0) + eqp += RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS3Transistor::PARAM_L, 0.2, 0.0) + nl2.device_class_by_name("NMOS").equal_parameters = eqp + good = comp.compare(nl1, nl2) + + assert_equal(logger.text, <<"END") +begin_circuit BUF BUF +match_nets OUT OUT +match_nets IN IN +match_ambiguous_nets INT $10 +match_ambiguous_nets INT2 $11 +match_pins $0 $1 +match_pins $1 $3 +match_pins $2 $0 +match_pins $3 $2 +match_devices $1 $1 +match_devices $3 $2 +match_devices $5 $3 +match_devices $7 $4 +match_devices $2 $5 +match_devices $4 $6 +match_devices $6 $7 +match_devices $8 $8 +end_circuit BUF BUF MATCH +END + + assert_equal(good, true) + end def test_6 @@ -735,6 +821,50 @@ match_pins $4 $4 match_subcircuits $2 $1 match_subcircuits $1 $2 end_circuit TOP TOP MATCH +END + + assert_equal(good, true) + + logger.clear + comp = RBA::NetlistComparer::new(logger) + + comp.equivalent_pins(nl2.circuit_by_name("NAND"), [ 1, 0 ]) + + good = comp.compare(nl1, nl2) + + assert_equal(logger.text, <<"END") +begin_circuit NAND NAND +match_nets VSS VSS +match_nets VDD VDD +match_nets B B +match_nets OUT OUT +match_nets A A +match_nets INT INT +match_pins $0 $0 +match_pins $1 $1 +match_pins $2 $2 +match_pins $3 $3 +match_pins $4 $4 +match_devices $1 $1 +match_devices $2 $2 +match_devices $3 $3 +match_devices $4 $4 +end_circuit NAND NAND MATCH +begin_circuit TOP TOP +match_nets OUT OUT +match_nets IN2 IN2 +match_nets VSS VSS +match_nets VDD VDD +match_nets IN1 IN1 +match_nets INT INT +match_pins $0 $0 +match_pins $1 $1 +match_pins $2 $2 +match_pins $3 $3 +match_pins $4 $4 +match_subcircuits $2 $1 +match_subcircuits $1 $2 +end_circuit TOP TOP MATCH END assert_equal(good, true) From aad52b77ba684871c09a741a0a408af9ee912cf4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 6 Apr 2019 19:46:13 +0200 Subject: [PATCH 29/54] Netlist compare: added the ability to filter small caps and high resistance devices --- src/db/db/dbNetlistCompare.cc | 78 +++++++++++++++++++-- src/db/db/dbNetlistCompare.h | 12 ++++ src/db/db/gsiDeclDbNetlistCompare.cc | 8 +++ src/db/unit_tests/dbNetlistCompareTests.cc | 67 ++++++++++++++++++ testdata/ruby/dbNetlistCompare.rb | 80 ++++++++++++++++++++++ 5 files changed, 238 insertions(+), 7 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 89c21be52..150968c94 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -22,10 +22,11 @@ */ #include "dbNetlistCompare.h" -#include "dbHash.h" +#include "dbNetlistDeviceClasses.h" #include "tlProgress.h" #include "tlTimer.h" #include "tlEquivalenceClusters.h" +#include "tlLog.h" namespace db { @@ -169,6 +170,40 @@ private: std::map m_pin_map, m_rev_pin_map; }; +// -------------------------------------------------------------------------------------------------------------------- +// DeviceFilter definition and implementation + +class DeviceFilter +{ +public: + DeviceFilter (double cap_threshold, double res_threshold) + : m_cap_threshold (cap_threshold), m_res_threshold (res_threshold) + { + // .. nothing yet .. + } + + bool filter (const db::Device *device) const + { + const db::DeviceClassResistor *res = dynamic_cast (device->device_class ()); + const db::DeviceClassCapacitor *cap = dynamic_cast (device->device_class ()); + + if (res) { + if (m_res_threshold > 0.0 && device->parameter_value (db::DeviceClassResistor::param_id_R) > m_res_threshold) { + return false; + } + } else if (cap) { + if (m_cap_threshold > 0.0 && device->parameter_value (db::DeviceClassCapacitor::param_id_C) < m_cap_threshold) { + return false; + } + } + + return true; + } + +private: + double m_cap_threshold, m_res_threshold; +}; + // -------------------------------------------------------------------------------------------------------------------- // DeviceCategorizer definition and implementation @@ -418,7 +453,7 @@ public: typedef std::vector, std::pair > >::const_iterator edge_iterator; - NetGraphNode (const db::Net *net, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const std::map *circuit_map, const CircuitPinMapper *pin_map) + NetGraphNode (const db::Net *net, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const DeviceFilter &device_filter, const std::map *circuit_map, const CircuitPinMapper *pin_map) : mp_net (net), m_other_net_index (std::numeric_limits::max ()) { if (! net) { @@ -517,6 +552,10 @@ public: for (db::Net::const_terminal_iterator i = net->begin_terminals (); i != net->end_terminals (); ++i) { const db::Device *d = i->device (); + if (! device_filter.filter (d)) { + continue; + } + size_t device_cat = device_categorizer.cat_for_device (d); size_t terminal1_id = translate_terminal_id (i->terminal_id (), d); @@ -731,7 +770,7 @@ public: // .. nothing yet .. } - void build (const db::Circuit *c, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const std::map *circuit_and_pin_mapping, const CircuitPinMapper *circuit_pin_mapper) + void build (const db::Circuit *c, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const db::DeviceFilter &device_filter, const std::map *circuit_and_pin_mapping, const CircuitPinMapper *circuit_pin_mapper) { tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Building net graph for circuit: ")) + c->name ()); @@ -739,7 +778,7 @@ public: m_net_index.clear (); // create a dummy node for a null net - m_nodes.push_back (NetGraphNode (0, device_categorizer, circuit_categorizer, circuit_and_pin_mapping, circuit_pin_mapper)); + m_nodes.push_back (NetGraphNode (0, device_categorizer, circuit_categorizer, device_filter, circuit_and_pin_mapping, circuit_pin_mapper)); size_t nets = 0; for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { @@ -748,7 +787,7 @@ public: m_nodes.reserve (nets); for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - NetGraphNode node (n.operator-> (), device_categorizer, circuit_categorizer, circuit_and_pin_mapping, circuit_pin_mapper); + NetGraphNode node (n.operator-> (), device_categorizer, circuit_categorizer, device_filter, circuit_and_pin_mapping, circuit_pin_mapper); m_nodes.push_back (node); } @@ -906,6 +945,21 @@ NetlistComparer::NetlistComparer (NetlistCompareLogger *logger) mp_device_categorizer.reset (new DeviceCategorizer ()); mp_circuit_categorizer.reset (new CircuitCategorizer ()); mp_circuit_pin_mapper.reset (new CircuitPinMapper ()); + + m_cap_threshold = -1.0; // not set + m_res_threshold = -1.0; // not set +} + +void +NetlistComparer::exclude_caps (double threshold) +{ + m_cap_threshold = threshold; +} + +void +NetlistComparer::exclude_resistors (double threshold) +{ + m_res_threshold = threshold; } void @@ -1112,12 +1166,14 @@ compute_subcircuit_key (const db::SubCircuit &subcircuit, const db::NetDeviceGra bool NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, const std::vector > &net_identity, bool &pin_mismatch, std::map &c12_circuit_and_pin_mapping, std::map &c22_circuit_and_pin_mapping) const { + db::DeviceFilter device_filter (m_cap_threshold, m_res_threshold); + db::NetDeviceGraph g1, g2; // NOTE: for normalization we map all subcircuits of c1 to c2. // Also, pin swapping will only happen there. - g1.build (c1, device_categorizer, circuit_categorizer, &c12_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); - g2.build (c2, device_categorizer, circuit_categorizer, &c22_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); + g1.build (c1, device_categorizer, circuit_categorizer, device_filter, &c12_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); + g2.build (c2, device_categorizer, circuit_categorizer, device_filter, &c22_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); // Match dummy nodes for null nets g1.identify (0, 0); @@ -1342,6 +1398,10 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (db::Circuit::const_device_iterator d = c1->begin_devices (); d != c1->end_devices (); ++d) { + if (! device_filter.filter (d.operator-> ())) { + continue; + } + std::vector > k = compute_device_key (*d, g1); bool mapped = true; @@ -1365,6 +1425,10 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (db::Circuit::const_device_iterator d = c2->begin_devices (); d != c2->end_devices (); ++d) { + if (! device_filter.filter (d.operator-> ())) { + continue; + } + std::vector > k = compute_device_key (*d, g2); bool mapped = true; diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h index b941aef52..eb9364733 100644 --- a/src/db/db/dbNetlistCompare.h +++ b/src/db/db/dbNetlistCompare.h @@ -195,6 +195,16 @@ public: */ void same_circuits (const db::Circuit *ca, const db::Circuit *cb); + /** + * @brief Exclude caps with less than the given capacity value + */ + void exclude_caps (double threshold); + + /** + * @brief Exclude resistors with more than the given resistance value + */ + void exclude_resistors (double threshold); + /** * @brief Actually compares the two netlists */ @@ -209,6 +219,8 @@ protected: std::auto_ptr mp_circuit_pin_mapper; std::auto_ptr mp_device_categorizer; std::auto_ptr mp_circuit_categorizer; + double m_cap_threshold; + double m_res_threshold; }; } diff --git a/src/db/db/gsiDeclDbNetlistCompare.cc b/src/db/db/gsiDeclDbNetlistCompare.cc index 2e7b6485e..36b9d86d3 100644 --- a/src/db/db/gsiDeclDbNetlistCompare.cc +++ b/src/db/db/gsiDeclDbNetlistCompare.cc @@ -454,6 +454,14 @@ Class decl_dbNetlistComparer ("db", "NetlistComparer", "This method makes a circuit circuit_a in netlist a identical to the corresponding\n" "circuit circuit_b in netlist b (see \\compare). By default circuits with the same name are identical.\n" ) + + gsi::method ("min_capacitance=", &db::NetlistComparer::exclude_caps, gsi::arg ("threshold"), + "@brief Excludes all capacitor devices with a capacitance values less than the given threshold.\n" + "To reset this constraint, set this attribute to zero." + ) + + gsi::method ("max_resistance=", &db::NetlistComparer::exclude_resistors, gsi::arg ("threshold"), + "@brief Excludes all resistor devices with a resistance values higher than the given threshold.\n" + "To reset this constraint, set this attribute to zero." + ) + gsi::method ("compare", &db::NetlistComparer::compare, gsi::arg ("netlist_a"), gsi::arg ("netlist_b"), "@brief Compares two netlists.\n" "This method will perform the actual netlist compare. It will return true if both netlists are identical. " diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index f873f5bcf..39338ad95 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -385,6 +385,73 @@ TEST(1_SimpleInverterMatchedDeviceClasses) EXPECT_EQ (good, true); } +TEST(1_SimpleInverterSkippedDevices) +{ + const char *nls1 = + "circuit INV ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device CAP $2 (A=OUT,B=IN) (C=1e-12);\n" + " device NMOS $3 (S=VSS,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + const char *nls2 = + "circuit INV ($1=VDD,$2=IN,$3=VSS,$4=OUT);\n" + " device NMOS $1 (S=OUT,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device CAP $2 (A=OUT,B=IN) (C=1e-13);\n" + " device RES $3 (A=OUT,B=IN) (R=1000);\n" + " device PMOS $4 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit INV INV\n" + "match_nets VDD VDD\n" + "match_nets VSS VSS\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $3 $2\n" + "match_devices $3 $1\n" + "match_devices_with_different_parameters $2 $2\n" + "device_mismatch (null) $3\n" + "match_devices $1 $4\n" + "end_circuit INV INV NOMATCH" + ); + EXPECT_EQ (good, false); + + comp.exclude_caps (1e-11); + comp.exclude_resistors (900.0); + + logger.clear (); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit INV INV\n" + "match_nets VDD VDD\n" + "match_nets VSS VSS\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $3 $2\n" + "match_devices $3 $1\n" + "match_devices $1 $4\n" + "end_circuit INV INV MATCH" + ); + EXPECT_EQ (good, true); +} + TEST(2_SimpleInverterWithForcedNetAssignment) { const char *nls1 = diff --git a/testdata/ruby/dbNetlistCompare.rb b/testdata/ruby/dbNetlistCompare.rb index b9d772e3c..5886deb15 100644 --- a/testdata/ruby/dbNetlistCompare.rb +++ b/testdata/ruby/dbNetlistCompare.rb @@ -865,6 +865,86 @@ match_pins $4 $4 match_subcircuits $2 $1 match_subcircuits $1 $2 end_circuit TOP TOP MATCH +END + + assert_equal(good, true) + + end + + def test_10 + + nls1 = <<"END" +circuit INV($1=IN,$2=OUT,$3=VDD,$4=VSS); + device PMOS $1(S=VDD,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device CAP $2 (A=OUT,B=IN) (C=1e-12); + device NMOS $2(S=VSS,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nls2 = <<"END" +circuit INV($1=VDD,$2=IN,$3=VSS,$4=OUT); + device NMOSB $1(S=OUT,G=IN,D=VSS)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device CAP $2 (A=OUT,B=IN) (C=1e-13); + device RES $3 (A=OUT,B=IN) (R=1000); + device PMOSB $2(S=VDD,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nl1 = RBA::Netlist::new + nl2 = RBA::Netlist::new + prep_nl(nl1, nls1) + prep_nl(nl2, nls2) + + logger = NetlistCompareTestLogger::new + comp = RBA::NetlistComparer::new(logger) + + comp.same_device_classes(nl1.device_class_by_name("PMOS"), nl2.device_class_by_name("PMOSB")) + + good = comp.compare(nl1, nl2) + assert_equal(good, false) + + logger.clear + comp.same_device_classes(nl1.device_class_by_name("NMOS"), nl2.device_class_by_name("NMOSB")) + good = comp.compare(nl1, nl2) + + assert_equal(logger.text(), <<"END") +begin_circuit INV INV +match_nets VDD VDD +match_nets VSS VSS +match_nets OUT OUT +match_nets IN IN +match_pins $0 $1 +match_pins $1 $3 +match_pins $2 $0 +match_pins $3 $2 +match_devices $3 $1 +match_devices_with_different_parameters $2 $2 +device_mismatch (null) $3 +match_devices $1 $4 +end_circuit INV INV NOMATCH +END + + assert_equal(good, false) + + comp.max_resistance = 900.0 + comp.min_capacitance = 1e-11 + + logger.clear + good = comp.compare(nl1, nl2) + + assert_equal(logger.text(), <<"END") +begin_circuit INV INV +match_nets VDD VDD +match_nets VSS VSS +match_nets OUT OUT +match_nets IN IN +match_pins $0 $1 +match_pins $1 $3 +match_pins $2 $0 +match_pins $3 $2 +match_devices $3 $1 +match_devices $1 $4 +end_circuit INV INV MATCH END assert_equal(good, true) From 8f1db684c0fff76eb6255f6a2aff768c6d3797c3 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 6 Apr 2019 20:33:29 +0200 Subject: [PATCH 30/54] Fix: account for rounding errors when doing default compare of device parameters. --- src/db/db/dbDeviceClass.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbDeviceClass.cc b/src/db/db/dbDeviceClass.cc index 6b5b7f1a5..0d5401cb6 100644 --- a/src/db/db/dbDeviceClass.cc +++ b/src/db/db/dbDeviceClass.cc @@ -208,6 +208,10 @@ size_t DeviceClass::terminal_id_for_name (const std::string &name) const throw tl::Exception (tl::to_string (tr ("Invalid terminal name")) + ": '" + name + "'"); } +// NOTE: to allow rounding errors for parameter comparison, we use +// a default relative tolerance. +const double relative_tolerance = 1e-6; + bool DeviceClass::less (const db::Device &a, const db::Device &b) { tl_assert (a.device_class () != 0); @@ -224,7 +228,7 @@ bool DeviceClass::less (const db::Device &a, const db::Device &b) const std::vector &pd = a.device_class ()->parameter_definitions (); for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { - int cmp = compare_parameters (a.parameter_value (p->id ()), b.parameter_value (p->id ()), 0.0, 0.0); + int cmp = compare_parameters (a.parameter_value (p->id ()), b.parameter_value (p->id ()), 0.0, relative_tolerance); if (cmp != 0) { return cmp < 0; } @@ -251,7 +255,7 @@ bool DeviceClass::equal (const db::Device &a, const db::Device &b) const std::vector &pd = a.device_class ()->parameter_definitions (); for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { - int cmp = compare_parameters (a.parameter_value (p->id ()), b.parameter_value (p->id ()), 0.0, 0.0); + int cmp = compare_parameters (a.parameter_value (p->id ()), b.parameter_value (p->id ()), 0.0, relative_tolerance); if (cmp != 0) { return false; } From 18ee59023ed3f68b166d4ad6ccf3b6ebe5b71dcc Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 6 Apr 2019 21:14:25 +0200 Subject: [PATCH 31/54] Speedup of Spice format netlist reader --- src/db/db/dbNetlistSpiceReader.cc | 23 +++++++++++++++++++++-- src/db/db/dbNetlistSpiceReader.h | 1 + 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbNetlistSpiceReader.cc b/src/db/db/dbNetlistSpiceReader.cc index 32e62e9db..b62a78939 100644 --- a/src/db/db/dbNetlistSpiceReader.cc +++ b/src/db/db/dbNetlistSpiceReader.cc @@ -49,6 +49,7 @@ void NetlistSpiceReader::read (tl::InputStream &stream, db::Netlist &netlist) mp_stream.reset (new tl::TextInputStream (stream)); mp_netlist = &netlist; mp_circuit = 0; + mp_nets_by_name.reset (0); try { @@ -83,6 +84,7 @@ void NetlistSpiceReader::finish () mp_stream.reset (0); mp_netlist = 0; mp_circuit = 0; + mp_nets_by_name.reset (0); } void NetlistSpiceReader::push_stream (const std::string &path) @@ -367,12 +369,25 @@ void NetlistSpiceReader::ensure_circuit () db::Net *NetlistSpiceReader::make_net (const std::string &name) { - db::Net *net = mp_circuit->net_by_name (name); - if (! net) { + if (! mp_nets_by_name.get ()) { + mp_nets_by_name.reset (new std::map ()); + } + + std::map::const_iterator n2n = mp_nets_by_name->find (name); + + db::Net *net = 0; + if (n2n == mp_nets_by_name->end ()) { + net = new db::Net (); net->set_name (name); mp_circuit->add_net (net); + + mp_nets_by_name->insert (std::make_pair (name, net)); + + } else { + net = n2n->second; } + return net; } @@ -477,6 +492,9 @@ void NetlistSpiceReader::read_circuit (tl::Extractor &ex) } } + std::auto_ptr > n2n (mp_nets_by_name.release ()); + mp_nets_by_name.reset (0); + std::swap (cc, mp_circuit); for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { @@ -490,6 +508,7 @@ void NetlistSpiceReader::read_circuit (tl::Extractor &ex) } } + mp_nets_by_name.reset (n2n.release ()); std::swap (cc, mp_circuit); ex.expect_end (); diff --git a/src/db/db/dbNetlistSpiceReader.h b/src/db/db/dbNetlistSpiceReader.h index 08d3991a1..73185fad6 100644 --- a/src/db/db/dbNetlistSpiceReader.h +++ b/src/db/db/dbNetlistSpiceReader.h @@ -55,6 +55,7 @@ private: db::Circuit *mp_circuit; std::auto_ptr mp_stream; std::vector > m_streams; + std::auto_ptr > mp_nets_by_name; std::string m_stored_line; void push_stream (const std::string &path); From df2bd5e80adc2315bbee72bd1508544a934a8c7d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 6 Apr 2019 23:36:08 +0200 Subject: [PATCH 32/54] Netlist: flatten subcircuits, circuits --- src/db/db/dbCircuit.cc | 81 +++++++++++++ src/db/db/dbCircuit.h | 9 ++ src/db/db/dbNetlist.cc | 14 +++ src/db/db/dbNetlist.h | 6 + src/db/db/gsiDeclDbNetlist.cc | 10 ++ src/db/unit_tests/dbNetlistTests.cc | 174 ++++++++++++++++++++++++++++ testdata/ruby/dbNetlist.rb | 60 ++++++++++ 7 files changed, 354 insertions(+) diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index a8175d2dc..382923fd4 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -317,6 +317,87 @@ void Circuit::unregister_ref (SubCircuit *r) m_refs.erase (r); } +void Circuit::flatten_subcircuit (SubCircuit *subcircuit) +{ + const db::Circuit *c = subcircuit->circuit_ref (); + + // copy the nets, produce a net map + + std::map net2net; + + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + + // TODO: cannot join pins through subcircuits currently + tl_assert (n->pin_count () <= 1); + + db::Net *outside_net = 0; + + if (n->pin_count () > 0) { + size_t pin_id = n->begin_pins ()->pin_id (); + outside_net = subcircuit->net_for_pin (pin_id); + } else { + outside_net = new db::Net (); + if (! n->name ().empty ()) { + outside_net->set_name (subcircuit->expanded_name () + "." + n->name ()); + } + add_net (outside_net); + } + + net2net.insert (std::make_pair (n.operator-> (), outside_net)); + + } + + // copy the devices + + for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) { + + db::Device *device = new db::Device (*d); + if (! d->name ().empty ()) { + device->set_name (subcircuit->expanded_name () + "." + d->name ()); + } + add_device (device); + + const std::vector &td = d->device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + + const db::Net *tnet = d->net_for_terminal (t->id ()); + if (tnet) { + std::map::const_iterator n2n = net2net.find (tnet); + tl_assert (n2n != net2net.end ()); + device->connect_terminal (t->id (), n2n->second); + } + + } + + } + + // copy the subcircuits + + for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) { + + db::SubCircuit *new_subcircuit = new db::SubCircuit (*sc); + if (! new_subcircuit->name ().empty ()) { + new_subcircuit->set_name (subcircuit->expanded_name () + "." + new_subcircuit->name ()); + } + add_subcircuit (new_subcircuit); + + const db::Circuit *cr = sc->circuit_ref (); + for (db::Circuit::const_pin_iterator p = cr->begin_pins (); p != cr->end_pins (); ++p) { + + const db::Net *pnet = sc->net_for_pin (p->id ()); + if (pnet) { + std::map::const_iterator n2n = net2net.find (pnet); + tl_assert (n2n != net2net.end ()); + new_subcircuit->connect_pin (p->id (), n2n->second); + } + + } + + } + + remove_subcircuit (subcircuit); +} + void Circuit::translate_circuits (const std::map &map) { for (subcircuit_iterator i = m_subcircuits.begin (); i != m_subcircuits.end (); ++i) { diff --git a/src/db/db/dbCircuit.h b/src/db/db/dbCircuit.h index 9f0b362b7..042a3e144 100644 --- a/src/db/db/dbCircuit.h +++ b/src/db/db/dbCircuit.h @@ -589,6 +589,15 @@ public: */ void combine_devices (); + /** + * @brief Flattens the given subcircuit + * + * The subcircuit is resolved into the parent circuit and finally removed. + * Net, device and subcircuit names are decorated with the subcircuit's name + * if required. + */ + void flatten_subcircuit (SubCircuit *subcircuit); + private: friend class Netlist; friend class Net; diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 4396c8e4d..c0550767c 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -369,6 +369,20 @@ void Netlist::remove_circuit (Circuit *circuit) m_circuits.erase (circuit); } +void Netlist::flatten_circuit (Circuit *circuit) +{ + std::vector refs; + for (db::Circuit::refs_iterator sc = circuit->begin_refs (); sc != circuit->end_refs (); ++sc) { + refs.push_back (sc.operator-> ()); + } + + for (std::vector::const_iterator r = refs.begin (); r != refs.end (); ++r) { + (*r)->circuit ()->flatten_subcircuit (*r); + } + + remove_circuit (circuit); +} + DeviceClass *Netlist::device_class_by_name (const std::string &name) { for (device_class_iterator d = begin_device_classes (); d != end_device_classes (); ++d) { diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 98d47f84b..95555d29c 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -138,6 +138,12 @@ public: */ void remove_circuit (Circuit *circuit); + /** + * @brief Flattens the given circuit + * All subcircuit references are replaced by the content of this circuit. + */ + void flatten_circuit (Circuit *circuit); + /** * @brief Begin iterator for the circuits of the netlist (non-const version) */ diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 40a5968b9..e65c03e04 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -956,6 +956,11 @@ Class decl_dbCircuit ("db", "Circuit", gsi::method ("remove_subcircuit", &db::Circuit::remove_subcircuit, gsi::arg ("subcircuit"), "@brief Removes the given subcircuit from the circuit\n" ) + + gsi::method ("flatten_subcircuit", &db::Circuit::flatten_subcircuit, gsi::arg ("subcircuit"), + "@brief Flattens a subcircuit\n" + "This method will substitute the given subcircuit by it's contents. The subcircuit is removed " + "after this." + ) + gsi::iterator ("each_subcircuit", (db::Circuit::subcircuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_subcircuits, (db::Circuit::subcircuit_iterator (db::Circuit::*) ()) &db::Circuit::end_subcircuits, "@brief Iterates over the subcircuits of the circuit" ) + @@ -1084,6 +1089,11 @@ Class decl_dbNetlist ("db", "Netlist", "@brief Removes the given circuit object from the netlist\n" "After the object has been removed, it becomes invalid and cannot be used further." ) + + gsi::method ("flatten_circuit", &db::Netlist::flatten_circuit, gsi::arg ("circuit"), + "@brief Flattens a subcircuit\n" + "This method will substitute all instances (subcircuits) of the given circuit by it's " + "contents. After this, the circuit is removed." + ) + gsi::method ("circuit_by_cell_index", (db::Circuit *(db::Netlist::*) (db::cell_index_type)) &db::Netlist::circuit_by_cell_index, gsi::arg ("cell_index"), "@brief Gets the circuit object for a given cell index.\n" "If the cell index is not valid or no circuit is registered with this index, nil is returned." diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 8169d05a3..3ab084247 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -22,6 +22,7 @@ #include "dbNetlist.h" +#include "dbNetlistDeviceClasses.h" #include "tlUnitTest.h" #include "tlString.h" @@ -1104,3 +1105,176 @@ TEST(13_DeviceAbstract) EXPECT_EQ (nl.begin_device_abstracts () == nl.end_device_abstracts (), true); } +TEST(20_FlattenSubCircuit) +{ + db::Netlist nl; + + db::DeviceClass *dc; + + dc = new db::DeviceClassMOS3Transistor (); + dc->set_name ("NMOS"); + nl.add_device_class (dc); + + dc = new db::DeviceClassMOS3Transistor (); + dc->set_name ("PMOS"); + nl.add_device_class (dc); + + nl.from_string ( + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN);\n" + " subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN);\n" + " subcircuit PTRANS SC3 ($1=$5,$2=OUT,$3=$2);\n" + " subcircuit NTRANS SC4 ($1=$4,$2=OUT,$3=$2);\n" + "end;\n" + "circuit PTRANS ($1=$1,$2=$2,$3=$3);\n" + " device PMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95);\n" + "end;\n" + "circuit NTRANS ($1=$1,$2=$2,$3=$3);\n" + " device NMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95);\n" + "end;\n" + ); + + db::Netlist nl2; + + nl2 = nl; + db::Circuit *inv2 = nl2.circuit_by_name ("INV2"); + inv2->flatten_subcircuit (inv2->subcircuit_by_name ("SC1")); + + EXPECT_EQ (nl2.to_string (), + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$5,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN);\n" + " subcircuit PTRANS SC3 ($1=$5,$2=OUT,$3=$2);\n" + " subcircuit NTRANS SC4 ($1=$4,$2=OUT,$3=$2);\n" + "end;\n" + "circuit PTRANS ($1=$1,$2=$2,$3=$3);\n" + " device PMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + "circuit NTRANS ($1=$1,$2=$2,$3=$3);\n" + " device NMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + nl2.flatten_circuit (nl2.circuit_by_name ("PTRANS")); + nl2.flatten_circuit (nl2.circuit_by_name ("NTRANS")); + + EXPECT_EQ (nl2.to_string (), + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$5,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $3 (S=$4,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); +} + +TEST(21_FlattenSubCircuit2) +{ + db::Netlist nl; + + db::DeviceClass *dc; + + dc = new db::DeviceClassMOS3Transistor (); + dc->set_name ("NMOS"); + nl.add_device_class (dc); + + dc = new db::DeviceClassMOS3Transistor (); + dc->set_name ("PMOS"); + nl.add_device_class (dc); + + nl.from_string ( + "circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD);\n" + " subcircuit INV2 INV2_SC1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" + " subcircuit INV2 INV2_SC2 (IN=FB,$2=(null),OUT=$I8,$4=VSS,$5=VDD);\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN);\n" + " subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN);\n" + " subcircuit PTRANS SC3 ($1=$5,$2=OUT,$3=$2);\n" + " subcircuit NTRANS SC4 ($1=$4,$2=OUT,$3=$2);\n" + "end;\n" + "circuit PTRANS ($1=$1,$2=$2,$3=$3);\n" + " device PMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95);\n" + "end;\n" + "circuit NTRANS ($1=$1,$2=$2,$3=$3);\n" + " device NMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95);\n" + "end;\n" + ); + + db::Netlist nl2; + + nl2 = nl; + db::Circuit *inv2 = nl2.circuit_by_name ("RINGO"); + inv2->flatten_subcircuit (inv2->subcircuit_by_name ("INV2_SC1")); + + EXPECT_EQ (nl2.to_string (), + "circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD);\n" + " subcircuit INV2 INV2_SC2 (IN=FB,$2=(null),OUT=$I8,$4=VSS,$5=VDD);\n" + " subcircuit PTRANS INV2_SC1.SC1 ($1=VDD,$2=FB,$3=$I8);\n" + " subcircuit NTRANS INV2_SC1.SC2 ($1=VSS,$2=FB,$3=$I8);\n" + " subcircuit PTRANS INV2_SC1.SC3 ($1=VDD,$2=OSC,$3=FB);\n" + " subcircuit NTRANS INV2_SC1.SC4 ($1=VSS,$2=OSC,$3=FB);\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN);\n" + " subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN);\n" + " subcircuit PTRANS SC3 ($1=$5,$2=OUT,$3=$2);\n" + " subcircuit NTRANS SC4 ($1=$4,$2=OUT,$3=$2);\n" + "end;\n" + "circuit PTRANS ($1=$1,$2=$2,$3=$3);\n" + " device PMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + "circuit NTRANS ($1=$1,$2=$2,$3=$3);\n" + " device NMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + inv2->flatten_subcircuit (inv2->subcircuit_by_name ("INV2_SC2")); + + EXPECT_EQ (nl2.to_string (), + "circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD);\n" + " subcircuit PTRANS INV2_SC1.SC1 ($1=VDD,$2=FB,$3=$I8);\n" + " subcircuit NTRANS INV2_SC1.SC2 ($1=VSS,$2=FB,$3=$I8);\n" + " subcircuit PTRANS INV2_SC1.SC3 ($1=VDD,$2=OSC,$3=FB);\n" + " subcircuit NTRANS INV2_SC1.SC4 ($1=VSS,$2=OSC,$3=FB);\n" + " subcircuit PTRANS INV2_SC2.SC1 ($1=VDD,$2=(null),$3=FB);\n" + " subcircuit NTRANS INV2_SC2.SC2 ($1=VSS,$2=(null),$3=FB);\n" + " subcircuit PTRANS INV2_SC2.SC3 ($1=VDD,$2=$I8,$3=(null));\n" + " subcircuit NTRANS INV2_SC2.SC4 ($1=VSS,$2=$I8,$3=(null));\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN);\n" + " subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN);\n" + " subcircuit PTRANS SC3 ($1=$5,$2=OUT,$3=$2);\n" + " subcircuit NTRANS SC4 ($1=$4,$2=OUT,$3=$2);\n" + "end;\n" + "circuit PTRANS ($1=$1,$2=$2,$3=$3);\n" + " device PMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + "circuit NTRANS ($1=$1,$2=$2,$3=$3);\n" + " device NMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); + + nl2 = nl; + nl2.flatten_circuit (nl2.circuit_by_name ("INV2")); + + EXPECT_EQ (nl2.to_string (), + "circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD);\n" + " subcircuit PTRANS INV2_SC1.SC1 ($1=VDD,$2=FB,$3=$I8);\n" + " subcircuit NTRANS INV2_SC1.SC2 ($1=VSS,$2=FB,$3=$I8);\n" + " subcircuit PTRANS INV2_SC1.SC3 ($1=VDD,$2=OSC,$3=FB);\n" + " subcircuit NTRANS INV2_SC1.SC4 ($1=VSS,$2=OSC,$3=FB);\n" + " subcircuit PTRANS INV2_SC2.SC1 ($1=VDD,$2=(null),$3=FB);\n" + " subcircuit NTRANS INV2_SC2.SC2 ($1=VSS,$2=(null),$3=FB);\n" + " subcircuit PTRANS INV2_SC2.SC3 ($1=VDD,$2=$I8,$3=(null));\n" + " subcircuit NTRANS INV2_SC2.SC4 ($1=VSS,$2=$I8,$3=(null));\n" + "end;\n" + "circuit PTRANS ($1=$1,$2=$2,$3=$3);\n" + " device PMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + "circuit NTRANS ($1=$1,$2=$2,$3=$3);\n" + " device NMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + "end;\n" + ); +} diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 0897f9ad2..ebdd87456 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -714,6 +714,66 @@ END end + def test_11_FlattenCircuits + + nl = RBA::Netlist::new + + dc = RBA::DeviceClassMOS3Transistor::new + dc.name = "NMOS" + nl.add(dc) + + dc = RBA::DeviceClassMOS3Transistor::new + dc.name = "PMOS" + nl.add(dc) + + nl.from_s(<<"END") +circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5); + subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN); + subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN); + subcircuit PTRANS SC3 ($1=$5,$2=OUT,$3=$2); + subcircuit NTRANS SC4 ($1=$4,$2=OUT,$3=$2); +end; +circuit PTRANS ($1=$1,$2=$2,$3=$3); + device PMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95); +end; +circuit NTRANS ($1=$1,$2=$2,$3=$3); + device NMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95); +end; +END + + nl2 = nl.dup + inv2 = nl2.circuit_by_name("INV2") + inv2.flatten_subcircuit(inv2.subcircuit_by_name("SC1")) + + assert_equal(nl2.to_s, <<"END") +circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5); + device PMOS $1 (S=$5,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); + subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN); + subcircuit PTRANS SC3 ($1=$5,$2=OUT,$3=$2); + subcircuit NTRANS SC4 ($1=$4,$2=OUT,$3=$2); +end; +circuit PTRANS ($1=$1,$2=$2,$3=$3); + device PMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); +end; +circuit NTRANS ($1=$1,$2=$2,$3=$3); + device NMOS $1 (S=$1,G=$3,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); +end; +END + + nl2.flatten_circuit(nl2.circuit_by_name("PTRANS")) + nl2.flatten_circuit(nl2.circuit_by_name("NTRANS")) + + assert_equal(nl2.to_s, <<"END") +circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5); + device PMOS $1 (S=$5,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); + device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); + device NMOS $3 (S=$4,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); + device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); +end; +END + + end + end load("test_epilogue.rb") From f6836b96a2ad39df2fc18f896e8af66bca22607a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 7 Apr 2019 10:15:57 +0200 Subject: [PATCH 33/54] WIP: some enhancements Spice writer: don't prefix model name with "M" Added "device_class_mismatch" message to netlist compare Assertion if device classes or circuits are nil on "same_..." --- src/db/db/dbCircuit.cc | 4 ++- src/db/db/dbNetlist.cc | 4 ++- src/db/db/dbNetlistCompare.cc | 39 ++++++++++++++++++++++ src/db/db/dbNetlistCompare.h | 6 ++++ src/db/db/dbNetlistSpiceWriter.cc | 4 +-- src/db/db/dbTestSupport.cc | 5 +++ src/db/db/gsiDeclDbNetlistCompare.cc | 20 +++++++++++ src/db/unit_tests/dbNetlistCompareTests.cc | 13 ++++++++ testdata/algo/nwriter5_au.txt | 4 +-- testdata/algo/nwriter6_au.txt | 4 +-- testdata/algo/nwriter8_au.txt | 4 +-- testdata/ruby/dbNetlistCompare.rb | 35 +++++++++++++++++++ 12 files changed, 132 insertions(+), 10 deletions(-) diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index 382923fd4..54c6f909a 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -319,6 +319,8 @@ void Circuit::unregister_ref (SubCircuit *r) void Circuit::flatten_subcircuit (SubCircuit *subcircuit) { + tl_assert (subcircuit != 0); + const db::Circuit *c = subcircuit->circuit_ref (); // copy the nets, produce a net map @@ -395,7 +397,7 @@ void Circuit::flatten_subcircuit (SubCircuit *subcircuit) } - remove_subcircuit (subcircuit); + delete subcircuit; } void Circuit::translate_circuits (const std::map &map) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index c0550767c..81eada167 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -371,6 +371,8 @@ void Netlist::remove_circuit (Circuit *circuit) void Netlist::flatten_circuit (Circuit *circuit) { + tl_assert (circuit != 0); + std::vector refs; for (db::Circuit::refs_iterator sc = circuit->begin_refs (); sc != circuit->end_refs (); ++sc) { refs.push_back (sc.operator-> ()); @@ -380,7 +382,7 @@ void Netlist::flatten_circuit (Circuit *circuit) (*r)->circuit ()->flatten_subcircuit (*r); } - remove_circuit (circuit); + delete circuit; } DeviceClass *Netlist::device_class_by_name (const std::string &name) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 150968c94..8ef48cacd 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -230,6 +230,16 @@ public: return 0; } + return cat_for_device_class (cls); + } + + bool has_cat_for_device_class (const db::DeviceClass *cls) + { + return m_cat_by_ptr.find (cls) != m_cat_by_ptr.end (); + } + + size_t cat_for_device_class (const db::DeviceClass *cls) + { std::map::const_iterator cp = m_cat_by_ptr.find (cls); if (cp != m_cat_by_ptr.end ()) { return cp->second; @@ -983,12 +993,16 @@ NetlistComparer::equivalent_pins (const db::Circuit *cb, const std::vectorsame_class (ca, cb); } void NetlistComparer::same_circuits (const db::Circuit *ca, const db::Circuit *cb) { + tl_assert (ca != 0); + tl_assert (cb != 0); mp_circuit_categorizer->same_circuit (ca, cb); } @@ -1017,6 +1031,31 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const mp_logger->begin_netlist (a, b); } + // check for device classes that don't match + + std::map > cat2dc; + + for (db::Netlist::const_device_class_iterator dc = a->begin_device_classes (); dc != a->end_device_classes (); ++dc) { + size_t cat = device_categorizer.cat_for_device_class (dc.operator-> ()); + cat2dc.insert (std::make_pair (cat, std::make_pair ((const db::DeviceClass *) 0, (const db::DeviceClass *) 0))).first->second.first = dc.operator-> (); + } + + for (db::Netlist::const_device_class_iterator dc = b->begin_device_classes (); dc != b->end_device_classes (); ++dc) { + size_t cat = device_categorizer.cat_for_device_class (dc.operator-> ()); + cat2dc.insert (std::make_pair (cat, std::make_pair ((const db::DeviceClass *) 0, (const db::DeviceClass *) 0))).first->second.second = dc.operator-> (); + } + + for (std::map >::const_iterator i = cat2dc.begin (); i != cat2dc.end (); ++i) { + if (! i->second.first || ! i->second.second) { + good = false; + if (mp_logger) { + mp_logger->device_class_mismatch (i->second.first, i->second.second); + } + } + } + + // check for circuits that don't match + for (std::map >::const_iterator i = cat2circuits.begin (); i != cat2circuits.end (); ++i) { if (! i->second.first || ! i->second.second) { good = false; diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h index eb9364733..f2cfa9bf3 100644 --- a/src/db/db/dbNetlistCompare.h +++ b/src/db/db/dbNetlistCompare.h @@ -56,6 +56,12 @@ public: */ virtual void end_netlist (const db::Netlist * /*a*/, const db::Netlist * /*b*/) { } + /** + * @brief There is a device class mismatch + * "a" is null if there is no match for b and vice versa. + */ + virtual void device_class_mismatch (const db::DeviceClass * /*a*/, const db::DeviceClass * /*b*/) { } + /** * @brief Begin logging for circuit a and b */ diff --git a/src/db/db/dbNetlistSpiceWriter.cc b/src/db/db/dbNetlistSpiceWriter.cc index 9bde4c2d0..ea4fec215 100644 --- a/src/db/db/dbNetlistSpiceWriter.cc +++ b/src/db/db/dbNetlistSpiceWriter.cc @@ -141,8 +141,8 @@ void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const os << net_to_string (dev.net_for_terminal (db::DeviceClassMOS3Transistor::terminal_id_S)); } - // Use "M" + device class name for the model - os << " M"; + // Use device class name for the model + os << " "; os << format_name (dev.device_class ()->name ()); os << " L=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_L)); diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index 6aa938e9d..39158adce 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -186,6 +186,11 @@ public: m_circuit = circuit2str (a) + " vs. " + circuit2str (b); } + virtual void device_class_mismatch (const db::DeviceClass *a, const db::DeviceClass *b) + { + out ("device_class_mismatch " + a->name () + " " + b->name ()); + } + virtual void circuit_skipped (const db::Circuit *a, const db::Circuit *b) { out ("circuit_skipped " + circuit2str (a) + " " + circuit2str (b)); diff --git a/src/db/db/gsiDeclDbNetlistCompare.cc b/src/db/db/gsiDeclDbNetlistCompare.cc index 36b9d86d3..5fd810d39 100644 --- a/src/db/db/gsiDeclDbNetlistCompare.cc +++ b/src/db/db/gsiDeclDbNetlistCompare.cc @@ -66,6 +66,20 @@ public: db::NetlistCompareLogger::end_netlist (a, b); } + virtual void device_class_mismatch (const db::DeviceClass *a, const db::DeviceClass *b) + { + if (cb_device_class_mismatch.can_issue ()) { + cb_device_class_mismatch.issue (&GenericNetlistCompareLogger::device_class_mismatch_fb, a, b); + } else { + db::NetlistCompareLogger::device_class_mismatch (a, b); + } + } + + void device_class_mismatch_fb (const db::DeviceClass *a, const db::DeviceClass *b) + { + db::NetlistCompareLogger::device_class_mismatch (a, b); + } + virtual void begin_circuit (const db::Circuit *a, const db::Circuit *b) { if (cb_begin_circuit.can_issue ()) { @@ -278,6 +292,7 @@ public: gsi::Callback cb_begin_netlist; gsi::Callback cb_end_netlist; + gsi::Callback cb_device_class_mismatch; gsi::Callback cb_begin_circuit; gsi::Callback cb_end_circuit; gsi::Callback cb_circuit_skipped; @@ -309,6 +324,11 @@ Class decl_GenericNetlistCompareLogger ("db", "Gene "@brief This function is called at the end of the compare process.\n" "This method is called once when the compare run ended.\n" ) + + gsi::callback ("device_class_mismatch", &GenericNetlistCompareLogger::device_class_mismatch, &GenericNetlistCompareLogger::cb_device_class_mismatch, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when device classes can't be compared.\n" + "This method is called when a device class can't be mapped to a partner in the other netlist. In this case, " + "this method is called with the one device class and nil for the other class.\n" + ) + gsi::callback ("begin_circuit", &GenericNetlistCompareLogger::begin_circuit, &GenericNetlistCompareLogger::cb_begin_circuit, gsi::arg ("a"), gsi::arg ("b"), "@brief This function is called when a new circuit is compared.\n" "This compare procedure will run the netlist compare circuit vs. circuit in a bottom-up fashion.\n" diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 39338ad95..fb250d97d 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -58,6 +58,11 @@ public: out ("circuit_mismatch " + circuit2str (a) + " " + circuit2str (b)); } + virtual void device_class_mismatch (const db::DeviceClass *a, const db::DeviceClass *b) + { + out ("device_class_mismatch " + dc2str (a) + " " + dc2str (b)); + } + virtual void match_nets (const db::Net *a, const db::Net *b) { out ("match_nets " + net2str (a) + " " + net2str (b)); @@ -126,6 +131,11 @@ public: private: std::vector m_texts; + std::string dc2str (const db::DeviceClass *x) const + { + return x ? x->name () : "(null)"; + } + std::string circuit2str (const db::Circuit *x) const { return x ? x->name () : "(null)"; @@ -366,6 +376,9 @@ TEST(1_SimpleInverterMatchedDeviceClasses) logger.clear (); comp.same_device_classes (nl1.device_class_by_name ("NMOS"), nl2.device_class_by_name ("NMOSB")); + // avoids device class mismatch errors: + comp.same_device_classes (nl1.device_class_by_name ("NMOSB"), nl2.device_class_by_name ("NMOS")); + comp.same_device_classes (nl1.device_class_by_name ("PMOSB"), nl2.device_class_by_name ("PMOS")); good = comp.compare (&nl1, &nl2); EXPECT_EQ (logger.text (), diff --git a/testdata/algo/nwriter5_au.txt b/testdata/algo/nwriter5_au.txt index 123eb9eac..d2a989553 100644 --- a/testdata/algo/nwriter5_au.txt +++ b/testdata/algo/nwriter5_au.txt @@ -10,7 +10,7 @@ * net 3 n3 * net 4 n4 * device instance $1 0,0 M3CLS -M$1 1 4 3 1 MM3CLS L=0.25U W=0.18U AS=1.2P AD=0.75P PS=2.2U PD=1.75U +M$1 1 4 3 1 M3CLS L=0.25U W=0.18U AS=1.2P AD=0.75P PS=2.2U PD=1.75U * device instance $2 0,0 M3CLS -M$2 3 4 2 3 MM3CLS L=1.4U W=0.25U AS=1.3P AD=0.85P PS=2.3U PD=1.85U +M$2 3 4 2 3 M3CLS L=1.4U W=0.25U AS=1.3P AD=0.85P PS=2.3U PD=1.85U .ENDS C1 diff --git a/testdata/algo/nwriter6_au.txt b/testdata/algo/nwriter6_au.txt index 33ef8c225..d9d1a6802 100644 --- a/testdata/algo/nwriter6_au.txt +++ b/testdata/algo/nwriter6_au.txt @@ -12,7 +12,7 @@ * net 4 n4 * net 5 n5 * device instance $1 0,0 M4CLS -M$1 1 4 3 5 MM4CLS L=0.25U W=0.18U AS=1.2P AD=0.75P PS=2.2U PD=1.75U +M$1 1 4 3 5 M4CLS L=0.25U W=0.18U AS=1.2P AD=0.75P PS=2.2U PD=1.75U * device instance $2 0,0 M4CLS -M$2 3 4 2 5 MM4CLS L=1.4U W=0.25U AS=1.3P AD=0.85P PS=2.3U PD=1.85U +M$2 3 4 2 5 M4CLS L=1.4U W=0.25U AS=1.3P AD=0.85P PS=2.3U PD=1.85U .ENDS C1 diff --git a/testdata/algo/nwriter8_au.txt b/testdata/algo/nwriter8_au.txt index 7b3e360df..e6707fc46 100644 --- a/testdata/algo/nwriter8_au.txt +++ b/testdata/algo/nwriter8_au.txt @@ -28,7 +28,7 @@ XSC2 3 2 4 3 C1 * net 4 n4 * net 5 n5 * device instance $1 0,0 M4CLS -M$1 1 4 3 5 MM4CLS L=0.25U W=0.18U AS=1.2P AD=0.75P PS=2.2U PD=1.75U +M$1 1 4 3 5 M4CLS L=0.25U W=0.18U AS=1.2P AD=0.75P PS=2.2U PD=1.75U * device instance $2 0,0 M4CLS -M$2 3 4 2 5 MM4CLS L=1.4U W=0.25U AS=1.3P AD=0.85P PS=2.3U PD=1.85U +M$2 3 4 2 5 M4CLS L=1.4U W=0.25U AS=1.3P AD=0.85P PS=2.3U PD=1.85U .ENDS C1 diff --git a/testdata/ruby/dbNetlistCompare.rb b/testdata/ruby/dbNetlistCompare.rb index 5886deb15..47502e119 100644 --- a/testdata/ruby/dbNetlistCompare.rb +++ b/testdata/ruby/dbNetlistCompare.rb @@ -33,6 +33,10 @@ class NetlistCompareTestLogger < RBA::GenericNetlistCompareLogger @texts << text end + def device_class_mismatch(a, b) + out("device_class_mismatch " + dc2str(a) + " " + dc2str(b)) + end + def begin_circuit(a, b) out("begin_circuit " + circuit2str(a) + " " + circuit2str(b)) end @@ -101,6 +105,10 @@ class NetlistCompareTestLogger < RBA::GenericNetlistCompareLogger @texts = [] end + def dc2str(x) + return x ? x.name : "(null)" + end + def circuit2str(x) return x ? x.name : "(null)" end @@ -173,6 +181,27 @@ class NetlistCompare_TestClass < TestBase def test_1 + nl1 = RBA::Netlist::new + nl2 = RBA::Netlist::new + dc = RBA::DeviceClass::new + dc.name = "A" + nl1.add(dc) + dc = RBA::DeviceClass::new + dc.name = "B" + nl2.add(dc) + + logger = NetlistCompareTestLogger::new + comp = RBA::NetlistComparer::new(logger) + + good = comp.compare(nl1, nl2) + + assert_equal(logger.text, <<"END") +device_class_mismatch A (null) +device_class_mismatch (null) B +END + + assert_equal(good, false) + nls1 = <<"END" circuit INV($1=IN,$2=OUT,$3=VDD,$4=VSS); device PMOS $1(S=VDD,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); @@ -247,6 +276,9 @@ END logger.clear comp.same_device_classes(nl1.device_class_by_name("NMOS"), nl2.device_class_by_name("NMOSB")) + # avoids device class mismatch errors + comp.same_device_classes(nl1.device_class_by_name("NMOSB"), nl2.device_class_by_name("NMOS")) + comp.same_device_classes(nl1.device_class_by_name("PMOSB"), nl2.device_class_by_name("PMOS")) good = comp.compare(nl1, nl2) assert_equal(logger.text(), <<"END") @@ -905,6 +937,9 @@ END logger.clear comp.same_device_classes(nl1.device_class_by_name("NMOS"), nl2.device_class_by_name("NMOSB")) + # avoids device class mismatch errors + comp.same_device_classes(nl1.device_class_by_name("NMOSB"), nl2.device_class_by_name("NMOS")) + comp.same_device_classes(nl1.device_class_by_name("PMOSB"), nl2.device_class_by_name("PMOS")) good = comp.compare(nl1, nl2) assert_equal(logger.text(), <<"END") From c474fa6550a8d64b113cfd1c9e150aee3bb61220 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 7 Apr 2019 11:09:08 +0200 Subject: [PATCH 34/54] Bugfix: Spice reader needs to transform length units to micrometer --- src/db/db/dbNetlistSpiceReader.cc | 10 ++++++++- src/db/unit_tests/dbNetlistReaderTests.cc | 26 +++++++++++------------ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/db/db/dbNetlistSpiceReader.cc b/src/db/db/dbNetlistSpiceReader.cc index b62a78939..c0c790e80 100644 --- a/src/db/db/dbNetlistSpiceReader.cc +++ b/src/db/db/dbNetlistSpiceReader.cc @@ -597,7 +597,15 @@ void NetlistSpiceReader::read_mos4_device (tl::Extractor &ex) for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { std::map::const_iterator v = pv.find (i->name ()); if (v != pv.end ()) { - dev->set_parameter_value (i->id (), v->second); + // by conventions, dimensions are in micrometer + if (i->id () == db::DeviceClassMOS4Transistor::param_id_AD || i->id () == db::DeviceClassMOS4Transistor::param_id_AS) { + dev->set_parameter_value (i->id (), v->second * 1e12); + } else if (i->id () == db::DeviceClassMOS4Transistor::param_id_W + || i->id () == db::DeviceClassMOS4Transistor::param_id_L + || i->id () == db::DeviceClassMOS4Transistor::param_id_PD + || i->id () == db::DeviceClassMOS4Transistor::param_id_PS) { + dev->set_parameter_value (i->id (), v->second * 1e6); + } } } diff --git a/src/db/unit_tests/dbNetlistReaderTests.cc b/src/db/unit_tests/dbNetlistReaderTests.cc index 25e50da57..ce21c7797 100644 --- a/src/db/unit_tests/dbNetlistReaderTests.cc +++ b/src/db/unit_tests/dbNetlistReaderTests.cc @@ -43,7 +43,7 @@ TEST(1_BasicReader) " device RES $1 (A='6',B='1') (R=7650);\n" " device RES $2 (A='3',B='1') (R=7650);\n" " device RES $3 (A='3',B='2') (R=2670);\n" - " device MHVPMOS $4 (S='6',G='4',D='7',B='7') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=3.84e-06,PD=3.84e-06);\n" + " device MHVPMOS $4 (S='6',G='4',D='7',B='7') (L=0.25,W=1.5,AS=0.63,AD=0.63,PS=3.84,PD=3.84);\n" "end;\n" ); } @@ -74,14 +74,14 @@ TEST(2_ReaderWithSubcircuits) " subcircuit INVX1 $12 ($1='12',$2='13',$3='15',$4='12',$5='11',$6='15');\n" "end;\n" "circuit ND2X1 ($1='1',$2='2',$3='3',$4='4',$5='5',$6='6',$7='7');\n" - " device MLVPMOS $1 (S='2',G='6',D='1',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=3.85e-06,PD=1.95e-06);\n" - " device MLVPMOS $2 (S='1',G='5',D='2',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=1.95e-06,PD=3.85e-06);\n" - " device MLVNMOS $3 (S='3',G='6',D='8',B='7') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=2.75e-06,PD=1.4e-06);\n" - " device MLVNMOS $4 (S='8',G='5',D='2',B='7') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=1.4e-06,PD=2.75e-06);\n" + " device MLVPMOS $1 (S='2',G='6',D='1',B='4') (L=0.25,W=1.5,AS=0.6375,AD=0.3375,PS=3.85,PD=1.95);\n" + " device MLVPMOS $2 (S='1',G='5',D='2',B='4') (L=0.25,W=1.5,AS=0.3375,AD=0.6375,PS=1.95,PD=3.85);\n" + " device MLVNMOS $3 (S='3',G='6',D='8',B='7') (L=0.25,W=0.95,AS=0.40375,AD=0.21375,PS=2.75,PD=1.4);\n" + " device MLVNMOS $4 (S='8',G='5',D='2',B='7') (L=0.25,W=0.95,AS=0.21375,AD=0.40375,PS=1.4,PD=2.75);\n" "end;\n" "circuit INVX1 ($1='1',$2='2',$3='3',$4='4',$5='5',$6='6');\n" - " device MLVPMOS $1 (S='1',G='5',D='2',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=3.85e-06,PD=3.85e-06);\n" - " device MLVNMOS $2 (S='3',G='5',D='2',B='6') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=2.75e-06,PD=2.75e-06);\n" + " device MLVPMOS $1 (S='1',G='5',D='2',B='4') (L=0.25,W=1.5,AS=0.6375,AD=0.6375,PS=3.85,PD=3.85);\n" + " device MLVNMOS $2 (S='3',G='5',D='2',B='6') (L=0.25,W=0.95,AS=0.40375,AD=0.40375,PS=2.75,PD=2.75);\n" "end;\n" ); } @@ -98,14 +98,14 @@ TEST(3_ReaderWithSubcircuitsAltOrder) EXPECT_EQ (nl.to_string (), "circuit INVX1 ($1='1',$2='2',$3='3',$4='4',$5='5',$6='6');\n" - " device MLVPMOS $1 (S='1',G='5',D='2',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=0,PD=0);\n" - " device MLVNMOS $2 (S='3',G='5',D='2',B='6') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=0,PD=0);\n" + " device MLVPMOS $1 (S='1',G='5',D='2',B='4') (L=0.25,W=1.5,AS=0,AD=0,PS=0,PD=0);\n" + " device MLVNMOS $2 (S='3',G='5',D='2',B='6') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" "end;\n" "circuit ND2X1 ($1='1',$2='2',$3='3',$4='4',$5='5',$6='6',$7='7');\n" - " device MLVPMOS $1 (S='2',G='6',D='1',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=0,PD=0);\n" - " device MLVPMOS $2 (S='1',G='5',D='2',B='4') (L=2.5e-07,W=1.5e-06,AS=0,AD=0,PS=0,PD=0);\n" - " device MLVNMOS $3 (S='3',G='6',D='8',B='7') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=0,PD=0);\n" - " device MLVNMOS $4 (S='8',G='5',D='2',B='7') (L=2.5e-07,W=9.5e-07,AS=0,AD=0,PS=0,PD=0);\n" + " device MLVPMOS $1 (S='2',G='6',D='1',B='4') (L=0.25,W=1.5,AS=0,AD=0,PS=0,PD=0);\n" + " device MLVPMOS $2 (S='1',G='5',D='2',B='4') (L=0.25,W=1.5,AS=0,AD=0,PS=0,PD=0);\n" + " device MLVNMOS $3 (S='3',G='6',D='8',B='7') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" + " device MLVNMOS $4 (S='8',G='5',D='2',B='7') (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0);\n" "end;\n" "circuit RINGO ($1='11',$2='12',$3='13',$4='14',$5='15');\n" " subcircuit ND2X1 $1 ($1='12',$2='1',$3='15',$4='12',$5='11',$6='14',$7='15');\n" From c0bf5d955cdfecd311ddb65797a4f80b6fab750a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 7 Apr 2019 11:49:59 +0200 Subject: [PATCH 35/54] Removed a debug statement. --- src/db/db/dbNetlistCompare.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 8ef48cacd..9cad3d334 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -1504,7 +1504,6 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, good = false; } else { if (mp_logger) { -dc.equals (dm->second, std::make_pair (d.operator-> (), device_cat)); // @@@ mp_logger->match_devices_with_different_parameters (dm->second.first, d.operator-> ()); } good = false; From be35646c24184fca4ee743767ac05cbd0409a24a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 8 Apr 2019 21:20:22 +0200 Subject: [PATCH 36/54] Spice reader/writer: more consistent with respect to allowed characters now. --- src/db/db/dbNetlistSpiceReader.cc | 20 +++++++++++--------- src/db/db/dbNetlistSpiceWriter.cc | 4 +++- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/db/db/dbNetlistSpiceReader.cc b/src/db/db/dbNetlistSpiceReader.cc index c0c790e80..787ce4f4d 100644 --- a/src/db/db/dbNetlistSpiceReader.cc +++ b/src/db/db/dbNetlistSpiceReader.cc @@ -33,6 +33,8 @@ namespace db { +static const char *allowed_name_chars = "_.:,!+$/&\\#[]|"; + NetlistSpiceReader::NetlistSpiceReader () : mp_netlist (0), mp_stream (0) { @@ -140,7 +142,7 @@ std::string NetlistSpiceReader::get_line () if (ex.test_without_case (".include")) { std::string path; - ex.read_word_or_quoted (path, "_.:,!+$/\\"); + ex.read_word_or_quoted (path, allowed_name_chars); push_stream (path); @@ -394,7 +396,7 @@ db::Net *NetlistSpiceReader::make_net (const std::string &name) void NetlistSpiceReader::read_subcircuit (tl::Extractor &ex) { std::string sc_name; - ex.read_word_or_quoted (sc_name, "_.:,!+$/\\"); + ex.read_word_or_quoted (sc_name, allowed_name_chars); std::vector nn; std::map pv; @@ -402,7 +404,7 @@ void NetlistSpiceReader::read_subcircuit (tl::Extractor &ex) while (! ex.at_end ()) { std::string n; - ex.read_word_or_quoted (n, "_.:,!+$/\\"); + ex.read_word_or_quoted (n, allowed_name_chars); if (ex.test ("=")) { // a parameter @@ -455,7 +457,7 @@ void NetlistSpiceReader::read_subcircuit (tl::Extractor &ex) void NetlistSpiceReader::read_circuit (tl::Extractor &ex) { std::string nc; - ex.read_word_or_quoted (nc, "_.:,!+$/\\"); + ex.read_word_or_quoted (nc, allowed_name_chars); std::vector nn; std::map pv; @@ -463,7 +465,7 @@ void NetlistSpiceReader::read_circuit (tl::Extractor &ex) while (! ex.at_end ()) { std::string n; - ex.read_word_or_quoted (n, "_.:,!+$/\\"); + ex.read_word_or_quoted (n, allowed_name_chars); if (ex.test ("=")) { // a parameter @@ -517,13 +519,13 @@ void NetlistSpiceReader::read_circuit (tl::Extractor &ex) void NetlistSpiceReader::read_device (db::DeviceClass *dev_cls, size_t param_id, tl::Extractor &ex) { std::string dn; - ex.read_word_or_quoted (dn, "_.:,!+$/\\"); + ex.read_word_or_quoted (dn, allowed_name_chars); std::vector nn; while (! ex.at_end () && nn.size () < 2) { nn.push_back (std::string ()); - ex.read_word_or_quoted (nn.back (), "_.:,!+$/\\"); + ex.read_word_or_quoted (nn.back (), allowed_name_chars); } if (nn.size () != 2) { @@ -548,7 +550,7 @@ void NetlistSpiceReader::read_device (db::DeviceClass *dev_cls, size_t param_id, void NetlistSpiceReader::read_mos4_device (tl::Extractor &ex) { std::string dn; - ex.read_word_or_quoted (dn, "_.:,!+$/\\"); + ex.read_word_or_quoted (dn, allowed_name_chars); std::vector nn; std::map pv; @@ -556,7 +558,7 @@ void NetlistSpiceReader::read_mos4_device (tl::Extractor &ex) while (! ex.at_end ()) { std::string n; - ex.read_word_or_quoted (n, "_.:,!+$/\\"); + ex.read_word_or_quoted (n, allowed_name_chars); if (ex.test ("=")) { // a parameter diff --git a/src/db/db/dbNetlistSpiceWriter.cc b/src/db/db/dbNetlistSpiceWriter.cc index ea4fec215..fa5736086 100644 --- a/src/db/db/dbNetlistSpiceWriter.cc +++ b/src/db/db/dbNetlistSpiceWriter.cc @@ -31,6 +31,8 @@ namespace db { +static const char *allowed_name_chars = "_.:,!+$/&\\#[]"; + // -------------------------------------------------------------------------------- NetlistSpiceWriterDelegate::NetlistSpiceWriterDelegate () @@ -257,7 +259,7 @@ std::string NetlistSpiceWriter::net_to_string (const db::Net *net) const nn += "\\"; } for (const char *cp = n.c_str (); *cp; ++cp) { - if (! isalnum (*cp) && strchr (".$!&\\#+:,", *cp) == 0) { + if (! isalnum (*cp) && strchr (allowed_name_chars, *cp) == 0) { nn += tl::sprintf ("\\x%02x", (unsigned char) *cp); } else if (*cp == ',') { nn += "|"; From 7cdd40dabbc41fd15f74d5af3efc1f54c48ca08a Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 8 Apr 2019 21:21:34 +0200 Subject: [PATCH 37/54] Netlist compare: more detailed derivation of net assignments from known nets (pairing by deduction) --- src/db/db/dbNetlistCompare.cc | 69 ++++++++++++++++------ src/db/unit_tests/dbNetlistCompareTests.cc | 8 +-- testdata/ruby/dbNetlistCompare.rb | 4 +- 3 files changed, 56 insertions(+), 25 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 9cad3d334..d43e813fd 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -770,6 +770,14 @@ namespace std namespace db { +struct CompareNodePtr +{ + bool operator() (const NetGraphNode *a, const NetGraphNode *b) const + { + return *a < *b; + } +}; + class NetDeviceGraph { public: @@ -798,7 +806,9 @@ public: for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { NetGraphNode node (n.operator-> (), device_categorizer, circuit_categorizer, device_filter, circuit_and_pin_mapping, circuit_pin_mapper); - m_nodes.push_back (node); + if (! node.empty () || n->pin_count () > 0) { + m_nodes.push_back (node); + } } std::sort (m_nodes.begin (), m_nodes.end ()); @@ -867,22 +877,25 @@ public: ++ee; } - size_t count = 0; - NetGraphNode::edge_iterator ec; + std::vector nodes_with_same_path; + nodes_with_same_path.reserve (ee - e); + for (NetGraphNode::edge_iterator i = e; i != ee; ++i) { - if (! m_nodes[i->second.first].has_other ()) { - ec = i; - ++count; + const NetGraphNode *n = &m_nodes[i->second.first]; + if (! n->has_other ()) { + nodes_with_same_path.push_back (n); } } - if (count == 1) { // if non-ambiguous, non-assigned + std::sort (nodes_with_same_path.begin (), nodes_with_same_path.end (), CompareNodePtr ()); + + if (! nodes_with_same_path.empty ()) { // if non-ambiguous, non-assigned #if defined(PRINT_DEBUG_NETCOMPARE) tl::log << "considering " << n->net ()->expanded_name () << " to " << ec->second.second->expanded_name (); #endif - NetGraphNode::edge_iterator e_other = nother->find_edge (ec->first); + NetGraphNode::edge_iterator e_other = nother->find_edge (e->first); if (e_other != nother->end ()) { #if defined(PRINT_DEBUG_NETCOMPARE) @@ -896,21 +909,39 @@ public: } size_t count_other = 0; - NetGraphNode::edge_iterator ec_other; - for (NetGraphNode::edge_iterator i = e_other; i != ee_other; ++i) { - if (! other.m_nodes[i->second.first].has_other ()) { - ec_other = i; - ++count_other; + size_t node_index = 0, other_node_index = 0; + for (NetGraphNode::edge_iterator i = e_other; i != ee_other && count_other < 2; ++i) { + + const NetGraphNode *n = &other.m_nodes[i->second.first]; + if (! n->has_other ()) { + + if (nodes_with_same_path.size () == 1) { + + // a single candiate: just take this one -> this may render + // inexact matches, but further propagates net pairing + other_node_index = i->second.first; + node_index = node_index_for_net (nodes_with_same_path.front ()->net ()); + ++count_other; + + } else { + + std::vector::const_iterator in_this = std::lower_bound (nodes_with_same_path.begin (), nodes_with_same_path.end (), n, CompareNodePtr ()); + if (in_this != nodes_with_same_path.end () && *n == **in_this) { + other_node_index = i->second.first; + node_index = node_index_for_net ((*in_this)->net ()); + ++count_other; + } + + } + } + } -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::log << "identity count = " << count_other; -#endif if (count_other == 1) { - confirm_identity (*this, begin () + ec->second.first, other, other.begin () + ec_other->second.first, logger); + confirm_identity (*this, begin () + node_index, other, other.begin () + other_node_index, logger); ++added; - more.push_back (ec->second.first); + more.push_back (node_index); } } @@ -1234,7 +1265,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, size_t new_identities = 0; for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { - if (i1->has_other ()) { + if (i1->has_other () && i1->net ()) { #if defined(PRINT_DEBUG_NETCOMPARE) tl::log << "deriving new identities from " << i1->net ()->expanded_name (); #endif diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index fb250d97d..519f818b8 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -658,8 +658,8 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" + "match_nets INT $10\n" "match_nets IN IN\n" - "match_ambiguous_nets INT $10\n" "match_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" @@ -710,8 +710,8 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" + "match_nets INT $10\n" "match_nets IN IN\n" - "match_ambiguous_nets INT $10\n" "match_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" @@ -736,8 +736,8 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" + "match_nets INT $10\n" "match_nets IN IN\n" - "match_ambiguous_nets INT $10\n" "match_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" @@ -816,8 +816,8 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" + "match_nets INT $10\n" "match_nets IN IN\n" - "match_ambiguous_nets INT $10\n" "match_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" diff --git a/testdata/ruby/dbNetlistCompare.rb b/testdata/ruby/dbNetlistCompare.rb index 47502e119..b955ba481 100644 --- a/testdata/ruby/dbNetlistCompare.rb +++ b/testdata/ruby/dbNetlistCompare.rb @@ -460,8 +460,8 @@ END assert_equal(logger.text, <<"END") begin_circuit BUF BUF match_nets OUT OUT +match_nets INT $10 match_nets IN IN -match_ambiguous_nets INT $10 match_nets INT2 $11 match_pins $0 $1 match_pins $1 $3 @@ -887,8 +887,8 @@ match_nets OUT OUT match_nets IN2 IN2 match_nets VSS VSS match_nets VDD VDD -match_nets IN1 IN1 match_nets INT INT +match_nets IN1 IN1 match_pins $0 $0 match_pins $1 $1 match_pins $2 $2 From 2e9422a753b5ccb4b259700549fb49686025de23 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 8 Apr 2019 21:32:41 +0200 Subject: [PATCH 38/54] Netlist compare: a little less freedom when picking derived net pairs ... --- src/db/db/dbNetlistCompare.cc | 2 +- src/db/unit_tests/dbNetlistCompareTests.cc | 8 ++++---- testdata/ruby/dbNetlistCompare.rb | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index d43e813fd..7e49f641d 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -926,7 +926,7 @@ public: } else { std::vector::const_iterator in_this = std::lower_bound (nodes_with_same_path.begin (), nodes_with_same_path.end (), n, CompareNodePtr ()); - if (in_this != nodes_with_same_path.end () && *n == **in_this) { + if (in_this != nodes_with_same_path.end () && *n == **in_this && (in_this + 1 == nodes_with_same_path.end () || ! (*n == **(in_this + 1)))) { other_node_index = i->second.first; node_index = node_index_for_net ((*in_this)->net ()); ++count_other; diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 519f818b8..fb250d97d 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -658,8 +658,8 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" - "match_nets INT $10\n" "match_nets IN IN\n" + "match_ambiguous_nets INT $10\n" "match_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" @@ -710,8 +710,8 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" - "match_nets INT $10\n" "match_nets IN IN\n" + "match_ambiguous_nets INT $10\n" "match_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" @@ -736,8 +736,8 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" - "match_nets INT $10\n" "match_nets IN IN\n" + "match_ambiguous_nets INT $10\n" "match_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" @@ -816,8 +816,8 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" - "match_nets INT $10\n" "match_nets IN IN\n" + "match_ambiguous_nets INT $10\n" "match_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" diff --git a/testdata/ruby/dbNetlistCompare.rb b/testdata/ruby/dbNetlistCompare.rb index b955ba481..8f8e254c9 100644 --- a/testdata/ruby/dbNetlistCompare.rb +++ b/testdata/ruby/dbNetlistCompare.rb @@ -460,8 +460,8 @@ END assert_equal(logger.text, <<"END") begin_circuit BUF BUF match_nets OUT OUT -match_nets INT $10 match_nets IN IN +match_ambiguous_nets INT $10 match_nets INT2 $11 match_pins $0 $1 match_pins $1 $3 From 6b6cc5a34fa3e9b20dd7423344d09f50d3b29f74 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 9 Apr 2019 16:44:47 +0200 Subject: [PATCH 39/54] WIP: network compare, debugging output. --- src/db/db/dbNetlistCompare.cc | 119 +++++++++++++++++----------------- 1 file changed, 61 insertions(+), 58 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 7e49f641d..6616b0680 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -28,6 +28,9 @@ #include "tlEquivalenceClusters.h" #include "tlLog.h" +// verbose debug output +#define PRINT_DEBUG_NETCOMPARE + namespace db { @@ -864,6 +867,9 @@ public: NetGraphNode *n = & m_nodes[*index]; NetGraphNode *nother = & other.m_nodes[n->other_net_index ()]; +if (n->net()->name() == "decode_instruction_anticipated[23]") { + printf("@@@1\n"); +} // non-ambiguous paths to non-assigned nodes create a node identity on the // end of this path @@ -887,20 +893,13 @@ public: } } - std::sort (nodes_with_same_path.begin (), nodes_with_same_path.end (), CompareNodePtr ()); - if (! nodes_with_same_path.empty ()) { // if non-ambiguous, non-assigned -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::log << "considering " << n->net ()->expanded_name () << " to " << ec->second.second->expanded_name (); -#endif + std::sort (nodes_with_same_path.begin (), nodes_with_same_path.end (), CompareNodePtr ()); NetGraphNode::edge_iterator e_other = nother->find_edge (e->first); if (e_other != nother->end ()) { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::log << "candidate accepted"; -#endif NetGraphNode::edge_iterator ee_other = e_other; ++ee_other; @@ -939,6 +938,9 @@ public: } if (count_other == 1) { +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << "deduction from pair " << n->net ()->expanded_name () << " vs. " << nother->net ()->expanded_name (); +#endif confirm_identity (*this, begin () + node_index, other, other.begin () + other_node_index, logger); ++added; more.push_back (node_index); @@ -1256,24 +1258,27 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, g2.identify (ni2, ni1); } +#if defined(PRINT_DEBUG_NETCOMPARE) + int iter = 0; +#endif + bool good = true; while (good) { #if defined(PRINT_DEBUG_NETCOMPARE) - tl::log << "new iteration ..."; + ++iter; + tl::info << "new compare iteration #" << iter; + tl::info << "deducing from present nodes ..."; #endif size_t new_identities = 0; for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { if (i1->has_other () && i1->net ()) { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::log << "deriving new identities from " << i1->net ()->expanded_name (); -#endif size_t ni = g1.derive_node_identities (i1 - g1.begin (), g2, mp_logger); new_identities += ni; if (ni > 0) { #if defined(PRINT_DEBUG_NETCOMPARE) - tl::log << ni << " new identities."; + tl::info << ni << " new identities."; #endif } } @@ -1288,79 +1293,77 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, break; } - if (new_identities == 0) { + new_identities = 0; #if defined(PRINT_DEBUG_NETCOMPARE) - tl::log << "checking topological identity ..."; + tl::info << "checking topological identity ..."; #endif - // first pass: without ambiguities, second pass: match ambiguous nets - for (int pass = 0; pass < 2 && new_identities == 0; ++pass) { + // first pass: without ambiguities, second pass: match ambiguous nets + for (int pass = 0; pass < 2 && new_identities == 0; ++pass) { - // derive new identities through topology + // derive new identities through topology - bool same_as_prev = false; + bool same_as_prev = false; - db::NetDeviceGraph::node_iterator i1 = g1.begin (), i2 = g2.begin (); - for ( ; i1 != g1.end () && i2 != g2.end (); ) { + db::NetDeviceGraph::node_iterator i1 = g1.begin (), i2 = g2.begin (); + for ( ; i1 != g1.end () && i2 != g2.end (); ) { - bool same_as_next = false; + bool same_as_next = false; - if (i1->has_other ()) { - ++i1; - } else if (i2->has_other ()) { - ++i2; - } else if (*i1 < *i2) { - ++i1; - } else if (*i2 < *i1) { - ++i2; - } else { + if (i1->has_other ()) { + ++i1; + } else if (i2->has_other ()) { + ++i2; + } else if (*i1 < *i2) { + ++i1; + } else if (*i2 < *i1) { + ++i2; + } else { - db::NetDeviceGraph::node_iterator ii1 = i1, ii2 = i2; + db::NetDeviceGraph::node_iterator ii1 = i1, ii2 = i2; - ++i1; - ++i2; + ++i1; + ++i2; - same_as_next = (i1 != g1.end () && *i1 == *ii1) || (i2 != g2.end () && *i2 == *ii2); + same_as_next = (i1 != g1.end () && *i1 == *ii1) || (i2 != g2.end () && *i2 == *ii2); - // found a candidate - a single node with the same edges + // found a candidate - a single node with the same edges - bool ambiguous = (same_as_next || same_as_prev); - if (! ambiguous || pass) { + bool ambiguous = (same_as_next || same_as_prev); + if (! ambiguous || pass) { - // For ambiguous nets make the pins exchangable + // For ambiguous nets make the pins exchangable - if (same_as_next) { + if (same_as_next) { - if (ii1->net () && i1->net ()) { - for (db::Net::const_pin_iterator pp = ii1->net ()->begin_pins (); pp != ii1->net ()->end_pins (); ++pp) { - for (db::Net::const_pin_iterator p = i1->net ()->begin_pins (); p != i1->net ()->end_pins (); ++p) { - mp_circuit_pin_mapper->map_pins (c1, pp->pin_id (), p->pin_id ()); - } + if (ii1->net () && i1->net ()) { + for (db::Net::const_pin_iterator pp = ii1->net ()->begin_pins (); pp != ii1->net ()->end_pins (); ++pp) { + for (db::Net::const_pin_iterator p = i1->net ()->begin_pins (); p != i1->net ()->end_pins (); ++p) { + mp_circuit_pin_mapper->map_pins (c1, pp->pin_id (), p->pin_id ()); } } - - if (ii2->net () && i2->net ()) { - for (db::Net::const_pin_iterator pp = ii2->net ()->begin_pins (); pp != ii2->net ()->end_pins (); ++pp) { - for (db::Net::const_pin_iterator p = i2->net ()->begin_pins (); p != i2->net ()->end_pins (); ++p) { - mp_circuit_pin_mapper->map_pins (c2, pp->pin_id (), p->pin_id ()); - } - } - } - } - db::NetDeviceGraph::confirm_identity (g1, ii1, g2, ii2, mp_logger, ambiguous); - ++new_identities; + if (ii2->net () && i2->net ()) { + for (db::Net::const_pin_iterator pp = ii2->net ()->begin_pins (); pp != ii2->net ()->end_pins (); ++pp) { + for (db::Net::const_pin_iterator p = i2->net ()->begin_pins (); p != i2->net ()->end_pins (); ++p) { + mp_circuit_pin_mapper->map_pins (c2, pp->pin_id (), p->pin_id ()); + } + } + } } + db::NetDeviceGraph::confirm_identity (g1, ii1, g2, ii2, mp_logger, ambiguous); + ++new_identities; + } - same_as_prev = same_as_next; - } + same_as_prev = same_as_next; + } } From a3edd95f9477fc86d24290bb64740b5d3ba6720c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 9 Apr 2019 22:31:03 +0200 Subject: [PATCH 40/54] WIP: new backtracking algorithm for net matching. --- src/db/db/dbNetlistCompare.cc | 337 ++++++++++++++++++++++++---------- 1 file changed, 244 insertions(+), 93 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 6616b0680..2797e1ffc 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -617,6 +617,11 @@ public: m_other_net_index = index; } + void unset_other_net () + { + m_other_net_index = std::numeric_limits::max (); + } + bool empty () const { return m_edges.empty (); @@ -841,6 +846,11 @@ public: m_nodes [net_index].set_other_net (other_net_index); } + void unidentify (size_t net_index) + { + m_nodes [net_index].unset_other_net (); + } + node_iterator begin () const { return m_nodes.begin (); @@ -851,114 +861,249 @@ public: return m_nodes.end (); } - size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, NetlistCompareLogger *logger) + /** + * @brief Implementation of the backtracking algorithm + * + * This method derives new node assignments based on the (proposed) + * identity of nodes this->[net_index] and other[node]. + * The return value will be: + * + * >0 if node identity could be established. The return value + * is the number of new node pairs established. All + * node pairs (including the initial proposed identity) + * are assigned. + * ==0 identity could be established. No more assignments are made. + * max no decision could be made because the max. complexity + * was exhausted. No assignments were made. + * + * (here: max=max of size_t). The complexity is measured in + * backtracking depth (number of graph jumps) and decision tree + * branching complexity N (="n_branch", means: N*N decisions to be made). + * + * The limits "depth_max" and "n_branch_max" are attributes of the graph. + * + * If tentative is true, assignments will not be retained and just the + * status is reported. + */ + size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, bool tentative) { - size_t added = 0; + const size_t depth_max = 8; + const size_t n_branch_max = 100; - std::vector todo, more; - more.push_back (net_index); + NetGraphNode *n = & m_nodes[net_index]; + NetGraphNode *nother = & other.m_nodes[n->other_net_index ()]; - while (! more.empty ()) { - - todo.swap (more); - more.clear (); - - for (std::vector::const_iterator index = todo.begin (); index != todo.end (); ++index) { - - NetGraphNode *n = & m_nodes[*index]; - NetGraphNode *nother = & other.m_nodes[n->other_net_index ()]; -if (n->net()->name() == "decode_instruction_anticipated[23]") { - printf("@@@1\n"); -} - - // non-ambiguous paths to non-assigned nodes create a node identity on the - // end of this path - - for (NetGraphNode::edge_iterator e = n->begin (); e != n->end (); ) { - - NetGraphNode::edge_iterator ee = e; - ++ee; - - while (ee != n->end () && ee->first == e->first) { - ++ee; - } - - std::vector nodes_with_same_path; - nodes_with_same_path.reserve (ee - e); - - for (NetGraphNode::edge_iterator i = e; i != ee; ++i) { - const NetGraphNode *n = &m_nodes[i->second.first]; - if (! n->has_other ()) { - nodes_with_same_path.push_back (n); - } - } - - if (! nodes_with_same_path.empty ()) { // if non-ambiguous, non-assigned - - std::sort (nodes_with_same_path.begin (), nodes_with_same_path.end (), CompareNodePtr ()); - - NetGraphNode::edge_iterator e_other = nother->find_edge (e->first); - if (e_other != nother->end ()) { - - NetGraphNode::edge_iterator ee_other = e_other; - ++ee_other; - - while (ee_other != nother->end () && ee_other->first == e_other->first) { - ++ee_other; - } - - size_t count_other = 0; - size_t node_index = 0, other_node_index = 0; - for (NetGraphNode::edge_iterator i = e_other; i != ee_other && count_other < 2; ++i) { - - const NetGraphNode *n = &other.m_nodes[i->second.first]; - if (! n->has_other ()) { - - if (nodes_with_same_path.size () == 1) { - - // a single candiate: just take this one -> this may render - // inexact matches, but further propagates net pairing - other_node_index = i->second.first; - node_index = node_index_for_net (nodes_with_same_path.front ()->net ()); - ++count_other; - - } else { - - std::vector::const_iterator in_this = std::lower_bound (nodes_with_same_path.begin (), nodes_with_same_path.end (), n, CompareNodePtr ()); - if (in_this != nodes_with_same_path.end () && *n == **in_this && (in_this + 1 == nodes_with_same_path.end () || ! (*n == **(in_this + 1)))) { - other_node_index = i->second.first; - node_index = node_index_for_net ((*in_this)->net ()); - ++count_other; - } - - } - - } - - } - - if (count_other == 1) { #if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << "deduction from pair " << n->net ()->expanded_name () << " vs. " << nother->net ()->expanded_name (); + if (! tentative) { + tl::info << "deducing from pair: " << n->net ()->expanded_name () << " vs. " << nother->net ()->expanded_name (); + } #endif - confirm_identity (*this, begin () + node_index, other, other.begin () + other_node_index, logger); - ++added; - more.push_back (node_index); - } + size_t new_nodes = 0; + + // non-ambiguous paths to non-assigned nodes create a node identity on the + // end of this path + + for (NetGraphNode::edge_iterator e = n->begin (); e != n->end (); ) { + + NetGraphNode::edge_iterator ee = e; + ++ee; + + while (ee != n->end () && ee->first == e->first) { + ++ee; + } + + std::vector nodes_with_same_path; + nodes_with_same_path.reserve (ee - e); + + std::vector other_nodes_with_same_path; + other_nodes_with_same_path.reserve (ee - e); + + for (NetGraphNode::edge_iterator i = e; i != ee; ++i) { + const NetGraphNode *n = &m_nodes[i->second.first]; + if (! n->has_other ()) { + nodes_with_same_path.push_back (n); + } + } + + if (! nodes_with_same_path.empty ()) { // if non-ambiguous, non-assigned + + std::sort (nodes_with_same_path.begin (), nodes_with_same_path.end (), CompareNodePtr ()); + + NetGraphNode::edge_iterator e_other = nother->find_edge (e->first); + if (e_other != nother->end ()) { + + NetGraphNode::edge_iterator ee_other = e_other; + ++ee_other; + + while (ee_other != nother->end () && ee_other->first == e_other->first) { + ++ee_other; + } + + size_t count_other = 0; + for (NetGraphNode::edge_iterator i = e_other; i != ee_other && count_other < 2; ++i) { + + const NetGraphNode *n = &other.m_nodes[i->second.first]; + if (! n->has_other ()) { + other_nodes_with_same_path.push_back (n); } } - e = ee; + std::sort (other_nodes_with_same_path.begin (), other_nodes_with_same_path.end (), CompareNodePtr ()); } } + if (nodes_with_same_path.size () == 1 && other_nodes_with_same_path.size () == 1) { + + if (depth + 1 == depth_max) { + return std::numeric_limits::max (); + } + + // a single candiate: just take this one -> this may render + // inexact matches, but further propagates net pairing + + size_t ni = node_index_for_net (nodes_with_same_path.front ()->net ()); + size_t other_ni = other.node_index_for_net (other_nodes_with_same_path.front ()->net ()); + if (! tentative) { + + identify (ni, other_ni); + other.identify (other_ni, ni); + +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << "deduced match: " << nodes_with_same_path.front ()->net ()->expanded_name () << " vs. " << other_nodes_with_same_path.front ()->net ()->expanded_name (); +#endif + if (logger) { + logger->match_nets (nodes_with_same_path.front ()->net (), other_nodes_with_same_path.front ()->net ()); + } + + // unconditionally continue here. + // @@@ derive_node_identities (ni, other, depth + 1, n_branch, logger, false); + + } + + new_nodes += 1; + + } else if (! nodes_with_same_path.empty () || ! other_nodes_with_same_path.empty ()) { + + if (depth + 1 == depth_max) { + return std::numeric_limits::max (); + } + + if (nodes_with_same_path.size () != other_nodes_with_same_path.size ()) { + return std::numeric_limits::max (); + } + + for (size_t i = 0; i < nodes_with_same_path.size (); ++i) { + if (! (*nodes_with_same_path[i] == *other_nodes_with_same_path[i])) { + return std::numeric_limits::max (); + } + } + + std::vector::iterator n1 = nodes_with_same_path.begin (); + std::vector::iterator n2 = other_nodes_with_same_path.begin (); + + while (n1 != nodes_with_same_path.end () && n2 != other_nodes_with_same_path.end ()) { + + std::vector::iterator nn1 = n1, nn2 = n2; + + ++nn1; + ++nn2; + while (nn1 != nodes_with_same_path.end () && **nn1 == **n1) { + ++nn1; + ++nn2; + } + + size_t num = nn1 - n1; + if (num * n_branch > n_branch_max) { + return std::numeric_limits::max (); + } + + std::vector > pairs; + + for (std::vector::iterator i1 = n1; i1 != nn1; ++i1) { + + std::vector::iterator i2; + for (i2 = n2; i2 != nn2; ++i2) { + + if (! *i2) { + continue; + } + + size_t ni = node_index_for_net ((*i1)->net ()); + size_t other_ni = other.node_index_for_net ((*i2)->net ()); + + identify (ni, other_ni); + other.identify (other_ni, ni); + + size_t bt_count = derive_node_identities (ni, other, depth + 1, num * n_branch, logger, true /*tentative*/); + + unidentify (ni); + other.unidentify (other_ni); + + if (bt_count != std::numeric_limits::max ()) { + // identified a pair + new_nodes += bt_count + 1; + pairs.push_back (std::make_pair (*i1, *i2)); + *i2 = 0; + break; + } + + } + + if (i2 == nn2) { + // a mismatch - stop here. + return std::numeric_limits::max (); + } + + } + + if (! tentative) { + + for (std::vector >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) { + + size_t ni = node_index_for_net (p->first->net ()); + size_t other_ni = other.node_index_for_net (p->second->net ()); + + identify (ni, other_ni); + other.identify (other_ni, ni); + + size_t bt_count = derive_node_identities (ni, other, depth + 1, num * n_branch, logger, false /*not tentative*/); + tl_assert (bt_count != std::numeric_limits::max ()); + +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << "deduced match: " << p->first->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name (); +#endif + if (logger) { + logger->match_nets (p->first->net (), p->second->net ()); + } + + } + + } + + } + + } + + e = ee; + } - return added; +#if defined(PRINT_DEBUG_NETCOMPARE) + if (! tentative && new_nodes > 0 && new_nodes != std::numeric_limits::max ()) { + tl::info << "finished pair deduction: " << n->net ()->expanded_name () << " vs. " << nother->net ()->expanded_name () << " with " << new_nodes << " new pairs"; + } +#endif + + return new_nodes; + } + + size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, NetlistCompareLogger *logger) + { + return derive_node_identities (net_index, other, 0, 1, logger, false); } static void confirm_identity (db::NetDeviceGraph &g1, db::NetDeviceGraph::node_iterator s1, db::NetDeviceGraph &g2, db::NetDeviceGraph::node_iterator s2, db::NetlistCompareLogger *logger, bool ambiguous = false) @@ -1122,6 +1267,9 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const if (all_subcircuits_verified (ca, verified_circuits_a) && all_subcircuits_verified (cb, verified_circuits_b)) { +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << "treating circuit: " << ca->name () << " vs. " << cb->name (); +#endif if (mp_logger) { mp_logger->begin_circuit (ca, cb); } @@ -1276,9 +1424,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, if (i1->has_other () && i1->net ()) { size_t ni = g1.derive_node_identities (i1 - g1.begin (), g2, mp_logger); new_identities += ni; - if (ni > 0) { + if (ni > 0 && ni != std::numeric_limits::max ()) { #if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << ni << " new identities."; + tl::info << ni << " new identities."; #endif } } @@ -1355,6 +1503,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << "topological match: " << ii1->net ()->expanded_name () << " vs. " << ii2->net ()->expanded_name (); +#endif db::NetDeviceGraph::confirm_identity (g1, ii1, g2, ii2, mp_logger, ambiguous); ++new_identities; From f34d161e2f0e87b9963bfa87b896a7cec2210d02 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 9 Apr 2019 23:13:40 +0200 Subject: [PATCH 41/54] WIP: new backtracking algorithm for net matching. --- src/db/db/dbNetlistCompare.cc | 14 ++++++--- src/db/unit_tests/dbNetlistCompareTests.cc | 36 +++++++++++----------- testdata/ruby/dbNetlistCompare.rb | 28 ++++++++--------- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 2797e1ffc..1aa331426 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -973,14 +973,14 @@ public: other.identify (other_ni, ni); #if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << "deduced match: " << nodes_with_same_path.front ()->net ()->expanded_name () << " vs. " << other_nodes_with_same_path.front ()->net ()->expanded_name (); + tl::info << "deduced match (singular): " << nodes_with_same_path.front ()->net ()->expanded_name () << " vs. " << other_nodes_with_same_path.front ()->net ()->expanded_name (); #endif if (logger) { logger->match_nets (nodes_with_same_path.front ()->net (), other_nodes_with_same_path.front ()->net ()); } // unconditionally continue here. - // @@@ derive_node_identities (ni, other, depth + 1, n_branch, logger, false); + derive_node_identities (ni, other, depth + 1, n_branch, logger, false); } @@ -1064,6 +1064,12 @@ public: for (std::vector >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) { + if (p->first->has_other () || p->second->has_other ()) { + // this may happen if "derive_node_identities" creates new pairs + // TODO: actually *both* should be paired, not just one. + continue; + } + size_t ni = node_index_for_net (p->first->net ()); size_t other_ni = other.node_index_for_net (p->second->net ()); @@ -1419,11 +1425,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, tl::info << "deducing from present nodes ..."; #endif - size_t new_identities = 0; for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { if (i1->has_other () && i1->net ()) { size_t ni = g1.derive_node_identities (i1 - g1.begin (), g2, mp_logger); - new_identities += ni; if (ni > 0 && ni != std::numeric_limits::max ()) { #if defined(PRINT_DEBUG_NETCOMPARE) tl::info << ni << " new identities."; @@ -1441,7 +1445,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, break; } - new_identities = 0; + size_t new_identities = 0; #if defined(PRINT_DEBUG_NETCOMPARE) tl::info << "checking topological identity ..."; diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index fb250d97d..4cb7c998d 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -595,8 +595,8 @@ TEST(4_BufferTwoPaths) "match_nets VDD VDD\n" "match_nets IN IN\n" "match_nets VSS VSS\n" - "match_ambiguous_nets INT $10\n" - "match_ambiguous_nets INT2 $11\n" + "match_nets INT $10\n" + "match_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -658,9 +658,9 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" - "match_nets IN IN\n" "match_ambiguous_nets INT $10\n" "match_nets INT2 $11\n" + "match_nets IN IN\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -685,8 +685,8 @@ TEST(5_BufferTwoPathsDifferentParameters) "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" "match_nets IN IN\n" - "match_ambiguous_nets INT $10\n" - "match_ambiguous_nets INT2 $11\n" + "match_nets INT2 $11\n" + "match_nets INT $10\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -710,9 +710,9 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" - "match_nets IN IN\n" "match_ambiguous_nets INT $10\n" "match_nets INT2 $11\n" + "match_nets IN IN\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -736,9 +736,9 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" - "match_nets IN IN\n" "match_ambiguous_nets INT $10\n" "match_nets INT2 $11\n" + "match_nets IN IN\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -763,8 +763,8 @@ TEST(5_BufferTwoPathsDifferentParameters) "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" "match_nets IN IN\n" - "match_ambiguous_nets INT $10\n" - "match_ambiguous_nets INT2 $11\n" + "match_nets INT2 $11\n" + "match_nets INT $10\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -790,8 +790,8 @@ TEST(5_BufferTwoPathsDifferentParameters) "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" "match_nets IN IN\n" - "match_ambiguous_nets INT $10\n" - "match_ambiguous_nets INT2 $11\n" + "match_nets INT2 $11\n" + "match_nets INT $10\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -816,9 +816,9 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" - "match_nets IN IN\n" "match_ambiguous_nets INT $10\n" "match_nets INT2 $11\n" + "match_nets IN IN\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -879,10 +879,10 @@ TEST(5_BufferTwoPathsDifferentDeviceClasses) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" - "match_nets IN IN\n" "match_nets INT $10\n" - "match_nets OUT OUT\n" + "match_nets IN IN\n" "match_nets INT2 $11\n" + "match_nets OUT OUT\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -945,9 +945,9 @@ TEST(6_BufferTwoPathsAdditionalResistor) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets INT $10\n" - "match_nets OUT OUT\n" - "match_nets INT2 $11\n" "match_nets IN IN\n" + "match_nets INT2 $11\n" + "match_nets OUT OUT\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -2025,12 +2025,12 @@ TEST(16_UniqueSubCircuitMatching) "match_nets BULK,VSS BULK,VSS\n" "match_nets $I22 $I22\n" "match_nets $I13 $I13\n" - "match_nets $I7 $I7\n" "match_nets $I23 $I23\n" "match_nets $I5 $I5\n" - "match_nets $I25 $I25\n" "match_nets $I24 $I24\n" "match_nets $I6 $I6\n" + "match_nets $I25 $I25\n" + "match_nets $I7 $I7\n" "match_subcircuits $1 $1\n" "match_subcircuits $4 $2\n" "match_subcircuits $3 $3\n" diff --git a/testdata/ruby/dbNetlistCompare.rb b/testdata/ruby/dbNetlistCompare.rb index 8f8e254c9..15a2f2f72 100644 --- a/testdata/ruby/dbNetlistCompare.rb +++ b/testdata/ruby/dbNetlistCompare.rb @@ -392,8 +392,8 @@ match_nets OUT OUT match_nets VDD VDD match_nets IN IN match_nets VSS VSS -match_ambiguous_nets INT $10 -match_ambiguous_nets INT2 $11 +match_nets INT $10 +match_nets INT2 $11 match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -460,9 +460,9 @@ END assert_equal(logger.text, <<"END") begin_circuit BUF BUF match_nets OUT OUT -match_nets IN IN match_ambiguous_nets INT $10 match_nets INT2 $11 +match_nets IN IN match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -489,8 +489,8 @@ END begin_circuit BUF BUF match_nets OUT OUT match_nets IN IN -match_ambiguous_nets INT $10 -match_ambiguous_nets INT2 $11 +match_nets INT2 $11 +match_nets INT $10 match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -518,8 +518,8 @@ END begin_circuit BUF BUF match_nets OUT OUT match_nets IN IN -match_ambiguous_nets INT $10 -match_ambiguous_nets INT2 $11 +match_nets INT2 $11 +match_nets INT $10 match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -547,8 +547,8 @@ END begin_circuit BUF BUF match_nets OUT OUT match_nets IN IN -match_ambiguous_nets INT $10 -match_ambiguous_nets INT2 $11 +match_nets INT2 $11 +match_nets INT $10 match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -614,10 +614,10 @@ END assert_equal(logger.text, <<"END") begin_circuit BUF BUF -match_nets IN IN match_nets INT $10 -match_nets OUT OUT +match_nets IN IN match_nets INT2 $11 +match_nets OUT OUT match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -685,9 +685,9 @@ END assert_equal(logger.text, <<"END") begin_circuit BUF BUF match_nets INT $10 -match_nets OUT OUT -match_nets INT2 $11 match_nets IN IN +match_nets INT2 $11 +match_nets OUT OUT match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -887,8 +887,8 @@ match_nets OUT OUT match_nets IN2 IN2 match_nets VSS VSS match_nets VDD VDD -match_nets INT INT match_nets IN1 IN1 +match_nets INT INT match_pins $0 $0 match_pins $1 $1 match_pins $2 $2 From c0b1c4f775ba72d3b751ab0dafae172568839adb Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 11 Apr 2019 00:13:19 +0200 Subject: [PATCH 42/54] WIP: enhanced backtracking for netlist compare --- src/db/db/dbNetlistCompare.cc | 769 +++++++++++++++++++--------------- 1 file changed, 420 insertions(+), 349 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 1aa331426..530ef240c 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -29,7 +29,7 @@ #include "tlLog.h" // verbose debug output -#define PRINT_DEBUG_NETCOMPARE +// #define PRINT_DEBUG_NETCOMPARE namespace db { @@ -796,38 +796,7 @@ public: // .. nothing yet .. } - void build (const db::Circuit *c, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const db::DeviceFilter &device_filter, const std::map *circuit_and_pin_mapping, const CircuitPinMapper *circuit_pin_mapper) - { - tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Building net graph for circuit: ")) + c->name ()); - - m_nodes.clear (); - m_net_index.clear (); - - // create a dummy node for a null net - m_nodes.push_back (NetGraphNode (0, device_categorizer, circuit_categorizer, device_filter, circuit_and_pin_mapping, circuit_pin_mapper)); - - size_t nets = 0; - for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - ++nets; - } - m_nodes.reserve (nets); - - for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - NetGraphNode node (n.operator-> (), device_categorizer, circuit_categorizer, device_filter, circuit_and_pin_mapping, circuit_pin_mapper); - if (! node.empty () || n->pin_count () > 0) { - m_nodes.push_back (node); - } - } - - std::sort (m_nodes.begin (), m_nodes.end ()); - - for (std::vector::const_iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { - m_net_index.insert (std::make_pair (i->net (), i - m_nodes.begin ())); - } - for (std::vector::iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { - i->apply_net_index (m_net_index); - } - } + void build (const db::Circuit *c, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const db::DeviceFilter &device_filter, const std::map *circuit_and_pin_mapping, const CircuitPinMapper *circuit_pin_mapper); size_t node_index_for_net (const db::Net *net) const { @@ -861,6 +830,11 @@ public: return m_nodes.end (); } + const db::Circuit *circuit () const + { + return mp_circuit; + } + /** * @brief Implementation of the backtracking algorithm * @@ -885,251 +859,394 @@ public: * If tentative is true, assignments will not be retained and just the * status is reported. */ - size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, bool tentative) + size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, bool tentative); + + size_t derive_node_identities_from_node_set (const std::vector &nodes, const std::vector &other_nodes, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, bool tentative, bool picky); + + size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper) { - const size_t depth_max = 8; - const size_t n_branch_max = 100; - - NetGraphNode *n = & m_nodes[net_index]; - NetGraphNode *nother = & other.m_nodes[n->other_net_index ()]; - -#if defined(PRINT_DEBUG_NETCOMPARE) - if (! tentative) { - tl::info << "deducing from pair: " << n->net ()->expanded_name () << " vs. " << nother->net ()->expanded_name (); - } -#endif - - size_t new_nodes = 0; - - // non-ambiguous paths to non-assigned nodes create a node identity on the - // end of this path - - for (NetGraphNode::edge_iterator e = n->begin (); e != n->end (); ) { - - NetGraphNode::edge_iterator ee = e; - ++ee; - - while (ee != n->end () && ee->first == e->first) { - ++ee; - } - - std::vector nodes_with_same_path; - nodes_with_same_path.reserve (ee - e); - - std::vector other_nodes_with_same_path; - other_nodes_with_same_path.reserve (ee - e); - - for (NetGraphNode::edge_iterator i = e; i != ee; ++i) { - const NetGraphNode *n = &m_nodes[i->second.first]; - if (! n->has_other ()) { - nodes_with_same_path.push_back (n); - } - } - - if (! nodes_with_same_path.empty ()) { // if non-ambiguous, non-assigned - - std::sort (nodes_with_same_path.begin (), nodes_with_same_path.end (), CompareNodePtr ()); - - NetGraphNode::edge_iterator e_other = nother->find_edge (e->first); - if (e_other != nother->end ()) { - - NetGraphNode::edge_iterator ee_other = e_other; - ++ee_other; - - while (ee_other != nother->end () && ee_other->first == e_other->first) { - ++ee_other; - } - - size_t count_other = 0; - for (NetGraphNode::edge_iterator i = e_other; i != ee_other && count_other < 2; ++i) { - - const NetGraphNode *n = &other.m_nodes[i->second.first]; - if (! n->has_other ()) { - other_nodes_with_same_path.push_back (n); - } - - } - - std::sort (other_nodes_with_same_path.begin (), other_nodes_with_same_path.end (), CompareNodePtr ()); - - } - - } - - if (nodes_with_same_path.size () == 1 && other_nodes_with_same_path.size () == 1) { - - if (depth + 1 == depth_max) { - return std::numeric_limits::max (); - } - - // a single candiate: just take this one -> this may render - // inexact matches, but further propagates net pairing - - size_t ni = node_index_for_net (nodes_with_same_path.front ()->net ()); - size_t other_ni = other.node_index_for_net (other_nodes_with_same_path.front ()->net ()); - if (! tentative) { - - identify (ni, other_ni); - other.identify (other_ni, ni); - -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << "deduced match (singular): " << nodes_with_same_path.front ()->net ()->expanded_name () << " vs. " << other_nodes_with_same_path.front ()->net ()->expanded_name (); -#endif - if (logger) { - logger->match_nets (nodes_with_same_path.front ()->net (), other_nodes_with_same_path.front ()->net ()); - } - - // unconditionally continue here. - derive_node_identities (ni, other, depth + 1, n_branch, logger, false); - - } - - new_nodes += 1; - - } else if (! nodes_with_same_path.empty () || ! other_nodes_with_same_path.empty ()) { - - if (depth + 1 == depth_max) { - return std::numeric_limits::max (); - } - - if (nodes_with_same_path.size () != other_nodes_with_same_path.size ()) { - return std::numeric_limits::max (); - } - - for (size_t i = 0; i < nodes_with_same_path.size (); ++i) { - if (! (*nodes_with_same_path[i] == *other_nodes_with_same_path[i])) { - return std::numeric_limits::max (); - } - } - - std::vector::iterator n1 = nodes_with_same_path.begin (); - std::vector::iterator n2 = other_nodes_with_same_path.begin (); - - while (n1 != nodes_with_same_path.end () && n2 != other_nodes_with_same_path.end ()) { - - std::vector::iterator nn1 = n1, nn2 = n2; - - ++nn1; - ++nn2; - while (nn1 != nodes_with_same_path.end () && **nn1 == **n1) { - ++nn1; - ++nn2; - } - - size_t num = nn1 - n1; - if (num * n_branch > n_branch_max) { - return std::numeric_limits::max (); - } - - std::vector > pairs; - - for (std::vector::iterator i1 = n1; i1 != nn1; ++i1) { - - std::vector::iterator i2; - for (i2 = n2; i2 != nn2; ++i2) { - - if (! *i2) { - continue; - } - - size_t ni = node_index_for_net ((*i1)->net ()); - size_t other_ni = other.node_index_for_net ((*i2)->net ()); - - identify (ni, other_ni); - other.identify (other_ni, ni); - - size_t bt_count = derive_node_identities (ni, other, depth + 1, num * n_branch, logger, true /*tentative*/); - - unidentify (ni); - other.unidentify (other_ni); - - if (bt_count != std::numeric_limits::max ()) { - // identified a pair - new_nodes += bt_count + 1; - pairs.push_back (std::make_pair (*i1, *i2)); - *i2 = 0; - break; - } - - } - - if (i2 == nn2) { - // a mismatch - stop here. - return std::numeric_limits::max (); - } - - } - - if (! tentative) { - - for (std::vector >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) { - - if (p->first->has_other () || p->second->has_other ()) { - // this may happen if "derive_node_identities" creates new pairs - // TODO: actually *both* should be paired, not just one. - continue; - } - - size_t ni = node_index_for_net (p->first->net ()); - size_t other_ni = other.node_index_for_net (p->second->net ()); - - identify (ni, other_ni); - other.identify (other_ni, ni); - - size_t bt_count = derive_node_identities (ni, other, depth + 1, num * n_branch, logger, false /*not tentative*/); - tl_assert (bt_count != std::numeric_limits::max ()); - -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << "deduced match: " << p->first->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name (); -#endif - if (logger) { - logger->match_nets (p->first->net (), p->second->net ()); - } - - } - - } - - } - - } - - e = ee; - - } - -#if defined(PRINT_DEBUG_NETCOMPARE) - if (! tentative && new_nodes > 0 && new_nodes != std::numeric_limits::max ()) { - tl::info << "finished pair deduction: " << n->net ()->expanded_name () << " vs. " << nother->net ()->expanded_name () << " with " << new_nodes << " new pairs"; - } -#endif - - return new_nodes; - } - - size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, NetlistCompareLogger *logger) - { - return derive_node_identities (net_index, other, 0, 1, logger, false); - } - - static void confirm_identity (db::NetDeviceGraph &g1, db::NetDeviceGraph::node_iterator s1, db::NetDeviceGraph &g2, db::NetDeviceGraph::node_iterator s2, db::NetlistCompareLogger *logger, bool ambiguous = false) - { - if (logger) { - if (ambiguous) { - logger->match_ambiguous_nets (s1->net (), s2->net ()); - } else { - logger->match_nets (s1->net (), s2->net ()); - } - } - g1.identify (s1 - g1.begin (), s2 - g2.begin ()); - g2.identify (s2 - g2.begin (), s1 - g1.begin ()); + return derive_node_identities (net_index, other, 0, 1, logger, circuit_pin_mapper, false); } private: std::vector m_nodes; std::map m_net_index; + const db::Circuit *mp_circuit; }; +// @@@ make attribute +const size_t depth_max = 8; +const size_t n_branch_max = 100; + +void +NetDeviceGraph::build (const db::Circuit *c, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const db::DeviceFilter &device_filter, const std::map *circuit_and_pin_mapping, const CircuitPinMapper *circuit_pin_mapper) +{ + tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Building net graph for circuit: ")) + c->name ()); + + mp_circuit = c; + + m_nodes.clear (); + m_net_index.clear (); + + // create a dummy node for a null net + m_nodes.push_back (NetGraphNode (0, device_categorizer, circuit_categorizer, device_filter, circuit_and_pin_mapping, circuit_pin_mapper)); + + size_t nets = 0; + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + ++nets; + } + m_nodes.reserve (nets); + + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + NetGraphNode node (n.operator-> (), device_categorizer, circuit_categorizer, device_filter, circuit_and_pin_mapping, circuit_pin_mapper); + if (! node.empty () || n->pin_count () > 0) { + m_nodes.push_back (node); + } + } + + std::sort (m_nodes.begin (), m_nodes.end ()); + + for (std::vector::const_iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { + m_net_index.insert (std::make_pair (i->net (), i - m_nodes.begin ())); + } + for (std::vector::iterator i = m_nodes.begin (); i != m_nodes.end (); ++i) { + i->apply_net_index (m_net_index); + } +} + +size_t +NetDeviceGraph::derive_node_identities (size_t net_index, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, bool tentative) +{ + NetGraphNode *n = & m_nodes[net_index]; + NetGraphNode *nother = & other.m_nodes[n->other_net_index ()]; + +#if defined(PRINT_DEBUG_NETCOMPARE) + if (! tentative) { + tl::info << "deducing from pair: " << n->net ()->expanded_name () << " vs. " << nother->net ()->expanded_name (); + } +#endif + + size_t new_nodes = 0; + + // non-ambiguous paths to non-assigned nodes create a node identity on the + // end of this path + + for (NetGraphNode::edge_iterator e = n->begin (); e != n->end (); ) { + + NetGraphNode::edge_iterator ee = e; + ++ee; + + while (ee != n->end () && ee->first == e->first) { + ++ee; + } + + std::vector nodes; + nodes.reserve (ee - e); + + std::vector other_nodes; + other_nodes.reserve (ee - e); + + for (NetGraphNode::edge_iterator i = e; i != ee; ++i) { + const NetGraphNode *n = &m_nodes[i->second.first]; + if (! n->has_other ()) { + nodes.push_back (n); + } + } + + if (! nodes.empty ()) { // if non-ambiguous, non-assigned + + NetGraphNode::edge_iterator e_other = nother->find_edge (e->first); + if (e_other != nother->end ()) { + + NetGraphNode::edge_iterator ee_other = e_other; + ++ee_other; + + while (ee_other != nother->end () && ee_other->first == e_other->first) { + ++ee_other; + } + + size_t count_other = 0; + for (NetGraphNode::edge_iterator i = e_other; i != ee_other && count_other < 2; ++i) { + + const NetGraphNode *n = &other.m_nodes[i->second.first]; + if (! n->has_other ()) { + other_nodes.push_back (n); + } + + } + + } + + } + + if (! nodes.empty () || ! other_nodes.empty ()) { + + std::sort (nodes.begin (), nodes.end (), CompareNodePtr ()); + std::sort (other_nodes.begin (), other_nodes.end (), CompareNodePtr ()); + + derive_node_identities_from_node_set (nodes, other_nodes, other, depth, n_branch, logger, circuit_pin_mapper, tentative, true); + + } + + e = ee; + + } + +#if defined(PRINT_DEBUG_NETCOMPARE) + if (! tentative && new_nodes > 0 && new_nodes != std::numeric_limits::max ()) { + tl::info << "finished pair deduction: " << n->net ()->expanded_name () << " vs. " << nother->net ()->expanded_name () << " with " << new_nodes << " new pairs"; + } +#endif + + return new_nodes; +} + +size_t +NetDeviceGraph::derive_node_identities_from_node_set (const std::vector &nodes, const std::vector &other_nodes, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, bool tentative, bool picky) +{ + size_t new_nodes = 0; + + if (depth + 1 == depth_max) { + return std::numeric_limits::max (); + } + + if (picky) { + + if (nodes.size () != other_nodes.size ()) { + return std::numeric_limits::max (); + } + + for (size_t i = 0; i < nodes.size (); ++i) { + if (! (*nodes[i] == *other_nodes[i]) || nodes[i]->has_other () != other_nodes[i]->has_other ()) { + return std::numeric_limits::max (); + } + } + + } + + std::vector::const_iterator n1 = nodes.begin (); + std::vector::const_iterator n2 = other_nodes.begin (); + + while (n1 != nodes.end () && n2 != other_nodes.end ()) { + + if (**n1 < **n2) { + ++n1; + continue; + } else if (**n2 < **n1) { + ++n2; + continue; + } else if ((*n1)->has_other ()) { + ++n1; + continue; + } else if ((*n2)->has_other ()) { + ++n2; + continue; + } + + std::vector::const_iterator nn1 = n1, nn2 = n2; + + size_t num = 1; + ++nn1; + ++nn2; + while (nn1 != nodes.end () && nn2 != other_nodes.end ()) { + if ((*nn1)->has_other ()) { + ++nn1; + } else if ((*nn2)->has_other ()) { + ++nn2; + } else if (! (**nn1 == **n1) || ! (**nn2 == **n2)) { + break; + } else { + ++num; + ++nn1; + ++nn2; + } + } + + if (num == 1) { + + // a single candiate: just take this one -> this may render + // inexact matches, but further propagates net pairing + + size_t ni = node_index_for_net ((*n1)->net ()); + size_t other_ni = other.node_index_for_net ((*n2)->net ()); + if (! tentative) { + + identify (ni, other_ni); + other.identify (other_ni, ni); + +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << "deduced match (singular): " << (*n1)->net ()->expanded_name () << " vs. " << (*n2)->net ()->expanded_name (); +#endif + if (logger) { + logger->match_nets ((*n1)->net (), (*n2)->net ()); + } + + // unconditionally continue here. + derive_node_identities (ni, other, depth + 1, n_branch, logger, circuit_pin_mapper, false); + + } + + new_nodes += 1; + + } else { + + if (num * n_branch > n_branch_max) { + return std::numeric_limits::max (); + } + + std::vector > pairs; + tl::equivalence_clusters equivalent_other_nodes; + std::set seen; + + for (std::vector::const_iterator i1 = n1; i1 != nn1; ++i1) { + + if ((*i1)->has_other ()) { + continue; + } + + bool any = false; + + std::vector::const_iterator i2; + for (i2 = n2; i2 != nn2; ++i2) { + + if ((*i2)->has_other ()) { + continue; + } + + if (seen.find (*i2) != seen.end ()) { + continue; + } + + size_t ni = node_index_for_net ((*i1)->net ()); + size_t other_ni = other.node_index_for_net ((*i2)->net ()); + + identify (ni, other_ni); + other.identify (other_ni, ni); + + size_t bt_count = derive_node_identities (ni, other, depth + 1, num * n_branch, logger, circuit_pin_mapper, true /*tentative*/); + + unidentify (ni); + other.unidentify (other_ni); + + if (bt_count != std::numeric_limits::max ()) { + + if (any) { + + // there is already a known pair, so we can mark *i2 and the previous *i2 as equivalent + // (makes them ambiguous) + equivalent_other_nodes.same (*i2, pairs.back ().second); + + } else { + + // identified a new pair + new_nodes += bt_count + 1; + pairs.push_back (std::make_pair (*i1, *i2)); + seen.insert (*i2); + any = true; + + } + + } + + } + + if (! any && picky) { + // a mismatch - stop here. + return std::numeric_limits::max (); + } + + } + + if (! tentative) { + + // collect ambiguous nodes and mark their pins as swappable + + std::map other2this; + for (std::vector >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) { + other2this.insert (std::make_pair (p->second, p->first)); + } + + for (size_t cid = 1; cid <= equivalent_other_nodes.size (); ++cid) { + + tl::equivalence_clusters::cluster_iterator c = equivalent_other_nodes.begin_cluster (cid); + const NetGraphNode *c1 = other2this[(*c)->first]; + + tl::equivalence_clusters::cluster_iterator cc = c; + ++cc; + for ( ; cc != equivalent_other_nodes.end_cluster (cid); ++cc) { + + for (db::Net::const_pin_iterator pp = (*cc)->first->net ()->begin_pins (); pp != (*cc)->first->net ()->end_pins (); ++pp) { + for (db::Net::const_pin_iterator p = (*c)->first->net ()->begin_pins (); p != (*c)->first->net ()->end_pins (); ++p) { +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << "pin swapping due to ambiguous nets for circuit (B) " << other.circuit ()->name () << ": " << pp->pin ()->expanded_name () << " and " << p->pin ()->expanded_name (); +#endif + circuit_pin_mapper->map_pins (other.circuit (), pp->pin_id (), p->pin_id ()); + } + } + + const NetGraphNode *cc1 = other2this[(*cc)->first]; + + for (db::Net::const_pin_iterator pp = cc1->net ()->begin_pins (); pp != cc1->net ()->end_pins (); ++pp) { + for (db::Net::const_pin_iterator p = c1->net ()->begin_pins (); p != c1->net ()->end_pins (); ++p) { +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << "pin swapping due to ambiguous nets for circuit (A) " << circuit ()->name () << ": " << pp->pin ()->expanded_name () << " and " << p->pin ()->expanded_name (); +#endif + circuit_pin_mapper->map_pins (circuit (), pp->pin_id (), p->pin_id ()); + } + } + + } + + } + + // issue the matching pairs + + for (std::vector >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) { + + size_t ni = node_index_for_net (p->first->net ()); + size_t other_ni = other.node_index_for_net (p->second->net ()); + + identify (ni, other_ni); + other.identify (other_ni, ni); + +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << "deduced match: " << p->first->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name (); +#endif + if (logger) { + bool ambiguous = equivalent_other_nodes.has_attribute (p->second); + if (ambiguous) { + logger->match_ambiguous_nets (p->first->net (), p->second->net ()); + } else { + logger->match_nets (p->first->net (), p->second->net ()); + } + } + + } + + // And seek further from these pairs + + for (std::vector >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) { + + size_t ni = node_index_for_net (p->first->net ()); + + size_t bt_count = derive_node_identities (ni, other, depth + 1, num * n_branch, logger, circuit_pin_mapper, false /*not tentative*/); + tl_assert (bt_count != std::numeric_limits::max ()); + + } + + } + + } + + n1 = nn1; + n2 = nn2; + + } + + return new_nodes; +} + + // -------------------------------------------------------------------------------------------------------------------- // NetlistComparer implementation @@ -1417,7 +1534,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, #endif bool good = true; - while (good) { + while (true) { #if defined(PRINT_DEBUG_NETCOMPARE) ++iter; @@ -1425,10 +1542,13 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, tl::info << "deducing from present nodes ..."; #endif + size_t new_identities = 0; + for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { if (i1->has_other () && i1->net ()) { - size_t ni = g1.derive_node_identities (i1 - g1.begin (), g2, mp_logger); + size_t ni = g1.derive_node_identities (i1 - g1.begin (), g2, mp_logger, mp_circuit_pin_mapper.get ()); if (ni > 0 && ni != std::numeric_limits::max ()) { + new_identities += ni; #if defined(PRINT_DEBUG_NETCOMPARE) tl::info << ni << " new identities."; #endif @@ -1436,95 +1556,46 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, } } - bool any_without = false; - for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end () && ! any_without; ++i1) { - any_without = ! i1->has_other (); - } - - if (! any_without) { - break; - } - - size_t new_identities = 0; - #if defined(PRINT_DEBUG_NETCOMPARE) tl::info << "checking topological identity ..."; #endif - // first pass: without ambiguities, second pass: match ambiguous nets - for (int pass = 0; pass < 2 && new_identities == 0; ++pass) { + // derive new identities through topology: first collect all nets with the same topological signature - // derive new identities through topology - - bool same_as_prev = false; - - db::NetDeviceGraph::node_iterator i1 = g1.begin (), i2 = g2.begin (); - for ( ; i1 != g1.end () && i2 != g2.end (); ) { - - bool same_as_next = false; - - if (i1->has_other ()) { - ++i1; - } else if (i2->has_other ()) { - ++i2; - } else if (*i1 < *i2) { - ++i1; - } else if (*i2 < *i1) { - ++i2; - } else { - - db::NetDeviceGraph::node_iterator ii1 = i1, ii2 = i2; - - ++i1; - ++i2; - - same_as_next = (i1 != g1.end () && *i1 == *ii1) || (i2 != g2.end () && *i2 == *ii2); - - // found a candidate - a single node with the same edges - - bool ambiguous = (same_as_next || same_as_prev); - if (! ambiguous || pass) { - - // For ambiguous nets make the pins exchangable - - if (same_as_next) { - - if (ii1->net () && i1->net ()) { - for (db::Net::const_pin_iterator pp = ii1->net ()->begin_pins (); pp != ii1->net ()->end_pins (); ++pp) { - for (db::Net::const_pin_iterator p = i1->net ()->begin_pins (); p != i1->net ()->end_pins (); ++p) { - mp_circuit_pin_mapper->map_pins (c1, pp->pin_id (), p->pin_id ()); - } - } - } - - if (ii2->net () && i2->net ()) { - for (db::Net::const_pin_iterator pp = ii2->net ()->begin_pins (); pp != ii2->net ()->end_pins (); ++pp) { - for (db::Net::const_pin_iterator p = i2->net ()->begin_pins (); p != i2->net ()->end_pins (); ++p) { - mp_circuit_pin_mapper->map_pins (c2, pp->pin_id (), p->pin_id ()); - } - } - } - - } - -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << "topological match: " << ii1->net ()->expanded_name () << " vs. " << ii2->net ()->expanded_name (); -#endif - db::NetDeviceGraph::confirm_identity (g1, ii1, g2, ii2, mp_logger, ambiguous); - ++new_identities; - - } - - } - - same_as_prev = same_as_next; + std::vector nodes, other_nodes; + nodes.reserve (g1.end () - g1.begin ()); + for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { + if (! i1->has_other () && i1->net ()) { + nodes.push_back (i1.operator-> ()); } + } + other_nodes.reserve (g2.end () - g2.begin ()); + for (db::NetDeviceGraph::node_iterator i2 = g2.begin (); i2 != g2.end (); ++i2) { + if (! i2->has_other () && i2->net ()) { + other_nodes.push_back (i2.operator-> ()); + } + } + + if (nodes.empty () || other_nodes.empty ()) { + if (! nodes.empty () || ! other_nodes.empty ()) { + good = false; + } + break; + } + + size_t ni = g1.derive_node_identities_from_node_set (nodes, other_nodes, g2, 0, 1, mp_logger, mp_circuit_pin_mapper.get (), false /*not tentative*/, false /*don't require a full match*/); + if (ni > 0 && ni != std::numeric_limits::max ()) { + new_identities += ni; +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << ni << " new identities."; +#endif } if (new_identities == 0) { good = false; + break; } } From e03a524fcfc70b067aa1843274d0b6fb819c48b7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 11 Apr 2019 00:47:36 +0200 Subject: [PATCH 43/54] WIP: netlist compare, bug fixes. --- src/db/db/dbNetlistCompare.cc | 52 +++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 530ef240c..6094148e9 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -1010,6 +1010,36 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vector::max (); } + if (nodes.size () == 1 && other_nodes.size () == 1) { + + // a single candiate: just take this one -> this may render + // inexact matches, but further propagates net pairing + + if (! tentative) { + + size_t ni = node_index_for_net (nodes.front ()->net ()); + size_t other_ni = other.node_index_for_net (other_nodes.front ()->net ()); + + identify (ni, other_ni); + other.identify (other_ni, ni); + +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << "deduced match (singular): " << nodes.front ()->net ()->expanded_name () << " vs. " << other_nodes.front ()->net ()->expanded_name (); +#endif + if (logger) { + logger->match_nets (nodes.front ()->net (), other_nodes.front ()->net ()); + } + + // unconditionally continue here. + derive_node_identities (ni, other, depth + 1, n_branch, logger, circuit_pin_mapper, false); + + } + + new_nodes += 1; + return new_nodes; + + } + if (picky) { if (nodes.size () != other_nodes.size ()) { @@ -1029,18 +1059,20 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vectorhas_other ()) { + ++n1; + continue; + } else if ((*n2)->has_other ()) { + ++n2; + continue; + } + if (**n1 < **n2) { ++n1; continue; } else if (**n2 < **n1) { ++n2; continue; - } else if ((*n1)->has_other ()) { - ++n1; - continue; - } else if ((*n2)->has_other ()) { - ++n2; - continue; } std::vector::const_iterator nn1 = n1, nn2 = n2; @@ -1067,10 +1099,11 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vector this may render // inexact matches, but further propagates net pairing - size_t ni = node_index_for_net ((*n1)->net ()); - size_t other_ni = other.node_index_for_net ((*n2)->net ()); if (! tentative) { + size_t ni = node_index_for_net ((*n1)->net ()); + size_t other_ni = other.node_index_for_net ((*n2)->net ()); + identify (ni, other_ni); other.identify (other_ni, ni); @@ -1585,6 +1618,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, break; } + std::sort (nodes.begin (), nodes.end (), CompareNodePtr ()); + std::sort (other_nodes.begin (), other_nodes.end (), CompareNodePtr ()); + size_t ni = g1.derive_node_identities_from_node_set (nodes, other_nodes, g2, 0, 1, mp_logger, mp_circuit_pin_mapper.get (), false /*not tentative*/, false /*don't require a full match*/); if (ni > 0 && ni != std::numeric_limits::max ()) { new_identities += ni; From 187baf294161f9cee96c1a6165028f6bf460a9ce Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 12 Apr 2019 00:15:36 +0200 Subject: [PATCH 44/54] WIP: enhanced backtracking of netlist compare. --- src/db/db/dbNetlistCompare.cc | 268 +++++++++++++-------- src/db/unit_tests/dbNetlistCompareTests.cc | 267 +++++++++++++------- 2 files changed, 350 insertions(+), 185 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 6094148e9..f0395199f 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -859,14 +859,9 @@ public: * If tentative is true, assignments will not be retained and just the * status is reported. */ - size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, bool tentative); + size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, bool tentative, bool with_ambiguous); - size_t derive_node_identities_from_node_set (const std::vector &nodes, const std::vector &other_nodes, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, bool tentative, bool picky); - - size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper) - { - return derive_node_identities (net_index, other, 0, 1, logger, circuit_pin_mapper, false); - } + size_t derive_node_identities_from_node_set (const std::vector &nodes, const std::vector &other_nodes, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, bool tentative, bool with_ambiguous); private: std::vector m_nodes; @@ -915,7 +910,7 @@ NetDeviceGraph::build (const db::Circuit *c, DeviceCategorizer &device_categoriz } size_t -NetDeviceGraph::derive_node_identities (size_t net_index, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, bool tentative) +NetDeviceGraph::derive_node_identities (size_t net_index, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, bool tentative, bool with_ambiguous) { NetGraphNode *n = & m_nodes[net_index]; NetGraphNode *nother = & other.m_nodes[n->other_net_index ()]; @@ -984,7 +979,34 @@ NetDeviceGraph::derive_node_identities (size_t net_index, NetDeviceGraph &other, std::sort (nodes.begin (), nodes.end (), CompareNodePtr ()); std::sort (other_nodes.begin (), other_nodes.end (), CompareNodePtr ()); - derive_node_identities_from_node_set (nodes, other_nodes, other, depth, n_branch, logger, circuit_pin_mapper, tentative, true); + // for the purpose of match evaluation we require an exact match of the node structure + + if (tentative && (nodes.size () > 1 || other_nodes.size () > 1)) { + + if (nodes.size () != other_nodes.size ()) { + return std::numeric_limits::max (); + } + + for (size_t i = 0; i < nodes.size (); ++i) { + if (! (*nodes[i] == *other_nodes[i]) || nodes[i]->has_other () != other_nodes[i]->has_other ()) { + return std::numeric_limits::max (); + } + } + + } + + // propagate pairing in picky mode: this means we only accept exact a match if the node set + // is exactly identical and no ambiguous nodes are present when ambiguous nodes are forbidden + + size_t bt_count = derive_node_identities_from_node_set (nodes, other_nodes, other, depth, n_branch, logger, circuit_pin_mapper, tentative, with_ambiguous); + + if (bt_count == std::numeric_limits::max ()) { + if (tentative) { + return bt_count; + } + } else { + new_nodes += bt_count; + } } @@ -993,7 +1015,7 @@ NetDeviceGraph::derive_node_identities (size_t net_index, NetDeviceGraph &other, } #if defined(PRINT_DEBUG_NETCOMPARE) - if (! tentative && new_nodes > 0 && new_nodes != std::numeric_limits::max ()) { + if (! tentative && new_nodes > 0) { tl::info << "finished pair deduction: " << n->net ()->expanded_name () << " vs. " << nother->net ()->expanded_name () << " with " << new_nodes << " new pairs"; } #endif @@ -1001,8 +1023,29 @@ NetDeviceGraph::derive_node_identities (size_t net_index, NetDeviceGraph &other, return new_nodes; } +namespace { + +struct NodeRange +{ + NodeRange (size_t _num, std::vector::const_iterator _n1, std::vector::const_iterator _nn1, std::vector::const_iterator _n2, std::vector::const_iterator _nn2) + : num (_num), n1 (_n1), nn1 (_nn1), n2 (_n2), nn2 (_nn2) + { + // .. nothing yet .. + } + + bool operator< (const NodeRange &other) const + { + return num < other.num; + } + + size_t num; + std::vector::const_iterator n1, nn1, n2, nn2; +}; + +} + size_t -NetDeviceGraph::derive_node_identities_from_node_set (const std::vector &nodes, const std::vector &other_nodes, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, bool tentative, bool picky) +NetDeviceGraph::derive_node_identities_from_node_set (const std::vector &nodes, const std::vector &other_nodes, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, bool tentative, bool with_ambiguous) { size_t new_nodes = 0; @@ -1031,7 +1074,7 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vector::max (); - } - - for (size_t i = 0; i < nodes.size (); ++i) { - if (! (*nodes[i] == *other_nodes[i]) || nodes[i]->has_other () != other_nodes[i]->has_other ()) { - return std::numeric_limits::max (); - } - } - - } + std::vector node_ranges; std::vector::const_iterator n1 = nodes.begin (); std::vector::const_iterator n2 = other_nodes.begin (); @@ -1094,36 +1127,61 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vector this may render - // inexact matches, but further propagates net pairing + // in tentative mode ambiguous nodes don't make a match without + // with_ambiguous + if (num > 1 && tentative && ! with_ambiguous) { + return std::numeric_limits::max (); + } - if (! tentative) { + n1 = nn1; + n2 = nn2; - size_t ni = node_index_for_net ((*n1)->net ()); - size_t other_ni = other.node_index_for_net ((*n2)->net ()); + } - identify (ni, other_ni); - other.identify (other_ni, ni); + if (with_ambiguous) { + std::stable_sort (node_ranges.begin (), node_ranges.end ()); + } + + for (std::vector::const_iterator nr = node_ranges.begin (); nr != node_ranges.end (); ++nr) { + + if (nr->num == 1) { + + if (! (*nr->n1)->has_other () && ! (*nr->n2)->has_other ()) { + + // A single candiate: just take this one -> this may render + // inexact matches, but further propagates net pairing + + if (! tentative) { + + size_t ni = node_index_for_net ((*nr->n1)->net ()); + size_t other_ni = other.node_index_for_net ((*nr->n2)->net ()); + + identify (ni, other_ni); + other.identify (other_ni, ni); + + #if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << "deduced match (singular): " << (*nr->n1)->net ()->expanded_name () << " vs. " << (*nr->n2)->net ()->expanded_name (); + #endif + if (logger) { + logger->match_nets ((*nr->n1)->net (), (*nr->n2)->net ()); + } + + // unconditionally continue here. + derive_node_identities (ni, other, depth + 1, n_branch, logger, circuit_pin_mapper, tentative, with_ambiguous); -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << "deduced match (singular): " << (*n1)->net ()->expanded_name () << " vs. " << (*n2)->net ()->expanded_name (); -#endif - if (logger) { - logger->match_nets ((*n1)->net (), (*n2)->net ()); } - // unconditionally continue here. - derive_node_identities (ni, other, depth + 1, n_branch, logger, circuit_pin_mapper, false); + new_nodes += 1; } - new_nodes += 1; - } else { - if (num * n_branch > n_branch_max) { + if (nr->num * n_branch > n_branch_max) { return std::numeric_limits::max (); } @@ -1131,7 +1189,7 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vector equivalent_other_nodes; std::set seen; - for (std::vector::const_iterator i1 = n1; i1 != nn1; ++i1) { + for (std::vector::const_iterator i1 = nr->n1; i1 != nr->nn1; ++i1) { if ((*i1)->has_other ()) { continue; @@ -1140,7 +1198,7 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vector::const_iterator i2; - for (i2 = n2; i2 != nn2; ++i2) { + for (i2 = nr->n2; i2 != nr->nn2; ++i2) { if ((*i2)->has_other ()) { continue; @@ -1156,13 +1214,16 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vectornum * n_branch, logger, circuit_pin_mapper, true /*tentative*/, with_ambiguous); unidentify (ni); other.unidentify (other_ni); if (bt_count != std::numeric_limits::max ()) { + // we have a match ... + if (any) { // there is already a known pair, so we can mark *i2 and the previous *i2 as equivalent @@ -1183,7 +1244,7 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vector::max (); } @@ -1262,7 +1323,7 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vectorfirst->net ()); - size_t bt_count = derive_node_identities (ni, other, depth + 1, num * n_branch, logger, circuit_pin_mapper, false /*not tentative*/); + size_t bt_count = derive_node_identities (ni, other, depth + 1, nr->num * n_branch, logger, circuit_pin_mapper, tentative, with_ambiguous); tl_assert (bt_count != std::numeric_limits::max ()); } @@ -1271,9 +1332,6 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vectorhas_other () && i1->net ()) { - size_t ni = g1.derive_node_identities (i1 - g1.begin (), g2, mp_logger, mp_circuit_pin_mapper.get ()); - if (ni > 0 && ni != std::numeric_limits::max ()) { - new_identities += ni; -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << ni << " new identities."; -#endif + good = true; + while (true) { + + #if defined(PRINT_DEBUG_NETCOMPARE) + ++iter; + tl::info << "new compare iteration #" << iter; + tl::info << "deducing from present nodes ..."; + #endif + + size_t new_identities = 0; + + for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { + if (i1->has_other () && i1->net ()) { + size_t ni = g1.derive_node_identities (i1 - g1.begin (), g2, 0, 1, mp_logger, mp_circuit_pin_mapper.get (), false /*not tentative*/, pass > 0 /*with ambiguities*/); + if (ni > 0 && ni != std::numeric_limits::max ()) { + new_identities += ni; + #if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << ni << " new identities."; + #endif + } } } - } -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << "checking topological identity ..."; -#endif + #if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << "checking topological identity ..."; + #endif - // derive new identities through topology: first collect all nets with the same topological signature + // derive new identities through topology: first collect all nets with the same topological signature - std::vector nodes, other_nodes; + std::vector nodes, other_nodes; - nodes.reserve (g1.end () - g1.begin ()); - for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { - if (! i1->has_other () && i1->net ()) { - nodes.push_back (i1.operator-> ()); + nodes.reserve (g1.end () - g1.begin ()); + for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { + if (! i1->has_other () && i1->net ()) { + nodes.push_back (i1.operator-> ()); + } } - } - other_nodes.reserve (g2.end () - g2.begin ()); - for (db::NetDeviceGraph::node_iterator i2 = g2.begin (); i2 != g2.end (); ++i2) { - if (! i2->has_other () && i2->net ()) { - other_nodes.push_back (i2.operator-> ()); + other_nodes.reserve (g2.end () - g2.begin ()); + for (db::NetDeviceGraph::node_iterator i2 = g2.begin (); i2 != g2.end (); ++i2) { + if (! i2->has_other () && i2->net ()) { + other_nodes.push_back (i2.operator-> ()); + } } - } - if (nodes.empty () || other_nodes.empty ()) { - if (! nodes.empty () || ! other_nodes.empty ()) { + if (nodes.empty () || other_nodes.empty ()) { + if (! nodes.empty () || ! other_nodes.empty ()) { + good = false; + } + // this assumes that we don't gain anything here. Stop now. + break; + } + + std::sort (nodes.begin (), nodes.end (), CompareNodePtr ()); + std::sort (other_nodes.begin (), other_nodes.end (), CompareNodePtr ()); + + size_t ni = g1.derive_node_identities_from_node_set (nodes, other_nodes, g2, 0, 1, mp_logger, mp_circuit_pin_mapper.get (), false /*not tentative*/, pass > 0 /*with ambiguities*/); + if (ni > 0 && ni != std::numeric_limits::max ()) { + new_identities += ni; + #if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << ni << " new identities."; + #endif + } + + if (new_identities == 0) { good = false; + break; } - break; - } - std::sort (nodes.begin (), nodes.end (), CompareNodePtr ()); - std::sort (other_nodes.begin (), other_nodes.end (), CompareNodePtr ()); - - size_t ni = g1.derive_node_identities_from_node_set (nodes, other_nodes, g2, 0, 1, mp_logger, mp_circuit_pin_mapper.get (), false /*not tentative*/, false /*don't require a full match*/); - if (ni > 0 && ni != std::numeric_limits::max ()) { - new_identities += ni; -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << ni << " new identities."; -#endif - } - - if (new_identities == 0) { - good = false; - break; } } - // Report missing net assignment for (db::NetDeviceGraph::node_iterator i = g1.begin (); i != g1.end (); ++i) { diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 4cb7c998d..36760f41c 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -335,9 +335,9 @@ TEST(1_SimpleInverter) EXPECT_EQ (logger.text (), "begin_circuit INV INV\n" "match_nets VDD VDD\n" - "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" + "match_nets VSS VSS\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -384,9 +384,9 @@ TEST(1_SimpleInverterMatchedDeviceClasses) EXPECT_EQ (logger.text (), "begin_circuit INV INV\n" "match_nets VDD VDD\n" - "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" + "match_nets VSS VSS\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -427,8 +427,8 @@ TEST(1_SimpleInverterSkippedDevices) EXPECT_EQ (logger.text (), "begin_circuit INV INV\n" "match_nets VDD VDD\n" - "match_nets VSS VSS\n" "match_nets OUT OUT\n" + "match_nets VSS VSS\n" "match_nets IN IN\n" "match_pins $0 $1\n" "match_pins $1 $3\n" @@ -451,9 +451,9 @@ TEST(1_SimpleInverterSkippedDevices) EXPECT_EQ (logger.text (), "begin_circuit INV INV\n" "match_nets VDD VDD\n" - "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" + "match_nets VSS VSS\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -536,11 +536,11 @@ TEST(3_Buffer) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" - "match_nets OUT OUT\n" "match_nets VDD VDD\n" + "match_nets OUT OUT\n" + "match_nets INT $10\n" "match_nets IN IN\n" "match_nets VSS VSS\n" - "match_nets INT $10\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -591,12 +591,12 @@ TEST(4_BufferTwoPaths) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" - "match_nets OUT OUT\n" "match_nets VDD VDD\n" - "match_nets IN IN\n" + "match_nets OUT OUT\n" "match_nets VSS VSS\n" - "match_nets INT $10\n" - "match_nets INT2 $11\n" + "match_nets IN IN\n" + "match_ambiguous_nets INT $10\n" + "match_ambiguous_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -658,9 +658,9 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" - "match_ambiguous_nets INT $10\n" - "match_nets INT2 $11\n" + "match_nets INT $10\n" "match_nets IN IN\n" + "match_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -685,8 +685,8 @@ TEST(5_BufferTwoPathsDifferentParameters) "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" "match_nets IN IN\n" - "match_nets INT2 $11\n" - "match_nets INT $10\n" + "match_ambiguous_nets INT $10\n" + "match_ambiguous_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -710,9 +710,9 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" - "match_ambiguous_nets INT $10\n" - "match_nets INT2 $11\n" + "match_nets INT $10\n" "match_nets IN IN\n" + "match_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -736,9 +736,9 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" - "match_ambiguous_nets INT $10\n" - "match_nets INT2 $11\n" + "match_nets INT $10\n" "match_nets IN IN\n" + "match_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -763,8 +763,8 @@ TEST(5_BufferTwoPathsDifferentParameters) "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" "match_nets IN IN\n" - "match_nets INT2 $11\n" - "match_nets INT $10\n" + "match_ambiguous_nets INT $10\n" + "match_ambiguous_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -790,8 +790,8 @@ TEST(5_BufferTwoPathsDifferentParameters) "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" "match_nets IN IN\n" - "match_nets INT2 $11\n" - "match_nets INT $10\n" + "match_ambiguous_nets INT $10\n" + "match_ambiguous_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -816,9 +816,9 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets OUT OUT\n" - "match_ambiguous_nets INT $10\n" - "match_nets INT2 $11\n" + "match_nets INT $10\n" "match_nets IN IN\n" + "match_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -1006,11 +1006,11 @@ TEST(6_BufferTwoPathsAdditionalDevices) EXPECT_EQ (logger.text (), "begin_circuit BUF BUF\n" "match_nets INT $11\n" - "match_nets IN IN\n" "match_nets VDD VDD\n" - "match_nets OUT OUT\n" - "match_nets VSS VSS\n" + "match_nets IN IN\n" "match_nets INT2 $10\n" + "match_nets VSS VSS\n" + "match_nets OUT OUT\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -1057,9 +1057,9 @@ TEST(7_Resistors) EXPECT_EQ (logger.text (), "begin_circuit TRIANGLE TRIANGLE\n" + "match_nets P2 P2\n" "match_nets P1 P1\n" "match_nets P3 P3\n" - "match_nets P2 P2\n" "match_pins $0 $0\n" "match_pins $1 $1\n" "match_pins $2 $2\n" @@ -1223,7 +1223,7 @@ TEST(8_DiodesDontMatchOnSwappedPins) EXPECT_EQ (logger.text (), "begin_circuit TRIANGLE TRIANGLE\n" - "match_ambiguous_nets P1 P3\n" + "match_nets P1 P3\n" "match_nets P2 P1\n" "match_nets P3 P2\n" "match_pins $0 $2\n" @@ -1272,9 +1272,9 @@ TEST(10_SimpleSubCircuits) EXPECT_EQ (logger.text (), "begin_circuit INV INV\n" "match_nets VDD VDD\n" - "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" + "match_nets VSS VSS\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -1284,9 +1284,9 @@ TEST(10_SimpleSubCircuits) "end_circuit INV INV MATCH\n" "begin_circuit TOP TOP\n" "match_nets OUT OUT\n" + "match_nets VDD VDD\n" "match_nets IN IN\n" "match_nets VSS VSS\n" - "match_nets VDD VDD\n" "match_nets INT INT\n" "match_pins $0 $2\n" "match_pins $1 $0\n" @@ -1339,9 +1339,9 @@ TEST(10_SimpleSubCircuitsMatchedNames) EXPECT_EQ (logger.text (), "begin_circuit INV INVB\n" "match_nets VDD VDD\n" - "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" + "match_nets VSS VSS\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -1351,9 +1351,9 @@ TEST(10_SimpleSubCircuitsMatchedNames) "end_circuit INV INVB MATCH\n" "begin_circuit TOP TOP\n" "match_nets OUT OUT\n" + "match_nets VDD VDD\n" "match_nets IN IN\n" "match_nets VSS VSS\n" - "match_nets VDD VDD\n" "match_nets INT INT\n" "match_pins $0 $2\n" "match_pins $1 $0\n" @@ -1404,18 +1404,28 @@ TEST(11_MismatchingSubcircuits) "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" - "net_mismatch VDD (null)\n" - "net_mismatch (null) VDD\n" + "match_nets VDD VDD\n" "match_pins $0 $1\n" "match_pins $1 $3\n" - "pin_mismatch $2 (null)\n" + "match_pins $2 $0\n" "match_pins $3 $2\n" - "pin_mismatch (null) $0\n" - "device_mismatch $1 (null)\n" "match_devices $2 $1\n" "device_mismatch (null) $2\n" + "device_mismatch $1 (null)\n" "end_circuit INV INV NOMATCH\n" - "circuit_skipped TOP TOP" + "begin_circuit TOP TOP\n" + "match_nets OUT OUT\n" + "match_nets VDD VDD\n" + "match_nets IN IN\n" + "match_nets VSS VSS\n" + "match_nets INT INT\n" + "match_pins $0 $2\n" + "match_pins $1 $0\n" + "match_pins $2 $1\n" + "match_pins $3 $3\n" + "match_subcircuits $2 $1\n" + "match_subcircuits $1 $2\n" + "end_circuit TOP TOP MATCH" ); EXPECT_EQ (good, false); @@ -1456,9 +1466,9 @@ TEST(12_MismatchingSubcircuitsDuplicates) EXPECT_EQ (logger.text (), "begin_circuit INV INV\n" "match_nets VDD VDD\n" - "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" + "match_nets VSS VSS\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -1471,17 +1481,14 @@ TEST(12_MismatchingSubcircuitsDuplicates) "match_nets VDD VDD\n" "match_nets VSS VSS\n" "match_nets INT INT\n" - "net_mismatch OUT (null)\n" - "net_mismatch (null) OUT\n" + "match_nets OUT OUT\n" "match_pins $0 $1\n" - "pin_mismatch $1 (null)\n" + "match_pins $1 $0\n" "match_pins $2 $2\n" "match_pins $3 $3\n" - "pin_mismatch (null) $0\n" - "subcircuit_mismatch $2 (null)\n" - "subcircuit_mismatch $3 (null)\n" - "subcircuit_mismatch (null) $1\n" + "match_subcircuits $2 $1\n" "match_subcircuits $1 $2\n" + "subcircuit_mismatch $3 (null)\n" "end_circuit TOP TOP NOMATCH" ); @@ -1527,9 +1534,9 @@ TEST(13_MismatchingSubcircuitsAdditionalHierarchy) "circuit_mismatch VIA (null)\n" "begin_circuit INV INV\n" "match_nets VDD VDD\n" - "match_nets VSS VSS\n" "match_nets OUT OUT\n" "match_nets IN IN\n" + "match_nets VSS VSS\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -1539,9 +1546,9 @@ TEST(13_MismatchingSubcircuitsAdditionalHierarchy) "end_circuit INV INV MATCH\n" "begin_circuit TOP TOP\n" "match_nets OUT OUT\n" + "match_nets VDD VDD\n" "match_nets IN IN\n" "match_nets VSS VSS\n" - "match_nets VDD VDD\n" "match_nets INT INT\n" "match_pins $0 $1\n" "match_pins $1 $0\n" @@ -1594,11 +1601,11 @@ TEST(14_Subcircuit2Nand) EXPECT_EQ (logger.text (), "begin_circuit NAND NAND\n" "match_nets VSS VSS\n" + "match_nets INT INT\n" + "match_nets OUT OUT\n" "match_nets VDD VDD\n" "match_nets B B\n" - "match_nets OUT OUT\n" "match_nets A A\n" - "match_nets INT INT\n" "match_pins $0 $0\n" "match_pins $1 $1\n" "match_pins $2 $2\n" @@ -1611,11 +1618,11 @@ TEST(14_Subcircuit2Nand) "end_circuit NAND NAND MATCH\n" "begin_circuit TOP TOP\n" "match_nets OUT OUT\n" - "match_nets VSS VSS\n" - "match_nets VDD VDD\n" "match_nets IN1 IN1\n" - "match_nets INT INT\n" "match_nets IN2 IN2\n" + "match_nets INT INT\n" + "match_nets VDD VDD\n" + "match_nets VSS VSS\n" "match_pins $0 $0\n" "match_pins $1 $1\n" "match_pins $2 $2\n" @@ -1668,11 +1675,11 @@ TEST(14_Subcircuit2NandMismatchNoSwap) EXPECT_EQ (logger.text (), "begin_circuit NAND NAND\n" "match_nets VSS VSS\n" + "match_nets INT INT\n" + "match_nets OUT OUT\n" "match_nets VDD VDD\n" "match_nets B B\n" - "match_nets OUT OUT\n" "match_nets A A\n" - "match_nets INT INT\n" "match_pins $0 $0\n" "match_pins $1 $1\n" "match_pins $2 $2\n" @@ -1689,18 +1696,16 @@ TEST(14_Subcircuit2NandMismatchNoSwap) "match_nets INT IN1\n" "match_nets VDD VDD\n" "match_nets VSS VSS\n" - "net_mismatch IN2 (null)\n" - "net_mismatch (null) IN2\n" + "match_nets IN2 IN2\n" "pin_mismatch $0 (null)\n" - "pin_mismatch $1 (null)\n" + "match_pins $1 $1\n" "match_pins $2 $2\n" "match_pins $3 $3\n" "match_pins $4 $4\n" - "pin_mismatch (null) $1\n" "pin_mismatch (null) $0\n" - "subcircuit_mismatch $1 (null)\n" "match_subcircuits $2 $1\n" "subcircuit_mismatch (null) $2\n" + "subcircuit_mismatch $1 (null)\n" "end_circuit TOP TOP NOMATCH" ); @@ -1746,11 +1751,11 @@ TEST(14_Subcircuit2MatchWithSwap) EXPECT_EQ (logger.text (), "begin_circuit NAND NAND\n" "match_nets VSS VSS\n" + "match_nets INT INT\n" + "match_nets OUT OUT\n" "match_nets VDD VDD\n" "match_nets B B\n" - "match_nets OUT OUT\n" "match_nets A A\n" - "match_nets INT INT\n" "match_pins $0 $0\n" "match_pins $1 $1\n" "match_pins $2 $2\n" @@ -1763,11 +1768,11 @@ TEST(14_Subcircuit2MatchWithSwap) "end_circuit NAND NAND MATCH\n" "begin_circuit TOP TOP\n" "match_nets OUT OUT\n" - "match_nets VSS VSS\n" - "match_nets VDD VDD\n" "match_nets IN1 IN1\n" - "match_nets INT INT\n" "match_nets IN2 IN2\n" + "match_nets INT INT\n" + "match_nets VDD VDD\n" + "match_nets VSS VSS\n" "match_pins $0 $0\n" "match_pins $1 $1\n" "match_pins $2 $2\n" @@ -1831,11 +1836,11 @@ TEST(15_EmptySubCircuitTest) "match_pins D D\n" "end_circuit TRANS TRANS MATCH\n" "begin_circuit INV2 INV2\n" - "match_nets IN IN\n" - "match_nets OUT OUT\n" "match_nets $5 $5\n" - "match_nets $4 $4\n" + "match_nets OUT OUT\n" "match_nets $2 $2\n" + "match_nets IN IN\n" + "match_nets $4 $4\n" "match_pins IN IN\n" "match_pins $1 $1\n" "match_pins OUT OUT\n" @@ -1905,11 +1910,11 @@ TEST(15_EmptySubCircuitWithoutPinNames) "match_pins $2 $2\n" "end_circuit TRANS TRANS MATCH\n" "begin_circuit INV2 INV2\n" + "match_nets $5 $5\n" "match_nets OUT OUT\n" + "match_nets $2 $2\n" "match_nets IN IN\n" "match_nets $4 $4\n" - "match_nets $2 $2\n" - "match_nets $5 $5\n" "match_pins IN IN\n" "match_pins $1 $1\n" "match_pins OUT OUT\n" @@ -1980,12 +1985,12 @@ TEST(16_UniqueSubCircuitMatching) EXPECT_EQ (logger.text (), "begin_circuit INV2 INV2\n" "match_nets VDD VDD\n" + "match_nets OUT OUT\n" + "match_nets $3 $3\n" "match_nets $1 $1\n" + "match_nets IN IN\n" "match_nets VSS VSS\n" "match_nets BULK BULK\n" - "match_nets OUT OUT\n" - "match_nets IN IN\n" - "match_nets $3 $3\n" "match_pins $0 $0\n" "match_pins IN IN\n" "match_pins $2 $2\n" @@ -2000,14 +2005,14 @@ TEST(16_UniqueSubCircuitMatching) "end_circuit INV2 INV2 MATCH\n" "begin_circuit INV2PAIR INV2PAIR\n" "match_nets $I2 $I2\n" - "match_nets $I3 $I3\n" - "match_nets BULK BULK\n" - "match_nets $I6 $I6\n" - "match_nets $I5 $I5\n" - "match_nets $I4 $I4\n" "match_nets $I1 $I1\n" + "match_nets $I3 $I3\n" "match_nets $I7 $I7\n" + "match_nets $I4 $I4\n" + "match_nets $I5 $I5\n" "match_nets $I8 $I8\n" + "match_nets $I6 $I6\n" + "match_nets BULK BULK\n" "match_pins BULK BULK\n" "match_pins $1 $1\n" "match_pins $2 $2\n" @@ -2020,17 +2025,17 @@ TEST(16_UniqueSubCircuitMatching) "end_circuit INV2PAIR INV2PAIR MATCH\n" "begin_circuit RINGO RINGO\n" "match_nets OSC OSC\n" - "match_nets FB FB\n" - "match_nets VDD VDD\n" "match_nets BULK,VSS BULK,VSS\n" + "match_nets FB FB\n" "match_nets $I22 $I22\n" + "match_nets VDD VDD\n" "match_nets $I13 $I13\n" "match_nets $I23 $I23\n" "match_nets $I5 $I5\n" - "match_nets $I24 $I24\n" - "match_nets $I6 $I6\n" - "match_nets $I25 $I25\n" "match_nets $I7 $I7\n" + "match_nets $I25 $I25\n" + "match_nets $I6 $I6\n" + "match_nets $I24 $I24\n" "match_subcircuits $1 $1\n" "match_subcircuits $4 $2\n" "match_subcircuits $3 $3\n" @@ -2041,3 +2046,97 @@ TEST(16_UniqueSubCircuitMatching) EXPECT_EQ (good, true); } + +TEST(17_InherentlyAmbiguousDecoder) +{ + const char *nls1 = + "circuit NAND ($0=A,$1=B,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=A,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $3 (S=VSS,G=A,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=INT,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit DECODER ($0=A,$1=B,$2=NQ0,$3=NQ1,$4=NQ2,$5=NQ3,$6=VDD,$7=VSS);\n" + " subcircuit NAND $1 ($0=A,$1=A,$2=NA,$3=VDD,$4=VSS);\n" + " subcircuit NAND $2 ($0=B,$1=B,$2=NB,$3=VDD,$4=VSS);\n" + " subcircuit NAND $3 ($0=NA,$1=NB,$2=NQ0,$3=VDD,$4=VSS);\n" + " subcircuit NAND $4 ($0=A,$1=NB,$2=NQ1,$3=VDD,$4=VSS);\n" + " subcircuit NAND $5 ($0=NA,$1=B,$2=NQ2,$3=VDD,$4=VSS);\n" + " subcircuit NAND $6 ($0=A,$1=B,$2=NQ3,$3=VDD,$4=VSS);\n" + "end;\n"; + + const char *nls2 = + "circuit NAND ($0=A,$1=B,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=A,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=VDD,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $3 (S=VSS,G=A,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=INT,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit DECODER ($0=A,$1=B,$2=NQ0,$3=NQ1,$4=NQ2,$5=NQ3,$6=VDD,$7=VSS);\n" + " subcircuit NAND $1 ($0=B,$1=B,$2=NB,$3=VDD,$4=VSS);\n" + " subcircuit NAND $2 ($0=A,$1=NB,$2=NQ1,$3=VDD,$4=VSS);\n" + " subcircuit NAND $3 ($0=A,$1=A,$2=NA,$3=VDD,$4=VSS);\n" + " subcircuit NAND $4 ($0=A,$1=B,$2=NQ3,$3=VDD,$4=VSS);\n" + " subcircuit NAND $5 ($0=NA,$1=NB,$2=NQ0,$3=VDD,$4=VSS);\n" + " subcircuit NAND $6 ($0=NA,$1=B,$2=NQ2,$3=VDD,$4=VSS);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + comp.equivalent_pins (nl2.circuit_by_name ("NAND"), 0, 1); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit NAND NAND\n" + "match_nets VSS VSS\n" + "match_nets INT INT\n" + "match_nets OUT OUT\n" + "match_nets VDD VDD\n" + "match_nets B B\n" + "match_nets A A\n" + "match_pins $0 $0\n" + "match_pins $1 $1\n" + "match_pins $2 $2\n" + "match_pins $3 $3\n" + "match_pins $4 $4\n" + "match_devices $1 $1\n" + "match_devices $2 $2\n" + "match_devices $3 $3\n" + "match_devices $4 $4\n" + "end_circuit NAND NAND MATCH\n" + "begin_circuit DECODER DECODER\n" + "match_nets NA NB\n" + "match_nets A B\n" + "match_nets VDD VDD\n" + "match_nets B A\n" + "match_nets NB NA\n" + "match_nets VSS VSS\n" + "match_ambiguous_nets NQ0 NQ0\n" + "match_ambiguous_nets NQ2 NQ1\n" + "match_ambiguous_nets NQ1 NQ2\n" + "match_ambiguous_nets NQ3 NQ3\n" + "match_pins $0 $1\n" + "match_pins $1 $0\n" + "match_pins $2 $2\n" + "match_pins $3 $4\n" + "match_pins $4 $3\n" + "match_pins $5 $5\n" + "match_pins $6 $6\n" + "match_pins $7 $7\n" + "match_subcircuits $1 $1\n" + "match_subcircuits $5 $2\n" + "match_subcircuits $2 $3\n" + "match_subcircuits $6 $4\n" + "match_subcircuits $3 $5\n" + "match_subcircuits $4 $6\n" + "end_circuit DECODER DECODER MATCH" + ); + + EXPECT_EQ (good, true); +} + From 648aa9e0774ebcfdf3c0e4a7cf789716e69f1032 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 12 Apr 2019 00:23:45 +0200 Subject: [PATCH 45/54] WIP: fixed unit tests. --- testdata/ruby/dbNetlistCompare.rb | 58 +++++++++++++++---------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/testdata/ruby/dbNetlistCompare.rb b/testdata/ruby/dbNetlistCompare.rb index 15a2f2f72..e78ad6564 100644 --- a/testdata/ruby/dbNetlistCompare.rb +++ b/testdata/ruby/dbNetlistCompare.rb @@ -229,9 +229,9 @@ END assert_equal(logger.text, <<"END") begin_circuit INV INV match_nets VDD VDD -match_nets VSS VSS match_nets OUT OUT match_nets IN IN +match_nets VSS VSS match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -284,9 +284,9 @@ END assert_equal(logger.text(), <<"END") begin_circuit INV INV match_nets VDD VDD -match_nets VSS VSS match_nets OUT OUT match_nets IN IN +match_nets VSS VSS match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -388,12 +388,12 @@ END assert_equal(logger.text, <<"END") begin_circuit BUF BUF -match_nets OUT OUT match_nets VDD VDD -match_nets IN IN +match_nets OUT OUT match_nets VSS VSS -match_nets INT $10 -match_nets INT2 $11 +match_nets IN IN +match_ambiguous_nets INT $10 +match_ambiguous_nets INT2 $11 match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -460,9 +460,9 @@ END assert_equal(logger.text, <<"END") begin_circuit BUF BUF match_nets OUT OUT -match_ambiguous_nets INT $10 -match_nets INT2 $11 +match_nets INT $10 match_nets IN IN +match_nets INT2 $11 match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -489,8 +489,8 @@ END begin_circuit BUF BUF match_nets OUT OUT match_nets IN IN -match_nets INT2 $11 -match_nets INT $10 +match_ambiguous_nets INT $10 +match_ambiguous_nets INT2 $11 match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -518,8 +518,8 @@ END begin_circuit BUF BUF match_nets OUT OUT match_nets IN IN -match_nets INT2 $11 -match_nets INT $10 +match_ambiguous_nets INT $10 +match_ambiguous_nets INT2 $11 match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -547,8 +547,8 @@ END begin_circuit BUF BUF match_nets OUT OUT match_nets IN IN -match_nets INT2 $11 -match_nets INT $10 +match_ambiguous_nets INT $10 +match_ambiguous_nets INT2 $11 match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -751,9 +751,9 @@ END assert_equal(logger.text, <<"END") begin_circuit INV INVB match_nets VDD VDD -match_nets VSS VSS match_nets OUT OUT match_nets IN IN +match_nets VSS VSS match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -763,9 +763,9 @@ match_devices $1 $2 end_circuit INV INVB MATCH begin_circuit TOP TOP match_nets OUT OUT +match_nets VDD VDD match_nets IN IN match_nets VSS VSS -match_nets VDD VDD match_nets INT INT match_pins $0 $2 match_pins $1 $0 @@ -823,11 +823,11 @@ END assert_equal(logger.text, <<"END") begin_circuit NAND NAND match_nets VSS VSS +match_nets INT INT +match_nets OUT OUT match_nets VDD VDD match_nets B B -match_nets OUT OUT match_nets A A -match_nets INT INT match_pins $0 $0 match_pins $1 $1 match_pins $2 $2 @@ -840,11 +840,11 @@ match_devices $4 $4 end_circuit NAND NAND MATCH begin_circuit TOP TOP match_nets OUT OUT -match_nets VSS VSS -match_nets VDD VDD match_nets IN1 IN1 -match_nets INT INT match_nets IN2 IN2 +match_nets INT INT +match_nets VDD VDD +match_nets VSS VSS match_pins $0 $0 match_pins $1 $1 match_pins $2 $2 @@ -867,11 +867,11 @@ END assert_equal(logger.text, <<"END") begin_circuit NAND NAND match_nets VSS VSS +match_nets INT INT +match_nets OUT OUT match_nets VDD VDD match_nets B B -match_nets OUT OUT match_nets A A -match_nets INT INT match_pins $0 $0 match_pins $1 $1 match_pins $2 $2 @@ -884,11 +884,11 @@ match_devices $4 $4 end_circuit NAND NAND MATCH begin_circuit TOP TOP match_nets OUT OUT -match_nets IN2 IN2 -match_nets VSS VSS -match_nets VDD VDD -match_nets IN1 IN1 match_nets INT INT +match_nets IN2 IN2 +match_nets IN1 IN1 +match_nets VDD VDD +match_nets VSS VSS match_pins $0 $0 match_pins $1 $1 match_pins $2 $2 @@ -945,8 +945,8 @@ END assert_equal(logger.text(), <<"END") begin_circuit INV INV match_nets VDD VDD -match_nets VSS VSS match_nets OUT OUT +match_nets VSS VSS match_nets IN IN match_pins $0 $1 match_pins $1 $3 @@ -970,9 +970,9 @@ END assert_equal(logger.text(), <<"END") begin_circuit INV INV match_nets VDD VDD -match_nets VSS VSS match_nets OUT OUT match_nets IN IN +match_nets VSS VSS match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 From e855d8df35d76726f6a4a5729385380852730dbd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 12 Apr 2019 00:31:48 +0200 Subject: [PATCH 46/54] WIP: fixed unit tests. --- src/db/db/dbTestSupport.cc | 7 +++- testdata/drc/drcSimpleTests_au10a.cir | 52 +++++++++++++-------------- testdata/drc/drcSimpleTests_au10b.cir | 52 +++++++++++++-------------- testdata/drc/drcSimpleTests_au11a.cir | 2 +- testdata/drc/drcSimpleTests_au11b.cir | 2 +- testdata/drc/drcSimpleTests_au9a.cir | 12 +++---- testdata/drc/drcSimpleTests_au9b.cir | 12 +++---- 7 files changed, 72 insertions(+), 67 deletions(-) diff --git a/src/db/db/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index 39158adce..1d71cacca 100644 --- a/src/db/db/dbTestSupport.cc +++ b/src/db/db/dbTestSupport.cc @@ -188,7 +188,7 @@ public: virtual void device_class_mismatch (const db::DeviceClass *a, const db::DeviceClass *b) { - out ("device_class_mismatch " + a->name () + " " + b->name ()); + out ("device_class_mismatch " + device_class2str (a) + " " + device_class2str (b)); } virtual void circuit_skipped (const db::Circuit *a, const db::Circuit *b) @@ -260,6 +260,11 @@ private: bool m_new_circuit; std::string m_circuit; + std::string device_class2str (const db::DeviceClass *x) const + { + return x ? x->name () : "(null)"; + } + std::string circuit2str (const db::Circuit *x) const { return x ? x->name () : "(null)"; diff --git a/testdata/drc/drcSimpleTests_au10a.cir b/testdata/drc/drcSimpleTests_au10a.cir index 2e308383c..51eca50d7 100644 --- a/testdata/drc/drcSimpleTests_au10a.cir +++ b/testdata/drc/drcSimpleTests_au10a.cir @@ -8,60 +8,60 @@ * net 29 FB * net 43 BULK,VSS * device instance $1 2.65,5.8 LVPMOS -M$1 2 27 28 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U +M$1 2 27 28 28 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U * device instance $2 3.35,5.8 LVPMOS -M$2 28 29 2 28 MLVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U +M$2 28 29 2 28 LVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U * device instance $3 5.05,5.8 LVPMOS -M$3 28 2 3 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$3 28 2 3 28 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $4 6.85,5.8 LVPMOS -M$4 28 3 4 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$4 28 3 4 28 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $5 8.65,5.8 LVPMOS -M$5 28 4 5 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$5 28 4 5 28 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $6 10.45,5.8 LVPMOS -M$6 28 5 6 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$6 28 5 6 28 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $7 12.25,5.8 LVPMOS -M$7 28 6 7 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$7 28 6 7 28 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $8 14.05,5.8 LVPMOS -M$8 28 7 8 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$8 28 7 8 28 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $9 15.85,5.8 LVPMOS -M$9 28 8 9 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$9 28 8 9 28 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $10 17.65,5.8 LVPMOS -M$10 28 9 10 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$10 28 9 10 28 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $11 19.45,5.8 LVPMOS -M$11 28 10 11 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$11 28 10 11 28 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $12 21.25,5.8 LVPMOS -M$12 28 11 29 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$12 28 11 29 28 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $13 23.05,5.8 LVPMOS -M$13 28 29 12 28 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$13 28 29 12 28 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $14 2.65,2.135 LVNMOS -M$14 43 27 14 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U +M$14 43 27 14 43 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U + PD=1.4U * device instance $15 3.35,2.135 LVNMOS -M$15 14 29 2 43 MLVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U +M$15 14 29 2 43 LVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U * device instance $16 5.05,2.135 LVNMOS -M$16 43 2 3 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$16 43 2 3 43 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U * device instance $17 6.85,2.135 LVNMOS -M$17 43 3 4 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$17 43 3 4 43 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U * device instance $18 8.65,2.135 LVNMOS -M$18 43 4 5 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$18 43 4 5 43 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U * device instance $19 10.45,2.135 LVNMOS -M$19 43 5 6 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$19 43 5 6 43 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U * device instance $20 12.25,2.135 LVNMOS -M$20 43 6 7 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$20 43 6 7 43 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U * device instance $21 14.05,2.135 LVNMOS -M$21 43 7 8 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$21 43 7 8 43 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U * device instance $22 15.85,2.135 LVNMOS -M$22 43 8 9 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$22 43 8 9 43 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U * device instance $23 17.65,2.135 LVNMOS -M$23 43 9 10 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U +M$23 43 9 10 43 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U + PD=2.75U * device instance $24 19.45,2.135 LVNMOS -M$24 43 10 11 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U +M$24 43 10 11 43 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U + PD=2.75U * device instance $25 21.25,2.135 LVNMOS -M$25 43 11 29 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U +M$25 43 11 29 43 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U + PD=2.75U * device instance $26 23.05,2.135 LVNMOS -M$26 43 29 12 43 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U +M$26 43 29 12 43 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U + PD=2.75U .ENDS RINGO diff --git a/testdata/drc/drcSimpleTests_au10b.cir b/testdata/drc/drcSimpleTests_au10b.cir index dec252d6a..fea92bd02 100644 --- a/testdata/drc/drcSimpleTests_au10b.cir +++ b/testdata/drc/drcSimpleTests_au10b.cir @@ -13,59 +13,59 @@ * net 15 FB * net 16 BULK,VSS * device instance $1 2.65,5.8 LVPMOS -M$1 1 13 14 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U +M$1 1 13 14 14 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U * device instance $2 3.35,5.8 LVPMOS -M$2 14 15 1 14 MLVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U +M$2 14 15 1 14 LVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U * device instance $3 5.05,5.8 LVPMOS -M$3 14 1 2 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$3 14 1 2 14 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $4 6.85,5.8 LVPMOS -M$4 14 2 3 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$4 14 2 3 14 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $5 8.65,5.8 LVPMOS -M$5 14 3 4 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$5 14 3 4 14 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $6 10.45,5.8 LVPMOS -M$6 14 4 5 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$6 14 4 5 14 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $7 12.25,5.8 LVPMOS -M$7 14 5 6 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$7 14 5 6 14 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $8 14.05,5.8 LVPMOS -M$8 14 6 7 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$8 14 6 7 14 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $9 15.85,5.8 LVPMOS -M$9 14 7 8 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$9 14 7 8 14 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $10 17.65,5.8 LVPMOS -M$10 14 8 9 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$10 14 8 9 14 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $11 19.45,5.8 LVPMOS -M$11 14 9 10 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$11 14 9 10 14 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $12 21.25,5.8 LVPMOS -M$12 14 10 15 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$12 14 10 15 14 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $13 23.05,5.8 LVPMOS -M$13 14 15 11 14 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$13 14 15 11 14 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $14 2.65,2.135 LVNMOS -M$14 16 13 12 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U +M$14 16 13 12 16 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U + PD=1.4U * device instance $15 3.35,2.135 LVNMOS -M$15 12 15 1 16 MLVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U +M$15 12 15 1 16 LVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U * device instance $16 5.05,2.135 LVNMOS -M$16 16 1 2 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$16 16 1 2 16 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U * device instance $17 6.85,2.135 LVNMOS -M$17 16 2 3 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$17 16 2 3 16 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U * device instance $18 8.65,2.135 LVNMOS -M$18 16 3 4 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$18 16 3 4 16 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U * device instance $19 10.45,2.135 LVNMOS -M$19 16 4 5 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$19 16 4 5 16 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U * device instance $20 12.25,2.135 LVNMOS -M$20 16 5 6 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$20 16 5 6 16 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U * device instance $21 14.05,2.135 LVNMOS -M$21 16 6 7 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$21 16 6 7 16 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U * device instance $22 15.85,2.135 LVNMOS -M$22 16 7 8 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$22 16 7 8 16 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U * device instance $23 17.65,2.135 LVNMOS -M$23 16 8 9 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$23 16 8 9 16 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U * device instance $24 19.45,2.135 LVNMOS -M$24 16 9 10 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U +M$24 16 9 10 16 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U + PD=2.75U * device instance $25 21.25,2.135 LVNMOS -M$25 16 10 15 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U +M$25 16 10 15 16 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U + PD=2.75U * device instance $26 23.05,2.135 LVNMOS -M$26 16 15 11 16 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U +M$26 16 15 11 16 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U + PD=2.75U .ENDS RINGO diff --git a/testdata/drc/drcSimpleTests_au11a.cir b/testdata/drc/drcSimpleTests_au11a.cir index 8b06b1937..0af71b7dc 100644 --- a/testdata/drc/drcSimpleTests_au11a.cir +++ b/testdata/drc/drcSimpleTests_au11a.cir @@ -13,5 +13,5 @@ R$2 3 1 7650 * device instance $3 4.665,0.335 RES R$3 3 2 2670 * device instance $4 1.765,7.485 HVPMOS -M$4 6 4 7 7 MHVPMOS L=0.25U W=1.5U AS=0.63P AD=0.63P PS=3.84U PD=3.84U +M$4 6 4 7 7 HVPMOS L=0.25U W=1.5U AS=0.63P AD=0.63P PS=3.84U PD=3.84U .ENDS TOP diff --git a/testdata/drc/drcSimpleTests_au11b.cir b/testdata/drc/drcSimpleTests_au11b.cir index a98b9510e..441476203 100644 --- a/testdata/drc/drcSimpleTests_au11b.cir +++ b/testdata/drc/drcSimpleTests_au11b.cir @@ -15,5 +15,5 @@ R$1 4 1 7650 * device instance $2 2.85,0.335 RES R$2 2 1 10320 * device instance $4 1.765,7.485 HVPMOS -M$4 4 3 5 5 MHVPMOS L=0.25U W=1.5U AS=0.63P AD=0.63P PS=3.84U PD=3.84U +M$4 4 3 5 5 HVPMOS L=0.25U W=1.5U AS=0.63P AD=0.63P PS=3.84U PD=3.84U .ENDS TOP diff --git a/testdata/drc/drcSimpleTests_au9a.cir b/testdata/drc/drcSimpleTests_au9a.cir index e62b44d8e..8d0e86af5 100644 --- a/testdata/drc/drcSimpleTests_au9a.cir +++ b/testdata/drc/drcSimpleTests_au9a.cir @@ -77,13 +77,13 @@ X$5 5 POLYM1 * cell instance $6 r0 *1 0.8,3.1 X$6 6 POLYM1 * device instance $1 0.85,5.8 LVPMOS -M$1 2 6 1 4 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U +M$1 2 6 1 4 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U * device instance $2 1.55,5.8 LVPMOS -M$2 1 5 2 4 MLVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U +M$2 1 5 2 4 LVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U * device instance $3 0.85,2.135 LVNMOS -M$3 3 6 10 9 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U PD=1.4U +M$3 3 6 10 9 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U PD=1.4U * device instance $4 1.55,2.135 LVNMOS -M$4 10 5 2 9 MLVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U +M$4 10 5 2 9 LVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U .ENDS ND2X1 * cell INVX1 @@ -106,9 +106,9 @@ X$2 5 2 3 NMOS2 * cell instance $3 r0 *1 0.6,3.1 X$3 5 POLYM1 * device instance $1 0.85,5.8 LVPMOS -M$1 1 5 2 4 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$1 1 5 2 4 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $2 0.85,2.135 LVNMOS -M$2 3 5 2 7 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$2 3 5 2 7 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U .ENDS INVX1 * cell M1M2 diff --git a/testdata/drc/drcSimpleTests_au9b.cir b/testdata/drc/drcSimpleTests_au9b.cir index 32e7dfc09..493292287 100644 --- a/testdata/drc/drcSimpleTests_au9b.cir +++ b/testdata/drc/drcSimpleTests_au9b.cir @@ -54,13 +54,13 @@ X$12 12 13 15 12 11 15 INVX1 * net 6 A * net 7 BULK * device instance $1 0.85,5.8 LVPMOS -M$1 2 6 1 4 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U +M$1 2 6 1 4 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.3375P PS=3.85U PD=1.95U * device instance $2 1.55,5.8 LVPMOS -M$2 1 5 2 4 MLVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U +M$2 1 5 2 4 LVPMOS L=0.25U W=1.5U AS=0.3375P AD=0.6375P PS=1.95U PD=3.85U * device instance $3 0.85,2.135 LVNMOS -M$3 3 6 8 7 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U PD=1.4U +M$3 3 6 8 7 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.21375P PS=2.75U PD=1.4U * device instance $4 1.55,2.135 LVNMOS -M$4 8 5 2 7 MLVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U +M$4 8 5 2 7 LVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U .ENDS ND2X1 * cell INVX1 @@ -77,7 +77,7 @@ M$4 8 5 2 7 MLVNMOS L=0.25U W=0.95U AS=0.21375P AD=0.40375P PS=1.4U PD=2.75U * net 5 IN * net 6 BULK * device instance $1 0.85,5.8 LVPMOS -M$1 1 5 2 4 MLVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U +M$1 1 5 2 4 LVPMOS L=0.25U W=1.5U AS=0.6375P AD=0.6375P PS=3.85U PD=3.85U * device instance $2 0.85,2.135 LVNMOS -M$2 3 5 2 6 MLVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U +M$2 3 5 2 6 LVNMOS L=0.25U W=0.95U AS=0.40375P AD=0.40375P PS=2.75U PD=2.75U .ENDS INVX1 From 4e85ae7db041dd18449b026ceb936e6bd1ab4a7b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 13 Apr 2019 02:48:10 +0200 Subject: [PATCH 47/54] WIP: netlist compare (better backtracking) --- src/db/db/dbNetlistCompare.cc | 304 +++++++++++++-------- src/db/unit_tests/dbNetlistCompareTests.cc | 12 +- 2 files changed, 190 insertions(+), 126 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index f0395199f..145ddac63 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -29,7 +29,7 @@ #include "tlLog.h" // verbose debug output -// #define PRINT_DEBUG_NETCOMPARE +#define PRINT_DEBUG_NETCOMPARE namespace db { @@ -482,7 +482,6 @@ public: const db::Circuit *cr = sc->circuit_ref (); size_t this_pin_id = pin_id; - pin_id = pin_map->normalize_pin_id (cr, pin_id); std::map::const_iterator icm = circuit_map->find (cr); if (icm == circuit_map->end ()) { @@ -505,6 +504,10 @@ public: cr = cm->other (); pin_id = cm->other_pin_from_this_pin (pin_id); + // realize pin swapping by normalization of pin ID + + pin_id = pin_map->normalize_pin_id (cr, pin_id); + // we cannot afford creating edges from all to all other pins, so we just create edges to the previous and next // pin. This may take more iterations to solve, but should be equivalent. @@ -786,6 +789,8 @@ struct CompareNodePtr } }; +class TentativeNodeMapping; + class NetDeviceGraph { public: @@ -859,9 +864,9 @@ public: * If tentative is true, assignments will not be retained and just the * status is reported. */ - size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, bool tentative, bool with_ambiguous); + size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, TentativeNodeMapping *tentative, bool with_ambiguous); - size_t derive_node_identities_from_node_set (const std::vector &nodes, const std::vector &other_nodes, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, bool tentative, bool with_ambiguous); + size_t derive_node_identities_from_node_set (const std::vector &nodes, const std::vector &other_nodes, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, TentativeNodeMapping *tentative, bool with_ambiguous); private: std::vector m_nodes; @@ -869,6 +874,63 @@ private: const db::Circuit *mp_circuit; }; +// -------------------------------------------------------------------------------------------------------------------- + +struct NodeRange +{ + NodeRange (size_t _num, std::vector::const_iterator _n1, std::vector::const_iterator _nn1, std::vector::const_iterator _n2, std::vector::const_iterator _nn2) + : num (_num), n1 (_n1), nn1 (_nn1), n2 (_n2), nn2 (_nn2) + { + // .. nothing yet .. + } + + bool operator< (const NodeRange &other) const + { + return num < other.num; + } + + size_t num; + std::vector::const_iterator n1, nn1, n2, nn2; +}; + +// -------------------------------------------------------------------------------------------------------------------- + +class TentativeNodeMapping +{ +public: + TentativeNodeMapping (NetDeviceGraph *g1, NetDeviceGraph *g2) + : mp_g1 (g1), mp_g2 (g2) + { } + + ~TentativeNodeMapping () + { + for (std::vector >::const_iterator i = m_to_undo.begin (); i != m_to_undo.end (); ++i) { + mp_g1->unidentify (i->first); + mp_g2->unidentify (i->second); + } + } + + static void map_pair (TentativeNodeMapping *nm, NetDeviceGraph *g1, size_t n1, NetDeviceGraph *g2, size_t n2) + { + g1->identify (n1, n2); + g2->identify (n2, n1); + if (nm) { + nm->keep (n1, n2); + } + } + +private: + std::vector > m_to_undo; + NetDeviceGraph *mp_g1, *mp_g2; + + void keep (size_t n1, size_t n2) + { + m_to_undo.push_back (std::make_pair (n1, n2)); + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + // @@@ make attribute const size_t depth_max = 8; const size_t n_branch_max = 100; @@ -910,14 +972,21 @@ NetDeviceGraph::build (const db::Circuit *c, DeviceCategorizer &device_categoriz } size_t -NetDeviceGraph::derive_node_identities (size_t net_index, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, bool tentative, bool with_ambiguous) +NetDeviceGraph::derive_node_identities (size_t net_index, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, TentativeNodeMapping *tentative, bool with_ambiguous) { NetGraphNode *n = & m_nodes[net_index]; NetGraphNode *nother = & other.m_nodes[n->other_net_index ()]; +#if defined(PRINT_DEBUG_NETCOMPARE) + std::string indent; + for (size_t d = 0; d < depth; ++d) { + indent += " "; + } +#endif + #if defined(PRINT_DEBUG_NETCOMPARE) if (! tentative) { - tl::info << "deducing from pair: " << n->net ()->expanded_name () << " vs. " << nother->net ()->expanded_name (); + tl::info << indent << "deducing from pair: " << n->net ()->expanded_name () << " vs. " << nother->net ()->expanded_name (); } #endif @@ -943,9 +1012,7 @@ NetDeviceGraph::derive_node_identities (size_t net_index, NetDeviceGraph &other, for (NetGraphNode::edge_iterator i = e; i != ee; ++i) { const NetGraphNode *n = &m_nodes[i->second.first]; - if (! n->has_other ()) { - nodes.push_back (n); - } + nodes.push_back (n); } if (! nodes.empty ()) { // if non-ambiguous, non-assigned @@ -964,9 +1031,7 @@ NetDeviceGraph::derive_node_identities (size_t net_index, NetDeviceGraph &other, for (NetGraphNode::edge_iterator i = e_other; i != ee_other && count_other < 2; ++i) { const NetGraphNode *n = &other.m_nodes[i->second.first]; - if (! n->has_other ()) { - other_nodes.push_back (n); - } + other_nodes.push_back (n); } @@ -981,15 +1046,18 @@ NetDeviceGraph::derive_node_identities (size_t net_index, NetDeviceGraph &other, // for the purpose of match evaluation we require an exact match of the node structure - if (tentative && (nodes.size () > 1 || other_nodes.size () > 1)) { + if (tentative) { if (nodes.size () != other_nodes.size ()) { return std::numeric_limits::max (); } - for (size_t i = 0; i < nodes.size (); ++i) { - if (! (*nodes[i] == *other_nodes[i]) || nodes[i]->has_other () != other_nodes[i]->has_other ()) { - return std::numeric_limits::max (); + // 1:1 pairing is less strict + if (nodes.size () > 1 || other_nodes.size () > 1) { + for (size_t i = 0; i < nodes.size (); ++i) { + if (! (*nodes[i] == *other_nodes[i])) { + return std::numeric_limits::max (); + } } } @@ -1016,37 +1084,23 @@ NetDeviceGraph::derive_node_identities (size_t net_index, NetDeviceGraph &other, #if defined(PRINT_DEBUG_NETCOMPARE) if (! tentative && new_nodes > 0) { - tl::info << "finished pair deduction: " << n->net ()->expanded_name () << " vs. " << nother->net ()->expanded_name () << " with " << new_nodes << " new pairs"; + tl::info << indent << "finished pair deduction: " << n->net ()->expanded_name () << " vs. " << nother->net ()->expanded_name () << " with " << new_nodes << " new pairs"; } #endif return new_nodes; } -namespace { - -struct NodeRange -{ - NodeRange (size_t _num, std::vector::const_iterator _n1, std::vector::const_iterator _nn1, std::vector::const_iterator _n2, std::vector::const_iterator _nn2) - : num (_num), n1 (_n1), nn1 (_nn1), n2 (_n2), nn2 (_nn2) - { - // .. nothing yet .. - } - - bool operator< (const NodeRange &other) const - { - return num < other.num; - } - - size_t num; - std::vector::const_iterator n1, nn1, n2, nn2; -}; - -} - size_t -NetDeviceGraph::derive_node_identities_from_node_set (const std::vector &nodes, const std::vector &other_nodes, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, bool tentative, bool with_ambiguous) +NetDeviceGraph::derive_node_identities_from_node_set (const std::vector &nodes, const std::vector &other_nodes, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, TentativeNodeMapping *tentative, bool with_ambiguous) { +#if defined(PRINT_DEBUG_NETCOMPARE) + std::string indent; + for (size_t d = 0; d < depth; ++d) { + indent += " "; + } +#endif + size_t new_nodes = 0; if (depth + 1 == depth_max) { @@ -1055,30 +1109,50 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vector this may render - // inexact matches, but further propagates net pairing + if (! nodes.front ()->has_other () && ! other_nodes.front ()->has_other ()) { - if (! tentative) { + // a single candiate: just take this one -> this may render + // inexact matches, but further propagates net pairing size_t ni = node_index_for_net (nodes.front ()->net ()); size_t other_ni = other.node_index_for_net (other_nodes.front ()->net ()); - identify (ni, other_ni); - other.identify (other_ni, ni); + TentativeNodeMapping::map_pair (tentative, this, ni, &other, other_ni); #if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << "deduced match (singular): " << nodes.front ()->net ()->expanded_name () << " vs. " << other_nodes.front ()->net ()->expanded_name (); + tl::info << indent << "deduced match (singular): " << nodes.front ()->net ()->expanded_name () << " vs. " << other_nodes.front ()->net ()->expanded_name (); #endif - if (logger) { - logger->match_nets (nodes.front ()->net (), other_nodes.front ()->net ()); + if (! tentative) { + if (logger) { + logger->match_nets (nodes.front ()->net (), other_nodes.front ()->net ()); + } } - // unconditionally continue here. - derive_node_identities (ni, other, depth + 1, n_branch, logger, circuit_pin_mapper, tentative, with_ambiguous); + // continue here. + size_t bt_count = derive_node_identities (ni, other, depth + 1, n_branch, logger, circuit_pin_mapper, tentative, with_ambiguous); + + if (bt_count != std::numeric_limits::max ()) { + new_nodes += bt_count; + } else if (tentative) { + return bt_count; + } + + new_nodes += 1; + + } else if (nodes.front ()->has_other ()) { + + // this decision leads to a contradiction + if (other.node_index_for_net (other_nodes.front ()->net ()) != nodes.front ()->other_net_index ()) { + return std::numeric_limits::max (); + } + + } else { + + // mismatch of assignment state + return std::numeric_limits::max (); } - new_nodes += 1; return new_nodes; } @@ -1155,36 +1229,50 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vector this may render // inexact matches, but further propagates net pairing + size_t ni = node_index_for_net ((*nr->n1)->net ()); + size_t other_ni = other.node_index_for_net ((*nr->n2)->net ()); + + TentativeNodeMapping::map_pair (tentative, this, ni, &other, other_ni); + +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << indent << "deduced match (singular): " << (*nr->n1)->net ()->expanded_name () << " vs. " << (*nr->n2)->net ()->expanded_name (); +#endif if (! tentative) { - - size_t ni = node_index_for_net ((*nr->n1)->net ()); - size_t other_ni = other.node_index_for_net ((*nr->n2)->net ()); - - identify (ni, other_ni); - other.identify (other_ni, ni); - - #if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << "deduced match (singular): " << (*nr->n1)->net ()->expanded_name () << " vs. " << (*nr->n2)->net ()->expanded_name (); - #endif if (logger) { logger->match_nets ((*nr->n1)->net (), (*nr->n2)->net ()); } - - // unconditionally continue here. - derive_node_identities (ni, other, depth + 1, n_branch, logger, circuit_pin_mapper, tentative, with_ambiguous); - } - new_nodes += 1; + // continue here. + size_t bt_count = derive_node_identities (ni, other, depth + 1, n_branch, logger, circuit_pin_mapper, tentative, with_ambiguous); + + if (bt_count != std::numeric_limits::max ()) { + new_nodes += bt_count; + new_nodes += 1; + } else if (tentative) { + new_nodes = bt_count; + } + + } else if ((*nr->n1)->has_other ()) { + + // this decision leads to a contradiction + if (other.node_index_for_net ((*nr->n2)->net ()) != (*nr->n1)->other_net_index ()) { + return std::numeric_limits::max (); + } + + } else { + + // mismatch of assignment state + return std::numeric_limits::max (); } + } else if (nr->num * n_branch > n_branch_max) { + + return std::numeric_limits::max (); + } else { - if (nr->num * n_branch > n_branch_max) { - return std::numeric_limits::max (); - } - std::vector > pairs; tl::equivalence_clusters equivalent_other_nodes; std::set seen; @@ -1211,17 +1299,20 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vectornet ()); size_t other_ni = other.node_index_for_net ((*i2)->net ()); - identify (ni, other_ni); - other.identify (other_ni, ni); + TentativeNodeMapping tn (this, &other); + TentativeNodeMapping::map_pair (&tn, this, ni, &other, other_ni); // try this candidate in tentative mode - size_t bt_count = derive_node_identities (ni, other, depth + 1, nr->num * n_branch, logger, circuit_pin_mapper, true /*tentative*/, with_ambiguous); - - unidentify (ni); - other.unidentify (other_ni); +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << indent << "trying in tentative mode: " << (*i1)->net ()->expanded_name () << " vs. " << (*i2)->net ()->expanded_name (); +#endif + size_t bt_count = derive_node_identities (ni, other, depth + 1, nr->num * n_branch, logger, circuit_pin_mapper, &tn, with_ambiguous); if (bt_count != std::numeric_limits::max ()) { +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << indent << "match found"; +#endif // we have a match ... if (any) { @@ -1245,6 +1336,9 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vector::max (); } @@ -1253,46 +1347,6 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vector other2this; - for (std::vector >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) { - other2this.insert (std::make_pair (p->second, p->first)); - } - - for (size_t cid = 1; cid <= equivalent_other_nodes.size (); ++cid) { - - tl::equivalence_clusters::cluster_iterator c = equivalent_other_nodes.begin_cluster (cid); - const NetGraphNode *c1 = other2this[(*c)->first]; - - tl::equivalence_clusters::cluster_iterator cc = c; - ++cc; - for ( ; cc != equivalent_other_nodes.end_cluster (cid); ++cc) { - - for (db::Net::const_pin_iterator pp = (*cc)->first->net ()->begin_pins (); pp != (*cc)->first->net ()->end_pins (); ++pp) { - for (db::Net::const_pin_iterator p = (*c)->first->net ()->begin_pins (); p != (*c)->first->net ()->end_pins (); ++p) { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << "pin swapping due to ambiguous nets for circuit (B) " << other.circuit ()->name () << ": " << pp->pin ()->expanded_name () << " and " << p->pin ()->expanded_name (); -#endif - circuit_pin_mapper->map_pins (other.circuit (), pp->pin_id (), p->pin_id ()); - } - } - - const NetGraphNode *cc1 = other2this[(*cc)->first]; - - for (db::Net::const_pin_iterator pp = cc1->net ()->begin_pins (); pp != cc1->net ()->end_pins (); ++pp) { - for (db::Net::const_pin_iterator p = c1->net ()->begin_pins (); p != c1->net ()->end_pins (); ++p) { -#if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << "pin swapping due to ambiguous nets for circuit (A) " << circuit ()->name () << ": " << pp->pin ()->expanded_name () << " and " << p->pin ()->expanded_name (); -#endif - circuit_pin_mapper->map_pins (circuit (), pp->pin_id (), p->pin_id ()); - } - } - - } - - } - // issue the matching pairs for (std::vector >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) { @@ -1300,11 +1354,10 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vectorfirst->net ()); size_t other_ni = other.node_index_for_net (p->second->net ()); - identify (ni, other_ni); - other.identify (other_ni, ni); + TentativeNodeMapping::map_pair (tentative, this, ni, &other, other_ni); #if defined(PRINT_DEBUG_NETCOMPARE) - tl::info << "deduced match: " << p->first->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name (); + tl::info << indent << "deduced match: " << p->first->net ()->expanded_name () << " vs. " << p->second->net ()->expanded_name (); #endif if (logger) { bool ambiguous = equivalent_other_nodes.has_attribute (p->second); @@ -1328,6 +1381,17 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vector >::const_iterator p = pairs.begin (); p != pairs.end (); ++p) { + + size_t ni = node_index_for_net (p->first->net ()); + size_t other_ni = other.node_index_for_net (p->second->net ()); + + TentativeNodeMapping::map_pair (tentative, this, ni, &other, other_ni); + + } + } } @@ -1643,7 +1707,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { if (i1->has_other () && i1->net ()) { - size_t ni = g1.derive_node_identities (i1 - g1.begin (), g2, 0, 1, mp_logger, mp_circuit_pin_mapper.get (), false /*not tentative*/, pass > 0 /*with ambiguities*/); + size_t ni = g1.derive_node_identities (i1 - g1.begin (), g2, 0, 1, mp_logger, mp_circuit_pin_mapper.get (), 0 /*not tentative*/, pass > 0 /*with ambiguities*/); if (ni > 0 && ni != std::numeric_limits::max ()) { new_identities += ni; #if defined(PRINT_DEBUG_NETCOMPARE) @@ -1686,7 +1750,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, std::sort (nodes.begin (), nodes.end (), CompareNodePtr ()); std::sort (other_nodes.begin (), other_nodes.end (), CompareNodePtr ()); - size_t ni = g1.derive_node_identities_from_node_set (nodes, other_nodes, g2, 0, 1, mp_logger, mp_circuit_pin_mapper.get (), false /*not tentative*/, pass > 0 /*with ambiguities*/); + size_t ni = g1.derive_node_identities_from_node_set (nodes, other_nodes, g2, 0, 1, mp_logger, mp_circuit_pin_mapper.get (), 0 /*not tentative*/, pass > 0 /*with ambiguities*/); if (ni > 0 && ni != std::numeric_limits::max ()) { new_identities += ni; #if defined(PRINT_DEBUG_NETCOMPARE) diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 36760f41c..bf229877d 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -335,9 +335,8 @@ TEST(1_SimpleInverter) EXPECT_EQ (logger.text (), "begin_circuit INV INV\n" "match_nets VDD VDD\n" - "match_nets OUT OUT\n" - "match_nets IN IN\n" "match_nets VSS VSS\n" + "match_nets IN IN\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -2072,10 +2071,11 @@ TEST(17_InherentlyAmbiguousDecoder) " device NMOS $3 (S=VSS,G=A,D=INT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" " device NMOS $4 (S=INT,G=B,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" "end;\n" - "circuit DECODER ($0=A,$1=B,$2=NQ0,$3=NQ1,$4=NQ2,$5=NQ3,$6=VDD,$7=VSS);\n" - " subcircuit NAND $1 ($0=B,$1=B,$2=NB,$3=VDD,$4=VSS);\n" - " subcircuit NAND $2 ($0=A,$1=NB,$2=NQ1,$3=VDD,$4=VSS);\n" - " subcircuit NAND $3 ($0=A,$1=A,$2=NA,$3=VDD,$4=VSS);\n" + // BTW: this shows that pin swapping can't be automated ... + "circuit DECODER ($0=B,$1=A,$2=NQ0,$3=NQ1,$4=NQ2,$5=NQ3,$6=VDD,$7=VSS);\n" + " subcircuit NAND $1 ($0=A,$1=A,$2=NA,$3=VDD,$4=VSS);\n" + " subcircuit NAND $2 ($0=B,$1=B,$2=NB,$3=VDD,$4=VSS);\n" + " subcircuit NAND $3 ($0=A,$1=NB,$2=NQ1,$3=VDD,$4=VSS);\n" " subcircuit NAND $4 ($0=A,$1=B,$2=NQ3,$3=VDD,$4=VSS);\n" " subcircuit NAND $5 ($0=NA,$1=NB,$2=NQ0,$3=VDD,$4=VSS);\n" " subcircuit NAND $6 ($0=NA,$1=B,$2=NQ2,$3=VDD,$4=VSS);\n" From 92524dcf57f8af85945a8b9345ca2fda48c053ad Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 13 Apr 2019 19:56:08 +0200 Subject: [PATCH 48/54] WIP: netlist compare - bugfixed latest version and updated tests. --- src/db/db/dbNetlistCompare.cc | 61 +++++++++++-- src/db/db/dbNetlistCompare.h | 3 +- src/db/unit_tests/dbNetlistCompareTests.cc | 99 +++++++++++++++++----- testdata/ruby/dbNetlistCompare.rb | 8 +- 4 files changed, 134 insertions(+), 37 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 145ddac63..91d72f621 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -29,7 +29,7 @@ #include "tlLog.h" // verbose debug output -#define PRINT_DEBUG_NETCOMPARE +// #define PRINT_DEBUG_NETCOMPARE namespace db { @@ -100,6 +100,16 @@ public: } } + size_t is_mapped (const db::Circuit *circuit, size_t pin_id) const + { + std::map >::const_iterator pm = m_pin_map.find (circuit); + if (pm != m_pin_map.end ()) { + return pm->second.has_attribute (pin_id); + } else { + return false; + } + } + size_t normalize_pin_id (const db::Circuit *circuit, size_t pin_id) const { std::map >::const_iterator pm = m_pin_map.find (circuit); @@ -1468,6 +1478,7 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const // we need to create a copy because this method is supposed to be const. db::CircuitCategorizer circuit_categorizer = *mp_circuit_categorizer; db::DeviceCategorizer device_categorizer = *mp_device_categorizer; + db::CircuitPinMapper circuit_pin_mapper = *mp_circuit_pin_mapper; bool good = true; @@ -1553,7 +1564,7 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const } bool pin_mismatch = false; - bool g = compare_circuits (ca, cb, device_categorizer, circuit_categorizer, *net_identity, pin_mismatch, c12_pin_mapping, c22_pin_mapping); + bool g = compare_circuits (ca, cb, device_categorizer, circuit_categorizer, circuit_pin_mapper, *net_identity, pin_mismatch, c12_pin_mapping, c22_pin_mapping); if (! g) { good = false; } @@ -1563,6 +1574,8 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const verified_circuits_b.insert (cb); } + derive_pin_equivalence (ca, cb, &circuit_pin_mapper); + if (mp_logger) { mp_logger->end_circuit (ca, cb, g); } @@ -1586,6 +1599,36 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const return good; } +static +std::vector collect_pins_with_empty_nets (const db::Circuit *c, CircuitPinMapper *circuit_pin_mapper) +{ + std::vector pins; + + for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { + const db::Net *net = n.operator-> (); + if (net->pin_count () > 0 && net->terminal_count () == 0 && net->subcircuit_pin_count () == 0) { + for (db::Net::const_pin_iterator p = net->begin_pins (); p != net->end_pins (); ++p) { + if (! circuit_pin_mapper->is_mapped (c, p->pin_id ())) { + pins.push_back (p->pin_id ()); + } + } + } + } + + return pins; +} + +void +NetlistComparer::derive_pin_equivalence (const db::Circuit *ca, const db::Circuit *cb, CircuitPinMapper *circuit_pin_mapper) +{ + std::vector pa, pb; + pa = collect_pins_with_empty_nets (ca, circuit_pin_mapper); + pb = collect_pins_with_empty_nets (cb, circuit_pin_mapper); + + circuit_pin_mapper->map_pins (ca, pa); + circuit_pin_mapper->map_pins (cb, pb); +} + bool NetlistComparer::all_subcircuits_verified (const db::Circuit *c, const std::set &verified_circuits) const { @@ -1662,7 +1705,7 @@ compute_subcircuit_key (const db::SubCircuit &subcircuit, const db::NetDeviceGra } bool -NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, const std::vector > &net_identity, bool &pin_mismatch, std::map &c12_circuit_and_pin_mapping, std::map &c22_circuit_and_pin_mapping) const +NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, db::CircuitPinMapper &circuit_pin_mapper, const std::vector > &net_identity, bool &pin_mismatch, std::map &c12_circuit_and_pin_mapping, std::map &c22_circuit_and_pin_mapping) const { db::DeviceFilter device_filter (m_cap_threshold, m_res_threshold); @@ -1670,8 +1713,8 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, // NOTE: for normalization we map all subcircuits of c1 to c2. // Also, pin swapping will only happen there. - g1.build (c1, device_categorizer, circuit_categorizer, device_filter, &c12_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); - g2.build (c2, device_categorizer, circuit_categorizer, device_filter, &c22_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); + g1.build (c1, device_categorizer, circuit_categorizer, device_filter, &c12_circuit_and_pin_mapping, &circuit_pin_mapper); + g2.build (c2, device_categorizer, circuit_categorizer, device_filter, &c22_circuit_and_pin_mapping, &circuit_pin_mapper); // Match dummy nodes for null nets g1.identify (0, 0); @@ -1707,7 +1750,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { if (i1->has_other () && i1->net ()) { - size_t ni = g1.derive_node_identities (i1 - g1.begin (), g2, 0, 1, mp_logger, mp_circuit_pin_mapper.get (), 0 /*not tentative*/, pass > 0 /*with ambiguities*/); + size_t ni = g1.derive_node_identities (i1 - g1.begin (), g2, 0, 1, mp_logger, &circuit_pin_mapper, 0 /*not tentative*/, pass > 0 /*with ambiguities*/); if (ni > 0 && ni != std::numeric_limits::max ()) { new_identities += ni; #if defined(PRINT_DEBUG_NETCOMPARE) @@ -1750,7 +1793,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, std::sort (nodes.begin (), nodes.end (), CompareNodePtr ()); std::sort (other_nodes.begin (), other_nodes.end (), CompareNodePtr ()); - size_t ni = g1.derive_node_identities_from_node_set (nodes, other_nodes, g2, 0, 1, mp_logger, mp_circuit_pin_mapper.get (), 0 /*not tentative*/, pass > 0 /*with ambiguities*/); + size_t ni = g1.derive_node_identities_from_node_set (nodes, other_nodes, g2, 0, 1, mp_logger, &circuit_pin_mapper, 0 /*not tentative*/, pass > 0 /*with ambiguities*/); if (ni > 0 && ni != std::numeric_limits::max ()) { new_identities += ni; #if defined(PRINT_DEBUG_NETCOMPARE) @@ -1960,7 +2003,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (db::Circuit::const_subcircuit_iterator sc = c1->begin_subcircuits (); sc != c1->end_subcircuits (); ++sc) { - std::vector > k = compute_subcircuit_key (*sc, g1, &c12_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); + std::vector > k = compute_subcircuit_key (*sc, g1, &c12_circuit_and_pin_mapping, &circuit_pin_mapper); bool mapped = true; for (std::vector >::iterator i = k.begin (); i != k.end () && mapped; ++i) { @@ -1983,7 +2026,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (db::Circuit::const_subcircuit_iterator sc = c2->begin_subcircuits (); sc != c2->end_subcircuits (); ++sc) { - std::vector > k = compute_subcircuit_key (*sc, g2, &c22_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); + std::vector > k = compute_subcircuit_key (*sc, g2, &c22_circuit_and_pin_mapping, &circuit_pin_mapper); bool mapped = true; for (std::vector >::iterator i = k.begin (); i != k.end (); ++i) { diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h index f2cfa9bf3..88e76af2b 100644 --- a/src/db/db/dbNetlistCompare.h +++ b/src/db/db/dbNetlistCompare.h @@ -217,8 +217,9 @@ public: bool compare (const db::Netlist *a, const db::Netlist *b) const; protected: - bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, const std::vector > &net_identity, bool &pin_mismatch, std::map &c12_circuit_and_pin_mapping, std::map &c22_circuit_and_pin_mapping) const; + bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, db::CircuitPinMapper &circuit_pin_mapper, const std::vector > &net_identity, bool &pin_mismatch, std::map &c12_circuit_and_pin_mapping, std::map &c22_circuit_and_pin_mapping) const; bool all_subcircuits_verified (const db::Circuit *c, const std::set &verified_circuits) const; + static void derive_pin_equivalence (const db::Circuit *ca, const db::Circuit *cb, CircuitPinMapper *circuit_pin_mapper); NetlistCompareLogger *mp_logger; std::map, std::vector > > m_same_nets; diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index bf229877d..92c907966 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -335,8 +335,9 @@ TEST(1_SimpleInverter) EXPECT_EQ (logger.text (), "begin_circuit INV INV\n" "match_nets VDD VDD\n" - "match_nets VSS VSS\n" + "match_nets OUT OUT\n" "match_nets IN IN\n" + "match_nets VSS VSS\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -880,8 +881,8 @@ TEST(5_BufferTwoPathsDifferentDeviceClasses) "begin_circuit BUF BUF\n" "match_nets INT $10\n" "match_nets IN IN\n" - "match_nets INT2 $11\n" "match_nets OUT OUT\n" + "match_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -945,8 +946,8 @@ TEST(6_BufferTwoPathsAdditionalResistor) "begin_circuit BUF BUF\n" "match_nets INT $10\n" "match_nets IN IN\n" - "match_nets INT2 $11\n" "match_nets OUT OUT\n" + "match_nets INT2 $11\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -1007,9 +1008,9 @@ TEST(6_BufferTwoPathsAdditionalDevices) "match_nets INT $11\n" "match_nets VDD VDD\n" "match_nets IN IN\n" - "match_nets INT2 $10\n" "match_nets VSS VSS\n" "match_nets OUT OUT\n" + "match_nets INT2 $10\n" "match_pins $0 $1\n" "match_pins $1 $3\n" "match_pins $2 $0\n" @@ -1617,9 +1618,9 @@ TEST(14_Subcircuit2Nand) "end_circuit NAND NAND MATCH\n" "begin_circuit TOP TOP\n" "match_nets OUT OUT\n" - "match_nets IN1 IN1\n" - "match_nets IN2 IN2\n" "match_nets INT INT\n" + "match_nets IN2 IN2\n" + "match_nets IN1 IN1\n" "match_nets VDD VDD\n" "match_nets VSS VSS\n" "match_pins $0 $0\n" @@ -1767,9 +1768,9 @@ TEST(14_Subcircuit2MatchWithSwap) "end_circuit NAND NAND MATCH\n" "begin_circuit TOP TOP\n" "match_nets OUT OUT\n" - "match_nets IN1 IN1\n" - "match_nets IN2 IN2\n" "match_nets INT INT\n" + "match_nets IN2 IN2\n" + "match_nets IN1 IN1\n" "match_nets VDD VDD\n" "match_nets VSS VSS\n" "match_pins $0 $0\n" @@ -1886,7 +1887,8 @@ TEST(15_EmptySubCircuitWithoutPinNames) " subcircuit TRANS $3 ($1=OUT,$2=$5,$3=$2);\n" " subcircuit TRANS $4 ($1=OUT,$2=$4,$3=$2);\n" "end;\n" - // This circuit is an abstract and it's pins are defined by the pin names + // This circuit is an abstract and it's pins are not defined by the pin names -> + // they are internally marked as swappable "circuit TRANS ($1=$1,$2=$2,$3=$3);\n" "end;\n"; @@ -2110,27 +2112,27 @@ TEST(17_InherentlyAmbiguousDecoder) "match_devices $4 $4\n" "end_circuit NAND NAND MATCH\n" "begin_circuit DECODER DECODER\n" - "match_nets NA NB\n" - "match_nets A B\n" - "match_nets VDD VDD\n" - "match_nets B A\n" - "match_nets NB NA\n" "match_nets VSS VSS\n" - "match_ambiguous_nets NQ0 NQ0\n" - "match_ambiguous_nets NQ2 NQ1\n" - "match_ambiguous_nets NQ1 NQ2\n" - "match_ambiguous_nets NQ3 NQ3\n" - "match_pins $0 $1\n" - "match_pins $1 $0\n" + "match_nets VDD VDD\n" + "match_ambiguous_nets A B\n" + "match_ambiguous_nets B A\n" + "match_nets NB NA\n" + "match_nets NA NB\n" + "match_nets NQ0 NQ0\n" + "match_nets NQ2 NQ1\n" + "match_nets NQ1 NQ2\n" + "match_nets NQ3 NQ3\n" + "match_pins $0 $0\n" + "match_pins $1 $1\n" "match_pins $2 $2\n" "match_pins $3 $4\n" "match_pins $4 $3\n" "match_pins $5 $5\n" "match_pins $6 $6\n" "match_pins $7 $7\n" - "match_subcircuits $1 $1\n" - "match_subcircuits $5 $2\n" - "match_subcircuits $2 $3\n" + "match_subcircuits $2 $1\n" + "match_subcircuits $1 $2\n" + "match_subcircuits $5 $3\n" "match_subcircuits $6 $4\n" "match_subcircuits $3 $5\n" "match_subcircuits $4 $6\n" @@ -2138,5 +2140,56 @@ TEST(17_InherentlyAmbiguousDecoder) ); EXPECT_EQ (good, true); + + logger.clear (); + comp.same_nets (nl1.circuit_by_name ("DECODER")->net_by_name ("A"), nl2.circuit_by_name ("DECODER")->net_by_name ("A")); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit NAND NAND\n" + "match_nets VSS VSS\n" + "match_nets INT INT\n" + "match_nets OUT OUT\n" + "match_nets VDD VDD\n" + "match_nets B B\n" + "match_nets A A\n" + "match_pins $0 $0\n" + "match_pins $1 $1\n" + "match_pins $2 $2\n" + "match_pins $3 $3\n" + "match_pins $4 $4\n" + "match_devices $1 $1\n" + "match_devices $2 $2\n" + "match_devices $3 $3\n" + "match_devices $4 $4\n" + "end_circuit NAND NAND MATCH\n" + "begin_circuit DECODER DECODER\n" + "match_nets NB NB\n" + "match_nets B B\n" + "match_nets NA NA\n" + "match_nets VDD VDD\n" + "match_nets VSS VSS\n" + "match_nets NQ0 NQ0\n" + "match_nets NQ2 NQ2\n" + "match_nets NQ1 NQ1\n" + "match_nets NQ3 NQ3\n" + "match_pins $0 $1\n" + "match_pins $1 $0\n" + "match_pins $2 $2\n" + "match_pins $3 $3\n" + "match_pins $4 $4\n" + "match_pins $5 $5\n" + "match_pins $6 $6\n" + "match_pins $7 $7\n" + "match_subcircuits $1 $1\n" + "match_subcircuits $2 $2\n" + "match_subcircuits $4 $3\n" + "match_subcircuits $6 $4\n" + "match_subcircuits $3 $5\n" + "match_subcircuits $5 $6\n" + "end_circuit DECODER DECODER MATCH" + ); + + EXPECT_EQ (good, true); } diff --git a/testdata/ruby/dbNetlistCompare.rb b/testdata/ruby/dbNetlistCompare.rb index e78ad6564..f51bfd979 100644 --- a/testdata/ruby/dbNetlistCompare.rb +++ b/testdata/ruby/dbNetlistCompare.rb @@ -616,8 +616,8 @@ END begin_circuit BUF BUF match_nets INT $10 match_nets IN IN -match_nets INT2 $11 match_nets OUT OUT +match_nets INT2 $11 match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -686,8 +686,8 @@ END begin_circuit BUF BUF match_nets INT $10 match_nets IN IN -match_nets INT2 $11 match_nets OUT OUT +match_nets INT2 $11 match_pins $0 $1 match_pins $1 $3 match_pins $2 $0 @@ -840,9 +840,9 @@ match_devices $4 $4 end_circuit NAND NAND MATCH begin_circuit TOP TOP match_nets OUT OUT -match_nets IN1 IN1 -match_nets IN2 IN2 match_nets INT INT +match_nets IN2 IN2 +match_nets IN1 IN1 match_nets VDD VDD match_nets VSS VSS match_pins $0 $0 From 699e94a45f58e60aa570b79759701ba47fa282d5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 14 Apr 2019 19:11:42 +0200 Subject: [PATCH 49/54] WIP: added configuration options (complexity, depth) for net compare --- src/db/db/dbNetlistCompare.cc | 73 +++++++++----- src/db/db/dbNetlistCompare.h | 44 +++++++++ src/db/db/gsiDeclDbNetlistCompare.cc | 23 +++++ src/db/unit_tests/dbNetlistCompareTests.cc | 109 +++++++++++++++++++++ 4 files changed, 223 insertions(+), 26 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 91d72f621..9ddcae997 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -29,7 +29,7 @@ #include "tlLog.h" // verbose debug output -// #define PRINT_DEBUG_NETCOMPARE +#define PRINT_DEBUG_NETCOMPARE namespace db { @@ -874,9 +874,9 @@ public: * If tentative is true, assignments will not be retained and just the * status is reported. */ - size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, TentativeNodeMapping *tentative, bool with_ambiguous); + size_t derive_node_identities (size_t net_index, NetDeviceGraph &other, size_t depth, size_t max_depth, size_t n_branch, size_t max_n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, TentativeNodeMapping *tentative, bool with_ambiguous); - size_t derive_node_identities_from_node_set (const std::vector &nodes, const std::vector &other_nodes, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, TentativeNodeMapping *tentative, bool with_ambiguous); + size_t derive_node_identities_from_node_set (const std::vector &nodes, const std::vector &other_nodes, NetDeviceGraph &other, size_t depth, size_t max_depth, size_t n_branch, size_t max_n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, TentativeNodeMapping *tentative, bool with_ambiguous); private: std::vector m_nodes; @@ -941,10 +941,6 @@ private: // -------------------------------------------------------------------------------------------------------------------- -// @@@ make attribute -const size_t depth_max = 8; -const size_t n_branch_max = 100; - void NetDeviceGraph::build (const db::Circuit *c, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const db::DeviceFilter &device_filter, const std::map *circuit_and_pin_mapping, const CircuitPinMapper *circuit_pin_mapper) { @@ -982,7 +978,7 @@ NetDeviceGraph::build (const db::Circuit *c, DeviceCategorizer &device_categoriz } size_t -NetDeviceGraph::derive_node_identities (size_t net_index, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, TentativeNodeMapping *tentative, bool with_ambiguous) +NetDeviceGraph::derive_node_identities (size_t net_index, NetDeviceGraph &other, size_t depth, size_t max_depth, size_t n_branch, size_t max_n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, TentativeNodeMapping *tentative, bool with_ambiguous) { NetGraphNode *n = & m_nodes[net_index]; NetGraphNode *nother = & other.m_nodes[n->other_net_index ()]; @@ -1076,7 +1072,7 @@ NetDeviceGraph::derive_node_identities (size_t net_index, NetDeviceGraph &other, // propagate pairing in picky mode: this means we only accept exact a match if the node set // is exactly identical and no ambiguous nodes are present when ambiguous nodes are forbidden - size_t bt_count = derive_node_identities_from_node_set (nodes, other_nodes, other, depth, n_branch, logger, circuit_pin_mapper, tentative, with_ambiguous); + size_t bt_count = derive_node_identities_from_node_set (nodes, other_nodes, other, depth, max_depth, n_branch, max_n_branch, logger, circuit_pin_mapper, tentative, with_ambiguous); if (bt_count == std::numeric_limits::max ()) { if (tentative) { @@ -1102,18 +1098,22 @@ NetDeviceGraph::derive_node_identities (size_t net_index, NetDeviceGraph &other, } size_t -NetDeviceGraph::derive_node_identities_from_node_set (const std::vector &nodes, const std::vector &other_nodes, NetDeviceGraph &other, size_t depth, size_t n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, TentativeNodeMapping *tentative, bool with_ambiguous) +NetDeviceGraph::derive_node_identities_from_node_set (const std::vector &nodes, const std::vector &other_nodes, NetDeviceGraph &other, size_t depth, size_t max_depth, size_t n_branch, size_t max_n_branch, NetlistCompareLogger *logger, CircuitPinMapper *circuit_pin_mapper, TentativeNodeMapping *tentative, bool with_ambiguous) { #if defined(PRINT_DEBUG_NETCOMPARE) std::string indent; for (size_t d = 0; d < depth; ++d) { indent += " "; } + indent += "*" + tl::to_string (n_branch) + " "; #endif size_t new_nodes = 0; - if (depth + 1 == depth_max) { + if (depth > max_depth) { +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << indent << "max. depth exhausted (" << depth + 1 << ">" << max_depth << ")"; +#endif return std::numeric_limits::max (); } @@ -1139,7 +1139,7 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vector::max ()) { new_nodes += bt_count; @@ -1232,6 +1232,8 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vector::const_iterator nr = node_ranges.begin (); nr != node_ranges.end (); ++nr) { + // @@@ node ranges might have changed - adjust to real count and skip leading pairs assigned already + if (nr->num == 1) { if (! (*nr->n1)->has_other () && ! (*nr->n2)->has_other ()) { @@ -1254,7 +1256,7 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vector::max ()) { new_nodes += bt_count; @@ -1277,12 +1279,18 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vectornum * n_branch > n_branch_max) { + } else if (nr->num * n_branch > max_n_branch) { +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << indent << "max. complexity exhausted (" << nr->num << "*" << n_branch << ">" << max_n_branch << ") - mismatch."; +#endif return std::numeric_limits::max (); } else { +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << indent << "analyzing ambiguity group with " << nr->num << " members"; +#endif std::vector > pairs; tl::equivalence_clusters equivalent_other_nodes; std::set seen; @@ -1316,7 +1324,7 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vectornet ()->expanded_name () << " vs. " << (*i2)->net ()->expanded_name (); #endif - size_t bt_count = derive_node_identities (ni, other, depth + 1, nr->num * n_branch, logger, circuit_pin_mapper, &tn, with_ambiguous); + size_t bt_count = derive_node_identities (ni, other, depth + 1, max_depth, nr->num * n_branch, max_n_branch, logger, circuit_pin_mapper, &tn, with_ambiguous); if (bt_count != std::numeric_limits::max ()) { @@ -1386,7 +1394,7 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vectorfirst->net ()); - size_t bt_count = derive_node_identities (ni, other, depth + 1, nr->num * n_branch, logger, circuit_pin_mapper, tentative, with_ambiguous); + size_t bt_count = derive_node_identities (ni, other, depth + 1, max_depth, nr->num * n_branch, max_n_branch, logger, circuit_pin_mapper, tentative, with_ambiguous); tl_assert (bt_count != std::numeric_limits::max ()); } @@ -1404,6 +1412,10 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vectornum << " members"; +#endif + } } @@ -1424,6 +1436,9 @@ NetlistComparer::NetlistComparer (NetlistCompareLogger *logger) m_cap_threshold = -1.0; // not set m_res_threshold = -1.0; // not set + + m_max_depth = 8; + m_max_n_branch = 100; } void @@ -1737,32 +1752,38 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (int pass = 0; pass < 2; ++pass) { +#if defined(PRINT_DEBUG_NETCOMPARE) + if (pass > 0) { + tl::info << "including ambiguous nodes now."; + } +#endif + good = true; while (true) { - #if defined(PRINT_DEBUG_NETCOMPARE) +#if defined(PRINT_DEBUG_NETCOMPARE) ++iter; tl::info << "new compare iteration #" << iter; tl::info << "deducing from present nodes ..."; - #endif +#endif size_t new_identities = 0; for (db::NetDeviceGraph::node_iterator i1 = g1.begin (); i1 != g1.end (); ++i1) { if (i1->has_other () && i1->net ()) { - size_t ni = g1.derive_node_identities (i1 - g1.begin (), g2, 0, 1, mp_logger, &circuit_pin_mapper, 0 /*not tentative*/, pass > 0 /*with ambiguities*/); + size_t ni = g1.derive_node_identities (i1 - g1.begin (), g2, 0, m_max_depth, 1, m_max_n_branch, mp_logger, &circuit_pin_mapper, 0 /*not tentative*/, pass > 0 /*with ambiguities*/); if (ni > 0 && ni != std::numeric_limits::max ()) { new_identities += ni; - #if defined(PRINT_DEBUG_NETCOMPARE) +#if defined(PRINT_DEBUG_NETCOMPARE) tl::info << ni << " new identities."; - #endif +#endif } } } - #if defined(PRINT_DEBUG_NETCOMPARE) +#if defined(PRINT_DEBUG_NETCOMPARE) tl::info << "checking topological identity ..."; - #endif +#endif // derive new identities through topology: first collect all nets with the same topological signature @@ -1793,12 +1814,12 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, std::sort (nodes.begin (), nodes.end (), CompareNodePtr ()); std::sort (other_nodes.begin (), other_nodes.end (), CompareNodePtr ()); - size_t ni = g1.derive_node_identities_from_node_set (nodes, other_nodes, g2, 0, 1, mp_logger, &circuit_pin_mapper, 0 /*not tentative*/, pass > 0 /*with ambiguities*/); + size_t ni = g1.derive_node_identities_from_node_set (nodes, other_nodes, g2, 0, m_max_depth, 1, m_max_n_branch, mp_logger, &circuit_pin_mapper, 0 /*not tentative*/, pass > 0 /*with ambiguities*/); if (ni > 0 && ni != std::numeric_limits::max ()) { new_identities += ni; - #if defined(PRINT_DEBUG_NETCOMPARE) +#if defined(PRINT_DEBUG_NETCOMPARE) tl::info << ni << " new identities."; - #endif +#endif } if (new_identities == 0) { diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h index 88e76af2b..c2946a997 100644 --- a/src/db/db/dbNetlistCompare.h +++ b/src/db/db/dbNetlistCompare.h @@ -211,6 +211,48 @@ public: */ void exclude_resistors (double threshold); + /** + * @brief Sets the maximum seach depth + * + * This value limits the search depth of the backtracking algorithm to the + * given number of jumps. + */ + void set_max_depth (size_t n) + { + m_max_depth = n; + } + + /** + * @brief Gets the maximum search depth + */ + size_t max_depth () const + { + return m_max_depth; + } + + /** + * @brief Sets the maximum branch complexity + * + * This value limits the maximum branch complexity of the backtracking algorithm. + * The complexity is the accumulated number of branch options with ambiguous + * net matches. Backtracking will stop when the maximum number of options + * has been exceeded. + * As the computational complexity is the square of the branch count, + * this value should be adjusted carefully. + */ + void set_max_branch_complexity (size_t n) + { + m_max_n_branch = n; + } + + /** + * @brief Gets the maximum branch complexity + */ + size_t max_branch_complexity () const + { + return m_max_n_branch; + } + /** * @brief Actually compares the two netlists */ @@ -228,6 +270,8 @@ protected: std::auto_ptr mp_circuit_categorizer; double m_cap_threshold; double m_res_threshold; + size_t m_max_n_branch; + size_t m_max_depth; }; } diff --git a/src/db/db/gsiDeclDbNetlistCompare.cc b/src/db/db/gsiDeclDbNetlistCompare.cc index 5fd810d39..e5a652ecb 100644 --- a/src/db/db/gsiDeclDbNetlistCompare.cc +++ b/src/db/db/gsiDeclDbNetlistCompare.cc @@ -482,6 +482,29 @@ Class decl_dbNetlistComparer ("db", "NetlistComparer", "@brief Excludes all resistor devices with a resistance values higher than the given threshold.\n" "To reset this constraint, set this attribute to zero." ) + + gsi::method ("max_depth=", &db::NetlistComparer::set_max_depth, gsi::arg ("n"), + "@brief Sets the maximum seach depth\n" + "This value limits the search depth of the backtracking algorithm to the\n" + "given number of jumps.\n" + ) + + gsi::method ("max_depth", &db::NetlistComparer::max_depth, + "@brief Gets the maximum seach depth\n" + "See \\max_depth= for details." + ) + + gsi::method ("max_branch_complexity=", &db::NetlistComparer::set_max_branch_complexity, gsi::arg ("n"), + "@brief Sets the maximum branch complexity\n" + "This value limits the maximum branch complexity of the backtracking algorithm.\n" + "The complexity is the accumulated number of branch options with ambiguous\n" + "net matches. Backtracking will stop when the maximum number of options\n" + "has been exceeded.\n" + "\n" + "As the computational complexity is the square of the branch count,\n" + "this value should be adjusted carefully.\n" + ) + + gsi::method ("max_branch_complexity", &db::NetlistComparer::max_branch_complexity, + "@brief Gets the maximum branch complexity\n" + "See \\max_branch_complexity= for details." + ) + gsi::method ("compare", &db::NetlistComparer::compare, gsi::arg ("netlist_a"), gsi::arg ("netlist_b"), "@brief Compares two netlists.\n" "This method will perform the actual netlist compare. It will return true if both netlists are identical. " diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 92c907966..0df3f06ff 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -2193,3 +2193,112 @@ TEST(17_InherentlyAmbiguousDecoder) EXPECT_EQ (good, true); } +TEST(18_ClockTree) +{ + const char *nls1 = + "circuit INV (IN=IN,OUT=OUT,VDD=VDD,VSS=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $2 (S=VSS,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TREE ();\n" + " subcircuit INV T (IN=IN,OUT=S,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TR (IN=S,OUT=SR,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TL (IN=S,OUT=SL,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TRR (IN=SR,OUT=SRR,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TRL (IN=SR,OUT=SRL,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TLR (IN=SL,OUT=SLR,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TLL (IN=SL,OUT=SLL,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TRRR (IN=SRR,OUT=SRRR,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TRRL (IN=SRR,OUT=SRRL,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TRLR (IN=SRL,OUT=SRLR,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TRLL (IN=SRL,OUT=SRLL,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TLRR (IN=SLR,OUT=SLRR,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TLRL (IN=SLR,OUT=SLRL,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TLLR (IN=SLL,OUT=SLLR,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TLLL (IN=SLL,OUT=SLLL,VDD=VDD,VSS=VSS);\n" + "end;\n"; + + const char *nls2 = + "circuit INV (IN=IN,OUT=OUT,VDD=VDD,VSS=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $2 (S=VSS,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n" + "circuit TREE ();\n" + " subcircuit INV TLRR (IN=SLR,OUT=SLRR,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TR (IN=S,OUT=SR,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TRRL (IN=SRR,OUT=SRRL,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TRLR (IN=SRL,OUT=SRLR,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TLR (IN=SL,OUT=SLR,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TLL (IN=SL,OUT=SLL,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TRRR (IN=SRR,OUT=SRRR,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TLLL (IN=SLL,OUT=SLLL,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TLRL (IN=SLR,OUT=SLRL,VDD=VDD,VSS=VSS);\n" + " subcircuit INV T (IN=IN,OUT=S,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TLLR (IN=SLL,OUT=SLLR,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TL (IN=S,OUT=SL,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TRR (IN=SR,OUT=SRR,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TRLL (IN=SRL,OUT=SRLL,VDD=VDD,VSS=VSS);\n" + " subcircuit INV TRL (IN=SR,OUT=SRL,VDD=VDD,VSS=VSS);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit INV INV\n" + "match_nets VDD VDD\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_nets VSS VSS\n" + "match_pins IN IN\n" + "match_pins OUT OUT\n" + "match_pins VDD VDD\n" + "match_pins VSS VSS\n" + "match_devices $1 $1\n" + "match_devices $2 $2\n" + "end_circuit INV INV MATCH\n" + "begin_circuit TREE TREE\n" + "match_nets IN IN\n" + "match_nets S S\n" + "match_nets VDD VDD\n" + "match_nets VSS VSS\n" + "match_ambiguous_nets SL SR\n" + "match_ambiguous_nets SR SL\n" + "match_ambiguous_nets SLL SRL\n" + "match_ambiguous_nets SLR SRR\n" + "match_ambiguous_nets SLLL SRLL\n" + "match_ambiguous_nets SLLR SRLR\n" + "match_ambiguous_nets SLRL SRRL\n" + "match_ambiguous_nets SLRR SRRR\n" + "match_ambiguous_nets SRL SLL\n" + "match_ambiguous_nets SRR SLR\n" + "match_ambiguous_nets SRLL SLLR\n" + "match_ambiguous_nets SRLR SLLL\n" + "match_ambiguous_nets SRRL SLRR\n" + "match_ambiguous_nets SRRR SLRL\n" + "match_subcircuits TRRL TLRR\n" + "match_subcircuits TL TR\n" + "match_subcircuits TLRL TRRL\n" + "match_subcircuits TLLR TRLR\n" + "match_subcircuits TRR TLR\n" + "match_subcircuits TRL TLL\n" + "match_subcircuits TLRR TRRR\n" + "match_subcircuits TRLR TLLL\n" + "match_subcircuits TRRR TLRL\n" + "match_subcircuits T T\n" + "match_subcircuits TRLL TLLR\n" + "match_subcircuits TR TL\n" + "match_subcircuits TLR TRR\n" + "match_subcircuits TLLL TRLL\n" + "match_subcircuits TLL TRL\n" + "end_circuit TREE TREE MATCH" + ); + EXPECT_EQ (good, true); +} + From 9f3bea92fbaaa2c6c23b14ff78ef0a960895a63c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 14 Apr 2019 19:22:07 +0200 Subject: [PATCH 50/54] WIP: less strict pin matching (for top levels with/without pins). Fixed tests. --- src/db/db/dbNetlistCompare.cc | 123 ++++++++++++--------- src/db/unit_tests/dbNetlistCompareTests.cc | 4 +- 2 files changed, 71 insertions(+), 56 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 9ddcae997..ab1a45527 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -1599,6 +1599,7 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const if (mp_logger) { mp_logger->circuit_skipped (ca, cb); + good = false; } } @@ -1849,77 +1850,91 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, // Report pin assignment // This step also does the pin identity mapping. - std::multimap net2pin; - for (db::Circuit::const_pin_iterator p = c2->begin_pins (); p != c2->end_pins (); ++p) { - const db::Net *net = c2->net_for_pin (p->id ()); - if (net) { - net2pin.insert (std::make_pair (g2.node_index_for_net (net), p.operator-> ())); - } - } + if (c1->pin_count () > 0 && c2->pin_count () > 0) { - CircuitMapper &c12_pin_mapping = c12_circuit_and_pin_mapping [c1]; - c12_pin_mapping.set_other (c2); - - // dummy mapping: we show this circuit is used. - CircuitMapper &c22_pin_mapping = c22_circuit_and_pin_mapping [c2]; - c22_pin_mapping.set_other (c2); - - for (db::Circuit::const_pin_iterator p = c1->begin_pins (); p != c1->end_pins (); ++p) { - - const db::Net *net = c1->net_for_pin (p->id ()); - if (! net) { - continue; - } - - const db::NetGraphNode &n = *(g1.begin () + g1.node_index_for_net (net)); - - if (! n.has_other ()) { - if (mp_logger) { - mp_logger->pin_mismatch (p.operator-> (), 0); + std::multimap net2pin; + for (db::Circuit::const_pin_iterator p = c2->begin_pins (); p != c2->end_pins (); ++p) { + const db::Net *net = c2->net_for_pin (p->id ()); + if (net) { + net2pin.insert (std::make_pair (g2.node_index_for_net (net), p.operator-> ())); } - pin_mismatch = true; - good = false; - continue; } - std::multimap::iterator np = net2pin.find (n.other_net_index ()); - for (db::Net::const_pin_iterator pi = net->begin_pins (); pi != net->end_pins (); ++pi) { + CircuitMapper &c12_pin_mapping = c12_circuit_and_pin_mapping [c1]; + c12_pin_mapping.set_other (c2); - if (np != net2pin.end () && np->first == n.other_net_index ()) { + // dummy mapping: we show this circuit is used. + CircuitMapper &c22_pin_mapping = c22_circuit_and_pin_mapping [c2]; + c22_pin_mapping.set_other (c2); + for (db::Circuit::const_pin_iterator p = c1->begin_pins (); p != c1->end_pins (); ++p) { + + const db::Net *net = c1->net_for_pin (p->id ()); + if (! net) { + continue; + } + + const db::NetGraphNode &n = *(g1.begin () + g1.node_index_for_net (net)); + + if (! n.has_other ()) { if (mp_logger) { - mp_logger->match_pins (pi->pin (), np->second); - } - c12_pin_mapping.map_pin (pi->pin ()->id (), np->second->id ()); - // dummy mapping: we show this pin is used. - c22_pin_mapping.map_pin (np->second->id (), np->second->id ()); - - std::multimap::iterator np_delete = np; - ++np; - net2pin.erase (np_delete); - - } else { - - if (mp_logger) { - mp_logger->pin_mismatch (pi->pin (), 0); + mp_logger->pin_mismatch (p.operator-> (), 0); } pin_mismatch = true; good = false; + continue; + } + + std::multimap::iterator np = net2pin.find (n.other_net_index ()); + for (db::Net::const_pin_iterator pi = net->begin_pins (); pi != net->end_pins (); ++pi) { + + if (np != net2pin.end () && np->first == n.other_net_index ()) { + + if (mp_logger) { + mp_logger->match_pins (pi->pin (), np->second); + } + c12_pin_mapping.map_pin (pi->pin ()->id (), np->second->id ()); + // dummy mapping: we show this pin is used. + c22_pin_mapping.map_pin (np->second->id (), np->second->id ()); + + std::multimap::iterator np_delete = np; + ++np; + net2pin.erase (np_delete); + + } else { + + if (mp_logger) { + mp_logger->pin_mismatch (pi->pin (), 0); + } + pin_mismatch = true; + good = false; + + } } } - } - - for (std::multimap::iterator np = net2pin.begin (); np != net2pin.end (); ++np) { - if (mp_logger) { - mp_logger->pin_mismatch (0, np->second); + for (std::multimap::iterator np = net2pin.begin (); np != net2pin.end (); ++np) { + if (mp_logger) { + mp_logger->pin_mismatch (0, np->second); + } + pin_mismatch = true; + good = false; } - pin_mismatch = true; - good = false; - } + } else { + + // skip pin mapping in case one circuit does not feature pins + // This is often the case for top-level circuits. We don't necessarily need pins for them. + // We still report those circuits with "pin mismatch" so they don't get considered within + // subcircuits. + + if (c1->pin_count () != c2->pin_count ()) { + pin_mismatch = true; + } + + } // Report device assignment diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 0df3f06ff..98792743f 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -2033,10 +2033,10 @@ TEST(16_UniqueSubCircuitMatching) "match_nets $I13 $I13\n" "match_nets $I23 $I23\n" "match_nets $I5 $I5\n" + "match_nets $I24 $I24\n" + "match_nets $I6 $I6\n" "match_nets $I7 $I7\n" "match_nets $I25 $I25\n" - "match_nets $I6 $I6\n" - "match_nets $I24 $I24\n" "match_subcircuits $1 $1\n" "match_subcircuits $4 $2\n" "match_subcircuits $3 $3\n" From 3ebdfa83f9a458ec0485a9b43cfe8c9ec414a55c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 14 Apr 2019 19:38:31 +0200 Subject: [PATCH 51/54] Netlist compare: successfully applied the netlist compare to a bigger example. --- src/db/db/dbNetlistCompare.cc | 38 +++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index ab1a45527..29092538d 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -29,7 +29,8 @@ #include "tlLog.h" // verbose debug output -#define PRINT_DEBUG_NETCOMPARE +// TODO: make this a feature? +// #define PRINT_DEBUG_NETCOMPARE namespace db { @@ -1230,11 +1231,40 @@ NetDeviceGraph::derive_node_identities_from_node_set (const std::vector::const_iterator nr = node_ranges.begin (); nr != node_ranges.end (); ++nr) { + for (std::vector::iterator nr = node_ranges.begin (); nr != node_ranges.end (); ++nr) { - // @@@ node ranges might have changed - adjust to real count and skip leading pairs assigned already + // node ranges might have changed - adjust to real count and skip leading pairs assigned already - if (nr->num == 1) { + while (nr->n1 != nr->nn1 && nr->n2 != nr->nn2) { + if ((*nr->n1)->has_other ()) { + ++nr->n1; + } else if ((*nr->n2)->has_other ()) { + ++nr->n2; + } else { + break; + } + } + + nr->num = 0; + std::vector::const_iterator i1 = nr->n1, i2 = nr->n2; + + while (i1 != nr->nn1 && i2 != nr->nn2) { + if ((*i1)->has_other ()) { + ++i1; + } else if ((*i2)->has_other ()) { + ++i2; + } else { + ++nr->num; + ++i1; + ++i2; + } + } + + if (nr->num < 1) { + + // ignore this - it got obsolete. + + } else if (nr->num == 1) { if (! (*nr->n1)->has_other () && ! (*nr->n2)->has_other ()) { From eabf5581865040e6d635262b9a240f6ccc59c71d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 15 Apr 2019 23:24:27 +0200 Subject: [PATCH 52/54] netlist exaction: selective net joining with labels Now, a glob pattern can be used to identify the labels which implicitly join nets. Also, net joining now only happens on top level. --- src/db/db/dbCircuit.cc | 5 +- src/db/db/dbDeviceClass.cc | 7 ++ src/db/db/dbDeviceClass.h | 26 ++++++- src/db/db/dbHierNetworkProcessor.cc | 2 +- src/db/db/dbLayoutToNetlist.cc | 4 +- src/db/db/dbLayoutToNetlist.h | 2 +- src/db/db/dbNetlistDeviceClasses.cc | 8 +-- src/db/db/dbNetlistExtractor.cc | 14 ++-- src/db/db/dbNetlistExtractor.h | 2 +- src/db/db/gsiDeclDbLayoutToNetlist.cc | 19 +++++- src/db/db/gsiDeclDbNetlist.cc | 9 +++ src/db/unit_tests/dbNetlistCompareTests.cc | 17 ++++- src/db/unit_tests/dbNetlistExtractorTests.cc | 14 +++- src/db/unit_tests/dbNetlistTests.cc | 4 +- src/drc/drc/built-in-macros/drc.lym | 37 +++++++++- src/drc/unit_tests/drcSimpleTests.cc | 38 +++++++++++ testdata/drc/drcSimpleTests_12.drc | 66 ++++++++++++++++++ testdata/drc/drcSimpleTests_au12a.cir | 68 +++++++++++++++++++ testdata/drc/implicit_nets.gds | Bin 0 -> 3276 bytes testdata/ruby/dbNetlist.rb | 4 ++ 20 files changed, 316 insertions(+), 30 deletions(-) create mode 100644 testdata/drc/drcSimpleTests_12.drc create mode 100644 testdata/drc/drcSimpleTests_au12a.cir create mode 100644 testdata/drc/implicit_nets.gds diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index 54c6f909a..2b5b6a09d 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -30,7 +30,7 @@ namespace db // Circuit class implementation Circuit::Circuit () - : mp_netlist (0), + : m_cell_index (0), mp_netlist (0), m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices), m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets), @@ -45,7 +45,7 @@ Circuit::Circuit () } Circuit::Circuit (const Circuit &other) - : gsi::ObjectBase (other), tl::Object (other), mp_netlist (0), + : gsi::ObjectBase (other), tl::Object (other), m_cell_index (0), mp_netlist (0), m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices), m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets), @@ -80,6 +80,7 @@ Circuit &Circuit::operator= (const Circuit &other) clear (); m_name = other.m_name; + m_cell_index = other.m_cell_index; m_pins = other.m_pins; std::map device_table; diff --git a/src/db/db/dbDeviceClass.cc b/src/db/db/dbDeviceClass.cc index 0d5401cb6..b42ddfcd0 100644 --- a/src/db/db/dbDeviceClass.cc +++ b/src/db/db/dbDeviceClass.cc @@ -118,6 +118,7 @@ DeviceClass &DeviceClass::operator= (const DeviceClass &other) m_parameter_definitions = other.m_parameter_definitions; m_name = other.m_name; m_description = other.m_description; + mp_pc_delegate.reset (const_cast (other.mp_pc_delegate.get ())); } return *this; } @@ -228,6 +229,9 @@ bool DeviceClass::less (const db::Device &a, const db::Device &b) const std::vector &pd = a.device_class ()->parameter_definitions (); for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (! p->is_primary ()) { + continue; + } int cmp = compare_parameters (a.parameter_value (p->id ()), b.parameter_value (p->id ()), 0.0, relative_tolerance); if (cmp != 0) { return cmp < 0; @@ -255,6 +259,9 @@ bool DeviceClass::equal (const db::Device &a, const db::Device &b) const std::vector &pd = a.device_class ()->parameter_definitions (); for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (! p->is_primary ()) { + continue; + } int cmp = compare_parameters (a.parameter_value (p->id ()), b.parameter_value (p->id ()), 0.0, relative_tolerance); if (cmp != 0) { return false; diff --git a/src/db/db/dbDeviceClass.h b/src/db/db/dbDeviceClass.h index bece69f4d..6b0388a79 100644 --- a/src/db/db/dbDeviceClass.h +++ b/src/db/db/dbDeviceClass.h @@ -124,7 +124,7 @@ public: * @brief Creates an empty device parameter definition */ DeviceParameterDefinition () - : m_name (), m_description (), m_default_value (0.0), m_id (0) + : m_name (), m_description (), m_default_value (0.0), m_id (0), m_is_primary (true) { // .. nothing yet .. } @@ -132,8 +132,8 @@ public: /** * @brief Creates a device parameter definition with the given name and description */ - DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0) - : m_name (name), m_description (description), m_default_value (default_value), m_id (0) + DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0, bool is_primary = true) + : m_name (name), m_description (description), m_default_value (default_value), m_id (0), m_is_primary (is_primary) { // .. nothing yet .. } @@ -186,6 +186,25 @@ public: m_default_value = d; } + /** + * @brief Sets a value indicating whether the parameter is a primary parameter + * + * If this flag is set to true (the default), the parameter is considered a primary parameter. + * Only primary parameters are compared by default. + */ + void set_is_primary (bool p) + { + m_is_primary = p; + } + + /** + * @brief Gets a value indicating whether the parameter is a primary parameter + */ + bool is_primary () const + { + return m_is_primary; + } + /** * @brief Gets the parameter ID */ @@ -200,6 +219,7 @@ private: std::string m_name, m_description; double m_default_value; size_t m_id; + bool m_is_primary; void set_id (size_t id) { diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index a1016b750..593f031c2 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1769,7 +1769,7 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou tl::RelativeProgress progress (tl::to_string (tr ("Computing local clusters")), called.size (), 1); for (std::set::const_iterator c = called.begin (); c != called.end (); ++c) { - build_local_cluster (layout, layout.cell (*c), shape_flags, conn, attr_equivalence); + build_local_cluster (layout, layout.cell (*c), shape_flags, conn, *c == cell.cell_index () ? attr_equivalence : 0); ++progress; } } diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 6b610fefa..b9bb376ca 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -245,7 +245,7 @@ size_t LayoutToNetlist::global_net_id (const std::string &name) return m_conn.global_net_id (name); } -void LayoutToNetlist::extract_netlist (bool join_nets_by_label) +void LayoutToNetlist::extract_netlist (const std::string &joined_net_names) { if (m_netlist_extracted) { throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); @@ -255,7 +255,7 @@ void LayoutToNetlist::extract_netlist (bool join_nets_by_label) } db::NetlistExtractor netex; - netex.extract_nets (dss (), m_layout_index, m_conn, *mp_netlist, m_net_clusters, join_nets_by_label); + netex.extract_nets (dss (), m_layout_index, m_conn, *mp_netlist, m_net_clusters, joined_net_names); m_netlist_extracted = true; } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index e3f9436fe..5003deb03 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -296,7 +296,7 @@ public: * @brief Runs the netlist extraction * See the class description for more details. */ - void extract_netlist (bool join_nets_by_label = true); + void extract_netlist (const std::string &joined_net_names = std::string ()); /** * @brief Marks the netlist as extracted diff --git a/src/db/db/dbNetlistDeviceClasses.cc b/src/db/db/dbNetlistDeviceClasses.cc index 05a1a59c8..1a15a0df3 100644 --- a/src/db/db/dbNetlistDeviceClasses.cc +++ b/src/db/db/dbNetlistDeviceClasses.cc @@ -214,10 +214,10 @@ DeviceClassMOS3Transistor::DeviceClassMOS3Transistor () add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0)); add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0)); - add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0)); - add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0)); - add_parameter_definition (db::DeviceParameterDefinition ("PS", "Source perimeter (micrometer)", 0.0)); - add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0)); + add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0, false)); + add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0, false)); + add_parameter_definition (db::DeviceParameterDefinition ("PS", "Source perimeter (micrometer)", 0.0, false)); + add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0, false)); } bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 40cd6439f..4ae5449da 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -23,6 +23,7 @@ #include "dbNetlistExtractor.h" #include "dbDeepShapeStore.h" #include "dbNetlistDeviceExtractor.h" +#include "tlGlobPattern.h" namespace db { @@ -34,15 +35,18 @@ NetlistExtractor::NetlistExtractor () } static void -build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type net_name_id, tl::equivalence_clusters &eq) +build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type net_name_id, const std::string &joined_net_names, tl::equivalence_clusters &eq) { std::map > prop_by_name; + tl::GlobPattern jn_pattern (joined_net_names); for (db::PropertiesRepository::iterator i = layout->properties_repository ().begin (); i != layout->properties_repository ().end (); ++i) { for (db::PropertiesRepository::properties_set::const_iterator p = i->second.begin (); p != i->second.end (); ++p) { if (p->first == net_name_id) { std::string nn = p->second.to_string (); - prop_by_name [nn].insert (i->first); + if (jn_pattern.match (nn)) { + prop_by_name [nn].insert (i->first); + } } } } @@ -58,7 +62,7 @@ build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type } void -NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, bool join_nets_by_label) +NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, const std::string &joined_net_names) { mp_clusters = &clusters; mp_layout = &dss.const_layout (layout_index); @@ -77,8 +81,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo // the big part: actually extract the nets tl::equivalence_clusters net_name_equivalence; - if (m_text_annot_name_id.first && join_nets_by_label) { - build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, net_name_equivalence); + if (m_text_annot_name_id.first && ! joined_net_names.empty ()) { + build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, joined_net_names, net_name_equivalence); } mp_clusters->build (*mp_layout, *mp_cell, db::ShapeIterator::Polygons, conn, &net_name_equivalence); diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h index edbe6a1c6..6eba650f2 100644 --- a/src/db/db/dbNetlistExtractor.h +++ b/src/db/db/dbNetlistExtractor.h @@ -82,7 +82,7 @@ public: * @brief Extract the nets * See the class description for more details. */ - void extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, bool join_nets_by_label = true); + void extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, const std::string &joined_net_names = std::string ()); private: hier_clusters_type *mp_clusters; diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 78860e07c..070980970 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -288,10 +288,23 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::method ("global_net_name", &db::LayoutToNetlist::global_net_name, gsi::arg ("global_net_id"), "@brief Gets the global net name for the given global net ID." ) + - gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, gsi::arg ("join_nets_by_label", true), + gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, gsi::arg ("join_net_names", std::string ()), "@brief Runs the netlist extraction\n" - "If join_nets_by_label is true, nets on the same hierarchy level carrying the same label will be connected " - "implicitly even if there is no physical connection.\n" + "'join_net_names' is a glob expression for labels. Nets on top level carrying the same label which matches this glob " + "expression will be connected implicitly even if there is no physical connection. This feature is useful to simulate a connection " + "which will be made later when integrating the component.\n" + "\n" + "Valid glob expressions are:\n" + "@ul\n" + "@li \"\" no implicit connections.@/li\n" + "@li \"*\" to make all labels candidates for implicit connections.@/li\n" + "@li \"VDD\" to make all 'VDD'' nets candidates for implicit connections.@/li\n" + "@li \"VDD\" to make all 'VDD'+suffix nets candidates for implicit connections.@/li\n" + "@li \"{VDD,VSS}\" to all VDD and VSS nets candidates for implicit connections.@/li\n" + "@/ul\n" + "\n" + "Label matching is case sensitive.\n" + "\n" "See the class description for more details.\n" ) + gsi::method_ext ("internal_layout", &l2n_internal_layout, diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index e65c03e04..244f87168 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -462,6 +462,15 @@ Class decl_dbDeviceParameterDefinition ("db", "De "@brief Sets the default value of the parameter.\n" "The default value is used to initialize parameters of \\Device objects." ) + + gsi::method ("is_primary?", &db::DeviceParameterDefinition::is_primary, + "@brief Gets a value indicating whether the parameter is a primary parameter\n" + "See \\is_primary= for details about this predicate." + ) + + gsi::method ("is_primary=", &db::DeviceParameterDefinition::set_is_primary, gsi::arg ("primary"), + "@brief Sets a value indicating whether the parameter is a primary parameter\n" + "If this flag is set to true (the default), the parameter is considered a primary parameter.\n" + "Only primary parameters are compared by default.\n" + ) + gsi::method ("id", &db::DeviceParameterDefinition::id, "@brief Gets the ID of the parameter.\n" "The ID of the parameter is used in some places to refer to a specific parameter (e.g. in " diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 98792743f..289bec5c5 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -228,6 +228,17 @@ TEST(0_EqualDeviceParameters) EXPECT_EQ (dc.less (d1, d2), false); EXPECT_EQ (dc.less (d2, d1), false); + // AD, AS, PD and PS aren't a primary parameter, so we don't compare it. + d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 1.0); + d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 1.0); + d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 1.0); + d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 1.0); + + EXPECT_EQ (dc.equal (d1, d2), true); + EXPECT_EQ (dc.equal (d2, d1), true); + EXPECT_EQ (dc.less (d1, d2), false); + EXPECT_EQ (dc.less (d2, d1), false); + d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 41.0); EXPECT_EQ (dc.equal (d1, d2), false); @@ -1837,10 +1848,10 @@ TEST(15_EmptySubCircuitTest) "end_circuit TRANS TRANS MATCH\n" "begin_circuit INV2 INV2\n" "match_nets $5 $5\n" - "match_nets OUT OUT\n" "match_nets $2 $2\n" "match_nets IN IN\n" "match_nets $4 $4\n" + "match_nets OUT OUT\n" "match_pins IN IN\n" "match_pins $1 $1\n" "match_pins OUT OUT\n" @@ -1912,10 +1923,10 @@ TEST(15_EmptySubCircuitWithoutPinNames) "end_circuit TRANS TRANS MATCH\n" "begin_circuit INV2 INV2\n" "match_nets $5 $5\n" - "match_nets OUT OUT\n" "match_nets $2 $2\n" "match_nets IN IN\n" "match_nets $4 $4\n" + "match_nets OUT OUT\n" "match_pins IN IN\n" "match_pins $1 $1\n" "match_pins OUT OUT\n" @@ -1988,8 +1999,8 @@ TEST(16_UniqueSubCircuitMatching) "match_nets VDD VDD\n" "match_nets OUT OUT\n" "match_nets $3 $3\n" - "match_nets $1 $1\n" "match_nets IN IN\n" + "match_nets $1 $1\n" "match_nets VSS VSS\n" "match_nets BULK BULK\n" "match_pins $0 $0\n" diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index f3a8cd1f7..b9e67104d 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -483,7 +483,7 @@ TEST(2_DeviceAndNetExtractionFlat) // extract the nets // don't use "join_nets_by_label" because the flattened texts will spoil everything - net_ex.extract_nets (dss, 0, conn, nl, cl, false); + net_ex.extract_nets (dss, 0, conn, nl, cl); // debug layers produced for nets // 202/0 -> Active @@ -716,7 +716,17 @@ TEST(3_DeviceAndNetExtractionWithImplicitConnections) // extract the nets - net_ex.extract_nets (dss, 0, conn, nl, cl); + db::Netlist nl2 = nl; + net_ex.extract_nets (dss, 0, conn, nl2, cl, "{VDDZ,VSSZ,NEXT,FB}"); + + EXPECT_EQ (all_net_names_unique (nl2), true); + + nl2 = nl; + net_ex.extract_nets (dss, 0, conn, nl2, cl, "{VDDZ,VSSZ,NEXT}"); + + EXPECT_EQ (all_net_names_unique (nl2), false); + + net_ex.extract_nets (dss, 0, conn, nl, cl, "*"); EXPECT_EQ (all_net_names_unique (nl), true); diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 3ab084247..4df092672 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -259,11 +259,13 @@ TEST(1_DeviceTerminalDefinition) dc.clear_terminal_definitions (); EXPECT_EQ (dc.terminal_definitions ().empty (), true); - db::DeviceParameterDefinition ppd ("P1", "Parameter 1", 1.0); + db::DeviceParameterDefinition ppd ("P1", "Parameter 1", 1.0, false); dc.add_parameter_definition (ppd); + EXPECT_EQ (ppd.is_primary (), false); db::DeviceParameterDefinition ppd2 ("P2", "Parameter 2"); dc.add_parameter_definition (ppd2); + EXPECT_EQ (ppd.is_primary (), true); EXPECT_EQ (pd2string (dc.parameter_definitions ()[0]), "P1(Parameter 1)=1 #0"); EXPECT_EQ (pd2string (dc.parameter_definitions ()[1]), "P2(Parameter 2)=0 #1"); diff --git a/src/drc/drc/built-in-macros/drc.lym b/src/drc/drc/built-in-macros/drc.lym index bb82ea4f4..e869a76c4 100644 --- a/src/drc/drc/built-in-macros/drc.lym +++ b/src/drc/drc/built-in-macros/drc.lym @@ -3804,9 +3804,36 @@ CODE @connections = [] @global_connections = [] @layers = {} + @join_nets = "" modified end + # %DRC% + # @name join_nets + # @brief Specifies a search pattern for labels which create implicit net connections + # @synopsis join_nets(label_pattern) + # Use this method to supply a glob pattern for labels which create implicit net connections + # on the top level circuit. This feature is useful to connect identically labelled nets + # while a component isn't integrated yet. If the component is integrated, net may be connected + # on a higher hierarchy level - e.g. by a power mesh. Inside the component this net consists + # of individual islands. To properly perform netlist extraction and comparison, these islands + # need to be connected even though there isn't a physical connection. "join_nets" can + # achive this if these islands are labelled with the same text on the top level of the + # component. + # + # Glob pattern are used which resemble shell file pattern: "*" is for all labels, "VDD" + # for all "VDD" labels (pattern act case sensitive). "VDD*" is for all labels beginning + # with "VDD" (still different labels will be connected to different nets!). "{VDD,VSS}" + # is either "VDD" or "VSS". + # + # The search pattern is applied on the next net extraction. The search pattern is cleared + # on "clear_connections". + + def join_nets(arg) + @join_nets = arg + modified + end + # %DRC% # @brief Performs an antenna check # @name antenna_check @@ -3957,7 +3984,7 @@ CODE @global_connections.each { |l,n| @l2n.connect_global(@layers[l], n) } # run extraction in a timed environment - @engine._cmd(@l2n, :extract_netlist) + @engine._cmd(@l2n, :extract_netlist, @join_nets) @l2n end @@ -4922,6 +4949,12 @@ CODE # @synopsis clear_connections # See \Netter#clear_connections for a description of that function + # %DRC% + # @name join_nets + # @brief Specifies a label pattern for implicit net connections + # @synopsis join_nets(label_pattern) + # See \Netter#join_nets for a description of that function + # %DRC% # @name antenna_check # @brief Performs an antenna check @@ -4940,7 +4973,7 @@ CODE # @synopsis extract_devices(extractor, layer_hash) # See \Netter#extract_devices for a description of that function - %w(connect connect_global clear_connections antenna_check l2n_data extract_devices).each do |f| + %w(connect connect_global clear_connections join_nets antenna_check l2n_data extract_devices).each do |f| eval <<"CODE" def #{f}(*args) _netter.#{f}(*args) diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 6d27bd9ae..c3a0a68c5 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -352,11 +352,13 @@ static void compare_netlists (tl::TestBase *_this, const std::string &cir, const db::NetlistSpiceReader reader; { + tl::info << "Output: " << cir; tl::InputStream is (cir); reader.read (is, nl); } { + tl::info << "Golden: " << cir_au; tl::InputStream is (cir_au); reader.read (is, nl_au); } @@ -495,3 +497,39 @@ TEST(11_CustomDevices) CHECKPOINT (); compare_netlists (_this, output_simplified, au_simplified); } + +TEST(12_NetlistJoinLabels) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_12.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/implicit_nets.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au12a.cir"; + + std::string output = this->tmp_file ("tmp.cir"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + "$drc_test_target_simplified = nil\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + // verify + + CHECKPOINT (); + compare_netlists (_this, output, au); +} diff --git a/testdata/drc/drcSimpleTests_12.drc b/testdata/drc/drcSimpleTests_12.drc new file mode 100644 index 000000000..0792169a5 --- /dev/null +++ b/testdata/drc/drcSimpleTests_12.drc @@ -0,0 +1,66 @@ +# Flat extraction + +source($drc_test_source) + +deep + +# Drawing layers + +nwell = input(1, 0) +active = input(2, 0) +poly = input(3, 0) +poly_lbl = input(3, 1) +diff_cont = input(4, 0) +poly_cont = input(5, 0) +metal1 = input(6, 0) +metal1_lbl = input(6, 1) +via1 = input(7, 0) +metal2 = input(8, 0) +metal2_lbl = input(8, 1) + +# Computed layers + +pactive = active & nwell +pgate = active & poly +psd = active - pgate + +nactive = active - nwell +ngate = nactive & poly +nsd = nactive - ngate + +# PMOS transistor device extraction + +pmos_ex = RBA::DeviceExtractorMOS3Transistor::new("PMOS") +extract_devices(pmos_ex, { "SD" => psd, "G" => pgate, "P" => poly }) + +# NMOS transistor device extraction + +nmos_ex = RBA::DeviceExtractorMOS3Transistor::new("NMOS") +extract_devices(nmos_ex, { "SD" => nsd, "G" => ngate, "P" => poly }) + +# Define connectivity for netlist extraction + +# Inter-layer +connect(psd, diff_cont) +connect(nsd, diff_cont) +connect(poly, poly_cont) +connect(poly_cont, metal1) +connect(diff_cont, metal1) +connect(metal1, via1) +connect(via1, metal2) +connect(poly, poly_lbl) +connect(metal1, metal1_lbl) +connect(metal2, metal2_lbl) + +# Actually performs the extraction + +join_nets("{VDDZ,VSSZ,NEXT,FB}") + +netlist = l2n_data.netlist + +# Writes the netlist + +writer = RBA::NetlistSpiceWriter::new + +netlist.write($drc_test_target, writer, "RINGO netlist") + diff --git a/testdata/drc/drcSimpleTests_au12a.cir b/testdata/drc/drcSimpleTests_au12a.cir new file mode 100644 index 000000000..b459b2a77 --- /dev/null +++ b/testdata/drc/drcSimpleTests_au12a.cir @@ -0,0 +1,68 @@ +* RINGO netlist + +* cell RINGO +.SUBCKT RINGO +* net 1 FB +* net 2 OSC +* net 3 NEXT +* net 4 VSSZ,VSS +* net 5 VDDZ,VDD +* cell instance $1 r180 *1 -0.24,9.18 +X$1 16 1 2 4 5 INV2 +* cell instance $2 r0 *1 0,0 +X$2 1 14 15 4 5 INV2 +* cell instance $3 r180 *1 10.32,9.18 +X$3 3 9 19 4 5 INV2 +* cell instance $4 r0 *1 10.56,0 +X$4 20 10 3 4 5 INV2 +* cell instance $5 r180 *1 7.68,9.18 +X$5 19 8 18 4 5 INV2 +* cell instance $6 r180 *1 5.04,9.18 +X$6 18 7 17 4 5 INV2 +* cell instance $7 r180 *1 2.4,9.18 +X$7 17 6 16 4 5 INV2 +* cell instance $8 r0 *1 2.64,0 +X$8 15 13 22 4 5 INV2 +* cell instance $9 r0 *1 5.28,0 +X$9 22 12 21 4 5 INV2 +* cell instance $10 r0 *1 7.92,0 +X$10 21 11 20 4 5 INV2 +.ENDS RINGO + +* cell INV2 +* pin IN +* pin +* pin OUT +* pin +* pin +.SUBCKT INV2 1 2 3 4 5 +* net 1 IN +* net 3 OUT +* cell instance $1 r0 *1 -0.4,0 +X$1 2 4 1 TRANS +* cell instance $2 r0 *1 -0.4,2.8 +X$2 2 5 1 TRANS +* cell instance $3 m0 *1 0.4,2.8 +X$3 5 3 2 TRANS +* cell instance $4 m0 *1 0.4,0 +X$4 4 3 2 TRANS +* device instance $1 -0.4,0 PMOS +M$1 2 1 4 2 PMOS L=0.25U W=0.95U AS=0.49875P AD=0.26125P PS=2.95U PD=1.5U +* device instance $2 0.4,0 PMOS +M$2 4 2 3 4 PMOS L=0.25U W=0.95U AS=0.26125P AD=0.49875P PS=1.5U PD=2.95U +* device instance $3 -0.4,2.8 PMOS +M$3 2 1 5 2 PMOS L=0.25U W=0.95U AS=0.49875P AD=0.26125P PS=2.95U PD=1.5U +* device instance $4 0.4,2.8 PMOS +M$4 5 2 3 5 PMOS L=0.25U W=0.95U AS=0.26125P AD=0.49875P PS=1.5U PD=2.95U +* device instance $5 -0.4,0 NMOS +M$5 2 1 4 2 NMOS L=0.25U W=0.95U AS=0.49875P AD=0.26125P PS=2.95U PD=1.5U +* device instance $6 0.4,0 NMOS +M$6 4 2 3 4 NMOS L=0.25U W=0.95U AS=0.26125P AD=0.49875P PS=1.5U PD=2.95U +.ENDS INV2 + +* cell TRANS +* pin +* pin +* pin +.SUBCKT TRANS 1 2 3 +.ENDS TRANS diff --git a/testdata/drc/implicit_nets.gds b/testdata/drc/implicit_nets.gds new file mode 100644 index 0000000000000000000000000000000000000000..0b8e3faf949ab06c07f009651db362b27729fa00 GIT binary patch literal 3276 zcmbtWOK2295Uriv?fqocs8NX~!A}$gjUaxIfM|?Lh$Nbk9 zF5zUpl0!*aM0r{h{!dxQ>~Cu!s!67I?7F_K{oSMS?X4pl2k-S2sV*JO%NtYd+SSob zl*$sBIS%$Wa~&dDU!8Q_sWYxyUd>39_(bH~P*gi3rRE3xRG43Ous(_}&>aGP2~h3j z(KtRb>0>_PZe@OeYNx&^K73b#Gb-o~0m~d;Q0>w2naA-36I+>wua9c4h;PE@pL&h> zS5Uv#;GIY8Mn$zVTKR|gi2d1H2Qm-!Z&g%#z|Tqll74)%g1eZVm&9+0YN{E@b$^aA z5+#>Q)C|SWN7k6Oj-E}F$`j=m=&TfYHp-{GjlAAZznUMH7}ly;nz%%S9dMG{5j#}) z=sx}lwL+m2CQk6YdZ!|Kk6}EDoqfz;uyI;a%=Jgr8`W8la~)wM%JrjfcNNvn7{eb! ztj$v8)P3ofqS}+2Exz_k?%M=r#31ejTF3Y1yQ10|qxh)kLT??&Jj9=)sP=e1&l&Rb z=1ucfRC`?hKaOAsi>}L<;n~NGD5~8Qt^Bj$`%=0F&JNctUBg-45T(y5s+}>8kA5`? zzwk`>ifYd_S^R_@pXWsQb3Y1SQSF$cu->HuK5|`&`kfO@^#Gw4ifRW&@KLu1sNaBK z2HIP?hvF^)v~;x8S3?04YIYS^H32D z;-}XPI*8|FS6j!vPIyAivI|5NJrzTi{81gW*766U*J=i%*BZv7*VgTvb`6>dd!a7f z^1@s9HFhmBSoQgj>>|7yK%Kq!!l!@ZojW>vZ%jaS;0*!iHxt!;Pbn*vru0^nhOts< z{7I1+!19s=;nqsMmZf*DIqrRAf=YNo%LMIhnP!4w+0ffwqK30PfB(YXFo-dFN|?)z zJ-xn4T2nql^rk0xn18eRe(fxkom>mQg6lHY-^Z;P2~h2I1?%+S z-Tqsdpa1OmsC_MN7x=BM|G=NbtXCEPk59+!{Qde_D*NLO3UL!E4a{7{xo-uic5_qe z_a`5U`W*E?ELeXKQ}Y=0_fhS@@KeHyKY5z@=pV5DAo_PQK(#C1sz07})2Re@8pn Date: Tue, 16 Apr 2019 00:09:41 +0200 Subject: [PATCH 53/54] Fixed a segfault in an application test. --- src/lay/lay/layMacroController.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lay/lay/layMacroController.cc b/src/lay/lay/layMacroController.cc index 202e91756..28dde80c9 100644 --- a/src/lay/lay/layMacroController.cc +++ b/src/lay/lay/layMacroController.cc @@ -834,10 +834,12 @@ add_collections_to_file_watcher (const lym::MacroCollection &collection, tl::Fil void MacroController::sync_file_watcher () { - m_file_watcher->clear (); - m_file_watcher->enable (false); - add_collections_to_file_watcher (lym::MacroCollection::root (), m_file_watcher); - m_file_watcher->enable (true); + if (m_file_watcher) { + m_file_watcher->clear (); + m_file_watcher->enable (false); + add_collections_to_file_watcher (lym::MacroCollection::root (), m_file_watcher); + m_file_watcher->enable (true); + } } void From 197d99ab629b4594d93f2ef7a20845da521d6728 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 16 Apr 2019 07:10:34 +0200 Subject: [PATCH 54/54] Unit test fixed. --- src/db/unit_tests/dbNetlistTests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 4df092672..ed1ba213f 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -265,7 +265,7 @@ TEST(1_DeviceTerminalDefinition) db::DeviceParameterDefinition ppd2 ("P2", "Parameter 2"); dc.add_parameter_definition (ppd2); - EXPECT_EQ (ppd.is_primary (), true); + EXPECT_EQ (ppd2.is_primary (), true); EXPECT_EQ (pd2string (dc.parameter_definitions ()[0]), "P1(Parameter 1)=1 #0"); EXPECT_EQ (pd2string (dc.parameter_definitions ()[1]), "P2(Parameter 2)=0 #1");