netlist: unify solver calls. (nw)

Calls to the solver used different paths. These changes unify these
calls and provide a common call for discontinuous state changes on
analog components (change_state).
This commit is contained in:
couriersud 2020-04-25 18:08:04 +02:00
parent 14f92c9786
commit d3ee7e21c6
16 changed files with 185 additions and 171 deletions

View File

@ -49,17 +49,10 @@ namespace netlist
NETLIB_UPDATE_PARAM(switch1)
{
m_R.solve_now();
if (!m_POS())
m_R.change_state([this]()
{
m_R.set_R(R_OFF);
}
else
{
m_R.set_R(R_ON);
}
m_R.solve_later();
m_R.set_R(m_POS() ? R_ON : R_OFF);
});
}
// ----------------------------------------------------------------------------------------
@ -112,21 +105,19 @@ namespace netlist
NETLIB_UPDATE_PARAM(switch2)
{
// FIXME: We only need to update the net first if this is a time stepping net
m_R1.solve_now();
m_R2.solve_now();
if (!m_POS())
{
m_R1.set_R(R_ON);
m_R2.set_R(R_OFF);
}
// R1 and R2 are connected. However this net may be a rail net.
// The code here thus is a bit more complex.
nl_fptype r1 = m_POS() ? R_OFF : R_ON;
nl_fptype r2 = m_POS() ? R_ON : R_OFF;
if (m_R1.solver() == m_R2.solver())
m_R1.change_state([this, &r1, &r2]() { m_R1.set_R(r1); m_R2.set_R(r2); });
else
{
m_R1.set_R(R_OFF);
m_R2.set_R(R_ON);
m_R1.change_state([this, &r1]() { m_R1.set_R(r1); });
m_R2.change_state([this, &r2]() { m_R2.set_R(r2); });
}
m_R1.solve_later();
m_R2.solve_later();
}
} //namespace analog

View File

