mirror of https://github.com/KLayout/klayout.git
WIP: added full LVS test.
This commit is contained in:
parent
b48453633f
commit
9625caea65
|
|
@ -56,9 +56,9 @@ static const std::string indent1 (" ");
|
|||
static const std::string indent2 (" ");
|
||||
|
||||
template <class Keys>
|
||||
std_writer_impl<Keys>::std_writer_impl (tl::OutputStream &stream, double dbu)
|
||||
std_writer_impl<Keys>::std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description)
|
||||
: mp_stream (&stream), m_dbu (dbu),
|
||||
m_progress (tl::to_string (tr ("Writing L2N database")), 10000)
|
||||
m_progress (progress_description.empty () ? tl::to_string (tr ("Writing L2N database")) : progress_description, 10000)
|
||||
{
|
||||
m_progress.set_format (tl::to_string (tr ("%.0f MB")));
|
||||
m_progress.set_unit (1024 * 1024);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ template <class Keys>
|
|||
class std_writer_impl
|
||||
{
|
||||
public:
|
||||
std_writer_impl (tl::OutputStream &stream, double dbu);
|
||||
std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description = std::string ());
|
||||
|
||||
void write (const db::LayoutToNetlist *l2n);
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class std_writer_impl
|
|||
: public l2n_std_format::std_writer_impl<typename Keys::l2n_keys>
|
||||
{
|
||||
public:
|
||||
std_writer_impl (tl::OutputStream &stream, double dbu);
|
||||
std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description = std::string ());
|
||||
|
||||
void write (const db::LayoutVsSchematic *l2n);
|
||||
|
||||
|
|
@ -80,8 +80,8 @@ static const std::string indent1 (" ");
|
|||
static const std::string indent2 (" ");
|
||||
|
||||
template <class Keys>
|
||||
std_writer_impl<Keys>::std_writer_impl (tl::OutputStream &stream, double dbu)
|
||||
: l2n_std_format::std_writer_impl<typename Keys::l2n_keys> (stream, dbu)
|
||||
std_writer_impl<Keys>::std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description)
|
||||
: l2n_std_format::std_writer_impl<typename Keys::l2n_keys> (stream, dbu, progress_description.empty () ? tl::to_string (tr ("Writing LVS database")) : progress_description)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
|
|||
|
|
@ -264,35 +264,46 @@ private:
|
|||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// DeviceCategorizer definition and implementation
|
||||
// generic_categorizer definition and implementation
|
||||
|
||||
/**
|
||||
* @brief A device categorizer
|
||||
* @brief A generic categorizer
|
||||
*
|
||||
* The objective of this class is to supply a category ID for a given device class.
|
||||
* The category ID also identities equivalent device classes from netlist A and B.
|
||||
* The objective of this class is to supply a category ID for a given object.
|
||||
* The category ID also identities equivalent objects from netlist A and B.
|
||||
*/
|
||||
class DeviceCategorizer
|
||||
template <class Obj>
|
||||
class generic_categorizer
|
||||
{
|
||||
public:
|
||||
DeviceCategorizer ()
|
||||
generic_categorizer ()
|
||||
: m_next_cat (0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void same_class (const db::DeviceClass *ca, const db::DeviceClass *cb)
|
||||
void same (const Obj *ca, const Obj *cb)
|
||||
{
|
||||
if (! ca && ! cb) {
|
||||
return;
|
||||
} else if (! ca) {
|
||||
same (cb, ca);
|
||||
} else if (! cb) {
|
||||
// makeing a object same as null will make this device being ignored
|
||||
m_cat_by_ptr [ca] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// reuse existing category if one is assigned already -> this allows associating
|
||||
// multiple categories to other ones (A->C, B->C)
|
||||
std::map<const db::DeviceClass *, size_t>::const_iterator cpa = m_cat_by_ptr.find (ca);
|
||||
std::map<const db::DeviceClass *, size_t>::const_iterator cpb = m_cat_by_ptr.find (cb);
|
||||
typename std::map<const Obj *, size_t>::const_iterator cpa = m_cat_by_ptr.find (ca);
|
||||
typename std::map<const Obj *, size_t>::const_iterator cpb = m_cat_by_ptr.find (cb);
|
||||
|
||||
if (cpa != m_cat_by_ptr.end () && cpb != m_cat_by_ptr.end ()) {
|
||||
|
||||
if (cpa->second != cpb->second) {
|
||||
// join categories (cat(B)->cat(A))
|
||||
for (std::map<const db::DeviceClass *, size_t>::iterator cp = m_cat_by_ptr.begin (); cp != m_cat_by_ptr.end (); ++cp) {
|
||||
for (typename std::map<const Obj *, size_t>::iterator cp = m_cat_by_ptr.begin (); cp != m_cat_by_ptr.end (); ++cp) {
|
||||
if (cp->second == cpb->second) {
|
||||
cp->second = cpa->second;
|
||||
}
|
||||
|
|
@ -319,24 +330,14 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
size_t cat_for_device (const db::Device *device)
|
||||
{
|
||||
const db::DeviceClass *cls = device->device_class ();
|
||||
if (! cls) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cat_for_device_class (cls);
|
||||
}
|
||||
|
||||
bool has_cat_for_device_class (const db::DeviceClass *cls)
|
||||
bool has_cat_for (const Obj *cls)
|
||||
{
|
||||
return m_cat_by_ptr.find (cls) != m_cat_by_ptr.end ();
|
||||
}
|
||||
|
||||
size_t cat_for_device_class (const db::DeviceClass *cls)
|
||||
size_t cat_for (const Obj *cls)
|
||||
{
|
||||
std::map<const db::DeviceClass *, size_t>::const_iterator cp = m_cat_by_ptr.find (cls);
|
||||
typename std::map<const Obj *, size_t>::const_iterator cp = m_cat_by_ptr.find (cls);
|
||||
if (cp != m_cat_by_ptr.end ()) {
|
||||
return cp->second;
|
||||
}
|
||||
|
|
@ -359,11 +360,56 @@ public:
|
|||
}
|
||||
|
||||
public:
|
||||
std::map<const db::DeviceClass *, size_t> m_cat_by_ptr;
|
||||
std::map<const Obj *, size_t> m_cat_by_ptr;
|
||||
std::map<std::string, size_t> m_cat_by_name;
|
||||
size_t m_next_cat;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// DeviceCategorizer definition and implementation
|
||||
|
||||
/**
|
||||
* @brief A device categorizer
|
||||
*
|
||||
* The objective of this class is to supply a category ID for a given device class.
|
||||
* The category ID also identities equivalent device classes from netlist A and B.
|
||||
*/
|
||||
class DeviceCategorizer
|
||||
: private generic_categorizer<db::DeviceClass>
|
||||
{
|
||||
public:
|
||||
DeviceCategorizer ()
|
||||
: generic_categorizer<db::DeviceClass> ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void same_class (const db::DeviceClass *ca, const db::DeviceClass *cb)
|
||||
{
|
||||
generic_categorizer<db::DeviceClass>::same (ca, cb);
|
||||
}
|
||||
|
||||
size_t cat_for_device (const db::Device *device)
|
||||
{
|
||||
const db::DeviceClass *cls = device->device_class ();
|
||||
if (! cls) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cat_for_device_class (cls);
|
||||
}
|
||||
|
||||
bool has_cat_for_device_class (const db::DeviceClass *cls)
|
||||
{
|
||||
return generic_categorizer<db::DeviceClass>::has_cat_for (cls);
|
||||
}
|
||||
|
||||
size_t cat_for_device_class (const db::DeviceClass *cls)
|
||||
{
|
||||
return generic_categorizer<db::DeviceClass>::cat_for (cls);
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// CircuitCategorizer definition and implementation
|
||||
|
||||
|
|
@ -374,19 +420,26 @@ public:
|
|||
* The category ID also identities equivalent circuit from netlist A and B.
|
||||
*/
|
||||
class CircuitCategorizer
|
||||
: private generic_categorizer<db::Circuit>
|
||||
{
|
||||
public:
|
||||
CircuitCategorizer ()
|
||||
: m_next_cat (0)
|
||||
: generic_categorizer<db::Circuit> ()
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
void same_circuit (const db::Circuit *ca, const db::Circuit *cb)
|
||||
{
|
||||
++m_next_cat;
|
||||
m_cat_by_ptr.insert (std::make_pair (ca, m_next_cat));
|
||||
m_cat_by_ptr.insert (std::make_pair (cb, m_next_cat));
|
||||
// no arbitrary cross-pairing
|
||||
if (ca && has_cat_for (ca)) {
|
||||
throw tl::Exception (tl::to_string (tr ("Circuit is already paired with other circuit: ")) + ca->name ());
|
||||
}
|
||||
if (cb && has_cat_for (cb)) {
|
||||
throw tl::Exception (tl::to_string (tr ("Circuit is already paired with other circuit: ")) + cb->name ());
|
||||
}
|
||||
|
||||
generic_categorizer<db::Circuit>::same (ca, cb);
|
||||
}
|
||||
|
||||
size_t cat_for_subcircuit (const db::SubCircuit *subcircuit)
|
||||
|
|
@ -401,32 +454,8 @@ public:
|
|||
|
||||
size_t cat_for_circuit (const db::Circuit *cr)
|
||||
{
|
||||
std::map<const db::Circuit *, size_t>::const_iterator cp = m_cat_by_ptr.find (cr);
|
||||
if (cp != m_cat_by_ptr.end ()) {
|
||||
return cp->second;
|
||||
}
|
||||
|
||||
std::string cr_name = cr->name ();
|
||||
#if defined(COMPARE_CASE_INSENSITIVE)
|
||||
cr_name = tl::to_upper_case (cr_name);
|
||||
#endif
|
||||
|
||||
std::map<std::string, size_t>::const_iterator c = m_cat_by_name.find (cr_name);
|
||||
if (c != m_cat_by_name.end ()) {
|
||||
m_cat_by_ptr.insert (std::make_pair (cr, c->second));
|
||||
return c->second;
|
||||
} else {
|
||||
++m_next_cat;
|
||||
m_cat_by_name.insert (std::make_pair (cr_name, m_next_cat));
|
||||
m_cat_by_ptr.insert (std::make_pair (cr, m_next_cat));
|
||||
return m_next_cat;
|
||||
}
|
||||
return generic_categorizer<db::Circuit>::cat_for (cr);
|
||||
}
|
||||
|
||||
public:
|
||||
std::map<const db::Circuit *, size_t> m_cat_by_ptr;
|
||||
std::map<std::string, size_t> m_cat_by_name;
|
||||
size_t m_next_cat;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
|
@ -626,12 +655,16 @@ public:
|
|||
for (db::Net::const_subcircuit_pin_iterator i = net->begin_subcircuit_pins (); i != net->end_subcircuit_pins (); ++i) {
|
||||
|
||||
const db::SubCircuit *sc = i->subcircuit ();
|
||||
size_t circuit_cat = circuit_categorizer.cat_for_subcircuit (sc);
|
||||
if (! circuit_cat) {
|
||||
// circuit is ignored
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t pin_id = i->pin ()->id ();
|
||||
const db::Circuit *cr = sc->circuit_ref ();
|
||||
const db::Net *net_at_pin = cr->net_for_pin (pin_id);
|
||||
|
||||
size_t this_pin_id = pin_id;
|
||||
|
||||
std::map<const db::Circuit *, CircuitMapper>::const_iterator icm = circuit_map->find (cr);
|
||||
if (icm == circuit_map->end ()) {
|
||||
// this can happen if the other circuit is not present - this is allowed for single-pin
|
||||
|
|
@ -662,7 +695,7 @@ public:
|
|||
|
||||
if (! net_at_pin || net_at_pin->is_floating ()) {
|
||||
|
||||
Transition ed (sc, circuit_categorizer.cat_for_subcircuit (sc), pin_id, pin_id);
|
||||
Transition ed (sc, circuit_cat, pin_id, pin_id);
|
||||
|
||||
std::map<const db::Net *, size_t>::const_iterator in = n2entry.find (0);
|
||||
if (in == n2entry.end ()) {
|
||||
|
|
@ -714,7 +747,7 @@ public:
|
|||
|
||||
// NOTE: if a pin mapping is given, EdgeDesc::pin1_id and EdgeDesc::pin2_id are given
|
||||
// as pin ID's of the other circuit.
|
||||
Transition ed (sc, circuit_categorizer.cat_for_subcircuit (sc), pin_id, pin_map->normalize_pin_id (cr, pin2_id));
|
||||
Transition ed (sc, circuit_cat, pin_id, pin_map->normalize_pin_id (cr, pin2_id));
|
||||
|
||||
const db::Net *net2 = sc->net_for_pin (this_pin2_id);
|
||||
|
||||
|
|
@ -738,6 +771,11 @@ public:
|
|||
}
|
||||
|
||||
size_t device_cat = device_categorizer.cat_for_device (d);
|
||||
if (! device_cat) {
|
||||
// device is ignored
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t terminal1_id = translate_terminal_id (i->terminal_id (), d);
|
||||
|
||||
const std::vector<db::DeviceTerminalDefinition> &td = d->device_class ()->terminal_definitions ();
|
||||
|
|
@ -1757,16 +1795,12 @@ NetlistComparer::equivalent_pins (const db::Circuit *cb, const std::vector<size_
|
|||
void
|
||||
NetlistComparer::same_device_classes (const db::DeviceClass *ca, const db::DeviceClass *cb)
|
||||
{
|
||||
tl_assert (ca != 0);
|
||||
tl_assert (cb != 0);
|
||||
mp_device_categorizer->same_class (ca, cb);
|
||||
}
|
||||
|
||||
void
|
||||
NetlistComparer::same_circuits (const db::Circuit *ca, const db::Circuit *cb)
|
||||
{
|
||||
tl_assert (ca != 0);
|
||||
tl_assert (cb != 0);
|
||||
mp_circuit_categorizer->same_circuit (ca, cb);
|
||||
}
|
||||
|
||||
|
|
@ -1799,15 +1833,26 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const
|
|||
bool good = true;
|
||||
|
||||
std::map<size_t, std::pair<const db::Circuit *, const db::Circuit *> > cat2circuits;
|
||||
std::set<const db::Circuit *> verified_circuits_a, verified_circuits_b;
|
||||
|
||||
for (db::Netlist::const_circuit_iterator i = a->begin_circuits (); i != a->end_circuits (); ++i) {
|
||||
size_t cat = circuit_categorizer.cat_for_circuit (i.operator-> ());
|
||||
cat2circuits[cat].first = i.operator-> ();
|
||||
if (cat) {
|
||||
cat2circuits[cat].first = i.operator-> ();
|
||||
} else {
|
||||
// skip circuit (but count it as verified)
|
||||
verified_circuits_a.insert (i.operator-> ());
|
||||
}
|
||||
}
|
||||
|
||||
for (db::Netlist::const_circuit_iterator i = b->begin_circuits (); i != b->end_circuits (); ++i) {
|
||||
size_t cat = circuit_categorizer.cat_for_circuit (i.operator-> ());
|
||||
cat2circuits[cat].second = i.operator-> ();
|
||||
if (cat) {
|
||||
cat2circuits[cat].second = i.operator-> ();
|
||||
} else {
|
||||
// skip circuit (but count it as verified)
|
||||
verified_circuits_b.insert (i.operator-> ());
|
||||
}
|
||||
}
|
||||
|
||||
if (mp_logger) {
|
||||
|
|
@ -1820,17 +1865,23 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const
|
|||
|
||||
for (db::Netlist::const_device_class_iterator dc = a->begin_device_classes (); dc != a->end_device_classes (); ++dc) {
|
||||
size_t cat = device_categorizer.cat_for_device_class (dc.operator-> ());
|
||||
cat2dc.insert (std::make_pair (cat, std::make_pair ((const db::DeviceClass *) 0, (const db::DeviceClass *) 0))).first->second.first = dc.operator-> ();
|
||||
if (cat) {
|
||||
cat2dc.insert (std::make_pair (cat, std::make_pair ((const db::DeviceClass *) 0, (const db::DeviceClass *) 0))).first->second.first = dc.operator-> ();
|
||||
}
|
||||
}
|
||||
|
||||
for (db::Netlist::const_device_class_iterator dc = b->begin_device_classes (); dc != b->end_device_classes (); ++dc) {
|
||||
size_t cat = device_categorizer.cat_for_device_class (dc.operator-> ());
|
||||
cat2dc.insert (std::make_pair (cat, std::make_pair ((const db::DeviceClass *) 0, (const db::DeviceClass *) 0))).first->second.second = dc.operator-> ();
|
||||
if (cat) {
|
||||
cat2dc.insert (std::make_pair (cat, std::make_pair ((const db::DeviceClass *) 0, (const db::DeviceClass *) 0))).first->second.second = dc.operator-> ();
|
||||
}
|
||||
}
|
||||
|
||||
for (std::map<size_t, std::pair<const db::DeviceClass *, const db::DeviceClass *> >::const_iterator i = cat2dc.begin (); i != cat2dc.end (); ++i) {
|
||||
if (! i->second.first || ! i->second.second) {
|
||||
good = false;
|
||||
// NOTE: device class mismatch does not set good to false.
|
||||
// Reasoning: a device class may not be present because there is no device of a certain kind (e.g. in SPICE).
|
||||
// This isn't necessarily a failure.
|
||||
if (mp_logger) {
|
||||
mp_logger->device_class_mismatch (i->second.first, i->second.second);
|
||||
}
|
||||
|
|
@ -1848,12 +1899,14 @@ NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const
|
|||
}
|
||||
}
|
||||
|
||||
std::set<const db::Circuit *> verified_circuits_a, verified_circuits_b;
|
||||
std::map<const db::Circuit *, CircuitMapper> c12_pin_mapping, c22_pin_mapping;
|
||||
|
||||
for (db::Netlist::const_bottom_up_circuit_iterator c = a->begin_bottom_up (); c != a->end_bottom_up (); ++c) {
|
||||
|
||||
size_t ccat = circuit_categorizer.cat_for_circuit (c.operator-> ());
|
||||
if (! ccat) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::map<size_t, std::pair<const db::Circuit *, const db::Circuit *> >::const_iterator i = cat2circuits.find (ccat);
|
||||
tl_assert (i != cat2circuits.end ());
|
||||
|
|
@ -2384,6 +2437,12 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
|
|||
continue;
|
||||
}
|
||||
|
||||
size_t device_cat = device_categorizer.cat_for_device (d.operator-> ());
|
||||
if (! device_cat) {
|
||||
// device is ignored
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_t, size_t> > k = compute_device_key (*d, g1);
|
||||
|
||||
bool mapped = true;
|
||||
|
|
@ -2400,7 +2459,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
|
|||
good = false;
|
||||
} else {
|
||||
// TODO: report devices which cannot be distiguished topologically?
|
||||
device_map.insert (std::make_pair (k, std::make_pair (d.operator-> (), device_categorizer.cat_for_device (d.operator-> ()))));
|
||||
device_map.insert (std::make_pair (k, std::make_pair (d.operator-> (), device_cat)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2411,6 +2470,12 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
|
|||
continue;
|
||||
}
|
||||
|
||||
size_t device_cat = device_categorizer.cat_for_device (d.operator-> ());
|
||||
if (! device_cat) {
|
||||
// device is ignored
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_t, size_t> > k = compute_device_key (*d, g2);
|
||||
|
||||
bool mapped = true;
|
||||
|
|
@ -2437,8 +2502,6 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
|
|||
|
||||
db::DeviceCompare dc;
|
||||
|
||||
size_t device_cat = device_categorizer.cat_for_device (d.operator-> ());
|
||||
|
||||
if (! dc.equals (dm->second, std::make_pair (d.operator-> (), device_cat))) {
|
||||
if (dm->second.second != device_cat) {
|
||||
if (mp_logger) {
|
||||
|
|
@ -2477,6 +2540,12 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
|
|||
|
||||
for (db::Circuit::const_subcircuit_iterator sc = c1->begin_subcircuits (); sc != c1->end_subcircuits (); ++sc) {
|
||||
|
||||
size_t sc_cat = circuit_categorizer.cat_for_subcircuit (sc.operator-> ());
|
||||
if (! sc_cat) {
|
||||
// subcircuit is ignored
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_t, size_t> > k = compute_subcircuit_key (*sc, g1, &c12_circuit_and_pin_mapping, &circuit_pin_mapper);
|
||||
|
||||
bool mapped = true;
|
||||
|
|
@ -2493,7 +2562,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
|
|||
good = false;
|
||||
} else if (! k.empty ()) {
|
||||
// TODO: report devices which cannot be distiguished topologically?
|
||||
subcircuit_map.insert (std::make_pair (k, std::make_pair (sc.operator-> (), circuit_categorizer.cat_for_subcircuit (sc.operator-> ()))));
|
||||
subcircuit_map.insert (std::make_pair (k, std::make_pair (sc.operator-> (), sc_cat)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2503,6 +2572,12 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
|
|||
|
||||
for (db::Circuit::const_subcircuit_iterator sc = c2->begin_subcircuits (); sc != c2->end_subcircuits (); ++sc) {
|
||||
|
||||
size_t sc_cat = circuit_categorizer.cat_for_subcircuit (sc.operator-> ());
|
||||
if (! sc_cat) {
|
||||
// subcircuit is ignored
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_t, size_t> > k = compute_subcircuit_key (*sc, g2, &c22_circuit_and_pin_mapping, &circuit_pin_mapper);
|
||||
|
||||
bool mapped = true;
|
||||
|
|
@ -2528,7 +2603,6 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
|
|||
} else {
|
||||
|
||||
db::SubCircuitCompare scc;
|
||||
size_t sc_cat = circuit_categorizer.cat_for_subcircuit (sc.operator-> ());
|
||||
|
||||
if (! scc.equals (scm->second, std::make_pair (sc.operator-> (), sc_cat))) {
|
||||
if (mp_logger) {
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ void NetlistSpiceReader::finish ()
|
|||
|
||||
// purge nets with single connections (this way unconnected pins can be realized)
|
||||
if (mp_netlist) {
|
||||
mp_netlist->purge_nets ();
|
||||
// @@@ mp_netlist->purge_nets ();
|
||||
}
|
||||
|
||||
mp_stream.reset (0);
|
||||
|
|
@ -611,7 +611,11 @@ std::string NetlistSpiceReader::read_name (tl::Extractor &ex)
|
|||
{
|
||||
// TODO: allow configuring Spice reader as case sensitive?
|
||||
// this is easy to do: just avoid to_upper here:
|
||||
#if 0 // @@@
|
||||
return tl::to_upper_case (read_name_with_case (ex));
|
||||
#else
|
||||
return read_name_with_case (ex);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool NetlistSpiceReader::read_element (tl::Extractor &ex, const std::string &element, const std::string &name)
|
||||
|
|
|
|||
|
|
@ -687,6 +687,48 @@ TEST(1_SimpleInverterSkippedDevices)
|
|||
" device $1:$4 [Match]\n"
|
||||
);
|
||||
EXPECT_EQ (good, true);
|
||||
|
||||
xref.clear ();
|
||||
|
||||
comp_xref.exclude_caps (-1);
|
||||
comp_xref.exclude_resistors (-1);
|
||||
comp_xref.same_device_classes (0, nl2.device_class_by_name ("RES"));
|
||||
comp_xref.same_device_classes (0, nl2.device_class_by_name ("CAP"));
|
||||
comp_xref.same_device_classes (nl1.device_class_by_name ("RES"), 0);
|
||||
comp_xref.same_device_classes (nl1.device_class_by_name ("CAP"), 0);
|
||||
|
||||
good = comp_xref.compare (&nl1, &nl2);
|
||||
|
||||
EXPECT_EQ (xref2s (xref),
|
||||
"INV:INV [Match]:\n"
|
||||
" pin $0:$1 [Match]\n"
|
||||
" pin $1:$3 [Match]\n"
|
||||
" pin $2:$0 [Match]\n"
|
||||
" pin $3:$2 [Match]\n"
|
||||
" net IN:IN [Match]\n"
|
||||
" terminal (null):$2[B]\n"
|
||||
" terminal (null):$3[B]\n"
|
||||
" terminal $1[G]:$4[G]\n"
|
||||
" terminal $2[B]:(null)\n"
|
||||
" terminal $3[G]:$1[G]\n"
|
||||
" pin $0:$1\n"
|
||||
" net OUT:OUT [Match]\n"
|
||||
" terminal (null):$2[A]\n"
|
||||
" terminal (null):$3[A]\n"
|
||||
" terminal $1[D]:$4[D]\n"
|
||||
" terminal $2[A]:(null)\n"
|
||||
" terminal $3[D]:$1[S]\n"
|
||||
" pin $1:$3\n"
|
||||
" net VDD:VDD [Match]\n"
|
||||
" terminal $1[S]:$4[S]\n"
|
||||
" pin $2:$0\n"
|
||||
" net VSS:VSS [Match]\n"
|
||||
" terminal $3[S]:$1[D]\n"
|
||||
" pin $3:$2\n"
|
||||
" device $3:$1 [Match]\n"
|
||||
" device $1:$4 [Match]\n"
|
||||
);
|
||||
EXPECT_EQ (good, true);
|
||||
}
|
||||
|
||||
TEST(2_SimpleInverterWithForcedNetAssignment)
|
||||
|
|
@ -1713,6 +1755,43 @@ TEST(11_MismatchingSubcircuits)
|
|||
" subcircuit $1:$2 [Match]\n"
|
||||
);
|
||||
EXPECT_EQ (good, false);
|
||||
|
||||
xref.clear ();
|
||||
|
||||
// ignore the subcircuits to make them match
|
||||
comp_xref.same_circuits (nl1.circuit_by_name ("INV"), 0);
|
||||
comp_xref.same_circuits (0, nl2.circuit_by_name ("INV"));
|
||||
good = comp_xref.compare (&nl1, &nl2);
|
||||
|
||||
// nets are now ambiguous
|
||||
EXPECT_EQ (xref2s (xref),
|
||||
"TOP:TOP [Match]:\n"
|
||||
" pin $0:$0 [Match]\n"
|
||||
" pin $1:$1 [Match]\n"
|
||||
" pin $2:$2 [Match]\n"
|
||||
" pin $3:$3 [Match]\n"
|
||||
" net IN:OUT [MatchWithWarning]\n"
|
||||
" pin $0:$0\n"
|
||||
" subcircuit_pin (null):$1[$3]\n"
|
||||
" subcircuit_pin $1[$0]:(null)\n"
|
||||
" net OUT:VDD [MatchWithWarning]\n"
|
||||
" pin $1:$1\n"
|
||||
" subcircuit_pin (null):$1[$0]\n"
|
||||
" subcircuit_pin (null):$2[$0]\n"
|
||||
" subcircuit_pin $2[$1]:(null)\n"
|
||||
" net VDD:IN [MatchWithWarning]\n"
|
||||
" pin $2:$2\n"
|
||||
" subcircuit_pin (null):$2[$1]\n"
|
||||
" subcircuit_pin $1[$2]:(null)\n"
|
||||
" subcircuit_pin $2[$2]:(null)\n"
|
||||
" net VSS:VSS [MatchWithWarning]\n"
|
||||
" pin $3:$3\n"
|
||||
" subcircuit_pin (null):$1[$2]\n"
|
||||
" subcircuit_pin (null):$2[$2]\n"
|
||||
" subcircuit_pin $1[$3]:(null)\n"
|
||||
" subcircuit_pin $2[$3]:(null)\n"
|
||||
);
|
||||
EXPECT_EQ (good, true);
|
||||
}
|
||||
|
||||
TEST(12_MismatchingSubcircuitsDuplicates)
|
||||
|
|
|
|||
|
|
@ -196,18 +196,21 @@ module LVS
|
|||
# This method will force an equivalence between the two circuits.
|
||||
# By default, circuits are identified by name. If names are different, this
|
||||
# method allows establishing an explicit correspondence.
|
||||
#
|
||||
# One of the circuits may be nil. In this case, the corresponding
|
||||
# other circuit is mapped to "nothing", i.e. ignored.
|
||||
#
|
||||
# Before this method can be used, a schematic netlist needs to be loaded with
|
||||
# \schematic.
|
||||
|
||||
def same_circuits(a, b)
|
||||
|
||||
a.is_a?(String) || b.is_a?(String) || raise("Both arguments of 'same_circuits' need to be strings")
|
||||
a.is_a?(String) || a == nil || b.is_a?(String) || b == nil || raise("Both arguments of 'same_circuits' need to be strings or nil")
|
||||
|
||||
( nl_a, nl_b ) = _ensure_two_netlists
|
||||
|
||||
circuit_a = nl_a.circuit_by_name(a) || raise("Not a valid circuit name in extracted netlist: #{a}")
|
||||
circuit_b = nl_b.circuit_by_name(b) || raise("Not a valid circuit name in reference netlist: #{b}")
|
||||
circuit_a = a && (nl_a.circuit_by_name(a) || raise("Not a valid circuit name in extracted netlist: #{a}"))
|
||||
circuit_b = b && (nl_b.circuit_by_name(b) || raise("Not a valid circuit name in reference netlist: #{b}"))
|
||||
|
||||
@comparer.same_circuits(circuit_a, circuit_b)
|
||||
|
||||
|
|
@ -222,17 +225,20 @@ module LVS
|
|||
# By default, device classes are identified by name. If names are different, this
|
||||
# method allows establishing an explicit correspondence.
|
||||
#
|
||||
# One of the device classes may be nil. In this case, the corresponding
|
||||
# other device class is mapped to "nothing", i.e. ignored.
|
||||
#
|
||||
# Before this method can be used, a schematic netlist needs to be loaded with
|
||||
# \schematic.
|
||||
|
||||
def same_device_classes(a, b)
|
||||
|
||||
a.is_a?(String) || b.is_a?(String) || raise("Both arguments of 'same_device_classes' need to be strings")
|
||||
a.is_a?(String) || a == nil || b.is_a?(String) || b == nil || raise("Both arguments of 'same_device_classes' need to be strings or nil")
|
||||
|
||||
( nl_a, nl_b ) = _ensure_two_netlists
|
||||
|
||||
dc_a = nl_a.device_class_by_name(a) || raise("Not a valid device class in extracted netlist: #{a}")
|
||||
dc_b = nl_b.device_class_by_name(b) || raise("Not a valid device class in reference netlist: #{b}")
|
||||
dc_a = a && (nl_a.device_class_by_name(a) || raise("Not a valid device class in extracted netlist: #{a}"))
|
||||
dc_b = b && (nl_b.device_class_by_name(b) || raise("Not a valid device class in reference netlist: #{b}"))
|
||||
|
||||
@comparer.same_device_classes(dc_a, dc_b)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
|
||||
/*
|
||||
|
||||
KLayout Layout Viewer
|
||||
Copyright (C) 2006-2019 Matthias Koefferlein
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
#include "tlUnitTest.h"
|
||||
#include "dbReader.h"
|
||||
#include "dbTestSupport.h"
|
||||
#include "dbNetlist.h"
|
||||
#include "dbNetlistSpiceReader.h"
|
||||
#include "lymMacro.h"
|
||||
#include "tlFileUtils.h"
|
||||
|
||||
void run_test (tl::TestBase *_this, const std::string &suffix, const std::string &layout, bool with_l2n = false, bool with_lvsdb = false)
|
||||
{
|
||||
std::string rs = tl::testsrc ();
|
||||
rs += "/testdata/lvs/" + suffix + ".lvs";
|
||||
|
||||
std::string src = tl::testsrc ();
|
||||
src += "/testdata/lvs/" + layout;
|
||||
|
||||
std::string au_lvsdb = tl::testsrc ();
|
||||
au_lvsdb += "/testdata/lvs/" + suffix + ".lvsdb.gz";
|
||||
|
||||
std::string au_cir = tl::testsrc ();
|
||||
au_cir += "/testdata/lvs/" + suffix + ".cir.gz";
|
||||
|
||||
std::string au_l2n = tl::testsrc ();
|
||||
au_l2n += "/testdata/lvs/" + suffix + ".l2n.gz";
|
||||
|
||||
std::string output_lvsdb = _this->tmp_file ("tmp.lvsdb");
|
||||
std::string output_cir = _this->tmp_file ("tmp.cir");
|
||||
std::string output_l2n = _this->tmp_file ("tmp.l2n");
|
||||
|
||||
{
|
||||
// Set some variables
|
||||
lym::Macro config;
|
||||
config.set_text (tl::sprintf (
|
||||
"$lvs_test_source = '%s'\n"
|
||||
"$lvs_test_target_lvsdb = '%s'\n"
|
||||
"$lvs_test_target_cir = '%s'\n"
|
||||
"$lvs_test_target_l2n = '%s'\n"
|
||||
, src, output_lvsdb, output_cir, output_l2n)
|
||||
);
|
||||
config.set_interpreter (lym::Macro::Ruby);
|
||||
EXPECT_EQ (config.run (), 0);
|
||||
}
|
||||
|
||||
lym::Macro lvs;
|
||||
lvs.load_from (rs);
|
||||
EXPECT_EQ (lvs.run (), 0);
|
||||
|
||||
_this->compare_text_files (output_cir, au_cir);
|
||||
if (with_lvsdb) {
|
||||
_this->compare_text_files (output_lvsdb, au_lvsdb);
|
||||
}
|
||||
if (with_l2n) {
|
||||
_this->compare_text_files (output_l2n, au_l2n);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(1_full)
|
||||
{
|
||||
run_test (_this, "vexriscv", "vexriscv.oas.gz");
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ include($$PWD/../../lib_ut.pri)
|
|||
SOURCES = \
|
||||
lvsBasicTests.cc \
|
||||
lvsSimpleTests.cc \
|
||||
lvsTests.cc
|
||||
|
||||
INCLUDEPATH += $$DRC_INC $$TL_INC $$RDB_INC $$DB_INC $$GSI_INC $$LYM_INC
|
||||
DEPENDPATH += $$DRC_INC $$TL_INC $$RDB_INC $$DB_INC $$GSI_INC $$LYM_INC
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -0,0 +1,111 @@
|
|||
|
||||
source($lvs_test_source)
|
||||
|
||||
# will get pretty big:
|
||||
# report_lvs($lvs_test_target_lvsdb)
|
||||
|
||||
target_netlist($lvs_test_target_cir, write_spice(true), "Extracted by KLayout")
|
||||
|
||||
schematic("vexriscv_schematic.cir.gz")
|
||||
|
||||
deep
|
||||
|
||||
# Drawing layers
|
||||
|
||||
nwell = input(1, 0)
|
||||
pactive = input(4, 0)
|
||||
nactive = input(3, 0)
|
||||
ntie = input(5, 0)
|
||||
ptie = input(6, 0)
|
||||
|
||||
poly = input(7, 0)
|
||||
cont = input(10, 0)
|
||||
metal1 = input(11, 0)
|
||||
via1 = input(14, 0)
|
||||
metal2 = input(16, 0)
|
||||
via2 = input(18, 0)
|
||||
metal3 = input(19, 0)
|
||||
via3 = input(21, 0)
|
||||
metal4 = input(22, 0)
|
||||
via4 = input(25, 0)
|
||||
metal5 = input(26, 0)
|
||||
|
||||
# Bulk layer for terminal provisioning
|
||||
|
||||
bulk = polygon_layer
|
||||
|
||||
# Computed layers
|
||||
|
||||
poly_cont = cont & poly
|
||||
diff_cont = cont - poly
|
||||
|
||||
pgate = pactive & poly
|
||||
psd = pactive - pgate
|
||||
|
||||
ngate = nactive & poly
|
||||
nsd = nactive - ngate
|
||||
|
||||
# Device extraction
|
||||
|
||||
# PMOS transistor device extraction
|
||||
extract_devices(mos4("PMOS"), { "SD" => psd, "G" => pgate, "W" => nwell,
|
||||
"tS" => psd, "tD" => psd, "tG" => poly, "tW" => nwell })
|
||||
|
||||
# NMOS transistor device extraction
|
||||
extract_devices(mos4("NMOS"), { "SD" => nsd, "G" => ngate, "W" => bulk,
|
||||
"tS" => nsd, "tD" => nsd, "tG" => poly, "tW" => bulk })
|
||||
|
||||
# Define connectivity for netlist extraction
|
||||
|
||||
# Inter-layer
|
||||
connect(psd, diff_cont)
|
||||
connect(nsd, diff_cont)
|
||||
connect(poly, poly_cont)
|
||||
connect(poly_cont, metal1)
|
||||
connect(diff_cont, metal1)
|
||||
connect(diff_cont, ntie)
|
||||
connect(diff_cont, ptie)
|
||||
connect(nwell, ntie)
|
||||
connect(metal1, via1)
|
||||
connect(via1, metal2)
|
||||
connect(metal2, via2)
|
||||
connect(via2, metal3)
|
||||
connect(metal3, via3)
|
||||
connect(via3, metal4)
|
||||
connect(metal4, via4)
|
||||
connect(via4, metal5)
|
||||
|
||||
# Global
|
||||
connect_global(ptie, "BULK")
|
||||
connect_global(bulk, "BULK")
|
||||
|
||||
# Implicit
|
||||
connect_implicit("VDD")
|
||||
connect_implicit("VSS")
|
||||
|
||||
# Compare section
|
||||
|
||||
netlist.flatten_circuit("*_{x0,x1,x2,x4}")
|
||||
|
||||
same_device_classes("PMOS", "tp")
|
||||
same_device_classes("NMOS", "tn")
|
||||
|
||||
# Ignore all caps from the schematic
|
||||
same_device_classes(nil, "CAP")
|
||||
|
||||
# Increase the default complexity from 100 to 200
|
||||
# This is required because the clock tree is incorrect and exhibits manifold ambiguities
|
||||
# (the netlists are just samples, not necessarily functional).
|
||||
# The algorithm needs enough freedom to follow all these branches and different variants.
|
||||
max_branch_complexity(200)
|
||||
|
||||
schematic.combine_devices
|
||||
|
||||
netlist.combine_devices
|
||||
|
||||
if ! compare
|
||||
raise "Netlists don't match"
|
||||
else
|
||||
puts "Congratulations! Netlists match."
|
||||
end
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue