WIP: first steps towards global nets.

This commit is contained in:
Matthias Koefferlein 2019-01-06 15:28:40 +01:00
parent eb435d5d85
commit 64c2548ab8
16 changed files with 406 additions and 13 deletions

View File

@ -269,7 +269,8 @@ HEADERS = \
dbNetlistDeviceExtractor.h \
dbNetlistExtractor.h \
dbNetlistDeviceExtractorClasses.h \
dbLayoutToNetlist.h
dbLayoutToNetlist.h \
dbHierNetworkProcessor.h
!equals(HAVE_QT, "0") {

View File

@ -77,6 +77,60 @@ Connectivity::connect (const db::DeepLayer &la, const db::DeepLayer &lb)
connect (la.layer (), lb.layer ());
}
Connectivity::global_nets_type s_empty_global_nets;
Connectivity::global_nets_iterator
Connectivity::begin_global_connections (unsigned int l) const
{
std::map<unsigned int, global_nets_type>::const_iterator g = m_global_connections.find (l);
if (g != m_global_connections.end ()) {
return g->second.begin ();
} else {
return s_empty_global_nets.begin ();
}
}
Connectivity::global_nets_iterator
Connectivity::end_global_connections (unsigned int l) const
{
std::map<unsigned int, global_nets_type>::const_iterator g = m_global_connections.find (l);
if (g != m_global_connections.end ()) {
return g->second.end ();
} else {
return s_empty_global_nets.end ();
}
}
size_t
Connectivity::connect_global (unsigned int l, const std::string &gn)
{
for (std::vector<std::string>::const_iterator i = m_global_net_names.begin (); i != m_global_net_names.end (); ++i) {
if (*i == gn) {
size_t id = i - m_global_net_names.begin ();
m_global_connections [l].insert (id);
return id;
}
}
size_t id = m_global_net_names.size ();
m_global_connections [l].insert (id);
m_global_net_names.push_back (gn);
return id;
}
size_t
Connectivity::connect_global (const db::DeepLayer &l, const std::string &gn)
{
return connect_global (l.layer (), gn);
}
const std::string &
Connectivity::global_net_name (size_t id) const
{
tl_assert (id < m_global_net_names.size ());
return m_global_net_names [id];
}
Connectivity::layer_iterator
Connectivity::begin_layers () const
{

View File

@ -52,6 +52,8 @@ class DB_PUBLIC Connectivity
public:
typedef std::set<unsigned int> layers_type;
typedef layers_type::const_iterator layer_iterator;
typedef std::set<size_t> global_nets_type;
typedef global_nets_type::const_iterator global_nets_iterator;
/**
* @brief Creates a connectivity object without any connections
@ -68,6 +70,11 @@ public:
*/
void connect (unsigned int la, unsigned int lb);
/**
* @brief Adds a connection to a global net
*/
size_t connect_global (unsigned int l, const std::string &gn);
/**
* @brief Adds intra-layer connectivity for layer l
* This is a convenience method that takes a db::DeepLayer object.
@ -82,6 +89,16 @@ public:
*/
void connect (const db::DeepLayer &la, const db::DeepLayer &lb);
/**
* @brief Adds a connection to a global net
*/
size_t connect_global (const db::DeepLayer &la, const std::string &gn);
/**
* @brief Gets the global net name per ID
*/
const std::string &global_net_name (size_t id) const;
/**
* @brief Begin iterator for the layers involved
*/
@ -102,6 +119,16 @@ public:
*/
layer_iterator end_connected (unsigned int layer) const;
/**
* @brief Begin iterator for the global connections for a specific layer
*/
global_nets_iterator begin_global_connections (unsigned int layer) const;
/**
* @brief End iterator for the layers connected to a specific layer
*/
global_nets_iterator end_global_connections (unsigned int layer) const;
/**
* @brief Returns true, if the given shapes on the given layers interact
*
@ -123,6 +150,8 @@ public:
private:
layers_type m_all_layers;
std::map<unsigned int, layers_type> m_connected;
std::vector<std::string> m_global_net_names;
std::map<unsigned int, global_nets_type> m_global_connections;
};
/**

View File

@ -149,6 +149,27 @@ void LayoutToNetlist::connect (const db::Region &a, const db::Region &b)
m_conn.connect (dla.layer (), dlb.layer ());
}
size_t LayoutToNetlist::connect_global (const db::Region &l, const std::string &gn)
{
if (m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
}
if (! is_deep (l)) {
throw (tl::Exception (tl::to_string (tr ("Non-hierarchical layers cannot be used in intra-layer connectivity for netlist extraction"))));
}
// we need to keep a reference, so we can safely delete the region
db::DeepLayer dl (l);
m_dlrefs.insert (dl);
return m_conn.connect_global (dl.layer (), gn);
}
const std::string &LayoutToNetlist::global_net_name (size_t id) const
{
return m_conn.global_net_name (id);
}
void LayoutToNetlist::extract_netlist ()
{
if (m_netlist_extracted) {

View File

@ -165,6 +165,17 @@ public:
*/
void connect (const db::Region &a, const db::Region &b);
/**
* @brief Connects the given layer with a global net with the given name
* Returns the global net ID
*/
size_t connect_global (const db::Region &l, const std::string &gn);
/**
* @brief Gets the global net name for a given global net ID
*/
const std::string &global_net_name (size_t id) const;
/**
* @brief Runs the netlist extraction
* See the class description for more details.

View File

@ -113,8 +113,22 @@ const Net *Device::net_for_terminal (size_t terminal_id) const
return 0;
}
void Device::connect_terminal_global (size_t terminal_id, size_t global_net_id)
{
connect_terminal (terminal_id, 0);
m_global_connections.push_back (std::make_pair (terminal_id, global_net_id));
}
void Device::connect_terminal (size_t terminal_id, Net *net)
{
for (size_t i = 0; i < m_global_connections.size (); ) {
if (m_global_connections [i].first == terminal_id) {
m_global_connections.erase (m_global_connections.begin () + i);
} else {
++i;
}
}
if (net_for_terminal (terminal_id) == net) {
return;
}

View File

@ -806,6 +806,9 @@ class DB_PUBLIC Device
: public tl::Object
{
public:
typedef std::vector<std::pair<size_t, size_t> > global_connections;
typedef global_connections::const_iterator global_connections_iterator;
/**
* @brief Default constructor
*/
@ -881,6 +884,31 @@ public:
return m_name;
}
/**
* @brief Gets the global connections iterator (begin)
* Global connections are terminals attached to a global net.
* This iterator delivers a pair of terminal ID (first)
* and global net ID (second).
* See Connectivity for the definition of the global net ID.
*/
global_connections_iterator begin_global_connections () const
{
return m_global_connections.begin ();
}
/**
* @brief Gets the global connections iterator (end)
*/
global_connections_iterator end_global_connections () const
{
return m_global_connections.end ();
}
/**
* @brief Connects the given terminal to the given global net
*/
void connect_terminal_global (size_t terminal_id, size_t global_net_id);
/**
* @brief Gets the net attached to a specific terminal
* Returns 0 if no net is attached.
@ -901,6 +929,8 @@ public:
* If the net is 0 the terminal is disconnected.
* If non-null, a NetTerminalRef object will be inserted into the
* net and connected with the given terminal.
* If the terminal is connected to a global net, it will be
* disconnected from there.
*/
void connect_terminal (size_t terminal_id, Net *net);
@ -936,6 +966,7 @@ private:
std::vector<double> m_parameters;
size_t m_id;
Circuit *mp_circuit;
global_connections m_global_connections;
/**
* @brief Sets the terminal reference for a specific terminal

View File

@ -64,7 +64,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect
circuits.insert (std::make_pair (c->cell_index (), c.operator-> ()));
}
std::map<db::cell_index_type, std::map<size_t, size_t> > pins_per_cluster;
std::map<db::cell_index_type, std::map<size_t, size_t> > pins_per_cluster_per_cell;
std::map<db::cell_index_type, std::map<size_t, db::Net *> > global_nets_per_cell;
for (db::Layout::bottom_up_const_iterator cid = layout.begin_bottom_up (); cid != layout.end_bottom_up (); ++cid) {
@ -88,7 +89,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect
circuit = k->second;
}
std::map<size_t, size_t> &c2p = pins_per_cluster [*cid];
std::map<size_t, db::Net *> &global_nets = global_nets_per_cell [*cid];
std::map<size_t, size_t> &c2p = pins_per_cluster_per_cell [*cid];
std::map<db::InstElement, db::SubCircuit *> subcircuits;
@ -98,8 +100,19 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect
net->set_cluster_id (*c);
circuit->add_net (net);
// make global net connections for clusters which connect to such
std::set<size_t> global_net_ids;
std::vector<unsigned int> layers = clusters.cluster_by_id (*c).layers ();
for (std::vector<unsigned int>::const_iterator l = layers.begin (); l != layers.end (); ++l) {
global_net_ids.insert (conn.begin_global_connections (*l), conn.end_global_connections (*l));
}
for (std::set<size_t>::const_iterator g = global_net_ids.begin (); g != global_net_ids.end (); ++g) {
global_nets.insert (std::make_pair (*g, net));
assign_net_name (conn.global_net_name (*g), net);
}
// make subcircuit connections (also make the subcircuits if required) from the connections of the clusters
make_and_connect_subcircuits (circuit, clusters, *c, net, subcircuits, circuits, pins_per_cluster, layout.dbu ());
make_and_connect_subcircuits (circuit, clusters, *c, net, subcircuits, circuits, pins_per_cluster_per_cell, layout.dbu ());
// collect the properties - we know that the cluster attributes are property ID's because the
// cluster processor converts shape property IDs to attributes
@ -110,7 +123,7 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect
if (terminal_annot_name_id.first && j->first == terminal_annot_name_id.second) {
make_device_terminal_from_property (j->second, circuit, net);
} else if (text_annot_name_id.first && j->first == text_annot_name_id.second) {
make_net_name_from_property (j->second, net);
assign_net_name (j->second.to_string (), net);
}
}
}
@ -123,6 +136,49 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, const db::Connect
}
// make global net connections for devices
for (db::Circuit::device_iterator d = circuit->begin_devices (); d != circuit->end_devices (); ++d) {
for (db::Device::global_connections_iterator g = d->begin_global_connections (); g != d->end_global_connections (); ++g) {
db::Net *&net = global_nets [g->second];
if (! net) {
net = new db::Net (conn.global_net_name (g->second));
circuit->add_net (net);
}
net->add_terminal (db::NetTerminalRef (d.operator-> (), g->first));
}
}
// make the global net connections into subcircuits - if necessary by creating pins into the subcircuit
for (db::Circuit::subcircuit_iterator sc = circuit->begin_subcircuits (); sc != circuit->end_subcircuits (); ++sc) {
db::Circuit *subcircuit = sc->circuit_ref ();
const std::map<size_t, db::Net *> &sc_gn = global_nets_per_cell [subcircuit->cell_index ()];
for (std::map<size_t, db::Net *>::const_iterator g = global_nets.begin (); g != global_nets.end (); ++g) {
std::map<size_t, db::Net *>::const_iterator gg = sc_gn.find (g->first);
if (gg != sc_gn.end ()) {
size_t pin_id = 0;
if (gg->second->pin_count () > 0) {
pin_id = gg->second->begin_pins ()->pin_id ();
} else {
pin_id = subcircuit->add_pin (conn.global_net_name (gg->first)).id ();
subcircuit->connect_pin (pin_id, gg->second);
}
g->second->add_subcircuit_pin (db::NetSubcircuitPinRef (sc.operator-> (), pin_id));
}
}
}
}
}
@ -192,14 +248,14 @@ void NetlistExtractor::make_device_terminal_from_property (const tl::Variant &v,
}
}
void NetlistExtractor::make_net_name_from_property (const tl::Variant &v, db::Net *net)
void NetlistExtractor::assign_net_name (const std::string &n, db::Net *net)
{
std::string n = v.to_string ();
if (! n.empty ()) {
std::string nn = n;
if (! nn.empty ()) {
if (! net->name ().empty ()) {
n = net->name () + "," + n;
nn = net->name () + "," + nn;
}
net->set_name (n);
net->set_name (nn);
}
}

View File

@ -95,7 +95,7 @@ private:
hier_clusters_type m_net_clusters;
void make_device_terminal_from_property (const tl::Variant &v, db::Circuit *circuit, db::Net *net);
void make_net_name_from_property (const tl::Variant &v, db::Net *net);
void assign_net_name (const std::string &n, db::Net *net);
/**
* @brief Make a pin connection from clusters

View File

@ -32,6 +32,13 @@ Class<db::Connectivity> decl_dbConnectivity ("db", "Connectivity",
) +
gsi::method ("connect", (void (db::Connectivity::*) (unsigned int, unsigned int)) &db::Connectivity::connect, gsi::arg ("layer_a"), gsi::arg ("layer_b"),
"@brief Specifies inter-layer connectivity.\n"
) +
gsi::method ("connect_global", (size_t (db::Connectivity::*) (unsigned int, const std::string &)) &db::Connectivity::connect_global, gsi::arg ("layer"), gsi::arg ("global_net_name"),
"@brief Connects the given layer to the global net given by name.\n"
"Returns the ID of the global net."
) +
gsi::method ("global", &db::Connectivity::global_net_name, gsi::arg ("global_net_id"),
"@brief Gets the name for a given global net ID.\n"
),
"@brief This class specifies connections between different layers."
"Connections are build using \\connect. There are basically two flavours of connections: intra-layer and inter-layer.\n"
@ -45,6 +52,11 @@ Class<db::Connectivity> decl_dbConnectivity ("db", "Connectivity",
"\n"
"All layers are specified in terms of layer indexes. Layer indexes are layout layer indexes (see \\Layout class).\n"
"\n"
"The connectivity object also manages the global nets. Global nets are substrate for example "
"and they are propagated automatically from subcircuits to circuits. "
"Global nets are defined by name and are managed through IDs. To get the name for a given ID, use "
"\\global_net_name."
"\n"
"This class has been introduced in version 0.26.\n"
);

View File

@ -123,6 +123,14 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"@brief Defines an inter-layer connection for the given layers.\n"
"The conditions mentioned with intra-layer \\connect apply for this method too.\n"
) +
gsi::method ("connect_global", (void (db::LayoutToNetlist::*) (const db::Region &, const std::string &)) &db::LayoutToNetlist::connect_global, gsi::arg ("l"), gsi::arg ("global_net_name"),
"@brief Defines a connection of the given layer with a global net.\n"
"This method returns the ID of the global net. Use \\global_net_name to get "
"the name back from the ID."
) +
gsi::method ("global_net_name", &db::LayoutToNetlist::global_net_name, gsi::arg ("global_net_id"),
"@brief Gets the global net name for the given global net ID."
) +
gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist,
"@brief Runs the netlist extraction\n"
"See the class description for more details.\n"

View File

@ -61,6 +61,44 @@ static void device_disconnect_terminal_by_name (db::Device *device, const std::s
device_connect_terminal_by_name (device, terminal_name, 0);
}
static tl::Variant device_terminal_for_global_net (const db::Device *device, size_t global_net)
{
for (db::Device::global_connections_iterator g = device->begin_global_connections (); g != device->end_global_connections (); ++g) {
if (g->second == global_net) {
return tl::Variant (g->first);
}
}
return tl::Variant ();
}
static tl::Variant device_global_net_for_terminal (const db::Device *device, size_t terminal_id)
{
for (db::Device::global_connections_iterator g = device->begin_global_connections (); g != device->end_global_connections (); ++g) {
if (g->first == terminal_id) {
return tl::Variant (g->second);
}
}
return tl::Variant ();
}
static tl::Variant device_global_net_for_terminal_name (const db::Device *device, const std::string &terminal_name)
{
if (! device->device_class ()) {
throw tl::Exception (tl::to_string (tr ("Device does not have a device class")));
}
size_t terminal_id = device->device_class ()->terminal_id_for_name (terminal_name);
return device_global_net_for_terminal (device, terminal_id);
}
static void device_connect_terminal_global_by_name (db::Device *device, const std::string &terminal_name, size_t global_net)
{
if (! device->device_class ()) {
throw tl::Exception (tl::to_string (tr ("Device does not have a device class")));
}
size_t terminal_id = device->device_class ()->terminal_id_for_name (terminal_name);
device->connect_terminal_global (terminal_id, global_net);
}
Class<db::Device> decl_dbDevice ("db", "Device",
gsi::method ("device_class", &db::Device::device_class,
"@brief Gets the device class the device belongs to.\n"
@ -91,15 +129,37 @@ Class<db::Device> decl_dbDevice ("db", "Device",
) +
gsi::method_ext ("disconnect_terminal", &device_disconnect_terminal, gsi::arg ("terminal_id"),
"@brief Disconnects the given terminal from any net.\n"
"If the terminal has been connected to a global, this connection will be disconnected too."
) +
gsi::method_ext ("connect_terminal", &device_connect_terminal_by_name, gsi::arg ("terminal_name"), gsi::arg ("net"),
"@brief Connects the given terminal to the specified net.\n"
"This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised."
"This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised.\n"
"If the terminal has been connected to a global net, it will be disconnected from there."
) +
gsi::method_ext ("disconnect_terminal", &device_disconnect_terminal_by_name, gsi::arg ("terminal_name"),
"@brief Disconnects the given terminal from any net.\n"
"This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised."
) +
gsi::method ("connect_terminal_global", &db::Device::connect_terminal_global, gsi::arg ("terminal_id"), gsi::arg ("global_net_id"),
"@brief Connects the given terminal to the given global net.\n"
"The global net ID is taken from \\Connectivity (connect_global, etc.).\n"
"If the terminal was already connected to another net, it will be disconnected from there."
) +
gsi::method_ext ("connect_terminal_global", &device_connect_terminal_global_by_name, gsi::arg ("terminal_name"), gsi::arg ("global_net_id"),
"@brief Connects the given terminal to the given global net.\n"
"This version accepts a terminal name. If the name is not a valid terminal name, an exception is raised."
) +
gsi::method_ext ("terminal_on_global_net", &device_terminal_for_global_net, gsi::arg ("global_net_id"),
"@brief Gets the terminal ID for the given global net or nil if no terminal is not that global net.\n"
"The global net ID is managed by the \\Connectivity object."
) +
gsi::method_ext ("global_net_on_terminal", &device_global_net_for_terminal, gsi::arg ("terminal_id"),
"@brief Gets the global net ID for the given terminal ID or nil if the terminal is not connected to a global net.\n"
) +
gsi::method_ext ("global_net_on_terminal", &device_global_net_for_terminal_name, gsi::arg ("terminal_name"),
"@brief Gets the global net ID for the given terminal name or nil if the terminal is not connected to a global net.\n"
"If the name is not a valid terminal name, an exception is raised."
) +
gsi::method ("parameter", (double (db::Device::*) (size_t) const) &db::Device::parameter_value, gsi::arg ("param_id"),
"@brief Gets the parameter value for the given parameter ID."
) +

View File

@ -44,6 +44,18 @@ static std::string l2s (db::Connectivity::layer_iterator b, db::Connectivity::la
return s;
}
static std::string gn2s (db::Connectivity::global_nets_iterator b, db::Connectivity::global_nets_iterator e)
{
std::string s;
for (db::Connectivity::global_nets_iterator i = b; i != e; ++i) {
if (! s.empty ()) {
s += ",";
}
s += tl::to_string (*i);
}
return s;
}
TEST(1_Connectivity)
{
db::Connectivity conn;
@ -69,6 +81,27 @@ TEST(1_Connectivity)
EXPECT_EQ (l2s (conn.begin_connected (0), conn.end_connected (0)), "0,1,2");
EXPECT_EQ (l2s (conn.begin_connected (1), conn.end_connected (1)), "0,1");
EXPECT_EQ (l2s (conn.begin_connected (2), conn.end_connected (2)), "0,2");
EXPECT_EQ (conn.connect_global (0, "GLOBAL"), size_t (0));
EXPECT_EQ (gn2s (conn.begin_global_connections (2), conn.end_global_connections (2)), "");
EXPECT_EQ (gn2s (conn.begin_global_connections (0), conn.end_global_connections (0)), "0");
EXPECT_EQ (conn.connect_global (2, "GLOBAL2"), size_t (1));
EXPECT_EQ (gn2s (conn.begin_global_connections (2), conn.end_global_connections (2)), "1");
EXPECT_EQ (conn.connect_global (0, "GLOBAL2"), size_t (1));
EXPECT_EQ (gn2s (conn.begin_global_connections (0), conn.end_global_connections (0)), "0,1");
EXPECT_EQ (conn.global_net_name (0), "GLOBAL");
EXPECT_EQ (conn.global_net_name (1), "GLOBAL2");
db::Connectivity conn2 = conn;
EXPECT_EQ (l2s (conn2.begin_connected (0), conn2.end_connected (0)), "0,1,2");
EXPECT_EQ (l2s (conn2.begin_connected (1), conn2.end_connected (1)), "0,1");
EXPECT_EQ (l2s (conn2.begin_connected (2), conn2.end_connected (2)), "0,2");
EXPECT_EQ (gn2s (conn2.begin_global_connections (0), conn2.end_global_connections (0)), "0,1");
EXPECT_EQ (conn2.global_net_name (0), "GLOBAL");
EXPECT_EQ (conn2.global_net_name (1), "GLOBAL2");
}
TEST(2_ShapeInteractions)

View File

@ -74,7 +74,6 @@ TEST(1_DeviceTerminalDefinition)
dc.clear_parameter_definitions ();
EXPECT_EQ (dc.parameter_definitions ().empty (), true);
}
TEST(2_DeviceClass)
@ -1025,3 +1024,53 @@ TEST(12_NetlistTopology)
EXPECT_EQ (td2string (nl.get ()), "c1,c3,c2");
EXPECT_EQ (bu2string (nl.get ()), "c2,c3,c1");
}
TEST(13_DeviceGlobalNets)
{
db::DeviceTerminalDefinition pd;
pd.set_name ("name");
pd.set_description ("nothing yet");
db::DeviceTerminalDefinition pd2;
pd2.set_name ("name2");
pd2.set_description ("now it has something");
db::DeviceClass dc;
dc.set_name ("devname");
dc.set_description ("devdesc");
dc.add_terminal_definition (pd);
dc.add_terminal_definition (pd2);
db::Device d (&dc);
db::Net n;
d.connect_terminal_global (0, 17);
db::Device::global_connections_iterator g;
g = d.begin_global_connections ();
EXPECT_EQ (g != d.end_global_connections (), true);
EXPECT_EQ (g->first, size_t (0));
EXPECT_EQ (g->second, size_t (17));
++g;
EXPECT_EQ (g == d.end_global_connections (), true);
d.connect_terminal (0, &n);
g = d.begin_global_connections ();
EXPECT_EQ (g == d.end_global_connections (), true);
EXPECT_EQ (d.net_for_terminal (0) == &n, true);
d.connect_terminal_global (0, 17);
EXPECT_EQ (d.net_for_terminal (0) == 0, true);
g = d.begin_global_connections ();
EXPECT_EQ (g != d.end_global_connections (), true);
EXPECT_EQ (g->first, size_t (0));
EXPECT_EQ (g->second, size_t (17));
d.connect_terminal (0, 0);
g = d.begin_global_connections ();
EXPECT_EQ (g == d.end_global_connections (), true);
EXPECT_EQ (d.net_for_terminal (0) == 0, true);
}

View File

@ -61,6 +61,10 @@ class DBLayoutToNetlist_TestClass < TestBase
assert_equal(l2n.internal_layout.cell(ci).name, ly2.cell(cm.cell_mapping(ci)).name)
end
rmetal1 = l2n.make_polygon_layer( ly.layer(6, 0) )
bulk_id = l2n.connect_global(rmetal1, "BULK")
assert_equal(l2n.global_net_name(bulk_id), "BULK")
end
def test_2_ShapesFromNet

View File

@ -257,6 +257,16 @@ class DBNetlist_TestClass < TestBase
assert_equal(d1.net_for_terminal(1).inspect, "nil")
assert_equal(d1.net_for_terminal(0).inspect, "nil")
d2.connect_terminal(0, net)
assert_equal(net.terminal_count, 1)
d2.connect_terminal_global(0, 1)
assert_equal(net.terminal_count, 0)
assert_equal(d2.terminal_on_global_net(1), 0)
assert_equal(d2.terminal_on_global_net(17).inspect, "nil")
assert_equal(d2.global_net_on_terminal(0), 1)
assert_equal(d2.global_net_on_terminal(1).inspect, "nil")
end
def test_5_SubCircuit