mirror of https://github.com/KLayout/klayout.git
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:
parent
89ffd7e3da
commit
8e9f15669f
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue