Implemented SPICE writer + tests.

This commit is contained in:
Matthias Koefferlein 2019-01-31 00:07:10 +01:00
parent 7d06ea83c1
commit 4068478887
23 changed files with 2541 additions and 11 deletions

View File

@ -164,7 +164,9 @@ SOURCES = \
dbLayoutToNetlistFormatDefs.cc \ dbLayoutToNetlistFormatDefs.cc \
dbDeviceAbstract.cc \ dbDeviceAbstract.cc \
dbLocalOperationUtils.cc \ dbLocalOperationUtils.cc \
gsiDeclDbDeepShapeStore.cc gsiDeclDbDeepShapeStore.cc \
dbNetlistSpiceWriter.cc \
dbNetlistWriter.cc
HEADERS = \ HEADERS = \
dbArray.h \ dbArray.h \
@ -293,7 +295,9 @@ HEADERS = \
dbLayoutToNetlistFormatDefs.h \ dbLayoutToNetlistFormatDefs.h \
dbDeviceAbstract.h \ dbDeviceAbstract.h \
dbLocalOperationUtils.h \ dbLocalOperationUtils.h \
dbDeepRegion.h dbDeepRegion.h \
dbNetlistSpiceWriter.h \
dbNetlistWriter.h
!equals(HAVE_QT, "0") { !equals(HAVE_QT, "0") {

View File

@ -0,0 +1,402 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbNetlistSpiceWriter.h"
#include "dbNetlist.h"
#include "dbNetlistDeviceClasses.h"
#include "tlStream.h"
#include <sstream>
namespace db
{
// --------------------------------------------------------------------------------
NetlistSpiceWriterDelegate::NetlistSpiceWriterDelegate ()
: mp_writer (0)
{
// .. nothing yet ..
}
NetlistSpiceWriterDelegate::~NetlistSpiceWriterDelegate ()
{
// .. nothing yet ..
}
std::string NetlistSpiceWriterDelegate::net_to_string (const db::Net *net) const
{
tl_assert (mp_writer != 0);
return mp_writer->net_to_string (net);
}
std::string NetlistSpiceWriterDelegate::format_name (const std::string &name) const
{
tl_assert (mp_writer != 0);
return mp_writer->format_name (name);
}
void NetlistSpiceWriterDelegate::emit_line (const std::string &line) const
{
tl_assert (mp_writer != 0);
mp_writer->emit_line (line);
}
void NetlistSpiceWriterDelegate::emit_comment (const std::string &comment) const
{
tl_assert (mp_writer != 0);
mp_writer->emit_comment (comment);
}
void NetlistSpiceWriterDelegate::attach_writer (NetlistSpiceWriter *writer)
{
mp_writer = writer;
}
void NetlistSpiceWriterDelegate::write_header () const
{
// .. nothing yet ..
}
void NetlistSpiceWriterDelegate::write_device_intro (const db::DeviceClass &) const
{
// .. nothing yet ..
}
void NetlistSpiceWriterDelegate::write_device (const db::Device &dev) const
{
const db::DeviceClass *dc = dev.device_class ();
const db::DeviceClassCapacitor *cap = dynamic_cast<const db::DeviceClassCapacitor *> (dc);
const db::DeviceClassInductor *ind = dynamic_cast<const db::DeviceClassInductor *> (dc);
const db::DeviceClassResistor *res = dynamic_cast<const db::DeviceClassResistor *> (dc);
const db::DeviceClassDiode *diode = dynamic_cast<const db::DeviceClassDiode *> (dc);
const db::DeviceClassMOS3Transistor *mos3 = dynamic_cast<const db::DeviceClassMOS3Transistor *> (dc);
const db::DeviceClassMOS4Transistor *mos4 = dynamic_cast<const db::DeviceClassMOS4Transistor *> (dc);
std::ostringstream os;
if (cap) {
os << "C";
os << format_name (dev.expanded_name ());
os << format_terminals (dev);
os << " ";
os << tl::sprintf ("%.12g", dev.parameter_value (db::DeviceClassCapacitor::param_id_C));
} else if (ind) {
os << "L";
os << format_name (dev.expanded_name ());
os << format_terminals (dev);
os << " ";
os << tl::sprintf ("%.12g", dev.parameter_value (db::DeviceClassInductor::param_id_L));
} else if (res) {
os << "R";
os << format_name (dev.expanded_name ());
os << format_terminals (dev);
os << " ";
os << tl::sprintf ("%.12g", dev.parameter_value (db::DeviceClassResistor::param_id_R));
} else if (diode) {
os << "D";
os << format_name (dev.expanded_name ());
os << format_terminals (dev);
// Use "D" + device class name for the model
os << " D";
os << format_name (dev.device_class ()->name ());
} else if (mos3 || mos4) {
os << "M";
os << format_name (dev.expanded_name ());
os << format_terminals (dev);
if (mos3) {
// we assume for the MOS3 type the bulk is connected to Source
os << " ";
os << net_to_string (dev.net_for_terminal (db::DeviceClassMOS3Transistor::terminal_id_S));
}
// Use "M" + device class name for the model
os << " M";
os << format_name (dev.device_class ()->name ());
os << " L=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_L));
os << " W=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_W));
os << " AS=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AS));
os << " AD=" << tl::sprintf ("%.12gU", dev.parameter_value (db::DeviceClassMOS3Transistor::param_id_AD));
} else {
// Write unknown devices as subcircuits (CAUTION: potential name clash)
os << "XD_" << format_name (dev.expanded_name ());
os << format_terminals (dev);
os << " ";
os << format_name (dev.device_class ()->name ());
os << " PARAMS:";
os << format_params (dev);
}
emit_line (os.str ());
}
std::string NetlistSpiceWriterDelegate::format_terminals (const db::Device &dev) const
{
std::ostringstream os;
const std::vector<db::DeviceTerminalDefinition> &td = dev.device_class ()->terminal_definitions ();
for (std::vector<db::DeviceTerminalDefinition>::const_iterator i = td.begin (); i != td.end (); ++i) {
os << " " << net_to_string (dev.net_for_terminal (i->id ()));
}
return os.str ();
}
std::string NetlistSpiceWriterDelegate::format_params (const db::Device &dev) const
{
std::ostringstream os;
const std::vector<db::DeviceParameterDefinition> &pd = dev.device_class ()->parameter_definitions ();
for (std::vector<db::DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
os << " " << i->name () << "=" << tl::to_string (dev.parameter_value (i->id ()));
}
return os.str ();
}
// --------------------------------------------------------------------------------
NetlistSpiceWriter::NetlistSpiceWriter (NetlistSpiceWriterDelegate *delegate)
: mp_netlist (0), mp_stream (0), mp_delegate (delegate)
{
static NetlistSpiceWriterDelegate std_delegate;
if (! delegate) {
mp_delegate.reset (&std_delegate);
}
}
NetlistSpiceWriter::~NetlistSpiceWriter ()
{
// .. nothing yet ..
}
void NetlistSpiceWriter::write (tl::OutputStream &stream, const db::Netlist &netlist, const std::string &description)
{
mp_stream = &stream;
mp_netlist = &netlist;
mp_delegate->attach_writer (this);
try {
do_write (description);
mp_stream = 0;
mp_netlist = 0;
mp_delegate->attach_writer (0);
} catch (...) {
mp_stream = 0;
mp_netlist = 0;
mp_delegate->attach_writer (0);
throw;
}
}
std::string NetlistSpiceWriter::net_to_string (const db::Net *net) const
{
std::map<const db::Net *, size_t>::const_iterator n = m_net_to_spice_id.find (net);
if (! net || n == m_net_to_spice_id.end ()) {
// TODO: this should assert or similar
return "0";
} else {
return tl::to_string (n->second);
}
}
void NetlistSpiceWriter::emit_line (const std::string &line) const
{
tl_assert (mp_stream != 0);
int max_length = 80;
bool first = true;
const char *cp = line.c_str ();
do {
const char *cpn = cp;
const char *cspc = 0;
int c = 0;
int l = first ? max_length : max_length - 2;
while (*cpn && (c < l || ! cspc)) {
if (isspace (*cpn)) {
cspc = cpn;
}
++c;
++cpn;
}
if (! first) {
*mp_stream << "+ ";
}
if (! *cpn) {
*mp_stream << cp << "\n";
break;
} else {
while (*cp && (cp != cspc || ! cspc)) {
*mp_stream << *cp++;
}
*mp_stream << "\n";
}
first = false;
while (*cp && isspace (*cp)) {
++cp;
}
} while (*cp);
}
void NetlistSpiceWriter::emit_comment (const std::string &comment) const
{
tl_assert (mp_stream != 0);
// TODO: should do some line breaking or reduction for long lines
// or when lines contain newlines
*mp_stream << "* " << comment << "\n";
}
std::string NetlistSpiceWriter::format_name (const std::string &s) const
{
// TODO: escape or replace special chars
return s;
}
void NetlistSpiceWriter::do_write (const std::string &description)
{
if (! description.empty ()) {
emit_comment (description);
}
mp_delegate->write_header ();
for (db::Netlist::const_device_class_iterator dc = mp_netlist->begin_device_classes (); dc != mp_netlist->end_device_classes (); ++dc) {
mp_delegate->write_device_intro (*dc);
}
for (db::Netlist::const_top_down_circuit_iterator c = mp_netlist->begin_top_down (); c != mp_netlist->end_top_down (); ++c) {
const db::Circuit &circuit = **c;
// assign internal node numbers to the nets
m_net_to_spice_id.clear ();
size_t nid = 0;
for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) {
m_net_to_spice_id.insert (std::make_pair (n.operator-> (), ++nid));
}
write_circuit_header (circuit);
for (db::Circuit::const_subcircuit_iterator i = circuit.begin_subcircuits (); i != circuit.end_subcircuits (); ++i) {
write_subcircuit_call (*i);
}
for (db::Circuit::const_device_iterator i = circuit.begin_devices (); i != circuit.end_devices (); ++i) {
// TODO: make this configurable?
std::string comment = "device instance " + i->expanded_name () + " " + i->position ().to_string () + " " + i->device_class ()->name ();
emit_comment (comment);
mp_delegate->write_device (*i);
}
write_circuit_end (circuit);
}
}
void NetlistSpiceWriter::write_subcircuit_call (const db::SubCircuit &subcircuit) const
{
// TODO: make this configurable?
std::string comment = "cell instance " + subcircuit.expanded_name() + " " + subcircuit.trans ().to_string ();
emit_comment (comment);
std::ostringstream os;
os << "X";
os << format_name (subcircuit.expanded_name ());
for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p) {
os << " ";
os << net_to_string (subcircuit.net_for_pin (p->id ()));
}
os << " ";
os << format_name (subcircuit.circuit_ref ()->name ());
emit_line (os.str ());
}
void NetlistSpiceWriter::write_circuit_header (const db::Circuit &circuit) const
{
emit_line ("");
emit_comment ("cell " + circuit.name ());
for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) {
emit_comment ("pin " + p->name ());
}
std::ostringstream os;
os << ".SUBCKT ";
os << format_name (circuit.name ());
for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) {
os << " ";
os << net_to_string (circuit.net_for_pin (p->id ()));
}
emit_line (os.str ());
for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) {
if (! n->name ().empty ()) {
emit_comment ("net " + net_to_string (n.operator-> ()) + " " + n->name ());
}
}
}
void NetlistSpiceWriter::write_circuit_end (const db::Circuit &circuit) const
{
emit_line (".ENDS " + format_name (circuit.name ()));
}
}

