mirror of
https://github.com/holub/mame
synced 2025-06-18 02:08:56 +03:00

This removes all allocation code from pstring. const_iterator is consequently now based on pstring::const_iterator. Removed pstring_buffer. This was class wasn't a good idea. Vas was right: This change did not impact runtime performance. Startup performance (string intensive) increased. (nw)
1322 lines
33 KiB
C++
1322 lines
33 KiB
C++
// license:GPL-2.0+
|
|
// copyright-holders:Couriersud
|
|
/*
|
|
* nlbase.c
|
|
*
|
|
*/
|
|
|
|
#include "solver/nld_matrix_solver.h"
|
|
#include "solver/nld_solver.h"
|
|
|
|
#include "plib/putil.h"
|
|
#include "plib/palloc.h"
|
|
|
|
#include "nl_base.h"
|
|
#include "devices/nlid_system.h"
|
|
#include "devices/nlid_proxy.h"
|
|
#include "macro/nlm_base.h"
|
|
|
|
#include "nl_errstr.h"
|
|
|
|
#include <cstring>
|
|
#include <cmath>
|
|
#include <limits>
|
|
|
|
namespace netlist
|
|
{
|
|
namespace detail
|
|
{
|
|
|
|
static plib::mempool *pool()
|
|
{
|
|
static plib::mempool *s_pool = nullptr;
|
|
if (s_pool == nullptr)
|
|
s_pool = new plib::mempool(65536, 16);
|
|
return s_pool;
|
|
}
|
|
|
|
void * object_t::operator new (size_t size)
|
|
{
|
|
void *ret = nullptr;
|
|
if ((USE_MEMPOOL))
|
|
ret = pool()->alloc(size);
|
|
else
|
|
ret = ::operator new(size);
|
|
return ret;
|
|
}
|
|
|
|
void object_t::operator delete (void * mem)
|
|
{
|
|
if (mem)
|
|
{
|
|
if ((USE_MEMPOOL))
|
|
pool()->free(mem);
|
|
else
|
|
::operator delete(mem);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
nl_exception::~nl_exception()
|
|
{
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// logic_family_ttl_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
logic_family_desc_t::logic_family_desc_t()
|
|
{
|
|
}
|
|
|
|
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;
|
|
}
|
|
virtual plib::owned_ptr<devices::nld_base_d_to_a_proxy> create_d_a_proxy(netlist_t &anetlist, const pstring &name, logic_output_t *proxied) const override;
|
|
virtual plib::owned_ptr<devices::nld_base_a_to_d_proxy> create_a_d_proxy(netlist_t &anetlist, const pstring &name, logic_input_t *proxied) const override;
|
|
};
|
|
|
|
plib::owned_ptr<devices::nld_base_d_to_a_proxy> logic_family_ttl_t::create_d_a_proxy(netlist_t &anetlist, const pstring &name, logic_output_t *proxied) const
|
|
{
|
|
return plib::owned_ptr<devices::nld_base_d_to_a_proxy>::Create<devices::nld_d_to_a_proxy>(anetlist, name, proxied);
|
|
}
|
|
plib::owned_ptr<devices::nld_base_a_to_d_proxy> logic_family_ttl_t::create_a_d_proxy(netlist_t &anetlist, const pstring &name, logic_input_t *proxied) const
|
|
{
|
|
return plib::owned_ptr<devices::nld_base_a_to_d_proxy>::Create<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;
|
|
}
|
|
virtual plib::owned_ptr<devices::nld_base_d_to_a_proxy> create_d_a_proxy(netlist_t &anetlist, const pstring &name, logic_output_t *proxied) const override;
|
|
virtual plib::owned_ptr<devices::nld_base_a_to_d_proxy> create_a_d_proxy(netlist_t &anetlist, const pstring &name, logic_input_t *proxied) const override;
|
|
};
|
|
|
|
plib::owned_ptr<devices::nld_base_d_to_a_proxy> logic_family_cd4xxx_t::create_d_a_proxy(netlist_t &anetlist, const pstring &name, logic_output_t *proxied) const
|
|
{
|
|
return plib::owned_ptr<devices::nld_base_d_to_a_proxy>::Create<devices::nld_d_to_a_proxy>(anetlist, name, proxied);
|
|
}
|
|
plib::owned_ptr<devices::nld_base_a_to_d_proxy> logic_family_cd4xxx_t::create_a_d_proxy(netlist_t &anetlist, const pstring &name, logic_input_t *proxied) const
|
|
{
|
|
return plib::owned_ptr<devices::nld_base_a_to_d_proxy>::Create<devices::nld_a_to_d_proxy>(anetlist, name, proxied);
|
|
}
|
|
|
|
const logic_family_desc_t *family_TTL()
|
|
{
|
|
static logic_family_ttl_t obj;
|
|
return &obj;
|
|
}
|
|
const logic_family_desc_t *family_CD4XXX()
|
|
{
|
|
static logic_family_cd4xxx_t obj;
|
|
return &obj;
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// queue_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
detail::queue_t::queue_t(netlist_t &nl)
|
|
: timed_queue<pqentry_t<net_t *, netlist_time>>(512)
|
|
, netlist_ref(nl)
|
|
, plib::state_manager_t::callback_t()
|
|
, m_qsize(0)
|
|
, m_times(512)
|
|
, m_net_ids(512)
|
|
{
|
|
}
|
|
|
|
void detail::queue_t::register_state(plib::state_manager_t &manager, const pstring &module)
|
|
{
|
|
netlist().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());
|
|
}
|
|
|
|
void detail::queue_t::on_pre_save()
|
|
{
|
|
netlist().log().debug("on_pre_save\n");
|
|
m_qsize = this->size();
|
|
netlist().log().debug("current time {1} qsize {2}\n", netlist().time().as_double(), m_qsize);
|
|
for (std::size_t i = 0; i < m_qsize; i++ )
|
|
{
|
|
m_times[i] = this->listptr()[i].m_exec_time.as_raw();
|
|
m_net_ids[i] = netlist().find_net_id(this->listptr()[i].m_object);
|
|
}
|
|
}
|
|
|
|
|
|
void detail::queue_t::on_post_load()
|
|
{
|
|
this->clear();
|
|
netlist().log().debug("current time {1} qsize {2}\n", netlist().time().as_double(), m_qsize);
|
|
for (std::size_t i = 0; i < m_qsize; i++ )
|
|
{
|
|
detail::net_t *n = netlist().m_nets[m_net_ids[i]].get();
|
|
this->push(queue_t::entry_t(netlist_time::from_raw(m_times[i]),n));
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// object_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
detail::object_t::object_t(const pstring &aname)
|
|
: m_name(aname)
|
|
{
|
|
}
|
|
|
|
detail::object_t::~object_t()
|
|
{
|
|
}
|
|
|
|
const pstring &detail::object_t::name() const
|
|
{
|
|
return m_name;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// device_object_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
detail::device_object_t::device_object_t(core_device_t &dev, const pstring &aname)
|
|
: object_t(aname)
|
|
, m_device(dev)
|
|
{
|
|
}
|
|
|
|
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
|
|
{
|
|
netlist().log().fatal(MF_1_UNKNOWN_TYPE_FOR_OBJECT, name());
|
|
return terminal_type::TERMINAL; // please compiler
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// netlist_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
netlist_t::netlist_t(const pstring &aname)
|
|
: m_time(netlist_time::zero())
|
|
, m_queue(*this)
|
|
, m_mainclock(nullptr)
|
|
, m_solver(nullptr)
|
|
, m_params(nullptr)
|
|
, m_name(aname)
|
|
, m_log(this)
|
|
, m_lib(nullptr)
|
|
, m_state()
|
|
{
|
|
state().save_item(this, static_cast<plib::state_manager_t::callback_t &>(m_queue), "m_queue");
|
|
state().save_item(this, m_time, "m_time");
|
|
m_setup = plib::make_unique<setup_t>(*this);
|
|
/* FIXME: doesn't really belong here */
|
|
NETLIST_NAME(base)(*m_setup);
|
|
}
|
|
|
|
netlist_t::~netlist_t()
|
|
{
|
|
m_nets.clear();
|
|
m_devices.clear();
|
|
}
|
|
|
|
nl_double netlist_t::gmin() const
|
|
{
|
|
return solver()->gmin();
|
|
}
|
|
|
|
void netlist_t::register_dev(plib::owned_ptr<core_device_t> dev)
|
|
{
|
|
for (auto & d : m_devices)
|
|
if (d->name() == dev->name())
|
|
log().fatal(MF_1_DUPLICATE_NAME_DEVICE_LIST, d->name());
|
|
m_devices.push_back(std::move(dev));
|
|
}
|
|
|
|
void netlist_t::remove_dev(core_device_t *dev)
|
|
{
|
|
m_devices.erase(
|
|
std::remove_if(
|
|
m_devices.begin(),
|
|
m_devices.end(),
|
|
[&] (plib::owned_ptr<core_device_t> const& p)
|
|
{
|
|
return p.get() == dev;
|
|
}),
|
|
m_devices.end()
|
|
);
|
|
}
|
|
|
|
|
|
|
|
void netlist_t::start()
|
|
{
|
|
setup().start_devices();
|
|
|
|
/* load the library ... */
|
|
|
|
/* make sure the solver and parameters are started first! */
|
|
|
|
for (auto & e : setup().m_device_factory)
|
|
{
|
|
if ( setup().factory().is_class<devices::NETLIB_NAME(solver)>(e.second)
|
|
|| setup().factory().is_class<devices::NETLIB_NAME(netlistparams)>(e.second))
|
|
{
|
|
auto dev = plib::owned_ptr<device_t>(e.second->Create(*this, e.first));
|
|
register_dev(std::move(dev));
|
|
}
|
|
}
|
|
|
|
log().debug("Searching for mainclock and solver ...\n");
|
|
|
|
m_solver = get_single_device<devices::NETLIB_NAME(solver)>("solver");
|
|
m_params = get_single_device<devices::NETLIB_NAME(netlistparams)>("parameter");
|
|
|
|
/* create devices */
|
|
|
|
log().debug("Creating devices ...\n");
|
|
for (auto & e : setup().m_device_factory)
|
|
{
|
|
if ( !setup().factory().is_class<devices::NETLIB_NAME(solver)>(e.second)
|
|
&& !setup().factory().is_class<devices::NETLIB_NAME(netlistparams)>(e.second))
|
|
{
|
|
auto dev = plib::owned_ptr<device_t>(e.second->Create(*this, e.first));
|
|
register_dev(std::move(dev));
|
|
}
|
|
}
|
|
|
|
log().debug("Searching for mainclock\n");
|
|
m_mainclock = get_single_device<devices::NETLIB_NAME(mainclock)>("mainclock");
|
|
|
|
bool use_deactivate = (m_params->m_use_deactivate() ? true : false);
|
|
|
|
for (auto &d : m_devices)
|
|
{
|
|
if (use_deactivate)
|
|
{
|
|
auto p = setup().m_param_values.find(d->name() + ".HINT_NO_DEACTIVATE");
|
|
if (p != setup().m_param_values.end())
|
|
{
|
|
//FIXME: turn this into a proper function
|
|
bool error;
|
|
auto v = p->second.as_double(&error);
|
|
if (error || std::abs(v - std::floor(v)) > 1e-6 )
|
|
log().fatal(MF_1_HND_VAL_NOT_SUPPORTED, p->second);
|
|
d->set_hint_deactivate(v == 0.0);
|
|
}
|
|
}
|
|
else
|
|
d->set_hint_deactivate(false);
|
|
}
|
|
|
|
pstring libpath = plib::util::environment("NL_BOOSTLIB", plib::util::buildpath({".", "nlboost.so"}));
|
|
m_lib = plib::make_unique<plib::dynlib>(libpath);
|
|
|
|
/* resolve inputs */
|
|
setup().resolve_inputs();
|
|
|
|
log().verbose("looking for two terms connected to rail nets ...");
|
|
for (auto & t : get_device_list<analog::NETLIB_NAME(twoterm)>())
|
|
{
|
|
if (t->m_N.net().isRailNet() && t->m_P.net().isRailNet())
|
|
{
|
|
log().warning(MW_3_REMOVE_DEVICE_1_CONNECTED_ONLY_TO_RAILS_2_3,
|
|
t->name(), t->m_N.net().name(), t->m_P.net().name());
|
|
t->m_N.net().remove_terminal(t->m_N);
|
|
t->m_P.net().remove_terminal(t->m_P);
|
|
remove_dev(t);
|
|
}
|
|
}
|
|
|
|
log().verbose("initialize solver ...\n");
|
|
|
|
if (m_solver == nullptr)
|
|
{
|
|
for (auto &p : m_nets)
|
|
if (p->is_analog())
|
|
log().fatal(MF_0_NO_SOLVER);
|
|
}
|
|
else
|
|
m_solver->post_start();
|
|
|
|
for (auto &n : m_nets)
|
|
for (auto & term : n->m_core_terms)
|
|
{
|
|
//core_device_t *dev = reinterpret_cast<core_device_t *>(term->m_delegate.object());
|
|
core_device_t *dev = &term->device();
|
|
dev->set_default_delegate(*term);
|
|
}
|
|
|
|
}
|
|
|
|
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_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_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_t::rebuild_lists()
|
|
{
|
|
for (auto & net : m_nets)
|
|
net->rebuild_list();
|
|
}
|
|
|
|
|
|
void netlist_t::reset()
|
|
{
|
|
m_time = netlist_time::zero();
|
|
m_queue.clear();
|
|
if (m_mainclock != nullptr)
|
|
m_mainclock->m_Q.net().set_time(netlist_time::zero());
|
|
//if (m_solver != nullptr)
|
|
// m_solver->do_reset();
|
|
|
|
std::unordered_map<core_device_t *, bool> m;
|
|
|
|
for (auto &d : m_devices)
|
|
{
|
|
m[d.get()] = d->get_hint_deactivate();
|
|
}
|
|
|
|
|
|
// Reset all nets once !
|
|
for (auto & n : m_nets)
|
|
n->reset();
|
|
|
|
// Reset all devices once !
|
|
for (auto & dev : m_devices)
|
|
dev->do_reset();
|
|
|
|
// Make sure everything depending on parameters is set
|
|
for (auto & dev : m_devices)
|
|
dev->update_param();
|
|
|
|
// 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.
|
|
*/
|
|
|
|
const unsigned startup_strategy = 1; //! \note make this a parameter
|
|
|
|
switch (startup_strategy)
|
|
{
|
|
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->m_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();
|
|
}
|
|
core_device_t *dev = reinterpret_cast<core_device_t *>(term->m_delegate.object());
|
|
if (!plib::container::contains(d, dev))
|
|
d.push_back(dev);
|
|
}
|
|
log().verbose("Call update on devices which need parameter update:");
|
|
for (auto & dev : m_devices)
|
|
if (dev->needs_update_after_param_change())
|
|
{
|
|
if (!plib::container::contains(d, dev.get()))
|
|
{
|
|
d.push_back(dev.get());
|
|
log().verbose("\t ...{1}", dev->name());
|
|
dev->update_dev();
|
|
}
|
|
}
|
|
log().verbose("Devices not yet updated:");
|
|
for (auto &dev : m_devices)
|
|
if (!plib::container::contains(d, dev.get()))
|
|
log().verbose("\t ...{1}", dev->name());
|
|
//x->update_dev();
|
|
}
|
|
break;
|
|
case 1: // brute force backward
|
|
{
|
|
std::size_t i = m_devices.size();
|
|
while (i>0)
|
|
m_devices[--i]->update_dev();
|
|
}
|
|
break;
|
|
case 2: // brute force forward
|
|
{
|
|
for (auto &d : m_devices)
|
|
d->update_dev();
|
|
}
|
|
break;
|
|
}
|
|
|
|
#if 1
|
|
/* the above may screw up m_active and the list */
|
|
for (auto &n : m_nets)
|
|
n->rebuild_list();
|
|
#endif
|
|
}
|
|
|
|
|
|
void netlist_t::process_queue(const netlist_time &delta) NL_NOEXCEPT
|
|
{
|
|
netlist_time stop(m_time + delta);
|
|
|
|
m_queue.push(detail::queue_t::entry_t(stop, nullptr));
|
|
|
|
m_stat_mainloop.start();
|
|
|
|
if (m_mainclock == nullptr)
|
|
{
|
|
detail::queue_t::entry_t e(m_queue.top());
|
|
m_queue.pop();
|
|
m_time = e.m_exec_time;
|
|
while (e.m_object != nullptr)
|
|
{
|
|
e.m_object->update_devs();
|
|
m_perf_out_processed.inc();
|
|
e = m_queue.top();
|
|
m_queue.pop();
|
|
m_time = e.m_exec_time;
|
|
}
|
|
}
|
|
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.time());
|
|
|
|
detail::queue_t::entry_t e;
|
|
|
|
do
|
|
{
|
|
while (m_queue.top().m_exec_time > mc_time)
|
|
{
|
|
m_time = mc_time;
|
|
mc_net.toggle_new_Q();
|
|
mc_net.update_devs();
|
|
mc_time += inc;
|
|
}
|
|
|
|
e = m_queue.top();
|
|
m_queue.pop();
|
|
m_time = e.m_exec_time;
|
|
if (e.m_object != nullptr)
|
|
{
|
|
e.m_object->update_devs();
|
|
m_perf_out_processed.inc();
|
|
}
|
|
} while (e.m_object != nullptr);
|
|
mc_net.set_time(mc_time);
|
|
}
|
|
m_stat_mainloop.stop();
|
|
}
|
|
|
|
void netlist_t::print_stats() const
|
|
{
|
|
if (nperftime_t::enabled)
|
|
{
|
|
std::vector<size_t> index;
|
|
for (size_t i=0; i<m_devices.size(); i++)
|
|
index.push_back(i);
|
|
|
|
std::sort(index.begin(), index.end(),
|
|
[&](size_t i1, size_t i2) { return m_devices[i1]->m_stat_total_time.total() < m_devices[i2]->m_stat_total_time.total(); });
|
|
|
|
nperftime_t::type total_time(0);
|
|
uint_least64_t total_count(0);
|
|
|
|
for (auto & j : index)
|
|
{
|
|
auto entry = m_devices[j].get();
|
|
log().verbose("Device {1:20} : {2:12} {3:12} {4:15} {5:12}", entry->name(),
|
|
entry->m_stat_call_count(), entry->m_stat_total_time.count(),
|
|
entry->m_stat_total_time.total(), entry->m_stat_inc_active());
|
|
total_time += entry->m_stat_total_time.total();
|
|
total_count += entry->m_stat_total_time.count();
|
|
}
|
|
|
|
nperftime_t overhead;
|
|
nperftime_t test;
|
|
overhead.start();
|
|
for (int j=0; j<100000;j++)
|
|
{
|
|
test.start();
|
|
test.stop();
|
|
}
|
|
overhead.stop();
|
|
|
|
nperftime_t::type total_overhead = overhead()
|
|
* static_cast<nperftime_t::type>(total_count)
|
|
/ static_cast<nperftime_t::type>(200000);
|
|
|
|
log().verbose("Queue Pushes {1:15}", queue().m_prof_call());
|
|
log().verbose("Queue Moves {1:15}", queue().m_prof_sortmove());
|
|
|
|
log().verbose("Total loop {1:15}", m_stat_mainloop());
|
|
/* Only one serialization should be counted in total time */
|
|
/* But two are contained in m_stat_mainloop */
|
|
log().verbose("Total devices {1:15}", total_time);
|
|
log().verbose("");
|
|
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::type overhead_per_pop = (m_stat_mainloop()-2*total_overhead - (total_time - total_overhead))
|
|
/ static_cast<nperftime_t::type>(queue().m_prof_call());
|
|
log().verbose("Overhead per pop {1:11}", overhead_per_pop );
|
|
log().verbose("");
|
|
for (auto &entry : m_devices)
|
|
{
|
|
if (entry->m_stat_inc_active() > 3 * entry->m_stat_total_time.count())
|
|
log().verbose("HINT({}, NO_DEACTIVATE)", entry->name());
|
|
}
|
|
}
|
|
}
|
|
|
|
core_device_t *netlist_t::get_single_device(const pstring &classname, bool (*cc)(core_device_t *))
|
|
{
|
|
core_device_t *ret = nullptr;
|
|
for (auto &d : m_devices)
|
|
{
|
|
if (cc(d.get()))
|
|
{
|
|
if (ret != nullptr)
|
|
this->log().fatal(MF_1_MORE_THAN_ONE_1_DEVICE_FOUND, classname);
|
|
else
|
|
ret = d.get();
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// core_device_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
core_device_t::core_device_t(netlist_t &owner, const pstring &name)
|
|
: object_t(name)
|
|
, logic_family_t()
|
|
, netlist_ref(owner)
|
|
, m_hint_deactivate(false)
|
|
{
|
|
if (logic_family() == nullptr)
|
|
set_logic_family(family_TTL());
|
|
}
|
|
|
|
core_device_t::core_device_t(core_device_t &owner, const pstring &name)
|
|
: object_t(owner.name() + "." + name)
|
|
, logic_family_t()
|
|
, netlist_ref(owner.netlist())
|
|
, m_hint_deactivate(false)
|
|
{
|
|
set_logic_family(owner.logic_family());
|
|
if (logic_family() == nullptr)
|
|
set_logic_family(family_TTL());
|
|
owner.netlist().register_dev(plib::owned_ptr<core_device_t>(this, false));
|
|
}
|
|
|
|
core_device_t::~core_device_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);
|
|
}
|
|
|
|
plib::plog_base<NL_DEBUG> &core_device_t::log()
|
|
{
|
|
return netlist().log();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// device_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
device_t::device_t(netlist_t &owner, const pstring &name)
|
|
: core_device_t(owner, name)
|
|
{
|
|
}
|
|
|
|
device_t::device_t(core_device_t &owner, const pstring &name)
|
|
: core_device_t(owner, name)
|
|
{
|
|
}
|
|
|
|
device_t::~device_t()
|
|
{
|
|
//log().debug("~net_device_t\n");
|
|
}
|
|
|
|
setup_t &device_t::setup()
|
|
{
|
|
return netlist().setup();
|
|
}
|
|
|
|
void device_t::register_subalias(const pstring &name, detail::core_terminal_t &term)
|
|
{
|
|
pstring alias = this->name() + "." + name;
|
|
|
|
// everything already fully qualified
|
|
setup().register_alias_nofqn(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;
|
|
|
|
// everything already fully qualified
|
|
setup().register_alias_nofqn(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 device_t::connect(const pstring &t1, const pstring &t2)
|
|
{
|
|
setup().register_link_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_2_ERROR_CONNECTING_1_TO_2, t1.name(), t2.name());
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// family_setter_t
|
|
// -----------------------------------------------------------------------------
|
|
|
|
detail::family_setter_t::family_setter_t(core_device_t &dev, const pstring &desc)
|
|
{
|
|
dev.set_logic_family(dev.netlist().setup().family_from_model(desc));
|
|
}
|
|
|
|
detail::family_setter_t::family_setter_t(core_device_t &dev, const logic_family_desc_t *desc)
|
|
{
|
|
dev.set_logic_family(desc);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// net_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
detail::net_t::net_t(netlist_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)
|
|
, m_in_queue(*this, "m_in_queue", QS_DELIVERED)
|
|
, m_time(*this, "m_time", netlist_time::zero())
|
|
, m_active(*this, "m_active", 0)
|
|
, m_cur_Analog(*this, "m_cur_Analog", 0.0)
|
|
, m_railterminal(mr)
|
|
{
|
|
}
|
|
|
|
detail::net_t::~net_t()
|
|
{
|
|
netlist().state().remove_save_items(this);
|
|
}
|
|
|
|
void detail::net_t::inc_active(core_terminal_t &term) NL_NOEXCEPT
|
|
{
|
|
++m_active;
|
|
m_list_active.push_front(&term);
|
|
nl_assert(m_active <= static_cast<int>(num_cons()));
|
|
if (m_active == 1)
|
|
{
|
|
railterminal().device().do_inc_active();
|
|
if (m_in_queue == QS_DELAYED_DUE_TO_INACTIVE)
|
|
{
|
|
if (m_time > netlist().time())
|
|
{
|
|
m_in_queue = QS_QUEUED; /* pending */
|
|
netlist().queue().push(queue_t::entry_t(m_time, this));
|
|
}
|
|
else
|
|
{
|
|
m_in_queue = QS_DELIVERED;
|
|
m_cur_Q = m_new_Q;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void detail::net_t::dec_active(core_terminal_t &term) NL_NOEXCEPT
|
|
{
|
|
--m_active;
|
|
nl_assert(m_active >= 0);
|
|
m_list_active.remove(&term);
|
|
if (m_active == 0)
|
|
railterminal().device().do_dec_active();
|
|
}
|
|
|
|
void detail::net_t::rebuild_list()
|
|
{
|
|
/* rebuild m_list */
|
|
|
|
int cnt = 0;
|
|
m_list_active.clear();
|
|
for (auto & term : m_core_terms)
|
|
if (term->state() != logic_t::STATE_INP_PASSIVE)
|
|
{
|
|
m_list_active.push_back(term);
|
|
cnt++;
|
|
}
|
|
m_active = cnt;
|
|
}
|
|
|
|
void detail::net_t::update_devs() NL_NOEXCEPT
|
|
{
|
|
nl_assert(this->isRailNet());
|
|
|
|
const uint8_t masks[4] =
|
|
{
|
|
0,
|
|
core_terminal_t::STATE_INP_LH | core_terminal_t::STATE_INP_ACTIVE,
|
|
core_terminal_t::STATE_INP_HL | core_terminal_t::STATE_INP_ACTIVE,
|
|
0
|
|
};
|
|
|
|
const auto mask = masks[ (m_cur_Q << 1) | m_new_Q ];
|
|
|
|
m_cur_Q = m_new_Q;
|
|
m_in_queue = QS_DELIVERED; /* mark as taken ... */
|
|
|
|
for (auto & p : m_list_active)
|
|
{
|
|
p.device().m_stat_call_count.inc();
|
|
if ((p.state() & mask) != 0)
|
|
{
|
|
p.device().m_stat_total_time.start();
|
|
p.m_delegate();
|
|
p.device().m_stat_total_time.stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
void detail::net_t::reset()
|
|
{
|
|
m_time = netlist_time::zero();
|
|
m_active = 0;
|
|
m_in_queue = QS_DELIVERED;
|
|
|
|
m_new_Q = 0;
|
|
m_cur_Q = 0;
|
|
m_cur_Analog = 0.0;
|
|
|
|
/* rebuild m_list */
|
|
|
|
m_list_active.clear();
|
|
for (core_terminal_t *ct : m_core_terms)
|
|
m_list_active.push_back(ct);
|
|
|
|
for (core_terminal_t *ct : m_core_terms)
|
|
ct->reset();
|
|
|
|
for (core_terminal_t *ct : m_core_terms)
|
|
if (ct->state() != logic_t::STATE_INP_PASSIVE)
|
|
m_active++;
|
|
}
|
|
|
|
void detail::net_t::add_terminal(detail::core_terminal_t &terminal)
|
|
{
|
|
for (auto &t : m_core_terms)
|
|
if (t == &terminal)
|
|
netlist().log().fatal(MF_2_NET_1_DUPLICATE_TERMINAL_2, name(),
|
|
t->name());
|
|
|
|
terminal.set_net(this);
|
|
|
|
m_core_terms.push_back(&terminal);
|
|
|
|
if (terminal.state() != logic_t::STATE_INP_PASSIVE)
|
|
m_active++;
|
|
}
|
|
|
|
void detail::net_t::remove_terminal(detail::core_terminal_t &terminal)
|
|
{
|
|
if (plib::container::contains(m_core_terms, &terminal))
|
|
{
|
|
terminal.set_net(nullptr);
|
|
plib::container::remove(m_core_terms, &terminal);
|
|
}
|
|
else
|
|
netlist().log().fatal(MF_2_REMOVE_TERMINAL_1_FROM_NET_2, terminal.name(),
|
|
this->name());
|
|
if (terminal.state() != logic_t::STATE_INP_PASSIVE)
|
|
m_active--;
|
|
}
|
|
|
|
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();
|
|
m_active = 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// logic_net_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
logic_net_t::logic_net_t(netlist_t &nl, const pstring &aname, detail::core_terminal_t *mr)
|
|
: net_t(nl, aname, mr)
|
|
{
|
|
}
|
|
|
|
logic_net_t::~logic_net_t()
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// analog_net_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
analog_net_t::analog_net_t(netlist_t &nl, const pstring &aname, detail::core_terminal_t *mr)
|
|
: net_t(nl, aname, mr)
|
|
, m_solver(nullptr)
|
|
{
|
|
}
|
|
|
|
analog_net_t::~analog_net_t()
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// 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)
|
|
, m_net(nullptr)
|
|
, m_state(*this, "m_state", state)
|
|
{
|
|
}
|
|
|
|
detail::core_terminal_t::~core_terminal_t()
|
|
{
|
|
}
|
|
|
|
void detail::core_terminal_t::reset()
|
|
{
|
|
if (is_type(OUTPUT))
|
|
set_state(STATE_OUT);
|
|
else
|
|
set_state(STATE_INP_ACTIVE);
|
|
}
|
|
|
|
void detail::core_terminal_t::set_net(net_t *anet)
|
|
{
|
|
m_net = anet;
|
|
}
|
|
|
|
void detail::core_terminal_t::clear_net()
|
|
{
|
|
m_net = nullptr;
|
|
}
|
|
|
|
analog_t::analog_t(core_device_t &dev, const pstring &aname, const state_e state)
|
|
: core_terminal_t(dev, aname, state)
|
|
{
|
|
}
|
|
|
|
analog_t::~analog_t()
|
|
{
|
|
}
|
|
|
|
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)
|
|
{
|
|
}
|
|
|
|
logic_t::~logic_t()
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// terminal_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
terminal_t::terminal_t(core_device_t &dev, const pstring &aname)
|
|
: analog_t(dev, aname, STATE_BIDIR)
|
|
, m_otherterm(nullptr)
|
|
, m_Idr1(nullptr)
|
|
, m_go1(nullptr)
|
|
, m_gt1(nullptr)
|
|
{
|
|
netlist().setup().register_term(*this);
|
|
}
|
|
|
|
terminal_t::~terminal_t()
|
|
{
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
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.netlist(), name() + ".net", this)
|
|
{
|
|
this->set_net(&m_my_net);
|
|
netlist().m_nets.push_back(plib::owned_ptr<logic_net_t>(&m_my_net, false));
|
|
set_logic_family(dev.logic_family());
|
|
netlist().setup().register_term(*this);
|
|
}
|
|
|
|
logic_output_t::~logic_output_t()
|
|
{
|
|
}
|
|
|
|
void logic_output_t::initial(const netlist_sig_t val)
|
|
{
|
|
if (has_net())
|
|
net().initial(val);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// analog_input_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
analog_input_t::analog_input_t(core_device_t &dev, const pstring &aname)
|
|
: analog_t(dev, aname, STATE_INP_ACTIVE)
|
|
{
|
|
netlist().setup().register_term(*this);
|
|
}
|
|
|
|
analog_input_t::~analog_input_t()
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// analog_output_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
analog_output_t::analog_output_t(core_device_t &dev, const pstring &aname)
|
|
: analog_t(dev, aname, STATE_OUT)
|
|
, m_my_net(dev.netlist(), name() + ".net", this)
|
|
{
|
|
netlist().m_nets.push_back(plib::owned_ptr<analog_net_t>(&m_my_net, false));
|
|
this->set_net(&m_my_net);
|
|
|
|
//net().m_cur_Analog = NL_FCONST(0.0);
|
|
netlist().setup().register_term(*this);
|
|
}
|
|
|
|
analog_output_t::~analog_output_t()
|
|
{
|
|
}
|
|
|
|
void analog_output_t::initial(const nl_double val)
|
|
{
|
|
net().set_Q_Analog(val);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// logic_input_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());
|
|
netlist().setup().register_term(*this);
|
|
}
|
|
|
|
logic_input_t::~logic_input_t()
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// Parameters ...
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
param_t::param_t(device_t &device, const pstring &name)
|
|
: device_object_t(device, device.name() + "." + name)
|
|
{
|
|
device.setup().register_param(this->name(), *this);
|
|
}
|
|
|
|
param_t::~param_t()
|
|
{
|
|
}
|
|
|
|
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
|
|
{
|
|
netlist().log().fatal(MF_1_UNKNOWN_PARAM_TYPE, name());
|
|
return POINTER; /* Please compiler */
|
|
}
|
|
}
|
|
|
|
|
|
void param_t::update_param()
|
|
{
|
|
device().update_param();
|
|
if (device().needs_update_after_param_change())
|
|
device().update_dev();
|
|
}
|
|
|
|
const pstring param_model_t::model_type()
|
|
{
|
|
if (m_map.size() == 0)
|
|
netlist().setup().model_parse(this->Value(), m_map);
|
|
return m_map["COREMODEL"];
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
param_str_t::~param_str_t()
|
|
{
|
|
}
|
|
|
|
void param_str_t::changed()
|
|
{
|
|
}
|
|
|
|
param_double_t::param_double_t(device_t &device, const pstring &name, const double val)
|
|
: param_t(device, name)
|
|
{
|
|
m_param = device.setup().get_initial_param_val(this->name(),val);
|
|
netlist().save(*this, m_param, "m_param");
|
|
}
|
|
#if 0
|
|
param_double_t::~param_double_t()
|
|
{
|
|
}
|
|
#endif
|
|
param_int_t::param_int_t(device_t &device, const pstring &name, const int val)
|
|
: param_t(device, name)
|
|
{
|
|
m_param = device.setup().get_initial_param_val(this->name(),val);
|
|
netlist().save(*this, m_param, "m_param");
|
|
}
|
|
|
|
#if 0
|
|
param_int_t::~param_int_t()
|
|
{
|
|
}
|
|
#endif
|
|
|
|
param_logic_t::param_logic_t(device_t &device, const pstring &name, const bool val)
|
|
: param_t(device, name)
|
|
{
|
|
m_param = device.setup().get_initial_param_val(this->name(),val);
|
|
netlist().save(*this, m_param, "m_param");
|
|
}
|
|
|
|
#if 0
|
|
param_logic_t::~param_logic_t()
|
|
{
|
|
}
|
|
#endif
|
|
|
|
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");
|
|
}
|
|
|
|
#if 0
|
|
param_ptr_t::~param_ptr_t()
|
|
{
|
|
}
|
|
#endif
|
|
|
|
void param_model_t::changed()
|
|
{
|
|
netlist().log().fatal(MF_1_MODEL_1_CAN_NOT_BE_CHANGED_AT_RUNTIME, name());
|
|
m_map.clear();
|
|
}
|
|
|
|
const pstring param_model_t::model_value_str(const pstring &entity)
|
|
{
|
|
if (m_map.size() == 0)
|
|
netlist().setup().model_parse(this->Value(), m_map);
|
|
return netlist().setup().model_value_str(m_map, entity);
|
|
}
|
|
|
|
nl_double param_model_t::model_value(const pstring &entity)
|
|
{
|
|
if (m_map.size() == 0)
|
|
netlist().setup().model_parse(this->Value(), m_map);
|
|
return netlist().setup().model_value(m_map, entity);
|
|
}
|
|
|
|
param_data_t::param_data_t(device_t &device, const pstring &name)
|
|
: param_str_t(device, name, "")
|
|
{
|
|
}
|
|
|
|
void param_data_t::changed()
|
|
{
|
|
}
|
|
|
|
std::unique_ptr<plib::pistream> param_data_t::stream()
|
|
{
|
|
return device().netlist().setup().get_data_stream(Value());
|
|
}
|
|
|
|
namespace devices
|
|
{
|
|
// ----------------------------------------------------------------------------------------
|
|
// mainclock
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
void NETLIB_NAME(mainclock)::mc_update(logic_net_t &net)
|
|
{
|
|
net.toggle_new_Q();
|
|
net.update_devs();
|
|
}
|
|
|
|
|
|
} //namespace devices
|
|
|
|
bool detail::core_terminal_t::is_logic() const NL_NOEXCEPT
|
|
{
|
|
return dynamic_cast<const logic_t *>(this) != nullptr;
|
|
}
|
|
|
|
bool detail::core_terminal_t::is_analog() const NL_NOEXCEPT
|
|
{
|
|
return dynamic_cast<const analog_t *>(this) != nullptr;
|
|
}
|
|
|
|
bool detail::net_t::is_logic() const NL_NOEXCEPT
|
|
{
|
|
return dynamic_cast<const logic_net_t *>(this) != nullptr;
|
|
}
|
|
|
|
bool detail::net_t::is_analog() const NL_NOEXCEPT
|
|
{
|
|
return dynamic_cast<const analog_net_t *>(this) != nullptr;
|
|
}
|
|
|
|
|
|
} // namespace netlist
|