Netlist compare: configurable device parameter compare scheme.

This commit is contained in:
Matthias Koefferlein 2019-04-06 15:19:43 +02:00
parent 43f65e4d29
commit da5680ef24
7 changed files with 765 additions and 48 deletions

View File

@ -21,10 +21,81 @@
*/
#include "dbDeviceClass.h"
#include "dbDevice.h"
namespace db
{
// --------------------------------------------------------------------------------
// EqualDeviceParameters implementation
static int compare_parameters (double pa, double pb, double absolute, double relative)
{
double pa_min = pa - absolute;
double pa_max = pa + absolute;
double mean = 0.5 * (fabs (pa) + fabs (pb));
pa_min -= mean * relative;
pa_max += mean * relative;
// NOTE: parameter values may be small (e.g. pF for caps) -> no epsilon
if (pa_max < pb) {
return -1;
} else if (pa_min > pb) {
return 1;
} else {
return 0;
}
}
EqualDeviceParameters::EqualDeviceParameters ()
{
// .. nothing yet ..
}
EqualDeviceParameters::EqualDeviceParameters (size_t parameter_id)
{
m_compare_set.push_back (std::make_pair (parameter_id, std::make_pair (0.0, 0.0)));
}
EqualDeviceParameters::EqualDeviceParameters (size_t parameter_id, double relative, double absolute)
{
m_compare_set.push_back (std::make_pair (parameter_id, std::make_pair (relative, absolute)));
}
bool EqualDeviceParameters::less (const db::Device &a, const db::Device &b) const
{
for (std::vector<std::pair<size_t, std::pair<double, double> > >::const_iterator c = m_compare_set.begin (); c != m_compare_set.end (); ++c) {
int cmp = compare_parameters (a.parameter_value (c->first), b.parameter_value (c->first), c->second.first, c->second.second);
if (cmp != 0) {
return cmp < 0;
}
}
return false;
}
bool EqualDeviceParameters::equal (const db::Device &a, const db::Device &b) const
{
for (std::vector<std::pair<size_t, std::pair<double, double> > >::const_iterator c = m_compare_set.begin (); c != m_compare_set.end (); ++c) {
int cmp = compare_parameters (a.parameter_value (c->first), b.parameter_value (c->first), c->second.first, c->second.second);
if (cmp != 0) {
return false;
}
}
return true;
}
EqualDeviceParameters &EqualDeviceParameters::operator+= (const EqualDeviceParameters &other)
{
for (std::vector<std::pair<size_t, std::pair<double, double> > >::const_iterator c = other.m_compare_set.begin (); c != other.m_compare_set.end (); ++c) {
m_compare_set.push_back (*c);
}
return *this;
}
// --------------------------------------------------------------------------------
// DeviceClass class implementation
@ -137,4 +208,58 @@ size_t DeviceClass::terminal_id_for_name (const std::string &name) const
throw tl::Exception (tl::to_string (tr ("Invalid terminal name")) + ": '" + name + "'");
}
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 ();
}
if (pcd != 0) {
return pcd->less (a, b);
} else {
const std::vector<db::DeviceParameterDefinition> &pd = a.device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator p = pd.begin (); p != pd.end (); ++p) {
int cmp = compare_parameters (a.parameter_value (p->id ()), b.parameter_value (p->id ()), 0.0, 0.0);
if (cmp != 0) {
return cmp < 0;
}
}
return false;
}
}
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 ();
}
if (pcd != 0) {
return pcd->equal (a, b);
} else {
const std::vector<db::DeviceParameterDefinition> &pd = a.device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator p = pd.begin (); p != pd.end (); ++p) {
int cmp = compare_parameters (a.parameter_value (p->id ()), b.parameter_value (p->id ()), 0.0, 0.0);
if (cmp != 0) {
return false;
}
}
return true;
}
}
}

View File

