// license:GPL-2.0+ // copyright-holders:Couriersud /* * nlbase.h * * A mixed signal circuit simulation * * D: Device * O: Rail output (output) * I: Infinite impedance input (input) * T: Terminal (finite impedance) * * +---+ +---+ +---+ +---+ +---+ * | | | | | | | | | | * | D | | D | | D | | D | | D | * | | | | | | | | | | * +-O-+ +-I-+ +-I-+ +-T-+ +-T-+ * | | | | | * +-+---------+---------+---------+---------+-+ * | rail net | * +-------------------------------------------+ * * A rail net is a net which is driven by exactly one output with an * (idealized) internal resistance of zero. * Ideally, it can deliver infinite current. * * A infinite resistance input does not source or sink current. * * Terminals source or sink finite (but never zero) current. * * The system differentiates between analog and logic input and outputs and * analog terminals. Analog and logic devices can not be connected to the * same net. Instead, proxy devices are inserted automatically: * * +---+ +---+ * | | | | * | D1| | D2| * | A | | L | * +-O-+ +-I-+ * | | * +-+---------+---+ * | rail net | * +---------------+ * * is converted into * +----------+ * | | * +---+ +-+-+ | +---+ * | | | L | A-L | | | * | D1| | D | Proxy | | D2| * | A | | A | | | | * +-O-+ +-I-+ | +-I-+ * | | | | * +-+---------+--+ +-+-----+-------+ * | rail net (A) | | rail net (L) | * +--------------| +---------------+ * * This works both analog to logic as well as logic to analog. * * The above is an advanced implementation of the existing discrete * subsystem in MAME. Instead of relying on a fixed time-step, analog devices * could either connect to fixed time-step clock or use an internal clock * to update them. This would however introduce macro devices for RC, diodes * and transistors again. * * ============================================================================ * * Instead, the following approach in case of a pure terminal/input network * is taken: * * +---+ +---+ +---+ +---+ +---+ * | | | | | | | | | | * | D | | D | | D | | D | | D | * | | | | | | | | | | * +-T-+ +-I-+ +-I-+ +-T-+ +-T-+ * | | | | | * '+' | | '-' '-' * +-+---------+---------+---------+---------+-+ * | Calculated net | * +-------------------------------------------+ * * SPICE uses the following basic two terminal device: * * (k) * +-----T-----+ * | | | * | +--+--+ | * | | | | * | R | | * | R | | * | R I | * | | I | Device n * | V+ I | * | V | | * | V- | | * | | | | * | +--+--+ | * | | | * +-----T-----+ * (l) * * This is a resistance in series to a voltage source and paralleled by a * current source. This is suitable to model voltage sources, current sources, * resistors, capacitors, inductances and diodes. * * I(n,l) = - I(n,k) = ( V(k) - V - V(l) ) * (1/R(n)) + I(n) * * Now, the sum of all currents for a given net must be 0: * * Sum(n,I(n,l)) = 0 = sum(n, ( V(k) - V(n) - V(l) ) * (1/R(n)) + I(n) ) * * With G(n) = 1 / R(n) and sum(n, G(n)) = Gtot and k=k(n) * * 0 = - V(l) * Gtot + sum(n, (V(k(n)) - V(n)) * G(n) + I(n)) * * and with l=l(n) and fixed k * * 0 = -V(k) * Gtot + sum(n, ( V(l(n) + V(n) ) * G(n) - I(n)) * * These equations represent a linear Matrix equation (with more math). * * In the end the solution of the analog subsystem boils down to * * (G - D) * V = I * * with G being the conductance matrix, D a diagonal matrix with the total * conductance on the diagonal elements, V the net voltage vector and I the * current vector. * * By using solely two terminal devices, we can simplify the whole calculation * significantly. A BJT now is a four terminal device with two terminals being * connected internally. * * The system is solved using an iterative approach: * * G * V - D * V = I * * assuming V=Vn=Vo * * Vn = D-1 * (I - G * Vo) * * Each terminal thus has three properties: * * a) Resistance * b) Voltage source * c) Current source/sink * * Going forward, the approach can be extended e.g. to use a linear * equation solver. * * The formal representation of the circuit will stay the same, thus scales. * */ #ifndef NLBASE_H_ #define NLBASE_H_ #include #include #include //#include #include #include "nl_lists.h" #include "nl_time.h" #include "plib/pdynlib.h" #include "plib/pstate.h" #include "plib/pfmtlog.h" // ---------------------------------------------------------------------------------------- // Type definitions // ---------------------------------------------------------------------------------------- using netlist_sig_t = std::uint_least32_t; //============================================================ // MACROS / New Syntax //============================================================ #define NETLIB_NAME(chip) nld_ ## chip #define NETLIB_OBJECT_DERIVED(name, pclass) \ class NETLIB_NAME(name) : public NETLIB_NAME(pclass) #define NETLIB_OBJECT(name) \ class NETLIB_NAME(name) : public device_t #define NETLIB_CONSTRUCTOR_DERIVED(cname, pclass) \ private: family_setter_t m_famsetter; \ public: template NETLIB_NAME(cname)(CLASS &owner, const pstring name) \ : NETLIB_NAME(pclass)(owner, name) #define NETLIB_CONSTRUCTOR(cname) \ private: family_setter_t m_famsetter; \ public: template NETLIB_NAME(cname)(CLASS &owner, const pstring name) \ : device_t(owner, name) #define NETLIB_DESTRUCTOR(name) public: virtual ~NETLIB_NAME(name)() #define NETLIB_CONSTRUCTOR_EX(cname, ...) \ private: family_setter_t m_famsetter; \ public: template NETLIB_NAME(cname)(CLASS &owner, const pstring name, __VA_ARGS__) \ : device_t(owner, name) #define NETLIB_DYNAMIC() \ public: virtual bool is_dynamic() const override { return true; } #define NETLIB_TIMESTEP() \ public: virtual bool is_timestep() const override { return true; } \ public: virtual void step_time(const nl_double step) override #define NETLIB_UPDATE_AFTER_PARAM_CHANGE() \ public: virtual bool needs_update_after_param_change() const override { return true; } #define NETLIB_FAMILY(family) , m_famsetter(*this, family) #define NETLIB_UPDATE_TERMINALSI() public: virtual void update_terminals() override #define NETLIB_UPDATEI() protected: virtual void update() NOEXCEPT override #define NETLIB_UPDATE_PARAMI() public: virtual void update_param() override #define NETLIB_RESETI() protected: virtual void reset() override #define NETLIB_SUB(chip) nld_ ## chip #define NETLIB_SUBXX(chip) std::unique_ptr< nld_ ## chip > #define NETLIB_UPDATE(chip) void NETLIB_NAME(chip) :: update(void) NOEXCEPT #define NETLIB_RESET(chip) void NETLIB_NAME(chip) :: reset(void) #define NETLIB_STOP(chip) void NETLIB_NAME(chip) :: stop(void) #define NETLIB_UPDATE_PARAM(chip) void NETLIB_NAME(chip) :: update_param(void) #define NETLIB_FUNC_VOID(chip, name, params) void NETLIB_NAME(chip) :: name params #define NETLIB_UPDATE_TERMINALS(chip) void NETLIB_NAME(chip) :: update_terminals(void) //============================================================ // Asserts //============================================================ #if defined(MAME_DEBUG) #define nl_assert(x) do { if (1) if (!(x)) throw fatalerror_e(plib::pfmt("assert: {1}:{2}: {3}")(__FILE__)(__LINE__)(#x) ); } while (0) #else #define nl_assert(x) do { if (0) if (!(x)) throw fatalerror_e(plib::pfmt("assert: {1}:{2}: {3}")(__FILE__)(__LINE__)(#x) ); } while (0) #endif #define nl_assert_always(x, msg) do { if (!(x)) throw fatalerror_e(plib::pfmt("Fatal error: {1}\nCaused by assert: {2}:{3}: {4}")(msg)(__FILE__)(__LINE__)(#x)); } while (0) // ----------------------------------------------------------------------------- // forward definitions // ----------------------------------------------------------------------------- //============================================================ // Namespace starts //============================================================ namespace netlist { namespace devices { class matrix_solver_t; class NETLIB_NAME(gnd); class NETLIB_NAME(solver); class NETLIB_NAME(mainclock); class NETLIB_NAME(netlistparams); class NETLIB_NAME(base_proxy); class NETLIB_NAME(base_d_to_a_proxy); } //============================================================ // Exceptions //============================================================ class fatalerror_e : public plib::pexception { public: explicit fatalerror_e(const pstring text) : plib::pexception(text) { } fatalerror_e(const fatalerror_e &e) : plib::pexception(e) { } virtual ~fatalerror_e() noexcept {} }; class logic_output_t; class analog_net_t; class logic_net_t; class net_t; class setup_t; class netlist_t; class core_device_t; class device_t; // ----------------------------------------------------------------------------- // model_map_t // ----------------------------------------------------------------------------- using model_map_t = std::unordered_map; // ----------------------------------------------------------------------------- // logic_family_t // ----------------------------------------------------------------------------- class logic_family_desc_t { public: logic_family_desc_t() {} virtual ~logic_family_desc_t() {} virtual plib::owned_ptr create_d_a_proxy(netlist_t &anetlist, const pstring &name, logic_output_t *proxied) const = 0; nl_double m_low_thresh_V; nl_double m_high_thresh_V; nl_double m_low_V; nl_double m_high_V; nl_double m_R_low; nl_double m_R_high; }; class logic_family_t { public: logic_family_t() : m_logic_family(nullptr) {} ~logic_family_t() { } const logic_family_desc_t *logic_family() const { return m_logic_family; } void set_logic_family(const logic_family_desc_t *fam) { m_logic_family = fam; } protected: const logic_family_desc_t *m_logic_family; }; /* Terminals inherit the family description from the device * The default is the ttl family, but any device can override the family. * For individual terminals, these can be overwritten as well. * * Only devices of type GENERIC should have a family description entry */ const logic_family_desc_t *family_TTL(); const logic_family_desc_t *family_CD4XXX(); // ----------------------------------------------------------------------------- // State variables - use to preserve state // ----------------------------------------------------------------------------- template struct state_var { public: template state_var(O &owner, const pstring name, const T &value); state_var(const state_var &rhs) NOEXCEPT = default; state_var(state_var &&rhs) NOEXCEPT = default; state_var &operator=(state_var rhs) { std::swap(rhs.m_value, this->m_value); return *this; } state_var &operator=(const T rhs) { m_value = rhs; return *this; } operator T & () { return m_value; } T & operator()() { return m_value; } operator const T & () const { return m_value; } const T & operator()() const { return m_value; } T * ptr() { return &m_value; } const T * ptr() const { return &m_value; } private: T m_value; }; template struct state_var { public: state_var(device_t &dev, const pstring name, const T & value); state_var(const state_var &rhs) NOEXCEPT = default; state_var(state_var &&rhs) NOEXCEPT = default; state_var &operator=(const state_var rhs) { m_value = rhs.m_value; return *this; } state_var &operator=(const T rhs) { m_value = rhs; return *this; } T & operator[](const std::size_t i) { return m_value[i]; } const T & operator[](const std::size_t i) const { return m_value[i]; } private: T m_value[N]; }; // ----------------------------------------------------------------------------- // State variables - predefined and c++11 non-optioanl // ----------------------------------------------------------------------------- using state_var_u8 = state_var; using state_var_s8 = state_var; using state_var_u32 = state_var; using state_var_s32 = state_var; // ----------------------------------------------------------------------------- // object_t // ----------------------------------------------------------------------------- class object_t { P_PREVENT_COPYING(object_t) public: object_t(const pstring &aname); ~object_t(); const pstring &name() const; //netlist_t & m_netlist; //netlist_t & netlist() { return m_netlist; } //const netlist_t & netlist() const { return m_netlist; } private: pstring m_name; public: void * operator new (size_t size, void *ptr) { return ptr; } void operator delete (void *ptr, void *) { } void * operator new (size_t size); void operator delete (void * mem); }; struct netlist_ref { netlist_ref(netlist_t &nl) : m_netlist(nl) { } netlist_t & netlist() { return m_netlist; } const netlist_t & netlist() const { return m_netlist; } private: netlist_t & m_netlist; }; // ----------------------------------------------------------------------------- // device_object_t // ----------------------------------------------------------------------------- class device_object_t : public object_t { P_PREVENT_COPYING(device_object_t) public: enum type_t { TERMINAL = 0, INPUT = 1, OUTPUT = 2, PARAM = 3, }; device_object_t(core_device_t &dev, const pstring &aname, const type_t atype); core_device_t &device() const { return m_device; } type_t type() const { return m_type; } bool is_type(const type_t atype) const { return (m_type == atype); } netlist_t &netlist(); private: core_device_t & m_device; const type_t m_type; }; // ----------------------------------------------------------------------------- // core_terminal_t // ----------------------------------------------------------------------------- class core_terminal_t : public device_object_t, public plib::linkedlist_t::element_t { P_PREVENT_COPYING(core_terminal_t) public: using list_t = std::vector; /* needed here ... */ enum state_e { STATE_INP_PASSIVE = 0, STATE_INP_ACTIVE = 1, STATE_INP_HL = 2, STATE_INP_LH = 4, STATE_OUT = 128, STATE_NONEX = 256 }; core_terminal_t(core_device_t &dev, const pstring &aname, const type_t atype); virtual ~core_terminal_t() { } void set_net(net_t *anet); void clear_net(); bool has_net() const { return (m_net != nullptr); } const net_t & net() const { return *m_net;} net_t & net() { return *m_net;} bool is_logic() const; bool is_analog() const; bool is_state(const state_e astate) const { return (m_state == astate); } state_e state() const { return m_state; } void set_state(const state_e astate) { nl_assert(astate != STATE_NONEX); m_state = astate; } void reset(); private: net_t * m_net; state_var m_state; }; // ----------------------------------------------------------------------------- // analog_t // ----------------------------------------------------------------------------- class analog_t : public core_terminal_t { public: analog_t(core_device_t &dev, const pstring &aname, const type_t atype) : core_terminal_t(dev, aname, atype) { } const analog_net_t & net() const; analog_net_t & net(); }; // ----------------------------------------------------------------------------- // terminal_t // ----------------------------------------------------------------------------- class terminal_t : public analog_t { P_PREVENT_COPYING(terminal_t) public: terminal_t(core_device_t &dev, const pstring &aname); terminal_t *m_otherterm; void set(const nl_double G) { set_ptr(m_Idr1, 0); set_ptr(m_go1, G); set_ptr(m_gt1, G); } void set(const nl_double GO, const nl_double GT) { set_ptr(m_Idr1, 0); set_ptr(m_go1, GO); set_ptr(m_gt1, GT); } void set(const nl_double GO, const nl_double GT, const nl_double I) { set_ptr(m_Idr1, I); set_ptr(m_go1, GO); set_ptr(m_gt1, GT); } void schedule_solve(); void schedule_after(const netlist_time &after); void set_ptrs(nl_double *gt, nl_double *go, nl_double *Idr) { m_gt1 = gt; m_go1 = go; m_Idr1 = Idr; } private: void set_ptr(nl_double *ptr, const nl_double val) { if (ptr != nullptr && *ptr != val) { *ptr = val; } } state_var m_Idr1; // drive current state_var m_go1; // conductance for Voltage from other term state_var m_gt1; // conductance for total conductance }; // ----------------------------------------------------------------------------- // logic_t // ----------------------------------------------------------------------------- class logic_t : public core_terminal_t, public logic_family_t { public: logic_t(core_device_t &dev, const pstring &aname, const type_t atype) : core_terminal_t(dev, aname, atype), logic_family_t(), m_proxy(nullptr) { } bool has_proxy() const { return (m_proxy != nullptr); } devices::nld_base_proxy *get_proxy() const { return m_proxy; } void set_proxy(devices::nld_base_proxy *proxy) { m_proxy = proxy; } logic_net_t & net(); const logic_net_t & net() const; protected: private: devices::nld_base_proxy *m_proxy; }; // ----------------------------------------------------------------------------- // logic_input_t // ----------------------------------------------------------------------------- class logic_input_t : public logic_t { public: logic_input_t(core_device_t &dev, const pstring &aname); netlist_sig_t Q() const; void inactivate(); void activate(); void activate_hl(); void activate_lh(); }; // ----------------------------------------------------------------------------- // analog_input_t // ----------------------------------------------------------------------------- class analog_input_t : public analog_t { public: analog_input_t(core_device_t &dev, const pstring &aname); nl_double Q_Analog() const; }; // ----------------------------------------------------------------------------- // net_t // ----------------------------------------------------------------------------- class net_t : public object_t, public netlist_ref { P_PREVENT_COPYING(net_t) public: net_t(netlist_t &nl, const pstring &aname, core_terminal_t *mr = nullptr); virtual ~net_t(); void reset(); void register_con(core_terminal_t &terminal); void merge_net(net_t *othernet); bool is_logic() const; bool is_analog() const; void toggle_new_Q() { m_new_Q ^= 1; } void force_queue_execution() { m_new_Q = (m_cur_Q ^ 1); } void push_to_queue(const netlist_time delay) NOEXCEPT; void reschedule_in_queue(const netlist_time delay) NOEXCEPT; bool is_queued() const { return m_in_queue == 1; } void update_devs(); const netlist_time time() const { return m_time; } void set_time(const netlist_time ntime) { m_time = ntime; } bool isRailNet() const { return !(m_railterminal == nullptr); } core_terminal_t & railterminal() const { return *m_railterminal; } std::size_t num_cons() const { return m_core_terms.size(); } void inc_active(core_terminal_t &term); void dec_active(core_terminal_t &term); void rebuild_list(); /* rebuild m_list after a load */ void move_connections(net_t *new_net); std::vector m_core_terms; // save post-start m_list ... protected: state_var m_new_Q; state_var m_cur_Q; state_var m_time; state_var_s32 m_active; state_var_u8 m_in_queue; /* 0: not in queue, 1: in queue, 2: last was taken */ private: plib::linkedlist_t m_list_active; core_terminal_t * m_railterminal; public: // FIXME: Have to fix the public at some time state_var m_cur_Analog; }; class logic_net_t : public net_t { P_PREVENT_COPYING(logic_net_t) public: logic_net_t(netlist_t &nl, const pstring &aname, core_terminal_t *mr = nullptr); virtual ~logic_net_t() { } netlist_sig_t Q() const { return m_cur_Q; } netlist_sig_t new_Q() const { return m_new_Q; } void initial(const netlist_sig_t val) { m_cur_Q = m_new_Q = val; } void set_Q(const netlist_sig_t newQ, const netlist_time delay) NOEXCEPT { if (newQ != m_new_Q) { m_new_Q = newQ; push_to_queue(delay); } } void set_Q_time(const netlist_sig_t newQ, const netlist_time at) { if (newQ != m_new_Q) { m_in_queue = 0; m_time = at; } m_cur_Q = m_new_Q = newQ; } /* internal state support * FIXME: get rid of this and implement export/import in MAME */ netlist_sig_t &Q_state_ptr() { return m_cur_Q; } protected: private: }; class analog_net_t : public net_t { P_PREVENT_COPYING(analog_net_t) public: using list_t = std::vector; analog_net_t(netlist_t &nl, const pstring &aname, core_terminal_t *mr = nullptr); virtual ~analog_net_t() { } nl_double Q_Analog() const { return m_cur_Analog; } nl_double &Q_Analog_state_ptr() { return m_cur_Analog; } //FIXME: needed by current solver code devices::matrix_solver_t *solver() { return m_solver; } void set_solver(devices::matrix_solver_t *solver) { m_solver = solver; } bool already_processed(std::vector &groups); void process_net(std::vector &groups); private: devices::matrix_solver_t *m_solver; }; // ----------------------------------------------------------------------------- // logic_output_t // ----------------------------------------------------------------------------- class logic_output_t : public logic_t { P_PREVENT_COPYING(logic_output_t) public: logic_output_t(core_device_t &dev, const pstring &aname); void initial(const netlist_sig_t val); void set_Q(const netlist_sig_t newQ, const netlist_time delay) NOEXCEPT { m_my_net.set_Q(newQ, delay); // take the shortcut } private: logic_net_t m_my_net; }; class analog_output_t : public analog_t { P_PREVENT_COPYING(analog_output_t) public: analog_output_t(core_device_t &dev, const pstring &aname); void initial(const nl_double val); void set_Q(const nl_double newQ); private: analog_net_t m_my_net; }; // ----------------------------------------------------------------------------- // param_t // ----------------------------------------------------------------------------- class param_t : public device_object_t { P_PREVENT_COPYING(param_t) public: enum param_type_t { MODEL, STRING, DOUBLE, INTEGER, LOGIC }; param_t(const param_type_t atype, device_t &device, const pstring &name); virtual ~param_t() {} param_type_t param_type() const { return m_param_type; } private: const param_type_t m_param_type; }; template class param_template_t : public param_t { P_PREVENT_COPYING(param_template_t) public: param_template_t(device_t &device, const pstring name, const C val); operator const C() const { return Value(); } void setTo(const C ¶m); void initial(const C &val) { m_param = val; } C Value() const { return m_param; } protected: virtual void changed() { } C m_param; private: }; using param_double_t = param_template_t; using param_int_t = param_template_t; using param_str_t = param_template_t; using param_logic_t = param_template_t; class param_model_t : public param_str_t { public: param_model_t(device_t &device, const pstring name, const pstring val) : param_str_t(device, name, val) { } /* these should be cached! */ nl_double model_value(const pstring &entity); const pstring model_value_str(const pstring &entity); const pstring model_type(); protected: void changed() override { m_map.clear(); } private: model_map_t m_map; }; // ----------------------------------------------------------------------------- // core_device_t // ----------------------------------------------------------------------------- class core_device_t : public object_t, public logic_family_t, public netlist_ref { P_PREVENT_COPYING(core_device_t) public: core_device_t(netlist_t &owner, const pstring &name); core_device_t(core_device_t &owner, const pstring &name); virtual ~core_device_t(); void update_dev() { m_stat_total_time.start(); do_update(); m_stat_total_time.stop(); } void do_update() NOEXCEPT { #if (NL_PMF_TYPE == NL_PMF_TYPE_GNUC_PMF) (this->*m_static_update)(); #elif ((NL_PMF_TYPE == NL_PMF_TYPE_GNUC_PMF_CONV) || (NL_PMF_TYPE == NL_PMF_TYPE_INTERNAL)) m_static_update(this); #else update(); #endif } void set_delegate_pointer(); void stop_dev(); void do_inc_active() { if (m_hint_deactivate) { m_stat_inc_active.inc(); inc_active(); } } void do_dec_active() { if (m_hint_deactivate) dec_active(); } void do_reset() { reset(); } void set_hint_deactivate(bool v) { m_hint_deactivate = v; } netlist_sig_t INPLOGIC_PASSIVE(logic_input_t &inp); netlist_sig_t INPLOGIC(const logic_input_t &inp) const { nl_assert(inp.state() != logic_t::STATE_INP_PASSIVE); return inp.Q(); } void OUTLOGIC(logic_output_t &out, const netlist_sig_t val, const netlist_time delay) NOEXCEPT; nl_double INPANALOG(const analog_input_t &inp) const { return inp.Q_Analog(); } nl_double TERMANALOG(const terminal_t &term) const { return term.net().Q_Analog(); } void OUTANALOG(analog_output_t &out, const nl_double val) { out.set_Q(val); } /* stats */ nperftime_t m_stat_total_time; nperfcount_t m_stat_call_count; nperfcount_t m_stat_inc_active; protected: virtual void update() NOEXCEPT { } virtual void inc_active() { } virtual void dec_active() { } virtual void stop() { } virtual void reset() { } public: virtual void step_time(ATTR_UNUSED const nl_double st) { } virtual void update_terminals() { } virtual void update_param() {} virtual bool is_dynamic() const { return false; } virtual bool is_timestep() const { return false; } virtual bool needs_update_after_param_change() const { return false; } private: bool m_hint_deactivate; #if (NL_PMF_TYPE == NL_PMF_TYPE_GNUC_PMF) typedef void (core_device_t::*net_update_delegate)(); #elif ((NL_PMF_TYPE == NL_PMF_TYPE_GNUC_PMF_CONV) || (NL_PMF_TYPE == NL_PMF_TYPE_INTERNAL)) using net_update_delegate = MEMBER_ABI void (*)(core_device_t *); #endif #if (NL_PMF_TYPE > NL_PMF_TYPE_VIRTUAL) net_update_delegate m_static_update; #endif }; // ----------------------------------------------------------------------------- // device_t // ----------------------------------------------------------------------------- class device_t : public core_device_t { P_PREVENT_COPYING(device_t) public: template device_t(C &owner, const pstring &name) : core_device_t(owner, name) { } virtual ~device_t(); setup_t &setup(); #if 1 template void register_sub(const pstring &name, std::unique_ptr &dev) { dev.reset(new C(*this, name)); } #endif void register_subalias(const pstring &name, core_terminal_t &term); void register_subalias(const pstring &name, const pstring &aliased); void connect_late(const pstring &t1, const pstring &t2); void connect_late(core_terminal_t &t1, core_terminal_t &t2); void connect_post_start(core_terminal_t &t1, core_terminal_t &t2); protected: NETLIB_UPDATEI() { } NETLIB_UPDATE_TERMINALSI() { } private: }; // ----------------------------------------------------------------------------- // family_setter_t // ----------------------------------------------------------------------------- struct family_setter_t { family_setter_t() { } family_setter_t(core_device_t &dev, const char *desc); family_setter_t(core_device_t &dev, const logic_family_desc_t *desc); }; // ----------------------------------------------------------------------------- // nld_base_dummy : basis for dummy devices // ----------------------------------------------------------------------------- NETLIB_OBJECT(base_dummy) { public: NETLIB_CONSTRUCTOR(base_dummy) { } }; // ----------------------------------------------------------------------------- // queue_t // ----------------------------------------------------------------------------- class queue_t : public timed_queue, public object_t, public netlist_ref, public plib::state_manager_t::callback_t { public: explicit queue_t(netlist_t &nl); protected: void register_state(plib::state_manager_t &manager, const pstring &module) override; void on_pre_save() override; void on_post_load() override; private: struct names_t { char m_buf[64]; }; int m_qsize; std::vector m_times; std::vector m_names; }; // ----------------------------------------------------------------------------- // netlist_t // ----------------------------------------------------------------------------- class netlist_t : public plib::plog_dispatch_intf { friend class setup_t; P_PREVENT_COPYING(netlist_t) public: explicit netlist_t(const pstring &aname); virtual ~netlist_t(); pstring name() const { return m_name; } void start(); void stop(); const queue_t &queue() const { return m_queue; } queue_t &queue() { return m_queue; } const netlist_time time() const { return m_time; } devices::NETLIB_NAME(solver) *solver() const { return m_solver; } devices::NETLIB_NAME(gnd) *gnd() const { return m_gnd; } nl_double gmin() const; void push_to_queue(net_t &out, const netlist_time attime) NOEXCEPT; void remove_from_queue(net_t &out); void process_queue(const netlist_time &delta); void abort_current_queue_slice() { m_queue.retime(m_time, nullptr); } void rebuild_lists(); /* must be called after post_load ! */ void set_setup(setup_t *asetup) { m_setup = asetup; } setup_t &setup() { return *m_setup; } void register_dev(plib::owned_ptr dev); net_t *find_net(const pstring &name); template std::vector get_device_list() { std::vector tmp; for (auto &d : m_devices) { device_class *dev = dynamic_cast(d.get()); if (dev != nullptr) tmp.push_back(dev); } return tmp; } template device_class *get_single_device(const char *classname) { device_class *ret = nullptr; for (auto &d : m_devices) { device_class *dev = dynamic_cast(d.get()); if (dev != nullptr) { if (ret != nullptr) this->log().fatal("more than one {1} device found", classname); else ret = dev; } } return ret; } plib::plog_base &log() { return m_log; } const plib::plog_base &log() const { return m_log; } plib::state_manager_t &state() { return m_state; } template void save(O &owner, C &state, const pstring &stname) { this->state().save_item(static_cast(&owner), state, pstring(owner.name()) + "." + stname); } template void save(O &owner, C *state, const pstring &stname, const int count) { this->state().save_state_ptr(static_cast(&owner), pstring(owner.name()) + "." + stname, plib::state_manager_t::datatype_f::f(), count, state); } virtual void reset(); plib::dynlib &lib() { return *m_lib; } void print_stats() const; std::vector> m_devices; /* sole use is to manage lifetime of net objects */ std::vector> m_nets; /* sole use is to manage lifetime of family objects */ std::vector>> m_family_cache; private: plib::state_manager_t m_state; /* mostly rw */ netlist_time m_time; queue_t m_queue; nperftime_t m_stat_mainloop; /* mostly ro */ devices::NETLIB_NAME(mainclock) * m_mainclock; devices::NETLIB_NAME(solver) * m_solver; devices::NETLIB_NAME(gnd) * m_gnd; devices::NETLIB_NAME(netlistparams) *m_params; pstring m_name; setup_t *m_setup; plib::plog_base m_log; plib::dynlib *m_lib; // external lib needs to be loaded as long as netlist exists // performance nperfcount_t m_perf_out_processed; nperfcount_t m_perf_inp_processed; nperfcount_t m_perf_inp_active; }; // ----------------------------------------------------------------------------- // Support classes for devices // ----------------------------------------------------------------------------- template class object_array_t : public plib::uninitialised_array_t { public: struct init { const char *p[N]; }; object_array_t(core_device_t &dev, init names) { for (std::size_t i = 0; iemplace(i, dev, names.p[i]); } }; // ----------------------------------------------------------------------------- // inline implementations // ----------------------------------------------------------------------------- template inline void param_template_t::setTo(const C ¶m) { if (m_param != param) { m_param = param; changed(); device().update_param(); if (device().needs_update_after_param_change()) device().update_dev(); } } inline bool core_terminal_t::is_logic() const { return dynamic_cast(this) != nullptr; } inline bool core_terminal_t::is_analog() const { return dynamic_cast(this) != nullptr; } inline bool net_t::is_logic() const { return dynamic_cast(this) != nullptr; } inline bool net_t::is_analog() const { return dynamic_cast(this) != nullptr; } inline void logic_input_t::inactivate() { if (!is_state(STATE_INP_PASSIVE)) { set_state(STATE_INP_PASSIVE); net().dec_active(*this); } } inline void logic_input_t::activate() { if (is_state(STATE_INP_PASSIVE)) { net().inc_active(*this); set_state(STATE_INP_ACTIVE); } } inline void logic_input_t::activate_hl() { if (is_state(STATE_INP_PASSIVE)) { net().inc_active(*this); set_state(STATE_INP_HL); } } inline void logic_input_t::activate_lh() { if (is_state(STATE_INP_PASSIVE)) { net().inc_active(*this); set_state(STATE_INP_LH); } } inline void net_t::push_to_queue(const netlist_time delay) NOEXCEPT { if (!is_queued() && (num_cons() != 0)) { m_time = netlist().time() + delay; m_in_queue = (m_active > 0); /* queued ? */ if (m_in_queue) { netlist().push_to_queue(*this, m_time); } } } inline void net_t::reschedule_in_queue(const netlist_time delay) NOEXCEPT { if (is_queued()) netlist().remove_from_queue(*this); m_time = netlist().time() + delay; m_in_queue = (m_active > 0); /* queued ? */ if (m_in_queue) { netlist().push_to_queue(*this, m_time); } } inline const analog_net_t & analog_t::net() const { return static_cast(core_terminal_t::net()); } inline analog_net_t & analog_t::net() { return static_cast(core_terminal_t::net()); } inline logic_net_t & logic_t::net() { return *static_cast(&core_terminal_t::net()); } inline const logic_net_t & logic_t::net() const { return static_cast(core_terminal_t::net()); } inline netlist_sig_t logic_input_t::Q() const { return net().Q(); } inline nl_double analog_input_t::Q_Analog() const { return net().Q_Analog(); } inline void analog_output_t::set_Q(const nl_double newQ) { if (newQ != net().Q_Analog()) { net().m_cur_Analog = newQ; net().toggle_new_Q(); net().push_to_queue(NLTIME_FROM_NS(1)); } } inline void netlist_t::push_to_queue(net_t &out, const netlist_time attime) NOEXCEPT { m_queue.push(attime, &out); } inline void netlist_t::remove_from_queue(net_t &out) { m_queue.remove(&out); } inline void core_device_t::OUTLOGIC(logic_output_t &out, const netlist_sig_t val, const netlist_time delay) NOEXCEPT { out.set_Q(val, delay); } template template state_var::state_var(O &owner, const pstring name, const T &value) : m_value(value) { owner.netlist().save(owner, m_value, name); } template state_var::state_var(device_t &dev, const pstring name, const T & value) { dev.netlist().save(dev, m_value, name); for (std::size_t i=0; i