mirror of https://github.com/KLayout/klayout.git
commit
d1acd722ad
|
|
@ -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,48 @@ void NetlistSpiceWriter::write (tl::OutputStream &stream, const db::Netlist &net
|
|||
|
||||
std::string NetlistSpiceWriter::net_to_string (const db::Net *net) const
|
||||
{
|
||||
std::map<const db::Net *, size_t>::const_iterator n = m_net_to_spice_id.find (net);
|
||||
if (! net || n == m_net_to_spice_id.end ()) {
|
||||
// TODO: this should assert or similar
|
||||
return "0";
|
||||
if (m_use_net_names) {
|
||||
|
||||
if (! net) {
|
||||
|
||||
return "0";
|
||||
|
||||
} 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 () + 1);
|
||||
if (!isalnum (*n.c_str ())) {
|
||||
nn += "\\";
|
||||
}
|
||||
for (const char *cp = n.c_str (); *cp; ++cp) {
|
||||
if (! isalnum (*cp) && strchr (".$!&\\#+:,", *cp) == 0) {
|
||||
nn += tl::sprintf ("\\x%02x", (unsigned char) *cp);
|
||||
} else if (*cp == ',') {
|
||||
nn += "|";
|
||||
} else {
|
||||
nn += *cp;
|
||||
}
|
||||
}
|
||||
|
||||
return nn;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
return tl::to_string (n->second);
|
||||
|
||||
std::map<const db::Net *, size_t>::const_iterator n = m_net_to_spice_id.find (net);
|
||||
if (! net || n == m_net_to_spice_id.end ()) {
|
||||
// TODO: this should assert or similar
|
||||
return "0";
|
||||
} else {
|
||||
return tl::to_string (n->second);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -389,9 +430,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 ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<NetlistSpiceWriterDelegate> mp_delegate;
|
||||
std::map<const db::Net *, size_t> m_net_to_spice_id;
|
||||
bool m_use_net_names;
|
||||
|
||||
void do_write (const std::string &description);
|
||||
|
||||
|
|
|
|||
|
|
@ -1168,6 +1168,13 @@ Class<db::NetlistSpiceWriter> 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"
|
||||
|
|
|
|||
|
|
@ -728,6 +728,103 @@ TEST(8_WriterSubcircuits)
|
|||
compare_netlists (_this, path, au_path);
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
Loading…
Reference in New Issue