netlist: more header file separation

This commit is contained in:
couriersud 2020-08-08 19:43:54 +02:00
parent 7df1f8de26
commit 923de88abb
10 changed files with 521 additions and 623 deletions

View File

@ -50,9 +50,15 @@ project "netlist"
MAME_DIR .. "src/lib/netlist/nl_setup.cpp",
MAME_DIR .. "src/lib/netlist/nl_setup.h",
MAME_DIR .. "src/lib/netlist/nl_types.h",
MAME_DIR .. "src/lib/netlist/core/analog.h",
MAME_DIR .. "src/lib/netlist/core/base_objects.h",
MAME_DIR .. "src/lib/netlist/core/core_device.h",
MAME_DIR .. "src/lib/netlist/core/device.h",
MAME_DIR .. "src/lib/netlist/core/exec.h",
MAME_DIR .. "src/lib/netlist/core/logic_family.h",
MAME_DIR .. "src/lib/netlist/core/logic.h",
MAME_DIR .. "src/lib/netlist/core/nets.h",
MAME_DIR .. "src/lib/netlist/core/object_array.h",
MAME_DIR .. "src/lib/netlist/core/param.h",
MAME_DIR .. "src/lib/netlist/core/setup.h",
MAME_DIR .. "src/lib/netlist/core/state_var.h",

View File

@ -156,6 +156,11 @@ namespace netlist
analog_net_t m_my_net;
};
inline solver::matrix_solver_t *analog_t::solver() const noexcept
{
return (this->has_net() ? net().solver() : nullptr);
}
} // namespace netlist

View File

@ -181,9 +181,6 @@ namespace netlist
netlist_state_t &state() noexcept;
const netlist_state_t &state() const noexcept;
netlist_t &exec() noexcept;
const netlist_t &exec() const noexcept;
private:
core_device_t * m_device;
};

View File

