mirror of https://github.com/KLayout/klayout.git
Implemented SPICE writer + tests.
This commit is contained in:
parent
7d06ea83c1
commit
4068478887
|
|
@ -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") {
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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 ..
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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."
|
||||
);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1078,3 +1078,4 @@ TEST(13_DeviceAbstract)
|
|||
|
||||
EXPECT_EQ (nl.begin_device_abstracts () == nl.end_device_abstracts (), true);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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")
|
||||
|
||||
|
||||
Loading…
Reference in New Issue