WIP: utilizing netlist compare for DRC checks as well

+ Some enhancements (e.g. enable pin swapping for pins
  without names and devices or subcircuits)
This commit is contained in:
Matthias Koefferlein 2019-04-02 22:39:29 +02:00
parent 89ffd7e3da
commit 8e9f15669f
5 changed files with 184 additions and 82 deletions

View File

@ -25,6 +25,7 @@
#include "dbHash.h"
#include "tlProgress.h"
#include "tlTimer.h"
#include "tlEquivalenceClusters.h"
namespace db
{
@ -98,7 +99,7 @@ public:
void map_pins (const db::Circuit *circuit, size_t pin1_id, size_t pin2_id)
{
m_pin_map [circuit].insert (std::make_pair (pin1_id, pin2_id));
m_pin_map [circuit].same (pin1_id, pin2_id);
}
void map_pins (const db::Circuit *circuit, const std::vector<size_t> &pin_ids)
@ -107,26 +108,26 @@ public:
return;
}
std::map<size_t, size_t> &pm = m_pin_map [circuit];
tl::equivalence_clusters<size_t> &pm = m_pin_map [circuit];
for (size_t i = 1; i < pin_ids.size (); ++i) {
pm.insert (std::make_pair (pin_ids [i], pin_ids [0]));
pm.same (pin_ids [0], pin_ids [i]);
}
}
size_t normalize_pin_id (const db::Circuit *circuit, size_t pin_id) const
{
std::map<const db::Circuit *, std::map<size_t, size_t> >::const_iterator pm = m_pin_map.find (circuit);
std::map<const db::Circuit *, tl::equivalence_clusters<size_t> >::const_iterator pm = m_pin_map.find (circuit);
if (pm != m_pin_map.end ()) {
std::map<size_t, size_t>::const_iterator ipm = pm->second.find (pin_id);
if (ipm != pm->second.end ()) {
return ipm->second;
size_t cluster_id = pm->second.cluster_id (pin_id);
if (cluster_id > 0) {
return (*pm->second.begin_cluster (cluster_id))->first;
}
}
return pin_id;
}
private:
std::map<const db::Circuit *, std::map<size_t, size_t> > m_pin_map;
std::map<const db::Circuit *, tl::equivalence_clusters<size_t> > m_pin_map;
};
// --------------------------------------------------------------------------------------------------------------------
@ -450,6 +451,7 @@ public:
size_t pin_id = i->pin ()->id ();
const db::Circuit *cr = sc->circuit_ref ();
size_t this_pin_id = pin_id;
pin_id = pin_map->normalize_pin_id (cr, pin_id);
std::map<const db::Circuit *, CircuitMapper>::const_iterator icm = circuit_map->find (cr);
@ -476,7 +478,6 @@ public:
// we cannot afford creating edges from all to all other pins, so we just create edges to the previous and next
// pin. This may take more iterations to solve, but should be equivalent.
std::vector<size_t> pids;
size_t pin_count = cr->pin_count ();
// take a number if additional pins as edges: this allows identifying a pin as dependent
@ -484,6 +485,12 @@ public:
// 5 additional pins should be sufficient to capture one additional non-power pin.
size_t take_additional_pins = 5;
std::vector<size_t> pids;
pids.reserve (take_additional_pins + 1);
// this symmetrizes the pin list with respect to the before-normalization pin id:
pids.push_back (pin_id);
for (size_t n = 0; n < take_additional_pins; ++n) {
size_t add_pin_id = (pin_id + n + 1) % pin_count;
if (add_pin_id == pin_id) {
@ -500,12 +507,17 @@ public:
for (std::vector<size_t>::const_iterator i = pids.begin (); i != pids.end (); ++i) {
size_t pin2_id = *i;
size_t this_pin2_id = cm->this_pin_from_other_pin (pin2_id);
if (this_pin2_id == this_pin_id) {
// we should not go back to our original, non-normalized pin
continue;
}
// NOTE: if a pin mapping is given, EdgeDesc::pin1_id and EdgeDesc::pin2_id are given
// as pin ID's of the other circuit.
EdgeDesc ed (sc, circuit_categorizer.cat_for_subcircuit (sc), pin_id, pin_map->normalize_pin_id (cr, pin2_id));
size_t this_pin2_id = cm->this_pin_from_other_pin (pin2_id);
const db::Net *net2 = sc->net_for_pin (this_pin2_id);
std::map<const db::Net *, size_t>::const_iterator in = n2entry.find (net2);
@ -660,8 +672,7 @@ private:
/**
* @brief Compares edges as "less"
* Edge comparison is based on the pins attached (name of the first pin) or net
* name if no pins are attached on both nets.
* Edge comparison is based on the pins attached (name of the first pin).
*/
static bool edge_less (const db::Net *a, const db::Net *b)
{
@ -679,7 +690,7 @@ private:
return pna < pnb;
}
}
return a->name () < b->name ();
return false;
} else {
return false;
}
@ -705,7 +716,7 @@ private:
return pna == pnb;
}
}
return a->name () == b->name ();
return true;
} else {
return true;
}
@ -1205,9 +1216,35 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
same_as_next = (i1 != g1.end () && *i1 == *ii1) || (i2 != g2.end () && *i2 == *ii2);
// found a candidate - a single node with the same edges
if (! (same_as_next || same_as_prev) || pass) {
db::NetDeviceGraph::confirm_identity (g1, ii1, g2, ii2, mp_logger, same_as_next || same_as_prev);
bool ambiguous = (same_as_next || same_as_prev);
if (! ambiguous || pass) {
// For ambiguous nets make the pins exchangable
if (same_as_next) {
if (ii1->net () && i1->net ()) {
for (db::Net::const_pin_iterator pp = ii1->net ()->begin_pins (); pp != ii1->net ()->end_pins (); ++pp) {
for (db::Net::const_pin_iterator p = i1->net ()->begin_pins (); p != i1->net ()->end_pins (); ++p) {
mp_circuit_pin_mapper->map_pins (c1, pp->pin_id (), p->pin_id ());
}
}
}
if (ii2->net () && i2->net ()) {
for (db::Net::const_pin_iterator pp = ii2->net ()->begin_pins (); pp != ii2->net ()->end_pins (); ++pp) {
for (db::Net::const_pin_iterator p = i2->net ()->begin_pins (); p != i2->net ()->end_pins (); ++p) {
mp_circuit_pin_mapper->map_pins (c2, pp->pin_id (), p->pin_id ());
}
}
}
}
db::NetDeviceGraph::confirm_identity (g1, ii1, g2, ii2, mp_logger, ambiguous);
++new_identities;
}
}

