1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
|
// 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(pstring(D::gnd()), m_RN.N());
register_subalias(pstring(D::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]();}
static constexpr const char *vcc() { return "VCC"; }
static constexpr const char *gnd() { return "GND"; }
};
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)
}
static constexpr const char *vcc() { return "VDD"; }
static constexpr const char *gnd() { return "VSS"; }
};
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
|