@ -15,24 +15,21 @@ namespace analog
// nld_twoterm
// ----------------------------------------------------------------------------------------
void NETLIB_NAME(twoterm)::solve_now()
solver::matrix_solver_t * NETLIB_NAME(twoterm)::solver() const noexcept
{
// we only need to call the non-rail terminal
if (m_P.has_net() && !m_P.net().is_rail_net())
m_P.solve_now();
else if (m_N.has_net() && !m_N.net().is_rail_net())
m_N.solve_now();
auto *solv(m_P.solver());
if (solv)
return solv;
return m_N.solver();
}
void NETLIB_NAME(twoterm)::solve_later(netlist_time delay) noexcept
{
// we only need to call the non-rail terminal
if (m_P.has_net() && !m_P.net().is_rail_net())
m_P.schedule_solve_after(delay);
else if (m_N.has_net() && !m_N.net().is_rail_net())
m_N.schedule_solve_after(delay);
}
void NETLIB_NAME(twoterm)::solve_now() const
{
auto *solv(solver());
if (solv)
solv->solve_now();
}
NETLIB_UPDATE(twoterm)
{
@ -66,19 +63,22 @@ namespace analog
NETLIB_UPDATE_PARAM(POT)
{
// FIXME: We only need to update the net first if this is a time stepping net
m_R1.solve_now();
m_R2.solve_now();
nl_fptype v = m_Dial();
if (m_DialIsLog())
v = (plib::exp(v) - nlconst::one()) / (plib::exp(nlconst::one()) - nlconst::one());
if (m_Reverse())
v = nlconst::one() - v;
m_R1.set_R(std::max(m_R() * v, exec().gmin()));
m_R2.set_R(std::max(m_R() * (nlconst::one() - v), exec().gmin()));
m_R1.solve_later();
m_R2.solve_later();
nl_fptype r1(std::max(m_R() * v, exec().gmin()));
nl_fptype r2(std::max(m_R() * (nlconst::one() - v), exec().gmin()));
if (m_R1.solver() == m_R2.solver())
m_R1.change_state([this, &r1, &r2]() { m_R1.set_R(r1); m_R2.set_R(r2); });
else
{
m_R1.change_state([this, &r1]() { m_R1.set_R(r1); });
m_R2.change_state([this, &r2]() { m_R2.set_R(r2); });
}
}
@ -100,17 +100,17 @@ namespace analog
NETLIB_UPDATE_PARAM(POT2)
{
// FIXME: We only need to update the net first if this is a time stepping net
m_R1.solve_now();
nl_fptype v = m_Dial();
if (m_DialIsLog())
v = (plib::exp(v) - nlconst::one()) / (plib::exp(nlconst::one()) - nlconst::one());
if (m_Reverse())
v = nlconst::one() - v;
m_R1.set_R(std::max(m_R() * v, exec().gmin()));
m_R1.solve_later();
m_R1.change_state([this, &v]()
{
m_R1.set_R(std::max(m_R() * v, exec().gmin()));
});
}
// ----------------------------------------------------------------------------------------

View File

@ -86,9 +86,17 @@ namespace analog
NETLIB_UPDATEI();
void solve_now();
solver::matrix_solver_t *solver() const noexcept;
void solve_later(netlist_time delay = netlist_time::quantum()) noexcept;
void solve_now() const;
template <typename F>
void change_state(F f, netlist_time delay = netlist_time::quantum())
{
auto *solv(solver());
if (solv)
solv->change_state(f, delay);
}
void set_G_V_I(nl_fptype G, nl_fptype V, nl_fptype I) const noexcept
{
@ -168,9 +176,10 @@ namespace analog
NETLIB_UPDATE_PARAMI()
{
// FIXME: We only need to update the net first if this is a time stepping net
solve_now();
set_R(std::max(m_R(), exec().gmin()));
solve_later();
change_state([this]()
{
set_R(std::max(m_R(), exec().gmin()));
});
}
private:
@ -524,11 +533,12 @@ namespace analog
{
// FIXME: We only need to update the net first if this is a time stepping net
//FIXME: works only for CS without function
solve_now();
const auto zero(nlconst::zero());
set_mat(zero, zero, -m_I(),
zero, zero, m_I());
solve_later();
change_state([this]()
{
const auto zero(nlconst::zero());
set_mat(zero, zero, -m_I(),
zero, zero, m_I());
});
}
private:

View File

@ -69,9 +69,7 @@ namespace netlist
if (R > nlconst::zero() && (m_last != new_state))
{
m_last = new_state;
m_R.solve_now();
m_R.set_R(R);
m_R.solve_later();
m_R.change_state([this, &R]() -> void { this->m_R.set_R(R);});
}
}

View File

@ -43,12 +43,14 @@ namespace netlist { namespace devices {
NETLIB_UPDATE(CD4316_GATE)
{
m_R.solve_now();
if (m_S() && !m_E())
m_R.set_R(m_base_r());
else
m_R.set_R(plib::reciprocal(exec().gmin()));
m_R.solve_later(NLTIME_FROM_NS(1));
m_R.change_state([this]()
{
if (m_S() && !m_E())
m_R.set_R(m_base_r());
else
m_R.set_R(plib::reciprocal(exec().gmin()));
}
, NLTIME_FROM_NS(1));
}
NETLIB_DEVICE_IMPL(CD4316_GATE, "CD4316_GATE", "")

View File

@ -28,7 +28,6 @@ namespace netlist
, m_Q(*this, "_Q")
, m_inc(netlist_time::from_hz(24000 * 2))
, m_shift(*this, "m_shift", 0)
, m_is_timestep(false)
{
connect(m_feedback, m_Q);
@ -57,9 +56,6 @@ namespace netlist
/* state */
state_var_u32 m_shift;
/* cache */
bool m_is_timestep;
};
NETLIB_RESET(MM5837_dip)
@ -74,7 +70,6 @@ namespace netlist
log().warning(MW_FREQUENCY_OUTSIDE_OF_SPECS_1(m_FREQ()));
m_shift = 0x1ffff;
m_is_timestep = (m_RV.m_P.net().solver()->timestep_device_count() > 0);
}
NETLIB_UPDATE_PARAM(MM5837_dip)
@ -104,11 +99,10 @@ namespace netlist
const nl_fptype R = state ? m_R_HIGH : m_R_LOW;
const nl_fptype V = state ? m_VDD() : m_VSS();
// We only need to update the net first if this is a time stepping net
if (m_is_timestep)
m_RV.solve_now();
m_RV.set_G_V_I(plib::reciprocal(R), V, nlconst::zero());
m_RV.solve_later(NLTIME_FROM_NS(1));
m_RV.change_state([this, &R, &V]()
{
m_RV.set_G_V_I(plib::reciprocal(R), V, nlconst::zero());
});
}
}

