netlist: MB3614 again, function controlled VARCLOCK and other

improvements.

- fix MB3614 parameter
- Added VARCLOCK which derives step size from function
- optimized function handling in CS and VS
- fixed a bug in ppreprocessor
- add trunc to pfunction
- added opamp_amplification_curve to derive characteristic
  amplification curve
This commit is contained in:
couriersud 2019-04-06 14:11:06 +02:00
parent 140dc2237e
commit 58e6383ada
14 changed files with 165 additions and 41 deletions

View File

@ -32,7 +32,7 @@ NETLIST_START(cmos_inverter_clk)
//CLOCK(V, 500000)
#else
VS(V, 5)
PARAM(V.FUNC, "T 5e6 *")
PARAM(V.FUNC, "T * 5e6")
#endif
MOSFET(P, "PMOS(VTO=-0.5 GAMMA=0.5 TOX=20n)")

View File

@ -111,11 +111,11 @@ NETLIST_START(dummy)
NET_C(RX1.2, XU16.7)
#endif
/* The opamp actually has an FPF of about 200k. This doesn't work here and causes oscillations.
/* The opamp actually has an FPF of about 1000k. This doesn't work here and causes oscillations.
* FPF here therefore about half the Solver clock.
*/
PARAM(XU16.B.MODEL, "MB3614(TYPE=3 UGF=22k)")
PARAM(XU17.C.MODEL, "MB3614(TYPE=3 UGF=22k)")
PARAM(XU16.B.MODEL, "MB3614(TYPE=3)")
PARAM(XU17.C.MODEL, "MB3614(TYPE=3 UGF=44k)")
#if 0
PARAM(XU17.A.MODEL, "MB3614(TYPE=1)")
PARAM(XU17.B.MODEL, "MB3614(TYPE=1)")

View File

@ -0,0 +1,72 @@
// license:GPL-2.0+
// copyright-holders:Couriersud
/*
* Script to analyze opamp amplification as a function of frequency.
*
* ./nltool -t 0.5 -f nl_examples/opamp_amplification_curve.cpp
*
* t=0.0: 10 Hz
* t=0.1: 100 Hz
* t=0.2: 1000 Hz
* t=0.3: 10000 Hz
* t=0.4: 100000 Hz
* ....
*
* ./plot_nl.sh --log Y Z
*/
#include "netlist/devices/net_lib.h"
#define OPAMP_TEST "MB3614(FPF=10 UGF=1000k)"
NETLIST_START(main)
/* Standard stuff */
//VARCLOCK(clk, "0.5 / pow(10, 1 + T * 4)")
//CLOCK(clk, 1000)
SOLVER(Solver, 48000)
PARAM(Solver.ACCURACY, 1e-7)
PARAM(Solver.NR_LOOPS, 300)
PARAM(Solver.DYNAMIC_TS, 1)
PARAM(Solver.DYNAMIC_MIN_TIMESTEP, 1e-7)
VS(vs, 0)
PARAM(vs.FUNC, "0.001 * sin(6.28 * pow(10, 1 + 10*T) * T)")
//PARAM(vs.FUNC, "0.001 * sin(6.28 * pow(10, trunc((1 + T * 4)*2)/2) * T)")
//PARAM(vs.FUNC, "1.001 * sin(6.28 * 100 * T)")
PARAM(vs.R, 0.001)
ALIAS(clk, vs.1)
NET_C(vs.2, GND)
ANALOG_INPUT(V12, 12)
ANALOG_INPUT(VM12, -12)
OPAMP(op,OPAMP_TEST)
NET_C(op.GND, VM12)
NET_C(op.VCC, V12)
/* Opamp B wired as inverting amplifier connected to output of first opamp */
RES(R1, 0.1)
RES(R2, 10000)
NET_C(op.PLUS, GND)
NET_C(op.MINUS, R2.2)
NET_C(op.MINUS, R1.2)
NET_C(clk, R1.1)
NET_C(op.OUT, R2.1)
RES(RL, 2000)
NET_C(RL.2, GND)
NET_C(RL.1, op.OUT)
AFUNC(f, 1, "A0 * 1000")
NET_C(f.A0, op.OUT)
#if 1
LOG(log_Y, R1.1)
LOG(log_Z, f)
#endif
NETLIST_END()

