Made SPICE netlist elements case insensitive in LVS scripts

This commit is contained in:
Matthias Koefferlein 2021-03-24 22:11:15 +01:00
parent 3777d311af
commit c48be51cb6
14 changed files with 178 additions and 71 deletions

View File

@ -186,8 +186,10 @@ void Circuit::rename_pin (size_t id, const std::string &name)
const Pin *Circuit::pin_by_name (const std::string &name) const
{
std::string nn = mp_netlist ? mp_netlist->normalize_name (name) : name;
for (Circuit::const_pin_iterator p = begin_pins (); p != end_pins (); ++p) {
if (p->name () == name) {
if (p->name () == nn) {
return p.operator-> ();
}
}
@ -331,6 +333,11 @@ void Circuit::remove_pin (size_t id)
}
}
Net *Circuit::net_by_name (const std::string &name)
{
return m_net_by_name.object_by (mp_netlist ? mp_netlist->normalize_name (name) : name);
}
void Circuit::add_net (Net *net)
{
if (! net) {
@ -423,6 +430,11 @@ void Circuit::remove_device (Device *device)
m_devices.erase (device);
}
Device *Circuit::device_by_name (const std::string &name)
{
return m_device_by_name.object_by (mp_netlist ? mp_netlist->normalize_name (name) : name);
}
void Circuit::add_subcircuit (SubCircuit *subcircuit)
{
if (! subcircuit) {
@ -456,6 +468,11 @@ void Circuit::remove_subcircuit (SubCircuit *subcircuit)
m_subcircuits.erase (subcircuit);
}
SubCircuit *Circuit::subcircuit_by_name (const std::string &name)
{
return m_subcircuit_by_name.object_by (mp_netlist ? mp_netlist->normalize_name (name) : name);
}
void Circuit::register_ref (SubCircuit *r)
{
m_refs.push_back (r);

View File

@ -496,10 +496,7 @@ public:
*
* If the name is not valid, null is returned.
*/
Net *net_by_name (const std::string &name)
{
return m_net_by_name.object_by (name);
}
Net *net_by_name (const std::string &name);
/**
* @brief Adds a device to this circuit
@ -556,10 +553,7 @@ public:
*
* If the name is not valid, null is returned.
*/
Device *device_by_name (const std::string &name)
{
return m_device_by_name.object_by (name);
}
Device *device_by_name (const std::string &name);
/**
* @brief Begin iterator for the devices of the circuit (non-const version)
@ -648,10 +642,7 @@ public:
*
* If the name is not valid, null is returned.
*/
SubCircuit *subcircuit_by_name (const std::string &name)
{
return m_subcircuit_by_name.object_by (name);
}
SubCircuit *subcircuit_by_name (const std::string &name);
/**
* @brief Begin iterator for the subcircuits of the circuit (non-const version)

View File

@ -204,6 +204,16 @@ Net::~Net ()
clear ();
}
Netlist *Net::netlist ()
{
return mp_circuit ? mp_circuit->netlist () : 0;
}
const Netlist *Net::netlist () const
{
return mp_circuit ? mp_circuit->netlist () : 0;
}
void Net::clear ()
{
m_name.clear ();

View File

@ -41,6 +41,7 @@ class Circuit;
class DeviceTerminalDefinition;
class DeviceClass;
class Pin;
class Netlist;
/**
* @brief A reference to a terminal of a device
@ -418,6 +419,18 @@ public:
return mp_circuit;
}
/**
* @brief Gets the netlist the net lives in
* This pointer is 0 if the net is not part of a netlist.
*/
Netlist *netlist ();
/**
* @brief Gets the netlist the net lives in (const version)
* This pointer is 0 if the net is not part of a netlist.
*/
const Netlist *netlist () const;
/**
* @brief Clears the circuit
*/

View File

@ -31,7 +31,7 @@ namespace db
// Netlist class implementation
Netlist::Netlist (NetlistManipulationCallbacks *callbacks)
: mp_callbacks (callbacks),
: m_case_sensitive (true), mp_callbacks (callbacks),
m_valid_topology (false), m_lock_count (0),
m_circuit_by_name (this, &Netlist::begin_circuits, &Netlist::end_circuits),
m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits),
@ -44,7 +44,8 @@ Netlist::Netlist (NetlistManipulationCallbacks *callbacks)
}
Netlist::Netlist (const Netlist &other)
: gsi::ObjectBase (other), tl::Object (other), m_valid_topology (false), m_lock_count (0),
: gsi::ObjectBase (other), tl::Object (other), m_case_sensitive (true),
m_valid_topology (false), m_lock_count (0),
m_circuit_by_name (this, &Netlist::begin_circuits, &Netlist::end_circuits),
m_circuit_by_cell_index (this, &Netlist::begin_circuits, &Netlist::end_circuits),
m_device_abstract_by_name (this, &Netlist::begin_device_abstracts, &Netlist::end_device_abstracts),
@ -69,6 +70,8 @@ Netlist &Netlist::operator= (const Netlist &other)
clear ();
set_case_sensitive (other.is_case_sensitive ());
std::map<const DeviceClass *, DeviceClass *> dct;
for (const_device_class_iterator dc = other.begin_device_classes (); dc != other.end_device_classes (); ++dc) {
DeviceClass *dc_new = dc->clone ();
@ -100,6 +103,34 @@ Netlist &Netlist::operator= (const Netlist &other)
return *this;
}
void Netlist::set_case_sensitive (bool f)
{
m_case_sensitive = f;
}
int Netlist::name_compare (bool case_sensitive, const std::string &n1, const std::string &n2)
{
// TODO: unicode support?
if (case_sensitive) {
return strcmp (n1.c_str (), n2.c_str ());
} else {
#if defined(_WIN32)
return _stricmp (n1.c_str (), n2.c_str ());
#else
return strcasecmp (n1.c_str (), n2.c_str ());
#endif
}
}
std::string Netlist::normalize_name (bool case_sensitive, const std::string &n)
{
if (case_sensitive) {
return n;
} else {
return tl::to_upper_case (n);
}
}
void Netlist::circuits_changed ()
{
m_circuit_by_cell_index.invalidate ();
@ -472,8 +503,10 @@ void Netlist::flatten ()
DeviceClass *Netlist::device_class_by_name (const std::string &name)
{
std::string nn = m_case_sensitive ? name : normalize_name (name);
for (device_class_iterator d = begin_device_classes (); d != end_device_classes (); ++d) {
if (d->name () == name) {
if (d->name () == nn) {
return d.operator-> ();
}
}

View File

@ -102,6 +102,19 @@ public:
*/
void clear ();
/**
* @brief Returns a value indicating whether the netlist names are case sensitive
*/
bool is_case_sensitive () const
{
return m_case_sensitive;
}
/**
* @brief Sets a value indicating whether the netlist names are case sensitive
*/
void set_case_sensitive (bool f);
/**
* @brief Returns a parsable string representation of the netlist
*
@ -225,7 +238,7 @@ public:
*/
Circuit *circuit_by_name (const std::string &name)
{
return m_circuit_by_name.object_by (name);
return m_circuit_by_name.object_by (normalize_name (name));
}
/**
@ -235,7 +248,7 @@ public:
*/
const Circuit *circuit_by_name (const std::string &name) const
{
return m_circuit_by_name.object_by (name);
return m_circuit_by_name.object_by (normalize_name (name));
}
/**
@ -429,7 +442,7 @@ public:
*/
DeviceAbstract *device_abstract_by_name (const std::string &name)
{
return m_device_abstract_by_name.object_by (name);
return m_device_abstract_by_name.object_by (normalize_name (name));
}
/**
@ -439,7 +452,7 @@ public:
*/
const DeviceAbstract *device_abstract_by_name (const std::string &name) const
{
return m_device_abstract_by_name.object_by (name);
return m_device_abstract_by_name.object_by (normalize_name (name));
}
/**
@ -502,10 +515,29 @@ public:
*/
void combine_devices ();
/**
* @brief Compares two names with the given case sensitivity
*/
static int name_compare (bool case_sensitive, const std::string &n1, const std::string &n2);
/**
* @brief Normalizes a name with the given case sensitivity
*/
static std::string normalize_name (bool case_sensitive, const std::string &n);
/**
* @brief Normalizes a name with the given case sensitivity of the netlist
*/
std::string normalize_name (const std::string &n) const
{
return normalize_name (is_case_sensitive (), n);
}
private:
friend class Circuit;
friend class DeviceAbstract;
bool m_case_sensitive;
tl::weak_ptr<db::NetlistManipulationCallbacks> mp_callbacks;
circuit_list m_circuits;
device_class_list m_device_classes;

View File

@ -48,15 +48,12 @@ struct GlobalCompareOptions
debug_netcompare = tl::app_flag ("netlist-compare-debug-netcompare");
// $KLAYOUT_NETLIST_COMPARE_DEBUG_NETGRAPH
debug_netgraph = tl::app_flag ("netlist-compare-debug-netgraph");
// $KLAYOUT_NETLIST_COMPARE_CASE_SENSITIVE
compare_case_sensitive = tl::app_flag ("netlist-compare-case-sensitive");
m_is_initialized = true;
}
}
bool debug_netcompare;
bool debug_netgraph;
bool compare_case_sensitive;
private:
bool m_is_initialized;
@ -90,29 +87,19 @@ namespace db
// --------------------------------------------------------------------------------------------------------------------
// A generic string compare
static int name_compare (const std::string &n1, const std::string &n2)
bool combined_case_sensitive (const db::Netlist *a, const db::Netlist *b)
{
// TODO: unicode support?
if (options ()->compare_case_sensitive) {
return strcmp (n1.c_str (), n2.c_str ());
} else {
#if defined(_WIN32)
return _stricmp (n1.c_str (), n2.c_str ());
#else
return strcasecmp (n1.c_str (), n2.c_str ());
#endif
}
bool csa = a ? a->is_case_sensitive () : true;
bool csb = b ? b->is_case_sensitive () : true;
return csa && csb;
}
static inline std::string normalize_name (const std::string &n)
int name_compare (const db::Net *a, const db::Net *b)
{
if (options ()->compare_case_sensitive) {
return n;
} else {
return tl::to_upper_case (n);
}
return db::Netlist::name_compare (combined_case_sensitive (a->netlist (), b->netlist ()), a->name (), b->name ());
}
// --------------------------------------------------------------------------------------------------------------------
// DeviceCompare definition and implementation
@ -404,11 +391,16 @@ class generic_categorizer
{
public:
generic_categorizer (bool with_name = true)
: m_next_cat (0), m_with_name (with_name)
: m_next_cat (0), m_with_name (with_name), m_case_sensitive (true)
{
// .. nothing yet ..
}
void set_case_sensitive (bool f)
{
m_case_sensitive = f;
}
void same (const Obj *ca, const Obj *cb)
{
if (! ca && ! cb) {
@ -471,7 +463,7 @@ public:
if (m_with_name) {
std::string cls_name = normalize_name (cls->name ());
std::string cls_name = db::Netlist::normalize_name (m_case_sensitive, cls->name ());
std::map<std::string, size_t>::const_iterator c = m_cat_by_name.find (cls_name);
if (c != m_cat_by_name.end ()) {
@ -498,6 +490,7 @@ public:
std::map<std::string, size_t> m_cat_by_name;
size_t m_next_cat;
bool m_with_name;
bool m_case_sensitive;
};
// --------------------------------------------------------------------------------------------------------------------
@ -559,6 +552,11 @@ public:
return m_strict_device_categories.find (cat) != m_strict_device_categories.end ();
}
void set_case_sensitive (bool f)
{
generic_categorizer::set_case_sensitive (f);
}
private:
std::set<size_t> m_strict_device_categories;
};
@ -606,6 +604,11 @@ public:
{
return generic_categorizer<db::Circuit>::cat_for (cr);
}
void set_case_sensitive (bool f)
{
generic_categorizer::set_case_sensitive (f);
}
};
// --------------------------------------------------------------------------------------------------------------------
@ -1461,7 +1464,7 @@ NetGraphNode::net_less (const db::Net *a, const db::Net *b)
const std::string &pna = a->begin_pins ()->pin ()->name ();
const std::string &pnb = b->begin_pins ()->pin ()->name ();
if (! pna.empty () && ! pnb.empty ()) {
return name_compare (pna, pnb) < 0;
return db::Netlist::name_compare (combined_case_sensitive (a->netlist (), b->netlist ()), pna, pnb) < 0;
}
}
return false;
@ -1484,7 +1487,7 @@ NetGraphNode::edge_equal (const db::Net *a, const db::Net *b)
const std::string &pna = a->begin_pins ()->pin ()->name ();
const std::string &pnb = b->begin_pins ()->pin ()->name ();
if (! pna.empty () && ! pnb.empty ()) {
return name_compare (pna, pnb) == 0;
return db::Netlist::name_compare (combined_case_sensitive (a->netlist (), b->netlist ()), pna, pnb) == 0;
}
}
return true;
@ -2256,7 +2259,7 @@ namespace {
bool operator() (const std::pair<const NetGraphNode *, NetGraphNode::edge_iterator> &a, const std::pair<const NetGraphNode *, NetGraphNode::edge_iterator>b) const
{
tl_assert (a.first->net () && b.first->net ());
return name_compare (a.first->net ()->name (), b.first->net ()->name ()) < 0;
return name_compare (a.first->net (), b.first->net ()) < 0;
}
};
@ -2317,7 +2320,7 @@ static bool net_names_are_different (const db::Net *a, const db::Net *b)
if (! a || ! b || a->name ().empty () || b->name ().empty ()) {
return false;
} else {
return name_compare (a->name (), b->name ()) != 0;
return name_compare (a, b) != 0;
}
}
@ -2326,7 +2329,7 @@ static bool net_names_are_equal (const db::Net *a, const db::Net *b)
if (! a || ! b || a->name ().empty () || b->name ().empty ()) {
return false;
} else {
return name_compare (a->name (), b->name ()) == 0;
return name_compare (a, b) == 0;
}
}
@ -2815,6 +2818,7 @@ NetlistComparer::NetlistComparer (NetlistCompareLogger *logger)
m_depth_first = true;
m_dont_consider_net_names = false;
m_case_sensitive = false;
}
NetlistComparer::~NetlistComparer ()
@ -2887,6 +2891,7 @@ NetlistComparer::unmatched_circuits (db::Netlist *a, db::Netlist *b, std::vector
{
// we need to create a copy because this method is supposed to be const.
db::CircuitCategorizer circuit_categorizer = *mp_circuit_categorizer;
circuit_categorizer.set_case_sensitive (m_case_sensitive);
std::map<size_t, std::pair<std::vector<db::Circuit *>, std::vector<db::Circuit *> > > cat2circuits;
@ -2928,11 +2933,16 @@ NetlistComparer::unmatched_circuits (db::Netlist *a, db::Netlist *b, std::vector
bool
NetlistComparer::compare (const db::Netlist *a, const db::Netlist *b) const
{
m_case_sensitive = combined_case_sensitive (a, b);
// we need to create a copy because this method is supposed to be const.
db::CircuitCategorizer circuit_categorizer = *mp_circuit_categorizer;
db::DeviceCategorizer device_categorizer = *mp_device_categorizer;
db::CircuitPinMapper circuit_pin_mapper = *mp_circuit_pin_mapper;
circuit_categorizer.set_case_sensitive (m_case_sensitive);
device_categorizer.set_case_sensitive (m_case_sensitive);
bool good = true;
std::map<size_t, std::pair<std::vector<const db::Circuit *>, std::vector<const db::Circuit *> > > cat2circuits;
@ -3745,14 +3755,14 @@ NetlistComparer::do_pin_assignment (const db::Circuit *c1, const db::NetGraph &g
for (db::Circuit::const_pin_iterator p = c2->begin_pins (); p != c2->end_pins (); ++p) {
const db::Net *net = c2->net_for_pin (p->id ());
if (!net && !p->name ().empty ()) {
abstract_pins_by_name.insert (std::make_pair (normalize_name (p->name ()), std::make_pair ((const db::Pin *) 0, (const db::Pin *) 0))).first->second.second = p.operator-> ();
abstract_pins_by_name.insert (std::make_pair (db::Netlist::normalize_name (m_case_sensitive, p->name ()), std::make_pair ((const db::Pin *) 0, (const db::Pin *) 0))).first->second.second = p.operator-> ();
}
}
for (db::Circuit::const_pin_iterator p = c1->begin_pins (); p != c1->end_pins (); ++p) {
const db::Net *net = c1->net_for_pin (p->id ());
if (!net && !p->name ().empty ()) {
abstract_pins_by_name.insert (std::make_pair (normalize_name (p->name ()), std::make_pair ((const db::Pin *) 0, (const db::Pin *) 0))).first->second.first = p.operator-> ();
abstract_pins_by_name.insert (std::make_pair (db::Netlist::normalize_name (m_case_sensitive, p->name ()), std::make_pair ((const db::Pin *) 0, (const db::Pin *) 0))).first->second.first = p.operator-> ();
}
}

View File

@ -364,6 +364,7 @@ protected:
size_t m_max_depth;
bool m_depth_first;
bool m_dont_consider_net_names;
mutable bool m_case_sensitive;
};
}

View File

@ -299,6 +299,9 @@ void NetlistSpiceReader::read (tl::InputStream &stream, db::Netlist &netlist)
m_global_nets.clear ();
m_circuits_read.clear ();
// SPICE netlists are case insensitive
netlist.set_case_sensitive (false);
try {
mp_delegate->start (&netlist);
@ -775,13 +778,7 @@ std::string NetlistSpiceReader::read_name_with_case (tl::Extractor &ex)
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 1
return tl::to_upper_case (read_name_with_case (ex));
#else
return read_name_with_case (ex);
#endif
return mp_netlist->normalize_name (read_name_with_case (ex));
}
bool NetlistSpiceReader::read_element (tl::Extractor &ex, const std::string &element, const std::string &name)

View File

@ -298,8 +298,7 @@ module LVS
# resolve otherwise.
#
# circuit_a and net_a are for the layout netlist, circuit_b and net_b for the schematic netlist.
# Note that SPICE netlists are normalized to @b upper case @/b. So enter
# upper case circuit or net names for SPICE schematic netlists.
# Names are case sensitive for layout-derived netlists and case-insensitive for SPICE schematic netlists.
#
# Use this method andwhere in the script before the \compare call.
@ -378,8 +377,7 @@ module LVS
# method allows establishing an explicit correspondence.
#
# circuit_a is for the layout netlist, circuit_b for the schematic netlist.
# Note that SPICE netlists are normalized to @b upper case @/b. So enter
# upper case circuit names for SPICE schematic netlists.
# Names are case sensitive for layout-derived netlists and case-insensitive for SPICE schematic netlists.
#
# One of the circuits may be nil. In this case, the corresponding
# other circuit is mapped to "nothing", i.e. ignored.
@ -420,8 +418,7 @@ module LVS
# \schematic.
#
# class_a is for the layout netlist, class_b for the schematic netlist.
# Note that SPICE netlists are normalized to @b upper case @/b. So enter
# upper case device names for SPICE schematic netlists.
# Names are case sensitive for layout-derived netlists and case-insensitive for SPICE schematic netlists.
#
# One of the device classes may be "nil". In this case, the corresponding
# other device class is mapped to "nothing", i.e. ignored.
@ -484,8 +481,7 @@ module LVS
# The circuit argument is either a circuit name (a string) or a Circuit object
# from the schematic netlist.
#
# Note that SPICE netlists are normalized to @b upper case @/b. So enter
# upper case circuit names for SPICE schematic netlists.
# Names are case sensitive for layout-derived netlists and case-insensitive for SPICE schematic netlists.
#
# The pin arguments are zero-based pin numbers, where 0 is the first number, 1 the second etc.
# If the netlist provides named pins, names can be used instead of numbers. Again, use upper

View File

@ -28,7 +28,7 @@
#include "lymMacro.h"
#include "tlFileUtils.h"
void run_test (tl::TestBase *_this, const std::string &suffix, const std::string &layout, bool with_l2n = false, const std::string &top = std::string ())
void run_test (tl::TestBase *_this, const std::string &suffix, const std::string &layout, bool with_l2n = false, const std::string &top = std::string (), bool change_case = false)
{
std::string rs = tl::testsrc ();
rs += "/testdata/lvs/" + suffix + ".lvs";
@ -58,7 +58,8 @@ void run_test (tl::TestBase *_this, const std::string &suffix, const std::string
"$lvs_test_target_cir = '%s'\n"
"$lvs_test_target_l2n = '%s'\n"
"$lvs_test_top = '%s'\n"
, src, output_lvsdb, output_cir, output_l2n, top)
"$change_case = %s\n"
, src, output_lvsdb, output_cir, output_l2n, top, change_case ? "true" : "false")
);
config.set_interpreter (lym::Macro::Ruby);
EXPECT_EQ (config.run (), 0);
@ -108,11 +109,15 @@ TEST(5_simple_same_device_classes)
TEST(6_simple_pin_swapping)
{
run_test (_this, "ringo_simple_pin_swapping", "ringo.gds");
// change case
run_test (_this, "ringo_simple_pin_swapping", "ringo.gds", false, std::string (), true);
}
TEST(7_net_and_circuit_equivalence)
{
run_test (_this, "ringo_simple_net_and_circuit_equivalence", "ringo_renamed.gds");
// change case
run_test (_this, "ringo_simple_net_and_circuit_equivalence", "ringo_renamed.gds", false, std::string (), true);
}
TEST(8_simplification)
@ -143,6 +148,8 @@ TEST(12_simple_dmos)
TEST(13_simple_ringo_device_subcircuits)
{
run_test (_this, "ringo_device_subcircuits", "ringo.gds");
// change case
run_test (_this, "ringo_device_subcircuits", "ringo.gds", false, std::string (), true);
}
TEST(14_simple_ringo_mixed_hierarchy)

View File

@ -116,7 +116,7 @@ connect_global(bulk, "SUBSTRATE")
connect_global(ptie, "SUBSTRATE")
# Test same_device_classes
same_device_classes("PMOS", "XPMOS")
same_device_classes("PMOS", $change_case ? "xpMos" : "XPMOS")
# Compare section

View File

@ -8,7 +8,7 @@ target_netlist($lvs_test_target_cir, write_spice, "Extracted by KLayout")
schematic("ringo.cir")
# preempt configuration (see below)
same_nets("top", "ENABLE", "RINGO", "ENABLE")
same_nets("top", "ENABLE", $change_case ? "Ringo" : "RINGO", $change_case ? "enable" : "ENABLE")
deep
@ -71,8 +71,8 @@ connect_global(ptie, "SUBSTRATE")
# Compare section
same_circuits("top", "RINGO")
same_circuits("INV", "INVX1")
same_circuits("top", $change_case ? "ringo" : "RINGO")
same_circuits("INV", $change_case ? "invX1" : "INVX1")
same_circuits("DOESNOTEXIST", "DOESNOTEXIST2")
same_nets("DOESNOTEXIST", "ENABLE", "DOESNOTEXIST2", "ENABLE")

View File

@ -8,7 +8,7 @@ target_netlist($lvs_test_target_cir, write_spice, "Extracted by KLayout")
schematic("ringo_pin_swapping.cir")
# preempt configuration
equivalent_pins("ND2X1", 4, 5)
equivalent_pins($change_case ? "nd2X1" : "ND2X1", 4, 5)
deep