From f6a04074f63600f6971c42f3d0e33c69d6875458 Mon Sep 17 00:00:00 2001 From: couriersud Date: Sun, 26 Jan 2020 21:28:48 +0100 Subject: netlist: Fix/extend SPICE format conversion. [Couriersud] "./nltool -c convert -f UA741.mod" now runs without errors. UA741.mod is the official TI SPICE subckt for the 741 opamp. This commit fixes a number of issues and adds linear POLY support for E/F SPICE models. --- src/lib/netlist/tools/nl_convert.cpp | 208 +++++++++++++++++++++++++++++++---- src/lib/netlist/tools/nl_convert.h | 32 ++++++ 2 files changed, 220 insertions(+), 20 deletions(-) diff --git a/src/lib/netlist/tools/nl_convert.cpp b/src/lib/netlist/tools/nl_convert.cpp index 030eb6d47c9..0f59820a642 100644 --- a/src/lib/netlist/tools/nl_convert.cpp +++ b/src/lib/netlist/tools/nl_convert.cpp @@ -9,6 +9,7 @@ #include #include +#include // FIXME: temporarily defined here - should be in a file // FIXME: family logic in netlist is convoluted, create @@ -60,7 +61,7 @@ static lib_map_t read_lib_map(const pstring &lm) nl_convert_base_t::nl_convert_base_t() : out(&m_buf) - , m_numberchars("0123456789-+e.") + , m_numberchars("0123456789-+Ee.") { m_buf.imbue(std::locale::classic()); m_units = { @@ -83,6 +84,14 @@ nl_convert_base_t::nl_convert_base_t() {"MIL", "{1}", 25.4e-6} }; + dev_map = + { + { "VCCS", {"OP", "ON", "IP", "IN"} }, + { "VCVS", {"OP", "ON", "IP", "IN"} }, + { "CCCS", {"OP", "ON", "IP", "IN"} }, + { "CCVS", {"OP", "ON", "IP", "IN"} }, + { "VS", {"1", "2"} }, + }; } nl_convert_base_t::~nl_convert_base_t() @@ -149,8 +158,65 @@ void nl_convert_base_t::add_term(const pstring &netname, const pstring &termname net->terminals().push_back(termname); } +void nl_convert_base_t::add_term(const pstring &netname, const pstring &devname, unsigned term) +{ + auto e = dev_map.find(get_device(devname)->type()); + if (e == dev_map.end()) + out("// ERROR: No terminals found for device {}\n", devname); + else + { + if (term >= e->second.size()) + out("// ERROR: {} : Term {} exceeds number of terminals {}\n", netname, devname, term); + else + add_term(netname, devname + "." + e->second[term]); + } +} + +void nl_convert_base_t::add_device_extra_s(const pstring &devname, const pstring &extra) +{ + auto dev = get_device(devname); + if (dev == nullptr) + out("// ERROR: Device {} not found\n", devname); + else + { + dev->add_extra(extra); + } +} + + + void nl_convert_base_t::dump_nl() { + // do replacements + for (auto &r : m_replace) + { + // Get the device entry + auto *d = get_device(r.m_ce); + if (d == nullptr) + { + out("ERROR: Can not find <{}>\n", r.m_ce); + continue; + } + + auto e = dev_map.find(d->type()); + if (e == dev_map.end()) + { + out("ERROR: Can not find type {}\n", d->type()); + continue; + } + pstring term1 = r.m_ce + "." + e->second[0]; + // scan all nets + for (auto &n : m_nets) + { + for (auto &t : n.second->terminals()) + { + if (t == term1) + t = r.m_repterm; + } + } + add_term(r.m_net, term1); + } + for (auto & alias : m_ext_alias) { net_t *net = m_nets[alias].get(); @@ -180,6 +246,9 @@ void nl_convert_base_t::dump_nl() else out("{}({})\n", m_devs[j]->type().c_str(), m_devs[j]->name().c_str()); + for (auto &e : m_devs[j]->extra()) + out("{}\n", e); + } // print nets for (auto & i : m_nets) @@ -195,6 +264,7 @@ void nl_convert_base_t::dump_nl() out(")\n"); } } + m_replace.clear(); m_devs.clear(); m_nets.clear(); m_pins.clear(); @@ -293,6 +363,22 @@ static pstring rem(const std::vector &vps, std::size_t start) return r; } +static int npoly(const pstring &s) +{ + // Brute force + if (s=="POLY(1)") + return 1; + if (s=="POLY(2)") + return 2; + if (s=="POLY(3)") + return 3; + if (s=="POLY(4)") + return 4; + if (s=="POLY(5)") + return 5; + return -1; +} + void nl_convert_spice_t::process_line(const pstring &line) { if (line != "") @@ -386,24 +472,105 @@ void nl_convert_spice_t::process_line(const pstring &line) add_term(tt[2], tt[0] + ".N"); break; case 'E': - add_device("VCVS", tt[0]); + { + auto n=npoly(tt[3]); + if (n<0) + { + add_device("VCVS", tt[0]); + add_term(tt[1], tt[0], 0); + add_term(tt[2], tt[0], 1); + add_term(tt[3], tt[0], 2); + add_term(tt[4], tt[0], 3); + add_device_extra(tt[0], "PARAM({}, {})", tt[0] + ".G", tt[5]); + } + else + { + int sce(4); + int scoeff(5 + n); + if ((tt.size() != 5 + 2 * n) || (tt[scoeff-1] != "0")) + { + out("// IGNORED {}: {}\n", tt[0].c_str(), line.c_str()); + break; + } + pstring lastnet = tt[1]; + for (int i=0; i does Kicad use different notation from LTSPICE - add_term(tt[1], tt[0] + ".K"); - add_term(tt[2], tt[0] + ".A"); + add_device("DIODE", tt[0], m_subckt + tt[3]); + // Spice D Anode Kathode model + // FIXME ==> does Kicad use different notation from LTSPICE ? + add_term(tt[1], tt[0] + ".A"); + add_term(tt[2], tt[0] + ".K"); break; case 'U': case 'X': diff --git a/src/lib/netlist/tools/nl_convert.h b/src/lib/netlist/tools/nl_convert.h index 876215c6b29..4d32eb7e2a6 100644 --- a/src/lib/netlist/tools/nl_convert.h +++ b/src/lib/netlist/tools/nl_convert.h @@ -22,6 +22,7 @@ class nl_convert_base_t { public: + using str_list = std::vector; COPYASSIGNMOVE(nl_convert_base_t, delete) @@ -42,7 +43,16 @@ protected: void add_device(const pstring &atype, const pstring &aname, double aval); void add_device(const pstring &atype, const pstring &aname); + void add_device_extra_s(const pstring &devname, const pstring &extra); + + template + void add_device_extra(const pstring &devname, const pstring &fmt, Args&&... args) + { + add_device_extra_s(devname, plib::pfmt(fmt)(std::forward(args)...)); + } + void add_term(const pstring &netname, const pstring &termname); + void add_term(const pstring &netname, const pstring &devname, unsigned term); void dump_nl(); @@ -52,6 +62,15 @@ protected: double get_sp_val(const pstring &sin); plib::putf8_fmt_writer out; + + struct replace_t + { + pstring m_ce; // controlling element - must be twoterm + pstring m_repterm; // replace with terminal + pstring m_net; // connect to net + }; + std::vector m_replace; + private: struct net_t @@ -102,16 +121,19 @@ private: const pstring &type() const { return m_type;} const pstring &model() const { return m_model;} double value() const { return m_val;} + const str_list &extra() const { return m_extra;} bool has_model() const { return m_model != ""; } bool has_value() const { return m_has_val; } + void add_extra(const pstring &s) { m_extra.push_back(s); } private: pstring m_type; pstring m_name; pstring m_model; double m_val; bool m_has_val; + str_list m_extra; }; struct unit_t { @@ -133,9 +155,17 @@ private: pstring m_alias; }; + private: void add_device(plib::unique_ptr dev); + dev_t *get_device(const pstring &name) + { + for (auto &e : m_devs) + if (e->name() == name) + return e.get(); + return nullptr; + } std::stringstream m_buf; @@ -147,6 +177,8 @@ private: std::vector m_units; pstring m_numberchars; + std::unordered_map dev_map; + }; class nl_convert_spice_t : public nl_convert_base_t -- cgit v1.2.3