View File

@ -0,0 +1,111 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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
*/
#ifndef HDR_dbNetlistSpiceWriter
#define HDR_dbNetlistSpiceWriter
#include "dbCommon.h"
#include "dbNetlistWriter.h"
#include "tlObject.h"
#include <string>
#include <map>
namespace db
{
class DeviceClass;
class Device;
class Net;
class NetlistSpiceWriter;
class Circuit;
class SubCircuit;
/**
* @brief A device writer delegate for the SPICE writer
*
* This delegate is supposed to provide the mapping of devices to parametrized SPICE subcircuits.
* It is generic, so it can be used for other cases of device mapping.
*/
class DB_PUBLIC NetlistSpiceWriterDelegate
: public tl::Object
{
public:
NetlistSpiceWriterDelegate ();
virtual ~NetlistSpiceWriterDelegate ();
virtual void write_header () const;
virtual void write_device_intro (const db::DeviceClass &cls) const;
virtual void write_device (const db::Device &dev) const;
protected:
std::string net_to_string (const db::Net *net) const;
void emit_line (const std::string &line) const;
void emit_comment (const std::string &comment) const;
std::string format_name (const std::string &s) const;
std::string format_terminals (const db::Device &dev) const;
std::string format_params (const db::Device &dev) const;
private:
friend class NetlistSpiceWriter;
NetlistSpiceWriter *mp_writer;
void attach_writer (NetlistSpiceWriter *writer);
};
/**
* @brief A SPICE format writer for netlists
*
* Specialization happens through the device writer delegate.
*/
class DB_PUBLIC NetlistSpiceWriter
: public NetlistWriter
{
public:
NetlistSpiceWriter (NetlistSpiceWriterDelegate *delegate = 0);
virtual ~NetlistSpiceWriter ();
virtual void write (tl::OutputStream &stream, const db::Netlist &netlist, const std::string &description);
private:
friend class NetlistSpiceWriterDelegate;
const db::Netlist *mp_netlist;
tl::OutputStream *mp_stream;
tl::weak_ptr<NetlistSpiceWriterDelegate> mp_delegate;
std::map<const db::Net *, size_t> m_net_to_spice_id;
void do_write (const std::string &description);
std::string net_to_string (const db::Net *net) const;
void emit_line (const std::string &line) const;
void emit_comment (const std::string &comment) const;
std::string format_name (const std::string &name) const;
void write_subcircuit_call (const db::SubCircuit &subcircuit) const;
void write_circuit_header (const db::Circuit &circuit) const;
void write_circuit_end (const db::Circuit &circuit) const;
};
}
#endif

View File

@ -0,0 +1,31 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbNetlistWriter.h"
namespace db
{
// .. nothing yet ..
}

View File

@ -0,0 +1,90 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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
*/
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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
*/
#ifndef HDR_dbNetlistWriter
#define HDR_dbNetlistWriter
#include "dbCommon.h"
#include "tlTypeTraits.h"
#include <string>
namespace tl
{
class OutputStream;
}
namespace db
{
class Netlist;
/**
* @brief A common base class for netlist writers
*/
class DB_PUBLIC NetlistWriter
{
public:
NetlistWriter () { }
virtual ~NetlistWriter () { }
virtual void write (tl::OutputStream &stream, const db::Netlist &netlist, const std::string &description = std::string ()) = 0;
};
}
namespace tl
{
template <>
struct tl::type_traits<db::NetlistWriter>
: public tl::type_traits<void>
{
typedef tl::false_tag has_default_constructor;
typedef tl::false_tag has_copy_constructor;
};
}
#endif

View File

