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 27900a7c9..4fb4723bd 100644 --- a/src/db/db/dbDeviceClass.cc +++ b/src/db/db/dbDeviceClass.cc @@ -22,11 +22,34 @@ #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 @@ -36,7 +59,6 @@ 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) @@ -112,21 +134,14 @@ bool EqualDeviceParameters::less (const db::Device &a, const db::Device &b) cons seen.insert (c->first); } - const std::vector &pd = a.device_class ()->parameter_definitions (); + const std::vector &pd = primary_device_class (a, b)->parameter_definitions (); for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { - - if (seen.find (p->id ()) != seen.end ()) { - continue; - } - - const db::DeviceParameterDefinition *pdb = b.device_class ()->parameter_definition (p->id ()); - if (pdb && pdb->is_primary () && p->is_primary ()) { + 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; @@ -166,13 +181,13 @@ bool AllDeviceParametersAreEqual::less (const db::Device &a, const db::Device &b // 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); } @@ -299,10 +314,7 @@ 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; } @@ -315,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 69fe5aaa6..aac1e5bab 100644 --- a/src/db/db/dbDeviceClass.h +++ b/src/db/db/dbDeviceClass.h @@ -734,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 */ @@ -762,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 9b11a8093..59e9f301f 100644 --- a/src/db/db/dbNetlistSpiceReader.cc +++ b/src/db/db/dbNetlistSpiceReader.cc @@ -625,9 +625,6 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin std::map::const_iterator v = params.find (i->name ()); if (v != params.end ()) { device->set_parameter_value (i->id (), v->second / i->si_scaling ()); - // Make given parameters primary. This way they are netlisted again and participate in netlist compare when - // they are made primary in the extracted netlist too. - i->set_is_primary (true); } else if (i->id () == defp) { device->set_parameter_value (i->id (), value / i->si_scaling ()); } diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index f2d394e9a..156a07a4d 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 (), diff --git a/src/lay/lay/doc/manual/lvs_compare.xml b/src/lay/lay/doc/manual/lvs_compare.xml index 5b3fe3073..9fdc21953 100644 --- a/src/lay/lay/doc/manual/lvs_compare.xml +++ b/src/lay/lay/doc/manual/lvs_compare.xml @@ -205,6 +205,57 @@ same_device_classes("POLYRES", nil)
tolerance("NMOS", "L", 0.05, 0.01)
 tolerance("NMOS", "L", :absolute => 0.05, :relative => 0.01)
+

Ignoring parameters

+ +

+ Some device parameters can be ignore in the 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 while "ignore_parameter" will disable + it for compare. +

+ +

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 second, it is compare against + the schematic "L" parameter, provided that one is given in the netlist. No compare happens if no "L" parameter is present in the + netlist. +

+ +

+ This behavior is overridden by a "tolerance" or "ignore_parameter" specification for that parameter or if a customer + device comparer is installed. +

+ +

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

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

+ As this will make the parameter to disappear from the netlist, it's often more useful to use "ignore_parameter" instead + which has the same effect on the compare step, but will keep it in the netlist. +

+

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/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); }