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
parent 5a53a2ecd2
commit b649ab78b9
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)
{
if (in >= high)
return ONE;
else if (in <= low)
return ZERO;
if (low <= high) {
/* Normal operation. */
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;
}
@ -110,22 +121,34 @@ void cm_adc_bridge(ARGS)
in_high; /* analog output value corresponding to '1'
digital input */
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
values plus their previous values */
test; /* temp holding variable for digital states */
/* determine "width" of the node bridge... */
size = PORT_SIZE(in);
in_high = PARAM(in_high);
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 (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 */
cm_event_alloc(0, size * (int) sizeof(Digital_State_t));
cm_event_alloc(0, size_out * (int) sizeof(Digital_State_t));
/* Get discrete addresses */
@ -133,7 +156,7 @@ void cm_adc_bridge(ARGS)
/* Ensure output on first call. */
for (i = 0; i < size; i++)
for (i = 0; i < size_out; i++)
out[i] = UNKNOWN + 1;
return;
}
@ -143,13 +166,75 @@ void cm_adc_bridge(ARGS)
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) {
case ANALOG: /** analog call...check for breakpoint calls. **/
/* 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);
if (test != out[i]) {
if (test != out[i]) {
/* call for event breakpoint... */
cm_event_queue(TIME);
@ -161,9 +246,9 @@ void cm_adc_bridge(ARGS)
case EVENT: /** discrete call...lots to do **/
/* 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);
if (test != out[i]) {
if (test != out[i]) {
/* Post changed value. */
OUTPUT_STATE(out[i]) = test;
@ -175,6 +260,12 @@ void cm_adc_bridge(ARGS)
OUTPUT_DELAY(out[i]) = PARAM(rise_delay);
break;
default:
if (in_low > in_high) {
/* Input is in hysteresis band. */
OUTPUT_CHANGED(out[i]) = FALSE;
continue;
}
if (out[i] == ZERO)
OUTPUT_DELAY(out[i]) = PARAM(rise_delay);
else

View File

@ -24,6 +24,11 @@ Spice_Model_Name: adc_bridge
C_Function_Name: cm_adc_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:
@ -32,11 +37,12 @@ Description: "input" "output"
Direction: in out
Default_Type: v d
Allowed_Types: [v,vd,i,id,vnam] [d]
Vector: yes yes
Vector_Bounds: - -
Vector: yes yes
Vector_Bounds: - -
Null_Allowed: no no
/* If in_low > in_high the adc_bridge has hysteresis - a Schmitt trigger. */
PARAMETER_TABLE:
@ -72,3 +78,15 @@ Limits: [1e-12 -] [1e-12 -]
Vector: no no
Vector_Bounds: - -
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
MODIFICATIONS
16 Aug 1991 Jeffrey P. Murray
2 Oct 1991 Jeffrey P. Murray
SUMMARY
This file contains the model-specific routines used to
functionally describe the dac_bridge code model.
INTERFACES
FILE ROUTINE CALLED
@ -38,11 +35,9 @@ INTERFACES
CMevt.c void *cm_event_alloc()
void *cm_event_get_ptr()
REFERENCED FILES
Inputs from and outputs to ARGS structure.
NON-STANDARD FEATURES
@ -115,8 +110,7 @@ NON-STANDARD FEATURES
NONE
==============================================================================*/
=============================================================================*/
/* Instances of this structure track digital input changes. */
@ -125,6 +119,29 @@ struct d_data {
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 ===*/
/************************************************
@ -135,9 +152,7 @@ struct d_data {
* Created 6/3/91 J.P.Murray *
************************************************/
void cm_dac_bridge(ARGS)
{
double out_low, /* analog output value corresponding to '0'
digital input */
@ -157,18 +172,16 @@ void cm_dac_bridge(ARGS)
time_inc; /* time increment since last analog call */
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 */
*in_old; /* array holding previous input values */
*in_old; /* array holding previous input values */
/* determine "width" of the node bridge... */
size = PORT_SIZE(in);
/** Read in remaining model parameters **/
/* Read in model parameters. **/
out_low = PARAM(out_low);
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 (!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;
} else {
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 (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 */
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 */
cm_analog_alloc(0, size * (int) sizeof(double));
cm_analog_alloc(0, size_out * (int)sizeof(double));
/* Retrieve allocated addresses. */
@ -201,88 +232,202 @@ void cm_dac_bridge(ARGS)
out = (double *) cm_analog_get_ptr(0, 0);
/* read current input values */
for (i=0; i<size; i++) {
for (i = 0; i < size_in; i++) {
in[i].i = INPUT_STATE(in[i]);
}
/* Output initial analog levels based on input values */
for (i=0; i<size; i++) { /* assign addresses */
switch (in[i].i) {
if (multi) {
/* 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;
break;
break;
case UNKNOWN: out[i] = out_undef;
break;
break;
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);
}
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) {
double when, iota, vout, interval[2];
int step, step_count;
case EVENT: /** discrete call... **/
/* Test to see if any change has occurred in an input */
/* 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... */
in[i].i_changed = TIME;
/* post current time as a breakpoint */
cm_analog_set_perm_bkpt(TIME);
if (multi) {
in[0].i_changed = TIME;
break;
} else {
in[i].i_changed = TIME;
}
}
}
break;
case ANALOG: /** analog call... **/
level_inc = out_high - out_low;
rise_slope = level_inc / t_rise;
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 ***/
switch (in[i].i) {
case ONE:
out[i] = out_high;
vout = out_high;
break;
case ZERO:
out[i] = out_low;
vout = out_low;
break;
case UNKNOWN:
out[i] = out_undef;
vout = out_undef;
break;
}
} else if ( in_old[i].i == in[i].i ) {
@ -295,44 +440,41 @@ void cm_dac_bridge(ARGS)
switch (in[i].i) {
case ZERO:
if (out_old[i] > out_low) { /* output still dropping */
out[i] = out_old[i] - fall_slope * time_inc;
if ( out_low > out[i])
out[i] = out_low;
vout = out_old[i] - fall_slope * time_inc;
if (out_low > vout)
vout = out_low;
} else { /* output at out_low */
out[i] = out_low;
vout = out_low;
}
break;
case ONE:
if (out_old[i] < out_high) { /* output still rising */
out[i] = out_old[i] + rise_slope * time_inc;
if ( out_high < out[i])
out[i] = out_high;
vout = out_old[i] + rise_slope * time_inc;
if (out_high < vout)
vout = out_high;
} else { /* output at out_high */
out[i] = out_high;
vout = out_high;
}
break;
case UNKNOWN:
if (out_old[i] < out_undef) { /* output still rising */
out[i] = out_old[i] + rise_slope * time_inc;
if ( out_undef < out[i])
out[i] = out_undef;
vout = out_old[i] + rise_slope * time_inc;
if (out_undef < vout)
vout = out_undef;
} else {
if (out_old[i] > out_undef) { /* output still falling */
out[i] = out_old[i] - fall_slope * time_inc;
if ( out_undef > out[i])
out[i] = out_undef;
vout = out_old[i] - fall_slope * time_inc;
if (out_undef > vout)
vout = out_undef;
} else { /* output at out_undef */
out[i] = out_undef;
vout = out_undef;
}
}
break;
}
} else {
double when, iota, vout, interval[2];
int step, step_count;
/* There HAS been a change in this digital input
since the last analog access. Determine when the change
occurred and calculate the current output, then
@ -412,9 +554,8 @@ void cm_dac_bridge(ARGS)
}
if (when > 0.0)
cm_analog_set_perm_bkpt(when + TIME);
out[i] = vout;
}
OUTPUT(out[i]) = out[i];
OUTPUT(out[i]) = out[i] = vout;
}
break;