View File

@ -283,8 +283,6 @@ private:
void DB_PUBLIC compare_netlist (tl::TestBase *_this, const db::Netlist &netlist, const std::string &au_nl_string)
{
db::NetlistComparer comp (0);
db::Netlist au_nl;
for (db::Netlist::const_device_class_iterator d = netlist.begin_device_classes (); d != netlist.end_device_classes (); ++d) {
au_nl.add_device_class (d->clone ());
@ -292,6 +290,8 @@ void DB_PUBLIC compare_netlist (tl::TestBase *_this, const db::Netlist &netlist,
au_nl.from_string (au_nl_string);
db::NetlistComparer comp (0);
if (! comp.compare (&netlist, &au_nl)) {
_this->raise ("Compare failed - see log for details.\n\nActual:\n" + netlist.to_string () + "\nGolden:\n" + au_nl_string);
// Compare once again - this time with logger
@ -301,5 +301,18 @@ void DB_PUBLIC compare_netlist (tl::TestBase *_this, const db::Netlist &netlist,
}
}
void DB_PUBLIC compare_netlist (tl::TestBase *_this, const db::Netlist &netlist, const db::Netlist &netlist_au)
{
db::NetlistComparer comp (0);
if (! comp.compare (&netlist, &netlist_au)) {
_this->raise ("Compare failed - see log for details.\n\nActual:\n" + netlist.to_string () + "\nGolden:\n" + netlist_au.to_string ());
// Compare once again - this time with logger
CompareLogger logger;
db::NetlistComparer comp (&logger);
comp.compare (&netlist, &netlist_au);
}
}
}

View File

@ -79,6 +79,11 @@ void DB_PUBLIC compare_layouts (tl::TestBase *_this, const db::Layout &layout, c
*/
void DB_PUBLIC compare_netlist (tl::TestBase *_this, const db::Netlist &netlist, const std::string &au_nl_string);
/**
* @brief Compares a netlist against another netlist
*/
void DB_PUBLIC compare_netlist (tl::TestBase *_this, const db::Netlist &netlist, const db::Netlist &netlist_au);
}
#endif

View File

@ -1273,11 +1273,11 @@ TEST(14_Subcircuit2Nand)
"end_circuit NAND NAND MATCH\n"
"begin_circuit TOP TOP\n"
"match_nets OUT OUT\n"
"match_nets IN2 IN2\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets INT INT\n"
"match_nets IN1 IN1\n"
"match_nets INT INT\n"
"match_nets IN2 IN2\n"
"match_pins $0 $0\n"
"match_pins $1 $1\n"
"match_pins $2 $2\n"
@ -1425,11 +1425,11 @@ TEST(14_Subcircuit2MatchWithSwap)
"end_circuit NAND NAND MATCH\n"
"begin_circuit TOP TOP\n"
"match_nets OUT OUT\n"
"match_nets IN2 IN2\n"
"match_nets VSS VSS\n"
"match_nets VDD VDD\n"
"match_nets INT INT\n"
"match_nets IN1 IN1\n"
"match_nets INT INT\n"
"match_nets IN2 IN2\n"
"match_pins $0 $0\n"
"match_pins $1 $1\n"
"match_pins $2 $2\n"
@ -1517,6 +1517,80 @@ TEST(15_EmptySubCircuitTest)
EXPECT_EQ (good, true);
}
TEST(15_EmptySubCircuitWithoutPinNames)
{
const char *nls1 =
"circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n"
" device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n"
" device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n"
" device NMOS $3 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n"
" device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n"
" subcircuit TRANS $1 ($1=$2,$2=$4,$3=IN);\n"
" subcircuit TRANS $2 ($1=$2,$2=$5,$3=IN);\n"
" subcircuit TRANS $3 ($1=$5,$2=OUT,$3=$2);\n"
" subcircuit TRANS $4 ($1=$4,$2=OUT,$3=$2);\n"
"end;\n"
"circuit TRANS ($1=$1,$2=$2,$3=$3);\n"
"end;\n";
const char *nls2 =
"circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n"
" device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n"
" device NMOS $1 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n"
" device PMOS $3 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n"
" device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n"
" subcircuit TRANS $1 ($1=$4,$2=$2,$3=IN);\n"
" subcircuit TRANS $2 ($1=$5,$2=$2,$3=IN);\n"
" subcircuit TRANS $3 ($1=OUT,$2=$5,$3=$2);\n"
" subcircuit TRANS $4 ($1=OUT,$2=$4,$3=$2);\n"
"end;\n"
// This circuit is an abstract and it's pins are defined by the pin names
"circuit TRANS ($1=$1,$2=$2,$3=$3);\n"
"end;\n";
db::Netlist nl1, nl2;
prep_nl (nl1, nls1);
prep_nl (nl2, nls2);
NetlistCompareTestLogger logger;
db::NetlistComparer comp (&logger);
bool good = comp.compare (&nl1, &nl2);
EXPECT_EQ (logger.text (),
"begin_circuit TRANS TRANS\n"
"match_ambiguous_nets $1 $1\n"
"match_ambiguous_nets $2 $2\n"
"match_ambiguous_nets $3 $3\n"
"match_pins $0 $0\n"
"match_pins $1 $1\n"
"match_pins $2 $2\n"
"end_circuit TRANS TRANS MATCH\n"
"begin_circuit INV2 INV2\n"
"match_nets OUT OUT\n"
"match_nets IN IN\n"
"match_nets $4 $4\n"
"match_nets $2 $2\n"
"match_nets $5 $5\n"
"match_pins IN IN\n"
"match_pins $1 $1\n"
"match_pins OUT OUT\n"
"match_pins $3 $3\n"
"match_pins $4 $4\n"
"match_devices $1 $1\n"
"match_devices $3 $2\n"
"match_devices $2 $3\n"
"match_devices $4 $4\n"
"match_subcircuits $1 $1\n"
"match_subcircuits $2 $2\n"
"match_subcircuits $3 $3\n"
"match_subcircuits $4 $4\n"
"end_circuit INV2 INV2 MATCH"
);
EXPECT_EQ (good, true);
}
TEST(16_UniqueSubCircuitMatching)
{
const char *nls1 =

View File

@ -23,6 +23,8 @@
#include "tlUnitTest.h"
#include "dbReader.h"
#include "dbTestSupport.h"
#include "dbNetlist.h"
#include "dbNetlistSpiceReader.h"
#include "lymMacro.h"
#include "tlFileUtils.h"
@ -343,6 +345,25 @@ TEST(8_TextsAndPolygons)
db::compare_layouts (_this, layout, au, db::NoNormalization);
}
static void compare_netlists (tl::TestBase *_this, const std::string &cir, const std::string &cir_au)
{
db::Netlist nl, nl_au;
db::NetlistSpiceReader reader;
{
tl::InputStream is (cir);
reader.read (is, nl);
}
{
tl::InputStream is (cir_au);
reader.read (is, nl_au);
}
db::compare_netlist (_this, nl, nl_au);
}
TEST(9_NetlistExtraction)
{
std::string rs = tl::testsrc ();
@ -380,27 +401,11 @@ TEST(9_NetlistExtraction)
// verify
{
tl::InputStream is (output);
tl::InputStream is_au (au);
CHECKPOINT ();
compare_netlists (_this, output, au);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (output),
tl::absolute_file_path (au)));
}
}
{
tl::InputStream is (output_simplified);
tl::InputStream is_au (au_simplified);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed (simplified netlist) - see\n actual: %s\n golden: %s",
tl::absolute_file_path (output_simplified),
tl::absolute_file_path (au_simplified)));
}
}
CHECKPOINT ();
compare_netlists (_this, output_simplified, au_simplified);
}
TEST(10_NetlistExtractionFlat)
@ -440,27 +445,11 @@ TEST(10_NetlistExtractionFlat)
// verify
{
tl::InputStream is (output);
tl::InputStream is_au (au);
CHECKPOINT ();
compare_netlists (_this, output, au);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (output),
tl::absolute_file_path (au)));
}
}
{
tl::InputStream is (output_simplified);
tl::InputStream is_au (au_simplified);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed (simplified netlist) - see\n actual: %s\n golden: %s",
tl::absolute_file_path (output_simplified),
tl::absolute_file_path (au_simplified)));
}
}
CHECKPOINT ();
compare_netlists (_this, output_simplified, au_simplified);
}
TEST(11_CustomDevices)
@ -500,25 +489,9 @@ TEST(11_CustomDevices)
// verify
{
tl::InputStream is (output);
tl::InputStream is_au (au);
CHECKPOINT ();
compare_netlists (_this, output, au);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (output),
tl::absolute_file_path (au)));
}
}
{
tl::InputStream is (output_simplified);
tl::InputStream is_au (au_simplified);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed (simplified netlist) - see\n actual: %s\n golden: %s",
tl::absolute_file_path (output_simplified),
tl::absolute_file_path (au_simplified)));
}
}
CHECKPOINT ();
compare_netlists (_this, output_simplified, au_simplified);
}