WIP: GSI binding for netlist comparer.

This commit is contained in:
Matthias Koefferlein 2019-04-04 23:41:46 +02:00
parent 52fb8b0f65
commit c5a56dbc5f
3 changed files with 480 additions and 1 deletions

View File

@ -175,7 +175,8 @@ SOURCES = \
dbRegionProcessors.cc \
dbNetlistCompare.cc \
dbNetlistReader.cc \
dbNetlistSpiceReader.cc
dbNetlistSpiceReader.cc \
gsiDeclDbNetlistCompare.cc
HEADERS = \
dbArray.h \

View File

@ -213,4 +213,16 @@ protected:
}
namespace tl
{
template<> struct type_traits<db::NetlistComparer> : public tl::type_traits<void>
{
// 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

View File

@ -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> (&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> (&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> (&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> (&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> (&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> (&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> (&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> (&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> (&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> (&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> (&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> (&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> (&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> (&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> (&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> (&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> (&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<GenericNetlistCompareLogger> 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<db::NetlistComparer> 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<size_t> &)) &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."
);
}