From c5a56dbc5f47c8acb2c9b6aa21d37aeae8b33a64 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 4 Apr 2019 23:41:46 +0200 Subject: [PATCH] WIP: GSI binding for netlist comparer. --- src/db/db/db.pro | 3 +- src/db/db/dbNetlistCompare.h | 12 + src/db/db/gsiDeclDbNetlistCompare.cc | 466 +++++++++++++++++++++++++++ 3 files changed, 480 insertions(+), 1 deletion(-) create mode 100644 src/db/db/gsiDeclDbNetlistCompare.cc diff --git a/src/db/db/db.pro b/src/db/db/db.pro index cd1acda6e..1556c253a 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -175,7 +175,8 @@ SOURCES = \ dbRegionProcessors.cc \ dbNetlistCompare.cc \ dbNetlistReader.cc \ - dbNetlistSpiceReader.cc + dbNetlistSpiceReader.cc \ + gsiDeclDbNetlistCompare.cc HEADERS = \ dbArray.h \ diff --git a/src/db/db/dbNetlistCompare.h b/src/db/db/dbNetlistCompare.h index b0fcf8009..b941aef52 100644 --- a/src/db/db/dbNetlistCompare.h +++ b/src/db/db/dbNetlistCompare.h @@ -213,4 +213,16 @@ protected: } +namespace tl +{ + +template<> struct type_traits : public tl::type_traits +{ + // mark "NetlistDeviceExtractor" as having a default ctor and no copy ctor + typedef tl::false_tag has_copy_constructor; + typedef tl::false_tag has_default_constructor; +}; + +} + #endif diff --git a/src/db/db/gsiDeclDbNetlistCompare.cc b/src/db/db/gsiDeclDbNetlistCompare.cc new file mode 100644 index 000000000..04d7c8f0c --- /dev/null +++ b/src/db/db/gsiDeclDbNetlistCompare.cc @@ -0,0 +1,466 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2019 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "gsiDecl.h" +#include "dbNetlistCompare.h" + +namespace { + +/** + * @brief A NetlistDeviceExtractor implementation that allows reimplementation of the virtual methods + */ +class GenericNetlistCompareLogger + : public db::NetlistCompareLogger +{ +public: + GenericNetlistCompareLogger () + : db::NetlistCompareLogger () + { + // .. nothing yet .. + } + + virtual void begin_netlist (const db::Netlist *a, const db::Netlist *b) + { + if (cb_begin_netlist.can_issue ()) { + cb_begin_netlist.issue (&GenericNetlistCompareLogger::begin_netlist_fb, a, b); + } else { + db::NetlistCompareLogger::begin_netlist (a, b); + } + } + + void begin_netlist_fb (const db::Netlist *a, const db::Netlist *b) + { + db::NetlistCompareLogger::begin_netlist (a, b); + } + + virtual void end_netlist (const db::Netlist *a, const db::Netlist *b) + { + if (cb_end_netlist.can_issue ()) { + cb_end_netlist.issue (&GenericNetlistCompareLogger::end_netlist_fb, a, b); + } else { + db::NetlistCompareLogger::end_netlist (a, b); + } + } + + void end_netlist_fb (const db::Netlist *a, const db::Netlist *b) + { + db::NetlistCompareLogger::end_netlist (a, b); + } + + virtual void begin_circuit (const db::Circuit *a, const db::Circuit *b) + { + if (cb_begin_circuit.can_issue ()) { + cb_begin_circuit.issue (&GenericNetlistCompareLogger::begin_circuit_fb, a, b); + } else { + db::NetlistCompareLogger::begin_circuit (a, b); + } + } + + void begin_circuit_fb (const db::Circuit *a, const db::Circuit *b) + { + db::NetlistCompareLogger::begin_circuit (a, b); + } + + virtual void end_circuit (const db::Circuit *a, const db::Circuit *b, bool matching) + { + if (cb_end_circuit.can_issue ()) { + cb_end_circuit.issue (&GenericNetlistCompareLogger::end_circuit_fb, a, b, matching); + } else { + db::NetlistCompareLogger::end_circuit (a, b, matching); + } + } + + void end_circuit_fb (const db::Circuit *a, const db::Circuit *b, bool matching) + { + db::NetlistCompareLogger::end_circuit (a, b, matching); + } + + virtual void circuit_skipped (const db::Circuit *a, const db::Circuit *b) + { + if (cb_circuit_skipped.can_issue ()) { + cb_circuit_skipped.issue (&GenericNetlistCompareLogger::circuit_skipped_fb, a, b); + } else { + db::NetlistCompareLogger::circuit_skipped (a, b); + } + } + + void circuit_skipped_fb (const db::Circuit *a, const db::Circuit *b) + { + db::NetlistCompareLogger::circuit_skipped (a, b); + } + + virtual void circuit_mismatch (const db::Circuit *a, const db::Circuit *b) + { + if (cb_circuit_mismatch.can_issue ()) { + cb_circuit_mismatch.issue (&GenericNetlistCompareLogger::circuit_mismatch_fb, a, b); + } else { + db::NetlistCompareLogger::circuit_mismatch (a, b); + } + } + + void circuit_mismatch_fb (const db::Circuit *a, const db::Circuit *b) + { + db::NetlistCompareLogger::circuit_mismatch (a, b); + } + + virtual void match_nets (const db::Net *a, const db::Net *b) + { + if (cb_match_nets.can_issue ()) { + cb_match_nets.issue (&GenericNetlistCompareLogger::match_nets_fb, a, b); + } else { + db::NetlistCompareLogger::match_nets (a, b); + } + } + + void match_nets_fb (const db::Net *a, const db::Net *b) + { + db::NetlistCompareLogger::match_nets (a, b); + } + + virtual void match_ambiguous_nets (const db::Net *a, const db::Net *b) + { + if (cb_match_ambiguous_nets.can_issue ()) { + cb_match_ambiguous_nets.issue (&GenericNetlistCompareLogger::match_ambiguous_nets_fb, a, b); + } else { + db::NetlistCompareLogger::match_ambiguous_nets (a, b); + } + } + + void match_ambiguous_nets_fb (const db::Net *a, const db::Net *b) + { + db::NetlistCompareLogger::match_ambiguous_nets (a, b); + } + + virtual void net_mismatch (const db::Net *a, const db::Net *b) + { + if (cb_net_mismatch.can_issue ()) { + cb_net_mismatch.issue (&GenericNetlistCompareLogger::net_mismatch_fb, a, b); + } else { + db::NetlistCompareLogger::net_mismatch (a, b); + } + } + + void net_mismatch_fb (const db::Net *a, const db::Net *b) + { + db::NetlistCompareLogger::net_mismatch (a, b); + } + + virtual void match_devices (const db::Device *a, const db::Device *b) + { + if (cb_match_devices.can_issue ()) { + cb_match_devices.issue (&GenericNetlistCompareLogger::match_devices_fb, a, b); + } else { + db::NetlistCompareLogger::match_devices (a, b); + } + } + + void match_devices_fb (const db::Device *a, const db::Device *b) + { + db::NetlistCompareLogger::match_devices (a, b); + } + + virtual void match_devices_with_different_parameters (const db::Device *a, const db::Device *b) + { + if (cb_match_devices_with_different_parameters.can_issue ()) { + cb_match_devices_with_different_parameters.issue (&GenericNetlistCompareLogger::match_devices_with_different_parameters_fb, a, b); + } else { + db::NetlistCompareLogger::match_devices_with_different_parameters (a, b); + } + } + + void match_devices_with_different_parameters_fb (const db::Device *a, const db::Device *b) + { + db::NetlistCompareLogger::match_devices_with_different_parameters (a, b); + } + + virtual void match_devices_with_different_device_classes (const db::Device *a, const db::Device *b) + { + if (cb_match_devices_with_different_device_classes.can_issue ()) { + cb_match_devices_with_different_device_classes.issue (&GenericNetlistCompareLogger::match_devices_with_different_device_classes_fb, a, b); + } else { + db::NetlistCompareLogger::match_devices_with_different_device_classes (a, b); + } + } + + void match_devices_with_different_device_classes_fb (const db::Device *a, const db::Device *b) + { + db::NetlistCompareLogger::match_devices_with_different_device_classes (a, b); + } + + virtual void device_mismatch (const db::Device *a, const db::Device *b) + { + if (cb_device_mismatch.can_issue ()) { + cb_device_mismatch.issue (&GenericNetlistCompareLogger::device_mismatch_fb, a, b); + } else { + db::NetlistCompareLogger::device_mismatch (a, b); + } + } + + void device_mismatch_fb (const db::Device *a, const db::Device *b) + { + db::NetlistCompareLogger::device_mismatch (a, b); + } + + virtual void match_pins (const db::Pin *a, const db::Pin *b) + { + if (cb_match_pins.can_issue ()) { + cb_match_pins.issue (&GenericNetlistCompareLogger::match_pins_fb, a, b); + } else { + db::NetlistCompareLogger::match_pins (a, b); + } + } + + void match_pins_fb (const db::Pin *a, const db::Pin *b) + { + db::NetlistCompareLogger::match_pins (a, b); + } + + virtual void pin_mismatch (const db::Pin *a, const db::Pin *b) + { + if (cb_pin_mismatch.can_issue ()) { + cb_pin_mismatch.issue (&GenericNetlistCompareLogger::pin_mismatch_fb, a, b); + } else { + db::NetlistCompareLogger::pin_mismatch (a, b); + } + } + + void pin_mismatch_fb (const db::Pin *a, const db::Pin *b) + { + db::NetlistCompareLogger::pin_mismatch (a, b); + } + + virtual void match_subcircuits (const db::SubCircuit *a, const db::SubCircuit *b) + { + if (cb_match_subcircuits.can_issue ()) { + cb_match_subcircuits.issue (&GenericNetlistCompareLogger::match_subcircuits_fb, a, b); + } else { + db::NetlistCompareLogger::match_subcircuits (a, b); + } + } + + void match_subcircuits_fb (const db::SubCircuit *a, const db::SubCircuit *b) + { + db::NetlistCompareLogger::match_subcircuits (a, b); + } + + virtual void subcircuit_mismatch (const db::SubCircuit *a, const db::SubCircuit *b) + { + if (cb_subcircuit_mismatch.can_issue ()) { + cb_subcircuit_mismatch.issue (&GenericNetlistCompareLogger::subcircuit_mismatch_fb, a, b); + } else { + db::NetlistCompareLogger::subcircuit_mismatch (a, b); + } + } + + void subcircuit_mismatch_fb (const db::SubCircuit *a, const db::SubCircuit *b) + { + db::NetlistCompareLogger::subcircuit_mismatch (a, b); + } + + gsi::Callback cb_begin_netlist; + gsi::Callback cb_end_netlist; + gsi::Callback cb_begin_circuit; + gsi::Callback cb_end_circuit; + gsi::Callback cb_circuit_skipped; + gsi::Callback cb_match_nets; + gsi::Callback cb_net_mismatch; + gsi::Callback cb_circuit_mismatch; + gsi::Callback cb_match_ambiguous_nets; + gsi::Callback cb_match_devices; + gsi::Callback cb_match_devices_with_different_parameters; + gsi::Callback cb_match_devices_with_different_device_classes; + gsi::Callback cb_device_mismatch; + gsi::Callback cb_match_pins; + gsi::Callback cb_pin_mismatch; + gsi::Callback cb_match_subcircuits; + gsi::Callback cb_subcircuit_mismatch; +}; + +} + +namespace gsi +{ + +Class decl_GenericNetlistCompareLogger (decl_GenericNetlistCompareLogger, "db", "GenericNetlistCompareLogger", + gsi::callback ("begin_netlist", &GenericNetlistCompareLogger::begin_netlist, &GenericNetlistCompareLogger::cb_begin_netlist, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called at the beginning of the compare process.\n" + "This method is called once when the compare run begins.\n" + ) + + gsi::callback ("end_netlist", &GenericNetlistCompareLogger::end_netlist, &GenericNetlistCompareLogger::cb_end_netlist, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called at the end of the compare process.\n" + "This method is called once when the compare run ended.\n" + ) + + gsi::callback ("begin_circuit", &GenericNetlistCompareLogger::begin_circuit, &GenericNetlistCompareLogger::cb_begin_circuit, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when a new circuit is compared.\n" + "This compare procedure will run the netlist compare circuit vs. circuit in a bottom-up fashion.\n" + "Before each circuit is compared, this method is called once with the circuits that are about to be compared.\n" + "After the circuit has been compared, \\end_circuit will be called.\n" + "\n" + "In some cases, the compare algorithm will decide that circuits can't be compared. This happens if for " + "some or all subcircuits the pin assignment can't be derived. In this case, \\circuit_skipped will be called once " + "instead of \\begin_circuit and \\end_circuit.\n" + ) + + gsi::callback ("end_circuit", &GenericNetlistCompareLogger::end_circuit, &GenericNetlistCompareLogger::cb_end_circuit, gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("matching"), + "@brief This function is called at the end of the compare process.\n" + "The 'matching' argument indicates whether the circuits have been identified as identical.\n" + "See \\begin_circuit for details." + ) + + gsi::callback ("circuit_skipped", &GenericNetlistCompareLogger::circuit_skipped, &GenericNetlistCompareLogger::cb_circuit_skipped, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when circuits can't be compared.\n" + "If there is a known circuit pair, but the circuits can be compared - for example because subcircuits can't be identified - this method will be called with " + "both circuits.\n" + "\n" + "This method is called instead of \\begin_circuit and \\end_circuit." + ) + + gsi::callback ("circuit_mismatch", &GenericNetlistCompareLogger::circuit_mismatch, &GenericNetlistCompareLogger::cb_circuit_mismatch, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when circuits can't be compared.\n" + "This method is called when a circuit can't be mapped to a partner in the other netlist. In this case, " + "this method is called with the one circuit and nil for the other circuit.\n" + "\n" + "This method is called instead of \\begin_circuit and \\end_circuit." + ) + + gsi::callback ("match_nets", &GenericNetlistCompareLogger::match_nets, &GenericNetlistCompareLogger::cb_match_nets, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two nets are identified.\n" + "If two nets are identified as a corresponding pair, this method will be called with both nets.\n" + "If the nets can be paired, but this match is ambiguous, \\match_ambiguous_nets_fb will be called instead.\n" + "If nets can't be matched to a partner, \\net_mismatch will be called.\n" + ) + + gsi::callback ("match_ambiguous_nets", &GenericNetlistCompareLogger::match_ambiguous_nets, &GenericNetlistCompareLogger::cb_match_ambiguous_nets, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two nets are identified, but this choice is ambiguous.\n" + "See \\match_nets for more details." + ) + + gsi::callback ("net_mismatch", &GenericNetlistCompareLogger::net_mismatch, &GenericNetlistCompareLogger::cb_net_mismatch, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when a net can't be paired.\n" + "This method will be called, if a net cannot be identified as identical with another net. The corresponding argument " + "will identify the net and source netlist. The other argument will be nil.\n" + ) + + gsi::callback ("match_devices", &GenericNetlistCompareLogger::match_devices, &GenericNetlistCompareLogger::cb_match_devices, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two devices are identified.\n" + "If two devices are identified as a corresponding pair, this method will be called with both devices.\n" + "If the devices can be paired, but the device parameters don't match, \\match_devices_with_different_parameters will be called instead.\n" + "If the devices can be paired, but the device classes don't match, \\match_devices_with_different_device_classes will be called instead.\n" + "If devices can't be matched, \\device_mismatch will be called with the one device considered and the other device being nil." + ) + + gsi::callback ("match_devices_with_different_parameters", &GenericNetlistCompareLogger::match_devices_with_different_parameters, &GenericNetlistCompareLogger::cb_match_devices_with_different_parameters, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two devices are identified but have different parameters.\n" + "See \\match_devices for details.\n" + ) + + gsi::callback ("match_devices_with_different_device_classes", &GenericNetlistCompareLogger::match_devices_with_different_device_classes, &GenericNetlistCompareLogger::cb_match_devices_with_different_device_classes, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two devices are identified but have different device classes.\n" + "See \\match_devices for details.\n" + ) + + gsi::callback ("device_mismatch", &GenericNetlistCompareLogger::device_mismatch, &GenericNetlistCompareLogger::cb_device_mismatch, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two devices can't be paired.\n" + "This will report the device considered in a or b. The other argument is nil." + "See \\match_devices for details.\n" + ) + + gsi::callback ("match_pins", &GenericNetlistCompareLogger::match_pins, &GenericNetlistCompareLogger::cb_match_pins, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two pins are identified.\n" + "If two pins are identified as a corresponding pair, this method will be called with both pins.\n" + "If pins can't be matched, \\pin_mismatch will be called with the one pin considered and the other pin being nil." + ) + + gsi::callback ("pin_mismatch", &GenericNetlistCompareLogger::pin_mismatch, &GenericNetlistCompareLogger::cb_pin_mismatch, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two pins can't be paired.\n" + "This will report the pin considered in a or b. The other argument is nil." + "See \\match_pins for details.\n" + ) + + gsi::callback ("match_subcircuits", &GenericNetlistCompareLogger::match_subcircuits, &GenericNetlistCompareLogger::cb_match_subcircuits, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two subcircuits are identified.\n" + "If two subcircuits are identified as a corresponding pair, this method will be called with both subcircuits.\n" + "If subcircuits can't be matched, \\subcircuit_mismatch will be called with the one subcircuit considered and the other subcircuit being nil." + ) + + gsi::callback ("subcircuit_mismatch", &GenericNetlistCompareLogger::subcircuit_mismatch, &GenericNetlistCompareLogger::cb_subcircuit_mismatch, gsi::arg ("a"), gsi::arg ("b"), + "@brief This function is called when two subcircuits can't be paired.\n" + "This will report the subcircuit considered in a or b. The other argument is nil." + "See \\match_subcircuits for details.\n" + ), + "@brief An event receiver for the netlist compare feature.\n" + "The \\NetlistComparer class will send compare events to a logger derived from this class. " + "Use this class to implement your own logger class. You can override on of it's methods to receive certain " + "kind of events." + "\n" + "This class has been introduced in version 0.26.\n" +); + +db::NetlistComparer *make_comparer (db::NetlistCompareLogger *logger) +{ + return new db::NetlistComparer (logger); +} + +Class decl_dbNetlistComparer ("db", "NetlistComparer", + gsi::constructor ("new", &make_comparer, gsi::arg ("logger", (db::NetlistCompareLogger *) 0), + "@brief Creates a new comparer object." + "The logger is a delegate or event receiver which the comparer will send compare events to. " + "See the class description for more details." + ) + + gsi::method ("same_nets", &db::NetlistComparer::same_nets, gsi::arg ("net_a"), gsi::arg ("net_b"), + "@brief Marks two nets as identical\n" + "This makes a net net_a in netlist a identical to the corresponding\n" + "net net_b in netlist b (see \\compare).\n" + "Otherwise, the algorithm will try to identify nets according to their topology. " + "This method can be used to supply hints to the compare algorithm. It will use " + "these hints to derive further identities." + ) + + gsi::method ("equivalent_pins", (void (db::NetlistComparer::*) (const db::Circuit *, size_t, size_t)) &db::NetlistComparer::equivalent_pins, gsi::arg ("circuit_b"), gsi::arg ("pin_id1"), gsi::arg ("pin_id2"), + "@brief Marks two pins of the given circuit as equivalent (i.e. they can be swapped)\n" + "Only circuits from the second input can be given swappable pins. " + "This will imply the same swappable pins on the equivalent circuit of the first input. " + "To mark multiple pins as swappable, use the version that takes a list of pins." + ) + + gsi::method ("equivalent_pins", (void (db::NetlistComparer::*) (const db::Circuit *, const std::vector &)) &db::NetlistComparer::equivalent_pins, gsi::arg ("circuit_b"), gsi::arg ("pin_ids"), + "@brief Marks several pins of the given circuit as equivalent (i.e. they can be swapped)\n" + "Only circuits from the second input can be given swappable pins. " + "This will imply the same swappable pins on the equivalent circuit of the first input. " + "This version is a generic variant of the two-pin version of this method." + ) + + gsi::method ("same_device_classes", &db::NetlistComparer::same_device_classes, gsi::arg ("dev_cls_a"), gsi::arg ("dev_cls_b"), + "@brief Marks two device classes as identical\n" + "This makes a device class dev_cls_a in netlist a identical to the corresponding\n" + "device class dev_cls_b in netlist b (see \\compare).\n" + "By default device classes with the same name are identical.\n" + ) + + gsi::method ("same_circuits", &db::NetlistComparer::same_circuits, gsi::arg ("circuit_a"), gsi::arg ("circuit_b"), + "@brief Marks two circuits as identical\n" + "This method makes a circuit circuit_a in netlist a identical to the corresponding\n" + "circuit circuit_b in netlist b (see \\compare). By default circuits with the same name are identical.\n" + ) + + gsi::method ("compare", &db::NetlistComparer::compare, gsi::arg ("netlist_a"), gsi::arg ("netlist_b"), + "@brief Compares two netlists\n" + "This method will perform the actual netlist compare. It will return true if both netlists are identical. " + "If the comparer has been configured with \\same_nets or similar methods, the objects given there must " + "be located inside 'circuit_a' and 'circuit_b' respectively." + ), + "@brief Compares two netlists\n" + "This class performs the actual comparison of two netlists.\n" + "It can be used with an event receiver to log the errors and net mismatches. " + "Event receivers are derived from class \\GenericNetlistCompareLogger." + "\n" + "The netlist comparer can be configured in different ways. Specifically hints can be given for nets, device classes or circuits. " + "Equivalence hints can be given with \\same_nets, \\same_circuits etc.\n" + "\n" + "Another configuration relates to swappable pins of subcircuits. If pins marked this way, the compare algorithm may swap them to " + "achieve net matching. Swappable pins belong to an 'equivalence group' and can be defined with \\equivalent_pins.\n" + "\n" + "This class has been introduced in version 0.26." +); + +}