diff options
Diffstat (limited to 'src/lib/netlist/nl_base.cpp')
-rw-r--r-- | src/lib/netlist/nl_base.cpp | 1834 |
1 files changed, 975 insertions, 859 deletions
diff --git a/src/lib/netlist/nl_base.cpp b/src/lib/netlist/nl_base.cpp index 851c7b8059f..ddb4f1bca6a 100644 --- a/src/lib/netlist/nl_base.cpp +++ b/src/lib/netlist/nl_base.cpp @@ -1,1083 +1,1199 @@ -// license:GPL-2.0+ +// license:BSD-3-Clause // copyright-holders:Couriersud -/* - * nlbase.c - * - */ + +#include "nl_errstr.h" + +#include "core/devices.h" +#include "core/logic.h" +#include "core/setup.h" + +#include "devices/nlid_proxy.h" #include "solver/nld_matrix_solver.h" #include "solver/nld_solver.h" #include "plib/palloc.h" +#include "plib/pdynlib.h" +#include "plib/pfmtlog.h" #include "plib/pmempool.h" #include "plib/putil.h" -#include "devices/nlid_proxy.h" -#include "devices/nlid_system.h" -#include "macro/nlm_base.h" -#include "nl_base.h" - -#include "nl_errstr.h" - -#include <cmath> -//#include <cstring> #include <limits> -namespace netlist -{ - -// ---------------------------------------------------------------------------------------- -// logic_family_ttl_t -// ---------------------------------------------------------------------------------------- - -// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, modernize-use-equals-default) -logic_family_desc_t::logic_family_desc_t() -{ -} - -class logic_family_ttl_t : public logic_family_desc_t -{ -public: - logic_family_ttl_t() : logic_family_desc_t() - { - m_fixed_V = 5.0; - m_low_thresh_PCNT = 0.8 / 5.0; - m_high_thresh_PCNT = 2.0 / 5.0; - // m_low_V - these depend on sinked/sourced current. Values should be suitable for typical applications. - m_low_VO = 0.1; - m_high_VO = 1.0; // 4.0 - m_R_low = 1.0; - m_R_high = 130.0; - } - pool_owned_ptr<devices::nld_base_d_to_a_proxy> create_d_a_proxy(netlist_state_t &anetlist, const pstring &name, logic_output_t *proxied) const override; - pool_owned_ptr<devices::nld_base_a_to_d_proxy> create_a_d_proxy(netlist_state_t &anetlist, const pstring &name, logic_input_t *proxied) const override; -}; - -pool_owned_ptr<devices::nld_base_d_to_a_proxy> logic_family_ttl_t::create_d_a_proxy(netlist_state_t &anetlist, const pstring &name, logic_output_t *proxied) const -{ - return pool().make_poolptr<devices::nld_d_to_a_proxy>(anetlist, name, proxied); -} -pool_owned_ptr<devices::nld_base_a_to_d_proxy> logic_family_ttl_t::create_a_d_proxy(netlist_state_t &anetlist, const pstring &name, logic_input_t *proxied) const -{ - return pool().make_poolptr<devices::nld_a_to_d_proxy>(anetlist, name, proxied); -} - -class logic_family_cd4xxx_t : public logic_family_desc_t -{ -public: - logic_family_cd4xxx_t() : logic_family_desc_t() - { - m_fixed_V = 0.0; - m_low_thresh_PCNT = 1.5 / 5.0; - m_high_thresh_PCNT = 3.5 / 5.0; - // m_low_V - these depend on sinked/sourced current. Values should be suitable for typical applications. - m_low_VO = 0.05; - m_high_VO = 0.05; // 4.95 - m_R_low = 10.0; - m_R_high = 10.0; - } - pool_owned_ptr<devices::nld_base_d_to_a_proxy> create_d_a_proxy(netlist_state_t &anetlist, const pstring &name, logic_output_t *proxied) const override; - pool_owned_ptr<devices::nld_base_a_to_d_proxy> create_a_d_proxy(netlist_state_t &anetlist, const pstring &name, logic_input_t *proxied) const override; -}; - -pool_owned_ptr<devices::nld_base_d_to_a_proxy> logic_family_cd4xxx_t::create_d_a_proxy(netlist_state_t &anetlist, const pstring &name, logic_output_t *proxied) const -{ - return pool().make_poolptr<devices::nld_d_to_a_proxy>(anetlist, name, proxied); -} - -pool_owned_ptr<devices::nld_base_a_to_d_proxy> logic_family_cd4xxx_t::create_a_d_proxy(netlist_state_t &anetlist, const pstring &name, logic_input_t *proxied) const -{ - return pool().make_poolptr<devices::nld_a_to_d_proxy>(anetlist, name, proxied); -} +NETLIST_EXTERNAL(base_lib) -const logic_family_desc_t *family_TTL() -{ - static logic_family_ttl_t obj; - return &obj; -} -const logic_family_desc_t *family_CD4XXX() +namespace netlist { - static logic_family_cd4xxx_t obj; - return &obj; -} + // ------------------------------------------------------------------------- + // detail::core_terminal_t + // ------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// queue_t -// ---------------------------------------------------------------------------------------- + detail::terminal_type detail::core_terminal_t::type() const noexcept(false) + { + if (plib::dynamic_downcast<const terminal_t *>(this)) + return terminal_type::TERMINAL; + if (plib::dynamic_downcast<const logic_input_t *>(this) + || plib::dynamic_downcast<const analog_input_t *>(this)) + return terminal_type::INPUT; + if (plib::dynamic_downcast<const logic_output_t *>(this) + || plib::dynamic_downcast<const analog_output_t *>(this)) + return terminal_type::OUTPUT; -detail::queue_t::queue_t(netlist_state_t &nl) - : timed_queue<pqentry_t<net_t *, netlist_time>, false, true>(512) - , netlist_ref(nl) - , m_qsize(0) - , m_times(512) - , m_net_ids(512) -{ -} + state().log().fatal(MF_UNKNOWN_TYPE_FOR_OBJECT(name())); + throw nl_exception(MF_UNKNOWN_TYPE_FOR_OBJECT(name())); + // return terminal_type::TERMINAL; // please compiler + } -void detail::queue_t::register_state(plib::state_manager_t &manager, const pstring &module) -{ - //state().log().debug("register_state\n"); - manager.save_item(this, m_qsize, module + "." + "qsize"); - 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()); -} + // ------------------------------------------------------------------------- + // detail::device_object_t + // ------------------------------------------------------------------------- -void detail::queue_t::on_pre_save(plib::state_manager_t &manager) -{ - plib::unused_var(manager); - m_qsize = this->size(); - for (std::size_t i = 0; i < m_qsize; i++ ) + detail::device_object_t::device_object_t(core_device_t *dev, + const pstring &aname) + : object_t(aname) + , m_device(dev) { - m_times[i] = this->listptr()[i].m_exec_time.as_raw(); - m_net_ids[i] = state().find_net_id(this->listptr()[i].m_object); } -} - -void detail::queue_t::on_post_load(plib::state_manager_t &manager) -{ - plib::unused_var(manager); - this->clear(); - for (std::size_t i = 0; i < m_qsize; i++ ) + netlist_state_t &detail::device_object_t::state() noexcept { - detail::net_t *n = state().nets()[m_net_ids[i]].get(); - this->push(queue_t::entry_t(netlist_time::from_raw(m_times[i]),n)); + return m_device->state(); } -} - -// ---------------------------------------------------------------------------------------- -// netlist_ref_t -// ---------------------------------------------------------------------------------------- -detail::netlist_ref::netlist_ref(netlist_state_t &nl) -: m_netlist(nl.exec()) { } + const netlist_state_t &detail::device_object_t::state() const noexcept + { + return m_device->state(); + } -// ---------------------------------------------------------------------------------------- -// device_object_t -// ---------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // detail::netlist_object_t + // ------------------------------------------------------------------------- -detail::device_object_t::device_object_t(core_device_t &dev, const pstring &aname) -: object_t(aname) -, m_device(dev) -{ -} + netlist_state_t &detail::netlist_object_t::state() noexcept + { + return m_netlist.nl_state(); + } -detail::terminal_type detail::core_terminal_t::type() const -{ - if (dynamic_cast<const terminal_t *>(this) != nullptr) - return terminal_type::TERMINAL; - else if (dynamic_cast<const logic_input_t *>(this) != nullptr) - return terminal_type::INPUT; - else if (dynamic_cast<const logic_output_t *>(this) != nullptr) - return terminal_type::OUTPUT; - else if (dynamic_cast<const analog_input_t *>(this) != nullptr) - return terminal_type::INPUT; - else if (dynamic_cast<const analog_output_t *>(this) != nullptr) - return terminal_type::OUTPUT; - else + const netlist_state_t &detail::netlist_object_t::state() const noexcept { - state().log().fatal(MF_UNKNOWN_TYPE_FOR_OBJECT(name())); - return terminal_type::TERMINAL; // please compiler + return m_netlist.nl_state(); } -} -// ---------------------------------------------------------------------------------------- -// netlist_t -// ---------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // netlist_t + // ------------------------------------------------------------------------- -netlist_t::netlist_t(const pstring &aname, plib::unique_ptr<callbacks_t> callbacks) - : m_state(plib::make_unique<netlist_state_t>(aname, *this, std::move(callbacks))) + netlist_t::netlist_t(netlist_state_t &state, const pstring &aname) + : m_state(state) , m_solver(nullptr) - , m_time(netlist_time::zero()) - , m_mainclock(nullptr) - , m_queue(*m_state) - , m_stats(false) -{ - devices::initialize_factory(nlstate().setup().factory()); - NETLIST_NAME(base)(nlstate().setup()); - run_state_manager().save_item(this, static_cast<plib::state_manager_t::callback_t &>(m_queue), "m_queue"); - run_state_manager().save_item(this, m_time, "m_time"); -} - -// ---------------------------------------------------------------------------------------- -// netlist_t -// ---------------------------------------------------------------------------------------- - -netlist_state_t::netlist_state_t(const pstring &aname, - netlist_t & anetlist, - plib::unique_ptr<callbacks_t> &&callbacks) -: m_name(aname) -, m_netlist(anetlist) -, m_state() -, m_callbacks(std::move(callbacks)) // Order is important here -, m_log(*m_callbacks) -, m_setup(plib::make_unique<setup_t>(*this)) -{ - pstring libpath = plib::util::environment("NL_BOOSTLIB", plib::util::buildpath({".", "nlboost.so"})); - m_lib = plib::make_unique<plib::dynlib>(libpath); -} - - -void netlist_t::stop() -{ - log().debug("Printing statistics ...\n"); - print_stats(); - log().debug("Stopping solver device ...\n"); - if (m_solver != nullptr) - m_solver->stop(); -} - -detail::net_t *netlist_state_t::find_net(const pstring &name) const -{ - for (auto & net : m_nets) - if (net->name() == name) - return net.get(); - - return nullptr; -} - -std::size_t netlist_state_t::find_net_id(const detail::net_t *net) const -{ - for (std::size_t i = 0; i < m_nets.size(); i++) - if (m_nets[i].get() == net) - return i; - return std::numeric_limits<std::size_t>::max(); -} - - - -void netlist_state_t::rebuild_lists() -{ - for (auto & net : m_nets) - net->rebuild_list(); -} - - -void netlist_state_t::compile_defines(std::vector<std::pair<pstring, pstring>> &defs) -{ -//#define ENTRY(x) { #x, PSTRINGIFY(x) } -#define ENTRY(x) std::pair<pstring, pstring>(#x, PSTRINGIFY(x)) - defs.emplace_back(ENTRY(PHAS_RDTSCP)); - defs.emplace_back(ENTRY(PUSE_ACCURATE_STATS)); - defs.emplace_back(ENTRY(PHAS_INT128)); - defs.emplace_back(ENTRY(USE_ALIGNED_OPTIMIZATIONS)); - defs.emplace_back(ENTRY(NVCCBUILD)); - defs.emplace_back(ENTRY(USE_MEMPOOL)); - defs.emplace_back(ENTRY(USE_QUEUE_STATS)); - defs.emplace_back(ENTRY(USE_COPY_INSTEAD_OF_REFERENCE)); - defs.emplace_back(ENTRY(USE_TRUTHTABLE_7448)); - defs.emplace_back(ENTRY(NL_DEBUG)); - defs.emplace_back(ENTRY(HAS_OPENMP)); - defs.emplace_back(ENTRY(USE_OPENMP)); - - defs.emplace_back(ENTRY(PPMF_TYPE)); - defs.emplace_back(ENTRY(PHAS_PMF_INTERNAL)); - -#undef ENTRY -} - -void netlist_t::reset() -{ - log().debug("Searching for mainclock\n"); - m_mainclock = m_state->get_single_device<devices::NETLIB_NAME(mainclock)>("mainclock"); - - log().debug("Searching for solver\n"); - m_solver = m_state->get_single_device<devices::NETLIB_NAME(solver)>("solver"); + , m_time(netlist_time_ext::zero()) + , m_main_clock(nullptr) + , m_use_stats(false) + , m_queue( + state.pool(), config::max_queue_size::value, + detail::queue_t::id_delegate(&netlist_state_t ::find_net_id, &state), + detail::queue_t::obj_delegate(&netlist_state_t ::net_by_id, &state)) + { + state.save(*this, + static_cast<plib::state_manager_t::callback_t &>(m_queue), + aname, "m_queue"); + state.save(*this, m_time, aname, "m_time"); + } - m_time = netlist_time::zero(); - m_queue.clear(); - if (m_mainclock != nullptr) - m_mainclock->m_Q.net().set_next_scheduled_time(netlist_time::zero()); - //if (m_solver != nullptr) - // m_solver->reset(); + void netlist_t::reset() + { + log().debug("Searching for main clock\n"); + m_main_clock = m_state + .get_single_device<devices::NETLIB_NAME(mainclock)>( + "mainclock"); + + log().debug("Searching for solver\n"); + m_solver = m_state.get_single_device<devices::NETLIB_NAME(solver)>( + "solver"); + + // Don't reset time + // m_time = netlist_time_ext::zero(); + m_queue.clear(); + if (m_main_clock != nullptr) + m_main_clock->m_Q.net().set_next_scheduled_time(m_time); + // if (m_solver != nullptr) + // m_solver->reset(); + + m_state.reset(); + } - m_state->reset(); -} + void netlist_t::stop() + { + log().debug("Printing statistics ...\n"); + print_stats(); + log().debug("Stopping solver device ...\n"); + if (m_solver != nullptr) + m_solver->stop(); + } -void netlist_state_t::reset() -{ - //FIXME: never used ??? - std::unordered_map<core_device_t *, bool> m; + void netlist_t::print_stats() const + { + if (m_use_stats) + { + netlist_state_t::stats_info si{m_queue, m_stat_mainloop, + m_perf_out_processed}; + m_state.print_stats(si); + } + log().verbose("Current pool memory allocated: {1:12} kB", + nl_state().pool().cur_alloc() >> 10); + log().verbose("Maximum pool memory allocated: {1:12} kB", + nl_state().pool().max_alloc() >> 10); + } - // Reset all nets once ! - log().verbose("Call reset on all nets:"); - for (auto & n : nets()) - n->reset(); + // ------------------------------------------------------------------------- + // netlist_state_t + // ------------------------------------------------------------------------- - // Reset all devices once ! - log().verbose("Call reset on all devices:"); - for (auto & dev : m_devices) - dev.second->reset(); + netlist_state_t::netlist_state_t(const pstring & name, + plib::plog_delegate logger) + : m_log(logger) + , m_dummy_version(1) + { + m_setup = plib::make_unique<setup_t, host_arena>(*this); + // create the run interface + m_netlist = plib::make_unique<netlist_t>(m_pool, *this, name); + + // Make sure save states are invalidated when a new version is deployed + + m_state.save_item(this, m_dummy_version, pstring("V") + version()); + + // Initialize factory + devices::initialize_factory(m_setup->parser().factory()); + + // Add default include file + const pstring content + = "#define RES_R(res) (res) \n" + "#define RES_K(res) ((res) * 1e3) \n" + "#define RES_M(res) ((res) * 1e6) \n" + "#define CAP_U(cap) ((cap) * 1e-6) \n" + "#define CAP_N(cap) ((cap) * 1e-9) \n" + "#define CAP_P(cap) ((cap) * 1e-12) \n" + "#define IND_U(ind) ((ind) * 1e-6) \n" + "#define IND_N(ind) ((ind) * 1e-9) \n" + "#define IND_P(ind) ((ind) * 1e-12) \n"; + m_setup->parser().add_include<plib::psource_str_t>( + "netlist/devices/net_lib.h", content); + + // This is for core macro libraries + m_setup->parser().add_include<plib::psource_str_t>("devices/net_lib.h", + content); +#if 1 + NETLIST_NAME(base_lib)(m_setup->parser()); + //#m_setup->parser().register_source<source_pattern_t>("../macro/modules/nlmod_{1}.cpp"); + //#m_setup->parser().register_source<source_pattern_t>("../macro/nlm_{1}.cpp"); +#else + #if 1 + pstring dir = "src/lib/netlist/"; + m_setup->parser().register_source<source_pattern_t>( + dir + "/macro/nlm_{1}.cpp", true); + m_setup->parser().register_source<source_pattern_t>( + dir + "/generated/nlm_{1}.cpp", true); + m_setup->parser().register_source<source_pattern_t>( + dir + "/macro/modules/nlmod_{1}.cpp", true); + m_setup->parser().include("base_lib"); + #else + // FIXME: This is very slow - need optimized parsing scanning + pstring dir = "src/lib/netlist/macro/"; + // m_setup->parser().register_source<source_pattern_t>("src/lib/netlist/macro/nlm_{}.cpp"); + m_setup->parser().register_source<source_file_t>(dir + + "nlm_base_lib.cpp"); + m_setup->parser().register_source<source_file_t>(dir + + "nlm_opamp_lib.cpp"); + m_setup->parser().register_source<source_file_t>(dir + + "nlm_roms_lib.cpp"); + m_setup->parser().register_source<source_file_t>( + dir + "nlm_cd4xxx_lib.cpp"); + m_setup->parser().register_source<source_file_t>( + dir + "nlm_otheric_lib.cpp"); + m_setup->parser().register_source<source_file_t>( + dir + "nlm_ttl74xx_lib.cpp"); + m_setup->parser().include("base_lib"); + #endif +#endif + } - // Make sure everything depending on parameters is set - // Currently analog input and logic input also - // push their outputs to queue. + void netlist_state_t::set_static_solver_lib( + std::unique_ptr<plib::dynamic_library_base> &&lib) + { + m_lib = std::move(lib); + } - log().verbose("Call update_param on all devices:"); - for (auto & dev : m_devices) - dev.second->update_param(); + std::size_t netlist_state_t::find_net_id(const detail::net_t *net) const + { + // special case for queue end processing items + if (net == nullptr) + return std::numeric_limits<std::size_t>::max() - 1; - // Step all devices once ! - /* - * INFO: The order here affects power up of e.g. breakout. However, such - * variations are explicitly stated in the breakout manual. - */ + for (std::size_t i = 0; i < m_nets.size(); i++) + if (m_nets[i].get() == net) + return i; - auto *netlist_params = get_single_device<devices::NETLIB_NAME(netlistparams)>("parameter"); + return std::numeric_limits<std::size_t>::max(); + } - switch (netlist_params->m_startup_strategy()) + detail::net_t *netlist_state_t::net_by_id(std::size_t id) const { - case 0: - { - std::vector<core_device_t *> d; - std::vector<nldelegate *> t; - log().verbose("Using default startup strategy"); - for (auto &n : m_nets) - for (auto & term : n->core_terms()) - if (term->m_delegate.has_object()) - { - if (!plib::container::contains(t, &term->m_delegate)) - { - t.push_back(&term->m_delegate); - term->m_delegate(); - } - auto *dev = reinterpret_cast<core_device_t *>(term->m_delegate.object()); - if (!plib::container::contains(d, dev)) - d.push_back(dev); - } - log().verbose("Devices not yet updated:"); - for (auto &dev : m_devices) - if (!plib::container::contains(d, dev.second.get())) - { - log().verbose("\t ...{1}", dev.second->name()); - dev.second->update(); - } - } - break; - case 1: // brute force backward - { - log().verbose("Using brute force backward startup strategy"); + // special case for queue end processing items + if (id == std::numeric_limits<std::size_t>::max() - 1) + return nullptr; - for (auto &n : m_nets) // only used if USE_COPY_INSTEAD_OF_REFERENCE == 1 - n->update_inputs(); + return m_nets[id].get(); + } - std::size_t i = m_devices.size(); - while (i>0) - m_devices[--i].second->update(); + void netlist_state_t::rebuild_lists() + { + for (auto &net : m_nets) + net->rebuild_list(); + } - for (auto &n : m_nets) // only used if USE_COPY_INSTEAD_OF_REFERENCE == 1 - n->update_inputs(); + void netlist_state_t::compile_defines( + std::vector<std::pair<pstring, pstring>> &defs) + { +#define ENTRY(x) \ + if (pstring(#x) != PSTRINGIFY(x)) \ + defs.emplace_back(std::pair<pstring, pstring>(#x, PSTRINGIFY(x))); \ + else \ + defs.emplace_back(std::pair<pstring, pstring>(#x, "<NOT DEFINED>")); +#define ENTRY_EX(x) \ + defs.emplace_back(std::pair<pstring, pstring>(#x, plib::pfmt("{}")(x))); + ENTRY(NL_VERSION_MAJOR) + ENTRY(NL_VERSION_MINOR) + ENTRY(NL_VERSION_PATCHLEVEL) + + ENTRY(PUSE_ACCURATE_STATS) + ENTRY(PHAS_INT128) + ENTRY(PUSE_ALIGNED_OPTIMIZATIONS) + ENTRY(PHAS_OPENMP) + ENTRY(PUSE_OPENMP) + ENTRY(PUSE_FLOAT128) + ENTRY_EX(config::use_mempool::value) + ENTRY_EX(config::use_queue_stats::value) + ENTRY(NL_USE_COPY_INSTEAD_OF_REFERENCE) + ENTRY(NL_USE_FLOAT128) + ENTRY_EX(config::use_float_matrix::value) + ENTRY_EX(config::use_long_double_matrix::value) + ENTRY(NL_DEBUG) + ENTRY(__NVCC__) + + ENTRY(__cplusplus) + ENTRY(__VERSION__) + + ENTRY(__GNUC__) + ENTRY(__GNUC_MINOR__) + ENTRY(__GNUC_PATCHLEVEL__) + + ENTRY(__clang__) + ENTRY(__clang_major__) + ENTRY(__clang_minor__) + ENTRY(__clang_patchlevel__) + ENTRY(__clang_version__) + + ENTRY(OPENMP) + ENTRY(_OPENMP) + + ENTRY(__x86_64__) + ENTRY(__i386__) + ENTRY(_WIN32) + ENTRY(_MSC_VER) + ENTRY(__APPLE__) + ENTRY(__unix__) + ENTRY(__linux__) + + ENTRY_EX(sizeof(base_device_t)) + ENTRY_EX(sizeof(device_t)) + ENTRY_EX(sizeof(logic_t)) + ENTRY_EX(sizeof(logic_input_t)) + ENTRY_EX(sizeof(logic_output_t)) + ENTRY_EX(sizeof(param_model_t)) + ENTRY_EX(sizeof(param_logic_t)) + ENTRY_EX(sizeof(state_var<int>)) + ENTRY_EX(sizeof(pstring)) + ENTRY_EX(sizeof(core_device_t::stats_t)) + ENTRY_EX(sizeof(std::vector<detail::core_terminal_t *>)) + ENTRY_EX(sizeof(plib::plog_level)) + + ENTRY_EX(sizeof(nl_delegate)) + ENTRY(PPMF_FORCE_TYPE) + ENTRY(PHAS_PMF_INTERNAL) - } - break; - case 2: // brute force forward - { - log().verbose("Using brute force forward startup strategy"); - for (auto &d : m_devices) - d.second->update(); - } - break; +#undef ENTRY +#undef ENTRY_EX } -#if 1 - /* the above may screw up m_active and the list */ - rebuild_lists(); -#endif -} - -void netlist_t::process_queue(const netlist_time delta) NL_NOEXCEPT -{ - if (!m_stats) - process_queue_stats<false>(delta); - else + pstring netlist_state_t::version() { - auto sm_guard(m_stat_mainloop.guard()); - process_queue_stats<true>(delta); + return plib::pfmt("{1}.{2}")(NL_VERSION_MAJOR, NL_VERSION_MINOR); } -} -template <bool KEEP_STATS> -void netlist_t::process_queue_stats(const netlist_time delta) NL_NOEXCEPT -{ - netlist_time stop(m_time + delta); + pstring netlist_state_t::version_patchlevel() + { + return plib::pfmt("{1}.{2}.{3}")(NL_VERSION_MAJOR, NL_VERSION_MINOR, + NL_VERSION_PATCHLEVEL); + } - qpush(detail::queue_t::entry_t(stop, nullptr)); + void netlist_state_t::free_setup_resources() { m_setup = nullptr; } - if (m_mainclock == nullptr) + void netlist_state_t::reset() { - detail::queue_t::entry_t e(m_queue.pop()); - m_time = e.m_exec_time; - while (e.m_object != nullptr) + // Reset all nets once ! + log().verbose("Call reset on all nets:"); + for (auto &n : nets()) + n->reset(); + + // Reset all devices once ! + log().verbose("Call reset on all devices:"); + for (auto &dev : m_devices) + dev.second->reset(); + + // Make sure everything depending on parameters is set + // Currently analog input and logic input also + // push their outputs to queue. + + std::vector<core_device_t *> devices_called; + log().verbose("Call update_param on all devices:"); + for (auto &dev : m_devices) { - e.m_object->template update_devs<KEEP_STATS>(); - if (KEEP_STATS) m_perf_out_processed.inc(); - e = m_queue.pop(); - m_time = e.m_exec_time; + dev.second->update_param(); + if (!plib::container::contains(devices_called, dev.second.get())) + devices_called.push_back(dev.second.get()); } - } - else - { - logic_net_t &mc_net(m_mainclock->m_Q.net()); - const netlist_time inc(m_mainclock->m_inc); - netlist_time mc_time(mc_net.next_scheduled_time()); - do + // Step all devices once ! + // + // INFO: The order here affects power up of e.g. breakout. However, such + // variations are explicitly stated in the breakout manual. + + auto *netlist_params = get_single_device< + devices::NETLIB_NAME(netlistparams)>("parameter"); + + switch (netlist_params->m_startup_strategy()) { - while (m_queue.top().m_exec_time > mc_time) + case 0: { - m_time = mc_time; - mc_net.toggle_new_Q(); - mc_net.update_devs<KEEP_STATS>(); - mc_time += inc; + std::vector<const nl_delegate *> t; + log().verbose("Using default startup strategy"); + for (auto &n : m_nets) + { + n->update_inputs(); // only used if + // USE_COPY_INSTEAD_OF_REFERENCE == 1 + for (detail::core_terminal_t *term : n->core_terms_copy()) + { + if (!plib::container::contains(t, &term->delegate())) + { + t.push_back(&term->delegate()); + term->run_delegate(); + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + auto *dev = reinterpret_cast<core_device_t *>( + term->delegate().object()); + if (!plib::container::contains(devices_called, dev)) + devices_called.push_back(dev); + } + } + log().verbose("Devices not yet updated:"); + for (auto &dev : m_devices) + if (!plib::container::contains(devices_called, + dev.second.get())) + { + // FIXME: doesn't seem to be needed, use cases include + // analog output devices. Check and remove + log().error("\t Device {1} not yet updated", + dev.second->name()); + // dev.second->update(); + } } + break; + } - detail::queue_t::entry_t e(m_queue.pop()); - m_time = e.m_exec_time; - if (e.m_object != nullptr) - { - e.m_object->template update_devs<KEEP_STATS>(); - if (KEEP_STATS) m_perf_out_processed.inc(); - } - else - break; - } while (true); //while (e.m_object != nullptr); - mc_net.set_next_scheduled_time(mc_time); + // the above may screw up m_active and the list + rebuild_lists(); } -} -void netlist_t::print_stats() const -{ - if (m_stats) + void netlist_state_t::print_stats(stats_info &si) const { std::vector<size_t> index; - for (size_t i=0; i < m_state->m_devices.size(); i++) + for (size_t i = 0; i < this->m_devices.size(); i++) index.push_back(i); std::sort(index.begin(), index.end(), - [&](size_t i1, size_t i2) { return m_state->m_devices[i1].second->m_stats->m_stat_total_time.total() < m_state->m_devices[i2].second->m_stats->m_stat_total_time.total(); }); - - nperftime_t<true>::type total_time(0); - nperftime_t<true>::ctype total_count(0); - - for (auto & j : index) + [&](size_t i1, size_t i2) + { + return this->m_devices[i1] + .second->stats() + ->m_stat_total_time.total() + < this->m_devices[i2] + .second->stats() + ->m_stat_total_time.total(); + }); + + plib::pperftime_t<true>::type total_time(0); + plib::pperftime_t<true>::ctype total_count(0); + + for (auto &j : index) { - auto entry = m_state->m_devices[j].second.get(); - auto stats = m_state->m_devices[j].second.get()->m_stats.get(); - log().verbose("Device {1:20} : {2:12} {3:12} {4:15} {5:12}", entry->name(), - stats->m_stat_call_count(), stats->m_stat_total_time.count(), - stats->m_stat_total_time.total(), stats->m_stat_inc_active()); + auto *entry = this->m_devices[j].second.get(); + auto *stats = entry->stats(); + log().verbose( + "Device {1:20} : {2:12} {3:12} {4:15} {5:12}", entry->name(), + stats->m_stat_call_count(), stats->m_stat_total_time.count(), + stats->m_stat_total_time.total(), stats->m_stat_inc_active()); total_time += stats->m_stat_total_time.total(); total_count += stats->m_stat_total_time.count(); } log().verbose("Total calls : {1:12} {2:12} {3:12}", total_count, - total_time, total_time / static_cast<decltype(total_time)>(total_count)); + total_time, + total_time + / gsl::narrow<decltype(total_time)>( + (total_count > 0) ? total_count : 1)); - log().verbose("Total loop {1:15}", m_stat_mainloop()); + log().verbose("Total loop {1:15}", si.m_stat_mainloop()); log().verbose("Total time {1:15}", total_time); // FIXME: clang complains about unreachable code without - const auto clang_workaround_unreachable_code = USE_QUEUE_STATS; + const bool clang_workaround_unreachable_code( + config::use_queue_stats::value); if (clang_workaround_unreachable_code) { - /* Only one serialization should be counted in total time */ - /* But two are contained in m_stat_mainloop */ - nperftime_t<true> overhead; - nperftime_t<true> test; + // Only one serialization should be counted in total time + // But two are contained in m_stat_mainloop + plib::pperftime_t<true> overhead; + plib::pperftime_t<true> test; { auto overhead_guard(overhead.guard()); - for (int j=0; j<100000;j++) + for (int j = 0; j < 100000; j++) { auto test_guard(test.guard()); } } - nperftime_t<true>::type total_overhead = overhead() - * static_cast<nperftime_t<true>::type>(total_count) - / static_cast<nperftime_t<true>::type>(200000); + plib::pperftime_t<true>::type total_overhead + = overhead() + * gsl::narrow<plib::pperftime_t<true>::type>(total_count) + / gsl::narrow<plib::pperftime_t<true>::type>(200000); - log().verbose("Queue Pushes {1:15}", m_queue.m_prof_call()); - log().verbose("Queue Moves {1:15}", m_queue.m_prof_sortmove()); - log().verbose("Queue Removes {1:15}", m_queue.m_prof_remove()); - log().verbose("Queue Retimes {1:15}", m_queue.m_prof_retime()); + log().verbose("Queue Pushes {1:15}", si.m_queue.m_prof_call()); + log().verbose("Queue Moves {1:15}", + si.m_queue.m_prof_sort_move()); + log().verbose("Queue Removes {1:15}", si.m_queue.m_prof_remove()); log().verbose(""); - log().verbose("Take the next lines with a grain of salt. They depend on the measurement implementation."); + log().verbose( + "Take the next lines with a grain of salt. They depend on the measurement implementation."); log().verbose("Total overhead {1:15}", total_overhead); - nperftime_t<true>::type overhead_per_pop = (m_stat_mainloop()-2*total_overhead - (total_time - total_overhead)) - / static_cast<nperftime_t<true>::type>(m_queue.m_prof_call()); - log().verbose("Overhead per pop {1:11}", overhead_per_pop ); + plib::pperftime_t<true>::type overhead_per_pop + = (si.m_stat_mainloop() - 2 * total_overhead + - (total_time - total_overhead)) + / gsl::narrow<plib::pperftime_t<true>::type>( + si.m_queue.m_prof_call()); + log().verbose("Overhead per pop {1:11}", overhead_per_pop); log().verbose(""); } auto trigger = total_count * 200 / 1000000; // 200 ppm - for (auto &entry : m_state->m_devices) + for (const auto &entry : this->m_devices) { - auto ep = entry.second.get(); - auto stats = ep->m_stats.get(); - // Factor of 3 offers best performace increase - if (stats->m_stat_inc_active() > 3 * stats->m_stat_total_time.count() + auto *ep = entry.second.get(); + auto *stats = ep->stats(); + // Factor of 3 offers best performance increase + if (stats->m_stat_inc_active() + > 3 * stats->m_stat_total_time.count() && stats->m_stat_inc_active() > trigger) log().verbose("HINT({}, NO_DEACTIVATE) // {} {} {}", ep->name(), - static_cast<double>(stats->m_stat_inc_active()) / static_cast<double>(stats->m_stat_total_time.count()), - stats->m_stat_inc_active(), stats->m_stat_total_time.count()); + gsl::narrow<nl_fptype>(stats->m_stat_inc_active()) + / gsl::narrow<nl_fptype>( + stats->m_stat_total_time.count()), + stats->m_stat_inc_active(), + stats->m_stat_total_time.count()); } + log().verbose(""); } -} -core_device_t *netlist_state_t::get_single_device(const pstring &classname, bool (*cc)(core_device_t *)) const -{ - core_device_t *ret = nullptr; - for (auto &d : m_devices) + core_device_t * + netlist_state_t::get_single_device(const pstring &classname, + bool (*cc)(core_device_t *)) const { - if (cc(d.second.get())) + core_device_t *ret = nullptr; + for (const auto &d : m_devices) { - if (ret != nullptr) - m_log.fatal(MF_MORE_THAN_ONE_1_DEVICE_FOUND(classname)); - else + if (cc(d.second.get())) + { + if (ret != nullptr) + { + m_log.fatal(MF_MORE_THAN_ONE_1_DEVICE_FOUND(classname)); + throw nl_exception( + MF_MORE_THAN_ONE_1_DEVICE_FOUND(classname)); + } ret = d.second.get(); + } } + return ret; } - return ret; -} + nlparse_t &netlist_state_t::parser() noexcept { return m_setup->parser(); } + const nlparse_t &netlist_state_t::parser() const noexcept + { + return m_setup->parser(); + } -// ---------------------------------------------------------------------------------------- -// core_device_t -// ---------------------------------------------------------------------------------------- + void netlist_state_t::remove_device(core_device_t *dev) + { + for (auto it = m_devices.begin(); it != m_devices.end(); it++) + if (it->second.get() == dev) + { + m_state.remove_save_items(dev); + m_devices.erase(it); + return; + } + } -core_device_t::core_device_t(netlist_state_t &owner, const pstring &name) - : object_t(name) - , logic_family_t() - , netlist_ref(owner) - , m_hint_deactivate(false) - , m_active_outputs(*this, "m_active_outputs", 1) -{ - if (logic_family() == nullptr) - set_logic_family(family_TTL()); - if (exec().stats_enabled()) - m_stats = plib::make_unique<stats_t>(); -} - -core_device_t::core_device_t(core_device_t &owner, const pstring &name) - : object_t(owner.name() + "." + name) - , logic_family_t() - , netlist_ref(owner.state()) + // ------------------------------------------------------------------------- + // core_device_t + // ------------------------------------------------------------------------- + + core_device_t::core_device_t(core_device_param_t data) + : netlist_object_t(data.owner.exec(), data.name) , m_hint_deactivate(false) , m_active_outputs(*this, "m_active_outputs", 1) -{ - set_logic_family(owner.logic_family()); - if (logic_family() == nullptr) - set_logic_family(family_TTL()); - state().add_dev(this->name(), pool_owned_ptr<core_device_t>(this, false)); - if (exec().stats_enabled()) - m_stats = plib::make_unique<stats_t>(); -} - -void core_device_t::set_default_delegate(detail::core_terminal_t &term) -{ - if (!term.m_delegate.is_set()) - term.m_delegate.set(&core_device_t::update, this); -} - -log_type & core_device_t::log() -{ - return state().log(); -} - -// ---------------------------------------------------------------------------------------- -// device_t -// ---------------------------------------------------------------------------------------- - -device_t::device_t(netlist_state_t &owner, const pstring &name) -: core_device_t(owner, name) -{ -} + { + if (exec().stats_enabled()) + m_stats = state().make_pool_object<stats_t>(); + } -device_t::device_t(core_device_t &owner, const pstring &name) -: core_device_t(owner, name) -{ -} + log_type &core_device_t::log() { return state().log(); } -setup_t &device_t::setup() -{ - return state().setup(); -} + // ------------------------------------------------------------------------- + // base_device_t + // ------------------------------------------------------------------------- -const setup_t &device_t::setup() const -{ - return state().setup(); -} + base_device_t::base_device_t(base_device_param_t data) + : core_device_t(data) + { + } -void device_t::register_subalias(const pstring &name, detail::core_terminal_t &term) -{ - pstring alias = this->name() + "." + name; + void base_device_t::register_sub_alias(const pstring & name, + const detail::core_terminal_t &term) + { + pstring alias = this->name() + "." + name; - // everything already fully qualified - setup().register_alias_nofqn(alias, term.name()); -} + // everything already fully qualified + state().parser().register_fqn_alias(detail::alias_type::FUNCTIONAL, + alias, term.name()); + } -void device_t::register_subalias(const pstring &name, const pstring &aliased) -{ - pstring alias = this->name() + "." + name; - pstring aliased_fqn = this->name() + "." + aliased; + void base_device_t::register_sub_alias(const pstring &name, + const pstring &aliased) + { + pstring alias = this->name() + "." + name; + pstring aliased_fqn = this->name() + "." + aliased; - // everything already fully qualified - setup().register_alias_nofqn(alias, aliased_fqn); -} + // everything already fully qualified + state().parser().register_fqn_alias(detail::alias_type::FUNCTIONAL, + alias, aliased_fqn); + } -void device_t::connect(detail::core_terminal_t &t1, detail::core_terminal_t &t2) -{ - setup().register_link_fqn(t1.name(), t2.name()); -} + void base_device_t::connect(const detail::core_terminal_t &t1, + const detail::core_terminal_t &t2) + { + state().parser().register_connection_fqn(t1.name(), t2.name()); + } -void device_t::connect(const pstring &t1, const pstring &t2) -{ - setup().register_link_fqn(name() + "." + t1, name() + "." + t2); -} + void base_device_t::connect(const pstring &t1, const pstring &t2) + { + state().parser().register_connection_fqn(name() + "." + t1, + name() + "." + t2); + } -/* FIXME: this is only used by solver code since matrix solvers are started in - * post_start. - */ -void device_t::connect_post_start(detail::core_terminal_t &t1, detail::core_terminal_t &t2) -{ - if (!setup().connect(t1, t2)) - log().fatal(MF_ERROR_CONNECTING_1_TO_2(t1.name(), t2.name())); -} + // ------------------------------------------------------------------------- + // device_t + // ------------------------------------------------------------------------- + device_t::device_t(device_param_t data) + : base_device_t(data) + , m_model(*this, "MODEL", pstring(config::DEFAULT_LOGIC_FAMILY())) + { + set_logic_family(state().setup().family_from_model(m_model())); + if (logic_family() == nullptr) + throw nl_exception(MF_NULLPTR_FAMILY(this->name(), m_model())); + } -// ----------------------------------------------------------------------------- -// family_setter_t -// ----------------------------------------------------------------------------- + device_t::device_t(device_param_t data, const pstring &model) + : base_device_t(data) + , m_model(*this, "MODEL", model) + { + set_logic_family(state().setup().family_from_model(m_model())); + if (logic_family() == nullptr) + throw nl_exception(MF_NULLPTR_FAMILY(this->name(), m_model())); + } -// NOLINTNEXTLINE(modernize-use-equals-default) -detail::family_setter_t::family_setter_t() -{ -} + device_t::device_t(device_param_t data, const logic_family_desc_t *desc) + : base_device_t(data) + , m_model(*this, "MODEL", pstring("")) + { + set_logic_family(desc); + if (logic_family() == nullptr) + throw nl_exception(MF_NULLPTR_FAMILY( + this->name(), "<pointer provided by constructor>")); + } -detail::family_setter_t::family_setter_t(core_device_t &dev, const pstring &desc) -{ - dev.set_logic_family(dev.setup().family_from_model(desc)); -} + // ------------------------------------------------------------------------- + // analog_t + // ------------------------------------------------------------------------- -detail::family_setter_t::family_setter_t(core_device_t &dev, const logic_family_desc_t *desc) -{ - dev.set_logic_family(desc); -} + analog_t::analog_t(core_device_t &dev, const pstring &aname, + const state_e state, nl_delegate delegate) + : core_terminal_t(dev, aname, state, delegate) + { + } -// ---------------------------------------------------------------------------------------- -// net_t -// ---------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // net_t + // ------------------------------------------------------------------------- -detail::net_t::net_t(netlist_state_t &nl, const pstring &aname, core_terminal_t *mr) - : object_t(aname) - , netlist_ref(nl) - , m_new_Q(*this, "m_new_Q", 0) - , m_cur_Q (*this, "m_cur_Q", 0) + detail::net_t::net_t(netlist_state_t &nl, const pstring &aname, + core_terminal_t *rail_terminal) + : netlist_object_t(nl.exec(), aname) + , m_new_Q(*this, "m_new_Q", netlist_sig_t(0)) + , m_cur_Q(*this, "m_cur_Q", netlist_sig_t(0)) , m_in_queue(*this, "m_in_queue", queue_status::DELIVERED) - , m_next_scheduled_time(*this, "m_time", netlist_time::zero()) - , m_railterminal(mr) -{ -} - -void detail::net_t::rebuild_list() -{ - /* rebuild m_list */ + , m_next_scheduled_time(*this, "m_time", netlist_time_ext::zero()) + , m_rail_terminal(rail_terminal) + { + props::add(this, props::value_type()); + } - m_list_active.clear(); - for (auto & term : m_core_terms) - if (term->terminal_state() != logic_t::STATE_INP_PASSIVE) - { - m_list_active.push_back(term); - term->set_copied_input(m_cur_Q); - } -} + bool detail::net_t::is_logic() const noexcept + { + return bool(plib::dynamic_downcast<const logic_net_t *>(this)); + } -template <bool KEEP_STATS, typename T> -void detail::net_t::process(const T mask, netlist_sig_t sig) -{ - m_cur_Q = sig; + bool detail::net_t::is_analog() const noexcept + { + return bool(plib::dynamic_downcast<const analog_net_t *>(this)); + } - if (KEEP_STATS) + void detail::net_t::rebuild_list() { - for (auto & p : m_list_active) - { - p.set_copied_input(sig); - auto *stats = p.device().m_stats.get(); - stats->m_stat_call_count.inc(); - if ((p.terminal_state() & mask)) + // rebuild m_list + + m_list_active.clear(); + for (core_terminal_t *term : core_terms_ref()) + if (term->terminal_state() != logic_t::STATE_INP_PASSIVE) { - auto g(stats->m_stat_total_time.guard()); - p.m_delegate(); + m_list_active.push_back(term); + term->set_copied_input(m_cur_Q); } - } } - else + + void detail::net_t::reset() noexcept { - for (auto & p : m_list_active) + m_next_scheduled_time = exec().time(); + m_in_queue = queue_status::DELIVERED; + + m_new_Q = 0; + m_cur_Q = 0; + + if (auto p = plib::dynamic_downcast<analog_net_t *>(this)) + (*p)->set_Q_Analog(nlconst::zero()); + + // rebuild m_list and reset terminals to active or analog out state + + m_list_active.clear(); + for (core_terminal_t *ct : core_terms_copy()) { - p.set_copied_input(sig); - if ((p.terminal_state() & mask)) - p.m_delegate(); + ct->reset(); + if (ct->terminal_state() != logic_t::STATE_INP_PASSIVE) + m_list_active.push_back(ct); + ct->set_copied_input(m_cur_Q); } } -} -template <bool KEEP_STATS> -void detail::net_t::update_devs() NL_NOEXCEPT -{ - nl_assert(this->isRailNet()); +#if NL_USE_INPLACE_CORE_TERMS + void detail::net_t::remove_terminal(detail::core_terminal_t &term) + { + m_core_terms.remove(&term); + } + + void detail::net_t::remove_all_terminals() { m_core_terms.clear(); } - const unsigned int new_Q(m_new_Q); + void detail::net_t::add_terminal(detail::core_terminal_t &terminal) + { + for (detail::core_terminal_t *t : m_core_terms) + if (t == &terminal) + { + state().log().fatal( + MF_NET_1_DUPLICATE_TERMINAL_2(this->name(), t->name())); + throw nl_exception( + MF_NET_1_DUPLICATE_TERMINAL_2(this->name(), t->name())); + } - const unsigned int mask((new_Q << core_terminal_t::INP_LH_SHIFT) - | (m_cur_Q << core_terminal_t::INP_HL_SHIFT)); + terminal.set_net(this); + + m_core_terms.push_back(&terminal); + } - m_in_queue = queue_status::DELIVERED; /* mark as taken ... */ - switch (mask) +#else + void detail::net_t::remove_terminal(detail::core_terminal_t &term) { - case core_terminal_t::STATE_INP_HL: - case core_terminal_t::STATE_INP_LH: - process<KEEP_STATS>(mask | core_terminal_t::STATE_INP_ACTIVE, new_Q); - break; - default: - /* do nothing */ - break; + // net.core_terms().remove(p); + for (auto pp = state().core_terms(*this).begin(); + pp != state().core_terms(*this).end(); pp++) + if (*pp == &term) + { + state().core_terms(*this).erase(pp); + break; + } } -} -void detail::net_t::reset() -{ - m_next_scheduled_time = netlist_time::zero(); - m_in_queue = queue_status::DELIVERED; + void detail::net_t::remove_all_terminals() + { + state().core_terms(*this).clear(); + } - m_new_Q = 0; - m_cur_Q = 0; + void detail::net_t::add_terminal(detail::core_terminal_t &terminal) + { + for (detail::core_terminal_t *t : core_terms_ref()) + if (t == &terminal) + { + state().log().fatal( + MF_NET_1_DUPLICATE_TERMINAL_2(this->name(), t->name())); + throw nl_exception( + MF_NET_1_DUPLICATE_TERMINAL_2(this->name(), t->name())); + } - auto *p = dynamic_cast<analog_net_t *>(this); + terminal.set_net(this); - if (p != nullptr) - p->m_cur_Analog = 0.0; + state().core_terms(*this).push_back(&terminal); + } +#endif - /* rebuild m_list and reset terminals to active or analog out state */ + // ------------------------------------------------------------------------- + // logic_net_t + // ------------------------------------------------------------------------- - m_list_active.clear(); - for (core_terminal_t *ct : m_core_terms) + logic_net_t::logic_net_t(netlist_state_t &nl, const pstring &aname, + detail::core_terminal_t *rail_terminal) + : net_t(nl, aname, rail_terminal) { - ct->reset(); - if (ct->terminal_state() != logic_t::STATE_INP_PASSIVE) - m_list_active.push_back(ct); - ct->set_copied_input(m_cur_Q); } -} -void detail::net_t::add_terminal(detail::core_terminal_t &terminal) -{ - for (auto &t : m_core_terms) - if (t == &terminal) - state().log().fatal(MF_NET_1_DUPLICATE_TERMINAL_2(name(), t->name())); + // ------------------------------------------------------------------------- + // analog_net_t + // ------------------------------------------------------------------------- - terminal.set_net(this); + analog_net_t::analog_net_t(netlist_state_t &nl, const pstring &aname, + detail::core_terminal_t *rail_terminal) + : net_t(nl, aname, rail_terminal) + , m_cur_Analog(*this, "m_cur_Analog", nlconst::zero()) + , m_solver(nullptr) + { + } - m_core_terms.push_back(&terminal); -} + void analog_net_t::reset() noexcept + { + net_t::reset(); + m_cur_Analog = nlconst::zero(); + } -void detail::net_t::remove_terminal(detail::core_terminal_t &terminal) -{ - if (plib::container::contains(m_core_terms, &terminal)) + // ------------------------------------------------------------------------- + // core_terminal_t + // ------------------------------------------------------------------------- + + detail::core_terminal_t::core_terminal_t(core_device_t &dev, + const pstring &aname, + const state_e state, + nl_delegate delegate) + : device_object_t(&dev, dev.name() + "." + aname) + , m_Q_CIR(*this, "m_Q", 0) + , m_delegate(delegate) + , m_net(nullptr) + , m_state(*this, "m_state", state) { - terminal.set_net(nullptr); - plib::container::remove(m_core_terms, &terminal); } - else - state().log().fatal(MF_REMOVE_TERMINAL_1_FROM_NET_2(terminal.name(), this->name())); -} -void detail::net_t::move_connections(detail::net_t &dest_net) -{ - for (auto &ct : m_core_terms) - dest_net.add_terminal(*ct); - m_core_terms.clear(); -} + bool detail::core_terminal_t::is_logic() const noexcept + { + return bool(plib::dynamic_downcast<const logic_t *>(this)); + } -// ---------------------------------------------------------------------------------------- -// logic_net_t -// ---------------------------------------------------------------------------------------- + bool detail::core_terminal_t::is_logic_input() const noexcept + { + return bool(plib::dynamic_downcast<const logic_input_t *>(this)); + } -logic_net_t::logic_net_t(netlist_state_t &nl, const pstring &aname, detail::core_terminal_t *mr) - : net_t(nl, aname, mr) -{ -} + bool detail::core_terminal_t::is_logic_output() const noexcept + { + return bool(plib::dynamic_downcast<const logic_output_t *>(this)); + } -// ---------------------------------------------------------------------------------------- -// analog_net_t -// ---------------------------------------------------------------------------------------- + bool detail::core_terminal_t::is_tristate_output() const noexcept + { + return bool(plib::dynamic_downcast<const tristate_output_t *>(this)); + } -analog_net_t::analog_net_t(netlist_state_t &nl, const pstring &aname, detail::core_terminal_t *mr) - : net_t(nl, aname, mr) - , m_cur_Analog(*this, "m_cur_Analog", 0.0) - , m_solver(nullptr) -{ -} - -// ---------------------------------------------------------------------------------------- -// core_terminal_t -// ---------------------------------------------------------------------------------------- - -detail::core_terminal_t::core_terminal_t(core_device_t &dev, const pstring &aname, - const state_e state, nldelegate delegate) -: device_object_t(dev, dev.name() + "." + aname) -, plib::linkedlist_t<core_terminal_t>::element_t() -, m_delegate(delegate) -#if USE_COPY_INSTEAD_OF_REFERENCE -, m_Q(*this, "m_Q", 0) -#endif -, m_net(nullptr) -, m_state(*this, "m_state", state) -{ -} + bool detail::core_terminal_t::is_analog() const noexcept + { + return bool(plib::dynamic_downcast<const analog_t *>(this)); + } -analog_t::analog_t(core_device_t &dev, const pstring &aname, const state_e state, - nldelegate delegate) -: core_terminal_t(dev, aname, state, delegate) -{ -} + bool detail::core_terminal_t::is_analog_input() const noexcept + { + return bool(plib::dynamic_downcast<const analog_input_t *>(this)); + } -logic_t::logic_t(core_device_t &dev, const pstring &aname, const state_e state, - nldelegate delegate) - : core_terminal_t(dev, aname, state, delegate) - , logic_family_t() - , m_proxy(nullptr) -{ -} - -// ---------------------------------------------------------------------------------------- -// terminal_t -// ---------------------------------------------------------------------------------------- - -terminal_t::terminal_t(core_device_t &dev, const pstring &aname, terminal_t *otherterm) -: analog_t(dev, aname, STATE_BIDIR) -, m_Idr1(nullptr) -, m_go1(nullptr) -, m_gt1(nullptr) -, m_connected_terminal(otherterm) -{ - state().setup().register_term(*this); -} + bool detail::core_terminal_t::is_analog_output() const noexcept + { + return bool(plib::dynamic_downcast<const analog_output_t *>(this)); + } -void terminal_t::solve_now() -{ - // Nets may belong to railnets which do not have a solver attached - if (this->has_net()) - if (net().solver() != nullptr) - net().solver()->update_forced(); -} + // ------------------------------------------------------------------------- + // terminal_t + // ------------------------------------------------------------------------- -void terminal_t::schedule_solve_after(const netlist_time after) -{ - // Nets may belong to railnets which do not have a solver attached - if (this->has_net()) - if (net().solver() != nullptr) - net().solver()->update_after(after); -} - -// ---------------------------------------------------------------------------------------- -// net_input_t -// ---------------------------------------------------------------------------------------- - -// ---------------------------------------------------------------------------------------- -// net_output_t -// ---------------------------------------------------------------------------------------- - -// ---------------------------------------------------------------------------------------- -// logic_output_t -// ---------------------------------------------------------------------------------------- - -logic_output_t::logic_output_t(core_device_t &dev, const pstring &aname) - : logic_t(dev, aname, STATE_OUT) - , m_my_net(dev.state(), name() + ".net", this) -{ - this->set_net(&m_my_net); - state().register_net(pool_owned_ptr<logic_net_t>(&m_my_net, false)); - set_logic_family(dev.logic_family()); - state().setup().register_term(*this); -} + terminal_t::terminal_t(core_device_t &dev, const pstring &aname, + terminal_t *other_terminal, nl_delegate delegate) + : terminal_t(dev, aname, other_terminal, {nullptr, nullptr}, delegate) + { + } -void logic_output_t::initial(const netlist_sig_t val) -{ - if (has_net()) - net().initial(val); -} + terminal_t::terminal_t(core_device_t &dev, const pstring &aname, + terminal_t * other_terminal, + const std::array<terminal_t *, 2> &splitter_terms, + nl_delegate delegate) + : analog_t(dev, aname, STATE_BIDIR, delegate) + , m_Idr(nullptr) + , m_go(nullptr) + , m_gt(nullptr) + { + state().setup().register_term(*this, other_terminal, splitter_terms); + } -// ---------------------------------------------------------------------------------------- -// analog_input_t -// ---------------------------------------------------------------------------------------- + void terminal_t::set_ptrs(nl_fptype *gt, nl_fptype *go, + nl_fptype *Idr) noexcept(false) + { + // NOLINTNEXTLINE(readability-implicit-bool-conversion) + if (!(gt && go && Idr) && (gt || go || Idr)) + { + throw nl_exception( + "Either all pointers must be set or none for terminal {}", + name()); + } -analog_input_t::analog_input_t(core_device_t &dev, const pstring &aname, - nldelegate delegate) -: analog_t(dev, aname, STATE_INP_ACTIVE, delegate) -{ - state().setup().register_term(*this); -} + m_gt = gt; + m_go = go; + m_Idr = Idr; + } -// ---------------------------------------------------------------------------------------- -// analog_output_t -// ---------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // logic_t + // ------------------------------------------------------------------------- -analog_output_t::analog_output_t(core_device_t &dev, const pstring &aname) - : analog_t(dev, aname, STATE_OUT) - , m_my_net(dev.state(), name() + ".net", this) -{ - state().register_net(pool_owned_ptr<analog_net_t>(&m_my_net, false)); - this->set_net(&m_my_net); + logic_t::logic_t(device_t &dev, const pstring &aname, + const state_e terminal_state, nl_delegate delegate) + : core_terminal_t(dev, aname, terminal_state, delegate) + , logic_family_t(dev.logic_family()) + { + } - //net().m_cur_Analog = NL_FCONST(0.0); - state().setup().register_term(*this); -} + // ------------------------------------------------------------------------- + // logic_input_t + // ------------------------------------------------------------------------- -void analog_output_t::initial(const nl_double val) -{ - net().set_Q_Analog(val); -} + logic_input_t::logic_input_t(device_t &dev, const pstring &aname, + nl_delegate delegate) + : logic_t(dev, aname, STATE_INP_ACTIVE, delegate) + { + state().setup().register_term(*this); + } -// ----------------------------------------------------------------------------- -// logic_input_t -// ----------------------------------------------------------------------------- + // ------------------------------------------------------------------------- + // logic_output_t + // ------------------------------------------------------------------------- -logic_input_t::logic_input_t(core_device_t &dev, const pstring &aname, - nldelegate delegate) - : logic_t(dev, aname, STATE_INP_ACTIVE, delegate) -{ - set_logic_family(dev.logic_family()); - state().setup().register_term(*this); -} + logic_output_t::logic_output_t(device_t &dev, const pstring &aname, + [[maybe_unused]] bool dummy) + : logic_t(dev, aname, STATE_OUT, nl_delegate()) + , m_my_net(dev.state(), name() + ".net", this) + { + this->set_net(&m_my_net); + state().register_net( + device_arena::owned_ptr<logic_net_t>(&m_my_net, false)); + state().setup().register_term(*this); + } -// ---------------------------------------------------------------------------------------- -// Parameters ... -// ---------------------------------------------------------------------------------------- + void logic_output_t::initial(const netlist_sig_t val) noexcept + { + if (has_net()) + net().initial(val); + } -param_t::param_t(device_t &device, const pstring &name) - : device_object_t(device, device.name() + "." + name) -{ - device.setup().register_param_t(this->name(), *this); -} + // ------------------------------------------------------------------------- + // tristate_output_t + // ------------------------------------------------------------------------- + + tristate_output_t::tristate_output_t(device_t &dev, const pstring &aname, + bool force_logic) + : logic_output_t(dev, aname) + , m_last_logic(dev, name() + "." + "m_last_logic", 1) // force change + , m_tristate(dev, name() + "." + "m_tristate", + force_logic ? 0 : 2) // force change + , m_force_logic(force_logic) + { + } -param_t::param_type_t param_t::param_type() const -{ - if (dynamic_cast<const param_str_t *>(this) != nullptr) - return STRING; - else if (dynamic_cast<const param_double_t *>(this) != nullptr) - return DOUBLE; - else if (dynamic_cast<const param_int_t *>(this) != nullptr) - return INTEGER; - else if (dynamic_cast<const param_logic_t *>(this) != nullptr) - return LOGIC; - else if (dynamic_cast<const param_ptr_t *>(this) != nullptr) - return POINTER; - else + // ------------------------------------------------------------------------- + // analog_input_t + // ------------------------------------------------------------------------- + + analog_input_t::analog_input_t(core_device_t &dev, const pstring &aname, + nl_delegate delegate) + : analog_t(dev, aname, STATE_INP_ACTIVE, delegate) { - state().log().fatal(MF_UNKNOWN_PARAM_TYPE(name())); - return POINTER; /* Please compiler */ + state().setup().register_term(*this); } -} + // ------------------------------------------------------------------------- + // analog_output_t + // ------------------------------------------------------------------------- -void param_t::update_param() -{ - device().update_param(); -} + analog_output_t::analog_output_t(core_device_t &dev, const pstring &aname) + : analog_t(dev, aname, STATE_OUT, nl_delegate()) + , m_my_net(dev.state(), name() + ".net", this) + { + state().register_net( + device_arena::owned_ptr<analog_net_t>(&m_my_net, false)); + this->set_net(&m_my_net); -pstring param_t::get_initial(const device_t &dev, bool *found) -{ - pstring res = dev.setup().get_initial_param_val(this->name(), ""); - *found = (res != ""); - return res; -} + // net().m_cur_Analog = NL_FCONST(0.0); + state().setup().register_term(*this); + } -const pstring param_model_t::type() -{ - return state().setup().models().type(str()); -} + void analog_output_t::initial(nl_fptype val) noexcept + { + net().set_Q_Analog(val); + } -param_str_t::param_str_t(device_t &device, const pstring &name, const pstring &val) -: param_t(device, name) -{ - m_param = device.setup().get_initial_param_val(this->name(),val); -} + // ------------------------------------------------------------------------- + // Parameters ... + // ------------------------------------------------------------------------- -void param_str_t::changed() -{ -} + // device-less, it's the responsibility of the owner to register! + param_t::param_t(const pstring &name) + : device_object_t(nullptr, name) + { + } -param_ptr_t::param_ptr_t(device_t &device, const pstring &name, uint8_t * val) -: param_t(device, name) -{ - m_param = val; //device.setup().get_initial_param_val(this->name(),val); - //netlist().save(*this, m_param, "m_param"); -} + param_t::param_t(core_device_t &device, const pstring &name) + : device_object_t(&device, device.name() + "." + name) + { + device.state().setup().register_param_t(*this); + } -void param_model_t::changed() -{ - state().log().fatal(MF_MODEL_1_CAN_NOT_BE_CHANGED_AT_RUNTIME(name())); -} + // placed here to avoid weak vtable warnings + param_t::~param_t() noexcept = default; -const pstring param_model_t::value_str(const pstring &entity) -{ - return state().setup().models().value_str(str(), entity); -} + param_t::param_type_t param_t::param_type() const noexcept(false) + { + if (plib::dynamic_downcast<const param_str_t *>(this)) + return STRING; + if (plib::dynamic_downcast<const param_fp_t *>(this)) + return DOUBLE; + if (plib::dynamic_downcast<const param_int_t *>(this)) + return INTEGER; + if (plib::dynamic_downcast<const param_logic_t *>(this)) + return LOGIC; + if (plib::dynamic_downcast<const param_ptr_t *>(this)) + return POINTER; -nl_double param_model_t::value(const pstring &entity) -{ - return state().setup().models().value(str(), entity); -} + state().log().fatal(MF_UNKNOWN_PARAM_TYPE(name())); + throw nl_exception(MF_UNKNOWN_PARAM_TYPE(name())); + } + pstring param_t::get_initial(const core_device_t *dev, bool *found) const + { + pstring res = dev->state().setup().get_initial_param_val(this->name(), + ""); + *found = (!res.empty()); + return res; + } -plib::unique_ptr<std::istream> param_data_t::stream() -{ - return device().setup().get_data_stream(str()); -} + param_str_t::param_str_t(core_device_t &device, const pstring &name, + const pstring &val) + : param_t(device, name) + { + m_param = plib::make_unique<pstring, host_arena>(val); + *m_param = device.state().setup().get_initial_param_val(this->name(), + val); + } - bool detail::core_terminal_t::is_logic() const NL_NOEXCEPT + param_str_t::param_str_t(netlist_state_t &state, const pstring &name, + const pstring &val) + : param_t(name) { - return dynamic_cast<const logic_t *>(this) != nullptr; + // device-less parameter, no registration, owner is responsible + m_param = plib::make_unique<pstring, host_arena>(val); + *m_param = state.setup().get_initial_param_val(this->name(), val); } - bool detail::core_terminal_t::is_logic_input() const NL_NOEXCEPT + void param_str_t::changed() noexcept {} + + param_ptr_t::param_ptr_t(core_device_t &device, const pstring &name, + uint8_t *val) + : param_t(device, name) + , m_param(val) { - return dynamic_cast<const logic_input_t *>(this) != nullptr; } - bool detail::core_terminal_t::is_logic_output() const NL_NOEXCEPT + void param_model_t::changed() noexcept {} + + pstring param_model_t::type() { - return dynamic_cast<const logic_output_t *>(this) != nullptr; + auto mod = state().setup().models().get_model(str()); + return mod.type(); } - bool detail::core_terminal_t::is_analog() const NL_NOEXCEPT + pstring param_model_t::value_str(const pstring &entity) { - return dynamic_cast<const analog_t *>(this) != nullptr; + return state().setup().models().get_model(str()).value_str(entity); } - bool detail::core_terminal_t::is_analog_input() const NL_NOEXCEPT + nl_fptype param_model_t::value(const pstring &entity) { - return dynamic_cast<const analog_input_t *>(this) != nullptr; + return state().setup().models().get_model(str()).value(entity); } - bool detail::core_terminal_t::is_analog_output() const NL_NOEXCEPT + plib::istream_uptr param_data_t::stream() { - return dynamic_cast<const analog_output_t *>(this) != nullptr; + return device().state().parser().get_data_stream(str()); } + // ------------------------------------------------------------------------- + // netlist_t + // + // Hot section + // + // Any changes below will impact performance. + // ------------------------------------------------------------------------- - bool detail::net_t::is_logic() const NL_NOEXCEPT + template <bool KEEP_STATS> + void netlist_t::process_queue_stats(const netlist_time_ext delta) noexcept { - return dynamic_cast<const logic_net_t *>(this) != nullptr; + netlist_time_ext stop(m_time + delta); + + queue_push(stop, nullptr); + + if (m_main_clock == 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_main_clock->m_Q.net()); + const netlist_time inc(m_main_clock->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(); + detail::net_t *const obj(top->object()); + m_queue.pop(); + + if (!!(obj == nullptr)) + break; + + obj->template update_devs<KEEP_STATS>(); + + if (KEEP_STATS) + m_perf_out_processed.inc(); + } while (true); + + mc_net.set_next_scheduled_time(mc_time); + } } - bool detail::net_t::is_analog() const NL_NOEXCEPT + void netlist_t::process_queue(netlist_time_ext delta) noexcept { - return dynamic_cast<const analog_net_t *>(this) != nullptr; + 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>; + template struct state_var<std::uint64_t>; + template struct state_var<std::int8_t>; + template struct state_var<std::int16_t>; + template struct state_var<std::int32_t>; + template struct state_var<std::int64_t>; + template struct state_var<bool>; + + template class param_num_t<std::uint8_t>; + template class param_num_t<std::uint16_t>; + template class param_num_t<std::uint32_t>; + template class param_num_t<std::uint64_t>; + template class param_num_t<std::int8_t>; + template class param_num_t<std::int16_t>; + template class param_num_t<std::int32_t>; + template class param_num_t<std::int64_t>; + template class param_num_t<long double>; + template class param_num_t<double>; + template class param_num_t<float>; + template class param_num_t<bool>; + + template class param_model_t::value_base_t<float>; + template class param_model_t::value_base_t<double>; + template class param_model_t::value_base_t<long double>; + + template class object_array_t<logic_input_t, 1>; + template class object_array_t<logic_input_t, 2>; + template class object_array_t<logic_input_t, 3>; + template class object_array_t<logic_input_t, 4>; + template class object_array_t<logic_input_t, 5>; + template class object_array_t<logic_input_t, 6>; + template class object_array_t<logic_input_t, 7>; + template class object_array_t<logic_input_t, 8>; + + template class object_array_t<logic_output_t, 1>; + template class object_array_t<logic_output_t, 2>; + template class object_array_t<logic_output_t, 3>; + template class object_array_t<logic_output_t, 4>; + template class object_array_t<logic_output_t, 5>; + template class object_array_t<logic_output_t, 6>; + template class object_array_t<logic_output_t, 7>; + template class object_array_t<logic_output_t, 8>; } // namespace netlist |