View File

@ -39,13 +39,13 @@ namespace netlist
NETLIB_UPDATE_PARAM(r2r_dac)
{
// FIXME: We only need to update the net first if this is a time stepping net
solve_now();
nl_fptype V = m_VIN() / static_cast<nl_fptype>(1 << m_num())
* static_cast<nl_fptype>(m_val());
this->set_G_V_I(plib::reciprocal(m_R()), V, nlconst::zero());
solve_later();
change_state([this, &V]()
{
this->set_G_V_I(plib::reciprocal(m_R()), V, nlconst::zero());
}
);
}
} //namespace analog

View File

@ -67,7 +67,6 @@ namespace netlist
, m_RVO(*this, "RVO")
, m_model(*this, "MODEL", "TTL_7414_GATE")
, m_last_state(*this, "m_last_var", 1)
, m_is_timestep(false)
{
register_subalias("Q", m_RVO.m_P);
@ -82,7 +81,6 @@ namespace netlist
m_last_state = 1;
m_RVI.reset();
m_RVO.reset();
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());
}
@ -95,10 +93,10 @@ namespace netlist
if (va < m_model.m_VTM)
{
m_last_state = 0;
if (m_is_timestep)
m_RVO.solve_now();
m_RVO.set_G_V_I(plib::reciprocal(m_model.m_ROH()), m_model.m_VOH, nlconst::zero());
m_RVO.solve_later();
m_RVO.change_state([this]()
{
m_RVO.set_G_V_I(plib::reciprocal(m_model.m_ROH()), m_model.m_VOH, nlconst::zero());
});
}
}
else
@ -106,10 +104,10 @@ namespace netlist
if (va > m_model.m_VTP)
{
m_last_state = 1;
if (m_is_timestep)
m_RVO.solve_now();
m_RVO.set_G_V_I(plib::reciprocal(m_model.m_ROL()), m_model.m_VOL, nlconst::zero());
m_RVO.solve_later();
m_RVO.change_state([this]()
{
m_RVO.set_G_V_I(plib::reciprocal(m_model.m_ROL()), m_model.m_VOL, nlconst::zero());
});
}
}
}
@ -121,7 +119,6 @@ namespace netlist
analog::NETLIB_SUB(twoterm) m_RVO;
schmitt_trigger_model_t m_model;
state_var<int> m_last_state;
bool m_is_timestep;
};
NETLIB_DEVICE_IMPL(schmitt_trigger, "SCHMITT_TRIGGER", "MODEL")

View File

@ -117,7 +117,6 @@ namespace netlist
, m_RP(*this, "RP")
, m_RN(*this, "RN")
, m_last_state(*this, "m_last_var", -1)
, m_is_timestep(false)
{
register_subalias("Q", m_RN.m_P);
@ -145,7 +144,6 @@ namespace netlist
m_last_state = -1;
m_RN.reset();
m_RP.reset();
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,
@ -158,28 +156,27 @@ namespace netlist
const auto state = static_cast<int>(m_I());
if (state != m_last_state)
{
// We only need to update the net first if this is a time stepping net
if (m_is_timestep)
// RN, RP are connected ...
m_RN.change_state([this, &state]()
{
m_RN.solve_now(); // RN, RP are connected ...
}
if (state)
{
m_RN.set_G_V_I(G_OFF,
nlconst::zero(),
nlconst::zero());
m_RP.set_G_V_I(plib::reciprocal(logic_family()->R_high()),
logic_family()->high_offset_V(), nlconst::zero());
}
else
{
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,
nlconst::zero(),
nlconst::zero());
}
m_RN.solve_later(); // RN, RP are connected ...
if (state)
{
m_RN.set_G_V_I(G_OFF,
nlconst::zero(),
nlconst::zero());
m_RP.set_G_V_I(plib::reciprocal(logic_family()->R_high()),
logic_family()->high_offset_V(), nlconst::zero());
}
else
{
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,
nlconst::zero(),
nlconst::zero());
}
});
m_last_state = state;
}
}