@ -0,0 +1,130 @@
// license:GPL-2.0+
// copyright-holders:Couriersud
///
/// \file device.h
///
#ifndef NL_CORE_DEVICE_H_
#define NL_CORE_DEVICE_H_
#include "../nltypes.h"
#include "../plib/pstring.h"
#include "base_objects.h"
#include "logic_family.h"
namespace netlist
{
// -----------------------------------------------------------------------------
// core_device_t
// -----------------------------------------------------------------------------
// FIXME: belongs into detail namespace
class core_device_t : public detail::netlist_object_t
{
public:
core_device_t(netlist_state_t &owner, const pstring &name);
core_device_t(core_device_t &owner, const pstring &name);
PCOPYASSIGNMOVE(core_device_t, delete)
virtual ~core_device_t() noexcept = default;
void do_inc_active() noexcept
{
if (m_hint_deactivate)
{
if (++m_active_outputs == 1)
{
if (m_stats)
m_stats->m_stat_inc_active.inc();
inc_active();
}
}
}
void do_dec_active() noexcept
{
if (m_hint_deactivate)
if (--m_active_outputs == 0)
{
dec_active();
}
}
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(); }
#if 0
virtual void update() noexcept { }
#endif
virtual void reset() { }
protected:
virtual void inc_active() noexcept { }
virtual void dec_active() noexcept { }
log_type & log();
public:
virtual void timestep(timestep_type ts_type, nl_fptype st) noexcept { plib::unused_var(ts_type, st); }
virtual void update_terminals() noexcept { }
virtual void update_param() noexcept {}
virtual bool is_dynamic() const noexcept { return false; }
virtual bool is_timestep() const noexcept { return false; }
private:
bool m_hint_deactivate;
state_var_s32 m_active_outputs;
device_arena::unique_ptr<stats_t> m_stats;
};
// -----------------------------------------------------------------------------
// base_device_t
// -----------------------------------------------------------------------------
class base_device_t : public core_device_t
{
public:
base_device_t(netlist_state_t &owner, const pstring &name);
base_device_t(base_device_t &owner, const pstring &name);
PCOPYASSIGNMOVE(base_device_t, delete)
~base_device_t() noexcept override = default;
template<class O, class C, typename... Args>
void create_and_register_subdevice(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)...);
}
void register_subalias(const pstring &name, const detail::core_terminal_t &term);
void register_subalias(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_

View File

@ -5,132 +5,20 @@
/// \file device.h
///
#ifndef NL_CORE_DEVICE_H_
#define NL_CORE_DEVICE_H_
#ifndef NL_DEVICE_H_
#define NL_DEVICE_H_
#include "../nltypes.h"
#include "../plib/pstring.h"
#include "base_objects.h"
#include "logic_family.h"
#include "core_device.h"
#include "param.h"
namespace netlist
{
// -----------------------------------------------------------------------------
// core_device_t
// -----------------------------------------------------------------------------
// FIXME: belongs into detail namespace
class core_device_t : public detail::netlist_object_t
{
public:
core_device_t(netlist_state_t &owner, const pstring &name);
core_device_t(core_device_t &owner, const pstring &name);
PCOPYASSIGNMOVE(core_device_t, delete)
virtual ~core_device_t() noexcept = default;
void do_inc_active() noexcept
{
if (m_hint_deactivate)
{
if (++m_active_outputs == 1)
{
if (m_stats)
m_stats->m_stat_inc_active.inc();
inc_active();
}
}
}
void do_dec_active() noexcept
{
if (m_hint_deactivate)
if (--m_active_outputs == 0)
{
dec_active();
}
}
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(); }
#if 0
virtual void update() noexcept { }
#endif
virtual void reset() { }
protected:
virtual void inc_active() noexcept { }
virtual void dec_active() noexcept { }
log_type & log();
public:
virtual void timestep(timestep_type ts_type, nl_fptype st) noexcept { plib::unused_var(ts_type, st); }
virtual void update_terminals() noexcept { }
virtual void update_param() noexcept {}
virtual bool is_dynamic() const noexcept { return false; }
virtual bool is_timestep() const noexcept { return false; }
private:
bool m_hint_deactivate;
state_var_s32 m_active_outputs;
device_arena::unique_ptr<stats_t> m_stats;
};
// -----------------------------------------------------------------------------
// base_device_t
// -----------------------------------------------------------------------------
class base_device_t : public core_device_t
{
public:
base_device_t(netlist_state_t &owner, const pstring &name);
base_device_t(base_device_t &owner, const pstring &name);
PCOPYASSIGNMOVE(base_device_t, delete)
~base_device_t() noexcept override = default;
template<class O, class C, typename... Args>
void create_and_register_subdevice(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)...);
}
void register_subalias(const pstring &name, const detail::core_terminal_t &term);
void register_subalias(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:
};
// -----------------------------------------------------------------------------
// device_t
// -----------------------------------------------------------------------------
class device_t : public base_device_t,
public logic_family_t
class device_t : public base_device_t,
public logic_family_t
{
public:
device_t(netlist_state_t &owner, const pstring &name);
@ -160,4 +48,4 @@ namespace netlist
} // namespace netlist
#endif // NL_CORE_DEVICE_H_
#endif // NL_DEVICE_H_

120
src/lib/netlist/core/exec.h Normal file
View File

