diff --git a/src/spicelib/devices/csw/cswload.c b/src/spicelib/devices/csw/cswload.c index 44b23d46d..2e5504f15 100644 --- a/src/spicelib/devices/csw/cswload.c +++ b/src/spicelib/devices/csw/cswload.c @@ -1,7 +1,7 @@ /********** Copyright 1990 Regents of the University of California. All rights reserved. Author: 1985 Gordon Jacobs -Modified: 2000 AlansFixes +Modified: 2001 Jon Engelbert **********/ #include "ngspice.h" @@ -25,8 +25,10 @@ CSWload(inModel,ckt) CSWinstance *here; double g_now; double i_ctrl; - double previous_state; - double current_state; + double previous_state = -1; + double current_state = -1, old_current_state = -1; + double REALLY_OFF = 0, REALLY_ON = 1; // switch is on or off, not in hysteresis region. + double HYST_OFF = 2, HYST_ON = 3; // switch is on or off while control value is in hysteresis region. /* loop through all the switch models */ for( ; model != NULL; model = model->CSWnextModel ) { @@ -34,7 +36,13 @@ CSWload(inModel,ckt) /* loop through all the instances of the model */ for (here = model->CSWinstances; here != NULL ; here=here->CSWnextInstance) { - if (here->CSWowner != ARCHme) continue; + + if (here->CSWowner != ARCHme) continue; + + old_current_state = *(ckt->CKTstates[0] + here->CSWstate); + previous_state = *(ckt->CKTstates[1] + here->CSWstate); + i_ctrl = *(ckt->CKTrhsOld + + here->CSWcontBranch); /* decide the state of the switch */ @@ -42,71 +50,94 @@ CSWload(inModel,ckt) if(here->CSWzero_stateGiven) { /* switch specified "on" */ - *(ckt->CKTstate0 + here->CSWstate) = 1.0; - current_state = 1.0; + if ((model->CSWiHysteresis >= 0) && (i_ctrl > (model->CSWiThreshold + model->CSWiHysteresis))) + current_state = REALLY_ON; + else if ((model->CSWiHysteresis < 0) && (i_ctrl > (model->CSWiThreshold - model->CSWiHysteresis))) + current_state = REALLY_ON; + else + current_state = HYST_ON; } else { - *(ckt->CKTstate0 + here->CSWstate) = 0.0; - current_state = 0.0; + if ((model->CSWiHysteresis >= 0) && (i_ctrl < (model->CSWiThreshold - model->CSWiHysteresis))) + current_state = REALLY_OFF; + else if ((model->CSWiHysteresis < 0) && (i_ctrl < (model->CSWiThreshold + model->CSWiHysteresis))) + current_state = REALLY_OFF; + else + current_state = HYST_OFF; } - - *(ckt->CKTstate0 + (here->CSWstate+1)) = 0; - + } else if (ckt->CKTmode & (MODEINITSMSIG)) { - previous_state = *(ckt->CKTstate0 + here->CSWstate); current_state = previous_state; } else if (ckt->CKTmode & (MODEINITFLOAT)) { /* use state0 since INITTRAN or INITPRED already called */ - previous_state = *(ckt->CKTstate0 + here->CSWstate); - i_ctrl = *(ckt->CKTrhsOld + - here->CSWcontBranch); - if(i_ctrl > (model->CSWiThreshold+model->CSWiHysteresis)) { - *(ckt->CKTstate0 + here->CSWstate) = 1.0; - current_state = 1.0; - } - else if(i_ctrl < (model->CSWiThreshold - - model->CSWiHysteresis)) { - *(ckt->CKTstate0 + here->CSWstate) = 0.0; - current_state = 0.0; - } - else { - current_state = previous_state; - } - *(ckt->CKTstate0 + (here->CSWstate+1)) = i_ctrl; + if (model->CSWiHysteresis > 0) { + if (i_ctrl > (model->CSWiThreshold + model->CSWiHysteresis)) { + current_state = REALLY_ON; + } else if (i_ctrl < (model->CSWiThreshold - model->CSWiHysteresis)) { + current_state = REALLY_OFF; + } else { + current_state = previous_state; + } + } else { + if (i_ctrl > (model->CSWiThreshold - model->CSWiHysteresis)) { + current_state = REALLY_ON; + } else if (i_ctrl < (model->CSWiThreshold + model->CSWiHysteresis)) { + current_state = REALLY_OFF; + } else { // in hysteresis... change value if going from low to hysteresis, or from hi to hysteresis. + // if previous state was in hysteresis, then don't change the state.. + if ((previous_state == HYST_OFF) || (previous_state == HYST_ON)) { + current_state = previous_state; + } else if (previous_state == REALLY_ON) { + current_state = HYST_OFF; + } else if (previous_state == REALLY_OFF) { + current_state = HYST_ON; + } else + internalerror("bad value for previous region in swload"); + } + } - if(current_state != previous_state) { - ckt->CKTnoncon++; /* ensure one more iteration */ - ckt->CKTtroubleElt = (GENinstance *) here; + if(current_state != old_current_state) { + ckt->CKTnoncon++; /* ensure one more iteration */ + ckt->CKTtroubleElt = (GENinstance *) here; } } else if (ckt->CKTmode & (MODEINITTRAN|MODEINITPRED)) { - previous_state = *(ckt->CKTstate1 + here->CSWstate); - i_ctrl = *(ckt->CKTrhsOld + - here->CSWcontBranch); + if (model->CSWiHysteresis > 0) { + if (i_ctrl > (model->CSWiThreshold + model->CSWiHysteresis)) { + current_state = REALLY_ON; + } else if (i_ctrl < (model->CSWiThreshold - model->CSWiHysteresis)) { + current_state = REALLY_OFF; + } else { + current_state = previous_state; + } + } else { + if (i_ctrl > (model->CSWiThreshold - model->CSWiHysteresis)) { + current_state = REALLY_ON; + } else if (i_ctrl < (model->CSWiThreshold + model->CSWiHysteresis)) { + current_state = REALLY_OFF; + } else { // in hysteresis... change value if going from low to hysteresis, or from hi to hysteresis. + // if previous state was in hysteresis, then don't change the state.. + if ((previous_state == HYST_OFF) || (previous_state == HYST_ON)) { + current_state = previous_state; + } else if (previous_state == REALLY_ON) { + current_state = HYST_OFF; + } else if (previous_state == REALLY_OFF) { + current_state = HYST_ON; + } else + internalerror("bad value for previous region in cswload"); + } + } + } - if(i_ctrl > (model->CSWiThreshold+model->CSWiHysteresis)) { - current_state = 1; - } else if(i_ctrl < (model->CSWiThreshold - - model->CSWiHysteresis)) { - current_state = 0; - } else { - current_state = previous_state; - } - - if(current_state == 0) { - *(ckt->CKTstate0 + here->CSWstate) = 0.0; - } else { - *(ckt->CKTstate0 + here->CSWstate) = 1.0; - } - - *(ckt->CKTstate0 + (here->CSWstate+1)) = i_ctrl; - - } - - g_now = current_state?(model->CSWonConduct):(model->CSWoffConduct); + *(ckt->CKTstates[0] + here->CSWstate) = current_state; + *(ckt->CKTstates[1] + here->CSWstate) = previous_state; + if ((current_state == REALLY_ON) || (current_state == HYST_ON)) + g_now = model->CSWonConduct; + else + g_now = model->CSWoffConduct; here->CSWcond = g_now; *(here->CSWposPosptr) += g_now; diff --git a/src/spicelib/devices/csw/cswmpar.c b/src/spicelib/devices/csw/cswmpar.c index 5b4573c76..b7708f2be 100644 --- a/src/spicelib/devices/csw/cswmpar.c +++ b/src/spicelib/devices/csw/cswmpar.c @@ -1,6 +1,7 @@ /********** Copyright 1990 Regents of the University of California. All rights reserved. Author: 1985 Gordon Jacobs +Modified: 2001 Jon Englebert **********/ /* */ @@ -40,7 +41,8 @@ CSWmParam(param,value,inModel) break; case CSW_IHYS: /* take absolute value of hysteresis voltage */ - model->CSWiHysteresis = fabs(value->rValue); +// model->CSWiHysteresis = fabs(value->rValue); + model->CSWiHysteresis = value->rValue; model->CSWhystGiven = TRUE; break; default: diff --git a/src/spicelib/devices/sw/swload.c b/src/spicelib/devices/sw/swload.c index de335cef0..aaa38b484 100644 --- a/src/spicelib/devices/sw/swload.c +++ b/src/spicelib/devices/sw/swload.c @@ -1,7 +1,7 @@ /********** Copyright 1990 Regents of the University of California. All rights reserved. Author: 1985 Gordon Jacobs -Modified: 2000 AlansFixes +Modified: 2001 Jon Engelbert **********/ #include "ngspice.h" @@ -24,8 +24,14 @@ SWload(inModel,ckt) SWinstance *here; double g_now; double v_ctrl; - double previous_state; - double current_state; + double previous_state = -1; + double current_state = -1; + double old_current_state = -1; + double UNKNOWN = -1; + double REALLY_OFF = 0, REALLY_ON = 1; // switch is on or off, not in hysteresis region. + double HYST_OFF = 2, HYST_ON = 3; // switch is on or off while control value is in hysteresis region. +// double previous_region = -1; +// double current_region = -1; /* loop through all the switch models */ for( ; model != NULL; model = model->SWnextModel ) { @@ -34,78 +40,113 @@ SWload(inModel,ckt) for (here = model->SWinstances; here != NULL ; here=here->SWnextInstance) { if (here->SWowner != ARCHme) continue; + + old_current_state = *(ckt->CKTstates[0] + here->SWstate); + previous_state = *(ckt->CKTstates[1] + here->SWstate); - /* decide the state of the switch */ - + v_ctrl = *(ckt->CKTrhsOld + here->SWposCntrlNode) + - *(ckt->CKTrhsOld + here->SWnegCntrlNode); + + /* decide the state of the switch */ + if(ckt->CKTmode & (MODEINITFIX|MODEINITJCT)) { if(here->SWzero_stateGiven) { /* switch specified "on" */ - *(ckt->CKTstate0 + here->SWstate) = 1.0; - current_state = 1.0; + if ((model->SWvHysteresis >= 0) && (v_ctrl > (model->SWvThreshold + model->SWvHysteresis))) + current_state = REALLY_ON; + else if ((model->SWvHysteresis < 0) && (v_ctrl > (model->SWvThreshold - model->SWvHysteresis))) + current_state = REALLY_ON; + else + current_state = HYST_ON; } else { - *(ckt->CKTstate0 + here->SWstate) = 0.0; - current_state = 0.0; + if ((model->SWvHysteresis >= 0) && (v_ctrl < (model->SWvThreshold - model->SWvHysteresis))) + current_state = REALLY_OFF; + else if ((model->SWvHysteresis < 0) && (v_ctrl < (model->SWvThreshold + model->SWvHysteresis))) + current_state = REALLY_OFF; + else + current_state = HYST_OFF; } - *(ckt->CKTstate0 + (here->SWstate+1)) = 0; - } else if (ckt->CKTmode & (MODEINITSMSIG)) { - previous_state = *(ckt->CKTstate0 + here->SWstate); current_state = previous_state; } else if (ckt->CKTmode & (MODEINITFLOAT)) { /* use state0 since INITTRAN or INITPRED already called */ - previous_state = *(ckt->CKTstate0 + here->SWstate); - v_ctrl = *(ckt->CKTrhsOld + here->SWposCntrlNode) - - *(ckt->CKTrhsOld + here->SWnegCntrlNode); - if(v_ctrl > (model->SWvThreshold + model->SWvHysteresis)) { - *(ckt->CKTstate0 + here->SWstate) = 1.0; - current_state = 1.0; - } else if(v_ctrl < (model->SWvThreshold - - model->SWvHysteresis)) { - *(ckt->CKTstate0 + here->SWstate) = 0.0; - current_state = 0.0; - } else { - current_state = previous_state; - } - - *(ckt->CKTstate0 + (here->SWstate+1)) = v_ctrl; - - if(current_state != previous_state) { + if (model->SWvHysteresis > 0) { + if (v_ctrl > (model->SWvThreshold + model->SWvHysteresis)) { + current_state = REALLY_ON; + } else if (v_ctrl < (model->SWvThreshold - model->SWvHysteresis)) { + current_state = REALLY_OFF; + } else { + current_state = old_current_state; + } + } else { // negative hysteresis case. + if (v_ctrl > (model->SWvThreshold - model->SWvHysteresis)) + { + current_state = REALLY_ON; + } else if (v_ctrl < (model->SWvThreshold + model->SWvHysteresis)) + { + current_state = REALLY_OFF; + } else { // in hysteresis... change value if going from low to hysteresis, or from hi to hysteresis. + // if previous state was in hysteresis, then don't change the state.. + if ((previous_state == HYST_OFF) || (previous_state == HYST_ON)) { + current_state = previous_state; + } else if (previous_state == REALLY_ON) { + current_state = HYST_OFF; + } else if (previous_state == REALLY_OFF) { + current_state = HYST_ON; + } else + internalerror("bad value for previous state in swload"); + } + } + + if(current_state != old_current_state) { ckt->CKTnoncon++; /* ensure one more iteration */ - ckt->CKTtroubleElt = (GENinstance *) here; + ckt->CKTtroubleElt = (GENinstance *) here; } } else if(ckt->CKTmode & (MODEINITTRAN|MODEINITPRED) ) { - previous_state = *(ckt->CKTstate1 + here->SWstate); - v_ctrl = *(ckt->CKTrhsOld + here->SWposCntrlNode) - - *(ckt->CKTrhsOld + here->SWnegCntrlNode); + if (model->SWvHysteresis > 0) { + if (v_ctrl > (model->SWvThreshold + model->SWvHysteresis)) + current_state = REALLY_ON; + else if (v_ctrl < (model->SWvThreshold - model->SWvHysteresis)) + current_state = REALLY_OFF; + else + current_state = previous_state; + } else { // negative hysteresis case. + if (v_ctrl > (model->SWvThreshold - model->SWvHysteresis)) + current_state = REALLY_ON; + else if (v_ctrl < (model->SWvThreshold + model->SWvHysteresis)) + current_state = REALLY_OFF; + else { + current_state = 0.0; + if ((previous_state == HYST_ON) || (previous_state == HYST_OFF)) { + current_state = previous_state; + } else if (previous_state == REALLY_ON) { + current_state = REALLY_OFF; + } else if (previous_state == REALLY_OFF) { + current_state = REALLY_ON; + } + } + } + } +// code added to force the state to be updated. +// there is a possible problem. What if, during the transient analysis, the time is stepped +// forward enough to change the switch's state, but that time point is rejected as being too +// distant and then the time is pushed back to a time before the switch changed states. +// After analyzing the transient code, it seems that this is not a problem because state updating +// occurs before the convergence loop in transient processing. + *(ckt->CKTstates[0] + here->SWstate) = current_state; - if(v_ctrl > (model->SWvThreshold + model->SWvHysteresis)) { - current_state = 1.0; - } else if(v_ctrl < (model->SWvThreshold - - model->SWvHysteresis)) { - current_state = 0.0; - } else { - current_state = previous_state; - } - - if(current_state == 0) { - *(ckt->CKTstate0 + here->SWstate) = 0.0; - } else { - *(ckt->CKTstate0 + here->SWstate) = 1.0; - } - - *(ckt->CKTstate0 + (here->SWstate+1)) = v_ctrl; - - } - - g_now = current_state?(model->SWonConduct):(model->SWoffConduct); + if ((current_state == REALLY_ON) || (current_state == HYST_ON)) + g_now = model->SWonConduct; + else + g_now = model->SWoffConduct; here->SWcond = g_now; *(here->SWposPosptr) += g_now; diff --git a/src/spicelib/devices/sw/swmparam.c b/src/spicelib/devices/sw/swmparam.c index b337adfc2..a2b25c5f1 100644 --- a/src/spicelib/devices/sw/swmparam.c +++ b/src/spicelib/devices/sw/swmparam.c @@ -1,6 +1,7 @@ /********** Copyright 1990 Regents of the University of California. All rights reserved. Author: 1985 Gordon Jacobs +Modified: 2001 Jon Engelbert **********/ /* */ @@ -41,8 +42,9 @@ SWmParam(param,value,inModel) break; case SW_MOD_VHYS: /* take absolute value of hysteresis voltage */ - model->SWvHysteresis = (value->rValue < 0) ? -(value->rValue) : - value->rValue; +// model->SWvHysteresis = (value->rValue < 0) ? -(value->rValue) : +// value->rValue; + model->SWvHysteresis = value->rValue; model->SWhystGiven = TRUE; break; default: