diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index 0460bad9a..7a9fe43d0 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -23,6 +23,8 @@ #include "dbCircuit.h" #include "dbNetlist.h" +#include + namespace db { @@ -30,7 +32,7 @@ namespace db // Circuit class implementation Circuit::Circuit () - : m_cell_index (0), mp_netlist (0), + : m_dont_purge (false), m_cell_index (0), mp_netlist (0), m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices), m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets), @@ -45,7 +47,7 @@ Circuit::Circuit () } Circuit::Circuit (const Circuit &other) - : gsi::ObjectBase (other), tl::Object (other), m_cell_index (0), mp_netlist (0), + : gsi::ObjectBase (other), tl::Object (other), m_dont_purge (false), m_cell_index (0), mp_netlist (0), m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices), m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets), @@ -80,6 +82,7 @@ Circuit &Circuit::operator= (const Circuit &other) clear (); m_name = other.m_name; + m_dont_purge = other.m_dont_purge; m_cell_index = other.m_cell_index; m_pins = other.m_pins; @@ -191,6 +194,11 @@ void Circuit::set_name (const std::string &name) } } +void Circuit::set_dont_purge (bool dp) +{ + m_dont_purge = dp; +} + void Circuit::set_cell_index (const db::cell_index_type ci) { m_cell_index = ci; @@ -436,6 +444,35 @@ void Circuit::set_pin_ref_for_pin (size_t pin_id, Net::pin_iterator iter) m_pin_refs [pin_id] = iter; } +void Circuit::blank () +{ + tl_assert (netlist () != 0); + + std::set cs; + for (subcircuit_iterator i = m_subcircuits.begin (); i != m_subcircuits.end (); ++i) { + cs.insert (i->circuit_ref ()); + } + + // weak pointers are good because deleting a subcircuit might delete others ahead in + // this list: + std::list > called_circuits; + for (std::set::const_iterator c = cs.begin (); c != cs.end (); ++c) { + called_circuits.push_back (*c); + } + + m_nets.clear (); + m_subcircuits.clear (); + m_devices.clear (); + + for (std::list >::iterator c = called_circuits.begin (); c != called_circuits.end (); ++c) { + if (c->get () && ! (*c)->has_refs ()) { + netlist ()->purge_circuit (c->get ()); + } + } + + set_dont_purge (true); +} + const Net *Circuit::net_for_pin (size_t pin_id) const { if (pin_id < m_pin_refs.size ()) { diff --git a/src/db/db/dbCircuit.h b/src/db/db/dbCircuit.h index 1f7a02eed..6e84784e2 100644 --- a/src/db/db/dbCircuit.h +++ b/src/db/db/dbCircuit.h @@ -164,6 +164,20 @@ public: return m_name; } + /** + * @brief Sets or resets the "don't purge" flag + * This flag will prevent "purge" from deleting this circuit. It is set by "blank". + */ + void set_dont_purge (bool dp); + + /** + * @brief Gets or resets the "don't purge" flag + */ + bool dont_purge () const + { + return m_dont_purge; + } + /** * @brief The index of the circuit in the netlist * CAUTION: this attribute is used for internal purposes and may not be valid always. @@ -215,6 +229,23 @@ public: return m_refs.begin (); } + /** + * @brief Gets the references to this circuit (end, const version) + * This iterator will deliver all subcircuits referencing this circuit + */ + const_refs_iterator end_refs () const + { + return m_refs.end (); + } + + /** + * @brief Returns a value indicating whether the circuit has references + */ + bool has_refs () const + { + return begin_refs () != end_refs (); + } + /** * @brief Gets the child circuits iterator (begin) * The child circuits are the circuits referenced by all subcircuits @@ -261,15 +292,6 @@ public: */ const_parent_circuit_iterator end_parents () const; - /** - * @brief Gets the references to this circuit (end, const version) - * This iterator will deliver all subcircuits referencing this circuit - */ - const_refs_iterator end_refs () const - { - return m_refs.end (); - } - /** * @brief Clears the pins */ @@ -658,6 +680,16 @@ public: */ void flatten_subcircuit (SubCircuit *subcircuit); + /** + * @brief Blanks out the circuit + * + * This will remove all innards of the circuit (nets, devices, subcircuits) + * and circuits which will itself are not longer be called after this. + * This operation will eventually leave a blackbox model of the circuit + * containing only pins. + */ + void blank (); + private: friend class Netlist; friend class Net; @@ -665,6 +697,7 @@ private: friend class Device; std::string m_name; + bool m_dont_purge; db::cell_index_type m_cell_index; net_list m_nets; pin_list m_pins; diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 1c86b2e91..9b245fbf1 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -373,6 +373,12 @@ void Netlist::remove_circuit (Circuit *circuit) m_circuits.erase (circuit); } +void Netlist::purge_circuit (Circuit *circuit) +{ + circuit->blank (); + remove_circuit (circuit); +} + void Netlist::flatten_circuit (Circuit *circuit) { tl_assert (circuit != 0); @@ -463,7 +469,7 @@ void Netlist::purge () Circuit *circuit = c.operator-> (); circuit->purge_nets (); - if (circuit->begin_nets () == circuit->end_nets ()) { + if (circuit->begin_nets () == circuit->end_nets () && ! circuit->dont_purge ()) { // No nets left: delete the subcircuits that refer to us and finally delete the circuit while (circuit->begin_refs () != circuit->end_refs ()) { @@ -563,13 +569,17 @@ std::string Netlist::to_string () const std::string ps; const db::SubCircuit &subcircuit = *sc; const db::Circuit *circuit = sc->circuit_ref (); - for (db::Circuit::const_pin_iterator p = circuit->begin_pins (); p != circuit->end_pins (); ++p) { - if (p != circuit->begin_pins ()) { - ps += ","; + if (circuit) { + for (db::Circuit::const_pin_iterator p = circuit->begin_pins (); p != circuit->end_pins (); ++p) { + if (p != circuit->begin_pins ()) { + ps += ","; + } + ps += pin2string (*p) + "=" + net2string (subcircuit.net_for_pin (p->id ())); } - ps += pin2string (*p) + "=" + net2string (subcircuit.net_for_pin (p->id ())); + res += std::string (" subcircuit ") + tl::to_word_or_quoted_string (circuit->name ()) + " " + subcircuit2string (*sc) + " (" + ps + ");\n"; + } else { + res += std::string (" subcircuit (null);\n"); } - res += std::string (" subcircuit ") + tl::to_word_or_quoted_string (circuit->name ()) + " " + subcircuit2string (*sc) + " (" + ps + ");\n"; } res += std::string ("end;\n"); diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index d145083cf..df443c2c0 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -138,6 +138,14 @@ public: */ void remove_circuit (Circuit *circuit); + /** + * @brief Purges a circuit from the netlist + * In contrast to "delete", this method will + * also remove subcircuits unless called + * otherwise. + */ + void purge_circuit (Circuit *circuit); + /** * @brief Gets the number of circuits */ @@ -449,6 +457,9 @@ public: * * This method will purge all nets which return "floating". Circuits which don't have any * nets (or only floating ones) and removed. Their subcircuits are disconnected. + * + * This method respects the circuit's "dont_purge" flag and will not purge them if this + * flag is set. */ void purge (); diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 5db3e0393..745573e42 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -1081,6 +1081,10 @@ Class decl_dbCircuit ("db", "Circuit", "@brief Iterates over the parent circuits of this circuit\n" "Child circuits are the ones that are referencing this circuit via subcircuits." ) + + gsi::method ("has_refs", &db::Circuit::has_refs, + "@brief Returns a value indicating whether the circuit has references\n" + "A circuit has references if there is at least one subcircuit referring to it." + ) + gsi::iterator ("each_ref", (db::Circuit::refs_iterator (db::Circuit::*) ()) &db::Circuit::begin_refs, (db::Circuit::refs_iterator (db::Circuit::*) ()) &db::Circuit::end_refs, "@brief Iterates over the subcircuit objects referencing this circuit\n" ) + @@ -1170,6 +1174,14 @@ Class decl_dbCircuit ("db", "Circuit", gsi::iterator ("each_subcircuit", (db::Circuit::subcircuit_iterator (db::Circuit::*) ()) &db::Circuit::begin_subcircuits, (db::Circuit::subcircuit_iterator (db::Circuit::*) ()) &db::Circuit::end_subcircuits, "@brief Iterates over the subcircuits of the circuit" ) + + gsi::method ("blank", &db::Circuit::blank, + "@brief Blanks out the circuit\n" + "This method will remove all the innards of the circuit and just leave the pins. " + "The pins won't be connected to inside nets anymore, but the circuit can still be " + "called by subcircuit references. " + "This method will eventually create a 'circuit abstract' (or black box). It will " + "set the \\dont_purge flag to mark this circuit as 'intentionally empty'." + ) + gsi::method ("netlist", (db::Netlist *(db::Circuit::*) ()) &db::Circuit::netlist, "@brief Gets the netlist object the circuit lives in" ) + @@ -1179,6 +1191,14 @@ Class decl_dbCircuit ("db", "Circuit", gsi::method ("name", &db::Circuit::name, "@brief Gets the name of the circuit" ) + + gsi::method ("dont_purge", &db::Circuit::dont_purge, + "@brief Gets a value indicating whether the circuit can be purged on \\Netlist#purge.\n" + ) + + gsi::method ("dont_purge=", &db::Circuit::set_dont_purge, gsi::arg ("f"), + "@brief Sets a value indicating whether the circuit can be purged on \\Netlist#purge.\n" + "If this attribute is set to true, \\Netlist#purge will never delete this circuit.\n" + "This flag therefore marks this circuit as 'precious'." + ) + gsi::method ("cell_index=", &db::Circuit::set_cell_index, gsi::arg ("cell_index"), "@brief Sets the cell index\n" "The cell index relates a circuit with a cell from a layout. It's intended to " @@ -1287,7 +1307,7 @@ static void read_netlist (db::Netlist *nl, const std::string &file, db::NetlistR static void flatten_circuit_by_name (db::Netlist *nl, const std::string &name_pattern) { - std::list circuits_to_flatten; + std::list > circuits_to_flatten; tl::GlobPattern pat (name_pattern); for (db::Netlist::circuit_iterator c = nl->begin_circuits (); c != nl->end_circuits (); ++c) { if (pat.match (c->name ())) { @@ -1295,8 +1315,27 @@ static void flatten_circuit_by_name (db::Netlist *nl, const std::string &name_pa } } - for (std::list::const_iterator c = circuits_to_flatten.begin (); c != circuits_to_flatten.end (); ++c) { - nl->flatten_circuit (*c); + for (std::list >::iterator c = circuits_to_flatten.begin (); c != circuits_to_flatten.end (); ++c) { + if (c->get ()) { + nl->flatten_circuit (c->get ()); + } + } +} + +static void blank_circuit_by_name (db::Netlist *nl, const std::string &name_pattern) +{ + std::list > circuits_to_blank; + tl::GlobPattern pat (name_pattern); + for (db::Netlist::circuit_iterator c = nl->begin_circuits (); c != nl->end_circuits (); ++c) { + if (pat.match (c->name ())) { + circuits_to_blank.push_back (c.operator-> ()); + } + } + + for (std::list >::iterator c = circuits_to_blank.begin (); c != circuits_to_blank.end (); ++c) { + if (c->get ()) { + (*c)->blank (); + } } } @@ -1308,7 +1347,15 @@ Class decl_dbNetlist ("db", "Netlist", ) + gsi::method ("remove", &db::Netlist::remove_circuit, gsi::arg ("circuit"), "@brief Removes the given circuit object from the netlist\n" - "After the object has been removed, it becomes invalid and cannot be used further." + "After the circuit has been removed, the object becomes invalid and cannot be used further. " + "A circuit with references (see \\has_refs) should not be removed as the " + "subcircuits calling it would afterwards point to nothing." + ) + + gsi::method ("purge_circuit", &db::Netlist::purge_circuit, gsi::arg ("circuit"), + "@brief Removes the given circuit object and all child circuits which are not used otherwise from the netlist\n" + "After the circuit has been removed, the object becomes invalid and cannot be used further. " + "A circuit with references (see \\has_refs) should not be removed as the " + "subcircuits calling it would afterwards point to nothing." ) + gsi::method ("flatten_circuit", &db::Netlist::flatten_circuit, gsi::arg ("circuit"), "@brief Flattens a subcircuit\n" @@ -1318,9 +1365,19 @@ Class decl_dbNetlist ("db", "Netlist", gsi::method_ext ("flatten_circuit", &flatten_circuit_by_name, gsi::arg ("pattern"), "@brief Flattens circuits matching a certain pattern\n" "This method will substitute all instances (subcircuits) of all circuits with names matching the given name pattern. " - "The name pattern is a glob expression. For example, 'flatten_circuit(\"np*\")' will flatten all circuits witt names " + "The name pattern is a glob expression. For example, 'flatten_circuit(\"np*\")' will flatten all circuits with names " "starting with 'np'." ) + + gsi::method_ext ("blank_circuit", &blank_circuit_by_name, gsi::arg ("pattern"), + "@brief Blanks circuits matching a certain pattern\n" + "This method will erase everything from inside the circuits matching the given pattern. It will only leave pins which are " + "not connected to any net. Hence, this method forms 'abstract' or black-box circuits which can be instantiated through " + "subcircuits like the former ones, but are empty shells.\n" + "The name pattern is a glob expression. For example, 'flatten_circuit(\"np*\")' will blank out all circuits with names " + "starting with 'np'.\n" + "\n" + "For more details see \\Circuit#blank which is the corresponding method on the actual object." + ) + gsi::method ("circuit_by_cell_index", (db::Circuit *(db::Netlist::*) (db::cell_index_type)) &db::Netlist::circuit_by_cell_index, gsi::arg ("cell_index"), "@brief Gets the circuit object for a given cell index.\n" "If the cell index is not valid or no circuit is registered with this index, nil is returned." @@ -1390,7 +1447,9 @@ Class decl_dbNetlist ("db", "Netlist", gsi::method ("purge", &db::Netlist::purge, "@brief Purge unused nets, circuits and subcircuits.\n" "This method will purge all nets which return \\floating == true. Circuits which don't have any " - "nets (or only floating ones) and removed. Their subcircuits are disconnected." + "nets (or only floating ones) and removed. Their subcircuits are disconnected.\n" + "This method respects the \\Circuit#dont_purge attribute and will never delete circuits " + "with this flag set." ) + gsi::method ("purge_nets", &db::Netlist::purge_nets, "@brief Purges floating nets.\n" diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 6ed838c41..cf13e7011 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -1297,3 +1297,103 @@ TEST(21_FlattenSubCircuit2) "end;\n" ); } + +TEST(22_BlankCircuit) +{ + db::Netlist nl; + + db::DeviceClass *dc; + + dc = new db::DeviceClassMOS3Transistor (); + dc->set_name ("NMOS"); + nl.add_device_class (dc); + + dc = new db::DeviceClassMOS3Transistor (); + dc->set_name ("PMOS"); + nl.add_device_class (dc); + + nl.from_string ( + "circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD);\n" + " subcircuit INV2 INV2_SC1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" + " subcircuit INV2 INV2_SC2 (IN=FB,$2=(null),OUT=$I8,$4=VSS,$5=VDD);\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN);\n" + " subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN);\n" + " subcircuit PTRANS SC3 ($1=$5,$2=OUT,$3=$2);\n" + " subcircuit NTRANS SC4 ($1=$4,$2=OUT,$3=$2);\n" + "end;\n" + "circuit PTRANS ($1=$1,$2=$2,$3=$3);\n" + " device PMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95);\n" + "end;\n" + "circuit NTRANS ($1=$1,$2=$2,$3=$3);\n" + " device NMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95);\n" + "end;\n" + ); + + db::Netlist nl2; + + nl2 = nl; + db::Circuit *circuit = nl2.circuit_by_name ("INV2"); + nl2.purge_circuit (circuit); + + EXPECT_EQ (nl2.to_string (), + "circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD);\n" + " subcircuit (null);\n" + " subcircuit (null);\n" + "end;\n" + ); + + nl2 = nl; + circuit = nl2.circuit_by_name ("INV2"); + EXPECT_EQ (circuit->dont_purge (), false); + circuit->blank (); + EXPECT_EQ (circuit->dont_purge (), true); + + EXPECT_EQ (nl2.to_string (), + "circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD);\n" + " subcircuit INV2 INV2_SC1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" + " subcircuit INV2 INV2_SC2 (IN=FB,$2=(null),OUT=$I8,$4=VSS,$5=VDD);\n" + "end;\n" + "circuit INV2 (IN=(null),$2=(null),OUT=(null),$4=(null),$5=(null));\n" + "end;\n" + ); + + // purge won't delete INV2 + nl2.purge (); + + EXPECT_EQ (nl2.to_string (), + "circuit RINGO (IN=(null),OSC=OSC,VSS=VSS,VDD=VDD);\n" + " subcircuit INV2 INV2_SC1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" + " subcircuit INV2 INV2_SC2 (IN=FB,$2=(null),OUT=$I8,$4=VSS,$5=VDD);\n" + "end;\n" + "circuit INV2 (IN=(null),$2=(null),OUT=(null),$4=(null),$5=(null));\n" + "end;\n" + ); + + circuit->set_dont_purge (false); + + // now it will ... + nl2.purge (); + + EXPECT_EQ (nl2.to_string (), + "" + ); + + nl2 = nl; + circuit = nl2.circuit_by_name ("RINGO"); + nl2.purge_circuit (circuit); + + EXPECT_EQ (nl2.to_string (), + "" + ); + + nl2 = nl; + circuit = nl2.circuit_by_name ("RINGO"); + circuit->blank (); + + EXPECT_EQ (nl2.to_string (), + "circuit RINGO (IN=(null),OSC=(null),VSS=(null),VDD=(null));\n" + "end;\n" + ); +} diff --git a/src/lay/lay/doc/manual/lvs_tweaks.xml b/src/lay/lay/doc/manual/lvs_tweaks.xml index 9e9e81dd1..99c2a6d60 100644 --- a/src/lay/lay/doc/manual/lvs_tweaks.xml +++ b/src/lay/lay/doc/manual/lvs_tweaks.xml @@ -23,7 +23,7 @@

