mirror of
https://github.com/holub/mame
synced 2025-06-10 06:47:18 +03:00
249 lines
6.3 KiB
C++
249 lines
6.3 KiB
C++
// license:GPL-2.0+
|
|
// copyright-holders:Couriersud
|
|
/*
|
|
* nld_74123.c
|
|
*
|
|
*/
|
|
|
|
#include "netlist/analog/nlid_twoterm.h"
|
|
#include "nlid_system.h"
|
|
|
|
namespace netlist
|
|
{
|
|
namespace devices
|
|
{
|
|
|
|
/// \brief Base monostable device
|
|
///
|
|
/// The basic operation is the following:
|
|
///
|
|
/// After a trigger signal has been detected, the output changes to high.
|
|
/// The capacitor is quickly discharged until it reaches Vlow.
|
|
/// The device toggles into charging mode until Vhigh is reached. At this
|
|
/// point, the output is set to low. Charging continues until VCC - or
|
|
/// more specific VRC is reached.
|
|
///
|
|
/// Using
|
|
///
|
|
/// - Vlow = alpha * VCC
|
|
/// - Vhigh = (1-alpha) * VCC
|
|
/// - Ignoring Rext during discharge
|
|
///
|
|
/// we have calculate the following time constants:
|
|
///
|
|
/// - tD = - X*C * ln(alpha)
|
|
/// - tC = - R*C * ln(alhpa)
|
|
/// - tL = - R*C * ln(1.0 - alpha)
|
|
///
|
|
/// where tD denotes the time to discharge from VCC to Vlow, tC denotes
|
|
/// the time to charge from 0.0 to Vhigh and tL denotes the time to charge
|
|
/// from 0 to Vlow. X denotes the internal resistance used to discharge the
|
|
/// capacitor. The total impulse duration is thus
|
|
///
|
|
/// tP = tD + tC - tL
|
|
///
|
|
/// Using K = ln(1 - alpha) - ln (alpha) = ln(1/alpha - 1)
|
|
///
|
|
/// we get
|
|
///
|
|
/// tP = R*C*K * (1 + X/R * ln(alpha) / K)
|
|
///
|
|
/// and alpha = 1 / (1 + exp(K))
|
|
///
|
|
/// Datasheets express the right term usually as
|
|
///
|
|
/// (1 + f * 1000 / R) = (1 + X/R * ln(alpha) / K)
|
|
///
|
|
/// and thus
|
|
///
|
|
/// X = f * 1000 * K / ln(alpha)
|
|
///
|
|
/// FIXME: Currently X is given directly (as RI). It would be better to use
|
|
/// f (usually 0.7) and K to calculate X.
|
|
///
|
|
template <typename D>
|
|
NETLIB_OBJECT(74123_base)
|
|
{
|
|
NETLIB_CONSTRUCTOR(74123_base)
|
|
, m_RP(*this, "RP")
|
|
, m_RN(*this, "RN")
|
|
, m_RP_Q(*this, "_RP_Q")
|
|
, m_RN_Q(*this, "_RN_Q")
|
|
, m_I(*this, D::names(), NETLIB_DELEGATE(ab_clear))
|
|
, m_Q(*this, "Q")
|
|
, m_QQ(*this, "QQ")
|
|
, m_CV(*this, "_CV") // internal
|
|
, m_last_trig(*this, "m_last_trig", 0)
|
|
, m_state(*this, "m_state", 0)
|
|
, m_KP(plib::reciprocal(nlconst::one() + plib::exp(D::K())))
|
|
//, m_power_pins(*this)
|
|
{
|
|
|
|
register_subalias("GND", m_RN.N());
|
|
register_subalias("VCC", m_RP.P());
|
|
register_subalias("C", m_RN.N());
|
|
register_subalias("RC", m_RN.P());
|
|
|
|
connect(m_RP_Q, m_RP.I());
|
|
connect(m_RN_Q, m_RN.I());
|
|
|
|
connect(m_RN.P(), m_RP.N());
|
|
connect(m_CV, m_RN.P());
|
|
|
|
m_RP.m_RON.set(D::RI());
|
|
m_RN.m_RON.set(D::RI());
|
|
}
|
|
|
|
NETLIB_HANDLERI(ab_clear)
|
|
{
|
|
netlist_sig_t m_trig(0);
|
|
|
|
m_trig = D::trigfunc(m_I);
|
|
|
|
if (!D::clear(m_I))
|
|
{
|
|
m_Q.push(0, D::t_C_to_Q::value());
|
|
m_QQ.push(1, D::t_C_to_Q::value());
|
|
/* quick charge until trigger */
|
|
/* FIXME: SGS datasheet shows quick charge to 5V,
|
|
* though schematics indicate quick charge to Vhigh only.
|
|
*/
|
|
m_RP_Q.push(1, D::t_C_to_Q::value()); // R_ON
|
|
m_RN_Q.push(0, D::t_C_to_Q::value()); // R_OFF
|
|
m_state = 2; //charging (quick)
|
|
}
|
|
else if (!m_last_trig && m_trig)
|
|
{
|
|
// FIXME: Timing!
|
|
m_Q.push(1, D::t_AB_to_Q::value());
|
|
m_QQ.push(0, D::t_AB_to_Q::value());
|
|
|
|
m_RN_Q.push(1, D::t_AB_to_Q::value()); // R_ON
|
|
m_RP_Q.push(0, D::t_AB_to_Q::value()); // R_OFF
|
|
|
|
m_state = 1; // discharging
|
|
}
|
|
|
|
m_last_trig = m_trig;
|
|
}
|
|
|
|
NETLIB_UPDATEI()
|
|
{
|
|
if (m_state == 1)
|
|
{
|
|
const nl_fptype vLow = m_KP * m_RP.P()();
|
|
if (m_CV() < vLow)
|
|
{
|
|
m_RN_Q.push(0, NLTIME_FROM_NS(10)); // R_OFF
|
|
m_state = 2; // charging
|
|
}
|
|
}
|
|
if (m_state == 2)
|
|
{
|
|
const nl_fptype vHigh = (nlconst::one() - m_KP) * m_RP.P()();
|
|
if (m_CV() > vHigh)
|
|
{
|
|
m_RP_Q.push(0, NLTIME_FROM_NS(10)); // R_OFF
|
|
|
|
m_Q.push(0, NLTIME_FROM_NS(10));
|
|
m_QQ.push(1, NLTIME_FROM_NS(10));
|
|
m_state = 0; // waiting
|
|
}
|
|
}
|
|
}
|
|
|
|
NETLIB_RESETI()
|
|
{
|
|
m_RP.reset();
|
|
m_RN.reset();
|
|
|
|
//m_RP.set_R(R_OFF);
|
|
//m_RN.set_R(R_OFF);
|
|
|
|
m_last_trig = 0;
|
|
m_state = 0;
|
|
}
|
|
|
|
private:
|
|
NETLIB_SUB(sys_dsw1) m_RP;
|
|
NETLIB_SUB(sys_dsw1) m_RN;
|
|
|
|
logic_output_t m_RP_Q;
|
|
logic_output_t m_RN_Q;
|
|
|
|
object_array_t<logic_input_t, 3> m_I;
|
|
logic_output_t m_Q;
|
|
logic_output_t m_QQ;
|
|
|
|
analog_input_t m_CV;
|
|
|
|
state_var<netlist_sig_t> m_last_trig;
|
|
state_var<unsigned> m_state;
|
|
nl_fptype m_KP;
|
|
//nld_power_pins m_power_pins; // not needed, device exposes VCC and GND
|
|
};
|
|
|
|
struct desc_74123 : public desc_base
|
|
{
|
|
using t_AB_to_Q = time_ns<10>;
|
|
using t_C_to_Q = time_ns<10>;
|
|
static constexpr nl_fptype K() { return nlconst::magic(0.4); }
|
|
static constexpr nl_fptype RI() { return nlconst::magic(400.0); }
|
|
|
|
static constexpr std::array<const char *, 3> names() { return {"CLRQ", "A", "B"};}
|
|
template<typename T>
|
|
static netlist_sig_t trigfunc(const T &in)
|
|
{
|
|
return ((in[1]() | (in[2]() ^ 1)) ^ 1) & in[0](); // ((m_A() | (m_B() ^ 1)) ^ 1) & m_CLRQ()
|
|
}
|
|
template<typename T> static constexpr netlist_sig_t clear(const T &in) { return in[0]();}
|
|
};
|
|
|
|
struct desc_74121 : public desc_74123
|
|
{
|
|
static constexpr nl_fptype K() { return nlconst::magic(0.7); }
|
|
|
|
static constexpr std::array<const char *, 3> names() { return {"A1", "A2", "B"};}
|
|
template<typename T>
|
|
static netlist_sig_t trigfunc(const T &in)
|
|
{
|
|
return ((in[0]() ^ 1) | (in[1]() ^ 1)) & in[2](); // (~A1 | ~A2) & B
|
|
}
|
|
template<typename T> static constexpr netlist_sig_t clear(const T &in) { plib::unused_var(in); return 1;}
|
|
};
|
|
|
|
struct desc_9602 : public desc_74123
|
|
{
|
|
template<typename T>
|
|
static netlist_sig_t trigfunc(const T &in)
|
|
{
|
|
return ((in[1]() ^ 1) | in[2]()); // (m_A() ^ 1) | m_B()
|
|
}
|
|
};
|
|
|
|
struct desc_4538 : public desc_74123
|
|
{
|
|
using t_AB_to_Q = time_ns<300>;
|
|
using t_C_to_Q = time_ns<250>;
|
|
static constexpr nl_fptype K() { return nlconst::one(); } // CD4538 datasheet states PW=RC
|
|
|
|
template<typename T>
|
|
static netlist_sig_t trigfunc(const T &in)
|
|
{
|
|
return (in[1]() | (in[2]() ^ 1)); // m_A() | (m_B() ^ 1)
|
|
}
|
|
};
|
|
|
|
using NETLIB_NAME(74123) = NETLIB_NAME(74123_base)<desc_74123>;
|
|
using NETLIB_NAME(74121) = NETLIB_NAME(74123_base)<desc_74121>;
|
|
using NETLIB_NAME(4538) = NETLIB_NAME(74123_base)<desc_4538>;
|
|
using NETLIB_NAME(9602) = NETLIB_NAME(74123_base)<desc_9602>;
|
|
|
|
NETLIB_DEVICE_IMPL(74123, "TTL_74123", "")
|
|
NETLIB_DEVICE_IMPL(74121, "TTL_74121", "")
|
|
NETLIB_DEVICE_IMPL(4538, "CD4538", "")
|
|
NETLIB_DEVICE_IMPL(9602, "TTL_9602", "")
|
|
|
|
} //namespace devices
|
|
} // namespace netlist
|