diff options
Diffstat (limited to 'src/lib/netlist/nl_setup.c')
-rw-r--r-- | src/lib/netlist/nl_setup.c | 1054 |
1 files changed, 1054 insertions, 0 deletions
diff --git a/src/lib/netlist/nl_setup.c b/src/lib/netlist/nl_setup.c new file mode 100644 index 00000000000..94cb85ced03 --- /dev/null +++ b/src/lib/netlist/nl_setup.c @@ -0,0 +1,1054 @@ +// license:GPL-2.0+ +// copyright-holders:Couriersud +/* + * nlsetup.c + * + */ + +#include <cstdio> + +#include "solver/nld_solver.h" + +#include "plib/palloc.h" +#include "nl_base.h" +#include "nl_setup.h" +#include "nl_parser.h" +#include "nl_util.h" +#include "nl_factory.h" +#include "devices/net_lib.h" +#include "devices/nld_system.h" +#include "analog/nld_twoterm.h" + +static NETLIST_START(base) + TTL_INPUT(ttlhigh, 1) + TTL_INPUT(ttllow, 0) + NET_REGISTER_DEV(GND, GND) + NET_REGISTER_DEV(PARAMETER, NETLIST) + + LOCAL_SOURCE(diode_models) + LOCAL_SOURCE(bjt_models) + LOCAL_SOURCE(family_models) + LOCAL_SOURCE(TTL74XX_lib) + LOCAL_SOURCE(CD4XXX_lib) + LOCAL_SOURCE(OPAMP_lib) + LOCAL_SOURCE(otheric_lib) + + INCLUDE(diode_models); + INCLUDE(bjt_models); + INCLUDE(family_models); + INCLUDE(TTL74XX_lib); + INCLUDE(CD4XXX_lib); + INCLUDE(OPAMP_lib); + INCLUDE(otheric_lib); + +NETLIST_END() + + +// ---------------------------------------------------------------------------------------- +// setup_t +// ---------------------------------------------------------------------------------------- + +namespace netlist +{ +setup_t::setup_t(netlist_t *netlist) + : m_netlist(netlist) + , m_proxy_cnt(0) +{ + netlist->set_setup(this); + m_factory = palloc(factory_list_t(*this)); +} + +void setup_t::init() +{ + initialize_factory(factory()); + NETLIST_NAME(base)(*this); +} + + +setup_t::~setup_t() +{ + m_links.clear(); + m_alias.clear(); + m_params.clear(); + m_terminals.clear(); + m_params_temp.clear(); + + netlist().set_setup(NULL); + pfree(m_factory); + m_sources.clear_and_free(); + + pstring::resetmem(); +} + +ATTR_COLD pstring setup_t::build_fqn(const pstring &obj_name) const +{ + if (m_stack.empty()) + return netlist().name() + "." + obj_name; + else + return m_stack.peek() + "." + obj_name; +} + +void setup_t::namespace_push(const pstring &aname) +{ + if (m_stack.empty()) + m_stack.push(netlist().name() + "." + aname); + else + m_stack.push(m_stack.peek() + "." + aname); +} + +void setup_t::namespace_pop() +{ + m_stack.pop(); +} + + +device_t *setup_t::register_dev(device_t *dev, const pstring &name) +{ + pstring fqn = build_fqn(name); + + dev->init(netlist(), fqn); + + if (!(netlist().m_devices.add(dev, false)==true)) + log().fatal("Error adding {1} to device list\n", name); + return dev; +} + +void setup_t::register_lib_entry(const pstring &name) +{ + if (m_lib.contains(name)) + log().warning("Lib entry collection already contains {1}. IGNORED", name); + else + m_lib.add(name); +} + +device_t *setup_t::register_dev(const pstring &classname, const pstring &name) +{ + if (m_lib.contains(classname)) + { + namespace_push(name); + include(classname); + namespace_pop(); + return NULL; + } + else + { + device_t *dev = factory().new_device_by_name(classname); + //device_t *dev = factory().new_device_by_classname(classname); + if (dev == NULL) + log().fatal("Class {1} not found!\n", classname); + return register_dev(dev, name); + } +} + +void setup_t::register_model(const pstring &model_in) +{ + int pos = model_in.find(" "); + if (pos < 0) + log().fatal("Unable to parse model: {1}", model_in); + pstring model = model_in.left(pos).trim().ucase(); + pstring def = model_in.substr(pos + 1).trim(); + if (!m_models.add(model, def)) + log().fatal("Model already exists: {1}", model_in); +} + +void setup_t::register_alias_nofqn(const pstring &alias, const pstring &out) +{ + if (!m_alias.add(alias, out)) + log().fatal("Error adding alias {1} to alias list\n", alias); +} + +void setup_t::register_alias(const pstring &alias, const pstring &out) +{ + pstring alias_fqn = build_fqn(alias); + pstring out_fqn = build_fqn(out); + register_alias_nofqn(alias_fqn, out_fqn); +} + +void setup_t::register_dippins_arr(const pstring &terms) +{ + pstring_list_t list(terms,", "); + if (list.size() == 0 || (list.size() % 2) == 1) + log().fatal("You must pass an equal number of pins to DIPPINS"); + unsigned n = list.size(); + for (unsigned i = 0; i < n / 2; i++) + { + register_alias(pfmt("{1}")(i+1), list[i * 2]); + register_alias(pfmt("{1}")(n-i), list[i * 2 + 1]); + } +} + +pstring setup_t::objtype_as_astr(object_t &in) const +{ + switch (in.type()) + { + case terminal_t::TERMINAL: + return "TERMINAL"; + case terminal_t::INPUT: + return "INPUT"; + case terminal_t::OUTPUT: + return "OUTPUT"; + case terminal_t::NET: + return "NET"; + case terminal_t::PARAM: + return "PARAM"; + case terminal_t::DEVICE: + return "DEVICE"; + case terminal_t::NETLIST: + return "NETLIST"; + case terminal_t::QUEUE: + return "QUEUE"; + } + // FIXME: noreturn + log().fatal("Unknown object type {1}\n", (unsigned) in.type()); + return "Error"; +} + + +void setup_t::register_object(device_t &dev, const pstring &name, object_t &obj) +{ + switch (obj.type()) + { + case terminal_t::TERMINAL: + case terminal_t::INPUT: + case terminal_t::OUTPUT: + { + core_terminal_t &term = dynamic_cast<core_terminal_t &>(obj); + if (obj.isType(terminal_t::OUTPUT)) + { + if (obj.isFamily(terminal_t::LOGIC)) + dynamic_cast<logic_output_t &>(term).init_object(dev, dev.name() + "." + name); + else if (obj.isFamily(terminal_t::ANALOG)) + dynamic_cast<analog_output_t &>(term).init_object(dev, dev.name() + "." + name); + else + log().fatal("Error adding {1} {2} to terminal list, neither LOGIC nor ANALOG\n", objtype_as_astr(term), term.name()); + } + else + term.init_object(dev, dev.name() + "." + name); + + if (!m_terminals.add(term.name(), &term)) + log().fatal("Error adding {1} {2} to terminal list\n", objtype_as_astr(term), term.name()); + log().debug("{1} {2}\n", objtype_as_astr(term), name); + } + break; + case terminal_t::NET: + break; + case terminal_t::PARAM: + { + param_t ¶m = dynamic_cast<param_t &>(obj); + //printf("name: {1}\n", name); + if (m_params_temp.contains(name)) + { + const pstring val = m_params_temp[name]; + switch (param.param_type()) + { + case param_t::DOUBLE: + { + log().debug("Found parameter ... {1} : {1}\n", name, val); + double vald = 0; + if (sscanf(val.cstr(), "%lf", &vald) != 1) + log().fatal("Invalid number conversion {1} : {2}\n", name, val); + dynamic_cast<param_double_t &>(param).initial(vald); + } + break; + case param_t::INTEGER: + case param_t::LOGIC: + { + log().debug("Found parameter ... {1} : {2}\n", name, val); + double vald = 0; + if (sscanf(val.cstr(), "%lf", &vald) != 1) + log().fatal("Invalid number conversion {1} : {2}\n", name, val); + dynamic_cast<param_int_t &>(param).initial((int) vald); + } + break; + case param_t::STRING: + { + dynamic_cast<param_str_t &>(param).initial(val); + } + break; + case param_t::MODEL: + //dynamic_cast<param_model_t &>(param).initial(val); + dynamic_cast<param_model_t &>(param).initial(val); + break; + default: + log().fatal("Parameter is not supported {1} : {2}\n", name, val); + } + } + if (!m_params.add(param.name(), ¶m)) + log().fatal("Error adding parameter {1} to parameter list\n", name); + } + break; + case terminal_t::DEVICE: + log().fatal("Device registration not yet supported - {1}\n", name); + break; + case terminal_t::NETLIST: + log().fatal("Netlist registration not yet supported - {1}\n", name); + break; + case terminal_t::QUEUE: + log().fatal("QUEUE registration not yet supported - {1}\n", name); + break; + } +} + +void setup_t::register_link_arr(const pstring &terms) +{ + pstring_list_t list(terms,", "); + if (list.size() < 2) + log().fatal("You must pass at least 2 terminals to NET_C"); + for (std::size_t i = 1; i < list.size(); i++) + { + register_link(list[0], list[i]); + } +} + + +void setup_t::register_link_fqn(const pstring &sin, const pstring &sout) +{ + link_t temp = link_t(sin, sout); + log().debug("link {1} <== {2}\n", sin, sout); + m_links.add(temp); +} + +void setup_t::register_link(const pstring &sin, const pstring &sout) +{ + register_link_fqn(build_fqn(sin), build_fqn(sout)); +} + +void setup_t::remove_connections(const pstring pin) +{ + pstring pinfn = build_fqn(pin); + bool found = false; + for (std::size_t i = 0; i < m_links.size(); i++) + { + if ((m_links[i].e1 == pinfn) || (m_links[i].e2 == pinfn)) + { + log().verbose("removing connection: {1} <==> {2}\n", m_links[i].e1, m_links[i].e2); + m_links.remove_at(i); + found = true; + } + } + if (!found) + log().fatal("remove_connections: found no occurrence of {1}\n", pin); +} + + +void setup_t::register_frontier(const pstring attach, const double r_IN, const double r_OUT) +{ + static int frontier_cnt = 0; + pstring frontier_name = pfmt("frontier_{1}")(frontier_cnt); + frontier_cnt++; + device_t *front = 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); + bool found = false; + for (std::size_t i = 0; i < m_links.size(); i++) + { + if (m_links[i].e1 == attfn) + { + m_links[i].e1 = front->name() + ".I"; + found = true; + } + else if (m_links[i].e2 == attfn) + { + m_links[i].e2 = front->name() + ".I"; + found = true; + } + } + if (!found) + log().fatal("Frontier setup: found no occurrence of {1}\n", attach); + register_link(attach, frontier_name + ".Q"); +} + + +void setup_t::register_param(const pstring ¶m, const double value) +{ + // FIXME: there should be a better way + register_param(param, pfmt("{1}").e(value,".9")); +} + +void setup_t::register_param(const pstring ¶m, const pstring &value) +{ + pstring fqn = build_fqn(param); + + int idx = m_params_temp.index_of(fqn); + if (idx < 0) + { + if (!m_params_temp.add(fqn, value)) + log().fatal("Unexpected error adding parameter {1} to parameter list\n", param); + } + else + { + log().warning("Overwriting {1} old <{2}> new <{3}>\n", fqn, m_params_temp.value_at(idx), value); + m_params_temp[fqn] = value; + } +} + +const pstring setup_t::resolve_alias(const pstring &name) const +{ + pstring temp = name; + pstring ret; + + /* FIXME: Detect endless loop */ + do { + ret = temp; + int p = m_alias.index_of(ret); + temp = (p>=0 ? m_alias.value_at(p) : ""); + } while (temp != ""); + + log().debug("{1}==>{2}\n", name, ret); + return ret; +} + +core_terminal_t *setup_t::find_terminal(const pstring &terminal_in, bool required) +{ + const pstring &tname = resolve_alias(terminal_in); + int ret; + + ret = m_terminals.index_of(tname); + /* look for default */ + if (ret < 0) + { + /* look for ".Q" std output */ + ret = m_terminals.index_of(tname + ".Q"); + } + + core_terminal_t *term = (ret < 0 ? NULL : m_terminals.value_at(ret)); + + if (term == NULL && required) + log().fatal("terminal {1}({2}) not found!\n", terminal_in, tname); + if (term != NULL) + log().debug("Found input {1}\n", tname); + return term; +} + +core_terminal_t *setup_t::find_terminal(const pstring &terminal_in, object_t::type_t atype, bool required) +{ + const pstring &tname = resolve_alias(terminal_in); + int ret; + + ret = m_terminals.index_of(tname); + /* look for default */ + if (ret < 0 && atype == object_t::OUTPUT) + { + /* look for ".Q" std output */ + ret = m_terminals.index_of(tname + ".Q"); + } + if (ret < 0 && required) + log().fatal("terminal {1}({2}) not found!\n", terminal_in, tname); + + core_terminal_t *term = (ret < 0 ? NULL : m_terminals.value_at(ret)); + + if (term != NULL && term->type() != atype) + { + if (required) + log().fatal("object {1}({2}) found but wrong type\n", terminal_in, tname); + else + term = NULL; + } + if (term != NULL) + log().debug("Found input {1}\n", tname); + + return term; +} + +param_t *setup_t::find_param(const pstring ¶m_in, bool required) +{ + const pstring param_in_fqn = build_fqn(param_in); + + const pstring &outname = resolve_alias(param_in_fqn); + int ret; + + ret = m_params.index_of(outname); + if (ret < 0 && required) + log().fatal("parameter {1}({2}) not found!\n", param_in_fqn, outname); + if (ret != -1) + log().debug("Found parameter {1}\n", outname); + return (ret == -1 ? NULL : m_params.value_at(ret)); +} + +// FIXME avoid dynamic cast here +devices::nld_base_proxy *setup_t::get_d_a_proxy(core_terminal_t &out) +{ + nl_assert(out.isFamily(terminal_t::LOGIC)); + + //printf("proxy for {1}\n", out.name());; + logic_output_t &out_cast = dynamic_cast<logic_output_t &>(out); + devices::nld_base_proxy *proxy = out_cast.get_proxy(); + + if (proxy == NULL) + { + // create a new one ... + devices::nld_base_d_to_a_proxy *new_proxy = out_cast.logic_family()->create_d_a_proxy(&out_cast); + pstring x = pfmt("proxy_da_{1}_{2}")(out.name())(m_proxy_cnt); + m_proxy_cnt++; + + register_dev(new_proxy, x); + new_proxy->start_dev(); + +#if 1 + /* connect all existing terminals to new net */ + + for (std::size_t i = 0; i < out.net().m_core_terms.size(); i++) + { + core_terminal_t *p = out.net().m_core_terms[i]; + p->clear_net(); // de-link from all nets ... + if (!connect(new_proxy->proxy_term(), *p)) + log().fatal("Error connecting {1} to {2}\n", new_proxy->proxy_term().name(), (*p).name()); + } + out.net().m_core_terms.clear(); // clear the list +#endif + out.net().register_con(new_proxy->in()); + out_cast.set_proxy(new_proxy); + proxy = new_proxy; + } + return proxy; +} + +void setup_t::connect_input_output(core_terminal_t &in, core_terminal_t &out) +{ + if (out.isFamily(terminal_t::ANALOG) && in.isFamily(terminal_t::LOGIC)) + { + logic_input_t &incast = dynamic_cast<logic_input_t &>(in); + devices::nld_a_to_d_proxy *proxy = palloc(devices::nld_a_to_d_proxy(&incast)); + incast.set_proxy(proxy); + pstring x = pfmt("proxy_ad_{1}_{2}")(in.name())( m_proxy_cnt); + m_proxy_cnt++; + + register_dev(proxy, x); + proxy->start_dev(); + + proxy->m_Q.net().register_con(in); + out.net().register_con(proxy->m_I); + + } + else if (out.isFamily(terminal_t::LOGIC) && in.isFamily(terminal_t::ANALOG)) + { + devices::nld_base_proxy *proxy = get_d_a_proxy(out); + + connect_terminals(proxy->proxy_term(), in); + //proxy->out().net().register_con(in); + } + else + { + if (in.has_net()) + out.net().merge_net(&in.net()); + else + out.net().register_con(in); + } +} + + +void setup_t::connect_terminal_input(terminal_t &term, core_terminal_t &inp) +{ + if (inp.isFamily(terminal_t::ANALOG)) + { + connect_terminals(inp, term); + } + else if (inp.isFamily(terminal_t::LOGIC)) + { + logic_input_t &incast = dynamic_cast<logic_input_t &>(inp); + log().debug("connect_terminal_input: connecting proxy\n"); + devices::nld_a_to_d_proxy *proxy = palloc(devices::nld_a_to_d_proxy(&incast)); + incast.set_proxy(proxy); + pstring x = pfmt("proxy_ad_{1}_{2}")(inp.name())(m_proxy_cnt); + m_proxy_cnt++; + + register_dev(proxy, x); + proxy->start_dev(); + + connect_terminals(term, proxy->m_I); + + if (inp.has_net()) + //fatalerror("logic inputs can only belong to one net!\n"); + proxy->m_Q.net().merge_net(&inp.net()); + else + proxy->m_Q.net().register_con(inp); + } + else + { + log().fatal("Netlist: Severe Error"); + } +} + +void setup_t::connect_terminal_output(terminal_t &in, core_terminal_t &out) +{ + if (out.isFamily(terminal_t::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()) + out.net().merge_net(&in.net()); + else + out.net().register_con(in); + } + else if (out.isFamily(terminal_t::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 + { + log().fatal("Netlist: Severe Error"); + } +} + +void setup_t::connect_terminals(core_terminal_t &t1, core_terminal_t &t2) +{ + //nl_assert(in.isType(netlist_terminal_t::TERMINAL)); + //nl_assert(out.isType(netlist_terminal_t::TERMINAL)); + + if (t1.has_net() && t2.has_net()) + { + log().debug("T2 and T1 have net\n"); + t1.net().merge_net(&t2.net()); + } + else if (t2.has_net()) + { + log().debug("T2 has net\n"); + t2.net().register_con(t1); + } + else if (t1.has_net()) + { + log().debug("T1 has net\n"); + t1.net().register_con(t2); + } + else + { + log().debug("adding net ...\n"); + analog_net_t *anet = palloc(analog_net_t); + t1.set_net(*anet); + //m_netlist.solver()->m_nets.add(anet); + // FIXME: Nets should have a unique name + t1.net().init_object(netlist(),"net." + t1.name() ); + t1.net().register_con(t2); + t1.net().register_con(t1); + } +} + +static core_terminal_t &resolve_proxy(core_terminal_t &term) +{ + if (term.isFamily(core_terminal_t::LOGIC)) + { + logic_t &out = dynamic_cast<logic_t &>(term); + if (out.has_proxy()) + return out.get_proxy()->proxy_term(); + } + return term; +} + +bool setup_t::connect_input_input(core_terminal_t &t1, core_terminal_t &t2) +{ + bool ret = false; + if (t1.has_net()) + { + if (t1.net().isRailNet()) + ret = connect(t2, t1.net().railterminal()); + if (!ret) + { + for (std::size_t i=0; i<t1.net().m_core_terms.size(); i++) + { + if (t1.net().m_core_terms[i]->isType(core_terminal_t::TERMINAL) + /*|| t1.net().m_core_terms[i]->isType(netlist_core_terminal_t::OUTPUT)*/) + { + ret = connect(t2, *t1.net().m_core_terms[i]); + } + if (ret) + break; + } + } + } + if (!ret && t2.has_net()) + { + if (t2.net().isRailNet()) + ret = connect(t1, t2.net().railterminal()); + if (!ret) + { + for (std::size_t i=0; i<t2.net().m_core_terms.size(); i++) + { + if (t2.net().m_core_terms[i]->isType(core_terminal_t::TERMINAL) + /*|| t2.net().m_core_terms[i]->isType(netlist_core_terminal_t::OUTPUT)*/) + { + ret = connect(t1, *t2.net().m_core_terms[i]); + } + if (ret) + break; + } + } + } + return ret; +} + + + +bool setup_t::connect(core_terminal_t &t1_in, core_terminal_t &t2_in) +{ + log().debug("Connecting {1} to {2}\n", t1_in.name(), t2_in.name()); + core_terminal_t &t1 = resolve_proxy(t1_in); + core_terminal_t &t2 = resolve_proxy(t2_in); + bool ret = true; + + if (t1.isType(core_terminal_t::OUTPUT) && t2.isType(core_terminal_t::INPUT)) + { + if (t2.has_net() && t2.net().isRailNet()) + log().fatal("Input {1} already connected\n", t2.name()); + connect_input_output(t2, t1); + } + else if (t1.isType(core_terminal_t::INPUT) && t2.isType(core_terminal_t::OUTPUT)) + { + if (t1.has_net() && t1.net().isRailNet()) + log().fatal("Input {1} already connected\n", t1.name()); + connect_input_output(t1, t2); + } + else if (t1.isType(core_terminal_t::OUTPUT) && t2.isType(core_terminal_t::TERMINAL)) + { + connect_terminal_output(dynamic_cast<terminal_t &>(t2), t1); + } + else if (t1.isType(core_terminal_t::TERMINAL) && t2.isType(core_terminal_t::OUTPUT)) + { + connect_terminal_output(dynamic_cast<terminal_t &>(t1), t2); + } + else if (t1.isType(core_terminal_t::INPUT) && t2.isType(core_terminal_t::TERMINAL)) + { + connect_terminal_input(dynamic_cast<terminal_t &>(t2), t1); + } + else if (t1.isType(core_terminal_t::TERMINAL) && t2.isType(core_terminal_t::INPUT)) + { + connect_terminal_input(dynamic_cast<terminal_t &>(t1), t2); + } + else if (t1.isType(core_terminal_t::TERMINAL) && t2.isType(core_terminal_t::TERMINAL)) + { + connect_terminals(dynamic_cast<terminal_t &>(t1), dynamic_cast<terminal_t &>(t2)); + } + else if (t1.isType(core_terminal_t::INPUT) && t2.isType(core_terminal_t::INPUT)) + { + ret = connect_input_input(t1, t2); + } + else + ret = false; + //netlist().error("Connecting {1} to {2} not supported!\n", t1.name(), t2.name()); + return ret; +} + +void setup_t::resolve_inputs() +{ + bool has_twoterms = false; + + 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. + */ + int tries = 100; + while (m_links.size() > 0 && tries > 0) // FIXME: convert into constant + { + unsigned li = 0; + while (li < m_links.size()) + { + const pstring t1s = m_links[li].e1; + const pstring t2s = m_links[li].e2; + core_terminal_t *t1 = find_terminal(t1s); + core_terminal_t *t2 = find_terminal(t2s); + + if (connect(*t1, *t2)) + m_links.remove_at(li); + else + li++; + } + tries--; + } + if (tries == 0) + { + for (std::size_t i = 0; i < m_links.size(); i++ ) + log().warning("Error connecting {1} to {2}\n", m_links[i].e1, m_links[i].e2); + + log().fatal("Error connecting -- bailing out\n"); + } + + log().verbose("deleting empty nets ..."); + + // delete empty nets ... and save m_list ... + + net_t::list_t todelete; + + for (std::size_t i = 0; i<netlist().m_nets.size(); i++) + { + if (netlist().m_nets[i]->num_cons() == 0) + { + todelete.add(netlist().m_nets[i]); + } + else + { + netlist().m_nets[i]->rebuild_list(); + } + } + + for (std::size_t i=0; i < todelete.size(); i++) + { + log().verbose("Deleting net {1} ...", todelete[i]->name()); + netlist().m_nets.remove(todelete[i]); + if (!todelete[i]->isRailNet()) + pfree(todelete[i]); + } + + pstring errstr(""); + + log().verbose("looking for terminals not connected ..."); + for (std::size_t i = 0; i < m_terminals.size(); i++) + { + core_terminal_t *term = m_terminals.value_at(i); + if (!term->has_net()) + errstr += pfmt("Found terminal {1} without a net\n")(term->name()); + else if (term->net().num_cons() == 0) + log().warning("Found terminal {1} without connections", + term->name()); + } + if (errstr != "") + log().fatal("{1}", errstr); + + + log().verbose("looking for two terms connected to rail nets ...\n"); + // FIXME: doesn't find internal devices. This needs to be more clever + for (std::size_t i=0; i < netlist().m_devices.size(); i++) + { + devices::NETLIB_NAME(twoterm) *t = dynamic_cast<devices::NETLIB_NAME(twoterm) *>(netlist().m_devices[i]); + if (t != NULL) + { + has_twoterms = true; + if (t->m_N.net().isRailNet() && t->m_P.net().isRailNet()) +#if 0 + netlist().fatal("Found device {1} connected only to railterminals {2}/{3}\n", + t->name(), t->m_N.net().name(), t->m_P.net().name()); +#else + log().warning("Found device {1} connected only to railterminals {2}/{3}\n", + t->name(), t->m_N.net().name(), t->m_P.net().name()); +#endif + } + } + + log().verbose("initialize solver ...\n"); + + if (netlist().solver() == NULL) + { + if (has_twoterms) + log().fatal("No solver found for this net although analog elements are present\n"); + } + else + netlist().solver()->post_start(); + +} + +void setup_t::start_devices() +{ + pstring env = nl_util::environment("NL_LOGS"); + + if (env != "") + { + log().debug("Creating dynamic logs ...\n"); + pstring_list_t ll(env, ":"); + for (unsigned i=0; i < ll.size(); i++) + { + log().debug("{1}: <{2}>\n",i, ll[i]); + log().debug("{1}: <{2}>\n",i, ll[i]); + device_t *nc = factory().new_device_by_name("LOG"); + pstring name = "log_" + ll[i]; + register_dev(nc, name); + register_link(name + ".I", ll[i]); + } + } + + netlist().start(); +} + +void setup_t::print_stats() const +{ +#if (NL_KEEP_STATISTICS) + { + for (std::size_t i = 0; i < netlist().m_started_devices.size(); i++) + { + core_device_t *entry = netlist().m_started_devices[i]; + printf("Device %20s : %12d %12d %15ld\n", entry->name(), entry->stat_call_count, entry->stat_update_count, (long int) entry->stat_total_time / (entry->stat_update_count + 1)); + } + printf("Queue Pushes %15d\n", netlist().queue().m_prof_call); + printf("Queue Moves %15d\n", netlist().queue().m_prof_sortmove); + } +#endif +} + +// ---------------------------------------------------------------------------------------- +// Model / family +// ---------------------------------------------------------------------------------------- + +class logic_family_std_proxy_t : public logic_family_desc_t +{ +public: + logic_family_std_proxy_t() { } + virtual devices::nld_base_d_to_a_proxy *create_d_a_proxy(logic_output_t *proxied) const + { + return palloc(devices::nld_d_to_a_proxy(proxied)); + } +}; + +logic_family_desc_t *setup_t::family_from_model(const pstring &model) +{ + model_map_t map; + model_parse(model, map); + + if (setup_t::model_value_str(map, "TYPE") == "TTL") + return netlist_family_TTL; + if (setup_t::model_value_str(map, "TYPE") == "CD4XXX") + return netlist_family_CD4XXX; + + logic_family_std_proxy_t *ret = palloc(logic_family_std_proxy_t); + + ret->m_low_thresh_V = setup_t::model_value(map, "IVL"); + ret->m_high_thresh_V = setup_t::model_value(map, "IVH"); + ret->m_low_V = setup_t::model_value(map, "OVL"); + ret->m_high_V = setup_t::model_value(map, "OVH"); + ret->m_R_low = setup_t::model_value(map, "ORL"); + ret->m_R_high = setup_t::model_value(map, "ORH"); + + return ret; +} + +static pstring model_string(model_map_t &map) +{ + pstring ret = map["COREMODEL"] + "("; + for (unsigned i=0; i<map.size(); i++) + ret = ret + map.key_at(i) + "=" + map.value_at(i) + " "; + + return ret + ")"; +} + + +void setup_t::model_parse(const pstring &model_in, model_map_t &map) +{ + pstring model = model_in; + int pos = 0; + pstring key; + + while (true) + { + pos = model.find("("); + if (pos >= 0) break; + + key = model.ucase(); + if (!m_models.contains(key)) + log().fatal("Model {1} not found\n", model); + model = m_models[key]; + } + pstring xmodel = model.left(pos); + + if (xmodel.equals("_")) + map["COREMODEL"] = key; + else + { + if (m_models.contains(xmodel)) + model_parse(xmodel, map); + else + log().fatal("Model doesn't exist {1}\n", xmodel); + } + + pstring remainder=model.substr(pos+1).trim(); + if (!remainder.endsWith(")")) + log().fatal("Model error {1}\n", model); + remainder = remainder.left(remainder.len() - 1); + + pstring_list_t pairs(remainder," ", true); + for (unsigned i=0; i<pairs.size(); i++) + { + int pose = pairs[i].find("="); + if (pose < 0) + log().fatal("Model error on pair {1}\n", model); + map[pairs[i].left(pose).ucase()] = pairs[i].substr(pose+1); + } +} + +const pstring setup_t::model_value_str(model_map_t &map, const pstring &entity) +{ + pstring ret; + + if (entity != entity.ucase()) + log().fatal("model parameters should be uppercase:{1} {2}\n", entity, model_string(map)); + if (!map.contains(entity)) + log().fatal("Entity {1} not found in model {2}\n", entity, model_string(map)); + else + ret = map[entity]; + + return ret; +} + +nl_double setup_t::model_value(model_map_t &map, const pstring &entity) +{ + pstring tmp = model_value_str(map, entity); + + nl_double factor = NL_FCONST(1.0); + pstring numfac = tmp.right(1); + switch (numfac.code_at(0)) + { + case 'M': factor = 1e6; 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 (numfac < "0" || numfac > "9") + fatalerror_e(pfmt("Unknown number factor <{1}> in: {2}")(numfac)(entity)); + } + if (factor != NL_FCONST(1.0)) + tmp = tmp.left(tmp.len() - 1); + return tmp.as_double() * factor; +} + +// ---------------------------------------------------------------------------------------- +// Sources +// ---------------------------------------------------------------------------------------- + +void setup_t::include(const pstring &netlist_name) +{ + for (std::size_t i=0; i < m_sources.size(); i++) + { + if (m_sources[i]->parse(*this, netlist_name)) + return; + } + log().fatal("unable to find {1} in source collection", netlist_name); +} + +// ---------------------------------------------------------------------------------------- +// base sources +// ---------------------------------------------------------------------------------------- + +bool source_string_t::parse(setup_t &setup, const pstring &name) +{ + pimemstream istrm(m_str.cstr(), m_str.len()); + pomemstream ostrm; + + pimemstream istrm2(ppreprocessor().process(istrm, ostrm)); + return parser_t(istrm2, setup).parse(name); +} + +bool source_mem_t::parse(setup_t &setup, const pstring &name) +{ + pimemstream istrm(m_str.cstr(), m_str.len()); + pomemstream ostrm; + + pimemstream istrm2(ppreprocessor().process(istrm, ostrm)); + return parser_t(istrm2, setup).parse(name); +} + +bool source_file_t::parse(setup_t &setup, const pstring &name) +{ + pifilestream istrm(m_filename); + pomemstream ostrm; + + pimemstream istrm2(ppreprocessor().process(istrm, ostrm)); + return parser_t(istrm2, setup).parse(name); +} + +} |