From aad52b77ba684871c09a741a0a408af9ee912cf4 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 6 Apr 2019 19:46:13 +0200 Subject: [PATCH] Netlist compare: added the ability to filter small caps and high resistance devices --- src/db/db/dbNetlistCompare.cc | 78 +++++++++++++++++++-- src/db/db/dbNetlistCompare.h | 12 ++++ src/db/db/gsiDeclDbNetlistCompare.cc | 8 +++ src/db/unit_tests/dbNetlistCompareTests.cc | 67 ++++++++++++++++++ testdata/ruby/dbNetlistCompare.rb | 80 ++++++++++++++++++++++ 5 files changed, 238 insertions(+), 7 deletions(-) diff --git a/src/db/db/dbNetlistCompare.cc b/src/db/db/dbNetlistCompare.cc index 89c21be52..150968c94 100644 --- a/src/db/db/dbNetlistCompare.cc +++ b/src/db/db/dbNetlistCompare.cc @@ -22,10 +22,11 @@ */ #include "dbNetlistCompare.h" -#include "dbHash.h" +#include "dbNetlistDeviceClasses.h" #include "tlProgress.h" #include "tlTimer.h" #include "tlEquivalenceClusters.h" +#include "tlLog.h" namespace db { @@ -169,6 +170,40 @@ private: std::map m_pin_map, m_rev_pin_map; }; +// -------------------------------------------------------------------------------------------------------------------- +// DeviceFilter definition and implementation + +class DeviceFilter +{ +public: + DeviceFilter (double cap_threshold, double res_threshold) + : m_cap_threshold (cap_threshold), m_res_threshold (res_threshold) + { + // .. nothing yet .. + } + + bool filter (const db::Device *device) const + { + const db::DeviceClassResistor *res = dynamic_cast (device->device_class ()); + const db::DeviceClassCapacitor *cap = dynamic_cast (device->device_class ()); + + if (res) { + if (m_res_threshold > 0.0 && device->parameter_value (db::DeviceClassResistor::param_id_R) > m_res_threshold) { + return false; + } + } else if (cap) { + if (m_cap_threshold > 0.0 && device->parameter_value (db::DeviceClassCapacitor::param_id_C) < m_cap_threshold) { + return false; + } + } + + return true; + } + +private: + double m_cap_threshold, m_res_threshold; +}; + // -------------------------------------------------------------------------------------------------------------------- // DeviceCategorizer definition and implementation @@ -418,7 +453,7 @@ public: typedef std::vector, std::pair > >::const_iterator edge_iterator; - NetGraphNode (const db::Net *net, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const std::map *circuit_map, const CircuitPinMapper *pin_map) + NetGraphNode (const db::Net *net, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const DeviceFilter &device_filter, const std::map *circuit_map, const CircuitPinMapper *pin_map) : mp_net (net), m_other_net_index (std::numeric_limits::max ()) { if (! net) { @@ -517,6 +552,10 @@ public: for (db::Net::const_terminal_iterator i = net->begin_terminals (); i != net->end_terminals (); ++i) { const db::Device *d = i->device (); + if (! device_filter.filter (d)) { + continue; + } + size_t device_cat = device_categorizer.cat_for_device (d); size_t terminal1_id = translate_terminal_id (i->terminal_id (), d); @@ -731,7 +770,7 @@ public: // .. nothing yet .. } - void build (const db::Circuit *c, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const std::map *circuit_and_pin_mapping, const CircuitPinMapper *circuit_pin_mapper) + void build (const db::Circuit *c, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const db::DeviceFilter &device_filter, const std::map *circuit_and_pin_mapping, const CircuitPinMapper *circuit_pin_mapper) { tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Building net graph for circuit: ")) + c->name ()); @@ -739,7 +778,7 @@ public: m_net_index.clear (); // create a dummy node for a null net - m_nodes.push_back (NetGraphNode (0, device_categorizer, circuit_categorizer, circuit_and_pin_mapping, circuit_pin_mapper)); + m_nodes.push_back (NetGraphNode (0, device_categorizer, circuit_categorizer, device_filter, circuit_and_pin_mapping, circuit_pin_mapper)); size_t nets = 0; for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { @@ -748,7 +787,7 @@ public: m_nodes.reserve (nets); for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) { - NetGraphNode node (n.operator-> (), device_categorizer, circuit_categorizer, circuit_and_pin_mapping, circuit_pin_mapper); + NetGraphNode node (n.operator-> (), device_categorizer, circuit_categorizer, device_filter, circuit_and_pin_mapping, circuit_pin_mapper); m_nodes.push_back (node); } @@ -906,6 +945,21 @@ NetlistComparer::NetlistComparer (NetlistCompareLogger *logger) mp_device_categorizer.reset (new DeviceCategorizer ()); mp_circuit_categorizer.reset (new CircuitCategorizer ()); mp_circuit_pin_mapper.reset (new CircuitPinMapper ()); + + m_cap_threshold = -1.0; // not set + m_res_threshold = -1.0; // not set +} + +void +NetlistComparer::exclude_caps (double threshold) +{ + m_cap_threshold = threshold; +} + +void +NetlistComparer::exclude_resistors (double threshold) +{ + m_res_threshold = threshold; } void @@ -1112,12 +1166,14 @@ compute_subcircuit_key (const db::SubCircuit &subcircuit, const db::NetDeviceGra bool NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, const std::vector > &net_identity, bool &pin_mismatch, std::map &c12_circuit_and_pin_mapping, std::map &c22_circuit_and_pin_mapping) const { + db::DeviceFilter device_filter (m_cap_threshold, m_res_threshold); + db::NetDeviceGraph g1, g2; // NOTE: for normalization we map all subcircuits of c1 to c2. // Also, pin swapping will only happen there. - g1.build (c1, device_categorizer, circuit_categorizer, &c12_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); - g2.build (c2, device_categorizer, circuit_categorizer, &c22_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); + g1.build (c1, device_categorizer, circuit_categorizer, device_filter, &c12_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); + g2.build (c2, device_categorizer, circuit_categorizer, device_filter, &c22_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ()); // Match dummy nodes for null nets g1.identify (0, 0); @@ -1342,6 +1398,10 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (db::Circuit::const_device_iterator d = c1->begin_devices (); d != c1->end_devices (); ++d) { + if (! device_filter.filter (d.operator-> ())) { + continue; + } + std::vector > k = compute_device_key (*d, g1); bool mapped = true; @@ -1365,6 +1425,10 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, for (db::Circuit::const_device_iterator d = c2->begin_devices (); d != c2->end_devices (); ++d) { + if (! device_filter.filter (d.operator-> ())) { + continue; + } + std::vector > k = compute_device_key (*d, g2); bool mapped = true; diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h index b941aef52..eb9364733 100644 --- a/src/db/db/dbNetlistCompare.h +++ b/src/db/db/dbNetlistCompare.h @@ -195,6 +195,16 @@ public: */ void same_circuits (const db::Circuit *ca, const db::Circuit *cb); + /** + * @brief Exclude caps with less than the given capacity value + */ + void exclude_caps (double threshold); + + /** + * @brief Exclude resistors with more than the given resistance value + */ + void exclude_resistors (double threshold); + /** * @brief Actually compares the two netlists */ @@ -209,6 +219,8 @@ protected: std::auto_ptr mp_circuit_pin_mapper; std::auto_ptr mp_device_categorizer; std::auto_ptr mp_circuit_categorizer; + double m_cap_threshold; + double m_res_threshold; }; } diff --git a/src/db/db/gsiDeclDbNetlistCompare.cc b/src/db/db/gsiDeclDbNetlistCompare.cc index 2e7b6485e..36b9d86d3 100644 --- a/src/db/db/gsiDeclDbNetlistCompare.cc +++ b/src/db/db/gsiDeclDbNetlistCompare.cc @@ -454,6 +454,14 @@ Class decl_dbNetlistComparer ("db", "NetlistComparer", "This method makes a circuit circuit_a in netlist a identical to the corresponding\n" "circuit circuit_b in netlist b (see \\compare). By default circuits with the same name are identical.\n" ) + + gsi::method ("min_capacitance=", &db::NetlistComparer::exclude_caps, gsi::arg ("threshold"), + "@brief Excludes all capacitor devices with a capacitance values less than the given threshold.\n" + "To reset this constraint, set this attribute to zero." + ) + + gsi::method ("max_resistance=", &db::NetlistComparer::exclude_resistors, gsi::arg ("threshold"), + "@brief Excludes all resistor devices with a resistance values higher than the given threshold.\n" + "To reset this constraint, set this attribute to zero." + ) + gsi::method ("compare", &db::NetlistComparer::compare, gsi::arg ("netlist_a"), gsi::arg ("netlist_b"), "@brief Compares two netlists.\n" "This method will perform the actual netlist compare. It will return true if both netlists are identical. " diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index f873f5bcf..39338ad95 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -385,6 +385,73 @@ TEST(1_SimpleInverterMatchedDeviceClasses) EXPECT_EQ (good, true); } +TEST(1_SimpleInverterSkippedDevices) +{ + const char *nls1 = + "circuit INV ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n" + " device PMOS $1 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device CAP $2 (A=OUT,B=IN) (C=1e-12);\n" + " device NMOS $3 (S=VSS,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + const char *nls2 = + "circuit INV ($1=VDD,$2=IN,$3=VSS,$4=OUT);\n" + " device NMOS $1 (S=OUT,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device CAP $2 (A=OUT,B=IN) (C=1e-13);\n" + " device RES $3 (A=OUT,B=IN) (R=1000);\n" + " device PMOS $4 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + "end;\n"; + + db::Netlist nl1, nl2; + prep_nl (nl1, nls1); + prep_nl (nl2, nls2); + + NetlistCompareTestLogger logger; + db::NetlistComparer comp (&logger); + + bool good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit INV INV\n" + "match_nets VDD VDD\n" + "match_nets VSS VSS\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $3 $2\n" + "match_devices $3 $1\n" + "match_devices_with_different_parameters $2 $2\n" + "device_mismatch (null) $3\n" + "match_devices $1 $4\n" + "end_circuit INV INV NOMATCH" + ); + EXPECT_EQ (good, false); + + comp.exclude_caps (1e-11); + comp.exclude_resistors (900.0); + + logger.clear (); + good = comp.compare (&nl1, &nl2); + + EXPECT_EQ (logger.text (), + "begin_circuit INV INV\n" + "match_nets VDD VDD\n" + "match_nets VSS VSS\n" + "match_nets OUT OUT\n" + "match_nets IN IN\n" + "match_pins $0 $1\n" + "match_pins $1 $3\n" + "match_pins $2 $0\n" + "match_pins $3 $2\n" + "match_devices $3 $1\n" + "match_devices $1 $4\n" + "end_circuit INV INV MATCH" + ); + EXPECT_EQ (good, true); +} + TEST(2_SimpleInverterWithForcedNetAssignment) { const char *nls1 = diff --git a/testdata/ruby/dbNetlistCompare.rb b/testdata/ruby/dbNetlistCompare.rb index b9d772e3c..5886deb15 100644 --- a/testdata/ruby/dbNetlistCompare.rb +++ b/testdata/ruby/dbNetlistCompare.rb @@ -865,6 +865,86 @@ match_pins $4 $4 match_subcircuits $2 $1 match_subcircuits $1 $2 end_circuit TOP TOP MATCH +END + + assert_equal(good, true) + + end + + def test_10 + + nls1 = <<"END" +circuit INV($1=IN,$2=OUT,$3=VDD,$4=VSS); + device PMOS $1(S=VDD,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device CAP $2 (A=OUT,B=IN) (C=1e-12); + device NMOS $2(S=VSS,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nls2 = <<"END" +circuit INV($1=VDD,$2=IN,$3=VSS,$4=OUT); + device NMOSB $1(S=OUT,G=IN,D=VSS)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); + device CAP $2 (A=OUT,B=IN) (C=1e-13); + device RES $3 (A=OUT,B=IN) (R=1000); + device PMOSB $2(S=VDD,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5); +end; +END + + nl1 = RBA::Netlist::new + nl2 = RBA::Netlist::new + prep_nl(nl1, nls1) + prep_nl(nl2, nls2) + + logger = NetlistCompareTestLogger::new + comp = RBA::NetlistComparer::new(logger) + + comp.same_device_classes(nl1.device_class_by_name("PMOS"), nl2.device_class_by_name("PMOSB")) + + good = comp.compare(nl1, nl2) + assert_equal(good, false) + + logger.clear + comp.same_device_classes(nl1.device_class_by_name("NMOS"), nl2.device_class_by_name("NMOSB")) + good = comp.compare(nl1, nl2) + + assert_equal(logger.text(), <<"END") +begin_circuit INV INV +match_nets VDD VDD +match_nets VSS VSS +match_nets OUT OUT +match_nets IN IN +match_pins $0 $1 +match_pins $1 $3 +match_pins $2 $0 +match_pins $3 $2 +match_devices $3 $1 +match_devices_with_different_parameters $2 $2 +device_mismatch (null) $3 +match_devices $1 $4 +end_circuit INV INV NOMATCH +END + + assert_equal(good, false) + + comp.max_resistance = 900.0 + comp.min_capacitance = 1e-11 + + logger.clear + good = comp.compare(nl1, nl2) + + assert_equal(logger.text(), <<"END") +begin_circuit INV INV +match_nets VDD VDD +match_nets VSS VSS +match_nets OUT OUT +match_nets IN IN +match_pins $0 $1 +match_pins $1 $3 +match_pins $2 $0 +match_pins $3 $2 +match_devices $3 $1 +match_devices $1 $4 +end_circuit INV INV MATCH END assert_equal(good, true)