Complete version of intertial delay for d_tristate. The State and

Strength members are treated independently and combined in the output.
This commit is contained in:
Giles Atkinson 2023-08-08 13:14:25 +01:00 committed by Holger Vogt
parent 502f15522e
commit 738881a014
2 changed files with 296 additions and 48 deletions

View File

@ -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;

View File

@ -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