diff --git a/src/db/db/dbDevice.cc b/src/db/db/dbDevice.cc index 37224ceb9..2fe3bdecd 100644 --- a/src/db/db/dbDevice.cc +++ b/src/db/db/dbDevice.cc @@ -90,6 +90,16 @@ void Device::set_circuit (Circuit *circuit) mp_circuit = circuit; } +const Netlist *Device::netlist () const +{ + return mp_circuit ? mp_circuit->netlist () : 0; +} + +Netlist *Device::netlist () +{ + return mp_circuit ? mp_circuit->netlist () : 0; +} + void Device::set_name (const std::string &n) { m_name = n; diff --git a/src/db/db/dbDevice.h b/src/db/db/dbDevice.h index 336411b1b..4efceaf47 100644 --- a/src/db/db/dbDevice.h +++ b/src/db/db/dbDevice.h @@ -193,6 +193,16 @@ public: return mp_circuit; } + /** + * @brief Gets the netlist, the device lives in + */ + const Netlist *netlist () const; + + /** + * @brief Gets the netlist, the device lives in + */ + Netlist *netlist (); + /** * @brief Sets the name */ diff --git a/src/db/db/dbDeviceClass.cc b/src/db/db/dbDeviceClass.cc index 4c27c7409..4fb4723bd 100644 --- a/src/db/db/dbDeviceClass.cc +++ b/src/db/db/dbDeviceClass.cc @@ -22,16 +22,50 @@ #include "dbDeviceClass.h" #include "dbDevice.h" +#include "dbNetlist.h" #include "tlClassRegistry.h" namespace db { +// -------------------------------------------------------------------------------- + +/** + * @brief Returns the primary device class for both given devices + * One of the devices lives in a primary netlist. This one is taken for the device class. + */ +static const db::DeviceClass *primary_device_class (const db::Device &a, const db::Device &b) +{ + tl_assert (a.device_class () != 0); + tl_assert (b.device_class () != 0); + + const db::DeviceClass *dca = a.device_class ()->primary_class () ? a.device_class ()->primary_class () : a.device_class (); + const db::DeviceClass *dcb = b.device_class ()->primary_class () ? b.device_class ()->primary_class () : b.device_class (); + + if (dca != dcb) { + // different devices, same category while sorting devices - take the one with the "lower" name + return dca->name () < dcb->name () ? dca : dcb; + } else { + return dca; + } +} + // -------------------------------------------------------------------------------- // EqualDeviceParameters implementation -static int compare_parameters (double pa, double pb, double absolute, double relative) +// NOTE: to allow rounding errors for parameter comparison, we use +// a default relative tolerance. +const double default_relative_tolerance = 1e-6; + +const double default_absolute_tolerance = 0.0; + +static int compare_parameters (double pa, double pb, double absolute = default_absolute_tolerance, double relative = default_relative_tolerance) { + // absolute value < 0 means: ignore this parameter (= always match) + if (absolute < 0.0) { + return 0; + } + double pa_min = pa - absolute; double pa_max = pa + absolute; @@ -57,14 +91,31 @@ EqualDeviceParameters::EqualDeviceParameters () // .. nothing yet .. } -EqualDeviceParameters::EqualDeviceParameters (size_t parameter_id) +EqualDeviceParameters::EqualDeviceParameters (size_t parameter_id, bool ignore) { - m_compare_set.push_back (std::make_pair (parameter_id, std::make_pair (0.0, 0.0))); + m_compare_set.push_back (std::make_pair (parameter_id, std::make_pair (ignore ? -1.0 : 0.0, 0.0))); } -EqualDeviceParameters::EqualDeviceParameters (size_t parameter_id, double relative, double absolute) +EqualDeviceParameters::EqualDeviceParameters (size_t parameter_id, double absolute, double relative) { - m_compare_set.push_back (std::make_pair (parameter_id, std::make_pair (relative, absolute))); + m_compare_set.push_back (std::make_pair (parameter_id, std::make_pair (std::max (0.0, absolute), std::max (0.0, relative)))); +} + +std::string EqualDeviceParameters::to_string () const +{ + std::string res; + for (std::vector > >::const_iterator c = m_compare_set.begin (); c != m_compare_set.end (); ++c) { + if (!res.empty ()) { + res += ";"; + } + res += "#" + tl::to_string (c->first) + ":"; + if (c->second.first < 0.0) { + res += "ignore"; + } else { + res += "A" + tl::to_string (c->second.first) + "/R" + tl::to_string (c->second.second); + } + } + return res; } bool EqualDeviceParameters::less (const db::Device &a, const db::Device &b) const @@ -76,6 +127,23 @@ bool EqualDeviceParameters::less (const db::Device &a, const db::Device &b) cons } } + // compare the remaining parameters with a default precision + + std::set seen; + for (std::vector > >::const_iterator c = m_compare_set.begin (); c != m_compare_set.end (); ++c) { + seen.insert (c->first); + } + + const std::vector &pd = primary_device_class (a, b)->parameter_definitions (); + for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (p->is_primary () && seen.find (p->id ()) == seen.end ()) { + int cmp = compare_parameters (a.parameter_value (p->id ()), b.parameter_value (p->id ())); + if (cmp != 0) { + return cmp < 0; + } + } + } + return false; } @@ -109,58 +177,17 @@ bool AllDeviceParametersAreEqual::less (const db::Device &a, const db::Device &b return false; } -// -------------------------------------------------------------------------------- -// PrimaryDeviceParametersAreEqual class implementation - -class DB_PUBLIC PrimaryDeviceParametersAreEqual - : public DeviceParameterCompareDelegate -{ -public: - PrimaryDeviceParametersAreEqual (double relative); - - virtual bool less (const db::Device &a, const db::Device &b) const; - -private: - double m_relative; -}; - -PrimaryDeviceParametersAreEqual::PrimaryDeviceParametersAreEqual (double relative) - : m_relative (relative) -{ - // .. nothing yet .. -} - -bool PrimaryDeviceParametersAreEqual::less (const db::Device &a, const db::Device &b) const -{ - const std::vector &pd = a.device_class ()->parameter_definitions (); - for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { - const db::DeviceParameterDefinition *pdb = b.device_class ()->parameter_definition (p->id ()); - if (! pdb) { - continue; - } - if (! pdb->is_primary () && ! p->is_primary ()) { - continue; - } - int cmp = compare_parameters (a.parameter_value (p->id ()), b.parameter_value (p->id ()), 0.0, m_relative); - if (cmp != 0) { - return cmp < 0; - } - } - - return false; -} - // -------------------------------------------------------------------------------- // DeviceClass class implementation DeviceClass::DeviceClass () - : m_strict (false), mp_netlist (0), m_supports_parallel_combination (false), m_supports_serial_combination (false) + : m_strict (false), mp_netlist (0), m_supports_parallel_combination (false), m_supports_serial_combination (false), mp_primary_class (0) { // .. nothing yet .. } DeviceClass::DeviceClass (const DeviceClass &other) - : gsi::ObjectBase (other), tl::Object (other), tl::UniqueId (other), m_strict (false), mp_netlist (0), m_supports_parallel_combination (false), m_supports_serial_combination (false) + : gsi::ObjectBase (other), tl::Object (other), tl::UniqueId (other), m_strict (false), mp_netlist (0), m_supports_parallel_combination (false), m_supports_serial_combination (false), mp_primary_class (0) { operator= (other); } @@ -279,22 +306,15 @@ 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; - // The default compare delegate -static PrimaryDeviceParametersAreEqual default_compare (relative_tolerance); +static EqualDeviceParameters default_compare; 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 (); - } + const db::DeviceParameterCompareDelegate *pcd = primary_device_class (a, b)->parameter_compare_delegate (); if (! pcd) { pcd = &default_compare; } @@ -307,10 +327,7 @@ 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 (); - } + const db::DeviceParameterCompareDelegate *pcd = primary_device_class (a, b)->parameter_compare_delegate (); if (! pcd) { pcd = &default_compare; } diff --git a/src/db/db/dbDeviceClass.h b/src/db/db/dbDeviceClass.h index a6306cdf8..aac1e5bab 100644 --- a/src/db/db/dbDeviceClass.h +++ b/src/db/db/dbDeviceClass.h @@ -309,11 +309,14 @@ class DB_PUBLIC EqualDeviceParameters { public: EqualDeviceParameters (); - EqualDeviceParameters (size_t parameter_id); - EqualDeviceParameters (size_t parameter_id, double relative, double absolute); + EqualDeviceParameters (size_t parameter_id, bool ignore = false); + EqualDeviceParameters (size_t parameter_id, double absolute, double relative); virtual bool less (const db::Device &a, const db::Device &b) const; + // for test purposes + std::string to_string () const; + EqualDeviceParameters &operator+= (const EqualDeviceParameters &other); EqualDeviceParameters operator+ (const EqualDeviceParameters &other) const @@ -507,6 +510,14 @@ public: return m_parameter_definitions; } + /** + * @brief Gets the parameter definitions + */ + std::vector ¶meter_definitions_non_const () + { + return m_parameter_definitions; + } + /** * @brief Adds a parameter definition */ @@ -723,6 +734,22 @@ public: return mp_device_combiner.get (); } + /** + * @brief Internally used by the netlist comparer to temporarily attach a device class pointing to the primary one + */ + void set_primary_class (const db::DeviceClass *primary) const + { + mp_primary_class = primary; + } + + /** + * @brief Internally used by the netlist comparer to temporarily attach a device class pointing to the primary one + */ + const db::DeviceClass *primary_class () const + { + return mp_primary_class; + } + /** * @brief Generate memory statistics */ @@ -751,6 +778,7 @@ private: bool m_supports_parallel_combination; bool m_supports_serial_combination; std::map m_equivalent_terminal_ids; + mutable const db::DeviceClass *mp_primary_class; void set_netlist (db::Netlist *nl) { diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 47a226961..72213a323 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -44,7 +44,7 @@ Netlist::Netlist (NetlistManipulationCallbacks *callbacks) } Netlist::Netlist (const Netlist &other) - : gsi::ObjectBase (other), tl::Object (other), m_case_sensitive (true), + : gsi::ObjectBase (other), tl::Object (other), m_valid_topology (false), m_lock_count (0), m_circuit_by_name (this, &Netlist::begin_circuits, &Netlist::end_circuits), m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits), diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 86016c64b..ec2d65b27 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -3059,9 +3059,37 @@ NetlistComparer::unmatched_circuits (db::Netlist *a, db::Netlist *b, std::vector } } +static void clear_primary_classes (const db::Netlist *nl) +{ + for (db::Netlist::const_device_class_iterator dc = nl->begin_device_classes (); dc != nl->end_device_classes (); ++dc) { + dc->set_primary_class (0); + } +} + bool NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const { + bool res = false; + + try { + res = compare_impl (a, b); + clear_primary_classes (a); + clear_primary_classes (b); + } catch (...) { + clear_primary_classes (a); + clear_primary_classes (b); + throw; + } + + return res; +} + +bool +NetlistComparer::compare_impl (const db::Netlist *a, const db::Netlist *b) const +{ + clear_primary_classes (a); + clear_primary_classes (b); + m_case_sensitive = combined_case_sensitive (a, b); // we need to create a copy because this method is supposed to be const. @@ -3130,21 +3158,12 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const } } - // impose the compare tolerances of the layout (first netlist) on the schematic (second netlist) - // TODO: this is kind of clumsy. But it's very important to use the same device sorting for both netlists, so we play this trick. - // A better solution was to have a common compare framework for both netlists. + // Register the primary netlist's device classes as primary ones for the second netlist's device classes. + // This way, the tolerances and parameter definitions are imposed on the second netlist, creating a common basis. for (std::map >::const_iterator i = cat2dc.begin (); i != cat2dc.end (); ++i) { - if (i->second.first && i->second.second) { - - const db::DeviceClass *da = i->second.first; - db::DeviceClass *db = const_cast (i->second.second); - - const db::DeviceParameterCompareDelegate *cmp = da->parameter_compare_delegate (); - db->set_parameter_compare_delegate (const_cast (cmp)); - + i->second.second->set_primary_class (i->second.first); } - } // decide whether to use a device category in strict mode diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h index 197d5ef6c..3cb89cb3e 100644 --- a/src/db/db/dbNetlistCompare.h +++ b/src/db/db/dbNetlistCompare.h @@ -354,6 +354,7 @@ private: NetlistComparer &operator= (const NetlistComparer &); protected: + bool compare_impl (const db::Netlist *a, const db::Netlist *b) const; bool compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, db::CircuitPinMapper &circuit_pin_mapper, const std::vector, bool> > &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; std::string generate_subcircuits_not_verified_warning (const db::Circuit *ca, const std::set &verified_circuits_a, const db::Circuit *cb, const std::set &verified_circuits_b) const; diff --git a/src/db/db/dbNetlistSpiceReader.cc b/src/db/db/dbNetlistSpiceReader.cc index 0f0fe8312..59e9f301f 100644 --- a/src/db/db/dbNetlistSpiceReader.cc +++ b/src/db/db/dbNetlistSpiceReader.cc @@ -620,8 +620,8 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin defp = db::DeviceClassInductor::param_id_L; } - const std::vector &pd = cls->parameter_definitions (); - for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { + std::vector &pd = cls->parameter_definitions_non_const (); + for (std::vector::iterator i = pd.begin (); i != pd.end (); ++i) { std::map::const_iterator v = params.find (i->name ()); if (v != params.end ()) { device->set_parameter_value (i->id (), v->second / i->si_scaling ()); diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 2162bb342..c98c967bf 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -908,6 +908,11 @@ db::EqualDeviceParameters *make_equal_dp (size_t param_id, double absolute, doub return new db::EqualDeviceParameters (param_id, absolute, relative); } +db::EqualDeviceParameters *make_ignore_dp (size_t param_id) +{ + return new db::EqualDeviceParameters (param_id, true); +} + 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" @@ -921,6 +926,15 @@ Class decl_dbEqualDeviceParameters ("db", "EqualDevic "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::constructor ("ignore", &make_ignore_dp, gsi::arg ("param_id"), + "@brief Creates a device parameter comparer which ignores the parameter.\n" + "\n" + "This specification can be used to make a parameter ignored. Starting with version 0.27.4, all primary parameters " + "are compared. Before 0.27.4, giving a tolerance meant only those parameters are compared. To exclude a primary " + "parameter from the compare, use the 'ignore' specification for that parameter.\n" + "\n" + "This constructor has been introduced in version 0.27.4.\n" + ) + 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" @@ -928,7 +942,8 @@ Class decl_dbEqualDeviceParameters ("db", "EqualDevic 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" - ), + ) + + gsi::method ("to_string", &db::EqualDeviceParameters::to_string, "@hide"), "@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" diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 7ef82e780..7eefa0e09 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -404,6 +404,13 @@ TEST(0_EqualDeviceParameters) 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), false); + EXPECT_EQ (dc.equal (d2, d1), false); + EXPECT_EQ (dc.less (d1, d2), false); + EXPECT_EQ (dc.less (d2, d1), true); + + *eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_W, true); // ignore W + EXPECT_EQ (dc.equal (d1, d2), true); EXPECT_EQ (dc.equal (d2, d1), true); EXPECT_EQ (dc.less (d1, d2), false); @@ -959,7 +966,9 @@ TEST(5_BufferTwoPathsDifferentParameters) EXPECT_EQ (good, false); logger.clear (); - nl1.device_class_by_name ("NMOS")->set_parameter_compare_delegate (new db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 1.5, 0.0)); + db::EqualDeviceParameters *eql = new db::EqualDeviceParameters (); + *eql += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 1.5, 0.0); + nl1.device_class_by_name ("NMOS")->set_parameter_compare_delegate (eql); good = comp.compare (&nl1, &nl2); EXPECT_EQ (logger.text (), @@ -4712,3 +4721,164 @@ TEST(29_EmptySubCircuitsFromSPICE) EXPECT_EQ (comp.compare (&a, &b), true); EXPECT_EQ (comp.compare (&a, &c), false); } + +TEST(30_ComparePrimaryAndOtherParameters) +{ + db::Netlist nl1, nl2; + db::DeviceClass *dc1, *dc2; + db::Circuit *circuit; + db::Device *d; + db::Net *n; + std::string txt; + bool good; + + dc1 = new db::DeviceClassResistor (); + dc1->set_name ("RES"); + nl1.add_device_class (dc1); + circuit = new db::Circuit (); + circuit->set_name ("X"); + d = new db::Device (); + d->set_device_class (dc1); + d->set_name ("D"); + d->set_parameter_value (db::DeviceClassResistor::param_id_L, 1.0); + d->set_parameter_value (db::DeviceClassResistor::param_id_R, 10.0); + d->set_parameter_value (db::DeviceClassResistor::param_id_W, 0.25); + circuit->add_device (d); + n = new db::Net (); + circuit->add_net (n); + n->set_name ("1"); + d->connect_terminal (db::DeviceClassResistor::terminal_id_A, n); + n->add_pin (db::NetPinRef (circuit->add_pin ("1").id ())); + n = new db::Net (); + circuit->add_net (n); + n->set_name ("2"); + d->connect_terminal (db::DeviceClassResistor::terminal_id_B, n); + n->add_pin (db::NetPinRef (circuit->add_pin ("2").id ())); + nl1.add_circuit (circuit); + + dc2 = new db::DeviceClassResistor (); + dc2->set_name ("RES"); + nl2.add_device_class (dc2); + circuit = new db::Circuit (); + circuit->set_name ("X"); + d = new db::Device (); + d->set_device_class (dc2); + d->set_name ("D"); + d->set_parameter_value (db::DeviceClassResistor::param_id_L, 1.1); // differs + d->set_parameter_value (db::DeviceClassResistor::param_id_R, 10.0); + d->set_parameter_value (db::DeviceClassResistor::param_id_W, 0.20); // differs + circuit->add_device (d); + n = new db::Net (); + circuit->add_net (n); + n->set_name ("1"); + d->connect_terminal (db::DeviceClassResistor::terminal_id_A, n); + n->add_pin (db::NetPinRef (circuit->add_pin ("1").id ())); + n = new db::Net (); + circuit->add_net (n); + n->set_name ("2"); + d->connect_terminal (db::DeviceClassResistor::terminal_id_B, n); + n->add_pin (db::NetPinRef (circuit->add_pin ("2").id ())); + nl2.add_circuit (circuit); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + comp.set_dont_consider_net_names (true); + + good = comp.compare (&nl1, &nl2); + + txt = logger.text (); + + // we did not enable L and W, hence that works + EXPECT_EQ (good, true); + + EXPECT_EQ (txt, + "begin_circuit X X\n" + "match_ambiguous_nets 1 1\n" + "match_ambiguous_nets 2 2\n" + "match_pins 1 1\n" + "match_pins 2 2\n" + "match_devices D D\n" + "end_circuit X X MATCH" + ); + + // changing R will make the compare fail + + d->set_parameter_value (db::DeviceClassResistor::param_id_R, 12.0); + + logger.clear (); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (good, false); + + // if we install a delegate which introduces a tolerance (absolute 1) it will still not match + + dc1->set_parameter_compare_delegate (new db::EqualDeviceParameters (db::DeviceClassResistor::param_id_R, 1.0, 0.0)); + + logger.clear (); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (good, false); + + // if we install a delegate which introduces a tolerance (absolute 2.0) it will match + + dc1->set_parameter_compare_delegate (new db::EqualDeviceParameters (db::DeviceClassResistor::param_id_R, 2.0, 0.0)); + + logger.clear (); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (good, true); + + // removing the comparer will make it non-matching + + dc1->set_parameter_compare_delegate (0); + + logger.clear (); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (good, false); + + // disabling the parameter will make it match too + + dc1->parameter_definition_non_const (db::DeviceClassResistor::param_id_R)->set_is_primary (false); + + logger.clear (); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (good, true); + + // enabling the parameter again will make it mismatch again + + dc1->parameter_definition_non_const (db::DeviceClassResistor::param_id_R)->set_is_primary (true); + + logger.clear (); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (good, false); + + // we can install an ignore handler to make it match again + + dc1->set_parameter_compare_delegate (new db::EqualDeviceParameters (db::DeviceClassResistor::param_id_R, true)); + + logger.clear (); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (good, true); + + // if we enable the L parameter we'll get a mismatch again + + dc1->parameter_definition_non_const (db::DeviceClassResistor::param_id_L)->set_is_primary (true); + + logger.clear (); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (good, false); + + // until we install another tolerance + + *dynamic_cast (dc1->parameter_compare_delegate ()) += db::EqualDeviceParameters (db::DeviceClassResistor::param_id_L, 0.11, 0.0); + + logger.clear (); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (good, true); +} diff --git a/src/lay/lay/doc/about/lvs_ref_global.xml b/src/lay/lay/doc/about/lvs_ref_global.xml index ab05ff346..00e53ebbf 100644 --- a/src/lay/lay/doc/about/lvs_ref_global.xml +++ b/src/lay/lay/doc/about/lvs_ref_global.xml @@ -55,6 +55,24 @@ See Netter#compare for a descrip

