diff options
Diffstat (limited to 'src/lib/netlist/core/nets.h')
-rw-r--r-- | src/lib/netlist/core/nets.h | 351 |
1 files changed, 201 insertions, 150 deletions
diff --git a/src/lib/netlist/core/nets.h b/src/lib/netlist/core/nets.h index a121f261a50..280cda2013e 100644 --- a/src/lib/netlist/core/nets.h +++ b/src/lib/netlist/core/nets.h @@ -17,13 +17,10 @@ #include "../plib/plists.h" #include "../plib/pstring.h" -#ifndef AVOID_NOOP_QUEUE_PUSHES -#error AVOID_NOOP_QUEUE_PUSHES not defined -#endif +#include <algorithm> namespace netlist { - namespace detail { // ----------------------------------------------------------------------------- @@ -43,7 +40,10 @@ namespace netlist net_t(netlist_state_t &nl, const pstring &aname, core_terminal_t *rail_terminal = nullptr); - PCOPYASSIGNMOVE(net_t, delete) + 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; @@ -55,7 +55,10 @@ namespace netlist // Any changes below will impact performance. // ----------------------------------------------------------------------------- - void toggle_new_Q() noexcept { m_new_Q = (m_cur_Q ^ 1); } + constexpr void toggle_new_Q() noexcept + { + m_new_Q = (m_cur_Q ^ 1); + } void toggle_and_push_to_queue(const netlist_time &delay) noexcept { @@ -63,133 +66,28 @@ namespace netlist push_to_queue(delay); } - void push_to_queue(const netlist_time &delay) noexcept - { - if (!!is_queued()) - exec().qremove(this); + void push_to_queue(const netlist_time &delay) noexcept; - m_next_scheduled_time = exec().time() + delay; -#if (AVOID_NOOP_QUEUE_PUSHES) - 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)); - if (m_in_queue == queue_status::QUEUED) - exec().qpush(m_next_scheduled_time, this); - else - update_inputs(); -#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().qpush(m_next_scheduled_time, this); - else - update_inputs(); -#endif + constexpr bool is_queued() const noexcept + { + return m_in_queue == queue_status::QUEUED; } - constexpr bool is_queued() const noexcept { return m_in_queue == queue_status::QUEUED; } // ----------------------------------------------------------------------------- // Very hot // ----------------------------------------------------------------------------- template <bool KEEP_STATS> - void 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 (!AVOID_NOOP_QUEUE_PUSHES) - if (new_Q ^ cur_Q) -#endif - { - const auto mask = (new_Q << core_terminal_t::INP_LH_SHIFT) - | (cur_Q << core_terminal_t::INP_HL_SHIFT); - m_cur_Q = new_Q; - - if (!KEEP_STATS) - { - for (auto &p : m_list_active) - { - p.set_copied_input(new_Q); - if ((p.terminal_state() & mask) != 0) - p.run_delegate(); - } - } - else - { - for (auto & 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(); - } - } - } - } - } + void update_devs() noexcept; constexpr const netlist_time_ext &next_scheduled_time() const noexcept { return m_next_scheduled_time; } - void set_next_scheduled_time(netlist_time_ext next_time) noexcept { m_next_scheduled_time = next_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 - { - 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 (AVOID_NOOP_QUEUE_PUSHES) - if (m_next_scheduled_time > exec().time() - && (m_cur_Q != m_new_Q)) -#else - if (m_next_scheduled_time > exec().time()) -#endif - { - 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); - } - } - - 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()) - { -#if (AVOID_NOOP_QUEUE_PUSHES) - if (!!is_queued()) - { - exec().qremove(this); - m_in_queue = queue_status::DELAYED_DUE_TO_INACTIVE; - } -#endif - rail_terminal().device().do_dec_active(); - } - } + void add_to_active_list(core_terminal_t &term) noexcept; + void remove_from_active_list(core_terminal_t &term) noexcept; // ----------------------------------------------------------------------------- // setup stuff - cold @@ -200,17 +98,31 @@ namespace netlist void rebuild_list(); // rebuild m_list after a load - //std::vector<core_terminal_t *> &core_terms() noexcept { return state().m_core_terms[this]; } - void update_inputs() noexcept { -#if NL_USE_COPY_INSTEAD_OF_REFERENCE - for (auto & term : m_core_terms) - term->m_Q = m_cur_Q; -#endif - // nothing needs to be done if define not set + if constexpr (config::use_copy_instead_of_reference::value) + { + for (auto *term : core_terms_ref()) + term->set_copied_input(m_cur_Q); + } + } + + // ----------------------------------------------------------------------------- + // net management + // ----------------------------------------------------------------------------- + + const std::vector<detail::core_terminal_t *> core_terms_copy() + { + 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); + void remove_all_terminals(); + void add_terminal(detail::core_terminal_t &terminal); + + bool core_terms_empty() noexcept { return core_terms_ref().empty(); } protected: // only used for logic nets @@ -224,49 +136,188 @@ namespace netlist } // only used for logic nets - void set_Q_and_push(const netlist_sig_t &newQ, const netlist_time &delay) noexcept + 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 { - gsl_Expects(delay >= netlist_time::zero()); + 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()); - if (newQ != m_new_Q) + 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) { - m_new_Q = newQ; - push_to_queue(delay); + 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(); + } + } } } + } - // only used for logic nets - void set_Q_time(const netlist_sig_t &newQ, const netlist_time_ext &at) noexcept + inline void net_t::add_to_active_list(core_terminal_t &term) noexcept + { + if (!m_list_active.empty()) { - gsl_Expects(at >= netlist_time_ext::zero()); - - if (newQ != m_new_Q) + 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) { - m_in_queue = queue_status::DELAYED_DUE_TO_INACTIVE; - m_next_scheduled_time = at; - m_cur_Q = m_new_Q = newQ; + // 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) { - m_cur_Q = newQ; - update_inputs(); + // 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(); } + } - private: - state_var<netlist_sig_t> m_new_Q; - state_var<netlist_sig_t> m_cur_Q; - state_var<queue_status> m_in_queue; - plib::linked_list_t<core_terminal_t> m_list_active; - state_var<netlist_time_ext> m_next_scheduled_time; + // 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()); - core_terminal_t * m_rail_terminal; - //std::vector<core_terminal_t *> m_core_terms; // save post-start m_list ... + 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: |