Netlist tweaks can also be applied to the schematic netlist. For example to flatten away - a model subcircuit called "NMOS", use this expression: + a model subcircuit called "NMOS", use this :

schematic.flatten_circuit("NMOS")
@@ -110,6 +110,46 @@
netlist.flatten_circuit("CIRCUIT_NAME")
+

+ The argument to "flatten_circuit" is a glob pattern (shell-like). + For example, "NMOS*" will flatten all circuits starting with "NMOS". +

+ +

Black boxing (circuit abstraction)

+ +

+ Circuit abstraction is a technique to reduce the verification overhead. + At an early stage it might be useful to replace a cell by a simplified version or + by a raw pin frame. The circuits extracted from such cells is basically empty or + are intentionally simplified. But as long as there is something inside the + cell which the parent circuit connects to, pins will be generated. These + pins then can be thought of as the circuit's abstraction. +

+ +

+ A useful method in this context is the "blank_circuit" method. It clears + a circuit's innards and leaves only the pins. You can use this method to + ensure abstracts in both the layout netlist and the schematic. After this, + the compare algorithm will identify both circuits as identical, provided + they feature the same number of pins. +

+ +

+ To wipe out the innards of a circuit, use the method: +

+ +
netlist.blank_circuit("CIRCUIT_NAME")
+schematic.blank_circuit("CIRCUIT_NAME")
+ +