See Netter#consider_net_names for a description of that function.

+

"disable_parameter" - Specifies whether to disable a parameter from a given device class for netlisting and default compare

+ +

Usage:

+
    +
  • disable_parameter(device_class_name, parameter_name)
  • +
+

+See Netter#disable_parameter for a description of that function. +

+

"enable_parameter" - Specifies whether to enable a parameter from a given device class for netlisting and default compare

+ +

Usage:

+
    +
  • enable_parameter(device_class_name, parameter_name)
  • +
+

+See Netter#enable_parameter for a description of that function. +

"equivalent_pins" - Marks pins as equivalent

Usage:

@@ -64,6 +82,15 @@ See
Netter#consider_net_n

See Netter#equivalent_pins for a description of that function.

+

"ignore_parameter" - Specifies whether to ignore a parameter from a given device class for the compare

+ +

Usage:

+
    +
  • ignore_parameter(device_class_name, parameter_name)
  • +
+

+See Netter#ignore_parameter for a description of that function. +

"join_symmetric_nets" - Joins symmetric nets of selected circuits on the extracted netlist

Usage:

diff --git a/src/lay/lay/doc/about/lvs_ref_netter.xml b/src/lay/lay/doc/about/lvs_ref_netter.xml index 4b33f3fe5..1d8b68c17 100644 --- a/src/lay/lay/doc/about/lvs_ref_netter.xml +++ b/src/lay/lay/doc/about/lvs_ref_netter.xml @@ -127,6 +127,38 @@ will employ net names to resolve ambiguities. If set to false, ambiguities will be resolved based on the topology alone. Topology resolution is more expensive.