View File

@ -119,7 +119,6 @@ namespace devices
analog::NETLIB_NAME(twoterm) m_RP;
analog::NETLIB_NAME(twoterm) m_RN;
state_var<int> m_last_state;
bool m_is_timestep;
};
} // namespace devices

View File

@ -463,9 +463,10 @@ namespace devices
const nl_fptype R = state ? m_RON() : m_ROFF();
// FIXME: We only need to update the net first if this is a time stepping net
m_R.solve_now();
m_R.set_R(R);
m_R.solve_later();
m_R.change_state([this, &R]()
{
m_R.set_R(R);
});
}
}

View File

@ -813,18 +813,10 @@ namespace netlist
void terminal_t::solve_now()
{
const auto *solv(solver());
// Nets may belong to railnets which do not have a solver attached
if (this->has_net())
if (net().solver() != nullptr)
net().solver()->solve_now();
}
void terminal_t::schedule_solve_after(netlist_time after) noexcept
{
// 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);
if (solv)
solver()->solve_now();
}
// ----------------------------------------------------------------------------------------

View File

@ -798,6 +798,8 @@ namespace netlist
const analog_net_t & net() const noexcept;
analog_net_t & net() noexcept;
solver::matrix_solver_t *solver() const noexcept;
};
// -----------------------------------------------------------------------------
@ -834,7 +836,6 @@ namespace netlist
}
void solve_now();
void schedule_solve_after(netlist_time after) noexcept;
void set_ptrs(nl_fptype *gt, nl_fptype *go, nl_fptype *Idr) noexcept(false);
@ -945,6 +946,11 @@ namespace netlist
solver::matrix_solver_t *solver() const noexcept { return m_solver; }
void set_solver(solver::matrix_solver_t *solver) noexcept { m_solver = solver; }
friend constexpr bool operator==(const analog_net_t &lhs, const analog_net_t &rhs) noexcept
{
return &lhs == &rhs;
}
private:
state_var<nl_fptype> m_cur_Analog;
solver::matrix_solver_t *m_solver;
@ -1940,6 +1946,13 @@ namespace netlist
return static_cast<analog_net_t &>(core_terminal_t::net());
}
inline solver::matrix_solver_t *analog_t::solver() const noexcept
{
if (this->has_net())
return net().solver();
return nullptr;
}
inline nl_fptype terminal_t::operator ()() const noexcept { return net().Q_Analog(); }
inline void terminal_t::set_ptrs(nl_fptype *gt, nl_fptype *go, nl_fptype *Idr) noexcept(false)

View File