View File

@ -223,18 +223,14 @@ namespace netlist
m_EBUF->reset();
m_CP->reset();
m_RP.reset();
#if 0
double CP = 0.0;
double RP = 1000;
double G = m_model.m_UGF / m_model.m_FPF / RP;
#else
double CP = m_model.m_DAB / m_model.m_SLEW;
double RP = 0.5 / constants::pi() / CP / m_model.m_FPF;
double G = m_model.m_UGF / m_model.m_FPF / RP;
#endif
printf("OPAMP %s: %g %g %g\n", name().c_str(), CP, RP, G);
//printf("OPAMP %s: %g %g %g\n", name().c_str(), CP, RP, G);
if (m_model.m_SLEW / (4.0 * constants::pi() * 0.0258) < m_model.m_UGF)
printf("failed!\n");
log().warning("Opamp <{1}> parameters fail convergence criteria", this->name());
m_CP->m_C.setTo(CP);
m_RP.set_R(RP);

View File

@ -399,11 +399,12 @@ namespace analog
, m_V(*this, "V", 0.0)
, m_func(*this,"FUNC", "")
, m_compiled(this->name() + ".FUNCC", this, this->state().run_state_manager())
, m_funcparam({0.0})
{
register_subalias("P", m_P);
register_subalias("N", m_N);
if (m_func() != "")
m_compiled.compile_postfix(std::vector<pstring>({{"T"}}), m_func());
m_compiled.compile(std::vector<pstring>({{"T"}}), m_func());
}
NETLIB_IS_TIMESTEP(m_func() != "")
@ -411,8 +412,9 @@ namespace analog
NETLIB_TIMESTEPI()
{
m_t += step;
m_funcparam[0] = m_t;
this->set_G_V_I(1.0 / m_R(),
m_compiled.evaluate(std::vector<double>({m_t})),
m_compiled.evaluate(m_funcparam),
0.0);
}
@ -431,6 +433,7 @@ namespace analog
param_double_t m_V;
param_str_t m_func;
plib::pfunction m_compiled;
std::vector<double> m_funcparam;
};
// -----------------------------------------------------------------------------
@ -445,18 +448,20 @@ namespace analog
, m_I(*this, "I", 1.0)
, m_func(*this,"FUNC", "")
, m_compiled(this->name() + ".FUNCC", this, this->state().run_state_manager())
, m_funcparam({0.0})
{
register_subalias("P", m_P);
register_subalias("N", m_N);
if (m_func() != "")
m_compiled.compile_postfix(std::vector<pstring>({{"T"}}), m_func());
m_compiled.compile(std::vector<pstring>({{"T"}}), m_func());
}
NETLIB_IS_TIMESTEP(m_func() != "")
NETLIB_TIMESTEPI()
{
m_t += step;
const double I = m_compiled.evaluate(std::vector<double>({m_t}));
m_funcparam[0] = m_t;
const double I = m_compiled.evaluate(m_funcparam);
set_mat(0.0, 0.0, -I,
0.0, 0.0, I);
}
@ -476,6 +481,7 @@ namespace analog
param_double_t m_I;
param_str_t m_func;
plib::pfunction m_compiled;
std::vector<double> m_funcparam;
};

View File

@ -52,6 +52,7 @@ namespace devices
LIB_ENTRY(log)
LIB_ENTRY(logD)
LIB_ENTRY(clock)
LIB_ENTRY(varclock)
LIB_ENTRY(extclock)
LIB_ENTRY(mainclock)
LIB_ENTRY(gnd)

View File

@ -18,20 +18,6 @@ namespace netlist
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// clock
// ----------------------------------------------------------------------------------------
NETLIB_UPDATE_PARAM(clock)
{
m_inc = netlist_time::from_double(1.0 / (m_freq() * 2.0));
}
NETLIB_UPDATE(clock)
{
m_Q.push(!m_feedback(), m_inc);
}
// ----------------------------------------------------------------------------------------
// extclock
// ----------------------------------------------------------------------------------------
@ -123,6 +109,7 @@ namespace netlist
NETLIB_DEVICE_IMPL(function, "AFUNC", "N,FUNC")
NETLIB_DEVICE_IMPL(analog_input, "ANALOG_INPUT", "IN")
NETLIB_DEVICE_IMPL(clock, "CLOCK", "FREQ")
NETLIB_DEVICE_IMPL(varclock, "VARCLOCK", "FUNC")
NETLIB_DEVICE_IMPL(extclock, "EXTCLOCK", "FREQ,PATTERN")
NETLIB_DEVICE_IMPL(res_sw, "RES_SWITCH", "+IN,+P1,+P2")
NETLIB_DEVICE_IMPL(mainclock, "MAINCLOCK", "FREQ")

