mirror of
https://github.com/holub/mame
synced 2025-04-20 23:42:22 +03:00
netlist: improve performance up to 65% on audio netlists. [Couriersud]
This commit introduces precompiled static solver code. Due to additional optimizations the compiler can use because the detail calculation steps for the solution are known e.g. the kidniki netlist sees a 100% speed increase. In all environments (windows/*nix/osx) the source for the static solver code can be created using bash src/lib/netlist/nl_create_mame_solvers.sh This will create src/lib/netlist/generated/static_solvers.cpp which is compiled into the mame binary. The script is just a temporary workaround. The intention is that nltool whill be able to create this file with one call. There are other improvements in this commit speeding up the processing of timestep and dynamic calculations.
This commit is contained in:
parent
d4093c59db
commit
5b6013caea
@ -233,4 +233,6 @@ project "netlist"
|
||||
MAME_DIR .. "src/lib/netlist/macro/nlm_opamp.h",
|
||||
MAME_DIR .. "src/lib/netlist/macro/nlm_other.cpp",
|
||||
MAME_DIR .. "src/lib/netlist/macro/nlm_other.h",
|
||||
|
||||
MAME_DIR .. "src/lib/netlist/generated/static_solvers.cpp",
|
||||
}
|
||||
|
@ -462,6 +462,10 @@ includedirs {
|
||||
MAME_DIR .. "src/lib/netlist",
|
||||
}
|
||||
|
||||
defines {
|
||||
"NL_DISABLE_DYNAMIC_LOAD=1",
|
||||
}
|
||||
|
||||
files {
|
||||
MAME_DIR .. "src/lib/netlist/prg/nltool.cpp",
|
||||
}
|
||||
|
@ -52,7 +52,6 @@ DEFINE_DEVICE_TYPE(NETLIST_LOGIC_OUTPUT, netlist_mame_logic_output_device, "nl
|
||||
DEFINE_DEVICE_TYPE(NETLIST_ANALOG_OUTPUT, netlist_mame_analog_output_device, "nl_analog_out", "Netlist Analog Output")
|
||||
DEFINE_DEVICE_TYPE(NETLIST_STREAM_OUTPUT, netlist_mame_stream_output_device, "nl_stream_out", "Netlist Stream Output")
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// Special netlist extension devices ....
|
||||
// ----------------------------------------------------------------------------------------
|
||||
@ -88,7 +87,6 @@ protected:
|
||||
m_parent.logerror("netlist ERROR: %s\n", ls.c_str());
|
||||
break;
|
||||
case plib::plog_level::FATAL:
|
||||
//throw emu_fatalerror(1, "netlist FATAL: %s\n", ls.c_str());
|
||||
m_parent.logerror("netlist FATAL: %s\n", ls.c_str());
|
||||
break;
|
||||
}
|
||||
@ -128,10 +126,14 @@ protected:
|
||||
case plib::plog_level::FATAL:
|
||||
osd_printf_error("netlist FATAL: %s\n", ls);
|
||||
break;
|
||||
//throw emu_fatalerror(1, "netlist FATAL: %s\n", ls.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
plib::unique_ptr<plib::dynlib_base> static_solver_lib() const noexcept override
|
||||
{
|
||||
return plib::make_unique<plib::dynlib_static>(nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
|
@ -124,6 +124,29 @@ namespace analog
|
||||
nl_fptype m_gmin;
|
||||
};
|
||||
|
||||
// Constant model for constant capacitor model
|
||||
// "Circuit simulation", page 274
|
||||
struct generic_capacitor_const
|
||||
{
|
||||
public:
|
||||
generic_capacitor_const(device_t &dev, const pstring &name)
|
||||
: m_gmin(nlconst::zero())
|
||||
{
|
||||
}
|
||||
|
||||
// Returns { G, Ieq }
|
||||
std::pair<nl_fptype, nl_fptype> timestep(nl_fptype cap, nl_fptype v, nl_fptype step) const noexcept
|
||||
{
|
||||
const nl_fptype h(plib::reciprocal(step));
|
||||
const nl_fptype G(cap * h + m_gmin);
|
||||
return { G, - G * v };
|
||||
}
|
||||
void setparams(nl_fptype gmin) noexcept { m_gmin = gmin; }
|
||||
private:
|
||||
nl_fptype m_gmin;
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// A generic diode model to be used in other devices (Diode, BJT ...)
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -155,9 +178,9 @@ namespace analog
|
||||
, nlconst::magic(1)
|
||||
, nlconst::magic(1e-15)
|
||||
, nlconst::magic(300.0));
|
||||
m_name = name;
|
||||
//m_name = name;
|
||||
}
|
||||
pstring m_name;
|
||||
//pstring m_name;
|
||||
// Basic math
|
||||
//
|
||||
// I(V) = f(V)
|
||||
|
@ -164,8 +164,8 @@ namespace analog
|
||||
NETLIB_UPDATE_TERMINALS(D)
|
||||
{
|
||||
m_D.update_diode(deltaV());
|
||||
const nl_fptype G = m_D.G();
|
||||
const nl_fptype I = m_D.Ieq();
|
||||
const nl_fptype G(m_D.G());
|
||||
const nl_fptype I(m_D.Ieq());
|
||||
set_mat( G, -G, -I,
|
||||
-G, G, I);
|
||||
//set(m_D.G(), 0.0, m_D.Ieq());
|
||||
|
@ -245,7 +245,44 @@ namespace analog
|
||||
// -----------------------------------------------------------------------------
|
||||
// nld_C
|
||||
// -----------------------------------------------------------------------------
|
||||
#if 1
|
||||
NETLIB_OBJECT_DERIVED(C, twoterm)
|
||||
{
|
||||
public:
|
||||
NETLIB_CONSTRUCTOR_DERIVED(C, twoterm)
|
||||
, m_C(*this, "C", nlconst::magic(1e-6))
|
||||
, m_cap(*this, "m_cap")
|
||||
{
|
||||
}
|
||||
|
||||
NETLIB_IS_TIMESTEP(true)
|
||||
NETLIB_TIMESTEPI()
|
||||
{
|
||||
// G, Ieq
|
||||
const auto res(m_cap.timestep(m_C(), deltaV(), step));
|
||||
const nl_fptype G = res.first;
|
||||
const nl_fptype I = res.second;
|
||||
set_mat( G, -G, -I,
|
||||
-G, G, I);
|
||||
}
|
||||
|
||||
NETLIB_RESETI()
|
||||
{
|
||||
m_cap.setparams(exec().gmin());
|
||||
}
|
||||
|
||||
param_fp_t m_C;
|
||||
protected:
|
||||
//NETLIB_UPDATEI();
|
||||
//FIXME: should be able to change
|
||||
NETLIB_UPDATE_PARAMI() { }
|
||||
|
||||
private:
|
||||
generic_capacitor_const m_cap;
|
||||
};
|
||||
|
||||
#else
|
||||
// Code preserved as a basis for a current/voltage controlled capacitor
|
||||
NETLIB_OBJECT_DERIVED(C, twoterm)
|
||||
{
|
||||
public:
|
||||
@ -292,7 +329,7 @@ namespace analog
|
||||
//generic_capacitor<capacitor_e::VARIABLE_CAPACITY> m_cap;
|
||||
generic_capacitor<capacitor_e::CONSTANT_CAPACITY> m_cap;
|
||||
};
|
||||
|
||||
#endif
|
||||
// -----------------------------------------------------------------------------
|
||||
// nld_L
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -74,7 +74,7 @@ namespace netlist
|
||||
log().warning(MW_FREQUENCY_OUTSIDE_OF_SPECS_1(m_FREQ()));
|
||||
|
||||
m_shift = 0x1ffff;
|
||||
m_is_timestep = m_RV.m_P.net().solver()->has_timestep_devices();
|
||||
m_is_timestep = (m_RV.m_P.net().solver()->timestep_device_count() > 0);
|
||||
}
|
||||
|
||||
NETLIB_UPDATE_PARAM(MM5837_dip)
|
||||
|
@ -82,7 +82,7 @@ namespace netlist
|
||||
m_last_state = 1;
|
||||
m_RVI.reset();
|
||||
m_RVO.reset();
|
||||
m_is_timestep = m_RVO.m_P.net().solver()->has_timestep_devices();
|
||||
m_is_timestep = (m_RVO.m_P.net().solver()->timestep_device_count() > 0);
|
||||
m_RVI.set_G_V_I(plib::reciprocal(m_model.m_RI()), m_model.m_VI, nlconst::zero());
|
||||
m_RVO.set_G_V_I(plib::reciprocal(m_model.m_ROL()), m_model.m_VOL, nlconst::zero());
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ namespace netlist
|
||||
m_last_state = -1;
|
||||
m_RN.reset();
|
||||
m_RP.reset();
|
||||
m_is_timestep = m_RN.m_P.net().solver()->has_timestep_devices();
|
||||
m_is_timestep = (m_RN.m_P.net().solver()->timestep_device_count() > 0);
|
||||
m_RN.set_G_V_I(plib::reciprocal(logic_family()->R_low()),
|
||||
logic_family()->low_offset_V(), nlconst::zero());
|
||||
m_RP.set_G_V_I(G_OFF,
|
||||
|
9871
src/lib/netlist/generated/static_solvers.cpp
Executable file
9871
src/lib/netlist/generated/static_solvers.cpp
Executable file
File diff suppressed because it is too large
Load Diff
@ -18,9 +18,20 @@
|
||||
|
||||
#include <limits>
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// Statically compiled solvers for mame netlist
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
extern plib::dynlib_static_sym nl_static_syms[];
|
||||
|
||||
namespace netlist
|
||||
{
|
||||
|
||||
plib::unique_ptr<plib::dynlib_base> callbacks_t:: static_solver_lib() const
|
||||
{
|
||||
return plib::make_unique<plib::dynlib_static>(nl_static_syms);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// logic_family_ttl_t
|
||||
// ----------------------------------------------------------------------------------------
|
||||
@ -201,8 +212,8 @@ namespace netlist
|
||||
, m_extended_validation(false)
|
||||
, m_dummy_version(1)
|
||||
{
|
||||
pstring libpath = plib::util::environment("NL_BOOSTLIB", plib::util::buildpath({".", "nlboost.so"}));
|
||||
m_lib = plib::make_unique<plib::dynlib>(libpath);
|
||||
|
||||
m_lib = m_callbacks->static_solver_lib();
|
||||
|
||||
m_setup = plib::make_unique<setup_t>(*this);
|
||||
// create the run interface
|
||||
@ -795,9 +806,9 @@ namespace netlist
|
||||
|
||||
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_Idr(nullptr)
|
||||
, m_go(nullptr)
|
||||
, m_gt(nullptr)
|
||||
{
|
||||
state().setup().register_term(*this, *otherterm);
|
||||
}
|
||||
|
@ -187,6 +187,8 @@ namespace netlist
|
||||
/// \brief Delegate type for device notification.
|
||||
///
|
||||
using nldelegate = plib::pmfp<void>;
|
||||
using nldelegate_ts = plib::pmfp<void, nl_fptype>;
|
||||
using nldelegate_dyn = plib::pmfp<void>;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// forward definitions
|
||||
@ -825,11 +827,11 @@ namespace netlist
|
||||
void set_go_gt_I(nl_fptype GO, nl_fptype GT, nl_fptype I) const noexcept
|
||||
{
|
||||
// Check for rail nets ...
|
||||
if (m_go1 != nullptr)
|
||||
if (m_go != nullptr)
|
||||
{
|
||||
*m_Idr1 = I;
|
||||
*m_go1 = GO;
|
||||
*m_gt1 = GT;
|
||||
*m_Idr = I;
|
||||
*m_go = GO;
|
||||
*m_gt = GT;
|
||||
}
|
||||
}
|
||||
|
||||
@ -837,11 +839,11 @@ namespace netlist
|
||||
void schedule_solve_after(netlist_time after) noexcept;
|
||||
|
||||
void set_ptrs(nl_fptype *gt, nl_fptype *go, nl_fptype *Idr) noexcept(false);
|
||||
private:
|
||||
|
||||
nl_fptype *m_Idr1; // drive current
|
||||
nl_fptype *m_go1; // conductance for Voltage from other term
|
||||
nl_fptype *m_gt1; // conductance for total conductance
|
||||
private:
|
||||
nl_fptype *m_Idr; // drive current
|
||||
nl_fptype *m_go; // conductance for Voltage from other term
|
||||
nl_fptype *m_gt; // conductance for total conductance
|
||||
|
||||
};
|
||||
|
||||
@ -1430,7 +1432,7 @@ namespace netlist
|
||||
log_type & log() noexcept { return m_log; }
|
||||
const log_type &log() const noexcept { return m_log; }
|
||||
|
||||
plib::dynlib &lib() const noexcept { return *m_lib; }
|
||||
plib::dynlib_base &lib() const noexcept { return *m_lib; }
|
||||
|
||||
netlist_t &exec() noexcept { return *m_netlist; }
|
||||
const netlist_t &exec() const noexcept { return *m_netlist; }
|
||||
@ -1576,7 +1578,7 @@ namespace netlist
|
||||
|
||||
pstring m_name;
|
||||
unique_pool_ptr<netlist_t> m_netlist;
|
||||
plib::unique_ptr<plib::dynlib> m_lib; // external lib needs to be loaded as long as netlist exists
|
||||
plib::unique_ptr<plib::dynlib_base> m_lib; // external lib needs to be loaded as long as netlist exists
|
||||
plib::state_manager_t m_state;
|
||||
plib::unique_ptr<callbacks_t> m_callbacks;
|
||||
log_type m_log;
|
||||
@ -1951,9 +1953,9 @@ namespace netlist
|
||||
throw nl_exception("Inconsistent nullptrs for terminal {}", name());
|
||||
}
|
||||
|
||||
m_gt1 = gt;
|
||||
m_go1 = go;
|
||||
m_Idr1 = Idr;
|
||||
m_gt = gt;
|
||||
m_go = go;
|
||||
m_Idr = Idr;
|
||||
}
|
||||
|
||||
inline logic_net_t & logic_t::net() noexcept
|
||||
|
40
src/lib/netlist/nl_create_mame_solvers.sh
Normal file
40
src/lib/netlist/nl_create_mame_solvers.sh
Normal file
@ -0,0 +1,40 @@
|
||||
|
||||
OUTDIR=/tmp/static_sysms
|
||||
GENERATED=src/lib/netlist/generated/static_solvers.cpp
|
||||
|
||||
if [ _$OS = "_Windows_NT" ]; then
|
||||
NLTOOL=./nltool.exe
|
||||
CXX=g++.exe
|
||||
else
|
||||
NLTOOL=./nltool
|
||||
CXX=g++
|
||||
fi
|
||||
|
||||
rm -rf $OUTDIR
|
||||
mkdir $OUTDIR
|
||||
|
||||
for i in src/mame/machine/nl_*.cpp src/mame/audio/nl_*.cpp; do
|
||||
nn=`basename $i .cpp | sed -e "s/nl_//g"`
|
||||
cn=`grep "^//NL_CONTAINS" $i | cut -f2-99 "-d "`
|
||||
if [ _"$cn" != _"" ]; then
|
||||
nn=$cn
|
||||
fi
|
||||
for j in $nn; do
|
||||
echo $i : $j
|
||||
if [ $j != "pongdoubles" ]; then
|
||||
$NLTOOL -c static -f $i -n $j -t 6 --dir $OUTDIR
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# $CXX -shared -x c++ -fPIC -O3 -march=native -mtune=native -ffast-math $OUTDIR/*.c -o nlboost.so
|
||||
|
||||
echo '#include "plib/pdynlib.h"' > $GENERATED
|
||||
cat $OUTDIR/*.c | sed -e 's/extern "C"/static/' >> $GENERATED
|
||||
echo 'plib::dynlib_static_sym nl_static_syms[] = {' >> $GENERATED
|
||||
for i in $OUTDIR/*.c; do
|
||||
n=`basename $i .c`
|
||||
echo '{ "'${n}'", reinterpret_cast<void *>(&'${n}')},' >> $GENERATED
|
||||
done
|
||||
echo '{"", nullptr}' >> $GENERATED
|
||||
echo '};' >> $GENERATED
|
@ -20,6 +20,7 @@
|
||||
#include "plib/pstring.h"
|
||||
#include "plib/ptime.h"
|
||||
#include "plib/putil.h"
|
||||
#include "plib/pdynlib.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
@ -57,6 +58,9 @@ namespace netlist
|
||||
///
|
||||
virtual void vlog(const plib::plog_level &l, const pstring &ls) const noexcept = 0;
|
||||
|
||||
/// \brief provide library with static solver implementations.
|
||||
///
|
||||
virtual plib::unique_ptr<plib::dynlib_base> static_solver_lib() const;
|
||||
};
|
||||
|
||||
using log_type = plib::plog_base<callbacks_t, NL_DEBUG>;
|
||||
|
@ -55,7 +55,7 @@ WCHAR *wstring_from_utf8(const char *utf8string)
|
||||
|
||||
namespace plib {
|
||||
dynlib::dynlib(const pstring &libname)
|
||||
: m_isLoaded(false), m_lib(nullptr)
|
||||
: m_lib(nullptr)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
//fprintf(stderr, "win: loading <%s>\n", libname.c_str());
|
||||
@ -65,7 +65,7 @@ dynlib::dynlib(const pstring &libname)
|
||||
else
|
||||
m_lib = GetModuleHandle(nullptr);
|
||||
if (m_lib != nullptr)
|
||||
m_isLoaded = true;
|
||||
m_is_loaded = true;
|
||||
//else
|
||||
// fprintf(stderr, "win: library <%s> not found!\n", libname.c_str());
|
||||
delete [] buffer;
|
||||
@ -78,14 +78,14 @@ dynlib::dynlib(const pstring &libname)
|
||||
else
|
||||
m_lib = dlopen(nullptr, RTLD_LAZY);
|
||||
if (m_lib != nullptr)
|
||||
m_isLoaded = true;
|
||||
m_is_loaded = true;
|
||||
//else
|
||||
// printf("library <%s> not found: %s\n", libname.c_str(), dlerror());
|
||||
#endif
|
||||
}
|
||||
|
||||
dynlib::dynlib(const pstring &path, const pstring &libname)
|
||||
: m_isLoaded(false), m_lib(nullptr)
|
||||
: m_lib(nullptr)
|
||||
{
|
||||
// FIXME: implement path search
|
||||
plib::unused_var(path);
|
||||
@ -97,7 +97,7 @@ dynlib::dynlib(const pstring &path, const pstring &libname)
|
||||
else
|
||||
m_lib = GetModuleHandle(nullptr);
|
||||
if (m_lib != nullptr)
|
||||
m_isLoaded = true;
|
||||
m_is_loaded = true;
|
||||
else
|
||||
{
|
||||
//printf("win: library <%s> not found!\n", libname.c_str());
|
||||
@ -112,7 +112,7 @@ dynlib::dynlib(const pstring &path, const pstring &libname)
|
||||
else
|
||||
m_lib = dlopen(nullptr, RTLD_LAZY);
|
||||
if (m_lib != nullptr)
|
||||
m_isLoaded = true;
|
||||
m_is_loaded = true;
|
||||
else
|
||||
{
|
||||
//printf("library <%s> not found!\n", libname.c_str());
|
||||
|
@ -12,62 +12,108 @@
|
||||
#include "ptypes.h"
|
||||
|
||||
namespace plib {
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// pdynlib: dynamic loading of libraries ...
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// pdynlib: dynamic loading of libraries ...
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
class dynlib : public nocopyassignmove
|
||||
{
|
||||
public:
|
||||
explicit dynlib(const pstring &libname);
|
||||
dynlib(const pstring &path, const pstring &libname);
|
||||
|
||||
~dynlib();
|
||||
COPYASSIGNMOVE(dynlib, delete)
|
||||
|
||||
bool isLoaded() const { return m_isLoaded; }
|
||||
|
||||
template <typename T>
|
||||
T getsym(const pstring &name) const noexcept
|
||||
class dynlib_base : public nocopyassignmove
|
||||
{
|
||||
return reinterpret_cast<T>(getsym_p(name));
|
||||
}
|
||||
private:
|
||||
void *getsym_p(const pstring &name) const noexcept;
|
||||
public:
|
||||
explicit dynlib_base() : m_is_loaded(false) { }
|
||||
|
||||
bool m_isLoaded;
|
||||
void *m_lib;
|
||||
};
|
||||
virtual ~dynlib_base() { }
|
||||
COPYASSIGNMOVE(dynlib_base, delete)
|
||||
|
||||
template <typename R, typename... Args>
|
||||
class dynproc
|
||||
{
|
||||
public:
|
||||
using calltype = R(*) (Args... args);
|
||||
bool isLoaded() const { return m_is_loaded; }
|
||||
|
||||
dynproc() : m_sym(nullptr) { }
|
||||
template <typename T>
|
||||
T getsym(const pstring &name) const noexcept
|
||||
{
|
||||
return reinterpret_cast<T>(getsym_p(name));
|
||||
}
|
||||
|
||||
dynproc(dynlib &dl, const pstring &name) noexcept
|
||||
protected:
|
||||
bool m_is_loaded;
|
||||
virtual void *getsym_p(const pstring &name) const noexcept = 0;
|
||||
};
|
||||
|
||||
class dynlib : public dynlib_base
|
||||
{
|
||||
m_sym = dl.getsym<calltype>(name);
|
||||
}
|
||||
public:
|
||||
explicit dynlib(const pstring &libname);
|
||||
dynlib(const pstring &path, const pstring &libname);
|
||||
|
||||
void load(dynlib &dl, const pstring &name) noexcept
|
||||
~dynlib();
|
||||
|
||||
protected:
|
||||
void *getsym_p(const pstring &name) const noexcept override;
|
||||
|
||||
private:
|
||||
void *m_lib;
|
||||
};
|
||||
|
||||
struct dynlib_static_sym
|
||||
{
|
||||
m_sym = dl.getsym<calltype>(name);
|
||||
}
|
||||
const char *name;
|
||||
void *addr;
|
||||
};
|
||||
|
||||
R operator ()(Args&&... args) const
|
||||
class dynlib_static : public dynlib_base
|
||||
{
|
||||
return m_sym(std::forward<Args>(args)...);
|
||||
//return m_sym(args...);
|
||||
}
|
||||
public:
|
||||
explicit dynlib_static(const dynlib_static_sym *syms)
|
||||
: m_syms(syms)
|
||||
{
|
||||
if (syms != nullptr)
|
||||
m_is_loaded = true;
|
||||
}
|
||||
|
||||
bool resolved() const noexcept { return m_sym != nullptr; }
|
||||
private:
|
||||
calltype m_sym;
|
||||
};
|
||||
protected:
|
||||
void *getsym_p(const pstring &name) const noexcept override
|
||||
{
|
||||
const dynlib_static_sym *p = m_syms;
|
||||
while (p->name[0] != 0)
|
||||
{
|
||||
if (name == pstring(p->name))
|
||||
return p->addr;
|
||||
p++;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
const dynlib_static_sym *m_syms;
|
||||
};
|
||||
|
||||
template <typename R, typename... Args>
|
||||
class dynproc
|
||||
{
|
||||
public:
|
||||
using calltype = R(*) (Args... args);
|
||||
|
||||
dynproc() : m_sym(nullptr) { }
|
||||
|
||||
dynproc(dynlib_base &dl, const pstring &name) noexcept
|
||||
{
|
||||
m_sym = dl.getsym<calltype>(name);
|
||||
}
|
||||
|
||||
void load(dynlib_base &dl, const pstring &name) noexcept
|
||||
{
|
||||
m_sym = dl.getsym<calltype>(name);
|
||||
}
|
||||
|
||||
R operator ()(Args&&... args) const
|
||||
{
|
||||
return m_sym(std::forward<Args>(args)...);
|
||||
//return m_sym(args...);
|
||||
}
|
||||
|
||||
bool resolved() const noexcept { return m_sym != nullptr; }
|
||||
private:
|
||||
calltype m_sym;
|
||||
};
|
||||
|
||||
} // namespace plib
|
||||
|
||||
#endif // PSTRING_H_
|
||||
#endif // PDYNLIB_H_
|
||||
|
@ -67,6 +67,20 @@ namespace plib
|
||||
return (*this)[r][c];
|
||||
}
|
||||
|
||||
T * data() noexcept
|
||||
{
|
||||
return m_v.data();
|
||||
}
|
||||
|
||||
const T * data() const noexcept
|
||||
{
|
||||
return m_v.data();
|
||||
}
|
||||
|
||||
size_type didx(size_type r, size_type c) const noexcept
|
||||
{
|
||||
return m_stride * r + c;
|
||||
}
|
||||
private:
|
||||
|
||||
size_type m_N;
|
||||
|
@ -287,6 +287,19 @@ namespace plib
|
||||
const std::string &token,
|
||||
std::size_t maxsplit);
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// simple hash
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
template <typename T>
|
||||
std::size_t hash(const T *buf, std::size_t size)
|
||||
{
|
||||
std::size_t result = 5381;
|
||||
for (const T* p = buf; p != buf + size; p++)
|
||||
result = ((result << 5) + result ) ^ (result >> (32 - 5)) ^ static_cast<std::size_t>(*p);
|
||||
return result;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
// penum - strongly typed enumeration
|
||||
//============================================================
|
||||
|
@ -23,6 +23,11 @@
|
||||
#include <ios>
|
||||
#include <iostream> // scanf
|
||||
|
||||
#ifndef NL_DISABLE_DYNAMIC_LOAD
|
||||
#define NL_DISABLE_DYNAMIC_LOAD 0
|
||||
#endif
|
||||
|
||||
|
||||
class tool_app_t : public plib::app
|
||||
{
|
||||
public:
|
||||
@ -47,7 +52,8 @@ public:
|
||||
opt_dir(*this, "d", "dir", "", "output directory for the generated files"),
|
||||
|
||||
opt_grp4(*this, "Options for run command", "These options are only used by the run command."),
|
||||
opt_ttr (*this, "t", "time_to_run", 1, "time to run the emulation (seconds)\n\n abc def\n\n xyz"),
|
||||
opt_ttr (*this, "t", "time_to_run", 1, "time to run the emulation (seconds)"),
|
||||
opt_boostlib(*this, "", "boost_lib", "builtin", "generic: will use generic solvers.\nbuiltin: Use optimized solvers compiled in.\nsomelib.so: Use library with precompiled solvers."),
|
||||
opt_stats(*this, "s", "statistics", "gather runtime statistics"),
|
||||
opt_logs(*this, "l", "log" , "define terminal to log. This option may be specified repeatedly."),
|
||||
opt_inp(*this, "i", "input", "", "input file to process (default is none)"),
|
||||
@ -94,6 +100,7 @@ public:
|
||||
plib::option_str opt_dir;
|
||||
plib::option_group opt_grp4;
|
||||
plib::option_num<nl_fptype> opt_ttr;
|
||||
plib::option_str opt_boostlib;
|
||||
plib::option_bool opt_stats;
|
||||
plib::option_vec opt_logs;
|
||||
plib::option_str opt_inp;
|
||||
@ -191,6 +198,23 @@ public:
|
||||
|
||||
void vlog(const plib::plog_level &l, const pstring &ls) const noexcept override;
|
||||
|
||||
plib::unique_ptr<plib::dynlib_base> static_solver_lib() const override
|
||||
{
|
||||
if (m_app.opt_boostlib() == "builtin")
|
||||
return netlist::callbacks_t::static_solver_lib();
|
||||
else if (m_app.opt_boostlib() == "generic")
|
||||
return plib::make_unique<plib::dynlib_static>(nullptr);
|
||||
if (NL_DISABLE_DYNAMIC_LOAD)
|
||||
{
|
||||
throw netlist::nl_exception("Dynamic library loading not supported due to project security concerns.");
|
||||
}
|
||||
else
|
||||
{
|
||||
//pstring libpath = plib::util::environment("NL_BOOSTLIB", plib::util::buildpath({".", "nlboost.so"}));
|
||||
return plib::make_unique<plib::dynlib>(m_app.opt_boostlib());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
tool_app_t &m_app;
|
||||
};
|
||||
|
@ -66,6 +66,8 @@ namespace solver
|
||||
void matrix_solver_t::setup_base(const analog_net_t::list_t &nets)
|
||||
{
|
||||
log().debug("New solver setup\n");
|
||||
std::vector<core_device_t *> step_devices;
|
||||
std::vector<core_device_t *> dynamic_devices;
|
||||
|
||||
m_terms.clear();
|
||||
|
||||
@ -90,11 +92,11 @@ namespace solver
|
||||
{
|
||||
case detail::terminal_type::TERMINAL:
|
||||
if (p->device().is_timestep())
|
||||
if (!plib::container::contains(m_step_devices, &p->device()))
|
||||
m_step_devices.push_back(&p->device());
|
||||
if (!plib::container::contains(step_devices, &p->device()))
|
||||
step_devices.push_back(&p->device());
|
||||
if (p->device().is_dynamic())
|
||||
if (!plib::container::contains(m_dynamic_devices, &p->device()))
|
||||
m_dynamic_devices.push_back(&p->device());
|
||||
if (!plib::container::contains(dynamic_devices, &p->device()))
|
||||
dynamic_devices.push_back(&p->device());
|
||||
{
|
||||
auto *pterm = dynamic_cast<terminal_t *>(p);
|
||||
add_term(k, pterm);
|
||||
@ -131,6 +133,10 @@ namespace solver
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto &d : step_devices)
|
||||
m_step_funcs.push_back(nldelegate_ts(&core_device_t::timestep, d));
|
||||
for (auto &d : dynamic_devices)
|
||||
m_dynamic_funcs.push_back(nldelegate_dyn(&core_device_t::update_terminals, d));
|
||||
}
|
||||
|
||||
void matrix_solver_t::sort_terms(matrix_sort_type_e sort)
|
||||
@ -383,11 +389,11 @@ namespace solver
|
||||
inp->push(inp->proxied_net()->Q_Analog());
|
||||
}
|
||||
|
||||
void matrix_solver_t::update_dynamic()
|
||||
void matrix_solver_t::update_dynamic() noexcept
|
||||
{
|
||||
// update all non-linear devices
|
||||
for (auto &dyn : m_dynamic_devices)
|
||||
dyn->update_terminals();
|
||||
for (auto &dyn : m_dynamic_funcs)
|
||||
dyn();
|
||||
}
|
||||
|
||||
void matrix_solver_t::reset()
|
||||
@ -400,7 +406,7 @@ namespace solver
|
||||
const netlist_time new_timestep = solve(exec().time());
|
||||
update_inputs();
|
||||
|
||||
if (m_params.m_dynamic_ts && has_timestep_devices() && new_timestep > netlist_time::zero())
|
||||
if (m_params.m_dynamic_ts && (timestep_device_count() != 0) && new_timestep > netlist_time::zero())
|
||||
{
|
||||
m_Q_sync.net().toggle_and_push_to_queue(new_timestep);
|
||||
}
|
||||
@ -418,17 +424,17 @@ namespace solver
|
||||
|
||||
update_inputs();
|
||||
|
||||
if (m_params.m_dynamic_ts && has_timestep_devices())
|
||||
if (m_params.m_dynamic_ts && (timestep_device_count() != 0))
|
||||
{
|
||||
m_Q_sync.net().toggle_and_push_to_queue(netlist_time::from_fp(m_params.m_min_timestep));
|
||||
}
|
||||
}
|
||||
|
||||
void matrix_solver_t::step(const netlist_time &delta)
|
||||
void matrix_solver_t::step(netlist_time delta) noexcept
|
||||
{
|
||||
const auto dd(delta.as_fp<nl_fptype>());
|
||||
for (auto &d : m_step_devices)
|
||||
d->timestep(dd);
|
||||
for (auto &d : m_step_funcs)
|
||||
d(dd);
|
||||
}
|
||||
|
||||
netlist_time matrix_solver_t::solve(netlist_time_ext now)
|
||||
@ -445,7 +451,7 @@ namespace solver
|
||||
step(static_cast<netlist_time>(delta));
|
||||
|
||||
++m_stat_vsolver_calls;
|
||||
if (has_dynamic_devices())
|
||||
if (dynamic_device_count() != 0)
|
||||
{
|
||||
std::size_t this_resched(0);
|
||||
std::size_t newton_loops = 0;
|
||||
@ -574,8 +580,8 @@ namespace solver
|
||||
log().verbose("==============================================");
|
||||
log().verbose("Solver {1}", this->name());
|
||||
log().verbose(" ==> {1} nets", this->m_terms.size()); //, (*(*groups[i].first())->m_core_terms.first())->name());
|
||||
log().verbose(" has {1} elements", this->has_dynamic_devices() ? "dynamic" : "no dynamic");
|
||||
log().verbose(" has {1} elements", this->has_timestep_devices() ? "timestep" : "no timestep");
|
||||
log().verbose(" has {1} dynamic elements", this->dynamic_device_count());
|
||||
log().verbose(" has {1} timestep elements", this->timestep_device_count());
|
||||
log().verbose(" {1:6.3} average newton raphson loops",
|
||||
static_cast<nl_fptype>(this->m_stat_newton_raphson) / static_cast<nl_fptype>(this->m_stat_vsolver_calls));
|
||||
log().verbose(" {1:10} invocations ({2:6.0} Hz) {3:10} gs fails ({4:6.2} %) {5:6.3} average",
|
||||
|
@ -182,8 +182,8 @@ namespace solver
|
||||
netlist_time solve(netlist_time_ext now);
|
||||
void update_inputs();
|
||||
|
||||
bool has_dynamic_devices() const noexcept { return !m_dynamic_devices.empty(); }
|
||||
bool has_timestep_devices() const noexcept { return !m_step_devices.empty(); }
|
||||
std::size_t dynamic_device_count() const noexcept { return m_dynamic_funcs.size(); }
|
||||
std::size_t timestep_device_count() const noexcept { return m_step_funcs.size(); }
|
||||
|
||||
void update_forced();
|
||||
void update_after(netlist_time after) noexcept
|
||||
@ -238,8 +238,8 @@ namespace solver
|
||||
state_var<std::size_t> m_stat_vsolver_calls;
|
||||
|
||||
state_var<netlist_time_ext> m_last_step;
|
||||
std::vector<core_device_t *> m_step_devices;
|
||||
std::vector<core_device_t *> m_dynamic_devices;
|
||||
std::vector<nldelegate_ts> m_step_funcs;
|
||||
std::vector<nldelegate_dyn> m_dynamic_funcs;
|
||||
|
||||
logic_input_t m_fb_sync;
|
||||
logic_output_t m_Q_sync;
|
||||
@ -251,8 +251,8 @@ namespace solver
|
||||
|
||||
void sort_terms(matrix_sort_type_e sort);
|
||||
|
||||
void update_dynamic();
|
||||
void step(const netlist_time &delta);
|
||||
void update_dynamic() noexcept;
|
||||
void step(netlist_time delta) noexcept;
|
||||
|
||||
int get_net_idx(const analog_net_t *net) const noexcept;
|
||||
std::pair<int, int> get_left_right_of_diag(std::size_t irow, std::size_t idiag);
|
||||
@ -311,7 +311,7 @@ namespace solver
|
||||
plib::parray<FT, SIZE> m_RHS;
|
||||
|
||||
PALIGNAS_VECTOROPT()
|
||||
plib::parray2D<float_type *, SIZE, 0> m_mat_ptr;
|
||||
plib::pmatrix2d<float_type *> m_mat_ptr;
|
||||
|
||||
// FIXME: below should be private
|
||||
// state - variable time_stepping
|
||||
|
@ -124,7 +124,7 @@ namespace solver
|
||||
|
||||
mat_type mat;
|
||||
|
||||
plib::dynproc<void, FT * , FT * , FT * > m_proc;
|
||||
plib::dynproc<void, FT *, FT *, FT *, FT *, FT ** > m_proc;
|
||||
|
||||
};
|
||||
|
||||
@ -140,7 +140,45 @@ namespace solver
|
||||
pstring fpsuffix(fp_constants<FT>::suffix());
|
||||
|
||||
for (std::size_t i = 0; i < mat.nz_num; i++)
|
||||
strm("{1} m_A{2} = m_A[{3}];\n", fptype, i, i);
|
||||
strm("{1} m_A{2}(0.0);\n", fptype, i, i);
|
||||
|
||||
for (std::size_t k = 0; k < iN; k++)
|
||||
{
|
||||
auto &net = this->m_terms[k];
|
||||
|
||||
// FIXME: gonn, gtn and Idr - which float types should they have?
|
||||
|
||||
//auto gtot_t = std::accumulate(gt, gt + term_count, plib::constants<FT>::zero());
|
||||
//*tcr_r[railstart] = static_cast<FT>(gtot_t); //mat.A[mat.diag[k]] += gtot_t;
|
||||
auto pd = this->m_mat_ptr[k][net.railstart()] - &this->mat.A[0];
|
||||
pstring terms = plib::pfmt("m_A{1} = gt[{2}]")(pd, this->m_gtn.didx(k,0));
|
||||
for (std::size_t i=1; i < net.count(); i++)
|
||||
terms += plib::pfmt(" + gt[{1}]")(this->m_gtn.didx(k,i));
|
||||
|
||||
strm("\t{1};\n", terms);
|
||||
|
||||
//for (std::size_t i = 0; i < railstart; i++)
|
||||
// *tcr_r[i] += static_cast<FT>(go[i]);
|
||||
|
||||
for (std::size_t i = 0; i < net.railstart(); i++)
|
||||
{
|
||||
auto p = this->m_mat_ptr[k][i] - &this->mat.A[0];
|
||||
strm("\tm_A{1} = m_A{1} + go[{2}];\n", p, this->m_gonn.didx(k,i));
|
||||
}
|
||||
|
||||
//auto RHS_t(std::accumulate(Idr, Idr + term_count, plib::constants<FT>::zero()));
|
||||
|
||||
terms = plib::pfmt("{1} RHS{2} = Idr[{3}]")(fptype, k, this->m_Idrn.didx(k,0));
|
||||
for (std::size_t i=1; i < net.count(); i++)
|
||||
terms += plib::pfmt(" + Idr[{1}]")(this->m_Idrn.didx(k,i));
|
||||
//for (std::size_t i = railstart; i < term_count; i++)
|
||||
// RHS_t += (- go[i]) * *cnV[i];
|
||||
|
||||
for (std::size_t i = net.railstart(); i < net.count(); i++)
|
||||
terms += plib::pfmt(" - go[{1}] * *cnV[{1}]")(this->m_gonn.didx(k,i), this->m_connected_net_Vn.didx(k,i));
|
||||
|
||||
strm("\t{1};\n", terms);
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < iN - 1; i++)
|
||||
{
|
||||
@ -178,13 +216,13 @@ namespace solver
|
||||
pj++; pii++;
|
||||
}
|
||||
//RHS[j] += f1 * RHS[i];
|
||||
strm("\tRHS[{1}] += f{2}_{3} * RHS[{4}];\n", j, i, j, i);
|
||||
strm("\tRHS{1} += f{2}_{3} * RHS{4};\n", j, i, j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//new_V[iN - 1] = RHS[iN - 1] / mat.A[mat.diag[iN - 1]];
|
||||
strm("\tV[{1}] = RHS[{2}] / m_A{3};\n", iN - 1, iN - 1, mat.diag[iN - 1]);
|
||||
strm("\tV[{1}] = RHS{2} / m_A{3};\n", iN - 1, iN - 1, mat.diag[iN - 1]);
|
||||
for (std::size_t j = iN - 1; j-- > 0;)
|
||||
{
|
||||
strm("\t{1} tmp{2} = 0.0{3};\n", fptype, j, fpsuffix);
|
||||
@ -193,7 +231,7 @@ namespace solver
|
||||
{
|
||||
strm("\ttmp{1} += m_A{2} * V[{3}];\n", j, pk, mat.col_idx[pk]);
|
||||
}
|
||||
strm("\tV[{1}] = (RHS[{1}] - tmp{1}) / m_A{4};\n", j, j, j, mat.diag[j]);
|
||||
strm("\tV[{1}] = (RHS{1} - tmp{1}) / m_A{4};\n", j, j, j, mat.diag[j]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,8 +242,8 @@ namespace solver
|
||||
t.imbue(std::locale::classic());
|
||||
plib::putf8_fmt_writer w(&t);
|
||||
generate_code(w);
|
||||
std::hash<typename std::remove_const<std::remove_reference<decltype(t.str())>::type>::type> h;
|
||||
return plib::pfmt("nl_gcr_{1:x}_{2}")(h( t.str() ))(mat.nz_num);
|
||||
//std::hash<typename std::remove_const<std::remove_reference<decltype(t.str())>::type>::type> h;
|
||||
return plib::pfmt("nl_gcr_{1:x}_{2}")(plib::hash( t.str().c_str(), t.str().size() ))(mat.nz_num);
|
||||
}
|
||||
|
||||
template <typename FT, int SIZE>
|
||||
@ -217,7 +255,9 @@ namespace solver
|
||||
pstring name = static_compile_name();
|
||||
pstring fptype(fp_constants<FT>::name());
|
||||
|
||||
strm.writeline(plib::pfmt("extern \"C\" void {1}({2} * __restrict m_A, {2} * __restrict RHS, {2} * __restrict V)\n")(name, fptype));
|
||||
strm.writeline(plib::pfmt("extern \"C\" void {1}({2} * __restrict V, "
|
||||
"{2} * __restrict go, {2} * __restrict gt, "
|
||||
"{2} * __restrict Idr, {2} ** __restrict cnV)\n")(name, fptype));
|
||||
strm.writeline("{\n");
|
||||
generate_code(strm);
|
||||
strm.writeline("}\n");
|
||||
@ -228,20 +268,21 @@ namespace solver
|
||||
template <typename FT, int SIZE>
|
||||
unsigned matrix_solver_GCR_t<FT, SIZE>::vsolve_non_dynamic(bool newton_raphson)
|
||||
{
|
||||
// clear matrix
|
||||
mat.set_scalar(plib::constants<FT>::zero());
|
||||
|
||||
// populate matrix
|
||||
this->fill_matrix_and_rhs();
|
||||
|
||||
// now solve it
|
||||
|
||||
if (m_proc.resolved())
|
||||
{
|
||||
m_proc(&mat.A[0], &this->m_RHS[0], &this->m_new_V[0]);
|
||||
m_proc(&this->m_new_V[0],
|
||||
this->m_gonn.data(), this->m_gtn.data(), this->m_Idrn.data(),
|
||||
this->m_connected_net_Vn.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
// clear matrix
|
||||
mat.set_scalar(plib::constants<FT>::zero());
|
||||
|
||||
// populate matrix
|
||||
this->fill_matrix_and_rhs();
|
||||
|
||||
// now solve it
|
||||
// parallel is slow -- very slow
|
||||
// mat.gaussian_elimination_parallel(RHS);
|
||||
mat.gaussian_elimination(this->m_RHS);
|
||||
|
@ -391,8 +391,8 @@ namespace devices
|
||||
|
||||
log().verbose("Solver {1}", ms->name());
|
||||
log().verbose(" ==> {1} nets", grp.size());
|
||||
log().verbose(" has {1} elements", ms->has_dynamic_devices() ? "dynamic" : "no dynamic");
|
||||
log().verbose(" has {1} elements", ms->has_timestep_devices() ? "timestep" : "no timestep");
|
||||
log().verbose(" has {1} dynamic elements", ms->dynamic_device_count());
|
||||
log().verbose(" has {1} timestep elements", ms->timestep_device_count());
|
||||
for (auto &n : grp)
|
||||
{
|
||||
log().verbose("Net {1}", n->name());
|
||||
@ -403,7 +403,7 @@ namespace devices
|
||||
}
|
||||
|
||||
m_mat_solvers_all.push_back(ms.get());
|
||||
if (ms->has_timestep_devices())
|
||||
if (ms->timestep_device_count() != 0)
|
||||
m_mat_solvers_timestepping.push_back(ms.get());
|
||||
|
||||
m_mat_solvers.emplace_back(std::move(ms));
|
||||
|
@ -6,11 +6,7 @@
|
||||
#error Somehow nl_base.h made it into the include chain.
|
||||
#endif
|
||||
|
||||
#ifndef NLTOOL_VERSION
|
||||
#define USE_FRONTIERS 1
|
||||
#else
|
||||
#define USE_FRONTIERS 1
|
||||
#endif
|
||||
|
||||
/* if we use frontiers, use fixed STV for smaller matrix sizes */
|
||||
#if (USE_FRONTIERS)
|
||||
@ -361,7 +357,7 @@ NETLIST_END()
|
||||
|
||||
NETLIST_START(kidniki)
|
||||
|
||||
#if (0 || USE_FRONTIERS)
|
||||
#if (1 || USE_FRONTIERS)
|
||||
SOLVER(Solver, 48000)
|
||||
PARAM(Solver.ACCURACY, 1e-7)
|
||||
PARAM(Solver.NR_LOOPS, 300)
|
||||
|
Loading…
Reference in New Issue
Block a user