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 (" ");
|
static const std::string indent2 (" ");
|
||||||
|
|
||||||
template <class Keys>
|
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),
|
: 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_format (tl::to_string (tr ("%.0f MB")));
|
||||||
m_progress.set_unit (1024 * 1024);
|
m_progress.set_unit (1024 * 1024);
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ template <class Keys>
|
||||||
class std_writer_impl
|
class std_writer_impl
|
||||||
{
|
{
|
||||||
public:
|
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);
|
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 l2n_std_format::std_writer_impl<typename Keys::l2n_keys>
|
||||||
{
|
{
|
||||||
public:
|
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);
|
void write (const db::LayoutVsSchematic *l2n);
|
||||||
|
|
||||||
|
|
@ -80,8 +80,8 @@ static const std::string indent1 (" ");
|
||||||
static const std::string indent2 (" ");
|
static const std::string indent2 (" ");
|
||||||
|
|
||||||
template <class Keys>
|
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)
|
||||||
: l2n_std_format::std_writer_impl<typename Keys::l2n_keys> (stream, dbu)
|
: 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 ..
|
// .. 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 objective of this class is to supply a category ID for a given object.
|
||||||
* The category ID also identities equivalent device classes from netlist A and B.
|
* The category ID also identities equivalent objects from netlist A and B.
|
||||||
*/
|
*/
|
||||||
class DeviceCategorizer
|
template <class Obj>
|
||||||
|
class generic_categorizer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DeviceCategorizer ()
|
generic_categorizer ()
|
||||||
: m_next_cat (0)
|
: m_next_cat (0)
|
||||||
{
|
{
|
||||||
// .. nothing yet ..
|
// .. 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
|
// reuse existing category if one is assigned already -> this allows associating
|
||||||
// multiple categories to other ones (A->C, B->C)
|
// 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);
|
typename std::map<const Obj *, 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 cpb = m_cat_by_ptr.find (cb);
|
||||||
|
|
||||||
if (cpa != m_cat_by_ptr.end () && cpb != m_cat_by_ptr.end ()) {
|
if (cpa != m_cat_by_ptr.end () && cpb != m_cat_by_ptr.end ()) {
|
||||||
|
|
||||||
if (cpa->second != cpb->second) {
|
if (cpa->second != cpb->second) {
|
||||||
// join categories (cat(B)->cat(A))
|
// 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) {
|
if (cp->second == cpb->second) {
|
||||||
cp->second = cpa->second;
|
cp->second = cpa->second;
|
||||||
}
|
}
|
||||||
|
|
@ -319,24 +330,14 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t cat_for_device (const db::Device *device)
|
bool has_cat_for (const Obj *cls)
|
||||||
{
|
|
||||||
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 m_cat_by_ptr.find (cls) != m_cat_by_ptr.end ();
|
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 ()) {
|
if (cp != m_cat_by_ptr.end ()) {
|
||||||
return cp->second;
|
return cp->second;
|
||||||
}
|
}
|
||||||
|
|
@ -359,11 +360,56 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
std::map<std::string, size_t> m_cat_by_name;
|
||||||
size_t m_next_cat;
|
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
|
// CircuitCategorizer definition and implementation
|
||||||
|
|
||||||
|
|
@ -374,19 +420,26 @@ public:
|
||||||
* The category ID also identities equivalent circuit from netlist A and B.
|
* The category ID also identities equivalent circuit from netlist A and B.
|
||||||
*/
|
*/
|
||||||
class CircuitCategorizer
|
class CircuitCategorizer
|
||||||
|
: private generic_categorizer<db::Circuit>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CircuitCategorizer ()
|
CircuitCategorizer ()
|
||||||
: m_next_cat (0)
|
: generic_categorizer<db::Circuit> ()
|
||||||
{
|
{
|
||||||
// .. nothing yet ..
|
// .. nothing yet ..
|
||||||
}
|
}
|
||||||
|
|
||||||
void same_circuit (const db::Circuit *ca, const db::Circuit *cb)
|
void same_circuit (const db::Circuit *ca, const db::Circuit *cb)
|
||||||
{
|
{
|
||||||
++m_next_cat;
|
// no arbitrary cross-pairing
|
||||||
m_cat_by_ptr.insert (std::make_pair (ca, m_next_cat));
|
if (ca && has_cat_for (ca)) {
|
||||||
m_cat_by_ptr.insert (std::make_pair (cb, m_next_cat));
|
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)
|
size_t cat_for_subcircuit (const db::SubCircuit *subcircuit)
|
||||||
|
|
@ -401,32 +454,8 @@ public:
|
||||||
|
|
||||||
size_t cat_for_circuit (const db::Circuit *cr)
|
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);
|
return generic_categorizer<db::Circuit>::cat_for (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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
for (db::Net::const_subcircuit_pin_iterator i = net->begin_subcircuit_pins (); i != net->end_subcircuit_pins (); ++i) {
|
||||||
|
|
||||||
const db::SubCircuit *sc = i->subcircuit ();
|
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 ();
|
size_t pin_id = i->pin ()->id ();
|
||||||
const db::Circuit *cr = sc->circuit_ref ();
|
const db::Circuit *cr = sc->circuit_ref ();
|
||||||
const db::Net *net_at_pin = cr->net_for_pin (pin_id);
|
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);
|
std::map<const db::Circuit *, CircuitMapper>::const_iterator icm = circuit_map->find (cr);
|
||||||
if (icm == circuit_map->end ()) {
|
if (icm == circuit_map->end ()) {
|
||||||
// this can happen if the other circuit is not present - this is allowed for single-pin
|
// 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 ()) {
|
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);
|
std::map<const db::Net *, size_t>::const_iterator in = n2entry.find (0);
|
||||||
if (in == n2entry.end ()) {
|
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
|
// NOTE: if a pin mapping is given, EdgeDesc::pin1_id and EdgeDesc::pin2_id are given
|
||||||
// as pin ID's of the other circuit.
|
// 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);
|
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);
|
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);
|
size_t terminal1_id = translate_terminal_id (i->terminal_id (), d);
|
||||||
|
|
||||||
const std::vector<db::DeviceTerminalDefinition> &td = d->device_class ()->terminal_definitions ();
|
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
|
void
|
||||||
NetlistComparer::same_device_classes (const db::DeviceClass *ca, const db::DeviceClass *cb)
|
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);
|
mp_device_categorizer->same_class (ca, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
NetlistComparer::same_circuits (const db::Circuit *ca, const db::Circuit *cb)
|
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);
|
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;
|
bool good = true;
|
||||||
|
|
||||||
std::map<size_t, std::pair<const db::Circuit *, const db::Circuit *> > cat2circuits;
|
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) {
|
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-> ());
|
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) {
|
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-> ());
|
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) {
|
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) {
|
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-> ());
|
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) {
|
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-> ());
|
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) {
|
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) {
|
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) {
|
if (mp_logger) {
|
||||||
mp_logger->device_class_mismatch (i->second.first, i->second.second);
|
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;
|
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) {
|
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-> ());
|
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);
|
std::map<size_t, std::pair<const db::Circuit *, const db::Circuit *> >::const_iterator i = cat2circuits.find (ccat);
|
||||||
tl_assert (i != cat2circuits.end ());
|
tl_assert (i != cat2circuits.end ());
|
||||||
|
|
@ -2384,6 +2437,12 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
|
||||||
continue;
|
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);
|
std::vector<std::pair<size_t, size_t> > k = compute_device_key (*d, g1);
|
||||||
|
|
||||||
bool mapped = true;
|
bool mapped = true;
|
||||||
|
|
@ -2400,7 +2459,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
|
||||||
good = false;
|
good = false;
|
||||||
} else {
|
} else {
|
||||||
// TODO: report devices which cannot be distiguished topologically?
|
// 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;
|
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);
|
std::vector<std::pair<size_t, size_t> > k = compute_device_key (*d, g2);
|
||||||
|
|
||||||
bool mapped = true;
|
bool mapped = true;
|
||||||
|
|
@ -2437,8 +2502,6 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
|
||||||
|
|
||||||
db::DeviceCompare dc;
|
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 (! dc.equals (dm->second, std::make_pair (d.operator-> (), device_cat))) {
|
||||||
if (dm->second.second != device_cat) {
|
if (dm->second.second != device_cat) {
|
||||||
if (mp_logger) {
|
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) {
|
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);
|
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;
|
bool mapped = true;
|
||||||
|
|
@ -2493,7 +2562,7 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
|
||||||
good = false;
|
good = false;
|
||||||
} else if (! k.empty ()) {
|
} else if (! k.empty ()) {
|
||||||
// TODO: report devices which cannot be distiguished topologically?
|
// 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) {
|
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);
|
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;
|
bool mapped = true;
|
||||||
|
|
@ -2528,7 +2603,6 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
db::SubCircuitCompare scc;
|
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 (! scc.equals (scm->second, std::make_pair (sc.operator-> (), sc_cat))) {
|
||||||
if (mp_logger) {
|
if (mp_logger) {
|
||||||
|
|
|
||||||
|
|
@ -231,7 +231,7 @@ void NetlistSpiceReader::finish ()
|
||||||
|
|
||||||
// purge nets with single connections (this way unconnected pins can be realized)
|
// purge nets with single connections (this way unconnected pins can be realized)
|
||||||
if (mp_netlist) {
|
if (mp_netlist) {
|
||||||
mp_netlist->purge_nets ();
|
// @@@ mp_netlist->purge_nets ();
|
||||||
}
|
}
|
||||||
|
|
||||||
mp_stream.reset (0);
|
mp_stream.reset (0);
|
||||||
|
|
@ -611,7 +611,11 @@ std::string NetlistSpiceReader::read_name (tl::Extractor &ex)
|
||||||
{
|
{
|
||||||
// TODO: allow configuring Spice reader as case sensitive?
|
// TODO: allow configuring Spice reader as case sensitive?
|
||||||
// this is easy to do: just avoid to_upper here:
|
// this is easy to do: just avoid to_upper here:
|
||||||
|
#if 0 // @@@
|
||||||
return tl::to_upper_case (read_name_with_case (ex));
|
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)
|
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"
|
" device $1:$4 [Match]\n"
|
||||||
);
|
);
|
||||||
EXPECT_EQ (good, true);
|
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)
|
TEST(2_SimpleInverterWithForcedNetAssignment)
|
||||||
|
|
@ -1713,6 +1755,43 @@ TEST(11_MismatchingSubcircuits)
|
||||||
" subcircuit $1:$2 [Match]\n"
|
" subcircuit $1:$2 [Match]\n"
|
||||||
);
|
);
|
||||||
EXPECT_EQ (good, false);
|
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)
|
TEST(12_MismatchingSubcircuitsDuplicates)
|
||||||
|
|
|
||||||
|
|
@ -196,18 +196,21 @@ module LVS
|
||||||
# This method will force an equivalence between the two circuits.
|
# This method will force an equivalence between the two circuits.
|
||||||
# By default, circuits are identified by name. If names are different, this
|
# By default, circuits are identified by name. If names are different, this
|
||||||
# method allows establishing an explicit correspondence.
|
# 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
|
# Before this method can be used, a schematic netlist needs to be loaded with
|
||||||
# \schematic.
|
# \schematic.
|
||||||
|
|
||||||
def same_circuits(a, b)
|
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
|
( 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_a = 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_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)
|
@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
|
# By default, device classes are identified by name. If names are different, this
|
||||||
# method allows establishing an explicit correspondence.
|
# 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
|
# Before this method can be used, a schematic netlist needs to be loaded with
|
||||||
# \schematic.
|
# \schematic.
|
||||||
|
|
||||||
def same_device_classes(a, b)
|
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
|
( 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_a = 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_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)
|
@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 = \
|
SOURCES = \
|
||||||
lvsBasicTests.cc \
|
lvsBasicTests.cc \
|
||||||
lvsSimpleTests.cc \
|
lvsSimpleTests.cc \
|
||||||
|
lvsTests.cc
|
||||||
|
|
||||||
INCLUDEPATH += $$DRC_INC $$TL_INC $$RDB_INC $$DB_INC $$GSI_INC $$LYM_INC
|
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
|
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