diff --git a/src/xspice/icm/digital/adc_bridge/cfunc.mod b/src/xspice/icm/digital/adc_bridge/cfunc.mod index bdb96fd3b..b8ab7a5cb 100644 --- a/src/xspice/icm/digital/adc_bridge/cfunc.mod +++ b/src/xspice/icm/digital/adc_bridge/cfunc.mod @@ -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 diff --git a/src/xspice/icm/digital/adc_bridge/ifspec.ifs b/src/xspice/icm/digital/adc_bridge/ifspec.ifs index efcd030d0..9a7cb1e57 100644 --- a/src/xspice/icm/digital/adc_bridge/ifspec.ifs +++ b/src/xspice/icm/digital/adc_bridge/ifspec.ifs @@ -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 diff --git a/src/xspice/icm/digital/dac_bridge/cfunc.mod b/src/xspice/icm/digital/dac_bridge/cfunc.mod index ae3a34b07..8bb78d702 100644 --- a/src/xspice/icm/digital/dac_bridge/cfunc.mod +++ b/src/xspice/icm/digital/dac_bridge/cfunc.mod @@ -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 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;