summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/netlist/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/netlist/core')
-rw-r--r--src/lib/netlist/core/analog.h173
-rw-r--r--src/lib/netlist/core/base_objects.h317
-rw-r--r--src/lib/netlist/core/core_device.h203
-rw-r--r--src/lib/netlist/core/device.h116
-rw-r--r--src/lib/netlist/core/device_macros.h154
-rw-r--r--src/lib/netlist/core/devices.h166
-rw-r--r--src/lib/netlist/core/exec.h124
-rw-r--r--src/lib/netlist/core/logic.h212
-rw-r--r--src/lib/netlist/core/logic_family.h145
-rw-r--r--src/lib/netlist/core/netlist_state.h295
-rw-r--r--src/lib/netlist/core/nets.h401
-rw-r--r--src/lib/netlist/core/object_array.h251
-rw-r--r--src/lib/netlist/core/param.h367
-rw-r--r--src/lib/netlist/core/queue.h107
-rw-r--r--src/lib/netlist/core/setup.h377
-rw-r--r--src/lib/netlist/core/state_var.h214
16 files changed, 3622 insertions, 0 deletions
diff --git a/src/lib/netlist/core/analog.h b/src/lib/netlist/core/analog.h
new file mode 100644
index 00000000000..a6f15b76f58
--- /dev/null
+++ b/src/lib/netlist/core/analog.h
@@ -0,0 +1,173 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+///
+/// \file param.h
+///
+
+#ifndef NL_CORE_ANALOG_H_
+#define NL_CORE_ANALOG_H_
+
+#include "../nltypes.h"
+#include "base_objects.h"
+#include "nets.h"
+
+#include "../plib/plists.h"
+#include "../plib/pstring.h"
+
+#include <array>
+#include <utility>
+
+namespace netlist
+{
+ // -------------------------------------------------------------------------
+ // analog_t
+ // -------------------------------------------------------------------------
+
+ class analog_t : public detail::core_terminal_t
+ {
+ public:
+ analog_t(core_device_t &dev, const pstring &aname, state_e state,
+ nl_delegate delegate);
+
+ const analog_net_t &net() const noexcept
+ {
+ return plib::downcast<const analog_net_t &>(core_terminal_t::net());
+ }
+
+ analog_net_t &net() noexcept
+ {
+ return plib::downcast<analog_net_t &>(core_terminal_t::net());
+ }
+
+ solver::matrix_solver_t *solver() const noexcept;
+ };
+
+ /// \brief Base class for terminals.
+ ///
+ /// Each \ref nld_two_terminal object consists of two terminals. Terminals
+ /// are at the core of analog netlists and are connected to \ref net_t
+ /// objects.
+ ///
+ class terminal_t : public analog_t
+ {
+ public:
+ /// \brief constructor
+ ///
+ /// \param dev object owning the terminal
+ /// \param aname name of this terminal
+ /// \param other_terminal pointer to the sibling terminal
+ terminal_t(core_device_t &dev, const pstring &aname,
+ terminal_t *other_terminal, nl_delegate delegate);
+
+ terminal_t(core_device_t &dev, const pstring &aname,
+ terminal_t * other_terminal,
+ const std::array<terminal_t *, 2> &splitter_terms,
+ nl_delegate delegate);
+
+ /// \brief Returns voltage of connected net
+ ///
+ /// \return voltage of net this terminal is connected to
+ nl_fptype operator()() const noexcept { return net().Q_Analog(); }
+
+ /// \brief sets conductivity value of this terminal
+ ///
+ /// \param G Conductivity
+ void set_conductivity(nl_fptype G) const noexcept
+ {
+ set_go_gt_I(-G, G, nlconst::zero());
+ }
+
+ void set_go_gt(nl_fptype GO, nl_fptype GT) const noexcept
+ {
+ set_go_gt_I(GO, GT, nlconst::zero());
+ }
+
+ void set_go_gt_I(nl_fptype GO, nl_fptype GT,
+ nl_fptype I) const noexcept;
+
+ void set_ptrs(nl_fptype *gt, nl_fptype *go, nl_fptype *Idr) noexcept(
+ false);
+
+ private:
+ nl_fptype *m_Idr; //!< drive current
+ nl_fptype *m_go; //!< conductance for Voltage from other term
+ nl_fptype *m_gt; //!< conductance for total conductance
+ };
+
+ // -------------------------------------------------------------------------
+ // analog_input_t
+ // -------------------------------------------------------------------------
+
+ /// \brief terminal providing analog input voltage.
+ ///
+ /// This terminal class provides a voltage measurement. The conductance
+ /// against ground is infinite.
+ class analog_input_t : public analog_t
+ {
+ public:
+ /// \brief Constructor
+ analog_input_t(core_device_t &dev, //!< owning device
+ const pstring & aname, //!< name of terminal
+ nl_delegate delegate //!< delegate
+ );
+
+ /// \brief returns voltage at terminal.
+ /// \returns voltage at terminal.
+ nl_fptype operator()() const noexcept { return Q_Analog(); }
+
+ /// \brief returns voltage at terminal.
+ /// \returns voltage at terminal.
+ nl_fptype Q_Analog() const noexcept { return net().Q_Analog(); }
+ };
+
+ // -------------------------------------------------------------------------
+ // analog_output_t
+ // -------------------------------------------------------------------------
+
+ class analog_output_t : public analog_t
+ {
+ public:
+ analog_output_t(core_device_t &dev, const pstring &aname);
+
+ void push(nl_fptype val) noexcept;
+
+ void initial(nl_fptype val) noexcept;
+
+ private:
+ analog_net_t m_my_net;
+ };
+
+ // -------------------------------------------------------------------------
+ // out of class
+ // -------------------------------------------------------------------------
+
+ inline solver::matrix_solver_t *analog_t::solver() const noexcept
+ {
+ return (this->has_net() ? net().solver() : nullptr);
+ }
+
+ inline void terminal_t::set_go_gt_I(nl_fptype GO, nl_fptype GT,
+ nl_fptype I) const noexcept
+ {
+ // Check for rail nets ...
+ if (m_go != nullptr)
+ {
+ *m_Idr = I;
+ *m_go = GO;
+ *m_gt = GT;
+ }
+ }
+
+ inline void analog_output_t::push(nl_fptype val) noexcept
+ {
+ if (val != m_my_net.Q_Analog())
+ {
+ m_my_net.set_Q_Analog(val);
+ m_my_net.toggle_and_push_to_queue(netlist_time::quantum());
+ }
+ }
+
+} // namespace netlist
+
+#endif // NL_CORE_ANALOG_H_
diff --git a/src/lib/netlist/core/base_objects.h b/src/lib/netlist/core/base_objects.h
new file mode 100644
index 00000000000..b62c82e160c
--- /dev/null
+++ b/src/lib/netlist/core/base_objects.h
@@ -0,0 +1,317 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+///
+/// \file param.h
+///
+
+#ifndef NL_CORE_BASE_OBJECTS_H_
+#define NL_CORE_BASE_OBJECTS_H_
+
+#include "../nltypes.h"
+#include "netlist_state.h"
+#include "state_var.h"
+
+#include "../plib/palloc.h"
+#include "../plib/pchrono.h"
+#include "../plib/pexception.h"
+#include "../plib/plists.h"
+#include "../plib/pmempool.h"
+#include "../plib/ppmf.h"
+
+#include <unordered_map>
+
+namespace netlist
+{
+ /// \brief Delegate type for device notification.
+ ///
+ using nl_delegate = plib::pmfp<void()>;
+ using nl_delegate_ts = plib::pmfp<void(detail::time_step_type, nl_fptype)>;
+ using nl_delegate_dyn = plib::pmfp<void()>;
+} // namespace netlist
+
+namespace netlist::detail
+{
+
+ template <typename C, typename T>
+ struct property_store_t
+ {
+ using value_type = T;
+ using key_type = const C *;
+ using store_type = std::unordered_map<key_type, value_type>;
+
+ static void add(key_type obj, const value_type &value) noexcept
+ {
+ try
+ {
+ store().insert({obj, value});
+ }
+ catch (...)
+ {
+ plib::terminate("exception in property_store_t.add()");
+ }
+ }
+
+ static const value_type &get(key_type obj) noexcept
+ {
+ try
+ {
+ typename store_type::iterator ret(store().find(obj));
+ if (ret == store().end())
+ plib::terminate(
+ "object not found in property_store_t.get()");
+ return ret->second;
+ }
+ catch (...)
+ {
+ plib::terminate("exception in property_store_t.get()");
+ }
+ }
+
+ static void remove(key_type obj) noexcept
+ {
+ try
+ {
+ store().erase(store().find(obj));
+ }
+ catch (...)
+ {
+ plib::terminate("exception in property_store_t.remove()");
+ }
+ }
+
+ static store_type &store() noexcept
+ {
+ static store_type static_store;
+ return static_store;
+ }
+ };
+
+ // -------------------------------------------------------------------------
+ // object_t
+ // -------------------------------------------------------------------------
+
+ /// \brief The base class for netlist devices, terminals and parameters.
+ ///
+ /// This class serves as the base class for all device, terminal and
+ /// objects.
+
+ class object_t
+ {
+ public:
+ /// \brief Constructor.
+ /// Every class derived from the object_t class must have a name.
+ ///
+ /// \param aname string containing name of the object
+
+ explicit object_t(const pstring &aname) { props::add(this, aname); }
+
+ PCOPYASSIGNMOVE(object_t, delete)
+ /// \brief return name of the object
+ ///
+ /// \returns name of the object.
+
+ const pstring &name() const noexcept { return props::get(this); }
+
+ protected:
+ using props = property_store_t<object_t, pstring>;
+
+ // only childs should be destructible
+ ~object_t() noexcept { props::remove(this); }
+
+ private:
+ };
+
+ /// \brief Base class for all objects being owned by a netlist
+ ///
+ /// The object provides adds \ref netlist_state_t and \ref netlist_t
+ /// accessors.
+ ///
+
+ class netlist_object_t : public object_t
+ {
+ public:
+ explicit netlist_object_t(netlist_t &nl, const pstring &name)
+ : object_t(name)
+ , m_netlist(nl)
+ {
+ }
+
+ ~netlist_object_t() = default;
+
+ PCOPYASSIGNMOVE(netlist_object_t, delete)
+
+ netlist_state_t &state() noexcept;
+ const netlist_state_t &state() const noexcept;
+
+ constexpr netlist_t &exec() noexcept { return m_netlist; }
+ constexpr const netlist_t &exec() const noexcept { return m_netlist; }
+
+ // to ease template design
+ template <typename T, typename... Args>
+ device_arena::unique_ptr<T>
+ make_pool_object(Args &&...args) noexcept(false)
+ {
+ return state().make_pool_object<T>(std::forward<Args>(args)...);
+ }
+
+ private:
+ netlist_t &m_netlist;
+ };
+
+ // -------------------------------------------------------------------------
+ // device_object_t
+ // -------------------------------------------------------------------------
+
+ /// \brief Base class for all objects being owned by a device.
+ ///
+ /// Serves as the base class of all objects being owned by a device.
+ ///
+ /// The class also supports device-less objects. In this case,
+ /// nullptr is passed in as the device object.
+ ///
+
+ class device_object_t : public object_t
+ {
+ public:
+ /// \brief Constructor.
+ ///
+ /// \param dev pointer to device owning the object.
+ /// \param name string holding the name of the device
+
+ device_object_t(core_device_t *dev, const pstring &name);
+
+ /// \brief returns reference to owning device.
+ /// \returns reference to owning device.
+
+ core_device_t &device() noexcept { return *m_device; }
+ const core_device_t &device() const noexcept { return *m_device; }
+
+ /// \brief The netlist owning the owner of this object.
+ /// \returns reference to netlist object.
+
+ netlist_state_t &state() noexcept;
+ const netlist_state_t &state() const noexcept;
+
+ private:
+ core_device_t *m_device;
+ };
+
+ // -------------------------------------------------------------------------
+ // core_terminal_t
+ // -------------------------------------------------------------------------
+
+ /// \brief Base class for all terminals.
+ ///
+ /// All terminals are derived from this class.
+ ///
+ class core_terminal_t
+ : public device_object_t
+ , public plib::linked_list_t<core_terminal_t, 0>::element_t
+#if NL_USE_INPLACE_CORE_TERMS
+ , public plib::linked_list_t<core_terminal_t, 1>::element_t
+#endif
+ {
+ public:
+ /// \brief Number of signal bits
+ ///
+ /// Going forward setting this to 8 will allow 8-bit signal
+ /// buses to be used in netlist, e.g. for more complex memory
+ /// arrangements.
+ /// Minimum value is 2 here to support tristate output on proxies.
+ static constexpr const unsigned int INP_BITS = 2;
+
+ static constexpr const unsigned int INP_MASK = (1 << INP_BITS) - 1;
+ static constexpr const unsigned int INP_HL_SHIFT = 0;
+ static constexpr const unsigned int INP_LH_SHIFT = INP_BITS;
+
+ static constexpr netlist_sig_t OUT_TRISTATE() { return INP_MASK; }
+
+ static_assert(INP_BITS * 2 <= sizeof(netlist_sig_t) * 8,
+ "netlist_sig_t size not sufficient");
+
+ enum state_e
+ {
+ STATE_INP_PASSIVE = 0,
+ STATE_INP_HL = (INP_MASK << INP_HL_SHIFT),
+ STATE_INP_LH = (INP_MASK << INP_LH_SHIFT),
+ STATE_INP_ACTIVE = STATE_INP_HL | STATE_INP_LH,
+ STATE_OUT = (1 << (2 * INP_BITS)),
+ STATE_BIDIR = (1 << (2 * INP_BITS + 1))
+ };
+
+ core_terminal_t(core_device_t &dev, const pstring &aname, state_e state,
+ nl_delegate delegate);
+ virtual ~core_terminal_t() noexcept = default;
+
+ PCOPYASSIGNMOVE(core_terminal_t, delete)
+
+ /// \brief The object type.
+ /// \returns type of the object
+ terminal_type type() const noexcept(false);
+
+ /// \brief Checks if object is of specified type.
+ /// \param atype type to check object against.
+ /// \returns true if object is of specified type else false.
+ bool is_type(const terminal_type atype) const noexcept(false)
+ {
+ return (type() == atype);
+ }
+
+ void set_net(net_t *anet) noexcept { m_net = anet; }
+ void clear_net() noexcept { m_net = nullptr; }
+ constexpr bool has_net() const noexcept { return (m_net != nullptr); }
+
+ constexpr net_t &net() const noexcept { return *m_net; }
+
+ bool is_logic() const noexcept;
+ bool is_logic_input() const noexcept;
+ bool is_logic_output() const noexcept;
+ bool is_tristate_output() const noexcept;
+ bool is_analog() const noexcept;
+ bool is_analog_input() const noexcept;
+ bool is_analog_output() const noexcept;
+
+ constexpr bool is_state(state_e state) const noexcept
+ {
+ return (m_state == state);
+ }
+ constexpr state_e terminal_state() const noexcept { return m_state; }
+ constexpr void set_state(state_e state) noexcept { m_state = state; }
+
+ void reset() noexcept
+ {
+ set_state(is_type(terminal_type::OUTPUT) ? STATE_OUT
+ : STATE_INP_ACTIVE);
+ }
+
+ constexpr void
+ set_copied_input([[maybe_unused]] netlist_sig_t val) noexcept
+ {
+ if constexpr (config::use_copy_instead_of_reference::value)
+ {
+ m_Q_CIR = val;
+ }
+ }
+
+ void set_delegate(const nl_delegate &delegate) noexcept
+ {
+ m_delegate = delegate;
+ }
+ const nl_delegate &delegate() const noexcept { return m_delegate; }
+ void run_delegate() const noexcept { return m_delegate(); }
+
+ protected:
+ // std::conditional_t<config::use_copy_instead_of_reference::value,
+ // state_var_sig, void *> m_Q;
+ state_var_sig m_Q_CIR;
+
+ private:
+ nl_delegate m_delegate;
+ net_t *m_net;
+ state_var<state_e> m_state;
+ };
+
+} // namespace netlist::detail
+
+#endif // NL_CORE_BASE_OBJECTS_H_
diff --git a/src/lib/netlist/core/core_device.h b/src/lib/netlist/core/core_device.h
new file mode 100644
index 00000000000..305a5aed14e
--- /dev/null
+++ b/src/lib/netlist/core/core_device.h
@@ -0,0 +1,203 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+///
+/// \file device.h
+///
+
+#ifndef NL_CORE_DEVICE_H_
+#define NL_CORE_DEVICE_H_
+
+#include "../nltypes.h"
+#include "base_objects.h"
+
+#include "../plib/pstring.h"
+
+namespace netlist
+{
+ // -------------------------------------------------------------------------
+ // core_device_t construction parameters
+ // -------------------------------------------------------------------------
+
+ struct core_device_data_t
+ {
+ friend class core_device_t;
+ friend class base_device_t;
+ friend class analog::NETLIB_NAME(two_terminal);
+ friend class logic_family_std_proxy_t;
+
+ template <unsigned m_NI, unsigned m_NO>
+ friend class devices::factory_truth_table_t;
+
+ template <class C, typename... Args>
+ friend class factory::device_element_t;
+ friend class factory::library_element_t;
+
+ template <typename DEVICE>
+ friend struct sub_device_wrapper;
+
+ friend class solver::matrix_solver_t;
+
+ private:
+ core_device_data_t(netlist_state_t &o, const pstring &n)
+ : owner(o)
+ , name(n)
+ {
+ }
+ netlist_state_t &owner;
+ const pstring & name;
+ };
+
+ // The type use to pass data on
+ using core_device_param_t = const core_device_data_t &;
+
+ // -------------------------------------------------------------------------
+ // core_device_t
+ // -------------------------------------------------------------------------
+ // FIXME: belongs into detail namespace
+
+ class core_device_t : public detail::netlist_object_t
+ {
+ public:
+ using constructor_data_t = core_device_data_t;
+ using constructor_param_t = core_device_param_t;
+
+ core_device_t(core_device_param_t data);
+
+ core_device_t(const core_device_t &) = delete;
+ core_device_t &operator=(const core_device_t &) = delete;
+ core_device_t(core_device_t &&) noexcept = delete;
+ core_device_t &operator=(core_device_t &&) noexcept = delete;
+
+ virtual ~core_device_t() noexcept = default;
+
+ void do_inc_active() noexcept;
+
+ void do_dec_active() noexcept;
+
+ void set_hint_deactivate(bool v) noexcept { m_hint_deactivate = v; }
+ bool get_hint_deactivate() const noexcept { return m_hint_deactivate; }
+ // Has to be set in device reset
+ void set_active_outputs(int n) noexcept { m_active_outputs = n; }
+
+ // stats
+ struct stats_t
+ {
+ // NL_KEEP_STATISTICS
+ plib::pperftime_t<true> m_stat_total_time;
+ plib::pperfcount_t<true> m_stat_call_count;
+ plib::pperfcount_t<true> m_stat_inc_active;
+ };
+
+ stats_t *stats() const noexcept { return m_stats.get(); }
+
+ virtual void reset() {}
+
+ void handler_noop() {}
+
+ protected:
+ using activate_delegate = plib::pmfp<void(bool)>;
+
+ activate_delegate m_activate;
+
+ log_type &log();
+
+ public:
+ virtual void time_step([[maybe_unused]] detail::time_step_type ts_type,
+ [[maybe_unused]] nl_fptype st) noexcept
+ {
+ }
+ virtual void update_terminals() noexcept {}
+
+ virtual void update_param() noexcept {}
+ virtual bool is_dynamic() const noexcept { return false; }
+ virtual bool is_time_step() const noexcept { return false; }
+
+ private:
+ // FIXME: should this be a state_var?
+ bool m_hint_deactivate;
+ state_var_s32 m_active_outputs;
+ device_arena::unique_ptr<stats_t> m_stats;
+ };
+
+ inline void core_device_t::do_inc_active() noexcept
+ {
+ gsl_Expects(m_active_outputs >= 0);
+
+ if (!m_activate.isnull() && m_hint_deactivate)
+ {
+ if (++m_active_outputs == 1)
+ {
+ if (m_stats)
+ m_stats->m_stat_inc_active.inc();
+ m_activate(true); // inc_active();
+ }
+ }
+ }
+
+ inline void core_device_t::do_dec_active() noexcept
+ {
+ gsl_Expects(m_active_outputs >= 1);
+
+ if (!m_activate.isnull() && m_hint_deactivate)
+ if (--m_active_outputs == 0)
+ {
+ m_activate(false); // dec_active();
+ }
+ }
+
+ // -------------------------------------------------------------------------
+ // core_device_t construction parameters
+ // -------------------------------------------------------------------------
+
+ using base_device_data_t = core_device_data_t;
+ // The type use to pass data on
+ using base_device_param_t = const base_device_data_t &;
+
+ // -------------------------------------------------------------------------
+ // base_device_t
+ // -------------------------------------------------------------------------
+
+ class base_device_t : public core_device_t
+ {
+ public:
+ using constructor_data_t = base_device_data_t;
+ using constructor_param_t = base_device_param_t;
+
+ base_device_t(base_device_param_t data);
+
+ PCOPYASSIGNMOVE(base_device_t, delete)
+
+ ~base_device_t() noexcept override = default;
+
+ template <class O, class C, typename... Args>
+ void create_and_register_sub_device(O &owner, const pstring &name,
+ device_arena::unique_ptr<C> &dev, Args &&...args)
+ {
+ // dev = state().make_pool_object<C>(owner, name,
+ // std::forward<Args>(args)...);
+ using dev_constructor_data_t = typename C::constructor_data_t;
+ dev = state().make_pool_object<C>(
+ dev_constructor_data_t{state(), owner.name() + "." + name},
+ std::forward<Args>(args)...);
+ state().register_device(dev->name(),
+ device_arena::owned_ptr<core_device_t>(dev.get(), false));
+ }
+
+ void register_sub_alias(const pstring &name,
+ const detail::core_terminal_t & term);
+ void register_sub_alias(const pstring &name, const pstring &aliased);
+
+ void connect(const pstring &t1, const pstring &t2);
+ void connect(const detail::core_terminal_t &t1,
+ const detail::core_terminal_t & t2);
+
+ protected:
+ // NETLIB_UPDATE_TERMINALSI() { }
+
+ private:
+ };
+
+} // namespace netlist
+
+#endif // NL_CORE_DEVICE_H_
diff --git a/src/lib/netlist/core/device.h b/src/lib/netlist/core/device.h
new file mode 100644
index 00000000000..12f3f7d174e
--- /dev/null
+++ b/src/lib/netlist/core/device.h
@@ -0,0 +1,116 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+///
+/// \file device.h
+///
+
+#ifndef NL_DEVICE_H_
+#define NL_DEVICE_H_
+
+#include "core_device.h"
+#include "logic_family.h"
+#include "param.h"
+
+namespace netlist
+{
+
+ // -------------------------------------------------------------------------
+ // device_t construction parameters
+ // -------------------------------------------------------------------------
+
+ using device_data_t = base_device_data_t;
+ // The type use to pass data on
+ using device_param_t = const device_data_t &;
+
+ // -------------------------------------------------------------------------
+ // device_t
+ // -------------------------------------------------------------------------
+
+ class device_t : public base_device_t, public logic_family_t
+ {
+ public:
+ using constructor_data_t = device_data_t;
+ using constructor_param_t = device_param_t;
+
+ device_t(device_param_t data);
+
+ device_t(device_param_t data, const pstring &model);
+ // only needed by proxies
+ device_t(device_param_t data, const logic_family_desc_t *desc);
+
+ device_t(const device_t &) = delete;
+ device_t &operator=(const device_t &) = delete;
+ device_t(device_t &&) noexcept = delete;
+ device_t &operator=(device_t &&) noexcept = delete;
+
+ ~device_t() noexcept override = default;
+
+ protected:
+ template <typename T1, typename T2>
+ void push_two(T1 &term1, netlist_sig_t newQ1,
+ const netlist_time &delay1, T2 &term2, netlist_sig_t newQ2,
+ const netlist_time &delay2) noexcept
+ {
+ if (delay2 < delay1)
+ {
+ term1.push(newQ1, delay1);
+ term2.push(newQ2, delay2);
+ }
+ else
+ {
+ term2.push(newQ2, delay2);
+ term1.push(newQ1, delay1);
+ }
+ }
+
+ // NETLIB_UPDATE_TERMINALSI() { }
+ private:
+ param_model_t m_model;
+ };
+
+ // -------------------------------------------------------------------------
+ // FIXME: Rename
+ // -------------------------------------------------------------------------
+
+ template <typename DEVICE>
+ struct sub_device_wrapper
+ {
+ using constructor_data_t = typename DEVICE::constructor_data_t;
+ using constructor_param_t = typename DEVICE::constructor_param_t;
+
+ template <typename... Args>
+ sub_device_wrapper(base_device_t &owner, const pstring &name,
+ Args &&...args)
+ {
+ // m_dev = owner.state().make_pool_object<DEVICE>(owner, name,
+ // std::forward<Args>(args)...);
+ m_dev = owner.state().make_pool_object<DEVICE>(
+ constructor_data_t{owner.state(), owner.name() + "." + name},
+ std::forward<Args>(args)...);
+ owner.state().register_device(
+ m_dev->name(),
+ device_arena::owned_ptr<core_device_t>(m_dev.get(), false));
+ }
+ template <typename... Args>
+ sub_device_wrapper(device_t &owner, const pstring &name, Args &&...args)
+ {
+ // m_dev = owner.state().make_pool_object<DEVICE>(owner, name,
+ // std::forward<Args>(args)...);
+ m_dev = owner.state().make_pool_object<DEVICE>(
+ constructor_data_t{owner.state(), owner.name() + "." + name},
+ std::forward<Args>(args)...);
+ owner.state().register_device(
+ m_dev->name(),
+ device_arena::owned_ptr<core_device_t>(m_dev.get(), false));
+ }
+ DEVICE & operator()() { return *m_dev; }
+ const DEVICE &operator()() const { return *m_dev; }
+
+ private:
+ device_arena::unique_ptr<DEVICE> m_dev;
+ };
+
+} // namespace netlist
+
+#endif // NL_DEVICE_H_
diff --git a/src/lib/netlist/core/device_macros.h b/src/lib/netlist/core/device_macros.h
new file mode 100644
index 00000000000..8f46f036412
--- /dev/null
+++ b/src/lib/netlist/core/device_macros.h
@@ -0,0 +1,154 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#ifndef NL_CORE_DEVICE_MACROS_H_
+#define NL_CORE_DEVICE_MACROS_H_
+
+///
+/// \file device_macros.h
+///
+
+// -----------------------------------------------------------------------------
+// MACROS / New Syntax
+// -----------------------------------------------------------------------------
+
+/// \brief Start a netlist device class.
+///
+/// Used to start defining a netlist device class.
+/// The simplest device without inputs or outputs would look like this:
+///
+/// NETLIB_OBJECT(some_object)
+/// {
+/// public:
+/// NETLIB_CONSTRUCTOR(some_object) { }
+/// };
+///
+/// Also refer to #NETLIB_CONSTRUCTOR.
+#define NETLIB_OBJECT(name) \
+ class NETLIB_NAME(name) \
+ : public device_t
+
+/// \brief Used to define the constructor of a netlist device.
+///
+/// Use this to define the constructor of a netlist device. Please refer to
+/// #NETLIB_OBJECT for an example.
+#define NETLIB_CONSTRUCTOR(cname) \
+public: \
+ NETLIB_NAME(cname)(constructor_param_t data) \
+ : device_t(data)
+
+/// \brief Used to define the constructor of a netlist device and define a
+/// default model.
+///
+///
+/// NETLIB_CONSTRUCTOR_MODEL(some_object, "TTL")
+/// {
+/// public:
+/// NETLIB_CONSTRUCTOR(some_object) { }
+/// };
+///
+#define NETLIB_CONSTRUCTOR_MODEL(cname, cmodel) \
+public: \
+ NETLIB_NAME(cname)(constructor_param_t data) \
+ : device_t(data, cmodel)
+
+/// \brief Used to define the destructor of a netlist device.
+/// The use of a destructor for netlist device should normally not be necessary.
+#define NETLIB_DESTRUCTOR(name) \
+public: \
+ virtual ~NETLIB_NAME(name)() noexcept override
+
+/// \brief Add this to a device definition to mark the device as dynamic.
+///
+/// If NETLIB_IS_DYNAMIC(true) is added to the device definition the device
+/// is treated as an analog dynamic device, i.e. \ref NETLIB_UPDATE_TERMINALSI
+/// is called on a each step of the Newton-Raphson step
+/// of solving the linear equations.
+///
+/// You may also use e.g. NETLIB_IS_DYNAMIC(m_func() != "") to only make the
+/// device a dynamic device if parameter m_func is set.
+///
+/// \param expr boolean expression
+///
+#define NETLIB_IS_DYNAMIC(expr) \
+public: \
+ virtual bool is_dynamic() const noexcept override { return expr; }
+
+/// \brief Add this to a device definition to mark the device as a time-stepping
+/// device.
+///
+/// You have to implement NETLIB_TIMESTEP in this case as well. Currently, only
+/// the capacitor and inductor devices uses this.
+///
+/// You may also use e.g. NETLIB_IS_TIMESTEP(m_func() != "") to only make the
+/// device a dynamic device if parameter m_func is set. This is used by the
+/// Voltage Source element.
+///
+/// Example:
+///
+/// \code
+/// NETLIB_TIMESTEP_IS_TIMESTEP()
+/// NETLIB_TIMESTEPI()
+/// {
+/// // Gpar should support convergence
+/// const nl_fptype G = m_C.Value() / step + m_GParallel;
+/// const nl_fptype I = -G/// deltaV();
+/// set(G, 0.0, I);
+/// }
+/// \endcode
+
+#define NETLIB_IS_TIMESTEP(expr) \
+public: \
+ virtual bool is_time_step() const noexcept override { return expr; }
+
+/// \brief Used to implement the time stepping code.
+///
+/// Please see \ref NETLIB_IS_TIMESTEP for an example.
+
+#define NETLIB_TIMESTEPI() \
+public: \
+ virtual void time_step(detail::time_step_type ts_type, \
+ nl_fptype step) noexcept override
+
+/// \brief Used to implement the body of the time stepping code.
+///
+/// Used when the implementation is outside the class definition
+///
+/// Please see \ref NETLIB_IS_TIMESTEP for an example.
+///
+/// \param cname Name of object as given to \ref NETLIB_OBJECT
+///
+#define NETLIB_TIMESTEP(cname) \
+ void NETLIB_NAME(cname)::time_step(detail::time_step_type ts_type, \
+ nl_fptype step) noexcept
+
+//#define NETLIB_DELEGATE(name) nl_delegate(&this_type :: name, this)
+#define NETLIB_DELEGATE(name) \
+ nl_delegate(&std::remove_pointer_t<decltype(this)>::name, this)
+
+#define NETLIB_DELEGATE_NOOP() \
+ nl_delegate(&core_device_t::handler_noop, \
+ static_cast<core_device_t *>(this))
+
+#define NETLIB_UPDATE_TERMINALSI() \
+ virtual void update_terminals() noexcept override
+#define NETLIB_HANDLERI(name) void name() noexcept
+#define NETLIB_UPDATE_PARAMI() virtual void update_param() noexcept override
+#define NETLIB_RESETI() virtual void reset() override
+
+#define NETLIB_SUB(chip) sub_device_wrapper<nld_##chip>
+#define NETLIB_SUB_NS(ns, chip) sub_device_wrapper<ns ::nld_##chip>
+
+#define NETLIB_SUB_UPTR(ns, chip) device_arena::unique_ptr<ns ::nld_##chip>
+
+#define NETLIB_HANDLER(chip, name) void NETLIB_NAME(chip)::name() noexcept
+
+#define NETLIB_RESET(chip) void NETLIB_NAME(chip)::reset(void)
+
+#define NETLIB_UPDATE_PARAM(chip) \
+ void NETLIB_NAME(chip)::update_param() noexcept
+
+#define NETLIB_UPDATE_TERMINALS(chip) \
+ void NETLIB_NAME(chip)::update_terminals() noexcept
+
+#endif // NL_CORE_DEVICE_MACROS_H_
diff --git a/src/lib/netlist/core/devices.h b/src/lib/netlist/core/devices.h
new file mode 100644
index 00000000000..29667723bf5
--- /dev/null
+++ b/src/lib/netlist/core/devices.h
@@ -0,0 +1,166 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+#ifndef NL_CORE_DEVICES_H_
+#define NL_CORE_DEVICES_H_
+
+///
+/// \file devices.h
+///
+/// The core is accessing members or type definitions of devices defined
+/// here directly (e.g. nld_nc_pin).
+///
+
+#include "analog.h"
+#include "device.h"
+#include "device_macros.h"
+#include "logic.h"
+#include "param.h"
+
+//============================================================
+// Namespace starts
+//============================================================
+
+namespace netlist::devices
+{
+ // -----------------------------------------------------------------------------
+ // main clock
+ // -----------------------------------------------------------------------------
+
+ NETLIB_OBJECT(mainclock)
+ {
+ NETLIB_CONSTRUCTOR(mainclock)
+ , m_Q(*this, "Q"), m_freq(*this, "FREQ", nlconst::magic(7159000.0 * 5))
+ {
+ m_inc = netlist_time::from_fp(
+ plib::reciprocal(m_freq() * nlconst::two()));
+ }
+
+ NETLIB_RESETI() { m_Q.net().set_next_scheduled_time(exec().time()); }
+
+ NETLIB_UPDATE_PARAMI()
+ {
+ m_inc = netlist_time::from_fp(
+ plib::reciprocal(m_freq() * nlconst::two()));
+ }
+
+ public:
+ logic_output_t m_Q; // NOLINT: needed in core
+ netlist_time m_inc; // NOLINT: needed in core
+ private:
+ param_fp_t m_freq;
+ };
+
+ // -----------------------------------------------------------------------------
+ // power pins - not a device, but a helper
+ // -----------------------------------------------------------------------------
+
+ /// \brief Power pins class.
+ ///
+ /// Power Pins are passive inputs. Delegate noop will silently ignore any
+ /// updates.
+
+ class nld_power_pins
+ {
+ public:
+ using constructor_type = nld_power_pins;
+
+ explicit nld_power_pins(device_t &owner)
+ : m_VCC(owner, owner.logic_family()->vcc_pin(), NETLIB_DELEGATE(noop))
+ , m_GND(owner, owner.logic_family()->gnd_pin(), NETLIB_DELEGATE(noop))
+ {
+ }
+
+ explicit nld_power_pins(device_t &owner, nl_delegate delegate)
+ : m_VCC(owner, owner.logic_family()->vcc_pin(), delegate)
+ , m_GND(owner, owner.logic_family()->gnd_pin(), delegate)
+ {
+ }
+
+ // Some devices like the 74LS629 have two pairs of supply pins.
+ explicit nld_power_pins(device_t &owner, const pstring &vcc,
+ const pstring &gnd)
+ : m_VCC(owner, vcc, NETLIB_DELEGATE(noop))
+ , m_GND(owner, gnd, NETLIB_DELEGATE(noop))
+ {
+ }
+
+ // Some devices like the 74LS629 have two pairs of supply pins.
+ explicit nld_power_pins(device_t &owner, const pstring &vcc,
+ const pstring &gnd, nl_delegate delegate)
+ : m_VCC(owner, vcc, delegate)
+ , m_GND(owner, gnd, delegate)
+ {
+ }
+
+ const analog_input_t &VCC() const noexcept { return m_VCC; }
+ const analog_input_t &GND() const noexcept { return m_GND; }
+
+ private:
+ void noop() {}
+ analog_input_t m_VCC;
+ analog_input_t m_GND;
+ };
+
+ // -----------------------------------------------------------------------------
+ // netlist parameters
+ // -----------------------------------------------------------------------------
+
+ NETLIB_OBJECT(netlistparams)
+ {
+ NETLIB_CONSTRUCTOR(netlistparams)
+ , m_use_deactivate(*this, "USE_DEACTIVATE", false)
+ , m_startup_strategy(*this, "STARTUP_STRATEGY", 0)
+ , m_mos_cap_model(*this, "DEFAULT_MOS_CAPMODEL", 2)
+ , m_max_link_loops(*this, "MAX_LINK_RESOLVE_LOOPS", 100)
+ {
+ }
+ // NETLIB_RESETI() {}
+ // NETLIB_UPDATE_PARAMI() { }
+ public:
+ param_logic_t m_use_deactivate;
+ param_num_t<unsigned> m_startup_strategy;
+ param_num_t<unsigned> m_mos_cap_model;
+ //! How many times do we try to resolve links (connections)
+ param_num_t<unsigned> m_max_link_loops;
+ };
+
+ // -----------------------------------------------------------------------------
+ // nld_nc_pin
+ //
+ // FIXME: This needs to optimized
+ // The input can be in de-activated state.
+ // -----------------------------------------------------------------------------
+
+ NETLIB_OBJECT(nc_pin)
+ {
+ public:
+ NETLIB_CONSTRUCTOR(nc_pin)
+ , m_I(*this, "I", NETLIB_DELEGATE_NOOP()) {}
+
+ protected:
+ // NETLIB_RESETI() {}
+
+ private:
+ analog_input_t m_I;
+ };
+
+ // -----------------------------------------------------------------------------
+ // nld_gnd
+ // -----------------------------------------------------------------------------
+
+ NETLIB_OBJECT(gnd)
+ {
+ NETLIB_CONSTRUCTOR(gnd)
+ , m_Q(*this, "Q") {}
+
+ NETLIB_UPDATE_PARAMI() { m_Q.push(nlconst::zero()); }
+
+ // NETLIB_RESETI() {}
+ protected:
+ analog_output_t m_Q;
+ };
+
+} // namespace netlist::devices
+
+#endif // NL_CORE_DEVICES_H_
diff --git a/src/lib/netlist/core/exec.h b/src/lib/netlist/core/exec.h
new file mode 100644
index 00000000000..091d7181e7d
--- /dev/null
+++ b/src/lib/netlist/core/exec.h
@@ -0,0 +1,124 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+///
+/// \file exec.h
+///
+
+#ifndef NL_CORE_EXEC_H_
+#define NL_CORE_EXEC_H_
+
+#include "../nltypes.h"
+#include "base_objects.h"
+#include "state_var.h"
+
+#include "../plib/plists.h"
+#include "../plib/pstring.h"
+
+namespace netlist
+{
+ // -------------------------------------------------------------------------
+ // netlist_t
+ // -------------------------------------------------------------------------
+
+ class netlist_t // NOLINT(clang-analyzer-optin.performance.Padding)
+ {
+ public:
+ explicit netlist_t(netlist_state_t &state, const pstring &aname);
+
+ netlist_t(const netlist_t &) = delete;
+ netlist_t &operator=(const netlist_t &) = delete;
+ netlist_t(netlist_t &&) noexcept = delete;
+ netlist_t &operator=(netlist_t &&) noexcept = delete;
+
+ virtual ~netlist_t() noexcept = default;
+
+ // run functions
+
+ constexpr const netlist_time_ext &time() const noexcept
+ {
+ return m_time;
+ }
+
+ void process_queue(netlist_time_ext delta) noexcept;
+ void abort_current_queue_slice() noexcept
+ {
+ queue_remove(nullptr);
+ queue_push(m_time, nullptr);
+ }
+
+ constexpr const detail::queue_t &queue() const noexcept
+ {
+ return m_queue;
+ }
+
+ template <typename... Args>
+ void queue_push(Args &&...args) noexcept
+ {
+ if (config::use_queue_stats::value && m_use_stats)
+ m_queue.emplace<true>(std::forward<Args>(
+ args)...); // NOLINT(performance-move-const-arg)
+ else
+ m_queue.emplace<false>(std::forward<Args>(
+ args)...); // NOLINT(performance-move-const-arg)
+ }
+
+ template <class R>
+ void queue_remove(R &&elem) noexcept
+ {
+ if (config::use_queue_stats::value && m_use_stats)
+ m_queue.remove<true>(std::forward<R>(elem));
+ else
+ m_queue.remove<false>(std::forward<R>(elem));
+ }
+
+ // Control functions
+
+ void stop();
+ void reset();
+
+ // only used by nltool to create static c-code
+ devices::nld_solver *solver() const noexcept { return m_solver; }
+
+ // FIXME: force late type resolution
+ template <typename X = devices::nld_solver>
+ nl_fptype gmin([[maybe_unused]] X *solver = nullptr) const noexcept
+ {
+ return static_cast<X *>(m_solver)->gmin();
+ }
+
+ netlist_state_t & nl_state() noexcept { return m_state; }
+ const netlist_state_t &nl_state() const noexcept { return m_state; }
+
+ log_type & log() noexcept { return m_state.log(); }
+ const log_type &log() const noexcept { return m_state.log(); }
+
+ void print_stats() const;
+
+ constexpr bool stats_enabled() const noexcept { return m_use_stats; }
+ void enable_stats(bool val) noexcept { m_use_stats = val; }
+
+ private:
+ template <bool KEEP_STATS>
+ void process_queue_stats(netlist_time_ext delta) noexcept;
+
+ netlist_state_t & m_state;
+ devices::nld_solver *m_solver;
+
+ // mostly rw
+ // PALIGNAS(16)
+ netlist_time_ext m_time;
+ devices::nld_mainclock *m_main_clock;
+
+ // PALIGNAS_CACHELINE()
+ // PALIGNAS(16)
+ bool m_use_stats;
+ detail::queue_t m_queue;
+ // performance
+ plib::pperftime_t<true> m_stat_mainloop;
+ plib::pperfcount_t<true> m_perf_out_processed;
+ };
+
+} // namespace netlist
+
+#endif // NL_CORE_EXEC_H_
diff --git a/src/lib/netlist/core/logic.h b/src/lib/netlist/core/logic.h
new file mode 100644
index 00000000000..ef567398c84
--- /dev/null
+++ b/src/lib/netlist/core/logic.h
@@ -0,0 +1,212 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+///
+/// \file logic.h
+///
+
+#ifndef NL_CORE_LOGIC_H_
+#define NL_CORE_LOGIC_H_
+
+#include "../nltypes.h"
+#include "base_objects.h"
+#include "logic_family.h"
+#include "nets.h"
+#include "state_var.h"
+
+#include "../plib/plists.h"
+#include "../plib/pstring.h"
+
+#include <array>
+#include <utility>
+
+namespace netlist
+{
+ // -------------------------------------------------------------------------
+ // logic_t
+ // -------------------------------------------------------------------------
+
+ class logic_t
+ : public detail::core_terminal_t
+ , public logic_family_t
+ {
+ public:
+ logic_t(device_t &dev, const pstring &aname, state_e terminal_state,
+ nl_delegate delegate);
+
+ logic_net_t &net() noexcept
+ {
+ return plib::downcast<logic_net_t &>(core_terminal_t::net());
+ }
+ constexpr const logic_net_t &net() const noexcept
+ {
+ return plib::downcast<const logic_net_t &>(core_terminal_t::net());
+ }
+ };
+
+ // -------------------------------------------------------------------------
+ // logic_input_t
+ // -------------------------------------------------------------------------
+
+ class logic_input_t : public logic_t
+ {
+ public:
+ logic_input_t(device_t &dev, const pstring &aname,
+ nl_delegate delegate);
+
+ // const netlist_sig_t &operator()() const noexcept
+ constexpr netlist_sig_t operator()() const noexcept
+ {
+ gsl_Expects(terminal_state() != STATE_INP_PASSIVE);
+ if constexpr (config::use_copy_instead_of_reference::value)
+ return m_Q_CIR;
+ else
+ return net().Q();
+ }
+
+ void inactivate() noexcept
+ {
+ if (!is_state(STATE_INP_PASSIVE))
+ {
+ set_state(STATE_INP_PASSIVE);
+ net().remove_from_active_list(*this);
+ }
+ }
+
+ void activate() noexcept
+ {
+ if (is_state(STATE_INP_PASSIVE))
+ {
+ net().add_to_active_list(*this);
+ set_state(STATE_INP_ACTIVE);
+ }
+ }
+
+ void activate_hl() noexcept
+ {
+ if (is_state(STATE_INP_PASSIVE))
+ {
+ net().add_to_active_list(*this);
+ set_state(STATE_INP_HL);
+ }
+ }
+
+ void activate_lh() noexcept
+ {
+ if (is_state(STATE_INP_PASSIVE))
+ {
+ net().add_to_active_list(*this);
+ set_state(STATE_INP_LH);
+ }
+ }
+ };
+
+ // -------------------------------------------------------------------------
+ // logic_output_t
+ // -------------------------------------------------------------------------
+
+ class logic_output_t : public logic_t
+ {
+ public:
+ /// \brief logic output constructor
+ ///
+ /// The third parameter does nothing. It is provided only for
+ /// compatibility with tristate_output_t in templatized device models
+ ///
+ /// \param dev Device owning this output
+ /// \param aname The name of this output
+ /// \param dummy Dummy parameter to allow construction like tristate
+ /// output
+ ///
+ logic_output_t(device_t &dev, const pstring &aname, bool dummy = false);
+
+ void initial(netlist_sig_t val) noexcept;
+
+ void push(netlist_sig_t newQ, const netlist_time &delay) noexcept
+ {
+ gsl_Expects(delay >= netlist_time::zero());
+
+ m_my_net.set_Q_and_push(newQ, delay); // take the shortcut
+ }
+
+ void set_Q_time(netlist_sig_t newQ, const netlist_time_ext &at) noexcept
+ {
+ m_my_net.set_Q_time(newQ, at); // take the shortcut
+ }
+
+ /// \brief Dummy implementation for templatized generic devices
+ ///
+ /// This function shall never be called. It is defined here so that
+ /// templatized generic device models do not have to do tons of
+ /// template magic.
+ ///
+ /// This function terminates if actually called.
+ ///
+ [[noreturn]] static void set_tristate([[maybe_unused]] netlist_sig_t v,
+ [[maybe_unused]] netlist_time ts_off_on,
+ [[maybe_unused]] netlist_time ts_on_off)
+ {
+ plib::terminate(
+ "set_tristate on logic_output should never be called!");
+ }
+
+ private:
+ logic_net_t m_my_net;
+ };
+
+ // -------------------------------------------------------------------------
+ // tristate_output_t
+ // -------------------------------------------------------------------------
+
+ /// \brief Tristate output
+ ///
+ /// In a lot of applications tristate enable inputs are just connected to
+ /// VCC/GND to permanently enable the outputs. In this case a pure
+ /// implementation using analog outputs would not perform well.
+ ///
+ /// For this object during creation it can be decided if a logic output or
+ /// a tristate output is used. Generally the owning device uses parameter
+ /// FORCE_TRISTATE_LOGIC to determine this.
+ ///
+ /// This is the preferred way to implement tristate outputs.
+ ///
+
+ class tristate_output_t : public logic_output_t
+ {
+ public:
+ tristate_output_t(device_t &dev, const pstring &aname,
+ bool force_logic);
+
+ void push(netlist_sig_t newQ, netlist_time delay) noexcept
+ {
+ if (!m_tristate)
+ logic_output_t::push(newQ, delay);
+ m_last_logic = newQ;
+ }
+
+ void set_tristate(netlist_sig_t v, netlist_time ts_off_on,
+ netlist_time ts_on_off) noexcept
+ {
+ if (!m_force_logic)
+ if (v != m_tristate)
+ {
+ logic_output_t::push((v != 0) ? OUT_TRISTATE()
+ : m_last_logic,
+ v ? ts_off_on : ts_on_off);
+ m_tristate = v;
+ }
+ }
+
+ bool is_force_logic() const noexcept { return m_force_logic; }
+
+ private:
+ using logic_output_t::initial;
+ using logic_output_t::set_Q_time;
+ state_var<netlist_sig_t> m_last_logic;
+ state_var<netlist_sig_t> m_tristate;
+ bool m_force_logic;
+ };
+
+} // namespace netlist
+
+#endif // NL_CORE_LOGIC_H_
diff --git a/src/lib/netlist/core/logic_family.h b/src/lib/netlist/core/logic_family.h
new file mode 100644
index 00000000000..216e74ead2a
--- /dev/null
+++ b/src/lib/netlist/core/logic_family.h
@@ -0,0 +1,145 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+///
+/// \file logic_family.h
+///
+
+#ifndef NL_CORE_LOGIC_FAMILY_H_
+#define NL_CORE_LOGIC_FAMILY_H_
+
+#include "../nltypes.h"
+
+#include "../plib/palloc.h"
+#include "../plib/pmempool.h"
+#include "../plib/pstring.h"
+
+namespace netlist
+{
+ /// \brief Logic families descriptors are used to create proxy devices.
+ /// The logic family describes the analog capabilities of logic devices,
+ /// inputs and outputs.
+
+ class logic_family_desc_t
+ {
+ public:
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init,
+ // modernize-use-equals-default)
+ logic_family_desc_t() = default;
+
+ logic_family_desc_t(const logic_family_desc_t &) = delete;
+ logic_family_desc_t &operator=(const logic_family_desc_t &) = delete;
+
+ // FOXME: Should be move constructible
+ logic_family_desc_t(logic_family_desc_t &&) noexcept = delete;
+ logic_family_desc_t &operator=(
+ logic_family_desc_t &&) noexcept = delete;
+
+ virtual ~logic_family_desc_t() noexcept = default;
+
+ virtual device_arena::unique_ptr<devices::nld_base_d_to_a_proxy>
+ create_d_a_proxy(netlist_state_t &anetlist, const pstring &name,
+ const logic_output_t *proxied) const = 0;
+ virtual device_arena::unique_ptr<devices::nld_base_a_to_d_proxy>
+ create_a_d_proxy(netlist_state_t &anetlist, const pstring &name,
+ const logic_input_t *proxied) const = 0;
+
+ nl_fptype low_threshold_V(nl_fptype VN, nl_fptype VP) const noexcept
+ {
+ return VN + (VP - VN) * m_low_threshold_PCNT;
+ }
+ nl_fptype high_threshold_V(nl_fptype VN, nl_fptype VP) const noexcept
+ {
+ return VN + (VP - VN) * m_high_threshold_PCNT;
+ }
+ nl_fptype low_offset_V() const noexcept { return m_low_VO; }
+ nl_fptype high_offset_V() const noexcept { return m_high_VO; }
+ nl_fptype R_low() const noexcept { return m_R_low; }
+ nl_fptype R_high() const noexcept { return m_R_high; }
+
+ bool is_above_high_threshold_V(nl_fptype V, nl_fptype VN,
+ nl_fptype VP) const noexcept
+ {
+ return V > high_threshold_V(VN, VP);
+ }
+
+ bool is_below_low_threshold_V(nl_fptype V, nl_fptype VN,
+ nl_fptype VP) const noexcept
+ {
+ return V < low_threshold_V(VN, VP);
+ }
+
+ pstring vcc_pin() const { return pstring(m_vcc); }
+ pstring gnd_pin() const { return pstring(m_gnd); }
+
+ nl_fptype m_low_threshold_PCNT; //!< low input threshold offset. If the
+ //!< input voltage is below this value
+ //!< times supply voltage, a "0" input
+ //!< is signalled
+ nl_fptype m_high_threshold_PCNT; //!< high input threshold offset. If
+ //!< the input voltage is above the
+ //!< value times supply voltage, a "0"
+ //!< input is signalled
+ nl_fptype m_low_VO; //!< low output voltage offset. This voltage is
+ //!< output if the ouput is "0"
+ nl_fptype m_high_VO; //!< high output voltage offset. The supply voltage
+ //!< minus this offset is output if the ouput is
+ //!< "1"
+ nl_fptype m_R_low; //!< low output resistance. Value of series resistor
+ //!< used for low output
+ nl_fptype m_R_high; //!< high output resistance. Value of series
+ //!< resistor used for high output
+ const char *m_vcc; //!< default power pin name for positive supply
+ const char *m_gnd; //!< default power pin name for negative supply
+ };
+
+ /// \brief Base class for devices, terminals, outputs and inputs which
+ /// support
+ /// logic families.
+ /// This class is a storage container to store the logic family for a
+ /// netlist object. You will not directly use it. Please refer to
+ /// \ref NETLIB_FAMILY to learn how to define a logic family for a device.
+ ///
+ /// All terminals inherit the family description from the device
+ /// The default is the ttl family, but any device can override the family.
+ /// For individual terminals, the family can be overwritten as well.
+ ///
+
+ class logic_family_t
+ {
+ public:
+ logic_family_t()
+ : m_logic_family(nullptr)
+ {
+ }
+ logic_family_t(const logic_family_desc_t *d)
+ : m_logic_family(d)
+ {
+ }
+
+ logic_family_t(const logic_family_t &) = delete;
+ logic_family_t &operator=(const logic_family_t &) = delete;
+
+ // FIXME: logic family can be move constructible.
+ logic_family_t(logic_family_t &&) noexcept = delete;
+ logic_family_t &operator=(logic_family_t &&) noexcept = delete;
+
+ const logic_family_desc_t *logic_family() const noexcept
+ {
+ return m_logic_family;
+ }
+ void set_logic_family(const logic_family_desc_t *fam) noexcept
+ {
+ m_logic_family = fam;
+ }
+
+ protected:
+ ~logic_family_t() noexcept = default; // prohibit polymorphic
+ // destruction
+ private:
+ const logic_family_desc_t *m_logic_family;
+ };
+
+} // namespace netlist
+
+#endif // NL_CORE_LOGIC_FAMILY_H_
diff --git a/src/lib/netlist/core/netlist_state.h b/src/lib/netlist/core/netlist_state.h
new file mode 100644
index 00000000000..505665d9acf
--- /dev/null
+++ b/src/lib/netlist/core/netlist_state.h
@@ -0,0 +1,295 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+///
+/// \file netlist_state.h
+///
+
+#ifndef NL_CORE_NETLIST_STATE_H_
+#define NL_CORE_NETLIST_STATE_H_
+
+#include "../nltypes.h"
+#include "queue.h"
+
+#include "../plib/plists.h"
+#include "../plib/pmempool.h"
+#include "../plib/pstate.h"
+#include "../plib/pstring.h"
+
+#include <array>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+namespace netlist
+{
+ // -----------------------------------------------------------------------------
+ // netlist_state__t
+ // -----------------------------------------------------------------------------
+
+ class netlist_state_t
+ {
+ public:
+ using nets_collection_type = std::vector<
+ device_arena::owned_ptr<detail::net_t>>;
+ using family_collection_type = std::unordered_map<
+ pstring, host_arena::unique_ptr<logic_family_desc_t>>;
+
+ // need to preserve order of device creation ...
+ using devices_collection_type = std::vector<
+ std::pair<pstring, device_arena::owned_ptr<core_device_t>>>;
+
+ netlist_state_t(const pstring &name, plib::plog_delegate logger);
+
+ PCOPYASSIGNMOVE(netlist_state_t, delete)
+
+ /// \brief Destructor
+ ///
+ /// The destructor is virtual to allow implementation specific devices
+ /// to connect to the outside world. For examples see MAME netlist.cpp.
+ ///
+ virtual ~netlist_state_t() noexcept = default;
+
+ template <class C>
+ static bool check_class(core_device_t *p) noexcept
+ {
+ return bool(plib::dynamic_downcast<C *>(p));
+ }
+
+ core_device_t *
+ get_single_device(const pstring &classname,
+ bool (*cc)(core_device_t *)) const noexcept(false);
+
+ /// \brief Get single device filtered by class and name
+ ///
+ /// \tparam C Device class for which devices will be returned
+ /// \param name Name of the device
+ ///
+ /// \return pointers to device
+
+ template <class C>
+ C *get_single_device(const pstring &name) const
+ {
+ return dynamic_cast<C *>(get_single_device(name, check_class<C>));
+ }
+
+ /// \brief Get vector of devices
+ ///
+ /// \tparam C Device class for which devices will be returned
+ ///
+ /// \return vector with pointers to devices
+
+ template <class C>
+ std::vector<C *> get_device_list() const
+ {
+ std::vector<C *> tmp;
+ for (const auto &d : m_devices)
+ {
+ if (auto dev = plib::dynamic_downcast<C *>(d.second.get()))
+ tmp.push_back(*dev);
+ }
+ return tmp;
+ }
+
+ // logging
+
+ log_type &log() noexcept { return m_log; }
+ const log_type &log() const noexcept { return m_log; }
+
+ plib::dynamic_library_base &static_solver_lib() const noexcept
+ {
+ return *m_lib;
+ }
+
+ /// \brief provide library with static solver implementations.
+ ///
+ /// By default no static solvers are provided since these are
+ /// determined by the specific use case. You can pass such a collection
+ /// of symbols with this method.
+ ///
+ void set_static_solver_lib(
+ std::unique_ptr<plib::dynamic_library_base> &&lib);
+
+ netlist_t &exec() noexcept { return *m_netlist; }
+ const netlist_t &exec() const noexcept { return *m_netlist; }
+
+ // state handling
+ plib::state_manager_t &run_state_manager() noexcept { return m_state; }
+
+ template <typename O, typename C>
+ void
+ save(O &owner, C &state, const pstring &module, const pstring &stname)
+ {
+ this->run_state_manager().save_item(plib::void_ptr_cast(&owner),
+ state, module + "." + stname);
+ }
+
+ template <typename O, typename C>
+ void save(O &owner, C *state, const pstring &module,
+ const pstring &stname, const std::size_t count)
+ {
+ this->run_state_manager().save_state_ptr(
+ plib::void_ptr_cast(&owner), module + "." + stname,
+ plib::state_manager_t::dtype<C>(), count, state);
+ }
+
+ // FIXME: only used by queue_t save state
+ std::size_t find_net_id(const detail::net_t *net) const;
+ detail::net_t *net_by_id(std::size_t id) const;
+
+ template <typename T>
+ void register_net(device_arena::owned_ptr<T> &&net)
+ {
+ m_nets.push_back(std::move(net));
+ }
+
+ /// \brief Get device pointer by name
+ ///
+ ///
+ /// \param name Name of the device
+ ///
+ /// \return core_device_t pointer if device exists, else nullptr
+
+ core_device_t *find_device(const pstring &name) const
+ {
+ for (const auto &d : m_devices)
+ if (d.first == name)
+ return d.second.get();
+ return nullptr;
+ }
+
+ /// \brief Register device using owned_ptr
+ ///
+ /// Used to register owned devices. These are devices declared as
+ /// objects in another devices.
+ ///
+ /// \param name Name of the device
+ /// \param dev Device to be registered
+
+ template <typename T>
+ void register_device(const pstring &name,
+ device_arena::owned_ptr<T> &&dev) noexcept(false)
+ {
+ for (auto &d : m_devices)
+ if (d.first == name)
+ {
+ dev.release();
+ log().fatal(MF_DUPLICATE_NAME_DEVICE_LIST(name));
+ throw nl_exception(MF_DUPLICATE_NAME_DEVICE_LIST(name));
+ }
+ // m_devices.push_back(std::move(dev));
+ m_devices.insert(m_devices.end(), {name, std::move(dev)});
+ }
+
+ /// \brief Register device using unique_ptr
+ ///
+ /// Used to register devices.
+ ///
+ /// \param name Name of the device
+ /// \param dev Device to be registered
+
+ template <typename T>
+ void
+ register_device(const pstring &name, device_arena::unique_ptr<T> &&dev)
+ {
+ register_device(name, device_arena::owned_ptr<T>(
+ dev.release(), true, dev.get_deleter()));
+ }
+
+ /// \brief Remove device
+ ///
+ /// Care needs to be applied if this is called to remove devices with
+ /// sub-devices which may have registered state.
+ ///
+ /// \param dev Device to be removed
+
+ void remove_device(core_device_t *dev);
+
+ setup_t &setup() noexcept { return *m_setup; }
+ const setup_t &setup() const noexcept { return *m_setup; }
+
+ nlparse_t &parser() noexcept;
+ const nlparse_t &parser() const noexcept;
+
+ // FIXME: make a post load member and include code there
+ void rebuild_lists(); // must be called after post_load !
+
+ static void
+ compile_defines(std::vector<std::pair<pstring, pstring>> &defs);
+ static pstring version();
+ static pstring version_patchlevel();
+
+ nets_collection_type &nets() noexcept { return m_nets; }
+ const nets_collection_type &nets() const noexcept { return m_nets; }
+
+ devices_collection_type &devices() noexcept { return m_devices; }
+ const devices_collection_type &devices() const noexcept
+ {
+ return m_devices;
+ }
+
+ family_collection_type &family_cache() { return m_family_cache; }
+
+ template <typename T, typename... Args>
+ device_arena::unique_ptr<T> make_pool_object(Args &&...args)
+ {
+ return plib::make_unique<T>(m_pool, std::forward<Args>(args)...);
+ }
+ // memory pool - still needed in some places
+ device_arena &pool() noexcept { return m_pool; }
+ const device_arena &pool() const noexcept { return m_pool; }
+
+ struct stats_info
+ {
+ const detail::queue_t &m_queue; // performance
+ const plib::pperftime_t<true> &m_stat_mainloop;
+ const plib::pperfcount_t<true> &m_perf_out_processed;
+ };
+
+ /// \brief print statistics gathered during run
+ ///
+ void print_stats(stats_info &si) const;
+
+ /// \brief call reset on all netlist components
+ ///
+ void reset();
+
+ /// \brief prior to running free no longer needed resources
+ ///
+ void free_setup_resources();
+#if !(NL_USE_INPLACE_CORE_TERMS)
+ std::vector<detail::core_terminal_t *> &
+ core_terms(const detail::net_t &net)
+ {
+ return m_core_terms[&net];
+ }
+#endif
+ private:
+ device_arena m_pool; // must be deleted last!
+
+ device_arena::unique_ptr<netlist_t> m_netlist;
+ std::unique_ptr<plib::dynamic_library_base> m_lib;
+ plib::state_manager_t m_state;
+ log_type m_log;
+
+ // FIXME: should only be available during device construction
+ host_arena::unique_ptr<setup_t> m_setup;
+
+ nets_collection_type m_nets;
+ // sole use is to manage lifetime of net objects
+ devices_collection_type m_devices;
+ // sole use is to manage lifetime of family objects
+ family_collection_type m_family_cache;
+#if !(NL_USE_INPLACE_CORE_TERMS)
+ // all terms for a net
+ std::unordered_map<const detail::net_t *,
+ std::vector<detail::core_terminal_t *>>
+ m_core_terms;
+#endif
+ // dummy version
+ int m_dummy_version;
+ };
+
+} // namespace netlist
+
+#endif // NL_CORE_NETLIST_STATE_H_
diff --git a/src/lib/netlist/core/nets.h b/src/lib/netlist/core/nets.h
new file mode 100644
index 00000000000..1a81c7ecd1c
--- /dev/null
+++ b/src/lib/netlist/core/nets.h
@@ -0,0 +1,401 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+///
+/// \file nets.h
+///
+
+#ifndef NL_CORE_NETS_H_
+#define NL_CORE_NETS_H_
+
+#include "../nltypes.h"
+#include "base_objects.h"
+#include "core_device.h"
+#include "exec.h"
+#include "state_var.h"
+
+#include "../plib/plists.h"
+#include "../plib/pstring.h"
+
+#include <algorithm>
+
+namespace netlist
+{
+ namespace detail
+ {
+
+ // ---------------------------------------------------------------------
+ // net_t
+ // ---------------------------------------------------------------------
+
+ class net_t : public netlist_object_t
+ {
+ public:
+ enum class queue_status
+ {
+ DELAYED_DUE_TO_INACTIVE = 0,
+ QUEUED,
+ DELIVERED
+ };
+
+ net_t(netlist_state_t &nl, const pstring &aname,
+ core_terminal_t *rail_terminal = nullptr);
+
+ net_t(const net_t &) = delete;
+ net_t &operator=(const net_t &) = delete;
+ net_t(net_t &&) noexcept = delete;
+ net_t &operator=(net_t &&) noexcept = delete;
+
+ virtual ~net_t() noexcept = default;
+
+ virtual void reset() noexcept;
+
+ // -----------------------------------------------------------------
+ // Hot section
+ //
+ // Any changes below will impact performance.
+ // -----------------------------------------------------------------
+
+ constexpr void toggle_new_Q() noexcept { m_new_Q = (m_cur_Q ^ 1); }
+
+ void toggle_and_push_to_queue(const netlist_time &delay) noexcept
+ {
+ toggle_new_Q();
+ push_to_queue(delay);
+ }
+
+ void push_to_queue(const netlist_time &delay) noexcept;
+
+ constexpr bool is_queued() const noexcept
+ {
+ return m_in_queue == queue_status::QUEUED;
+ }
+
+ // -----------------------------------------------------------------
+ // Very hot
+ // -----------------------------------------------------------------
+
+ template <bool KEEP_STATS>
+ void update_devs() noexcept;
+
+ constexpr const netlist_time_ext &
+ next_scheduled_time() const noexcept
+ {
+ return m_next_scheduled_time;
+ }
+ void set_next_scheduled_time(
+ const netlist_time_ext &next_time) noexcept
+ {
+ m_next_scheduled_time = next_time;
+ }
+
+ bool is_rail_net() const noexcept
+ {
+ return !(m_rail_terminal == nullptr);
+ }
+ core_terminal_t &rail_terminal() const noexcept
+ {
+ return *m_rail_terminal;
+ }
+
+ void add_to_active_list(core_terminal_t &term) noexcept;
+ void remove_from_active_list(core_terminal_t &term) noexcept;
+
+ // -----------------------------------------------------------------
+ // setup stuff - cold
+ // -----------------------------------------------------------------
+
+ bool is_logic() const noexcept;
+ bool is_analog() const noexcept;
+
+ void rebuild_list() noexcept(false); // rebuild m_list after a load
+
+ void update_inputs() noexcept
+ {
+ if constexpr (config::use_copy_instead_of_reference::value)
+ {
+ for (auto *term : core_terms_ref())
+ term->set_copied_input(m_cur_Q);
+ }
+ }
+
+ // -----------------------------------------------------------------
+ // net management
+ // -----------------------------------------------------------------
+
+ std::vector<detail::core_terminal_t *>
+ core_terms_copy() noexcept(false)
+ {
+ std::vector<detail::core_terminal_t *> ret(
+ core_terms_ref().size());
+ std::copy(core_terms_ref().begin(), core_terms_ref().end(),
+ ret.begin());
+ return ret;
+ }
+
+ void remove_terminal(detail::core_terminal_t &term) noexcept(false);
+ void remove_all_terminals() noexcept(false);
+ void add_terminal(detail::core_terminal_t &terminal) noexcept(
+ false);
+
+ bool core_terms_empty() noexcept(false)
+ {
+ return core_terms_ref().empty();
+ }
+
+ protected:
+ // only used for logic nets
+ constexpr const netlist_sig_t &Q() const noexcept
+ {
+ return m_cur_Q;
+ }
+
+ // only used for logic nets
+ void initial(netlist_sig_t val) noexcept
+ {
+ m_cur_Q = m_new_Q = val;
+ update_inputs();
+ }
+
+ // only used for logic nets
+ void set_Q_and_push(netlist_sig_t newQ,
+ const netlist_time & delay) noexcept;
+
+ // only used for logic nets
+ void set_Q_time(netlist_sig_t newQ,
+ const netlist_time_ext & at) noexcept;
+
+ private:
+#if NL_USE_INPLACE_CORE_TERMS
+ const plib::linked_list_t<core_terminal_t, 1> &
+ core_terms_ref() const noexcept
+ {
+ return m_core_terms;
+ }
+#else
+ std::vector<detail::core_terminal_t *> &core_terms_ref()
+ {
+ return state().core_terms(*this);
+ }
+#endif
+ state_var<netlist_sig_t> m_new_Q;
+ state_var<netlist_sig_t> m_cur_Q;
+ state_var<queue_status> m_in_queue;
+ // FIXME: this needs to be saved as well
+ plib::linked_list_t<core_terminal_t, 0> m_list_active;
+ state_var<netlist_time_ext> m_next_scheduled_time;
+
+ core_terminal_t *m_rail_terminal;
+#if NL_USE_INPLACE_CORE_TERMS
+ plib::linked_list_t<core_terminal_t, 1> m_core_terms;
+#endif
+ };
+
+ inline void net_t::push_to_queue(const netlist_time &delay) noexcept
+ {
+ if (is_queued())
+ exec().queue_remove(this);
+
+ m_next_scheduled_time = exec().time() + delay;
+ if constexpr (config::avoid_noop_queue_pushes::value)
+ m_in_queue = (m_list_active.empty()
+ ? queue_status::DELAYED_DUE_TO_INACTIVE
+ : (m_new_Q != m_cur_Q
+ ? queue_status::QUEUED
+ : queue_status::DELIVERED));
+ else
+ m_in_queue = m_list_active.empty()
+ ? queue_status::DELAYED_DUE_TO_INACTIVE
+ : queue_status::QUEUED;
+
+ if (m_in_queue == queue_status::QUEUED)
+ exec().queue_push(m_next_scheduled_time, this);
+ else
+ update_inputs();
+ }
+
+ template <bool KEEP_STATS>
+ void net_t::update_devs() noexcept
+ {
+ gsl_Expects(this->is_rail_net());
+
+ m_in_queue = queue_status::DELIVERED; // mark as taken ...
+
+ const netlist_sig_t new_Q(m_new_Q);
+ const netlist_sig_t cur_Q(m_cur_Q);
+ if (config::avoid_noop_queue_pushes::value
+ || ((new_Q ^ cur_Q) != 0))
+ {
+ m_cur_Q = new_Q;
+ const auto mask = (new_Q << core_terminal_t::INP_LH_SHIFT)
+ | (cur_Q << core_terminal_t::INP_HL_SHIFT);
+
+ if (!KEEP_STATS)
+ {
+ for (core_terminal_t *p : m_list_active)
+ {
+ p->set_copied_input(new_Q);
+ if ((p->terminal_state() & mask) != 0)
+ p->run_delegate();
+ }
+ }
+ else
+ {
+ for (core_terminal_t *p : m_list_active)
+ {
+ p->set_copied_input(new_Q);
+ auto *stats(p->device().stats());
+ stats->m_stat_call_count.inc();
+ if ((p->terminal_state() & mask))
+ {
+ auto g(stats->m_stat_total_time.guard());
+ p->run_delegate();
+ }
+ }
+ }
+ }
+ }
+
+ inline void net_t::add_to_active_list(core_terminal_t &term) noexcept
+ {
+ if (!m_list_active.empty())
+ {
+ term.set_copied_input(m_cur_Q);
+ m_list_active.push_front(&term);
+ }
+ else
+ {
+ m_list_active.push_front(&term);
+ rail_terminal().device().do_inc_active();
+ if (m_in_queue == queue_status::DELAYED_DUE_TO_INACTIVE)
+ {
+ // if we avoid queue pushes we must test if m_cur_Q and
+ // m_new_Q are equal
+ if ((!config::avoid_noop_queue_pushes::value
+ || (m_cur_Q != m_new_Q))
+ && (m_next_scheduled_time > exec().time()))
+ {
+ m_in_queue = queue_status::QUEUED; // pending
+ exec().queue_push(m_next_scheduled_time, this);
+ }
+ else
+ {
+ m_in_queue = queue_status::DELIVERED;
+ m_cur_Q = m_new_Q;
+ }
+ update_inputs();
+ }
+ else
+ term.set_copied_input(m_cur_Q);
+ }
+ }
+
+ inline void net_t::remove_from_active_list(
+ core_terminal_t &term) noexcept
+ {
+ gsl_Expects(!m_list_active.empty());
+ m_list_active.remove(&term);
+ if (m_list_active.empty())
+ {
+ if constexpr (true || config::avoid_noop_queue_pushes::value)
+ {
+ // All our connected outputs have signalled they no longer
+ // will act on input. We thus remove any potentially queued
+ // events and mark them.
+ // FIXME: May cause regression test to fail - revisit in
+ // this case
+ //
+ // This code is definitively needed for the
+ // AVOID_NOOP_QUEUE_PUSHES code path - therefore I left
+ // the if statement in and enabled it for all code paths
+ if (is_queued())
+ {
+ exec().queue_remove(this);
+ m_in_queue = queue_status::DELAYED_DUE_TO_INACTIVE;
+ }
+ }
+ rail_terminal().device().do_dec_active();
+ }
+ }
+
+ // only used for logic nets
+ inline void net_t::set_Q_and_push(netlist_sig_t newQ,
+ const netlist_time & delay) noexcept
+ {
+ gsl_Expects(delay >= netlist_time::zero());
+
+ if (newQ != m_new_Q)
+ {
+ m_new_Q = newQ;
+ push_to_queue(delay);
+ }
+ }
+
+ // only used for logic nets
+ inline void net_t::set_Q_time(netlist_sig_t newQ,
+ const netlist_time_ext & at) noexcept
+ {
+ gsl_Expects(at >= netlist_time_ext::zero());
+
+ if (newQ != m_new_Q)
+ {
+ m_in_queue = queue_status::DELAYED_DUE_TO_INACTIVE;
+ m_next_scheduled_time = at;
+ m_cur_Q = m_new_Q = newQ;
+ update_inputs();
+ }
+ else
+ {
+ m_cur_Q = newQ;
+ update_inputs();
+ }
+ }
+
+ } // namespace detail
+
+ class analog_net_t : public detail::net_t
+ {
+ public:
+ analog_net_t(netlist_state_t &nl, const pstring &aname,
+ detail::core_terminal_t *rail_terminal = nullptr);
+
+ void reset() noexcept override;
+
+ const nl_fptype &Q_Analog() const noexcept { return m_cur_Analog; }
+ void set_Q_Analog(nl_fptype v) noexcept { m_cur_Analog = v; }
+ // used by solver code ...
+ nl_fptype *Q_Analog_state_ptr() noexcept { return *m_cur_Analog; }
+
+ // FIXME: needed by current solver code
+ solver::matrix_solver_t *solver() const noexcept { return m_solver; }
+ void set_solver(solver::matrix_solver_t *solver) noexcept
+ {
+ m_solver = solver;
+ }
+
+ friend constexpr bool operator==(const analog_net_t &lhs,
+ const analog_net_t & rhs) noexcept
+ {
+ return &lhs == &rhs;
+ }
+
+ private:
+ state_var<nl_fptype> m_cur_Analog;
+ solver::matrix_solver_t *m_solver;
+ };
+
+ class logic_net_t : public detail::net_t
+ {
+ public:
+ logic_net_t(netlist_state_t &nl, const pstring &aname,
+ detail::core_terminal_t *rail_terminal = nullptr);
+
+ using detail::net_t::initial;
+ using detail::net_t::Q;
+ using detail::net_t::set_Q_and_push;
+ using detail::net_t::set_Q_time;
+ };
+
+} // namespace netlist
+
+#endif // NL_CORE_NETS_H_
diff --git a/src/lib/netlist/core/object_array.h b/src/lib/netlist/core/object_array.h
new file mode 100644
index 00000000000..cf1bc5ea4af
--- /dev/null
+++ b/src/lib/netlist/core/object_array.h
@@ -0,0 +1,251 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+///
+/// \file param.h
+///
+
+#ifndef NL_CORE_OBJECT_ARRAY_H_
+#define NL_CORE_OBJECT_ARRAY_H_
+
+#include "base_objects.h"
+#include "logic.h"
+
+#include "../nltypes.h"
+
+#include "../plib/pfmtlog.h"
+#include "../plib/plists.h"
+#include "../plib/pstring.h"
+
+#include <array>
+#include <utility>
+
+namespace netlist
+{
+ template<class C, std::size_t N>
+ class object_array_base_t : public plib::static_vector<C, N>
+ {
+ public:
+ template<class D, typename... Args>
+ //object_array_base_t(D &dev, const std::initializer_list<const char *> &names, Args&&... args)
+ object_array_base_t(D &dev, std::array<const char *, N> &&names, Args&&... args)
+ {
+ for (std::size_t i = 0; i<N; i++)
+ this->emplace_back(dev, pstring(names[i]), std::forward<Args>(args)...);
+ }
+
+ template<class D>
+ object_array_base_t(D &dev, const pstring &fmt)
+ {
+ for (std::size_t i = 0; i<N; i++)
+ this->emplace_back(dev, formatted(fmt, i));
+ }
+
+ template<class D, typename... Args>
+ object_array_base_t(D &dev, std::size_t offset, const pstring &fmt, Args&&... args)
+ {
+ for (std::size_t i = 0; i<N; i++)
+ this->emplace_back(dev, formatted(fmt, i+offset), std::forward<Args>(args)...);
+ }
+
+ template<class D>
+ object_array_base_t(D &dev, std::size_t offset, const pstring &fmt, nl_delegate delegate)
+ {
+ for (std::size_t i = 0; i<N; i++)
+ this->emplace_back(dev, formatted(fmt, i+offset), delegate);
+ }
+
+ template<class D>
+ object_array_base_t(D &dev, std::size_t offset, std::size_t output_mask, const pstring &fmt)
+ {
+ for (std::size_t i = 0; i<N; i++)
+ {
+ pstring name(formatted(fmt, i+offset));
+ if ((output_mask >> i) & 1)
+ name += "Q";
+ this->emplace(i, dev, name);
+ }
+ }
+ protected:
+ object_array_base_t() = default;
+
+ static pstring formatted(const pstring &fmt, std::size_t n)
+ {
+ if (N != 1)
+ return plib::pfmt(fmt)(n);
+ return plib::pfmt(fmt)("");
+ }
+ };
+
+
+ template<class C, std::size_t N>
+ class object_array_t : public object_array_base_t<C, N>
+ {
+ public:
+ using base_type = object_array_base_t<C, N>;
+ using base_type::base_type;
+ };
+
+ template<std::size_t N>
+ class object_array_t<logic_input_t,N> : public object_array_base_t<logic_input_t, N>
+ {
+ public:
+ using base_type = object_array_base_t<logic_input_t, N>;
+ using base_type::base_type;
+
+ template<class D, std::size_t ND>
+ object_array_t(D &dev, std::size_t offset, std::size_t output_mask,
+ const pstring &fmt, std::array<nl_delegate, ND> &&delegates)
+ {
+ static_assert(N <= ND, "initializer_list size mismatch");
+ std::size_t i = 0;
+ for (auto &e : delegates)
+ {
+ if (i < N)
+ {
+ pstring name(this->formatted(fmt, i+offset));
+ if ((output_mask >> i) & 1)
+ name += "Q";
+ this->emplace_back(dev, name, e);
+ }
+ i++;
+ }
+ }
+
+ //using value_type = typename plib::fast_type_for_bits<N>::type;
+ using value_type = std::uint32_t;
+ value_type operator ()()
+ {
+ if (N == 1) return e<0>() ;
+ if (N == 2) return e<0>() | (e<1>() << 1);
+ if (N == 3) return e<0>() | (e<1>() << 1) | (e<2>() << 2);
+ if (N == 4) return e<0>() | (e<1>() << 1) | (e<2>() << 2) | (e<3>() << 3);
+ if (N == 5) return e<0>() | (e<1>() << 1) | (e<2>() << 2) | (e<3>() << 3)
+ | (e<4>() << 4);
+ if (N == 6) return e<0>() | (e<1>() << 1) | (e<2>() << 2) | (e<3>() << 3)
+ | (e<4>() << 4) | (e<5>() << 5);
+ if (N == 7) return e<0>() | (e<1>() << 1) | (e<2>() << 2) | (e<3>() << 3)
+ | (e<4>() << 4) | (e<5>() << 5) | (e<6>() << 6);
+ if (N == 8) return e<0>() | (e<1>() << 1) | (e<2>() << 2) | (e<3>() << 3)
+ | (e<4>() << 4) | (e<5>() << 5) | (e<6>() << 6) | (e<7>() << 7);
+
+ value_type r(0);
+ for (std::size_t i = 0; i < N; i++)
+ r = static_cast<value_type>((*this)[i]() << (N-1)) | (r >> 1);
+ return r;
+ }
+
+ private:
+ template <std::size_t P>
+ constexpr value_type e() const { return (*this)[P](); }
+ };
+
+ template<std::size_t N>
+ class object_array_t<logic_output_t,N> : public object_array_base_t<logic_output_t, N>
+ {
+ public:
+ using base_type = object_array_base_t<logic_output_t, N>;
+ using base_type::base_type;
+
+ template <typename T>
+ void push(const T &v, const netlist_time &t)
+ {
+ if (N >= 1) (*this)[0].push((v >> 0) & 1, t);
+ if (N >= 2) (*this)[1].push((v >> 1) & 1, t);
+ if (N >= 3) (*this)[2].push((v >> 2) & 1, t);
+ if (N >= 4) (*this)[3].push((v >> 3) & 1, t);
+ if (N >= 5) (*this)[4].push((v >> 4) & 1, t);
+ if (N >= 6) (*this)[5].push((v >> 5) & 1, t);
+ if (N >= 7) (*this)[6].push((v >> 6) & 1, t);
+ if (N >= 8) (*this)[7].push((v >> 7) & 1, t);
+ for (std::size_t i = 8; i < N; i++)
+ (*this)[i].push((v >> i) & 1, t);
+ }
+
+ template<typename T>
+ void push(const T &v, const netlist_time * t)
+ {
+ if (N >= 1) (*this)[0].push((v >> 0) & 1, t[0]);
+ if (N >= 2) (*this)[1].push((v >> 1) & 1, t[1]);
+ if (N >= 3) (*this)[2].push((v >> 2) & 1, t[2]);
+ if (N >= 4) (*this)[3].push((v >> 3) & 1, t[3]);
+ if (N >= 5) (*this)[4].push((v >> 4) & 1, t[4]);
+ if (N >= 6) (*this)[5].push((v >> 5) & 1, t[5]);
+ if (N >= 7) (*this)[6].push((v >> 6) & 1, t[6]);
+ if (N >= 8) (*this)[7].push((v >> 7) & 1, t[7]);
+ for (std::size_t i = 8; i < N; i++)
+ (*this)[i].push((v >> i) & 1, t[i]);
+ }
+
+ template<typename T, std::size_t NT>
+ void push(const T &v, const std::array<const netlist_time, NT> &t)
+ {
+ static_assert(NT >= N, "Not enough timing entries provided");
+
+ push(v, t.data());
+ }
+
+ void set_tristate(netlist_sig_t v,
+ netlist_time ts_off_on, netlist_time ts_on_off) noexcept
+ {
+ for (std::size_t i = 0; i < N; i++)
+ (*this)[i].set_tristate(v, ts_off_on, ts_on_off);
+ }
+ };
+
+ template<std::size_t N>
+ class object_array_t<tristate_output_t, N> : public object_array_base_t<tristate_output_t, N>
+ {
+ public:
+ using base_type = object_array_base_t<tristate_output_t, N>;
+ using base_type::base_type;
+
+ template <typename T>
+ void push(const T &v, const netlist_time &t)
+ {
+ if (N >= 1) (*this)[0].push((v >> 0) & 1, t);
+ if (N >= 2) (*this)[1].push((v >> 1) & 1, t);
+ if (N >= 3) (*this)[2].push((v >> 2) & 1, t);
+ if (N >= 4) (*this)[3].push((v >> 3) & 1, t);
+ if (N >= 5) (*this)[4].push((v >> 4) & 1, t);
+ if (N >= 6) (*this)[5].push((v >> 5) & 1, t);
+ if (N >= 7) (*this)[6].push((v >> 6) & 1, t);
+ if (N >= 8) (*this)[7].push((v >> 7) & 1, t);
+ for (std::size_t i = 8; i < N; i++)
+ (*this)[i].push((v >> i) & 1, t);
+ }
+
+ void set_tristate(netlist_sig_t v,
+ netlist_time ts_off_on, netlist_time ts_on_off) noexcept
+ {
+ for (std::size_t i = 0; i < N; i++)
+ (*this)[i].set_tristate(v, ts_off_on, ts_on_off);
+ }
+ };
+
+ // -----------------------------------------------------------------------------
+ // Externals
+ // -----------------------------------------------------------------------------
+
+ extern template class object_array_t<logic_input_t, 1>;
+ extern template class object_array_t<logic_input_t, 2>;
+ extern template class object_array_t<logic_input_t, 3>;
+ extern template class object_array_t<logic_input_t, 4>;
+ extern template class object_array_t<logic_input_t, 5>;
+ extern template class object_array_t<logic_input_t, 6>;
+ extern template class object_array_t<logic_input_t, 7>;
+ extern template class object_array_t<logic_input_t, 8>;
+
+ extern template class object_array_t<logic_output_t, 1>;
+ extern template class object_array_t<logic_output_t, 2>;
+ extern template class object_array_t<logic_output_t, 3>;
+ extern template class object_array_t<logic_output_t, 4>;
+ extern template class object_array_t<logic_output_t, 5>;
+ extern template class object_array_t<logic_output_t, 6>;
+ extern template class object_array_t<logic_output_t, 7>;
+ extern template class object_array_t<logic_output_t, 8>;
+
+} // namespace netlist
+
+
+#endif // NL_CORE_OBJECT_ARRAY_H_
diff --git a/src/lib/netlist/core/param.h b/src/lib/netlist/core/param.h
new file mode 100644
index 00000000000..34e6c3b1b1e
--- /dev/null
+++ b/src/lib/netlist/core/param.h
@@ -0,0 +1,367 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+///
+/// \file param.h
+///
+
+#ifndef NL_CORE_PARAM_H_
+#define NL_CORE_PARAM_H_
+
+#include "../nltypes.h"
+#include "base_objects.h"
+#include "core_device.h"
+#include "setup.h"
+
+#include "../plib/palloc.h"
+#include "../plib/pfunction.h"
+#include "../plib/pstream.h"
+#include "../plib/pstring.h"
+#include "../plib/putil.h" // psource_t
+
+#include <memory>
+
+namespace netlist
+{
+ /// @brief Base class for all device parameters
+ ///
+ /// All device parameters classes derive from this object.
+ class param_t : public detail::device_object_t
+ {
+ public:
+ enum param_type_t
+ {
+ STRING,
+ DOUBLE,
+ INTEGER,
+ LOGIC,
+ POINTER // Special-case which is always initialized at MAME startup
+ // time
+ };
+
+ // device-less, it's the responsibility of the owner to register!
+ param_t(const pstring &name);
+
+ param_t(core_device_t &device, const pstring &name);
+
+ PCOPYASSIGNMOVE(param_t, delete)
+ virtual ~param_t() noexcept;
+
+ param_type_t param_type() const noexcept(false);
+
+ virtual pstring value_string() const = 0;
+
+ protected:
+ pstring get_initial(const core_device_t *dev, bool *found) const;
+
+ template <typename C>
+ void set_and_update_param(C &p, const C v) noexcept
+ {
+ if (p != v)
+ {
+ p = v;
+ device().update_param();
+ }
+ }
+ };
+
+ // -----------------------------------------------------------------------------
+ // numeric parameter template
+ // -----------------------------------------------------------------------------
+
+ template <typename T>
+ class param_num_t final : public param_t
+ {
+ public:
+ using value_type = T;
+
+ param_num_t(core_device_t &device, const pstring &name, T val) noexcept(
+ false);
+
+ constexpr const T &operator()() const noexcept { return m_param; }
+ constexpr operator const T &() const noexcept { return m_param; }
+
+ void set(const T &param) noexcept
+ {
+ set_and_update_param(m_param, param);
+ }
+
+ pstring value_string() const override
+ {
+ return plib::pfmt("{}").e(gsl::narrow<nl_fptype>(m_param));
+ }
+
+ private:
+ T m_param;
+ };
+
+ template <typename T>
+ class param_enum_t final : public param_t
+ {
+ public:
+ using value_type = T;
+
+ param_enum_t(core_device_t &device, const pstring &name,
+ T val) noexcept(false);
+
+ constexpr T operator()() const noexcept { return m_param; }
+ constexpr operator T() const noexcept { return m_param; }
+ void set(const T &param) noexcept
+ {
+ set_and_update_param(m_param, param);
+ }
+
+ pstring value_string() const override
+ {
+ // returns the numerical value
+ return plib::pfmt("{}")(static_cast<int>(m_param));
+ }
+
+ private:
+ T m_param;
+ };
+
+ // -----------------------------------------------------------------------------
+ // pointer parameter
+ // -----------------------------------------------------------------------------
+
+ // FIXME: not a core component -> legacy
+ class param_ptr_t final : public param_t
+ {
+ public:
+ param_ptr_t(core_device_t &device, const pstring &name,
+ std::uint8_t *val);
+ std::uint8_t *operator()() const noexcept { return m_param; }
+ void set(std::uint8_t *param) noexcept
+ {
+ set_and_update_param(m_param, param);
+ }
+
+ pstring value_string() const override
+ {
+ // returns something which errors
+ return {"PTRERROR"};
+ }
+
+ private:
+ std::uint8_t *m_param;
+ };
+
+ // -----------------------------------------------------------------------------
+ // string parameter
+ // -----------------------------------------------------------------------------
+
+ class param_str_t : public param_t
+ {
+ public:
+ param_str_t(core_device_t &device, const pstring &name,
+ const pstring &val);
+ // FIXME: The device less constructor is only used by macro parameters
+ // Every macro device gets a nld_wrapper object as the owner.
+ // Use this as the owner and get rid of this constructor.
+ param_str_t(netlist_state_t &state, const pstring &name,
+ const pstring &val);
+
+ pstring operator()() const noexcept { return str(); }
+ void set(const pstring &param)
+ {
+ if (*m_param != param)
+ {
+ *m_param = param;
+ changed();
+ device().update_param();
+ }
+ }
+ pstring value_string() const override { return *m_param; }
+
+ protected:
+ virtual void changed() noexcept;
+ pstring str() const noexcept { return *m_param; }
+
+ private:
+ host_arena::unique_ptr<pstring> m_param;
+ };
+
+ // -----------------------------------------------------------------------------
+ // model parameter
+ // -----------------------------------------------------------------------------
+
+ class param_model_t : public param_str_t
+ {
+ public:
+ template <typename T>
+ class value_base_t
+ {
+ public:
+ template <typename P, typename Y = T,
+ typename DUMMY = std::enable_if_t<
+ plib::is_arithmetic<Y>::value>>
+ value_base_t(P &param, const pstring &name)
+ : m_value(gsl::narrow<T>(param.value(name)))
+ {
+ }
+ template <typename P, typename Y = T,
+ std::enable_if_t<!plib::is_arithmetic<Y>::value, int> = 0>
+ value_base_t(P &param, const pstring &name)
+ : m_value(static_cast<T>(param.value_str(name)))
+ {
+ }
+ T operator()() const noexcept { return m_value; }
+ operator T() const noexcept { return m_value; }
+
+ private:
+ const T m_value;
+ };
+
+ using value_t = value_base_t<nl_fptype>;
+ using value_str_t = value_base_t<pstring>;
+
+ param_model_t(core_device_t &device, const pstring &name,
+ const pstring &val)
+ : param_str_t(device, name, val)
+ {
+ }
+
+ pstring value_str(const pstring &entity);
+ nl_fptype value(const pstring &entity);
+ pstring type();
+ // hide this
+ void set(const pstring &param) = delete;
+
+ protected:
+ void changed() noexcept override;
+
+ private:
+ };
+
+ // -----------------------------------------------------------------------------
+ // data parameter
+ // -----------------------------------------------------------------------------
+
+ class param_data_t : public param_str_t
+ {
+ public:
+ param_data_t(core_device_t &device, const pstring &name)
+ : param_str_t(device, name, "")
+ {
+ }
+
+ plib::istream_uptr stream();
+
+ protected:
+ void changed() noexcept override {}
+ };
+
+ // -----------------------------------------------------------------------------
+ // rom parameter
+ // -----------------------------------------------------------------------------
+
+ template <typename ST, std::size_t AW, std::size_t DW>
+ class param_rom_t final : public param_data_t
+ {
+ public:
+ param_rom_t(core_device_t &device, const pstring &name);
+
+ const ST &operator[](std::size_t n) const noexcept { return m_data[n]; }
+
+ protected:
+ void changed() noexcept override
+ {
+ plib::istream_read(*stream(), m_data.data(), 1 << AW);
+ }
+
+ private:
+ std::array<ST, 1 << AW> m_data;
+ };
+
+ template <typename T>
+ param_num_t<T>::param_num_t(core_device_t &device, const pstring &name,
+ const T val)
+ : param_t(device, name)
+ , m_param(val)
+ {
+ bool found = false;
+ pstring p = this->get_initial(&device, &found);
+ if (found)
+ {
+ plib::pfunction<nl_fptype> func;
+ func.compile_infix(p, {});
+ auto valx = func.evaluate();
+ if (plib::is_integral<T>::value)
+ if (plib::abs(valx - plib::trunc(valx)) > nlconst::magic(1e-6))
+ throw nl_exception(MF_INVALID_NUMBER_CONVERSION_1_2(
+ device.name() + "." + name, p));
+ m_param = plib::narrow_cast<T>(valx);
+ }
+
+ device.state().save(*this, m_param, this->name(), "m_param");
+ }
+
+ template <typename T>
+ param_enum_t<T>::param_enum_t(core_device_t &device, const pstring &name,
+ const T val)
+ : param_t(device, name)
+ , m_param(val)
+ {
+ bool found = false;
+ pstring p = this->get_initial(&device, &found);
+ if (found)
+ {
+ T temp(val);
+ bool ok = temp.set_from_string(p);
+ if (!ok)
+ {
+ device.state().log().fatal(
+ MF_INVALID_ENUM_CONVERSION_1_2(name, p));
+ throw nl_exception(MF_INVALID_ENUM_CONVERSION_1_2(name, p));
+ }
+ m_param = temp;
+ }
+
+ device.state().save(*this, m_param, this->name(), "m_param");
+ }
+
+ template <typename ST, std::size_t AW, std::size_t DW>
+ param_rom_t<ST, AW, DW>::param_rom_t(core_device_t &device,
+ const pstring & name)
+ : param_data_t(device, name)
+ {
+ auto f = this->stream();
+ if (!f.empty())
+ {
+ plib::istream_read(*f, m_data.data(), 1 << AW);
+ // FIXME: check for failbit if not in validation.
+ }
+ else
+ device.state().log().warning(MW_ROM_NOT_FOUND(str()));
+ }
+
+ // -----------------------------------------------------------------------------
+ // Externals
+ // -----------------------------------------------------------------------------
+
+ extern template class param_num_t<std::uint8_t>;
+ extern template class param_num_t<std::uint16_t>;
+ extern template class param_num_t<std::uint32_t>;
+ extern template class param_num_t<std::uint64_t>;
+ extern template class param_num_t<std::int8_t>;
+ extern template class param_num_t<std::int16_t>;
+ extern template class param_num_t<std::int32_t>;
+ extern template class param_num_t<std::int64_t>;
+ extern template class param_num_t<float>;
+ extern template class param_num_t<double>;
+ extern template class param_num_t<long double>;
+ extern template class param_num_t<bool>;
+
+ // FIXME: Should not be used as parameters. Fix later.
+ using param_logic_t = param_num_t<bool>;
+ using param_int_t = param_num_t<int>;
+ using param_fp_t = param_num_t<nl_fptype>;
+
+ extern template class param_model_t::value_base_t<float>;
+ extern template class param_model_t::value_base_t<double>;
+ extern template class param_model_t::value_base_t<long double>;
+
+} // namespace netlist
+
+#endif // NL_CORE_PARAM_H_
diff --git a/src/lib/netlist/core/queue.h b/src/lib/netlist/core/queue.h
new file mode 100644
index 00000000000..9fdd6ac1c64
--- /dev/null
+++ b/src/lib/netlist/core/queue.h
@@ -0,0 +1,107 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+///
+/// \file queue.h
+///
+
+#ifndef NL_CORE_QUEUE_H_
+#define NL_CORE_QUEUE_H_
+
+#include "../nl_errstr.h"
+#include "../nltypes.h"
+#include "queue.h"
+
+#include "../plib/pstate.h"
+#include "../plib/pstring.h"
+#include "../plib/ptimed_queue.h"
+
+#include <array>
+#include <queue>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+namespace netlist::detail
+{
+ // -----------------------------------------------------------------------------
+ // queue_t
+ // -----------------------------------------------------------------------------
+
+ // We don't need a thread-safe queue currently. Parallel processing of
+ // solvers will update inputs after parallel processing.
+
+ template <typename A, typename O>
+ class queue_base
+ : public config::timed_queue<A, plib::queue_entry_t<netlist_time_ext, O *>>
+ , public plib::state_manager_t::callback_t
+ {
+ public:
+ using entry_t = plib::queue_entry_t<netlist_time_ext, O *>;
+ using base_queue = config::timed_queue<A, entry_t>;
+ using id_delegate = plib::pmfp<std::size_t(const O *)>;
+ using obj_delegate = plib::pmfp<O *(std::size_t)>;
+
+ explicit queue_base(A &arena, std::size_t size, id_delegate get_id,
+ obj_delegate get_obj)
+ : base_queue(arena, size)
+ , m_size(0)
+ , m_times(size)
+ , m_net_ids(size)
+ , m_get_id(get_id)
+ , m_obj_by_id(get_obj)
+ {
+ }
+
+ ~queue_base() noexcept override = default;
+
+ queue_base(const queue_base &) = delete;
+ queue_base(queue_base &&) = delete;
+ queue_base &operator=(const queue_base &) = delete;
+ queue_base &operator=(queue_base &&) = delete;
+
+ protected:
+ void register_state(plib::state_manager_t &manager,
+ const pstring & module) override
+ {
+ manager.save_item(this, m_size, module + "." + "size");
+ manager.save_item(this, &m_times[0], module + "." + "times",
+ m_times.size());
+ manager.save_item(this, &m_net_ids[0], module + "." + "names",
+ m_net_ids.size());
+ }
+ void on_pre_save(
+ [[maybe_unused]] plib::state_manager_t &manager) override
+ {
+ m_size = this->size();
+ for (std::size_t i = 0; i < m_size; i++)
+ {
+ m_times[i] = this->list_pointer()[i].exec_time().as_raw();
+ m_net_ids[i] = m_get_id(this->list_pointer()[i].object());
+ }
+ }
+ void on_post_load(
+ [[maybe_unused]] plib::state_manager_t &manager) override
+ {
+ this->clear();
+ for (std::size_t i = 0; i < m_size; i++)
+ {
+ O *n = m_obj_by_id(m_net_ids[i]);
+ this->template push<false>(
+ entry_t(netlist_time_ext::from_raw(m_times[i]), n));
+ }
+ }
+
+ private:
+ std::size_t m_size;
+ std::vector<netlist_time_ext::internal_type> m_times;
+ std::vector<std::size_t> m_net_ids;
+ id_delegate m_get_id;
+ obj_delegate m_obj_by_id;
+ };
+
+ using queue_t = queue_base<device_arena, net_t>;
+
+} // namespace netlist::detail
+
+#endif // NL_CORE_QUEUE_H_
diff --git a/src/lib/netlist/core/setup.h b/src/lib/netlist/core/setup.h
new file mode 100644
index 00000000000..0168417c35c
--- /dev/null
+++ b/src/lib/netlist/core/setup.h
@@ -0,0 +1,377 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+///
+/// \file setup.h
+///
+
+#ifndef NL_CORE_SETUP_H_
+#define NL_CORE_SETUP_H_
+
+#include "../nl_config.h"
+#include "../nl_factory.h"
+#include "../nl_setup.h"
+#include "../nltypes.h"
+
+#include "../plib/pstream.h"
+#include "../plib/pstring.h"
+
+#include <initializer_list>
+#include <memory>
+#include <stack>
+#include <unordered_map>
+#include <vector>
+
+
+namespace netlist
+{
+
+ // ----------------------------------------------------------------------------------------
+ // Collection of models
+ // ----------------------------------------------------------------------------------------
+
+ class models_t
+ {
+ public:
+ using raw_map_t = std::unordered_map<pstring, pstring>;
+ using map_t = std::unordered_map<pstring, pstring>;
+ class model_t
+ {
+ public:
+ model_t(const pstring &model, const map_t &map)
+ : m_model(model), m_map(map) { }
+
+ pstring value_str(const pstring &entity) const;
+
+ nl_fptype value(const pstring &entity) const;
+
+ pstring type() const { return value_str("COREMODEL"); }
+
+ private:
+ static pstring model_string(const map_t &map);
+
+ const pstring m_model; // only for error messages
+ const map_t &m_map;
+ };
+
+ models_t(const raw_map_t &models)
+ : m_models(models)
+ {}
+
+ model_t get_model(const pstring &model);
+
+ std::vector<pstring> known_models() const
+ {
+ std::vector<pstring> ret;
+ for (const auto &e : m_models)
+ ret.push_back(e.first);
+ return ret;
+ }
+
+ private:
+
+ void model_parse(const pstring &model, map_t &map);
+
+ const raw_map_t &m_models;
+ std::unordered_map<pstring, map_t> m_cache;
+ };
+
+ namespace detail
+ {
+ struct alias_t
+ {
+ alias_t(alias_type type, pstring alias, pstring references)
+ : m_type(type)
+ , m_alias(alias)
+ , m_references(references)
+ {}
+
+ alias_t(const alias_t &) = default;
+ alias_t &operator=(const alias_t &) = default;
+ alias_t(alias_t &&) noexcept = default;
+ alias_t &operator=(alias_t &&) noexcept = default;
+
+ pstring name() const { return m_alias; }
+ pstring references() const { return m_references; }
+ alias_type type() const { return m_type; }
+ private:
+ alias_type m_type;
+ pstring m_alias;
+ pstring m_references;
+ };
+
+ ///
+ /// \brief class containing the abstract net list
+ ///
+ /// After parsing a net list this class contains all raw
+ /// connections, parameter values and devices.
+ struct abstract_t
+ {
+ using connection_t = std::pair<pstring, pstring>;
+
+ abstract_t(log_type &log) : m_factory(log) { }
+ std::unordered_map<pstring, alias_t> m_aliases;
+ std::vector<connection_t> m_connections;
+ std::unordered_map<pstring, pstring> m_param_values;
+ models_t::raw_map_t m_models;
+
+ // need to preserve order of device creation ...
+ std::vector<std::pair<pstring, factory::element_t *>> m_device_factory;
+ // lifetime control only - can be cleared before run
+ std::vector<std::pair<pstring, pstring>> m_default_params;
+ std::unordered_map<pstring, bool> m_hints;
+ factory::list_t m_factory;
+ };
+ } // namespace detail
+
+ // -----------------------------------------------------------------------------
+ // param_ref_t
+ // -----------------------------------------------------------------------------
+
+ struct param_ref_t
+ {
+ param_ref_t() noexcept : m_device(nullptr), m_param(nullptr) {}
+ param_ref_t(core_device_t &device, param_t &param) noexcept
+ : m_device(&device)
+ , m_param(&param)
+ { }
+
+ ~param_ref_t() = default;
+ PCOPYASSIGNMOVE(param_ref_t, default)
+
+ const core_device_t &device() const noexcept { return *m_device; }
+ param_t &param() const noexcept { return *m_param; }
+
+ bool is_valid() const noexcept { return (m_device != nullptr) && (m_param != nullptr); }
+ private:
+ core_device_t *m_device;
+ param_t *m_param;
+ };
+
+ // ----------------------------------------------------------------------------------------
+ // setup_t
+ // ----------------------------------------------------------------------------------------
+
+ class setup_t
+ {
+ public:
+
+ explicit setup_t(netlist_state_t &nlstate);
+ ~setup_t() noexcept = default;
+
+ PCOPYASSIGNMOVE(setup_t, delete)
+
+ // called from param_t creation
+ void register_param_t(param_t &param);
+ pstring get_initial_param_val(const pstring &name, const pstring &def) const;
+
+ void register_term(detail::core_terminal_t &term);
+ void register_term(terminal_t &term, terminal_t *other_term, const std::array<terminal_t *, 2> &splitter_terms);
+
+ // called from matrix_solver_t::get_connected_net
+ // returns the terminal being part of a two terminal device.
+ terminal_t *get_connected_terminal(const terminal_t &term) const noexcept
+ {
+ auto ret(m_connected_terminals.find(&term));
+ return (ret != m_connected_terminals.end()) ? ret->second[0] : nullptr;
+ }
+
+ // called from net_splitter
+ const std::array<terminal_t *, 4> *get_connected_terminals(const terminal_t &term) const noexcept
+ {
+ auto ret(m_connected_terminals.find(&term));
+ return (ret != m_connected_terminals.end()) ? &ret->second : nullptr;
+ }
+
+ // get family -> truth table
+ const logic_family_desc_t *family_from_model(const pstring &model);
+
+ param_ref_t find_param(const pstring &param_in) const;
+ // needed by nltool
+ std::vector<pstring> get_terminals_for_device_name(const pstring &devname) const;
+
+ // needed by proxy device to check power terminals
+ detail::core_terminal_t *find_terminal(const pstring &terminal_in, detail::terminal_type atype, bool required = true) const;
+ detail::core_terminal_t *find_terminal(const pstring &terminal_in, bool required = true) const;
+ pstring de_alias(const pstring &alias) const;
+
+ // run preparation
+
+ void prepare_to_run();
+
+ models_t &models() noexcept { return m_models; }
+ const models_t &models() const noexcept { return m_models; }
+
+ netlist_state_t &nlstate() noexcept { return m_nlstate; }
+ const netlist_state_t &nlstate() const noexcept { return m_nlstate; }
+
+ nlparse_t &parser() noexcept { return m_parser; }
+ const nlparse_t &parser() const noexcept { return m_parser; }
+
+ log_type &log() noexcept;
+ const log_type &log() const noexcept;
+
+ private:
+
+ void resolve_inputs();
+ pstring resolve_alias(const pstring &name) const;
+
+ void merge_nets(detail::net_t &this_net, detail::net_t &other_net);
+
+ void connect_terminals(detail::core_terminal_t &t1, detail::core_terminal_t &t2);
+ void connect_input_output(detail::core_terminal_t &input, detail::core_terminal_t &output);
+ void connect_terminal_output(detail::core_terminal_t &terminal, detail::core_terminal_t &output);
+ void connect_terminal_input(detail::core_terminal_t &terminal, detail::core_terminal_t &input);
+ bool connect_input_input(detail::core_terminal_t &input1, detail::core_terminal_t &input2);
+
+ bool connect(detail::core_terminal_t &t1, detail::core_terminal_t &t2);
+
+ // helpers
+ static pstring termtype_as_str(detail::core_terminal_t &in);
+
+ devices::nld_base_proxy *get_d_a_proxy(const detail::core_terminal_t &out);
+ devices::nld_base_proxy *get_a_d_proxy(detail::core_terminal_t &inp);
+ detail::core_terminal_t &resolve_proxy(detail::core_terminal_t &term);
+
+ // net manipulations
+
+ //void remove_terminal(detail::net_t &net, detail::core_terminal_t &terminal) noexcept(false);
+ void move_connections(detail::net_t &net, detail::net_t &dest_net);
+ void delete_empty_nets();
+
+ detail::abstract_t m_abstract;
+ nlparse_t m_parser;
+ netlist_state_t &m_nlstate;
+
+ models_t m_models;
+
+ // FIXME: currently only used during setup
+ devices::nld_netlistparams * m_netlist_params;
+
+ // FIXME: can be cleared before run
+ std::unordered_map<pstring, detail::core_terminal_t *> m_terminals;
+ // FIXME: Limited to 3 additional terminals
+ std::unordered_map<const terminal_t *,
+ std::array<terminal_t *, 4>> m_connected_terminals;
+ std::unordered_map<pstring, param_ref_t> m_params;
+ std::unordered_map<const detail::core_terminal_t *,
+ devices::nld_base_proxy *> m_proxies;
+ std::vector<host_arena::unique_ptr<param_t>> m_defparam_lifetime;
+
+ unsigned m_proxy_cnt;
+ };
+
+ // ----------------------------------------------------------------------------------------
+ // Specific netlist `psource_t` implementations
+ // ----------------------------------------------------------------------------------------
+
+ class source_netlist_t : public plib::psource_t
+ {
+ public:
+
+ source_netlist_t() = default;
+
+ PCOPYASSIGNMOVE(source_netlist_t, delete)
+ ~source_netlist_t() noexcept override = default;
+
+ virtual bool parse(nlparse_t &setup, const pstring &name);
+ };
+
+ class source_data_t : public plib::psource_t
+ {
+ public:
+
+ source_data_t() = default;
+
+ PCOPYASSIGNMOVE(source_data_t, delete)
+ ~source_data_t() noexcept override = default;
+ };
+
+ class source_string_t : public source_netlist_t
+ {
+ public:
+
+ explicit source_string_t(const pstring &source)
+ : m_str(source)
+ {
+ }
+
+ protected:
+ plib::istream_uptr stream(const pstring &name) override;
+
+ private:
+ pstring m_str;
+ };
+
+ class source_file_t : public source_netlist_t
+ {
+ public:
+
+ explicit source_file_t(const pstring &filename)
+ : m_filename(filename)
+ {
+ }
+
+ protected:
+ plib::istream_uptr stream(const pstring &name) override;
+
+ private:
+ pstring m_filename;
+ };
+
+ class source_pattern_t : public source_netlist_t
+ {
+ public:
+
+ explicit source_pattern_t(const pstring &pat, bool force_lowercase)
+ : m_pattern(pat)
+ , m_force_lowercase(force_lowercase)
+ {
+ }
+
+ protected:
+ plib::istream_uptr stream(const pstring &name) override;
+
+ private:
+ pstring m_pattern;
+ bool m_force_lowercase;
+ };
+
+ class source_mem_t : public source_netlist_t
+ {
+ public:
+ explicit source_mem_t(const char *mem)
+ : m_str(mem)
+ {
+ }
+
+ protected:
+ plib::istream_uptr stream(const pstring &name) override;
+
+ private:
+ std::string m_str;
+ };
+
+ class source_proc_t : public source_netlist_t
+ {
+ public:
+ source_proc_t(const pstring &name, nlsetup_func setup_func)
+ : m_setup_func(setup_func)
+ , m_setup_func_name(name)
+ {
+ }
+
+ bool parse(nlparse_t &setup, const pstring &name) override;
+
+ protected:
+ plib::istream_uptr stream(const pstring &name) override;
+
+ private:
+ nlsetup_func m_setup_func;
+ pstring m_setup_func_name;
+ };
+
+} // namespace netlist
+
+
+#endif // NL_CORE_SETUP_H_
diff --git a/src/lib/netlist/core/state_var.h b/src/lib/netlist/core/state_var.h
new file mode 100644
index 00000000000..a2b0ae5f477
--- /dev/null
+++ b/src/lib/netlist/core/state_var.h
@@ -0,0 +1,214 @@
+// license:BSD-3-Clause
+// copyright-holders:Couriersud
+
+///
+/// \file state_var.h
+///
+
+#ifndef NL_CORE_STATE_VAR_H_
+#define NL_CORE_STATE_VAR_H_
+
+#include "../nltypes.h"
+
+#include "../plib/pfmtlog.h"
+#include "../plib/pstring.h"
+
+namespace netlist
+{
+ /// \brief A persistent variable template.
+ /// Use the state_var template to define a variable whose value is saved.
+ /// Within a device definition use
+ ///
+ /// ```
+ /// NETLIB_OBJECT(abc)
+ /// {
+ /// NETLIB_CONSTRUCTOR(abc)
+ /// , m_var(*this, "myvar", 0)
+ /// ...
+ /// state_var<unsigned> m_var;
+ /// }
+ /// ```
+ ///
+ template <typename T>
+ struct state_var
+ {
+ public:
+ using value_type = T;
+
+ template <typename O>
+ //! Constructor.
+ state_var(O & owner, //!< owner must have a netlist() method.
+ const pstring &name, //!< identifier/name for this state variable
+ const T & value //!< Initial value after construction
+ );
+
+ template <typename O>
+ //! Constructor.
+ state_var(O & owner, //!< owner must have a netlist() method.
+ const pstring &name //!< identifier/name for this state variable
+ );
+
+ state_var(state_var &&) noexcept = delete;
+ state_var &operator=(state_var &&) noexcept = delete;
+
+ //! Destructor.
+ ~state_var() noexcept = default;
+
+ //! Copy Constructor removed.
+ constexpr state_var(const state_var &rhs) = delete;
+ //! Assignment operator to assign value of a state var.
+ constexpr state_var &operator=(const state_var &rhs) noexcept
+ {
+ if (this != &rhs)
+ m_value = rhs.m_value;
+ return *this;
+ } // OSX doesn't like noexcept
+ //! Assignment operator to assign value of type T.
+ constexpr state_var &operator=(const T &rhs) noexcept
+ {
+ m_value = rhs;
+ return *this;
+ }
+ //! Assignment move operator to assign value of type T.
+ // constexpr state_var &operator=(T &&rhs) noexcept { std::swap(m_value,
+ // rhs); return *this; }
+ constexpr state_var &operator=(T &&rhs) noexcept
+ {
+ m_value = std::move(rhs);
+ return *this;
+ }
+ //! Return non-const value of state variable.
+ constexpr operator T &() noexcept { return m_value; }
+ //! Return const value of state variable.
+ constexpr operator const T &() const noexcept { return m_value; }
+ //! Return non-const value of state variable.
+ constexpr T &var() noexcept { return m_value; }
+ //! Return const value of state variable.
+ constexpr const T &var() const noexcept { return m_value; }
+ //! Return non-const value of state variable.
+ constexpr T &operator()() noexcept { return m_value; }
+ //! Return const value of state variable.
+ constexpr const T &operator()() const noexcept { return m_value; }
+ //! Access state variable by ->.
+ constexpr T *operator->() noexcept { return &m_value; }
+ //! Access state variable by const ->.
+ constexpr const T *operator->() const noexcept { return &m_value; }
+ //! Access state variable by *.
+ constexpr T *operator*() noexcept { return &m_value; }
+ //! Access state variable by const *.
+ constexpr const T *operator*() const noexcept { return &m_value; }
+
+ private:
+ T m_value;
+ };
+
+ /// \brief A persistent array template.
+ /// Use this state_var template to define an array whose contents are
+ /// saved. Please refer to \ref state_var.
+ ///
+ /// \tparam C container class to use.
+
+ template <typename C>
+ struct state_container : public C
+ {
+ public:
+ using value_type = typename C::value_type;
+ //! Constructor.
+ template <typename O>
+ state_container(O & owner, //!< owner must have a netlist() method.
+ const pstring & name, //!< identifier/name for this state variable
+ const value_type &value //!< Initial value after construction
+ );
+ //! Constructor.
+ template <typename O>
+ state_container(O & owner, //!< owner must have a netlist() method.
+ const pstring & name, //!< identifier/name for this state variable
+ std::size_t n, //!< number of elements to allocate
+ const value_type &value //!< Initial value after construction
+ );
+ //! Copy Constructor.
+ state_container(const state_container &rhs) noexcept = default;
+ //! Destructor.
+ ~state_container() noexcept = default;
+ //! Move Constructor.
+ state_container(state_container &&rhs) noexcept = default;
+ state_container &operator=(
+ const state_container &rhs) noexcept = default;
+ state_container &operator=(state_container &&rhs) noexcept = default;
+ };
+
+ // -----------------------------------------------------------------------------
+ // State variables - predefined and c++11 non-optional
+ // -----------------------------------------------------------------------------
+
+ /// \brief predefined state variable type for uint8_t
+ using state_var_u8 = state_var<std::uint8_t>;
+ /// \brief predefined state variable type for int8_t
+ using state_var_s8 = state_var<std::int8_t>;
+
+ /// \brief predefined state variable type for uint32_t
+ using state_var_u32 = state_var<std::uint32_t>;
+ /// \brief predefined state variable type for int32_t
+ using state_var_s32 = state_var<std::int32_t>;
+ /// \brief predefined state variable type for sig_t
+ using state_var_sig = state_var<netlist_sig_t>;
+
+ template <typename T>
+ template <typename O>
+ state_var<T>::state_var(O &owner, const pstring &name, const T &value)
+ : m_value(value)
+ {
+ owner.state().save(owner, m_value, owner.name(), name);
+ }
+
+ template <typename T>
+ template <typename O>
+ state_var<T>::state_var(O &owner, const pstring &name)
+ {
+ owner.state().save(owner, m_value, owner.name(), name);
+ }
+
+ template <typename C>
+ template <typename O>
+ state_container<C>::state_container(O &owner, const pstring &name,
+ const state_container<C>::value_type &value)
+ {
+ owner.state().save(owner, static_cast<C &>(*this), owner.name(), name);
+ for (std::size_t i = 0; i < this->size(); i++)
+ (*this)[i] = value;
+ }
+
+ template <typename C>
+ template <typename O>
+ state_container<C>::state_container(O &owner, const pstring &name,
+ std::size_t n, const state_container<C>::value_type &value)
+ : C(n, value)
+ {
+ owner.state().save(owner, static_cast<C &>(*this), owner.name(), name);
+ }
+
+ // -----------------------------------------------------------------------------
+ // Externals
+ // -----------------------------------------------------------------------------
+
+ extern template struct state_var<std::uint8_t>;
+ extern template struct state_var<std::uint16_t>;
+ extern template struct state_var<std::uint32_t>;
+ extern template struct state_var<std::uint64_t>;
+ extern template struct state_var<std::int8_t>;
+ extern template struct state_var<std::int16_t>;
+ extern template struct state_var<std::int32_t>;
+ extern template struct state_var<std::int64_t>;
+ extern template struct state_var<bool>;
+
+} // namespace netlist
+
+namespace plib
+{
+ template <typename X>
+ struct format_traits<netlist::state_var<X>> : format_traits<X>
+ {
+ };
+} // namespace plib
+
+#endif // NL_CORE_STATE_VAR_H_