WIP: to_string for netlist, tests, some bugfixes on device combination.

This commit is contained in:
Matthias Koefferlein 2018-12-29 22:18:58 +01:00
parent edbafae43e
commit 45b35f3aae
9 changed files with 746 additions and 105 deletions

View File

@ -380,6 +380,12 @@ Net::Net ()
// .. nothing yet ..
}
Net::Net (const std::string &name)
: m_cluster_id (0), mp_circuit (0)
{
m_name = name;
}
Net::Net (const Net &other)
: m_cluster_id (0), mp_circuit (0)
{
@ -861,7 +867,7 @@ void Circuit::purge_nets ()
*/
static void check_device_before_remove (db::Circuit *c, const db::Device *d)
{
if (d->device_class () != 0) {
if (d->device_class () == 0) {
throw tl::Exception (tl::to_string (tr ("Internal error: No device class after removing device in device combination")) + ": name=" + d->name () + ", circuit=" + c->name ());
}
const std::vector<db::DeviceTerminalDefinition> &pd = d->device_class ()->terminal_definitions ();
@ -872,11 +878,13 @@ static void check_device_before_remove (db::Circuit *c, const db::Device *d)
}
}
void Circuit::combine_parallel_devices (const db::DeviceClass &cls)
bool Circuit::combine_parallel_devices (const db::DeviceClass &cls)
{
typedef std::vector<const db::Net *> key_type;
std::map<key_type, std::vector<db::Device *> > combination_candidates;
bool any = false;
// identify the candidates for combination - all devices sharing the same nets
// are candidates for combination in parallel mode
for (device_iterator d = begin_devices (); d != end_devices (); ++d) {
@ -904,12 +912,13 @@ void Circuit::combine_parallel_devices (const db::DeviceClass &cls)
for (std::map<key_type, std::vector<db::Device *> >::iterator cc = combination_candidates.begin (); cc != combination_candidates.end (); ++cc) {
std::vector<db::Device *> &cl = cc->second;
for (size_t i = 0; i != cl.size () - 1; ++i) {
for (size_t j = i + 1; j != cl.size (); ) {
for (size_t i = 0; i < cl.size () - 1; ++i) {
for (size_t j = i + 1; j < cl.size (); ) {
if (cls.combine_devices (cl [i], cl [j])) {
check_device_before_remove (this, cl [j]); // sanity check
delete cl [j];
cl.erase (cl.begin () + j);
any = true;
} else {
++j;
}
@ -917,6 +926,8 @@ void Circuit::combine_parallel_devices (const db::DeviceClass &cls)
}
}
return any;
}
static std::pair<db::Device *, db::Device *> attached_two_devices (db::Net &net, const db::DeviceClass &cls)
@ -955,8 +966,10 @@ static bool same_or_swapped (const std::pair<T, T> &p1, const std::pair<T, T> &p
return (p1.first == p2.first && p1.second == p2.second) || (p1.first == p2.second && p1.second == p2.first);
}
void Circuit::combine_serial_devices (const db::DeviceClass &cls)
bool Circuit::combine_serial_devices(const db::DeviceClass &cls)
{
bool any = false;
for (net_iterator n = begin_nets (); n != end_nets (); ++n) {
std::pair<db::Device *, db::Device *> dd = attached_two_devices (*n, cls);
@ -993,11 +1006,14 @@ void Circuit::combine_serial_devices (const db::DeviceClass &cls)
if (cls.combine_devices (dd.first, dd.second)) {
check_device_before_remove (this, dd.second); // sanity check
delete dd.second;
any = true;
}
}
}
return any;
}
void Circuit::combine_devices ()
@ -1005,12 +1021,27 @@ void Circuit::combine_devices ()
tl_assert (netlist () != 0);
for (Netlist::device_class_iterator dc = netlist ()->begin_device_classes (); dc != netlist ()->end_device_classes (); ++dc) {
if (dc->supports_parallel_combination ()) {
combine_parallel_devices (*dc);
}
if (dc->supports_serial_combination ()) {
combine_serial_devices (*dc);
// repeat the combination step unless no combination happens - this is required to take care of combinations that arise after
// other combinations have been realized.
bool any = true;
while (any) {
any = false;
if (dc->supports_parallel_combination ()) {
if (combine_parallel_devices (*dc)) {
any = true;
}
}
if (dc->supports_serial_combination ()) {
if (combine_serial_devices (*dc)) {
any = true;
}
}
}
}
}
@ -1500,4 +1531,97 @@ void Netlist::combine_devices ()
}
}
static std::string net2string (const db::Net *net)
{
return net ? tl::to_word_or_quoted_string (net->expanded_name ()) : "(null)";
}
static std::string device2string (const db::Device &device)
{
if (device.name ().empty ()) {
return "$" + tl::to_string (device.id ());
} else {
return tl::to_word_or_quoted_string (device.name ());
}
}
static std::string subcircuit2string (const db::SubCircuit &subcircuit)
{
if (subcircuit.name ().empty ()) {
return "$" + tl::to_string (subcircuit.id ());
} else {
return tl::to_word_or_quoted_string (subcircuit.name ());
}
}
static std::string pin2string (const db::Pin &pin)
{
if (pin.name ().empty ()) {
// the pin ID is zero-based and essentially the index, so we add 1 to make it compliant with the other IDs
return "$" + tl::to_string (pin.id () + 1);
} else {
return tl::to_word_or_quoted_string (pin.name ());
}
}
std::string Netlist::to_string () const
{
std::string res;
for (db::Netlist::const_circuit_iterator c = begin_circuits (); c != end_circuits (); ++c) {
std::string ps;
for (db::Circuit::const_pin_iterator p = c->begin_pins (); p != c->end_pins (); ++p) {
if (! ps.empty ()) {
ps += ",";
}
ps += pin2string (*p) + "=" + net2string (c->net_for_pin (p->id ()));
}
res += std::string ("Circuit ") + c->name () + " (" + ps + "):\n";
#if 0 // for debugging
for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) {
res += " N" + net_name (n.operator-> ()) + " pins=" + tl::to_string (n->pin_count ()) + " terminals=" + tl::to_string (n->terminal_count ()) + "\n";
}
#endif
for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) {
std::string ts;
const std::vector<db::DeviceTerminalDefinition> &td = d->device_class ()->terminal_definitions ();
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
if (t != td.begin ()) {
ts += ",";
}
ts += t->name () + "=" + net2string (d->net_for_terminal (t->id ()));
}
std::string ps;
const std::vector<db::DeviceParameterDefinition> &pd = d->device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator p = pd.begin (); p != pd.end (); ++p) {
if (p != pd.begin ()) {
ps += ",";
}
ps += p->name () + "=" + tl::to_string (d->parameter_value (p->id ()));
}
res += std::string (" D") + d->device_class ()->name () + " " + device2string (*d) + " (" + ts + ") [" + ps + "]\n";
}
for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) {
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 += ",";
}
const db::Pin &pin = *p;
ps += pin2string (pin) + "=" + net2string (subcircuit.net_for_pin (pin.id ()));
}
res += std::string (" X") + circuit->name () + " " + subcircuit2string (*sc) + " (" + ps + ")\n";
}
}
return res;
}
}

