From aff8212f2f93269db37895ef87a30dd348185555 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Tue, 23 Jul 2019 00:14:43 +0200 Subject: [PATCH] Provide 'align' method to auto-align circuit and cell hierarchy in LVS --- src/db/db/dbNetlistCompare.cc | 43 +++++++++++++++++++ src/db/db/dbNetlistCompare.h | 7 ++++ src/db/db/gsiDeclDbNetlistCompare.cc | 24 +++++++++++ src/lvs/lvs/built-in-macros/_lvs_engine.rb | 8 +++- src/lvs/lvs/built-in-macros/_lvs_netter.rb | 49 +++++++++++++++++++++- 5 files changed, 129 insertions(+), 2 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 11fb51ba2..8e9d81ea1 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -2083,6 +2083,49 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b, NetlistCom return res; } +void +NetlistComparer::unmatched_circuits (db::Netlist *a, db::Netlist *b, std::vector &in_a, std::vector &in_b) const +{ + // we need to create a copy because this method is supposed to be const. + db::CircuitCategorizer circuit_categorizer = *mp_circuit_categorizer; + + std::map > cat2circuits; + + for (db::Netlist::circuit_iterator i = a->begin_circuits (); i != a->end_circuits (); ++i) { + size_t cat = circuit_categorizer.cat_for_circuit (i.operator-> ()); + if (cat && i->begin_refs () != i->end_refs ()) { + cat2circuits[cat].first = i.operator-> (); + } + } + + for (db::Netlist::circuit_iterator i = b->begin_circuits (); i != b->end_circuits (); ++i) { + size_t cat = circuit_categorizer.cat_for_circuit (i.operator-> ()); + if (cat && i->begin_refs () != i->end_refs ()) { + cat2circuits[cat].second = i.operator-> (); + } + } + + size_t na = 0, nb = 0; + for (std::map >::const_iterator i = cat2circuits.begin (); i != cat2circuits.end (); ++i) { + if (! i->second.first) { + ++nb; + } else if (! i->second.second) { + ++na; + } + } + + in_a.reserve (na); + in_b.reserve (nb); + + for (std::map >::const_iterator i = cat2circuits.begin (); i != cat2circuits.end (); ++i) { + if (! i->second.first) { + in_b.push_back (i->second.second); + } else if (! i->second.second) { + in_a.push_back (i->second.first); + } + } +} + bool NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const { diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h index 7fa46f99a..d35dd0770 100644 --- a/src/db/db/dbNetlistCompare.h +++ b/src/db/db/dbNetlistCompare.h @@ -266,6 +266,13 @@ public: return m_max_n_branch; } + /** + * @brief Gets the list of circuits without matching circuit in the other netlist + * The result can be used to flatten these circuits prior to compare. + * Mismatching top level circuits are not reported because they cannot be flattened. + */ + void unmatched_circuits (db::Netlist *a, db::Netlist *b, std::vector &in_a, std::vector &in_b) const; + /** * @brief Actually compares the two netlists */ diff --git a/src/db/db/gsiDeclDbNetlistCompare.cc b/src/db/db/gsiDeclDbNetlistCompare.cc index d9dd10c2c..0afaf06de 100644 --- a/src/db/db/gsiDeclDbNetlistCompare.cc +++ b/src/db/db/gsiDeclDbNetlistCompare.cc @@ -452,6 +452,20 @@ static db::NetlistComparer *make_comparer1 (GenericNetlistCompareLogger *logger) return new db::NetlistComparer (logger); } +static std::vector unmatched_circuits_a (const db::NetlistComparer *comparer, db::Netlist *a, db::Netlist *b) +{ + std::vector res_a, res_b; + comparer->unmatched_circuits (a, b, res_a, res_b); + return res_a; +} + +static std::vector unmatched_circuits_b (const db::NetlistComparer *comparer, db::Netlist *a, db::Netlist *b) +{ + std::vector res_a, res_b; + comparer->unmatched_circuits (a, b, res_a, res_b); + return res_b; +} + Class decl_dbNetlistComparer ("db", "NetlistComparer", gsi::constructor ("new", &make_comparer0, "@brief Creates a new comparer object.\n" @@ -524,6 +538,16 @@ Class decl_dbNetlistComparer ("db", "NetlistComparer", "@brief Gets the maximum branch complexity\n" "See \\max_branch_complexity= for details." ) + + gsi::method_ext ("unmatched_circuits_a", &unmatched_circuits_a, gsi::arg ("a"), gsi::arg ("b"), + "@brief Returns a list of circuits in A for which there is not corresponding circuit in B\n" + "This list can be used to flatten these circuits so they do not participate in the compare process.\n" + "Top level circuits are not included as they cannot be flattened.\n" + ) + + gsi::method_ext ("unmatched_circuits_b", &unmatched_circuits_b, gsi::arg ("a"), gsi::arg ("b"), + "@brief Returns a list of circuits in B for which there is not corresponding circuit in A\n" + "This list can be used to flatten these circuits so they do not participate in the compare process.\n" + "Top level circuits are not included as they cannot be flattened.\n" + ) + gsi::method ("compare", (bool (db::NetlistComparer::*) (const db::Netlist *, const db::Netlist *) const) &db::NetlistComparer::compare, gsi::arg ("netlist_a"), gsi::arg ("netlist_b"), "@brief Compares two netlists.\n" "This method will perform the actual netlist compare. It will return true if both netlists are identical. " diff --git a/src/lvs/lvs/built-in-macros/_lvs_engine.rb b/src/lvs/lvs/built-in-macros/_lvs_engine.rb index bc928d1e6..b7a1dc2b7 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_engine.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_engine.rb @@ -94,6 +94,12 @@ module LVS # @synopsis compare # See \Netter#compare for a description of that function. + # %LVS% + # @name align + # @brief Aligns the extracted netlist vs. the schematic by flattening circuits where required + # @synopsis align + # See \Netter#align for a description of that function. + # %LVS% # @name same_nets # @brief Establishes an equivalence between the nets @@ -143,7 +149,7 @@ module LVS # @synopsis max_depth(n) # See \Netter#max_depth for a description of that function. - %w(schematic compare same_nets same_circuits same_device_classes equivalent_pins min_caps max_res max_depth max_branch_complexity).each do |f| + %w(schematic compare align same_nets same_circuits same_device_classes equivalent_pins min_caps max_res max_depth max_branch_complexity).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 16014e9d0..4b445e3da 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_netter.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_netter.rb @@ -91,6 +91,46 @@ module LVS data end + # %LVS% + # @name align + # @brief Aligns the extracted netlist vs. the schematic + # @synopsis align + # The align method will modify the netlists in case of missing + # corresponding circuits. It will flatten these circuits, thus + # improving the equivalence between the netlists. Top level circuits + # are not flattened. + # + # This feature is in particular useful to remove structural cells + # like device PCells, reuse blocks etc. + # + # This method will also remove schematic circuits for which there is + # no corresponding layout cell. In the extreme case of flat layout this + # will result in a flat vs. flat compare. + # + # "netlist.flatten_circuit(...)" or "schematic.flatten_circuit(...)" + # are other (explicit) ways to flatten circuits. + # + # Please note that flattening circuits has some side effects such + # as loss of details in the cross reference and net layout. + + def align + + nl = _ensure_two_netlists + + # flatten layout cells for which there is no corresponding schematic circuit + @comparer.unmatched_circuits_a(*nl).each do |c| + @engine.info("Flatten layout cell (no schematic): #{c.name}") + nl[0].flatten_circuit(c) + end + + # flatten schematic circuits for which there is no corresponding layout cell + @comparer.unmatched_circuits_b(*nl).each do |c| + @engine.info("Flatten schematic circuit (no layout): #{c.name}") + nl[1].flatten_circuit(c) + end + + end + # %LVS% # @name compare # @brief Compares the extracted netlist vs. the schematic @@ -99,12 +139,19 @@ module LVS # The compare can be configured in more details using \same_nets, \same_circuits, # \same_device_classes and \equivalent_pins. # + # The compare method will also modify the netlists in case of missing + # corresponding circuits: the unpaired circuit will be flattened then. + # # This method will return true, if the netlists are equivalent and false # otherwise. def compare - lvs_data.reference = _ensure_two_netlists[1] + + nl = _ensure_two_netlists + lvs_data.reference = nl[1] + lvs_data.compare(@comparer) + end def _ensure_two_netlists