diff options
Diffstat (limited to 'src/lib/netlist/core')
-rw-r--r-- | src/lib/netlist/core/analog.h | 173 | ||||
-rw-r--r-- | src/lib/netlist/core/base_objects.h | 317 | ||||
-rw-r--r-- | src/lib/netlist/core/core_device.h | 203 | ||||
-rw-r--r-- | src/lib/netlist/core/device.h | 116 | ||||
-rw-r--r-- | src/lib/netlist/core/device_macros.h | 154 | ||||
-rw-r--r-- | src/lib/netlist/core/devices.h | 166 | ||||
-rw-r--r-- | src/lib/netlist/core/exec.h | 124 | ||||
-rw-r--r-- | src/lib/netlist/core/logic.h | 212 | ||||
-rw-r--r-- | src/lib/netlist/core/logic_family.h | 145 | ||||
-rw-r--r-- | src/lib/netlist/core/netlist_state.h | 295 | ||||
-rw-r--r-- | src/lib/netlist/core/nets.h | 401 | ||||
-rw-r--r-- | src/lib/netlist/core/object_array.h | 251 | ||||
-rw-r--r-- | src/lib/netlist/core/param.h | 367 | ||||
-rw-r--r-- | src/lib/netlist/core/queue.h | 107 | ||||
-rw-r--r-- | src/lib/netlist/core/setup.h | 377 | ||||
-rw-r--r-- | src/lib/netlist/core/state_var.h | 214 |
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 ¶m) 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 ¶m) 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 ¶m) + { + 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 ¶m, 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 ¶m, 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 ¶m) = 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 ¶m) noexcept + : m_device(&device) + , m_param(¶m) + { } + + ~param_ref_t() = default; + PCOPYASSIGNMOVE(param_ref_t, default) + + const core_device_t &device() const noexcept { return *m_device; } + param_t ¶m() 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 ¶m); + 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 ¶m_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_ |