View File

@ -36,6 +36,10 @@
NET_REGISTER_DEV(CLOCK, name) \
PARAM(name.FREQ, freq)
#define VARCLOCK(name, func) \
NET_REGISTER_DEV(VARCLOCK, name) \
PARAM(name.FUNC, func)
#define EXTCLOCK(name, freq, pattern) \
NET_REGISTER_DEV(EXTCLOCK, name) \
PARAM(name.FREQ, freq) \

View File

@ -94,9 +94,17 @@ namespace netlist
connect(m_feedback, m_Q);
}
NETLIB_UPDATEI();
//NETLIB_RESETI();
NETLIB_UPDATE_PARAMI();
NETLIB_UPDATE_PARAMI()
{
m_inc = netlist_time::from_double(1.0 / (m_freq() * 2.0));
}
NETLIB_UPDATEI()
{
m_Q.push(!m_feedback(), m_inc);
}
private:
logic_input_t m_feedback;
@ -106,6 +114,42 @@ namespace netlist
netlist_time m_inc;
};
// -----------------------------------------------------------------------------
// varclock
// -----------------------------------------------------------------------------
NETLIB_OBJECT(varclock)
{
NETLIB_CONSTRUCTOR(varclock)
, m_feedback(*this, "FB")
, m_Q(*this, "Q")
, m_func(*this,"FUNC", "")
, m_compiled(this->name() + ".FUNCC", this, this->state().run_state_manager())
, m_funcparam({0.0})
{
if (m_func() != "")
m_compiled.compile(std::vector<pstring>({{"T"}}), m_func());
connect(m_feedback, m_Q);
}
//NETLIB_RESETI();
//NETLIB_UPDATE_PARAMI()
NETLIB_UPDATEI()
{
m_funcparam[0] = exec().time().as_double();
const netlist_time m_inc = netlist_time::from_double(m_compiled.evaluate(m_funcparam));
m_Q.push(!m_feedback(), m_inc);
}
private:
logic_input_t m_feedback;
logic_output_t m_Q;
param_str_t m_func;
plib::pfunction m_compiled;
std::vector<double> m_funcparam;
};
// -----------------------------------------------------------------------------
// extclock
// -----------------------------------------------------------------------------

View File

@ -225,7 +225,7 @@ NETLIST_START(OPAMP_lib)
NET_MODEL("LM324 OPAMP(TYPE=3 VLH=2.0 VLL=0.2 FPF=5 UGF=500k SLEW=0.3M RI=1000k RO=50 DAB=0.00075)")
NET_MODEL("LM358 OPAMP(TYPE=3 VLH=2.0 VLL=0.2 FPF=5 UGF=500k SLEW=0.3M RI=1000k RO=50 DAB=0.001)")
NET_MODEL("MB3614 OPAMP(TYPE=3 VLH=1.4 VLL=0.02 FPF=2 UGF=200k SLEW=0.6M RI=1000k RO=50 DAB=0.002)")
NET_MODEL("MB3614 OPAMP(TYPE=3 VLH=1.4 VLL=0.02 FPF=10 UGF=1000k SLEW=0.6M RI=1000k RO=50 DAB=0.002)")
NET_MODEL("UA741 OPAMP(TYPE=3 VLH=1.0 VLL=1.0 FPF=5 UGF=1000k SLEW=0.5M RI=2000k RO=75 DAB=0.0017)")
NET_MODEL("LM747 OPAMP(TYPE=3 VLH=1.0 VLL=1.0 FPF=5 UGF=1000k SLEW=0.5M RI=2000k RO=50 DAB=0.0017)")
NET_MODEL("LM747A OPAMP(TYPE=3 VLH=2.0 VLL=2.0 FPF=5 UGF=1000k SLEW=0.7M RI=6000k RO=50 DAB=0.0015)")

