WIP: added circuit blankout.

This commit is contained in:
Matthias Koefferlein 2019-07-06 19:50:20 +02:00
parent fb8a64b0e1
commit 5ce8dd2684
8 changed files with 410 additions and 24 deletions

View File

@ -23,6 +23,8 @@
#include "dbCircuit.h"
#include "dbNetlist.h"
#include <set>
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<db::Circuit *> 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<tl::weak_ptr<db::Circuit> > called_circuits;
for (std::set<db::Circuit *>::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<tl::weak_ptr<db::Circuit> >::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 ()) {

View File

@ -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;

View File

@ -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");

View File

@ -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 ();

View File

@ -1081,6 +1081,10 @@ Class<db::Circuit> 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<db::Circuit> 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<db::Circuit> 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<db::Circuit *> circuits_to_flatten;
std::list<tl::weak_ptr<db::Circuit> > 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<db::Circuit *>::const_iterator c = circuits_to_flatten.begin (); c != circuits_to_flatten.end (); ++c) {
nl->flatten_circuit (*c);
for (std::list<tl::weak_ptr<db::Circuit> >::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<tl::weak_ptr<db::Circuit> > 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<tl::weak_ptr<db::Circuit> >::iterator c = circuits_to_blank.begin (); c != circuits_to_blank.end (); ++c) {
if (c->get ()) {
(*c)->blank ();
}
}
}
@ -1308,7 +1347,15 @@ Class<db::Netlist> 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<db::Netlist> 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<db::Netlist> 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"

View File

@ -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"
);
}

View File

@ -23,7 +23,7 @@
<p>
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 <class_doc href="Netlist#flatten_circuit"/>:
</p>
<pre>schematic.flatten_circuit("NMOS")</pre>
@ -110,6 +110,46 @@
<pre>netlist.flatten_circuit("CIRCUIT_NAME")</pre>
<p>
The argument to "flatten_circuit" is a glob pattern (shell-like).
For example, "NMOS*" will flatten all circuits starting with "NMOS".
</p>
<h2>Black boxing (circuit abstraction)</h2>
<p>
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.
</p>
<p>
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.
</p>
<p>
To wipe out the innards of a circuit, use the <class_doc href="Netlist#blank_circuit"/> method:
</p>
<pre>netlist.blank_circuit("CIRCUIT_NAME")
schematic.blank_circuit("CIRCUIT_NAME")</pre>
<p>
The argument to "blank_circuit" is a glob pattern (shell-like).
For example, "MEMORY*" will blank out all circuits starting with "MEMORY".
</p>
<p><b>NOTE:</b> Use "blank_circuit" before "purge" or "simplify" (see below). This method
sets a flag (<class_doc href="Circuit#dont_purge"/>) which prevents purging of abstract
circuits.</p>
<h2>Purging (elimination of redundancy)</h2>
<p>

View File

@ -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