Refactoring and GSI binding for combined device interface.

This commit is contained in:
Matthias Koefferlein 2019-05-10 18:32:05 +02:00
parent 675a96eb9e
commit 0f0dd42b4d
7 changed files with 348 additions and 66 deletions

View File

@ -189,12 +189,12 @@ void Device::set_parameter_value (const std::string &name, double v)
void Device::add_others_terminals (unsigned int this_terminal, db::Device *other, unsigned int other_terminal)
{
std::vector<OtherTerminalRef> &terminals = m_reconnected_terminals [this_terminal];
std::vector<DeviceReconnectedTerminal> &terminals = m_reconnected_terminals [this_terminal];
std::map<unsigned int, std::vector<OtherTerminalRef> >::const_iterator ot = other->m_reconnected_terminals.find (other_terminal);
std::map<unsigned int, std::vector<DeviceReconnectedTerminal> >::const_iterator ot = other->m_reconnected_terminals.find (other_terminal);
if (ot == other->m_reconnected_terminals.end ()) {
terminals.push_back (OtherTerminalRef (other_abstracts ().size (), other_terminal));
terminals.push_back (DeviceReconnectedTerminal (other_abstracts ().size (), other_terminal));
} else {
@ -217,7 +217,7 @@ void Device::init_terminal_routes ()
size_t n = device_class ()->terminal_definitions ().size ();
for (size_t i = 0; i < n; ++i) {
m_reconnected_terminals [i].push_back (OtherTerminalRef (0, i));
m_reconnected_terminals [i].push_back (DeviceReconnectedTerminal (0, i));
}
}
@ -258,11 +258,11 @@ void Device::join_device (db::Device *other)
m_other_abstracts.reserve (m_other_abstracts.size () + 1 + other->m_other_abstracts.size ());
m_other_abstracts.push_back (std::make_pair (other->device_abstract (), d));
m_other_abstracts.push_back (db::DeviceAbstractRef (other->device_abstract (), d));
for (std::vector<std::pair<const db::DeviceAbstract *, db::DVector> >::const_iterator a = other->m_other_abstracts.begin (); a != other->m_other_abstracts.end (); ++a) {
for (std::vector<db::DeviceAbstractRef>::const_iterator a = other->m_other_abstracts.begin (); a != other->m_other_abstracts.end (); ++a) {
m_other_abstracts.push_back (*a);
m_other_abstracts.back ().second += d;
m_other_abstracts.back ().offset += d;
}
}
@ -281,8 +281,8 @@ void Device::translate_device_abstracts (const std::map<const DeviceAbstract *,
{
set_device_abstract (map_da (map, device_abstract ()));
for (std::vector<std::pair<const db::DeviceAbstract *, db::DVector> >::iterator a = m_other_abstracts.begin (); a != m_other_abstracts.end (); ++a) {
a->first = map_da (map, a->first);
for (std::vector<db::DeviceAbstractRef>::iterator a = m_other_abstracts.begin (); a != m_other_abstracts.end (); ++a) {
a->device_abstract = map_da (map, a->device_abstract);
}
}

View File

@ -39,6 +39,51 @@ class Circuit;
class DeviceClass;
class DeviceAbstract;
/**
* @brief A structure describing a terminal reference into another device abstract
*/
struct DeviceReconnectedTerminal
{
DeviceReconnectedTerminal (size_t _device_index, unsigned int _other_terminal_id)
: device_index (_device_index), other_terminal_id (_other_terminal_id)
{
// .. nothing yet ..
}
DeviceReconnectedTerminal ()
: device_index (0), other_terminal_id (0)
{
// .. nothing yet ..
}
size_t device_index;
unsigned int other_terminal_id;
};
/**
* @brief A structure describing a reference to another device abstract
*
* This structure is used within Device to reference more than the standard
* device abstract.
*/
struct DeviceAbstractRef
{
DeviceAbstractRef (const db::DeviceAbstract *_device_abstract, const db::DVector &_offset)
: device_abstract (_device_abstract), offset (_offset)
{
// .. nothing yet ..
}
DeviceAbstractRef ()
: device_abstract (0), offset ()
{
// .. nothing yet ..
}
const db::DeviceAbstract *device_abstract;
db::DVector offset;
};
/**
* @brief An actual device
*
@ -55,21 +100,6 @@ public:
typedef std::vector<std::pair<size_t, size_t> > global_connections;
typedef global_connections::const_iterator global_connections_iterator;
/**
* @brief A structure describing a terminal reference into another device abstract
*/
struct OtherTerminalRef
{
OtherTerminalRef (size_t _device_index, unsigned int _other_terminal_id)
: device_index (_device_index), other_terminal_id (_other_terminal_id)
{
// .. nothing yet ..
}
size_t device_index;
unsigned int other_terminal_id;
};
/**
* @brief Default constructor
*/
@ -269,9 +299,9 @@ public:
* The returned vector (if any) is a complete list of terminals connected to the given
* logical device terminal.
*/
const std::vector<OtherTerminalRef> *reconnected_terminals_for (unsigned int this_terminal) const
const std::vector<DeviceReconnectedTerminal> *reconnected_terminals_for (unsigned int this_terminal) const
{
std::map<unsigned int, std::vector<OtherTerminalRef> >::const_iterator t = m_reconnected_terminals.find (this_terminal);
std::map<unsigned int, std::vector<DeviceReconnectedTerminal> >::const_iterator t = m_reconnected_terminals.find (this_terminal);
if (t != m_reconnected_terminals.end ()) {
return & t->second;
} else {
@ -282,7 +312,17 @@ public:
/**
* @brief Gets the map of reconnected terminals
*/
const std::map<unsigned int, std::vector<OtherTerminalRef> > &reconnected_terminals () const
const std::map<unsigned int, std::vector<DeviceReconnectedTerminal> > &reconnected_terminals () const
{
return m_reconnected_terminals;
}
/**
* @brief Gets the map of reconnected terminals (non-const version)
*
* NOTE: don't use this method to modify this container! It's provided for persistence implementation only.
*/
std::map<unsigned int, std::vector<DeviceReconnectedTerminal> > &reconnected_terminals ()
{
return m_reconnected_terminals;
}
@ -293,27 +333,17 @@ public:
* This list does not include the intrinsic original abstract of the device.
* This vector is non-empty if this device is a combined one.
*/
const std::vector<std::pair<const db::DeviceAbstract *, db::DVector> > &other_abstracts () const
const std::vector<DeviceAbstractRef> &other_abstracts () const
{
return m_other_abstracts;
}
/**
* @brief Gets the map of reconnected terminals (non-const version)
*
* NOTE: don't use this method to modify this container! It's provided for persistence implementation only.
*/
std::map<unsigned int, std::vector<OtherTerminalRef> > &reconnected_terminals ()
{
return m_reconnected_terminals;
}
/**
* @brief Gets the set of other device abstracts (non-const version)
*
* NOTE: don't use this method to modify this container! It's provided for persistence implementation only.
*/
std::vector<std::pair<const db::DeviceAbstract *, db::DVector> > &other_abstracts ()
std::vector<DeviceAbstractRef> &other_abstracts ()
{
return m_other_abstracts;
}
@ -330,8 +360,8 @@ private:
std::vector<double> m_parameters;
size_t m_id;
Circuit *mp_circuit;
std::vector<std::pair<const db::DeviceAbstract *, db::DVector> > m_other_abstracts;
std::map<unsigned int, std::vector<OtherTerminalRef> > m_reconnected_terminals;
std::vector<DeviceAbstractRef> m_other_abstracts;
std::map<unsigned int, std::vector<DeviceReconnectedTerminal> > m_reconnected_terminals;
/**
* @brief Translates the device abstracts

View File

@ -556,7 +556,7 @@ LayoutToNetlistStandardReader::read_device (db::LayoutToNetlist *l2n, db::Circui
db::DeviceAbstract *da = device_model_by_name (l2n->netlist (), n);
device->other_abstracts ().push_back (std::make_pair (da, db::DVector (dbu * dx, dbu * dy)));
device->other_abstracts ().push_back (db::DeviceAbstractRef (da, db::DVector (dbu * dx, dbu * dy)));
} else if (test (skeys::connect_key) || test (lkeys::connect_key)) {
@ -577,7 +577,7 @@ LayoutToNetlistStandardReader::read_device (db::LayoutToNetlist *l2n, db::Circui
size_t touter_id = terminal_id (dm->device_class (), touter);
size_t tinner_id = terminal_id (dm->device_class (), tinner);
device->reconnected_terminals () [touter_id].push_back (db::Device::OtherTerminalRef (size_t (device_comp_index), tinner_id));
device->reconnected_terminals () [touter_id].push_back (db::DeviceReconnectedTerminal (size_t (device_comp_index), tinner_id));
} else if (test (skeys::terminal_key) || test (lkeys::terminal_key)) {
@ -642,10 +642,10 @@ LayoutToNetlistStandardReader::read_device (db::LayoutToNetlist *l2n, db::Circui
ccell.insert (inst);
insts.push_back (inst);
const std::vector<std::pair<const db::DeviceAbstract *, db::DVector> > &other_devices = device->other_abstracts ();
for (std::vector<std::pair<const db::DeviceAbstract *, db::DVector> >::const_iterator i = other_devices.begin (); i != other_devices.end (); ++i) {
const std::vector<db::DeviceAbstractRef> &other_devices = device->other_abstracts ();
for (std::vector<db::DeviceAbstractRef>::const_iterator i = other_devices.begin (); i != other_devices.end (); ++i) {
db::CellInstArray other_inst (db::CellInst (i->first->cell_index ()), db::Trans (db::Vector (x, y) + dbu_inv * i->second));
db::CellInstArray other_inst (db::CellInst (i->device_abstract->cell_index ()), db::Trans (db::Vector (x, y) + dbu_inv * i->offset));
ccell.insert (other_inst);
insts.push_back (other_inst);
@ -662,13 +662,13 @@ LayoutToNetlistStandardReader::read_device (db::LayoutToNetlist *l2n, db::Circui
if (! device->reconnected_terminals ().empty ()) {
const std::vector<db::Device::OtherTerminalRef> *tr = device->reconnected_terminals_for (tid);
const std::vector<db::DeviceReconnectedTerminal> *tr = device->reconnected_terminals_for (tid);
if (tr) {
for (std::vector<db::Device::OtherTerminalRef>::const_iterator i = tr->begin (); i != tr->end (); ++i) {
for (std::vector<db::DeviceReconnectedTerminal>::const_iterator i = tr->begin (); i != tr->end (); ++i) {
const db::DeviceAbstract *da = dm;
if (i->device_index > 0) {
da = device->other_abstracts () [i->device_index - 1].first;
da = device->other_abstracts () [i->device_index - 1].device_abstract;
}
Connections ref (net->cluster_id (), da->cluster_id_for_terminal (i->other_terminal_id));
connections [insts [i->device_index]].push_back (ref);

View File

@ -500,19 +500,19 @@ void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n, const db::Dev
tl_assert (device.device_abstract () != 0);
*mp_stream << " " << tl::to_word_or_quoted_string (device.device_abstract ()->name ()) << endl;
const std::vector<std::pair<const db::DeviceAbstract *, db::DVector> > &other_abstracts = device.other_abstracts ();
for (std::vector<std::pair<const db::DeviceAbstract *, db::DVector> >::const_iterator a = other_abstracts.begin (); a != other_abstracts.end (); ++a) {
const std::vector<db::DeviceAbstractRef> &other_abstracts = device.other_abstracts ();
for (std::vector<db::DeviceAbstractRef>::const_iterator a = other_abstracts.begin (); a != other_abstracts.end (); ++a) {
db::Vector pos = dbu_inv * a->second;
db::Vector pos = dbu_inv * a->offset;
*mp_stream << " " << Keys::device_key << "(" << tl::to_word_or_quoted_string (a->first->name ()) << " " << pos.x () << " " << pos.y () << ")" << endl;
*mp_stream << " " << Keys::device_key << "(" << tl::to_word_or_quoted_string (a->device_abstract->name ()) << " " << pos.x () << " " << pos.y () << ")" << endl;
}
const std::map<unsigned int, std::vector<db::Device::OtherTerminalRef> > &reconnected_terminals = device.reconnected_terminals ();
for (std::map<unsigned int, std::vector<db::Device::OtherTerminalRef> >::const_iterator t = reconnected_terminals.begin (); t != reconnected_terminals.end (); ++t) {
const std::map<unsigned int, std::vector<db::DeviceReconnectedTerminal> > &reconnected_terminals = device.reconnected_terminals ();
for (std::map<unsigned int, std::vector<db::DeviceReconnectedTerminal> >::const_iterator t = reconnected_terminals.begin (); t != reconnected_terminals.end (); ++t) {
for (std::vector<db::Device::OtherTerminalRef>::const_iterator c = t->second.begin (); c != t->second.end (); ++c) {
for (std::vector<db::DeviceReconnectedTerminal>::const_iterator c = t->second.begin (); c != t->second.end (); ++c) {
*mp_stream << " " << Keys::connect_key << "(" << c->device_index << " " << tl::to_word_or_quoted_string (td [t->first].name ()) << " " << tl::to_word_or_quoted_string (td [c->other_terminal_id].name ()) << ")" << endl;
}

View File

@ -70,6 +70,160 @@ static void device_disconnect_terminal_by_name (db::Device *device, const std::s
device_connect_terminal_by_name (device, terminal_name, 0);
}
static size_t get_device_index (const db::DeviceReconnectedTerminal *obj)
{
return obj->device_index;
}
static void set_device_index (db::DeviceReconnectedTerminal *obj, size_t device_index)
{
obj->device_index = device_index;
}
static size_t get_other_terminal_id (const db::DeviceReconnectedTerminal *obj)
{
return obj->other_terminal_id;
}
static void set_other_terminal_id (db::DeviceReconnectedTerminal *obj, size_t other_terminal_id)
{
obj->other_terminal_id = other_terminal_id;
}
Class<db::DeviceReconnectedTerminal> decl_dbDeviceReconnectedTerminal ("db", "DeviceReconnectedTerminal",
gsi::method_ext ("device_index=", &set_device_index, gsi::arg ("device_index"),
"@brief The device abstract index setter.\n"
"See the class description for details."
) +
gsi::method_ext ("device_index", &get_device_index,
"@brief The device abstract index getter.\n"
"See the class description for details."
) +
gsi::method_ext ("other_terminal_id=", &set_other_terminal_id, gsi::arg ("other_terminal_id"),
"@brief The setter for the abstract's connected terminal.\n"
"See the class description for details."
) +
gsi::method_ext ("other_terminal_id", &get_other_terminal_id,
"@brief The getter for the abstract's connected terminal.\n"
"See the class description for details."
),
"@brief Describes a terminal rerouting in combined devices.\n"
"Combined devices are implemented as a generalization of the device abstract concept in \\Device. For "
"combined devices, multiple \\DeviceAbstract references are present. To support different combination schemes, "
"device-to-abstract routing is supported. Parallel combinations will route all outer terminals to corresponding "
"terminals of all device abstracts (because of terminal swapping these may be different ones).\n"
"\n"
"This object describes one route to an abstract's terminal. The device index is 0 for the main device abstract and "
"1 for the first combined device abstract.\n"
"\n"
"This class has been introduced in version 0.26.\n"
);
static const db::DeviceAbstract *get_device_abstract (const db::DeviceAbstractRef *obj)
{
return obj->device_abstract;
}
static void set_device_abstract (db::DeviceAbstractRef *obj, const db::DeviceAbstract *device_abstract)
{
obj->device_abstract = device_abstract;
}
static db::DVector get_offset (const db::DeviceAbstractRef *obj)
{
return obj->offset;
}
static void set_offset (db::DeviceAbstractRef *obj, const db::DVector &offset)
{
obj->offset = offset;
}
Class<db::DeviceAbstractRef> decl_dbDeviceAbstractRef ("db", "DeviceAbstractRef",
gsi::method_ext ("device_abstract=", &set_device_abstract, gsi::arg ("device_abstract"),
"@brief The setter for the device abstract reference.\n"
"See the class description for details."
) +
gsi::method_ext ("device_abstract", &get_device_abstract,
"@brief The getter for the device abstract reference.\n"
"See the class description for details."
) +
gsi::method_ext ("offset=", &set_offset, gsi::arg ("offset"),
"@brief The setter for the offset.\n"
"See the class description for details."
) +
gsi::method_ext ("offset", &get_offset,
"@brief The getter for the offset.\n"
"See the class description for details."
),
"@brief Describes an additional device abstract reference for combined devices.\n"
"Combined devices are implemented as a generalization of the device abstract concept in \\Device. For "
"combined devices, multiple \\DeviceAbstract references are present. This class describes such an "
"additional reference. A reference is a pointer to an abstract plus an offset by which the abstract "
"is shifted geometrically as compared to the first (initial) abstract.\n"
"\n"
"This class has been introduced in version 0.26.\n"
);
static bool is_combined_device (const db::Device *device)
{
return ! device->reconnected_terminals ().empty ();
}
static std::vector<db::DeviceReconnectedTerminal>::const_iterator begin_reconnected_terminals_for (const db::Device *device, size_t terminal_id)
{
static std::vector<db::DeviceReconnectedTerminal> empty;
const std::vector<db::DeviceReconnectedTerminal> *ti = device->reconnected_terminals_for (terminal_id);
if (! ti) {
return empty.begin ();
} else {
return ti->begin ();
}
}
static std::vector<db::DeviceReconnectedTerminal>::const_iterator end_reconnected_terminals_for (const db::Device *device, size_t terminal_id)
{
static std::vector<db::DeviceReconnectedTerminal> empty;
const std::vector<db::DeviceReconnectedTerminal> *ti = device->reconnected_terminals_for (terminal_id);
if (! ti) {
return empty.end ();
} else {
return ti->end ();
}
}
static void clear_reconnected_terminals (db::Device *device)
{
device->reconnected_terminals ().clear ();
}
static void add_reconnected_terminals (db::Device *device, size_t outer_terminal, const db::DeviceReconnectedTerminal &t)
{
device->reconnected_terminals () [outer_terminal].push_back (t);
}
static std::vector<db::DeviceAbstractRef>::const_iterator begin_other_abstracts (const db::Device *device)
{
return device->other_abstracts ().begin ();
}
static std::vector<db::DeviceAbstractRef>::const_iterator end_other_abstracts (const db::Device *device)
{
return device->other_abstracts ().end ();
}
static void clear_other_abstracts (db::Device *device)
{
device->other_abstracts ().clear ();
}
static void add_other_abstracts (db::Device *device, const db::DeviceAbstractRef &ref)
{
device->other_abstracts ().push_back (ref);
}
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"
@ -78,6 +232,39 @@ Class<db::Device> decl_dbDevice ("db", "Device",
"@brief Gets the device abstract for this device instance.\n"
"See \\DeviceAbstract for more details.\n"
) +
gsi::method ("device_abstract=", &db::Device::set_device_abstract,
"@hide\n"
"Provided for test purposes mainly. Be careful with pointers!"
) +
gsi::method_ext ("is_combined_device?", &is_combined_device,
"@brief Returns true, if the device is a combined device.\n"
"Combined devices feature multiple device abstracts and device-to-abstract terminal connections.\n"
"See \\each_reconnected_terminal and \\each_combined_abstract for more details.\n"
) +
gsi::iterator_ext ("each_reconnected_terminal_for", &begin_reconnected_terminals_for, &end_reconnected_terminals_for, gsi::arg ("terminal_id"),
"@brief Iterates over the reconnected terminal specifications for a given outer terminal.\n"
"This feature applies to combined devices. This iterator will deliver all device-to-abstract terminal reroutings.\n"
) +
gsi::method_ext ("clear_reconnected_terminals", &clear_reconnected_terminals,
"@hide\n"
"Provided for test purposes mainly."
) +
gsi::method_ext ("add_reconnected_terminal_for", &add_reconnected_terminals, gsi::arg ("outer_terminal"), gsi::arg ("descriptor"),
"@hide\n"
"Provided for test purposes mainly."
) +
gsi::iterator_ext ("each_combined_abstract", &begin_other_abstracts, &end_other_abstracts,
"@brief Iterates over the combined device specifications.\n"
"This feature applies to combined devices. This iterator will deliver all device abstracts present in addition to the default device abstract.\n"
) +
gsi::method_ext ("clear_combined_abstracts", &clear_other_abstracts,
"@hide\n"
"Provided for test purposes mainly."
) +
gsi::method_ext ("add_combined_abstract", &add_other_abstracts, gsi::arg ("ref"),
"@hide\n"
"Provided for test purposes mainly."
) +
gsi::method ("circuit", (db::Circuit *(db::Device::*) ()) &db::Device::circuit,
"@brief Gets the circuit the device lives in."
) +

View File

@ -758,9 +758,9 @@ NetlistBrowserPage::adjust_view ()
bbox += trans * bbox_for_device_abstract (layout, (*device)->device_abstract (), db::DVector ());
const std::vector<std::pair<const db::DeviceAbstract *, db::DVector> > &oda = (*device)->other_abstracts ();
for (std::vector<std::pair<const db::DeviceAbstract *, db::DVector> >::const_iterator a = oda.begin (); a != oda.end (); ++a) {
bbox += trans * bbox_for_device_abstract (layout, a->first, a->second);
const std::vector<db::DeviceAbstractRef> &oda = (*device)->other_abstracts ();
for (std::vector<db::DeviceAbstractRef>::const_iterator a = oda.begin (); a != oda.end (); ++a) {
bbox += trans * bbox_for_device_abstract (layout, a->device_abstract, a->offset);
}
}
@ -835,10 +835,10 @@ NetlistBrowserPage::produce_highlights_for_device (const db::Device *device, siz
}
const std::vector<std::pair<const db::DeviceAbstract *, db::DVector> > &oda = device->other_abstracts ();
for (std::vector<std::pair<const db::DeviceAbstract *, db::DVector> >::const_iterator a = oda.begin (); a != oda.end (); ++a) {
const std::vector<db::DeviceAbstractRef> &oda = device->other_abstracts ();
for (std::vector<db::DeviceAbstractRef>::const_iterator a = oda.begin (); a != oda.end (); ++a) {
db::Box da_box = bbox_for_device_abstract (layout, a->first, a->second);
db::Box da_box = bbox_for_device_abstract (layout, a->device_abstract, a->offset);
if (! da_box.empty ()) {
if (n_markers == m_max_shape_count) {

View File

@ -262,7 +262,72 @@ class DBNetlist_TestClass < TestBase
d2.connect_terminal(0, net)
assert_equal(net.terminal_count, 1)
assert_equal(d1.is_combined_device?, false)
da = RBA::DeviceAbstract::new
da.name = "xyz"
d1.device_abstract = da
a = []
d1.each_combined_abstract { |i| a << i }
assert_equal(a.size, 0)
t = RBA::DeviceAbstractRef::new
t.device_abstract = d1.device_abstract
t.offset = RBA::DVector::new(1, 2)
d1.add_combined_abstract(t)
a = []
d1.each_combined_abstract { |i| a << i }
assert_equal(a.size, 1)
assert_equal(a.collect { |i| i.device_abstract.name }.join(","), "xyz")
assert_equal(a.collect { |i| i.offset.to_s }.join(","), "1,2")
d1.clear_combined_abstracts
a = []
d1.each_combined_abstract { |i| a << i }
assert_equal(a.size, 0)
a = []
d1.each_reconnected_terminal_for(0) { |i| a << i }
assert_equal(a.size, 0)
a = []
d1.each_reconnected_terminal_for(1) { |i| a << i }
assert_equal(a.size, 0)
t = RBA::DeviceReconnectedTerminal::new
t.device_index = 0
t.other_terminal_id = 2
d1.add_reconnected_terminal_for(1, t)
t = RBA::DeviceReconnectedTerminal::new
t.device_index = 1
t.other_terminal_id = 1
d1.add_reconnected_terminal_for(1, t)
a = []
d1.each_reconnected_terminal_for(0) { |i| a << i }
assert_equal(a.size, 0)
a = []
d1.each_reconnected_terminal_for(1) { |i| a << i }
assert_equal(a.size, 2)
assert_equal(a.collect { |i| i.device_index.to_s }.join(","), "0,1")
assert_equal(a.collect { |i| i.other_terminal_id.to_s }.join(","), "2,1")
d1.clear_reconnected_terminals
a = []
d1.each_reconnected_terminal_for(0) { |i| a << i }
assert_equal(a.size, 0)
a = []
d1.each_reconnected_terminal_for(1) { |i| a << i }
assert_equal(a.size, 0)
end
def test_5_SubCircuit
@ -648,11 +713,11 @@ END
names = []
nl.each_circuit_top_down { |c| names << c.name }
assert_equal(names.join(","), "C1,C2,C3")
assert_equal(names.join(","), "C3,C2,C1")
names = []
nl.each_circuit_bottom_up { |c| names << c.name }
assert_equal(names.join(","), "C3,C2,C1")
assert_equal(names.join(","), "C1,C2,C3")
names = []
c1.each_child { |c| names << c.name }
@ -691,11 +756,11 @@ END
names = []
nl.each_circuit_top_down { |c| names << c.name }
assert_equal(names.join(","), "C1,C2,C3")
assert_equal(names.join(","), "C1,C3,C2")
names = []
nl.each_circuit_bottom_up { |c| names << c.name }
assert_equal(names.join(","), "C3,C2,C1")
assert_equal(names.join(","), "C2,C3,C1")
c3.create_subcircuit(c2)
assert_equal(nl.top_circuit_count, 1)