@ -0,0 +1,120 @@
// license:GPL-2.0+
// copyright-holders:Couriersud
///
/// \file exec.h
///
#ifndef NL_CORE_EXEC_H_
#define NL_CORE_EXEC_H_
#include "base_objects.h"
#include "state_var.h"
#include "../nltypes.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);
PCOPYASSIGNMOVE(netlist_t, delete)
virtual ~netlist_t() noexcept = default;
// run functions
netlist_time_ext time() const noexcept { return m_time; }
void process_queue(netlist_time_ext delta) noexcept;
void abort_current_queue_slice() noexcept
{
if (!NL_USE_QUEUE_STATS || !m_use_stats)
m_queue.retime<false>(detail::queue_t::entry_t(m_time, nullptr));
else
m_queue.retime<true>(detail::queue_t::entry_t(m_time, nullptr));
}
const detail::queue_t &queue() const noexcept { return m_queue; }
template<typename... Args>
void qpush(Args&&...args) noexcept
{
if (!NL_USE_QUEUE_STATS || !m_use_stats)
m_queue.emplace<false>(std::forward<Args>(args)...); // NOLINT(performance-move-const-arg)
else
m_queue.emplace<true>(std::forward<Args>(args)...); // NOLINT(performance-move-const-arg)
}
template <class R>
void qremove(const R &elem) noexcept
{
if (!NL_USE_QUEUE_STATS || !m_use_stats)
m_queue.remove<false>(elem);
else
m_queue.remove<true>(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; }
// force late type resolution
template <typename X = devices::nld_solver>
nl_fptype gmin(X *solv = nullptr) const noexcept
{
plib::unused_var(solv);
return static_cast<X *>(m_solver)->gmin();
}
netlist_state_t &nlstate() noexcept { return m_state; }
const netlist_state_t &nlstate() 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;
bool use_stats() const { return m_use_stats; }
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_mainclock;
//PALIGNAS_CACHELINE()
//PALIGNAS(16)
detail::queue_t m_queue;
bool m_use_stats;
// performance
plib::pperftime_t<true> m_stat_mainloop;
plib::pperfcount_t<true> m_perf_out_processed;
};
} // namespace netlist
#endif // NL_CORE_EXEC_H_

View File

@ -10,6 +10,8 @@
#include "base_objects.h"
#include "state_var.h"
#include "core_device.h"
#include "exec.h"
#include "../nltypes.h"
#include "../plib/plists.h"
@ -43,6 +45,12 @@ namespace netlist
virtual void reset() noexcept;
// -----------------------------------------------------------------------------
// Hot section
//
// Any changes below will impact performance.
// -----------------------------------------------------------------------------
void toggle_new_Q() noexcept { m_new_Q = (m_cur_Q ^ 1); }
void toggle_and_push_to_queue(const netlist_time &delay) noexcept
@ -51,11 +59,42 @@ namespace netlist
push_to_queue(delay);
}
void push_to_queue(const netlist_time &delay) noexcept;
void push_to_queue(const netlist_time &delay) noexcept
{
if (has_connections())
{
if (!!is_queued())
exec().qremove(this);
const auto nst(exec().time() + delay);
m_next_scheduled_time = nst;
if (!m_list_active.empty())
{
m_in_queue = queue_status::QUEUED;
exec().qpush(nst, this);
}
else
{
m_in_queue = queue_status::DELAYED_DUE_TO_INACTIVE;
update_inputs();
}
}
}
NVCC_CONSTEXPR bool is_queued() const noexcept { return m_in_queue == queue_status::QUEUED; }
template <bool KEEP_STATS>
inline void update_devs() noexcept;
inline void update_devs() noexcept
{
nl_assert(this->is_rail_net());
m_in_queue = queue_status::DELIVERED; // mark as taken ...
if (m_new_Q ^ m_cur_Q)
{
process<KEEP_STATS>((m_new_Q << core_terminal_t::INP_LH_SHIFT)
| (m_cur_Q << core_terminal_t::INP_HL_SHIFT), m_new_Q);
}
}
netlist_time_ext next_scheduled_time() const noexcept { return m_next_scheduled_time; }
void set_next_scheduled_time(netlist_time_ext ntime) noexcept { m_next_scheduled_time = ntime; }
@ -65,10 +104,47 @@ namespace netlist
bool has_connections() const noexcept { return !m_core_terms.empty(); }
void add_to_active_list(core_terminal_t &term) noexcept;
void remove_from_active_list(core_terminal_t &term) noexcept;
void 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);
railterminal().device().do_inc_active();
if (m_in_queue == queue_status::DELAYED_DUE_TO_INACTIVE)
{
if (m_next_scheduled_time > exec().time())
{
m_in_queue = queue_status::QUEUED; // pending
exec().qpush(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);
}
}
// setup stuff
void 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())
railterminal().device().do_dec_active();
}
// -----------------------------------------------------------------------------
// setup stuff - cold
// -----------------------------------------------------------------------------
bool is_logic() const noexcept;
bool is_analog() const noexcept;
@ -135,8 +211,39 @@ namespace netlist
plib::linkedlist_t<core_terminal_t> m_list_active;
std::vector<core_terminal_t *> m_core_terms; // save post-start m_list ...
// -----------------------------------------------------------------------------
// Very hot
// -----------------------------------------------------------------------------
template <bool KEEP_STATS, typename T, typename S>
void process(T mask, const S &sig) noexcept;
void process(T mask, const S &sig) noexcept
{
m_cur_Q = sig;
if (KEEP_STATS)
{
for (auto & p : m_list_active)
{
p.set_copied_input(sig);
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();
}
}
}
else
{
for (auto &p : m_list_active)
{
p.set_copied_input(sig);
if ((p.terminal_state() & mask) != 0)
p.run_delegate();
}
}
}
};
} // namespace detail