+

"disable_parameter" - Indicates whether to disable a specific parameter for a given device

+ +

Usage:

+
    +
  • disable_parameter(device_class_name, parameter_name)
  • +
+

+Disabling a parameter is the inverse of enable_parameter. Disabling a parameter will +reset the "primary" flag of the parameter. This has several effects - e.g. the parameter will not be +used in device compare during netlist matching by default. +

+This is not a strong concept but rather +a hint for the system. Disabling a parameter for netlist compare without side effects +is possible with the ignore_parameter function. In the same way, tolerance will enable a parameter for +netlist compare regardless of the "primary" status of the parameter. +

+

"enable_parameter" - Indicates whether to enable a specific parameter for a given device

+ +

Usage:

+
    +
  • enable_parameter(device_class_name, parameter_name)
  • +
+

+The parameter is made "primary" which enables further applications - e.g. it is netlisted +for some elements which normally would not print that parameter, and the parameter +is compared in the default device compare scheme during netlist matching. +

+Enabling a parameter is rather a hint for the system and the effects can be controlled +by other means, so this is not a strong concept. For example, once a tolerance is +specified for a parameter, the "primary" flag of the parameter is not considered anymore. +The inverse the this function is disable_parameter. +

"equivalent_pins" - Marks pins as equivalent

Usage:

@@ -154,6 +186,20 @@ case pin names for SPICE netlists.

Use this method andwhere in the script before the compare call.

+

"ignore_parameter" - Skip a specific parameter for a given device class name during device compare

+ +

Usage:

+
    +
  • ignore_parameter(device_class_name, parameter_name)
  • +
+

+Use this function is ignore a parameter for a particular device class during the netlist compare. +Some parameters - for example "L" and "W" parameters of the resistor device - are "secondary" parameters +which are not ignored by default. Using "ignore_parameter" on such devices does not have an effect. +

+"ignore_parameter" and "tolerance" only have an effect with the default device comparer. Using a custom device comparer +will override the definitions by "ignore_parameter" or "tolerance". +

"join_symmetric_nets" - Joins symmetric nets of selected circuits on the extracted netlist

Usage:

@@ -395,5 +441,14 @@ Tolerances can be given in absolute units or relative or both. The relative tolerance is given as a factor, so 0.1 is a 10% tolerance. Absolute and relative tolerances add, so specifying both allows for a larger deviation. +

+Some device parameters - like the resistor's "L" and "W" parameters - are not compared by default. +These are "secondary" device parameters. Using a tolerance on such parameters will make these parameters +being compared even if they are secondary ones. +

+A function is skip a parameter during the device compare is "ignore_parameter". +

+"tolerance" and "ignore_parameter" only have an effect with the default device comparer. Using a custom device comparer +will override the definitions by "ignore_parameter" or "tolerance".

diff --git a/src/lay/lay/doc/manual/lvs_compare.xml b/src/lay/lay/doc/manual/lvs_compare.xml index 5b3fe3073..5593ddddc 100644 --- a/src/lay/lay/doc/manual/lvs_compare.xml +++ b/src/lay/lay/doc/manual/lvs_compare.xml @@ -205,6 +205,53 @@ same_device_classes("POLYRES", nil)
tolerance("NMOS", "L", 0.05, 0.01)
 tolerance("NMOS", "L", :absolute => 0.05, :relative => 0.01)
+

Ignoring parameters

+ +

+ It is possible to ignore certain parameters from certain devices in the netlist compare. + For example, if you don't want to compare the "L" parameter of the "NMOS" devices, use this statement: +

+ +
ignore_parameter("NMOS", "L")
+ +

+ This statement can be put into the script anywhere before the "compare" statement. +

+ +

+ By default, only "primary" parameters are compared. For a resistor for example, "R" is a primary parameter, the other ones + like "L", "W", "A" and "P" are not. Using "tolerance" will implicitly enable a parameter - even if it is not a primary one - while "ignore_parameter" will disable + a parameter for compare - even if it is a primary one. +

+ +

Enabling and disabling parameters

+ +

+ As mentioned before, some device parameters are primary while other are not. For example, for the resistor device, + "R" (the resistance value) is a primary parameter while the device length ("L") is not. You can make the "L" parameter + primary for a device class called "RES" by using: +

+ +
enable_parameter("RES", "L")
+ +

+ This has two effects: first, the "L" parameter is written into the Spice output netlist and in addition it is compared against + the schematic "L" parameter. +

+ +

+ Correspondingly, a primary parameter can be disabled using: +

+ +
disable_parameter("RES", "R")
+ +

+ This behavior is overridden by a "tolerance" or "ignore_parameter" specification for that parameter or if a custom + device comparer is installed. Netlisting is affected only for the elementary devices (R, C and L) and any Spice writer + delegate can choose to ignore the primary flag. A custom device comparer may also ignore this flag. + So after all, enabling or disabling a parameter is not a strong concept but rather a hint. +

+

Pin swapping

