Browse Source

Add parsing and translation of the FREQ form of E-source devices,

integrated with the existing parsing of AND/NAND/OR/NOR forms (inpcom.c).
For the implementation, add a new analog XSPICE code model, xfer.
Add an example to examples/sp.
pre-master-46
Giles Atkinson 3 years ago
committed by Holger Vogt
parent
commit
d31568bd83
  1. 185
      examples/sp/filter.lib
  2. 91
      examples/sp/filter.sp
  3. 270
      src/frontend/inpcom.c
  4. 1
      src/xspice/icm/analog/modpath.lst
  5. 130
      src/xspice/icm/analog/xfer/cfunc.mod
  6. 91
      src/xspice/icm/analog/xfer/ifspec.ifs

185
examples/sp/filter.lib

@ -0,0 +1,185 @@
.SUBCKT filter 1 2 3
*2-port S-parameter file
*Title: * simple test for xfer code model: comparison
*Generated by ngspice at Tue May 23 06:49:31 2023
* Hz S RI R
* Z1=50.000000 Z2=50.000000
R1N 1 10 -5.000000e+01
R1P 10 11 1.000000e+02
R2N 2 20 -5.000000e+01
R2P 20 21 1.000000e+02
*S11 FREQ DB PHASE
E11 11 12 FREQ {V(10,3)}= DB
+( 1.000000e+07Hz, 3.654967e-07, -7.619541e+01)
+( 1.487179e+07Hz, 1.964030e-07, -1.157703e+02)
+( 1.974359e+07Hz, -1.084219e-07, -1.569277e+02)
+( 2.461538e+07Hz, -5.321598e-06, -1.985756e+02)
+( 2.948718e+07Hz, -1.265047e-05, -2.395114e+02)
+( 3.435897e+07Hz, -2.727603e-05, -2.792878e+02)
+( 3.923077e+07Hz, -6.620604e-05, -3.184596e+02)
+( 4.410256e+07Hz, -1.624082e-04, -3.582070e+02)
+( 4.897436e+07Hz, -3.755273e-04, -3.996839e+02)
+( 5.384615e+07Hz, -7.725656e-04, -4.432805e+02)
+( 5.871795e+07Hz, -1.320643e-03, -4.881884e+02)
+( 6.358974e+07Hz, -1.599290e-03, -5.330311e+02)
+( 6.846154e+07Hz, -4.631092e-04, -5.776220e+02)
+( 7.333333e+07Hz, -1.388239e-02, -6.262936e+02)
+( 7.820513e+07Hz, -1.245578e+01, -8.015034e+02)
+( 8.307692e+07Hz, -1.142468e+00, -6.888087e+02)
+( 8.794872e+07Hz, -1.292324e+00, -7.542062e+02)
+( 9.282051e+07Hz, -2.163078e+00, -8.283885e+02)
+( 9.769231e+07Hz, -3.764829e+00, -9.213357e+02)
+( 1.025641e+08Hz, -4.165673e+00, -1.041098e+03)
+( 1.074359e+08Hz, -2.143343e+00, -1.139856e+03)
+( 1.123077e+08Hz, -1.084156e+00, -1.201868e+03)
+( 1.171795e+08Hz, -7.962690e-01, -1.249480e+03)
+( 1.220513e+08Hz, -1.204024e+00, -1.318959e+03)
+( 1.269231e+08Hz, -4.148462e-01, -1.169292e+03)
+( 1.317949e+08Hz, -1.026085e-02, -1.240330e+03)
+( 1.366667e+08Hz, -1.360661e-04, -1.265793e+03)
+( 1.415385e+08Hz, -2.410965e-03, -1.281511e+03)
+( 1.464103e+08Hz, -3.787891e-03, -1.293132e+03)
+( 1.512821e+08Hz, -3.602264e-03, -1.302433e+03)
+( 1.561538e+08Hz, -2.547410e-03, -1.310211e+03)
+( 1.610256e+08Hz, -1.530232e-03, -1.316893e+03)
+( 1.658974e+08Hz, -8.667728e-04, -1.322741e+03)
+( 1.707692e+08Hz, -4.905888e-04, -1.327928e+03)
+( 1.756410e+08Hz, -2.840626e-04, -1.332578e+03)
+( 1.805128e+08Hz, -1.688713e-04, -1.336783e+03)
+( 1.853846e+08Hz, -1.041703e-04, -1.340612e+03)
+( 1.902564e+08Hz, -6.546576e-05, -1.344121e+03)
+( 1.951282e+08Hz, -4.228835e-05, -1.347353e+03)
+( 2.000000e+08Hz, -2.867354e-05, -1.350343e+03)
*S12 FREQ DB PHASE
E12 12 3 FREQ {V(20,3)}= DB
+( 1.000000e+07Hz, -1.248758e+02, -1.373971e+02)
+( 1.487179e+07Hz, -9.831399e+01, -1.647281e+02)
+( 1.974359e+07Hz, -7.673566e+01, -2.026127e+02)
+( 2.461538e+07Hz, -5.898226e+01, -2.906518e+02)
+( 2.948718e+07Hz, -5.542440e+01, -3.666485e+02)
+( 3.435897e+07Hz, -5.204910e+01, -4.009490e+02)
+( 3.923077e+07Hz, -4.814977e+01, -4.271950e+02)
+( 4.410256e+07Hz, -4.426072e+01, -4.514100e+02)
+( 4.897436e+07Hz, -4.063346e+01, -4.755494e+02)
+( 5.384615e+07Hz, -3.749934e+01, -5.003303e+02)
+( 5.871795e+07Hz, -3.516924e+01, -5.256223e+02)
+( 6.358974e+07Hz, -3.433887e+01, -5.509590e+02)
+( 6.846154e+07Hz, -3.971774e+01, -5.765977e+02)
+( 7.333333e+07Hz, -2.496027e+01, -4.260244e+02)
+( 7.820513e+07Hz, -2.540060e-01, -5.317362e+02)
+( 8.307692e+07Hz, -6.358120e+00, -6.335796e+02)
+( 8.794872e+07Hz, -5.894279e+00, -6.719727e+02)
+( 9.282051e+07Hz, -4.063864e+00, -7.129969e+02)
+( 9.769231e+07Hz, -2.367660e+00, -7.603597e+02)
+( 1.025641e+08Hz, -2.098602e+00, -8.143919e+02)
+( 1.074359e+08Hz, -4.094614e+00, -8.636479e+02)
+( 1.123077e+08Hz, -6.557734e+00, -8.998592e+02)
+( 1.171795e+08Hz, -7.759301e+00, -9.295624e+02)
+( 1.220513e+08Hz, -6.159604e+00, -9.703161e+02)
+( 1.269231e+08Hz, -1.040475e+01, -1.077742e+03)
+( 1.317949e+08Hz, -2.627115e+01, -1.123641e+03)
+( 1.366667e+08Hz, -4.503397e+01, -9.675219e+02)
+( 1.415385e+08Hz, -3.255698e+01, -9.893598e+02)
+( 1.464103e+08Hz, -3.059532e+01, -1.012566e+03)
+( 1.512821e+08Hz, -3.081422e+01, -1.036661e+03)
+( 1.561538e+08Hz, -3.231825e+01, -1.058931e+03)
+( 1.610256e+08Hz, -3.453111e+01, -1.077318e+03)
+( 1.658974e+08Hz, -3.699828e+01, -1.091740e+03)
+( 1.707692e+08Hz, -3.947129e+01, -1.103061e+03)
+( 1.756410e+08Hz, -4.184769e+01, -1.112161e+03)
+( 1.805128e+08Hz, -4.409577e+01, -1.119694e+03)
+( 1.853846e+08Hz, -4.621280e+01, -1.126103e+03)
+( 1.902564e+08Hz, -4.820666e+01, -1.131686e+03)
+( 1.951282e+08Hz, -5.008836e+01, -1.136645e+03)
+( 2.000000e+08Hz, -5.186916e+01, -1.141123e+03)
*S21 FREQ DB PHASE
E21 21 22 FREQ {V(10,3)}= DB
+( 1.000000e+07Hz, -1.248758e+02, -1.373971e+02)
+( 1.487179e+07Hz, -9.831399e+01, -1.647281e+02)
+( 1.974359e+07Hz, -7.673566e+01, -2.026127e+02)
+( 2.461538e+07Hz, -5.898226e+01, -2.906518e+02)
+( 2.948718e+07Hz, -5.542440e+01, -3.666485e+02)
+( 3.435897e+07Hz, -5.204910e+01, -4.009490e+02)
+( 3.923077e+07Hz, -4.814977e+01, -4.271950e+02)
+( 4.410256e+07Hz, -4.426072e+01, -4.514100e+02)
+( 4.897436e+07Hz, -4.063346e+01, -4.755494e+02)
+( 5.384615e+07Hz, -3.749934e+01, -5.003303e+02)
+( 5.871795e+07Hz, -3.516924e+01, -5.256223e+02)
+( 6.358974e+07Hz, -3.433887e+01, -5.509590e+02)
+( 6.846154e+07Hz, -3.971774e+01, -5.765977e+02)
+( 7.333333e+07Hz, -2.496027e+01, -4.260244e+02)
+( 7.820513e+07Hz, -2.540060e-01, -5.317362e+02)
+( 8.307692e+07Hz, -6.358120e+00, -6.335796e+02)
+( 8.794872e+07Hz, -5.894279e+00, -6.719727e+02)
+( 9.282051e+07Hz, -4.063864e+00, -7.129969e+02)
+( 9.769231e+07Hz, -2.367660e+00, -7.603597e+02)
+( 1.025641e+08Hz, -2.098602e+00, -8.143919e+02)
+( 1.074359e+08Hz, -4.094614e+00, -8.636479e+02)
+( 1.123077e+08Hz, -6.557734e+00, -8.998592e+02)
+( 1.171795e+08Hz, -7.759301e+00, -9.295624e+02)
+( 1.220513e+08Hz, -6.159604e+00, -9.703161e+02)
+( 1.269231e+08Hz, -1.040475e+01, -1.077742e+03)
+( 1.317949e+08Hz, -2.627115e+01, -1.123641e+03)
+( 1.366667e+08Hz, -4.503397e+01, -9.675219e+02)
+( 1.415385e+08Hz, -3.255698e+01, -9.893598e+02)
+( 1.464103e+08Hz, -3.059532e+01, -1.012566e+03)
+( 1.512821e+08Hz, -3.081422e+01, -1.036661e+03)
+( 1.561538e+08Hz, -3.231825e+01, -1.058931e+03)
+( 1.610256e+08Hz, -3.453111e+01, -1.077318e+03)
+( 1.658974e+08Hz, -3.699828e+01, -1.091740e+03)
+( 1.707692e+08Hz, -3.947129e+01, -1.103061e+03)
+( 1.756410e+08Hz, -4.184769e+01, -1.112161e+03)
+( 1.805128e+08Hz, -4.409577e+01, -1.119694e+03)
+( 1.853846e+08Hz, -4.621280e+01, -1.126103e+03)
+( 1.902564e+08Hz, -4.820666e+01, -1.131686e+03)
+( 1.951282e+08Hz, -5.008836e+01, -1.136645e+03)
+( 2.000000e+08Hz, -5.186916e+01, -1.141123e+03)
*S22 FREQ DB PHASE
E22 22 3 FREQ {V(20,3)}= DB
+( 1.000000e+07Hz, 1.633257e-07, -1.859873e+01)
+( 1.487179e+07Hz, -1.597472e-07, -3.368599e+01)
+( 1.974359e+07Hz, -9.840549e-08, -6.829764e+01)
+( 2.461538e+07Hz, -5.748843e-06, -2.027281e+02)
+( 2.948718e+07Hz, -1.209271e-05, -3.137855e+02)
+( 3.435897e+07Hz, -2.686288e-05, -3.426102e+02)
+( 3.923077e+07Hz, -6.619051e-05, -3.559304e+02)
+( 4.410256e+07Hz, -1.626503e-04, -3.646130e+02)
+( 4.897436e+07Hz, -3.750315e-04, -3.714149e+02)
+( 5.384615e+07Hz, -7.720713e-04, -3.773802e+02)
+( 5.871795e+07Hz, -1.321131e-03, -3.830562e+02)
+( 6.358974e+07Hz, -1.599127e-03, -3.888868e+02)
+( 6.846154e+07Hz, -4.639695e-04, -3.955734e+02)
+( 7.333333e+07Hz, -1.388161e-02, -4.057551e+02)
+( 7.820513e+07Hz, -1.245578e+01, -4.419690e+02)
+( 8.307692e+07Hz, -1.142468e+00, -3.983505e+02)
+( 8.794872e+07Hz, -1.292324e+00, -4.097393e+02)
+( 9.282051e+07Hz, -2.163078e+00, -4.176053e+02)
+( 9.769231e+07Hz, -3.764828e+00, -4.193838e+02)
+( 1.025641e+08Hz, -4.165672e+00, -4.076855e+02)
+( 1.074359e+08Hz, -2.143343e+00, -4.074397e+02)
+( 1.123077e+08Hz, -1.084156e+00, -4.178506e+02)
+( 1.171795e+08Hz, -7.962687e-01, -4.296447e+02)
+( 1.220513e+08Hz, -1.204024e+00, -4.416736e+02)
+( 1.269231e+08Hz, -4.148459e-01, -4.461929e+02)
+( 1.317949e+08Hz, -1.026073e-02, -4.669521e+02)
+( 1.366667e+08Hz, -1.361528e-04, -4.892508e+02)
+( 1.415385e+08Hz, -2.411138e-03, -5.172082e+02)
+( 1.464103e+08Hz, -3.788042e-03, -5.519999e+02)
+( 1.512821e+08Hz, -3.601989e-03, -5.908887e+02)
+( 1.561538e+08Hz, -2.547212e-03, -6.276516e+02)
+( 1.610256e+08Hz, -1.529976e-03, -6.577430e+02)
+( 1.658974e+08Hz, -8.674572e-04, -6.807399e+02)
+( 1.707692e+08Hz, -4.901623e-04, -6.981937e+02)
+( 1.756410e+08Hz, -2.836349e-04, -7.117446e+02)
+( 1.805128e+08Hz, -1.694495e-04, -7.226059e+02)
+( 1.853846e+08Hz, -1.034247e-04, -7.315943e+02)
+( 1.902564e+08Hz, -6.587183e-05, -7.392507e+02)
+( 1.951282e+08Hz, -4.251059e-05, -7.459375e+02)
+( 2.000000e+08Hz, -2.798303e-05, -7.519027e+02)
.ENDS

