From 9356f320269407f3ccd361d997298d357b07cabd Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Thu, 21 Mar 2019 23:34:16 +0100 Subject: [PATCH 1/2] Fixed issue-245 (support Spice netlist with names instead of numbers) The option is in the Spice writer (writer.use_net_names=true). --- src/db/db/dbNetlistSpiceWriter.cc | 52 +++++++++--- src/db/db/dbNetlistSpiceWriter.h | 7 ++ src/db/db/gsiDeclDbNetlist.cc | 7 ++ src/db/unit_tests/dbNetlistWriterTests.cc | 97 +++++++++++++++++++++++ testdata/algo/nwriter9_au.txt | 19 +++++ 5 files changed, 173 insertions(+), 9 deletions(-) create mode 100644 testdata/algo/nwriter9_au.txt diff --git a/src/db/db/dbNetlistSpiceWriter.cc b/src/db/db/dbNetlistSpiceWriter.cc index e2c7586fa..d12843ea3 100644 --- a/src/db/db/dbNetlistSpiceWriter.cc +++ b/src/db/db/dbNetlistSpiceWriter.cc @@ -194,7 +194,7 @@ std::string NetlistSpiceWriterDelegate::format_params (const db::Device &dev) co // -------------------------------------------------------------------------------- NetlistSpiceWriter::NetlistSpiceWriter (NetlistSpiceWriterDelegate *delegate) - : mp_netlist (0), mp_stream (0), mp_delegate (delegate) + : mp_netlist (0), mp_stream (0), mp_delegate (delegate), m_use_net_names (false) { static NetlistSpiceWriterDelegate std_delegate; if (! delegate) { @@ -207,6 +207,11 @@ NetlistSpiceWriter::~NetlistSpiceWriter () // .. nothing yet .. } +void NetlistSpiceWriter::set_use_net_names (bool use_net_names) +{ + m_use_net_names = use_net_names; +} + void NetlistSpiceWriter::write (tl::OutputStream &stream, const db::Netlist &netlist, const std::string &description) { mp_stream = &stream; @@ -233,12 +238,39 @@ void NetlistSpiceWriter::write (tl::OutputStream &stream, const db::Netlist &net 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"; + if (m_use_net_names) { + + if (! net) { + + return "0"; + + } else { + + std::string n = net->expanded_name (); + std::string nn; + nn.reserve (n.size ()); + for (const char *cp = n.c_str (); *cp; ++cp) { + if (isspace (*cp)) { + nn += tl::sprintf ("$x%02x", (unsigned char) *cp); + } else { + nn += *cp; + } + } + + return nn; + + } + } else { - return tl::to_string (n->second); + + 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); + } + } } @@ -389,9 +421,11 @@ void NetlistSpiceWriter::write_circuit_header (const db::Circuit &circuit) const 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 ()); + if (! m_use_net_names) { + 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 ()); + } } } } diff --git a/src/db/db/dbNetlistSpiceWriter.h b/src/db/db/dbNetlistSpiceWriter.h index 2a5dfb418..ac3e83e97 100644 --- a/src/db/db/dbNetlistSpiceWriter.h +++ b/src/db/db/dbNetlistSpiceWriter.h @@ -86,6 +86,12 @@ public: virtual void write (tl::OutputStream &stream, const db::Netlist &netlist, const std::string &description); + void set_use_net_names (bool use_net_names); + bool use_net_names () const + { + return m_use_net_names; + } + private: friend class NetlistSpiceWriterDelegate; @@ -93,6 +99,7 @@ private: tl::OutputStream *mp_stream; tl::weak_ptr mp_delegate; std::map m_net_to_spice_id; + bool m_use_net_names; void do_write (const std::string &description); diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 0b5b4d0d8..cbfe718bd 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -1168,6 +1168,13 @@ Class db_NetlistSpiceWriter (db_NetlistWriter, "db", "Ne ) + gsi::constructor ("new", &new_spice_writer2, "@brief Creates a new writer with a delegate.\n" + ) + + gsi::method ("use_net_names=", &db::NetlistSpiceWriter::set_use_net_names, gsi::arg ("f"), + "@brief Sets a value indicating whether to use net names (true) or net numbers (false).\n" + "The default is to use net numbers." + ) + + gsi::method ("use_net_names", &db::NetlistSpiceWriter::use_net_names, + "@brief Gets a value indicating whether to use net names (true) or net numbers (false).\n" ), "@brief Implements a netlist writer for the SPICE format.\n" "Provide a delegate for customizing the way devices are written.\n" diff --git a/src/db/unit_tests/dbNetlistWriterTests.cc b/src/db/unit_tests/dbNetlistWriterTests.cc index 2132169a4..d681eaabc 100644 --- a/src/db/unit_tests/dbNetlistWriterTests.cc +++ b/src/db/unit_tests/dbNetlistWriterTests.cc @@ -763,6 +763,103 @@ TEST(8_WriterSubcircuits) } } +TEST(9_WriterNetNamesInsteadOfNumbers) +{ + 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 ("N 2"); + 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); + } + + db::Circuit *circuit2 = new db::Circuit (); + circuit2->set_name ("C2"); + nl.add_circuit (circuit2); + + { + db::Net *n1, *n2; + n1 = new db::Net (); + n1->set_name ("n1"); + circuit2->add_net (n1); + n2 = new db::Net (); + n2->set_name ("n2"); + circuit2->add_net (n2); + + db::SubCircuit *sc1 = new db::SubCircuit (circuit1, "SC1"); + circuit2->add_subcircuit (sc1); + sc1->connect_pin (0, n1); + sc1->connect_pin (1, n2); + + size_t pid1 = circuit2->add_pin ("p1").id (); + size_t pid2 = circuit2->add_pin ("p2").id (); + + circuit2->connect_pin (pid1, n1); + circuit2->connect_pin (pid2, n2); + } + + // verify against the input + + std::string path = tmp_file ("tmp_nwriter9.txt"); + { + tl::OutputStream stream (path); + db::NetlistSpiceWriter writer; + writer.set_use_net_names (true); + 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"), "nwriter9_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; diff --git a/testdata/algo/nwriter9_au.txt b/testdata/algo/nwriter9_au.txt new file mode 100644 index 000000000..570272e40 --- /dev/null +++ b/testdata/algo/nwriter9_au.txt @@ -0,0 +1,19 @@ +* written by unit test + +* cell C2 +* pin p1 +* pin p2 +.SUBCKT C2 n1 n2 +* cell instance SC1 r0 *1 0,0 +XSC1 n1 n2 C1 +.ENDS C2 + +* cell C1 +* pin p1 +* pin p2 +.SUBCKT C1 N1 N$x202 +* device instance $1 0,0 XCLS +XD_$1 N1 n3 XCLS PARAMS: U=-17 V=42 +* device instance $2 0,0 XCLS +XD_$2 n3 N$x202 XCLS PARAMS: U=17 V=-42 +.ENDS C1 From e545d6af3fc0927eaa89229b318e5b31ad44ee8f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 22 Mar 2019 00:05:17 +0100 Subject: [PATCH 2/2] Refined solution for issue-245 by providing a better name mapping (checked with ngspice) --- src/db/db/dbNetlistSpiceWriter.cc | 15 ++++++++++++--- testdata/algo/nwriter9_au.txt | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/db/db/dbNetlistSpiceWriter.cc b/src/db/db/dbNetlistSpiceWriter.cc index d12843ea3..9bde4c2d0 100644 --- a/src/db/db/dbNetlistSpiceWriter.cc +++ b/src/db/db/dbNetlistSpiceWriter.cc @@ -246,12 +246,21 @@ std::string NetlistSpiceWriter::net_to_string (const db::Net *net) const } else { + // Tested with ngspice: this tool likes in net names: . $ ! & \ # + : | (but not at beginning) + // It does not like: , ; + // We translate , to | for the net separator + std::string n = net->expanded_name (); std::string nn; - nn.reserve (n.size ()); + nn.reserve (n.size () + 1); + if (!isalnum (*n.c_str ())) { + nn += "\\"; + } for (const char *cp = n.c_str (); *cp; ++cp) { - if (isspace (*cp)) { - nn += tl::sprintf ("$x%02x", (unsigned char) *cp); + if (! isalnum (*cp) && strchr (".$!&\\#+:,", *cp) == 0) { + nn += tl::sprintf ("\\x%02x", (unsigned char) *cp); + } else if (*cp == ',') { + nn += "|"; } else { nn += *cp; } diff --git a/testdata/algo/nwriter9_au.txt b/testdata/algo/nwriter9_au.txt index 570272e40..831158d81 100644 --- a/testdata/algo/nwriter9_au.txt +++ b/testdata/algo/nwriter9_au.txt @@ -11,9 +11,9 @@ XSC1 n1 n2 C1 * cell C1 * pin p1 * pin p2 -.SUBCKT C1 N1 N$x202 +.SUBCKT C1 N1 N\x202 * device instance $1 0,0 XCLS XD_$1 N1 n3 XCLS PARAMS: U=-17 V=42 * device instance $2 0,0 XCLS -XD_$2 n3 N$x202 XCLS PARAMS: U=17 V=-42 +XD_$2 n3 N\x202 XCLS PARAMS: U=17 V=-42 .ENDS C1