From 047de0f2018c98ddd0515b41b2e5245f310bdd29 Mon Sep 17 00:00:00 2001 From: Vadim Kuznetsov Date: Thu, 22 May 2025 10:16:42 +0300 Subject: [PATCH] Add XSPICE models for transmission lines --- src/spinit.in | 1 + src/xspice/icm/GNUmakefile.in | 18 +- src/xspice/icm/tlines/cpline/cfunc.mod | 158 ++++++++ src/xspice/icm/tlines/cpline/ifspec.ifs | 176 +++++++++ src/xspice/icm/tlines/cpmlin/cfunc.mod | 496 ++++++++++++++++++++++++ src/xspice/icm/tlines/cpmlin/ifspec.ifs | 216 +++++++++++ src/xspice/icm/tlines/mlin/cfunc.mod | 133 +++++++ src/xspice/icm/tlines/mlin/ifspec.ifs | 173 +++++++++ src/xspice/icm/tlines/modpath.lst | 4 + src/xspice/icm/tlines/tline/cfunc.mod | 91 +++++ src/xspice/icm/tlines/tline/ifspec.ifs | 87 +++++ src/xspice/icm/tlines/udnpath.lst | 0 src/xspice/tlines/msline_common.c | 337 ++++++++++++++++ src/xspice/tlines/msline_common.h | 60 +++ src/xspice/tlines/tline_common.c | 86 ++++ src/xspice/tlines/tline_common.h | 63 +++ 16 files changed, 2095 insertions(+), 4 deletions(-) create mode 100644 src/xspice/icm/tlines/cpline/cfunc.mod create mode 100644 src/xspice/icm/tlines/cpline/ifspec.ifs create mode 100644 src/xspice/icm/tlines/cpmlin/cfunc.mod create mode 100644 src/xspice/icm/tlines/cpmlin/ifspec.ifs create mode 100644 src/xspice/icm/tlines/mlin/cfunc.mod create mode 100644 src/xspice/icm/tlines/mlin/ifspec.ifs create mode 100644 src/xspice/icm/tlines/modpath.lst create mode 100644 src/xspice/icm/tlines/tline/cfunc.mod create mode 100644 src/xspice/icm/tlines/tline/ifspec.ifs create mode 100644 src/xspice/icm/tlines/udnpath.lst create mode 100644 src/xspice/tlines/msline_common.c create mode 100644 src/xspice/tlines/msline_common.h create mode 100644 src/xspice/tlines/tline_common.c create mode 100644 src/xspice/tlines/tline_common.h diff --git a/src/spinit.in b/src/spinit.in index ee3794b65..669f6f444 100644 --- a/src/spinit.in +++ b/src/spinit.in @@ -25,6 +25,7 @@ if $?xspice_enabled @XSPICEINIT@ codemodel @pkglibdir@/xtradev.cm @XSPICEINIT@ codemodel @pkglibdir@/xtraevt.cm @XSPICEINIT@ codemodel @pkglibdir@/table.cm +@XSPICEINIT@ codemodel @pkglibdir@/tlines.cm end diff --git a/src/xspice/icm/GNUmakefile.in b/src/xspice/icm/GNUmakefile.in index e1344da26..d26ed30b8 100644 --- a/src/xspice/icm/GNUmakefile.in +++ b/src/xspice/icm/GNUmakefile.in @@ -7,11 +7,11 @@ include makedefs # The codemodels to make -CMDIRS = spice2poly digital analog xtradev xtraevt table +CMDIRS = spice2poly digital analog xtradev xtraevt table tlines #Invoke $(MAKE) for each of the CMDDIRS -all: dstring.o # One common dstring object file for all code modules +all: dstring.o msline_common.o tline_common.o# Common object files for all code modules for cm in $(CMDIRS) ; do \ $(MAKE) cm=$$cm $$cm/$$cm.cm \ || exit 1; \ @@ -36,6 +36,8 @@ uninstall: clean: rm -f dstring.o + rm -f msline_common.o + rm -f tline_common.o for cm in $(CMDIRS) ; do \ $(MAKE) cm=$$cm cm-clean \ || exit 1; \ @@ -49,6 +51,12 @@ NGSRCBUILDDIR = $(CURDIR)/../.. dstring.o: $(NGSRCDIR)/misc/dstring.c $(NGSRCDIR)/include/ngspice/dstring.h $(CC) $(CFLAGS) -I$(NGSRCDIR)/include -I$(NGSRCBUILDDIR)/include -fPIC -o $@ -c $< +msline_common.o: $(srcdir)/../tlines/msline_common.c $(srcdir)/../tlines/msline_common.h + $(CC) $(CFLAGS) -I$(srcdir)/../tlines -I$(NGSRCDIR)/include -I$(NGSRCBUILDDIR)/include -fPIC -o $@ -c $< + +tline_common.o: $(srcdir)/../tlines/tline_common.c $(srcdir)/../tlines/tline_common.h + $(CC) $(CFLAGS) -I$(srcdir)/../tlines -I$(NGSRCDIR)/include -I$(NGSRCBUILDDIR)/include -fPIC -o $@ -c $< + ifdef cm ifeq ($(OS),Windows_NT) @@ -70,6 +78,8 @@ cm-gens := \ cm-objs := \ $(cm)/dlmain.o \ dstring.o \ + tline_common.o \ + msline_common.o \ $(modlst:%=$(cm)/%/cfunc.o) \ $(modlst:%=$(cm)/%/ifspec.o) \ $(udnlst:%=$(cm)/%/udnfunc.o) @@ -160,11 +170,11 @@ $(cm)/dlmain.o : $(srcdir)/dlmain.c $(cm-descr) $(do-deps) $(cm)/%/cfunc.o : $(cm)/%/cfunc.c - $(COMPILE) $(gen_pp) -I$(srcdir)/$( +#include +#include +#include +#include + +#include "msline_common.h" +#include "tline_common.h" + + +static void copy_complex(double complex s, Complex_t *d) +{ + d->real = creal(s); + d->imag = cimag(s); +} + +cpline_state_t *sim_points = NULL; + + +void cm_cpline (ARGS) +{ + Complex_t z11, z12, z13, z14; + + /* how to get properties of this component, e.g. L, W */ + double l = PARAM(l); + double ze = PARAM(ze); + double zo = PARAM(zo); + double ere = PARAM(ere); + double ero = PARAM(ero); + double ae = PARAM(ae); + double ao = PARAM(ao); + ae = pow(10, 0.05*ae); + ao = pow(10, 0.05*ao); + + if(INIT) { + + } + + /* Compute the output */ + if(ANALYSIS == DC) { + + double V1 = INPUT(p1s); + double V2 = INPUT(p2s); + double V3 = INPUT(p3s); + double V4 = INPUT(p4s); + double I1 = INPUT(p1); + double I2 = INPUT(p2); + double I3 = INPUT(p3); + double I4 = INPUT(p4); + + double z = sqrt(ze*zo); + + double V2out = V1 + z*I1; + double V1out = V2 + z*I2; + OUTPUT(p1) = V1out + I1*z; + OUTPUT(p2) = V2out + I2*z; + + double V3out = V4 + z*I4; + double V4out = V3 + z*I3; + OUTPUT(p3) = V3out + I3*z; + OUTPUT(p4) = V4out + I4*z; + + cm_analog_auto_partial(); + } + else if(ANALYSIS == AC) { + double o = RAD_FREQ; + + double complex _Z11, _Z12, _Z13, _Z14; + double complex arg_e = log(ae)*l/2.0 + I*o*l/C0*sqrt(ere); + double complex arg_o = log(ao)*l/2.0 + I*o*l/C0*sqrt(ero); + + _Z11 = zo / (2*ctanh(arg_o)) + ze / (2*ctanh(arg_e)); + _Z12 = zo / (2*csinh(arg_o)) + ze / (2*csinh(arg_e)); + _Z13 = ze / (2*csinh(arg_e)) - zo / (2*csinh(arg_o)); + _Z14 = ze / (2*ctanh(arg_e)) - zo / (2*ctanh(arg_o)); + + copy_complex(_Z11,&z11); + copy_complex(_Z12,&z12); + copy_complex(_Z13,&z13); + copy_complex(_Z14,&z14); + + AC_GAIN(p1,p1) = z11; AC_GAIN(p2,p2) = z11; + AC_GAIN(p3,p3) = z11; AC_GAIN(p4,p4) = z11; + + AC_GAIN(p1,p2) = z12; AC_GAIN(p2,p1) = z12; + AC_GAIN(p3,p4) = z12; AC_GAIN(p4,p3) = z12; + + AC_GAIN(p1,p3) = z13; AC_GAIN(p3,p1) = z13; + AC_GAIN(p2,p4) = z13; AC_GAIN(p4,p2) = z13; + + AC_GAIN(p1,p4) = z14; AC_GAIN(p4,p1) = z14; + AC_GAIN(p2,p3) = z14; AC_GAIN(p3,p2) = z14; + } + else if(ANALYSIS == TRANSIENT) { + double t = TIME; + double Vp[PORT_NUM]; + double Ip[PORT_NUM]; + + Vp[0] = INPUT(p1s); + Vp[1] = INPUT(p2s); + Vp[2] = INPUT(p3s); + Vp[3] = INPUT(p4s); + Ip[0] = INPUT(p1); + Ip[1] = INPUT(p2); + Ip[2] = INPUT(p3); + Ip[3] = INPUT(p4); + double delay = l/(C0); + append_cpline_state(&sim_points, t, Vp, Ip, 1.2*delay); + if (t > delay) { + cpline_state_t *pp = find_cpline_state(sim_points, t - delay); + if (pp != NULL) { + + double J1e = 0.5*(Ip[3] + Ip[0]); + double J1o = 0.5*(Ip[0] - Ip[3]); + double J2e = 0.5*(Ip[1] + Ip[2]); + double J2o = 0.5*(Ip[1] - Ip[2]); + + + double J1et = 0.5*(pp->Ip[3] + pp->Ip[0]); + double J1ot = 0.5*(pp->Ip[0] - pp->Ip[3]); + double J2et = 0.5*(pp->Ip[1] + pp->Ip[2]); + double J2ot = 0.5*(pp->Ip[1] - pp->Ip[2]); + + + double V1et = 0.5*(pp->Vp[3] + pp->Vp[0]); + double V1ot = 0.5*(pp->Vp[0] - pp->Vp[3]); + double V2et = 0.5*(pp->Vp[1] + pp->Vp[2]); + double V2ot = 0.5*(pp->Vp[1] - pp->Vp[2]); + + double V1e = ze*J1e + V2et + ze*J2et; + double V1o = zo*J1o + V2ot + zo*J2ot; + double V2e = ze*J2e + V1et + ze*J1et; + double V2o = zo*J2o + V1ot + zo*J1ot; + + double V1 = V1o + V1e; + double V2 = V2o + V2e; + double V3 = V2e - V2o; + double V4 = V1e - V1o; + + OUTPUT(p1) = V1; + OUTPUT(p2) = V2; + OUTPUT(p3) = V3; + OUTPUT(p4) = V4; + } + cm_analog_auto_partial(); + } else { + cm_analog_auto_partial(); + } + } +} + diff --git a/src/xspice/icm/tlines/cpline/ifspec.ifs b/src/xspice/icm/tlines/cpline/ifspec.ifs new file mode 100644 index 000000000..aa5639010 --- /dev/null +++ b/src/xspice/icm/tlines/cpline/ifspec.ifs @@ -0,0 +1,176 @@ +/* =========================================================================== +FILE ifspec.ifs + +(c) vadim Kuznetsov 2025 + +=========================================================================== */ + +/* Ports connection + 4 --||||||-- 3 + + 1 --||||||-- 2 +*/ + +NAME_TABLE: + +Spice_Model_Name: cpline +C_Function_Name: cm_cpline +Description: "Generic transmission line" + + +PORT_TABLE: +Port_Name: p1 +Description: "Terminals Line1" +Direction: inout +Default_Type: hd +Allowed_Types: [hd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: p2 +Description: "Terminals Line1" +Direction: inout +Default_Type: hd +Allowed_Types: [hd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: p3 +Description: "Terminals Line2" +Direction: inout +Default_Type: hd +Allowed_Types: [hd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: p4 +Description: "Terminals Line2" +Direction: inout +Default_Type: hd +Allowed_Types: [hd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: p1s +Description: "Sensing terminals line 1" +Direction: in +Default_Type: vd +Allowed_Types: [vd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: p2s +Description: "Sensing terminals line 1" +Direction: in +Default_Type: vd +Allowed_Types: [vd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: p3s +Description: "Sensing terminals line 1" +Direction: in +Default_Type: vd +Allowed_Types: [vd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: p4s +Description: "Sensing terminals line 1" +Direction: in +Default_Type: vd +Allowed_Types: [vd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + + +PARAMETER_TABLE: +Parameter_Name: l +Description: "length" +Data_Type: real +Default_Value: 1.0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: ze +Description: "characteristic impedance of even mode" +Data_Type: real +Default_Value: 50.0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: zo +Description: "characteristic impedance of odd mode" +Data_Type: real +Default_Value: 50.0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + +PARAMETER_TABLE: +Parameter_Name: ae +Description: "attenuation per length (dB) even mode" +Data_Type: real +Default_Value: 0.0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + +PARAMETER_TABLE: +Parameter_Name: ao +Description: "attenuation per length (dB) odd mode" +Data_Type: real +Default_Value: 0.0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + +PARAMETER_TABLE: +Parameter_Name: ere +Description: "dielectric constant even mode" +Data_Type: real +Default_Value: 1.0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + +PARAMETER_TABLE: +Parameter_Name: ero +Description: "dielectric constant odd mode" +Data_Type: real +Default_Value: 1.0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + diff --git a/src/xspice/icm/tlines/cpmlin/cfunc.mod b/src/xspice/icm/tlines/cpmlin/cfunc.mod new file mode 100644 index 000000000..c1975dfec --- /dev/null +++ b/src/xspice/icm/tlines/cpmlin/cfunc.mod @@ -0,0 +1,496 @@ +/* =========================================================================== + FILE cfunc.mod + + (c) Vadim Kuznetsov 2025 + + */ + +#include +#include +#include +#include +#include + + +#include "msline_common.h" +#include "tline_common.h" + +static double ae, ao, be, bo, ze, zo, ee, eo; + +static void copy_complex(double complex s, Complex_t *d) +{ + d->real = creal(s); + d->imag = cimag(s); +} + + +static cpline_state_t *state = NULL; + +static void analyseQuasiStatic (double W, double h, double s, + double t, double er, + int SModel, double* Zle, + double* Zlo, double* ErEffe, + double* ErEffo); + +static void analyseDispersion (double W, double h, double s, + double t, double er, double Zle, + double Zlo, double ErEffe, + double ErEffo, double frequency, + int DModel, double *ZleFreq, + double *ZloFreq, + double *ErEffeFreq, + double *ErEffoFreq); + +static void calcPropagation (double W, double s, + double er, double h, double t, double tand, double rho, double D, + int SModel, int DModel, double frequency) +{ + + // quasi-static analysis + double Zle, ErEffe, Zlo, ErEffo; + analyseQuasiStatic (W, h, s, t, er, SModel, &Zle, &Zlo, &ErEffe, &ErEffo); + + // analyse dispersion of Zl and Er + double ZleFreq, ErEffeFreq, ZloFreq, ErEffoFreq; + analyseDispersion (W, h, s, t, er, Zle, Zlo, ErEffe, ErEffo, frequency, DModel, + &ZleFreq, &ZloFreq, &ErEffeFreq, &ErEffoFreq); + + // analyse losses of line + double ace, aco, ade, ado; + analyseLoss (W, t, er, rho, D, tand, Zle, Zlo, ErEffe, + frequency, HAMMERSTAD, &ace, &ade); + analyseLoss (W, t, er, rho, D, tand, Zlo, Zle, ErEffo, + frequency, HAMMERSTAD, &aco, &ado); + + // compute propagation constants for even and odd mode + double k0 = 2 * M_PI * frequency / C0; + ae = ace + ade; + ao = aco + ado; + be = sqrt (ErEffeFreq) * k0; + bo = sqrt (ErEffoFreq) * k0; + ze = ZleFreq; + zo = ZloFreq; + ee = ErEffeFreq; + eo = ErEffoFreq; +} + + + +/* The function calculates the quasi-static dielectric constants and + characteristic impedances for the even and odd mode based upon the + given line and substrate properties for parallel coupled microstrip + lines. */ +static void analyseQuasiStatic (double W, double h, double s, + double t, double er, + int SModel, double* Zle, + double* Zlo, double* ErEffe, + double* ErEffo) { + // initialize default return values + *ErEffe = er; *ErEffo = er; + *Zlo = 42.2; *Zle = 55.7; + + // normalized width and gap + double u = W / h; + double g = s / h; + + // HAMMERSTAD and JENSEN + if (SModel == HAMMERSTAD) { + double Zl1, Fe, Fo, a, b, fo, Mu, Alpha, Beta, ErEff; + double Pe, Po, r, fo1, q, p, n, Psi, Phi, m, Theta; + + // modifying equations for even mode + m = 0.2175 + pow (4.113 + pow (20.36 / g, 6.), -0.251) + + log (pow (g, 10.) / (1 + pow (g / 13.8, 10.))) / 323; + Alpha = 0.5 * exp (-g); + Psi = 1 + g / 1.45 + pow (g, 2.09) / 3.95; + Phi = 0.8645 * pow (u, 0.172); + Pe = Phi / (Psi * (Alpha * pow (u, m) + (1 - Alpha) * pow (u, -m))); + // TODO: is this ... Psi * (Alpha ... or ... Psi / (Alpha ... ? + + // modifying equations for odd mode + n = (1 / 17.7 + exp (-6.424 - 0.76 * log (g) - pow (g / 0.23, 5.))) * + log ((10 + 68.3 * sqr (g)) / (1 + 32.5 * pow (g, 3.093))); + Beta = 0.2306 + log (pow (g, 10.) / (1 + pow (g / 3.73, 10.))) / 301.8 + + log (1 + 0.646 * pow (g, 1.175)) / 5.3; + Theta = 1.729 + 1.175 * log (1 + 0.627 / (g + 0.327 * pow (g, 2.17))); + Po = Pe - Theta / Psi * exp (Beta * pow (u, -n) * log (u)); + + // further modifying equations + r = 1 + 0.15 * (1 - exp (1 - sqr (er - 1) / 8.2) / (1 + pow (g, -6.))); + fo1 = 1 - exp (-0.179 * pow (g, 0.15) - + 0.328 * pow (g, r) / log (M_E + pow (g / 7, 2.8))); + q = exp (-1.366 - g); + p = exp (-0.745 * pow (g, 0.295)) / cosh (pow (g, 0.68)); + fo = fo1 * exp (p * log (u) + q * sin (M_PI * log10 (u))); + + Mu = g * exp (-g) + u * (20 + sqr (g)) / (10 + sqr (g)); + Hammerstad_ab (Mu, er, &a, &b); + Fe = pow (1 + 10 / Mu, -a * b); + Hammerstad_ab (u, er, &a, &b); + Fo = fo * pow (1 + 10 / u, -a * b); + + // finally compute effective dielectric constants and impedances + *ErEffe = (er + 1) / 2 + (er - 1) / 2 * Fe; + *ErEffo = (er + 1) / 2 + (er - 1) / 2 * Fo; + + Hammerstad_er (u, er, a, b, &ErEff); // single microstrip + + // first variant + Zl1 = Z0 / (u + 1.98 * pow (u, 0.172)); + Zl1 /= sqrt (ErEff); + + // second variant + Hammerstad_zl (u, &Zl1); + Zl1 /= sqrt (ErEff); + + *Zle = Zl1 / (1 - Zl1 * Pe / Z0); + *Zlo = Zl1 / (1 - Zl1 * Po / Z0); + } + // KIRSCHNING and JANSEN + else if (SModel == KIRSCHING) { + double a, b, ae, be, ao, bo, v, co, d, ErEff, Zl1; + double q1, q2, q3, q4, q5, q6, q7, q8, q9, q10; + + // consider effect of finite strip thickness (JANSEN only) + double ue = u; + double uo = u; + if (t != 0 && s > 10 * (2 * t)) { + double dW = 0; + // SCHNEIDER, referred by JANSEN + if (u >= M_1_PI / 2 && M_1_PI / 2 > 2 * t / h) + dW = t * (1 + log (2 * h / t)) / M_PI; + else if (W > 2 * t) + dW = t * (1 + log (4 * M_PI * W / t)) / M_PI; + // JANSEN + double dt = 2 * t * h / s / er; + double We = W + dW * (1 - 0.5 * exp (-0.69 * dW / dt)); + double Wo = We + dt; + ue = We / h; + uo = Wo / h; + } + + // even relative dielectric constant + v = ue * (20 + sqr (g)) / (10 + sqr (g)) + g * exp (-g); + Hammerstad_ab (v, er, &ae, &be); + Hammerstad_er (v, er, ae, be, ErEffe); + + // odd relative dielectric constant + Hammerstad_ab (uo, er, &a, &b); + Hammerstad_er (uo, er, a, b, &ErEff); + d = 0.593 + 0.694 * exp (-0.562 * uo); + bo = 0.747 * er / (0.15 + er); + co = bo - (bo - 0.207) * exp (-0.414 * uo); + ao = 0.7287 * (ErEff - (er + 1) / 2) * (1 - exp (-0.179 * uo)); + *ErEffo = ((er + 1) / 2 + ao - ErEff) * exp (-co * pow (g, d)) + ErEff; + + // characteristic impedance of single line + Hammerstad_zl (u, &Zl1); + Zl1 /= sqrt (ErEff); + + // even characteristic impedance + q1 = 0.8695 * pow (ue, 0.194); + q2 = 1 + 0.7519 * g + 0.189 * pow (g, 2.31); + q3 = 0.1975 + pow (16.6 + pow (8.4 / g, 6.), -0.387) + + log (pow (g, 10.) / (1 + pow (g / 3.4, 10.))) / 241; + q4 = q1 / q2 * 2 / + (exp (-g) * pow (ue, q3) + (2 - exp (-g)) * pow (ue, -q3)); + *Zle = sqrt (ErEff / *ErEffe) * Zl1 / (1 - Zl1 * sqrt (ErEff) * q4 / Z0); + + // odd characteristic impedance + q5 = 1.794 + 1.14 * log (1 + 0.638 / (g + 0.517 * pow (g, 2.43))); + q6 = 0.2305 + log (pow (g, 10.) / (1 + pow (g / 5.8, 10.))) / 281.3 + + log (1 + 0.598 * pow (g, 1.154)) / 5.1; + q7 = (10 + 190 * sqr (g)) / (1 + 82.3 * cubic (g)); + q8 = exp (-6.5 - 0.95 * log (g) - pow (g / 0.15, 5.)); + q9 = log (q7) * (q8 + 1 / 16.5); + q10 = (q2 * q4 - q5 * exp (log (uo) * q6 * pow (uo, -q9))) / q2; + *Zlo = sqrt (ErEff / *ErEffo) * Zl1 / (1 - Zl1 * sqrt (ErEff) * q10 / Z0); + } +} + +/* The function computes the dispersion effects on the dielectric + constants and characteristic impedances for the even and odd mode + of parallel coupled microstrip lines. */ +static void analyseDispersion (double W, double h, double s, + double t, double er, double Zle, + double Zlo, double ErEffe, + double ErEffo, double frequency, + int DModel, double *ZleFreq, + double *ZloFreq, + double *ErEffeFreq, + double *ErEffoFreq) { + + // initialize default return values + *ZleFreq = Zle; + *ErEffeFreq = ErEffe; + *ZloFreq = Zlo; + *ErEffoFreq = ErEffo; + + // normalized width and gap + double u = W / h; + double g = s / h; + double ue, uo; + double B, dW, dt; + + // compute u_odd, u_even + if (t > 0.0) { + if (u < 0.1592) { + B = 2 * M_PI * W; + } else { + B = h; + } + dW = t * (1.0 + log(2 * B / t)) / M_PI; + dt = t / (er * g); + ue = (W + dW * (1.0 - 0.5 * exp( -0.69 * dW / dt ))) / h; + uo = ue + dt / h; + } else { + ue = u; + uo = u; + } + + // GETSINGER + if (DModel == GETSINGER) { + // even mode dispersion + Getsinger_disp (h, er, ErEffe, Zle / 2, + frequency, ErEffeFreq, ZleFreq); + *ZleFreq *= 2; + // odd mode dispersion + Getsinger_disp (h, er, ErEffo, Zlo * 2, + frequency, ErEffoFreq, ZloFreq); + *ZloFreq /= 2; + } + // KIRSCHNING and JANSEN + else if (DModel == DISP_KIRSCHING) { + double p1, p2, p3, p4, p5, p6, p7, Fe; + double fn = frequency * h * 1e-6; + + // even relative dielectric constant dispersion + p1 = 0.27488 * (0.6315 + 0.525 / pow (1 + 0.0157 * fn, 20.)) * ue - + 0.065683 * exp (-8.7513 * ue); + p2 = 0.33622 * (1 - exp (-0.03442 * er)); + p3 = 0.0363 * exp (-4.6 * ue) * (1 - exp (- pow (fn / 38.7, 4.97))); + p4 = 1 + 2.751 * (1 - exp (- pow (er / 15.916, 8.))); + p5 = 0.334 * exp (-3.3 * cubic (er / 15)) + 0.746; + p6 = p5 * exp (- pow (fn / 18, 0.368)); + p7 = 1 + 4.069 * p6 * pow (g, 0.479) * + exp (-1.347 * pow (g, 0.595) - 0.17 * pow (g, 2.5)); + Fe = p1 * p2 * pow ((p3 * p4 + 0.1844 * p7) * fn, 1.5763); + *ErEffeFreq = er - (er - ErEffe) / (1 + Fe); + + // odd relative dielectric constant dispersion + double p8, p9, p10, p11, p12, p13, p14, p15, Fo; + p1 = 0.27488 * (0.6315 + 0.525 / pow (1 + 0.0157 * fn, 20.)) * uo - + 0.065683 * exp (-8.7513 * uo); + p3 = 0.0363 * exp (-4.6 * uo) * (1 - exp (- pow (fn / 38.7, 4.97))); + p8 = 0.7168 * (1 + 1.076 / (1 + 0.0576 * (er - 1))); + p9 = p8 - 0.7913 * (1 - exp (- pow (fn / 20, 1.424))) * + atan (2.481 * pow (er / 8, 0.946)); + p10 = 0.242 * pow (er - 1, 0.55); + p11 = 0.6366 * (exp (-0.3401 * fn) - 1) * + atan (1.263 * pow (uo / 3, 1.629)); + p12 = p9 + (1 - p9) / (1 + 1.183 * pow (uo, 1.376)); + p13 = 1.695 * p10 / (0.414 + 1.605 * p10); + p14 = 0.8928 + 0.1072 * (1 - exp (-0.42 * pow (fn / 20, 3.215))); + p15 = fabs (1 - 0.8928 * (1 + p11) * + exp (-p13 * pow (g, 1.092)) * p12 / p14); + Fo = p1 * p2 * pow ((p3 * p4 + 0.1844) * fn * p15, 1.5763); + *ErEffoFreq = er - (er - ErEffo) / (1 + Fo); + + // dispersion of even characteristic impedance + double t, q11, q12, q13, q14, q15, q16, q17, q18, q19, q20, q21; + q11 = 0.893 * (1 - 0.3 / (1 + 0.7 * (er - 1))); + t = pow (fn / 20, 4.91); + q12 = 2.121 * t / (1 + q11 * t) * exp (-2.87 * g) * pow (g, 0.902); + q13 = 1 + 0.038 * pow (er / 8, 5.1); + t = quadr (er / 15); + q14 = 1 + 1.203 * t / (1 + t); + q15 = 1.887 * exp (-1.5 * pow (g, 0.84)) * pow (g, q14) / + (1 + 0.41 * pow (fn / 15, 3.) * + pow (u, 2 / q13) / (0.125 + pow (u, 1.626 / q13))); + q16 = q15 * (1 + 9 / (1 + 0.403 * sqr (er - 1))); + q17 = 0.394 * (1 - exp (-1.47 * pow (u / 7, 0.672))) * + (1 - exp (-4.25 * pow (fn / 20, 1.87))); + q18 = 0.61 * (1 - exp (-2.31 * pow (u / 8, 1.593))) / + (1 + 6.544 * pow (g, 4.17)); + q19 = 0.21 * quadr (g) / (1 + 0.18 * pow (g, 4.9)) / (1 + 0.1 * sqr (u)) / + (1 + pow (fn / 24, 3.)); + q20 = q19 * (0.09 + 1 / (1 + 0.1 * pow (er - 1, 2.7))); + t = pow (u, 2.5); + q21 = fabs (1 - 42.54 * pow (g, 0.133) * exp (-0.812 * g) * t / + (1 + 0.033 * t)); + + double re, qe, pe, de, Ce, q0, ZlFreq, ErEffFreq; + Kirschning_er (u, fn, er, ErEffe, &ErEffFreq); + Kirschning_zl (u, fn, er, ErEffe, ErEffFreq, Zle, &q0, &ZlFreq); + re = pow (fn / 28.843, 12.); + qe = 0.016 + pow (0.0514 * er * q21, 4.524); + pe = 4.766 * exp (-3.228 * pow (u, 0.641)); + t = pow (er - 1, 6.); + de = 5.086 * qe * re / (0.3838 + 0.386 * qe) * + exp (-22.2 * pow (u, 1.92)) / (1 + 1.2992 * re) * t / (1 + 10 * t); + Ce = 1 + 1.275 * (1 - exp (-0.004625 * pe * pow (er, 1.674) * + pow (fn / 18.365, 2.745))) - q12 + q16 - q17 + q18 + q20; + *ZleFreq = Zle * pow ((0.9408 * pow (ErEffFreq, Ce) - 0.9603) / + ((0.9408 - de) * pow (ErEffe, Ce) - 0.9603), q0); + + // dispersion of odd characteristic impedance + double q22, q23, q24, q25, q26, q27, q28, q29; + Kirschning_er (u, fn, er, ErEffo, &ErEffFreq); + Kirschning_zl (u, fn, er, ErEffo, ErEffFreq, Zlo, &q0, &ZlFreq); + q29 = 15.16 / (1 + 0.196 * sqr (er - 1)); + t = sqr (er - 1); + q25 = 0.3 * sqr (fn) / (10 + sqr (fn)) * (1 + 2.333 * t / (5 + t)); + t = pow ((er - 1) / 13, 12.); + q26 = 30 - 22.2 * t / (1 + 3 * t) - q29; + t = pow (er - 1, 1.5); + q27 = 0.4 * pow (g, 0.84) * (1 + 2.5 * t / (5 + t)); + t = pow (er - 1, 3.); + q28 = 0.149 * t / (94.5 + 0.038 * t); + q22 = 0.925 * pow (fn / q26, 1.536) / (1 + 0.3 * pow (fn / 30, 1.536)); + q23 = 1 + 0.005 * fn * q27 / (1 + 0.812 * pow (fn / 15, 1.9)) / + (1 + 0.025 * sqr (u)); + t = pow (u, 0.894); + q24 = 2.506 * q28 * t / (3.575 + t) * + pow ((1 + 1.3 * u) * fn / 99.25, 4.29); + *ZloFreq = ZlFreq + (Zlo * pow (*ErEffoFreq / ErEffo, q22) - ZlFreq * q23) / + (1 + q24 + pow (0.46 * g, 2.2) * q25); + + } +} + +void cm_cpmline (ARGS) +{ + Complex_t z11, z12, z13, z14; + + /* how to get properties of this component, e.g. L, W */ + double W = PARAM(w); + double l = PARAM(l); + double s = PARAM(s); + int SModel = PARAM(model); + int DModel = PARAM(disp); + + /* how to get properties of the substrate, e.g. Er, H */ + double er = PARAM(er); + double h = PARAM(h); + double t = PARAM(t); + double tand = PARAM(tand); + double rho = PARAM(rho); + double D = PARAM(d); + + + + /* Compute the output */ + if(ANALYSIS == DC) { + calcPropagation(W,s,er,h,t,tand,rho,D,SModel,DModel,0); + + double V1 = INPUT(p1s); + double V2 = INPUT(p2s); + double V3 = INPUT(p3s); + double V4 = INPUT(p4s); + double I1 = INPUT(p1); + double I2 = INPUT(p2); + double I3 = INPUT(p3); + double I4 = INPUT(p4); + + double z = sqrt(ze*zo); + + double V2out = V1 + z*I1; + double V1out = V2 + z*I2; + OUTPUT(p1) = V1out + I1*z; + OUTPUT(p2) = V2out + I2*z; + + double V3out = V4 + z*I4; + double V4out = V3 + z*I3; + OUTPUT(p3) = V3out + I3*z; + OUTPUT(p4) = V4out + I4*z; + + cm_analog_auto_partial(); + } + else if(ANALYSIS == AC) { + double o = RAD_FREQ; + calcPropagation(W,s,er,h,t,tand,rho,D,SModel,DModel, o/(2*M_PI)); + double complex _Z11, _Z12, _Z13, _Z14; + double complex ge = ae + I*be; + double complex go = ao + I*bo; + + _Z11 = zo / (2*ctanh(go*l)) + ze / (2*ctanh(ge*l)); + _Z12 = zo / (2*csinh(go*l)) + ze / (2*csinh(ge*l)); + _Z13 = ze / (2*csinh(ge*l)) - zo / (2*csinh(go*l)); + _Z14 = ze / (2*ctanh(ge*l)) - zo / (2*ctanh(go*l)); + + copy_complex(_Z11,&z11); + copy_complex(_Z12,&z12); + copy_complex(_Z13,&z13); + copy_complex(_Z14,&z14); + + AC_GAIN(p1,p1) = z11; AC_GAIN(p2,p2) = z11; + AC_GAIN(p3,p3) = z11; AC_GAIN(p4,p4) = z11; + + AC_GAIN(p1,p2) = z12; AC_GAIN(p2,p1) = z12; + AC_GAIN(p3,p4) = z12; AC_GAIN(p4,p3) = z12; + + AC_GAIN(p1,p3) = z13; AC_GAIN(p3,p1) = z13; + AC_GAIN(p2,p4) = z13; AC_GAIN(p4,p2) = z13; + + AC_GAIN(p1,p4) = z14; AC_GAIN(p4,p1) = z14; + AC_GAIN(p2,p3) = z14; AC_GAIN(p3,p2) = z14; + } + else if(ANALYSIS == TRANSIENT) { + calcPropagation(W,s,er,h,t,tand,rho,D,SModel,DModel,0); + double t = TIME; + double Vp[PORT_NUM]; + double Ip[PORT_NUM]; + double Vnew[PORT_NUM]; + Vp[0] = INPUT(p1s); + Vp[1] = INPUT(p2s); + Vp[2] = INPUT(p3s); + Vp[3] = INPUT(p4s); + Ip[0] = INPUT(p1); + Ip[1] = INPUT(p2); + Ip[2] = INPUT(p3); + Ip[3] = INPUT(p4); + double delay = l/(C0); + append_cpline_state(&state, t, Vp, Ip, 1.2*delay); + if (t > delay) { + cpline_state_t *pp = find_cpline_state(state, t - delay); + if (pp != NULL) { + + double J1e = 0.5*(Ip[3] + Ip[0]); + double J1o = 0.5*(Ip[0] - Ip[3]); + double J2e = 0.5*(Ip[1] + Ip[2]); + double J2o = 0.5*(Ip[1] - Ip[2]); + + + double J1et = 0.5*(pp->Ip[3] + pp->Ip[0]); + double J1ot = 0.5*(pp->Ip[0] - pp->Ip[3]); + double J2et = 0.5*(pp->Ip[1] + pp->Ip[2]); + double J2ot = 0.5*(pp->Ip[1] - pp->Ip[2]); + + + double V1et = 0.5*(pp->Vp[3] + pp->Vp[0]); + double V1ot = 0.5*(pp->Vp[0] - pp->Vp[3]); + double V2et = 0.5*(pp->Vp[1] + pp->Vp[2]); + double V2ot = 0.5*(pp->Vp[1] - pp->Vp[2]); + + double V1e = ze*J1e + V2et + ze*J2et; + double V1o = zo*J1o + V2ot + zo*J2ot; + double V2e = ze*J2e + V1et + ze*J1et; + double V2o = zo*J2o + V1ot + zo*J1ot; + + double V1 = V1o + V1e; + double V2 = V2o + V2e; + double V3 = V2e - V2o; + double V4 = V1e - V1o; + + OUTPUT(p1) = V1; + OUTPUT(p2) = V2; + OUTPUT(p3) = V3; + OUTPUT(p4) = V4; + } + cm_analog_auto_partial(); + } else { + cm_analog_auto_partial(); + } + } +} + diff --git a/src/xspice/icm/tlines/cpmlin/ifspec.ifs b/src/xspice/icm/tlines/cpmlin/ifspec.ifs new file mode 100644 index 000000000..04a58eb67 --- /dev/null +++ b/src/xspice/icm/tlines/cpmlin/ifspec.ifs @@ -0,0 +1,216 @@ +/* =========================================================================== +FILE ifspec.ifs + +(c) vadim Kuznetsov 2025 + +=========================================================================== */ + +/* Ports connection + 4 --||||||-- 3 + + 1 --||||||-- 2 +*/ + +NAME_TABLE: + +Spice_Model_Name: cpmlin +C_Function_Name: cm_cpmline +Description: "Generic transmission line" + + +PORT_TABLE: +Port_Name: p1 +Description: "Terminals Line1" +Direction: inout +Default_Type: hd +Allowed_Types: [hd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: p2 +Description: "Terminals Line1" +Direction: inout +Default_Type: hd +Allowed_Types: [hd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: p3 +Description: "Terminals Line2" +Direction: inout +Default_Type: hd +Allowed_Types: [hd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: p4 +Description: "Terminals Line2" +Direction: inout +Default_Type: hd +Allowed_Types: [hd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: p1s +Description: "Sensing terminals line 1" +Direction: in +Default_Type: vd +Allowed_Types: [vd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: p2s +Description: "Sensing terminals line 1" +Direction: in +Default_Type: vd +Allowed_Types: [vd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: p3s +Description: "Sensing terminals line 1" +Direction: in +Default_Type: vd +Allowed_Types: [vd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: p4s +Description: "Sensing terminals line 1" +Direction: in +Default_Type: vd +Allowed_Types: [vd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + + +PARAMETER_TABLE: +Parameter_Name: l +Description: "length" +Data_Type: real +Default_Value: 1.0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: w +Description: "width" +Data_Type: real +Default_Value: 1e-3 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: s +Description: "gap" +Data_Type: real +Default_Value: 1e-3 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + + +PARAMETER_TABLE: +Parameter_Name: model +Description: "Model type" +Data_Type: int +Default_Value: 0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + +PARAMETER_TABLE: +Parameter_Name: disp +Description: "Dispersion type" +Data_Type: int +Default_Value: 0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: er +Description: "Substrate dielectric permittivity" +Data_Type: real +Default_Value: 9.8 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: h +Description: "Substrate thickness" +Data_Type: real +Default_Value: 1e-3 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: t +Description: "Metal strip thickness" +Data_Type: real +Default_Value: 35e-6 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: tand +Description: "Substrate dielectric loss" +Data_Type: real +Default_Value: 2e-4 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + +PARAMETER_TABLE: +Parameter_Name: rho +Description: "Metal resistance" +Data_Type: real +Default_Value: 0.022e-6 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: d +Description: "RMS Substrate roughness" +Data_Type: real +Default_Value: 0.15e-6 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + diff --git a/src/xspice/icm/tlines/mlin/cfunc.mod b/src/xspice/icm/tlines/mlin/cfunc.mod new file mode 100644 index 000000000..7aea193ab --- /dev/null +++ b/src/xspice/icm/tlines/mlin/cfunc.mod @@ -0,0 +1,133 @@ +/* =========================================================================== + FILE cfunc.mod + + (c) Vadim Kuznetsov 2025 + + */ + +#include +#include +#include + +#include "tline_common.h" + +#include "msline_common.h" + +static tline_state_t *sim_points = NULL; + +static double zl, alpha, beta, ereff; + +static void calcPropagation (double W, int SModel, int DModel, + double er, double h, double t, double tand, double rho, double D, + double frequency) { + + /* local variables */ + double ac, ad; + double ZlEff, ErEff, WEff, ZlEffFreq, ErEffFreq; + + // quasi-static effective dielectric constant of substrate + line and + // the impedance of the microstrip line + mslineAnalyseQuasiStatic (W, h, t, er, SModel, &ZlEff, &ErEff, &WEff); + + // analyse dispersion of Zl and Er (use WEff here?) + mslineAnalyseDispersion (W, h, er, ZlEff, ErEff, frequency, DModel, + &ZlEffFreq, &ErEffFreq); + + // analyse losses of line + analyseLoss (W, t, er, rho, D, tand, ZlEff, ZlEff, ErEff, + frequency, HAMMERSTAD, &ac, &ad); + + // calculate propagation constants and reference impedance + zl = ZlEffFreq; + ereff = ErEffFreq; + alpha = ac + ad; + beta = sqrt (ErEffFreq) * 2 * M_PI * frequency / C0; +} + +void cm_mlin (ARGS) +{ + Complex_t z11, z21; + + + /* how to get properties of this component, e.g. L, W */ + double W = PARAM(w); + double l = PARAM(l); + int SModel = PARAM(model); + int DModel = PARAM(disp); + int TModel = PARAM(tranmodel); + + /* how to get properties of the substrate, e.g. Er, H */ + double er = PARAM(er); + double h = PARAM(h); + double t = PARAM(t); + double tand = PARAM(tand); + double rho = PARAM(rho); + double D = PARAM(d); + + + + /* Initialize/access instance specific storage for capacitor voltage */ + if(INIT) { + + } + + /* Compute the output */ + if(ANALYSIS == DC) { + + calcPropagation(W,SModel,DModel,er,h,t,tand,rho,D,0); + double V1 = INPUT(V1sens); + double V2 = INPUT(V2sens); + double I1 = INPUT(port1); + double I2 = INPUT(port2); + double V2out = V1 + zl*I1; + double V1out = V2 + zl*I2; + OUTPUT(port1) = V1out + I1*zl; + OUTPUT(port2) = V2out + I2*zl; + + cm_analog_auto_partial(); + } + else if(ANALYSIS == AC) { + double frequency = RAD_FREQ/(2.0*M_PI); + calcPropagation(W,SModel,DModel,er,h,t,tand,rho,D,frequency); + + double complex g = alpha + beta*I; + double complex _Z11 = zl / ctanh(g*l); + double complex _Z21 = zl / csinh(g*l); + + z11.real = creal(_Z11); z11.imag = cimag(_Z11); + z21.real = creal(_Z21); z21.imag = cimag(_Z21); + + AC_GAIN(port1,port1) = z11; AC_GAIN(port2,port2) = z11; + AC_GAIN(port1,port2) = z21; AC_GAIN(port2,port1) = z21; + } + else if(ANALYSIS == TRANSIENT) { + calcPropagation(W,SModel,DModel,er,h,t,tand,rho,D,0); + double t = TIME; + double V1 = INPUT(V1sens); + double V2 = INPUT(V2sens); + double I1 = INPUT(port1); + double I2 = INPUT(port2); + double delay = l/(C0) * sqrt(ereff); + if (TModel == TRAN_FULL) { + append_state(&sim_points, t, V1, V2, I1, I2, 1.2*delay); + } + if (t > delay && TModel == TRAN_FULL) { + tline_state_t *pp = get_state(sim_points, t - delay); + if (pp != NULL) { + double V2out = pp->V1 + zl*(pp->I1); + double V1out = pp->V2 + zl*(pp->I2); + OUTPUT(port1) = V1out + I1*zl; + OUTPUT(port2) = V2out + I2*zl; + } + cm_analog_auto_partial(); + } else { + double V2out = V1 + zl*I1; + double V1out = V2 + zl*I2; + OUTPUT(port1) = V1out + I1*zl; + OUTPUT(port2) = V2out + I2*zl; + cm_analog_auto_partial(); + } + + } +} + diff --git a/src/xspice/icm/tlines/mlin/ifspec.ifs b/src/xspice/icm/tlines/mlin/ifspec.ifs new file mode 100644 index 000000000..1e3404e69 --- /dev/null +++ b/src/xspice/icm/tlines/mlin/ifspec.ifs @@ -0,0 +1,173 @@ +/* =========================================================================== +FILE ifspec.ifs + +(c) vadim Kuznetsov 2025 + +=========================================================================== */ + +NAME_TABLE: + +Spice_Model_Name: mlin +C_Function_Name: cm_mlin +Description: "Microstrip line" + + +PORT_TABLE: +Port_Name: port1 +Description: "Microstrip terminals" +Direction: inout +Default_Type: hd +Allowed_Types: [hd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: port2 +Description: "Microstrip terminals" +Direction: inout +Default_Type: hd +Allowed_Types: [hd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: V1sens +Description: "Sensing terminals" +Direction: in +Default_Type: vd +Allowed_Types: [vd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: V2sens +Description: "Sensisng terminals" +Direction: in +Default_Type: vd +Allowed_Types: [vd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + + +PARAMETER_TABLE: +Parameter_Name: l +Description: "length" +Data_Type: real +Default_Value: 1e-2 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + +PARAMETER_TABLE: +Parameter_Name: w +Description: "width" +Data_Type: real +Default_Value: 1e-3 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + +PARAMETER_TABLE: +Parameter_Name: model +Description: "Model type" +Data_Type: int +Default_Value: 0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + +PARAMETER_TABLE: +Parameter_Name: disp +Description: "Dispersion type" +Data_Type: int +Default_Value: 0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: er +Description: "Substrate dielectric permittivity" +Data_Type: real +Default_Value: 9.8 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: h +Description: "Substrate thickness" +Data_Type: real +Default_Value: 1e-3 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: t +Description: "Metal strip thickness" +Data_Type: real +Default_Value: 35e-6 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: tand +Description: "Substrate dielectric loss" +Data_Type: real +Default_Value: 2e-4 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + +PARAMETER_TABLE: +Parameter_Name: rho +Description: "Metal resistance" +Data_Type: real +Default_Value: 0.022e-6 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: d +Description: "RMS Substrate roughness" +Data_Type: real +Default_Value: 0.15e-6 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + +PARAMETER_TABLE: +Parameter_Name: tranmodel +Description: "RMS Substrate roughness" +Data_Type: int +Default_Value: 0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + + + diff --git a/src/xspice/icm/tlines/modpath.lst b/src/xspice/icm/tlines/modpath.lst new file mode 100644 index 000000000..a7ce53ef9 --- /dev/null +++ b/src/xspice/icm/tlines/modpath.lst @@ -0,0 +1,4 @@ +mlin +tline +cpline +cpmlin diff --git a/src/xspice/icm/tlines/tline/cfunc.mod b/src/xspice/icm/tlines/tline/cfunc.mod new file mode 100644 index 000000000..61ec1f904 --- /dev/null +++ b/src/xspice/icm/tlines/tline/cfunc.mod @@ -0,0 +1,91 @@ +/* =========================================================================== + FILE cfunc.mod + + (c) Vadim Kuznetsov 2025 + + */ + +#include +#include +#include + +#include "msline_common.h" +#include "tline_common.h" + +static tline_state_t *sim_points = NULL; + + +void cm_tline (ARGS) +{ + Complex_t z11, z21; + + + /* how to get properties of this component, e.g. L, W */ + double z = PARAM(z); + double l = PARAM(l); + double a = PARAM(a); + + double alpha = pow(10,0.05*a); + alpha = log(alpha)/2.0; + + /* Initialize/access instance specific storage for capacitor voltage */ + if(INIT) { + + } + + /* Compute the output */ + if(ANALYSIS == DC) { + + double V1 = INPUT(V1sens); + double V2 = INPUT(V2sens); + double I1 = INPUT(in); + double I2 = INPUT(out); + double V2out = V1 + z*I1; + double V1out = V2 + z*I2; + OUTPUT(in) = V1out + I1*z; + OUTPUT(out) = V2out + I2*z; + + cm_analog_auto_partial(); + } + else if(ANALYSIS == AC) { + double beta = RAD_FREQ/C0; + double complex g = alpha + beta*I; + double complex _Z11 = z / ctanh(g*l); + double complex _Z21 = z / csinh (g*l); + + z11.real = creal(_Z11); + z11.imag = cimag(_Z11); + z21.real = creal(_Z21); + z21.imag = cimag(_Z21); + + + AC_GAIN(in, in) = z11; AC_GAIN(out,out) = z11; + AC_GAIN(in,out) = z21; AC_GAIN(out,in) = z21; + } + else if(ANALYSIS == TRANSIENT) { + double t = TIME; + double V1 = INPUT(V1sens); + double V2 = INPUT(V2sens); + double I1 = INPUT(in); + double I2 = INPUT(out); + double delay = l/(C0); + append_state(&sim_points, t, V1, V2, I1, I2, 1.2*delay); + if (t > delay) { + tline_state_t *pp = get_state(sim_points, t - delay); + if (pp != NULL) { + double V2out = pp->V1 + z*(pp->I1); + double V1out = pp->V2 + z*(pp->I2); + OUTPUT(in) = V1out + I1*z; + OUTPUT(out) = V2out + I2*z; + } + cm_analog_auto_partial(); + } else { + double V2out = V1 + z*I1; + double V1out = V2 + z*I2; + OUTPUT(in) = V1out + I1*z; + OUTPUT(out) = V2out + I2*z; + cm_analog_auto_partial(); + } + } +} + diff --git a/src/xspice/icm/tlines/tline/ifspec.ifs b/src/xspice/icm/tlines/tline/ifspec.ifs new file mode 100644 index 000000000..b3caa7491 --- /dev/null +++ b/src/xspice/icm/tlines/tline/ifspec.ifs @@ -0,0 +1,87 @@ +/* =========================================================================== +FILE ifspec.ifs + +(c) vadim Kuznetsov 2025 + +=========================================================================== */ + +NAME_TABLE: + +Spice_Model_Name: tline +C_Function_Name: cm_tline +Description: "Generic transmission line" + + +PORT_TABLE: +Port_Name: in +Description: "Terminals" +Direction: inout +Default_Type: hd +Allowed_Types: [hd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: out +Description: "Terminals" +Direction: inout +Default_Type: hd +Allowed_Types: [hd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: V1sens +Description: "Sensing terminals" +Direction: in +Default_Type: vd +Allowed_Types: [vd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + +PORT_TABLE: +Port_Name: V2sens +Description: "Sensisng terminals" +Direction: in +Default_Type: vd +Allowed_Types: [vd] +Vector: no +Vector_Bounds: - +Null_Allowed: no + + + +PARAMETER_TABLE: +Parameter_Name: l +Description: "length" +Data_Type: real +Default_Value: 1.0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: z +Description: "characteristic impedance" +Data_Type: real +Default_Value: 50.0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: +Parameter_Name: a +Description: "attenuation per length (dB)" +Data_Type: real +Default_Value: 0.0 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + diff --git a/src/xspice/icm/tlines/udnpath.lst b/src/xspice/icm/tlines/udnpath.lst new file mode 100644 index 000000000..e69de29bb diff --git a/src/xspice/tlines/msline_common.c b/src/xspice/tlines/msline_common.c new file mode 100644 index 000000000..0cac78357 --- /dev/null +++ b/src/xspice/tlines/msline_common.c @@ -0,0 +1,337 @@ +/* msline_common.c + * common definitions for microstrip devices + * (c) Vadim Kuznetsov 2025 + */ + +#include +#include + +#include "tline_common.h" +#include "msline_common.h" + +/* This function calculates the quasi-static impedance of a microstrip + * line, the value of the effective dielectric constant and the + * effective width due to the finite conductor thickness for the given + * microstrip line and substrate properties. */ +void mslineAnalyseQuasiStatic (double W, double h, double t, + double er, int Model, + double *ZlEff, double *ErEff, + double *WEff) { + + double z, e; + + // default values + e = er; + z = z0; + *WEff = W; + + // WHEELER + if (Model == WHEELER) { + double a, b, c, d, x, dW1, dWr, Wr; + + // compute strip thickness effect + if (t != 0) { + dW1 = t / M_PI * log (4 * M_E / sqrt (sqr (t / h) + + sqr (M_1_PI / (W / t + 1.10)))); + } + else dW1 = 0; + dWr = (1 + 1 / er) / 2 * dW1; + Wr = W + dWr; *WEff =Wr; + + // compute characteristic impedance + if (W / h < 3.3) { + c = log (4 * h / Wr + sqrt (sqr (4 * h / Wr) + 2)); + b = (er - 1) / (er + 1) / 2 * (log (M_PI_2) + log (2 * M_2_PI) / er); + z = (c - b) * Z0 / M_PI / sqrt (2 * (er + 1)); + } + else { + c = 1 + log (M_PI_2) + log (Wr / h / 2 + 0.94); + d = M_1_PI / 2 * (1 + log (sqr (M_PI) / 16)) * (er - 1) / sqr (er); + x = 2 * M_LN2 / M_PI + Wr / h / 2 + (er + 1) / 2 / M_PI / er * c + d; + z = Z0 / 2 / x / sqrt (er); + } + + // compute effective dielectric constant + if (W / h < 1.3) { + a = log (8 * h / Wr) + sqr (Wr / h) / 32; + b = (er - 1) / (er + 1) / 2 * (log (M_PI_2) + log (2 * M_2_PI) / er); + e = (er + 1) / 2 * sqr (a / (a - b)); + } + else { + a = (er - 1) / 2 / M_PI / er * (log (2.1349 * Wr / h + 4.0137) - + 0.5169 / er); + b = Wr / h / 2 + M_1_PI * log (8.5397 * Wr / h + 16.0547); + e = er * sqr ((b - a) / b); + } + } + // SCHNEIDER + else if (Model == SCHNEIDER) { + + double dW = 0, u = W / h; + + // consider strip thickness equations + if (t != 0 && t < W / 2) { + double arg = (u < M_1_PI / 2) ? 2 * M_PI * W / t : h / t; + dW = t / M_PI * (1 + log (2 * arg)); + if (t / dW >= 0.75) dW = 0; + } + *WEff = W + dW; u = *WEff / h; + + // effective dielectric constant + e = (er + 1) / 2 + (er - 1) / 2 / sqrt (1 + 10 / u); + + // characteristic impedance + if (u < 1.0) { + z = M_1_PI / 2 * log (8 / u + u / 4); + } + else { + z = 1 / (u + 2.42 - 0.44 / u + pow ((1. - 1. / u), 6.)); + } + z = Z0 * z / sqrt (e); + } + // HAMMERSTAD and JENSEN + else if (Model == HAMMERSTAD) { + double a, b, du1, du, u, ur, u1, zr, z1; + + u = W / h; // normalized width + t = t / h; // normalized thickness + + // compute strip thickness effect + if (t != 0) { + du1 = t / M_PI * log (1 + 4 * M_E / t / sqr (coth (sqrt (6.517 * u)))); + } + else du1 = 0; + du = du1 * (1 + sech (sqrt (er - 1))) / 2; + u1 = u + du1; + ur = u + du; + *WEff = ur * h; + + // compute impedances for homogeneous medium + Hammerstad_zl (ur, &zr); + Hammerstad_zl (u1, &z1); + + // compute effective dielectric constant + Hammerstad_ab (ur, er, &a, &b); + Hammerstad_er (ur, er, a, b, &e); + + // compute final characteristic impedance and dielectric constant + // including strip thickness effects + z = zr / sqrt (e); + e = e * sqr (z1 / zr); + } + + *ZlEff = z; + *ErEff = e; +} + +/* This function calculates the frequency dependent value of the + * effective dielectric constant and the microstrip line impedance for + * the given frequency. */ +void mslineAnalyseDispersion (double W, double h, double er, + double ZlEff, double ErEff, + double frequency, int Model, + double* ZlEffFreq, + double* ErEffFreq) { + + double e, z; + + // default values + z = *ZlEffFreq = ZlEff; + e = *ErEffFreq = ErEff; + + // GETSINGER + if (Model == GETSINGER) { + Getsinger_disp (h, er, ErEff, ZlEff, frequency, &e, &z); + } + // SCHNEIDER + else if (Model == DISP_SCHNEIDER) { + double k, f; + k = sqrt (ErEff / er); + f = 4 * h * frequency / C0 * sqrt (er - 1); + f = sqr (f); + e = ErEff * sqr ((1 + f) / (1 + k * f)); + z = ZlEff * sqrt (ErEff / e); + } + // YAMASHITA + else if (Model == YAMASHITA) { + double k, f; + k = sqrt (er / ErEff); + f = 4 * h * frequency / C0 * sqrt (er - 1) * + (0.5 + sqr (1 + 2 * log10 (1 + W / h))); + e = ErEff * sqr ((1 + k * pow (f, 1.5) / 4) / (1 + pow (f, 1.5) / 4)); + } + // KOBAYASHI + else if (Model == KOBAYASHI) { + double n, no, nc, fh, fk; + fk = C0 * atan (er * sqrt ((ErEff - 1) / (er - ErEff))) / + (2 * M_PI * h * sqrt (er - ErEff)); + fh = fk / (0.75 + (0.75 - 0.332 / pow (er, 1.73)) * W / h); + no = 1 + 1 / (1 + sqrt (W / h)) + 0.32 * cubic (1 / (1 + sqrt (W / h))); + if (W / h < 0.7) { + nc = 1 + 1.4 / (1 + W / h) * (0.15 - 0.235 * + exp (-0.45 * frequency / fh)); + } + else nc = 1; + n = no * nc < 2.32 ? no * nc : 2.32; + e = er - (er - ErEff) / (1 + pow (frequency / fh, n)); + } + // PRAMANICK and BHARTIA + else if (Model == PRAMANICK) { + double Weff, We, f; + f = 2 * MU0 * h * frequency * sqrt (ErEff / er) / ZlEff; + e = er - (er - ErEff) / (1 + sqr (f)); + Weff = Z0 * h / ZlEff / sqrt (ErEff); + We = W + (Weff - W) / (1 + sqr (f)); + z = Z0 * h / We / sqrt (e); + } + // HAMMERSTAD and JENSEN + else if (Model == DISP_HAMMERSTAD) { + double f, g; + g = sqr (M_PI) / 12 * (er - 1) / ErEff * sqrt (2 * M_PI * ZlEff / Z0); + f = 2 * MU0 * h * frequency / ZlEff; + e = er - (er - ErEff) / (1 + g * sqr (f)); + z = ZlEff * sqrt (ErEff / e) * (e - 1) / (ErEff - 1); + } + // KIRSCHNING and JANSEN + else if (Model == DISP_KIRSCHING) { + double r17, u = W / h, fn = frequency * h / 1e6; + + // dispersion of dielectric constant + Kirschning_er (u, fn, er, ErEff, &e); + + // dispersion of characteristic impedance + Kirschning_zl (u, fn, er, ErEff, e, ZlEff, &r17, &z); + } + + *ZlEffFreq = z; + *ErEffFreq = e; +} + + +/* Computes the exponent factors a(u) and b(er) used within the + * effective relative dielectric constant calculations for single and + * coupled microstrip lines by Hammerstad and Jensen. */ +void Hammerstad_ab (double u, double er, double *a, + double *b) { + *a = 1 + log ((quadr (u) + sqr (u / 52)) / (quadr (u) + 0.432)) / 49 + + log (1 + cubic (u / 18.1)) / 18.7; + *b = 0.564 * pow ((er - 0.9) / (er + 3), 0.053); +} + +/* The function computes the effective dielectric constant of a single + * microstrip. The equation is used in single and coupled microstrip + * calculations. */ +void Hammerstad_er (double u, double er, double a, + double b, double* e) { + *e = (er + 1) / 2 + (er - 1) / 2 * pow (1 + 10 / u, -a * b); +} + +/* This function computes the characteristic impedance of single + * microstrip line based upon the given width-height ratio. The + * equation is used in single and coupled microstrip calculations as + * well. */ +void Hammerstad_zl (double u, double *zl) { + double fu = 6 + (2 * M_PI - 6) * exp (- pow (30.666 / u, 0.7528)); + *zl = Z0 / 2 / M_PI * log (fu / u + sqrt (1 + sqr (2 / u))); +} + +/* Calculates dispersion effects for effective dielectric constant and + * characteristic impedance as defined by Getsinger (for single and + * coupled microstrips). */ +void Getsinger_disp (double h, double er, double ErEff, + double ZlEff, double frequency, + double *e, double *z) { + double g, f, d; + g = 0.6 + 0.009 * ZlEff; + f = frequency * 2 * MU0 * h / ZlEff; + *e = er - (er - ErEff) / (1 + g * sqr (f)); + d = (er - *e) * (*e - ErEff) / *e / (er - ErEff); + *z = ZlEff * sqrt (*e / ErEff) / (1 + d); // group delay model +} + +/* This function computes the dispersion of the effective dielectric + * constant of a single microstrip line. It is defined in a separate + * function because it is used within the coupled microstrip lines as + * well. */ +void Kirschning_er (double u, double fn, double er, + double ErEff, double* ErEffFreq) { + double p, p1, p2, p3, p4; + p1 = 0.27488 + (0.6315 + 0.525 / pow (1. + 0.0157 * fn, 20.)) * u - + 0.065683 * exp (-8.7513 * u); + p2 = 0.33622 * (1 - exp (-0.03442 * er)); + p3 = 0.0363 * exp (-4.6 * u) * (1 - exp (- pow (fn / 38.7, 4.97))); + p4 = 1 + 2.751 * (1 - exp (- pow (er / 15.916, 8.))); + p = p1 * p2 * pow ((0.1844 + p3 * p4) * fn, 1.5763); + *ErEffFreq = er - (er - ErEff) / (1 + p); +} + +/* Computes dispersion effects of characteristic impedance of a single + * microstrip line according to Kirschning and Jansen. Also used in + * coupled microstrip lines calculations. */ +void Kirschning_zl (double u, double fn, double er, + double ErEff, double ErEffFreq, + double ZlEff, double* r17, + double* ZlEffFreq) { + double r1, r2, r3, r4, r5, r6, r7, r8, r9, r10; + double r11, r12, r13, r14, r15, r16; + r1 = 0.03891 * pow (er, 1.4); + r2 = 0.267 * pow (u, 7.); + r3 = 4.766 * exp (-3.228 * pow (u, 0.641)); + r4 = 0.016 + pow (0.0514 * er, 4.524); + r5 = pow (fn / 28.843, 12.); + r6 = 22.20 * pow (u, 1.92); + r7 = 1.206 - 0.3144 * exp (-r1) * (1 - exp (-r2)); + r8 = 1 + 1.275 * (1 - exp (-0.004625 * r3 * + pow (er, 1.674) * pow (fn / 18.365, 2.745))); + r9 = 5.086 * r4 * r5 / (0.3838 + 0.386 * r4) * + exp (-r6) / (1 + 1.2992 * r5) * + pow (er - 1., 6.) / (1 + 10 * pow (er - 1., 6.)); + r10 = 0.00044 * pow (er, 2.136) + 0.0184; + r11 = pow (fn / 19.47, 6.) / (1 + 0.0962 * pow (fn / 19.47, 6.)); + r12 = 1 / (1 + 0.00245 * sqr (u)); + r13 = 0.9408 * pow (ErEffFreq, r8) - 0.9603; + r14 = (0.9408 - r9) * pow (ErEff, r8) - 0.9603; + r15 = 0.707 * r10 * pow (fn / 12.3, 1.097); + r16 = 1 + 0.0503 * sqr (er) * r11 * (1 - exp (- pow (u / 15., 6.))); + *r17 = r7 * (1 - 1.1241 * r12 / r16 * + exp (-0.026 * pow (fn, 1.15656) - r15)); + *ZlEffFreq = ZlEff * pow (r13 / r14, *r17); +} + +/* The function calculates the conductor and dielectric losses of a + * single microstrip line. */ +void analyseLoss (double W, double t, double er, + double rho, double D, double tand, + double ZlEff1, double ZlEff2, + double ErEff, + double frequency, int Model, + double* ac, double* ad) { + *ac = *ad = 0; + + // HAMMERSTAD and JENSEN + if (Model == HAMMERSTAD) { + double Rs, ds, l0, Kr, Ki; + + // conductor losses + if (t != 0.0) { + Rs = sqrt (M_PI * frequency * MU0 * rho); // skin resistance + ds = rho / Rs; // skin depth + // valid for t > 3 * ds + if (t < 3 * ds && frequency != 0) { + fprintf (stderr, + "WARNING: conductor loss calculation invalid for line " + "height t (%g) < 3 * skin depth (%g)\n", t, 3 * ds); + } + // current distribution factor + Ki = exp (-1.2 * pow ((ZlEff1 + ZlEff2) / 2 / Z0, 0.7)); + // D is RMS surface roughness + Kr = 1 + M_2_PI * atan (1.4 * sqr (D / ds)); + *ac = Rs / (ZlEff1 * W) * Ki * Kr; + } + + // dielectric losses + l0 = C0 / frequency; + *ad = M_PI * er / (er - 1) * (ErEff - 1) / sqrt (ErEff) * tand / l0; + } +} + diff --git a/src/xspice/tlines/msline_common.h b/src/xspice/tlines/msline_common.h new file mode 100644 index 000000000..a4c72c3a4 --- /dev/null +++ b/src/xspice/tlines/msline_common.h @@ -0,0 +1,60 @@ +/* msline_common.h + * common definitions for microstrip devices + * (c) Vadim Kuznetsov 2025 + */ + + +#ifndef MSLINE_COMMON_H +#define MSLINE_COMMON_H + +//TRAN model +#define TRAN_DC 0 +#define TRAN_FULL 1 + +// MS line model +#define HAMMERSTAD 0 +#define KIRSCHING 1 +#define WHEELER 2 +#define SCHNEIDER 3 + + +// Dispersion model +#define DISP_KIRSCHING 0 +#define KOBAYASHI 1 +#define YAMASHITA 2 +#define DISP_HAMMERSTAD 3 +#define GETSINGER 4 +#define DISP_SCHNEIDER 5 +#define PRAMANICK 6 + +void Hammerstad_ab (double, double, + double*, double*); +void Hammerstad_er (double, double, double, + double, double*); +void Hammerstad_zl (double, double*); +void Getsinger_disp (double, double, double, + double, double, + double*, double*); +void Kirschning_er (double, double, double, + double, double*); +void Kirschning_zl (double, double, double, + double, double, double, + double*, double*); + +void mslineAnalyseQuasiStatic (double W, double h, double t, + double er, int Model, + double *ZlEff, double *ErEff, + double *WEff); + +void mslineAnalyseDispersion (double W, double h, double er, + double ZlEff, double ErEff, + double frequency, int Model, + double* ZlEffFreq, + double* ErEffFreq); + +void analyseLoss (double, double, double, double, + double, double, double, double, + double, double, int, + double*, double*); + +#endif diff --git a/src/xspice/tlines/tline_common.c b/src/xspice/tlines/tline_common.c new file mode 100644 index 000000000..53bd4293f --- /dev/null +++ b/src/xspice/tlines/tline_common.c @@ -0,0 +1,86 @@ +/* tline_common.c + * common definitions for all transmission lines + * (c) Vadim Kuznetsov 2025 + */ + +#include +#include + +#include "tline_common.h" + +void append_state(tline_state_t **first, double time, double V1, double V2, + double I1, double I2, double tmax) +{ + tline_state_t *pp = (tline_state_t *) malloc(sizeof(tline_state_t)); + + pp->next = NULL; + pp->time = time; + pp->V1 = V1; pp->I1 = I1; + pp->V2 = V2; pp->I2 = I2; + + if (*first == NULL) { + *first = pp; + } else { + tline_state_t *pn = *first; + while (pn->next != NULL) { + pn = pn->next; + } + pn->next = pp; + + double t0 = (*first)->time; + + if ((time - t0) > tmax) { + tline_state_t *new_first = (*first)->next; + free(*first); + *first = new_first; + } + } +} + +tline_state_t *get_state(tline_state_t *first, double time) +{ + tline_state_t *pp = first; + while (pp != NULL && pp->time < time) { + pp = pp->next; + } + return pp; +} + + +void append_cpline_state(cpline_state_t **first, double time, double *Vp, double *Ip, double tmax) +{ + cpline_state_t *pp = (cpline_state_t *) malloc(sizeof(cpline_state_t)); + + pp->next = NULL; + pp->time = time; + memcpy(pp->Vp, Vp, PORT_NUM*sizeof(double)); + memcpy(pp->Ip, Ip, PORT_NUM*sizeof(double)); + + if (*first == NULL) { + *first = pp; + } else { + cpline_state_t *pn = *first; + while (pn->next != NULL) { + pn = pn->next; + } + pn->next = pp; + + double t0 = (*first)->time; + + if ((time - t0) > tmax) { + cpline_state_t *new_first = (*first)->next; + free(*first); + *first = new_first; + } + } +} + +cpline_state_t *find_cpline_state(cpline_state_t *first, double time) +{ + cpline_state_t *pp = first; + while (pp != NULL && pp->time < time) { + pp = pp->next; + } + return pp; +} + diff --git a/src/xspice/tlines/tline_common.h b/src/xspice/tlines/tline_common.h new file mode 100644 index 000000000..f531e54c3 --- /dev/null +++ b/src/xspice/tlines/tline_common.h @@ -0,0 +1,63 @@ +/* tline_common.h + * common definitions for all transmission lines + * (c) Vadim Kuznetsov 2025 + */ + + +#ifndef TLINE_COMMON_H +#define TLINE_COMMON_H + +// Constants +#define Z0 (120*M_PI) +#define z0 50.0 +#define MU0 (4*M_PI*1e-7) + +#define C0 299792458.0 + +#define GMIN 1e-12 + + +// Functions +#define sqr(x) (x*x) +#define cubic(x) (x*x*x) +#define quadr(x) (x*x*x*x) + +#define coth(x) (1.0/tanh(x)) +#define sech(x) (1.0/cosh(x)) +#define cosech(x) (1.0/sinh(x)) + +// Data structures to hold transient state + +typedef struct tline_state { + double time; + double I1; + double I2; + double V1; + double V2; + + struct tline_state *next; +} tline_state_t; + +// Functions to retrieve previous transient state +void append_state(tline_state_t **first, double time, double V1, double V2, + double I1, double I2, double tmax); + +tline_state_t *get_state(tline_state_t *first, double time); + +#define PORT_NUM 4 + +typedef struct cpline_state { + double time; + double Ip[PORT_NUM]; + double Vp[PORT_NUM]; + + struct cpline_state *next; +} cpline_state_t; + +void append_cpline_state(cpline_state_t **first, double time, double *Vp, double *Ip, double tmax); + +cpline_state_t *find_cpline_state(cpline_state_t *first, double time); + + + +#endif