@ -389,6 +389,20 @@ namespace solver
inp->push(inp->proxied_net()->Q_Analog());
}
bool matrix_solver_t::updates_net(const analog_net_t *net) const noexcept
{
if (net != nullptr)
{
for (const auto &t : m_terms )
if (t.is_net(net))
return true;
for (const auto &inp : m_inps)
if (&inp->net() == net)
return true;
}
return false;
}
void matrix_solver_t::update_dynamic() noexcept
{
// update all non-linear devices
@ -412,22 +426,6 @@ namespace solver
}
}
void matrix_solver_t::solve_now()
{
// this should only occur outside of execution and thus
// using time should be safe.
const netlist_time new_timestep = solve(exec().time());
plib::unused_var(new_timestep);
update_inputs();
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(netlist_time delta) noexcept
{
const auto dd(delta.as_fp<nl_fptype>());
@ -495,7 +493,7 @@ namespace solver
int matrix_solver_t::get_net_idx(const analog_net_t *net) const noexcept
{
for (std::size_t k = 0; k < m_terms.size(); k++)
if (m_terms[k].isNet(net))
if (m_terms[k].is_net(net))
return static_cast<int>(k);
return -1;
}

View File

@ -143,13 +143,11 @@ namespace solver
terminal_t **terms() noexcept { return m_terms.data(); }
template <typename FT, typename = std::enable_if<plib::is_floating_point<FT>::value, void>>
FT getV() const noexcept { return static_cast<FT>(m_net->Q_Analog()); }
nl_fptype getV() const noexcept { return m_net->Q_Analog(); }
template <typename FT, typename = std::enable_if<plib::is_floating_point<FT>::value, void>>
void setV(FT v) noexcept { m_net->set_Q_Analog(static_cast<nl_fptype>(v)); }
void setV(nl_fptype v) noexcept { m_net->set_Q_Analog(v); }
bool isNet(const analog_net_t * net) const noexcept { return net == m_net; }
bool is_net(const analog_net_t * net) const noexcept { return net == m_net; }
void set_railstart(std::size_t val) noexcept { m_railstart = val; }
@ -191,6 +189,14 @@ namespace solver
netlist_time solve(netlist_time_ext now);
void update_inputs();
/// \brief Checks if solver may alter a net
///
/// This checks if a solver will alter a net. Returns true if the
/// net is either part of the voltage vector or if it belongs to
/// the analog input nets connected to the solver.
bool updates_net(const analog_net_t *net) const noexcept;
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(); }
@ -199,11 +205,30 @@ namespace solver
/// This should only be called from update and update_param events.
/// It's purpose is to bring voltage values to the current timestep.
/// This will be called BEFORE updating object properties.
void solve_now();
void update_after(netlist_time after) noexcept
void solve_now()
{
m_Q_sync.net().toggle_and_push_to_queue(after);
// this should only occur outside of execution and thus
// using time should be safe.
const netlist_time new_timestep = solve(exec().time());
plib::unused_var(new_timestep);
update_inputs();
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));
}
}
template <typename F>
void change_state(F f, netlist_time delay = netlist_time::quantum())
{
// We only need to update the net first if this is a time stepping net
if (timestep_device_count() > 0)
solve_now();
f();
m_Q_sync.net().toggle_and_push_to_queue(delay);
}
// netdevice functions
@ -413,7 +438,7 @@ namespace solver
{
const std::size_t iN = size();
for (std::size_t i = 0; i < iN; i++)
this->m_terms[i].setV(m_new_V[i]);
this->m_terms[i].setV(static_cast<nl_fptype>(m_new_V[i]));
}
#else
// global tanh damping (4.197)
@ -439,7 +464,7 @@ namespace solver
const auto vntol(static_cast<float_type>(m_params.m_vntol));
for (std::size_t i = 0; i < iN; i++)
{
const auto vold(this->m_terms[i].template getV<float_type>());
const auto vold(static_cast<float_type>(this->m_terms[i].getV()));
const auto vnew(m_new_V[i]);
const auto tol(vntol + reltol * std::max(plib::abs(vnew),plib::abs(vold)));
if (plib::abs(vnew - vold) > tol)
@ -455,7 +480,7 @@ namespace solver
for (std::size_t k = 0; k < size(); k++)
{
const auto &t = m_terms[k];
const auto v(t.template getV<nl_fptype>());
const auto v(static_cast<nl_fptype>(t.getV()));
// avoid floating point exceptions
const nl_fptype DD_n = std::max(-fp_constants<nl_fptype>::TIMESTEP_MAXDIFF(),
std::min(+fp_constants<nl_fptype>::TIMESTEP_MAXDIFF(),(v - m_last_V[k])));

View File

@ -183,30 +183,27 @@ namespace devices
{
return create_solver<FT, -16>(net_count, sname, nets);
}
else if (net_count <= 32)
if (net_count <= 32)
{
return create_solver<FT, -32>(net_count, sname, nets);
}
else if (net_count <= 64)
if (net_count <= 64)
{
return create_solver<FT, -64>(net_count, sname, nets);
}
else if (net_count <= 128)
if (net_count <= 128)
{
return create_solver<FT, -128>(net_count, sname, nets);
}
else if (net_count <= 256)
if (net_count <= 256)
{
return create_solver<FT, -256>(net_count, sname, nets);
}
else if (net_count <= 512)
if (net_count <= 512)
{
return create_solver<FT, -512>(net_count, sname, nets);
}
else
{
return create_solver<FT, 0>(net_count, sname, nets);
}
return create_solver<FT, 0>(net_count, sname, nets);
break;
}
}