// license:GPL-2.0+ // copyright-holders:Couriersud /* * nlid_proxy.cpp * */ #include "nlid_proxy.h" #include "core/setup.h" #include "nl_errstr.h" #include "solver/nld_solver.h" #include namespace netlist { namespace devices { // ----------------------------------------------------------------------------- // nld_base_proxy // ----------------------------------------------------------------------------- static const std::array, 3> power_syms = {{ {"VCC", "VEE"}, {"VCC", "GND"}, {"VDD", "VSS"}}}; nld_base_proxy::nld_base_proxy(netlist_state_t &anetlist, const pstring &name, const logic_t *inout_proxied) : device_t(anetlist, name, inout_proxied->logic_family()) , m_tp(nullptr) , m_tn(nullptr) { if (logic_family() == nullptr) { throw nl_exception(MF_NULLPTR_FAMILY_NP("nld_base_proxy")); } bool f = false; for (const auto & pwr_sym : power_syms) { pstring devname = inout_proxied->device().name(); auto *tp_ct(anetlist.setup().find_terminal(devname + "." + pstring(pwr_sym.first), /*detail::terminal_type::INPUT,*/ false)); auto *tp_cn(anetlist.setup().find_terminal(devname + "." + pstring(pwr_sym.second), /*detail::terminal_type::INPUT,*/ false)); if ((tp_ct != nullptr) && (tp_cn != nullptr)) { if (!tp_ct->is_analog()) throw nl_exception(plib::pfmt("Not an analog terminal: {1}")(tp_ct->name())); if (!tp_cn->is_analog()) throw nl_exception(plib::pfmt("Not an analog terminal: {1}")(tp_cn->name())); auto *tp_t = dynamic_cast(tp_ct); auto *tn_t = dynamic_cast(tp_cn); if (f && (tp_t != nullptr && tn_t != nullptr)) log().warning(MI_MULTIPLE_POWER_TERMINALS_ON_DEVICE(inout_proxied->device().name(), m_tp->name(), m_tn->name(), tp_t != nullptr ? tp_t->name() : "", tn_t != nullptr ? tn_t->name() : "")); else if (tp_t != nullptr && tn_t != nullptr) { m_tp = tp_t; m_tn = tn_t; f = true; } } } if (!f) throw nl_exception(MF_NO_POWER_TERMINALS_ON_DEVICE_2(name, anetlist.setup().de_alias(inout_proxied->device().name()))); log().verbose("D/A Proxy: Found power terminals on device {1}", inout_proxied->device().name()); } // ---------------------------------------------------------------------------------------- // nld_a_to_d_proxy // ---------------------------------------------------------------------------------------- nld_base_a_to_d_proxy::nld_base_a_to_d_proxy(netlist_state_t &anetlist, const pstring &name, const logic_input_t *in_proxied) : nld_base_proxy(anetlist, name, in_proxied) { } nld_a_to_d_proxy::nld_a_to_d_proxy(netlist_state_t &anetlist, const pstring &name, const logic_input_t *in_proxied) : nld_base_a_to_d_proxy(anetlist, name, in_proxied) , m_Q(*this, "Q") , m_I(*this, "I", nldelegate(&nld_a_to_d_proxy::input, this)) { } NETLIB_HANDLER(a_to_d_proxy, input) { const auto v(m_I.Q_Analog()); const auto vn(m_tn->net().Q_Analog()); const auto vp(m_tp->net().Q_Analog()); if (logic_family()->is_above_high_thresh_V(v, vn, vp)) out().push(1, netlist_time::quantum()); else if (logic_family()->is_below_low_thresh_V(v, vn, vp)) out().push(0, netlist_time::quantum()); else { // do nothing } } // ---------------------------------------------------------------------------------------- // nld_d_to_a_proxy // ---------------------------------------------------------------------------------------- nld_base_d_to_a_proxy::nld_base_d_to_a_proxy(netlist_state_t &anetlist, const pstring &name, const logic_output_t *out_proxied) : nld_base_proxy(anetlist, name, out_proxied) { } nld_d_to_a_proxy::nld_d_to_a_proxy(netlist_state_t &anetlist, const pstring &name, const logic_output_t *out_proxied) : nld_base_d_to_a_proxy(anetlist, name, out_proxied) , m_I(*this, "I", nldelegate(&nld_d_to_a_proxy :: input, this)) , m_RP(*this, "RP") , m_RN(*this, "RN") , m_last_state(*this, "m_last_var", terminal_t::OUT_TRISTATE()) { register_subalias("Q", "RN.1"); log().verbose("D/A Proxy: Found power terminals on device {1}", out_proxied->device().name()); if (anetlist.is_extended_validation()) { // During validation, don't connect to terminals found // This will cause terminals not connected to a rail net to // fail connection stage. connect(m_RN.N(), m_RP.P()); } else { connect(m_RN.N(), *m_tn); connect(m_RP.P(), *m_tp); } connect(m_RN.P(), m_RP.N()); } void nld_d_to_a_proxy::reset() { //m_Q.initial(0.0); m_last_state = terminal_t::OUT_TRISTATE(); m_RN.reset(); m_RP.reset(); m_RN.set_G_V_I(plib::reciprocal(logic_family()->R_low()), logic_family()->low_offset_V(), nlconst::zero()); m_RP.set_G_V_I(G_OFF, nlconst::zero(), nlconst::zero()); } NETLIB_HANDLER(d_to_a_proxy ,input) { const auto state = m_I(); if (state != m_last_state) { // RN, RP are connected ... m_RN.change_state([this, &state]() { switch (state) { case 0: m_RN.set_G_V_I(plib::reciprocal(logic_family()->R_low()), logic_family()->low_offset_V(), nlconst::zero()); m_RP.set_G_V_I(G_OFF, nlconst::zero(), nlconst::zero()); break; case 1: m_RN.set_G_V_I(G_OFF, nlconst::zero(), nlconst::zero()); m_RP.set_G_V_I(plib::reciprocal(logic_family()->R_high()), logic_family()->high_offset_V(), nlconst::zero()); break; case terminal_t::OUT_TRISTATE(): m_RN.set_G_V_I(G_OFF, nlconst::zero(), nlconst::zero()); m_RP.set_G_V_I(G_OFF, nlconst::zero(), nlconst::zero()); break; default: plib::terminate("unknown state for proxy: this should never happen!"); } }); m_last_state = state; } } } //namespace devices } // namespace netlist