@ -22,8 +22,11 @@
#include "gsiDecl.h" #include "gsiDecl.h"
#include "dbNetlist.h" #include "dbNetlist.h"
#include "dbNetlistWriter.h"
#include "dbNetlistSpiceWriter.h"
#include "tlException.h" #include "tlException.h"
#include "tlInternational.h" #include "tlInternational.h"
#include "tlStream.h"
namespace gsi namespace gsi
{ {
@ -86,6 +89,10 @@ Class<db::Device> decl_dbDevice ("db", "Device",
gsi::method ("name", &db::Device::name, gsi::method ("name", &db::Device::name,
"@brief Gets the name of the device.\n" "@brief Gets the name of the device.\n"
) + ) +
gsi::method ("expanded_name", &db::Device::expanded_name,
"@brief Gets the expanded name of the device.\n"
"The expanded name takes the name of the device. If the name is empty, the numeric ID will be used to build a name. "
) +
gsi::method ("net_for_terminal", (db::Net *(db::Device::*) (size_t)) &db::Device::net_for_terminal, gsi::arg ("terminal_id"), gsi::method ("net_for_terminal", (db::Net *(db::Device::*) (size_t)) &db::Device::net_for_terminal, gsi::arg ("terminal_id"),
"@brief Gets the net connected to the specified terminal.\n" "@brief Gets the net connected to the specified terminal.\n"
"If the terminal is not connected, nil is returned for the net." "If the terminal is not connected, nil is returned for the net."
@ -209,6 +216,10 @@ Class<db::SubCircuit> decl_dbSubCircuit ("db", "SubCircuit",
gsi::method ("name", &db::SubCircuit::name, gsi::method ("name", &db::SubCircuit::name,
"@brief Gets the name of the subcircuit.\n" "@brief Gets the name of the subcircuit.\n"
) + ) +
gsi::method ("expanded_name", &db::SubCircuit::expanded_name,
"@brief Gets the expanded name of the subcircuit.\n"
"The expanded name takes the name of the subcircuit. If the name is empty, the numeric ID will be used to build a name. "
) +
gsi::method ("net_for_pin", (db::Net *(db::SubCircuit::*) (size_t)) &db::SubCircuit::net_for_pin, gsi::arg ("pin_id"), gsi::method ("net_for_pin", (db::Net *(db::SubCircuit::*) (size_t)) &db::SubCircuit::net_for_pin, gsi::arg ("pin_id"),
"@brief Gets the net connected to the specified pin of the subcircuit.\n" "@brief Gets the net connected to the specified pin of the subcircuit.\n"
"If the pin is not connected, nil is returned for the net." "If the pin is not connected, nil is returned for the net."
@ -465,9 +476,15 @@ Class<db::DeviceClass> decl_dbDeviceClass ("db", "DeviceClass",
gsi::method ("name", &db::DeviceClass::name, gsi::method ("name", &db::DeviceClass::name,
"@brief Gets the name of the device class." "@brief Gets the name of the device class."
) + ) +
gsi::method ("name=", &db::DeviceClass::set_name, gsi::arg ("name"),
"@brief Sets the name of the device class."
) +
gsi::method ("description", &db::DeviceClass::description, gsi::method ("description", &db::DeviceClass::description,
"@brief Gets the description text of the device class." "@brief Gets the description text of the device class."
) + ) +
gsi::method ("description=", &db::DeviceClass::set_description, gsi::arg ("description"),
"@brief Sets the description of the device class."
) +
gsi::method ("netlist", (db::Netlist *(db::DeviceClass::*) ()) &db::DeviceClass::netlist, gsi::method ("netlist", (db::Netlist *(db::DeviceClass::*) ()) &db::DeviceClass::netlist,
"@brief Gets the netlist the device class lives in." "@brief Gets the netlist the device class lives in."
) + ) +
@ -617,12 +634,6 @@ Class<GenericDeviceClass> decl_GenericDeviceClass (decl_dbDeviceClass, "db", "Ge
gsi::method ("clear_parameters", &GenericDeviceClass::clear_parameter_definitions, gsi::method ("clear_parameters", &GenericDeviceClass::clear_parameter_definitions,
"@brief Clears the list of parameters\n" "@brief Clears the list of parameters\n"
) + ) +
gsi::method ("name=", &GenericDeviceClass::set_name, gsi::arg ("name"),
"@brief Sets the name of the device\n"
) +
gsi::method ("description=", &GenericDeviceClass::set_description, gsi::arg ("description"),
"@brief Sets the description of the device\n"
) +
gsi::callback ("combine_devices", &GenericDeviceClass::combine_devices, &GenericDeviceClass::cb_combine_devices, gsi::arg ("a"), gsi::arg ("b"), gsi::callback ("combine_devices", &GenericDeviceClass::combine_devices, &GenericDeviceClass::cb_combine_devices, gsi::arg ("a"), gsi::arg ("b"),
"@brief Combines two devices.\n" "@brief Combines two devices.\n"
"This method shall test, whether the two devices can be combined. Both devices " "This method shall test, whether the two devices can be combined. Both devices "
@ -892,18 +903,27 @@ Class<db::Circuit> decl_dbCircuit ("db", "Circuit",
static void add_circuit (db::Netlist *nl, db::Circuit *c) static void add_circuit (db::Netlist *nl, db::Circuit *c)
{ {
tl_assert (c != 0);
c->keep (); c->keep ();
nl->add_circuit (c); nl->add_circuit (c);
} }
static void add_device_class (db::Netlist *nl, db::DeviceClass *cl) static void add_device_class (db::Netlist *nl, db::DeviceClass *cl)
{ {
tl_assert (cl != 0);
cl->keep (); cl->keep ();
nl->add_device_class (cl); nl->add_device_class (cl);
} }
static void write_netlist (const db::Netlist *nl, const std::string &file, db::NetlistWriter *writer, const std::string &description)
{
tl_assert (writer != 0);
tl::OutputStream os (file);
writer->write (os, *nl, description);
}
Class<db::Netlist> decl_dbNetlist ("db", "Netlist", Class<db::Netlist> decl_dbNetlist ("db", "Netlist",
gsi::method_ext ("add", &gsi::add_circuit, gsi::method_ext ("add", &gsi::add_circuit, gsi::arg ("circuit"),
"@brief Adds the circuit to the netlist\n" "@brief Adds the circuit to the netlist\n"
"This method will add the given circuit object to the netlist. " "This method will add the given circuit object to the netlist. "
"After the circuit has been added, it will be owned by the netlist." "After the circuit has been added, it will be owned by the netlist."
@ -938,7 +958,7 @@ Class<db::Netlist> decl_dbNetlist ("db", "Netlist",
gsi::iterator ("each_circuit", (db::Netlist::circuit_iterator (db::Netlist::*) ()) &db::Netlist::begin_circuits, (db::Netlist::circuit_iterator (db::Netlist::*) ()) &db::Netlist::end_circuits, gsi::iterator ("each_circuit", (db::Netlist::circuit_iterator (db::Netlist::*) ()) &db::Netlist::begin_circuits, (db::Netlist::circuit_iterator (db::Netlist::*) ()) &db::Netlist::end_circuits,
"@brief Iterates over the circuits of the netlist" "@brief Iterates over the circuits of the netlist"
) + ) +
gsi::method_ext ("add", &gsi::add_device_class, gsi::method_ext ("add", &gsi::add_device_class, gsi::arg ("device_class"),
"@brief Adds the device class to the netlist\n" "@brief Adds the device class to the netlist\n"
"This method will add the given device class object to the netlist. " "This method will add the given device class object to the netlist. "
"After the device class has been added, it will be owned by the netlist." "After the device class has been added, it will be owned by the netlist."
@ -978,6 +998,11 @@ Class<db::Netlist> decl_dbNetlist ("db", "Netlist",
"@brief Purges floating nets.\n" "@brief Purges floating nets.\n"
"Floating nets can be created as effect of reconnections of devices or pins. " "Floating nets can be created as effect of reconnections of devices or pins. "
"This method will eliminate all nets that make less than two connections." "This method will eliminate all nets that make less than two connections."
) +
gsi::method_ext ("write", &write_netlist, gsi::arg ("file"), gsi::arg ("writer"), gsi::arg ("description", std::string ()),
"@brief Writes the netlist to the given file using the given writer object to format the file\n"
"See \\NetlistSpiceWriter for an example for a formatter. "
"The description is an arbitrary text which will be put into the file somewhere at the beginning."
), ),
"@brief The netlist top-level class\n" "@brief The netlist top-level class\n"
"A netlist is a hierarchical structure of circuits. At least one circuit is the " "A netlist is a hierarchical structure of circuits. At least one circuit is the "
@ -991,4 +1016,204 @@ Class<db::Netlist> decl_dbNetlist ("db", "Netlist",
"The netlist class has been introduced with version 0.26." "The netlist class has been introduced with version 0.26."
); );
/**
* @brief A SPICE writer delegate base class for reimplementation
*/
class NetlistSpiceWriterDelegateImpl
: public db::NetlistSpiceWriterDelegate, public gsi::ObjectBase
{
public:
NetlistSpiceWriterDelegateImpl ()
: db::NetlistSpiceWriterDelegate ()
{
// .. nothing yet ..
}
virtual void write_header () const
{
if (cb_write_header.can_issue ()) {
cb_write_header.issue<db::NetlistSpiceWriterDelegate> (&db::NetlistSpiceWriterDelegate::write_header);
} else {
db::NetlistSpiceWriterDelegate::write_header ();
}
}
virtual void write_device_intro (const db::DeviceClass &cls) const
{
if (cb_write_device_intro.can_issue ()) {
cb_write_device_intro.issue<db::NetlistSpiceWriterDelegate, const db::DeviceClass &> (&db::NetlistSpiceWriterDelegate::write_device_intro, cls);
} else {
db::NetlistSpiceWriterDelegate::write_device_intro (cls);
}
}
virtual void write_device (const db::Device &dev) const
{
if (cb_write_device.can_issue ()) {
cb_write_device.issue<db::NetlistSpiceWriterDelegate, const db::Device &> (&db::NetlistSpiceWriterDelegate::write_device, dev);
} else {
org_write_device (dev);
}
}
virtual void org_write_device (const db::Device &dev) const
{
db::NetlistSpiceWriterDelegate::write_device (dev);
}
gsi::Callback cb_write_header;
gsi::Callback cb_write_device_intro;
gsi::Callback cb_write_device;
using db::NetlistSpiceWriterDelegate::emit_comment;
using db::NetlistSpiceWriterDelegate::emit_line;
using db::NetlistSpiceWriterDelegate::net_to_string;
using db::NetlistSpiceWriterDelegate::format_name;
};
Class<NetlistSpiceWriterDelegateImpl> db_NetlistSpiceWriterDelegate ("db", "NetlistSpiceWriterDelegate",
gsi::callback ("write_header", &NetlistSpiceWriterDelegateImpl::write_header, &NetlistSpiceWriterDelegateImpl::cb_write_header,
"@brief Writes the text at the beginning of the SPICE netlist\n"
"Reimplement this method to insert your own text at the beginning of the file"
) +
gsi::callback ("write_device_intro", &NetlistSpiceWriterDelegateImpl::write_device_intro, &NetlistSpiceWriterDelegateImpl::cb_write_device_intro, gsi::arg ("device_class"),
"@brief Inserts a text for the given device class\n"
"Reimplement this method to insert your own text at the beginning of the file for the given device class"
) +
gsi::callback ("write_device", &NetlistSpiceWriterDelegateImpl::write_device, &NetlistSpiceWriterDelegateImpl::cb_write_device, gsi::arg ("device"),
"@brief Inserts a text for the given device\n"
"Reimplement this method to write the given device in the desired way"
) +
gsi::method ("write_device", &NetlistSpiceWriterDelegateImpl::org_write_device, gsi::arg ("device"),
"@brief Calls the default implementation of the \\write_device method.\n"
"The default implementation will utilize the device class information to write native SPICE "
"elements for the devices."
) +
gsi::method ("emit_comment", &NetlistSpiceWriterDelegateImpl::emit_comment, gsi::arg ("comment"),
"@brief Writes the given comment into the file"
) +
gsi::method ("emit_line", &NetlistSpiceWriterDelegateImpl::emit_line, gsi::arg ("line"),
"@brief Writes the given line into the file"
) +
gsi::method ("net_to_string", &NetlistSpiceWriterDelegateImpl::net_to_string, gsi::arg ("net"),
"@brief Gets the node ID for the given net\n"
"The node ID is a numeric string instead of the full name of the net. Numeric IDs are used within "
"SPICE netlist because they are usually shorter.\n"
) +
gsi::method ("format_name", &NetlistSpiceWriterDelegateImpl::format_name, gsi::arg ("name"),
"@brief Formats the given name in a SPICE-compatible way"
),
"@brief Provides a delegate for the SPICE writer for doing special formatting for devices\n"
"Supply a customized class to provide a specialized writing scheme for devices. "
"You need a customized class if you want to implement special devices or you want to use "
"subcircuits rather than the built-in devices.\n"
"\n"
"See \\NetlistSpiceWriter for more details.\n"
"\n"
"This class has been introduced in version 0.26."
);
namespace {
class NetlistSpiceWriterWithOwnership
: public db::NetlistSpiceWriter
{
public:
NetlistSpiceWriterWithOwnership (NetlistSpiceWriterDelegateImpl *delegate)
: db::NetlistSpiceWriter (delegate), m_ownership (delegate)
{
if (delegate) {
delegate->keep ();
}
}
private:
tl::shared_ptr<NetlistSpiceWriterDelegateImpl> m_ownership;
};
}
db::NetlistSpiceWriter *new_spice_writer ()
{
return new db::NetlistSpiceWriter ();
}
db::NetlistSpiceWriter *new_spice_writer2 (NetlistSpiceWriterDelegateImpl *delegate)
{
return new NetlistSpiceWriterWithOwnership (delegate);
}
Class<db::NetlistWriter> db_NetlistWriter ("db", "NetlistWriter",
gsi::Methods (),
"@hide\n"
);
Class<db::NetlistSpiceWriter> db_NetlistSpiceWriter (db_NetlistWriter, "db", "NetlistSpiceWriter",
gsi::constructor ("new", &new_spice_writer,
"@brief Creates a new writer without delegate.\n"
) +
gsi::constructor ("new", &new_spice_writer2,
"@brief Creates a new writer with a delegate.\n"
),
"@brief Implements a netlist writer for the SPICE format.\n"
"Provide a delegate for customizing the way devices are written.\n"
"\n"
"Use the SPICE writer like this:\n"
"\n"
"@code\n"
"writer = RBA::NetlistSpiceWriter::new\n"
"netlist.write(path, writer)\n"
"@endcode\n"
"\n"
"You can give a custom description for the headline:\n"
"\n"
"@code\n"
"writer = RBA::NetlistSpiceWriter::new\n"
"netlist.write(path, writer, \"A custom description\")\n"
"@endcode\n"
"\n"
"To customize the output, you can use a device writer delegate.\n"
"The delegate is an object of a class derived from \\NetlistSpiceWriterDelegate which "
"reimplements several methods to customize the following parts:\n"
"\n"
"@ul\n"
"@li A global header (\\NetlistSpiceWriterDelegate#write_header): this method is called to print the part right after the headline @/li\n"
"@li A per-device class header (\\NetlistSpiceWriterDelegate#write_device_intro): this method is called for every device class and may print device-class specific headers (e.g. model definitions) @/li\n"
"@li Per-device output: this method (\\NetlistSpiceWriterDelegate#write_device): this method is called for every device and may print the device statement(s) in a specific way.\n"
"@/ul\n"
"\n"
"The delegate must use \\NetlistSpiceWriterDelegate#emit_line to print a line, \\NetlistSpiceWriterDelegate#emit_comment to print a comment etc.\n"
"For more method see \\NetlistSpiceWriterDelegate.\n"
"\n"
"A sample with a delegate is this:\n"
"\n"
"@code\n"
"class MyDelegate < RBA::NetlistSpiceWriterDelegate\n"
"\n"
" def write_header\n"
" emit_line(\"*** My special header\")\n"
" end\n"
"\n"
" def write_device_intro(cls)\n"
" emit_comment(\"My intro for class \" + cls.name)\n"
" end\n"
"\n"
" def write_device(dev)\n"
" if dev.device_class.name != \"MYDEVICE\"\n"
" emit_comment(\"Terminal #1: \" + net_to_string(dev.net_for_terminal(0)))\n"
" emit_comment(\"Terminal #2: \" + net_to_string(dev.net_for_terminal(1)))\n"
" super(dev)\n"
" emit_comment(\"After device \" + dev.expanded_name)\n"
" end\n"
"\n"
"end\n"
"\n"
"# write the netlist with delegate:\n"
"writer = RBA::NetlistSpiceWriter::new(MyDelegate::new)\n"
"netlist.write(path, writer)\n"
"@endcode\n"
"\n"
"This class has been introduced in version 0.26."
);
} }

View File

@ -1078,3 +1078,4 @@ TEST(13_DeviceAbstract)
EXPECT_EQ (nl.begin_device_abstracts () == nl.end_device_abstracts (), true); EXPECT_EQ (nl.begin_device_abstracts () == nl.end_device_abstracts (), true);
} }

View File

@ -0,0 +1,919 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2019 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 "dbNetlistWriter.h"
#include "dbNetlistSpiceWriter.h"
#include "dbNetlist.h"
#include "dbNetlistDeviceClasses.h"
#include "tlUnitTest.h"
#include "tlStream.h"
#include "tlFileUtils.h"
TEST(1_WriterResistorDevices)
{
db::Netlist nl;
db::DeviceClass *rcls = new db::DeviceClassResistor ();
db::DeviceClass *ccls = new db::DeviceClassCapacitor ();
db::DeviceClass *lcls = new db::DeviceClassInductor ();
db::DeviceClass *dcls = new db::DeviceClassDiode ();
db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor ();
db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor ();
rcls->set_name ("RCLS");
lcls->set_name ("LCLS");
ccls->set_name ("CCLS");
dcls->set_name ("DCLS");
m3cls->set_name ("M3CLS");
m4cls->set_name ("M4CLS");
nl.add_device_class (rcls);
nl.add_device_class (lcls);
nl.add_device_class (ccls);
nl.add_device_class (dcls);
nl.add_device_class (m3cls);
nl.add_device_class (m4cls);
db::Circuit *circuit1 = new db::Circuit ();
circuit1->set_name ("C1");
nl.add_circuit (circuit1);
db::Net *n1, *n2, *n3;
n1 = new db::Net ();
n1->set_name ("n1");
circuit1->add_net (n1);
n2 = new db::Net ();
n2->set_name ("n2");
circuit1->add_net (n2);
n3 = new db::Net ();
n3->set_name ("n3");
circuit1->add_net (n3);
db::Device *rdev1 = new db::Device (rcls);
rdev1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.7);
db::Device *rdev2 = new db::Device (rcls);
rdev2->set_parameter_value (db::DeviceClassResistor::param_id_R, 42e-6);
circuit1->add_device (rdev1);
circuit1->add_device (rdev2);
size_t pid1 = circuit1->add_pin ("p1").id ();
size_t pid2 = circuit1->add_pin ("p2").id ();
circuit1->connect_pin (pid1, n1);
circuit1->connect_pin (pid2, n2);
rdev1->connect_terminal (rdev1->device_class ()->terminal_id_for_name ("A"), n1);
rdev1->connect_terminal (rdev1->device_class ()->terminal_id_for_name ("B"), n3);
rdev2->connect_terminal (rdev2->device_class ()->terminal_id_for_name ("A"), n3);
rdev2->connect_terminal (rdev2->device_class ()->terminal_id_for_name ("B"), n2);
// verify against the input
std::string path = tmp_file ("tmp_nwriter1.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter1_au.txt");
tl::InputStream is (path);
tl::InputStream is_au (au_path);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (path),
tl::absolute_file_path (au_path)));
}
}
TEST(2_WriterCapacitorDevices)
{
db::Netlist nl;
db::DeviceClass *rcls = new db::DeviceClassResistor ();
db::DeviceClass *ccls = new db::DeviceClassCapacitor ();
db::DeviceClass *lcls = new db::DeviceClassInductor ();
db::DeviceClass *dcls = new db::DeviceClassDiode ();
db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor ();
db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor ();
rcls->set_name ("RCLS");
lcls->set_name ("LCLS");
ccls->set_name ("CCLS");
dcls->set_name ("DCLS");
m3cls->set_name ("M3CLS");
m4cls->set_name ("M4CLS");
nl.add_device_class (rcls);
nl.add_device_class (lcls);
nl.add_device_class (ccls);
nl.add_device_class (dcls);
nl.add_device_class (m3cls);
nl.add_device_class (m4cls);
db::Circuit *circuit1 = new db::Circuit ();
circuit1->set_name ("C1");
nl.add_circuit (circuit1);
db::Net *n1, *n2, *n3;
n1 = new db::Net ();
n1->set_name ("n1");
circuit1->add_net (n1);
n2 = new db::Net ();
n2->set_name ("n2");
circuit1->add_net (n2);
n3 = new db::Net ();
n3->set_name ("n3");
circuit1->add_net (n3);
db::Device *cdev1 = new db::Device (ccls);
cdev1->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 1.7e-12);
db::Device *cdev2 = new db::Device (ccls);
cdev2->set_parameter_value (db::DeviceClassCapacitor::param_id_C, 42e-15);
circuit1->add_device (cdev1);
circuit1->add_device (cdev2);
size_t pid1 = circuit1->add_pin ("p1").id ();
size_t pid2 = circuit1->add_pin ("p2").id ();
circuit1->connect_pin (pid1, n1);
circuit1->connect_pin (pid2, n2);
cdev1->connect_terminal (cdev1->device_class ()->terminal_id_for_name ("A"), n1);
cdev1->connect_terminal (cdev1->device_class ()->terminal_id_for_name ("B"), n3);
cdev2->connect_terminal (cdev2->device_class ()->terminal_id_for_name ("A"), n3);
cdev2->connect_terminal (cdev2->device_class ()->terminal_id_for_name ("B"), n2);
// verify against the input
std::string path = tmp_file ("tmp_nwriter2.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter2_au.txt");
tl::InputStream is (path);
tl::InputStream is_au (au_path);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (path),
tl::absolute_file_path (au_path)));
}
}
TEST(3_WriterInductorDevices)
{
db::Netlist nl;
db::DeviceClass *rcls = new db::DeviceClassResistor ();
db::DeviceClass *ccls = new db::DeviceClassCapacitor ();
db::DeviceClass *lcls = new db::DeviceClassInductor ();
db::DeviceClass *dcls = new db::DeviceClassDiode ();
db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor ();
db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor ();
rcls->set_name ("RCLS");
lcls->set_name ("LCLS");
ccls->set_name ("CCLS");
dcls->set_name ("DCLS");
m3cls->set_name ("M3CLS");
m4cls->set_name ("M4CLS");
nl.add_device_class (rcls);
nl.add_device_class (lcls);
nl.add_device_class (ccls);
nl.add_device_class (dcls);
nl.add_device_class (m3cls);
nl.add_device_class (m4cls);
db::Circuit *circuit1 = new db::Circuit ();
circuit1->set_name ("C1");
nl.add_circuit (circuit1);
db::Net *n1, *n2, *n3;
n1 = new db::Net ();
n1->set_name ("n1");
circuit1->add_net (n1);
n2 = new db::Net ();
n2->set_name ("n2");
circuit1->add_net (n2);
n3 = new db::Net ();
n3->set_name ("n3");
circuit1->add_net (n3);
db::Device *ldev1 = new db::Device (lcls);
ldev1->set_parameter_value (db::DeviceClassInductor::param_id_L, 1.7e-10);
db::Device *ldev2 = new db::Device (lcls);
ldev2->set_parameter_value (db::DeviceClassInductor::param_id_L, 42e-9);
circuit1->add_device (ldev1);
circuit1->add_device (ldev2);
size_t pid1 = circuit1->add_pin ("p1").id ();
size_t pid2 = circuit1->add_pin ("p2").id ();
circuit1->connect_pin (pid1, n1);
circuit1->connect_pin (pid2, n2);
ldev1->connect_terminal (ldev1->device_class ()->terminal_id_for_name ("A"), n1);
ldev1->connect_terminal (ldev1->device_class ()->terminal_id_for_name ("B"), n3);
ldev2->connect_terminal (ldev2->device_class ()->terminal_id_for_name ("A"), n3);
ldev2->connect_terminal (ldev2->device_class ()->terminal_id_for_name ("B"), n2);
// verify against the input
std::string path = tmp_file ("tmp_nwriter3.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter3_au.txt");
tl::InputStream is (path);
tl::InputStream is_au (au_path);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (path),
tl::absolute_file_path (au_path)));
}
}
TEST(4_WriterDiodeDevices)
{
db::Netlist nl;
db::DeviceClass *rcls = new db::DeviceClassResistor ();
db::DeviceClass *ccls = new db::DeviceClassCapacitor ();
db::DeviceClass *lcls = new db::DeviceClassInductor ();
db::DeviceClass *dcls = new db::DeviceClassDiode ();
db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor ();
db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor ();
rcls->set_name ("RCLS");
lcls->set_name ("LCLS");
ccls->set_name ("CCLS");
dcls->set_name ("DCLS");
m3cls->set_name ("M3CLS");
m4cls->set_name ("M4CLS");
nl.add_device_class (rcls);
nl.add_device_class (lcls);
nl.add_device_class (ccls);
nl.add_device_class (dcls);
nl.add_device_class (m3cls);
nl.add_device_class (m4cls);
db::Circuit *circuit1 = new db::Circuit ();
circuit1->set_name ("C1");
nl.add_circuit (circuit1);
db::Net *n1, *n2, *n3;
n1 = new db::Net ();
n1->set_name ("n1");
circuit1->add_net (n1);
n2 = new db::Net ();
n2->set_name ("n2");
circuit1->add_net (n2);
n3 = new db::Net ();
n3->set_name ("n3");
circuit1->add_net (n3);
db::Device *ddev1 = new db::Device (dcls);
ddev1->set_parameter_value (db::DeviceClassDiode::param_id_A, 1.7e-10);
db::Device *ddev2 = new db::Device (dcls);
ddev2->set_parameter_value (db::DeviceClassDiode::param_id_A, 42e-9);
circuit1->add_device (ddev1);
circuit1->add_device (ddev2);
size_t pid1 = circuit1->add_pin ("p1").id ();
size_t pid2 = circuit1->add_pin ("p2").id ();
circuit1->connect_pin (pid1, n1);
circuit1->connect_pin (pid2, n2);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("A"), n1);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("C"), n3);
ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("A"), n3);
ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("C"), n2);
// verify against the input
std::string path = tmp_file ("tmp_nwriter4.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter4_au.txt");
tl::InputStream is (path);
tl::InputStream is_au (au_path);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (path),
tl::absolute_file_path (au_path)));
}
}
TEST(5_WriterMOS3Devices)
{
db::Netlist nl;
db::DeviceClass *rcls = new db::DeviceClassResistor ();
db::DeviceClass *ccls = new db::DeviceClassCapacitor ();
db::DeviceClass *lcls = new db::DeviceClassInductor ();
db::DeviceClass *dcls = new db::DeviceClassDiode ();
db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor ();
db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor ();
rcls->set_name ("RCLS");
lcls->set_name ("LCLS");
ccls->set_name ("CCLS");
dcls->set_name ("DCLS");
m3cls->set_name ("M3CLS");
m4cls->set_name ("M4CLS");
nl.add_device_class (rcls);
nl.add_device_class (lcls);
nl.add_device_class (ccls);
nl.add_device_class (dcls);
nl.add_device_class (m3cls);
nl.add_device_class (m4cls);
db::Circuit *circuit1 = new db::Circuit ();
circuit1->set_name ("C1");
nl.add_circuit (circuit1);
db::Net *n1, *n2, *n3, *n4;
n1 = new db::Net ();
n1->set_name ("n1");
circuit1->add_net (n1);
n2 = new db::Net ();
n2->set_name ("n2");
circuit1->add_net (n2);
n3 = new db::Net ();
n3->set_name ("n3");
circuit1->add_net (n3);
n4 = new db::Net ();
n4->set_name ("n4");
circuit1->add_net (n4);
db::Device *ddev1 = new db::Device (m3cls);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.25);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.18);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.2);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.75);
db::Device *ddev2 = new db::Device (m3cls);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 1.4);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.25);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.3);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.85);
circuit1->add_device (ddev1);
circuit1->add_device (ddev2);
size_t pid1 = circuit1->add_pin ("p1").id ();
size_t pid2 = circuit1->add_pin ("p2").id ();
size_t pid3 = circuit1->add_pin ("p3").id ();
circuit1->connect_pin (pid1, n1);
circuit1->connect_pin (pid2, n2);
circuit1->connect_pin (pid3, n4);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("S"), n1);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("D"), n3);
ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("S"), n3);
ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4);
ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("D"), n2);
// verify against the input
std::string path = tmp_file ("tmp_nwriter5.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter5_au.txt");
tl::InputStream is (path);
tl::InputStream is_au (au_path);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (path),
tl::absolute_file_path (au_path)));
}
}
TEST(6_WriterMOS4Devices)
{
db::Netlist nl;
db::DeviceClass *rcls = new db::DeviceClassResistor ();
db::DeviceClass *ccls = new db::DeviceClassCapacitor ();
db::DeviceClass *lcls = new db::DeviceClassInductor ();
db::DeviceClass *dcls = new db::DeviceClassDiode ();
db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor ();
db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor ();
rcls->set_name ("RCLS");
lcls->set_name ("LCLS");
ccls->set_name ("CCLS");
dcls->set_name ("DCLS");
m3cls->set_name ("M3CLS");
m4cls->set_name ("M4CLS");
nl.add_device_class (rcls);
nl.add_device_class (lcls);
nl.add_device_class (ccls);
nl.add_device_class (dcls);
nl.add_device_class (m3cls);
nl.add_device_class (m4cls);
db::Circuit *circuit1 = new db::Circuit ();
circuit1->set_name ("C1");
nl.add_circuit (circuit1);
db::Net *n1, *n2, *n3, *n4, *n5;
n1 = new db::Net ();
n1->set_name ("n1");
circuit1->add_net (n1);
n2 = new db::Net ();
n2->set_name ("n2");
circuit1->add_net (n2);
n3 = new db::Net ();
n3->set_name ("n3");
circuit1->add_net (n3);
n4 = new db::Net ();
n4->set_name ("n4");
circuit1->add_net (n4);
n5 = new db::Net ();
n5->set_name ("n5");
circuit1->add_net (n5);
db::Device *ddev1 = new db::Device (m4cls);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.25);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.18);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.2);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.75);
db::Device *ddev2 = new db::Device (m4cls);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 1.4);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.25);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.3);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.85);
circuit1->add_device (ddev1);
circuit1->add_device (ddev2);
size_t pid1 = circuit1->add_pin ("p1").id ();
size_t pid2 = circuit1->add_pin ("p2").id ();
size_t pid3 = circuit1->add_pin ("p3").id ();
size_t pid4 = circuit1->add_pin ("p4").id ();
circuit1->connect_pin (pid1, n1);
circuit1->connect_pin (pid2, n2);
circuit1->connect_pin (pid3, n4);
circuit1->connect_pin (pid4, n5);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("S"), n1);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("D"), n3);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n5);
ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("S"), n3);
ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4);
ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("D"), n2);
ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n5);
// verify against the input
std::string path = tmp_file ("tmp_nwriter6.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter6_au.txt");
tl::InputStream is (path);
tl::InputStream is_au (au_path);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (path),
tl::absolute_file_path (au_path)));
}
}
TEST(7_WriterAnyDevices)
{
db::Netlist nl;
db::DeviceClass *cls = new db::DeviceClass ();
cls->add_terminal_definition (db::DeviceTerminalDefinition ("A", "a"));
cls->add_terminal_definition (db::DeviceTerminalDefinition ("B", "b"));
cls->add_parameter_definition (db::DeviceParameterDefinition ("U", "u"));
cls->add_parameter_definition (db::DeviceParameterDefinition ("V", "v"));
cls->set_name ("XCLS");
nl.add_device_class (cls);
db::Circuit *circuit1 = new db::Circuit ();
circuit1->set_name ("C1");
nl.add_circuit (circuit1);
db::Net *n1, *n2, *n3;
n1 = new db::Net ();
n1->set_name ("n1");
circuit1->add_net (n1);
n2 = new db::Net ();
n2->set_name ("n2");
circuit1->add_net (n2);
n3 = new db::Net ();
n3->set_name ("n3");
circuit1->add_net (n3);
db::Device *ddev1 = new db::Device (cls);
ddev1->set_parameter_value (0, -17);
ddev1->set_parameter_value (1, 42);
db::Device *ddev2 = new db::Device (cls);
ddev2->set_parameter_value (0, 17);
ddev2->set_parameter_value (1, -42);
circuit1->add_device (ddev1);
circuit1->add_device (ddev2);
size_t pid1 = circuit1->add_pin ("p1").id ();
size_t pid2 = circuit1->add_pin ("p2").id ();
circuit1->connect_pin (pid1, n1);
circuit1->connect_pin (pid2, n2);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("A"), n1);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n3);
ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("A"), n3);
ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("B"), n2);
// verify against the input
std::string path = tmp_file ("tmp_nwriter7.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter7_au.txt");
tl::InputStream is (path);
tl::InputStream is_au (au_path);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (path),
tl::absolute_file_path (au_path)));
}
}
TEST(8_WriterSubcircuits)
{
db::Netlist nl;
db::DeviceClass *rcls = new db::DeviceClassResistor ();
db::DeviceClass *ccls = new db::DeviceClassCapacitor ();
db::DeviceClass *lcls = new db::DeviceClassInductor ();
db::DeviceClass *dcls = new db::DeviceClassDiode ();
db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor ();
db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor ();
rcls->set_name ("RCLS");
lcls->set_name ("LCLS");
ccls->set_name ("CCLS");
dcls->set_name ("DCLS");
m3cls->set_name ("M3CLS");
m4cls->set_name ("M4CLS");
nl.add_device_class (rcls);
nl.add_device_class (lcls);
nl.add_device_class (ccls);
nl.add_device_class (dcls);
nl.add_device_class (m3cls);
nl.add_device_class (m4cls);
db::Circuit *circuit1 = new db::Circuit ();
circuit1->set_name ("C1");
nl.add_circuit (circuit1);
{
db::Net *n1, *n2, *n3, *n4, *n5;
n1 = new db::Net ();
n1->set_name ("n1");
circuit1->add_net (n1);
n2 = new db::Net ();
n2->set_name ("n2");
circuit1->add_net (n2);
n3 = new db::Net ();
n3->set_name ("n3");
circuit1->add_net (n3);
n4 = new db::Net ();
n4->set_name ("n4");
circuit1->add_net (n4);
n5 = new db::Net ();
n5->set_name ("n5");
circuit1->add_net (n5);
db::Device *ddev1 = new db::Device (m4cls);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 0.25);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.18);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.2);
ddev1->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.75);
db::Device *ddev2 = new db::Device (m4cls);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 1.4);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_W, 0.25);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AS, 1.3);
ddev2->set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 0.85);
circuit1->add_device (ddev1);
circuit1->add_device (ddev2);
size_t pid1 = circuit1->add_pin ("p1").id ();
size_t pid2 = circuit1->add_pin ("p2").id ();
size_t pid3 = circuit1->add_pin ("p3").id ();
size_t pid4 = circuit1->add_pin ("p4").id ();
circuit1->connect_pin (pid1, n1);
circuit1->connect_pin (pid2, n2);
circuit1->connect_pin (pid3, n4);
circuit1->connect_pin (pid4, n5);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("S"), n1);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("D"), n3);
ddev1->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n5);
ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("S"), n3);
ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("G"), n4);
ddev2->connect_terminal (ddev2->device_class ()->terminal_id_for_name ("D"), n2);
ddev2->connect_terminal (ddev1->device_class ()->terminal_id_for_name ("B"), n5);
}
db::Circuit *circuit2 = new db::Circuit ();
circuit2->set_name ("C2");
nl.add_circuit (circuit2);
{
db::Net *n1, *n2, *n3, *n4, *n5;
n1 = new db::Net ();
n1->set_name ("n1");
circuit2->add_net (n1);
n2 = new db::Net ();
n2->set_name ("n2");
circuit2->add_net (n2);
n3 = new db::Net ();
n3->set_name ("n3");
circuit2->add_net (n3);
n4 = new db::Net ();
n4->set_name ("n4");
circuit2->add_net (n4);
n5 = new db::Net ();
n5->set_name ("n5");
circuit2->add_net (n5);
db::SubCircuit *sc1 = new db::SubCircuit (circuit1, "SC1");
circuit2->add_subcircuit (sc1);
sc1->connect_pin (0, n1);
sc1->connect_pin (1, n3);
sc1->connect_pin (2, n4);
sc1->connect_pin (3, n3);
db::SubCircuit *sc2 = new db::SubCircuit (circuit1, "SC2");
circuit2->add_subcircuit (sc2);
sc2->connect_pin (0, n3);
sc2->connect_pin (1, n2);
sc2->connect_pin (2, n4);
sc2->connect_pin (3, n3);
size_t pid1 = circuit2->add_pin ("p1").id ();
size_t pid2 = circuit2->add_pin ("p2").id ();
size_t pid3 = circuit2->add_pin ("p3").id ();
circuit2->connect_pin (pid1, n1);
circuit2->connect_pin (pid2, n2);
circuit2->connect_pin (pid3, n4);
}
// verify against the input
std::string path = tmp_file ("tmp_nwriter8.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter8_au.txt");
tl::InputStream is (path);
tl::InputStream is_au (au_path);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (path),
tl::absolute_file_path (au_path)));
}
}
TEST(10_WriterLongLines)
{
db::Netlist nl;
db::DeviceClass *rcls = new db::DeviceClassResistor ();
rcls->set_name ("RCLS");
nl.add_device_class (rcls);
db::Circuit *circuit1 = new db::Circuit ();
circuit1->set_name ("C1withaverylongextensionthatgoesbeyondmultiplelinesunlessipasteeverythingtogetherwhichmakesithardtoreadbutexactlythatisthereasonwhyiwriteitthisway");
nl.add_circuit (circuit1);
db::Net *n0 = new db::Net ();
n0->set_name ("n0");
circuit1->add_net (n0);
size_t pid0 = circuit1->add_pin ("p0").id ();
circuit1->connect_pin (pid0, n0);
for (int i = 0; i < 100; ++i) {
db::Net *n = new db::Net ();
n->set_name ("n" + tl::to_string (i + 1));
circuit1->add_net (n);
size_t pid = circuit1->add_pin ("p" + tl::to_string (i + 1)).id ();
circuit1->connect_pin (pid, n);
db::Device *ddev = new db::Device (rcls);
circuit1->add_device (ddev);
ddev->connect_terminal (db::DeviceClassResistor::terminal_id_A, n0);
ddev->connect_terminal (db::DeviceClassResistor::terminal_id_B, n);
}
// verify against the input
std::string path = tmp_file ("tmp_nwriter10.txt");
{
tl::OutputStream stream (path);
db::NetlistSpiceWriter writer;
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter10_au.txt");
tl::InputStream is (path);
tl::InputStream is_au (au_path);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (path),
tl::absolute_file_path (au_path)));
}
}
namespace {
class MyDelegate
: public db::NetlistSpiceWriterDelegate
{
public:
MyDelegate ()
: db::NetlistSpiceWriterDelegate ()
{ }
void write_header () const
{
emit_line ("*** My special header");
}
void write_device_intro (const db::DeviceClass &cls) const
{
emit_line ("*** My intro for class " + cls.name ());
}
void write_device (const db::Device &dev) const
{
emit_line ("*** Before device " + dev.expanded_name ());
db::NetlistSpiceWriterDelegate::write_device (dev);
emit_line ("*** After device " + dev.expanded_name ());
}
};
}
TEST(20_Delegate)
{
db::Netlist nl;
db::DeviceClass *rcls = new db::DeviceClassResistor ();
db::DeviceClass *ccls = new db::DeviceClassCapacitor ();
db::DeviceClass *lcls = new db::DeviceClassInductor ();
db::DeviceClass *dcls = new db::DeviceClassDiode ();
db::DeviceClass *m3cls = new db::DeviceClassMOS3Transistor ();
db::DeviceClass *m4cls = new db::DeviceClassMOS4Transistor ();
rcls->set_name ("RCLS");
lcls->set_name ("LCLS");
ccls->set_name ("CCLS");
dcls->set_name ("DCLS");
m3cls->set_name ("M3CLS");
m4cls->set_name ("M4CLS");
nl.add_device_class (rcls);
nl.add_device_class (lcls);
nl.add_device_class (ccls);
nl.add_device_class (dcls);
nl.add_device_class (m3cls);
nl.add_device_class (m4cls);
db::Circuit *circuit1 = new db::Circuit ();
circuit1->set_name ("C1");
nl.add_circuit (circuit1);
db::Net *n1, *n2, *n3;
n1 = new db::Net ();
n1->set_name ("n1");
circuit1->add_net (n1);
n2 = new db::Net ();
n2->set_name ("n2");
circuit1->add_net (n2);
n3 = new db::Net ();
n3->set_name ("n3");
circuit1->add_net (n3);
db::Device *rdev1 = new db::Device (rcls);
rdev1->set_parameter_value (db::DeviceClassResistor::param_id_R, 1.7);
db::Device *rdev2 = new db::Device (rcls);
rdev2->set_parameter_value (db::DeviceClassResistor::param_id_R, 42e-6);
circuit1->add_device (rdev1);
circuit1->add_device (rdev2);
size_t pid1 = circuit1->add_pin ("p1").id ();
size_t pid2 = circuit1->add_pin ("p2").id ();
circuit1->connect_pin (pid1, n1);
circuit1->connect_pin (pid2, n2);
rdev1->connect_terminal (rdev1->device_class ()->terminal_id_for_name ("A"), n1);
rdev1->connect_terminal (rdev1->device_class ()->terminal_id_for_name ("B"), n3);
rdev2->connect_terminal (rdev2->device_class ()->terminal_id_for_name ("A"), n3);
rdev2->connect_terminal (rdev2->device_class ()->terminal_id_for_name ("B"), n2);
// verify against the input
std::string path = tmp_file ("tmp_nwriter20.txt");
{
tl::OutputStream stream (path);
MyDelegate delegate;
db::NetlistSpiceWriter writer (&delegate);
writer.write (stream, nl, "written by unit test");
}
std::string au_path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nwriter20_au.txt");
tl::InputStream is (path);
tl::InputStream is_au (au_path);
if (is.read_all () != is_au.read_all ()) {
_this->raise (tl::sprintf ("Compare failed - see\n actual: %s\n golden: %s",
tl::absolute_file_path (path),
tl::absolute_file_path (au_path)));
}
}