View File

@ -24,11 +24,14 @@
#include "../nltypes.h"
#include "base_objects.h"
#include "core_device.h"
#include "setup.h"
#include "../plib/palloc.h"
#include "../plib/pstream.h"
#include "../plib/pstring.h"
#include "../plib/putil.h" // psource_t
#include "../plib/pfunction.h"
#include <memory>
@ -63,8 +66,6 @@ namespace netlist
protected:
void update_param() noexcept;
pstring get_initial(const core_device_t *dev, bool *found) const;
template<typename C>
@ -73,7 +74,7 @@ namespace netlist
if (p != v)
{
p = v;
update_param();
device().update_param();
}
}
@ -170,13 +171,14 @@ namespace netlist
{
*m_param = param;
changed();
update_param();
device().update_param();
}
}
pstring valstr() const override
{
return *m_param;
}
protected:
virtual void changed() noexcept;
pstring str() const noexcept { return *m_param; }
@ -269,6 +271,63 @@ namespace netlist
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.stream(), m_data.data(), 1<<AW);
// FIXME: check for failbit if not in validation.
}
else
device.state().log().warning(MW_ROM_NOT_FOUND(str()));
}
} // namespace netlist

View File

@ -975,6 +975,83 @@ namespace netlist
}
}
// ----------------------------------------------------------------------------------------
// netlist_t
//
// Hot section
//
// Any changes below will impact performance.
// -----------------------------------------------------------------------------
template <bool KEEP_STATS>
void netlist_t::process_queue_stats(const netlist_time_ext delta) noexcept
{
netlist_time_ext stop(m_time + delta);
qpush(stop, nullptr);
if (m_mainclock == nullptr)
{
m_time = m_queue.top().exec_time();
detail::net_t *obj(m_queue.top().object());
m_queue.pop();
while (obj != nullptr)
{
obj->template update_devs<KEEP_STATS>();
if (KEEP_STATS)
m_perf_out_processed.inc();
const detail::queue_t::entry_t *top = &m_queue.top();
m_time = top->exec_time();
obj = top->object();
m_queue.pop();
}
}
else
{
logic_net_t &mc_net(m_mainclock->m_Q.net());
const netlist_time inc(m_mainclock->m_inc);
netlist_time_ext mc_time(mc_net.next_scheduled_time());
do
{
const detail::queue_t::entry_t *top = &m_queue.top();
while (top->exec_time() > mc_time)
{
m_time = mc_time;
mc_net.toggle_new_Q();
mc_net.update_devs<KEEP_STATS>();
top = &m_queue.top();
mc_time += inc;
}
m_time = top->exec_time();
auto *const obj(top->object());
m_queue.pop();
if (obj != nullptr)
obj->template update_devs<KEEP_STATS>();
else
break;
if (KEEP_STATS)
m_perf_out_processed.inc();
} while (true);
mc_net.set_next_scheduled_time(mc_time);
}
}
void netlist_t::process_queue(netlist_time_ext delta) noexcept
{
if (!m_use_stats)
process_queue_stats<false>(delta);
else
{
auto sm_guard(m_stat_mainloop.guard());
process_queue_stats<true>(delta);
}
}
template struct state_var<std::uint8_t>;
template struct state_var<std::uint16_t>;
template struct state_var<std::uint32_t>;