+ The argument to "blank_circuit" is a glob pattern (shell-like). + For example, "MEMORY*" will blank out all circuits starting with "MEMORY". +

+ +

NOTE: Use "blank_circuit" before "purge" or "simplify" (see below). This method + sets a flag () which prevents purging of abstract + circuits.

+

Purging (elimination of redundancy)

diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index c5a693b6b..df5309401 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -43,6 +43,10 @@ class DBNetlist_TestClass < TestBase assert_equal(nl.circuit_by_name("DOESNOTEXIST").inspect, "nil") cc = RBA::Circuit::new + assert_equal(cc.dont_purge, false) + cc.dont_purge = true + assert_equal(cc.dont_purge, true) + nl.add(cc) cc.name = "ABC" @@ -845,6 +849,98 @@ END nl3.flatten_circuit("{N,P}TRANS") assert_equal(nl3.to_s, nl2.to_s) + nl2 = nl.dup + nl2.flatten_circuit("*") # smoke test + assert_equal(nl2.to_s, "") + + end + + def test_12_BlankAndPurgeCircuits + + nl = RBA::Netlist::new + + dc = RBA::DeviceClassMOS3Transistor::new + dc.name = "NMOS" + nl.add(dc) + + dc = RBA::DeviceClassMOS3Transistor::new + dc.name = "PMOS" + nl.add(dc) + + nl.from_s(<<"END") +circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD); + subcircuit INV2 INV2_SC1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD); + subcircuit INV2 INV2_SC2 (IN=FB,$2=(null),OUT=$I8,$4=VSS,$5=VDD); +end; +circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5); + subcircuit PTRANS SC1 ($1=$5,$2=$2,$3=IN); + subcircuit NTRANS SC2 ($1=$4,$2=$2,$3=IN); + subcircuit PTRANS SC3 ($1=$5,$2=OUT,$3=$2); + subcircuit NTRANS SC4 ($1=$4,$2=OUT,$3=$2); +end; +circuit PTRANS ($1=$1,$2=$2,$3=$3); + device PMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95); +end; +circuit NTRANS ($1=$1,$2=$2,$3=$3); + device NMOS $1 (S=$1,D=$2,G=$3) (L=0.25,W=0.95); +end; +END + + nl2 = nl.dup + circuit = nl2.circuit_by_name("INV2") + nl2.purge_circuit(circuit) + + assert_equal(nl2.to_s, <<"END") +circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD); + subcircuit (null); + subcircuit (null); +end; +END + + nl2 = nl.dup + circuit = nl2.circuit_by_name("INV2") + circuit.blank + + assert_equal(nl2.to_s, <<"END") +circuit RINGO (IN=IN,OSC=OSC,VSS=VSS,VDD=VDD); + subcircuit INV2 INV2_SC1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD); + subcircuit INV2 INV2_SC2 (IN=FB,$2=(null),OUT=$I8,$4=VSS,$5=VDD); +end; +circuit INV2 (IN=(null),$2=(null),OUT=(null),$4=(null),$5=(null)); +end; +END + + nl2 = nl.dup + circuit = nl2.circuit_by_name("RINGO") + nl2.purge_circuit(circuit) + + assert_equal(nl2.to_s, "") + + nl2 = nl.dup + circuit = nl2.circuit_by_name("RINGO") + circuit.blank + + assert_equal(nl2.to_s, <<"END") +circuit RINGO (IN=(null),OSC=(null),VSS=(null),VDD=(null)); +end; +END + + nl2 = nl.dup + nl2.blank_circuit("*") + + assert_equal(nl2.to_s, <<"END") +circuit RINGO (IN=(null),OSC=(null),VSS=(null),VDD=(null)); +end; +END + + nl2 = nl.dup + nl2.blank_circuit("RINGO") + + assert_equal(nl2.to_s, <<"END") +circuit RINGO (IN=(null),OSC=(null),VSS=(null),VDD=(null)); +end; +END + end end