View File

@ -65,7 +65,8 @@ SOURCES = \
dbNetlistDeviceClassesTests.cc \ dbNetlistDeviceClassesTests.cc \
dbLayoutToNetlistTests.cc \ dbLayoutToNetlistTests.cc \
dbLayoutToNetlistWriterTests.cc \ dbLayoutToNetlistWriterTests.cc \
dbLayoutToNetlistReaderTests.cc dbLayoutToNetlistReaderTests.cc \
dbNetlistWriterTests.cc
INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC INCLUDEPATH += $$TL_INC $$DB_INC $$GSI_INC
DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC DEPENDPATH += $$TL_INC $$DB_INC $$GSI_INC

View File

@ -112,6 +112,7 @@ RUBYTEST (dbLayoutToNetlist, "dbLayoutToNetlist.rb")
RUBYTEST (dbMatrix, "dbMatrix.rb") RUBYTEST (dbMatrix, "dbMatrix.rb")
RUBYTEST (dbNetlist, "dbNetlist.rb") RUBYTEST (dbNetlist, "dbNetlist.rb")
RUBYTEST (dbNetlistDeviceClasses, "dbNetlistDeviceClasses.rb") RUBYTEST (dbNetlistDeviceClasses, "dbNetlistDeviceClasses.rb")
RUBYTEST (dbNetlistWriterTests, "dbNetlistWriterTests.rb")
RUBYTEST (dbPathTest, "dbPathTest.rb") RUBYTEST (dbPathTest, "dbPathTest.rb")
RUBYTEST (dbPCells, "dbPCells.rb") RUBYTEST (dbPCells, "dbPCells.rb")
RUBYTEST (dbPointTest, "dbPointTest.rb") RUBYTEST (dbPointTest, "dbPointTest.rb")