View File

@ -301,6 +301,11 @@ public:
*/
Net ();
/**
* @brief Creates a empty net with the give name
*/
Net (const std::string &name);
/**
* @brief Copy constructor
*/
@ -1374,8 +1379,8 @@ private:
void translate_circuits (const std::map<const Circuit *, Circuit *> &map);
void translate_device_classes (const std::map<const DeviceClass *, DeviceClass *> &map);
void set_netlist (Netlist *netlist);
void combine_parallel_devices (const db::DeviceClass &cls);
void combine_serial_devices (const db::DeviceClass &cls);
bool combine_parallel_devices (const db::DeviceClass &cls);
bool combine_serial_devices (const db::DeviceClass &cls);
void validate_device_id_table ();
void invalidate_device_id_table ();
@ -1806,6 +1811,13 @@ public:
*/
void clear ();
/**
* @brief Returns a string representation of the netlist
*
* This method is basically intended to testing.
*/
std::string to_string () const;
/**
* @brief Starts a sequence of operations during which topology updates are not desired
*

View File

@ -68,6 +68,11 @@ bool DeviceClassTwoTerminalDevice::combine_devices (Device *a, Device *b) const
// ------------------------------------------------------------------------------------
// DeviceClassResistor implementation
DB_PUBLIC size_t DeviceClassResistor::param_id_R = 0;
DB_PUBLIC size_t DeviceClassResistor::terminal_id_A = 0;
DB_PUBLIC size_t DeviceClassResistor::terminal_id_B = 1;
DeviceClassResistor::DeviceClassResistor ()
{
add_terminal_definition (db::DeviceTerminalDefinition ("A", "Terminal A"));

View File

@ -60,6 +60,11 @@ public:
return new DeviceClassResistor (*this);
}
static size_t param_id_R;
static size_t terminal_id_A;
static size_t terminal_id_B;
virtual void parallel (Device *a, Device *b) const;
virtual void serial (Device *a, Device *b) const;
};

View File

@ -105,6 +105,8 @@ Class<db::Device> decl_dbDevice ("db", "Device",
"Devices connect to nets through the \\Device#connect_terminal method. "
"Device terminals can be disconnected using \\Device#disconnect_terminal.\n"
"\n"
"Device objects are created inside a circuit with \\Circuit#create_device.\n"
"\n"
"This class has been added in version 0.26."
);
@ -177,6 +179,8 @@ Class<db::SubCircuit> decl_dbSubCircuit ("db", "SubCircuit",
"Subcircuits connect to nets through the \\SubCircuit#connect_pin method. "
"SubCircuit pins can be disconnected using \\SubCircuit#disconnect_pin.\n"
"\n"
"Subcircuit objects are created inside a circuit with \\Circuit#create_subcircuit.\n"
"\n"
"This class has been added in version 0.26."
);
@ -286,6 +290,8 @@ Class<db::Net> decl_dbNet ("db", "Net",
"pin or subcircuits of outgoing pins of the circuit the net lives in. "
"Terminals are connections made to specific terminals of devices.\n"
"\n"
"Net objects are created inside a circuit with \\Circuit#create_net.\n"
"\n"
"To connect a net to an outgoing pin of a circuit, use \\Circuit#connect_pin, to "
"disconnect a net from an outgoing pin use \\Circuit#disconnect_pin. "
"To connect a net to a pin of a subcircuit, use \\SubCircuit#connect_pin, to "
@ -840,6 +846,10 @@ Class<db::Netlist> decl_dbNetlist ("db", "Netlist",
gsi::iterator ("each_device_class", (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::begin_device_classes, (db::Netlist::device_class_iterator (db::Netlist::*) ()) &db::Netlist::end_device_classes,
"@brief Iterates over the device classes of the netlist"
) +
gsi::method ("to_s", &db::Netlist::to_string,
"@brief Converts the netlist to a string representation.\n"
"This method is intended for test purposes mainly."
) +
gsi::method ("combine_devices", &db::Netlist::combine_devices,
"@brief Combines devices where possible\n"
"This method will combine devices that can be combined according "
@ -847,6 +857,17 @@ Class<db::Netlist> decl_dbNetlist ("db", "Netlist",
"For example, serial or parallel resistors can be combined into "
"a single resistor.\n"
) +
gsi::method ("make_top_level_pins", &db::Netlist::make_top_level_pins,
"@brief Creates pins for top-level circuits.\n"
"This method will turn all named nets of top-level circuits (such that are not "
"referenced by subcircuits) into pins. This method can be used before purge to "
"avoid that purge will remove nets which are directly connecting to subcircuits."
) +
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."
) +
gsi::method ("purge_nets", &db::Netlist::purge_nets,
"@brief Purges floating nets.\n"
"Floating nets can be created as effect of reconnections of devices or pins. "

View File

@ -0,0 +1,555 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2018 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "dbNetlistDeviceClasses.h"
#include "dbNetlist.h"
#include "tlUnitTest.h"
#include <memory>
#include <limits>
TEST(1_SerialResistors)
{
db::DeviceClassResistor *res = new db::DeviceClassResistor ();
db::Netlist nl;
nl.add_device_class (res);
db::Device *r1 = new db::Device (res, "r1");
r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0);
db::Device *r2 = new db::Device (res, "r2");
r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0);
db::Circuit *circuit = new db::Circuit ();
nl.add_circuit (circuit);
db::Pin pin_a = circuit->add_pin (db::Pin ("A"));
db::Pin pin_b = circuit->add_pin (db::Pin ("B"));
circuit->add_device (r1);
circuit->add_device (r2);
db::Net *n1 = new db::Net ("n1");
circuit->add_net (n1);
circuit->connect_pin (pin_a.id (), n1);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1);
db::Net *n2 = new db::Net ("n2");
circuit->add_net (n2);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2);
db::Net *n3 = new db::Net ("n3");
circuit->add_net (n3);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3);
circuit->connect_pin (pin_b.id (), n3);
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n3):\n"
" D r1 (A=n1,B=n2) [R=1]\n"
" D r2 (A=n2,B=n3) [R=3]\n"
);
nl.combine_devices ();
nl.purge ();
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n3):\n"
" D r1 (A=n1,B=n3) [R=4]\n"
);
};
TEST(1_SerialResistors1Swapped)
{
db::DeviceClassResistor *res = new db::DeviceClassResistor ();
db::Netlist nl;
nl.add_device_class (res);
db::Device *r1 = new db::Device (res, "r1");
r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0);
db::Device *r2 = new db::Device (res, "r2");
r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0);
db::Circuit *circuit = new db::Circuit ();
nl.add_circuit (circuit);
db::Pin pin_a = circuit->add_pin (db::Pin ("A"));
db::Pin pin_b = circuit->add_pin (db::Pin ("B"));
circuit->add_device (r1);
circuit->add_device (r2);
db::Net *n1 = new db::Net ("n1");
circuit->add_net (n1);
circuit->connect_pin (pin_a.id (), n1);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1);
db::Net *n2 = new db::Net ("n2");
circuit->add_net (n2);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2);
db::Net *n3 = new db::Net ("n3");
circuit->add_net (n3);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n3);
circuit->connect_pin (pin_b.id (), n3);
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n3):\n"
" D r1 (A=n1,B=n2) [R=1]\n"
" D r2 (A=n3,B=n2) [R=3]\n"
);
nl.combine_devices ();
nl.purge ();
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n3):\n"
" D r1 (A=n1,B=n3) [R=4]\n"
);
};
TEST(1_SerialResistors1OtherSwapped)
{
db::DeviceClassResistor *res = new db::DeviceClassResistor ();
db::Netlist nl;
nl.add_device_class (res);
db::Device *r1 = new db::Device (res, "r1");
r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0);
db::Device *r2 = new db::Device (res, "r2");
r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0);
db::Circuit *circuit = new db::Circuit ();
nl.add_circuit (circuit);
db::Pin pin_a = circuit->add_pin (db::Pin ("A"));
db::Pin pin_b = circuit->add_pin (db::Pin ("B"));
circuit->add_device (r1);
circuit->add_device (r2);
db::Net *n1 = new db::Net ("n1");
circuit->add_net (n1);
circuit->connect_pin (pin_a.id (), n1);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1);
db::Net *n2 = new db::Net ("n2");
circuit->add_net (n2);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2);
db::Net *n3 = new db::Net ("n3");
circuit->add_net (n3);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3);
circuit->connect_pin (pin_b.id (), n3);
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n3):\n"
" D r1 (A=n2,B=n1) [R=1]\n"
" D r2 (A=n2,B=n3) [R=3]\n"
);
nl.combine_devices ();
nl.purge ();
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n3):\n"
" D r1 (A=n3,B=n1) [R=4]\n"
);
};
TEST(1_SerialResistors2Swapped)
{
db::DeviceClassResistor *res = new db::DeviceClassResistor ();
db::Netlist nl;
nl.add_device_class (res);
db::Device *r1 = new db::Device (res, "r1");
r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0);
db::Device *r2 = new db::Device (res, "r2");
r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0);
db::Circuit *circuit = new db::Circuit ();
nl.add_circuit (circuit);
db::Pin pin_a = circuit->add_pin (db::Pin ("A"));
db::Pin pin_b = circuit->add_pin (db::Pin ("B"));
circuit->add_device (r1);
circuit->add_device (r2);
db::Net *n1 = new db::Net ("n1");
circuit->add_net (n1);
circuit->connect_pin (pin_a.id (), n1);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1);
db::Net *n2 = new db::Net ("n2");
circuit->add_net (n2);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2);
db::Net *n3 = new db::Net ("n3");
circuit->add_net (n3);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n3);
circuit->connect_pin (pin_b.id (), n3);
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n3):\n"
" D r1 (A=n2,B=n1) [R=1]\n"
" D r2 (A=n3,B=n2) [R=3]\n"
);
nl.combine_devices ();
nl.purge ();
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n3):\n"
" D r1 (A=n3,B=n1) [R=4]\n"
);
};
TEST(1_SerialResistorsNoCombination)
{
db::DeviceClassResistor *res = new db::DeviceClassResistor ();
db::Netlist nl;
nl.add_device_class (res);
db::Device *r1 = new db::Device (res, "r1");
r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0);
db::Device *r2 = new db::Device (res, "r2");
r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0);
db::Circuit *circuit = new db::Circuit ();
nl.add_circuit (circuit);
db::Pin pin_a = circuit->add_pin (db::Pin ("A"));
db::Pin pin_b = circuit->add_pin (db::Pin ("B"));
db::Pin pin_c = circuit->add_pin (db::Pin ("C"));
circuit->add_device (r1);
circuit->add_device (r2);
db::Net *n1 = new db::Net ("n1");
circuit->add_net (n1);
circuit->connect_pin (pin_a.id (), n1);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1);
db::Net *n2 = new db::Net ("n2");
circuit->add_net (n2);
circuit->connect_pin (pin_c.id (), n2); // prevents combination
r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2);
db::Net *n3 = new db::Net ("n3");
circuit->add_net (n3);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3);
circuit->connect_pin (pin_b.id (), n3);
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n3,C=n2):\n"
" D r1 (A=n1,B=n2) [R=1]\n"
" D r2 (A=n2,B=n3) [R=3]\n"
);
nl.combine_devices ();
nl.purge ();
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n3,C=n2):\n"
" D r1 (A=n1,B=n2) [R=1]\n"
" D r2 (A=n2,B=n3) [R=3]\n"
);
};
TEST(1_ParallelResistors)
{
db::DeviceClassResistor *res = new db::DeviceClassResistor ();
db::Netlist nl;
nl.add_device_class (res);
db::Device *r1 = new db::Device (res, "r1");
r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 2.0);
db::Device *r2 = new db::Device (res, "r2");
r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0);
db::Circuit *circuit = new db::Circuit ();
nl.add_circuit (circuit);
db::Pin pin_a = circuit->add_pin (db::Pin ("A"));
db::Pin pin_b = circuit->add_pin (db::Pin ("B"));
circuit->add_device (r1);
circuit->add_device (r2);
db::Net *n1 = new db::Net ("n1");
circuit->add_net (n1);
circuit->connect_pin (pin_a.id (), n1);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1);
db::Net *n2 = new db::Net ("n2");
circuit->add_net (n2);
circuit->connect_pin (pin_b.id (), n2);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2);
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n2):\n"
" D r1 (A=n1,B=n2) [R=2]\n"
" D r2 (A=n1,B=n2) [R=3]\n"
);
nl.combine_devices ();
nl.purge ();
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n2):\n"
" D r1 (A=n1,B=n2) [R=1.2]\n"
);
};
TEST(1_ParallelResistors1Swapped)
{
db::DeviceClassResistor *res = new db::DeviceClassResistor ();
db::Netlist nl;
nl.add_device_class (res);
db::Device *r1 = new db::Device (res, "r1");
r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 2.0);
db::Device *r2 = new db::Device (res, "r2");
r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0);
db::Circuit *circuit = new db::Circuit ();
nl.add_circuit (circuit);
db::Pin pin_a = circuit->add_pin (db::Pin ("A"));
db::Pin pin_b = circuit->add_pin (db::Pin ("B"));
circuit->add_device (r1);
circuit->add_device (r2);
db::Net *n1 = new db::Net ("n1");
circuit->add_net (n1);
circuit->connect_pin (pin_a.id (), n1);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1);
db::Net *n2 = new db::Net ("n2");
circuit->add_net (n2);
circuit->connect_pin (pin_b.id (), n2);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2);
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n2):\n"
" D r1 (A=n2,B=n1) [R=2]\n"
" D r2 (A=n1,B=n2) [R=3]\n"
);
nl.combine_devices ();
nl.purge ();
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n2):\n"
" D r1 (A=n2,B=n1) [R=1.2]\n"
);
};
TEST(1_ParallelResistors1OtherSwapped)
{
db::DeviceClassResistor *res = new db::DeviceClassResistor ();
db::Netlist nl;
nl.add_device_class (res);
db::Device *r1 = new db::Device (res, "r1");
r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 2.0);
db::Device *r2 = new db::Device (res, "r2");
r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0);
db::Circuit *circuit = new db::Circuit ();
nl.add_circuit (circuit);
db::Pin pin_a = circuit->add_pin (db::Pin ("A"));
db::Pin pin_b = circuit->add_pin (db::Pin ("B"));
circuit->add_device (r1);
circuit->add_device (r2);
db::Net *n1 = new db::Net ("n1");
circuit->add_net (n1);
circuit->connect_pin (pin_a.id (), n1);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1);
db::Net *n2 = new db::Net ("n2");
circuit->add_net (n2);
circuit->connect_pin (pin_b.id (), n2);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2);
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n2):\n"
" D r1 (A=n1,B=n2) [R=2]\n"
" D r2 (A=n2,B=n1) [R=3]\n"
);
nl.combine_devices ();
nl.purge ();
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n2):\n"
" D r1 (A=n1,B=n2) [R=1.2]\n"
);
};
TEST(1_ParallelResistors2Swapped)
{
db::DeviceClassResistor *res = new db::DeviceClassResistor ();
db::Netlist nl;
nl.add_device_class (res);
db::Device *r1 = new db::Device (res, "r1");
r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 2.0);
db::Device *r2 = new db::Device (res, "r2");
r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0);
db::Circuit *circuit = new db::Circuit ();
nl.add_circuit (circuit);
db::Pin pin_a = circuit->add_pin (db::Pin ("A"));
db::Pin pin_b = circuit->add_pin (db::Pin ("B"));
circuit->add_device (r1);
circuit->add_device (r2);
db::Net *n1 = new db::Net ("n1");
circuit->add_net (n1);
circuit->connect_pin (pin_a.id (), n1);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n1);
db::Net *n2 = new db::Net ("n2");
circuit->add_net (n2);
circuit->connect_pin (pin_b.id (), n2);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2);
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n2):\n"
" D r1 (A=n2,B=n1) [R=2]\n"
" D r2 (A=n2,B=n1) [R=3]\n"
);
nl.combine_devices ();
nl.purge ();
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n2):\n"
" D r1 (A=n2,B=n1) [R=1.2]\n"
);
};
TEST(1_ComplexRegistorCombination)
{
db::DeviceClassResistor *res = new db::DeviceClassResistor ();
db::Netlist nl;
nl.add_device_class (res);
/**
* (n2)
* +--[ r1=1.0 ]--+--[ r2=1.0 ]--+
* | |
* <a> --x (n1) (n3) x--[ r4=0.8 ]--+-- <b>
* | | (n4)
* +----------[ r3=3.0 ]---------+
*/
db::Device *r1 = new db::Device (res, "r1");
r1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0);
db::Device *r2 = new db::Device (res, "r2");
r2->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.0);
db::Device *r3 = new db::Device (res, "r3");
r3->set_parameter_value (db::DeviceClassResistor::param_id_R, 3.0);
db::Device *r4 = new db::Device (res, "r4");
r4->set_parameter_value (db::DeviceClassResistor::param_id_R, 0.8);
db::Circuit *circuit = new db::Circuit ();
nl.add_circuit (circuit);
db::Pin pin_a = circuit->add_pin (db::Pin ("A"));
db::Pin pin_b = circuit->add_pin (db::Pin ("B"));
circuit->add_device (r1);
circuit->add_device (r2);
circuit->add_device (r3);
circuit->add_device (r4);
db::Net *n1 = new db::Net ("n1");
circuit->add_net (n1);
circuit->connect_pin (pin_a.id (), n1);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1);
r3->connect_terminal (db::DeviceClassResistor::terminal_id_A, n1);
db::Net *n2 = new db::Net ("n2");
circuit->add_net (n2);
r1->connect_terminal (db::DeviceClassResistor::terminal_id_B, n2);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_A, n2);
db::Net *n3 = new db::Net ("n3");
circuit->add_net (n3);
r2->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3);
r3->connect_terminal (db::DeviceClassResistor::terminal_id_B, n3);
r4->connect_terminal (db::DeviceClassResistor::terminal_id_A, n3);
db::Net *n4 = new db::Net ("n4");
circuit->add_net (n4);
circuit->connect_pin (pin_b.id (), n4);
r4->connect_terminal (db::DeviceClassResistor::terminal_id_B, n4);
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n4):\n"
" D r1 (A=n1,B=n2) [R=1]\n"
" D r2 (A=n2,B=n3) [R=1]\n"
" D r3 (A=n1,B=n3) [R=3]\n"
" D r4 (A=n3,B=n4) [R=0.8]\n"
);
nl.combine_devices ();
nl.purge ();
EXPECT_EQ (nl.to_string (),
"Circuit (A=n1,B=n4):\n"
" D r4 (A=n1,B=n4) [R=2]\n"
);
};