View File

@ -22,24 +22,7 @@
#include "core/object_array.h"
#include "core/param.h"
#include "core/state_var.h"
#include "plib/palloc.h" // owned_ptr
#include "plib/pfunction.h"
#include "plib/plists.h"
#include "plib/pmempool.h"
#include "plib/ppmf.h"
#include "plib/pstate.h"
#include "plib/pstream.h"
#include "plib/ptimed_queue.h"
#include "plib/ptypes.h"
#include "nl_errstr.h"
#include "nl_factory.h"
#include "nltypes.h"
#include <initializer_list>
#include <unordered_map>
#include <vector>
#include "core/exec.h"
//============================================================
// MACROS / New Syntax
@ -196,11 +179,6 @@ class NETLIB_NAME(name) : public delegator_t<base_device_t>
#define NETLIB_HANDLER(chip, name) void NETLIB_NAME(chip) :: name() noexcept
#if 0
#define NETLIB_UPDATEI() virtual void update() noexcept override
#define NETLIB_UPDATE(chip) NETLIB_HANDLER(chip, update)
#endif
#define NETLIB_RESET(chip) void NETLIB_NAME(chip) :: reset(void)
#define NETLIB_UPDATE_PARAM(chip) void NETLIB_NAME(chip) :: update_param() noexcept
@ -214,9 +192,6 @@ class NETLIB_NAME(name) : public delegator_t<base_device_t>
namespace netlist
{
namespace devices
{
// -----------------------------------------------------------------------------
@ -247,107 +222,6 @@ namespace netlist
};
} // namespace devices
// -----------------------------------------------------------------------------
// netlist_t
// -----------------------------------------------------------------------------
class netlist_t // NOLINT(clang-analyzer-optin.performance.Padding)
{
public:
explicit netlist_t(netlist_state_t &state, const pstring &aname);
PCOPYASSIGNMOVE(netlist_t, delete)
virtual ~netlist_t() noexcept = default;
// run functions
netlist_time_ext time() const noexcept { return m_time; }
void process_queue(netlist_time_ext delta) noexcept;
void abort_current_queue_slice() noexcept
{
if (!NL_USE_QUEUE_STATS || !m_use_stats)
m_queue.retime<false>(detail::queue_t::entry_t(m_time, nullptr));
else
m_queue.retime<true>(detail::queue_t::entry_t(m_time, nullptr));
}
const detail::queue_t &queue() const noexcept { return m_queue; }
template<typename... Args>
void qpush(Args&&...args) noexcept
{
if (!NL_USE_QUEUE_STATS || !m_use_stats)
m_queue.emplace<false>(std::forward<Args>(args)...); // NOLINT(performance-move-const-arg)
else
m_queue.emplace<true>(std::forward<Args>(args)...); // NOLINT(performance-move-const-arg)
}
template <class R>
void qremove(const R &elem) noexcept
{
if (!NL_USE_QUEUE_STATS || !m_use_stats)
m_queue.remove<false>(elem);
else
m_queue.remove<true>(elem);
}
// Control functions
void stop();
void reset();
// only used by nltool to create static c-code
devices::NETLIB_NAME(solver) *solver() const noexcept { return m_solver; }
// force late type resolution
template <typename X = devices::NETLIB_NAME(solver)>
nl_fptype gmin(X *solv = nullptr) const noexcept
{
plib::unused_var(solv);
return static_cast<X *>(m_solver)->gmin();
}
netlist_state_t &nlstate() noexcept { return m_state; }
const netlist_state_t &nlstate() 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;
bool use_stats() const { return m_use_stats; }
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::NETLIB_NAME(solver) * m_solver;
// mostly rw
//PALIGNAS(16)
netlist_time_ext m_time;
devices::NETLIB_NAME(mainclock) * m_mainclock;
//PALIGNAS_CACHELINE()
//PALIGNAS(16)
detail::queue_t m_queue;
bool m_use_stats;
// performance
plib::pperftime_t<true> m_stat_mainloop;
plib::pperfcount_t<true> m_perf_out_processed;
};
// -----------------------------------------------------------------------------
// Support classes for devices
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// power pins - not a device, but a helper
@ -400,371 +274,6 @@ namespace netlist
}
} // namespace devices
// -----------------------------------------------------------------------------
// Hot section
//
// Any changes below will impact performance.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// logic_input_t
// -----------------------------------------------------------------------------
#if 0
inline void logic_input_t::inactivate() noexcept
{
if (!is_state(STATE_INP_PASSIVE))
{
set_state(STATE_INP_PASSIVE);
net().remove_from_active_list(*this);
}
}
inline void logic_input_t::activate() noexcept
{
if (is_state(STATE_INP_PASSIVE))
{
net().add_to_active_list(*this);
set_state(STATE_INP_ACTIVE);
}
}
inline void logic_input_t::activate_hl() noexcept
{
if (is_state(STATE_INP_PASSIVE))
{
net().add_to_active_list(*this);
set_state(STATE_INP_HL);
}
}
inline void logic_input_t::activate_lh() noexcept
{
if (is_state(STATE_INP_PASSIVE))
{
net().add_to_active_list(*this);
set_state(STATE_INP_LH);
}
}
#endif
inline void detail::net_t::push_to_queue(const netlist_time &delay) noexcept
{
if (has_connections())
{
if (!!is_queued())
exec().qremove(this);
const auto nst(exec().time() + delay);
m_next_scheduled_time = nst;
if (!m_list_active.empty())
{
m_in_queue = queue_status::QUEUED;
exec().qpush(nst, this);
}
else
{
m_in_queue = queue_status::DELAYED_DUE_TO_INACTIVE;
update_inputs();
}
}
}
inline void detail::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);
railterminal().device().do_inc_active();
if (m_in_queue == queue_status::DELAYED_DUE_TO_INACTIVE)
{
if (m_next_scheduled_time > exec().time())
{
m_in_queue = queue_status::QUEUED; // pending
exec().qpush(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 detail::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())
railterminal().device().do_dec_active();
}
#if 0
inline nl_fptype terminal_t::operator ()() const noexcept
{
return net().Q_Analog();
}
inline const analog_net_t & analog_t::net() const noexcept
{
return plib::downcast<const analog_net_t &>(core_terminal_t::net());
}
inline analog_net_t & analog_t::net() noexcept
{
return plib::downcast<analog_net_t &>(core_terminal_t::net());
}
inline logic_net_t & logic_t::net() noexcept
{
return plib::downcast<logic_net_t &>(core_terminal_t::net());
}
inline const logic_net_t & logic_t::net() const noexcept
{
return plib::downcast<const logic_net_t &>(core_terminal_t::net());
}
inline netlist_sig_t logic_input_t::operator()() const noexcept
{
nl_assert(terminal_state() != STATE_INP_PASSIVE);
#if NL_USE_COPY_INSTEAD_OF_REFERENCE
return m_Q;
#else
return net().Q();
#endif
}
inline nl_fptype analog_input_t::Q_Analog() const noexcept
{
return net().Q_Analog();
}
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());
}
}
#endif
inline netlist_t &detail::device_object_t::exec() noexcept
{
return m_device->exec();
}
inline const netlist_t &detail::device_object_t::exec() const noexcept
{
return m_device->exec();
}
template <bool KEEP_STATS, typename T, typename S>
inline void detail::net_t::process(T mask, const S &sig) noexcept
{
m_cur_Q = sig;
if (KEEP_STATS)
{
for (auto & p : m_list_active)
{
p.set_copied_input(sig);
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();
}
}
}
else
{
for (auto &p : m_list_active)
{
p.set_copied_input(sig);
if ((p.terminal_state() & mask) != 0)
p.run_delegate();
}
}
}
template <bool KEEP_STATS>
inline void detail::net_t::update_devs() noexcept
{
nl_assert(this->is_rail_net());
m_in_queue = queue_status::DELIVERED; // mark as taken ...
if (m_new_Q ^ m_cur_Q)
{
process<KEEP_STATS>((m_new_Q << core_terminal_t::INP_LH_SHIFT)
| (m_cur_Q << core_terminal_t::INP_HL_SHIFT), m_new_Q);
}
}
template <bool KEEP_STATS>
inline void netlist_t::process_queue_stats(const netlist_time_ext delta) noexcept
{
netlist_time_ext stop(m_time + delta);
qpush(stop, nullptr);
if (m_mainclock == nullptr)
{
m_time = m_queue.top().exec_time();
detail::net_t *obj(m_queue.top().object());
m_queue.pop();
while (obj != nullptr)
{
obj->template update_devs<KEEP_STATS>();
if (KEEP_STATS)
m_perf_out_processed.inc();
const detail::queue_t::entry_t *top = &m_queue.top();
m_time = top->exec_time();
obj = top->object();
m_queue.pop();
}
}
else
{
logic_net_t &mc_net(m_mainclock->m_Q.net());
const netlist_time inc(m_mainclock->m_inc);
netlist_time_ext mc_time(mc_net.next_scheduled_time());
do
{
const detail::queue_t::entry_t *top = &m_queue.top();
while (top->exec_time() > mc_time)
{
m_time = mc_time;
mc_net.toggle_new_Q();
mc_net.update_devs<KEEP_STATS>();
top = &m_queue.top();
mc_time += inc;
}
m_time = top->exec_time();
auto *const obj(top->object());
m_queue.pop();
if (obj != nullptr)
obj->template update_devs<KEEP_STATS>();
else
break;
if (KEEP_STATS)
m_perf_out_processed.inc();
} while (true);
mc_net.set_next_scheduled_time(mc_time);
}
}
inline void netlist_t::process_queue(netlist_time_ext delta) noexcept
{
if (!m_use_stats)
process_queue_stats<false>(delta);
else
{
auto sm_guard(m_stat_mainloop.guard());
process_queue_stats<true>(delta);
}
}
// -----------------------------------------------------------------------------
// inline implementations - cold
// -----------------------------------------------------------------------------
#if 0
template<typename T, typename... Args>
inline device_arena::unique_ptr<T> detail::netlist_object_t::make_pool_object(Args&&... args)
{
return state().make_pool_object<T>(std::forward<Args>(args)...);
}
#endif
inline void param_t::update_param() noexcept
{
device().update_param();
}
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.stream(), m_data.data(), 1<<AW);
// FIXME: check for failbit if not in validation.
}
else
device.state().log().warning(MW_ROM_NOT_FOUND(str()));
}
#if 0
template<class O, class C, typename... Args>
void base_device_t::create_and_register_subdevice(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)...);
}
#endif
inline solver::matrix_solver_t *analog_t::solver() const noexcept
{
return (this->has_net() ? net().solver() : nullptr);
}
extern template struct state_var<std::uint8_t>;
extern template struct state_var<std::uint16_t>;