413
testdata/algo/nwriter10_au.txt vendored Normal file
View File

@ -0,0 +1,413 @@
* written by unit test
* cell C1withaverylongextensionthatgoesbeyondmultiplelinesunlessipasteeverythingtogetherwhichmakesithardtoreadbutexactlythatisthereasonwhyiwriteitthisway
* pin p0
* pin p1
* pin p2
* pin p3
* pin p4
* pin p5
* pin p6
* pin p7
* pin p8
* pin p9
* pin p10
* pin p11
* pin p12
* pin p13
* pin p14
* pin p15
* pin p16
* pin p17
* pin p18
* pin p19
* pin p20
* pin p21
* pin p22
* pin p23
* pin p24
* pin p25
* pin p26
* pin p27
* pin p28
* pin p29
* pin p30
* pin p31
* pin p32
* pin p33
* pin p34
* pin p35
* pin p36
* pin p37
* pin p38
* pin p39
* pin p40
* pin p41
* pin p42
* pin p43
* pin p44
* pin p45
* pin p46
* pin p47
* pin p48
* pin p49
* pin p50
* pin p51
* pin p52
* pin p53
* pin p54
* pin p55
* pin p56
* pin p57
* pin p58
* pin p59
* pin p60
* pin p61
* pin p62
* pin p63
* pin p64
* pin p65
* pin p66
* pin p67
* pin p68
* pin p69
* pin p70
* pin p71
* pin p72
* pin p73
* pin p74
* pin p75
* pin p76
* pin p77
* pin p78
* pin p79
* pin p80
* pin p81
* pin p82
* pin p83
* pin p84
* pin p85
* pin p86
* pin p87
* pin p88
* pin p89
* pin p90
* pin p91
* pin p92
* pin p93
* pin p94
* pin p95
* pin p96
* pin p97
* pin p98
* pin p99
* pin p100
.SUBCKT
+ C1withaverylongextensionthatgoesbeyondmultiplelinesunlessipasteeverythingtogetherwhichmakesithardtoreadbutexactlythatisthereasonwhyiwriteitthisway
+ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
+ 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
+ 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
+ 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
* net 1 n0
* net 2 n1
* net 3 n2
* net 4 n3
* net 5 n4
* net 6 n5
* net 7 n6
* net 8 n7
* net 9 n8
* net 10 n9
* net 11 n10
* net 12 n11
* net 13 n12
* net 14 n13
* net 15 n14
* net 16 n15
* net 17 n16
* net 18 n17
* net 19 n18
* net 20 n19
* net 21 n20
* net 22 n21
* net 23 n22
* net 24 n23
* net 25 n24
* net 26 n25
* net 27 n26
* net 28 n27
* net 29 n28
* net 30 n29
* net 31 n30
* net 32 n31
* net 33 n32
* net 34 n33
* net 35 n34
* net 36 n35
* net 37 n36
* net 38 n37
* net 39 n38
* net 40 n39
* net 41 n40
* net 42 n41
* net 43 n42
* net 44 n43
* net 45 n44
* net 46 n45
* net 47 n46
* net 48 n47
* net 49 n48
* net 50 n49
* net 51 n50
* net 52 n51
* net 53 n52
* net 54 n53
* net 55 n54
* net 56 n55
* net 57 n56
* net 58 n57
* net 59 n58
* net 60 n59
* net 61 n60
* net 62 n61
* net 63 n62
* net 64 n63
* net 65 n64
* net 66 n65
* net 67 n66
* net 68 n67
* net 69 n68
* net 70 n69
* net 71 n70
* net 72 n71
* net 73 n72
* net 74 n73
* net 75 n74
* net 76 n75
* net 77 n76
* net 78 n77
* net 79 n78
* net 80 n79
* net 81 n80
* net 82 n81
* net 83 n82
* net 84 n83
* net 85 n84
* net 86 n85
* net 87 n86
* net 88 n87
* net 89 n88
* net 90 n89
* net 91 n90
* net 92 n91
* net 93 n92
* net 94 n93
* net 95 n94
* net 96 n95
* net 97 n96
* net 98 n97
* net 99 n98
* net 100 n99
* net 101 n100
* device instance $1 0,0 RCLS
R$1 1 2 0
* device instance $2 0,0 RCLS
R$2 1 3 0
* device instance $3 0,0 RCLS
R$3 1 4 0
* device instance $4 0,0 RCLS
R$4 1 5 0
* device instance $5 0,0 RCLS
R$5 1 6 0
* device instance $6 0,0 RCLS
R$6 1 7 0
* device instance $7 0,0 RCLS
R$7 1 8 0
* device instance $8 0,0 RCLS
R$8 1 9 0
* device instance $9 0,0 RCLS
R$9 1 10 0
* device instance $10 0,0 RCLS
R$10 1 11 0
* device instance $11 0,0 RCLS
R$11 1 12 0
* device instance $12 0,0 RCLS
R$12 1 13 0
* device instance $13 0,0 RCLS
R$13 1 14 0
* device instance $14 0,0 RCLS
R$14 1 15 0
* device instance $15 0,0 RCLS
R$15 1 16 0
* device instance $16 0,0 RCLS
R$16 1 17 0
* device instance $17 0,0 RCLS
R$17 1 18 0
* device instance $18 0,0 RCLS
R$18 1 19 0
* device instance $19 0,0 RCLS
R$19 1 20 0
* device instance $20 0,0 RCLS
R$20 1 21 0
* device instance $21 0,0 RCLS
R$21 1 22 0
* device instance $22 0,0 RCLS
R$22 1 23 0
* device instance $23 0,0 RCLS
R$23 1 24 0
* device instance $24 0,0 RCLS
R$24 1 25 0
* device instance $25 0,0 RCLS
R$25 1 26 0
* device instance $26 0,0 RCLS
R$26 1 27 0
* device instance $27 0,0 RCLS
R$27 1 28 0
* device instance $28 0,0 RCLS
R$28 1 29 0
* device instance $29 0,0 RCLS
R$29 1 30 0
* device instance $30 0,0 RCLS
R$30 1 31 0
* device instance $31 0,0 RCLS
R$31 1 32 0
* device instance $32 0,0 RCLS
R$32 1 33 0
* device instance $33 0,0 RCLS
R$33 1 34 0
* device instance $34 0,0 RCLS
R$34 1 35 0
* device instance $35 0,0 RCLS
R$35 1 36 0
* device instance $36 0,0 RCLS
R$36 1 37 0
* device instance $37 0,0 RCLS
R$37 1 38 0
* device instance $38 0,0 RCLS
R$38 1 39 0
* device instance $39 0,0 RCLS
R$39 1 40 0
* device instance $40 0,0 RCLS
R$40 1 41 0
* device instance $41 0,0 RCLS
R$41 1 42 0
* device instance $42 0,0 RCLS
R$42 1 43 0
* device instance $43 0,0 RCLS
R$43 1 44 0
* device instance $44 0,0 RCLS
R$44 1 45 0
* device instance $45 0,0 RCLS
R$45 1 46 0
* device instance $46 0,0 RCLS
R$46 1 47 0
* device instance $47 0,0 RCLS
R$47 1 48 0
* device instance $48 0,0 RCLS
R$48 1 49 0
* device instance $49 0,0 RCLS
R$49 1 50 0
* device instance $50 0,0 RCLS
R$50 1 51 0
* device instance $51 0,0 RCLS
R$51 1 52 0
* device instance $52 0,0 RCLS
R$52 1 53 0
* device instance $53 0,0 RCLS
R$53 1 54 0
* device instance $54 0,0 RCLS
R$54 1 55 0
* device instance $55 0,0 RCLS
R$55 1 56 0
* device instance $56 0,0 RCLS
R$56 1 57 0
* device instance $57 0,0 RCLS
R$57 1 58 0
* device instance $58 0,0 RCLS
R$58 1 59 0
* device instance $59 0,0 RCLS
R$59 1 60 0
* device instance $60 0,0 RCLS
R$60 1 61 0
* device instance $61 0,0 RCLS
R$61 1 62 0
* device instance $62 0,0 RCLS
R$62 1 63 0
* device instance $63 0,0 RCLS
R$63 1 64 0
* device instance $64 0,0 RCLS
R$64 1 65 0
* device instance $65 0,0 RCLS
R$65 1 66 0
* device instance $66 0,0 RCLS
R$66 1 67 0
* device instance $67 0,0 RCLS
R$67 1 68 0
* device instance $68 0,0 RCLS
R$68 1 69 0
* device instance $69 0,0 RCLS
R$69 1 70 0
* device instance $70 0,0 RCLS
R$70 1 71 0
* device instance $71 0,0 RCLS
R$71 1 72 0
* device instance $72 0,0 RCLS
R$72 1 73 0
* device instance $73 0,0 RCLS
R$73 1 74 0
* device instance $74 0,0 RCLS
R$74 1 75 0
* device instance $75 0,0 RCLS
R$75 1 76 0
* device instance $76 0,0 RCLS
R$76 1 77 0
* device instance $77 0,0 RCLS
R$77 1 78 0
* device instance $78 0,0 RCLS
R$78 1 79 0
* device instance $79 0,0 RCLS
R$79 1 80 0
* device instance $80 0,0 RCLS
R$80 1 81 0
* device instance $81 0,0 RCLS
R$81 1 82 0
* device instance $82 0,0 RCLS
R$82 1 83 0
* device instance $83 0,0 RCLS
R$83 1 84 0
* device instance $84 0,0 RCLS
R$84 1 85 0
* device instance $85 0,0 RCLS
R$85 1 86 0
* device instance $86 0,0 RCLS
R$86 1 87 0
* device instance $87 0,0 RCLS
R$87 1 88 0
* device instance $88 0,0 RCLS
R$88 1 89 0
* device instance $89 0,0 RCLS
R$89 1 90 0
* device instance $90 0,0 RCLS
R$90 1 91 0
* device instance $91 0,0 RCLS
R$91 1 92 0
* device instance $92 0,0 RCLS
R$92 1 93 0
* device instance $93 0,0 RCLS
R$93 1 94 0
* device instance $94 0,0 RCLS
R$94 1 95 0
* device instance $95 0,0 RCLS
R$95 1 96 0
* device instance $96 0,0 RCLS
R$96 1 97 0
* device instance $97 0,0 RCLS
R$97 1 98 0
* device instance $98 0,0 RCLS
R$98 1 99 0
* device instance $99 0,0 RCLS
R$99 1 100 0
* device instance $100 0,0 RCLS
R$100 1 101 0
.ENDS
+ C1withaverylongextensionthatgoesbeyondmultiplelinesunlessipasteeverythingtogetherwhichmakesithardtoreadbutexactlythatisthereasonwhyiwriteitthisway

14
testdata/algo/nwriter1_au.txt vendored Normal file
View File

@ -0,0 +1,14 @@
* written by unit test
* cell C1
* pin p1
* pin p2
.SUBCKT C1 1 2
* net 1 n1
* net 2 n2
* net 3 n3
* device instance $1 0,0 RCLS
R$1 1 3 1.7
* device instance $2 0,0 RCLS
R$2 3 2 4.2e-05
.ENDS C1

25
testdata/algo/nwriter20_au.txt vendored Normal file
View File

@ -0,0 +1,25 @@
* written by unit test
*** My special header
*** My intro for class RCLS
*** My intro for class LCLS
*** My intro for class CCLS
*** My intro for class DCLS
*** My intro for class M3CLS
*** My intro for class M4CLS
* cell C1
* pin p1
* pin p2
.SUBCKT C1 1 2
* net 1 n1
* net 2 n2
* net 3 n3
* device instance $1 0,0 RCLS
*** Before device $1
R$1 1 3 1.7
*** After device $1
* device instance $2 0,0 RCLS
*** Before device $2
R$2 3 2 4.2e-05
*** After device $2
.ENDS C1

14
testdata/algo/nwriter2_au.txt vendored Normal file
View File

@ -0,0 +1,14 @@
* written by unit test
* cell C1
* pin p1
* pin p2
.SUBCKT C1 1 2
* net 1 n1
* net 2 n2
* net 3 n3
* device instance $1 0,0 CCLS
C$1 1 3 1.7e-12
* device instance $2 0,0 CCLS
C$2 3 2 4.2e-14
.ENDS C1

