Add new capabilities to the adc/dac_bridge XSPICE code models.

If either bridge has a single analog connection and two or more digital
connections it will act as a conventional multi-bit ADC or DAC.
When the low threshold is higher than the high threshold, adc_bridge
acts as a Schmitt trigger.
This commit is contained in:
Giles Atkinson 2024-11-04 18:24:04 +00:00 committed by Holger Vogt
parent 37b6fda497
commit 0f0f80f34f
3 changed files with 343 additions and 93 deletions

View File

@ -96,10 +96,21 @@ NON-STANDARD FEATURES
static Digital_State_t get_out_value(double in, double low, double high) static Digital_State_t get_out_value(double in, double low, double high)
{ {
if (in >= high) if (low <= high) {
return ONE; /* Normal operation. */
else if (in <= low)
return ZERO; if (in >= high)
return ONE;
else if (in <= low)
return ZERO;
} else {
/* (low > high)! Schmitt triger. */
if (in >= low)
return ONE;
else if (in <= high)
return ZERO;
}
return UNKNOWN; return UNKNOWN;
} }
@ -110,22 +121,34 @@ void cm_adc_bridge(ARGS)
in_high; /* analog output value corresponding to '1' in_high; /* analog output value corresponding to '1'
digital input */ digital input */
int i, /* generic loop counter index */ int i, /* generic loop counter index */
size; /* number of input & output ports */ size_in, /* number of input ports */
size_out; /* number of output ports */
Digital_State_t *out, /* base address of array holding all output Digital_State_t *out, /* base address of array holding all output
values plus their previous values */ values plus their previous values */
test; /* temp holding variable for digital states */ test; /* temp holding variable for digital states */
/* determine "width" of the node bridge... */
size = PORT_SIZE(in);
in_high = PARAM(in_high); in_high = PARAM(in_high);
in_low = PARAM(in_low); in_low = PARAM(in_low);
/* determine "width" of the node bridge... */
size_in = PORT_SIZE(in);
size_out = PORT_SIZE(out);
if (INIT) { /*** Test for INIT == TRUE. If so, allocate storage, etc. ***/ if (INIT) { /*** Test for INIT == TRUE. If so, allocate storage, etc. ***/
if (size_in != size_out) {
if (size_in != 1) {
cm_message_printf("Error: %d input ports with %d outputs",
size_in, size_out);
} else if (in_low >= in_high) {
cm_message_printf("Error: bad threshold values (low > high)");
}
}
/* Allocate storage for outputs */ /* Allocate storage for outputs */
cm_event_alloc(0, size * (int) sizeof(Digital_State_t)); cm_event_alloc(0, size_out * (int) sizeof(Digital_State_t));
/* Get discrete addresses */ /* Get discrete addresses */
@ -133,7 +156,7 @@ void cm_adc_bridge(ARGS)
/* Ensure output on first call. */ /* Ensure output on first call. */
for (i = 0; i < size; i++) for (i = 0; i < size_out; i++)
out[i] = UNKNOWN + 1; out[i] = UNKNOWN + 1;
return; return;
} }
@ -143,13 +166,75 @@ void cm_adc_bridge(ARGS)
out = (Digital_State_t *) cm_event_get_ptr(0, 0); out = (Digital_State_t *) cm_event_get_ptr(0, 0);
if (size_in != size_out) {
if (size_in != 1) {
if (size_in < size_out)
size_out = size_in;
else
size_in = size_out;
} else {
double in;
/* Single-input, multi-bit output option. */
in = (INPUT(in[0]) - in_low) / (in_high - in_low);
switch (CALL_TYPE) {
case ANALOG:
for (i = 0; i < size_out; i++) {
test = (in >= 0.5);
if (test != out[i]) {
/* call for event breakpoint... */
cm_event_queue(TIME);
break;
}
if (test)
in -= 0.5;
in *= 2.0;
}
break;
case EVENT: /** discrete call...lots to do **/
for (i = 0; i < size_out; i++) {
test = (in >= 0.5);
if (test != out[i]) {
switch (test) {
case ZERO:
OUTPUT_DELAY(out[i]) = PARAM(fall_delay);
break;
case ONE:
OUTPUT_DELAY(out[i]) = PARAM(rise_delay);
break;
default:
break;
}
out[i] = test;
OUTPUT_STATE(out[i]) = test;
OUTPUT_STRENGTH(out[i]) = STRONG;
} else {
OUTPUT_CHANGED(out[i]) = FALSE;
}
if (test)
in -= 0.5;
in *= 2.0;
}
break;
default:
break;
}
return;
}
}
/* Normal, multiple single-bit conversion output option. */
switch (CALL_TYPE) { switch (CALL_TYPE) {
case ANALOG: /** analog call...check for breakpoint calls. **/ case ANALOG: /** analog call...check for breakpoint calls. **/
/* loop through all inputs... */ /* loop through all inputs... */
for (i = 0; i < size; i++) { for (i = 0; i < size_out; i++) {
test = get_out_value(INPUT(in[i]), in_low, in_high); test = get_out_value(INPUT(in[i]), in_low, in_high);
if (test != out[i]) { if (test != out[i]) {
/* call for event breakpoint... */ /* call for event breakpoint... */
cm_event_queue(TIME); cm_event_queue(TIME);
@ -161,9 +246,9 @@ void cm_adc_bridge(ARGS)
case EVENT: /** discrete call...lots to do **/ case EVENT: /** discrete call...lots to do **/
/* loop through all inputs... */ /* loop through all inputs... */
for (i = 0; i < size; i++) { for (i = 0; i < size_out; i++) {
test = get_out_value(INPUT(in[i]), in_low, in_high); test = get_out_value(INPUT(in[i]), in_low, in_high);
if (test != out[i]) { if (test != out[i]) {
/* Post changed value. */ /* Post changed value. */
OUTPUT_STATE(out[i]) = test; OUTPUT_STATE(out[i]) = test;
@ -175,6 +260,12 @@ void cm_adc_bridge(ARGS)
OUTPUT_DELAY(out[i]) = PARAM(rise_delay); OUTPUT_DELAY(out[i]) = PARAM(rise_delay);
break; break;
default: default:
if (in_low > in_high) {
/* Input is in hysteresis band. */
OUTPUT_CHANGED(out[i]) = FALSE;
continue;
}
if (out[i] == ZERO) if (out[i] == ZERO)
OUTPUT_DELAY(out[i]) = PARAM(rise_delay); OUTPUT_DELAY(out[i]) = PARAM(rise_delay);
else else

View File

@ -24,6 +24,11 @@ Spice_Model_Name: adc_bridge
C_Function_Name: cm_adc_bridge C_Function_Name: cm_adc_bridge
Description: "analog-to-digital converter node bridge" Description: "analog-to-digital converter node bridge"
/* Input and output are vector ports. If port numbers are equal the
* device acts as a set of individual comparators (use %vd or %id for
* differential inputs. With a single input and multiple outputs,
* it is a single ADC with multi-bit output.
*/
PORT_TABLE: PORT_TABLE:
@ -32,11 +37,12 @@ Description: "input" "output"
Direction: in out Direction: in out
Default_Type: v d Default_Type: v d
Allowed_Types: [v,vd,i,id,vnam] [d] Allowed_Types: [v,vd,i,id,vnam] [d]
Vector: yes yes Vector: yes yes
Vector_Bounds: - - Vector_Bounds: - -
Null_Allowed: no no Null_Allowed: no no
/* If in_low > in_high the adc_bridge has hysteresis - a Schmitt trigger. */
PARAMETER_TABLE: PARAMETER_TABLE:
@ -72,3 +78,15 @@ Limits: [1e-12 -] [1e-12 -]
Vector: no no Vector: no no
Vector_Bounds: - - Vector_Bounds: - -
Null_Allowed: yes yes Null_Allowed: yes yes
PARAMETER_TABLE:
Parameter_Name: family
Description: "Logic family for bridging"
Data_Type: string
Default_Value: -
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes

View File

@ -14,19 +14,16 @@ AUTHORS
3 Jun 1991 Jeffrey P. Murray 3 Jun 1991 Jeffrey P. Murray
MODIFICATIONS MODIFICATIONS
16 Aug 1991 Jeffrey P. Murray 16 Aug 1991 Jeffrey P. Murray
2 Oct 1991 Jeffrey P. Murray 2 Oct 1991 Jeffrey P. Murray
SUMMARY SUMMARY
This file contains the model-specific routines used to This file contains the model-specific routines used to
functionally describe the dac_bridge code model. functionally describe the dac_bridge code model.
INTERFACES INTERFACES
FILE ROUTINE CALLED FILE ROUTINE CALLED
@ -38,11 +35,9 @@ INTERFACES
CMevt.c void *cm_event_alloc() CMevt.c void *cm_event_alloc()
void *cm_event_get_ptr() void *cm_event_get_ptr()
REFERENCED FILES REFERENCED FILES
Inputs from and outputs to ARGS structure. Inputs from and outputs to ARGS structure.
NON-STANDARD FEATURES NON-STANDARD FEATURES
@ -115,8 +110,7 @@ NON-STANDARD FEATURES
NONE NONE
==============================================================================*/ =============================================================================*/
/* Instances of this structure track digital input changes. */ /* Instances of this structure track digital input changes. */
@ -125,6 +119,29 @@ struct d_data {
double i_changed; // Time of input change. double i_changed; // Time of input change.
}; };
/* Relative output value for multi-bit input. */
static double get_out_val(struct d_data *dp, int size)
{
double v;
int i;
for (i = size - 1, v = 0.0; i >= 0; --i) {
v /= 2.0;
switch (dp[i].i) {
case ONE:
v += 0.5;
break;
case UNKNOWN:
v += 0.25;
break;
default:
break;
}
}
return v;
}
/*=== CM_DAC_BRIDGE ROUTINE ===*/ /*=== CM_DAC_BRIDGE ROUTINE ===*/
/************************************************ /************************************************
@ -135,9 +152,7 @@ struct d_data {
* Created 6/3/91 J.P.Murray * * Created 6/3/91 J.P.Murray *
************************************************/ ************************************************/
void cm_dac_bridge(ARGS) void cm_dac_bridge(ARGS)
{ {
double out_low, /* analog output value corresponding to '0' double out_low, /* analog output value corresponding to '0'
digital input */ digital input */
@ -157,18 +172,16 @@ void cm_dac_bridge(ARGS)
time_inc; /* time increment since last analog call */ time_inc; /* time increment since last analog call */
int i, /* generic loop counter index */ int i, /* generic loop counter index */
size; /* number of input & output ports */ multi, /* Multi-bit in, single real out. */
size_in, /* number of input ports */
size_out; /* number of output ports */
struct d_data *in, /* base address of array holding all input struct d_data *in, /* base address of array holding all input
values */ values */
*in_old; /* array holding previous input values */ *in_old; /* array holding previous input values */
/* determine "width" of the node bridge... */ /* Read in model parameters. **/
size = PORT_SIZE(in);
/** Read in remaining model parameters **/
out_low = PARAM(out_low); out_low = PARAM(out_low);
out_high = PARAM(out_high); out_high = PARAM(out_high);
@ -180,20 +193,38 @@ void cm_dac_bridge(ARGS)
/* if so, take out_undef as mean of out_high and out_low. */ /* if so, take out_undef as mean of out_high and out_low. */
if (!PARAM_NULL(out_low) && !PARAM_NULL(out_high) && if (!PARAM_NULL(out_low) && !PARAM_NULL(out_high) &&
PARAM_NULL(out_undef) ) { PARAM_NULL(out_undef)) {
out_undef = out_low + (out_high - out_low) / 2.0; out_undef = out_low + (out_high - out_low) / 2.0;
} else { } else {
out_undef = PARAM(out_undef); out_undef = PARAM(out_undef);
} }
/* determine "width" of the node bridge... */
size_in = PORT_SIZE(in);
size_out = PORT_SIZE(out);
multi = (size_in != size_out && size_out == 1);
if (!multi) {
if (size_in < size_out)
size_out = size_in;
else
size_in = size_out;
}
if (INIT) { /*** Test for INIT == TRUE. If so, allocate storage, etc. ***/ if (INIT) { /*** Test for INIT == TRUE. If so, allocate storage, etc. ***/
if (size_in != size_out && size_out != 1) {
cm_message_printf("Error: %d input ports with %d outputs",
size_in, size_out);
}
/* Allocate storage for inputs */ /* Allocate storage for inputs */
cm_event_alloc(0, size * (int) sizeof(struct d_data)); cm_event_alloc(0, size_in * (int)sizeof(struct d_data));
/* Allocate storage for outputs */ /* Allocate storage for outputs */
cm_analog_alloc(0, size * (int) sizeof(double)); cm_analog_alloc(0, size_out * (int)sizeof(double));
/* Retrieve allocated addresses. */ /* Retrieve allocated addresses. */
@ -201,88 +232,202 @@ void cm_dac_bridge(ARGS)
out = (double *) cm_analog_get_ptr(0, 0); out = (double *) cm_analog_get_ptr(0, 0);
/* read current input values */ /* read current input values */
for (i=0; i<size; i++) { for (i = 0; i < size_in; i++) {
in[i].i = INPUT_STATE(in[i]); in[i].i = INPUT_STATE(in[i]);
} }
/* Output initial analog levels based on input values */ /* Output initial analog levels based on input values */
for (i=0; i<size; i++) { /* assign addresses */ if (multi) {
switch (in[i].i) { /* Multi-bit input, single_output. */
OUTPUT(out[0]) = *out =
get_out_val(in, size_in) * (out_high - out_low) + out_low;
} else {
for (i = 0; i < size_in; i++) { /* assign addresses */
switch (in[i].i) {
case ZERO: out[i] = out_low; case ZERO: out[i] = out_low;
break; break;
case UNKNOWN: out[i] = out_undef; case UNKNOWN: out[i] = out_undef;
break; break;
case ONE: out[i] = out_high; case ONE: out[i] = out_high;
break; break;
}
OUTPUT(out[i]) = out[i];
} }
OUTPUT(out[i]) = out[i]; }
for (i = 0; i < size_in; i++)
LOAD(in[i]) = PARAM(input_load); LOAD(in[i]) = PARAM(input_load);
}
return; return;
} else { /*** This is not an initialization pass...read in parameters,
retrieve storage addresses and calculate new outputs,
if required. ***/
/** Retrieve previous values... **/
/* assign discrete addresses */
in = (struct d_data *) cm_event_get_ptr(0, 0);
in_old= (struct d_data *) cm_event_get_ptr(0, 1);
/* assign analog addresses */
out = (double *) cm_analog_get_ptr(0, 0);
out_old = (double *) cm_analog_get_ptr(0, 1);
/* read current input values */
for (i=0; i<size; i++) {
in[i].i = INPUT_STATE(in[i]);
}
} }
/* This is not an initialization pass...read in parameters,
retrieve storage addresses and calculate new outputs, if required.
*/
/** Retrieve previous values... **/
in = (struct d_data *) cm_event_get_ptr(0, 0);
in_old= (struct d_data *) cm_event_get_ptr(0, 1);
/* assign analog addresses */
out = (double *) cm_analog_get_ptr(0, 0);
out_old = (double *) cm_analog_get_ptr(0, 1);
/* read current input values */
for (i = 0; i < size_in; i++) {
in[i].i = INPUT_STATE(in[i]);
}
switch (CALL_TYPE) { switch (CALL_TYPE) {
double when, iota, vout, interval[2];
int step, step_count;
case EVENT: /** discrete call... **/ case EVENT: /** discrete call... **/
/* Test to see if any change has occurred in an input */ /* Test to see if any change has occurred in an input */
/* since the last digital call... */ /* since the last digital call... */
for (i=0; i<size; i++) { for (i = 0; i < size_in; i++) {
if (in[i].i != in_old[i].i) { /* if there has been a change... */ if (in[i].i != in_old[i].i) { /* if there has been a change... */
in[i].i_changed = TIME;
/* post current time as a breakpoint */ /* post current time as a breakpoint */
cm_analog_set_perm_bkpt(TIME); cm_analog_set_perm_bkpt(TIME);
if (multi) {
in[0].i_changed = TIME;
break;
} else {
in[i].i_changed = TIME;
}
} }
} }
break; break;
case ANALOG: /** analog call... **/ case ANALOG: /** analog call... **/
level_inc = out_high - out_low; level_inc = out_high - out_low;
rise_slope = level_inc / t_rise; rise_slope = level_inc / t_rise;
fall_slope = level_inc / t_fall; fall_slope = level_inc / t_fall;
time_inc = TIME - T(1); time_inc = T(0) - T(1);
for (i=0; i<size; i++) { if (multi) {
double v, target;
int changed;
/* Multi-bit input, single_output. */
v = get_out_val(in, size_in);
if (TIME == 0.0) {
OUTPUT(out[0]) = *out = v * level_inc + out_low;;
return;
}
vout = (out_old[0] - out_low) / level_inc; // Normalise.
for (i = 0, changed = 0; i < size_in; i++) {
if (in_old[i].i != in[i].i) {
changed = 1;
break;
}
}
if (!changed) {
if (vout < v) {
/* Continue rising. */
vout += time_inc / t_rise;
if (vout > v)
vout = v;
} else {
/* Continue falling. */
vout -= time_inc / t_fall;
if (vout < v)
vout = v;
}
} else {
/* There has been a change in input since the last
analog access. Determine when the change occurred
and calculate the current output, then set a breakpoint
for completion of the current transition.
*/
iota = time_inc * 1e-7; // Ignorable
if (T(0) - in[0].i_changed < iota) {
/* Previous input value in force for whole step. */
step_count = 1;
step = 0;
interval[0] = time_inc;
} else if (in[0].i_changed - T(1) < iota) {
/* New input value in force for whole step.
* Includes common no-change case where new == old.
*/
step_count = 2;
step = 1;
interval[1] = time_inc;
} else {
/* Calculate both sides of change. */
step_count = 2;
step = 0;
interval[0] = in[0].i_changed - T(1);
interval[1] = T(0) - in[0].i_changed;
}
when = -1.0;
for (; step < step_count; ++step) {
int last_step = (step == step_count - 1);
if (step == 0)
target = get_out_val(in_old, size_in);
else
target = v;
if (target > vout) {
/* Rising. */
vout += interval[step] / t_rise;
if (vout > v)
vout = v;
else if (last_step)
when = (v - vout) * t_rise;
} else if (target < vout) {
/* Falling. */
vout -= interval[step] / t_fall;
if (vout < v)
vout = v;
else if (last_step)
when = (vout - v) * t_fall;
}
}
if (when > 0.0)
cm_analog_set_perm_bkpt(when + TIME);
}
out[0] = vout * level_inc + out_low;
OUTPUT(out[0]) = out[0];
return;
}
/* Multiple single-bit conversions. */
for (i = 0; i < size_in; i++) {
if ( 0.0 == TIME ) { /*** DC analysis ***/ if ( 0.0 == TIME ) { /*** DC analysis ***/
switch (in[i].i) { switch (in[i].i) {
case ONE: case ONE:
out[i] = out_high; vout = out_high;
break; break;
case ZERO: case ZERO:
out[i] = out_low; vout = out_low;
break; break;
case UNKNOWN: case UNKNOWN:
out[i] = out_undef; vout = out_undef;
break; break;
} }
} else if ( in_old[i].i == in[i].i ) { } else if ( in_old[i].i == in[i].i ) {
@ -295,44 +440,41 @@ void cm_dac_bridge(ARGS)
switch (in[i].i) { switch (in[i].i) {
case ZERO: case ZERO:
if (out_old[i] > out_low) { /* output still dropping */ if (out_old[i] > out_low) { /* output still dropping */
out[i] = out_old[i] - fall_slope * time_inc; vout = out_old[i] - fall_slope * time_inc;
if ( out_low > out[i]) if (out_low > vout)
out[i] = out_low; vout = out_low;
} else { /* output at out_low */ } else { /* output at out_low */
out[i] = out_low; vout = out_low;
} }
break; break;
case ONE: case ONE:
if (out_old[i] < out_high) { /* output still rising */ if (out_old[i] < out_high) { /* output still rising */
out[i] = out_old[i] + rise_slope * time_inc; vout = out_old[i] + rise_slope * time_inc;
if ( out_high < out[i]) if (out_high < vout)
out[i] = out_high; vout = out_high;
} else { /* output at out_high */ } else { /* output at out_high */
out[i] = out_high; vout = out_high;
} }
break; break;
case UNKNOWN: case UNKNOWN:
if (out_old[i] < out_undef) { /* output still rising */ if (out_old[i] < out_undef) { /* output still rising */
out[i] = out_old[i] + rise_slope * time_inc; vout = out_old[i] + rise_slope * time_inc;
if ( out_undef < out[i]) if (out_undef < vout)
out[i] = out_undef; vout = out_undef;
} else { } else {
if (out_old[i] > out_undef) { /* output still falling */ if (out_old[i] > out_undef) { /* output still falling */
out[i] = out_old[i] - fall_slope * time_inc; vout = out_old[i] - fall_slope * time_inc;
if ( out_undef > out[i]) if (out_undef > vout)
out[i] = out_undef; vout = out_undef;
} else { /* output at out_undef */ } else { /* output at out_undef */
out[i] = out_undef; vout = out_undef;
} }
} }
break; break;
} }
} else { } else {
double when, iota, vout, interval[2];
int step, step_count;
/* There HAS been a change in this digital input /* There HAS been a change in this digital input
since the last analog access. Determine when the change since the last analog access. Determine when the change
occurred and calculate the current output, then occurred and calculate the current output, then
@ -412,9 +554,8 @@ void cm_dac_bridge(ARGS)
} }
if (when > 0.0) if (when > 0.0)
cm_analog_set_perm_bkpt(when + TIME); cm_analog_set_perm_bkpt(when + TIME);
out[i] = vout;
} }
OUTPUT(out[i]) = out[i]; OUTPUT(out[i]) = out[i] = vout;
} }
break; break;