/*$Id: d_switch.cc,v 26.14 2007/02/07 09:06:48 al Exp $ -*- C++ -*- * Copyright (C) 2001 Albert Davis * Author: Albert Davis * * This file is part of "Gnucap", the Gnu Circuit Analysis Package * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. *------------------------------------------------------------------ * voltage (and current) controlled switch. * netlist syntax: * device: Sxxxx n+ n- vc+ vc- mname | * model: .model mname SW * current controlled switch * device: Wxxxx n+ n- controlelement mname | * model: .model mname CSW */ //testing=script 2006.06.14 #include "l_dispatcher.h" #include "e_model.h" #include "e_elemnt.h" extern DISPATCHER model_dispatcher; extern DISPATCHER device_dispatcher; /*--------------------------------------------------------------------------*/ namespace { /*--------------------------------------------------------------------------*/ class MODEL_SWITCH : public MODEL_CARD { private: explicit MODEL_SWITCH(const MODEL_SWITCH& p); public: explicit MODEL_SWITCH(); private: // override virtual CARD* clone()const {return new MODEL_SWITCH(*this);} void parse_spice(CS&); void elabo1(); void print_front(OMSTREAM&,LANGUAGE)const; void print_params(OMSTREAM&,LANGUAGE)const; public: PARAMETER vt; /* threshold voltage */ PARAMETER vh; /* hysteresis voltage */ PARAMETER ron; /* on resistance */ PARAMETER roff; /* off resistance */ PARAMETER von; PARAMETER voff; enum control_t {VOLTAGE, CURRENT}; control_t type; /* current or voltage controlled */ private: static double const _default_vt; static double const _default_vh; static double const _default_ron; static double const _default_roff; }; /*--------------------------------------------------------------------------*/ class COMMON_SWITCH : public COMMON_COMPONENT { private: explicit COMMON_SWITCH(const COMMON_SWITCH& p) :COMMON_COMPONENT(p) {} public: explicit COMMON_SWITCH(int c=0) :COMMON_COMPONENT(c) {} bool operator==(const COMMON_COMPONENT&)const; COMMON_COMPONENT* clone()const {return new COMMON_SWITCH(*this);} const char* name()const {untested(); return "switch";} }; /*--------------------------------------------------------------------------*/ class SWITCH_BASE : public ELEMENT { protected: explicit SWITCH_BASE(); explicit SWITCH_BASE(const SWITCH_BASE& p); protected: // override virtual const char* dev_type()const {assert(common()); return common()->modelname().c_str();} bool print_type_in_spice()const {return true;} int max_nodes()const = 0; int min_nodes()const = 0; int out_nodes()const {untested(); return 2;} int matrix_nodes()const {return 2;} int net_nodes()const = 0; bool is_1port()const {untested(); return true;} CARD* clone()const = 0; void parse_spice(CS&); void print(OMSTREAM&,LANGUAGE)const; void elabo1(); //void map_nodes(); //ELEMENT void precalc(); void tr_iwant_matrix() {tr_iwant_matrix_passive();} void dc_begin() {tr_begin();} void tr_begin(); //void tr_restore(); //CARD/nothing void dc_advance(); void tr_advance(); bool tr_needs_eval()const {return analysis_is_static();} //void tr_queue_eval(); //ELEMENT bool do_tr(); void tr_load() {tr_load_passive();} DPAIR tr_review(); //void tr_accept(); //CARD/nothing void tr_unload() {untested(); tr_unload_passive();} double tr_involts()const {untested(); return tr_outvolts();} //double tr_input()const //ELEMENT double tr_involts_limited()const {unreachable(); return tr_outvolts_limited();} //double tr_input_limited()const //ELEMENT //double tr_amps()const //ELEMENT double tr_probe_num(CS&)const; void ac_iwant_matrix() {ac_iwant_matrix_passive();} void ac_begin() {_ev = _y0.f1; _acg = _m0.c1;} void do_ac(); void ac_load() {ac_load_passive();} COMPLEX ac_involts()const {untested(); return ac_outvolts();} //COMPLEX ac_amps()const; //ELEMENT //XPROBE ac_probe_ext(CS&)const;//ELEMENT protected: std::string _input_label; /*this is here instead of in Cswitch*/ const ELEMENT* _input; /* due to bad design and lazyness */ private: enum state_t {_UNKNOWN, _ON, _OFF}; state_t _ic; /* initial state, belongs in common */ double _time_future; enum {_keep_time_steps = 2}; double _time[_keep_time_steps]; double _in[_keep_time_steps]; state_t _state[_keep_time_steps]; }; /*--------------------------------------------------------------------------*/ class DEV_VSWITCH : public SWITCH_BASE { private: explicit DEV_VSWITCH(const DEV_VSWITCH& p) :SWITCH_BASE(p) {} public: explicit DEV_VSWITCH() :SWITCH_BASE() {} private: // override virtual int max_nodes()const {return 4;} int min_nodes()const {return 4;} int net_nodes()const {return 4;} CARD* clone()const {return new DEV_VSWITCH(*this);} //const char* dev_type()const {untested(); return "vswitch";} char id_letter()const {return 'S';} }; /*--------------------------------------------------------------------------*/ class DEV_CSWITCH : public SWITCH_BASE { private: explicit DEV_CSWITCH(const DEV_CSWITCH& p) :SWITCH_BASE(p) {} public: explicit DEV_CSWITCH() :SWITCH_BASE() {} private: // override virtual int max_nodes()const {return 2;} int min_nodes()const {return 2;} int net_nodes()const {return 2;} CARD* clone()const {return new DEV_CSWITCH(*this);} void elabo1(); //const char* dev_type()const {untested(); return "iswitch";} char id_letter()const {return 'W';} }; /*--------------------------------------------------------------------------*/ static COMMON_SWITCH Default_SWITCH(CC_STATIC); double const MODEL_SWITCH::_default_vt = 0.; double const MODEL_SWITCH::_default_vh = 0.; double const MODEL_SWITCH::_default_ron = 1.; double const MODEL_SWITCH::_default_roff = 1e12; /*--------------------------------------------------------------------------*/ bool COMMON_SWITCH::operator==(const COMMON_COMPONENT& x)const { const COMMON_SWITCH* p = dynamic_cast(&x); bool rv = p && COMMON_COMPONENT::operator==(x); if (rv) { untested(); } return rv; } /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ MODEL_SWITCH::MODEL_SWITCH() :MODEL_CARD(), vt(_default_vt), vh(_default_vh), ron(_default_ron), roff(_default_roff), von(_default_vt + _default_vh), voff(_default_vt - _default_vh), type(VOLTAGE) { } /*--------------------------------------------------------------------------*/ MODEL_SWITCH::MODEL_SWITCH(const MODEL_SWITCH& p) :MODEL_CARD(p), vt(p.vt), vh(p.vh), ron(p.ron), roff(p.roff), von(p.von), voff(p.voff), type(p.type) { } /*--------------------------------------------------------------------------*/ void MODEL_SWITCH::parse_spice(CS& cmd) { cmd.reset(); cmd.skiparg(); // skip known ".model" parse_label(cmd); int here = cmd.cursor(); ONE_OF || set(cmd, "SW", &type, VOLTAGE) || set(cmd, "CSW", &type, CURRENT) ; if (cmd.stuck(&here)) { untested(); cmd.warn(bWARNING, "need sw or csw"); } cmd.skip1b('('); cmd.stuck(&here); do{ ONE_OF || get(cmd, "VT", &vt) || get(cmd, "VH", &vh) //, mPOSITIVE) || get(cmd, "IT", &vt) || get(cmd, "IH", &vh) //, mPOSITIVE) || get(cmd, "RON", &ron) || get(cmd, "ROFF", &roff) || get(cmd, "VON", &von) || get(cmd, "VOFF", &voff) || get(cmd, "ION", &von) || get(cmd, "IOFF", &voff) ; }while (cmd.more() && !cmd.stuck(&here)); cmd.skip1b(')'); cmd.check(bWARNING, "what's this?"); } /*--------------------------------------------------------------------------*/ void MODEL_SWITCH::elabo1() { if (1 || !evaluated()) { const CARD_LIST* par_scope = scope(); assert(par_scope); MODEL_CARD::elabo1(); vt.e_val(_default_vt, par_scope); vh.e_val(_default_vh, par_scope); ron.e_val(_default_ron, par_scope); roff.e_val(_default_roff, par_scope); von.e_val(vt + vh, par_scope); voff.e_val(vt - vh, par_scope); }else{ untested(); } } /*--------------------------------------------------------------------------*/ void MODEL_SWITCH::print_front(OMSTREAM& o, LANGUAGE lang)const { assert(lang == lSPICE || lang == lVERILOG); switch (type) { case VOLTAGE: o << "sw"; break; case CURRENT: o << "csw"; break; } } /*--------------------------------------------------------------------------*/ void MODEL_SWITCH::print_params(OMSTREAM& o, LANGUAGE lang)const { switch (type) { case VOLTAGE: print_pair(o, lang, "vt", vt); print_pair(o, lang, "vh", vh); print_pair(o, lang, "von", von); print_pair(o, lang, "voff", voff); break; case CURRENT: print_pair(o, lang, "it", vt); print_pair(o, lang, "ih", vh); print_pair(o, lang, "ion", von); print_pair(o, lang, "ioff", voff); break; } print_pair(o, lang, "ron", ron); print_pair(o, lang, "roff", roff); } /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ SWITCH_BASE::SWITCH_BASE() :ELEMENT(), _input_label(), _input(0), _ic(_UNKNOWN), _time_future(0.) { attach_common(&Default_SWITCH); std::fill_n(_time, int(_keep_time_steps), 0.); std::fill_n(_in, int(_keep_time_steps), 0.); std::fill_n(_state, int(_keep_time_steps), _UNKNOWN); } /*--------------------------------------------------------------------------*/ SWITCH_BASE::SWITCH_BASE(const SWITCH_BASE& p) :ELEMENT(p), _input_label(p._input_label), _input(0), _ic(p._ic), _time_future(p._time_future) { notstd::copy_n(p._time, int(_keep_time_steps), _time); notstd::copy_n(p._in, int(_keep_time_steps), _in); notstd::copy_n(p._state, int(_keep_time_steps), _state); } /*--------------------------------------------------------------------------*/ void SWITCH_BASE::parse_spice(CS& cmd) { assert(has_common()); COMMON_SWITCH* c = prechecked_cast(common()->clone()); assert(c); assert(!c->has_model()); parse_Label(cmd); parse_nodes(cmd, max_nodes(), min_nodes(), 1/*model_name*/, 0/*start*/); if (typeid(*this) == typeid(DEV_CSWITCH)) { _input_label = cmd.ctos(TOKENTERM); _input_label[0] = toupper(_input_label[0]); }else{ assert(typeid(*this) == typeid(DEV_VSWITCH)); } c->parse_modelname(cmd); int here = cmd.cursor(); ONE_OF || set(cmd, "OFF", &_ic, _OFF) || set(cmd, "ON", &_ic, _ON) || set(cmd, "UNKNOWN",&_ic, _UNKNOWN) ; if (cmd.stuck(&here)) { cmd.check(bWARNING, "need off, on, or unknown"); }else{ cmd.check(bWARNING, "what's this?"); untested(); } attach_common(c); } /*--------------------------------------------------------------------------*/ void SWITCH_BASE::print(OMSTREAM& o, LANGUAGE lang)const { const COMMON_SWITCH* c = prechecked_cast(common()); assert(c); switch (lang) { case lACS:untested(); print_type(o, lang); print_label(o, lang); print_nodes(o, lang); if (_input) { // has been expanded o << " " << _input->short_label(); }else{ // not expanded o << " " << _input_label; /* could always print as not expanded, */ } /* but this way provides error check */ o << ' ' << c->modelname(); //print_args(o, lang); switch (_ic) { case _OFF: untested(); o << " off"; break; case _ON: untested(); o << " on"; break; case _UNKNOWN: break; } o << '\n'; break; case lSPICE: print_label(o, lang); print_nodes(o, lang); if (_input) { // has been expanded o << " " << _input->short_label(); }else if (_input_label != "") { // not expanded o << " " << _input_label; /* could always print as not expanded, */ }else{ // volt controlled /* but this way provides error check */ // don't even print the blanks } print_type(o, lang); //print_args(o, lang); switch (_ic) { case _OFF: untested(); o << " off"; break; case _ON: untested(); o << " on"; break; case _UNKNOWN: break; } o << '\n'; break; case lVERILOG:untested(); print_type(o, lang); //print_args(o, lang); switch (_ic) { case _OFF: untested(); o << " off"; break; case _ON: untested(); o << " on"; break; case _UNKNOWN: o << " "; break; } print_label(o, lang); print_nodes(o, lang); if (_input) {itested(); // has been expanded o << " " << _input->short_label(); }else{itested(); // not expanded o << " " << _input_label; /* could always print as not expanded, */ } /* but this way provides error check */ o << ";\n"; break; case lAUTO: unreachable(); break; } } /*--------------------------------------------------------------------------*/ void SWITCH_BASE::elabo1() { ELEMENT::elabo1(); const MODEL_SWITCH* m = dynamic_cast(attach_model()); if (!m) { untested(); assert(has_common()); error(bERROR, long_label() + ": model " + common()->modelname() + " is not a switch (SW or CSW)\n"); } } /*--------------------------------------------------------------------------*/ void SWITCH_BASE::precalc() { const COMMON_SWITCH* c = prechecked_cast(common()); assert(c); const MODEL_SWITCH* m = prechecked_cast(c->model()); assert(m); set_value(m->ron); _y0.f0 = LINEAR; _y0.f1 = (_ic == _ON) ? m->ron : m->roff; /* unknown is off */ _m0.c1 = 1./_y0.f1; _m0.c0 = 0.; _m1 = _m0; _state[1] = _state[0] = _ic; assert(_loss0 == 0.); assert(_loss1 == 0.); assert(!is_constant()); /* depends on input */ } /*--------------------------------------------------------------------------*/ void SWITCH_BASE::tr_begin() { const COMMON_SWITCH* c = prechecked_cast(common()); assert(c); const MODEL_SWITCH* m = prechecked_cast(c->model()); assert(m); assert(_loss0 == 0.); assert(_loss1 == 0.); assert(_y0.f0 == LINEAR); _y0.f1 = ((_ic == _ON) ? m->ron : m->roff); /* unknown is off */ _m0.c1 = 1./_y0.f1; assert(_m0.c0 == 0.); _m1 = _m0; _state[0] = _state[0] = _ic; _time[0] = _time[1] = 0.; set_converged(); } /*--------------------------------------------------------------------------*/ void SWITCH_BASE::dc_advance() { _state[1] = _state[0]; _time[0] = _time[1] = 0.; } /*--------------------------------------------------------------------------*/ void SWITCH_BASE::tr_advance() { const COMMON_SWITCH* c = prechecked_cast(common()); assert(c); const MODEL_SWITCH* m = prechecked_cast(c->model()); assert(m); if (_time[0] < SIM::time0) { // forward assert(_time[1] < _time[0] || _time[0] == 0.); _time[1] = _time[0]; _state[1] = _state[0]; _y0.x = _in[1] = _in[0]; if (_y0.x >= m->von) { _state[0] = _ON; }else if (_y0.x <= m->voff) { _state[0] = _OFF; }else{ _state[0] = _state[1]; } if (_state[1] != _state[0]) { _y0.f1 = (_state[0] == _ON) ? m->ron : m->roff; /* unknown is off */ _m0.c1 = 1./_y0.f1; q_eval(); } }else{ assert(_time[1] < SIM::time0); assert(_time[0] >= SIM::time0); assert(_time[1] < _time[0] || _time[0] == 0.); // else backward, don't do anything } assert(_y0.f1 == ((_state[0] == _ON) ? m->ron : m->roff)); assert(_y0.f0 == LINEAR); assert(_m0.c1 == 1./_y0.f1); assert(_m0.c0 == 0.); set_converged(); _time[0] = SIM::time0; } /*--------------------------------------------------------------------------*/ bool SWITCH_BASE::do_tr() { const COMMON_SWITCH* c = prechecked_cast(common()); assert(c); const MODEL_SWITCH* m = prechecked_cast(c->model()); assert(m); if (analysis_is_static()) { _y0.x = (_input) /* _y0.x is controlling value */ ? CARD::probe(_input,"I") /* current controlled */ : _n[IN1].v0() - _n[IN2].v0(); /* voltage controlled */ state_t new_state; if (_y0.x > m->von) { new_state = _ON; }else if (_y0.x < m->voff) { new_state = _OFF; }else{ new_state = _state[1]; } if (new_state != _state[0]) { _y0.f1 = (new_state == _ON) ? m->ron : m->roff; /* unknown is off */ _state[0] = new_state; _m0.c1 = 1./_y0.f1; q_load(); store_values(); set_not_converged(); }else{ set_converged(); } }else{ // Above isn't necessary because it was done in tr_advance, // and doesn't iterate. // I believe it is not necessary on restart, because it is stored. if (_state[0] != _state[1]) { q_load(); store_values(); }else{ // gets here only on "nobypass" } assert(converged()); } assert(_y0.f1 == ((_state[0] == _ON) ? m->ron : m->roff)); assert(_y0.f0 == LINEAR); //assert(_m0.c1 == 1./_y0.f1); assert(_m0.c0 == 0.); return converged(); } /*--------------------------------------------------------------------------*/ DPAIR SWITCH_BASE::tr_review() { const COMMON_SWITCH* c = prechecked_cast(common()); assert(c); const MODEL_SWITCH* m = prechecked_cast(c->model()); assert(m); _in[0] = (_input) ? CARD::probe(_input,"I") : _n[IN1].v0() - _n[IN2].v0(); double dt = _time[0] - _time[1]; double dv = _in[0] - _in[1]; double dvdt = dv / dt; #if 0 double target = (dvdt >= 0) ? m->von : m->voff; double new_dv = target - _in[1]; double new_dt = new_dv / dvdt; _time_future = (new_dt > 0) ? _time[1] + new_dt : NEVER; #elif 0 switch (_state[0]) { case _ON:{ double target = m->voff; double new_dv = target - _in[1]; double new_dt = new_dv / dvdt; _time_future = (new_dt > 0) ? _time[1] + new_dt : NEVER; assert(dvdt < 0 || new_dt < 0); } case _OFF:{ double target = m->von; double new_dv = target - _in[1]; double new_dt = new_dv / dvdt; if (dvdt >= 0 || new_dt < 0) { // input rising (ok) or negative (never) _time_future = (new_dt > 0) ? _time[1] + new_dt : NEVER; std::cout << dvdt << '#' << new_dt << '#' << _time_future << '\n'; }else{ // input falling (never) and positive _time_future = (new_dt > 0) ? _time[1] + new_dt : NEVER; std::cout << dvdt << ' ' << new_dt << ' ' << _time_future << '\n'; } } case _UNKNOWN:{ double target = (dvdt >= 0) ? m->von : m->voff; double new_dv = target - _in[1]; double new_dt = new_dv / dvdt; _time_future = (new_dt > 0) ? _time[1] + new_dt : NEVER; } } #else if (_state[0] != _ON && dvdt > 0) { double new_dv = m->von - _in[1]; _time_future = _time[1] + new_dv / dvdt; }else if (_state[0] != _OFF && dvdt < 0) { double new_dv = m->voff - _in[1]; _time_future = _time[1] + new_dv / dvdt; }else{ _time_future = NEVER; } #endif if (_time_future < _time[1] + 2*SIM::dtmin) { _time_future = _time[1] + 2*SIM::dtmin; } if (conchk(_time_future, _time[0], 2*SIM::dtmin, 0.)) { _time_future = _time[0] + 2*SIM::dtmin; } return DPAIR(NEVER, _time_future); } /*--------------------------------------------------------------------------*/ double SWITCH_BASE::tr_probe_num(CS& cmd)const { if (cmd.pmatch("DT")) { untested(); return _time[0] - _time[1]; }else if (cmd.pmatch("TIME")) { untested(); return _time[0]; }else if (cmd.pmatch("TIMEOld")) { untested(); return _time[1]; }else if (cmd.pmatch("TIMEFuture")) { return _time_future; }else{ return ELEMENT::tr_probe_num(cmd); } } /*--------------------------------------------------------------------------*/ void SWITCH_BASE::do_ac() { assert(_ev == _y0.f1); assert(_acg == _m0.c1); ac_load(); } /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ void DEV_CSWITCH::elabo1() { SWITCH_BASE::elabo1(); _input = dynamic_cast( find_in_my_scope(_input_label, bERROR)); if (!_input) { untested(); error(bERROR, long_label() + ": " + _input_label + " cannot be used as input\n"); } } /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/ MODEL_SWITCH p1; DISPATCHER::INSTALL d1(&model_dispatcher, "SW,CSW", &p1); DEV_VSWITCH p2; DEV_CSWITCH p3; DISPATCHER::INSTALL d2(&device_dispatcher, "S,vswitch", &p2), d3(&device_dispatcher, "W,cswitch,iswitch", &p3); } /*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/