mirror of https://github.com/KLayout/klayout.git
Netlist compare: added the ability to filter small caps and high resistance devices
This commit is contained in:
parent
da5680ef24
commit
aad52b77ba
|
|
@ -22,10 +22,11 @@
|
|||
*/
|
||||
|
||||
#include "dbNetlistCompare.h"
|
||||
#include "dbHash.h"
|
||||
#include "dbNetlistDeviceClasses.h"
|
||||
#include "tlProgress.h"
|
||||
#include "tlTimer.h"
|
||||
#include "tlEquivalenceClusters.h"
|
||||
#include "tlLog.h"
|
||||
|
||||
namespace db
|
||||
{
|
||||
|
|
@ -169,6 +170,40 @@ private:
|
|||
std::map<size_t, size_t> m_pin_map, m_rev_pin_map;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// DeviceFilter definition and implementation
|
||||
|
||||
class DeviceFilter
|
||||
{
|
||||
public:
|
||||
DeviceFilter (double cap_threshold, double res_threshold)
|
||||
: m_cap_threshold (cap_threshold), m_res_threshold (res_threshold)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool filter (const db::Device *device) const
|
||||
{
|
||||
const db::DeviceClassResistor *res = dynamic_cast<const db::DeviceClassResistor *> (device->device_class ());
|
||||
const db::DeviceClassCapacitor *cap = dynamic_cast<const db::DeviceClassCapacitor *> (device->device_class ());
|
||||
|
||||
if (res) {
|
||||
if (m_res_threshold > 0.0 && device->parameter_value (db::DeviceClassResistor::param_id_R) > m_res_threshold) {
|
||||
return false;
|
||||
}
|
||||
} else if (cap) {
|
||||
if (m_cap_threshold > 0.0 && device->parameter_value (db::DeviceClassCapacitor::param_id_C) < m_cap_threshold) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
double m_cap_threshold, m_res_threshold;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// DeviceCategorizer definition and implementation
|
||||
|
||||
|
|
@ -418,7 +453,7 @@ public:
|
|||
|
||||
typedef std::vector<std::pair<std::vector<EdgeDesc>, std::pair<size_t, const db::Net *> > >::const_iterator edge_iterator;
|
||||
|
||||
NetGraphNode (const db::Net *net, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const std::map<const db::Circuit *, CircuitMapper> *circuit_map, const CircuitPinMapper *pin_map)
|
||||
NetGraphNode (const db::Net *net, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const DeviceFilter &device_filter, const std::map<const db::Circuit *, CircuitMapper> *circuit_map, const CircuitPinMapper *pin_map)
|
||||
: mp_net (net), m_other_net_index (std::numeric_limits<size_t>::max ())
|
||||
{
|
||||
if (! net) {
|
||||
|
|
@ -517,6 +552,10 @@ public:
|
|||
for (db::Net::const_terminal_iterator i = net->begin_terminals (); i != net->end_terminals (); ++i) {
|
||||
|
||||
const db::Device *d = i->device ();
|
||||
if (! device_filter.filter (d)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t device_cat = device_categorizer.cat_for_device (d);
|
||||
size_t terminal1_id = translate_terminal_id (i->terminal_id (), d);
|
||||
|
||||
|
|
@ -731,7 +770,7 @@ public:
|
|||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void build (const db::Circuit *c, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const std::map<const db::Circuit *, CircuitMapper> *circuit_and_pin_mapping, const CircuitPinMapper *circuit_pin_mapper)
|
||||
void build (const db::Circuit *c, DeviceCategorizer &device_categorizer, CircuitCategorizer &circuit_categorizer, const db::DeviceFilter &device_filter, const std::map<const db::Circuit *, CircuitMapper> *circuit_and_pin_mapping, const CircuitPinMapper *circuit_pin_mapper)
|
||||
{
|
||||
tl::SelfTimer timer (tl::verbosity () >= 31, tl::to_string (tr ("Building net graph for circuit: ")) + c->name ());
|
||||
|
||||
|
|
@ -739,7 +778,7 @@ public:
|
|||
m_net_index.clear ();
|
||||
|
||||
// create a dummy node for a null net
|
||||
m_nodes.push_back (NetGraphNode (0, device_categorizer, circuit_categorizer, circuit_and_pin_mapping, circuit_pin_mapper));
|
||||
m_nodes.push_back (NetGraphNode (0, device_categorizer, circuit_categorizer, device_filter, circuit_and_pin_mapping, circuit_pin_mapper));
|
||||
|
||||
size_t nets = 0;
|
||||
for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) {
|
||||
|
|
@ -748,7 +787,7 @@ public:
|
|||
m_nodes.reserve (nets);
|
||||
|
||||
for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) {
|
||||
NetGraphNode node (n.operator-> (), device_categorizer, circuit_categorizer, circuit_and_pin_mapping, circuit_pin_mapper);
|
||||
NetGraphNode node (n.operator-> (), device_categorizer, circuit_categorizer, device_filter, circuit_and_pin_mapping, circuit_pin_mapper);
|
||||
m_nodes.push_back (node);
|
||||
}
|
||||
|
||||
|
|
@ -906,6 +945,21 @@ NetlistComparer::NetlistComparer (NetlistCompareLogger *logger)
|
|||
mp_device_categorizer.reset (new DeviceCategorizer ());
|
||||
mp_circuit_categorizer.reset (new CircuitCategorizer ());
|
||||
mp_circuit_pin_mapper.reset (new CircuitPinMapper ());
|
||||
|
||||
m_cap_threshold = -1.0; // not set
|
||||
m_res_threshold = -1.0; // not set
|
||||
}
|
||||
|
||||
void
|
||||
NetlistComparer::exclude_caps (double threshold)
|
||||
{
|
||||
m_cap_threshold = threshold;
|
||||
}
|
||||
|
||||
void
|
||||
NetlistComparer::exclude_resistors (double threshold)
|
||||
{
|
||||
m_res_threshold = threshold;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1112,12 +1166,14 @@ compute_subcircuit_key (const db::SubCircuit &subcircuit, const db::NetDeviceGra
|
|||
bool
|
||||
NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2, db::DeviceCategorizer &device_categorizer, db::CircuitCategorizer &circuit_categorizer, 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
|
||||
{
|
||||
db::DeviceFilter device_filter (m_cap_threshold, m_res_threshold);
|
||||
|
||||
db::NetDeviceGraph g1, g2;
|
||||
|
||||
// NOTE: for normalization we map all subcircuits of c1 to c2.
|
||||
// Also, pin swapping will only happen there.
|
||||
g1.build (c1, device_categorizer, circuit_categorizer, &c12_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ());
|
||||
g2.build (c2, device_categorizer, circuit_categorizer, &c22_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ());
|
||||
g1.build (c1, device_categorizer, circuit_categorizer, device_filter, &c12_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ());
|
||||
g2.build (c2, device_categorizer, circuit_categorizer, device_filter, &c22_circuit_and_pin_mapping, mp_circuit_pin_mapper.get ());
|
||||
|
||||
// Match dummy nodes for null nets
|
||||
g1.identify (0, 0);
|
||||
|
|
@ -1342,6 +1398,10 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
|
|||
|
||||
for (db::Circuit::const_device_iterator d = c1->begin_devices (); d != c1->end_devices (); ++d) {
|
||||
|
||||
if (! device_filter.filter (d.operator-> ())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_t, size_t> > k = compute_device_key (*d, g1);
|
||||
|
||||
bool mapped = true;
|
||||
|
|
@ -1365,6 +1425,10 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
|
|||
|
||||
for (db::Circuit::const_device_iterator d = c2->begin_devices (); d != c2->end_devices (); ++d) {
|
||||
|
||||
if (! device_filter.filter (d.operator-> ())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_t, size_t> > k = compute_device_key (*d, g2);
|
||||
|
||||
bool mapped = true;
|
||||
|
|
|
|||
|
|
@ -195,6 +195,16 @@ public:
|
|||
*/
|
||||
void same_circuits (const db::Circuit *ca, const db::Circuit *cb);
|
||||
|
||||
/**
|
||||
* @brief Exclude caps with less than the given capacity value
|
||||
*/
|
||||
void exclude_caps (double threshold);
|
||||
|
||||
/**
|
||||
* @brief Exclude resistors with more than the given resistance value
|
||||
*/
|
||||
void exclude_resistors (double threshold);
|
||||
|
||||
/**
|
||||
* @brief Actually compares the two netlists
|
||||
*/
|
||||
|
|
@ -209,6 +219,8 @@ protected:
|
|||
std::auto_ptr<CircuitPinMapper> mp_circuit_pin_mapper;
|
||||
std::auto_ptr<DeviceCategorizer> mp_device_categorizer;
|
||||
std::auto_ptr<CircuitCategorizer> mp_circuit_categorizer;
|
||||
double m_cap_threshold;
|
||||
double m_res_threshold;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -454,6 +454,14 @@ Class<db::NetlistComparer> decl_dbNetlistComparer ("db", "NetlistComparer",
|
|||
"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 ("min_capacitance=", &db::NetlistComparer::exclude_caps, gsi::arg ("threshold"),
|
||||
"@brief Excludes all capacitor devices with a capacitance values less than the given threshold.\n"
|
||||
"To reset this constraint, set this attribute to zero."
|
||||
) +
|
||||
gsi::method ("max_resistance=", &db::NetlistComparer::exclude_resistors, gsi::arg ("threshold"),
|
||||
"@brief Excludes all resistor devices with a resistance values higher than the given threshold.\n"
|
||||
"To reset this constraint, set this attribute to zero."
|
||||
) +
|
||||
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. "
|
||||
|
|
|
|||
|
|
@ -385,6 +385,73 @@ TEST(1_SimpleInverterMatchedDeviceClasses)
|
|||
EXPECT_EQ (good, true);
|
||||
}
|
||||
|
||||
TEST(1_SimpleInverterSkippedDevices)
|
||||
{
|
||||
const char *nls1 =
|
||||
"circuit INV ($1=IN,$2=OUT,$3=VDD,$4=VSS);\n"
|
||||
" device PMOS $1 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n"
|
||||
" device CAP $2 (A=OUT,B=IN) (C=1e-12);\n"
|
||||
" device NMOS $3 (S=VSS,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n"
|
||||
"end;\n";
|
||||
|
||||
const char *nls2 =
|
||||
"circuit INV ($1=VDD,$2=IN,$3=VSS,$4=OUT);\n"
|
||||
" device NMOS $1 (S=OUT,G=IN,D=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n"
|
||||
" device CAP $2 (A=OUT,B=IN) (C=1e-13);\n"
|
||||
" device RES $3 (A=OUT,B=IN) (R=1000);\n"
|
||||
" device PMOS $4 (S=VDD,G=IN,D=OUT) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\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 INV INV\n"
|
||||
"match_nets VDD VDD\n"
|
||||
"match_nets VSS VSS\n"
|
||||
"match_nets OUT OUT\n"
|
||||
"match_nets IN IN\n"
|
||||
"match_pins $0 $1\n"
|
||||
"match_pins $1 $3\n"
|
||||
"match_pins $2 $0\n"
|
||||
"match_pins $3 $2\n"
|
||||
"match_devices $3 $1\n"
|
||||
"match_devices_with_different_parameters $2 $2\n"
|
||||
"device_mismatch (null) $3\n"
|
||||
"match_devices $1 $4\n"
|
||||
"end_circuit INV INV NOMATCH"
|
||||
);
|
||||
EXPECT_EQ (good, false);
|
||||
|
||||
comp.exclude_caps (1e-11);
|
||||
comp.exclude_resistors (900.0);
|
||||
|
||||
logger.clear ();
|
||||
good = comp.compare (&nl1, &nl2);
|
||||
|
||||
EXPECT_EQ (logger.text (),
|
||||
"begin_circuit INV INV\n"
|
||||
"match_nets VDD VDD\n"
|
||||
"match_nets VSS VSS\n"
|
||||
"match_nets OUT OUT\n"
|
||||
"match_nets IN IN\n"
|
||||
"match_pins $0 $1\n"
|
||||
"match_pins $1 $3\n"
|
||||
"match_pins $2 $0\n"
|
||||
"match_pins $3 $2\n"
|
||||
"match_devices $3 $1\n"
|
||||
"match_devices $1 $4\n"
|
||||
"end_circuit INV INV MATCH"
|
||||
);
|
||||
EXPECT_EQ (good, true);
|
||||
}
|
||||
|
||||
TEST(2_SimpleInverterWithForcedNetAssignment)
|
||||
{
|
||||
const char *nls1 =
|
||||
|
|
|
|||
|
|
@ -865,6 +865,86 @@ match_pins $4 $4
|
|||
match_subcircuits $2 $1
|
||||
match_subcircuits $1 $2
|
||||
end_circuit TOP TOP MATCH
|
||||
END
|
||||
|
||||
assert_equal(good, true)
|
||||
|
||||
end
|
||||
|
||||
def test_10
|
||||
|
||||
nls1 = <<"END"
|
||||
circuit INV($1=IN,$2=OUT,$3=VDD,$4=VSS);
|
||||
device PMOS $1(S=VDD,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);
|
||||
device CAP $2 (A=OUT,B=IN) (C=1e-12);
|
||||
device NMOS $2(S=VSS,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);
|
||||
end;
|
||||
END
|
||||
|
||||
nls2 = <<"END"
|
||||
circuit INV($1=VDD,$2=IN,$3=VSS,$4=OUT);
|
||||
device NMOSB $1(S=OUT,G=IN,D=VSS)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);
|
||||
device CAP $2 (A=OUT,B=IN) (C=1e-13);
|
||||
device RES $3 (A=OUT,B=IN) (R=1000);
|
||||
device PMOSB $2(S=VDD,G=IN,D=OUT)(L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);
|
||||
end;
|
||||
END
|
||||
|
||||
nl1 = RBA::Netlist::new
|
||||
nl2 = RBA::Netlist::new
|
||||
prep_nl(nl1, nls1)
|
||||
prep_nl(nl2, nls2)
|
||||
|
||||
logger = NetlistCompareTestLogger::new
|
||||
comp = RBA::NetlistComparer::new(logger)
|
||||
|
||||
comp.same_device_classes(nl1.device_class_by_name("PMOS"), nl2.device_class_by_name("PMOSB"))
|
||||
|
||||
good = comp.compare(nl1, nl2)
|
||||
assert_equal(good, false)
|
||||
|
||||
logger.clear
|
||||
comp.same_device_classes(nl1.device_class_by_name("NMOS"), nl2.device_class_by_name("NMOSB"))
|
||||
good = comp.compare(nl1, nl2)
|
||||
|
||||
assert_equal(logger.text(), <<"END")
|
||||
begin_circuit INV INV
|
||||
match_nets VDD VDD
|
||||
match_nets VSS VSS
|
||||
match_nets OUT OUT
|
||||
match_nets IN IN
|
||||
match_pins $0 $1
|
||||
match_pins $1 $3
|
||||
match_pins $2 $0
|
||||
match_pins $3 $2
|
||||
match_devices $3 $1
|
||||
match_devices_with_different_parameters $2 $2
|
||||
device_mismatch (null) $3
|
||||
match_devices $1 $4
|
||||
end_circuit INV INV NOMATCH
|
||||
END
|
||||
|
||||
assert_equal(good, false)
|
||||
|
||||
comp.max_resistance = 900.0
|
||||
comp.min_capacitance = 1e-11
|
||||
|
||||
logger.clear
|
||||
good = comp.compare(nl1, nl2)
|
||||
|
||||
assert_equal(logger.text(), <<"END")
|
||||
begin_circuit INV INV
|
||||
match_nets VDD VDD
|
||||
match_nets VSS VSS
|
||||
match_nets OUT OUT
|
||||
match_nets IN IN
|
||||
match_pins $0 $1
|
||||
match_pins $1 $3
|
||||
match_pins $2 $0
|
||||
match_pins $3 $2
|
||||
match_devices $3 $1
|
||||
match_devices $1 $4
|
||||
end_circuit INV INV MATCH
|
||||
END
|
||||
|
||||
assert_equal(good, true)
|
||||
|
|
|
|||
Loading…
Reference in New Issue