91
examples/sp/filter.sp

@ -0,0 +1,91 @@
* Simple test for xfer code model: comparison
*
* This circuit compares the results of an AC analysis of a filter (node out)
* with those from a behavioural model controlled by measured S-parameters
* of that circuit (node xout). The AC analysis has more data points than
* that used to measure the S-parameters, to prevent the results from matching
* exactly.
* The use of S-parameters to create a behavioural simulation of a component
* was discussed here:
* https://sourceforge.net/p/ngspice/discussion/120973/thread/51228e0b01/
* Circuit from:
* Novarianti, Dini. (2019).
* Design and Implementation of Chebyshev Band Pass Filter with
* M-Derived Section in Frequency Band 88 - 108 MHz.
* Jurnal Jartel: Jurnal Jaringan Telekomunikasi. 8. 7-11.
* 10.33795/jartel.v8i1.147.
*
* https://www.researchgate.net/publication/352822864_Design_and_Implementation_of_Chebyshev_Band_Pass_Filter_with_M-Derived_Section_in_Frequency_Band_88_-_108_MHz
* Set this parameter to 1 to generate a Touchstone file that can be used
* to generate the behavioural part of the circuit, filter.lib
.param do_sp=0
.csparam do_sp=do_sp
.if (do_sp)
.csparam Rbase=50 ; This is required by "wrs2p", below.
vgen 1 0 dc 0 ac 1 portnum 1
.else
vgen in 0 dc 0 ac 1
rs in 1 50
.endif
l1 1 2 0.058u
c2 2 0 40.84p
l3 2 3 0.128u
c4 3 0 47.91p
l5 3 4 0.128u
c6 4 0 40.48p
l7 4 5 0.058u
la 5 6 0.044u
lb 6 a 0.078u
cb a 0 17.61p
lc 6 b 0.151u
cc b 0 34.12p
c7 6 7 26.035p
l8 7 0 0.0653u
c8 7 8 20.8p
l9 8 0 0.055u
c9 8 9 20.8p
l10 9 0 0.653u
c10 9 out 45.64p
.if (do_sp)
vl out 0 dc 0 ac 0 portnum 2
.else
rl out 0 50
* Behavioural circuit, for comparison.
.inc filter.lib
R1 in port1 50
xsp port1 xout 0 filter
R2 xout 0 50
.endif
.control
if $&do_sp
sp lin 40 10meg 200meg
wrs2p filter.s2p
plot S_1_1 S_2_2 polar
else
ac lin 400 10meg 200meg
plot db(mag(out)) 5*unwrap(ph(out)) db(mag(xout)) 5*unwrap(ph(xout))
end
.endc
.end

270
src/frontend/inpcom.c

@ -152,7 +152,7 @@ static char inp_get_elem_ident(char *type);
static void rem_mfg_from_models(struct card *start_card); static void rem_mfg_from_models(struct card *start_card);
static void inp_fix_macro_param_func_paren_io(struct card *begin_card); static void inp_fix_macro_param_func_paren_io(struct card *begin_card);
static void inp_fix_gnd_name(struct card *deck); static void inp_fix_gnd_name(struct card *deck);
static void inp_chk_for_multi_in_vcvs(struct card *deck, int *line_number);
static void inp_chk_for_e_source_to_xspice(struct card *deck, int *line_number);
static void inp_add_control_section(struct card *deck, int *line_number); static void inp_add_control_section(struct card *deck, int *line_number);
static char *get_quoted_token(char *string, char **token); static char *get_quoted_token(char *string, char **token);
static void replace_token(char *string, char *token, int where, int total); static void replace_token(char *string, char *token, int where, int total);
@ -985,7 +985,7 @@ struct card *inp_readall(FILE *fp, const char *dir_name,
subckt_w_params = NULL; subckt_w_params = NULL;
if (!cp_getvar("no_auto_gnd", CP_BOOL, NULL, 0)) if (!cp_getvar("no_auto_gnd", CP_BOOL, NULL, 0))
inp_fix_gnd_name(working); inp_fix_gnd_name(working);
inp_chk_for_multi_in_vcvs(working, &rv.line_number);
inp_chk_for_e_source_to_xspice(working, &rv.line_number);
/* "addcontrol" variable is set if "ngspice -a file" was used. */ /* "addcontrol" variable is set if "ngspice -a file" was used. */
@ -1899,7 +1899,6 @@ static void inp_fix_gnd_name(struct card *c)
} }
} }
/* /*
* transform a VCVS "gate" instance into a XSPICE instance * transform a VCVS "gate" instance into a XSPICE instance
* *
@ -1917,31 +1916,11 @@ static void inp_fix_gnd_name(struct card *c)
* the x,y list is fixed to length 2 * the x,y list is fixed to length 2
*/ */
static void inp_chk_for_multi_in_vcvs(struct card *c, int *line_number)
static int inp_chk_for_multi_in_vcvs(struct card *c, int *line_number)
{ {
int skip_control = 0;
for (; c; c = c->nextcard) {
char *line = c->line;
/* there is no e source inside .control ... .endc */
if (ciprefix(".control", line)) {
skip_control++;
continue;
}
else if (ciprefix(".endc", line)) {
skip_control--;
continue;
}
else if (skip_control > 0) {
continue;
}
if (*line == 'e') {
char *fcn_b;
char *fcn_b, *line;
line = c->line;
if (((fcn_b = strstr(line, "nand(")) != NULL || if (((fcn_b = strstr(line, "nand(")) != NULL ||
(fcn_b = strstr(line, "and(")) != NULL || (fcn_b = strstr(line, "and(")) != NULL ||
(fcn_b = strstr(line, "nor(")) != NULL || (fcn_b = strstr(line, "nor(")) != NULL ||
@ -1994,10 +1973,8 @@ static void inp_chk_for_multi_in_vcvs(struct card *c, int *line_number)
while (--xy_values1_b >= ctrl_nodes_b) while (--xy_values1_b >= ctrl_nodes_b)
if (*xy_values1_b == '{') if (*xy_values1_b == '{')
break; break;
}
else {
xy_values1_b =
skip_back_non_ws(xy_values1_b, ctrl_nodes_b);
} else {
xy_values1_b = skip_back_non_ws(xy_values1_b, ctrl_nodes_b);
} }
if (xy_values1_b <= ctrl_nodes_b) if (xy_values1_b <= ctrl_nodes_b)
break; break;
@ -2009,8 +1986,7 @@ static void inp_chk_for_multi_in_vcvs(struct card *c, int *line_number)
xy_values1_e = skip_ws(comma_ptr + 1); xy_values1_e = skip_ws(comma_ptr + 1);
if (*xy_values1_e == '{') { if (*xy_values1_e == '{') {
xy_values1_e = inp_spawn_brace(xy_values1_e); xy_values1_e = inp_spawn_brace(xy_values1_e);
}
else {
} else {
xy_values1_e = skip_non_ws(xy_values1_e); xy_values1_e = skip_non_ws(xy_values1_e);
} }
if (!xy_values1_e) if (!xy_values1_e)
@ -2037,8 +2013,7 @@ static void inp_chk_for_multi_in_vcvs(struct card *c, int *line_number)
get_comma_separated_values(xy_values1, xy_values1_b); get_comma_separated_values(xy_values1, xy_values1_b);
*xy_values1_e = keep; *xy_values1_e = keep;
xy_count2 =
get_comma_separated_values(xy_values2, xy_values2_b);
xy_count2 = get_comma_separated_values(xy_values2, xy_values2_b);
// place restrictions on only having 2 point values; this can // place restrictions on only having 2 point values; this can
// change later // change later
@ -2069,11 +2044,238 @@ static void inp_chk_for_multi_in_vcvs(struct card *c, int *line_number)
c = insert_new_line(c, m_instance, (*line_number)++, c->linenum_orig); c = insert_new_line(c, m_instance, (*line_number)++, c->linenum_orig);
c = insert_new_line(c, m_model, (*line_number)++, c->linenum_orig); c = insert_new_line(c, m_model, (*line_number)++, c->linenum_orig);
#endif #endif
return 1;
} else {
return 0; // No keyword match. */
} }
} }
/* replace the E and G source FREQ function by an XSPICE xfer instance
* (used by Touchstone to netlist converter programs).
* E1 n1 n2 FREQ {expression} = DB values ...
* will become
* B1_gen 1_gen 0 v = expression
* A1_gen 1_gen %d(n1 n2) 1_gen
* .model 1_gen xfer db=true table=[ values ]
*/
static void replace_freq(struct card *c, int *line_number)
{
#ifdef XSPICE
char *line, *e, *e_e, *n1, *n1_e, *n2, *n2_e, *freq;
char *expr, *expr_e, *in, *in_e, *keywd, *cp, *list, *list_e;
int db, ri, rad, got_key, diff;
char pt, key[4];
line = c->line;
/* First token is a node name. */
e = line + 1;
e_e = skip_non_ws(e);
n1 = skip_ws(e_e);
n1_e = skip_non_ws(n1);
freq = strstr(n1_e, "freq");
if (!freq || !isspace_c(freq[-1]) || !isspace_c(freq[4]))
return;
n2 = skip_ws(n1_e);
if (n2 == freq) {
n2 = NULL;
} else {
n2_e = skip_non_ws(n2);
if (freq != skip_ws(n2_e)) // Three nodes or another keyword.
return;
} }
/* Isolate the input expression. */
expr = skip_ws(freq + 4);
if (*expr != '{')
return;
expr = skip_ws(expr + 1);
expr_e = strchr(expr, '}');
if (!expr_e)
return;
skip_back_ws(expr_e, expr);
/* Is the expression just a node name, or v(node) or v(node1, node2)? */
in = NULL;
diff = 0;
if (*expr < '0' || *expr > '9') {
for (in_e = expr; in_e < expr_e; ++in_e) {
if ((*in_e < '0' || *in_e > '9') && (*in_e < 'a' || *in_e > 'z') &&
*in_e != '_')
break;
} }
if (in_e == expr_e) {
/* A simple identifier. */
in = expr;
}
}
if (expr[0] == 'v' && expr[1] == '(' && expr_e[-1] == ')') {
in = expr + 2;
in_e = expr_e - 1;
cp = strchr(in, ',');
diff = (cp && cp < in_e); // Assume v(n1, n2)
}
/* Look for a keyword. Previous processing may put braces around it. */
keywd = skip_ws(expr_e + 1);
if (*keywd == '=')
keywd = skip_ws(keywd + 1);
db = 1;
rad = 0;
ri = 0;
do {
if (!keywd)
return;
list = keywd; // Perhaps not keyword
if (*keywd == '{')
++keywd;
cp = key;
while (*keywd && !isspace_c(*keywd) && *keywd != '}' &&
cp - key < sizeof key - 1) {
*cp++ = *keywd++;
}
*cp = 0;
if (*keywd == '}')
++keywd;
if (!isspace_c(*keywd))
return;
/* Parse the format keyword, if any. */
got_key = 0;
if (!strcmp(key, "mag")) {
db = 0;
got_key = 1;
} else if (!strcmp(key, "db")) {
db = 1;
got_key = 1;
} else if (!strcmp(key, "rad")) {
rad = 1;
got_key = 1;
} else if (!strcmp(key, "deg")) {
rad = 0;
got_key = 1;
} else if (!strcmp(key, "r_i")) {
ri = 1;
got_key = 1;
}
/* Get the list of values. */
if (got_key)
list = skip_ws(keywd);
if (!list)
return;
keywd = list;
} while(got_key);
list_e = list + strlen(list) - 1;
skip_back_ws(list_e, list);
if (list >= list_e)
return;
/* All good, rewrite the line.
* Macro BSTR is used to pass counted string arguments to tprintf().
*/
#define BSTR(s) (int)(s##_e - s), s
pt = (*line == 'e') ? 'v' : 'i';
*line = '*'; // Make a comment
if (in) {
/* Connect input nodes directly. */
if (diff) {
/* Differential input. */
if (n2) {
line = tprintf("a_gen_%.*s %%vd(%.*s) %%%cd(%.*s %.*s) "
"gen_model_%.*s",
BSTR(e), BSTR(in), pt, BSTR(n1), BSTR(n2), BSTR(e));
} else {
line = tprintf("a_gen_%.*s %%vd(%.*s) %%%c(%.*s) "
"gen_model_%.*s",
BSTR(e), BSTR(in), pt, BSTR(n1), BSTR(e));
}
} else {
/* Single node input. */
if (n2) {
line = tprintf("a_gen_%.*s %.*s %%%cd(%.*s %.*s) "
"gen_model_%.*s",
BSTR(e), BSTR(in), pt, BSTR(n1), BSTR(n2),
BSTR(e));
} else {
line = tprintf("a_gen_%.*s %.*s %%%c(%.*s) gen_model_%.*s",
BSTR(e), BSTR(in), pt, BSTR(n1), BSTR(e));
}
}
} else {
/* Use a B-source for input. */
line = tprintf("b_gen_%.*s gen_node_%.*s 0 v=%.*s",
BSTR(e), BSTR(e), BSTR(expr));
c = insert_new_line(c, line, (*line_number)++, c->linenum_orig);
if (n2) {
line = tprintf("a_gen_%.*s gen_node_%.*s %%%cd(%.*s %.*s) "
"gen_model_%.*s",
BSTR(e), BSTR(e), pt, BSTR(n1), BSTR(n2), BSTR(e));
} else {
line = tprintf("a_gen_%.*s gen_node_%.*s %%%c(%.*s) "
"gen_model_%.*s",
BSTR(e), BSTR(e), pt, BSTR(n1), BSTR(e));
}
}
c = insert_new_line(c, line, (*line_number)++, c->linenum_orig);
line = tprintf(".model gen_model_%.*s xfer %s table = [%.*s]",
BSTR(e),
ri ? "r_i=true" : rad ? "rad=true" : !db ? "db=false" : "",
BSTR(list));
c = insert_new_line(c, line, (*line_number)++, c->linenum_orig);
#endif
}
/* Convert some E and G-source variants to XSPICE code models. */
static void inp_chk_for_e_source_to_xspice(struct card *c, int *line_number)
{
int skip_control = 0;
for (; c; c = c->nextcard) {
char *line = c->line;
/* there is no e source inside .control ... .endc */
if (ciprefix(".control", line)) {
skip_control++;
continue;
}
else if (ciprefix(".endc", line)) {
skip_control--;
continue;
}
else if (skip_control > 0) {
continue;
}
if (*line == 'e' && inp_chk_for_multi_in_vcvs(c, line_number))
continue;
if (*line != 'e' && *line != 'g')
continue;
/* Is it the FREQ form with S-parameter table? */
replace_freq(c, line_number);
}
}
/* If ngspice is started with option -a, then variable 'autorun' /* If ngspice is started with option -a, then variable 'autorun'
* will be set and a control section is inserted to try and ensure * will be set and a control section is inserted to try and ensure

1
src/xspice/icm/analog/modpath.lst

@ -14,6 +14,7 @@ sine
slew slew
square square
summer summer
xfer
s_xfer s_xfer
triangle triangle
file_source file_source

130
src/xspice/icm/analog/xfer/cfunc.mod

@ -0,0 +1,130 @@
/* Transfer function block for AC simulation, based on s_xfer code model. */
#include <stdlib.h>
#define PI 3.141592653589793238462643383279502884197
/* How the table information is stored internally. */
struct data_pt {
double f; /* Frequency, radians/sec. */
Mif_Complex_t s; /* The S-parameter. */
};
static void cleanup(ARGS, Mif_Callback_Reason_t reason)
{
struct data_pt *table;
switch (reason) {
case MIF_CB_DESTROY:
table = (struct data_pt *)STATIC_VAR(table);
if (table) {
free(table);
STATIC_VAR(table) = NULL;
}
break;
}
}
void cm_xfer(ARGS) /* structure holding parms, inputs, outputs, etc. */
{
struct data_pt *table;
Mif_Complex_t ac_gain;
double factor;
int span, size, i;
span = PARAM(span);
if (INIT) {
Mif_Boolean_t ri, db, rad;
int offset, bad = 0, j;
/* Validate table. */
offset = PARAM(offset);
size = PARAM_SIZE(table);
bad = size % span;
if (!bad) {
for (i = 0; i < size - span; i += span) {
if (PARAM(table[i]) < 0 ||
PARAM(table[i + span]) < PARAM(table[i])) {
bad = 1;
break;
}
}
}
if (bad) {
cm_message_send("Warning: badly formed table.");
return;
}
/* Allocate the internal table. */
size /= span;
table = (struct data_pt *)calloc(size, sizeof(struct data_pt));
STATIC_VAR(table) = table;
CALLBACK = cleanup;
/* Fill it. */
ri = PARAM(r_i);
db = PARAM(db);
rad = PARAM(rad);
for (i = 0, j = 0; i < size; i++, j += span) {
table[i].f = PARAM(table[j]) * 2.0 * PI;
if (ri) {
table[i].s.real = PARAM(table[j + offset]);
table[i].s.imag = PARAM(table[j + offset + 1]);
} else {
double phase, mag;
mag = PARAM(table[j + offset]);
if (db)
mag = pow(10, mag / 20);
phase = PARAM(table[j + offset + 1]);
if (!rad)
phase *= 2 * PI / 360;
table[i].s.real = mag * cos(phase);
table[i].s.imag = mag * sin(phase);
}
}
}
table = (struct data_pt *)STATIC_VAR(table);
if (!table)
return;
if (ANALYSIS == MIF_AC) {
double rv;
size = PARAM_SIZE(table) / span;
rv = RAD_FREQ;
if (rv <= table[0].f) {
ac_gain = table[0].s;
} else if (rv >= table[size - 1].f) {
ac_gain = table[size - 1].s;
} else {
for (i = 0; i < size; i++) {
if (table[i].f > rv)
break;
}
/* Linear interpolation. */
factor = (rv - table[i - 1].f) / (table[i].f - table[i - 1].f);
ac_gain.real = table[i - 1].s.real +
factor * (table[i].s.real - table[i - 1].s.real);
ac_gain.imag = table[i - 1].s.imag +
factor * (table[i].s.imag - table[i - 1].s.imag);
}
AC_GAIN(out, in) = ac_gain;
} else { /* DC, transient ... */
if (ANALYSIS == MIF_TRAN) {
if (!STATIC_VAR(warned)) {
STATIC_VAR(warned) = 1;
cm_message_send("The xfer code model does not support "
"transient analysis.");
}
}
OUTPUT(out) = table[0].s.real * INPUT(in);
}
}

