diff options
Diffstat (limited to 'src/lib/netlist/tools')
-rw-r--r-- | src/lib/netlist/tools/nl_convert.cpp | 694 | ||||
-rw-r--r-- | src/lib/netlist/tools/nl_convert.h | 168 |
2 files changed, 609 insertions, 253 deletions
diff --git a/src/lib/netlist/tools/nl_convert.cpp b/src/lib/netlist/tools/nl_convert.cpp index 3e705910bf4..2859034c166 100644 --- a/src/lib/netlist/tools/nl_convert.cpp +++ b/src/lib/netlist/tools/nl_convert.cpp @@ -1,24 +1,29 @@ -// license:GPL-2.0+ +// license:BSD-3-Clause // copyright-holders:Couriersud -/* - * nl_convert.c - * - */ #include "plib/palloc.h" -#include "nl_convert.h" +#include "plib/pstonum.h" +#include "plib/pstrutil.h" #include "plib/putil.h" +#include "nl_convert.h" + +#include <cstdio> #include <algorithm> -#include <cmath> #include <unordered_map> +#include <vector> + +namespace netlist::convert +{ -/* FIXME: temporarily defined here - should be in a file */ -/* FIXME: family logic in netlist is convoluted, create - * define a model param on core device - */ -/* Format: external name,netlist device,model */ -static const char * s_lib_map = +// FIXME: temporarily defined here - should be in a file +// FIXME: family logic in netlist is convoluted, create +// define a model param on core device + +// Format: external name,netlist device,model + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) +static constexpr const char s_lib_map[] = "SN74LS00D, TTL_7400_DIP, 74LSXX\n" "SN74LS04D, TTL_7404_DIP, 74LSXX\n" "SN74ALS08D, TTL_7408_DIP, 74ALSXX\n" @@ -43,47 +48,57 @@ using lib_map_t = std::unordered_map<pstring, lib_map_entry>; static lib_map_t read_lib_map(const pstring &lm) { - auto reader = plib::putf8_reader(std::istringstream(lm)); + auto reader = plib::putf8_reader(std::make_unique<std::istringstream>(putf8string(lm))); reader.stream().imbue(std::locale::classic()); lib_map_t m; - pstring line; - while (reader.readline(line)) + putf8string line; + while (reader.read_line(line)) { - std::vector<pstring> split(plib::psplit(line, ",")); + std::vector<pstring> split(plib::psplit(pstring(line), ',')); m[plib::trim(split[0])] = { plib::trim(split[1]), plib::trim(split[2]) }; } return m; } -/*------------------------------------------------- - convert - convert a spice netlist --------------------------------------------------*/ +// ------------------------------------------------- +// convert - convert a spice netlist +// ------------------------------------------------- nl_convert_base_t::nl_convert_base_t() : out(&m_buf) - , m_numberchars("0123456789-+e.") + , m_number_chars("0123456789-+Ee.") { m_buf.imbue(std::locale::classic()); 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 }, - {"µ", "CAP_U({1})", 1.0e-6 }, - {"N", "CAP_N({1})", 1.0e-9 }, - {"pF", "CAP_P({1})", 1.0e-12}, - {"P", "CAP_P({1})", 1.0e-12}, - {"F", "{1}e-15", 1.0e-15}, - - {"MIL", "{1}", 25.4e-6} + {"T", "{1}e12", 1.0e12 }, // NOLINT + {"G", "{1}e9", 1.0e9 }, // NOLINT + {"MEG", "RES_M({1})", 1.0e6 }, // NOLINT + {"k", "RES_K({1})", 1.0e3 }, // NOLINT: eagle + {"K", "RES_K({1})", 1.0e3 }, // NOLINT + {"", "{1}", 1.0e0 }, // NOLINT + {"M", "{1}e-3", 1.0e-3 }, // NOLINT + {"u", "CAP_U({1})", 1.0e-6 }, // NOLINT: eagle + {"U", "CAP_U({1})", 1.0e-6 }, // NOLINT + {"μ", "CAP_U({1})", 1.0e-6 }, // NOLINT + {"N", "CAP_N({1})", 1.0e-9 }, // NOLINT + {"pF", "CAP_P({1})", 1.0e-12}, // NOLINT + {"P", "CAP_P({1})", 1.0e-12}, // NOLINT + {"F", "{1}e-15", 1.0e-15}, // NOLINT + + {"MIL", "{1}", 25.4e-6} // NOLINT }; + 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"} }, + { "TTL_INPUT", {"Q", "VCC", "GND"} }, + { "DIODE", {"A", "K"} }, + { "POT", {"1", "2", "3"} }, + }; } nl_convert_base_t::~nl_convert_base_t() @@ -96,15 +111,20 @@ nl_convert_base_t::~nl_convert_base_t() void nl_convert_base_t::add_pin_alias(const pstring &devname, const pstring &name, const pstring &alias) { pstring pname = devname + "." + name; - m_pins.emplace(pname, plib::make_unique<pin_alias_t>(pname, devname + "." + alias)); + m_pins.emplace(pname, plib::make_unique<pin_alias_t, arena>(pname, devname + "." + alias)); } void nl_convert_base_t::add_ext_alias(const pstring &alias) { - m_ext_alias.push_back(alias); + m_ext_alias.emplace_back(alias, alias); } -void nl_convert_base_t::add_device(plib::unique_ptr<dev_t> dev) +void nl_convert_base_t::add_ext_alias(const pstring &alias, const pstring &net) +{ + m_ext_alias.emplace_back(alias, net); +} + +void nl_convert_base_t::add_device(arena::unique_ptr<dev_t> dev) { for (auto & d : m_devs) if (d->name() == dev->name()) @@ -117,31 +137,35 @@ void nl_convert_base_t::add_device(plib::unique_ptr<dev_t> dev) void nl_convert_base_t::add_device(const pstring &atype, const pstring &aname, const pstring &amodel) { - add_device(plib::make_unique<dev_t>(atype, aname, amodel)); + add_device(plib::make_unique<dev_t, arena>(atype, aname, amodel)); } void nl_convert_base_t::add_device(const pstring &atype, const pstring &aname, double aval) { - add_device(plib::make_unique<dev_t>(atype, aname, aval)); + add_device(plib::make_unique<dev_t, arena>(atype, aname, aval)); } void nl_convert_base_t::add_device(const pstring &atype, const pstring &aname) { - add_device(plib::make_unique<dev_t>(atype, aname)); + add_device(plib::make_unique<dev_t, arena>(atype, aname)); } void nl_convert_base_t::add_term(const pstring &netname, const pstring &termname) { + // Ignore NC nets! + if (plib::startsWith(netname,"NC_")) + return; + net_t * net = nullptr; auto idx = m_nets.find(netname); if (idx != m_nets.end()) net = m_nets[netname].get(); else { - auto nets = plib::make_unique<net_t>(netname); + auto nets = plib::make_unique<net_t, arena>(netname); net = nets.get(); m_nets.emplace(netname, std::move(nets)); } - /* if there is a pin alias, translate ... */ + // if there is a pin alias, translate ... pin_alias_t *alias = m_pins[termname].get(); if (alias != nullptr) @@ -150,19 +174,77 @@ 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(); + net_t *net = m_nets[alias.second].get(); // use the first terminal ... - out("ALIAS({}, {})\n", alias.c_str(), net->terminals()[0].c_str()); + out("ALIAS({}, {})\n", alias.first, net->terminals()[0]); // if the aliased net only has this one terminal connected ==> don't dump if (net->terminals().size() == 1) net->set_no_export(); } std::vector<size_t> sorted; + sorted.reserve(m_devs.size()); for (size_t i=0; i < m_devs.size(); i++) sorted.push_back(i); std::sort(sorted.begin(), sorted.end(), @@ -173,48 +255,63 @@ void nl_convert_base_t::dump_nl() std::size_t j = sorted[i]; if (m_devs[j]->has_value()) - out("{}({}, {})\n", m_devs[j]->type().c_str(), - m_devs[j]->name().c_str(), get_nl_val(m_devs[j]->value()).c_str()); + { + pstring t = m_devs[j]->type(); + pstring vals = (t == "RES" || t == "CAP") ? get_nl_val(m_devs[j]->value()) : plib::pfmt("{1:g}")(m_devs[j]->value()); + out("{}({}, {})\n", t, m_devs[j]->name(), vals); + } else if (m_devs[j]->has_model()) - out("{}({}, \"{}\")\n", m_devs[j]->type().c_str(), - m_devs[j]->name().c_str(), m_devs[j]->model().c_str()); + out("{}({}, \"{}\")\n", m_devs[j]->type(), + m_devs[j]->name(), m_devs[j]->model()); else - out("{}({})\n", m_devs[j]->type().c_str(), - m_devs[j]->name().c_str()); + out("{}({})\n", m_devs[j]->type(), + m_devs[j]->name()); + for (const auto &e : m_devs[j]->extra()) + out("{}\n", e); + } // print nets for (auto & i : m_nets) { net_t * net = i.second.get(); - if (!net->is_no_export()) + if (!net->is_no_export() && !(net->terminals().size() == 1 && net->terminals()[0] == "GND" )) { - out("NET_C({}", net->terminals()[0].c_str() ); + out("NET_C({}", net->terminals()[0] ); for (std::size_t j=1; j<net->terminals().size(); j++) { - out(", {}", net->terminals()[j].c_str() ); + out(", {}", net->terminals()[j] ); } out(")\n"); } } + m_replace.clear(); m_devs.clear(); m_nets.clear(); m_pins.clear(); m_ext_alias.clear(); } -const pstring nl_convert_base_t::get_nl_val(const double val) +pstring nl_convert_base_t::get_nl_val(double val) const { - for (auto &e : m_units) + for (const auto &e : m_units) { - if (e.m_mult <= std::abs(val)) - return plib::pfmt(e.m_func)(val / e.m_mult); + if (e.m_mult <= plib::abs(val)) + { + double v = val / e.m_mult; + if (plib::abs(v - std::round(v)) <= 1e-6) + return plib::pfmt(e.m_func)(static_cast<int>(std::round(v))); + return plib::pfmt(e.m_func)(v); + } } + + if (plib::abs(val - std::round(val)) <= 1e-6) + return plib::pfmt("{1}")(static_cast<int>(std::round(val))); return plib::pfmt("{1}")(val); } -double nl_convert_base_t::get_sp_unit(const pstring &unit) +double nl_convert_base_t::get_sp_unit(const pstring &unit) const { - for (auto &e : m_units) + for (const auto &e : m_units) { if (e.m_unit == unit) return e.m_mult; @@ -223,10 +320,10 @@ double nl_convert_base_t::get_sp_unit(const pstring &unit) return 0.0; } -double nl_convert_base_t::get_sp_val(const pstring &sin) +double nl_convert_base_t::get_sp_val(const pstring &sin) const { std::size_t p = 0; - while (p < sin.length() && (m_numberchars.find(sin.substr(p, 1)) != pstring::npos)) + while (p < sin.length() && (m_number_chars.find(sin.substr(p, 1)) != pstring::npos)) ++p; pstring val = plib::left(sin, p); pstring unit = sin.substr(p); @@ -234,40 +331,38 @@ double nl_convert_base_t::get_sp_val(const pstring &sin) return ret; } -#if 0 -std::vector<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 }, - {"µ", "CAP_U({1})", 1.0e-6 }, - {"N", "CAP_N({1})", 1.0e-9 }, - {"pF", "CAP_P({1})", 1.0e-12}, - {"P", "CAP_P({1})", 1.0e-12}, - {"F", "{1}e-15", 1.0e-15}, - - {"MIL", "{1}", 25.4e-6} -}; -#endif +void nl_convert_spice_t::convert_block(const str_list &contents) +{ + int linenumber = 1; + for (const auto &line : contents) + { + try + { + process_line(line); + } + catch ([[maybe_unused]] const plib::pexception &e) + { + fprintf(stderr, "Error on line: <%d>\n", linenumber); + throw; + } + linenumber++; + } +} + void nl_convert_spice_t::convert(const pstring &contents) { - std::vector<pstring> spnl(plib::psplit(contents, "\n")); + std::vector<pstring> spnl(plib::psplit(contents, '\n')); + std::vector<pstring> after_line_continuation; // Add gnd net // FIXME: Parameter - out("NETLIST_START(dummy)\n"); - add_term("0", "GND"); pstring line = ""; + // process line continuation + for (const auto &i : spnl) { // Basic preprocessing @@ -276,14 +371,56 @@ void nl_convert_spice_t::convert(const pstring &contents) line += inl.substr(1); else { - process_line(line); + after_line_continuation.push_back(line); line = inl; } } - process_line(line); + after_line_continuation.push_back(line); + spnl.clear(); // no longer needed + + // Process sub circuits + + std::vector<std::vector<pstring>> subckts; + std::vector<pstring> nl; + auto inp = after_line_continuation.begin(); + while (inp != after_line_continuation.end()) + { + if (plib::startsWith(*inp, ".SUBCKT")) + { + std::vector<pstring> sub; + while (inp != after_line_continuation.end()) + { + auto s(*inp); + sub.push_back(s); + inp++; + if (plib::startsWith(s, ".ENDS")) + break; + } + subckts.push_back(sub); + } + else + { + nl.push_back(*inp); + inp++; + } + } + + for (const auto &sub : subckts) + { + add_term("0", "GND"); + add_term("GND", "GND"); // For Kicad + convert_block(sub); + } + + out("NETLIST_START(dummy)\n"); + out("{\n"); + add_term("0", "GND"); + add_term("GND", "GND"); // For Kicad + + convert_block(nl); dump_nl(); // FIXME: Parameter - out("NETLIST_END()\n"); + out("}\n"); } static pstring rem(const std::vector<pstring> &vps, std::size_t start) @@ -294,58 +431,79 @@ static pstring rem(const std::vector<pstring> &vps, std::size_t start) return r; } +static int get_poly_count(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 != "") + if (!line.empty()) { //printf("// %s\n", line.c_str()); - std::vector<pstring> tt(plib::psplit(line, " ", true)); + std::vector<pstring> tt(plib::psplit(line, ' ', true)); double val = 0.0; switch (tt[0].at(0)) { case ';': - out("// {}\n", line.substr(1)); - break; case '*': - out("// {}\n", line.substr(1).c_str()); + out("// {}\n", line.substr(1)); break; case '.': if (tt[0] == ".SUBCKT") { m_subckt = tt[1] + "_"; out("NETLIST_START({})\n", tt[1]); + out("{\n"); for (std::size_t i=2; i<tt.size(); i++) add_ext_alias(tt[i]); } else if (tt[0] == ".ENDS") { dump_nl(); - out("NETLIST_END()\n"); + out("}\n"); m_subckt = ""; } else if (tt[0] == ".MODEL") { - out("NET_MODEL(\"{} {}\")\n", m_subckt + tt[1], rem(tt,2)); + pstring mod(rem(tt,2)); + // Filter out `ngspice` X=X model declarations + if (tt[1] != mod) + out("NET_MODEL(\"{} {}\")\n", m_subckt + tt[1], mod); + } + else if (tt[0] == ".TITLE" && tt[1] == "KICAD") + { + m_is_kicad = true; } else - out("// {}\n", line.c_str()); + out("// {}\n", line); break; case 'Q': { - /* check for fourth terminal ... should be numeric net - * including "0" or start with "N" (ltspice) - */ + // check for fourth terminal ... should be numeric net + // including "0" or start with "N" (`ltspice`) + pstring model; pstring pins ="CBE"; - bool err; - auto nval = plib::pstonum_ne<long, true>(tt[4], err); - plib::unused_var(nval); + bool err(false); + [[maybe_unused]] auto nval = plib::pstonum_ne<long>(tt[4], err); if ((!err || plib::startsWith(tt[4], "N")) && tt.size() > 5) model = tt[5]; else model = tt[4]; - std::vector<pstring> m(plib::psplit(model,"{")); + std::vector<pstring> m(plib::psplit(model, '{')); if (m.size() == 2) { if (m[1].length() != 4) @@ -367,6 +525,17 @@ void nl_convert_spice_t::process_line(const pstring &line) add_term(tt[2], tt[0] + ".2"); add_term(tt[3], tt[0] + ".3"); } + else if (plib::startsWith(tt[0], "RA")) + { + val = get_sp_val(tt.back()); + for (unsigned int res = 2; res < tt.size(); res++) + { + pstring devname = plib::pfmt("{}.{}")(tt[0], res); + add_device("RES", devname, val); + add_term(tt[1], devname); + add_term(tt[res], devname); + } + } else { val = get_sp_val(tt[3]); @@ -381,55 +550,117 @@ void nl_convert_spice_t::process_line(const pstring &line) add_term(tt[1], tt[0] + ".1"); add_term(tt[2], tt[0] + ".2"); break; - case 'B': // arbitrary behavioural current source - needs manual work afterwords + case 'B': // arbitrary behavioural current source - needs manual work afterwards add_device("CS", tt[0], "/*" + rem(tt, 3) + "*/"); add_term(tt[1], tt[0] + ".P"); add_term(tt[2], tt[0] + ".N"); break; case 'E': - add_device("VCVS", tt[0]); - add_term(tt[1], tt[0] + ".OP"); - add_term(tt[2], tt[0] + ".ON"); - add_term(tt[3], tt[0] + ".IP"); - add_term(tt[4], tt[0] + ".IN"); - out("PARAM({}, {})\n", tt[0] + ".G", tt[5]); - break; - case 'V': - // just simple Voltage sources .... - if (tt[2] == "0") + { + auto n=get_poly_count(tt[3]); + if (n<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"); + add_device("VCVS", tt[0], get_sp_val(tt[5])); + 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 - plib::perrlogger("Voltage Source {} not connected to GND\n", tt[0]); + { + unsigned sce(4); + auto scoeff(static_cast<unsigned>(5 + n)); + if ((tt.size() != 5 + 2 * static_cast<unsigned>(n)) || (tt[scoeff-1] != "0")) + { + out("// IGNORED {}: {}\n", tt[0], line); + break; + } + pstring last_net = tt[1]; + for (std::size_t i=0; i < static_cast<std::size_t>(n); i++) + { + pstring devname = plib::pfmt("{}{}")(tt[0], i); + pstring next_net = (i<static_cast<std::size_t>(n)-1) ? plib::pfmt("{}a{}")(tt[1], i) : tt[2]; + auto net2 = plib::psplit(plib::replace_all(plib::replace_all(tt[sce+i],")",""),"(",""),','); + add_device("VCVS", devname, get_sp_val(tt[scoeff+i])); + add_term(last_net, devname, 0); + add_term(next_net, devname, 1); + add_term(net2[0], devname, 2); + add_term(net2[1], devname, 3); + //# add_device_extra(devname, "PARAM({}, {})", devname + ".G", tt[scoeff+i]); + last_net = next_net; + } + } + } break; -#if 0 - // This is wrong ... Need to use something else for inputs! - case 'I': // Input pin special notation + case 'F': { - val = get_sp_val(tt[2]); - add_device("ANALOG_INPUT", tt[0], val); - add_term(tt[1], tt[0] + ".Q"); + auto n=get_poly_count(tt[3]); + unsigned sce(4); + unsigned scoeff(5 + static_cast<unsigned>(n)); + if (n<0) + { + sce = 3; + scoeff = 4; + n = 1; + } + else + { + if ((tt.size() != 5 + 2 * static_cast<unsigned>(n)) || (tt[scoeff-1] != "0")) + { + out("// IGNORED {}: {}\n", tt[0], line); + break; + } + } + for (std::size_t i=0; i < static_cast<std::size_t>(n); i++) + { + pstring devname = plib::pfmt("{}{}")(tt[0], i); + add_device("CCCS", devname, get_sp_val(tt[scoeff+i])); + add_term(tt[1], devname, 0); + add_term(tt[2], devname, 1); + + pstring extra_net_name = devname + "net"; + m_replace.push_back({tt[sce+i], devname + ".IP", extra_net_name }); + add_term(extra_net_name, devname + ".IN"); + //# add_device_extra(devname, "PARAM({}, {})", devname + ".G", tt[scoeff+i]); + } } break; -#else + case 'H': + add_device("CCVS", tt[0], get_sp_val(tt[4])); + add_term(tt[1], tt[0] + ".OP"); + add_term(tt[2], tt[0] + ".ON"); + m_replace.push_back({tt[3], tt[0] + ".IP", tt[2] + "a" }); + add_term(tt[2] + "a", tt[0] + ".IN"); + //add_device_extra(tt[0], "PARAM({}, {})", tt[0] + ".G", tt[4]); + break; + case 'G': + add_device("VCCS", tt[0], get_sp_val(tt[5])); + 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]); + break; + case 'V': + // only DC Voltage sources .... + val = get_sp_val(tt[3] == "DC" ? tt[4] : tt[3]); + add_device("VS", tt[0], val); + add_term(tt[1], tt[0] + ".1"); + add_term(tt[2], tt[0] + ".2"); + break; case 'I': { - val = get_sp_val(tt[3]); + val = get_sp_val(tt[3] == "DC" ? tt[4] : tt[3]); add_device("CS", tt[0], val); add_term(tt[1], tt[0] + ".1"); add_term(tt[2], tt[0] + ".2"); } break; -#endif 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"); + add_device("DIODE", tt[0], m_subckt + tt[3]); + add_term(tt[1], tt[0], 0); + add_term(tt[2], tt[0], 1); break; case 'U': case 'X': @@ -439,78 +670,149 @@ void nl_convert_spice_t::process_line(const pstring &line) // FIXME: Parameter pstring xname = plib::replace_all(tt[0], pstring("."), pstring("_")); - pstring tname = "TTL_" + tt[tt.size()-1] + "_DIP"; + // Extract parameters of form X=Y + std::vector<pstring> nets; + std::unordered_map<pstring, pstring> params; + for (std::size_t i=1; i < tt.size(); i++) + { + auto p = tt[i].find('='); + if (p != pstring::npos) + params.emplace(tt[i].substr(0,p), tt[i].substr(p+1)); + else + { + nets.push_back(tt[i]); + } + } + pstring modname = nets[nets.size()-1]; + pstring tname = modname; + if (plib::startsWith(modname, "7")) + tname = "TTL_" + modname + "_DIP"; + else if (plib::startsWith(modname, "4")) + tname = "CD" + modname + "_DIP"; + else if (modname == "ANALOG_INPUT" && params.size()== 1 && params.begin()->first == "V") + { + auto yname=pstring("I_") + tt[0].substr(1); + val = get_sp_val(params["V"]); + add_device(modname, yname, val); + add_term(nets[0], yname + ".Q"); + break; + } + else if (modname == "TTL_INPUT" && params.size()== 1 && params.begin()->first == "L") + { + auto yname=pstring("I_") + tt[0].substr(1); + val = get_sp_val(params["L"]); + add_device(modname, yname, val); + add_term(nets[0], yname, 0); + add_term(nets[1], yname, 1); + add_term(nets[2], yname, 2); + break; + } + else if (modname == "ALIAS" && nets.size() == 2 && params.empty()) + { + auto yname=tt[0].substr(1); + add_ext_alias(yname, nets[0]); + break; + } + else if (modname == "RPOT" && nets.size() == 4 && !params.empty()) + { + auto yname=tt[0]; + auto R = params.find("R"); + auto V = params.find("V"); + if (R != params.end()) + { + add_device("POT", yname, get_sp_val(R->second)); + add_term(nets[0], yname, 0); + add_term(nets[1], yname, 1); + add_term(nets[2], yname, 2); + if (V != params.end()) + add_device_extra(yname, "PARAM({}, {})", yname + ".DIAL", get_sp_val(V->second)); + } + else + out("// IGNORED {}: {}\n", tt[0], line); + break; + } + else + tname = modname + "_DIP"; + add_device(tname, xname); - for (std::size_t i=1; i < tt.size() - 1; i++) + for (std::size_t i=0; i < nets.size() - 1; i++) { - pstring term = plib::pfmt("{1}.{2}")(xname)(i); - add_term(tt[i], term); + // FIXME: + pstring term = plib::pfmt("{1}.{2}")(xname)(i+1); + add_term(nets[i], term); } break; } default: - out("// IGNORED {}: {}\n", tt[0].c_str(), line.c_str()); + out("// IGNORED {}: {}\n", tt[0], line); } } } -/*------------------------------------------------- - Eagle converter --------------------------------------------------*/ +//------------------------------------------------- +// Eagle converter +// ------------------------------------------------- -nl_convert_eagle_t::tokenizer::tokenizer(nl_convert_eagle_t &convert, plib::putf8_reader &&strm) - : plib::ptokenizer(std::move(strm)) +nl_convert_eagle_t::tokenizer::tokenizer(nl_convert_eagle_t &convert) + : plib::tokenizer_t() , m_convert(convert) { this->identifier_chars("abcdefghijklmnopqrstuvwvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_.-") .number_chars(".0123456789", "0123456789eE-.") //FIXME: processing of numbers .whitespace(pstring("") + ' ' + static_cast<char>(9) + static_cast<char>(10) + static_cast<char>(13)) - /* FIXME: gnetlist doesn't print comments */ + // FIXME: netlist doesn't print comments .comment("/*", "*/", "//") .string_char('\''); m_tok_ADD = register_token("ADD"); m_tok_VALUE = register_token("VALUE"); m_tok_SIGNAL = register_token("SIGNAL"); m_tok_SEMICOLON = register_token(";"); - /* currently not used, but required for parsing */ + // currently not used, but required for parsing register_token(")"); register_token("("); } -void nl_convert_eagle_t::tokenizer::verror(const pstring &msg, int line_num, const pstring &line) +void nl_convert_eagle_t::tokenizer::verror(const pstring &msg) { - m_convert.out("{} (line {}): {}\n", msg.c_str(), line_num, line.c_str()); + m_convert.out("{}\n", msg); } //FIXME: should accept a stream as well void nl_convert_eagle_t::convert(const pstring &contents) { - tokenizer tok(*this, plib::putf8_reader(std::istringstream(contents))); - tok.stream().stream().imbue(std::locale::classic()); + tokenizer tok(*this); + + tokenizer::token_store tokstor; + plib::putf8_reader u8reader(std::make_unique<std::istringstream>(putf8string(contents))); + + tok.append_to_store(&u8reader, tokstor); + tok.set_token_source(&tokstor); out("NETLIST_START(dummy)\n"); + out("{\n"); add_term("GND", "GND"); add_term("VCC", "VCC"); tokenizer::token_t token = tok.get_token(); while (true) { - if (token.is_type(tokenizer::ENDOFFILE)) + if (token.is_type(tokenizer::token_type::ENDOFFILE)) { dump_nl(); // FIXME: Parameter - out("NETLIST_END()\n"); + out("}\n"); return; } - else if (token.is(tok.m_tok_SEMICOLON)) + + if (token.is(tok.m_tok_SEMICOLON)) { - /* ignore empty statements */ + // ignore empty statements token = tok.get_token(); } else if (token.is(tok.m_tok_ADD)) { pstring name = tok.get_string(); - /* skip to semicolon */ + // skip to semicolon do { token = tok.get_token(); @@ -553,7 +855,7 @@ void nl_convert_eagle_t::convert(const pstring &contents) add_pin_alias(name, "1", "Q"); break; case 'D': - /* Pin 1 = Anode, Pin 2 = Cathode */ + // Pin 1 = Anode, Pin 2 = Cathode add_device("DIODE", name, sval); add_pin_alias(name, "1", "A"); add_pin_alias(name, "2", "K"); @@ -566,7 +868,7 @@ void nl_convert_eagle_t::convert(const pstring &contents) break; } default: - tok.error("// IGNORED " + name); + tok.error(plib::perrmsg("// IGNORED {1}", name)); } } @@ -576,7 +878,7 @@ void nl_convert_eagle_t::convert(const pstring &contents) token = tok.get_token(); while (!token.is(tok.m_tok_SEMICOLON)) { - /* fixme: should check for string */ + // fixme: should check for string pstring devname = token.str(); pstring pin = tok.get_string(); add_term(netname, devname + "." + pin); @@ -584,25 +886,25 @@ void nl_convert_eagle_t::convert(const pstring &contents) } else { - out("Unexpected {}\n", token.str().c_str()); + out("Unexpected {}\n", token.str()); return; } } } -/*------------------------------------------------- - RINF converter --------------------------------------------------*/ +// ------------------------------------------------- +// RINF converter +// ------------------------------------------------- -nl_convert_rinf_t::tokenizer::tokenizer(nl_convert_rinf_t &convert, plib::putf8_reader &&strm) - : plib::ptokenizer(std::move(strm)) +nl_convert_rinf_t::tokenizer::tokenizer(nl_convert_rinf_t &convert) + : plib::tokenizer_t() , m_convert(convert) { this->identifier_chars(".abcdefghijklmnopqrstuvwvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_-") .number_chars("0123456789", "0123456789eE-.") //FIXME: processing of numbers .whitespace(pstring("") + ' ' + static_cast<char>(9) + static_cast<char>(10) + static_cast<char>(13)) - /* FIXME: gnetlist doesn't print comments */ + // FIXME: netlist doesn't print comments .comment("","","//") // FIXME:needs to be confirmed .string_char('"'); m_tok_HEA = register_token(".HEA"); @@ -616,56 +918,62 @@ nl_convert_rinf_t::tokenizer::tokenizer(nl_convert_rinf_t &convert, plib::putf8_ m_tok_END = register_token(".END"); } -void nl_convert_rinf_t::tokenizer::verror(const pstring &msg, int line_num, const pstring &line) +void nl_convert_rinf_t::tokenizer::verror(const pstring &msg) { - m_convert.out("{} (line {}): {}\n", msg.c_str(), line_num, line.c_str()); + m_convert.out("{}\n", msg); } -/* token_id_t m_tok_HFA; - token_id_t m_tok_APP; - token_id_t m_tok_TIM; - token_id_t m_tok_TYP; - token_id_t m_tok_ADDC; - token_id_t m_tok_ATTC; - token_id_t m_tok_NET; - token_id_t m_tok_TER; - * - */ +// token_id_t m_tok_HFA; +// token_id_t m_tok_APP; +// token_id_t m_tok_TIM; +// token_id_t m_tok_TYP; +// token_id_t m_tok_ADDC; +// token_id_t m_tok_ATTC; +// token_id_t m_tok_NET; +// token_id_t m_tok_TER; void nl_convert_rinf_t::convert(const pstring &contents) { - tokenizer tok(*this, plib::putf8_reader(std::istringstream(contents))); - tok.stream().stream().imbue(std::locale::classic()); + tokenizer tok(*this); + + tokenizer::token_store tokstor; + plib::putf8_reader u8reader(std::make_unique<std::istringstream>(putf8string(contents))); + + tok.append_to_store(&u8reader, tokstor); + tok.set_token_source(&tokstor); + auto lm = read_lib_map(s_lib_map); out("NETLIST_START(dummy)\n"); + out("{\n"); add_term("GND", "GND"); add_term("VCC", "VCC"); tokenizer::token_t token = tok.get_token(); while (true) { - if (token.is_type(tokenizer::ENDOFFILE) || token.is(tok.m_tok_END)) + if (token.is_type(tokenizer::token_type::ENDOFFILE) || token.is(tok.m_tok_END)) { dump_nl(); // FIXME: Parameter - out("NETLIST_END()\n"); + out("}\n"); return; } - else if (token.is(tok.m_tok_HEA)) + + if (token.is(tok.m_tok_HEA)) { - /* seems to be start token - ignore */ + // seems to be start token - ignore token = tok.get_token(); } else if (token.is(tok.m_tok_APP)) { - /* version string */ + // version string pstring app = tok.get_string(); out("// APP: {}\n", app); token = tok.get_token(); } else if (token.is(tok.m_tok_TIM)) { - /* time */ + // time out("// TIM:"); for (int i=0; i<6; i++) { @@ -705,7 +1013,7 @@ void nl_convert_rinf_t::convert(const pstring &contents) pstring sim = attr["Simulation"]; pstring val = attr["Value"]; pstring com = attr["Comment"]; - if (val == "") + if (val.empty()) val = com; if (sim == "CAP") @@ -736,7 +1044,7 @@ void nl_convert_rinf_t::convert(const pstring &contents) if (token.is(tok.m_tok_TER)) { token = tok.get_token(); - while (token.is_type(plib::ptokenizer::IDENTIFIER)) + while (token.is_type(plib::token_reader_t::token_type::IDENTIFIER)) { pin = tok.get_identifier_or_number(); add_term(net, token.str() + "." + pin); @@ -746,7 +1054,7 @@ void nl_convert_rinf_t::convert(const pstring &contents) } #if 0 token = tok.get_token(); - /* skip to semicolon */ + // skip to semicolon do { token = tok.get_token(); @@ -789,7 +1097,7 @@ void nl_convert_rinf_t::convert(const pstring &contents) add_pin_alias(name, "1", "Q"); break; case 'D': - /* Pin 1 = Anode, Pin 2 = Cathode */ + // Pin 1 = Anode, Pin 2 = Cathode add_device("DIODE", name, sval); add_pin_alias(name, "1", "A"); add_pin_alias(name, "2", "K"); @@ -812,7 +1120,7 @@ void nl_convert_rinf_t::convert(const pstring &contents) token = tok.get_token(); while (!token.is(tok.m_tok_SEMICOLON)) { - /* fixme: should check for string */ + // fixme: should check for string pstring devname = token.str(); pstring pin = tok.get_string(); add_term(netname, devname + "." + pin); @@ -827,3 +1135,5 @@ void nl_convert_rinf_t::convert(const pstring &contents) } } + +} // namespace netlist::convert diff --git a/src/lib/netlist/tools/nl_convert.h b/src/lib/netlist/tools/nl_convert.h index e070514d5ed..0e36e85a253 100644 --- a/src/lib/netlist/tools/nl_convert.h +++ b/src/lib/netlist/tools/nl_convert.h @@ -1,35 +1,40 @@ -// license:GPL-2.0+ +// license:BSD-3-Clause // copyright-holders:Couriersud -/* - * nl_convert.h - * - */ - -#pragma once #ifndef NL_CONVERT_H_ #define NL_CONVERT_H_ -#include "plib/plists.h" -#include "plib/pparser.h" +/// +/// \file nl_convert.h +/// + +#include "plib/palloc.h" #include "plib/pstring.h" #include "plib/ptypes.h" #include <memory> -/*------------------------------------------------- - convert - convert a spice netlist --------------------------------------------------*/ +#include "../plib/ptokenizer.h" + +// ------------------------------------------------- +// convert - convert a spice netlist +// ------------------------------------------------- + +namespace netlist::convert +{ + +using arena = plib::aligned_arena<>; class nl_convert_base_t { public: + using str_list = std::vector<pstring>; - COPYASSIGNMOVE(nl_convert_base_t, delete) + PCOPYASSIGNMOVE(nl_convert_base_t, delete) virtual ~nl_convert_base_t(); - pstring result() { return pstring(m_buf.str()); } + pstring result() { return pstring(putf8string(m_buf.str())); } virtual void convert(const pstring &contents) = 0; @@ -39,21 +44,40 @@ protected: void add_pin_alias(const pstring &devname, const pstring &name, const pstring &alias); void add_ext_alias(const pstring &alias); + void add_ext_alias(const pstring &alias, const pstring &net); void add_device(const pstring &atype, const pstring &aname, const pstring &amodel); 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<typename... Args> + void add_device_extra(const pstring &devname, const pstring &fmt, Args&&... args) + { + add_device_extra_s(devname, plib::pfmt(fmt)(std::forward<Args>(args)...)); + } + void add_term(const pstring &netname, const pstring &termname); + void add_term(const pstring &netname, const pstring &devname, unsigned term); void dump_nl(); - const pstring get_nl_val(const double val); - double get_sp_unit(const pstring &unit); + pstring get_nl_val(double val) const; + double get_sp_unit(const pstring &unit) const; - double get_sp_val(const pstring &sin); + double get_sp_val(const pstring &sin) const; plib::putf8_fmt_writer out; + + struct replace_t + { + pstring m_ce; // controlling element - must be a two terminal element + pstring m_repterm; // replace with terminal + pstring m_net; // connect to net + }; + std::vector<replace_t> m_replace; + private: struct net_t @@ -62,10 +86,10 @@ private: explicit net_t(pstring aname) : m_name(std::move(aname)), m_no_export(false) {} - const pstring &name() { return m_name;} - std::vector<pstring> &terminals() { return m_terminals; } + const pstring &name() const { return m_name;} + std::vector<pstring> &terminals(){ return m_terminals; } void set_no_export() { m_no_export = true; } - bool is_no_export() { return m_no_export; } + bool is_no_export() const { return m_no_export; } private: pstring m_name; @@ -100,20 +124,23 @@ private: , m_has_val(false) {} - const pstring &name() { return m_name;} - const pstring &type() { return m_type;} - const pstring &model() { return m_model;} - const double &value() { return m_val;} + const pstring &name() const { return m_name;} + 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() { return m_model != ""; } - bool has_value() { return m_has_val; } + bool has_model() const { return !m_model.empty(); } + 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 { @@ -128,26 +155,33 @@ private: pin_alias_t(pstring name, pstring alias) : m_name(std::move(name)), m_alias(std::move(alias)) {} - const pstring &name() { return m_name; } - const pstring &alias() { return m_alias; } + const pstring &name() const { return m_name; } + const pstring &alias() const { return m_alias; } private: pstring m_name; pstring m_alias; }; -private: - - void add_device(plib::unique_ptr<dev_t> dev); + void add_device(arena::unique_ptr<dev_t> 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; - std::vector<plib::unique_ptr<dev_t>> m_devs; - std::unordered_map<pstring, plib::unique_ptr<net_t> > m_nets; - std::vector<pstring> m_ext_alias; - std::unordered_map<pstring, plib::unique_ptr<pin_alias_t>> m_pins; + std::vector<arena::unique_ptr<dev_t>> m_devs; + std::unordered_map<pstring, arena::unique_ptr<net_t> > m_nets; + std::vector<std::pair<pstring, pstring>> m_ext_alias; + std::unordered_map<pstring, arena::unique_ptr<pin_alias_t>> m_pins; std::vector<unit_t> m_units; - pstring m_numberchars; + pstring m_number_chars; + + std::unordered_map<pstring, str_list> dev_map; }; @@ -155,37 +189,44 @@ class nl_convert_spice_t : public nl_convert_base_t { public: - nl_convert_spice_t() : nl_convert_base_t() {} + nl_convert_spice_t() : m_is_kicad(false) { } void convert(const pstring &contents) override; protected: + bool is_kicad() const { return m_is_kicad; } + void convert_block(const str_list &contents); void process_line(const pstring &line); private: pstring m_subckt; + bool m_is_kicad; }; class nl_convert_eagle_t : public nl_convert_base_t { public: - nl_convert_eagle_t() : nl_convert_base_t() {} + nl_convert_eagle_t() = default; - class tokenizer : public plib::ptokenizer + class tokenizer : public plib::tokenizer_t, public plib::token_reader_t { public: - tokenizer(nl_convert_eagle_t &convert, plib::putf8_reader &&strm); + using token_t = tokenizer_t::token_t; + using token_type = tokenizer_t::token_type; + using token_id_t = tokenizer_t::token_id_t; + using token_store = tokenizer_t::token_store_t; - token_id_t m_tok_ADD; - token_id_t m_tok_VALUE; - token_id_t m_tok_SIGNAL; - token_id_t m_tok_SEMICOLON; + tokenizer(nl_convert_eagle_t &convert); + token_id_t m_tok_ADD; // NOLINT + token_id_t m_tok_VALUE; // NOLINT + token_id_t m_tok_SIGNAL; // NOLINT + token_id_t m_tok_SEMICOLON; // NOLINT protected: - void verror(const pstring &msg, int line_num, const pstring &line) override; + void verror(const pstring &msg) override; private: nl_convert_eagle_t &m_convert; @@ -204,26 +245,29 @@ class nl_convert_rinf_t : public nl_convert_base_t { public: - nl_convert_rinf_t() : nl_convert_base_t() {} + nl_convert_rinf_t() = default; - class tokenizer : public plib::ptokenizer + class tokenizer : public plib::tokenizer_t, public plib::token_reader_t { public: - tokenizer(nl_convert_rinf_t &convert, plib::putf8_reader &&strm); - - token_id_t m_tok_HEA; - token_id_t m_tok_APP; - token_id_t m_tok_TIM; - token_id_t m_tok_TYP; - token_id_t m_tok_ADDC; - token_id_t m_tok_ATTC; - token_id_t m_tok_NET; - token_id_t m_tok_TER; - token_id_t m_tok_END; - + using token_t = tokenizer_t::token_t; + using token_type = tokenizer_t::token_type; + using token_id_t = tokenizer_t::token_id_t; + using token_store = tokenizer_t::token_store_t; + tokenizer(nl_convert_rinf_t &convert); + + token_id_t m_tok_HEA; // NOLINT + token_id_t m_tok_APP; // NOLINT + token_id_t m_tok_TIM; // NOLINT + token_id_t m_tok_TYP; // NOLINT + token_id_t m_tok_ADDC; // NOLINT + token_id_t m_tok_ATTC; // NOLINT + token_id_t m_tok_NET; // NOLINT + token_id_t m_tok_TER; // NOLINT + token_id_t m_tok_END; // NOLINT protected: - void verror(const pstring &msg, int line_num, const pstring &line) override; + void verror(const pstring &msg) override; private: nl_convert_rinf_t &m_convert; @@ -238,4 +282,6 @@ private: }; -#endif /* NL_CONVERT_H_ */ +} // namespace netlist::convert + +#endif // NL_CONVERT_H_ |