From 4068478887262abc55dafe3d7621bc0a12575b8d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 31 Jan 2019 00:07:10 +0100 Subject: [PATCH] Implemented SPICE writer + tests. --- src/db/db/db.pro | 8 +- src/db/db/dbNetlistSpiceWriter.cc | 402 ++++++++++ src/db/db/dbNetlistSpiceWriter.h | 111 +++ src/db/db/dbNetlistWriter.cc | 31 + src/db/db/dbNetlistWriter.h | 90 +++ src/db/db/gsiDeclDbNetlist.cc | 241 +++++- src/db/unit_tests/dbNetlistTests.cc | 1 + src/db/unit_tests/dbNetlistWriterTests.cc | 919 ++++++++++++++++++++++ src/db/unit_tests/unit_tests.pro | 3 +- src/rba/unit_tests/rba.cc | 1 + testdata/algo/nwriter10_au.txt | 413 ++++++++++ testdata/algo/nwriter1_au.txt | 14 + testdata/algo/nwriter20_au.txt | 25 + testdata/algo/nwriter2_au.txt | 14 + testdata/algo/nwriter3_au.txt | 14 + testdata/algo/nwriter4_au.txt | 14 + testdata/algo/nwriter5_au.txt | 16 + testdata/algo/nwriter6_au.txt | 18 + testdata/algo/nwriter7_au.txt | 14 + testdata/algo/nwriter8_au.txt | 34 + testdata/algo/nwriter_rba1_au.txt | 13 + testdata/algo/nwriter_rba2_au.txt | 29 + testdata/ruby/dbNetlistWriterTests.rb | 127 +++ 23 files changed, 2541 insertions(+), 11 deletions(-) create mode 100644 src/db/db/dbNetlistSpiceWriter.cc create mode 100644 src/db/db/dbNetlistSpiceWriter.h create mode 100644 src/db/db/dbNetlistWriter.cc create mode 100644 src/db/db/dbNetlistWriter.h create mode 100644 src/db/unit_tests/dbNetlistWriterTests.cc create mode 100644 testdata/algo/nwriter10_au.txt create mode 100644 testdata/algo/nwriter1_au.txt create mode 100644 testdata/algo/nwriter20_au.txt create mode 100644 testdata/algo/nwriter2_au.txt create mode 100644 testdata/algo/nwriter3_au.txt create mode 100644 testdata/algo/nwriter4_au.txt create mode 100644 testdata/algo/nwriter5_au.txt create mode 100644 testdata/algo/nwriter6_au.txt create mode 100644 testdata/algo/nwriter7_au.txt create mode 100644 testdata/algo/nwriter8_au.txt create mode 100644 testdata/algo/nwriter_rba1_au.txt create mode 100644 testdata/algo/nwriter_rba2_au.txt create mode 100644 testdata/ruby/dbNetlistWriterTests.rb diff --git a/src/db/db/db.pro b/src/db/db/db.pro index ef6d26aa5..53e212822 100644 --- a/src/db/db/db.pro +++ b/src/db/db/db.pro @@ -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") { diff --git a/src/db/db/dbNetlistSpiceWriter.cc b/src/db/db/dbNetlistSpiceWriter.cc new file mode 100644 index 000000000..338edce2b --- /dev/null +++ b/src/db/db/dbNetlistSpiceWriter.cc @@ -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 + +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 (dc); + const db::DeviceClassInductor *ind = dynamic_cast (dc); + const db::DeviceClassResistor *res = dynamic_cast (dc); + const db::DeviceClassDiode *diode = dynamic_cast (dc); + const db::DeviceClassMOS3Transistor *mos3 = dynamic_cast (dc); + const db::DeviceClassMOS4Transistor *mos4 = dynamic_cast (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 &td = dev.device_class ()->terminal_definitions (); + for (std::vector::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 &pd = dev.device_class ()->parameter_definitions (); + for (std::vector::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_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 ())); +} + +} diff --git a/src/db/db/dbNetlistSpiceWriter.h b/src/db/db/dbNetlistSpiceWriter.h new file mode 100644 index 000000000..d8dd294d3 --- /dev/null +++ b/src/db/db/dbNetlistSpiceWriter.h @@ -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 +#include + +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 mp_delegate; + std::map 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 diff --git a/src/db/db/dbNetlistWriter.cc b/src/db/db/dbNetlistWriter.cc new file mode 100644 index 000000000..2c40a9c78 --- /dev/null +++ b/src/db/db/dbNetlistWriter.cc @@ -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 .. + +} diff --git a/src/db/db/dbNetlistWriter.h b/src/db/db/dbNetlistWriter.h new file mode 100644 index 000000000..2324f7454 --- /dev/null +++ b/src/db/db/dbNetlistWriter.h @@ -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 + +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 + : public tl::type_traits +{ + typedef tl::false_tag has_default_constructor; + typedef tl::false_tag has_copy_constructor; +}; + +} + +#endif diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 67954efcd..c572a3543 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -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 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 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 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 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 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 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 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 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 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::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::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::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 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 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", + gsi::Methods (), + "@hide\n" +); + +Class 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." +); + } diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 725573803..4d85928cd 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -1078,3 +1078,4 @@ TEST(13_DeviceAbstract) EXPECT_EQ (nl.begin_device_abstracts () == nl.end_device_abstracts (), true); } + diff --git a/src/db/unit_tests/dbNetlistWriterTests.cc b/src/db/unit_tests/dbNetlistWriterTests.cc new file mode 100644 index 000000000..24ec3f413 --- /dev/null +++ b/src/db/unit_tests/dbNetlistWriterTests.cc @@ -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))); + } +} + diff --git a/src/db/unit_tests/unit_tests.pro b/src/db/unit_tests/unit_tests.pro index 460b684a1..df7405b8c 100644 --- a/src/db/unit_tests/unit_tests.pro +++ b/src/db/unit_tests/unit_tests.pro @@ -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 diff --git a/src/rba/unit_tests/rba.cc b/src/rba/unit_tests/rba.cc index f3e9cef00..5c6d4175e 100644 --- a/src/rba/unit_tests/rba.cc +++ b/src/rba/unit_tests/rba.cc @@ -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") diff --git a/testdata/algo/nwriter10_au.txt b/testdata/algo/nwriter10_au.txt new file mode 100644 index 000000000..4b1c13f9d --- /dev/null +++ b/testdata/algo/nwriter10_au.txt @@ -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 diff --git a/testdata/algo/nwriter1_au.txt b/testdata/algo/nwriter1_au.txt new file mode 100644 index 000000000..54205fa66 --- /dev/null +++ b/testdata/algo/nwriter1_au.txt @@ -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 diff --git a/testdata/algo/nwriter20_au.txt b/testdata/algo/nwriter20_au.txt new file mode 100644 index 000000000..f120641fa --- /dev/null +++ b/testdata/algo/nwriter20_au.txt @@ -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 diff --git a/testdata/algo/nwriter2_au.txt b/testdata/algo/nwriter2_au.txt new file mode 100644 index 000000000..661626646 --- /dev/null +++ b/testdata/algo/nwriter2_au.txt @@ -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 diff --git a/testdata/algo/nwriter3_au.txt b/testdata/algo/nwriter3_au.txt new file mode 100644 index 000000000..acaf6fb6a --- /dev/null +++ b/testdata/algo/nwriter3_au.txt @@ -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 diff --git a/testdata/algo/nwriter4_au.txt b/testdata/algo/nwriter4_au.txt new file mode 100644 index 000000000..8622bc7f1 --- /dev/null +++ b/testdata/algo/nwriter4_au.txt @@ -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 diff --git a/testdata/algo/nwriter5_au.txt b/testdata/algo/nwriter5_au.txt new file mode 100644 index 000000000..2f6212938 --- /dev/null +++ b/testdata/algo/nwriter5_au.txt @@ -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 diff --git a/testdata/algo/nwriter6_au.txt b/testdata/algo/nwriter6_au.txt new file mode 100644 index 000000000..caae620b9 --- /dev/null +++ b/testdata/algo/nwriter6_au.txt @@ -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 diff --git a/testdata/algo/nwriter7_au.txt b/testdata/algo/nwriter7_au.txt new file mode 100644 index 000000000..8b4656dca --- /dev/null +++ b/testdata/algo/nwriter7_au.txt @@ -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 diff --git a/testdata/algo/nwriter8_au.txt b/testdata/algo/nwriter8_au.txt new file mode 100644 index 000000000..b17e53ee2 --- /dev/null +++ b/testdata/algo/nwriter8_au.txt @@ -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 diff --git a/testdata/algo/nwriter_rba1_au.txt b/testdata/algo/nwriter_rba1_au.txt new file mode 100644 index 000000000..17383a7b0 --- /dev/null +++ b/testdata/algo/nwriter_rba1_au.txt @@ -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 diff --git a/testdata/algo/nwriter_rba2_au.txt b/testdata/algo/nwriter_rba2_au.txt new file mode 100644 index 000000000..6e4a8506b --- /dev/null +++ b/testdata/algo/nwriter_rba2_au.txt @@ -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 diff --git a/testdata/ruby/dbNetlistWriterTests.rb b/testdata/ruby/dbNetlistWriterTests.rb new file mode 100644 index 000000000..5e02a0561 --- /dev/null +++ b/testdata/ruby/dbNetlistWriterTests.rb @@ -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") + +