diff --git a/src/xspice/icm/digital/d_tristate/cfunc.mod b/src/xspice/icm/digital/d_tristate/cfunc.mod index ac9cb086b..8f5d67bd5 100644 --- a/src/xspice/icm/digital/d_tristate/cfunc.mod +++ b/src/xspice/icm/digital/d_tristate/cfunc.mod @@ -107,24 +107,22 @@ NON-STANDARD FEATURES * Last Modified 11/26/91 * ************************************************/ +#define DEBUG 0 +#if DEBUG +const char * const Image[] = {"Idle", "Normal", "Same", "Revert", "Both"}; +#endif + void cm_d_tristate(ARGS) { - Digital_t *out; - Digital_State_t val, enable; + Digital_State_t val; Digital_Strength_t str; + Digital_t *out; struct idata *idp; if (INIT) { /* initial pass */ - /* define input loading... */ - LOAD(in) = PARAM(input_load); - LOAD(enable) = PARAM(enable_load); - OUTPUT_DELAY(out) = PARAM(delay); - - /* allocate storage for the previous output. */ + /* allocate storage for the outputs */ cm_event_alloc(0, sizeof (Digital_t)); - out = (Digital_t *)cm_event_get_ptr(0, 0); - out->state = (Digital_State_t)(UNKNOWN + 1); // Force initial output. /* Inertial delay? */ @@ -132,72 +130,324 @@ void cm_d_tristate(ARGS) cm_is_inertial(PARAM_NULL(inertial_delay) ? Not_set : PARAM(inertial_delay)); if (STATIC_VAR(is_inertial)) { - /* Allocate storage for event time. */ + /* Allocate storage for event times. A little rude, + * as strength values will be stored in idp[1].prev. + */ cm_event_alloc(1, 2 * sizeof (struct idata)); idp = (struct idata *)cm_event_get_ptr(1, 0); idp[1].when = idp[0].when = -1.0; } - /* Prepare initial output. */ - - out = (Digital_t *)cm_event_get_ptr(0, 0); + out = (Digital_t *) cm_event_get_ptr(0,0); out->state = (Digital_State_t)(UNKNOWN + 1); // Force initial output. + + /* define input loading... */ + LOAD(in) = PARAM(input_load); + LOAD(enable) = PARAM(enable_load); + OUTPUT_DELAY(out) = PARAM(delay); // Never changes, unless inertial. } else { out = (Digital_t *)cm_event_get_ptr(0, 0); } - /* Retrieve input values and static variables */ + /* Retrieve input values. */ val = INPUT_STATE(in); - - enable = INPUT_STATE(enable); - if (ZERO == enable) { + switch (INPUT_STATE(enable)) { + case ZERO: str = HI_IMPEDANCE; - } else if (UNKNOWN == enable) { - str = UNDETERMINED; - } else { + break; + case ONE: str = STRONG; + break; + default: + str = UNDETERMINED; + break; } - if (val == out->state && str == out->strength) { + /*** Check for change and output appropriate values ***/ + + if (val == out->state && str == out->strength) { /* output not changing */ OUTPUT_CHANGED(out) = FALSE; } else { if (STATIC_VAR(is_inertial) && ANALYSIS == TRANSIENT) { - int d_cancel, s_cancel; + /* Each channel (State, Strength) of the output has three values, + * that set by the input, the current node value and its value + * following a pending change. The channel is in one of + * five states: + * Idle - no new value and no pending output; + * Normal - there is a new value with no pending output; + * Same - no new value, there is pending output; + * Revert - new value same as current, conflicting pending output; + * Both - new value differs from both current and pending. + */ + + enum {Idle, Normal, Same, Revert, Both} + d_ctl, s_ctl, ctl1, ctl2; + struct idata *idp; + double first_time, second_time; /* Scheduled changes */ + double delay_1; /* Delay to first output. */ + double cancel_delay; /* Delay to canclling. */ + int d_first; + Digital_t reversion, restoration; idp = (struct idata *)cm_event_get_ptr(1, 0); - d_cancel = (idp[0].when > TIME && val == idp[0].prev); - s_cancel = (idp[1].when > TIME && - str == (Digital_Strength_t)idp[1].prev); - if ((d_cancel && s_cancel) || - (d_cancel && str == out->strength && TIME >= idp[1].when) || - (s_cancel && val == out->state && TIME >= idp[0].when)) { - double when; - /* Changing back: override pending change. */ + /* Combine two independent streams (state and strength) into + * a sequence of output events. Earlier changes cancel later ones + * that may need to be restored. + */ - when = d_cancel ? idp[0].when : idp[1].when; - if (s_cancel && when > idp[1].when) - when = idp[1].when; + /* Identify earlier change. */ - OUTPUT_DELAY(out) = (when - TIME) / 2.0; // Override - idp[1].when = idp[0].when = -1.0; + if (idp[0].when <= idp[1].when) { + first_time = idp[0].when; + second_time = idp[1].when; + d_first = 1; } else { - /* Normal transition, or third value during delay, - * or needs cancel followed by restore of - * the other component (fudge). - */ + first_time = idp[1].when; + second_time = idp[0].when; + d_first = 0; + } - OUTPUT_DELAY(out) = PARAM(delay); - if (val != out->state) { + /* What happens to state? */ + + OUTPUT_DELAY(out) = PARAM(delay); + if (idp[0].when <= TIME) { + if (val == out->state) { + d_ctl = Idle; /* Output is stable and no change. */ + idp[0].prev = val; + } else { + d_ctl = Normal; /* Output was stable, changing now. */ idp[0].prev = out->state; idp[0].when = TIME + OUTPUT_DELAY(out); } - if (str != out->strength) { - idp[1].prev = (Digital_State_t)out->strength; + } else { + if (val == out->state) { + d_ctl = Same; /* Output pending, no change. */ + } else if (val == idp[0].prev) { + d_ctl = Revert; /* Returning to previous state. */ + idp[0].when = -1.0; + } else { + d_ctl = Both; /* Output pending, now changing. */ + idp[0].when = TIME + OUTPUT_DELAY(out); + } + } + + /* Strength? */ + + if (idp[1].when <= TIME) { + if (str == out->strength) { + s_ctl = Idle; + idp[1].prev = str; + } else { + s_ctl = Normal; + idp[1].prev = out->strength; idp[1].when = TIME + OUTPUT_DELAY(out); } + } else { + if (str == out->strength) { + s_ctl = Same; + } else if (str == (Digital_Strength_t)idp[1].prev) { + s_ctl = Revert; + idp[1].when = -1.0; + } else { + s_ctl = Both; + idp[1].when = TIME + OUTPUT_DELAY(out); + } + } + + if (d_first) { + ctl1 = d_ctl; + ctl2 = s_ctl; + } else { + ctl1 = s_ctl; + ctl2 = d_ctl; + } +#if DEBUG + cm_message_printf("%g: %s first, " + "state ctl %s %d->%d->%d @ %g, " + "strength ctl %s %d->%d->%d @ %g", + TIME, d_first ? "state" : "strength", + Image[d_ctl], idp[0].prev, out->state, val, + idp[0].when, + Image[s_ctl], idp[1].prev, out->strength, str, + idp[1].when); +#endif + switch (ctl1) { + case Idle: + switch (ctl2) { + default: + break; + case Revert: + /* Normal output is used to revert. */ + + delay_1 = (second_time - TIME) / 2.0; +direct_revert: + if (d_first) { + str = (Digital_Strength_t)idp[1].prev; + } else { + val = idp[0].prev; + } + OUTPUT_DELAY(out) = delay_1; + break; + + case Both: + /* Push out reversion before normal output. */ + + cancel_delay = (second_time - TIME) / 2.0; +push_revert: + if (d_first) { + reversion.state = out->state; + reversion.strength = (Digital_Strength_t)idp[1].prev; + } else { + reversion.state = idp[0].prev; + reversion.strength = out->strength; + } + cm_schedule_output(2, 0, cancel_delay, &reversion); + break; + } + break; + case Normal: + switch (ctl2) { + default: + break; + case Revert: + /* Push out reversion before normal output. */ + + if (d_first) { + reversion.state = out->state; + reversion.strength = (Digital_Strength_t)idp[1].prev; + str = reversion.strength; + } else { + reversion.state = idp[0].prev; + val = reversion.state; + reversion.strength = out->strength; + } + cancel_delay = (second_time - TIME) / 2.0; + cm_schedule_output(2, 0, cancel_delay, &reversion); + break; + case Both: + /* Push out reversion before normal output. */ + + cancel_delay = (second_time - TIME) / 2.0; + goto push_revert; + break; + } + break; + case Same: + switch (ctl2) { + default: + break; + case Revert: + /* Normal output is used to revert. */ + + delay_1 = (first_time + second_time) / 2.0 - TIME; + goto direct_revert; + break; + case Both: + /* Push out reversion before normal output. */ + + cancel_delay = (first_time + second_time) / 2.0 - TIME; + goto push_revert; + break; + } + break; + case Revert: + switch (ctl2) { + default: + /* Ordinary reversion. */ + + delay_1 = (first_time - TIME) / 2.0; + d_first = !d_first; + goto direct_revert; + break; + + case Normal: + /* Push out state reversion before normal output. */ + + cancel_delay = (first_time - TIME) / 2.0; + d_first = !d_first; + goto push_revert; + break; + + case Same: + /* Set normal output time to restore scheduled output + * and push out reversion. + */ + + reversion.state = idp[0].prev; + reversion.strength = (Digital_Strength_t)idp[1].prev; + cancel_delay = (first_time - TIME) / 2.0; + cm_schedule_output(2, 0, cancel_delay, &reversion); + OUTPUT_DELAY(out) = second_time - TIME; + break; + + case Revert: + /* Revert both together. */ + + val = idp[0].prev; + str = (Digital_Strength_t)idp[1].prev; + OUTPUT_DELAY(out) = (first_time - TIME) / 2.0; + break; + + case Both: + /* Double revert with normal output. */ + + reversion.state = idp[0].prev; + reversion.strength = (Digital_Strength_t)idp[1].prev; + cancel_delay = (first_time - TIME) / 2.0; + cm_schedule_output(2, 0, cancel_delay, &reversion); + + if (d_first) { + val = reversion.state; + } else { + str = reversion.strength; + } + break; + } + break; + + case Both: + switch (ctl2) { + default: + /* Push out state reversion before normal output. */ + + cancel_delay = (first_time - TIME) / 2.0; + d_first = !d_first; + goto push_revert; + break; + + case Same: + /* Push out reversion, then restore scheduled change, + * then normal output. + */ + + reversion.state = idp[0].prev; + reversion.strength = (Digital_Strength_t)idp[1].prev; + cancel_delay = (first_time - TIME) / 2.0; + cm_schedule_output(2, 0, cancel_delay, &reversion); + + if (d_first) { + restoration.state = reversion.state; + restoration.strength = out->strength; + } else { + restoration.state = out->state; + restoration.strength = reversion.strength; + } + cm_schedule_output(2, 0, second_time - TIME, &restoration); + break; + + case Revert: + case Both: + /* Push out double reversion, then normal output. */ + + reversion.state = idp[0].prev; + reversion.strength = (Digital_Strength_t)idp[1].prev; + cancel_delay = (first_time - TIME) / 2.0; + cm_schedule_output(2, 0, cancel_delay, &reversion); + break; + } } } out->state = val; diff --git a/src/xspice/icm/digital/d_tristate/ifspec.ifs b/src/xspice/icm/digital/d_tristate/ifspec.ifs index 20fe5eef9..30f34631d 100644 --- a/src/xspice/icm/digital/d_tristate/ifspec.ifs +++ b/src/xspice/icm/digital/d_tristate/ifspec.ifs @@ -45,8 +45,8 @@ Description: "delay" Data_Type: real Default_Value: 1.0e-9 Limits: [1e-12 -] -Vector: no -Vector_Bounds: - +Vector: no +Vector_bounds: - Null_Allowed: yes @@ -61,10 +61,8 @@ Vector: no no Vector_Bounds: - - Null_Allowed: yes yes - PARAMETER_TABLE: - Parameter_Name: inertial_delay family Description: "swallow short pulses" "Logic family for bridging" Data_Type: boolean string