summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/netlist/tools/nl_convert.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/netlist/tools/nl_convert.cpp')
-rw-r--r--src/lib/netlist/tools/nl_convert.cpp459
1 files changed, 459 insertions, 0 deletions
diff --git a/src/lib/netlist/tools/nl_convert.cpp b/src/lib/netlist/tools/nl_convert.cpp
new file mode 100644
index 00000000000..dae68968445
--- /dev/null
+++ b/src/lib/netlist/tools/nl_convert.cpp
@@ -0,0 +1,459 @@
+// license:GPL-2.0+
+// copyright-holders:Couriersud
+/*
+ * nl_convert.c
+ *
+ */
+
+#include <algorithm>
+#include <cstdio>
+#include <cmath>
+#include "nl_convert.h"
+
+
+// for now, make buggy GCC/Mingw STFU about I64FMT
+#if (defined(__MINGW32__) && (__GNUC__ >= 5))
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat"
+#pragma GCC diagnostic ignored "-Wformat-extra-args"
+#endif
+
+template<typename Class>
+static plist_t<int> bubble(const pnamedlist_t<Class *> &sl)
+{
+ plist_t<int> ret(sl.size());
+ for (unsigned i=0; i<sl.size(); i++)
+ ret[i] = i;
+
+ for(unsigned i=0; i < sl.size(); i++)
+ {
+ for(unsigned j=i+1; j < sl.size(); j++)
+ {
+ if(sl[ret[i]]->name() > sl[ret[j]]->name())
+ {
+ std::swap(ret[i], ret[j]);
+ }
+ }
+ }
+ return ret;
+}
+
+/*-------------------------------------------------
+ convert - convert a spice netlist
+-------------------------------------------------*/
+
+void nl_convert_base_t::add_pin_alias(const pstring &devname, const pstring &name, const pstring &alias)
+{
+ m_pins.add(palloc(pin_alias_t(devname + "." + name, devname + "." + alias)), false);
+}
+
+void nl_convert_base_t::add_ext_alias(const pstring &alias)
+{
+ m_ext_alias.add(alias);
+}
+
+void nl_convert_base_t::add_device(const pstring &atype, const pstring &aname, const pstring &amodel)
+{
+ m_devs.add(palloc(dev_t(atype, aname, amodel)), false);
+}
+void nl_convert_base_t::add_device(const pstring &atype, const pstring &aname, double aval)
+{
+ m_devs.add(palloc(dev_t(atype, aname, aval)), false);
+}
+void nl_convert_base_t::add_device(const pstring &atype, const pstring &aname)
+{
+ m_devs.add(palloc(dev_t(atype, aname)), false);
+}
+
+void nl_convert_base_t::add_term(pstring netname, pstring termname)
+{
+ net_t * net = m_nets.find_by_name(netname);
+ if (net == NULL)
+ {
+ net = palloc(net_t(netname));
+ m_nets.add(net, false);
+ }
+
+ /* if there is a pin alias, translate ... */
+ pin_alias_t *alias = m_pins.find_by_name(termname);
+
+ if (alias != NULL)
+ net->terminals().add(alias->alias());
+ else
+ net->terminals().add(termname);
+}
+
+void nl_convert_base_t::dump_nl()
+{
+ for (std::size_t i=0; i<m_ext_alias.size(); i++)
+ {
+ net_t *net = m_nets.find_by_name(m_ext_alias[i]);
+ // use the first terminal ...
+ out("ALIAS({}, {})\n", m_ext_alias[i].cstr(), net->terminals()[0].cstr());
+ // if the aliased net only has this one terminal connected ==> don't dump
+ if (net->terminals().size() == 1)
+ net->set_no_export();
+ }
+ plist_t<int> sorted = bubble(m_devs);
+ for (std::size_t i=0; i<m_devs.size(); i++)
+ {
+ std::size_t j = sorted[i];
+
+ if (m_devs[j]->has_value())
+ out("{}({}, {})\n", m_devs[j]->type().cstr(),
+ m_devs[j]->name().cstr(), get_nl_val(m_devs[j]->value()).cstr());
+ else if (m_devs[j]->has_model())
+ out("{}({}, \"{}\")\n", m_devs[j]->type().cstr(),
+ m_devs[j]->name().cstr(), m_devs[j]->model().cstr());
+ else
+ out("{}({})\n", m_devs[j]->type().cstr(),
+ m_devs[j]->name().cstr());
+ }
+ // print nets
+ for (std::size_t i=0; i<m_nets.size(); i++)
+ {
+ net_t * net = m_nets[i];
+ if (!net->is_no_export())
+ {
+ //printf("Net {}\n", net->name().cstr());
+ out("NET_C({}", net->terminals()[0].cstr() );
+ for (std::size_t j=1; j<net->terminals().size(); j++)
+ {
+ out(", {}", net->terminals()[j].cstr() );
+ }
+ out(")\n");
+ }
+ }
+ m_devs.clear_and_free();
+ m_nets.clear_and_free();
+ m_pins.clear_and_free();
+ m_ext_alias.clear();
+}
+
+const pstring nl_convert_base_t::get_nl_val(const double val)
+{
+ {
+ int i = 0;
+ while (m_units[i].m_unit != "-" )
+ {
+ if (m_units[i].m_mult <= std::abs(val))
+ break;
+ i++;
+ }
+ return pfmt(m_units[i].m_func.cstr())(val / m_units[i].m_mult);
+ }
+}
+double nl_convert_base_t::get_sp_unit(const pstring &unit)
+{
+ int i = 0;
+ while (m_units[i].m_unit != "-")
+ {
+ if (m_units[i].m_unit == unit)
+ return m_units[i].m_mult;
+ i++;
+ }
+ fprintf(stderr, "Unit %s unknown\n", unit.cstr());
+ return 0.0;
+}
+
+double nl_convert_base_t::get_sp_val(const pstring &sin)
+{
+ int p = sin.len() - 1;
+ while (p>=0 && (sin.substr(p,1) < "0" || sin.substr(p,1) > "9"))
+ p--;
+ pstring val = sin.substr(0,p + 1);
+ pstring unit = sin.substr(p + 1);
+
+ double ret = get_sp_unit(unit) * val.as_double();
+ return ret;
+}
+
+nl_convert_base_t::unit_t nl_convert_base_t::m_units[] = {
+ {"T", "", 1.0e12 },
+ {"G", "", 1.0e9 },
+ {"MEG", "RES_M({1})", 1.0e6 },
+ {"k", "RES_K({1})", 1.0e3 }, /* eagle */
+ {"K", "RES_K({1})", 1.0e3 },
+ {"", "{1}", 1.0e0 },
+ {"M", "CAP_M({1})", 1.0e-3 },
+ {"u", "CAP_U({1})", 1.0e-6 }, /* eagle */
+ {"U", "CAP_U({1})", 1.0e-6 },
+ {"??", "CAP_U({1})", 1.0e-6 },
+ {"N", "CAP_N({1})", 1.0e-9 },
+ {"P", "CAP_P({1})", 1.0e-12},
+ {"F", "{1}e-15", 1.0e-15},
+
+ {"MIL", "{1}", 25.4e-6},
+
+ {"-", "{1}", 1.0 }
+};
+
+
+void nl_convert_spice_t::convert(const pstring &contents)
+{
+ pstring_list_t spnl(contents, "\n");
+
+ // Add gnd net
+
+ // FIXME: Parameter
+ out("NETLIST_START(dummy)\n");
+ add_term("0", "GND");
+
+ pstring line = "";
+
+ for (std::size_t i=0; i < spnl.size(); i++)
+ {
+ // Basic preprocessing
+ pstring inl = spnl[i].trim().ucase();
+ if (inl.startsWith("+"))
+ line = line + inl.substr(1);
+ else
+ {
+ process_line(line);
+ line = inl;
+ }
+ }
+ process_line(line);
+ dump_nl();
+ // FIXME: Parameter
+ out("NETLIST_END()\n");
+}
+
+void nl_convert_spice_t::process_line(const pstring &line)
+{
+ if (line != "")
+ {
+ pstring_list_t tt(line, " ", true);
+ double val = 0.0;
+ switch (tt[0].code_at(0))
+ {
+ case ';':
+ out("// {}\n", line.substr(1).cstr());
+ break;
+ case '*':
+ out("// {}\n", line.substr(1).cstr());
+ break;
+ case '.':
+ if (tt[0].equals(".SUBCKT"))
+ {
+ out("NETLIST_START({})\n", tt[1].cstr());
+ for (std::size_t i=2; i<tt.size(); i++)
+ add_ext_alias(tt[i]);
+ }
+ else if (tt[0].equals(".ENDS"))
+ {
+ dump_nl();
+ out("NETLIST_END()\n");
+ }
+ else
+ out("// {}\n", line.cstr());
+ break;
+ case 'Q':
+ {
+ bool cerr = false;
+ /* check for fourth terminal ... should be numeric net
+ * including "0" or start with "N" (ltspice)
+ */
+ ATTR_UNUSED int nval =tt[4].as_long(&cerr);
+ pstring model;
+ pstring pins ="CBE";
+
+ if ((!cerr || tt[4].startsWith("N")) && tt.size() > 5)
+ model = tt[5];
+ else
+ model = tt[4];
+ pstring_list_t m(model,"{");
+ if (m.size() == 2)
+ {
+ if (m[1].len() != 4)
+ fprintf(stderr, "error with model desc %s\n", model.cstr());
+ pins = m[1].left(3);
+ }
+ add_device("QBJT_EB", tt[0], m[0]);
+ add_term(tt[1], tt[0] + "." + pins.code_at(0));
+ add_term(tt[2], tt[0] + "." + pins.code_at(1));
+ add_term(tt[3], tt[0] + "." + pins.code_at(2));
+ }
+ break;
+ case 'R':
+ if (tt[0].startsWith("RV"))
+ {
+ val = get_sp_val(tt[4]);
+ add_device("POT", tt[0], val);
+ add_term(tt[1], tt[0] + ".1");
+ add_term(tt[2], tt[0] + ".2");
+ add_term(tt[3], tt[0] + ".3");
+ }
+ else
+ {
+ val = get_sp_val(tt[3]);
+ add_device("RES", tt[0], val);
+ add_term(tt[1], tt[0] + ".1");
+ add_term(tt[2], tt[0] + ".2");
+ }
+ break;
+ case 'C':
+ val = get_sp_val(tt[3]);
+ add_device("CAP", tt[0], val);
+ add_term(tt[1], tt[0] + ".1");
+ add_term(tt[2], tt[0] + ".2");
+ break;
+ case 'V':
+ // just simple Voltage sources ....
+ if (tt[2].equals("0"))
+ {
+ val = get_sp_val(tt[3]);
+ add_device("ANALOG_INPUT", tt[0], val);
+ add_term(tt[1], tt[0] + ".Q");
+ //add_term(tt[2], tt[0] + ".2");
+ }
+ else
+ fprintf(stderr, "Voltage Source %s not connected to GND\n", tt[0].cstr());
+ break;
+ case 'I': // Input pin special notation
+ {
+ val = get_sp_val(tt[2]);
+ add_device("ANALOG_INPUT", tt[0], val);
+ add_term(tt[1], tt[0] + ".Q");
+ }
+ break;
+ case 'D':
+ add_device("DIODE", tt[0], tt[3]);
+ /* FIXME ==> does Kicad use different notation from LTSPICE */
+ add_term(tt[1], tt[0] + ".K");
+ add_term(tt[2], tt[0] + ".A");
+ break;
+ case 'U':
+ case 'X':
+ {
+ // FIXME: specific code for KICAD exports
+ // last element is component type
+ // FIXME: Parameter
+
+ pstring xname = tt[0].replace(".", "_");
+ pstring tname = "TTL_" + tt[tt.size()-1] + "_DIP";
+ add_device(tname, xname);
+ for (std::size_t i=1; i < tt.size() - 1; i++)
+ {
+ pstring term = pfmt("{1}.{2}")(xname)(i);
+ add_term(tt[i], term);
+ }
+ break;
+ }
+ default:
+ out("// IGNORED {}: {}\n", tt[0].cstr(), line.cstr());
+ }
+ }
+}
+
+//FIXME: should accept a stream as well
+void nl_convert_eagle_t::convert(const pstring &contents)
+{
+ pistringstream istrm(contents);
+ eagle_tokenizer tok(*this, istrm);
+
+ out("NETLIST_START(dummy)\n");
+ add_term("GND", "GND");
+ add_term("VCC", "VCC");
+ eagle_tokenizer::token_t token = tok.get_token();
+ while (true)
+ {
+ if (token.is_type(eagle_tokenizer::ENDOFFILE))
+ {
+ dump_nl();
+ // FIXME: Parameter
+ out("NETLIST_END()\n");
+ return;
+ }
+ else if (token.is(tok.m_tok_SEMICOLON))
+ {
+ /* ignore empty statements */
+ token = tok.get_token();
+ }
+ else if (token.is(tok.m_tok_ADD))
+ {
+ pstring name = tok.get_string();
+ /* skip to semicolon */
+ do
+ {
+ token = tok.get_token();
+ } while (!token.is(tok.m_tok_SEMICOLON));
+ token = tok.get_token();
+ pstring sval = "";
+ if (token.is(tok.m_tok_VALUE))
+ {
+ pstring vname = tok.get_string();
+ sval = tok.get_string();
+ tok.require_token(tok.m_tok_SEMICOLON);
+ token = tok.get_token();
+ }
+ switch (name.code_at(0))
+ {
+ case 'Q':
+ {
+ add_device("QBJT", name, sval);
+ }
+ break;
+ case 'R':
+ {
+ double val = get_sp_val(sval);
+ add_device("RES", name, val);
+ }
+ break;
+ case 'C':
+ {
+ double val = get_sp_val(sval);
+ add_device("CAP", name, val);
+ }
+ break;
+ case 'P':
+ if (sval.ucase() == "HIGH")
+ add_device("TTL_INPUT", name, 1);
+ else if (sval.ucase() == "LOW")
+ add_device("TTL_INPUT", name, 0);
+ else
+ add_device("ANALOG_INPUT", name, sval.as_double());
+ add_pin_alias(name, "1", "Q");
+ break;
+ case 'D':
+ /* Pin 1 = Anode, Pin 2 = Cathode */
+ add_device("DIODE", name, sval);
+ add_pin_alias(name, "1", "A");
+ add_pin_alias(name, "2", "K");
+ break;
+ case 'U':
+ case 'X':
+ {
+ pstring tname = "TTL_" + sval + "_DIP";
+ add_device(tname, name);
+ break;
+ }
+ default:
+ tok.error("// IGNORED " + name);
+ }
+
+ }
+ else if (token.is(tok.m_tok_SIGNAL))
+ {
+ pstring netname = tok.get_string();
+ token = tok.get_token();
+ while (!token.is(tok.m_tok_SEMICOLON))
+ {
+ /* fixme: should check for string */
+ pstring devname = token.str();
+ pstring pin = tok.get_string();
+ add_term(netname, devname + "." + pin);
+ token = tok.get_token(); }
+ }
+ else
+ {
+ out("Unexpected {}\n", token.str().cstr());
+ return;
+ }
+ }
+
+}
+
+#if (defined(__MINGW32__) && (__GNUC__ >= 5))
+#pragma GCC diagnostic pop
+#endif