From bead3e3249017b09d31a2820045dfc81c0d3f2c8 Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Mon, 3 Jul 2023 19:19:03 +0100 Subject: [PATCH] Add an option for parameters to the xfer code model to be provided in a Touchstone-format file instead of as a model parameter. The change to mif_inp2.c allows the "table" parameter to be omitted. --- src/xspice/icm/analog/xfer/cfunc.mod | 314 +++++++++++++++++++++----- src/xspice/icm/analog/xfer/ifspec.ifs | 15 +- src/xspice/mif/mif_inp2.c | 4 - 3 files changed, 272 insertions(+), 61 deletions(-) diff --git a/src/xspice/icm/analog/xfer/cfunc.mod b/src/xspice/icm/analog/xfer/cfunc.mod index e57cbeaa8..6da543975 100644 --- a/src/xspice/icm/analog/xfer/cfunc.mod +++ b/src/xspice/icm/analog/xfer/cfunc.mod @@ -1,24 +1,32 @@ /* Transfer function block for AC simulation, based on s_xfer code model. */ #include +#include +#include +#include +#include #define PI 3.141592653589793238462643383279502884197 +#define ALLOC 1024 /* How the table information is stored internally. */ -struct data_pt { - double f; /* Frequency, radians/sec. */ - Mif_Complex_t s; /* The S-parameter. */ +struct data { + int size; /* Table size. */ + double *f; /* Frequency table, radians/sec. */ + Mif_Complex_t *s; /* The S-parameter table. */ }; static void cleanup(ARGS, Mif_Callback_Reason_t reason) { - struct data_pt *table; + struct data *table; switch (reason) { case MIF_CB_DESTROY: - table = (struct data_pt *)STATIC_VAR(table); + table = (struct data *)STATIC_VAR(table); if (table) { + free(table->f); + free(table->s); free(table); STATIC_VAR(table) = NULL; } @@ -26,94 +34,291 @@ static void cleanup(ARGS, Mif_Callback_Reason_t reason) } } +static double *read_file(const char *fn, int span, int offset, + int *size_p, Mif_Boolean_t *ri, Mif_Boolean_t *db) +{ + FILE *fp; + double *file_data; + double mult; + int i, j, line, size, count, skip, want; + char *cp, *word; + double vals[9]; + char buff[1024]; + + fp = fopen(fn, "r"); + if (fp == NULL) { + cm_message_printf("Can not open file %s: %s", fn, strerror(errno)); + return NULL; + } + + /* Find and examine the option line. */ + + line = 0; + while (fgets(buff, sizeof buff, fp)) { + ++line; + if (buff[0] == '#') + break; + } + if (buff[0] != '#') { + cm_message_printf("No option line found in file %s", fn); + fclose(fp); + return NULL; + } + mult = 1.0e9; // Default to GHz + for (cp = buff + 1; *cp; ++cp) { + while (isspace(*cp)) + ++cp; + if (*cp) { + word = cp; + while (*cp && !isspace(*cp)) + ++cp; + *cp++ = '\0'; + + if (!strcmp(word, "MHz")) + mult = 1.0e6; + else if (!strcmp(word, "KHz")) + mult = 1.0e3; + else if (!strcmp(word, "Hz")) + mult = 1.0; + else if (!strcmp(word, "DB")) + *db = MIF_TRUE; + else if (!strcmp(word, "RI")) + *ri = MIF_TRUE; + } + } + + /* Read the data: at most 9 values per line. */ + + size = ALLOC; + file_data = malloc(size * sizeof(double)); + if (!file_data) + goto bad; + want = skip = i = 0; + + while (fgets(buff, sizeof buff, fp)) { + ++line; + count = sscanf(buff, "%lg%lg%lg%lg%lg%lg%lg%lg%lg", + vals, vals + 1, vals + 2, vals + 3, vals + 4, + vals + 5, vals + 6, vals + 7, vals + 8); + if (!count) + continue; + if (span == 9 && count == 5) { + /* Special case: noise data in 2-port file. */ + + cm_message_printf("Ignoring noise parameters in file %s", fn); + break; + } + if (skip) { + if (count > skip) { + j = skip; + skip = 0; + } else { + skip -= count; + continue; + } + } else { + j = 0; + } + + /* Check allocation. */ + + if (i + 3 > size) { + size += ALLOC; + file_data = realloc(file_data, size * sizeof(double)); + if (!file_data) + goto bad; + } + + while (j < count) { + /* Store value. */ + + file_data[i++] = vals[j]; + switch (want) { + case 0: + /* Stored a frequency value. */ + + if (j != 0) + cm_message_printf("Warning: frequency not at start " + "of line %d", + line); + want = 2; + j += offset; + if (j >= count) + skip = j - count; + break; + case 1: + want = 0; + j += span - offset - 1; + if (j >= count) + skip = j - count; + break; + case 2: + j++; + want = 1; + skip = 0; + break; + } + } + } + + if (want || skip) + cm_message_send("Warning: unexpected end of file data."); + *size_p = i / 3; + bad: + fclose(fp); + return file_data; +} + void cm_xfer(ARGS) /* structure holding parms, inputs, outputs, etc. */ { - struct data_pt *table; + struct data *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. */ + Mif_Boolean_t ri, db, rad; + double *file_data; + int offset, bad = 0, j; 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; + span = PARAM(span); + if (offset < 1 || span < offset + 2) { + cm_message_send("Error: impossible span/offset."); + return; + } + + /* Table or File? */ + + if (PARAM(file) == NULL) { + /* Data given by "table" parameter. */ + + file_data = NULL; + size = PARAM_SIZE(table); + bad = size % span || size == 0; + + /* Validate table. */ + + 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; + if (bad) { + cm_message_send("Error: badly formed table."); + return; + } + size /= span; /* Size now in entries. */ + ri = PARAM(r_i); + db = PARAM(db); + rad = PARAM(rad); + } else { + /* Get data from a Touchstone file. */ + + if (!PARAM_NULL(table)) { + cm_message_send("Error: both file and table " + "parameters given."); + return; + } else { + db = MIF_FALSE; + ri = MIF_FALSE; + file_data = read_file(PARAM(file), span, offset, + &size, &ri, &db); + if (file_data == NULL) + return; + span = 3; + rad = MIF_FALSE; + + /* Explicit parameters override file. */ + + if (!PARAM_NULL(r_i)) + ri = PARAM(r_i); + if (!PARAM_NULL(db)) + db = PARAM(db); + if (!PARAM_NULL(rad)) + rad = PARAM(rad); + } } /* Allocate the internal table. */ - size /= span; - table = (struct data_pt *)calloc(size, sizeof(struct data_pt)); + table = (struct data *)malloc(sizeof(struct data)); + if (!table) + return; + table->size = size; + table->f = (double*)malloc(size * sizeof (double)); + if (!table->f) { + free(table); + return; + } + table->s = ( Mif_Complex_t *)malloc(size * sizeof (Mif_Complex_t)); + if (!table->s) { + free(table->f); + free(table); + return; + } 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; + double f, a, b; - mag = PARAM(table[j + offset]); + if (file_data) { + f = file_data[j]; + a = file_data[j + 1]; + b = file_data[j + 2]; + } else { + f = PARAM(table[j]); + a = PARAM(table[j + offset]); + b = PARAM(table[j + offset + 1]); + } + table->f[i] = f * 2.0 * PI; + if (ri) { + table->s[i].real = a; + table->s[i].imag = b; + } else { if (db) - mag = pow(10, mag / 20); - phase = PARAM(table[j + offset + 1]); + a = pow(10, a / 20); if (!rad) - phase *= 2 * PI / 360; - table[i].s.real = mag * cos(phase); - table[i].s.imag = mag * sin(phase); + b *= 2 * PI / 360; + table->s[i].real = a * cos(b); + table->s[i].imag = a * sin(b); } } + if (file_data) + free(file_data); } - - table = (struct data_pt *)STATIC_VAR(table); + table = (struct data *)STATIC_VAR(table); if (!table) return; if (ANALYSIS == MIF_AC) { double rv; - size = PARAM_SIZE(table) / span; + size = table->size; 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; + if (rv <= table->f[0]) { + ac_gain = table->s[0]; + } else if (rv >= table->f[size - 1]) { + ac_gain = table->s[size - 1]; } else { for (i = 0; i < size; i++) { - if (table[i].f > rv) + if (table->f[i] > 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); + factor = (rv - table->f[i - 1]) / (table->f[i] - table->f[i - 1]); + ac_gain.real = table->s[i - 1].real + + factor * (table->s[i].real - table->s[i - 1].real); + ac_gain.imag = table->s[i - 1].imag + + factor * (table->s[i].imag - table->s[i - 1].imag); } AC_GAIN(out, in) = ac_gain; } else { /* DC, transient ... */ @@ -124,7 +329,6 @@ void cm_xfer(ARGS) /* structure holding parms, inputs, outputs, etc. */ "transient analysis."); } } - OUTPUT(out) = table[0].s.real * INPUT(in); + OUTPUT(out) = table->s[0].real * INPUT(in); } } - diff --git a/src/xspice/icm/analog/xfer/ifspec.ifs b/src/xspice/icm/analog/xfer/ifspec.ifs index 41f2ac177..1caf58711 100644 --- a/src/xspice/icm/analog/xfer/ifspec.ifs +++ b/src/xspice/icm/analog/xfer/ifspec.ifs @@ -23,11 +23,22 @@ PARAMETER_TABLE: Parameter_Name: table Description: "PWL table: frequency/magnitude/phase" Data_Type: real -Default_Value: - +Default_Value: 0 Limits: - Vector: yes Vector_Bounds: [3 -] -Null_Allowed: no +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: file +Description: "File in Touchstone format" +Data_Type: string +Default_Value: - +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes PARAMETER_TABLE: diff --git a/src/xspice/mif/mif_inp2.c b/src/xspice/mif/mif_inp2.c index 41f606b9a..e09d8c378 100644 --- a/src/xspice/mif/mif_inp2.c +++ b/src/xspice/mif/mif_inp2.c @@ -579,10 +579,6 @@ MIF_INP2A ( tfree(emessage); gc_end(); return; - } else if((param_info->is_array) && (! param_info->has_conn_ref)) { - LITERR("Defaulted array parameter must have associated array connection"); - gc_end(); - return; } } if((! mdfast->param[i]->is_null) && (param_info->is_array)) {