mame/src/lib/netlist/nl_base.h
2016-06-07 23:13:34 +02:00

1327 lines
35 KiB
C++

// 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 <vector>
#include <memory>
#include <cmath>
#include "nl_lists.h"
#include "nl_time.h"
#include "plib/pdynlib.h"
#include "plib/pstate.h"
#include "plib/pfmtlog.h"
// ----------------------------------------------------------------------------------------
// Type definitions
// ----------------------------------------------------------------------------------------
/*
* unsigned int would be a 20% speed increase over UINT8 for pong.
* For breakout it causes a slight decrease.
*
*/
using netlist_sig_t = std::uint32_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 <class CLASS> 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 <class CLASS> 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 <class CLASS> 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 nld_base_d_to_a_proxy;
}
//============================================================
// Exceptions
//============================================================
class fatalerror_e : public plib::pexception
{
public:
fatalerror_e(const pstring &text) : plib::pexception(text) { }
virtual ~fatalerror_e() throw() {}
};
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 param_model_t;
// -----------------------------------------------------------------------------
// model_map_t
// -----------------------------------------------------------------------------
using model_map_t = plib::hashmap_t<pstring, pstring>;
// -----------------------------------------------------------------------------
// logic_family_t
// -----------------------------------------------------------------------------
class logic_family_desc_t
{
public:
logic_family_desc_t() {}
virtual ~logic_family_desc_t() {}
virtual plib::owned_ptr<devices::nld_base_d_to_a_proxy> 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();
// -----------------------------------------------------------------------------
// object_t
// -----------------------------------------------------------------------------
class object_t : public plib::pstate_interface_t<object_t>
{
P_PREVENT_COPYING(object_t)
public:
enum type_t {
TERMINAL = 0,
INPUT = 1,
OUTPUT = 2,
PARAM = 3,
NET = 4,
DEVICE = 5,
QUEUE = 6
};
object_t(netlist_t &nl, const pstring &aname, const type_t atype);
~object_t();
const pstring &name() const;
plib::pstate_manager_t &state_manager();
type_t type() const { return m_objtype; }
bool is_type(const type_t atype) const { return (m_objtype == atype); }
netlist_t & netlist() { return m_netlist; }
const netlist_t & netlist() const { return m_netlist; }
private:
netlist_t & m_netlist;
pstring m_name;
const type_t m_objtype;
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);
};
// -----------------------------------------------------------------------------
// device_object_t
// -----------------------------------------------------------------------------
class device_object_t : public object_t
{
P_PREVENT_COPYING(device_object_t)
public:
device_object_t(core_device_t &dev, const pstring &aname, const type_t atype);
core_device_t &device() const { return m_device; }
private:
core_device_t & m_device;
};
// -----------------------------------------------------------------------------
// core_terminal_t
// -----------------------------------------------------------------------------
class core_terminal_t : public device_object_t, public plib::linkedlist_t<core_terminal_t>::element_t
{
P_PREVENT_COPYING(core_terminal_t)
public:
using list_t = plib::pvector_t<core_terminal_t *>;
/* 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_e 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:
using list_t = plib::pvector_t<terminal_t *>;
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;
}
}
nl_double *m_Idr1; // drive current
nl_double *m_go1; // conductance for Voltage from other term
nl_double *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
{
P_PREVENT_COPYING(net_t)
public:
using ptr_t = net_t *;
using list_t = plib::pvector_t<std::shared_ptr<net_t>>;
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 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; }
int 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);
plib::pvector_t<core_terminal_t *> m_core_terms; // save post-start m_list ...
protected:
netlist_sig_t m_new_Q;
netlist_sig_t m_cur_Q;
netlist_time m_time;
INT32 m_active;
UINT8 m_in_queue; /* 0: not in queue, 1: in queue, 2: last was taken */
private:
plib::linkedlist_t<core_terminal_t> m_list_active;
core_terminal_t * m_railterminal;
public:
// FIXME: Have to fix the public at some time
nl_double m_cur_Analog;
};
class logic_net_t : public net_t
{
P_PREVENT_COPYING(logic_net_t)
public:
using list_t = plib::pvector_t<logic_net_t *>;
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 = plib::pvector_t<analog_net_t *>;
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(plib::pvector_t<list_t> &groups);
void process_net(plib::pvector_t<list_t> &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 device_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 <typename C, param_t::param_type_t T>
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 &param);
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<nl_double, param_t::DOUBLE>;
using param_int_t = param_template_t<int, param_t::INTEGER>;
using param_str_t = param_template_t<pstring, param_t::STRING>;
using param_logic_t = param_template_t<int, param_t::INTEGER>;
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
{
P_PREVENT_COPYING(core_device_t)
public:
using list_t = plib::pvector_t<core_device_t *>;
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()
{
begin_timing(stat_total_time);
inc_stat(stat_update_count);
do_update();
end_timing(stat_total_time);
}
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() { inc_active(); }
void do_dec_active() { dec_active(); }
void do_reset() { reset(); }
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); }
#if (NL_KEEP_STATISTICS)
/* stats */
plib::ticks_t stat_total_time;
INT32 stat_update_count;
INT32 stat_call_count;
#endif
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:
#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
};
// -----------------------------------------------------------------------------
// param_ref_t
// -----------------------------------------------------------------------------
struct param_ref_t
{
param_ref_t(const pstring name, core_device_t &device, param_t &param)
: m_name(name)
, m_device(device)
, m_param(param)
{ }
pstring m_name;
core_device_t &m_device;
param_t &m_param;
};
// -----------------------------------------------------------------------------
// device_t
// -----------------------------------------------------------------------------
class device_t : public core_device_t
{
P_PREVENT_COPYING(device_t)
public:
template <class C>
device_t(C &owner, const pstring &name)
: core_device_t(owner, name) { }
virtual ~device_t();
setup_t &setup();
#if 1
template<class C>
void register_sub(const pstring &name, std::unique_ptr<C> &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);
plib::pvector_t<pstring> m_terminals;
protected:
NETLIB_UPDATEI() { }
NETLIB_UPDATE_TERMINALSI() { }
private:
void register_p(const pstring &name, object_t &obj);
void register_sub_p(device_t &dev);
};
// -----------------------------------------------------------------------------
// 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<net_t *, netlist_time>,
public object_t,
public plib::pstate_callback_t
{
public:
queue_t(netlist_t &nl);
protected:
void register_state(plib::pstate_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<netlist_time::INTERNALTYPE> m_times;
std::vector<names_t> m_names;
};
// -----------------------------------------------------------------------------
// netlist_t
// -----------------------------------------------------------------------------
class netlist_t : public plib::pstate_manager_t, public plib::plog_dispatch_intf //, public device_owner_t
{
P_PREVENT_COPYING(netlist_t)
public:
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_stop = netlist_time::zero(); }
bool use_deactivate() const { return m_use_deactivate; }
void rebuild_lists(); /* must be called after post_load ! */
void set_setup(setup_t *asetup) { m_setup = asetup; }
setup_t &setup() { return *m_setup; }
net_t *find_net(const pstring &name);
template<class device_class>
plib::pvector_t<device_class *> get_device_list()
{
plib::pvector_t<device_class *> tmp;
for (auto &d : m_devices)
{
device_class *dev = dynamic_cast<device_class *>(d.get());
if (dev != nullptr)
tmp.push_back(dev);
}
return tmp;
}
template<class device_class>
device_class *get_single_device(const char *classname)
{
device_class *ret = nullptr;
for (auto &d : m_devices)
{
device_class *dev = dynamic_cast<device_class *>(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<NL_DEBUG> &log() { return m_log; }
const plib::plog_base<NL_DEBUG> &log() const { return m_log; }
virtual void reset();
plib::dynlib &lib() { return *m_lib; }
void print_stats() const;
plib::pvector_t<plib::owned_ptr<core_device_t>> m_devices;
/* sole use is to manage lifetime of net objects */
net_t::list_t m_nets;
/* sole use is to manage lifetime of family objects */
std::vector<std::pair<pstring, std::unique_ptr<logic_family_desc_t>>> m_family_cache;
protected:
#if (NL_KEEP_STATISTICS)
// performance
int m_perf_out_processed;
int m_perf_inp_processed;
int m_perf_inp_active;
#endif
private:
netlist_time m_stop; // target time for current queue processing
netlist_time m_time;
bool m_use_deactivate;
queue_t m_queue;
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<NL_DEBUG> m_log;
plib::dynlib *m_lib; // external lib needs to be loaded as long as netlist exists
};
// -----------------------------------------------------------------------------
// Support classes for devices
// -----------------------------------------------------------------------------
template<class C, int N>
class object_array_t : public plib::uninitialised_array_t<C, N>
{
public:
struct init
{
const char *p[N];
};
object_array_t(core_device_t &dev, init names)
{
for (std::size_t i = 0; i<N; i++)
this->emplace(i, dev, names.p[i]);
}
};
// -----------------------------------------------------------------------------
// inline implementations
// -----------------------------------------------------------------------------
inline plib::pstate_manager_t &object_t::state_manager()
{
return m_netlist;
}
template <class C, param_t::param_type_t T>
inline void param_template_t<C, T>::setTo(const C &param)
{
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<const logic_t *>(this) != nullptr;
}
inline bool core_terminal_t::is_analog() const
{
return dynamic_cast<const analog_t *>(this) != nullptr;
}
inline bool net_t::is_logic() const
{
return dynamic_cast<const logic_net_t *>(this) != nullptr;
}
inline bool net_t::is_analog() const
{
return dynamic_cast<const analog_net_t *>(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<const analog_net_t &>(core_terminal_t::net());
}
inline analog_net_t & analog_t::net()
{
return static_cast<analog_net_t &>(core_terminal_t::net());
}
inline logic_net_t & logic_t::net()
{
return *static_cast<logic_net_t *>(&core_terminal_t::net());
}
inline const logic_net_t & logic_t::net() const
{
return static_cast<const logic_net_t &>(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);
}
}
NETLIST_SAVE_TYPE(netlist::core_terminal_t::state_e, pstate_data_type_e::DT_INT);
#endif /* NLBASE_H_ */