diff options
Diffstat (limited to 'src/lib/netlist/nl_setup.cpp')
-rw-r--r-- | src/lib/netlist/nl_setup.cpp | 2441 |
1 files changed, 1554 insertions, 887 deletions
diff --git a/src/lib/netlist/nl_setup.cpp b/src/lib/netlist/nl_setup.cpp index 447e78cfe2e..30393603daa 100644 --- a/src/lib/netlist/nl_setup.cpp +++ b/src/lib/netlist/nl_setup.cpp @@ -1,1244 +1,1911 @@ -// license:GPL-2.0+ +// license:BSD-3-Clause // copyright-holders:Couriersud -/* - * nlsetup.c - * - */ -#include "plib/palloc.h" -#include "analog/nld_twoterm.h" -#include "devices/nlid_proxy.h" -#include "devices/nlid_system.h" -#include "devices/nlid_truthtable.h" -#include "nl_base.h" +#include "nl_setup.h" + +#include "nl_errstr.h" #include "nl_factory.h" #include "nl_parser.h" -#include "nl_setup.h" -#include "plib/putil.h" + +#include "core/devices.h" +#include "core/setup.h" + +#include "devices/nlid_proxy.h" +#include "devices/nlid_truthtable.h" + #include "solver/nld_solver.h" -#include <cmath> +#include "plib/palloc.h" +#include "plib/penum.h" +#include "plib/pstonum.h" +#include "plib/pstrutil.h" +#include "plib/putil.h" + +#include <sstream> namespace netlist { - // ---------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------- // nl_parse_t - // ---------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------- - nlparse_t::nlparse_t(setup_t &setup, log_type &log) - : m_factory(log) - , m_setup(setup) + nlparse_t::nlparse_t(log_type &log, detail::abstract_t &abstract) + : m_abstract(abstract) , m_log(log) , m_frontier_cnt(0) - { } + { + } - void nlparse_t::register_alias(const pstring &alias, const pstring &out) + void + nlparse_t::register_fqn_alias(detail::alias_type type, const pstring &alias, + const pstring &points_to) + { + if (!m_abstract.m_aliases + .insert({alias, detail::alias_t(type, alias, points_to)}) + .second) + { + log().fatal(MF_ALIAS_ALREAD_EXISTS_1(alias)); + throw nl_exception(MF_ALIAS_ALREAD_EXISTS_1(alias)); + } + } + + void + nlparse_t::register_alias(const pstring &alias, const pstring &points_to) + { + detail::alias_type type = detail::alias_type::PACKAGE_PIN; + for (auto &c : alias) + if (c < '0' || c > '9') + { + type = detail::alias_type::READABILITY; + break; + } + + register_alias(type, alias, points_to); + } + + void + nlparse_t::register_alias(detail::alias_type type, const pstring &alias, + const pstring &points_to) { pstring alias_fqn = build_fqn(alias); - pstring out_fqn = build_fqn(out); - register_alias_nofqn(alias_fqn, out_fqn); + pstring points_to_fqn = build_fqn(points_to); + register_fqn_alias(type, alias_fqn, points_to_fqn); } - void nlparse_t::register_dippins_arr(const pstring &terms) + void nlparse_t::register_dip_alias_arr(const pstring &terms) { - std::vector<pstring> list(plib::psplit(terms,", ")); - if (list.size() == 0 || (list.size() % 2) == 1) - log().fatal(MF_DIP_PINS_MUST_BE_AN_EQUAL_NUMBER_OF_PINS_1(build_fqn(""))); + const auto list(plib::psplit(terms, pstring(", "))); + if (list.empty() || (list.size() % 2) == 1) + { + log().fatal( + MF_DIP_PINS_MUST_BE_AN_EQUAL_NUMBER_OF_PINS_1(build_fqn(""))); + throw nl_exception( + MF_DIP_PINS_MUST_BE_AN_EQUAL_NUMBER_OF_PINS_1(build_fqn(""))); + } std::size_t n = list.size(); for (std::size_t i = 0; i < n / 2; i++) { - register_alias(plib::pfmt("{1}")(i+1), list[i * 2]); - register_alias(plib::pfmt("{1}")(n-i), list[i * 2 + 1]); + register_alias(detail::alias_type::PACKAGE_PIN, + plib::pfmt("{1}")(i + 1), list[i * 2]); + register_alias(detail::alias_type::PACKAGE_PIN, + plib::pfmt("{1}")(n - i), list[i * 2 + 1]); } } - void nlparse_t::register_dev(const pstring &classname, const pstring &name) + void + nlparse_t::register_dev(const pstring & classname, + std::initializer_list<const char *> more_parameters) { - auto f = m_factory.factory_by_name(classname); - if (f == nullptr) - log().fatal(MF_CLASS_1_NOT_FOUND(classname)); - else + std::vector<pstring> params; + const auto * i(more_parameters.begin()); + pstring name(*i); + ++i; + for (; i != more_parameters.end(); ++i) { - /* make sure we parse macro library entries */ - f->macro_actions(*this, name); - pstring key = build_fqn(name); - if (device_exists(key)) - log().fatal(MF_DEVICE_ALREADY_EXISTS_1(name)); - else - m_device_factory.insert(m_device_factory.end(), {key, f}); + params.emplace_back(*i); + } + register_dev(classname, name, params); + } + + void + nlparse_t::register_dev(const pstring &classname, const pstring &name, + const std::vector<pstring> ¶ms_and_connections, + factory::element_t ** factory_element) + { + + auto *f = factory().factory_by_name(classname); + + // make sure we parse macro library entries + // FIXME: this could be done here if e.g. f + // would have an indication that this is macro element. + if (f->type() == factory::element_type::MACRO) + { + namespace_push(name); + include(f->name()); + namespace_pop(); + } + + pstring key = build_fqn(name); + if (device_exists(key)) + { + log().fatal(MF_DEVICE_ALREADY_EXISTS_1(key)); + throw nl_exception(MF_DEVICE_ALREADY_EXISTS_1(key)); + } + + m_abstract.m_device_factory.insert(m_abstract.m_device_factory.end(), + {key, f}); + + auto parameter_list = plib::psplit(f->param_desc(), ','); + + if (!params_and_connections.empty()) + { + auto token_ptr(params_and_connections.begin()); + auto token_end(params_and_connections.end()); + + for (const pstring &tp : parameter_list) + { + if (plib::startsWith(tp, "+")) + { + if (token_ptr == token_end) + { + auto err = MF_PARAM_COUNT_MISMATCH_2( + name, params_and_connections.size()); + log().fatal(err); + throw nl_exception(err); + // break; + } + pstring output_name = *token_ptr; + log().debug("Link: {1} {2}", tp, output_name); + + register_connection(name + "." + tp.substr(1), output_name); + ++token_ptr; + } + else if (plib::startsWith(tp, "@")) + { + pstring term = tp.substr(1); + log().debug("Link: {1} {2}", tp, term); + + register_connection(name + "." + term, term); + } + else + { + if (token_ptr == token_end) + { + auto err = MF_PARAM_COUNT_MISMATCH_2( + name, params_and_connections.size()); + log().fatal(err); + throw nl_exception(err); + } + pstring fully_qualified_name = name + "." + tp; + + log().debug("Default parameter: {1}\n", + fully_qualified_name); + + register_param(fully_qualified_name, *token_ptr); + + ++token_ptr; + } + } + if (token_ptr != params_and_connections.end()) + { + MF_PARAM_COUNT_EXCEEDED_2 err(name, + params_and_connections.size()); + log().fatal(err); + throw nl_exception(err); + } + } + if (factory_element != nullptr) + *factory_element = f; + } + + void nlparse_t::register_hint(const pstring &object_name, + const pstring &hint_name) + { + const auto name = build_fqn(object_name) + hint_name; + if (!m_abstract.m_hints.insert({name, false}).second) + { + log().fatal(MF_ADDING_HINT_1(name)); + throw nl_exception(MF_ADDING_HINT_1(name)); } } - void nlparse_t::register_link(const pstring &sin, const pstring &sout) + void nlparse_t::register_connection(const pstring &sin, const pstring &sout) { - register_link_fqn(build_fqn(sin), build_fqn(sout)); + register_connection_fqn(build_fqn(plib::trim(sin)), + build_fqn(plib::trim(sout))); } - void nlparse_t::register_link_arr(const pstring &terms) + void nlparse_t::register_connection_arr(const pstring &terms) { - std::vector<pstring> list(plib::psplit(terms,", ")); + const auto list(plib::psplit(terms, pstring(", "))); if (list.size() < 2) + { log().fatal(MF_NET_C_NEEDS_AT_LEAST_2_TERMINAL()); + throw nl_exception(MF_NET_C_NEEDS_AT_LEAST_2_TERMINAL()); + } for (std::size_t i = 1; i < list.size(); i++) { - register_link(list[0], list[i]); + register_connection(list[0], list[i]); } } void nlparse_t::include(const pstring &netlist_name) { - for (auto &source : m_sources) - { - if (source->parse(*this, netlist_name)) - return; - } + if (m_sources.for_all<source_netlist_t>( + [this, &netlist_name](source_netlist_t *src) + { return src->parse(*this, netlist_name); })) + return; log().fatal(MF_NOT_FOUND_IN_SOURCE_COLLECTION(netlist_name)); + throw nl_exception(MF_NOT_FOUND_IN_SOURCE_COLLECTION(netlist_name)); } - void nlparse_t::namespace_push(const pstring &aname) { if (m_namespace_stack.empty()) - //m_namespace_stack.push(netlist().name() + "." + aname); + // m_namespace_stack.push(netlist().name() + "." + aname); m_namespace_stack.push(aname); else m_namespace_stack.push(m_namespace_stack.top() + "." + aname); } - void nlparse_t::namespace_pop() - { - m_namespace_stack.pop(); - } + void nlparse_t::namespace_pop() { m_namespace_stack.pop(); } - void nlparse_t::register_param(const pstring ¶m, const double value) + void + nlparse_t::register_param_fp(const pstring ¶m, const nl_fptype value) { - if (std::abs(value - std::floor(value)) > 1e-30 || std::abs(value) > 1e9) + if (plib::abs(value - plib::floor(value)) > nlconst::magic(1e-30) + || plib::abs(value) > nlconst::magic(1e9)) register_param(param, plib::pfmt("{1:.9}").e(value)); else - register_param(param, plib::pfmt("{1}")(static_cast<long>(value))); + register_param(param, plib::pfmt("{1}")(gsl::narrow<long>(value))); } void nlparse_t::register_param(const pstring ¶m, const pstring &value) { pstring fqn = build_fqn(param); + pstring val(value); + + // strip " from stringified strings + if (plib::startsWith(value, "\"") && plib::endsWith(value, "\"")) + val = value.substr(1, value.length() - 2); - auto idx = m_param_values.find(fqn); - if (idx == m_param_values.end()) + // Replace "@." with the current namespace + val = plib::replace_all(val, "@.", namespace_prefix()); + auto idx = m_abstract.m_param_values.find(fqn); + if (idx == m_abstract.m_param_values.end()) { - if (!m_param_values.insert({fqn, value}).second) + if (!m_abstract.m_param_values.insert({fqn, val}).second) + { log().fatal(MF_ADDING_PARAMETER_1_TO_PARAMETER_LIST(param)); + throw nl_exception( + MF_ADDING_PARAMETER_1_TO_PARAMETER_LIST(param)); + } } else { - log().warning(MW_OVERWRITING_PARAM_1_OLD_2_NEW_3(fqn, idx->second, - value)); - m_param_values[fqn] = value; + if (idx->second.find("$(") == pstring::npos) + { + // There may be reason ... so make it an INFO + log().info( + MI_OVERWRITING_PARAM_1_OLD_2_NEW_3(fqn, idx->second, val)); + } + m_abstract.m_param_values[fqn] = val; } } - void nlparse_t::register_lib_entry(const pstring &name, const pstring &sourcefile) + void + nlparse_t::register_default_param(const pstring &name, const pstring &def) + { + // strip " from stringified strings + pstring val(def); + if (plib::startsWith(def, "\"") && plib::endsWith(def, "\"")) + val = def.substr(1, def.length() - 2); + // Replace "@." with the current namespace + val = plib::replace_all(val, "@.", namespace_prefix()); + m_abstract.m_default_params.emplace_back(namespace_prefix() + name, + val); + } + + factory::list_t &nlparse_t::factory() noexcept { - m_factory.register_device(plib::make_unique<factory::library_element_t>(name, name, "", sourcefile)); + return m_abstract.m_factory; } - void nlparse_t::register_frontier(const pstring &attach, const double r_IN, const double r_OUT) + const factory::list_t &nlparse_t::factory() const noexcept + { + return m_abstract.m_factory; + } + + void nlparse_t::register_lib_entry(const pstring & name, + const pstring & def_params, + plib::source_location &&loc) + { + factory().add(plib::make_unique<factory::library_element_t, host_arena>( + name, factory::properties(def_params, std::move(loc)))); + } + + void nlparse_t::register_frontier(const pstring &attach, + const pstring &r_IN, const pstring &r_OUT) { pstring frontier_name = plib::pfmt("frontier_{1}")(m_frontier_cnt); m_frontier_cnt++; register_dev("FRONTIER_DEV", frontier_name); register_param(frontier_name + ".RIN", r_IN); register_param(frontier_name + ".ROUT", r_OUT); - register_link(frontier_name + ".G", "GND"); - pstring attfn = build_fqn(attach); + register_connection(frontier_name + ".G", "GND"); + pstring attach_fully_qualified_name = build_fqn(attach); pstring front_fqn = build_fqn(frontier_name); - bool found = false; - for (auto & link : m_links) + bool found = false; + for (auto &link : m_abstract.m_connections) { - if (link.first == attfn) + if (link.first == attach_fully_qualified_name) { link.first = front_fqn + ".I"; found = true; } - else if (link.second == attfn) + else if (link.second == attach_fully_qualified_name) { link.second = front_fqn + ".I"; found = true; } } if (!found) + { log().fatal(MF_FOUND_NO_OCCURRENCE_OF_1(attach)); - register_link(attach, frontier_name + ".Q"); + throw nl_exception(MF_FOUND_NO_OCCURRENCE_OF_1(attach)); + } + register_connection(attach, frontier_name + ".Q"); } - void nlparse_t::tt_factory_create(tt_desc &desc, const pstring &sourcefile) + void nlparse_t::register_source_proc(const pstring &name, nlsetup_func func) { - auto fac = devices::tt_factory_create(desc, sourcefile); - m_factory.register_device(std::move(fac)); + register_source<netlist::source_proc_t>(name, func); } - pstring nlparse_t::build_fqn(const pstring &obj_name) const + void nlparse_t::truth_table_create(tt_desc &desc, const pstring &def_params, + plib::source_location &&loc) { - if (m_namespace_stack.empty()) - //return netlist().name() + "." + obj_name; - return obj_name; - else - return m_namespace_stack.top() + "." + obj_name; + auto fac = factory::truth_table_create( + desc, netlist::factory::properties(def_params, std::move(loc))); + factory().add(std::move(fac)); } - void nlparse_t::register_alias_nofqn(const pstring &alias, const pstring &out) + pstring nlparse_t::namespace_prefix() const { - if (!m_alias.insert({alias, out}).second) - log().fatal(MF_ADDING_ALI1_TO_ALIAS_LIST(alias)); + return (m_namespace_stack.empty() ? "" : m_namespace_stack.top() + "."); } - void nlparse_t::register_link_fqn(const pstring &sin, const pstring &sout) + pstring nlparse_t::build_fqn(const pstring &obj_name) const { - link_t temp = link_t(sin, sout); + return namespace_prefix() + obj_name; + } + + void + nlparse_t::register_connection_fqn(const pstring &sin, const pstring &sout) + { + detail::abstract_t::connection_t temp(sin, sout); log().debug("link {1} <== {2}", sin, sout); - m_links.push_back(temp); + m_abstract.m_connections.push_back(temp); } bool nlparse_t::device_exists(const pstring &name) const { - for (auto &d : m_device_factory) + for (const auto &d : m_abstract.m_device_factory) if (d.first == name) return true; return false; } - bool nlparse_t::parse_stream(plib::unique_ptr<std::istream> &&istrm, const pstring &name) + bool nlparse_t::parse_tokens(const plib::detail::token_store_t &tokens, + const pstring & name) + { + parser_t parser(*this); + return parser.parse(tokens, name); + } + + bool + nlparse_t::parse_stream(plib::istream_uptr &&in_stream, const pstring &name) { - plib::ppreprocessor y(&m_defines); - plib::ppreprocessor &x(y.process(std::move(istrm))); - return parser_t(std::move(x), *this).parse(name); - //return parser_t(std::move(plib::ppreprocessor(&m_defines).process(std::move(istrm))), *this).parse(name); +#if 0 + auto key = in_stream.filename(); + + if (m_source_cache.find(key) != m_source_cache.end()) + { + return parser_t(*this).parse(m_source_cache[key], name); + } + else + { + auto preprocessed = std::make_unique<std::stringstream>( + plib::ppreprocessor(m_includes, &m_defines).process(std::move(in_stream), in_stream.filename())); + + parser_t::token_store_t &st = m_source_cache[key]; + parser_t parser(*this); + parser.parse_tokens(plib::istream_uptr(std::move(preprocessed), key), st); + return parser.parse(st, name); + } +#else + const auto filename = in_stream.filename(); + auto preprocessed = std::make_unique<std::stringstream>( + putf8string(plib::ppreprocessor(m_includes, &m_defines) + .process(std::move(in_stream), filename))); + + parser_t::token_store_t st; + parser_t parser(*this); + parser.parse_tokens( + plib::istream_uptr(std::move(preprocessed), filename), st); + return parser.parse(st, name); +#endif } - void nlparse_t::add_define(const pstring &defstr) + void nlparse_t::add_define(const pstring &define) { - auto p = defstr.find('='); + auto p = define.find('='); if (p != pstring::npos) - add_define(plib::left(defstr, p), defstr.substr(p+1)); + add_define(plib::left(define, p), define.substr(p + 1)); else - add_define(defstr, "1"); + add_define(define, "1"); } - // ---------------------------------------------------------------------------------------- - // setup_t - // ---------------------------------------------------------------------------------------- + void nlparse_t::register_dynamic_log_devices( + const std::vector<pstring> &log_list) + { + log().debug("Creating dynamic logs ..."); + for (const pstring &ll : log_list) + { + pstring name = "log_" + ll; + register_dev("LOG", name); + register_connection(name + ".I", ll); + } + } + + void nlparse_t::remove_connections(const pstring &pin) + { + // FIXME: check use_cases - remove_connections may be dead + pstring pin_fully_qualified_name = build_fqn(pin); + bool found = false; -setup_t::setup_t(netlist_state_t &nlstate) - : nlparse_t(*this, nlstate.log()) + for (auto link = m_abstract.m_connections.begin(); + link != m_abstract.m_connections.end();) + { + if ((link->first == pin_fully_qualified_name) + || (link->second == pin_fully_qualified_name)) + { + log().verbose("removing connection: {1} <==> {2}\n", + link->first, link->second); + link = m_abstract.m_connections.erase(link); + found = true; + } + else + link++; + } + if (!found) + { + log().fatal(MF_FOUND_NO_OCCURRENCE_OF_1(pin)); + throw nl_exception(MF_FOUND_NO_OCCURRENCE_OF_1(pin)); + } + } + + void nlparse_t::register_model(const pstring &model_in) + { + auto pos = model_in.find(' '); + if (pos == pstring::npos) + throw nl_exception(MF_UNABLE_TO_PARSE_MODEL_1(model_in)); + pstring model = plib::ucase(plib::trim(plib::left(model_in, pos))); + pstring def = plib::trim(model_in.substr(pos + 1)); + if (!m_abstract.m_models.insert({model, def}).second) + { + // FIXME: Add an directive MODEL_OVERWRITE to netlist language + // throw nl_exception(MF_MODEL_ALREADY_EXISTS_1(model_in)); + log().info(MI_MODEL_OVERWRITE_1(model, model_in)); + m_abstract.m_models[model] = def; + } + } + + // ------------------------------------------------------------------------- + // Sources + // ------------------------------------------------------------------------- + + plib::istream_uptr nlparse_t::get_data_stream(const pstring &name) + { + auto strm = m_sources.get_stream<source_data_t>(name); + if (!strm.empty()) + return strm; + log().warning(MW_DATA_1_NOT_FOUND(name)); + return plib::istream_uptr(); + } + + // ------------------------------------------------------------------------- + // setup_t + // ------------------------------------------------------------------------- + + setup_t::setup_t(netlist_state_t &nlstate) + : m_abstract(nlstate.log()) + , m_parser(nlstate.log(), m_abstract) , m_nlstate(nlstate) + , m_models(m_abstract.m_models) // FIXME : parse abstract_t only , m_netlist_params(nullptr) , m_proxy_cnt(0) - , m_validation(false) -{ -} - -setup_t::~setup_t() noexcept -{ - // FIXME: can't see a need any longer - m_links.clear(); - m_params.clear(); - m_terminals.clear(); - m_param_values.clear(); - m_sources.clear(); -} - -pstring setup_t::termtype_as_str(detail::core_terminal_t &in) const -{ - switch (in.type()) { - case detail::terminal_type::TERMINAL: - return pstring("TERMINAL"); - case detail::terminal_type::INPUT: - return pstring("INPUT"); - case detail::terminal_type::OUTPUT: - return pstring("OUTPUT"); } - log().fatal(MF_UNKNOWN_OBJECT_TYPE_1(static_cast<unsigned>(in.type()))); - return pstring("Error"); -} -pstring setup_t::get_initial_param_val(const pstring &name, const pstring &def) const -{ - auto i = m_param_values.find(name); - if (i != m_param_values.end()) - return i->second; - else - return def; -} - -void setup_t::register_term(detail::core_terminal_t &term) -{ - if (!m_terminals.insert({term.name(), &term}).second) + pstring setup_t::termtype_as_str(detail::core_terminal_t &in) { - log().fatal(MF_ADDING_1_2_TO_TERMINAL_LIST(termtype_as_str(term), term.name())); + switch (in.type()) + { + case detail::terminal_type::TERMINAL: return "TERMINAL"; + case detail::terminal_type::INPUT: return "INPUT"; + case detail::terminal_type::OUTPUT: return "OUTPUT"; + } + return "Error"; // Tease gcc } - log().debug("{1} {2}\n", termtype_as_str(term), term.name()); -} -void setup_t::remove_connections(const pstring &pin) -{ - pstring pinfn = build_fqn(pin); - bool found = false; + pstring setup_t::get_initial_param_val(const pstring &name, + const pstring &def) const + { + // When `get_intial_param_val` is called the parameter `<name>` is + // already registered and the value `(value_string())` is set to the + // default value, e.g. "74XX" If thus `$(IC5E.A.MODEL)` is given for + // `name=="IC5E.A.MODEL"` `value_string()` below will return the + // default. + // FIXME: It may be more explicit and stable to test if pattern==name + // and return `def` in this case. + + auto i = m_abstract.m_param_values.find(name); + auto found_pat(false); + pstring v = (i == m_abstract.m_param_values.end()) ? def : i->second; - for (auto link = m_links.begin(); link != m_links.end(); ) + do + { + found_pat = false; + auto sp(plib::psplit(v, std::vector<pstring>({"$(", ")"}))); + std::size_t p(0); + v = ""; + while (p < sp.size()) + { + if (sp[p] == "$(") + { + p++; + pstring r; + while (p < sp.size() && sp[p] != ")") + r += sp[p++]; + p++; + auto k = m_params.find(r); + if (k != m_params.end()) + { + v = v + k->second.param().value_string(); + found_pat = true; + } + else + { + // pass - on + v = v + "$(" + r + ")"; + } + } + else + v += sp[p++]; + } + } while (found_pat); + + return v; + } + + void setup_t::register_term(detail::core_terminal_t &term) { - if ((link->first == pinfn) || (link->second == pinfn)) + log().debug("{1} {2}\n", termtype_as_str(term), term.name()); + if (!m_terminals.insert({term.name(), &term}).second) { - log().verbose("removing connection: {1} <==> {2}\n", link->first, link->second); - link = m_links.erase(link); - found = true; + log().fatal(MF_ADDING_1_2_TO_TERMINAL_LIST(termtype_as_str(term), + term.name())); + throw nl_exception(MF_ADDING_1_2_TO_TERMINAL_LIST( + termtype_as_str(term), term.name())); } - else - link++; } - if (!found) - log().fatal(MF_FOUND_NO_OCCURRENCE_OF_1(pin)); -} -void setup_t::register_param_t(const pstring &name, param_t ¶m) -{ - if (!m_params.insert({param.name(), param_ref_t(param.name(), param.device(), param)}).second) - log().fatal(MF_ADDING_PARAMETER_1_TO_PARAMETER_LIST(name)); -} + void + setup_t::register_term(terminal_t &term, terminal_t *other_term, + const std::array<terminal_t *, 2> &splitter_terms) + { + this->register_term(term); + m_connected_terminals.insert( + {&term, + {other_term, splitter_terms[0], splitter_terms[1], nullptr}}); + } -pstring setup_t::resolve_alias(const pstring &name) const -{ - pstring temp = name; - pstring ret; + void setup_t::register_param_t(param_t ¶m) + { + if (!m_params.insert({param.name(), param_ref_t(param.device(), param)}) + .second) + { + log().fatal(MF_ADDING_PARAMETER_1_TO_PARAMETER_LIST(param.name())); + throw nl_exception( + MF_ADDING_PARAMETER_1_TO_PARAMETER_LIST(param.name())); + } + } - /* FIXME: Detect endless loop */ - do { - ret = temp; - auto p = m_alias.find(ret); - temp = (p != m_alias.end() ? p->second : ""); - } while (temp != "" && temp != ret); + pstring setup_t::resolve_alias(const pstring &name) const + { + pstring temp = name; + pstring ret; - log().debug("{1}==>{2}\n", name, ret); - return ret; -} + // FIXME: Detect endless loop + do + { + ret = temp; + auto p = m_abstract.m_aliases.find(ret); + temp = (p != m_abstract.m_aliases.end() ? p->second.references() + : ""); + } while (!temp.empty() && temp != ret); -pstring setup_t::de_alias(const pstring &alias) const -{ - pstring temp = alias; - pstring ret; + log().debug("{1}==>{2}\n", name, ret); + return ret; + } - /* FIXME: Detect endless loop */ - do { - ret = temp; - temp = ""; - for (auto &e : m_alias) + pstring setup_t::de_alias(const pstring &alias) const + { + pstring temp = alias; + pstring ret; + + // FIXME: Detect endless loop + do { - // FIXME: this will resolve first one found - if (e.second == ret) + ret = temp; + temp = ""; + for (const auto &e : m_abstract.m_aliases) { - temp = e.first; - break; + // FIXME: this will resolve first one found + if (e.second.references() == ret) + { + temp = e.first; + break; + } } - } - } while (temp != "" && temp != ret); + } while (!temp.empty() && temp != ret); - log().debug("{1}==>{2}\n", alias, ret); - return ret; -} + log().debug("{1}==>{2}\n", alias, ret); + return ret; + } -std::vector<pstring> setup_t::get_terminals_for_device_name(const pstring &devname) const -{ - std::vector<pstring> terms; - for (auto & t : m_terminals) + std::vector<pstring> + setup_t::get_terminals_for_device_name(const pstring &devname) const { - if (plib::startsWith(t.second->name(), devname)) + std::vector<pstring> terms; + for (const auto &t : m_terminals) { - pstring tn(t.second->name().substr(devname.length()+1)); - if (tn.find('.') == pstring::npos) - terms.push_back(tn); + if (plib::startsWith(t.second->name(), devname)) + { + pstring tn(t.second->name().substr(devname.length() + 1)); + if (tn.find('.') == pstring::npos) + terms.push_back(tn); + } } - } - for (auto & t : m_alias) - { - if (plib::startsWith(t.first, devname)) + for (const auto &t : m_abstract.m_aliases) { - pstring tn(t.first.substr(devname.length()+1)); - //printf("\t%s %s %s\n", t.first.c_str(), t.second.c_str(), tn.c_str()); - if (tn.find('.') == pstring::npos) + if (plib::startsWith(t.first, devname)) { - terms.push_back(tn); - pstring resolved = resolve_alias(t.first); - //printf("\t%s %s %s\n", t.first.c_str(), t.second.c_str(), resolved.c_str()); - if (resolved != t.first) + pstring tn(t.first.substr(devname.length() + 1)); + if (tn.find('.') == pstring::npos) { - auto found = std::find(terms.begin(), terms.end(), resolved.substr(devname.length()+1)); - if (found!=terms.end()) - terms.erase(found); + terms.push_back(tn); + pstring resolved = resolve_alias(t.first); + if (resolved != t.first) + { + auto found = std::find( + terms.begin(), terms.end(), + resolved.substr(devname.length() + 1)); + if (found != terms.end()) + terms.erase(found); + } } } } + return terms; } - return terms; -} -detail::core_terminal_t *setup_t::find_terminal(const pstring &terminal_in, bool required) const -{ - const pstring &tname = resolve_alias(terminal_in); - auto ret = m_terminals.find(tname); - /* look for default */ - if (ret == m_terminals.end()) + detail::core_terminal_t * + setup_t::find_terminal(const pstring &terminal_in, bool required) const { - /* look for ".Q" std output */ - ret = m_terminals.find(tname + ".Q"); - } + const pstring &tname = resolve_alias(terminal_in); + auto ret = m_terminals.find(tname); + // look for default + if (ret == m_terminals.end()) + { + // look for ".Q" std output + ret = m_terminals.find(tname + ".Q"); + } - detail::core_terminal_t *term = (ret == m_terminals.end() ? nullptr : ret->second); + detail::core_terminal_t *term = (ret == m_terminals.end() + ? nullptr + : ret->second); - if (term == nullptr && required) - log().fatal(MF_TERMINAL_1_2_NOT_FOUND(terminal_in, tname)); - if (term != nullptr) - log().debug("Found input {1}\n", tname); - return term; -} + if (term == nullptr && required) + { + log().fatal(MF_TERMINAL_1_2_NOT_FOUND(terminal_in, tname)); + throw nl_exception(MF_TERMINAL_1_2_NOT_FOUND(terminal_in, tname)); + } + if (term != nullptr) + { + log().debug("Found input {1}\n", tname); + } -detail::core_terminal_t *setup_t::find_terminal(const pstring &terminal_in, - detail::terminal_type atype, bool required) const -{ - const pstring &tname = resolve_alias(terminal_in); - auto ret = m_terminals.find(tname); - /* look for default */ - if (ret == m_terminals.end() && atype == detail::terminal_type::OUTPUT) - { - /* look for ".Q" std output */ - ret = m_terminals.find(tname + ".Q"); + return term; } - if (ret == m_terminals.end() && required) - log().fatal(MF_TERMINAL_1_2_NOT_FOUND(terminal_in, tname)); - - detail::core_terminal_t *term = (ret == m_terminals.end() ? nullptr : ret->second); - if (term != nullptr && term->type() != atype) + detail::core_terminal_t * + setup_t::find_terminal(const pstring & terminal_in, + detail::terminal_type atype, bool required) const { - if (required) - log().fatal(MF_OBJECT_1_2_WRONG_TYPE(terminal_in, tname)); - else + const pstring &tname = resolve_alias(terminal_in); + auto ret = m_terminals.find(tname); + // look for default + if (ret == m_terminals.end() && atype == detail::terminal_type::OUTPUT) + { + // look for ".Q" std output + ret = m_terminals.find(tname + ".Q"); + } + if (ret == m_terminals.end() && required) + { + log().fatal(MF_TERMINAL_1_2_NOT_FOUND(terminal_in, tname)); + throw nl_exception(MF_TERMINAL_1_2_NOT_FOUND(terminal_in, tname)); + } + detail::core_terminal_t *term = (ret == m_terminals.end() + ? nullptr + : ret->second); + + if (term != nullptr && term->type() != atype) + { + if (required) + { + log().fatal(MF_OBJECT_1_2_WRONG_TYPE(terminal_in, tname)); + throw nl_exception( + MF_OBJECT_1_2_WRONG_TYPE(terminal_in, tname)); + } + term = nullptr; + } + if (term != nullptr) + log().debug("Found input {1}\n", tname); + + return term; } - if (term != nullptr) - log().debug("Found input {1}\n", tname); - return term; -} + param_ref_t setup_t::find_param(const pstring ¶m_in) const + { + const pstring resolved_param_name(resolve_alias(param_in)); + auto ret(m_params.find(resolved_param_name)); + if (ret == m_params.end()) + { + log().fatal( + MF_PARAMETER_1_2_NOT_FOUND(param_in, resolved_param_name)); + throw nl_exception( + MF_PARAMETER_1_2_NOT_FOUND(param_in, resolved_param_name)); + } + return ret->second; + } -param_t *setup_t::find_param(const pstring ¶m_in, bool required) const -{ - const pstring param_in_fqn = build_fqn(param_in); - - const pstring &outname = resolve_alias(param_in_fqn); - auto ret = m_params.find(outname); - if (ret == m_params.end() && required) - log().fatal(MF_PARAMETER_1_2_NOT_FOUND(param_in_fqn, outname)); - if (ret != m_params.end()) - log().debug("Found parameter {1}\n", outname); - return (ret == m_params.end() ? nullptr : &ret->second.m_param); -} - -devices::nld_base_proxy *setup_t::get_d_a_proxy(detail::core_terminal_t &out) -{ - nl_assert(out.is_logic()); + // NOLINTNEXTLINE(misc-no-recursion) + devices::nld_base_proxy * + setup_t::get_d_a_proxy(const detail::core_terminal_t &out) + { + auto iter_proxy(m_proxies.find(&out)); - auto &out_cast = static_cast<logic_output_t &>(out); - devices::nld_base_proxy *proxy = out_cast.get_proxy(); + if (iter_proxy != m_proxies.end()) + return iter_proxy->second; - if (proxy == nullptr) - { // create a new one ... + + auto out_cast = plib::dynamic_downcast<const logic_output_t *>(out); + nl_assert_always(bool(out_cast), "Not able to cast to logic_output_t&"); + pstring x = plib::pfmt("proxy_da_{1}_{2}")(out.name())(m_proxy_cnt); - auto new_proxy = - out_cast.logic_family()->create_d_a_proxy(m_nlstate, x, &out_cast); + auto new_proxy = (*out_cast)->logic_family()->create_d_a_proxy( + m_nlstate, x, *out_cast); m_proxy_cnt++; - /* connect all existing terminals to new net */ + // connect all existing terminals to new net - for (auto & p : out.net().core_terms()) + // Get a copy first + auto temp_terminals(out.net().core_terms_copy()); + // remove all terminals from out.net() + out.net().remove_all_terminals(); + + for (detail::core_terminal_t *p : temp_terminals) { p->clear_net(); // de-link from all nets ... if (!connect(new_proxy->proxy_term(), *p)) - log().fatal(MF_CONNECTING_1_TO_2( - new_proxy->proxy_term().name(), (*p).name())); + { + log().fatal(MF_CONNECTING_1_TO_2(new_proxy->proxy_term().name(), + (*p).name())); + throw nl_exception(MF_CONNECTING_1_TO_2( + new_proxy->proxy_term().name(), (*p).name())); + } } - out.net().core_terms().clear(); // clear the list out.net().add_terminal(new_proxy->in()); - out_cast.set_proxy(proxy); - proxy = new_proxy.get(); + auto *proxy(new_proxy.get()); + if (!m_proxies.insert({&out, proxy}).second) + throw nl_exception(MF_DUPLICATE_PROXY_1(out.name())); - m_nlstate.add_dev(new_proxy->name(), std::move(new_proxy)); + m_nlstate.register_device(new_proxy->name(), std::move(new_proxy)); + return proxy; } - return proxy; -} -devices::nld_base_proxy *setup_t::get_a_d_proxy(detail::core_terminal_t &inp) -{ - nl_assert(inp.is_logic()); + // NOLINTNEXTLINE(misc-no-recursion) + devices::nld_base_proxy * + setup_t::get_a_d_proxy(detail::core_terminal_t &inp) + { + gsl_Expects(inp.is_logic()); - auto &incast = dynamic_cast<logic_input_t &>(inp); - devices::nld_base_proxy *proxy = incast.get_proxy(); + const auto &logic_input_terminal = dynamic_cast<const logic_input_t &>( + inp); + + auto iter_proxy(m_proxies.find(&inp)); + + if (iter_proxy != m_proxies.end()) + return iter_proxy->second; - if (proxy != nullptr) - return proxy; - else - { log().debug("connect_terminal_input: connecting proxy\n"); - pstring x = plib::pfmt("proxy_ad_{1}_{2}")(inp.name())(m_proxy_cnt); - auto new_proxy = incast.logic_family()->create_a_d_proxy(m_nlstate, x, &incast); - //auto new_proxy = plib::owned_ptr<devices::nld_a_to_d_proxy>::Create(netlist(), x, &incast); - incast.set_proxy(new_proxy.get()); - m_proxy_cnt++; + auto new_proxy = logic_input_terminal.logic_family()->create_a_d_proxy( + m_nlstate, plib::pfmt("proxy_ad_{1}_{2}")(inp.name())(m_proxy_cnt), + &logic_input_terminal); - auto ret = new_proxy.get(); + auto *ret(new_proxy.get()); - /* connect all existing terminals to new net */ + if (!m_proxies.insert({&inp, ret}).second) + throw nl_exception(MF_DUPLICATE_PROXY_1(inp.name())); + + m_proxy_cnt++; + + // connect all existing terminals to new net if (inp.has_net()) { - for (auto & p : inp.net().core_terms()) + auto temp_terminals(inp.net().core_terms_copy()); + + inp.net().remove_all_terminals(); + + for (detail::core_terminal_t *p : temp_terminals) { - p->clear_net(); // de-link from all nets ... - if (!connect(ret->proxy_term(), *p)) - log().fatal(MF_CONNECTING_1_TO_2( + // inp may already belongs to the logic net. Thus skip it here. + // It will be removed by the clear further down. + if (p != &inp) + { + p->clear_net(); // de-link from all nets ... + if (!connect(ret->proxy_term(), *p)) + { + log().fatal(MF_CONNECTING_1_TO_2( ret->proxy_term().name(), (*p).name())); + throw nl_exception(MF_CONNECTING_1_TO_2( + ret->proxy_term().name(), (*p).name())); + } + } } - inp.net().core_terms().clear(); // clear the list } + inp.clear_net(); ret->out().net().add_terminal(inp); - m_nlstate.add_dev(new_proxy->name(), std::move(new_proxy)); + m_nlstate.register_device(new_proxy->name(), std::move(new_proxy)); return ret; } -} -void setup_t::merge_nets(detail::net_t &thisnet, detail::net_t &othernet) -{ - log().debug("merging nets ...\n"); - if (&othernet == &thisnet) + detail::core_terminal_t & + setup_t::resolve_proxy(detail::core_terminal_t &term) { - log().warning(MW_CONNECTING_1_TO_ITSELF(thisnet.name())); - return; // Nothing to do + if (term.is_logic()) + { + const auto &out = dynamic_cast<const logic_t &>(term); + auto iter_proxy(m_proxies.find(&out)); + if (iter_proxy != m_proxies.end()) + return iter_proxy->second->proxy_term(); + } + return term; } - if (thisnet.isRailNet() && othernet.isRailNet()) - log().fatal(MF_MERGE_RAIL_NETS_1_AND_2(thisnet.name(), othernet.name())); - - if (othernet.isRailNet()) + // NOLINTNEXTLINE(misc-no-recursion) + void setup_t::merge_nets(detail::net_t &this_net, detail::net_t &other_net) { - log().debug("othernet is railnet\n"); - merge_nets(othernet, thisnet); - } - else - { - othernet.move_connections(thisnet); - } -} + log().debug("merging nets ...\n"); + if (&other_net == &this_net) + { + log().warning(MW_CONNECTING_1_TO_ITSELF(this_net.name())); + return; // Nothing to do + } + if (this_net.is_rail_net() && other_net.is_rail_net()) + { + log().fatal( + MF_MERGE_RAIL_NETS_1_AND_2(this_net.name(), other_net.name())); + throw nl_exception( + MF_MERGE_RAIL_NETS_1_AND_2(this_net.name(), other_net.name())); + } + if (other_net.is_rail_net()) + { + log().debug("other net is a rail net\n"); + merge_nets(other_net, this_net); + } + else + { + move_connections(other_net, this_net); + } + } -void setup_t::connect_input_output(detail::core_terminal_t &in, detail::core_terminal_t &out) -{ - if (out.is_analog() && in.is_logic()) + // NOLINTNEXTLINE(misc-no-recursion) + void setup_t::connect_input_output(detail::core_terminal_t &input, + detail::core_terminal_t &output) { - auto proxy = get_a_d_proxy(in); + if (input.has_net() && input.net().is_rail_net()) + { + log().fatal(MF_INPUT_1_ALREADY_CONNECTED(input.name())); + throw nl_exception(MF_INPUT_1_ALREADY_CONNECTED(input.name())); + } + if (output.is_analog() && input.is_logic()) + { + auto *proxy = get_a_d_proxy(input); - out.net().add_terminal(proxy->proxy_term()); - } - else if (out.is_logic() && in.is_analog()) - { - devices::nld_base_proxy *proxy = get_d_a_proxy(out); + output.net().add_terminal(proxy->proxy_term()); + } + else if (output.is_logic() && input.is_analog()) + { + devices::nld_base_proxy *proxy = get_d_a_proxy(output); - connect_terminals(proxy->proxy_term(), in); - //proxy->out().net().register_con(in); - } - else - { - if (in.has_net()) - merge_nets(out.net(), in.net()); + connect_terminals(proxy->proxy_term(), input); + } else - out.net().add_terminal(in); + { + if (input.has_net()) + merge_nets(output.net(), input.net()); + else if (output.has_net()) + output.net().add_terminal(input); + else + { + log().fatal( + ME_TERMINALS_1_2_WITHOUT_NET(input.name(), output.name())); + throw nl_exception( + ME_TERMINALS_1_2_WITHOUT_NET(input.name(), output.name())); + } + } } -} - -void setup_t::connect_terminal_input(terminal_t &term, detail::core_terminal_t &inp) -{ - if (inp.is_analog()) + // NOLINTNEXTLINE(misc-no-recursion) + void setup_t::connect_terminal_input(detail::core_terminal_t &terminal, + detail::core_terminal_t &input) { - connect_terminals(inp, term); + if (input.is_analog()) + { + connect_terminals(input, terminal); + } + else if (input.is_logic()) + { + log().verbose("connect terminal {1} (in, {2}) to {3}\n", + input.name(), + input.is_analog() ? "analog" + : input.is_logic() ? "logic" + : "?", + terminal.name()); + auto *proxy = get_a_d_proxy(input); + + // out.net().register_con(proxy->proxy_term()); + connect_terminals(terminal, proxy->proxy_term()); + } + else + { + log().fatal(MF_OBJECT_INPUT_TYPE_1(input.name())); + throw nl_exception(MF_OBJECT_INPUT_TYPE_1(input.name())); + } } - else if (inp.is_logic()) - { - log().verbose("connect terminal {1} (in, {2}) to {3}\n", inp.name(), - inp.is_analog() ? pstring("analog") : inp.is_logic() ? pstring("logic") : pstring("?"), term.name()); - auto proxy = get_a_d_proxy(inp); - //out.net().register_con(proxy->proxy_term()); - connect_terminals(term, proxy->proxy_term()); - - } - else + // NOLINTNEXTLINE(misc-no-recursion) + void setup_t::connect_terminal_output(detail::core_terminal_t &terminal, + detail::core_terminal_t &output) { - log().fatal(MF_OBJECT_INPUT_TYPE_1(inp.name())); - } -} + if (output.is_analog()) + { + log().debug("connect_terminal_output: {1} {2}\n", terminal.name(), + output.name()); + // no proxy needed, just merge existing terminal net + if (terminal.has_net()) + { + if (&output.net() != &terminal.net()) + merge_nets(output.net(), terminal.net()); + else + // Only an info - some ICs (CD4538) connect pins internally + // to GND and the schematics again externally. This will + // cause this warning. + // FIXME: Add a hint to suppress the warning. + log().info(MI_CONNECTING_1_TO_2_SAME_NET( + terminal.name(), output.name(), terminal.net().name())); + } + else + output.net().add_terminal(terminal); + } + else if (output.is_logic()) + { + log().debug("connect_terminal_output: connecting proxy\n"); + devices::nld_base_proxy *proxy = get_d_a_proxy(output); -void setup_t::connect_terminal_output(terminal_t &in, detail::core_terminal_t &out) -{ - if (out.is_analog()) - { - log().debug("connect_terminal_output: {1} {2}\n", in.name(), out.name()); - /* no proxy needed, just merge existing terminal net */ - if (in.has_net()) - merge_nets(out.net(), in.net()); + connect_terminals(proxy->proxy_term(), terminal); + } else - out.net().add_terminal(in); + { + log().fatal(MF_OBJECT_OUTPUT_TYPE_1(output.name())); + throw nl_exception(MF_OBJECT_OUTPUT_TYPE_1(output.name())); + } } - else if (out.is_logic()) - { - log().debug("connect_terminal_output: connecting proxy\n"); - devices::nld_base_proxy *proxy = get_d_a_proxy(out); - connect_terminals(proxy->proxy_term(), in); - } - else + void setup_t::connect_terminals(detail::core_terminal_t &t1, + detail::core_terminal_t &t2) { - log().fatal(MF_OBJECT_OUTPUT_TYPE_1(out.name())); + if (t1.has_net() && t2.has_net()) + { + log().debug("T2 and T1 have net\n"); + merge_nets(t1.net(), t2.net()); + } + else if (t2.has_net()) + { + log().debug("T2 has net\n"); + t2.net().add_terminal(t1); + } + else if (t1.has_net()) + { + log().debug("T1 has net\n"); + t1.net().add_terminal(t2); + } + else + { + log().debug("adding analog net ...\n"); + // FIXME: Nets should have a unique name + auto new_net_uptr = plib::make_owned<analog_net_t>( + nlstate().pool(), m_nlstate, "net." + t1.name()); + auto *new_net_ptr = new_net_uptr.get(); + m_nlstate.register_net(std::move(new_net_uptr)); + t1.set_net(new_net_ptr); + new_net_ptr->add_terminal(t2); + new_net_ptr->add_terminal(t1); + } } -} -void setup_t::connect_terminals(detail::core_terminal_t &t1, detail::core_terminal_t &t2) -{ - if (t1.has_net() && t2.has_net()) - { - log().debug("T2 and T1 have net\n"); - merge_nets(t1.net(), t2.net()); - } - else if (t2.has_net()) - { - log().debug("T2 has net\n"); - t2.net().add_terminal(t1); - } - else if (t1.has_net()) + // NOLINTNEXTLINE(misc-no-recursion) + bool setup_t::connect_input_input(detail::core_terminal_t &input1, + detail::core_terminal_t &input2) { - log().debug("T1 has net\n"); - t1.net().add_terminal(t2); + bool ret = false; + if (input1.has_net()) // if input 1 already has a net + { + if (input1.net().is_rail_net()) // and the net is a rail net + ret = connect(input2, input1.net().rail_terminal()); // try to + // connect + // input 2 + // to rail + // terminal + // of input + // 1 net + if (!ret) + { + // the above was not successfull - try to connect input2 to + // TERMINAL type terminals of input 1 net's terminals + for (detail::core_terminal_t *t : + input1.net().core_terms_copy()) + { + if (t->is_type(detail::terminal_type::TERMINAL)) + ret = connect(input2, *t); + if (ret) + break; + } + } + } + // FIXME: We could use a helper connect_input_input_helper(input2, + // input1) here. + if (!ret && input2.has_net()) + { + if (input2.net().is_rail_net()) + ret = connect(input1, input2.net().rail_terminal()); + if (!ret) + { + for (detail::core_terminal_t *t : + input2.net().core_terms_copy()) + { + if (t->is_type(detail::terminal_type::TERMINAL)) + ret = connect(input1, *t); + if (ret) + break; + } + } + } + return ret; } - else + + // NOLINTNEXTLINE(misc-no-recursion) + bool setup_t::connect(detail::core_terminal_t &t1_in, + detail::core_terminal_t &t2_in) { - log().debug("adding analog net ...\n"); - // FIXME: Nets should have a unique name - auto anet = pool().make_poolptr<analog_net_t>(m_nlstate,"net." + t1.name()); - auto anetp = anet.get(); - m_nlstate.register_net(std::move(anet)); - t1.set_net(anetp); - anetp->add_terminal(t2); - anetp->add_terminal(t1); + using namespace detail; + + log().debug("Connecting {1} to {2}\n", t1_in.name(), t2_in.name()); + detail::core_terminal_t &t1 = resolve_proxy(t1_in); + detail::core_terminal_t &t2 = resolve_proxy(t2_in); + detail::terminal_type t1_type = t1.type(); + detail::terminal_type t2_type = t2.type(); + bool ret = true; + + switch (t1_type) + { + case terminal_type::TERMINAL: + { + switch (t2_type) + { + case terminal_type::TERMINAL: + connect_terminals(t1, t2); + break; + case terminal_type::INPUT: + connect_terminal_input(t1, t2); + break; + case terminal_type::OUTPUT: + connect_terminal_output(t1, t2); + break; + } + break; + } + case terminal_type::INPUT: + { + switch (t2_type) + { + case terminal_type::TERMINAL: + connect_terminal_input(t2, t1); + break; + case terminal_type::INPUT: + ret = connect_input_input(t1, t2); + break; + case terminal_type::OUTPUT: + connect_input_output(t1, t2); + break; + } + break; + } + case terminal_type::OUTPUT: + { + switch (t2_type) + { + case terminal_type::TERMINAL: + connect_terminal_output(t2, t1); + break; + case terminal_type::INPUT: + connect_input_output(t2, t1); + break; + case terminal_type::OUTPUT: ret = false; break; + } + break; + } + } + return ret; } -} -static detail::core_terminal_t &resolve_proxy(detail::core_terminal_t &term) -{ - if (term.is_logic()) + void setup_t::resolve_inputs() { - auto &out = dynamic_cast<logic_t &>(term); - if (out.has_proxy()) - return out.get_proxy()->proxy_term(); - } - return term; -} + log().verbose("Resolving inputs ..."); + + // Netlist can directly connect input to input. + // We therefore first park connecting inputs and retry + // after all other terminals were connected. -bool setup_t::connect_input_input(detail::core_terminal_t &t1, detail::core_terminal_t &t2) + unsigned tries = m_netlist_params->m_max_link_loops(); +#if 0 + // This code fails for some netlists when the element at position 0 + // is deleted. It will fail somewhere deep in std::pair releasing + // std::string called from erase. + // + // One example is the this netlist: + // + // #include "netlist/devices/net_lib.h" + // NETLIST_START(charge_discharge) { - bool ret = false; - if (t1.has_net()) - { - if (t1.net().isRailNet()) - ret = connect(t2, t1.net().railterminal()); - if (!ret) + // SOLVER(solver, 48000) // Fixed frequency solver + // CLOCK(I, 200) // 200 Hz clock as input, TTL logic output + // RES(R, RES_K(1)) + // CAP(C, CAP_U(1)) + // + // NET_C(I.Q, R.1) + // NET_C(R.2, C.1) + // NET_C(C.2, GND) + // + // ALIAS(O, R.2) // Output O == C.1 == R.2 + // // } + // + // Just save the net list as /tmp/test1.cpp, run + // ./nltool --cmd=run -t 0.05 -l O -l I /tmp/test1.cpp + // and see it crash with this code enabled. + // + // g++-7 (Ubuntu 7.4.0-1ubuntu1~16.04~ppa1) 7.4.0 + // + while (!m_connections.empty() && tries > 0) { - for (auto & t : t1.net().core_terms()) + auto li = m_connections.begin(); + while (li != m_connections.end()) { - if (t->is_type(detail::terminal_type::TERMINAL)) - ret = connect(t2, *t); - if (ret) - break; + const pstring t1s = li->first; + const pstring t2s = li->second; + detail::core_terminal_t *t1 = find_terminal(t1s); + detail::core_terminal_t *t2 = find_terminal(t2s); + + if (connect(*t1, *t2)) + li = m_connections.erase(li); + else + li++; } + tries--; } - } - if (!ret && t2.has_net()) - { - if (t2.net().isRailNet()) - ret = connect(t1, t2.net().railterminal()); - if (!ret) +#else + while (!m_abstract.m_connections.empty() && tries > 0) { - for (auto & t : t2.net().core_terms()) + for (std::size_t i = 0; i < m_abstract.m_connections.size();) { - if (t->is_type(detail::terminal_type::TERMINAL)) - ret = connect(t1, *t); - if (ret) - break; + const pstring t1s(m_abstract.m_connections[i].first); + const pstring t2s(m_abstract.m_connections[i].second); + detail::core_terminal_t *t1 = find_terminal(t1s); + detail::core_terminal_t *t2 = find_terminal(t2s); + if (connect(*t1, *t2)) + m_abstract.m_connections.erase( + m_abstract.m_connections.begin() + + plib::narrow_cast<std::ptrdiff_t>(i)); + else + i++; } + tries--; + } +#endif + if (tries == 0) + { + for (auto &link : m_abstract.m_connections) + log().warning(MF_CONNECTING_1_TO_2(de_alias(link.first), + de_alias(link.second))); + + log().fatal( + MF_LINK_TRIES_EXCEEDED(m_netlist_params->m_max_link_loops())); + throw nl_exception( + MF_LINK_TRIES_EXCEEDED(m_netlist_params->m_max_link_loops())); } - } - return ret; -} -bool setup_t::connect(detail::core_terminal_t &t1_in, detail::core_terminal_t &t2_in) -{ - log().debug("Connecting {1} to {2}\n", t1_in.name(), t2_in.name()); - detail::core_terminal_t &t1 = resolve_proxy(t1_in); - detail::core_terminal_t &t2 = resolve_proxy(t2_in); - bool ret = true; + log().verbose("deleting empty nets ..."); - if (t1.is_type(detail::terminal_type::OUTPUT) && t2.is_type(detail::terminal_type::INPUT)) - { - if (t2.has_net() && t2.net().isRailNet()) - log().fatal(MF_INPUT_1_ALREADY_CONNECTED(t2.name())); - connect_input_output(t2, t1); - } - else if (t1.is_type(detail::terminal_type::INPUT) && t2.is_type(detail::terminal_type::OUTPUT)) - { - if (t1.has_net() && t1.net().isRailNet()) - log().fatal(MF_INPUT_1_ALREADY_CONNECTED(t1.name())); - connect_input_output(t1, t2); - } - else if (t1.is_type(detail::terminal_type::OUTPUT) && t2.is_type(detail::terminal_type::TERMINAL)) - { - connect_terminal_output(dynamic_cast<terminal_t &>(t2), t1); - } - else if (t1.is_type(detail::terminal_type::TERMINAL) && t2.is_type(detail::terminal_type::OUTPUT)) - { - connect_terminal_output(dynamic_cast<terminal_t &>(t1), t2); - } - else if (t1.is_type(detail::terminal_type::INPUT) && t2.is_type(detail::terminal_type::TERMINAL)) - { - connect_terminal_input(dynamic_cast<terminal_t &>(t2), t1); - } - else if (t1.is_type(detail::terminal_type::TERMINAL) && t2.is_type(detail::terminal_type::INPUT)) - { - connect_terminal_input(dynamic_cast<terminal_t &>(t1), t2); - } - else if (t1.is_type(detail::terminal_type::TERMINAL) && t2.is_type(detail::terminal_type::TERMINAL)) - { - connect_terminals(dynamic_cast<terminal_t &>(t1), dynamic_cast<terminal_t &>(t2)); - } - else if (t1.is_type(detail::terminal_type::INPUT) && t2.is_type(detail::terminal_type::INPUT)) - { - ret = connect_input_input(t1, t2); - } - else - ret = false; - return ret; -} + // delete empty nets -void setup_t::resolve_inputs() -{ - log().verbose("Resolving inputs ..."); + delete_empty_nets(); - /* Netlist can directly connect input to input. - * We therefore first park connecting inputs and retry - * after all other terminals were connected. - */ - unsigned tries = m_netlist_params->m_max_link_loops(); - while (m_links.size() > 0 && tries > 0) - { + bool err(false); + + log().verbose("looking for terminals not connected ..."); + for (auto &i : m_terminals) + { + detail::core_terminal_t *term = i.second; + const pstring name_da = de_alias(term->name()); + bool is_nc_pin( + dynamic_cast<devices::NETLIB_NAME(nc_pin) *>(&term->device()) + != nullptr); + bool is_nc_flagged(false); + + auto hnc = m_abstract.m_hints.find(name_da + sHINT_NC); + if (hnc != m_abstract.m_hints.end()) + { + hnc->second = true; // mark as used + is_nc_flagged = true; + } + + if (term->has_net() && is_nc_pin) + { + log().error(ME_NC_PIN_1_WITH_CONNECTIONS(name_da)); + err = true; + } + else if (is_nc_pin) + { + // ignore + } + else if (!term->has_net()) + { + log().error(ME_TERMINAL_1_WITHOUT_NET(name_da)); + err = true; + } + else if (term->net().core_terms_empty()) + { + if (term->is_logic_input()) + log().warning( + MW_LOGIC_INPUT_1_WITHOUT_CONNECTIONS(name_da)); + else if (term->is_logic_output()) + { + if (!is_nc_flagged) + log().info( + MI_LOGIC_OUTPUT_1_WITHOUT_CONNECTIONS(name_da)); + } + else if (term->is_analog_output()) + { + if (!is_nc_flagged) + log().info( + MI_ANALOG_OUTPUT_1_WITHOUT_CONNECTIONS(name_da)); + } + else + log().warning(MW_TERMINAL_1_WITHOUT_CONNECTIONS(name_da)); + } + } - for (auto li = m_links.begin(); li != m_links.end(); ) + log().verbose("checking tristate consistency ..."); + for (auto &i : m_terminals) { - const pstring t1s = li->first; - const pstring t2s = li->second; - detail::core_terminal_t *t1 = find_terminal(t1s); - detail::core_terminal_t *t2 = find_terminal(t2s); + detail::core_terminal_t *term = i.second; + if (term->is_tristate_output()) + { + const auto &tri(dynamic_cast<tristate_output_t &>(*term)); + // check if we are connected to a proxy + const auto iter_proxy(m_proxies.find(&tri)); - //printf("%s %s\n", t1s.c_str(), t2s.c_str()); - if (connect(*t1, *t2)) - li = m_links.erase(li); - else - li++; + if (iter_proxy == m_proxies.end() && !tri.is_force_logic()) + { + log().error(ME_TRISTATE_NO_PROXY_FOUND_2( + term->name(), term->device().name())); + err = true; + } + else if (iter_proxy != m_proxies.end() && tri.is_force_logic()) + { + log().error(ME_TRISTATE_PROXY_FOUND_2( + term->name(), term->device().name())); + err = true; + } + } + } + if (err) + { + log().fatal(MF_TERMINALS_WITHOUT_NET()); + throw nl_exception(MF_TERMINALS_WITHOUT_NET()); } - tries--; } - if (tries == 0) - { - for (auto & link : m_links) - log().warning(MF_CONNECTING_1_TO_2(setup().de_alias(link.first), setup().de_alias(link.second))); - log().fatal(MF_LINK_TRIES_EXCEEDED(m_netlist_params->m_max_link_loops())); - } + void setup_t::move_connections(detail::net_t &net, detail::net_t &dest_net) + { + auto temp(net.core_terms_copy()); - log().verbose("deleting empty nets ..."); + net.remove_all_terminals(); - // delete empty nets + for (detail::core_terminal_t *ct : temp) + { + dest_net.add_terminal(*ct); + } + } - delete_empty_nets(); + log_type & setup_t::log() noexcept { return m_nlstate.log(); } + const log_type &setup_t::log() const noexcept { return m_nlstate.log(); } - bool err(false); + // ------------------------------------------------------------------------- + // Models + // ------------------------------------------------------------------------- - log().verbose("looking for terminals not connected ..."); - for (auto & i : m_terminals) + // NOLINTNEXTLINE(misc-no-recursion) + void models_t::model_parse(const pstring &model_in, map_t &map) { - detail::core_terminal_t *term = i.second; - if (!term->has_net() && dynamic_cast< devices::NETLIB_NAME(dummy_input) *>(&term->device()) != nullptr) - log().info(MI_DUMMY_1_WITHOUT_CONNECTIONS(term->name())); - else if (!term->has_net()) + pstring model = model_in; + std::size_t pos = 0; + pstring key; + + while (true) { - log().error(ME_TERMINAL_1_WITHOUT_NET(setup().de_alias(term->name()))); - err = true; + pos = model.find('('); + if (pos != pstring::npos) + break; + + key = plib::ucase(model); + auto i = m_models.find(key); + if (i == m_models.end()) + { + throw nl_exception(MF_MODEL_NOT_FOUND(pstring("xx") + model)); + } + + model = i->second; } - else if (term->net().num_cons() == 0) + pstring base_model = plib::left(model, pos); + + if (base_model == "_") + map["COREMODEL"] = key; + else { - if (term->is_logic_input()) - log().warning(MW_LOGIC_INPUT_1_WITHOUT_CONNECTIONS(term->name())); - else if (term->is_logic_output()) - log().info(MI_LOGIC_OUTPUT_1_WITHOUT_CONNECTIONS(term->name())); - else if (term->is_analog_output()) - log().info(MI_ANALOG_OUTPUT_1_WITHOUT_CONNECTIONS(term->name())); + auto i = m_models.find(base_model); + if (i != m_models.end()) + model_parse(base_model, map); else - log().warning(MW_TERMINAL_1_WITHOUT_CONNECTIONS(term->name())); + throw nl_exception(MF_MODEL_NOT_FOUND(model_in)); } - } - if (err) - log().fatal(MF_TERMINALS_WITHOUT_NET()); - -} -void setup_t::register_dynamic_log_devices() -{ - pstring env = plib::util::environment("NL_LOGS", ""); + pstring remainder = plib::trim(model.substr(pos + 1)); + if (!plib::endsWith(remainder, ")")) + throw nl_exception(MF_MODEL_ERROR_1(model)); + // FIMXE: Not optimal + remainder = plib::left(remainder, remainder.length() - 1); - if (env != "") - { - log().debug("Creating dynamic logs ..."); - std::vector<pstring> loglist(plib::psplit(env, ":")); - for (const pstring &ll : loglist) + const auto pairs(plib::psplit(remainder, ' ', true)); + for (const pstring &pe : pairs) { - pstring name = "log_" + ll; - auto nc = factory().factory_by_name("LOG")->Create(m_nlstate, name); - register_link(name + ".I", ll); - log().debug(" dynamic link {1}: <{2}>\n",ll, name); - m_nlstate.add_dev(nc->name(), std::move(nc)); + auto pose = pe.find('='); + if (pose == pstring::npos) + throw nl_exception(MF_MODEL_ERROR_ON_PAIR_1(model)); + map[plib::ucase(plib::left(pe, pose))] = pe.substr(pose + 1); } } -} - -log_type &setup_t::log() -{ - return m_nlstate.log(); -} -const log_type &setup_t::log() const -{ - return m_nlstate.log(); -} - -// ---------------------------------------------------------------------------------------- -// Models -// ---------------------------------------------------------------------------------------- - -void models_t::register_model(const pstring &model_in) -{ - auto pos = model_in.find(' '); - if (pos == pstring::npos) - throw nl_exception(MF_UNABLE_TO_PARSE_MODEL_1(model_in)); - pstring model = plib::ucase(plib::trim(plib::left(model_in, pos))); - pstring def = plib::trim(model_in.substr(pos + 1)); - if (!m_models.insert({model, def}).second) - throw nl_exception(MF_MODEL_ALREADY_EXISTS_1(model_in)); -} - -void models_t::model_parse(const pstring &model_in, model_map_t &map) -{ - pstring model = model_in; - std::size_t pos = 0; - pstring key; - - while (true) + pstring models_t::model_t::model_string(const map_t &map) { - pos = model.find('('); - if (pos != pstring::npos) break; + // operator [] has no const implementation + pstring ret = map.at("COREMODEL") + "("; + for (const auto &i : map) + ret += (i.first + '=' + i.second + ' '); - key = plib::ucase(model); - auto i = m_models.find(key); - if (i == m_models.end()) - throw nl_exception(MF_MODEL_NOT_FOUND(model)); - model = i->second; + return ret + ")"; } - pstring xmodel = plib::left(model, pos); - if (xmodel == "_") - map["COREMODEL"] = key; - else + models_t::model_t models_t::get_model(const pstring &model) { - auto i = m_models.find(xmodel); - if (i != m_models.end()) - model_parse(xmodel, map); - else - throw nl_exception(MF_MODEL_NOT_FOUND(model_in)); - } + map_t &map = m_cache[model]; - pstring remainder = plib::trim(model.substr(pos + 1)); - if (!plib::endsWith(remainder, ")")) - throw nl_exception(MF_MODEL_ERROR_1(model)); - // FIMXE: Not optimal - remainder = plib::left(remainder, remainder.size() - 1); + if (map.empty()) + model_parse(model, map); - std::vector<pstring> pairs(plib::psplit(remainder," ", true)); - for (pstring &pe : pairs) - { - auto pose = pe.find('='); - if (pose == pstring::npos) - throw nl_exception(MF_MODEL_ERROR_ON_PAIR_1(model)); - map[plib::ucase(plib::left(pe, pose))] = pe.substr(pose + 1); + return {model, map}; } -} -pstring models_t::model_string(model_map_t &map) -{ - pstring ret = map["COREMODEL"] + "("; - for (auto & i : map) - ret += (i.first + '=' + i.second + ' '); + pstring models_t::model_t::value_str(const pstring &entity) const + { + if (entity != plib::ucase(entity)) + throw nl_exception(MF_MODEL_PARAMETERS_NOT_UPPERCASE_1_2( + entity, model_string(m_map))); + const auto it(m_map.find(entity)); + if (it == m_map.end()) + throw nl_exception( + MF_ENTITY_1_NOT_FOUND_IN_MODEL_2(entity, model_string(m_map))); - return ret + ")"; -} + return it->second; + } -pstring models_t::value_str(const pstring &model, const pstring &entity) -{ - model_map_t &map = m_cache[model]; + nl_fptype models_t::model_t::value(const pstring &entity) const + { + pstring tmp = value_str(entity); - if (map.size() == 0) - model_parse(model , map); + nl_fptype factor = nlconst::one(); + auto p = std::next( + tmp.begin(), + plib::narrow_cast<pstring::difference_type>(tmp.length() - 1)); + switch (*p) + { + case 'M': factor = nlconst::magic(1e6); break; // NOLINT + case 'k': + case 'K': factor = nlconst::magic(1e3); break; // NOLINT + case 'm': factor = nlconst::magic(1e-3); break; // NOLINT + case 'u': factor = nlconst::magic(1e-6); break; // NOLINT + case 'n': factor = nlconst::magic(1e-9); break; // NOLINT + case 'p': factor = nlconst::magic(1e-12); break; // NOLINT + case 'f': factor = nlconst::magic(1e-15); break; // NOLINT + case 'a': factor = nlconst::magic(1e-18); break; // NOLINT + default: + if (*p < '0' || *p > '9') + throw nl_exception( + MF_UNKNOWN_NUMBER_FACTOR_IN_2(m_model, entity)); + } + if (factor != nlconst::one()) + tmp = plib::left(tmp, tmp.length() - 1); + // FIXME: check for errors + bool err(false); + auto val = plib::pstonum_ne<nl_fptype>(tmp, err); + if (err) + throw nl_exception(MF_MODEL_NUMBER_CONVERSION_ERROR( + entity, tmp, "double", m_model)); + return val * factor; + } + + // ------------------------------------------------------------------------- + // logic_family_std_proxy_t + // + // FIXME: all this belongs elsewhere + // ------------------------------------------------------------------------- + PENUM(family_type, CUSTOM, TTL, MOS, CMOS, NMOS, PMOS) + + class logic_family_std_proxy_t : public logic_family_desc_t + { + public: + logic_family_std_proxy_t(family_type ft) + : m_family_type(ft) + { + } + + // FIXME: create proxies based on family type (far future) + device_arena::unique_ptr<devices::nld_base_d_to_a_proxy> + create_d_a_proxy(netlist_state_t &anetlist, const pstring &name, + const logic_output_t *proxied) const override + { + switch (m_family_type) + { + case family_type::CUSTOM: + case family_type::TTL: + case family_type::MOS: + case family_type::CMOS: + case family_type::NMOS: + case family_type::PMOS: + return anetlist.make_pool_object<devices::nld_d_to_a_proxy>( + device_data_t{anetlist, name}, proxied); + } + return anetlist.make_pool_object<devices::nld_d_to_a_proxy>( + device_data_t{anetlist, name}, proxied); + } - pstring ret; + device_arena::unique_ptr<devices::nld_base_a_to_d_proxy> + create_a_d_proxy(netlist_state_t &anetlist, const pstring &name, + const logic_input_t *proxied) const override + { + switch (m_family_type) + { + case family_type::CUSTOM: + case family_type::TTL: + case family_type::MOS: + case family_type::CMOS: + case family_type::NMOS: + case family_type::PMOS: + return anetlist.make_pool_object<devices::nld_a_to_d_proxy>( + device_data_t{anetlist, name}, proxied); + } + return anetlist.make_pool_object<devices::nld_a_to_d_proxy>( + device_data_t{anetlist, name}, proxied); + } - if (entity != plib::ucase(entity)) - throw nl_exception(MF_MODEL_PARAMETERS_NOT_UPPERCASE_1_2(entity, model_string(map))); - if (map.find(entity) == map.end()) - throw nl_exception(MF_ENTITY_1_NOT_FOUND_IN_MODEL_2(entity, model_string(map))); - else - ret = map[entity]; + private: + family_type m_family_type; + }; + + // clang-format off + /// \brief Class representing the logic families. + /// + /// This is the model representation of the logic families. This is a + /// netlist specific model. Examples give values for TTL family + /// + // + /// |NL? |name |parameter |units| TTL | + /// |:--:|:-----|:----------------------------------------------------------|:----|------:| + /// | Y |IVL |Input voltage low threshold relative to supply voltage | |1.0e-14| + /// | Y |IVH |Input voltage high threshold relative to supply voltage | | 0| + /// | Y |OVL |Output voltage minimum voltage relative to supply voltage | |1.0e-14| + /// | Y |OVL |Output voltage maximum voltage relative to supply voltage | |1.0e-14| + /// | Y |ORL |Output output resistance for logic 0 | | 0| + /// | Y |ORH |Output output resistance for logic 1 | | 0| + /// + // clang-format on + class family_model_t + { + public: + template <typename P> + family_model_t(P &model) + : m_TYPE(model, "TYPE") + , m_IVL(model, "IVL") + , m_IVH(model, "IVH") + , m_OVL(model, "OVL") + , m_OVH(model, "OVH") + , m_ORL(model, "ORL") + , m_ORH(model, "ORH") + { + } - return ret; -} + param_model_t::value_str_t m_TYPE; //!< Family type (TTL, CMOS, ...) + param_model_t::value_t m_IVL; //!< Input voltage low threshold relative + //!< to supply voltage + param_model_t::value_t m_IVH; //!< Input voltage high threshold relative + //!< to supply voltage + param_model_t::value_t m_OVL; //!< Output voltage minimum voltage + //!< relative to supply voltage + param_model_t::value_t m_OVH; //!< Output voltage maximum voltage + //!< relative to supply voltage + param_model_t::value_t m_ORL; //!< Output output resistance for logic 0 + param_model_t::value_t m_ORH; //!< Output output resistance for logic 1 + }; -nl_double models_t::value(const pstring &model, const pstring &entity) -{ - model_map_t &map = m_cache[model]; - - if (map.size() == 0) - model_parse(model , map); - - pstring tmp = value_str(model, entity); - - nl_double factor = plib::constants<nl_double>::one(); - auto p = std::next(tmp.begin(), static_cast<pstring::difference_type>(tmp.size() - 1)); - switch (*p) - { - case 'M': factor = 1e6; break; - case 'k': factor = 1e3; break; - case 'K': factor = 1e3; break; - case 'm': factor = 1e-3; break; - case 'u': factor = 1e-6; break; - case 'n': factor = 1e-9; break; - case 'p': factor = 1e-12; break; - case 'f': factor = 1e-15; break; - case 'a': factor = 1e-18; break; - default: - if (*p < '0' || *p > '9') - throw nl_exception(MF_UNKNOWN_NUMBER_FACTOR_IN_1(entity)); - } - if (factor != plib::constants<nl_double>::one()) - tmp = plib::left(tmp, tmp.size() - 1); - // FIXME: check for errors - //printf("%s %s %e %e\n", entity.c_str(), tmp.c_str(), plib::pstonum<nl_double>(tmp), factor); - bool err(false); - auto val = plib::pstonum_ne<nl_double, true>(tmp, err); - if (err) - throw nl_exception(MF_MODEL_NUMBER_CONVERSION_ERROR(entity, tmp, "double", model)); - return val * factor; -} - -class logic_family_std_proxy_t : public logic_family_desc_t -{ -public: - logic_family_std_proxy_t() = default; - pool_owned_ptr<devices::nld_base_d_to_a_proxy> create_d_a_proxy(netlist_state_t &anetlist, - const pstring &name, logic_output_t *proxied) const override; - pool_owned_ptr<devices::nld_base_a_to_d_proxy> create_a_d_proxy(netlist_state_t &anetlist, const pstring &name, logic_input_t *proxied) const override; -}; - -pool_owned_ptr<devices::nld_base_d_to_a_proxy> logic_family_std_proxy_t::create_d_a_proxy(netlist_state_t &anetlist, - const pstring &name, logic_output_t *proxied) const -{ - return pool().make_poolptr<devices::nld_d_to_a_proxy>(anetlist, name, proxied); -} -pool_owned_ptr<devices::nld_base_a_to_d_proxy> logic_family_std_proxy_t::create_a_d_proxy(netlist_state_t &anetlist, const pstring &name, logic_input_t *proxied) const -{ - return pool().make_poolptr<devices::nld_a_to_d_proxy>(anetlist, name, proxied); -} + const logic_family_desc_t *setup_t::family_from_model(const pstring &model) + { + family_type ft(family_type::CUSTOM); + auto mod(m_models.get_model(model)); + family_model_t modv(mod); -const logic_family_desc_t *setup_t::family_from_model(const pstring &model) -{ + if (!ft.set_from_string(modv.m_TYPE())) + throw nl_exception(MF_UNKNOWN_FAMILY_TYPE_1(modv.m_TYPE(), model)); - if (m_models.value_str(model, "TYPE") == "TTL") - return family_TTL(); - if (m_models.value_str(model, "TYPE") == "CD4XXX") - return family_CD4XXX(); + auto it = m_nlstate.family_cache().find(model); + if (it != m_nlstate.family_cache().end()) + return it->second.get(); - auto it = m_nlstate.m_family_cache.find(model); - if (it != m_nlstate.m_family_cache.end()) - return it->second.get(); + auto ret = plib::make_unique<logic_family_std_proxy_t, host_arena>(ft); - auto ret = plib::make_unique<logic_family_std_proxy_t>(); + ret->m_low_threshold_PCNT = modv.m_IVL(); + ret->m_high_threshold_PCNT = modv.m_IVH(); + ret->m_low_VO = modv.m_OVL(); + ret->m_high_VO = modv.m_OVH(); + ret->m_R_low = modv.m_ORL(); + ret->m_R_high = modv.m_ORH(); - ret->m_fixed_V = m_models.value(model, "FV"); - ret->m_low_thresh_PCNT = m_models.value(model, "IVL"); - ret->m_high_thresh_PCNT = m_models.value(model, "IVH"); - ret->m_low_VO = m_models.value(model, "OVL"); - ret->m_high_VO = m_models. value(model, "OVH"); - ret->m_R_low = m_models.value(model, "ORL"); - ret->m_R_high = m_models.value(model, "ORH"); + switch (ft) + { + case family_type::CUSTOM: + case family_type::TTL: + case family_type::NMOS: + ret->m_vcc = "VCC"; + ret->m_gnd = "GND"; + break; + case family_type::MOS: + case family_type::CMOS: + case family_type::PMOS: + ret->m_vcc = "VDD"; + ret->m_gnd = "VSS"; + break; + } - auto retp = ret.get(); + auto *retp = ret.get(); - m_nlstate.m_family_cache.emplace(model, std::move(ret)); + m_nlstate.family_cache().emplace(model, std::move(ret)); - return retp; -} + return retp; + } -// ---------------------------------------------------------------------------------------- -// Sources -// ---------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // Device handling + // ------------------------------------------------------------------------- -plib::unique_ptr<std::istream> setup_t::get_data_stream(const pstring &name) -{ - for (auto &source : m_sources) + void setup_t::delete_empty_nets() { - if (source->type() == source_t::DATA) - { - auto strm = source->stream(name); - if (strm) - return strm; - } + m_nlstate.nets().erase( + std::remove_if( + m_nlstate.nets().begin(), m_nlstate.nets().end(), + [](device_arena::owned_ptr<detail::net_t> &net) + { + if (net->core_terms_empty()) + { + // FIXME: need to remove from state->m_core_terms as + // well. + net->state().log().verbose("Deleting net {1} ...", + net->name()); + net->state().run_state_manager().remove_save_items( + net.get()); + return true; + } + return false; + }), + m_nlstate.nets().end()); } - log().warning(MW_DATA_1_NOT_FOUND(name)); - return plib::unique_ptr<std::istream>(nullptr); -} + // ------------------------------------------------------------------------- + // Run preparation + // ------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// Device handling -// ---------------------------------------------------------------------------------------- + void setup_t::prepare_to_run() + { + pstring env_log_setting = plib::util::environment("NL_LOGS", ""); -void setup_t::delete_empty_nets() -{ - m_nlstate.nets().erase( - std::remove_if(m_nlstate.nets().begin(), m_nlstate.nets().end(), - [](pool_owned_ptr<detail::net_t> &x) - { - if (x->num_cons() == 0) - { - x->state().log().verbose("Deleting net {1} ...", x->name()); - x->state().run_state_manager().remove_save_items(x.get()); - return true; - } - else - return false; - }), m_nlstate.nets().end()); -} + if (!env_log_setting.empty()) + { + const auto list_of_logs(plib::psplit(env_log_setting, ':')); + m_parser.register_dynamic_log_devices(list_of_logs); + } -// ---------------------------------------------------------------------------------------- -// Run preparation -// ---------------------------------------------------------------------------------------- + // create default parameters first! -void setup_t::prepare_to_run() -{ - register_dynamic_log_devices(); + for (auto &e : m_abstract.m_default_params) + { + auto param(plib::make_unique<param_str_t, host_arena>( + nlstate(), e.first, e.second)); + register_param_t(*param); + m_defparam_lifetime.push_back(std::move(param)); + } - /* make sure the solver and parameters are started first! */ + // make sure the solver and parameters are started first! - for (auto & e : m_device_factory) - { - if ( factory().is_class<devices::NETLIB_NAME(solver)>(e.second) - || factory().is_class<devices::NETLIB_NAME(netlistparams)>(e.second)) + for (auto &e : m_abstract.m_device_factory) { - m_nlstate.add_dev(e.first, pool_owned_ptr<device_t>(e.second->Create(m_nlstate, e.first))); + if (m_parser.factory().is_class<devices::NETLIB_NAME(solver)>( + e.second) + || m_parser.factory() + .is_class<devices::NETLIB_NAME(netlistparams)>(e.second)) + { + m_nlstate.register_device( + e.first, e.second->make_device(nlstate().pool(), m_nlstate, + e.first)); + } } - } - log().debug("Searching for solver and parameters ...\n"); + log().debug("Searching for solver and parameters ...\n"); - auto solver = m_nlstate.get_single_device<devices::NETLIB_NAME(solver)>("solver"); - m_netlist_params = m_nlstate.get_single_device<devices::NETLIB_NAME(netlistparams)>("parameter"); + auto *solver = m_nlstate + .get_single_device<devices::NETLIB_NAME(solver)>( + "solver"); + m_netlist_params = m_nlstate.get_single_device< + devices::NETLIB_NAME(netlistparams)>("parameter"); - /* set default model parameters */ + // set default model parameters - m_models.register_model(plib::pfmt("NMOS_DEFAULT _(CAPMOD={1})")(m_netlist_params->m_mos_capmodel())); - m_models.register_model(plib::pfmt("PMOS_DEFAULT _(CAPMOD={1})")(m_netlist_params->m_mos_capmodel())); + // FIXME: this is not optimal + m_parser.register_model(plib::pfmt("NMOS_DEFAULT _(CAPMOD={1})")( + m_netlist_params->m_mos_cap_model())); + m_parser.register_model(plib::pfmt("PMOS_DEFAULT _(CAPMOD={1})")( + m_netlist_params->m_mos_cap_model())); - /* create devices */ + // create devices - log().debug("Creating devices ...\n"); - for (auto & e : m_device_factory) - { - if ( !factory().is_class<devices::NETLIB_NAME(solver)>(e.second) - && !factory().is_class<devices::NETLIB_NAME(netlistparams)>(e.second)) + log().debug("Creating devices ...\n"); + for (auto &e : m_abstract.m_device_factory) { - auto dev = pool_owned_ptr<device_t>(e.second->Create(m_nlstate, e.first)); - m_nlstate.add_dev(dev->name(), std::move(dev)); + if (!m_parser.factory().is_class<devices::NETLIB_NAME(solver)>( + e.second) + && !m_parser.factory() + .is_class<devices::NETLIB_NAME(netlistparams)>( + e.second)) + { + auto dev = e.second->make_device(m_nlstate.pool(), m_nlstate, + e.first); + m_nlstate.register_device(dev->name(), std::move(dev)); + } } - } - log().debug("Looking for unknown parameters ...\n"); - for (auto &p : m_param_values) - { - auto f = m_params.find(p.first); - if (f == m_params.end()) + const bool use_deactivate = m_netlist_params->m_use_deactivate(); + + for (auto &d : m_nlstate.devices()) { - if (plib::endsWith(p.first, sHINT_NO_DEACTIVATE)) + auto p = m_abstract.m_hints.find(d.second->name() + + sHINT_NO_DEACTIVATE); + if (p != m_abstract.m_hints.end()) { - // FIXME: get device name, check for device - auto *dev = m_nlstate.find_device(plib::replace_all(p.first, sHINT_NO_DEACTIVATE, "")); - if (dev == nullptr) - log().warning(MW_DEVICE_NOT_FOUND_FOR_HINT(p.first)); + p->second = true; // mark as used + d.second->set_hint_deactivate(false); } else - log().warning(MW_UNKNOWN_PARAMETER(p.first)); + d.second->set_hint_deactivate(use_deactivate); } - } - bool use_deactivate = m_netlist_params->m_use_deactivate() ? true : false; + // resolve inputs + resolve_inputs(); - for (auto &d : m_nlstate.devices()) - { - if (use_deactivate) + log().verbose("looking for two terms connected to rail nets ..."); + for (auto &t : + m_nlstate.get_device_list<analog::NETLIB_NAME(two_terminal)>()) { - auto p = m_param_values.find(d.second->name() + sHINT_NO_DEACTIVATE); - if (p != m_param_values.end()) + if (t->N().net().is_rail_net() && t->P().net().is_rail_net()) { - //FIXME: check for errors ... - bool err(false); - auto v = plib::pstonum_ne<double, true>(p->second, err); - if (err || std::abs(v - std::floor(v)) > 1e-6 ) - log().fatal(MF_HND_VAL_NOT_SUPPORTED(p->second)); - d.second->set_hint_deactivate(v == 0.0); + log().info(MI_REMOVE_DEVICE_1_CONNECTED_ONLY_TO_RAILS_2_3( + t->name(), t->N().net().name(), t->P().net().name())); + // The following would remove internal devices in e.g. MOSFETs + // as well. +#if 0 + remove_terminal(t->setup_N().net(), t->setup_N()); + remove_terminal(t->setup_P().net(), t->setup_P()); + m_nlstate.remove_device(t); +#endif } } - else - d.second->set_hint_deactivate(false); - } - /* resolve inputs */ - resolve_inputs(); + int error_count = 0; - log().verbose("looking for two terms connected to rail nets ..."); - for (auto & t : m_nlstate.get_device_list<analog::NETLIB_NAME(twoterm)>()) - { - if (t->m_N.net().isRailNet() && t->m_P.net().isRailNet()) + log().verbose("looking for unused hints ..."); + for (auto &h : m_abstract.m_hints) { - // We are not interested in power terminals - This is intended behaviour - if (!plib::endsWith(t->name(), pstring(".") + sPowerDevRes)) - log().info(MI_REMOVE_DEVICE_1_CONNECTED_ONLY_TO_RAILS_2_3( - t->name(), t->m_N.net().name(), t->m_P.net().name())); - t->m_N.net().remove_terminal(t->m_N); - t->m_P.net().remove_terminal(t->m_P); - m_nlstate.remove_dev(t); + if (!h.second) + { + log().error(MF_UNUSED_HINT_1(h.first)); + error_count++; + } } - else + + log().verbose("initialize solver ...\n"); + + if (solver == nullptr) { - if (plib::endsWith(t->name(), pstring(".") + sPowerDevRes)) - log().info(MI_POWER_TERMINALS_1_CONNECTED_ANALOG_2_3( - t->name(), t->m_N.net().name(), t->m_P.net().name())); + for (auto &p : m_nlstate.nets()) + if (p->is_analog()) + { + log().error(MF_NO_SOLVER()); + error_count++; + } } - } - - log().verbose("initialize solver ...\n"); + else + solver->post_start(); - if (solver == nullptr) - { - for (auto &p : m_nlstate.nets()) - if (p->is_analog()) - log().fatal(MF_NO_SOLVER()); - } - else - solver->post_start(); + log().debug("Looking for unknown parameters ...\n"); + for (auto &p : m_abstract.m_param_values) + { + auto f = m_params.find(p.first); + if (f == m_params.end()) + { + log().error(ME_UNKNOWN_PARAMETER(p.first)); + error_count++; + } + } - for (auto &n : m_nlstate.nets()) - for (auto & term : n->core_terms()) + for (auto &n : m_nlstate.nets()) { - //core_device_t *dev = reinterpret_cast<core_device_t *>(term->m_delegate.object()); - core_device_t *dev = &term->device(); - dev->set_default_delegate(*term); + for (detail::core_terminal_t *term : n->core_terms_copy()) + if (term->delegate().isnull()) + { + log().error(MF_DELEGATE_NOT_SET_1(term->name())); + error_count++; + } + n->rebuild_list(); } -} + if (error_count > 0) + { + log().fatal(MF_ERRORS_FOUND(error_count)); + throw nl_exception(MF_ERRORS_FOUND(error_count)); + } + } -// ---------------------------------------------------------------------------------------- -// base sources -// ---------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // base sources + // ------------------------------------------------------------------------- -bool source_t::parse(nlparse_t &setup, const pstring &name) -{ - if (m_type != SOURCE) - return false; - else + bool source_netlist_t::parse(nlparse_t &setup, const pstring &name) { auto strm(stream(name)); - if (strm) - return setup.parse_stream(std::move(strm), name); - else - return false; + return (!strm.empty()) ? setup.parse_stream(std::move(strm), name) + : false; } -} -plib::unique_ptr<std::istream> source_string_t::stream(const pstring &name) -{ - plib::unused_var(name); - auto ret(plib::make_unique<std::istringstream>(m_str)); - ret->imbue(std::locale::classic()); - return std::move(ret); -} + plib::istream_uptr + source_string_t::stream([[maybe_unused]] const pstring &name) + { + plib::istream_uptr ret( + std::make_unique<std::istringstream>(putf8string(m_str)), name); + ret->imbue(std::locale::classic()); + return ret; + } -plib::unique_ptr<std::istream> source_mem_t::stream(const pstring &name) -{ - plib::unused_var(name); - auto ret(plib::make_unique<std::istringstream>(m_str, std::ios_base::binary)); - ret->imbue(std::locale::classic()); - return std::move(ret); -} + plib::istream_uptr + source_mem_t::stream([[maybe_unused]] const pstring &name) + { + plib::istream_uptr ret( + std::make_unique<std::istringstream>(m_str, std::ios_base::binary), + name); + ret->imbue(std::locale::classic()); + return ret; + } -plib::unique_ptr<std::istream> source_file_t::stream(const pstring &name) -{ - plib::unused_var(name); - auto ret(plib::make_unique<std::ifstream>(plib::filesystem::u8path(m_filename))); - return std::move(ret); -} + plib::istream_uptr + source_file_t::stream([[maybe_unused]] const pstring &name) + { + auto f = std::make_unique<plib::ifstream>( + plib::filesystem::u8path(m_filename)); + if (f->is_open()) + { + return {std::move(f), m_filename}; + } -bool source_proc_t::parse(nlparse_t &setup, const pstring &name) -{ - if (name == m_setup_func_name) + return plib::istream_uptr(); + } + + plib::istream_uptr + source_pattern_t::stream([[maybe_unused]] const pstring &name) { - m_setup_func(setup); - return true; + pstring filename = plib::pfmt(m_pattern)( + m_force_lowercase ? plib::lcase(name) : name); + auto f = std::make_unique<plib::ifstream>( + plib::filesystem::u8path(filename)); + if (f->is_open()) + { + return {std::move(f), filename}; + } + + return plib::istream_uptr(); } - else + + bool source_proc_t::parse(nlparse_t &setup, const pstring &name) + { + if (name == m_setup_func_name) + { + m_setup_func(setup); + return true; + } + return false; -} + } -plib::unique_ptr<std::istream> source_proc_t::stream(const pstring &name) -{ - plib::unused_var(name); - plib::unique_ptr<std::istream> p(nullptr); - return p; -} + plib::istream_uptr + source_proc_t::stream([[maybe_unused]] const pstring &name) + { + return plib::istream_uptr(); + } } // namespace netlist - |