diff --git a/src/db/db/db.pro b/src/db/db/db.pro index fd68bd081..1556c253a 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -172,7 +172,11 @@ SOURCES = \ dbDeepEdgePairs.cc \ dbRegionUtils.cc \ dbEdgesUtils.cc \ - dbRegionProcessors.cc + dbRegionProcessors.cc \ + dbNetlistCompare.cc \ + dbNetlistReader.cc \ + dbNetlistSpiceReader.cc \ + gsiDeclDbNetlistCompare.cc HEADERS = \ dbArray.h \ @@ -310,7 +314,10 @@ HEADERS = \ dbRegionUtils.h \ dbEdgesUtils.h \ dbRegionProcessors.h \ - gsiDeclDbHelpers.h + gsiDeclDbHelpers.h \ + dbNetlistCompare.h \ + dbNetlistReader.h \ + dbNetlistSpiceReader.h !equals(HAVE_QT, "0") { diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index d1d193158..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; @@ -246,6 +247,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)); @@ -312,6 +318,89 @@ void Circuit::unregister_ref (SubCircuit *r) m_refs.erase (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 + + 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); + } + + } + + } + + delete 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 276ac520b..042a3e144 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) @@ -585,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/dbDeviceClass.cc b/src/db/db/dbDeviceClass.cc index e21ff80ea..b42ddfcd0 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 @@ -44,8 +115,10 @@ 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; + mp_pc_delegate.reset (const_cast (other.mp_pc_delegate.get ())); } return *this; } @@ -136,4 +209,68 @@ 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); + 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) { + 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; + } + } + + 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) { + 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; + } + } + + return true; + + } +} + } diff --git a/src/db/db/dbDeviceClass.h b/src/db/db/dbDeviceClass.h index 8c4b34aca..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) { @@ -207,6 +227,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 +295,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 +432,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 { @@ -403,6 +470,57 @@ 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; + } + + /** + * @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; @@ -410,6 +528,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/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 45c2623e0..593f031c2 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) @@ -1762,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/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index 0bcd6595a..5d14a904a 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/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index a0be5cfcd..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; } @@ -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/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/dbNetlist.cc b/src/db/db/dbNetlist.cc index 6cc04ea90..81eada167 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -369,6 +369,32 @@ void Netlist::remove_circuit (Circuit *circuit) m_circuits.erase (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-> ()); + } + + for (std::vector::const_iterator r = refs.begin (); r != refs.end (); ++r) { + (*r)->circuit ()->flatten_subcircuit (*r); + } + + delete 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); @@ -499,13 +525,7 @@ std::string Netlist::to_string () const 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 + 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; @@ -524,7 +544,7 @@ std::string Netlist::to_string () const } ps += p->name () + "=" + tl::to_string (d->parameter_value (p->id ())); } - res += std::string (" D") + d->device_class ()->name () + " " + device2string (*d) + " (" + ts + ") [" + ps + "]\n"; + 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) { @@ -535,15 +555,354 @@ std::string Netlist::to_string () const if (p != circuit->begin_pins ()) { ps += ","; } - const db::Pin &pin = *p; - ps += pin2string (pin) + "=" + net2string (subcircuit.net_for_pin (pin.id ())); + ps += pin2string (*p) + "=" + net2string (subcircuit.net_for_pin (p->id ())); } - res += std::string (" X") + circuit->name () + " " + subcircuit2string (*sc) + " (" + ps + ")\n"; + 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) +{ + 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 (); + + 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); + + if (circuit->pin_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 ())); + } + + ex.test (","); + + } + + 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"))); + } +} + +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) +{ + if (! ex.test ("(")) { + return; + } + + 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::Circuit *circuit, db::SubCircuit *subcircuit, std::map &n2n) +{ + db::Circuit *circuit_ref = subcircuit->circuit_ref (); + db::Circuit::pin_iterator pi = circuit_ref->begin_pins (); + + ex.expect ("("); + while (! ex.test (")")) { + + std::string pn; + if (ex.test ("$")) { + size_t i; + ex.read (i); + } else { + ex.read_word_or_quoted (pn); + } + + ex.expect ("="); + + if (pi == circuit_ref->end_pins ()) { + // add a dummy pin + circuit_ref->add_pin (pn); + pi = circuit_ref->end_pins (); + --pi; + } else if (! pi->name ().empty () && pi->name () != pn) { + ex.error (tl::to_string (tr ("Expected pin with name: ")) + pi->name ()); + } + + 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_ref->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, circuit, 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..95555d29c 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -88,12 +88,20 @@ public: void clear (); /** - * @brief Returns a string representation of the netlist + * @brief Returns a parsable string representation of the netlist * - * This method is basically intended to testing. + * This method returns a string suitable for being put into from_string. */ std::string to_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 * @@ -130,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) */ @@ -273,6 +287,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/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc new file mode 100644 index 000000000..29092538d --- /dev/null +++ b/src/db/db/dbNetlistCompare.cc @@ -0,0 +1,2149 @@ + + +/* + + 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 "dbNetlistDeviceClasses.h" +#include "tlProgress.h" +#include "tlTimer.h" +#include "tlEquivalenceClusters.h" +#include "tlLog.h" + +// verbose debug output +// TODO: make this a feature? +// #define PRINT_DEBUG_NETCOMPARE + +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; + } + return db::DeviceClass::less (*d1.first, *d2.first); + } + + bool equals (const std::pair &d1, const std::pair &d2) const + { + if (d1.second != d2.second) { + return false; + } + return db::DeviceClass::equal (*d1.first, *d2.first); + } +}; + +// -------------------------------------------------------------------------------------------------------------------- +// 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].same (pin1_id, pin2_id); + } + + void map_pins (const db::Circuit *circuit, const std::vector &pin_ids) + { + if (pin_ids.size () < 2) { + return; + } + + tl::equivalence_clusters &pm = m_pin_map [circuit]; + for (size_t i = 1; i < pin_ids.size (); ++i) { + pm.same (pin_ids [0], pin_ids [i]); + } + } + + 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); + if (pm != m_pin_map.end ()) { + 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; +}; + +// -------------------------------------------------------------------------------------------------------------------- +// 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)); + } + + 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); + 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; +}; + +// -------------------------------------------------------------------------------------------------------------------- +// 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 + +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; + } + + 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; + } + + 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 NetGraphNode +{ +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 (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); + } + + 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 (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; + } + + 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; + + inline bool is_for_subcircuit () const + { + return m_id1 > std::numeric_limits::max () / 2; + } + + 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; + + 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) { + return; + } + + 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 (); + + size_t this_pin_id = pin_id; + + 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; + } + + const CircuitMapper *cm = & icm->second; + + // 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; + } + + // 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); + + // 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. + + size_t pin_count = cr->pin_count (); + + // 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. + + 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) { + 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; + } + } + + 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)); + + 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 (); + 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); + + 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 unset_other_net () + { + m_other_net_index = std::numeric_limits::max (); + } + + 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) { + 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 NetGraphNode &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; + } + } + if (m_edges.empty ()) { + // do a more detailed analysis on the edges + return edge_less (net (), node.net ()); + } + return false; + } + + bool operator== (const NetGraphNode &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; + } + } + if (m_edges.empty ()) { + // do a more detailed analysis on the edges + return edge_equal (net (), node.net ()); + } + return true; + } + + void swap (NetGraphNode &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, NetGraphNode::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; + + /** + * @brief Compares edges as "less" + * 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) + { + 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 false; + } 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 true; + } else { + return true; + } + } +}; + +// -------------------------------------------------------------------------------------------------------------------- +// NetDeviceGraph definition and implementation + +} + +namespace std +{ + void swap (db::NetGraphNode &a, db::NetGraphNode &b) + { + a.swap (b); + } +} + +namespace db +{ + +struct CompareNodePtr +{ + bool operator() (const NetGraphNode *a, const NetGraphNode *b) const + { + return *a < *b; + } +}; + +class TentativeNodeMapping; + +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 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 + { + 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); + } + + void unidentify (size_t net_index) + { + m_nodes [net_index].unset_other_net (); + } + + node_iterator begin () const + { + return m_nodes.begin (); + } + + node_iterator end () const + { + return m_nodes.end (); + } + + const db::Circuit *circuit () const + { + return mp_circuit; + } + + /** + * @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 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 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; + std::map m_net_index; + 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)); + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +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 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 ()]; + +#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 << indent << "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]; + 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]; + 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 ()); + + // for the purpose of match evaluation we require an exact match of the node structure + + if (tentative) { + + if (nodes.size () != other_nodes.size ()) { + 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 (); + } + } + } + + } + + // 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, max_depth, n_branch, max_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; + } + + } + + e = ee; + + } + +#if defined(PRINT_DEBUG_NETCOMPARE) + if (! tentative && new_nodes > 0) { + tl::info << indent << "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 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 > max_depth) { +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << indent << "max. depth exhausted (" << depth + 1 << ">" << max_depth << ")"; +#endif + return std::numeric_limits::max (); + } + + if (nodes.size () == 1 && other_nodes.size () == 1) { + + if (! nodes.front ()->has_other () && ! other_nodes.front ()->has_other ()) { + + // 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 ()); + + TentativeNodeMapping::map_pair (tentative, this, ni, &other, other_ni); + +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << indent << "deduced match (singular): " << nodes.front ()->net ()->expanded_name () << " vs. " << other_nodes.front ()->net ()->expanded_name (); +#endif + if (! tentative) { + if (logger) { + logger->match_nets (nodes.front ()->net (), other_nodes.front ()->net ()); + } + } + + // continue here. + size_t bt_count = derive_node_identities (ni, other, depth + 1, max_depth, n_branch, max_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 (); + + } + + return new_nodes; + + } + + // Determine the range of nodes with same identity + + std::vector node_ranges; + + 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)->has_other ()) { + ++n1; + continue; + } else if ((*n2)->has_other ()) { + ++n2; + continue; + } + + if (**n1 < **n2) { + ++n1; + continue; + } else if (**n2 < **n1) { + ++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 || with_ambiguous) { + node_ranges.push_back (NodeRange (num, n1, nn1, n2, nn2)); + } + + // in tentative mode ambiguous nodes don't make a match without + // with_ambiguous + if (num > 1 && tentative && ! with_ambiguous) { + return std::numeric_limits::max (); + } + + n1 = nn1; + n2 = nn2; + + } + + if (with_ambiguous) { + std::stable_sort (node_ranges.begin (), node_ranges.end ()); + } + + 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 + + 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 ()) { + + // A single candiate: just take this one -> 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) { + if (logger) { + logger->match_nets ((*nr->n1)->net (), (*nr->n2)->net ()); + } + } + + // continue here. + size_t bt_count = derive_node_identities (ni, other, depth + 1, max_depth, n_branch, max_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 > 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; + + for (std::vector::const_iterator i1 = nr->n1; i1 != nr->nn1; ++i1) { + + if ((*i1)->has_other ()) { + continue; + } + + bool any = false; + + std::vector::const_iterator i2; + for (i2 = nr->n2; i2 != nr->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 ()); + + TentativeNodeMapping tn (this, &other); + TentativeNodeMapping::map_pair (&tn, this, ni, &other, other_ni); + + // try this candidate in tentative mode +#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, max_depth, nr->num * n_branch, max_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) { + + // 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 && tentative) { +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << indent << "mismatch."; +#endif + // a mismatch - stop here. + return std::numeric_limits::max (); + } + + } + + if (! tentative) { + + // 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 ()); + + TentativeNodeMapping::map_pair (tentative, this, ni, &other, other_ni); + +#if defined(PRINT_DEBUG_NETCOMPARE) + 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); + 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, max_depth, nr->num * n_branch, max_n_branch, logger, circuit_pin_mapper, tentative, with_ambiguous); + tl_assert (bt_count != std::numeric_limits::max ()); + + } + + } else { + + 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 ()); + + TentativeNodeMapping::map_pair (tentative, this, ni, &other, other_ni); + + } + + } + +#if defined(PRINT_DEBUG_NETCOMPARE) + tl::info << indent << "finished analysis of ambiguity group with " << nr->num << " members"; +#endif + + } + + } + + return new_nodes; +} + + +// -------------------------------------------------------------------------------------------------------------------- +// 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 ()); + + m_cap_threshold = -1.0; // not set + m_res_threshold = -1.0; // not set + + m_max_depth = 8; + m_max_n_branch = 100; +} + +void +NetlistComparer::exclude_caps (double threshold) +{ + m_cap_threshold = threshold; +} + +void +NetlistComparer::exclude_resistors (double threshold) +{ + m_res_threshold = threshold; +} + +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) +{ + tl_assert (ca != 0); + tl_assert (cb != 0); + mp_device_categorizer->same_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); +} + +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; + db::CircuitPinMapper circuit_pin_mapper = *mp_circuit_pin_mapper; + + 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); + } + + // 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; + if (mp_logger) { + mp_logger->circuit_mismatch (i->second.first, i->second.second); + } + } + } + + std::set verified_circuits_a, verified_circuits_b; + 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) { + + 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 defined(PRINT_DEBUG_NETCOMPARE) + tl::info << "treating circuit: " << ca->name () << " vs. " << cb->name (); +#endif + if (mp_logger) { + mp_logger->begin_circuit (ca, cb); + } + + bool pin_mismatch = false; + 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; + } + + if (! pin_mismatch) { + verified_circuits_a.insert (ca); + verified_circuits_b.insert (cb); + } + + derive_pin_equivalence (ca, cb, &circuit_pin_mapper); + + if (mp_logger) { + mp_logger->end_circuit (ca, cb, g); + } + + } else { + + if (mp_logger) { + mp_logger->circuit_skipped (ca, cb); + good = false; + } + + } + + } + + } + + if (mp_logger) { + mp_logger->begin_netlist (a, b); + } + + 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 +{ + 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 (); + + 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; + } + + 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) { + + if (cm->has_this_pin_for_other_pin (p->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)); + + } + + } + + 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, 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); + + 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, 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); + 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); + g1.identify (ni1, ni2); + g2.identify (ni2, ni1); + } + +#if defined(PRINT_DEBUG_NETCOMPARE) + int iter = 0; +#endif + + // two passes: one without ambiguities, the second one with + + bool good; + + 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) + ++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, 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) + tl::info << ni << " new identities."; +#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 + + 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; + } + // 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, 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) + 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) { + 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 () && mp_logger) { + mp_logger->net_mismatch (0, i->net ()); + } + } + + + // Report pin assignment + // This step also does the pin identity mapping. + + if (c1->pin_count () > 0 && c2->pin_count () > 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-> ())); + } + } + + 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); + } + 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); + } + 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 + + std::multimap >, std::pair > device_map; + + 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; + for (std::vector >::iterator i = k.begin (); i != k.end () && mapped; ++i) { + if (! g1.begin () [i->second].has_other ()) { + mapped = false; + } + } + + if (! mapped) { + if (mp_logger) { + 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) { + + if (! device_filter.filter (d.operator-> ())) { + continue; + } + + 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) { + + if (mp_logger) { + 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) { + if (mp_logger) { + mp_logger->match_devices_with_different_device_classes (dm->second.first, d.operator-> ()); + } + good = false; + } else { + if (mp_logger) { + mp_logger->match_devices_with_different_parameters (dm->second.first, d.operator-> ()); + } + good = false; + } + } else { + if (mp_logger) { + 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) { + if (mp_logger) { + 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, &c12_circuit_and_pin_mapping, &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) { + if (mp_logger) { + 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, &c22_circuit_and_pin_mapping, &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 ()) { + + if (mp_logger) { + 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))) { + if (mp_logger) { + mp_logger->subcircuit_mismatch (scm->second.first, sc.operator-> ()); + } + good = false; + } else { + if (mp_logger) { + 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) { + if (mp_logger) { + 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..c2946a997 --- /dev/null +++ b/src/db/db/dbNetlistCompare.h @@ -0,0 +1,291 @@ + +/* + + 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 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 + */ + 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 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 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 + */ + 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, 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; + 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; + size_t m_max_n_branch; + size_t m_max_depth; +}; + +} + +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/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/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/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index e8ba2bade..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); @@ -127,11 +131,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/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/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..787ce4f4d --- /dev/null +++ b/src/db/db/dbNetlistSpiceReader.cc @@ -0,0 +1,617 @@ + +/* + + 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 +{ + +static const char *allowed_name_chars = "_.:,!+$/&\\#[]|"; + +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; + mp_nets_by_name.reset (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; + mp_nets_by_name.reset (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, allowed_name_chars); + + 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) +{ + 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; +} + +void NetlistSpiceReader::read_subcircuit (tl::Extractor &ex) +{ + std::string sc_name; + ex.read_word_or_quoted (sc_name, allowed_name_chars); + + std::vector nn; + std::map pv; + + while (! ex.at_end ()) { + + std::string n; + ex.read_word_or_quoted (n, allowed_name_chars); + + 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, allowed_name_chars); + + std::vector nn; + std::map pv; + + while (! ex.at_end ()) { + + std::string n; + ex.read_word_or_quoted (n, allowed_name_chars); + + 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::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) { + db::Net *net = make_net (*i); + mp_circuit->connect_pin (i - nn.begin (), net); + } + + while (! at_end ()) { + if (read_element ()) { + break; + } + } + + mp_nets_by_name.reset (n2n.release ()); + 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, 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 (), allowed_name_chars); + } + + 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, allowed_name_chars); + + std::vector nn; + std::map pv; + + while (! ex.at_end ()) { + + std::string n; + ex.read_word_or_quoted (n, allowed_name_chars); + + 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 ()) { + // 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); + } + } + } + + ex.expect_end (); +} + +} diff --git a/src/db/db/dbNetlistSpiceReader.h b/src/db/db/dbNetlistSpiceReader.h new file mode 100644 index 000000000..73185fad6 --- /dev/null +++ b/src/db/db/dbNetlistSpiceReader.h @@ -0,0 +1,97 @@ + +/* + + 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::auto_ptr > mp_nets_by_name; + 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/dbNetlistSpiceWriter.cc b/src/db/db/dbNetlistSpiceWriter.cc index 9bde4c2d0..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 () @@ -141,8 +143,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)); @@ -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 += "|"; 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/dbTestSupport.cc b/src/db/db/dbTestSupport.cc index b8b1af7d5..1d71cacca 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,175 @@ 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::info << m_circuit; + m_new_circuit = false; + } + tl::info << text; + } + + virtual void begin_netlist (const db::Netlist * /*a*/, const db::Netlist * /*b*/) + { + tl::info << "Comparing netlists:"; + } + + virtual void end_netlist (const db::Netlist * /*a*/, const db::Netlist * /*b*/) + { + tl::info << "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 device_class_mismatch (const db::DeviceClass *a, const db::DeviceClass *b) + { + out ("device_class_mismatch " + device_class2str (a) + " " + device_class2str (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 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)"; + } + + 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::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); + + 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 + CompareLogger logger; + db::NetlistComparer comp (&logger); + comp.compare (&netlist, &au_nl); + } +} + +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 00fb38432..e079cf05b 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,16 @@ 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); + +/** + * @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/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 b51ca3394..244f87168 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" @@ -37,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 " @@ -456,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 " @@ -467,11 +482,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." @@ -527,6 +651,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 " @@ -586,11 +719,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 +804,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 " @@ -811,6 +965,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" ) + @@ -922,6 +1081,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" @@ -932,13 +1098,18 @@ 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." ) + 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" @@ -969,6 +1140,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" ) + @@ -976,6 +1151,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 " @@ -999,6 +1179,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. " @@ -1184,14 +1368,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 " @@ -1232,9 +1416,36 @@ 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." ); +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" + "@/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 new file mode 100644 index 000000000..e5a652ecb --- /dev/null +++ b/src/db/db/gsiDeclDbNetlistCompare.cc @@ -0,0 +1,531 @@ + +/* + + 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 gsi::ObjectBase, 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 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 ()) { + 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_device_class_mismatch; + 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 ("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 ("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" + "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 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"), + "@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" +); + +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_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" + "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 ("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 ("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. " + "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 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. 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 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/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 730d31ae0..b2d8b8fe9 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -337,28 +337,32 @@ 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" + 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" + " 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 +496,26 @@ 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" + 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" + " 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 +691,31 @@ 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" + 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" + " 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 +748,25 @@ 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" + 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" + " 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 +972,31 @@ 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" + 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" + " 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 +1029,25 @@ 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" + 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" + " 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 +1259,31 @@ 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" + 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" + " 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 +1316,25 @@ 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" + 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" + " 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 +1546,31 @@ 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" + 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" + " 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 +1600,23 @@ 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" + 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" + " 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 +1775,16 @@ 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" + 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" + " 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" + "end;\n" ); } @@ -1887,14 +1932,16 @@ 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" + 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" + " 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" + "end;\n" ); } @@ -2064,14 +2111,16 @@ 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" + 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" + " 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" + "end;\n" ); } @@ -2246,14 +2295,16 @@ 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" + 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" + " 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" + "end;\n" ); } diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc new file mode 100644 index 000000000..289bec5c5 --- /dev/null +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -0,0 +1,2315 @@ + +/* + + 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 "tlUnitTest.h" +#include "dbNetlistDeviceClasses.h" +#include "dbNetlistCompare.h" + +class NetlistCompareTestLogger + : public db::NetlistCompareLogger +{ +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) + { + out ("begin_circuit " + circuit2str (a) + " " + circuit2str (b)); + } + + virtual void end_circuit (const db::Circuit *a, const db::Circuit *b, bool matching) + { + out ("end_circuit " + circuit2str (a) + " " + circuit2str (b) + " " + (matching ? "MATCH" : "NOMATCH")); + } + + 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 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)); + } + + 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)); + } + + std::string text () const + { + return tl::join (m_texts, "\n"); + } + + void clear () + { + m_texts.clear (); + } + +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)"; + } + + 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)"; + } +}; + +static void prep_nl (db::Netlist &nl, const char *str) +{ + db::DeviceClass *dc; + + 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); + + 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); + + 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); +} + +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); + + // 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); + 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 = + "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 VDD VDD\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" + "match_pins $3 $2\n" + "match_devices $2 $1\n" + "match_devices $1 $2\n" + "end_circuit INV INV MATCH" + ); + 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")); + // 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 (), + "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 $0 $1\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $3 $2\n" + "match_devices $2 $1\n" + "match_devices $1 $2\n" + "end_circuit INV INV MATCH" + ); + 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 OUT OUT\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" + "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 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" + "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 = + "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->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 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_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 VDD VDD\n" + "match_nets OUT OUT\n" + "match_nets INT $10\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" + "match_pins $3 $2\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 VDD VDD\n" + "match_nets OUT OUT\n" + "match_nets VSS VSS\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); +} + +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->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 OUT OUT\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" + "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, 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 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" + "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 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" + "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 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" + "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) +{ + 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 INT $10\n" + "match_nets IN IN\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 $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 IN IN\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 $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 = + "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 VDD VDD\n" + "match_nets IN IN\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" + "match_pins $3 $2\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(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 P2 P2\n" + "match_nets P1 P1\n" + "match_nets P3 P3\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" + "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 $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" + "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 $0 $0\n" + "match_pins $1 $1\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 $1 $1\n" + "match_pins $2 $2\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_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 = + "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.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; + 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 $0 $1\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $3 $2\n" + "match_devices $2 $1\n" + "match_devices $1 $2\n" + "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 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); +} + +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 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" + "match_pins $3 $2\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 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, 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" + "match_nets VDD VDD\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $3 $2\n" + "match_devices $2 $1\n" + "device_mismatch (null) $2\n" + "device_mismatch $1 (null)\n" + "end_circuit INV INV NOMATCH\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 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); +} + +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=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=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 VDD VDD\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" + "match_pins $3 $2\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 VDD VDD\n" + "match_nets VSS VSS\n" + "match_nets INT INT\n" + "match_nets OUT OUT\n" + "match_pins $0 $1\n" + "match_pins $1 $0\n" + "match_pins $2 $2\n" + "match_pins $3 $3\n" + "match_subcircuits $2 $1\n" + "match_subcircuits $1 $2\n" + "subcircuit_mismatch $3 (null)\n" + "end_circuit TOP TOP NOMATCH" + ); + + 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 VDD VDD\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" + "match_pins $3 $2\n" + "match_devices $2 $1\n" + "match_devices $1 $2\n" + "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 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); +} + +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 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 TOP TOP\n" + "match_nets OUT OUT\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" + "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" + ); + + 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 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 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 IN2 IN2\n" + "pin_mismatch $0 (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) $0\n" + "match_subcircuits $2 $1\n" + "subcircuit_mismatch (null) $2\n" + "subcircuit_mismatch $1 (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 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 TOP TOP\n" + "match_nets OUT OUT\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" + "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" + ); + + EXPECT_EQ (good, true); +} + +TEST(15_EmptySubCircuitTest) +{ + 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 (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 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 (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 (), + "begin_circuit TRANS TRANS\n" + "match_nets $3 $3\n" + "match_nets $2 $1\n" + "match_nets $1 $2\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 $5 $5\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" + "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(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 not defined by the pin names -> + // they are internally marked as swappable + "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 $5 $5\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" + "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 = + "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 OUT OUT\n" + "match_nets $3 $3\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" + "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 $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" + "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 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 $I7 $I7\n" + "match_nets $I25 $I25\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); +} + +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" + // 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" + "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 VSS VSS\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 $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" + "end_circuit DECODER DECODER MATCH" + ); + + 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); +} + +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); +} + diff --git a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc index 095541605..37661487d 100644 --- a/src/db/unit_tests/dbNetlistDeviceClassesTests.cc +++ b/src/db/unit_tests/dbNetlistDeviceClassesTests.cc @@ -65,17 +65,19 @@ TEST(1_SerialResistors) 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" + "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" + "circuit '' (A=n1,B=n3);\n" + " device '' r1 (A=n1,B=n3) (R=4);\n" + "end;\n" ); } @@ -116,17 +118,19 @@ TEST(2_SerialResistors1Swapped) 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" + "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" + "circuit '' (A=n1,B=n3);\n" + " device '' r1 (A=n1,B=n3) (R=4);\n" + "end;\n" ); } @@ -167,17 +171,19 @@ TEST(3_SerialResistors1OtherSwapped) 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" + "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" + "circuit '' (A=n1,B=n3);\n" + " device '' r1 (A=n3,B=n1) (R=4);\n" + "end;\n" ); } @@ -218,17 +224,19 @@ TEST(4_SerialResistors2Swapped) 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" + "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" + "circuit '' (A=n1,B=n3);\n" + " device '' r1 (A=n3,B=n1) (R=4);\n" + "end;\n" ); } @@ -271,18 +279,20 @@ TEST(5_SerialResistorsNoCombination) 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" + "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" + "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" ); } @@ -320,17 +330,19 @@ TEST(6_ParallelResistors) 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" + "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" + "circuit '' (A=n1,B=n2);\n" + " device '' r1 (A=n1,B=n2) (R=1.2);\n" + "end;\n" ); } @@ -368,17 +380,19 @@ TEST(7_ParallelResistors1Swapped) 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" + "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" + "circuit '' (A=n1,B=n2);\n" + " device '' r1 (A=n2,B=n1) (R=1.2);\n" + "end;\n" ); } @@ -416,17 +430,19 @@ TEST(8_ParallelResistors1OtherSwapped) 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" + "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" + "circuit '' (A=n1,B=n2);\n" + " device '' r1 (A=n1,B=n2) (R=1.2);\n" + "end;\n" ); } @@ -464,17 +480,19 @@ TEST(9_ParallelResistors2Swapped) 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" + "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" + "circuit '' (A=n1,B=n2);\n" + " device '' r1 (A=n2,B=n1) (R=1.2);\n" + "end;\n" ); } @@ -537,19 +555,21 @@ TEST(10_ComplexRegistorCombination) 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" + "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" + "circuit '' (A=n1,B=n4);\n" + " device '' r4 (A=n1,B=n4) (R=2);\n" + "end;\n" ); } @@ -590,17 +610,19 @@ TEST(11_SerialInductors) 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" + "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" + "circuit '' (A=n1,B=n3);\n" + " device '' l1 (A=n1,B=n3) (L=4);\n" + "end;\n" ); } @@ -638,17 +660,19 @@ TEST(12_ParallelInductors) 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" + "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" + "circuit '' (A=n1,B=n2);\n" + " device '' l1 (A=n1,B=n2) (L=1.2);\n" + "end;\n" ); } @@ -689,17 +713,19 @@ TEST(13_SerialCapacitors) 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" + "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" + "circuit '' (A=n1,B=n3);\n" + " device '' c1 (A=n1,B=n3) (C=1.2);\n" + "end;\n" ); } @@ -737,17 +763,19 @@ TEST(14_ParallelCapacitors) 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" + "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" + "circuit '' (A=n1,B=n2);\n" + " device '' c1 (A=n1,B=n2) (C=4);\n" + "end;\n" ); } @@ -788,9 +816,10 @@ TEST(15_SerialDiodes) 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" + "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 (); @@ -799,9 +828,10 @@ 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" + "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" ); } @@ -839,17 +869,19 @@ TEST(16_ParallelDiodes) 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" + "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" + "circuit '' (A=n1,B=n2);\n" + " device '' d1 (A=n1,C=n2) (A=4);\n" + "end;\n" ); } @@ -887,9 +919,10 @@ TEST(17_AntiParallelDiodes) 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" + "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 (); @@ -898,9 +931,10 @@ 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" + "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" ); } @@ -955,17 +989,19 @@ TEST(20_ParallelMOS3Transistors) 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" + "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" + "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" ); } @@ -1020,17 +1056,19 @@ TEST(21_AntiParallelMOS3Transistors) 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" + "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" + "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" ); } @@ -1090,9 +1128,10 @@ TEST(22_ParallelMOS3TransistorsDisconnectedGates) 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" + "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 (); @@ -1101,9 +1140,10 @@ 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" + "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" ); } @@ -1158,9 +1198,10 @@ TEST(23_ParallelMOS3TransistorsDifferentLength) 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" + "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 (); @@ -1169,9 +1210,10 @@ 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" + "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" ); } @@ -1233,17 +1275,19 @@ TEST(30_ParallelMOS4Transistors) 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" + "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" + "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" ); } @@ -1305,17 +1349,19 @@ TEST(31_AntiParallelMOS4Transistors) 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" + "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" + "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" ); } @@ -1382,9 +1428,10 @@ TEST(32_ParallelMOS4TransistorsDisconnectedGates) 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" + "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 (); @@ -1393,9 +1440,10 @@ 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" + "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" ); } @@ -1462,9 +1510,10 @@ TEST(33_ParallelMOS4TransistorsDisconnectedBulk) 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" + "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: @@ -1473,9 +1522,10 @@ TEST(33_ParallelMOS4TransistorsDisconnectedBulk) 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" + "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" ); } @@ -1537,9 +1587,10 @@ TEST(34_ParallelMOS4TransistorsDifferentLength) 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" + "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 (); @@ -1548,9 +1599,10 @@ 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" + "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 4713081aa..b9e67104d 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,55 +281,71 @@ 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" + 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" + " 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 + 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_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 (); // 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 (), - "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" + 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" + " 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 @@ -466,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 @@ -493,50 +510,50 @@ 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. - 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" + CHECKPOINT (); + db::compare_netlist (_this, nl, + "circuit RINGO ();\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$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$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$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$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$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" ); // compare the collected test data @@ -699,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); @@ -728,28 +755,32 @@ 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" + 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" + " 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: @@ -760,23 +791,26 @@ 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" + 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" + " 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/dbNetlistReaderTests.cc b/src/db/unit_tests/dbNetlistReaderTests.cc new file mode 100644 index 000000000..ce21c7797 --- /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=0.25,W=1.5,AS=0.63,AD=0.63,PS=3.84,PD=3.84);\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=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=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" + ); +} + +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=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=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" + " 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/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 4d85928cd..ed1ba213f 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" @@ -258,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 (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"); @@ -501,6 +504,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 +615,28 @@ TEST(4_NetlistSubcircuits) "D:B,+c2p2\n" ); + 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" + "end;\n" + "circuit c2 (c2p1=n2a,c2p2=n2b);\n" + " device dc2 D (A=n2a,B=n2b) ();\n" + "end;\n" + ); + + nldup->from_string (nl->to_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" + "end;\n" + "circuit c2 (c2p1=n2a,c2p2=n2b);\n" + " device dc2 D (A=n2a,B=n2b) ();\n" + "end;\n" + ); + EXPECT_EQ (netlist2 (*nl), "c1:c1p1=n1a,c1p2=n1c\n" " Xsc1:c2p1=n1a,c2p2=n1b\n" @@ -1079,3 +1107,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/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 87fbdd2a0..39a1196b1 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -69,7 +69,9 @@ SOURCES = \ dbNetlistWriterTests.cc \ dbCellVariantsTests.cc \ dbDeepEdgesTests.cc \ - dbDeepEdgePairsTests.cc + dbDeepEdgePairsTests.cc \ + dbNetlistCompareTests.cc \ + dbNetlistReaderTests.cc INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC diff --git a/src/drc/drc/built-in-macros/drc.lym b/src/drc/drc/built-in-macros/drc.lym index e087638cb..e869a76c4 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% @@ -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 885495d8c..c3a0a68c5 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,27 @@ 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::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); + } + + db::compare_netlist (_this, nl, nl_au); +} + TEST(9_NetlistExtraction) { std::string rs = tl::testsrc (); @@ -380,27 +403,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 +447,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 +491,45 @@ 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); +} + +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/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/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 c04043318..ae8247e89 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/device_extract_l6.gds b/testdata/algo/device_extract_l6.gds index 73ece855b..6fbeeb989 100644 Binary files a/testdata/algo/device_extract_l6.gds and b/testdata/algo/device_extract_l6.gds differ 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 + 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/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_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_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/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 diff --git a/testdata/drc/implicit_nets.gds b/testdata/drc/implicit_nets.gds new file mode 100644 index 000000000..0b8e3faf9 Binary files /dev/null and b/testdata/drc/implicit_nets.gds differ 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, <