diff --git a/src/xspice/icm/analog/ltota/cfunc.mod b/src/xspice/icm/analog/ltota/cfunc.mod new file mode 100644 index 000000000..772d00268 --- /dev/null +++ b/src/xspice/icm/analog/ltota/cfunc.mod @@ -0,0 +1,425 @@ +/*.......1.........2.........3.........4.........5.........6.........7.........8 +================================================================================ + +FILE ltota/cfunc.mod + +Public Domain + +The ngspice team + + +AUTHORS + + 1 Jan 2023 Holger Vogt + + +MODIFICATIONS + + + +SUMMARY + + This file contains the model-specific routines used to + functionally describe the ltota code model. + + +INTERFACES + + FILE ROUTINE CALLED + + CMutil.c void cm_smooth_corner(); + void cm_smooth_discontinuity(); + void cm_climit_fcn() + +REFERENCED FILES + + Inputs from and outputs to ARGS structure. + + +NON-STANDARD FEATURES + + NONE + +===============================================================================*/ + +/*=== INCLUDE FILES ====================*/ + + + + +/*=== CONSTANTS ========================*/ + + + + +/*=== MACROS ===========================*/ + + + + +/*=== LOCAL VARIABLES & TYPEDEFS =======*/ + + + + +/*=== FUNCTION PROTOTYPE DEFINITIONS ===*/ + + + + + +/*============================================================================== + +FUNCTION void cm_ota() + +AUTHORS + + 1 Jan 2023 Holger Vogt + +MODIFICATIONS + + +SUMMARY + + This function implements the ltota code model. + +INTERFACES + + FILE ROUTINE CALLED + + CMutil.c void cm_smooth_corner(); + void cm_smooth_discontinuity(); + void cm_climit_fcn() + +RETURNED VALUE + + Returns inputs and outputs via ARGS structure. + +GLOBAL VARIABLES + + NONE + +NON-STANDARD FEATURES + + NONE + +==============================================================================*/ + +/*=== CM_ILIMIT ROUTINE ===*/ + +void cm_ota(ARGS) /* structure holding parms, + inputs, outputs, etc. */ +{ +* input parameters + double Ref, G, Iout, Isource, Isink, Ioffset, Vhigh, Vlow, Rclamp, Epsilon; +* noise parameters, not yet implemented + double EN, ENk, IN, INk, INcm, INcmk; + + double mult, curout, vout; + + Mif_Complex_t ac_gain; + + + + + /* Retrieve frequently used parameters... */ + + Ref = PARAM(Ref); + G = PARAM(G); + Iout = PARAM(Iout); + Isource = PARAM(Isource); + Isink = PARAM(Isink); + Ioffset = PARAM(Ioffset); + Vhigh = PARAM(Vhigh); + Vlow = PARAM(Vlow); + Rclamp = PARAM(Rclamp); + Epsilon = PARAM(Epsilon); + + /* Test to see if in3 or in4 are connected... */ + /* if not, assign 1 to muliplier */ + /* else multiplier equals the difference */ + + if ( PORT_NULL(in3) || PORT_NULL(in4) ) { + mult = 1.0; + } + else { + mult = INPUT(in3) - INPUT(in4); + } + + curout = (Ref - INPUT(in1) + INPUT(in2)) * mult * G + Ioffset; + + /* Retrieve frequently used inputs... */ + + vout = INPUT(out6); + + + + + /* Compute Veq plus derivatives using climit_fcn */ + + if(INIT != 1){ + /* If reasonable power and voltage values exist (i.e., not INIT)... */ + /* then calculate expected equivalent voltage values and derivs. */ + + cm_climit_fcn(INPUT(in), in_offset, pos_pwr_in, neg_pwr_in, + 0.0, 0.0, v_pwr_range, gain, MIF_FALSE, &veq, + &pveq_pvin, &pveq_pvneg, &pveq_pvpos); + } + else { + /* Initialization pass...set nominal values */ + + veq = (pos_pwr_in - neg_pwr_in) / 2.0; + pveq_pvin = 0.0; + pveq_pvpos = 0.0; + pveq_pvneg = 0.0; + } + + + /* Calculate Rout */ + + if (r_out_source == r_out_sink) { + /* r_out constant => no calculation necessary */ + + r_out = r_out_source; + pr_out_px = 0.0; + + } + else { /* Interpolate smoothly between sourcing & sinking values */ + cm_smooth_discontinuity(veq - vout, -r_out_domain, r_out_sink, r_out_domain, + r_out_source, &r_out, &pr_out_px); + } + + + + /* Calculate i_out & derivatives */ + + i_threshold_lower = -i_limit_sink + i_sink_range; + i_threshold_upper = i_limit_source - i_source_range; + + i_out = (veq - vout) / r_out; + pi_out_pvin = (pveq_pvin/r_out - veq*pr_out_px*pveq_pvin/ + (r_out*r_out)); + pi_out_pvout = (-1.0/r_out - vout*pr_out_px/(r_out*r_out)); + + pi_out_ppos_pwr = (pveq_pvpos/r_out - veq*pr_out_px*pveq_pvpos/ + (r_out*r_out)); + pi_out_pneg_pwr = (pveq_pvneg/r_out - veq*pr_out_px*pveq_pvneg/ + (r_out*r_out)); + + + /* Preset i_pos_pwr & i_neg_pwr & partials to 0.0 */ + + i_pos_pwr = 0.0; + pi_pos_pvin = 0.0; + pi_pos_pvneg = 0.0; + pi_pos_pvpos = 0.0; + pi_pos_pvout = 0.0; + + + i_neg_pwr = 0.0; + pi_neg_pvin = 0.0; + pi_neg_pvneg = 0.0; + pi_neg_pvpos = 0.0; + pi_neg_pvout = 0.0; + + + + + /* Determine operating point of i_out for limiting */ + + if (i_out < 0.0) { /* i_out sinking */ + if (i_out < i_threshold_lower) { + if (i_out < (-i_limit_sink-i_sink_range)) { /* i_out lower-limited */ + i_out = -i_limit_sink; + i_neg_pwr = -i_out; + pi_out_pvin = 0.0; + pi_out_pvout = 0.0; + pi_out_ppos_pwr = 0.0; + pi_out_pneg_pwr = 0.0; + } + else { /* i_out in lower smoothing region */ + cm_smooth_corner(i_out,-i_limit_sink,-i_limit_sink,i_sink_range, + 0.0,1.0,&i_out,&pi_out_plimit); + pi_out_pvin = pi_out_pvin * pi_out_plimit; + pi_out_pvout = pi_out_pvout * pi_out_plimit; + pi_out_ppos_pwr = pi_out_ppos_pwr * pi_out_plimit; + pi_out_pneg_pwr = pi_out_pneg_pwr * pi_out_plimit; + + i_neg_pwr = -i_out; + pi_neg_pvin = -pi_out_pvin; + pi_neg_pvneg = -pi_out_pneg_pwr; + pi_neg_pvpos = -pi_out_ppos_pwr; + pi_neg_pvout = -pi_out_pvout; + } + } + else { /* i_out in lower linear region...calculate i_neg_pwr */ + if (i_out > -2.0*i_sink_range) { /* i_out near 0.0...smooth i_neg_pwr */ + cm_smooth_corner(i_out,-i_sink_range,0.0,i_sink_range,1.0,0.0, + &i_neg_pwr,&pi_neg_plimit); + i_neg_pwr = -i_neg_pwr; + pi_neg_pvin = -pi_out_pvin * pi_neg_plimit; + pi_neg_pvneg = -pi_out_pneg_pwr * pi_neg_plimit; + pi_neg_pvpos = -pi_out_ppos_pwr * pi_neg_plimit; + pi_neg_pvout = -pi_out_pvout * pi_neg_plimit; + } + else { + i_neg_pwr = -i_out; /* Not near i_out=0.0 => i_neg_pwr=-i_out */ + pi_neg_pvin = -pi_out_pvin; + pi_neg_pvneg = -pi_out_pneg_pwr; + pi_neg_pvpos = -pi_out_ppos_pwr; + pi_neg_pvout = -pi_out_pvout; + } + } + } + else { /* i_out sourcing */ + if (i_out > i_threshold_upper) { + if (i_out > (i_limit_source + i_source_range)) { /* i_out upper-limited */ + i_out = i_limit_source; + i_pos_pwr = -i_out; + pi_out_pvin = 0.0; + pi_out_pvout = 0.0; + pi_out_ppos_pwr = 0.0; + pi_out_pneg_pwr = 0.0; + } + else { /* i_out in upper smoothing region */ + cm_smooth_corner(i_out,i_limit_source,i_limit_source,i_sink_range, + 1.0,0.0,&i_out,&pi_out_plimit); + pi_out_pvin = pi_out_pvin * pi_out_plimit; + pi_out_pvout = pi_out_pvout * pi_out_plimit; + pi_out_ppos_pwr = pi_out_ppos_pwr * pi_out_plimit; + pi_out_pneg_pwr = pi_out_pneg_pwr * pi_out_plimit; + + i_pos_pwr = -i_out; + pi_pos_pvin = -pi_out_pvin; + pi_pos_pvneg = -pi_out_pneg_pwr; + pi_pos_pvpos = -pi_out_ppos_pwr; + pi_pos_pvout = -pi_out_pvout; + } + } + else { /* i_out in upper linear region...calculate i_pos_pwr */ + if (i_out < 2.0*i_source_range) { /* i_out near 0.0...smooth i_pos_pwr */ + cm_smooth_corner(i_out,i_source_range,0.0,i_source_range,0.0,1.0, + &i_pos_pwr,&pi_pos_plimit); + i_pos_pwr = -i_pos_pwr; + pi_pos_pvin = -pi_out_pvin * pi_pos_plimit; + pi_pos_pvneg = -pi_out_pneg_pwr * pi_pos_plimit; + pi_pos_pvpos = -pi_out_ppos_pwr * pi_pos_plimit; + pi_pos_pvout = -pi_out_pvout * pi_pos_plimit; + } + else { /* Not near i_out=0.0 => i_pos_pwr=-i_out */ + i_pos_pwr = -i_out; + pi_pos_pvin = -pi_out_pvin; + pi_pos_pvneg = -pi_out_pneg_pwr; + pi_pos_pvpos = -pi_out_ppos_pwr; + pi_pos_pvout = -pi_out_pvout; + } + } + } + + + + + + if (ANALYSIS != MIF_AC) { /* DC & Transient Analyses */ + + + /* Debug line...REMOVE FOR FINAL VERSION!!! */ + /*OUTPUT(t1) = veq; + OUTPUT(t2) = r_out; + OUTPUT(t3) = pveq_pvin; + OUTPUT(t4) = pveq_pvpos; + OUTPUT(t5) = pveq_pvneg;*/ + + + OUTPUT(out) = -i_out; /* Remember...current polarity must be */ + PARTIAL(out,in) = -pi_out_pvin; /* reversed for SPICE...all previous code */ + PARTIAL(out,out) = -pi_out_pvout; /* assumes i_out positive when EXITING */ + /* the model and negative when entering. */ + /* SPICE assumes the opposite, so a */ + /* minus sign is added to all currents */ + /* and current partials to compensate for */ + /* this fact.... JPM */ + + if ( !PORT_NULL(neg_pwr) ) { + OUTPUT(neg_pwr) = -i_neg_pwr; + PARTIAL(neg_pwr,in) = -pi_neg_pvin; + PARTIAL(neg_pwr,out) = -pi_neg_pvout; + if(!PORT_NULL(pos_pwr)){ + PARTIAL(neg_pwr,pos_pwr) = -pi_neg_pvpos; + } + PARTIAL(neg_pwr,neg_pwr) = -pi_neg_pvneg; + PARTIAL(out,neg_pwr) = -pi_out_pneg_pwr; + } + + if ( !PORT_NULL(pos_pwr) ) { + OUTPUT(pos_pwr) = -i_pos_pwr; + PARTIAL(pos_pwr,in) = -pi_pos_pvin; + PARTIAL(pos_pwr,out) = -pi_pos_pvout; + PARTIAL(pos_pwr,pos_pwr) = -pi_pos_pvpos; + if ( !PORT_NULL(neg_pwr) ) { + PARTIAL(pos_pwr,neg_pwr) = -pi_pos_pvneg; + } + PARTIAL(out,pos_pwr) = -pi_out_ppos_pwr; + } + + } + else { /* AC Analysis */ + ac_gain.real = -pi_out_pvin; + ac_gain.imag= 0.0; + AC_GAIN(out,in) = ac_gain; + + ac_gain.real = -pi_out_pvout; + ac_gain.imag= 0.0; + AC_GAIN(out,out) = ac_gain; + + if ( !PORT_NULL(neg_pwr) ) { + ac_gain.real = -pi_neg_pvin; + ac_gain.imag= 0.0; + AC_GAIN(neg_pwr,in) = ac_gain; + + ac_gain.real = -pi_out_pneg_pwr; + ac_gain.imag= 0.0; + AC_GAIN(out,neg_pwr) = ac_gain; + + ac_gain.real = -pi_neg_pvout; + ac_gain.imag= 0.0; + AC_GAIN(neg_pwr,out) = ac_gain; + + ac_gain.real = -pi_neg_pvpos; + ac_gain.imag= 0.0; + AC_GAIN(neg_pwr,pos_pwr) = ac_gain; + + ac_gain.real = -pi_neg_pvneg; + ac_gain.imag= 0.0; + AC_GAIN(neg_pwr,neg_pwr) = ac_gain; + } + + + if ( !PORT_NULL(pos_pwr) ) { + ac_gain.real = -pi_pos_pvin; + ac_gain.imag= 0.0; + AC_GAIN(pos_pwr,in) = ac_gain; + + ac_gain.real = -pi_out_ppos_pwr; + ac_gain.imag= 0.0; + AC_GAIN(out,pos_pwr) = ac_gain; + + ac_gain.real = -pi_pos_pvout; + ac_gain.imag= 0.0; + AC_GAIN(pos_pwr,out) = ac_gain; + + ac_gain.real = -pi_pos_pvpos; + ac_gain.imag= 0.0; + AC_GAIN(pos_pwr,pos_pwr) = ac_gain; + + ac_gain.real = -pi_pos_pvneg; + ac_gain.imag= 0.0; + AC_GAIN(pos_pwr,neg_pwr) = ac_gain; + } + } +} + + + + + diff --git a/src/xspice/icm/analog/ltota/ifspec.ifs b/src/xspice/icm/analog/ltota/ifspec.ifs new file mode 100644 index 000000000..e5f13ac86 --- /dev/null +++ b/src/xspice/icm/analog/ltota/ifspec.ifs @@ -0,0 +1,191 @@ +/*.......1.........2.........3.........4.........5.........6.........7.........8 +================================================================================ +Public Domain + +The ngspice team + + +AUTHORS + + 31 Dec 2022 Holger Vogt + + +SUMMARY + + This file contains the interface specification file for the + analog ota code model. + +===============================================================================*/ + +NAME_TABLE: + + +C_Function_Name: cm_ota +Spice_Model_Name: ota +Description: "LTSPICE compatible OTA" + + +PORT_TABLE: + +Port_Name: in1 in2 +Description: "input1" "input2" +Direction: in in +Default_Type: v v +Allowed_Types: [v] [v] +Vector: no no +Vector_Bounds: - - +Null_Allowed: no no + +PORT_TABLE: + +Port_Name: in3 in4 +Description: "input3" "input4" +Direction: in in +Default_Type: v v +Allowed_Types: [v] [v] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + +PORT_TABLE: + +Port_Name: in5 +Description: "input5" +Direction: in +Default_Type: v +Allowed_Types: [v] +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PORT_TABLE: + +Port_Name: out6 out7 +Description: "out1" "out2" +Direction: inout inout +Default_Type: g i +Allowed_Types: [g, gd] [i, id] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes no + +PORT_TABLE: + +Port_Name: ground8 +Description: "external ground" +Direction: inout +Default_Type: g +Allowed_Types: [g,gd] +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: Ref G +Description: "input offset" "gain" +Data_Type: real real +Default_Value: 0.0 1.0e-6 +Limits: - - +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + +PARAMETER_TABLE: + +Parameter_Name: Isource Isink +Description: "current sourcing limit" "current sinking limit" +Data_Type: real real +Default_Value: 10.0e-5 -10.0e-5 +Limits: [1e-12 -] [1e-12 -] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + +PARAMETER_TABLE: + +Parameter_Name: Iout +Description: "current limit" +Data_Type: real +Default_Value: 10.0e-5 +Limits: [1e-12 -] +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: Asym Linear +Description: "indep. asym. limits" "disable output limiting" +Data_Type: boolean boolean +Default_Value: FALSE FALSE +Limits: - - +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + +PARAMETER_TABLE: + +Parameter_Name: Ioffset PowerUp +Description: "output offset current" "Disable pin 7 output current" +Data_Type: real boolean +Default_Value: 1e-6 FALSE +Limits: [1e-15 -] - +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + +PARAMETER_TABLE: + +Parameter_Name: Vhigh Vlow +Description: "pos. rail voltage" "neg. rail voltage" +Data_Type: real real +Default_Value: 2 0 +Limits: - - +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + +PARAMETER_TABLE: + +Parameter_Name: Rclamp Epsilon +Description: "clamping res. to rails" "Rclamp imped. v sm. range" +Data_Type: real real +Default_Value: 1.0 0 +Limits: [1e-9 1e9] [1e-9 1e9] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + +PARAMETER_TABLE: + +Parameter_Name: EN ENk +Description: "voltage noise density" "voltage noise knee freq." +Data_Type: real real +Default_Value: 0 0 +Limits: [1e-15 -] [1e-15 -] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + +PARAMETER_TABLE: + +Parameter_Name: IN INk +Description: "current noise density" "current noise knee freq." +Data_Type: real real +Default_Value: 0 0 +Limits: [1e-15 -] [1e-15 -] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + +PARAMETER_TABLE: + +Parameter_Name: INcm INcmk +Description: "common mode cur. noi dens." "common mode cur. noi knee freq." +Data_Type: real real +Default_Value: 1.0 1.0 +Limits: [1e-15 -] [1e-9 1e9] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes