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 \
dbDeviceAbstract.cc \
dbLocalOperationUtils.cc \
gsiDeclDbDeepShapeStore.cc
gsiDeclDbDeepShapeStore.cc \
dbNetlistSpiceWriter.cc \
dbNetlistWriter.cc
HEADERS = \
dbArray.h \
@ -293,7 +295,9 @@ HEADERS = \
dbLayoutToNetlistFormatDefs.h \
dbDeviceAbstract.h \
dbLocalOperationUtils.h \
dbDeepRegion.h
dbDeepRegion.h \
dbNetlistSpiceWriter.h \
dbNetlistWriter.h
!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 "dbNetlist.h"
#include "dbNetlistWriter.h"
#include "dbNetlistSpiceWriter.h"
#include "tlException.h"
#include "tlInternational.h"
#include "tlStream.h"
namespace gsi
{
@ -86,6 +89,10 @@ Class<db::Device> decl_dbDevice ("db", "Device",
gsi::method ("name", &db::Device::name,
"@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"),
"@brief Gets the net connected to the specified terminal.\n"
"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,
"@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"),
"@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."
@ -465,9 +476,15 @@ Class<db::DeviceClass> decl_dbDeviceClass ("db", "DeviceClass",
gsi::method ("name", &db::DeviceClass::name,
"@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,
"@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,
"@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,
"@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"),
"@brief Combines two devices.\n"
"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)
{
tl_assert (c != 0);
c->keep ();
nl->add_circuit (c);
}
static void add_device_class (db::Netlist *nl, db::DeviceClass *cl)
{
tl_assert (cl != 0);
cl->keep ();
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",
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"
"This method will add the given circuit object to 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,
"@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"
"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."
@ -978,6 +998,11 @@ Class<db::Netlist> decl_dbNetlist ("db", "Netlist",
"@brief Purges floating nets.\n"
"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."
) +
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"
"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."
);
/**
* @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);
}

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 \
dbLayoutToNetlistTests.cc \
dbLayoutToNetlistWriterTests.cc \
dbLayoutToNetlistReaderTests.cc
dbLayoutToNetlistReaderTests.cc \
dbNetlistWriterTests.cc
INCLUDEPATH += $$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 (dbNetlist, "dbNetlist.rb")
RUBYTEST (dbNetlistDeviceClasses, "dbNetlistDeviceClasses.rb")
RUBYTEST (dbNetlistWriterTests, "dbNetlistWriterTests.rb")
RUBYTEST (dbPathTest, "dbPathTest.rb")
RUBYTEST (dbPCells, "dbPCells.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")