WIP: better matching of subcircuits - attempt to map them even if not identical. This hopefully makes solving subcircuit connection problems easier.

This commit is contained in:
Matthias Koefferlein 2019-06-28 11:05:43 +02:00
parent 3310d34cf3
commit 910a36b83d
4 changed files with 291 additions and 37 deletions

View File

@ -1942,6 +1942,89 @@ compute_subcircuit_key (const db::SubCircuit &subcircuit, const db::NetGraph &g,
return k;
}
namespace {
inline double size_dist (size_t a, size_t b)
{
double d = a - b;
return d * d;
}
struct KeyDistance
{
typedef std::pair<std::vector<std::pair<size_t, size_t> >, const db::SubCircuit *> value_type;
double operator() (const value_type &a, const value_type &b) const
{
tl_assert (a.first.size () == b.first.size ());
double d = 0.0;
for (std::vector<std::pair<size_t, size_t> >::const_iterator i = a.first.begin (), j = b.first.begin (); i != a.first.end (); ++i, ++j) {
d += size_dist (i->first, j->first) + size_dist (i->second, j->second);
}
return d;
}
};
struct KeySize
{
typedef std::pair<std::vector<std::pair<size_t, size_t> >, const db::SubCircuit *> value_type;
bool operator() (const value_type &a, const value_type &b) const
{
return (a.first.size () < b.first.size ());
}
};
template <class Iter, class Distance>
void align (Iter i1, Iter i2, Iter j1, Iter j2, Distance distance)
{
// TODO: this can probably be done more efficiently
std::vector<Iter> vi, vj;
vi.reserve (std::max (i2 - i1, j2 - j1));
vj.reserve (std::max (i2 - i1, j2 - j1));
for (Iter i = i1; i != i2; ++i) {
vi.push_back (i);
}
for (Iter j = j1; j != j2; ++j) {
vj.push_back (j);
}
while (vi.size () < vj.size ()) {
vi.push_back (Iter ());
}
while (vj.size () < vi.size ()) {
vj.push_back (Iter ());
}
if (vi.size () <= 1) {
return;
}
// Caution: this is an O(2) algorithm ...
bool any_swapped = true;
for (size_t n = 0; n < vi.size () - 1 && any_swapped; ++n) {
any_swapped = false;
for (size_t m = n + 1; m < vj.size () - 1; ++m) {
if (vi [n] == Iter () || vi [m] == Iter () || vj [n] == Iter () || vj [m] == Iter ()) {
continue;
} else if (distance (*vi [n], *vj [m]) + distance (*vi [m], *vj [n]) < distance (*vi [n], *vj [n]) + distance (*vi [m], *vj [m])) {
// this will reduce the overall distance:
std::swap (vj [n], vj [m]);
any_swapped = true;
}
}
}
}
}
bool
NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, db::CircuitPinMapper &circuit_pin_mapper, const std::vector<std::pair<const Net *, const Net *> > &net_identity, bool &pin_mismatch, std::map<const db::Circuit *, CircuitMapper> &c12_circuit_and_pin_mapping, std::map<const db::Circuit *, CircuitMapper> &c22_circuit_and_pin_mapping) const
{
@ -2325,6 +2408,9 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
}
typedef std::vector<std::pair<std::vector<std::pair<size_t, size_t> >, const db::SubCircuit *> > unmatched_list;
unmatched_list unmatched_a, unmatched_b;
for (db::Circuit::const_subcircuit_iterator sc = c2->begin_subcircuits (); sc != c2->end_subcircuits (); ++sc) {
std::vector<std::pair<size_t, size_t> > k = compute_subcircuit_key (*sc, g2, &c22_circuit_and_pin_mapping, &circuit_pin_mapper);
@ -2345,7 +2431,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
if (! mapped || scm == subcircuit_map.end ()) {
if (mp_logger) {
mp_logger->subcircuit_mismatch (0, sc.operator-> ());
unmatched_b.push_back (std::make_pair (k, sc.operator-> ()));
}
good = false;
@ -2373,11 +2459,81 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
for (std::multimap<std::vector<std::pair<size_t, size_t> >, std::pair<const db::SubCircuit *, size_t> >::const_iterator scm = subcircuit_map.begin (); scm != subcircuit_map.end (); ++scm) {
if (mp_logger) {
mp_logger->subcircuit_mismatch (scm->second.first, 0);
unmatched_a.push_back (std::make_pair (scm->first, scm->second.first));
}
good = false;
}
// try to do some pairing between the mismatching subcircuits - even though we will still report them as
// mismatches it will give some better hint about what needs to be fixed
if (mp_logger) {
size_t max_analysis_set = 1000;
if (unmatched_a.size () + unmatched_b.size () > max_analysis_set) {
// don't try too much analysis - this may be a waste of time
for (unmatched_list::const_iterator i = unmatched_a.begin (); i != unmatched_a.end (); ++i) {
mp_logger->subcircuit_mismatch (i->second, 0);
}
for (unmatched_list::const_iterator i = unmatched_b.begin (); i != unmatched_b.end (); ++i) {
mp_logger->subcircuit_mismatch (0, i->second);
}
} else {
std::sort (unmatched_a.begin (), unmatched_a.end (), KeySize ());
std::sort (unmatched_b.begin (), unmatched_b.end (), KeySize ());
for (unmatched_list::iterator i = unmatched_a.begin (), j = unmatched_b.begin (); i != unmatched_a.end () || j != unmatched_b.end (); ) {
while (j != unmatched_b.end () && (i == unmatched_a.end () || j->first.size () < i->first.size ())) {
mp_logger->subcircuit_mismatch (0, j->second);
++j;
}
while (i != unmatched_a.end () && (j == unmatched_b.end () || i->first.size () < j->first.size ())) {
mp_logger->subcircuit_mismatch (i->second, 0);
++i;
}
if (i == unmatched_a.end () && j == unmatched_b.end ()) {
break;
}
unmatched_list::iterator ii = i, jj = j;
++i, ++j;
size_t n = ii->first.size ();
tl_assert (n == jj->first.size ());
while (i != unmatched_a.end () && i->first.size () == n) {
++i;
}
while (j != unmatched_b.end () && j->first.size () == n) {
++j;
}
align (ii, i, jj, j, KeyDistance ());
for ( ; ii != i && jj != j; ++ii, ++jj) {
mp_logger->subcircuit_mismatch (ii->second, jj->second);
}
for ( ; jj != j; ++jj) {
mp_logger->subcircuit_mismatch (0, jj->second);
}
for ( ; ii != i; ++ii) {
mp_logger->subcircuit_mismatch (ii->second, 0);
}
}
}
}
return good;
}

View File

@ -830,6 +830,11 @@ std::string devices_string (const std::pair<const db::Device *, const db::Device
static QString build_url (void *id, const std::string &tag, const std::string &title)
{
if (id == 0) {
// no link
return tl::to_qstring (tl::escaped_to_html (title));
}
std::string s = std::string ("<a href='int:");
s += tag;
s += "?id=";
@ -849,7 +854,15 @@ NetlistBrowserModel::make_link_to (const std::pair<const db::Net *, const db::Ne
if ((! nets.first || column == m_second_column) && (! nets.second || column == m_first_column)) {
return QString ();
} else {
void *id = make_id_circuit_net (mp_indexer->circuit_index (mp_indexer->parent_of (nets)), mp_indexer->net_index (nets));
IndexedNetlistModel::circuit_pair circuits = mp_indexer->parent_of (nets);
void *id = 0;
// NOTE: the nets may not be a valid net pair. In this case, circuits is (0, 0) and
// no link is generated
if (circuits.first || circuits.second) {
id = make_id_circuit_net (mp_indexer->circuit_index (circuits), mp_indexer->net_index (nets));
}
if (mp_indexer->is_single () || column == m_first_column) {
return build_url (id, "net", str_from_expanded_name (nets.first));
} else if (column == m_second_column) {
@ -857,6 +870,7 @@ NetlistBrowserModel::make_link_to (const std::pair<const db::Net *, const db::Ne
} else {
return build_url (id, "net", str_from_expanded_names (nets, mp_indexer->is_single ()));
}
}
}
@ -866,7 +880,15 @@ NetlistBrowserModel::make_link_to (const std::pair<const db::Device *, const db:
if ((! devices.first || column == m_second_column) && (! devices.second || column == m_first_column)) {
return QString ();
} else {
void *id = make_id_circuit_device (mp_indexer->circuit_index (mp_indexer->parent_of (devices)), mp_indexer->device_index (devices));
IndexedNetlistModel::circuit_pair circuits = mp_indexer->parent_of (devices);
void *id = 0;
// NOTE: the devices may not be a valid device pair. In this case, circuits is (0, 0) and
// no link is generated
if (circuits.first || circuits.second) {
id = make_id_circuit_device (mp_indexer->circuit_index (circuits), mp_indexer->device_index (devices));
}
if (mp_indexer->is_single () || column == m_first_column) {
return build_url (id, "device", str_from_expanded_name (devices.first));
} else if (column == m_second_column) {
@ -874,6 +896,7 @@ NetlistBrowserModel::make_link_to (const std::pair<const db::Device *, const db:
} else {
return build_url (id, "device", str_from_expanded_names (devices, mp_indexer->is_single ()));
}
}
}
@ -917,7 +940,15 @@ NetlistBrowserModel::make_link_to (const std::pair<const db::SubCircuit *, const
if ((! subcircuits.first || column == m_second_column) && (! subcircuits.second || column == m_first_column)) {
return QString ();
} else {
void *id = make_id_circuit_subcircuit (mp_indexer->circuit_index (mp_indexer->parent_of (subcircuits)), mp_indexer->subcircuit_index (subcircuits));
IndexedNetlistModel::circuit_pair circuits = mp_indexer->parent_of (subcircuits);
void *id = 0;
// NOTE: the subcircuits may not be a valid subcircuit pair. In this case, circuits is (0, 0) and
// no link is generated
if (circuits.first || circuits.second) {
id = make_id_circuit_subcircuit (mp_indexer->circuit_index (circuits), mp_indexer->subcircuit_index (subcircuits));
}
if (mp_indexer->is_single () || column == m_first_column) {
return build_url (id, "subcircuit", str_from_expanded_name (subcircuits.first));
} else if (column == m_second_column) {
@ -925,6 +956,7 @@ NetlistBrowserModel::make_link_to (const std::pair<const db::SubCircuit *, const
} else {
return build_url (id, "subcircuit", str_from_expanded_names (subcircuits, mp_indexer->is_single ()));
}
}
}
@ -1313,6 +1345,13 @@ static std::string search_string_from_names (const std::pair<const Obj *, const
}
}
bool
NetlistBrowserModel::is_valid_net_pair (const std::pair<const db::Net *, const db::Net *> &nets) const
{
IndexedNetlistModel::circuit_pair net_parent = mp_indexer->parent_of (nets);
return (net_parent.first != 0 || net_parent.second != 0);
}
db::NetlistCrossReference::Status
NetlistBrowserModel::status (const QModelIndex &index) const
{
@ -1347,7 +1386,17 @@ NetlistBrowserModel::status (const QModelIndex &index) const
IndexedNetlistModel::circuit_pair circuit_refs = circuit_refs_from_subcircuits (subcircuits);
IndexedNetlistModel::pin_pair pins = pins_from_id (id);
return mp_indexer->pin_from_index (circuit_refs, mp_indexer->pin_index (pins, circuit_refs)).second;
db::NetlistCrossReference::Status status = mp_indexer->pin_from_index (circuit_refs, mp_indexer->pin_index (pins, circuit_refs)).second;
if (status == db::NetlistCrossReference::Mismatch || status == db::NetlistCrossReference::NoMatch) {
return status;
}
// Another test here is to check whether the pins may be attached to an invalid net pair
if (! is_valid_net_pair (nets_from_subcircuit_pins (subcircuits, pins))) {
// This indicates a wrong connection: the nets are associated in a way which is a not
// corresponding to a mapped net pair. Report Mismatch here.
return db::NetlistCrossReference::Mismatch;
}
} else if (is_id_circuit_net (id)) {
@ -1371,11 +1420,31 @@ NetlistBrowserModel::status (const QModelIndex &index) const
return mp_indexer->subcircuit_from_index (circuits, mp_indexer->subcircuit_index (subcircuits)).second;
} else if (is_id_circuit_net_subcircuit_pin_others (id)) {
IndexedNetlistModel::net_subcircuit_pin_pair pinrefs = net_subcircuit_pinrefs_from_id (id);
IndexedNetlistModel::subcircuit_pair subcircuits = subcircuits_from_pinrefs (pinrefs);
size_t other_index = circuit_net_subcircuit_pin_other_index_from_id (id);
IndexedNetlistModel::circuit_pair circuit_refs = circuit_refs_from_subcircuits (subcircuits);
IndexedNetlistModel::pin_pair pins = mp_indexer->pin_from_index (circuit_refs, other_index).first;
if (! is_valid_net_pair (nets_from_subcircuit_pins (subcircuits, pins))) {
// This indicates a wrong connection: the nets are associated in a way which is a not
// corresponding to a mapped net pair. Report Mismatch here.
return db::NetlistCrossReference::Mismatch;
}
}
return db::NetlistCrossReference::None;
}
static std::string rewire_subcircuit_pins_status_hint ()
{
return tl::to_string (tr ("The nets attached to the pins are not equivalent.\nRewire the circuit or use 'equivalent_pins' in the LVS script to fix this issue."));
}
QVariant
NetlistBrowserModel::tooltip (const QModelIndex &index) const
{
@ -1412,6 +1481,14 @@ NetlistBrowserModel::tooltip (const QModelIndex &index) const
IndexedNetlistModel::pin_pair pins = pins_from_id (id);
hint = mp_indexer->pin_status_hint (circuit_refs, mp_indexer->pin_index (pins, circuit_refs));
if (hint.empty ()) {
// Another test here is to check whether the pins may be attached to an invalid net pair
if (! is_valid_net_pair (nets_from_subcircuit_pins (subcircuits, pins))) {
hint = rewire_subcircuit_pins_status_hint ();
}
}
} else if (is_id_circuit_net (id)) {
@ -1435,6 +1512,21 @@ NetlistBrowserModel::tooltip (const QModelIndex &index) const
hint = mp_indexer->subcircuit_status_hint (circuits, mp_indexer->subcircuit_index (subcircuits));
} else if (is_id_circuit_net_subcircuit_pin_others (id)) {
IndexedNetlistModel::net_subcircuit_pin_pair pinrefs = net_subcircuit_pinrefs_from_id (id);
IndexedNetlistModel::subcircuit_pair subcircuits = subcircuits_from_pinrefs (pinrefs);
size_t other_index = circuit_net_subcircuit_pin_other_index_from_id (id);
IndexedNetlistModel::circuit_pair circuit_refs = circuit_refs_from_subcircuits (subcircuits);
IndexedNetlistModel::pin_pair pins = mp_indexer->pin_from_index (circuit_refs, other_index).first;
if (! is_valid_net_pair (nets_from_subcircuit_pins (subcircuits, pins))) {
// This indicates a wrong connection: the nets are associated in a way which is a not
// corresponding to a mapped net pair. Report Mismatch here.
hint = rewire_subcircuit_pins_status_hint ();
}
}
if (hint.empty ()) {
@ -1773,7 +1865,7 @@ NetlistBrowserModel::icon (const QModelIndex &index) const
} else if (is_id_circuit_subcircuit (id)) {
return icon_for_circuit ();
} else if (is_id_circuit_subcircuit_pin (id) || is_id_circuit_net_pin (id) || is_id_circuit_net_subcircuit_pin_others (id)) {
} else if (is_id_circuit_subcircuit_pin (id) || is_id_circuit_net_pin (id)) {
return icon_for_pin ();
} else if (is_id_circuit_net_subcircuit_pin (id)) {
return icon_for_circuit ();

View File

@ -219,6 +219,8 @@ private:
return std::pair<const db::Netlist *, const db::Netlist *> (mp_l2ndb->netlist (), (const db::Netlist *)0);
}
bool is_valid_net_pair (const std::pair<const db::Net *, const db::Net *> &net) const;
QIcon icon_for_nets (const std::pair<const db::Net *, const db::Net *> &net) const;
QIcon icon_for_connection (const std::pair<const db::Net *, const db::Net *> &net) const;

View File

@ -238,10 +238,14 @@ static IndexedNetlistModel::circuit_pair get_parent_of (const Pair &pair, const
}
i = cache.find (pair);
tl_assert (i != cache.end ());
}
return i->second;
if (i == cache.end ()) {
return IndexedNetlistModel::circuit_pair ((const db::Circuit *) 0, (const db::Circuit *) 0);
} else {
return i->second;
}
}
IndexedNetlistModel::circuit_pair NetlistCrossReferenceModel::parent_of (const IndexedNetlistModel::net_pair &net_pair) const
@ -436,19 +440,19 @@ std::string NetlistCrossReferenceModel::circuit_pair_status_hint (const std::pai
if (cps.second == db::NetlistCrossReference::Mismatch || cps.second == db::NetlistCrossReference::NoMatch) {
if (! cps.first.first || ! cps.first.second) {
return tl::to_string (tr ("No matching circuit found in the other netlist.\n"
"By default, circuits are identified by their name. "
"By default, circuits are identified by their name.\n"
"A missing circuit probably means there is no circuit in the other netlist with this name.\n"
"If circuits with different names need to be associated, use 'same_circuits' in the "
"If circuits with different names need to be associated, use 'same_circuits' in the\n"
"LVS script to establish such an association."));
} else {
return tl::to_string (tr ("Circuits could be paired, but there is a mismatch inside.\n"
"Browse the circuit's component list to identify the mismatching elements."));
}
} else if (cps.second == db::NetlistCrossReference::Skipped) {
return tl::to_string (tr ("Circuits can only be matched if their child circuits have a known counterpart and a pin-to-pin "
"correspondence could be established for each child circuit.\n"
return tl::to_string (tr ("Circuits can only be matched if their child circuits have a known counterpart and a\n"
"pin-to-pin correspondence could be established for each child circuit.\n"
"This is not the case here. Browse the child circuits to identify the blockers.\n"
"Potential blockers are subcircuits without a corresponding other circuit or circuits "
"Potential blockers are subcircuits without a corresponding other circuit or circuits\n"
"where some pins could not be mapped to pins from the corresponding other circuit."));
}
return std::string ();
@ -469,15 +473,15 @@ std::string NetlistCrossReferenceModel::child_circuit_status_hint (const circuit
std::pair<IndexedNetlistModel::circuit_pair, NetlistCrossReferenceModel::Status> cps = child_circuit_from_index (circuits, index);
if (cps.second == db::NetlistCrossReference::Mismatch || cps.second == db::NetlistCrossReference::NoMatch) {
if (!cps.first.first || !cps.first.second) {
return tl::to_string (tr ("No matching subcircuit was found in the other netlist - this is likely because pin assignment "
"could not be derived from the nets connected to the pins.\n"
"Check, if the pins are attached properly. If pins need to be swappable, consider using 'equivalent_pins' "
"in the LVS script."));
return tl::to_string (tr ("No matching subcircuit was found in the other netlist - this is likely because pin\n"
"assignment could not be derived from the nets connected to the pins.\n"
"Check, if the pins are attached properly. If pins need to be swappable, consider using\n"
"'equivalent_pins' in the LVS script."));
} else {
return tl::to_string (tr ("Two different subcircuits fit here in the same way, but they are not "
return tl::to_string (tr ("Two different subcircuits fit here in the same way, but they are not\n"
"originating from equivalent circuits.\n"
"If the circuits behind the subcircuits are identical, using 'same_circuits' in the LVS script "
"helps to associate them."));
"If the circuits behind the subcircuits are identical, using 'same_circuits'\n"
"in the LVS script will associate them."));
}
}
return std::string ();
@ -487,9 +491,9 @@ std::string NetlistCrossReferenceModel::net_status_hint (const circuit_pair &cir
{
std::pair<IndexedNetlistModel::net_pair, NetlistCrossReferenceModel::Status> cps = net_from_index (circuits, index);
if (cps.second == db::NetlistCrossReference::Mismatch || cps.second == db::NetlistCrossReference::NoMatch) {
return tl::to_string (tr ("Nets don't match. Nets match, if connected subcircuit pins and device terminals match to a counterpart in "
"the other netlist (component-wise and pin/terminal-wise).\n"
"If there already is a net candidate from the other netlist, scan the net members for "
return tl::to_string (tr ("Nets don't match. Nets match, if connected subcircuit pins and device terminals match to a\n"
"counterpart in the other netlist (component-wise and pin/terminal-wise).\n"
"If there already is a net candidate from the other netlist, scan the net members for\n"
"mismatching items (with errors or warnings) and fix these issues.\n"
"Otherwise, look for the corresponding other net.\n"
"Net items not found in the reference netlist indicate additional connections.\n"
@ -504,15 +508,15 @@ std::string NetlistCrossReferenceModel::device_status_hint (const circuit_pair &
if (cps.second == db::NetlistCrossReference::Mismatch || cps.second == db::NetlistCrossReference::NoMatch) {
if (!cps.first.first || !cps.first.second) {
return tl::to_string (tr ("No matching device was found in the other netlist.\n"
"Devices are identified by the nets they are attached to. Unmatched devices mean that "
"Devices are identified by the nets they are attached to. Unmatched devices mean that\n"
"at least one terminal net isn't matched with a corresponding net from the other netlist.\n"
"Make all terminal nets match and the devices will match too."));
}
} else if (cps.second == db::NetlistCrossReference::MatchWithWarning) {
return tl::to_string (tr ("Topologically matching devices are found here but either the parameters or the "
return tl::to_string (tr ("Topologically matching devices are found here but either the parameters or the\n"
"device classes don't match.\n"
"If the device class is different but should be considered the same, "
"using 'same_device_class' in the LVS script will solve this issue."));
"If the device class is different but should be considered the same, using\n"
"'same_device_classed' in the LVS script will solve this issue."));
}
return std::string ();
}
@ -523,8 +527,8 @@ std::string NetlistCrossReferenceModel::pin_status_hint (const circuit_pair &cir
if (cps.second == db::NetlistCrossReference::Mismatch || cps.second == db::NetlistCrossReference::NoMatch) {
if (!cps.first.first || !cps.first.second) {
return tl::to_string (tr ("No matching pin was found in the other netlist.\n"
"Pins are identified by the nets they are attached to - pins on equivalent nets are also equivalent.\n"
"Making the nets match will make the pins match too."));
"Pins are identified by the nets they are attached to - pins on equivalent nets are also\n"
"equivalent. Making the nets match will make the pins match too."));
}
}
return std::string ();
@ -535,15 +539,15 @@ std::string NetlistCrossReferenceModel::subcircuit_status_hint (const circuit_pa
std::pair<IndexedNetlistModel::subcircuit_pair, NetlistCrossReferenceModel::Status> cps = subcircuit_from_index (circuits, index);
if (cps.second == db::NetlistCrossReference::Mismatch || cps.second == db::NetlistCrossReference::NoMatch) {
if (!cps.first.first || !cps.first.second) {
return tl::to_string (tr ("No matching subcircuit was found in the other netlist - this is likely because pin assignment "
return tl::to_string (tr ("No matching subcircuit was found in the other netlist - this is likely because pin assignment\n"
"could not be derived from the nets connected to the pins.\n"
"Check, if the pins are attached properly. If pins need to be swappable, consider using 'equivalent_pins' "
"in the LVS script."));
"Check, if the pins are attached properly. If pins need to be swappable, consider using\n"
"'equivalent_pins' in the LVS script."));
} else {
return tl::to_string (tr ("Two different subcircuits fit here in the same way, but they are not "
"originating from equivalent circuits.\n"
"If the circuits behind the subcircuits are identical, using 'same_circuits' in the LVS script "
"helps to associate them."));
return tl::to_string (tr ("Two different subcircuits fit here in the same way, but they are not originating from\n"
"equivalent circuits.\n"
"If the circuits behind the subcircuits are identical, using 'same_circuits' in the LVS script\n"
"will associate them."));
}
}
return std::string ();