diff --git a/src/lay/lay/doc/manual/lvs_device_extractors.xml b/src/lay/lay/doc/manual/lvs_device_extractors.xml index 7cf6449e2..6034741cd 100644 --- a/src/lay/lay/doc/manual/lvs_device_extractors.xml +++ b/src/lay/lay/doc/manual/lvs_device_extractors.xml @@ -362,18 +362,11 @@ extract_devices(bjt3(model_name), { "C" => collector, "B" => base, "E" => emitte

- Parameters can be fully enabled by using "enable_parameter" on the device class. - Hence it is possible to enable "W" and "L" on a resistor type using the following code: -

- -
dc = extract_devices(resistor("RES", 1), ...)
-dc.enable_parameter("W", true)
-dc.enable_parameter("L", true)
-
- -

- This will modify the parameters of the generated device class such that "W" and "L" are - fully enabled parameters. + Parameters can be fully enabled by using enable_parameter + or disabled using disable_parameter. + tolerance can be used to enable a parameter for compare + and to specify a compare tolerance. + ignore_parameter can be used to ignore a parameter in the compare step.

@@ -397,8 +390,7 @@ extract_devices(resistor("RES", 1, MyResistor), ...)

- The effect of this code is the same than the first one, but using a custom - device class opens the option to supply additional parameters for example + Using a custom device class opens the option to supply additional parameters for example or to implement some entirely new device while using the extraction mechanics of the resistor extractor. The only requirement is compatibility of the parameter and terminal definitions. diff --git a/src/lay/lay/doc/manual/lvs_tweaks.xml b/src/lay/lay/doc/manual/lvs_tweaks.xml index 6a8d505f9..65cf68762 100644 --- a/src/lay/lay/doc/manual/lvs_tweaks.xml +++ b/src/lay/lay/doc/manual/lvs_tweaks.xml @@ -1,4 +1,4 @@ - +?xml version="1.0" encoding="UTF-8"?> diff --git a/src/lvs/lvs/built-in-macros/_lvs_engine.rb b/src/lvs/lvs/built-in-macros/_lvs_engine.rb index 3a0506516..704c8d4d3 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_engine.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_engine.rb @@ -183,7 +183,27 @@ module LVS # @synopsis tolerance(device_class_name, parameter_name [, :absolute => absolute_tolerance] [, :relative => relative_tolerance]) # See \Netter#tolerance for a description of that function. - %w(schematic compare join_symmetric_nets tolerance blank_circuit align same_nets same_nets! same_circuits same_device_classes equivalent_pins min_caps max_res max_depth max_branch_complexity consider_net_names).each do |f| + # %LVS% + # @name ignore_parameter + # @brief Specifies whether to ignore a parameter from a given device class for the compare + # @synopsis ignore_parameter(device_class_name, parameter_name) + # See \Netter#ignore_parameter for a description of that function. + + # %LVS% + # @name enable_parameter + # @brief Specifies whether to enable a parameter from a given device class for netlisting and default compare + # @synopsis enable_parameter(device_class_name, parameter_name) + # See \Netter#enable_parameter for a description of that function. + + # %LVS% + # @name disable_parameter + # @brief Specifies whether to disable a parameter from a given device class for netlisting and default compare + # @synopsis disable_parameter(device_class_name, parameter_name) + # See \Netter#disable_parameter for a description of that function. + + %w(schematic compare join_symmetric_nets tolerance ignore_parameter enable_parameter disable_parameter + blank_circuit align same_nets same_nets! same_circuits same_device_classes equivalent_pins + min_caps max_res max_depth max_branch_complexity consider_net_names).each do |f| eval <<"CODE" def #{f}(*args) _netter.#{f}(*args) diff --git a/src/lvs/lvs/built-in-macros/_lvs_netter.rb b/src/lvs/lvs/built-in-macros/_lvs_netter.rb index 5c34685a3..c5fc9b3f3 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_netter.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_netter.rb @@ -110,6 +110,15 @@ module LVS # The relative tolerance is given as a factor, so 0.1 is a 10% tolerance. # Absolute and relative tolerances add, so specifying both allows for a larger # deviation. + # + # Some device parameters - like the resistor's "L" and "W" parameters - are not compared by default. + # These are "secondary" device parameters. Using a tolerance on such parameters will make these parameters + # being compared even if they are secondary ones. + # + # A function is skip a parameter during the device compare is "ignore_parameter". + # + # "tolerance" and "ignore_parameter" only have an effect with the default device comparer. Using a custom device comparer + # will override the definitions by "ignore_parameter" or "tolerance". def tolerance(device_class_name, parameter_name, *args) @@ -164,6 +173,99 @@ module LVS end + # %LVS% + # @name ignore_parameter + # @brief Skip a specific parameter for a given device class name during device compare + # @synopsis ignore_parameter(device_class_name, parameter_name) + # + # Use this function is ignore a parameter for a particular device class during the netlist compare. + # Some parameters - for example "L" and "W" parameters of the resistor device - are "secondary" parameters + # which are not ignored by default. Using "ignore_parameter" on such devices does not have an effect. + # + # "ignore_parameter" and "tolerance" only have an effect with the default device comparer. Using a custom device comparer + # will override the definitions by "ignore_parameter" or "tolerance". + + def ignore_parameter(device_class_name, parameter_name) + + device_class_name.is_a?(String) || raise("Device class argument of 'ignore_parameter' must be a string") + parameter_name.is_a?(String) || raise("Parameter name argument of 'ignore_parameter' must be a string") + + if self._l2n_data + # already extracted + self._ignore_parameter(self._l2n_data, device_class_name, parameter_name) + else + @post_extract_config << lambda { |l2n| self._ignore_parameter(l2n, device_class_name, parameter_name) } + end + + end + + def _ignore_parameter(l2n, device_class_name, parameter_name) + + dc = l2n.netlist.device_class_by_name(device_class_name) + if dc && dc.has_parameter?(parameter_name) + ep = RBA::EqualDeviceParameters::ignore(dc.parameter_id(parameter_name)) + if dc.equal_parameters == nil + dc.equal_parameters = ep + else + dc.equal_parameters += ep + end + end + + end + + # %LVS% + # @name enable_parameter + # @brief Indicates whether to enable a specific parameter for a given device + # @synopsis enable_parameter(device_class_name, parameter_name) + # The parameter is made "primary" which enables further applications - e.g. it is netlisted + # for some elements which normally would not print that parameter, and the parameter + # is compared in the default device compare scheme during netlist matching. + # + # Enabling a parameter is rather a hint for the system and the effects can be controlled + # by other means, so this is not a strong concept. For example, once a \tolerance is + # specified for a parameter, the "primary" flag of the parameter is not considered anymore. + # The inverse the this function is \disable_parameter. + + # %LVS% + # @name disable_parameter + # @brief Indicates whether to disable a specific parameter for a given device + # @synopsis disable_parameter(device_class_name, parameter_name) + # Disabling a parameter is the inverse of \enable_parameter. Disabling a parameter will + # reset the "primary" flag of the parameter. This has several effects - e.g. the parameter will not be + # used in device compare during netlist matching by default. + # + # This is not a strong concept but rather + # a hint for the system. Disabling a parameter for netlist compare without side effects + # is possible with the \ignore_parameter function. In the same way, \tolerance will enable a parameter for + # netlist compare regardless of the "primary" status of the parameter. + + [ :enable_parameter, :disable_parameter ].each do |mn| + eval <<"CODE" + def #{mn}(device_class_name, parameter_name) + + device_class_name.is_a?(String) || raise("Device class argument of '#{mn}' must be a string") + parameter_name.is_a?(String) || raise("Parameter name argument of '#{mn}' must be a string") + + if self._l2n_data + # already extracted + self._enable_parameter(self._l2n_data, device_class_name, parameter_name, :#{mn} == :enable_parameter) + else + @post_extract_config << lambda { |l2n| self._enable_parameter(l2n, device_class_name, parameter_name, :#{mn} == :enable_parameter) } + end + + end +CODE + end + + def _enable_parameter(l2n, device_class_name, parameter_name, enable) + + dc = l2n.netlist.device_class_by_name(device_class_name) + if dc && dc.has_parameter?(parameter_name) + dc.enable_parameter(parameter_name, enable) + end + + end + # %LVS% # @name align # @brief Aligns the extracted netlist vs. the schematic diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index bb8e3b4c6..880a5d50b 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -261,3 +261,10 @@ TEST(28_BlackBoxDevicesWithBlank) run_test (_this, "bbdevices5b", "bbdevices5.gds"); run_test (_this, "bbdevices6b", "bbdevices6.gds"); } + +TEST(29_DeviceCombineAndTolerances) +{ + run_test (_this, "res_combine1", "res_combine.gds"); + run_test (_this, "res_combine2", "res_combine.gds"); + run_test (_this, "res_combine3", "res_combine.gds"); +} diff --git a/src/lvs/unit_tests/lvsTests.cc b/src/lvs/unit_tests/lvsTests.cc index 50fb183c5..70a865243 100644 --- a/src/lvs/unit_tests/lvsTests.cc +++ b/src/lvs/unit_tests/lvsTests.cc @@ -145,7 +145,7 @@ TEST(15_private) TEST(16_private) { - // test_is_long_runner (); + // test_is_long_runner ();lvs-blackbox run_test (_this, "test_16.lvs", "test_16.cir.gz", "test_16.gds.gz", true); } diff --git a/testdata/lvs/res_combine.gds b/testdata/lvs/res_combine.gds new file mode 100644 index 000000000..720f12b40 Binary files /dev/null and b/testdata/lvs/res_combine.gds differ diff --git a/testdata/lvs/res_combine1.cir b/testdata/lvs/res_combine1.cir new file mode 100644 index 000000000..edae0acfc --- /dev/null +++ b/testdata/lvs/res_combine1.cir @@ -0,0 +1,7 @@ +* Extracted by KLayout + +* cell Res2 +.SUBCKT Res2 +* device instance $1 r0 *1 110.14,51.795 RPP1 +R$1 1 2 95 RPP1 L=420U W=2.21052631579U +.ENDS Res2 diff --git a/testdata/lvs/res_combine1.lvs b/testdata/lvs/res_combine1.lvs new file mode 100644 index 000000000..c1865dee1 --- /dev/null +++ b/testdata/lvs/res_combine1.lvs @@ -0,0 +1,96 @@ +source($lvs_test_source) +report_lvs($lvs_test_target_lvsdb, true) +target_netlist($lvs_test_target_cir, write_spice, "Extracted by KLayout") + +schematic("res_combine_schematic.cir") +deep + +# ------------------------------------------------------------------- +# Layers + +pimp = input(7, 0) +poly_dg = input(13, 0) +cont = input(15, 0) +met1_dg = input(16, 0) +sblk = input(34, 0) +rp_1 = sblk & poly_dg +rpp1 = rp_1 & pimp +p1trm = poly_dg - rpp1 + +class ResistorExtractor < RBA::GenericDeviceExtractor + + def initialize(name, sheet_rho) + self.name = name + @sheet_rho = sheet_rho + end + + def setup + define_layer("C", "Conductor") + define_layer("R", "Resistor") + register_device_class(RBA::DeviceClassResistor::new) + end + + def get_connectivity(layout, layers) + # this "connectivity" forms the shape clusters that make up the device + conn = RBA::Connectivity::new + conn.connect(layers[0], layers[1]) # collect touching contacts + conn.connect(layers[1], layers[1]) # combine resistor shapes into one area + conn + end + + def extract_devices(layer_geometry) + # layer_geometry provides the input layers in the order they are defined with "define_layer" + conductor = layer_geometry[0] + resistor = layer_geometry[1] + + resistor_regions = resistor.merged + + resistor_regions.each do |r| + terminals = conductor.interacting(resistor) + if terminals.size != 2 + error("Resistor shape does not touch marker border in exactly two places", r) + else + double_width = 0 + (terminals.edges & resistor.edges).merged.each do |e| + double_width += e.length + end + # A = L*W + # -> L = A/W + a = r.area*dbu*dbu + w = (double_width / 2.0)*dbu + l = a / w + + device = create_device + device.set_parameter(RBA::DeviceClassResistor::PARAM_R, @sheet_rho * l / w); + + device.set_parameter(RBA::DeviceClassResistor::PARAM_A, a) + device.set_parameter(RBA::DeviceClassResistor::PARAM_L, l) + device.set_parameter(RBA::DeviceClassResistor::PARAM_P, 2*l+2*w) + device.set_parameter(RBA::DeviceClassResistor::PARAM_W, w) + define_terminal(device, RBA::DeviceClassResistor::TERMINAL_A, 0, terminals[0]); + define_terminal(device, RBA::DeviceClassResistor::TERMINAL_B, 0, terminals[1]); + end + end + end +end + +# enable/disable +enable_parameter("RPP1", "L") +enable_parameter("RPP1", "W") +disable_parameter("RPP1", "R") + +extract_devices(ResistorExtractor::new("RPP1", 0.5), # intentionally wrong: 1565.15/5 + { "C" => p1trm, "R" => rpp1 }) + +connect(met1_dg, cont) +connect(p1trm, cont) + +netlist.flatten_circuit("Res1") +schematic.flatten_circuit("RES1") + +schematic.simplify + +# Netlist vs. netlist +align +netlist.simplify +compare diff --git a/testdata/lvs/res_combine1.lvsdb.1 b/testdata/lvs/res_combine1.lvsdb.1 new file mode 100644 index 000000000..971e6ddb9 --- /dev/null +++ b/testdata/lvs/res_combine1.lvsdb.1 @@ -0,0 +1,197 @@ +#%lvsdb-klayout + +# Layout +layout( + top(Res2) + unit(0.001) + + # Layer section + # This section lists the mask layers (drawing or derived) and their connections. + + # Mask layers + layer(l4 '15/0') + layer(l3 '16/0') + layer(l1) + + # Mask layer connectivity + connect(l4 l4 l3 l1) + connect(l3 l4 l3) + connect(l1 l4 l1) + + # Device class section + class(RPP1 RES + param(R 0 0) + param(L 1 0) + param(W 1 0) + ) + + # Device abstracts section + # Device abstracts list the pin shapes of the devices. + device(D$RPP1 RPP1 + terminal(A + rect(l1 (0 0) (540 2000)) + ) + terminal(B + rect(l1 (10540 0) (540 2000)) + ) + ) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(Res2 + + # Circuit boundary + rect((8285 720) (117975 57350)) + + # Nets with their geometries + net(1 + rect(l4 (120580 32490) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l4 (-22355 1390) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l4 (-21520 1755) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l3 (0 -4795) (1065 5850)) + rect(l3 (-5155 -6930) (52985 1475)) + rect(l3 (-27585 -395) (1065 5850)) + rect(l3 (20990 -5850) (1065 5850)) + rect(l3 (-1275 -1760) (340 1920)) + rect(l3 (-22475 -1890) (340 1920)) + rect(l3 (-21640 -1525) (340 1920)) + rect(l1 (42935 -2385) (540 2000)) + rect(l1 (-22675 -1970) (540 2000)) + rect(l1 (-21840 -1605) (540 2000)) + ) + net(2 + rect(l4 (19795 5575) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l3 (-195 -995) (2395 2500)) + rect(l3 (-2480 -1785) (340 1920)) + rect(l1 (-500 -1960) (540 2000)) + ) + + # Devices and their connections + device(1 D$RPP1 + device(D$RPP1 location(30 -3970)) + device(D$RPP1 location(-65 -8175)) + device(D$RPP1 location(-205 -12595)) + device(D$RPP1 location(-225 -16825)) + device(D$RPP1 location(-320 -20985)) + device(D$RPP1 location(-22135 30)) + device(D$RPP1 location(-22105 -3940)) + device(D$RPP1 location(-22200 -8145)) + device(D$RPP1 location(-22340 -12565)) + device(D$RPP1 location(-22360 -16795)) + device(D$RPP1 location(-22455 -20955)) + device(D$RPP1 location(-43435 425)) + device(D$RPP1 location(-43405 -3545)) + device(D$RPP1 location(-43500 -7750)) + device(D$RPP1 location(-43640 -12170)) + device(D$RPP1 location(-43660 -16400)) + device(D$RPP1 location(-43755 -20560)) + device(D$RPP1 location(-100755 -30885)) + device(D$RPP1 location(-100850 -35090)) + device(D$RPP1 location(-100990 -39510)) + device(D$RPP1 location(-101010 -43740)) + device(D$RPP1 location(-101105 -47900)) + device(D$RPP1 location(-81920 -3545)) + device(D$RPP1 location(-82015 -7750)) + device(D$RPP1 location(-82155 -12170)) + device(D$RPP1 location(-82175 -16400)) + device(D$RPP1 location(-63835 -30780)) + device(D$RPP1 location(-63930 -34985)) + device(D$RPP1 location(-64070 -39405)) + device(D$RPP1 location(-64090 -43635)) + device(D$RPP1 location(-82380 -26810)) + device(D$RPP1 location(-82270 -20560)) + device(D$RPP1 location(-82350 -30780)) + device(D$RPP1 location(-82445 -34985)) + device(D$RPP1 location(-82585 -39405)) + device(D$RPP1 location(-82605 -43635)) + device(D$RPP1 location(-82700 -47795)) + device(D$RPP1 location(-64185 -47795)) + device(D$RPP1 location(-63435 640)) + device(D$RPP1 location(-63405 -3330)) + device(D$RPP1 location(-63500 -7535)) + device(D$RPP1 location(-63640 -11955)) + device(D$RPP1 location(-63660 -16185)) + device(D$RPP1 location(-63755 -20345)) + device(D$RPP1 location(-63865 -26810)) + device(D$RPP1 location(-100465 855)) + device(D$RPP1 location(-81950 425)) + device(D$RPP1 location(-100435 -3115)) + device(D$RPP1 location(-100530 -7320)) + device(D$RPP1 location(-100670 -11740)) + device(D$RPP1 location(-100690 -15970)) + device(D$RPP1 location(-100785 -20130)) + device(D$RPP1 location(-100785 -26915)) + connect(5 A B) + connect(11 A B) + connect(17 A B) + connect(22 B B) + location(110140 51795) + param(R 95) + param(L 420) + param(W 2.21052631579) + param(A 1080) + param(P 1296) + terminal(A 1) + terminal(B 2) + ) + + ) +) + +# Reference netlist +reference( + + # Device class section + class(RPP1 RES) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(RES2 + + # Nets + net(1 name(GND)) + net(2 name(VDD)) + + # Outgoing pins and their connections to nets + pin(1 name(GND)) + pin(2 name(VDD)) + + # Devices and their connections + device(1 RPP1 + name(I0.I106.R0) + param(R 59475.7) + param(L 420) + param(W 2.21052631579) + param(A 0) + param(P 0) + terminal(A 2) + terminal(B 1) + ) + + ) +) + +# Cross reference +xref( + circuit(Res2 RES2 match + xref( + net(1 1 warning) + net(2 2 warning) + pin(() 0 match) + pin(() 1 match) + device(1 1 match) + ) + ) +) diff --git a/testdata/lvs/res_combine1.lvsdb.2 b/testdata/lvs/res_combine1.lvsdb.2 new file mode 100644 index 000000000..a482e6a93 --- /dev/null +++ b/testdata/lvs/res_combine1.lvsdb.2 @@ -0,0 +1,197 @@ +#%lvsdb-klayout + +# Layout +layout( + top(Res2) + unit(0.001) + + # Layer section + # This section lists the mask layers (drawing or derived) and their connections. + + # Mask layers + layer(l4 '15/0') + layer(l3 '16/0') + layer(l1) + + # Mask layer connectivity + connect(l4 l4 l3 l1) + connect(l3 l4 l3) + connect(l1 l4 l1) + + # Device class section + class(RPP1 RES + param(R 0 0) + param(L 1 0) + param(W 1 0) + ) + + # Device abstracts section + # Device abstracts list the pin shapes of the devices. + device(D$RPP1 RPP1 + terminal(A + rect(l1 (0 0) (540 2000)) + ) + terminal(B + rect(l1 (10540 0) (540 2000)) + ) + ) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(Res2 + + # Circuit boundary + rect((8285 720) (117975 57350)) + + # Nets with their geometries + net(1 + rect(l4 (120580 32490) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l4 (-22355 1390) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l4 (-21520 1755) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l3 (0 -4795) (1065 5850)) + rect(l3 (-5155 -6930) (52985 1475)) + rect(l3 (-27585 -395) (1065 5850)) + rect(l3 (20990 -5850) (1065 5850)) + rect(l3 (-1275 -1760) (340 1920)) + rect(l3 (-22475 -1890) (340 1920)) + rect(l3 (-21640 -1525) (340 1920)) + rect(l1 (42935 -2385) (540 2000)) + rect(l1 (-22675 -1970) (540 2000)) + rect(l1 (-21840 -1605) (540 2000)) + ) + net(2 + rect(l4 (19795 5575) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l3 (-195 -995) (2395 2500)) + rect(l3 (-2480 -1785) (340 1920)) + rect(l1 (-500 -1960) (540 2000)) + ) + + # Devices and their connections + device(1 D$RPP1 + device(D$RPP1 location(30 -3970)) + device(D$RPP1 location(-205 -12595)) + device(D$RPP1 location(-225 -16825)) + device(D$RPP1 location(-320 -20985)) + device(D$RPP1 location(-65 -8175)) + device(D$RPP1 location(-22135 30)) + device(D$RPP1 location(-22105 -3940)) + device(D$RPP1 location(-22340 -12565)) + device(D$RPP1 location(-22360 -16795)) + device(D$RPP1 location(-22455 -20955)) + device(D$RPP1 location(-22200 -8145)) + device(D$RPP1 location(-43435 425)) + device(D$RPP1 location(-43405 -3545)) + device(D$RPP1 location(-43640 -12170)) + device(D$RPP1 location(-43660 -16400)) + device(D$RPP1 location(-43755 -20560)) + device(D$RPP1 location(-43500 -7750)) + device(D$RPP1 location(-100755 -30885)) + device(D$RPP1 location(-100990 -39510)) + device(D$RPP1 location(-101010 -43740)) + device(D$RPP1 location(-101105 -47900)) + device(D$RPP1 location(-100850 -35090)) + device(D$RPP1 location(-81920 -3545)) + device(D$RPP1 location(-82155 -12170)) + device(D$RPP1 location(-82175 -16400)) + device(D$RPP1 location(-63835 -30780)) + device(D$RPP1 location(-64070 -39405)) + device(D$RPP1 location(-64090 -43635)) + device(D$RPP1 location(-82380 -26810)) + device(D$RPP1 location(-82270 -20560)) + device(D$RPP1 location(-82350 -30780)) + device(D$RPP1 location(-82585 -39405)) + device(D$RPP1 location(-82605 -43635)) + device(D$RPP1 location(-82700 -47795)) + device(D$RPP1 location(-64185 -47795)) + device(D$RPP1 location(-82445 -34985)) + device(D$RPP1 location(-63930 -34985)) + device(D$RPP1 location(-63435 640)) + device(D$RPP1 location(-63405 -3330)) + device(D$RPP1 location(-63640 -11955)) + device(D$RPP1 location(-63660 -16185)) + device(D$RPP1 location(-63755 -20345)) + device(D$RPP1 location(-63865 -26810)) + device(D$RPP1 location(-63500 -7535)) + device(D$RPP1 location(-82015 -7750)) + device(D$RPP1 location(-100465 855)) + device(D$RPP1 location(-81950 425)) + device(D$RPP1 location(-100435 -3115)) + device(D$RPP1 location(-100670 -11740)) + device(D$RPP1 location(-100690 -15970)) + device(D$RPP1 location(-100785 -20130)) + device(D$RPP1 location(-100785 -26915)) + device(D$RPP1 location(-100530 -7320)) + connect(4 A B) + connect(10 A B) + connect(16 A B) + connect(21 B B) + location(110140 51795) + param(R 95) + param(L 420) + param(W 2.21052631579) + param(A 1080) + param(P 1296) + terminal(A 1) + terminal(B 2) + ) + + ) +) + +# Reference netlist +reference( + + # Device class section + class(RPP1 RES) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(RES2 + + # Nets + net(1 name(GND)) + net(2 name(VDD)) + + # Outgoing pins and their connections to nets + pin(1 name(GND)) + pin(2 name(VDD)) + + # Devices and their connections + device(1 RPP1 + name(I0.I106.R0) + param(R 59475.7) + param(L 420) + param(W 2.21052631579) + param(A 0) + param(P 0) + terminal(A 2) + terminal(B 1) + ) + + ) +) + +# Cross reference +xref( + circuit(Res2 RES2 match + xref( + net(1 1 warning) + net(2 2 warning) + pin(() 0 match) + pin(() 1 match) + device(1 1 match) + ) + ) +) diff --git a/testdata/lvs/res_combine2.cir b/testdata/lvs/res_combine2.cir new file mode 100644 index 000000000..650763fba --- /dev/null +++ b/testdata/lvs/res_combine2.cir @@ -0,0 +1,7 @@ +* Extracted by KLayout + +* cell Res2 +.SUBCKT Res2 +* device instance $1 r0 *1 110.14,51.795 RPP1 +R$1 1 2 95 RPP1 +.ENDS Res2 diff --git a/testdata/lvs/res_combine2.lvs b/testdata/lvs/res_combine2.lvs new file mode 100644 index 000000000..d81811540 --- /dev/null +++ b/testdata/lvs/res_combine2.lvs @@ -0,0 +1,96 @@ +source($lvs_test_source) +report_lvs($lvs_test_target_lvsdb, true) +target_netlist($lvs_test_target_cir, write_spice, "Extracted by KLayout") + +schematic("res_combine_schematic.cir") +deep + +# ------------------------------------------------------------------- +# Layers + +pimp = input(7, 0) +poly_dg = input(13, 0) +cont = input(15, 0) +met1_dg = input(16, 0) +sblk = input(34, 0) +rp_1 = sblk & poly_dg +rpp1 = rp_1 & pimp +p1trm = poly_dg - rpp1 + +class ResistorExtractor < RBA::GenericDeviceExtractor + + def initialize(name, sheet_rho) + self.name = name + @sheet_rho = sheet_rho + end + + def setup + define_layer("C", "Conductor") + define_layer("R", "Resistor") + register_device_class(RBA::DeviceClassResistor::new) + end + + def get_connectivity(layout, layers) + # this "connectivity" forms the shape clusters that make up the device + conn = RBA::Connectivity::new + conn.connect(layers[0], layers[1]) # collect touching contacts + conn.connect(layers[1], layers[1]) # combine resistor shapes into one area + conn + end + + def extract_devices(layer_geometry) + # layer_geometry provides the input layers in the order they are defined with "define_layer" + conductor = layer_geometry[0] + resistor = layer_geometry[1] + + resistor_regions = resistor.merged + + resistor_regions.each do |r| + terminals = conductor.interacting(resistor) + if terminals.size != 2 + error("Resistor shape does not touch marker border in exactly two places", r) + else + double_width = 0 + (terminals.edges & resistor.edges).merged.each do |e| + double_width += e.length + end + # A = L*W + # -> L = A/W + a = r.area*dbu*dbu + w = (double_width / 2.0)*dbu + l = a / w + + device = create_device + device.set_parameter(RBA::DeviceClassResistor::PARAM_R, @sheet_rho * l / w); + + device.set_parameter(RBA::DeviceClassResistor::PARAM_A, a) + device.set_parameter(RBA::DeviceClassResistor::PARAM_L, l) + device.set_parameter(RBA::DeviceClassResistor::PARAM_P, 2*l+2*w) + device.set_parameter(RBA::DeviceClassResistor::PARAM_W, w) + define_terminal(device, RBA::DeviceClassResistor::TERMINAL_A, 0, terminals[0]); + define_terminal(device, RBA::DeviceClassResistor::TERMINAL_B, 0, terminals[1]); + end + end + end +end + +# tolerance/ignore +tolerance("RPP1", "W", 1e-5) +tolerance("RPP1", "L", 1e-5) +ignore_parameter("RPP1", "R") + +extract_devices(ResistorExtractor::new("RPP1", 0.5), # intentionally wrong: 1565.15/5 + { "C" => p1trm, "R" => rpp1 }) + +connect(met1_dg, cont) +connect(p1trm, cont) + +netlist.flatten_circuit("Res1") +schematic.flatten_circuit("RES1") + +schematic.simplify + +# Netlist vs. netlist +align +netlist.simplify +compare diff --git a/testdata/lvs/res_combine2.lvsdb.1 b/testdata/lvs/res_combine2.lvsdb.1 new file mode 100644 index 000000000..7c5410036 --- /dev/null +++ b/testdata/lvs/res_combine2.lvsdb.1 @@ -0,0 +1,193 @@ +#%lvsdb-klayout + +# Layout +layout( + top(Res2) + unit(0.001) + + # Layer section + # This section lists the mask layers (drawing or derived) and their connections. + + # Mask layers + layer(l4 '15/0') + layer(l3 '16/0') + layer(l1) + + # Mask layer connectivity + connect(l4 l4 l3 l1) + connect(l3 l4 l3) + connect(l1 l4 l1) + + # Device class section + class(RPP1 RES) + + # Device abstracts section + # Device abstracts list the pin shapes of the devices. + device(D$RPP1 RPP1 + terminal(A + rect(l1 (0 0) (540 2000)) + ) + terminal(B + rect(l1 (10540 0) (540 2000)) + ) + ) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(Res2 + + # Circuit boundary + rect((8285 720) (117975 57350)) + + # Nets with their geometries + net(1 + rect(l4 (120580 32490) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l4 (-22355 1390) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l4 (-21520 1755) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l3 (0 -4795) (1065 5850)) + rect(l3 (-5155 -6930) (52985 1475)) + rect(l3 (-27585 -395) (1065 5850)) + rect(l3 (20990 -5850) (1065 5850)) + rect(l3 (-1275 -1760) (340 1920)) + rect(l3 (-22475 -1890) (340 1920)) + rect(l3 (-21640 -1525) (340 1920)) + rect(l1 (42935 -2385) (540 2000)) + rect(l1 (-22675 -1970) (540 2000)) + rect(l1 (-21840 -1605) (540 2000)) + ) + net(2 + rect(l4 (19795 5575) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l3 (-195 -995) (2395 2500)) + rect(l3 (-2480 -1785) (340 1920)) + rect(l1 (-500 -1960) (540 2000)) + ) + + # Devices and their connections + device(1 D$RPP1 + device(D$RPP1 location(30 -3970)) + device(D$RPP1 location(-65 -8175)) + device(D$RPP1 location(-205 -12595)) + device(D$RPP1 location(-225 -16825)) + device(D$RPP1 location(-320 -20985)) + device(D$RPP1 location(-22135 30)) + device(D$RPP1 location(-22105 -3940)) + device(D$RPP1 location(-22200 -8145)) + device(D$RPP1 location(-22340 -12565)) + device(D$RPP1 location(-22360 -16795)) + device(D$RPP1 location(-22455 -20955)) + device(D$RPP1 location(-43435 425)) + device(D$RPP1 location(-43405 -3545)) + device(D$RPP1 location(-43500 -7750)) + device(D$RPP1 location(-43640 -12170)) + device(D$RPP1 location(-43660 -16400)) + device(D$RPP1 location(-43755 -20560)) + device(D$RPP1 location(-100755 -30885)) + device(D$RPP1 location(-100850 -35090)) + device(D$RPP1 location(-100990 -39510)) + device(D$RPP1 location(-101010 -43740)) + device(D$RPP1 location(-101105 -47900)) + device(D$RPP1 location(-81920 -3545)) + device(D$RPP1 location(-82015 -7750)) + device(D$RPP1 location(-82155 -12170)) + device(D$RPP1 location(-82175 -16400)) + device(D$RPP1 location(-63835 -30780)) + device(D$RPP1 location(-63930 -34985)) + device(D$RPP1 location(-64070 -39405)) + device(D$RPP1 location(-64090 -43635)) + device(D$RPP1 location(-82380 -26810)) + device(D$RPP1 location(-82270 -20560)) + device(D$RPP1 location(-82350 -30780)) + device(D$RPP1 location(-82445 -34985)) + device(D$RPP1 location(-82585 -39405)) + device(D$RPP1 location(-82605 -43635)) + device(D$RPP1 location(-82700 -47795)) + device(D$RPP1 location(-64185 -47795)) + device(D$RPP1 location(-63435 640)) + device(D$RPP1 location(-63405 -3330)) + device(D$RPP1 location(-63500 -7535)) + device(D$RPP1 location(-63640 -11955)) + device(D$RPP1 location(-63660 -16185)) + device(D$RPP1 location(-63755 -20345)) + device(D$RPP1 location(-63865 -26810)) + device(D$RPP1 location(-100465 855)) + device(D$RPP1 location(-81950 425)) + device(D$RPP1 location(-100435 -3115)) + device(D$RPP1 location(-100530 -7320)) + device(D$RPP1 location(-100670 -11740)) + device(D$RPP1 location(-100690 -15970)) + device(D$RPP1 location(-100785 -20130)) + device(D$RPP1 location(-100785 -26915)) + connect(5 A B) + connect(11 A B) + connect(17 A B) + connect(22 B B) + location(110140 51795) + param(R 95) + param(L 420) + param(W 2.21052631579) + param(A 1080) + param(P 1296) + terminal(A 1) + terminal(B 2) + ) + + ) +) + +# Reference netlist +reference( + + # Device class section + class(RPP1 RES) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(RES2 + + # Nets + net(1 name(GND)) + net(2 name(VDD)) + + # Outgoing pins and their connections to nets + pin(1 name(GND)) + pin(2 name(VDD)) + + # Devices and their connections + device(1 RPP1 + name(I0.I106.R0) + param(R 59475.7) + param(L 420) + param(W 2.21052631579) + param(A 0) + param(P 0) + terminal(A 2) + terminal(B 1) + ) + + ) +) + +# Cross reference +xref( + circuit(Res2 RES2 match + xref( + net(1 1 warning) + net(2 2 warning) + pin(() 0 match) + pin(() 1 match) + device(1 1 match) + ) + ) +) diff --git a/testdata/lvs/res_combine2.lvsdb.2 b/testdata/lvs/res_combine2.lvsdb.2 new file mode 100644 index 000000000..9e6fd6e42 --- /dev/null +++ b/testdata/lvs/res_combine2.lvsdb.2 @@ -0,0 +1,193 @@ +#%lvsdb-klayout + +# Layout +layout( + top(Res2) + unit(0.001) + + # Layer section + # This section lists the mask layers (drawing or derived) and their connections. + + # Mask layers + layer(l4 '15/0') + layer(l3 '16/0') + layer(l1) + + # Mask layer connectivity + connect(l4 l4 l3 l1) + connect(l3 l4 l3) + connect(l1 l4 l1) + + # Device class section + class(RPP1 RES) + + # Device abstracts section + # Device abstracts list the pin shapes of the devices. + device(D$RPP1 RPP1 + terminal(A + rect(l1 (0 0) (540 2000)) + ) + terminal(B + rect(l1 (10540 0) (540 2000)) + ) + ) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(Res2 + + # Circuit boundary + rect((8285 720) (117975 57350)) + + # Nets with their geometries + net(1 + rect(l4 (120580 32490) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l4 (-22355 1390) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l4 (-21520 1755) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l3 (0 -4795) (1065 5850)) + rect(l3 (-5155 -6930) (52985 1475)) + rect(l3 (-27585 -395) (1065 5850)) + rect(l3 (20990 -5850) (1065 5850)) + rect(l3 (-1275 -1760) (340 1920)) + rect(l3 (-22475 -1890) (340 1920)) + rect(l3 (-21640 -1525) (340 1920)) + rect(l1 (42935 -2385) (540 2000)) + rect(l1 (-22675 -1970) (540 2000)) + rect(l1 (-21840 -1605) (540 2000)) + ) + net(2 + rect(l4 (19795 5575) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l3 (-195 -995) (2395 2500)) + rect(l3 (-2480 -1785) (340 1920)) + rect(l1 (-500 -1960) (540 2000)) + ) + + # Devices and their connections + device(1 D$RPP1 + device(D$RPP1 location(30 -3970)) + device(D$RPP1 location(-205 -12595)) + device(D$RPP1 location(-225 -16825)) + device(D$RPP1 location(-320 -20985)) + device(D$RPP1 location(-65 -8175)) + device(D$RPP1 location(-22135 30)) + device(D$RPP1 location(-22105 -3940)) + device(D$RPP1 location(-22340 -12565)) + device(D$RPP1 location(-22360 -16795)) + device(D$RPP1 location(-22455 -20955)) + device(D$RPP1 location(-22200 -8145)) + device(D$RPP1 location(-43435 425)) + device(D$RPP1 location(-43405 -3545)) + device(D$RPP1 location(-43640 -12170)) + device(D$RPP1 location(-43660 -16400)) + device(D$RPP1 location(-43755 -20560)) + device(D$RPP1 location(-43500 -7750)) + device(D$RPP1 location(-100755 -30885)) + device(D$RPP1 location(-100990 -39510)) + device(D$RPP1 location(-101010 -43740)) + device(D$RPP1 location(-101105 -47900)) + device(D$RPP1 location(-100850 -35090)) + device(D$RPP1 location(-81920 -3545)) + device(D$RPP1 location(-82155 -12170)) + device(D$RPP1 location(-82175 -16400)) + device(D$RPP1 location(-63835 -30780)) + device(D$RPP1 location(-64070 -39405)) + device(D$RPP1 location(-64090 -43635)) + device(D$RPP1 location(-82380 -26810)) + device(D$RPP1 location(-82270 -20560)) + device(D$RPP1 location(-82350 -30780)) + device(D$RPP1 location(-82585 -39405)) + device(D$RPP1 location(-82605 -43635)) + device(D$RPP1 location(-82700 -47795)) + device(D$RPP1 location(-64185 -47795)) + device(D$RPP1 location(-82445 -34985)) + device(D$RPP1 location(-63930 -34985)) + device(D$RPP1 location(-63435 640)) + device(D$RPP1 location(-63405 -3330)) + device(D$RPP1 location(-63640 -11955)) + device(D$RPP1 location(-63660 -16185)) + device(D$RPP1 location(-63755 -20345)) + device(D$RPP1 location(-63865 -26810)) + device(D$RPP1 location(-63500 -7535)) + device(D$RPP1 location(-82015 -7750)) + device(D$RPP1 location(-100465 855)) + device(D$RPP1 location(-81950 425)) + device(D$RPP1 location(-100435 -3115)) + device(D$RPP1 location(-100670 -11740)) + device(D$RPP1 location(-100690 -15970)) + device(D$RPP1 location(-100785 -20130)) + device(D$RPP1 location(-100785 -26915)) + device(D$RPP1 location(-100530 -7320)) + connect(4 A B) + connect(10 A B) + connect(16 A B) + connect(21 B B) + location(110140 51795) + param(R 95) + param(L 420) + param(W 2.21052631579) + param(A 1080) + param(P 1296) + terminal(A 1) + terminal(B 2) + ) + + ) +) + +# Reference netlist +reference( + + # Device class section + class(RPP1 RES) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(RES2 + + # Nets + net(1 name(GND)) + net(2 name(VDD)) + + # Outgoing pins and their connections to nets + pin(1 name(GND)) + pin(2 name(VDD)) + + # Devices and their connections + device(1 RPP1 + name(I0.I106.R0) + param(R 59475.7) + param(L 420) + param(W 2.21052631579) + param(A 0) + param(P 0) + terminal(A 2) + terminal(B 1) + ) + + ) +) + +# Cross reference +xref( + circuit(Res2 RES2 match + xref( + net(1 1 warning) + net(2 2 warning) + pin(() 0 match) + pin(() 1 match) + device(1 1 match) + ) + ) +) diff --git a/testdata/lvs/res_combine3.cir b/testdata/lvs/res_combine3.cir new file mode 100644 index 000000000..650763fba --- /dev/null +++ b/testdata/lvs/res_combine3.cir @@ -0,0 +1,7 @@ +* Extracted by KLayout + +* cell Res2 +.SUBCKT Res2 +* device instance $1 r0 *1 110.14,51.795 RPP1 +R$1 1 2 95 RPP1 +.ENDS Res2 diff --git a/testdata/lvs/res_combine3.lvs b/testdata/lvs/res_combine3.lvs new file mode 100644 index 000000000..c957acf47 --- /dev/null +++ b/testdata/lvs/res_combine3.lvs @@ -0,0 +1,91 @@ +source($lvs_test_source) +report_lvs($lvs_test_target_lvsdb, true) +target_netlist($lvs_test_target_cir, write_spice, "Extracted by KLayout") + +schematic("res_combine_schematic.cir") +deep + +# ------------------------------------------------------------------- +# Layers + +pimp = input(7, 0) +poly_dg = input(13, 0) +cont = input(15, 0) +met1_dg = input(16, 0) +sblk = input(34, 0) +rp_1 = sblk & poly_dg +rpp1 = rp_1 & pimp +p1trm = poly_dg - rpp1 + +class ResistorExtractor < RBA::GenericDeviceExtractor + + def initialize(name, sheet_rho) + self.name = name + @sheet_rho = sheet_rho + end + + def setup + define_layer("C", "Conductor") + define_layer("R", "Resistor") + register_device_class(RBA::DeviceClassResistor::new) + end + + def get_connectivity(layout, layers) + # this "connectivity" forms the shape clusters that make up the device + conn = RBA::Connectivity::new + conn.connect(layers[0], layers[1]) # collect touching contacts + conn.connect(layers[1], layers[1]) # combine resistor shapes into one area + conn + end + + def extract_devices(layer_geometry) + # layer_geometry provides the input layers in the order they are defined with "define_layer" + conductor = layer_geometry[0] + resistor = layer_geometry[1] + + resistor_regions = resistor.merged + + resistor_regions.each do |r| + terminals = conductor.interacting(resistor) + if terminals.size != 2 + error("Resistor shape does not touch marker border in exactly two places", r) + else + double_width = 0 + (terminals.edges & resistor.edges).merged.each do |e| + double_width += e.length + end + # A = L*W + # -> L = A/W + a = r.area*dbu*dbu + w = (double_width / 2.0)*dbu + l = a / w + + device = create_device + device.set_parameter(RBA::DeviceClassResistor::PARAM_R, @sheet_rho * l / w); + + device.set_parameter(RBA::DeviceClassResistor::PARAM_A, a) + device.set_parameter(RBA::DeviceClassResistor::PARAM_L, l) + device.set_parameter(RBA::DeviceClassResistor::PARAM_P, 2*l+2*w) + device.set_parameter(RBA::DeviceClassResistor::PARAM_W, w) + define_terminal(device, RBA::DeviceClassResistor::TERMINAL_A, 0, terminals[0]); + define_terminal(device, RBA::DeviceClassResistor::TERMINAL_B, 0, terminals[1]); + end + end + end +end + +extract_devices(ResistorExtractor::new("RPP1", 0.5), # intentionally wrong: 1565.15/5 + { "C" => p1trm, "R" => rpp1 }) + +connect(met1_dg, cont) +connect(p1trm, cont) + +netlist.flatten_circuit("Res1") +schematic.flatten_circuit("RES1") + +schematic.simplify + +# Netlist vs. netlist +align +netlist.simplify +compare diff --git a/testdata/lvs/res_combine3.lvsdb.1 b/testdata/lvs/res_combine3.lvsdb.1 new file mode 100644 index 000000000..f6289f6b2 --- /dev/null +++ b/testdata/lvs/res_combine3.lvsdb.1 @@ -0,0 +1,196 @@ +#%lvsdb-klayout + +# Layout +layout( + top(Res2) + unit(0.001) + + # Layer section + # This section lists the mask layers (drawing or derived) and their connections. + + # Mask layers + layer(l4 '15/0') + layer(l3 '16/0') + layer(l1) + + # Mask layer connectivity + connect(l4 l4 l3 l1) + connect(l3 l4 l3) + connect(l1 l4 l1) + + # Device class section + class(RPP1 RES) + + # Device abstracts section + # Device abstracts list the pin shapes of the devices. + device(D$RPP1 RPP1 + terminal(A + rect(l1 (0 0) (540 2000)) + ) + terminal(B + rect(l1 (10540 0) (540 2000)) + ) + ) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(Res2 + + # Circuit boundary + rect((8285 720) (117975 57350)) + + # Nets with their geometries + net(1 + rect(l4 (120580 32490) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l4 (-22355 1390) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l4 (-21520 1755) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l3 (0 -4795) (1065 5850)) + rect(l3 (-5155 -6930) (52985 1475)) + rect(l3 (-27585 -395) (1065 5850)) + rect(l3 (20990 -5850) (1065 5850)) + rect(l3 (-1275 -1760) (340 1920)) + rect(l3 (-22475 -1890) (340 1920)) + rect(l3 (-21640 -1525) (340 1920)) + rect(l1 (42935 -2385) (540 2000)) + rect(l1 (-22675 -1970) (540 2000)) + rect(l1 (-21840 -1605) (540 2000)) + ) + net(2 + rect(l4 (19795 5575) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l3 (-195 -995) (2395 2500)) + rect(l3 (-2480 -1785) (340 1920)) + rect(l1 (-500 -1960) (540 2000)) + ) + + # Devices and their connections + device(1 D$RPP1 + device(D$RPP1 location(30 -3970)) + device(D$RPP1 location(-65 -8175)) + device(D$RPP1 location(-205 -12595)) + device(D$RPP1 location(-225 -16825)) + device(D$RPP1 location(-320 -20985)) + device(D$RPP1 location(-22135 30)) + device(D$RPP1 location(-22105 -3940)) + device(D$RPP1 location(-22200 -8145)) + device(D$RPP1 location(-22340 -12565)) + device(D$RPP1 location(-22360 -16795)) + device(D$RPP1 location(-22455 -20955)) + device(D$RPP1 location(-43435 425)) + device(D$RPP1 location(-43405 -3545)) + device(D$RPP1 location(-43500 -7750)) + device(D$RPP1 location(-43640 -12170)) + device(D$RPP1 location(-43660 -16400)) + device(D$RPP1 location(-43755 -20560)) + device(D$RPP1 location(-100755 -30885)) + device(D$RPP1 location(-100850 -35090)) + device(D$RPP1 location(-100990 -39510)) + device(D$RPP1 location(-101010 -43740)) + device(D$RPP1 location(-101105 -47900)) + device(D$RPP1 location(-81920 -3545)) + device(D$RPP1 location(-82015 -7750)) + device(D$RPP1 location(-82155 -12170)) + device(D$RPP1 location(-82175 -16400)) + device(D$RPP1 location(-63835 -30780)) + device(D$RPP1 location(-63930 -34985)) + device(D$RPP1 location(-64070 -39405)) + device(D$RPP1 location(-64090 -43635)) + device(D$RPP1 location(-82380 -26810)) + device(D$RPP1 location(-82270 -20560)) + device(D$RPP1 location(-82350 -30780)) + device(D$RPP1 location(-82445 -34985)) + device(D$RPP1 location(-82585 -39405)) + device(D$RPP1 location(-82605 -43635)) + device(D$RPP1 location(-82700 -47795)) + device(D$RPP1 location(-64185 -47795)) + device(D$RPP1 location(-63435 640)) + device(D$RPP1 location(-63405 -3330)) + device(D$RPP1 location(-63500 -7535)) + device(D$RPP1 location(-63640 -11955)) + device(D$RPP1 location(-63660 -16185)) + device(D$RPP1 location(-63755 -20345)) + device(D$RPP1 location(-63865 -26810)) + device(D$RPP1 location(-100465 855)) + device(D$RPP1 location(-81950 425)) + device(D$RPP1 location(-100435 -3115)) + device(D$RPP1 location(-100530 -7320)) + device(D$RPP1 location(-100670 -11740)) + device(D$RPP1 location(-100690 -15970)) + device(D$RPP1 location(-100785 -20130)) + device(D$RPP1 location(-100785 -26915)) + connect(5 A B) + connect(11 A B) + connect(17 A B) + connect(22 B B) + location(110140 51795) + param(R 95) + param(L 420) + param(W 2.21052631579) + param(A 1080) + param(P 1296) + terminal(A 1) + terminal(B 2) + ) + + ) +) + +# Reference netlist +reference( + + # Device class section + class(RPP1 RES) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(RES2 + + # Nets + net(1 name(GND)) + net(2 name(VDD)) + + # Outgoing pins and their connections to nets + pin(1 name(GND)) + pin(2 name(VDD)) + + # Devices and their connections + device(1 RPP1 + name(I0.I106.R0) + param(R 59475.7) + param(L 420) + param(W 2.21052631579) + param(A 0) + param(P 0) + terminal(A 2) + terminal(B 1) + ) + + ) +) + +# Cross reference +xref( + circuit(Res2 RES2 nomatch + xref( + net(() 1 mismatch) + net(() 2 mismatch) + net(1 () mismatch) + net(2 () mismatch) + pin(() 0 match) + pin(() 1 match) + device(() 1 mismatch) + device(1 () mismatch) + ) + ) +) diff --git a/testdata/lvs/res_combine3.lvsdb.2 b/testdata/lvs/res_combine3.lvsdb.2 new file mode 100644 index 000000000..7353b6eaa --- /dev/null +++ b/testdata/lvs/res_combine3.lvsdb.2 @@ -0,0 +1,196 @@ +#%lvsdb-klayout + +# Layout +layout( + top(Res2) + unit(0.001) + + # Layer section + # This section lists the mask layers (drawing or derived) and their connections. + + # Mask layers + layer(l4 '15/0') + layer(l3 '16/0') + layer(l1) + + # Mask layer connectivity + connect(l4 l4 l3 l1) + connect(l3 l4 l3) + connect(l1 l4 l1) + + # Device class section + class(RPP1 RES) + + # Device abstracts section + # Device abstracts list the pin shapes of the devices. + device(D$RPP1 RPP1 + terminal(A + rect(l1 (0 0) (540 2000)) + ) + terminal(B + rect(l1 (10540 0) (540 2000)) + ) + ) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(Res2 + + # Circuit boundary + rect((8285 720) (117975 57350)) + + # Nets with their geometries + net(1 + rect(l4 (120580 32490) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l4 (-22355 1390) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l4 (-21520 1755) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l3 (0 -4795) (1065 5850)) + rect(l3 (-5155 -6930) (52985 1475)) + rect(l3 (-27585 -395) (1065 5850)) + rect(l3 (20990 -5850) (1065 5850)) + rect(l3 (-1275 -1760) (340 1920)) + rect(l3 (-22475 -1890) (340 1920)) + rect(l3 (-21640 -1525) (340 1920)) + rect(l1 (42935 -2385) (540 2000)) + rect(l1 (-22675 -1970) (540 2000)) + rect(l1 (-21840 -1605) (540 2000)) + ) + net(2 + rect(l4 (19795 5575) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -745) (220 220)) + rect(l4 (-220 -750) (220 220)) + rect(l3 (-195 -995) (2395 2500)) + rect(l3 (-2480 -1785) (340 1920)) + rect(l1 (-500 -1960) (540 2000)) + ) + + # Devices and their connections + device(1 D$RPP1 + device(D$RPP1 location(30 -3970)) + device(D$RPP1 location(-205 -12595)) + device(D$RPP1 location(-225 -16825)) + device(D$RPP1 location(-320 -20985)) + device(D$RPP1 location(-65 -8175)) + device(D$RPP1 location(-22135 30)) + device(D$RPP1 location(-22105 -3940)) + device(D$RPP1 location(-22340 -12565)) + device(D$RPP1 location(-22360 -16795)) + device(D$RPP1 location(-22455 -20955)) + device(D$RPP1 location(-22200 -8145)) + device(D$RPP1 location(-43435 425)) + device(D$RPP1 location(-43405 -3545)) + device(D$RPP1 location(-43640 -12170)) + device(D$RPP1 location(-43660 -16400)) + device(D$RPP1 location(-43755 -20560)) + device(D$RPP1 location(-43500 -7750)) + device(D$RPP1 location(-100755 -30885)) + device(D$RPP1 location(-100990 -39510)) + device(D$RPP1 location(-101010 -43740)) + device(D$RPP1 location(-101105 -47900)) + device(D$RPP1 location(-100850 -35090)) + device(D$RPP1 location(-81920 -3545)) + device(D$RPP1 location(-82155 -12170)) + device(D$RPP1 location(-82175 -16400)) + device(D$RPP1 location(-63835 -30780)) + device(D$RPP1 location(-64070 -39405)) + device(D$RPP1 location(-64090 -43635)) + device(D$RPP1 location(-82380 -26810)) + device(D$RPP1 location(-82270 -20560)) + device(D$RPP1 location(-82350 -30780)) + device(D$RPP1 location(-82585 -39405)) + device(D$RPP1 location(-82605 -43635)) + device(D$RPP1 location(-82700 -47795)) + device(D$RPP1 location(-64185 -47795)) + device(D$RPP1 location(-82445 -34985)) + device(D$RPP1 location(-63930 -34985)) + device(D$RPP1 location(-63435 640)) + device(D$RPP1 location(-63405 -3330)) + device(D$RPP1 location(-63640 -11955)) + device(D$RPP1 location(-63660 -16185)) + device(D$RPP1 location(-63755 -20345)) + device(D$RPP1 location(-63865 -26810)) + device(D$RPP1 location(-63500 -7535)) + device(D$RPP1 location(-82015 -7750)) + device(D$RPP1 location(-100465 855)) + device(D$RPP1 location(-81950 425)) + device(D$RPP1 location(-100435 -3115)) + device(D$RPP1 location(-100670 -11740)) + device(D$RPP1 location(-100690 -15970)) + device(D$RPP1 location(-100785 -20130)) + device(D$RPP1 location(-100785 -26915)) + device(D$RPP1 location(-100530 -7320)) + connect(4 A B) + connect(10 A B) + connect(16 A B) + connect(21 B B) + location(110140 51795) + param(R 95) + param(L 420) + param(W 2.21052631579) + param(A 1080) + param(P 1296) + terminal(A 1) + terminal(B 2) + ) + + ) +) + +# Reference netlist +reference( + + # Device class section + class(RPP1 RES) + + # Circuit section + # Circuits are the hierarchical building blocks of the netlist. + circuit(RES2 + + # Nets + net(1 name(GND)) + net(2 name(VDD)) + + # Outgoing pins and their connections to nets + pin(1 name(GND)) + pin(2 name(VDD)) + + # Devices and their connections + device(1 RPP1 + name(I0.I106.R0) + param(R 59475.7) + param(L 420) + param(W 2.21052631579) + param(A 0) + param(P 0) + terminal(A 2) + terminal(B 1) + ) + + ) +) + +# Cross reference +xref( + circuit(Res2 RES2 nomatch + xref( + net(() 1 mismatch) + net(() 2 mismatch) + net(1 () mismatch) + net(2 () mismatch) + pin(() 0 match) + pin(() 1 match) + device(() 1 mismatch) + device(1 () mismatch) + ) + ) +) diff --git a/testdata/lvs/res_combine_schematic.cir b/testdata/lvs/res_combine_schematic.cir new file mode 100644 index 000000000..4b9915980 --- /dev/null +++ b/testdata/lvs/res_combine_schematic.cir @@ -0,0 +1,31 @@ + +.SUBCKT res1 MINUS PLUS +*.PININFO MINUS:B PLUS:B +RR7 net5 net3 1565.15 RPP1 W=2u L=10u M=1 +RR8 net3 net6 1565.15 RPP1 W=2u L=10u M=1 +RR9 net6 net7 1565.15 RPP1 W=2u L=10u M=1 +RR10 net7 MINUS 1565.15 RPP1 W=2u L=10u M=1 +RR6 net4 net5 1565.15 RPP1 W=2u L=10u M=1 +RR0 PLUS net4 1565.15 RPP1 W=2u L=10u M=1 +.ENDS + + +.SUBCKT res2_ MINUS PLUS +*.PININFO MINUS:B PLUS:B +XI106 net3 PLUS res1 +XI104 net7 net8 res1 +XI100 net6 MINUS res1 +XI105 net3 net7 res1 +XI107 net3 PLUS res1 +XI108 net3 PLUS res1 +XI101 net9 net6 res1 +XI102 net10 net9 res1 +XI103 net8 net10 res1 +.ENDS + + +.SUBCKT Res2 gnd vdd +*.PININFO gnd:B vdd:B +XI0 gnd vdd res2_ +.ENDS + diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index d5820f71f..dab25415d 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -1168,6 +1168,19 @@ END end + def test_15_deviceParameterCompare + + dpc = RBA::EqualDeviceParameters::new(1) + assert_equal(dpc.to_string, "#1:A0/R0") + + dpc += RBA::EqualDeviceParameters::new(2, 1.0, 0.25) + assert_equal(dpc.to_string, "#1:A0/R0;#2:A1/R0.25") + + dpc += RBA::EqualDeviceParameters::ignore(3) + assert_equal(dpc.to_string, "#1:A0/R0;#2:A1/R0.25;#3:ignore") + + end + end load("test_epilogue.rb")