@ -207,6 +207,53 @@ private:
}
};
/**
* @brief A device parameter compare delegate
*
* Device parameter compare delegates are used to establish
* device equivalence in the context of netlist comparison.
*/
class DB_PUBLIC DeviceParameterCompareDelegate
: public gsi::ObjectBase, public tl::Object
{
public:
DeviceParameterCompareDelegate () { }
virtual ~DeviceParameterCompareDelegate () { }
virtual bool less (const db::Device &a, const db::Device &b) const = 0;
virtual bool equal (const db::Device &a, const db::Device &b) const = 0;
};
/**
* @brief A parameter compare delegate that compares several parameters either relative or absolute (or both)
*
* The reasoning behind this class is to supply a chainable compare delegate: ab = a + b
* where a and b are compare delegates for two different parameters and ab is the combined compare delegate.
*/
class DB_PUBLIC EqualDeviceParameters
: public DeviceParameterCompareDelegate
{
public:
EqualDeviceParameters ();
EqualDeviceParameters (size_t parameter_id);
EqualDeviceParameters (size_t parameter_id, double relative, double absolute);
virtual bool less (const db::Device &a, const db::Device &b) const;
virtual bool equal (const db::Device &a, const db::Device &b) const;
EqualDeviceParameters &operator+= (const EqualDeviceParameters &other);
EqualDeviceParameters operator+ (const EqualDeviceParameters &other) const
{
EqualDeviceParameters pc (*this);
pc += other;
return pc;
}
private:
std::vector<std::pair<size_t, std::pair<double, double> > > m_compare_set;
};
/**
* @brief A device class
*
@ -228,14 +275,14 @@ public:
/**
* @brief Copy constructor
* NOTE: do not use this copy constructor as the device class
* is intended to subclassing.
* is intended for subclassing.
*/
DeviceClass (const DeviceClass &other);
/**
* @brief Assignment
* NOTE: do not use this copy constructor as the device class
* is intended to subclassing.
* is intended for subclassing.
*/
DeviceClass &operator= (const DeviceClass &other);
@ -365,7 +412,7 @@ public:
size_t terminal_id_for_name (const std::string &name) const;
/**
* @brief Clears the circuit
* @brief Clones the device class
*/
virtual DeviceClass *clone () const
{
@ -414,6 +461,46 @@ public:
return tid;
}
/**
* @brief Compares the parameters of the devices a and b
*
* a and b are expected to originate from this or an equivalent device class having
* the same parameters.
* This is the "less" operation. If a parameter compare delegate is registered, this
* compare request will be forwarded to the delegate.
*
* If two devices with different device classes are compared and only one of
* the classes features a delegate, the one with the delegate is employed.
*/
static bool less (const db::Device &a, const db::Device &b);
/**
* @brief Compares the parameters of the devices a and b
*
* a and b are expected to originate from this or an equivalent device class having
* the same parameters.
* This is the "equal" operation. If a parameter compare delegate is registered, this
* compare request will be forwarded to the delegate.
*
* If two devices with different device classes are compared and only one of
* the classes features a delegate, the one with the delegate is employed.
*/
static bool equal (const db::Device &a, const db::Device &b);
/**
* @brief Registers a compare delegate
*
* The reasoning behind chosing a delegate is that a delegate is efficient
* also in scripts if one of the standard delegates is taken.
*
* The device class takes ownership of the delegate.
*/
virtual void set_parameter_compare_delegate (db::DeviceParameterCompareDelegate *delegate)
{
delegate->keep (); // assume transfer of ownership for scripts
mp_pc_delegate.reset (delegate);
}
private:
friend class Netlist;
@ -421,6 +508,7 @@ private:
std::vector<DeviceTerminalDefinition> m_terminal_definitions;
std::vector<DeviceParameterDefinition> m_parameter_definitions;
db::Netlist *mp_netlist;
tl::shared_ptr<db::DeviceParameterCompareDelegate> mp_pc_delegate;
void set_netlist (db::Netlist *nl)
{

View File

@ -40,16 +40,7 @@ struct DeviceCompare
if (d1.second != d2.second) {
return d1.second < d2.second;
}
const std::vector<db::DeviceParameterDefinition> &dp = d1.first->device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = dp.begin (); i != dp.end (); ++i) {
double v1 = d1.first->parameter_value (i->id ());
double v2 = d2.first->parameter_value (i->id ());
if (fabs (v1 - v2) > db::epsilon) {
return v1 < v2;
}
}
return false;
return db::DeviceClass::less (*d1.first, *d2.first);
}
bool equals (const std::pair<const db::Device *, size_t> &d1, const std::pair<const db::Device *, size_t> &d2) const
@ -57,16 +48,7 @@ struct DeviceCompare
if (d1.second != d2.second) {
return false;
}
const std::vector<db::DeviceParameterDefinition> &dp = d1.first->device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = dp.begin (); i != dp.end (); ++i) {
double v1 = d1.first->parameter_value (i->id ());
double v2 = d2.first->parameter_value (i->id ());
if (fabs (v1 - v2) > db::epsilon) {
return false;
}
}
return true;
return db::DeviceClass::equal (*d1.first, *d2.first);
}
};
@ -1419,6 +1401,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
good = false;
} else {
if (mp_logger) {
dc.equals (dm->second, std::make_pair (d.operator-> (), device_cat)); // @@@
mp_logger->match_devices_with_different_parameters (dm->second.first, d.operator-> ());
}
good = false;

View File

@ -473,11 +473,120 @@ Class<db::DeviceParameterDefinition> decl_dbDeviceParameterDefinition ("db", "De
"This class has been added in version 0.26."
);
namespace
{
/**
* @brief A DeviceParameterCompare implementation that allows reimplementation of the virtual methods
*/
class GenericDeviceParameterCompare
: public db::EqualDeviceParameters
{
public:
GenericDeviceParameterCompare ()
: db::EqualDeviceParameters ()
{
// .. nothing yet ..
}
virtual bool less (const db::Device &a, const db::Device &b) const
{
if (cb_less.can_issue ()) {
return cb_less.issue<db::EqualDeviceParameters, bool, const db::Device &, const db::Device &> (&db::EqualDeviceParameters::less, a, b);
} else {
return db::EqualDeviceParameters::less (a, b);
}
}
virtual bool equal (const db::Device &a, const db::Device &b) const
{
if (cb_equal.can_issue ()) {
return cb_equal.issue<db::EqualDeviceParameters, bool, const db::Device &, const db::Device &> (&db::EqualDeviceParameters::equal, a, b);
} else {
return db::EqualDeviceParameters::equal (a, b);
}
}
gsi::Callback cb_less, cb_equal;
};
}
db::EqualDeviceParameters *make_equal_dp (size_t param_id, double absolute, double relative)
{
return new db::EqualDeviceParameters (param_id, absolute, relative);
}
Class<db::EqualDeviceParameters> decl_dbEqualDeviceParameters ("db", "EqualDeviceParameters",
gsi::constructor ("new", &make_equal_dp, gsi::arg ("param_id"), gsi::arg ("absolute", 0.0), gsi::arg ("relative", 0.0),
"@brief Creates a device parameter comparer for a single parameter.\n"
"'absolute' is the absolute deviation allowed for the parameter values. "
"'relative' is the relative deviation allowed for the parameter values (a value between 0 and 1).\n"
"\n"
"A value of 0 for both absolute and relative deviation means the parameters have to match exactly.\n"
"\n"
"If 'absolute' and 'relative' are both given, their deviations will add to the allowed difference between "
"two parameter values. The relative deviation will be applied to the mean value of both parameter values. "
"For example, when comparing parameter values of 40 and 60, a relative deviation of 0.35 means an absolute "
"deviation of 17.5 (= 0.35 * average of 40 and 60) which does not make both values match."
) +
gsi::method ("+", &db::EqualDeviceParameters::operator+, gsi::arg ("other"),
"@brief Combines two parameters for comparison.\n"
"The '+' operator will join the parameter comparers and produce one that checks the combined parameters.\n"
) +
gsi::method ("+=", &db::EqualDeviceParameters::operator+, gsi::arg ("other"),
"@brief Combines two parameters for comparison (in-place).\n"
"The '+=' operator will join the parameter comparers and produce one that checks the combined parameters.\n"
),
"@brief A device parameter equality comparer.\n"
"Attach this object to a device class with \\DeviceClass#equal_parameters= to make the device "
"class use this comparer:\n"
"\n"
"@code\n"
"# 20nm tolerance for length:\n"
"equal_device_parameters = RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS4Transistor::PARAM_L, 0.02, 0.0)\n"
"# one percent tolerance for width:\n"
"equal_device_parameters += RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS4Transistor::PARAM_W, 0.0, 0.01)\n"
"# applies the compare delegate:\n"
"netlist.device_class_by_name(\"NMOS\").equal_parameters = equal_device_parameters\n"
"@/code\n"
"\n"
"You can use this class to specify fuzzy equality criteria for the comparison of device parameters in "
"netlist verification or to confine the equality of devices to certain parameters only.\n"
"\n"
"This class has been added in version 0.26."
);
Class<GenericDeviceParameterCompare> decl_GenericDeviceParameterCompare (decl_dbEqualDeviceParameters, "db", "GenericDeviceParameterCompare",
gsi::callback ("equal", &GenericDeviceParameterCompare::equal, &GenericDeviceParameterCompare::cb_equal, gsi::arg ("device_a"), gsi::arg ("device_b"),
"@brief Compares the parameters of two devices for equality. "
"Returns true, if the parameters of device a and b are considered equal."
) +
gsi::callback ("less", &GenericDeviceParameterCompare::less, &GenericDeviceParameterCompare::cb_less, gsi::arg ("device_a"), gsi::arg ("device_b"),
"@brief Compares the parameters of two devices for a begin less than b. "
"Returns true, if the parameters of device a are considered less than those of device b."
),
"@brief A class implementing the comparison of device parameters.\n"
"Reimplement this class to provide a custom device parameter compare scheme.\n"
"Attach this object to a device class with \\DeviceClass#equal_parameters= to make the device "
"class use this comparer.\n"
"\n"
"This class is intended for special cases. In most scenarios it is easier to use \\EqualDeviceParameters instead of "
"implementing a custom comparer class.\n"
"\n"
"This class has been added in version 0.26."
);
static tl::id_type id_of_device_class (const db::DeviceClass *cls)
{
return tl::id_of (cls);
}
static void equal_parameters (db::DeviceClass *cls, db::EqualDeviceParameters *comparer)
{
cls->set_parameter_compare_delegate (comparer);
}
Class<db::DeviceClass> decl_dbDeviceClass ("db", "DeviceClass",
gsi::method ("name", &db::DeviceClass::name,
"@brief Gets the name of the device class."
@ -533,6 +642,15 @@ Class<db::DeviceClass> decl_dbDeviceClass ("db", "DeviceClass",
"@brief Returns the terminal ID of the terminal with the given name.\n"
"An exception is thrown if there is no terminal with the given name. Use \\has_terminal to check "
"whether the name is a valid terminal name."
) +
gsi::method_ext ("equal_parameters=", &equal_parameters, gsi::arg ("comparer"),
"@brief Specifies a device parameter comparer for netlist verification.\n"
"By default, all devices are compared with all parameters. If you want to select only certain parameters "
"for comparison or use a fuzzy compare criterion, use an \\EqualDeviceParameters object and assign it "
"to the device class of one netlist. You can also chain multiple \\EqualDeviceParameters objects with the '+' operator "
"for specifying multiple parameters in the equality check.\n"
"\n"
"In special cases, you can even implement a custom compare scheme by deriving your own comparer from the \\GenericDeviceParameterCompare class."
),
"@brief A class describing a specific type of device.\n"
"Device class objects live in the context of a \\Netlist object. After a "
@ -1231,14 +1349,14 @@ Class<db::NetlistSpiceWriter> db_NetlistSpiceWriter (db_NetlistWriter, "db", "Ne
"@code\n"
"writer = RBA::NetlistSpiceWriter::new\n"
"netlist.write(path, writer)\n"
"@endcode\n"
"@/code\n"
"\n"
"You can give a custom description for the headline:\n"
"\n"
"@code\n"
"writer = RBA::NetlistSpiceWriter::new\n"
"netlist.write(path, writer, \"A custom description\")\n"
"@endcode\n"
"@/code\n"
"\n"
"To customize the output, you can use a device writer delegate.\n"
"The delegate is an object of a class derived from \\NetlistSpiceWriterDelegate which "
@ -1279,7 +1397,7 @@ Class<db::NetlistSpiceWriter> db_NetlistSpiceWriter (db_NetlistWriter, "db", "Ne
"# write the netlist with delegate:\n"
"writer = RBA::NetlistSpiceWriter::new(MyDelegate::new)\n"
"netlist.write(path, writer)\n"
"@endcode\n"
"@/code\n"
"\n"
"This class has been introduced in version 0.26."
);
@ -1305,7 +1423,7 @@ Class<db::NetlistSpiceReader> db_NetlistSpiceReader (db_NetlistReader, "db", "Ne
"writer = RBA::NetlistSpiceReader::new\n"
"netlist = RBA::Netlist::new\n"
"netlist.read(path, reader)\n"
"@endcode\n"
"@/code\n"
"\n"
"This class has been introduced in version 0.26."
);

View File

@ -29,7 +29,7 @@ namespace {
* @brief A NetlistDeviceExtractor implementation that allows reimplementation of the virtual methods
*/
class GenericNetlistCompareLogger
: public db::NetlistCompareLogger
: public gsi::ObjectBase, public db::NetlistCompareLogger
{
public:
GenericNetlistCompareLogger ()
@ -296,6 +296,7 @@ public:
};
}
namespace gsi
{
@ -340,11 +341,13 @@ Class<GenericNetlistCompareLogger> decl_GenericNetlistCompareLogger ("db", "Gene
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 the nets can be paired, but this match is ambiguous, \\match_ambiguous_nets 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"
"This choice is a last-resort fallback to allow continuation of the compare procedure. It is likely that this "
"compare will fail later. Looking for ambiguous nets allows deduction of the origin of this faulty decision. "
"See \\match_nets for more details."
) +
gsi::callback ("net_mismatch", &GenericNetlistCompareLogger::net_mismatch, &GenericNetlistCompareLogger::cb_net_mismatch, gsi::arg ("a"), gsi::arg ("b"),
@ -369,7 +372,7 @@ Class<GenericNetlistCompareLogger> decl_GenericNetlistCompareLogger ("db", "Gene
) +
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."
"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"),
@ -379,7 +382,7 @@ Class<GenericNetlistCompareLogger> decl_GenericNetlistCompareLogger ("db", "Gene
) +
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."
"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"),
@ -389,7 +392,7 @@ Class<GenericNetlistCompareLogger> decl_GenericNetlistCompareLogger ("db", "Gene
) +
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."
"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"
@ -400,19 +403,28 @@ Class<GenericNetlistCompareLogger> decl_GenericNetlistCompareLogger ("db", "Gene
"This class has been introduced in version 0.26.\n"
);
static db::NetlistComparer *make_comparer (GenericNetlistCompareLogger *logger)
static db::NetlistComparer *make_comparer0 ()
{
return new db::NetlistComparer (0);
}
static db::NetlistComparer *make_comparer1 (GenericNetlistCompareLogger *logger)
{
return new db::NetlistComparer (logger);
}
Class<db::NetlistComparer> decl_dbNetlistComparer ("db", "NetlistComparer",
gsi::constructor ("new", &make_comparer, gsi::arg ("logger", (GenericNetlistCompareLogger *) 0),
"@brief Creates a new comparer object."
gsi::constructor ("new", &make_comparer0,
"@brief Creates a new comparer object.\n"
"See the class description for more details."
) +
gsi::constructor ("new", &make_comparer1, gsi::arg ("logger"),
"@brief Creates a new comparer object.\n"
"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"
"@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. "
@ -420,43 +432,46 @@ Class<db::NetlistComparer> decl_dbNetlistComparer ("db", "NetlistComparer",
"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"
"@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"
"@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"
"@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"
"@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"
"@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. "
"This class performs a comparison of two netlists.\n"
"It can be used with an event receiver (logger) to track 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"
"The netlist comparer can be configured in different ways. Specific hints can be given for nets, device classes or circuits "
"to improve efficiency and reliability of the graph equivalence deduction algorithm. "
"For example, objects can be marked as equivalent using \\same_nets, \\same_circuits etc. "
"The compare algorithm will then use these hints to derive further equivalences. This way, "
"ambiguities can be resolved.\n"
"\n"
"Another configuration relates to swappable pins of subcircuits. If pins marked this way, the compare algorithm may swap them to "
"Another configuration option relates to swappable pins of subcircuits. If pins are 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."

View File

@ -199,6 +199,106 @@ static void prep_nl (db::Netlist &nl, const char *str)
nl.from_string (str);
}
TEST(0_EqualDeviceParameters)
{
db::DeviceClassMOS3Transistor dc;
db::EqualDeviceParameters *eqp = new db::EqualDeviceParameters ();
*eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.0, 0.0);
dc.set_parameter_compare_delegate (eqp);
db::Device d1 (&dc);
db::Device d2 (&dc);
d1.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 40.0);
d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 40.0);
EXPECT_EQ (dc.equal (d1, d2), true);
EXPECT_EQ (dc.equal (d2, d1), true);
EXPECT_EQ (dc.less (d1, d2), false);
EXPECT_EQ (dc.less (d2, d1), false);
d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 41.0);
EXPECT_EQ (dc.equal (d1, d2), false);
EXPECT_EQ (dc.equal (d2, d1), false);
EXPECT_EQ (dc.less (d1, d2), true);
EXPECT_EQ (dc.less (d2, d1), false);
eqp = new db::EqualDeviceParameters ();
*eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.9, 0.0);
dc.set_parameter_compare_delegate (eqp);
EXPECT_EQ (dc.equal (d1, d2), false);
EXPECT_EQ (dc.equal (d2, d1), false);
EXPECT_EQ (dc.less (d1, d2), true);
EXPECT_EQ (dc.less (d2, d1), false);
eqp = new db::EqualDeviceParameters ();
*eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 1.0, 0.0);
dc.set_parameter_compare_delegate (eqp);
EXPECT_EQ (dc.equal (d1, d2), true);
EXPECT_EQ (dc.equal (d2, d1), true);
EXPECT_EQ (dc.less (d1, d2), false);
EXPECT_EQ (dc.less (d2, d1), false);
eqp = new db::EqualDeviceParameters ();
*eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 1.1, 0.0);
dc.set_parameter_compare_delegate (eqp);
EXPECT_EQ (dc.equal (d1, d2), true);
EXPECT_EQ (dc.equal (d2, d1), true);
EXPECT_EQ (dc.less (d1, d2), false);
EXPECT_EQ (dc.less (d2, d1), false);
eqp = new db::EqualDeviceParameters ();
*eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.5, 0.01);
dc.set_parameter_compare_delegate (eqp);
EXPECT_EQ (dc.equal (d1, d2), false);
EXPECT_EQ (dc.equal (d2, d1), false);
EXPECT_EQ (dc.less (d1, d2), true);
EXPECT_EQ (dc.less (d2, d1), false);
eqp = new db::EqualDeviceParameters ();
*eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.5, 0.013);
dc.set_parameter_compare_delegate (eqp);
EXPECT_EQ (dc.equal (d1, d2), true);
EXPECT_EQ (dc.equal (d2, d1), true);
EXPECT_EQ (dc.less (d1, d2), false);
EXPECT_EQ (dc.less (d2, d1), false);
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), true);
EXPECT_EQ (dc.equal (d2, d1), true);
EXPECT_EQ (dc.less (d1, d2), false);
EXPECT_EQ (dc.less (d2, d1), false);
eqp = new db::EqualDeviceParameters ();
*eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.5, 0.013);
*eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_W);
dc.set_parameter_compare_delegate (eqp);
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 = new db::EqualDeviceParameters ();
*eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.5, 0.013);
*eqp += db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_W, 0.3, 1e-6);
dc.set_parameter_compare_delegate (eqp);
EXPECT_EQ (dc.equal (d1, d2), true);
EXPECT_EQ (dc.equal (d2, d1), true);
EXPECT_EQ (dc.less (d1, d2), false);
EXPECT_EQ (dc.less (d2, d1), false);
}
TEST(1_SimpleInverter)
{
const char *nls1 =
@ -496,6 +596,164 @@ TEST(5_BufferTwoPathsDifferentParameters)
"end_circuit BUF BUF NOMATCH"
);
EXPECT_EQ (good, false);
logger.clear ();
nl2.device_class_by_name ("NMOS")->set_parameter_compare_delegate (new db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 1.5, 0.0));
good = comp.compare (&nl1, &nl2);
EXPECT_EQ (logger.text (),
"begin_circuit BUF BUF\n"
"match_nets OUT OUT\n"
"match_nets IN IN\n"
"match_ambiguous_nets INT $10\n"
"match_ambiguous_nets INT2 $11\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
"match_pins $3 $2\n"
"match_devices $1 $1\n"
"match_devices $3 $2\n"
"match_devices $5 $3\n"
"match_devices $7 $4\n"
"match_devices $2 $5\n"
"match_devices $4 $6\n"
"match_devices $6 $7\n"
"match_devices $8 $8\n"
"end_circuit BUF BUF MATCH"
);
EXPECT_EQ (good, true);
logger.clear ();
nl2.device_class_by_name ("NMOS")->set_parameter_compare_delegate (new db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.0, 0.0));
good = comp.compare (&nl1, &nl2);
EXPECT_EQ (logger.text (),
"begin_circuit BUF BUF\n"
"match_nets OUT OUT\n"
"match_nets IN IN\n"
"match_ambiguous_nets INT $10\n"
"match_nets INT2 $11\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
"match_pins $3 $2\n"
"match_devices $1 $1\n"
"match_devices $3 $2\n"
"match_devices $5 $3\n"
"match_devices $7 $4\n"
"match_devices $2 $5\n"
"match_devices $4 $6\n"
"match_devices_with_different_parameters $6 $7\n"
"match_devices $8 $8\n"
"end_circuit BUF BUF NOMATCH"
);
EXPECT_EQ (good, false);
logger.clear ();
nl2.device_class_by_name ("NMOS")->set_parameter_compare_delegate (new db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.0, 0.2));
good = comp.compare (&nl1, &nl2);
EXPECT_EQ (logger.text (),
"begin_circuit BUF BUF\n"
"match_nets OUT OUT\n"
"match_nets IN IN\n"
"match_ambiguous_nets INT $10\n"
"match_nets INT2 $11\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
"match_pins $3 $2\n"
"match_devices $1 $1\n"
"match_devices $3 $2\n"
"match_devices $5 $3\n"
"match_devices $7 $4\n"
"match_devices $2 $5\n"
"match_devices $4 $6\n"
"match_devices_with_different_parameters $6 $7\n"
"match_devices $8 $8\n"
"end_circuit BUF BUF NOMATCH"
);
EXPECT_EQ (good, false);
logger.clear ();
nl2.device_class_by_name ("NMOS")->set_parameter_compare_delegate (new db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.0, 0.4));
good = comp.compare (&nl1, &nl2);
EXPECT_EQ (logger.text (),
"begin_circuit BUF BUF\n"
"match_nets OUT OUT\n"
"match_nets IN IN\n"
"match_ambiguous_nets INT $10\n"
"match_ambiguous_nets INT2 $11\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
"match_pins $3 $2\n"
"match_devices $1 $1\n"
"match_devices $3 $2\n"
"match_devices $5 $3\n"
"match_devices $7 $4\n"
"match_devices $2 $5\n"
"match_devices $4 $6\n"
"match_devices $6 $7\n"
"match_devices $8 $8\n"
"end_circuit BUF BUF MATCH"
);
EXPECT_EQ (good, true);
logger.clear ();
db::EqualDeviceParameters eq_dp = db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_W) + db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L, 0.2, 0.0);
nl2.device_class_by_name ("NMOS")->set_parameter_compare_delegate (new db::EqualDeviceParameters (eq_dp));
good = comp.compare (&nl1, &nl2);
EXPECT_EQ (logger.text (),
"begin_circuit BUF BUF\n"
"match_nets OUT OUT\n"
"match_nets IN IN\n"
"match_ambiguous_nets INT $10\n"
"match_ambiguous_nets INT2 $11\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
"match_pins $3 $2\n"
"match_devices $1 $1\n"
"match_devices $3 $2\n"
"match_devices $5 $3\n"
"match_devices $7 $4\n"
"match_devices $2 $5\n"
"match_devices $4 $6\n"
"match_devices $6 $7\n"
"match_devices $8 $8\n"
"end_circuit BUF BUF MATCH"
);
EXPECT_EQ (good, true);
logger.clear ();
eq_dp = db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_W) + db::EqualDeviceParameters (db::DeviceClassMOS3Transistor::param_id_L);
nl2.device_class_by_name ("NMOS")->set_parameter_compare_delegate (new db::EqualDeviceParameters (eq_dp));
good = comp.compare (&nl1, &nl2);
EXPECT_EQ (logger.text (),
"begin_circuit BUF BUF\n"
"match_nets OUT OUT\n"
"match_nets IN IN\n"
"match_ambiguous_nets INT $10\n"
"match_nets INT2 $11\n"
"match_pins $0 $1\n"
"match_pins $1 $3\n"
"match_pins $2 $0\n"
"match_pins $3 $2\n"
"match_devices $1 $1\n"
"match_devices $3 $2\n"
"match_devices $5 $3\n"
"match_devices $7 $4\n"
"match_devices $2 $5\n"
"match_devices $4 $6\n"
"match_devices_with_different_parameters $6 $7\n"
"match_devices $8 $8\n"
"end_circuit BUF BUF NOMATCH"
);
EXPECT_EQ (good, false);
}
TEST(5_BufferTwoPathsDifferentDeviceClasses)

View File

@ -448,6 +448,92 @@ END
assert_equal(good, false)
logger.clear
eqp = RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS3Transistor::PARAM_L, 0.2, 0.0)
nl2.device_class_by_name("NMOS").equal_parameters = eqp
good = comp.compare(nl1, nl2)
assert_equal(logger.text, <<"END")
begin_circuit BUF BUF
match_nets OUT OUT
match_nets IN IN
match_ambiguous_nets INT $10
match_ambiguous_nets INT2 $11
match_pins $0 $1
match_pins $1 $3
match_pins $2 $0
match_pins $3 $2
match_devices $1 $1
match_devices $3 $2
match_devices $5 $3
match_devices $7 $4
match_devices $2 $5
match_devices $4 $6
match_devices $6 $7
match_devices $8 $8
end_circuit BUF BUF MATCH
END
assert_equal(good, true)
logger.clear
eqp = RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS3Transistor::PARAM_W, 0.01, 0.0)
eqp = eqp + RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS3Transistor::PARAM_L, 0.2, 0.0)
nl2.device_class_by_name("NMOS").equal_parameters = eqp
good = comp.compare(nl1, nl2)
assert_equal(logger.text, <<"END")
begin_circuit BUF BUF
match_nets OUT OUT
match_nets IN IN
match_ambiguous_nets INT $10
match_ambiguous_nets INT2 $11
match_pins $0 $1
match_pins $1 $3
match_pins $2 $0
match_pins $3 $2
match_devices $1 $1
match_devices $3 $2
match_devices $5 $3
match_devices $7 $4
match_devices $2 $5
match_devices $4 $6
match_devices $6 $7
match_devices $8 $8
end_circuit BUF BUF MATCH
END
assert_equal(good, true)
logger.clear
eqp = RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS3Transistor::PARAM_W, 0.01, 0.0)
eqp += RBA::EqualDeviceParameters::new(RBA::DeviceClassMOS3Transistor::PARAM_L, 0.2, 0.0)
nl2.device_class_by_name("NMOS").equal_parameters = eqp
good = comp.compare(nl1, nl2)
assert_equal(logger.text, <<"END")
begin_circuit BUF BUF
match_nets OUT OUT
match_nets IN IN
match_ambiguous_nets INT $10
match_ambiguous_nets INT2 $11
match_pins $0 $1
match_pins $1 $3
match_pins $2 $0
match_pins $3 $2
match_devices $1 $1
match_devices $3 $2
match_devices $5 $3
match_devices $7 $4
match_devices $2 $5
match_devices $4 $6
match_devices $6 $7
match_devices $8 $8
end_circuit BUF BUF MATCH
END
assert_equal(good, true)
end
def test_6
@ -735,6 +821,50 @@ match_pins $4 $4
match_subcircuits $2 $1
match_subcircuits $1 $2
end_circuit TOP TOP MATCH
END
assert_equal(good, true)
logger.clear
comp = RBA::NetlistComparer::new(logger)
comp.equivalent_pins(nl2.circuit_by_name("NAND"), [ 1, 0 ])
good = comp.compare(nl1, nl2)
assert_equal(logger.text, <<"END")
begin_circuit NAND NAND
match_nets VSS VSS
match_nets VDD VDD
match_nets B B
match_nets OUT OUT
match_nets A A
match_nets INT INT
match_pins $0 $0
match_pins $1 $1
match_pins $2 $2
match_pins $3 $3
match_pins $4 $4
match_devices $1 $1
match_devices $2 $2
match_devices $3 $3
match_devices $4 $4
end_circuit NAND NAND MATCH
begin_circuit TOP TOP
match_nets OUT OUT
match_nets IN2 IN2
match_nets VSS VSS
match_nets VDD VDD
match_nets IN1 IN1
match_nets INT INT
match_pins $0 $0
match_pins $1 $1
match_pins $2 $2
match_pins $3 $3
match_pins $4 $4
match_subcircuits $2 $1
match_subcircuits $1 $2
end_circuit TOP TOP MATCH
END
assert_equal(good, true)