91
src/xspice/icm/analog/xfer/ifspec.ifs

@ -0,0 +1,91 @@
/* Interface specification for PWL transfer function code model. */
NAME_TABLE:
Spice_Model_Name: xfer
C_Function_Name: cm_xfer
Description: "AC transfer function block"
PORT_TABLE:
Port_Name: in out
Description: "input" "output"
Direction: in out
Default_Type: v v
Allowed_Types: [v,vd,i,id] [v,vd,i,id]
Vector: no no
Vector_Bounds: - -
Null_Allowed: no no
PARAMETER_TABLE:
Parameter_Name: table
Description: "PWL table: frequency/magnitude/phase"
Data_Type: real
Default_Value: -
Limits: -
Vector: yes
Vector_Bounds: [3 -]
Null_Allowed: no
PARAMETER_TABLE:
Parameter_Name: r_i
Description: "table is in real/imaginary format"
Data_Type: boolean
Default_Value: false
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: db
Description: "table is in magnitude(dB)/phase format"
Data_Type: boolean
Default_Value: true
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: rad
Description: "phase in radians, not degrees"
Data_Type: boolean
Default_Value: false
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: span offset
Description: "Length of table rows" "Offset within row"
Data_Type: int int
Default_Value: 3 1
Limits: [ 3 - ] [ 1 - ]
Vector: no no
Vector_Bounds: - -
Null_Allowed: yes yes
/* This is used internally to store the table in compact complex form. */
STATIC_VAR_TABLE:
Static_Var_Name: table
Description: "Internal copy of data"
Data_Type: pointer
/* Only warn once about use in transient analysis. */
STATIC_VAR_TABLE:
Static_Var_Name: warned
Description: "Warning indicator"
Data_Type: int
Loading…
Cancel
Save