14
testdata/algo/nwriter3_au.txt vendored Normal file
View File

@ -0,0 +1,14 @@
* written by unit test
* cell C1
* pin p1
* pin p2
.SUBCKT C1 1 2
* net 1 n1
* net 2 n2
* net 3 n3
* device instance $1 0,0 LCLS
L$1 1 3 1.7e-10
* device instance $2 0,0 LCLS
L$2 3 2 4.2e-08
.ENDS C1

14
testdata/algo/nwriter4_au.txt vendored Normal file
View File

@ -0,0 +1,14 @@
* written by unit test
* cell C1
* pin p1
* pin p2
.SUBCKT C1 1 2
* net 1 n1
* net 2 n2
* net 3 n3
* device instance $1 0,0 DCLS
D$1 1 3 DDCLS
* device instance $2 0,0 DCLS
D$2 3 2 DDCLS
.ENDS C1

16
testdata/algo/nwriter5_au.txt vendored Normal file
View File

@ -0,0 +1,16 @@
* written by unit test
* cell C1
* pin p1
* pin p2
* pin p3
.SUBCKT C1 1 2 4
* net 1 n1
* net 2 n2
* net 3 n3
* net 4 n4
* device instance $1 0,0 M3CLS
M$1 1 4 3 1 MM3CLS L=0.25U W=0.18U AS=1.2U AD=0.75U
* device instance $2 0,0 M3CLS
M$2 3 4 2 3 MM3CLS L=1.4U W=0.25U AS=1.3U AD=0.85U
.ENDS C1

18
testdata/algo/nwriter6_au.txt vendored Normal file
View File

@ -0,0 +1,18 @@
* written by unit test
* cell C1
* pin p1
* pin p2
* pin p3
* pin p4
.SUBCKT C1 1 2 4 5
* net 1 n1
* net 2 n2
* net 3 n3
* net 4 n4
* net 5 n5
* device instance $1 0,0 M4CLS
M$1 1 4 3 5 1 MM4CLS L=0.25U W=0.18U AS=1.2U AD=0.75U
* device instance $2 0,0 M4CLS
M$2 3 4 2 5 3 MM4CLS L=1.4U W=0.25U AS=1.3U AD=0.85U
.ENDS C1

14
testdata/algo/nwriter7_au.txt vendored Normal file
View File

@ -0,0 +1,14 @@
* written by unit test
* cell C1
* pin p1
* pin p2
.SUBCKT C1 1 2
* net 1 n1
* net 2 n2
* net 3 n3
* device instance $1 0,0 XCLS
XD_$1 1 3 XCLS PARAMS: U=-17 V=42
* device instance $2 0,0 XCLS
XD_$2 3 2 XCLS PARAMS: U=17 V=-42
.ENDS C1

34
testdata/algo/nwriter8_au.txt vendored Normal file
View File

@ -0,0 +1,34 @@
* written by unit test
* cell C2
* pin p1
* pin p2
* pin p3
.SUBCKT C2 1 2 4
* net 1 n1
* net 2 n2
* net 3 n3
* net 4 n4
* net 5 n5
* cell instance SC1 r0 *1 0,0
XSC1 1 3 4 3 C1
* cell instance SC2 r0 *1 0,0
XSC2 3 2 4 3 C1
.ENDS C2
* cell C1
* pin p1
* pin p2
* pin p3
* pin p4
.SUBCKT C1 1 2 4 5
* net 1 n1
* net 2 n2
* net 3 n3
* net 4 n4
* net 5 n5
* device instance $1 0,0 M4CLS
M$1 1 4 3 5 1 MM4CLS L=0.25U W=0.18U AS=1.2U AD=0.75U
* device instance $2 0,0 M4CLS
M$2 3 4 2 5 3 MM4CLS L=1.4U W=0.25U AS=1.3U AD=0.85U
.ENDS C1

13
testdata/algo/nwriter_rba1_au.txt vendored Normal file
View File

@ -0,0 +1,13 @@
* cell C1
* pin p1
* pin p2
.SUBCKT C1 1 2
* net 1 n1
* net 2 n2
* net 3 n3
* device instance $1 0,0 RCLS
R$1 1 3 1.7
* device instance $2 0,0 RCLS
R$2 3 2 4.2e-05
.ENDS C1

29
testdata/algo/nwriter_rba2_au.txt vendored Normal file
View File

@ -0,0 +1,29 @@
* A comment
*** My special header
* My intro for class RCLS
* My intro for class LCLS
* My intro for class CCLS
* My intro for class DCLS
* My intro for class M3CLS
* My intro for class M4CLS
* cell C1
* pin p1
* pin p2
.SUBCKT C1 1 2
* net 1 n1
* net 2 n2
* net 3 n3
* device instance $1 0,0 RCLS
* Before device $0
* Terminal #1: 0
* Terminal #2: 0
R$0 0 0 1.7
* After device $0
* device instance $2 0,0 RCLS
* Before device $0
* Terminal #1: 0
* Terminal #2: 0
R$0 0 0 4.2e-05
* After device $0
.ENDS C1

127
testdata/ruby/dbNetlistWriterTests.rb vendored Normal file
View File

@ -0,0 +1,127 @@
# encoding: UTF-8
# KLayout Layout Viewer
# Copyright (C) 2006-2019 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
if !$:.member?(File::dirname($0))
$:.push(File::dirname($0))
end
load("test_prologue.rb")
class MyDelegate < RBA::NetlistSpiceWriterDelegate
def write_header
emit_line("*** My special header")
end
def write_device_intro(cls)
emit_comment("My intro for class " + cls.name)
end
def write_device(dev)
emit_comment("Before device " + dev.expanded_name)
emit_comment("Terminal #1: " + net_to_string(dev.net_for_terminal(0)))
emit_comment("Terminal #2: " + net_to_string(dev.net_for_terminal(1)))
super(dev)
emit_comment("After device " + dev.expanded_name)
end
end
class DBLayoutToNetlist_TestClass < TestBase
def test_1_Basic
nl = RBA::Netlist::new
rcls = RBA::DeviceClassResistor::new
ccls = RBA::DeviceClassCapacitor::new
lcls = RBA::DeviceClassInductor::new
dcls = RBA::DeviceClassDiode::new
m3cls = RBA::DeviceClassMOS3Transistor::new
m4cls = RBA::DeviceClassMOS4Transistor::new
rcls.name = "RCLS"
lcls.name = "LCLS"
ccls.name = "CCLS"
dcls.name = "DCLS"
m3cls.name = "M3CLS"
m4cls.name = "M4CLS"
nl.add(rcls)
nl.add(lcls)
nl.add(ccls)
nl.add(dcls)
nl.add(m3cls)
nl.add(m4cls)
circuit1 = RBA::Circuit::new
circuit1.name = "C1"
nl.add(circuit1)
n1 = circuit1.create_net("n1")
n2 = circuit1.create_net("n2")
n3 = circuit1.create_net("n3")
rdev1 = circuit1.create_device(rcls)
rdev1.set_parameter(RBA::DeviceClassResistor::PARAM_R, 1.7)
rdev2 = circuit1.create_device(rcls)
rdev2.set_parameter(RBA::DeviceClassResistor::PARAM_R, 42e-6)
pid1 = circuit1.create_pin("p1").id
pid2 = circuit1.create_pin("p2").id
circuit1.connect_pin(pid1, n1)
circuit1.connect_pin(pid2, n2)
rdev1.connect_terminal("A", n1)
rdev1.connect_terminal("B", n3)
rdev2.connect_terminal("A", n3)
rdev2.connect_terminal("B", n2)
# verify against the input
input = File.join($ut_testsrc, "testdata", "algo", "nwriter_rba1_au.txt")
writer = RBA::NetlistSpiceWriter::new
tmp = File::join($ut_testtmp, "tmp1.txt")
nl.write(tmp, writer)
assert_equal(File.open(tmp, "r").read, File.open(input, "r").read)
# verify against the input with delegate
input = File.join($ut_testsrc, "testdata", "algo", "nwriter_rba2_au.txt")
mydelegate = MyDelegate::new
writer = RBA::NetlistSpiceWriter::new(mydelegate)
# the delegate is kept by the SPICE writer ..
mydelegate = nil
GC.start
tmp = File::join($ut_testtmp, "tmp2.txt")
nl.write(tmp, writer, "A comment")
assert_equal(File.open(tmp, "r").read, File.open(input, "r").read)
end
end
load("test_epilogue.rb")