View File

@ -52,6 +52,8 @@ void pfunction::compile_postfix(const std::vector<pstring> &inputs,
{ rc.m_cmd = SIN; stk -= 0; }
else if (cmd == "cos")
{ rc.m_cmd = COS; stk -= 0; }
else if (cmd == "trunc")
{ rc.m_cmd = TRUNC; stk -= 0; }
else if (cmd == "rand")
{ rc.m_cmd = RAND; stk += 1; }
else
@ -72,16 +74,16 @@ void pfunction::compile_postfix(const std::vector<pstring> &inputs,
bool err;
rc.m_param = plib::pstonum_ne<decltype(rc.m_param)>(cmd, err);
if (err)
throw plib::pexception(plib::pfmt("nld_function: unknown/misformatted token <{1}> in <{2}>")(cmd)(expr));
throw plib::pexception(plib::pfmt("pfunction: unknown/misformatted token <{1}> in <{2}>")(cmd)(expr));
stk += 1;
}
}
if (stk < 1)
throw plib::pexception(plib::pfmt("nld_function: stack underflow on token <{1}> in <{2}>")(cmd)(expr));
throw plib::pexception(plib::pfmt("pfunction: stack underflow on token <{1}> in <{2}>")(cmd)(expr));
m_precompiled.push_back(rc);
}
if (stk != 1)
throw plib::pexception(plib::pfmt("nld_function: stack count different to one on <{2}>")(expr));
throw plib::pexception(plib::pfmt("pfunction: stack count different to one on <{2}>")(expr));
}
static int get_prio(const pstring &v)
@ -103,7 +105,7 @@ static int get_prio(const pstring &v)
static pstring pop_check(std::stack<pstring> &stk, const pstring &expr)
{
if (stk.size() == 0)
throw plib::pexception(plib::pfmt("nld_function: stack underflow during infix parsing of: <{1}>")(expr));
throw plib::pexception(plib::pfmt("pfunction: stack underflow during infix parsing of: <{1}>")(expr));
pstring res = stk.top();
stk.pop();
return res;
@ -201,6 +203,7 @@ double pfunction::evaluate(const std::vector<double> &values)
OP(POW, 1, std::pow(ST2, ST1))
OP(SIN, 0, std::sin(ST2))
OP(COS, 0, std::cos(ST2))
OP(TRUNC, 0, std::trunc(ST2))
case RAND:
stack[ptr++] = lfsr_random();
break;

View File

@ -34,6 +34,7 @@ namespace plib {
SIN,
COS,
RAND, /* random number between 0 and 1 */
TRUNC,
PUSH_CONST,
PUSH_INPUT
};

View File

@ -282,6 +282,7 @@ ppreprocessor::ppreprocessor(defines_map_type *defines)
m_expr_sep.emplace_back("&&");
m_expr_sep.emplace_back("||");
m_expr_sep.emplace_back("==");
m_expr_sep.emplace_back(",");
m_expr_sep.emplace_back(" ");
m_expr_sep.emplace_back("\t");
@ -508,9 +509,18 @@ pstring ppreprocessor::process_line(pstring line)
{
if (m_ifflag == 0)
{
if (lti.size() != 3)
error("PREPRO: only simple defines allowed: " + line);
m_defines.insert({lti[1], define_t(lti[1], lti[2])});
if (lti.size() < 2)
error("PREPRO: define needs at least one argument: " + line);
else if (lti.size() == 2)
m_defines.insert({lti[1], define_t(lti[1], "")});
else
{
pstring arg("");
for (int i=2; i<lti.size() - 1; i++)
arg += lti[i] + " ";
arg += lti[lti.size()-1];
m_defines.insert({lti[1], define_t(lti[1], arg)});
}
}
}
else

View File

@ -241,7 +241,7 @@ NETLIST_END()
static NETLIST_START(NOISE)
CS(FC, 0)
PARAM(FC.FUNC, "0.0000001 rand *")
PARAM(FC.FUNC, "0.0000001 * rand()")
ALIAS(E, FC.P)
ALIAS(B, FC.N)