View File

@ -49,11 +49,6 @@ static unsigned int layer_of (const db::Region &region)
return db::DeepLayer (region).layer ();
}
static std::string net_name (const db::Net *net)
{
return net ? net->expanded_name () : "(null)";
}
static std::string device_name (const db::Device &device)
{
if (device.name ().empty ()) {
@ -63,26 +58,6 @@ static std::string device_name (const db::Device &device)
}
}
static std::string subcircuit_name (const db::SubCircuit &subcircuit)
{
if (subcircuit.name ().empty ()) {
return "$" + tl::to_string (subcircuit.id ());
} else {
return subcircuit.name ();
}
}
static std::string pin_name (const db::Pin &pin)
{
if (pin.name ().empty ()) {
// the pin ID is zero-based and essentially the index, so we add 1 to make it compliant with the other IDs
return "$" + tl::to_string (pin.id () + 1);
} else {
return pin.name ();
}
}
class MOSFETExtractor
: public db::NetlistDeviceExtractor
{
@ -229,9 +204,7 @@ static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_la
return lid;
}
// @@@ TODO: move this somewhere else
static void dump_nets (const db::Netlist &nl, const db::hier_clusters<db::PolygonRef> &clusters, db::Layout &ly, const std::map<unsigned int, unsigned int> &lmap, const db::CellMapping &cmap)
static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters<db::PolygonRef> &clusters, db::Layout &ly, const std::map<unsigned int, unsigned int> &lmap, const db::CellMapping &cmap)
{
for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) {
@ -248,7 +221,7 @@ static void dump_nets (const db::Netlist &nl, const db::hier_clusters<db::Polygo
if (any_shapes) {
std::string nn = "NET_" + c->name () + "_" + net_name (n.operator-> ());
std::string nn = "NET_" + c->name () + "_" + n->expanded_name ();
db::Cell &net_cell = ly.cell (ly.add_cell (nn.c_str ()));
cell.insert (db::CellInstArray (db::CellInst (net_cell.cell_index ()), db::Trans ()));
@ -266,66 +239,6 @@ static void dump_nets (const db::Netlist &nl, const db::hier_clusters<db::Polygo
}
}
static std::string netlist2string (const db::Netlist &nl)
{
std::string res;
for (db::Netlist::const_circuit_iterator c = nl.begin_circuits (); c != nl.end_circuits (); ++c) {
std::string ps;
for (db::Circuit::const_pin_iterator p = c->begin_pins (); p != c->end_pins (); ++p) {
if (! ps.empty ()) {
ps += ",";
}
ps += pin_name (*p) + "=" + net_name (c->net_for_pin (p->id ()));
}
res += std::string ("Circuit ") + c->name () + " (" + ps + "):\n";
#if 0 // for debugging
for (db::Circuit::const_net_iterator n = c->begin_nets (); n != c->end_nets (); ++n) {
res += " N" + net_name (n.operator-> ()) + " pins=" + tl::to_string (n->pin_count ()) + " terminals=" + tl::to_string (n->terminal_count ()) + "\n";
}
#endif
for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) {
std::string ts;
const std::vector<db::DeviceTerminalDefinition> &td = d->device_class ()->terminal_definitions ();
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
if (t != td.begin ()) {
ts += ",";
}
ts += t->name () + "=" + net_name (d->net_for_terminal (t->id ()));
}
std::string ps;
const std::vector<db::DeviceParameterDefinition> &pd = d->device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator p = pd.begin (); p != pd.end (); ++p) {
if (p != pd.begin ()) {
ps += ",";
}
ps += p->name () + "=" + tl::to_string (d->parameter_value (p->id ()));
}
res += std::string (" D") + d->device_class ()->name () + " " + device_name (*d) + " (" + ts + ") [" + ps + "]\n";
}
for (db::Circuit::const_subcircuit_iterator sc = c->begin_subcircuits (); sc != c->end_subcircuits (); ++sc) {
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 += ",";
}
const db::Pin &pin = *p;
ps += pin_name (pin) + "=" + net_name (subcircuit.net_for_pin (pin.id ()));
}
res += std::string (" X") + circuit->name () + " " + subcircuit_name (*sc) + " (" + ps + ")\n";
}
}
return res;
}
TEST(1_DeviceAndNetExtraction)
{
db::Layout ly;
@ -475,10 +388,10 @@ TEST(1_DeviceAndNetExtraction)
// write nets to layout
db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ());
dump_nets (nl, net_ex.clusters (), ly, dump_map, cm);
dump_nets_to_layout (nl, net_ex.clusters (), ly, dump_map, cm);
// compare netlist as string
EXPECT_EQ (netlist2string (nl),
EXPECT_EQ (nl.to_string (),
"Circuit RINGO ():\n"
" XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n"
" XINV2 $2 (IN=FB,$2=$I38,OUT=$I19,$4=VSS,$5=VDD)\n"
@ -510,7 +423,7 @@ TEST(1_DeviceAndNetExtraction)
nl.purge ();
// compare netlist as string
EXPECT_EQ (netlist2string (nl),
EXPECT_EQ (nl.to_string (),
"Circuit RINGO (FB=FB,OSC=OSC,VSS=VSS,VDD=VDD):\n"
" XINV2 $1 (IN=$I8,$2=FB,OUT=OSC,$4=VSS,$5=VDD)\n"
" XINV2 $2 (IN=FB,$2=(null),OUT=$I19,$4=VSS,$5=VDD)\n"

View File

@ -62,7 +62,8 @@ SOURCES = \
dbNetlistPropertyTests.cc \
dbNetlistTests.cc \
dbNetlistExtractorTests.cc \
dbNetlistDeviceExtractorTests.cc
dbNetlistDeviceExtractorTests.cc \
dbNetlistDeviceClassesTests.cc
INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC
DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC

View File

@ -561,6 +561,11 @@ class DBNetlist_TestClass < TestBase
assert_equal(d1.parameter(0), -0.5)
assert_equal(d1.parameter(1), 42)
assert_equal(nl.to_s, <<END)
Circuit C ():
DDC $1 () [U=-0.5,V=42]
END
end
def test_10_NetlistTopology