From 9317355c0cbbb204309d3ed02836cba670b190b8 Mon Sep 17 00:00:00 2001 From: pnenzi Date: Wed, 23 Jul 2003 19:59:53 +0000 Subject: [PATCH] First batch of added file. --- src/xspice/Makefile.am | 20 + src/xspice/README | 41 + src/xspice/cm/Makefile.am | 17 + src/xspice/cm/cm.c | 701 +++++++++ src/xspice/cm/cmevt.c | 267 ++++ src/xspice/cm/cmmeters.c | 314 ++++ src/xspice/cm/cmutil.c | 523 +++++++ src/xspice/cmpp/Makefile | 36 + src/xspice/cmpp/cmpp.h | 285 ++++ src/xspice/cmpp/ifs_lex.l | 179 +++ src/xspice/cmpp/ifs_yacc.h | 81 + src/xspice/cmpp/ifs_yacc.y | 901 ++++++++++++ src/xspice/cmpp/main.c | 125 ++ src/xspice/cmpp/mod_lex.l | 107 ++ src/xspice/cmpp/mod_yacc.h | 49 + src/xspice/cmpp/mod_yacc.y | 559 +++++++ src/xspice/cmpp/pp_ifs.c | 88 ++ src/xspice/cmpp/pp_lst.c | 1082 ++++++++++++++ src/xspice/cmpp/pp_mod.c | 181 +++ src/xspice/cmpp/read_ifs.c | 175 +++ src/xspice/cmpp/util.c | 87 ++ src/xspice/cmpp/writ_ifs.c | 1304 +++++++++++++++++ src/xspice/enh/Makefile.am | 13 + src/xspice/enh/enh.c | 99 ++ src/xspice/enh/enhtrans.c | 536 +++++++ src/xspice/evt/Makefile.am | 27 + src/xspice/evt/evtaccept.c | 170 +++ src/xspice/evt/evtbackup.c | 645 ++++++++ src/xspice/evt/evtcall_hybrids.c | 78 + src/xspice/evt/evtdeque.c | 366 +++++ src/xspice/evt/evtdump.c | 350 +++++ src/xspice/evt/evtinit.c | 437 ++++++ src/xspice/evt/evtiter.c | 300 ++++ src/xspice/evt/evtload.c | 613 ++++++++ src/xspice/evt/evtnext_time.c | 93 ++ src/xspice/evt/evtnode_copy.c | 159 ++ src/xspice/evt/evtop.c | 321 ++++ src/xspice/evt/evtplot.c | 218 +++ src/xspice/evt/evtprint.c | 369 +++++ src/xspice/evt/evtqueue.c | 252 ++++ src/xspice/evt/evtsetup.c | 578 ++++++++ src/xspice/evt/evttermi.c | 512 +++++++ src/xspice/examples/analog_models1_ac.deck | 67 + src/xspice/examples/analog_models1_dc.deck | 61 + .../examples/analog_models1_swept_dc.deck | 61 + .../examples/analog_models1_transient.deck | 62 + src/xspice/examples/analog_models2_ac.deck | 66 + src/xspice/examples/analog_models2_dc.deck | 63 + .../examples/analog_models2_swept_dc.deck | 64 + .../examples/analog_models2_transient.deck | 67 + src/xspice/examples/analog_models3_ac.deck | 81 + src/xspice/examples/analog_models3_dc.deck | 79 + .../examples/analog_models3_swept_dc.deck | 79 + .../examples/analog_models3_transient.deck | 80 + src/xspice/examples/analog_models4_ac.deck | 78 + src/xspice/examples/analog_models4_dc.deck | 74 + .../examples/analog_models4_swept_dc.deck | 72 + .../examples/analog_models4_transient.deck | 76 + src/xspice/examples/arbitrary_phase.deck | 17 + src/xspice/examples/bad_io.deck | 17 + src/xspice/examples/bad_io_type.deck | 25 + src/xspice/examples/bad_name.deck | 16 + src/xspice/examples/bad_param.deck | 16 + src/xspice/examples/bad_param_type.deck | 16 + src/xspice/examples/d_to_real/Makefile | 40 + src/xspice/examples/d_to_real/cfunc.mod | 43 + src/xspice/examples/d_to_real/ifspec.ifs | 32 + src/xspice/examples/diffpair.in | 28 + src/xspice/examples/digital_invert.deck | 23 + src/xspice/examples/digital_models.deck | 20 + src/xspice/examples/digital_models1.deck | 77 + src/xspice/examples/digital_models2.deck | 91 ++ src/xspice/examples/digital_models3.deck | 92 ++ src/xspice/examples/digital_models4.deck | 91 ++ src/xspice/examples/dot_model_ref.deck | 19 + src/xspice/examples/hybrid_models1_dc.deck | 46 + .../examples/hybrid_models1_transient.deck | 48 + src/xspice/examples/initial_conditions.deck | 19 + src/xspice/examples/io_ordering.deck | 17 + src/xspice/examples/io_types.deck | 34 + src/xspice/examples/long_names.deck | 19 + src/xspice/examples/mixed_case.deck | 15 + src/xspice/examples/mixed_io_size.deck | 33 + src/xspice/examples/mixed_mode.deck | 98 ++ src/xspice/examples/mixed_ref.deck | 41 + src/xspice/examples/mosamp2.in | 42 + src/xspice/examples/mosmem.in | 27 + src/xspice/examples/nco/Makefile | 40 + src/xspice/examples/nco/cfunc.mod | 90 ++ src/xspice/examples/nco/ifspec.ifs | 38 + src/xspice/examples/param_defaults.deck | 16 + src/xspice/examples/param_types.deck | 23 + src/xspice/examples/parsing.deck | 16 + src/xspice/examples/polarity.deck | 26 + .../examples/print_param_types/Makefile | 40 + .../examples/print_param_types/cfunc.mod | 33 + .../examples/print_param_types/ifspec.ifs | 112 ++ src/xspice/examples/rca3040.in | 33 + src/xspice/examples/real_delay/Makefile | 40 + src/xspice/examples/real_delay/cfunc.mod | 46 + src/xspice/examples/real_delay/ifspec.ifs | 33 + src/xspice/examples/real_gain/Makefile | 40 + src/xspice/examples/real_gain/cfunc.mod | 39 + src/xspice/examples/real_gain/ifspec.ifs | 45 + src/xspice/examples/real_to_v/Makefile | 40 + src/xspice/examples/real_to_v/cfunc.mod | 75 + src/xspice/examples/real_to_v/ifspec.ifs | 33 + src/xspice/examples/rtlinv.in | 20 + src/xspice/examples/schmitt.in | 24 + src/xspice/examples/spice3.deck | 25 + src/xspice/examples/suffixes.deck | 25 + src/xspice/examples/supply_ramping.deck | 30 + src/xspice/examples/user_defined_nodes.deck | 30 + src/xspice/examples/xspice.deck | 22 + src/xspice/icm/Makefile.am | 194 +++ src/xspice/icm/README | 52 + src/xspice/icm/dlinfo.h | 9 + src/xspice/icm/dlmain.c | 408 ++++++ src/xspice/icm/icm_spice2poly/Makefile.am | 193 +++ src/xspice/icm/icm_spice2poly/README | 49 + src/xspice/icm/icm_spice2poly/cfunc.c | 305 ++++ src/xspice/icm/icm_spice2poly/cfunc.mod | 302 ++++ src/xspice/icm/icm_spice2poly/ifspec.c | 187 +++ src/xspice/icm/icm_spice2poly/ifspec.ifs | 75 + src/xspice/icm/icm_spice2poly/make.bat | 3 + src/xspice/icm/modpath.lst | 2 + src/xspice/icm/objects.inc | 1 + src/xspice/icm/poly/Makefile.am | 18 + src/xspice/icm/poly/cfunc.c | 307 ++++ src/xspice/icm/poly/cfunc.mod | 302 ++++ src/xspice/icm/poly/ifspec.c | 194 +++ src/xspice/icm/poly/ifspec.ifs | 75 + src/xspice/icm/poly/make.bat | 3 + src/xspice/icm/spice2poly.cm | Bin 0 -> 18495 bytes src/xspice/idn/Makefile.am | 12 + src/xspice/idn/idndig.c | 346 +++++ src/xspice/ipc/Makefile.am | 16 + src/xspice/ipc/ipc.c | 987 +++++++++++++ src/xspice/ipc/ipcaegis.c | 307 ++++ src/xspice/ipc/ipcsockets.c | 744 ++++++++++ src/xspice/ipc/ipcstdio.c | 69 + src/xspice/ipc/ipctiein.c | 530 +++++++ src/xspice/mif/Makefile.am | 26 + src/xspice/mif/mif.c | 60 + src/xspice/mif/mif_inp2.c | 941 ++++++++++++ src/xspice/mif/mifask.c | 231 +++ src/xspice/mif/mifconvt.c | 145 ++ src/xspice/mif/mifdelete.c | 199 +++ src/xspice/mif/mifdestr.c | 72 + src/xspice/mif/mifgetmod.c | 265 ++++ src/xspice/mif/mifgetvalue.c | 368 +++++ src/xspice/mif/mifload.c | 898 ++++++++++++ src/xspice/mif/mifmask.c | 220 +++ src/xspice/mif/mifmdelete.c | 123 ++ src/xspice/mif/mifmpara.c | 211 +++ src/xspice/mif/mifsetup.c | 456 ++++++ src/xspice/mif/miftrunc.c | 224 +++ src/xspice/mif/mifutil.c | 324 ++++ src/xspice/xspice.c | 81 + 159 files changed, 27058 insertions(+) create mode 100755 src/xspice/Makefile.am create mode 100755 src/xspice/README create mode 100755 src/xspice/cm/Makefile.am create mode 100755 src/xspice/cm/cm.c create mode 100755 src/xspice/cm/cmevt.c create mode 100755 src/xspice/cm/cmmeters.c create mode 100755 src/xspice/cm/cmutil.c create mode 100755 src/xspice/cmpp/Makefile create mode 100755 src/xspice/cmpp/cmpp.h create mode 100755 src/xspice/cmpp/ifs_lex.l create mode 100755 src/xspice/cmpp/ifs_yacc.h create mode 100755 src/xspice/cmpp/ifs_yacc.y create mode 100755 src/xspice/cmpp/main.c create mode 100755 src/xspice/cmpp/mod_lex.l create mode 100755 src/xspice/cmpp/mod_yacc.h create mode 100755 src/xspice/cmpp/mod_yacc.y create mode 100755 src/xspice/cmpp/pp_ifs.c create mode 100755 src/xspice/cmpp/pp_lst.c create mode 100755 src/xspice/cmpp/pp_mod.c create mode 100755 src/xspice/cmpp/read_ifs.c create mode 100755 src/xspice/cmpp/util.c create mode 100755 src/xspice/cmpp/writ_ifs.c create mode 100755 src/xspice/enh/Makefile.am create mode 100755 src/xspice/enh/enh.c create mode 100755 src/xspice/enh/enhtrans.c create mode 100755 src/xspice/evt/Makefile.am create mode 100755 src/xspice/evt/evtaccept.c create mode 100755 src/xspice/evt/evtbackup.c create mode 100755 src/xspice/evt/evtcall_hybrids.c create mode 100755 src/xspice/evt/evtdeque.c create mode 100755 src/xspice/evt/evtdump.c create mode 100755 src/xspice/evt/evtinit.c create mode 100755 src/xspice/evt/evtiter.c create mode 100755 src/xspice/evt/evtload.c create mode 100755 src/xspice/evt/evtnext_time.c create mode 100755 src/xspice/evt/evtnode_copy.c create mode 100755 src/xspice/evt/evtop.c create mode 100755 src/xspice/evt/evtplot.c create mode 100755 src/xspice/evt/evtprint.c create mode 100755 src/xspice/evt/evtqueue.c create mode 100755 src/xspice/evt/evtsetup.c create mode 100755 src/xspice/evt/evttermi.c create mode 100755 src/xspice/examples/analog_models1_ac.deck create mode 100755 src/xspice/examples/analog_models1_dc.deck create mode 100755 src/xspice/examples/analog_models1_swept_dc.deck create mode 100755 src/xspice/examples/analog_models1_transient.deck create mode 100755 src/xspice/examples/analog_models2_ac.deck create mode 100755 src/xspice/examples/analog_models2_dc.deck create mode 100755 src/xspice/examples/analog_models2_swept_dc.deck create mode 100755 src/xspice/examples/analog_models2_transient.deck create mode 100755 src/xspice/examples/analog_models3_ac.deck create mode 100755 src/xspice/examples/analog_models3_dc.deck create mode 100755 src/xspice/examples/analog_models3_swept_dc.deck create mode 100755 src/xspice/examples/analog_models3_transient.deck create mode 100755 src/xspice/examples/analog_models4_ac.deck create mode 100755 src/xspice/examples/analog_models4_dc.deck create mode 100755 src/xspice/examples/analog_models4_swept_dc.deck create mode 100755 src/xspice/examples/analog_models4_transient.deck create mode 100755 src/xspice/examples/arbitrary_phase.deck create mode 100755 src/xspice/examples/bad_io.deck create mode 100755 src/xspice/examples/bad_io_type.deck create mode 100755 src/xspice/examples/bad_name.deck create mode 100755 src/xspice/examples/bad_param.deck create mode 100755 src/xspice/examples/bad_param_type.deck create mode 100755 src/xspice/examples/d_to_real/Makefile create mode 100755 src/xspice/examples/d_to_real/cfunc.mod create mode 100755 src/xspice/examples/d_to_real/ifspec.ifs create mode 100755 src/xspice/examples/diffpair.in create mode 100755 src/xspice/examples/digital_invert.deck create mode 100755 src/xspice/examples/digital_models.deck create mode 100755 src/xspice/examples/digital_models1.deck create mode 100755 src/xspice/examples/digital_models2.deck create mode 100755 src/xspice/examples/digital_models3.deck create mode 100755 src/xspice/examples/digital_models4.deck create mode 100755 src/xspice/examples/dot_model_ref.deck create mode 100755 src/xspice/examples/hybrid_models1_dc.deck create mode 100755 src/xspice/examples/hybrid_models1_transient.deck create mode 100755 src/xspice/examples/initial_conditions.deck create mode 100755 src/xspice/examples/io_ordering.deck create mode 100755 src/xspice/examples/io_types.deck create mode 100755 src/xspice/examples/long_names.deck create mode 100755 src/xspice/examples/mixed_case.deck create mode 100755 src/xspice/examples/mixed_io_size.deck create mode 100755 src/xspice/examples/mixed_mode.deck create mode 100755 src/xspice/examples/mixed_ref.deck create mode 100755 src/xspice/examples/mosamp2.in create mode 100755 src/xspice/examples/mosmem.in create mode 100755 src/xspice/examples/nco/Makefile create mode 100755 src/xspice/examples/nco/cfunc.mod create mode 100755 src/xspice/examples/nco/ifspec.ifs create mode 100755 src/xspice/examples/param_defaults.deck create mode 100755 src/xspice/examples/param_types.deck create mode 100755 src/xspice/examples/parsing.deck create mode 100755 src/xspice/examples/polarity.deck create mode 100755 src/xspice/examples/print_param_types/Makefile create mode 100755 src/xspice/examples/print_param_types/cfunc.mod create mode 100755 src/xspice/examples/print_param_types/ifspec.ifs create mode 100755 src/xspice/examples/rca3040.in create mode 100755 src/xspice/examples/real_delay/Makefile create mode 100755 src/xspice/examples/real_delay/cfunc.mod create mode 100755 src/xspice/examples/real_delay/ifspec.ifs create mode 100755 src/xspice/examples/real_gain/Makefile create mode 100755 src/xspice/examples/real_gain/cfunc.mod create mode 100755 src/xspice/examples/real_gain/ifspec.ifs create mode 100755 src/xspice/examples/real_to_v/Makefile create mode 100755 src/xspice/examples/real_to_v/cfunc.mod create mode 100755 src/xspice/examples/real_to_v/ifspec.ifs create mode 100755 src/xspice/examples/rtlinv.in create mode 100755 src/xspice/examples/schmitt.in create mode 100755 src/xspice/examples/spice3.deck create mode 100755 src/xspice/examples/suffixes.deck create mode 100755 src/xspice/examples/supply_ramping.deck create mode 100755 src/xspice/examples/user_defined_nodes.deck create mode 100755 src/xspice/examples/xspice.deck create mode 100755 src/xspice/icm/Makefile.am create mode 100644 src/xspice/icm/README create mode 100644 src/xspice/icm/dlinfo.h create mode 100644 src/xspice/icm/dlmain.c create mode 100644 src/xspice/icm/icm_spice2poly/Makefile.am create mode 100644 src/xspice/icm/icm_spice2poly/README create mode 100644 src/xspice/icm/icm_spice2poly/cfunc.c create mode 100644 src/xspice/icm/icm_spice2poly/cfunc.mod create mode 100644 src/xspice/icm/icm_spice2poly/ifspec.c create mode 100644 src/xspice/icm/icm_spice2poly/ifspec.ifs create mode 100644 src/xspice/icm/icm_spice2poly/make.bat create mode 100644 src/xspice/icm/modpath.lst create mode 100644 src/xspice/icm/objects.inc create mode 100755 src/xspice/icm/poly/Makefile.am create mode 100755 src/xspice/icm/poly/cfunc.c create mode 100755 src/xspice/icm/poly/cfunc.mod create mode 100755 src/xspice/icm/poly/ifspec.c create mode 100755 src/xspice/icm/poly/ifspec.ifs create mode 100755 src/xspice/icm/poly/make.bat create mode 100755 src/xspice/icm/spice2poly.cm create mode 100755 src/xspice/idn/Makefile.am create mode 100755 src/xspice/idn/idndig.c create mode 100755 src/xspice/ipc/Makefile.am create mode 100755 src/xspice/ipc/ipc.c create mode 100755 src/xspice/ipc/ipcaegis.c create mode 100755 src/xspice/ipc/ipcsockets.c create mode 100755 src/xspice/ipc/ipcstdio.c create mode 100755 src/xspice/ipc/ipctiein.c create mode 100755 src/xspice/mif/Makefile.am create mode 100755 src/xspice/mif/mif.c create mode 100755 src/xspice/mif/mif_inp2.c create mode 100755 src/xspice/mif/mifask.c create mode 100755 src/xspice/mif/mifconvt.c create mode 100755 src/xspice/mif/mifdelete.c create mode 100755 src/xspice/mif/mifdestr.c create mode 100755 src/xspice/mif/mifgetmod.c create mode 100755 src/xspice/mif/mifgetvalue.c create mode 100755 src/xspice/mif/mifload.c create mode 100755 src/xspice/mif/mifmask.c create mode 100755 src/xspice/mif/mifmdelete.c create mode 100755 src/xspice/mif/mifmpara.c create mode 100755 src/xspice/mif/mifsetup.c create mode 100755 src/xspice/mif/miftrunc.c create mode 100755 src/xspice/mif/mifutil.c create mode 100755 src/xspice/xspice.c diff --git a/src/xspice/Makefile.am b/src/xspice/Makefile.am new file mode 100755 index 000000000..90ab4d779 --- /dev/null +++ b/src/xspice/Makefile.am @@ -0,0 +1,20 @@ +# Process this file with automake +CFLAGS = -g -O2 -Wall +CC = gcc +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) + +EXTRA_DIST = README + +## This is removed because icm relies upon the existance of all other +## libs. It is currently compiled manually, last. +##SUBDIRS = mif cm enh evt ipc idn icm + +SUBDIRS = mif cm enh evt ipc idn cmpp + +INCLUDES = -I$(top_srcdir)/src/include -I$(top_srcdir) + +MAINTAINERCLEANFILES = Makefile.in + +all: xspice.o +xspice.o: + $(COMPILE) -c xspice.c diff --git a/src/xspice/README b/src/xspice/README new file mode 100755 index 000000000..d0f621920 --- /dev/null +++ b/src/xspice/README @@ -0,0 +1,41 @@ +Spice Opus / XSpice code model support. +-------------------------------------- + +Use configure flag --enable-xspice to compile the support in, +when you run the ./configure script. +This creates a new command, "codemodel", which you can +use to load a codemodel. + +Some codemodels are included in the xspice/lib directory +with some examples in xspice/examples, compiled for linux glibc. + +Make sure the the library dir, xspice/lib, is in your LD_LIBRARY_PATH +enviromental variable, otherwise the libs will not be found! + +To create codemodels go to http://www.fe.uni-lj.si/spice/welcome.html +and download their trial version of spice opus for the codemodel toolkit! + +TODO: + Intergrate the ipc stuff from XSpice. + Create ng-spice capacity to create codemodels (a perl script) + Ngspice crashes when you try to plot a digital node + +Stefan Jones + 19/2/2002 + +----------------------------------------- +SPICE2 POLY codemodel support. + +SPICE2 POLY attributes are now available for controlled sources. To +use POLY attributes, configure tclspice/ngspice with the +--enable-xspice flag set as described above. After compilation of +ngspice, cd into $(top_srcdir)/src/xspice/icm and read the README file +there for instructions about how to get POLY support. (Hint: you have +to download some stuff from http://www.fe.uni-lj.si/ and edit the +Makefiles before you can do "make && make install" of the codemodel +stuff.) + +Please direct questions/comments/complaints to mailto:sdb@cloud9.net. + +6.22.2003 -- SDB. + diff --git a/src/xspice/cm/Makefile.am b/src/xspice/cm/Makefile.am new file mode 100755 index 000000000..41bcc96fe --- /dev/null +++ b/src/xspice/cm/Makefile.am @@ -0,0 +1,17 @@ +## Process this file with automake to produce Makefile.in +# +# JW 3/9/01 - had a go and makeing an autoconf script. + +noinst_LIBRARIES = libcmxsp.a + +libcmxsp_a_SOURCES = \ +cm.c \ +cmevt.c \ +cmmeters.c \ +cmutil.c + + + +INCLUDES = -I$(top_srcdir)/src/include -I$(top_srcdir)/src/spicelib/devices + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/xspice/cm/cm.c b/src/xspice/cm/cm.c new file mode 100755 index 000000000..6c5df7380 --- /dev/null +++ b/src/xspice/cm/cm.c @@ -0,0 +1,701 @@ +/* =========================================================================== +FILE CM.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains functions callable from user code models. + +INTERFACES + + cm_analog_alloc() + cm_analog_get_ptr() + cm_analog_integrate() + cm_analog_converge() + cm_analog_set_temp_bkpt() + cm_analog_set_perm_bkpt() + cm_analog_ramp_factor() + cm_analog_not_converged() + cm_analog_auto_partial() + + cm_message_get_errmsg() + cm_message_send() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ +#include "ngspice.h" +#include "cm.h" +#include "mif.h" +#include "cktdefs.h" +//#include "util.h" + + + + + +static void cm_static_integrate(int byte_index, + double integrand, + double *integral, + double *partial); + +/* + +cm_analog_alloc() + +This function is called from code model C functions to allocate +state storage for a particular instance. It computes the number +of doubles that need to be allocated in SPICE's state storage +vectors from the number of bytes specified in it's argument and +then allocates space for the states. An index into the SPICE +state-vectors is stored in the instance's data structure along +with a ``tag'' variable supplied by the caller so that the location +of the state storage area can be found by cm_analog_get_ptr(). + +*/ + +void *cm_analog_alloc( + int tag, /* The user-specified tag for this block of memory */ + int bytes) /* The number of bytes to allocate */ +{ + MIFinstance *here; + CKTcircuit *ckt; + + Mif_State_t *state; + + int doubles_needed; + int i; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + here = g_mif_info.instance; + ckt = g_mif_info.ckt; + + /* Scan states in instance struct and see if tag has already been used */ + for(i = 0; i < here->num_state; i++) { + if(tag == here->state[i].tag) { + g_mif_info.errmsg = "ERROR - cm_analog_alloc() - Tag already used in previous call\n"; + return(NULL); + } + } + + /* Compute number of doubles needed and allocate space in ckt->CKTstates[i] */ + doubles_needed = bytes / sizeof(double) + 1; + + /* Allocate space in instance struct for this state descriptor */ + if(here->num_state == 0) { + here->num_state = 1; + here->state = (void *) MALLOC(sizeof(Mif_State_t)); + } + else { + here->num_state++; + here->state = (void *) REALLOC(here->state, + here->num_state * sizeof(Mif_State_t)); + } + + /* Fill in the members of the state descriptor struct */ + state = &(here->state[here->num_state - 1]); + state->tag = tag; + state->index = ckt->CKTnumStates; + state->doubles = doubles_needed; + state->bytes = bytes; + + + /* Add the states to the ckt->CKTstates vectors */ + ckt->CKTnumStates += doubles_needed; + for(i=0;i<=ckt->CKTmaxOrder+1;i++) { + if(ckt->CKTnumStates == doubles_needed) + ckt->CKTstates[i] = (double *) MALLOC(ckt->CKTnumStates * sizeof(double)); + else + ckt->CKTstates[i] = (double *) REALLOC(ckt->CKTstates[i], + ckt->CKTnumStates * sizeof(double)); + } + + /* Return pointer to the allocated space in state 0 */ + return( (void *) (ckt->CKTstates[0] + (ckt->CKTnumStates - doubles_needed))); + +} + + +/* +cm_analog_get_ptr() + +This function is called from code model C functions to return a +pointer to state storage allocated with cm_analog_alloc(). A tag +specified in its argument list is used to locate the state in +question. A second argument specifies whether the desired state +is for the current timestep or from a preceding timestep. The +location of the state in memory is then computed and returned. +*/ + +void *cm_analog_get_ptr( + int tag, /* The user-specified tag for this block of memory */ + int timepoint) /* The timepoint of interest - 0=current 1=previous */ +{ + MIFinstance *here; + CKTcircuit *ckt; + + Mif_State_t *state=NULL; + + Mif_Boolean_t got_tag; + + int i; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + here = g_mif_info.instance; + ckt = g_mif_info.ckt; + + /* Scan states in instance struct and see if tag exists */ + for(got_tag = MIF_FALSE, i = 0; i < here->num_state; i++) { + if(tag == here->state[i].tag) { + state = &(here->state[i]); + got_tag = MIF_TRUE; + break; + } + } + + /* Return error if tag not found */ + if(! got_tag) { + g_mif_info.errmsg = "ERROR - cm_analog_get_ptr() - Bad tag\n"; + return(NULL); + } + + /* Return error if timepoint is not 0 or 1 */ + if((timepoint < 0) || (timepoint > 1)) { + g_mif_info.errmsg = "ERROR - cm_analog_get_ptr() - Bad timepoint\n"; + return(NULL); + } + + /* Return address of requested state in ckt->CKTstates[timepoint] vector */ + return( (void *) (ckt->CKTstates[timepoint] + state->index) ); + +} + + +/* +cm_analog_integrate() + +This function performs a numerical integration on the state +supplied in its argument list according to the integrand also +supplied in the argument list. The next value of the integral +and the partial derivative with respect to the integrand input is +returned. The integral argument must be a pointer to memory +previously allocated through a call to cm_analog_alloc(). If this is +the first call to cm_analog_integrate(), information is entered into the +instance structure to mark that the integral should be processed +by MIFtrunc and MIFconvTest. +*/ + +int cm_analog_integrate( + double integrand, /* The integrand */ + double *integral, /* The current and returned value of integral */ + double *partial) /* The partial derivative of integral wrt integrand */ +{ + + MIFinstance *here; + CKTcircuit *ckt; + + Mif_Intgr_t *intgr; + Mif_Boolean_t got_index; + + char *char_state0; + char *char_state; + + int byte_index; + int i; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + here = g_mif_info.instance; + ckt = g_mif_info.ckt; + + /* Check to be sure we're in transient analysis */ + if(g_mif_info.circuit.anal_type != MIF_TRAN) { + g_mif_info.errmsg = + "ERROR - cm_analog_integrate() - Called in non-transient analysis\n"; + *partial = 0.0; + return(MIF_ERROR); + } + + /* Preliminary check to be sure argument was allocated by cm_analog_alloc() */ + if(ckt->CKTnumStates <= 0) { + g_mif_info.errmsg = + "ERROR - cm_analog_integrate() - Integral must be memory allocated by cm_analog_alloc()\n"; + *partial = 0.0; + return(MIF_ERROR); + } + + /* Compute byte offset from start of state0 vector */ + char_state0 = (char *) ckt->CKTstate0; + char_state = (char *) integral; + byte_index = char_state - char_state0; + + /* Check to be sure argument address is in range of state0 vector */ + if((byte_index < 0) || + (byte_index > ((ckt->CKTnumStates - 1) * sizeof(double)) ) ) { + g_mif_info.errmsg = + "ERROR - cm_analog_integrate() - Argument must be in state vector 0\n"; + *partial = 0.0; + return(MIF_ERROR); + } + + /* Scan the intgr array in the instance struct to see if already exists */ + for(got_index = MIF_FALSE, i = 0; i < here->num_intgr; i++) { + if(here->intgr[i].byte_index == byte_index) { + got_index = MIF_TRUE; + } + } + + /* Report error if not found and this is not the first load pass in tran analysis */ + if((! got_index) && (! g_mif_info.circuit.anal_init)) { + g_mif_info.errmsg = + "ERROR - cm_analog_integrate() - New integral and not initialization pass\n"; + *partial = 0.0; + return(MIF_ERROR); + } + + /* If new integral state, allocate space in instance */ + /* struct for this intgr descriptor and register it with */ + /* the cm_analog_converge() function */ + if(! got_index) { + if(here->num_intgr == 0) { + here->num_intgr = 1; + here->intgr = (void *) MALLOC(sizeof(Mif_Intgr_t)); + } + else { + here->num_intgr++; + here->intgr = (void *) REALLOC(here->intgr, + here->num_intgr * sizeof(Mif_Intgr_t)); + } + intgr = &(here->intgr[here->num_intgr - 1]); + intgr->byte_index = byte_index; + if(cm_analog_converge(integral)) { + printf("%s\n",g_mif_info.errmsg); + g_mif_info.errmsg = "ERROR - cm_analog_integrate() - Failure in cm_analog_converge() call\n"; + return(MIF_ERROR); + } + } + + /* Compute the new integral and the partial */ + cm_static_integrate(byte_index, integrand, integral, partial); + + return(MIF_OK); +} + + +/* +cm_analog_converge() + +This function registers a state variable allocated with +cm_analog_alloc() to be subjected to a convergence test at the end of +each iteration. The state variable must be a double. +Information is entered into the instance structure to mark that +the state variable should be processed by MIFconvTest. +*/ + +int cm_analog_converge( + double *state) /* The state to be converged */ +{ + MIFinstance *here; + CKTcircuit *ckt; + + Mif_Conv_t *conv; + + char *char_state0; + char *char_state; + + int byte_index; + int i; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + here = g_mif_info.instance; + ckt = g_mif_info.ckt; + + /* Preliminary check to be sure argument was allocated by cm_analog_alloc() */ + if(ckt->CKTnumStates <= 0) { + g_mif_info.errmsg = + "ERROR - cm_analog_converge() - Argument must be memory allocated by cm_analog_alloc()\n"; + return(MIF_ERROR); + } + + /* Compute byte offset from start of state0 vector */ + char_state0 = (char *) ckt->CKTstate0; + char_state = (char *) state; + byte_index = char_state - char_state0; + + /* Check to be sure argument address is in range of state0 vector */ + if((byte_index < 0) || + (byte_index > ((ckt->CKTnumStates - 1) * sizeof(double)) ) ) { + g_mif_info.errmsg = + "ERROR - cm_analog_converge() - Argument must be in state vector 0\n"; + return(MIF_ERROR); + } + + /* Scan the conv array in the instance struct to see if already registered */ + /* If so, do nothing, just return */ + for(i = 0; i < here->num_conv; i++) { + if(here->conv[i].byte_index == byte_index) + return(MIF_OK); + } + + /* Allocate space in instance struct for this conv descriptor */ + if(here->num_conv == 0) { + here->num_conv = 1; + here->conv = (void *) MALLOC(sizeof(Mif_Conv_t)); + } + else { + here->num_conv++; + here->conv = (void *) REALLOC(here->conv, + here->num_conv * sizeof(Mif_Conv_t)); + } + + /* Fill in the conv descriptor data */ + conv = &(here->conv[here->num_conv - 1]); + conv->byte_index = byte_index; + conv->last_value = 1.0e30; /* There should be a better way ... */ + + return(MIF_OK); +} + + + +/* +cm_message_get_errmsg() + +This function returns the address of an error message string set +by a call to some code model support function. +*/ + +char *cm_message_get_errmsg(void) +{ + return(g_mif_info.errmsg); +} + + + + +/* +cm_analog_set_temp_bkpt() + +This function is called by a code model C function to set a +temporary breakpoint. These temporary breakpoints remain in +effect only until the next timestep is taken. A temporary +breakpoint added with a time less than the current time, but +greater than the last successful timestep causes the simulator to +abandon the current timestep and decrease the timestep to hit the +breakpoint. A temporary breakpoint with a time greater than the +current time causes the simulator to make the breakpoint the next +timepoint if the next timestep would produce a time greater than +that of the breakpoint. +*/ + + +int cm_analog_set_temp_bkpt( + double time) /* The time of the breakpoint to be set */ +{ + CKTcircuit *ckt; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + ckt = g_mif_info.ckt; + + /* Make sure breakpoint is not prior to last accepted timepoint */ + if(time < ((ckt->CKTtime - ckt->CKTdelta) + ckt->CKTminBreak)) { + g_mif_info.errmsg = + "ERROR - cm_analog_set_temp_bkpt() - Time < last accepted timepoint\n"; + return(MIF_ERROR); + } + + /* If too close to a permanent breakpoint or the current time, discard it */ + if( (fabs(time - ckt->CKTbreaks[0]) < ckt->CKTminBreak) || + (fabs(time - ckt->CKTbreaks[1]) < ckt->CKTminBreak) || + (fabs(time - ckt->CKTtime) < ckt->CKTminBreak) ) + return(MIF_OK); + + /* If < current dynamic breakpoint, make it the current breakpoint */ + if( time < g_mif_info.breakpoint.current) + g_mif_info.breakpoint.current = time; + + return(MIF_OK); +} + + + + +/* +cm_analog_set_perm_bkpt() + +This function is called by a code model C function to set a +permanent breakpoint. These permanent breakpoints remain in +effect from the time they are introduced until the simulation +time equals or exceeds the breakpoint time. A permanent +breakpoint added with a time less than the current time, but +greater than the last successful timestep causes the simulator to +abandon the current timestep and decrease the timestep to hit the +breakpoint. A permanent breakpoint with a time greater than the +current time causes the simulator to make the breakpoint the next +timepoint if the next timestep would produce a time greater than +that of the breakpoint. +*/ + + +int cm_analog_set_perm_bkpt( + double time) /* The time of the breakpoint to be set */ +{ + CKTcircuit *ckt; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + ckt = g_mif_info.ckt; + + /* Call cm_analog_set_temp_bkpt() to force backup if less than current time */ + if(time < (ckt->CKTtime + ckt->CKTminBreak)) + return(cm_analog_set_temp_bkpt(time)); + else + CKTsetBreak(ckt,time); + + return(MIF_OK); +} + + +/* +cm_analog_ramp_factor() + +This function returns the current value of the ramp factor +associated with the ``ramptime'' option. For this option +to work best, models with analog outputs that may be non-zero at +time zero should call this function and scale their outputs +and partials by the ramp factor. +*/ + + +double cm_analog_ramp_factor(void) +{ + + CKTcircuit *ckt; + + /* Get the address of the ckt and instance structs from g_mif_info */ + ckt = g_mif_info.ckt; + + + /* if ramptime == 0.0, no ramptime option given, so return 1.0 */ + /* this is the most common case, so it goes first */ + if(ckt->enh->ramp.ramptime == 0.0) + return(1.0); + + /* else if not transient analysis, return 1.0 */ + else if( (!(ckt->CKTmode & MODETRANOP)) && (!(ckt->CKTmode & MODETRAN)) ) + return(1.0); + + /* else if time >= ramptime, return 1.0 */ + else if(ckt->CKTtime >= ckt->enh->ramp.ramptime) + return(1.0); + + /* else time < end of ramp, so compute and return factor based on time */ + else + return(ckt->CKTtime / ckt->enh->ramp.ramptime); +} + + +/* ************************************************************ */ + + +/* + * Copyright (c) 1985 Thomas L. Quarles + * + * This is a modified version of the function NIintegrate() + * + * Modifications are Copyright 1991 Georgia Tech Research Institute + * + */ + +static void cm_static_integrate(int byte_index, + double integrand, + double *integral, + double *partial) +{ + CKTcircuit *ckt; + + double intgr[7]; + double cur=0; + double *double_ptr; + + double ceq; + double geq; + + char *char_ptr; + + int i; + + + /* Get the address of the ckt struct from g_mif_info */ + ckt = g_mif_info.ckt; + + /* Get integral values from current and previous timesteps */ + for(i = 0; i <= ckt->CKTorder; i++) { + char_ptr = (char *) ckt->CKTstates[i]; + char_ptr += byte_index; + double_ptr = (double *) char_ptr; + intgr[i] = *double_ptr; + } + + + /* Do what SPICE3C1 does for its implicit integration */ + + switch(ckt->CKTintegrateMethod) { + + case TRAPEZOIDAL: + + switch(ckt->CKTorder) { + + case 1: + cur = ckt->CKTag[1] * intgr[1]; + break; + + case 2: + /* WARNING - This code needs to be redone. */ + /* The correct code should rely on one previous value */ + /* of cur as done in NIintegrate() */ + cur = -0.5 * ckt->CKTag[0] * intgr[1]; + break; + } + + break; + + case GEAR: + cur = 0.0; + + switch(ckt->CKTorder) { + + case 6: + cur += ckt->CKTag[6] * intgr[6]; + /* fall through */ + case 5: + cur += ckt->CKTag[5] * intgr[5]; + /* fall through */ + case 4: + cur += ckt->CKTag[4] * intgr[4]; + /* fall through */ + case 3: + cur += ckt->CKTag[3] * intgr[3]; + /* fall through */ + case 2: + cur += ckt->CKTag[2] * intgr[2]; + /* fall through */ + case 1: + cur += ckt->CKTag[1] * intgr[1]; + break; + + } + break; + + } + + ceq = cur; + geq = ckt->CKTag[0]; + + /* WARNING: Take this out when the case 2: above is fixed */ + if((ckt->CKTintegrateMethod == TRAPEZOIDAL) && + (ckt->CKTorder == 2)) + geq *= 0.5; + + + /* The following code is equivalent to */ + /* the solution of one matrix iteration to produce the */ + /* integral value. */ + + *integral = (integrand - ceq) / geq; + *partial = 1.0 / geq; + +} + + + + + +/* +cm_analog_not_converged() + +This function tells the simulator not to allow the current +iteration to be the final iteration. It is called when +a code model performs internal limiting on one or more of +its inputs to assist convergence. +*/ + +void cm_analog_not_converged(void) +{ + (g_mif_info.ckt->CKTnoncon)++; +} + + + + +/* +cm_message_send() + +This function prints a message output from a code model, prepending +the instance name. +*/ + + +int cm_message_send( + char *msg) /* The message to output. */ +{ + MIFinstance *here; + + /* Get the address of the instance struct from g_mif_info */ + here = g_mif_info.instance; + + /* Print the name of the instance and the message */ + printf("\nInstance: %s Message: %s\n", (char *) here->MIFname, msg); + + return(0); +} + + + + + + +/* +cm_analog_auto_partial() + +This function tells the simulator to automatically compute +approximations of partial derivatives of analog outputs +with respect to analog inputs. When called from a code +model, it sets a flag in the g_mif_info structure +which tells function MIFload() and it's associated +MIFauto_partial() function to perform the necessary +calculations. +*/ + + +void cm_analog_auto_partial(void) +{ + g_mif_info.auto_partial.local = MIF_TRUE; +} + diff --git a/src/xspice/cm/cmevt.c b/src/xspice/cm/cmevt.c new file mode 100755 index 000000000..7854fa946 --- /dev/null +++ b/src/xspice/cm/cmevt.c @@ -0,0 +1,267 @@ +/* =========================================================================== +FILE CMevt.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains functions callable from user code models + that are associated with the event-driven algorithm. + +INTERFACES + + cm_event_alloc() + cm_event_get_ptr() + cm_event_queue() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +#include "ngspice.h" +#include "cktdefs.h" +//#include "util.h" + +#include "cm.h" +#include "mif.h" +#include "evt.h" + +#include "evtproto.h" + + + + + +/* +cm_event_alloc() + +This function is called from code model C functions to allocate +state storage for a particular event-driven +instance. It is similar to the +function cm_analog_alloc() used by analog models, but allocates states +that are rotated during event-driven 'timesteps' instead of analog +timesteps. +*/ + + +void *cm_event_alloc( + int tag, /* The user-specified tag for the memory block */ + int bytes) /* The number of bytes to be allocated */ +{ + + int inst_index; + int num_tags; + + MIFinstance *here; + CKTcircuit *ckt; + + void *ptr; + + Evt_State_Desc_t **desc_ptr; + Evt_State_Desc_t *desc; + + Evt_State_Data_t *state_data; + Evt_State_t *state; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + here = g_mif_info.instance; + ckt = g_mif_info.ckt; + + + /* If not initialization pass, return error */ + if(here->initialized) { + g_mif_info.errmsg = + "ERROR - cm_event_alloc() - Cannot alloc when not initialization pass\n"; + return(NULL); + } + + + /* Get pointers for fast access */ + inst_index = here->inst_index; + state_data = ckt->evt->data.state; + + + /* Scan state descriptor list to determine if tag is present and to */ + /* find the end of the list. Report error if duplicate tag */ + desc_ptr = &(state_data->desc[inst_index]); + desc = *desc_ptr; + num_tags = 1; + while(desc) { + if(desc->tag == tag) { + g_mif_info.errmsg = + "ERROR - cm_event_alloc() - Duplicate tag\n"; + return(NULL); + } + desc_ptr = &(desc->next); + desc = *desc_ptr; + num_tags++; + } + + /* Create a new state description structure at end of list */ + /* and fill in the data and update the total size */ + *desc_ptr = (void *) MALLOC(sizeof(Evt_State_Desc_t)); + desc = *desc_ptr; + desc->tag = tag; + desc->size = bytes; + desc->offset = state_data->total_size[inst_index]; + state_data->total_size[inst_index] += bytes; + + /* Create a new state structure if list starting at head is null */ + state = state_data->head[inst_index]; + if(state == NULL) { + state = (void *) MALLOC(sizeof(Evt_State_t)); + state_data->head[inst_index] = state; + } + + /* Create or enlarge the block and set the time */ + if(num_tags == 1) + state->block = MALLOC(state_data->total_size[inst_index]); + else + state->block = REALLOC(state->block, + state_data->total_size[inst_index]); + + state->step = g_mif_info.circuit.evt_step; + + + /* Return allocated memory */ + ptr = ((char *)state->block) + desc->offset; + return(ptr); +} + + + + + +/* +cm_event_get_ptr() + +This function is called from code model C functions to return a +pointer to state storage allocated with cm_event_alloc(). A tag +specified in its argument list is used to locate the state in +question. A second argument specifies whether the desired state +is for the current timestep or from a preceding timestep. The +location of the state in memory is then computed and returned. +*/ + + +void *cm_event_get_ptr( + int tag, /* The user-specified tag for the memory block */ + int timepoint) /* The timepoint - 0=current, 1=previous */ +{ + + int i; + int inst_index; + + MIFinstance *here; + CKTcircuit *ckt; + + void *ptr; + + Evt_State_Desc_t *desc; + + Evt_State_Data_t *state_data; + Evt_State_t *state; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + here = g_mif_info.instance; + ckt = g_mif_info.ckt; + + + /* If initialization pass, return error */ + if((! here->initialized) && (timepoint > 0)) { + g_mif_info.errmsg = + "ERROR - cm_event_get_ptr() - Cannot get_ptr(tag,1) during initialization pass\n"; + return(NULL); + } + + /* Get pointers for fast access */ + inst_index = here->inst_index; + state_data = ckt->evt->data.state; + + /* Scan state descriptor list to find the descriptor for this tag. */ + /* Report error if tag not found */ + desc = state_data->desc[inst_index]; + while(desc) { + if(desc->tag == tag) + break; + desc = desc->next; + } + + if(desc == NULL) { + g_mif_info.errmsg = + "ERROR - cm_event_get_ptr() - Specified tag not found\n"; + return(NULL); + } + + /* Get the state pointer from the current array */ + state = *(state_data->tail[inst_index]); + + /* Backup the specified number of timesteps */ + for(i = 0; i < timepoint; i++) + if(state->prev) + state = state->prev; + + /* Return pointer */ + ptr = ((char *) state->block) + desc->offset; + return(ptr); +} + + + + +/* +cm_event_queue() + +This function queues an event for an instance participating +in the event-driven algorithm. +*/ + + +int cm_event_queue( + double time) /* The time of the event to be queued */ +{ + + MIFinstance *here; + CKTcircuit *ckt; + + + /* Get the address of the ckt and instance structs from g_mif_info */ + here = g_mif_info.instance; + ckt = g_mif_info.ckt; + + /* If breakpoint time <= current event time, return error */ + if(time <= g_mif_info.circuit.evt_step) { + g_mif_info.errmsg = + "ERROR - cm_event_queue() - Event time cannot be <= current time\n"; + return(MIF_ERROR); + } + + /* Add the event time to the inst queue */ + EVTqueue_inst(ckt, here->inst_index, g_mif_info.circuit.evt_step, + time); + + return(MIF_OK); +} diff --git a/src/xspice/cm/cmmeters.c b/src/xspice/cm/cmmeters.c new file mode 100755 index 000000000..d49ed794a --- /dev/null +++ b/src/xspice/cm/cmmeters.c @@ -0,0 +1,314 @@ +/* =========================================================================== +FILE CMmeters.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains functions callable from code models. + These functions are primarily designed for use by the + "cmeter" and "lmeter" models provided in the XSPICE + code model library. + +INTERFACES + + cm_netlist_get_c() + cm_netlist_get_l() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ +#include "ngspice.h" +#include "cm.h" +#include "mif.h" + +#include "cktdefs.h" + +#include "mifdefs.h" +#include "cap/capdefs.h" +#include "ind/inddefs.h" +#include "vsrc/vsrcdefs.h" +#include "inpdefs.h" + + + +/* +cm_netlist_get_c() + +This is a special function designed for use with the c_meter +model. It returns the parallel combination of the capacitance +connected to the first port on the instance. +*/ + +double cm_netlist_get_c() +{ + CKTcircuit *ckt; + + MIFinstance *cmeter_inst; + CAPinstance *cap_inst; + VSRCinstance *vsrc_inst; + + CAPmodel *cap_head; + CAPmodel *cap_model; + VSRCmodel *vsrc_head; + VSRCmodel *vsrc_model; + + int cap_type; + int vsrc_type; + + int cmeter_node; + int vsrc_node; + + double c; + + + /* Get the circuit data structure and current instance */ + ckt = g_mif_info.ckt; + cmeter_inst = g_mif_info.instance; + + /* Get internal node number for positive node of cmeter input */ + cmeter_node = cmeter_inst->conn[0]->port[0]->smp_data.pos_node; + + /* Initialize total capacitance value to zero */ + c = 0.0; + + + /* ****************************************************** */ + /* Look for capacitors connected directly to cmeter input */ + /* ****************************************************** */ + + /* Get the head of the list of capacitor models in the circuit */ + cap_type = INPtypelook("Capacitor"); + if(cap_type < 0) { + printf("\nERROR - Capacitor type not supported in this binary\n"); + return(0); + } + cap_head = (CAPmodel *) ckt->CKThead[cap_type]; + + /* Scan through all capacitor instances and add in values */ + /* of any capacitors connected to cmeter input */ + + for(cap_model = cap_head; cap_model; cap_model = cap_model->CAPnextModel) { + for(cap_inst = cap_model->CAPinstances; + cap_inst; + cap_inst = cap_inst->CAPnextInstance) { + if((cmeter_node == cap_inst->CAPposNode) || + (cmeter_node == cap_inst->CAPnegNode)) { + c += cap_inst->CAPcapac; + } + } + } + + + /* ***************************************************************** */ + /* Look for capacitors connected through zero-valued voltage sources */ + /* ***************************************************************** */ + + /* Get the head of the list of voltage source models in the circuit */ + vsrc_type = INPtypelook("Vsource"); + if(vsrc_type < 0) { + printf("\nERROR - Vsource type not supported in this binary\n"); + return(0); + } + vsrc_head = (VSRCmodel *) ckt->CKThead[vsrc_type]; + + /* Scan through all voltage source instances and add in values */ + /* of any capacitors connected to cmeter input through voltage source */ + + for(vsrc_model = vsrc_head; vsrc_model; vsrc_model = vsrc_model->VSRCnextModel) { + for(vsrc_inst = vsrc_model->VSRCinstances; + vsrc_inst; + vsrc_inst = vsrc_inst->VSRCnextInstance) { + + /* Skip to next if not DC source with value = 0.0 */ + if((vsrc_inst->VSRCfunctionType != 0) || + (vsrc_inst->VSRCdcValue != 0.0)) + continue; + + /* See if voltage source is connected to cmeter input */ + /* If so, get other node voltage source is connected to */ + /* If not, skip to next source */ + if(cmeter_node == vsrc_inst->VSRCposNode) + vsrc_node = vsrc_inst->VSRCnegNode; + else if(cmeter_node == vsrc_inst->VSRCnegNode) + vsrc_node = vsrc_inst->VSRCposNode; + else + continue; + + + /* Scan through all capacitor instances and add in values */ + /* of any capacitors connected to the voltage source node */ + + for(cap_model = cap_head; cap_model; cap_model = cap_model->CAPnextModel) { + for(cap_inst = cap_model->CAPinstances; + cap_inst; + cap_inst = cap_inst->CAPnextInstance) { + if((vsrc_node == cap_inst->CAPposNode) || + (vsrc_node == cap_inst->CAPnegNode)) { + c += cap_inst->CAPcapac; + } + } + } + + + } /* end for all vsrc instances */ + } /* end for all vsrc models */ + + + /* Return the total capacitance value */ + return(c); +} + + + + +/* +cm_netlist_get_l() + +This is a special function designed for use with the l_meter +model. It returns the equivalent value of inductance +connected to the first port on the instance. +*/ + + +double cm_netlist_get_l() +{ + CKTcircuit *ckt; + + MIFinstance *lmeter_inst; + INDinstance *ind_inst; + VSRCinstance *vsrc_inst; + + INDmodel *ind_head; + INDmodel *ind_model; + VSRCmodel *vsrc_head; + VSRCmodel *vsrc_model; + + int ind_type; + int vsrc_type; + + int lmeter_node; + int vsrc_node; + + double l; + + + /* Get the circuit data structure and current instance */ + ckt = g_mif_info.ckt; + lmeter_inst = g_mif_info.instance; + + /* Get internal node number for positive node of lmeter input */ + lmeter_node = lmeter_inst->conn[0]->port[0]->smp_data.pos_node; + + /* Initialize total inductance to infinity */ + l = 1.0e12; + + + /* ****************************************************** */ + /* Look for inductors connected directly to lmeter input */ + /* ****************************************************** */ + + /* Get the head of the list of inductor models in the circuit */ + ind_type = INPtypelook("Inductor"); + if(ind_type < 0) { + printf("\nERROR - Inductor type not supported in this binary\n"); + return(0); + } + ind_head = (INDmodel *) ckt->CKThead[ind_type]; + + /* Scan through all inductor instances and add in values */ + /* of any inductors connected to lmeter input */ + + for(ind_model = ind_head; ind_model; ind_model = ind_model->INDnextModel) { + for(ind_inst = ind_model->INDinstances; + ind_inst; + ind_inst = ind_inst->INDnextInstance) { + if((lmeter_node == ind_inst->INDposNode) || + (lmeter_node == ind_inst->INDnegNode)) { + l = 1.0 / ( (1.0 / l) + (1.0 / ind_inst->INDinduct) ); + } + } + } + + + /* ***************************************************************** */ + /* Look for inductors connected through zero-valued voltage sources */ + /* ***************************************************************** */ + + /* Get the head of the list of voltage source models in the circuit */ + vsrc_type = INPtypelook("Vsource"); + if(vsrc_type < 0) { + printf("\nERROR - Vsource type not supported in this binary\n"); + return(0); + } + vsrc_head = (VSRCmodel *) ckt->CKThead[vsrc_type]; + + /* Scan through all voltage source instances and add in values */ + /* of any inductors connected to lmeter input through voltage source */ + + for(vsrc_model = vsrc_head; vsrc_model; vsrc_model = vsrc_model->VSRCnextModel) { + for(vsrc_inst = vsrc_model->VSRCinstances; + vsrc_inst; + vsrc_inst = vsrc_inst->VSRCnextInstance) { + + /* Skip to next if not DC source with value = 0.0 */ + if((vsrc_inst->VSRCfunctionType != 0) || + (vsrc_inst->VSRCdcValue != 0.0)) + continue; + + /* See if voltage source is connected to lmeter input */ + /* If so, get other node voltage source is connected to */ + /* If not, skip to next source */ + if(lmeter_node == vsrc_inst->VSRCposNode) + vsrc_node = vsrc_inst->VSRCnegNode; + else if(lmeter_node == vsrc_inst->VSRCnegNode) + vsrc_node = vsrc_inst->VSRCposNode; + else + continue; + + + /* Scan through all inductor instances and add in values */ + /* of any inductors connected to the voltage source node */ + + for(ind_model = ind_head; ind_model; ind_model = ind_model->INDnextModel) { + for(ind_inst = ind_model->INDinstances; + ind_inst; + ind_inst = ind_inst->INDnextInstance) { + if((vsrc_node == ind_inst->INDposNode) || + (vsrc_node == ind_inst->INDnegNode)) { + l = 1.0 / ( (1.0 / l) + (1.0 / ind_inst->INDinduct) ); + } + } + } + + + } /* end for all vsrc instances */ + } /* end for all vsrc models */ + + + /* Return the total capacitance value */ + return(l); +} + + diff --git a/src/xspice/cm/cmutil.c b/src/xspice/cm/cmutil.c new file mode 100755 index 000000000..f0cfe8303 --- /dev/null +++ b/src/xspice/cm/cmutil.c @@ -0,0 +1,523 @@ +/* =========================================================================== +FILE CMutil.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Jeff Murray + +MODIFICATIONS + + + +SUMMARY + + This file contains functions callable from user code models. + These functions were written to support code models in the + XSPICE library, but may be useful in general. + +INTERFACES + + cm_smooth_corner() + cm_smooth_discontinuity() + cm_smooth_pwl() + + cm_climit_fcn() + + cm_complex_set() + cm_complex_add() + cm_complex_subtract() + cm_complex_multiply() + cm_complex_divide() + + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ +#include +#include +#include "cm.h" + +/* Corner Smoothing Function ************************************ +* * +* The following function smooths the transition between two * +* slopes into a quadratic (parabolic) curve. The calling * +* function passes an x,y coordinate representing the * +* "breakpoint", a smoothing domain value (d), and the slopes at * +* both endpoints, and the x value itself. The situation is * +* shown below: B C * +* A |<-d->| ^ y * +* ---------*-----* | | * +* lower_slope-^ |<-d->|\ | | * +* \ | | * +* \ | *------>x * +* At Ax * +*****************************************************************/ + +void cm_smooth_discontinuity( + double x_input, /* The x value at which to compute y */ + double x_lower, /* The x value of the lower corner */ + double y_lower, /* The y value of the lower corner */ + double x_upper, /* The x value of the upper corner */ + double y_upper, /* The y value of the upper corner */ + double *y_output, /* The computed smoothed y value */ + double *dy_dx) /* The partial of y wrt x */ +{ + double x_center,y_center,a,b,c,center_slope; + + + /* Derive x_center, y_center & center_slope values */ + x_center = (x_upper + x_lower) / 2.0; + y_center = (y_upper + y_lower) / 2.0; + center_slope = 2.0 * (y_upper - y_lower) / (x_upper - x_lower); + + + if (x_input < x_lower) { /* x_input @ lower level */ + *y_output = y_lower; + *dy_dx = 0.0; + } + else { + if (x_input < x_center) { /* x_input in lower transition */ + a = center_slope / (x_upper - x_lower); + b = center_slope - 2.0 * a * x_center; + c = y_center - a * x_center * x_center - b * x_center; + *y_output = a * x_input * x_input + b * x_input + c; + *dy_dx = 2.0 * a * x_input + b; + } + else { /* x_input in upper transition */ + if (x_input < x_upper) { + a = -center_slope / (x_upper - x_lower); + b = -2.0 * a * x_upper; + c = y_upper - a * x_upper * x_upper - b * x_upper; + *y_output = a * x_input * x_input + b * x_input + c; + *dy_dx = 2.0 * a * x_input + b; + } + else { /* x_input @ upper level */ + *y_output = y_upper; + *dy_dx = 0.0; + } + } + } +} + + + +/* Controlled Limiter Function (modified CLIMIT) */ + +/* +This is a special function created for use with the CLIMIT +controlled limiter model. +*/ + +void cm_climit_fcn( + double in, /* The input value */ + double in_offset, /* The input offset */ + double cntl_upper, /* The upper control input value */ + double cntl_lower, /* The lower control input value */ + double lower_delta, /* The delta from control to limit value */ + double upper_delta, /* The delta from control to limit value */ + double limit_range, /* The limiting range */ + double gain, /* The gain from input to output */ + int percent, /* The fraction vs. absolute range flag */ + double *out_final, /* The output value */ + double *pout_pin_final, /* The partial of output wrt input */ + double *pout_pcntl_lower_final, /* The partial of output wrt lower control input */ + double *pout_pcntl_upper_final) /* The partial of output wrt upper control input */ +{ + +/* Define error message string constants */ + +char *climit_range_error = "\n**** ERROR ****\n* CLIMIT function linear range less than zero. *\n"; + + +double threshold_upper,threshold_lower,linear_range, + out_lower_limit,out_upper_limit,limited_out, + out,pout_pin,pout_pcntl_lower,pout_pcntl_upper,junk; + + /* Find Upper & Lower Limits */ + + out_lower_limit = cntl_lower + lower_delta; + out_upper_limit = cntl_upper - upper_delta; + + + if (percent == TRUE) /* Set range to absolute value */ + limit_range = limit_range * + (out_upper_limit - out_lower_limit); + + + + threshold_upper = out_upper_limit - /* Set Upper Threshold */ + limit_range; + threshold_lower = out_lower_limit + /* Set Lower Threshold */ + limit_range; + linear_range = threshold_upper - threshold_lower; + + + /* Test the linear region & make sure there IS one... */ + if (linear_range < 0.0) { + printf("%s\n",climit_range_error); +/* limited_out = 0.0; + pout_pin = 0.0; + pout_pcntl_lower = 0.0; + pout_pcntl_upper = 0.0; + return; +*/ } + + /* Compute Un-Limited Output */ + out = gain * (in_offset + in); + + + if (out < threshold_lower) { /* Limit Out @ Lower Bound */ + + pout_pcntl_upper= 0.0; + + if (out > (out_lower_limit - limit_range)) { /* Parabolic */ + cm_smooth_corner(out,out_lower_limit,out_lower_limit, + limit_range,0.0,1.0,&limited_out, + &pout_pin); + pout_pin = gain * pout_pin; + cm_smooth_discontinuity(out,out_lower_limit,1.0,threshold_lower, + 0.0,&pout_pcntl_lower,&junk); + } + else { /* Hard-Limited Region */ + limited_out = out_lower_limit; + pout_pin = 0.0; + pout_pcntl_lower = 1.0; + } + } + else { + if (out > threshold_upper) { /* Limit Out @ Upper Bound */ + + pout_pcntl_lower= 0.0; + + if (out < (out_upper_limit+limit_range)) { /* Parabolic */ + cm_smooth_corner(out,out_upper_limit,out_upper_limit, + limit_range,1.0,0.0,&limited_out, + &pout_pin); + pout_pin = gain * pout_pin; + cm_smooth_discontinuity(out,threshold_upper,0.0,out_upper_limit, + 1.0,&pout_pcntl_upper,&junk); + } + else { /* Hard-Limited Region */ + limited_out = out_upper_limit; + pout_pin = 0.0; + pout_pcntl_upper = 1.0; + } + } + else { /* No Limiting Needed */ + limited_out = out; + pout_pin = gain; + pout_pcntl_lower = 0.0; + pout_pcntl_upper = 0.0; + } + } + + + *out_final = limited_out; + *pout_pin_final = pout_pin; + *pout_pcntl_lower_final = pout_pcntl_lower; + *pout_pcntl_upper_final = pout_pcntl_upper; + +} + +/**** End Controlled Limiter Function ****/ + +/*=============================================================================*/ + + +/* Piecewise Linear Smoothing Function ********************* +* The following is a transfer curve function which * +* accepts as input an "x" value, and returns a "y" * +* value. The transfer characteristic is a smoothed * +* piece-wise linear curve described by *x and *y array * +* coordinate pairs. * +* * +* Created 8/14/91 * +* Last Modified 8/14/91 J.P.Murray * +***********************************************************/ + +/*********************************************************** +* * +* ^ x[4] * +* x[1] | * * +* | midpoint /|\ * +* | | / \ * +* | V | / | \ * +* *----*----* \ * +* midpoint /| | | \ * +* | / || * <- midpoint * +* V/ | |x[3] \ * +* <-----------*------------O------------\-------------> * +* | / | \ | | * +* / | \ * +* |/ | \| | * +* * | *-----*---> * +* /| | x[5] x[6] * +* / | * +* / x[0] | * +* / | * +* / | * +* / | * +* V * +* * +***********************************************************/ + +/*********************************************************** +* * +* Note that for the cm_smooth_pwl function, the arguments * +* are as listed below: * +* * +* * +* double x_input; input * * +* double *x; pointer to the x-coordinate * +* array * * +* double *y; pointer to the y-coordinate * +* array * * +* int size; size of the arrays * +* * +* double input_domain; smoothing range * * +* double dout_din; partial derivative of the * +* output w.r.t. the input * * +* * +***********************************************************/ + + +double cm_smooth_pwl(double x_input, double *x, double *y, int size, + double input_domain, double *dout_din) +{ + + int i; /* generic loop counter index */ + + double lower_seg; /* x segment below which input resides */ + double upper_seg; /* x segment above which the input resides */ + double lower_slope; /* slope of the lower segment */ + double upper_slope; /* slope of the upper segment */ + double out; /* output */ + double threshold_lower; /* value below which the output begins smoothing */ + double threshold_upper; /* value above which the output begins smoothing */ + + + /* char *limit_error="\n***ERROR***\nViolation of 50% rule in breakpoints!\n";*/ + + + + + + /* Determine segment boundaries within which x_input resides */ + + if (x_input <= (*(x+1) + *x)/2.0) {/*** x_input below lowest midpoint ***/ + *dout_din = (*(y+1) - *y)/(*(x+1) - *x); + out = *y + (x_input - *x) * *dout_din; + } + else { + if (x_input >= (*(x+size-2) + *(x+size-1))/2.0) { + /*** x_input above highest midpoint ***/ + *dout_din = (*(y+size-1) - *(y+size-2)) / + (*(x+size-1) - *(x+size-2)); + out = *(y+size-1) + (x_input - *(x+size-1)) * *dout_din; + } + else { /*** x_input within bounds of end midpoints... ***/ + /*** must determine position progressively & then ***/ + /*** calculate required output. ***/ + + for (i=1; i $@ + +ifs_lex.c : ifs_lex.l + $(LEX) -i -P$(*:lex=)yy $< > $@ + +install: + +clean: + rm -f $(cmpp_OBJS) $(cmpp_GEN) cmpp diff --git a/src/xspice/cmpp/cmpp.h b/src/xspice/cmpp/cmpp.h new file mode 100755 index 000000000..0a15c0e68 --- /dev/null +++ b/src/xspice/cmpp/cmpp.h @@ -0,0 +1,285 @@ +/*============================================================================ +FILE cmpp.h + +MEMBER OF process cmpp + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains shared constants, type definitions, + and function prototypes used in the cmpp process. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +#include + +#define IFSPEC_FILENAME "ifspec.ifs" +#define UDNFUNC_FILENAME "udnfunc.c" +#define MODPATH_FILENAME "modpath.lst" +#define UDNPATH_FILENAME "udnpath.lst" + +/* *********************************************************************** */ + +typedef enum { + + OK, /* Returned with no error */ + ERROR, /* Returned with error */ + +} Status_t; + + + +#define GET_IFS_TABLE 0 /* Read the entire ifs table */ +#define GET_IFS_NAME 1 /* Get the C function name out of the table only */ + +#define MAX_PATH_LEN 1024 /* Maximum pathname length */ +#define MAX_NAME_LEN 1024 /* Maximum SPICE name length */ +#define MAX_FN_LEN 31 /* Maximum filename length */ + + +/* ******************************************************************** */ +/* Structures used by parser to check for valid connections/parameters */ +/* ******************************************************************** */ + +/* + * The boolean type + */ + +typedef enum { + FALSE, + TRUE, +} Boolean_t; + + +/* + * The direction of a connector + */ + +typedef enum { + IN, + OUT, + INOUT, +} Dir_t; + + +/* + * The type of a port + */ + +typedef enum { + VOLTAGE, /* v - Single-ended voltage */ + DIFF_VOLTAGE, /* vd - Differential voltage */ + CURRENT, /* i - Single-ended current */ + DIFF_CURRENT, /* id - Differential current */ + VSOURCE_CURRENT, /* vnam - Vsource name for input current */ + CONDUCTANCE, /* g - Single-ended VCIS */ + DIFF_CONDUCTANCE, /* gd - Differential VCIS */ + RESISTANCE, /* h - Single-ended ICVS */ + DIFF_RESISTANCE, /* hd - Differential ICVS */ + DIGITAL, /* d - Digital */ + USER_DEFINED, /* - Any user defined type */ +} Port_Type_t; + + + +/* + * The type of a parameter or Static_Var + */ + +typedef enum { + BOOLEAN, + INTEGER, + REAL, + COMPLEX, + STRING, + POINTER, /* NOTE: POINTER should not be used for Parameters - only + * Static_Vars - this is enforced by the cmpp. + */ + } Data_Type_t; + + + +/* + * The complex type + */ + +typedef struct { + double real; + double imag; +} Complex_t; + + + +/* + * Values of different types. + * + * Note that a struct is used instead of a union for conformity + * with the use of the Mif_Value_t type in the simulator where + * the type must be statically initialized. ANSI C does not + * support useful initialization of unions. + * + */ + +typedef struct { + + Boolean_t bvalue; /* For BOOLEAN parameters */ + int ivalue; /* For INTEGER parameters */ + double rvalue; /* For REAL parameters */ + Complex_t cvalue; /* For COMPLEX parameters */ + char *svalue; /* For STRING parameters */ + +} Value_t; + + + +/* + * Information about the model as a whole + */ + + +typedef struct { + + char *c_fcn_name; /* Name used in the C function */ + char *model_name; /* Name used in a spice deck */ + char *description; /* Description of the model */ + +} Name_Info_t; + + + + +/* + * Information about a connection + */ + + +typedef struct { + + char *name; /* Name of this connection */ + char *description; /* Description of this connection */ + Dir_t direction; /* IN, OUT, or INOUT */ + Port_Type_t default_port_type; /* The default port type */ + char *default_type; /* The default type in string form */ + int num_allowed_types; /* The size of the allowed type arrays */ + Port_Type_t *allowed_port_type; /* Array of allowed types */ + char **allowed_type; /* Array of allowed types in string form */ + Boolean_t is_array; /* True if connection is an array */ + Boolean_t has_conn_ref; /* True if there is associated with an array conn */ + int conn_ref; /* Subscript of the associated array conn */ + Boolean_t has_lower_bound; /* True if there is an array size lower bound */ + int lower_bound; /* Array size lower bound */ + Boolean_t has_upper_bound; /* True if there is an array size upper bound */ + int upper_bound; /* Array size upper bound */ + Boolean_t null_allowed; /* True if null is allowed for this connection */ + +} Conn_Info_t; + + + + +/* + * Information about a parameter + */ + +typedef struct { + + char *name; /* Name of this parameter */ + char *description; /* Description of this parameter */ + Data_Type_t type; /* Data type, e.g. REAL, INTEGER, ... */ + Boolean_t has_default; /* True if there is a default value */ + Value_t default_value; /* The default value */ + Boolean_t has_lower_limit; /* True if there is a lower limit */ + Value_t lower_limit; /* The lower limit for this parameter */ + Boolean_t has_upper_limit; /* True if there is a upper limit */ + Value_t upper_limit; /* The upper limit for this parameter */ + Boolean_t is_array; /* True if parameter is an array */ + Boolean_t has_conn_ref; /* True if there is associated with an array conn */ + int conn_ref; /* Subscript of the associated array conn */ + Boolean_t has_lower_bound; /* True if there is an array size lower bound */ + int lower_bound; /* Array size lower bound */ + Boolean_t has_upper_bound; /* True if there is an array size upper bound */ + int upper_bound; /* Array size upper bound */ + Boolean_t null_allowed; /* True if null is allowed for this parameter */ + +} Param_Info_t; + + +/* + * Information about an instance variable + */ + +typedef struct { + + char *name; /* Name of this parameter */ + char *description; /* Description of this parameter */ + Data_Type_t type; /* Data type, e.g. REAL, INTEGER, ... */ + Boolean_t is_array; /* True if parameter is an array */ + +} Inst_Var_Info_t; + + + +/* + * The all encompassing structure for the ifs table information + */ + +typedef struct { + + Name_Info_t name; /* The name table entries */ + int num_conn; /* Number of entries in the connection table(s) */ + Conn_Info_t *conn; /* Array of connection info structs */ + int num_param; /* Number of entries in the parameter table(s) */ + Param_Info_t *param; /* Array of parameter info structs */ + int num_inst_var; /* Number of entries in the instance var table(s) */ + Inst_Var_Info_t *inst_var; /* Array of instance variable info structs */ + +} Ifs_Table_t; + + + +/* *********************************************************************** */ + + + +void preprocess_ifs_file(void); + +void preprocess_lst_files(void); + +void preprocess_mod_file(char *filename); + + +void print_error(char *message); + + +Status_t read_ifs_file(char *filename, int mode, Ifs_Table_t *ifs_table); + +Status_t write_ifs_c_file(char *filename, Ifs_Table_t *ifs_table); + + diff --git a/src/xspice/cmpp/ifs_lex.l b/src/xspice/cmpp/ifs_lex.l new file mode 100755 index 000000000..2d1df697d --- /dev/null +++ b/src/xspice/cmpp/ifs_lex.l @@ -0,0 +1,179 @@ +%option yylineno +%option noyywrap +%{ /* $Id$ */ + +/*============================================================================ +FILE ifs_lex.l + +MEMBER OF process cmpp + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Steve Tynor + +MODIFICATIONS + + 12/31/91 Bill Kuhn Change "array" to "vector" and "array_bounds" + to "vector_bounds". + +SUMMARY + + This file defines tokens applicable to parsing the ifspec.ifs + file, and actions to be taken on encountering those tokens. + +INTERFACES + + None. + +REFERENCED FILES + + ifs_yacc.y + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include "ifs_yacc.h" +#include "ifs_tok.h" + +int yyival; +double yydval; +extern int atoi(); +extern double atof(); + +/* + * IFS specs are case insensitive: + */ + +/* saj - use -i flex command line option +#undef input +#define input() (((yytchar=yysptr>yysbuf?U(*--yysptr):getc(yyin))==10?(yylineno++,yytchar):yytchar)==EOF?0:isupper(yytchar)?tolower(yytchar):yytchar) +*/ + +/*---------------------------------------------------------------------------*/ + +%} + +%start BOOL CTYPE DIR DTYPE + +%x comment stringl + +%p 5000 + +W [ \t\n] +A [_a-z] +D [0-9] +I [a-z_] +Z [0-9a-z_] +E [eE][+-]?{D}+ + +%% + +"/*" { BEGIN(comment); } + +[^*\n]* /* eat anything that's not a '*' */ + +"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ + +\n /* new line */ + +<> {ifs_yyerror ("Unterminated comment"); + BEGIN(INITIAL); + yyterminate(); } + +"*"+"/" { BEGIN(INITIAL); } + +"\"" { BEGIN(stringl); } + +[^\"]* { return TOK_STRING_LITERAL; } + +"\"" { BEGIN(INITIAL); } + +<> {ifs_yyerror ("Unterminated string literal"); + BEGIN(INITIAL); + yyterminate(); } + +allowed_types{W}*: {BEGIN CTYPE; return TOK_ALLOWED_TYPES;} +vector{W}*: {BEGIN BOOL; return TOK_ARRAY;} +vector_bounds{W}*: {return TOK_ARRAY_BOUNDS;} +c_function_name{W}*: {return TOK_C_FUNCTION_NAME;} +port_name{W}*: {return TOK_PORT_NAME;} +port_table{W}*: {return TOK_PORT_TABLE;} +data_type{W}*: {BEGIN DTYPE; return TOK_DATA_TYPE;} +default_type{W}*: {BEGIN CTYPE; return TOK_DEFAULT_TYPE;} +default_value{W}*: {return TOK_DEFAULT_VALUE;} +description{W}*: {return TOK_DESCRIPTION;} +direction{W}*: {BEGIN DIR; return TOK_DIRECTION;} +static_var_name{W}*: {return TOK_STATIC_VAR_NAME;} +static_var_table{W}*: {return TOK_STATIC_VAR_TABLE;} +limits{W}*: {return TOK_LIMITS;} +name_table{W}*: {return TOK_NAME_TABLE;} +null_allowed{W}*: {BEGIN BOOL; return TOK_NULL_ALLOWED;} +parameter_name{W}*: {return TOK_PARAMETER_NAME;} +parameter_table{W}*: {return TOK_PARAMETER_TABLE;} +spice_model_name{W}*: {return TOK_SPICE_MODEL_NAME;} + +yes {return TOK_BOOL_YES;} +no {return TOK_BOOL_NO;} +true {return TOK_BOOL_YES;} +false {return TOK_BOOL_NO;} + +v {return TOK_CTYPE_V;} +vd {return TOK_CTYPE_VD;} +vnam {return TOK_CTYPE_VNAM;} +i {return TOK_CTYPE_I;} +id {return TOK_CTYPE_ID;} +g {return TOK_CTYPE_G;} +gd {return TOK_CTYPE_GD;} +h {return TOK_CTYPE_H;} +hd {return TOK_CTYPE_HD;} +d {return TOK_CTYPE_D;} + +in {return TOK_DIR_IN;} +out {return TOK_DIR_OUT;} +inout {return TOK_DIR_INOUT;} + +real {return TOK_DTYPE_REAL;} +int {return TOK_DTYPE_INT;} +boolean {return TOK_DTYPE_BOOLEAN;} +complex {return TOK_DTYPE_COMPLEX;} +string {return TOK_DTYPE_STRING;} +pointer {return TOK_DTYPE_POINTER;} + +"<" {return TOK_LANGLE;} +">" {return TOK_RANGLE;} +"[" {return TOK_LBRACKET;} +"]" {return TOK_RBRACKET;} +"," {return TOK_COMMA;} +"-" {return TOK_DASH;} + + +{I}+{Z}* {return TOK_IDENTIFIER;} + +[+-]?{D}+ {yyival = atoi (yytext); + return TOK_INT_LITERAL;} + +[+-]?{D}+"."{D}*({E})? | +[+-]?{D}*"."{D}+({E})? | +[+-]?{D}+({E})? {yydval = atof (yytext); + return TOK_REAL_LITERAL;} + +. ; /* ignore anything else */ +\n ; /* ignore anything else */ + +%% + +/*--------------------------------------------------------------------------*/ +void reset_lex_context () +{ + BEGIN 0; +} diff --git a/src/xspice/cmpp/ifs_yacc.h b/src/xspice/cmpp/ifs_yacc.h new file mode 100755 index 000000000..611e9a786 --- /dev/null +++ b/src/xspice/cmpp/ifs_yacc.h @@ -0,0 +1,81 @@ +/* $Id$ */ + +/*============================================================================ +FILE ifs_yacc.h + +MEMBER OF process cmpp + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Steve Tynor + +MODIFICATIONS + + + +SUMMARY + + Typedefs needed by the YYSTYPE union (%union operator) in the yacc + file. These are only used in the yacc file, but must be defined here since + the generated token.h file includes a definition of the union YYSTYPE. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include "cmpp.h" + +typedef struct { + Boolean_t has_value; + Data_Type_t kind; + union { + Boolean_t bvalue; + int ivalue; + double rvalue; + Complex_t cvalue; + char *svalue; + } u; +} My_Value_t; + +typedef struct { + Boolean_t has_bound; + My_Value_t bound; +} Bound_t; + +typedef struct { + Boolean_t is_named; + union { + char *name; + struct { + Bound_t upper; + Bound_t lower; + } bounds; + } u; +} Range_t; + +typedef struct { + Port_Type_t kind; + char *id; /* undefined unless kind == USER_DEFINED */ +} My_Port_Type_t; + +typedef struct ctype_list_s { + My_Port_Type_t ctype; + struct ctype_list_s *next; +} Ctype_List_t; diff --git a/src/xspice/cmpp/ifs_yacc.y b/src/xspice/cmpp/ifs_yacc.y new file mode 100755 index 000000000..03767a362 --- /dev/null +++ b/src/xspice/cmpp/ifs_yacc.y @@ -0,0 +1,901 @@ +%{ /* $Id$ */ + +/*============================================================================ +FILE ifs_yacc.y + +MEMBER OF process cmpp + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Steve Tynor + +MODIFICATIONS + + 12/31/91 Bill Kuhn Fix bug in usage of strcmp in check_default_type() + +SUMMARY + + This file contains the BNF specification of the language used in + the ifspec.ifs file together with various support functions, + and parses the ifspec.ifs file to get the information from it + and place this information into a data structure + of type Ifs_Table_t. + +INTERFACES + + yyparse() - Generated automatically by UNIX 'yacc' utility. + +REFERENCED FILES + + ifs_lex.l + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include "ifs_yacc.h" + +extern int yylineno; +extern int yyival; +extern double yydval; +extern char *ifs_yytext; +extern char *strdup(); + +Boolean_t parser_just_names; +static Boolean_t saw_model_name; +static Boolean_t saw_function_name; + +static char *dtype_to_str[] = { + "BOOLEAN", "INTEGER", "REAL", "COMPLEX", "STRING", "POINTER" + }; + +static Boolean_t did_default_type; +static Boolean_t did_allowed_types; + +static int num_items; +static int item; +static int item_offset; +static Boolean_t num_items_fixed; + +Ifs_Table_t *parser_ifs_table; +#define TBL parser_ifs_table + +static int alloced_size [4]; + +/* + * !!!!! Make sure these are large enough so that they never get realloced + * !!!!! since that will cause garbage uninitialized data... + * !!!!! (FIX THIS!) + */ +#define DEFAULT_SIZE_CONN 100 +#define DEFAULT_SIZE_PARAM 100 +#define DEFAULT_SIZE_INST_VAR 100 +#define GROW_SIZE 10 + +typedef enum { + TBL_NAME, + TBL_PORT, + TBL_PARAMETER, + TBL_STATIC_VAR, +} Table_t; + +typedef struct { + Table_t table; + int record; +} Context_t; + +Context_t context; + +#define ITEM_BUFFER_SIZE 20 /* number of items that can be put in a table + * before requiring a new xxx_TABLE: keyword + */ +#define FOR_ITEM(i) for (i = item_offset; i < num_items; i++) +#define ITEM_BUF(i) item_buffer[i-item_offset] + +#define ASSIGN_BOUNDS(struct_name, i) \ + if (ITEM_BUF(i).range.is_named) {\ + TBL->struct_name[i].has_conn_ref = TRUE;\ + TBL->struct_name[i].conn_ref = find_conn_ref (ITEM_BUF(i).range.u.name);\ + } else {\ + TBL->struct_name[i].has_conn_ref = FALSE;\ + TBL->struct_name[i].has_lower_bound =\ + ITEM_BUF(i).range.u.bounds.lower.has_bound;\ + TBL->struct_name[i].has_upper_bound =\ + ITEM_BUF(i).range.u.bounds.upper.has_bound;\ + if (TBL->struct_name[i].has_lower_bound) {\ + assert (ITEM_BUF(i).range.u.bounds.lower.bound.kind == INTEGER);\ + TBL->struct_name[i].lower_bound =\ + ITEM_BUF(i).range.u.bounds.lower.bound.u.ivalue;\ + }\ + if (TBL->struct_name[i].has_upper_bound) {\ + assert (ITEM_BUF(i).range.u.bounds.upper.bound.kind == INTEGER);\ + TBL->struct_name[i].upper_bound =\ + ITEM_BUF(i).range.u.bounds.upper.bound.u.ivalue;\ + }\ + } + +/*---------------------------------------------------------------------------*/ +static void fatal (char *str) +{ + yyerror (str); + exit(1); +} + +/*---------------------------------------------------------------------------*/ +static int find_conn_ref (name) + char *name; +{ + int i; + char str[130]; + + for (i = 0; i < TBL->num_conn; i++) { + if (strcmp (name, TBL->conn[i].name) == 0) { + return i; + } + } + sprintf (str, "Port `%s' not found", name); + yyerror (str); +} + +typedef enum {C_DOUBLE, C_BOOLEAN, C_POINTER, C_UNDEF} Ctype_Class_t; + +/*---------------------------------------------------------------------------*/ +static Ctype_Class_t get_ctype_class (Port_Type_t type) +{ + switch (type) { + case USER_DEFINED: + return C_POINTER; + break; + case DIGITAL: + return C_BOOLEAN; + break; + default: + return C_DOUBLE; + break; + } +} + +/*---------------------------------------------------------------------------*/ +static void check_port_type_direction (Dir_t dir, Port_Type_t port_type) +{ + switch (port_type) { + case VOLTAGE: + case DIFF_VOLTAGE: + case CURRENT: + case DIFF_CURRENT: + case DIGITAL: + case USER_DEFINED: + /* + * anything goes + */ + break; + case VSOURCE_CURRENT: + if (dir != IN) { + yyerror ("Port type `vnam' is only valid for `in' ports"); + } + break; + case CONDUCTANCE: + case DIFF_CONDUCTANCE: + case RESISTANCE: + case DIFF_RESISTANCE: + if (dir != INOUT) { + yyerror ("Port types `g', `gd', `h', `hd' are only valid for `inout' ports"); + } + break; + default: + assert (0); + } +} + +/*---------------------------------------------------------------------------*/ +static void check_dtype_not_pointer (Data_Type_t dtype) +{ + if (dtype == POINTER) { + yyerror("Invalid parameter type - POINTER type valid only for STATIC_VARs"); + } +} + +/*---------------------------------------------------------------------------*/ +static void check_default_type (Conn_Info_t conn) +{ + int i; + + for (i = 0; i < conn.num_allowed_types; i++) { + if (conn.default_port_type == conn.allowed_port_type[i]) { + if ((conn.default_port_type != USER_DEFINED) || + (strcmp (conn.default_type, conn.allowed_type[i]) == 0)) { + return; + } + } + } + yyerror ("Port default type is not an allowed type"); +} + +/*---------------------------------------------------------------------------*/ +static void assign_ctype_list (conn, ctype_list) + Conn_Info_t *conn; + Ctype_List_t *ctype_list; +{ + int i; + Ctype_List_t *p; + Ctype_Class_t class = C_UNDEF; + + conn->num_allowed_types = 0; + for (p = ctype_list; p; p = p->next) { + conn->num_allowed_types++; + } + conn->allowed_type = (char**) calloc (conn->num_allowed_types, + sizeof (char*)); + conn->allowed_port_type = (Port_Type_t*) calloc (conn->num_allowed_types, + sizeof (Port_Type_t)); + if (! (conn->allowed_type && conn->allowed_port_type)) { + fatal ("Could not allocate memory"); + } + for (i = conn->num_allowed_types-1, p = ctype_list; p; i--, p = p->next) { + if (class == C_UNDEF) { + class = get_ctype_class (p->ctype.kind); + } + if (class != get_ctype_class (p->ctype.kind)) { + yyerror ("Incompatible port types in `allowed_types' clause"); + } + check_port_type_direction (conn->direction, p->ctype.kind); + + conn->allowed_port_type[i] = p->ctype.kind; + conn->allowed_type[i] = p->ctype.id; + } +} + +/*---------------------------------------------------------------------------*/ +static void assign_value (type, dest_value, src_value) + Data_Type_t type; + Value_t *dest_value; + My_Value_t src_value; +{ + char str[200]; + if ((type == REAL) && (src_value.kind == INTEGER)) { + dest_value->rvalue = src_value.u.ivalue; + return; + } else if (type != src_value.kind) { + sprintf (str, "Invalid parameter type (saw %s - expected %s)", + dtype_to_str[src_value.kind], + dtype_to_str[type] ); + yyerror (str); + } + switch (type) { + case BOOLEAN: + dest_value->bvalue = src_value.u.bvalue; + break; + case INTEGER: + dest_value->ivalue = src_value.u.ivalue; + break; + case REAL: + dest_value->rvalue = src_value.u.rvalue; + break; + case COMPLEX: + dest_value->cvalue = src_value.u.cvalue; + break; + case STRING: + dest_value->svalue = src_value.u.svalue; + break; + default: + yyerror ("INTERNAL ERROR - unexpected data type in `assign_value'"); + } +} + +/*---------------------------------------------------------------------------*/ +static void assign_limits (type, param, range) + Data_Type_t type; + Param_Info_t *param; + Range_t range; +{ + if (range.is_named) { + yyerror ("Named range not allowed for limits"); + } + param->has_lower_limit = range.u.bounds.lower.has_bound; + if (param->has_lower_limit) { + assign_value (type, ¶m->lower_limit, range.u.bounds.lower.bound); + } + param->has_upper_limit = range.u.bounds.upper.has_bound; + if (param->has_upper_limit) { + assign_value (type, ¶m->upper_limit, range.u.bounds.upper.bound); + } +} + +/*---------------------------------------------------------------------------*/ +static void check_item_num () +{ + if (item-item_offset >= ITEM_BUFFER_SIZE) { + fatal ("Too many items in table - split into sub-tables"); + } + if (item > alloced_size [context.table] ) { + switch (context.table) { + case TBL_NAME: + break; + case TBL_PORT: + alloced_size[context.table] += GROW_SIZE; + TBL->conn = (Conn_Info_t*) + realloc (TBL->conn, + alloced_size [context.table] * sizeof (Conn_Info_t)); + if (! TBL->conn) { + fatal ("Error allocating memory for port definition"); + } + break; + case TBL_PARAMETER: + alloced_size [context.table] += GROW_SIZE; + TBL->param = (Param_Info_t*) + realloc (TBL->param, + alloced_size [context.table] * sizeof (Param_Info_t)); + if (! TBL->param) { + fatal ("Error allocating memory for parameter definition"); + } + break; + case TBL_STATIC_VAR: + alloced_size [context.table] += GROW_SIZE; + TBL->inst_var = (Inst_Var_Info_t*) + realloc (TBL->inst_var, + alloced_size [context.table] * sizeof (Inst_Var_Info_t)); + if (! TBL->inst_var) { + fatal ("Error allocating memory for static variable definition"); + } + break; + } + } + item++; +} + +/*---------------------------------------------------------------------------*/ +static void check_end_item_num () +{ + if (num_items_fixed) { + if (item != num_items) { + char buf[200]; + sprintf + (buf, + "Wrong number of elements in sub-table (saw %d - expected %d)", + item - item_offset, + num_items - item_offset); + fatal (buf); + } + } else { + num_items = item; + num_items_fixed = TRUE; + switch (context.table) { + case TBL_NAME: + break; + case TBL_PORT: + TBL->num_conn = num_items; + break; + case TBL_PARAMETER: + TBL->num_param = num_items; + break; + case TBL_STATIC_VAR: + TBL->num_inst_var = num_items; + break; + } + } + item = item_offset; +} + +#define INIT(n) item = (n); item_offset = (n); num_items = (n); num_items_fixed = FALSE +#define ITEM check_item_num() +#define END check_end_item_num() + +%} + +%token TOK_ALLOWED_TYPES +%token TOK_ARRAY +%token TOK_ARRAY_BOUNDS +%token TOK_BOOL_NO +%token TOK_BOOL_YES +%token TOK_COMMA +%token TOK_PORT_NAME +%token TOK_PORT_TABLE +%token TOK_CTYPE_D +%token TOK_CTYPE_G +%token TOK_CTYPE_GD +%token TOK_CTYPE_H +%token TOK_CTYPE_HD +%token TOK_CTYPE_I +%token TOK_CTYPE_ID +%token TOK_CTYPE_V +%token TOK_CTYPE_VD +%token TOK_CTYPE_VNAM +%token TOK_C_FUNCTION_NAME +%token TOK_DASH +%token TOK_DATA_TYPE +%token TOK_DEFAULT_TYPE +%token TOK_DEFAULT_VALUE +%token TOK_DESCRIPTION +%token TOK_DIRECTION +%token TOK_DIR_IN +%token TOK_DIR_INOUT +%token TOK_DIR_OUT +%token TOK_DTYPE_BOOLEAN +%token TOK_DTYPE_COMPLEX +%token TOK_DTYPE_INT +%token TOK_DTYPE_POINTER +%token TOK_DTYPE_REAL +%token TOK_DTYPE_STRING +%token TOK_IDENTIFIER +%token TOK_STATIC_VAR_NAME +%token TOK_STATIC_VAR_TABLE +%token TOK_INT_LITERAL +%token TOK_LANGLE +%token TOK_LBRACKET +%token TOK_LIMITS +%token TOK_NAME_TABLE +%token TOK_NULL_ALLOWED +%token TOK_PARAMETER_NAME +%token TOK_PARAMETER_TABLE +%token TOK_RANGLE +%token TOK_RBRACKET +%token TOK_REAL_LITERAL +%token TOK_SPICE_MODEL_NAME +%token TOK_STRING_LITERAL + +%union { + Ctype_List_t *ctype_list; + Dir_t dir; + Boolean_t bool; + Range_t range; + Data_Type_t dtype; + My_Port_Type_t ctype; + My_Value_t value; + char *str; + Bound_t bound; + int ival; + double rval; + Complex_t cval; +} + +%type ctype_list delimited_ctype_list +%type direction +%type ctype +%type dtype +%type range int_range +%type value number integer_value value_or_dash +%type identifier string +%type bool +%type int_or_dash number_or_dash +%type integer +%type real +%type complex + +%start ifs_file + +%{ +/* + * resuse the Yacc union for our buffer: + */ +YYSTYPE item_buffer [ITEM_BUFFER_SIZE]; + +/* + * Shorthand for refering to the current element of the item buffer: + */ +#define BUF ITEM_BUF(item-1) + +%} + +%% + +ifs_file : {TBL->num_conn = 0; + TBL->num_param = 0; + TBL->num_inst_var = 0; + + saw_function_name = FALSE; + saw_model_name = FALSE; + + alloced_size [TBL_PORT] = DEFAULT_SIZE_CONN; + alloced_size [TBL_PARAMETER] = DEFAULT_SIZE_PARAM; + alloced_size [TBL_STATIC_VAR] = + DEFAULT_SIZE_INST_VAR; + + TBL->conn = (Conn_Info_t*) + calloc (DEFAULT_SIZE_CONN, + sizeof (Conn_Info_t)); + TBL->param = (Param_Info_t*) + calloc (DEFAULT_SIZE_PARAM, + sizeof (Param_Info_t)); + TBL->inst_var = (Inst_Var_Info_t*) + calloc (DEFAULT_SIZE_INST_VAR, + sizeof (Inst_Var_Info_t)); + if (! (TBL->conn && TBL->param && + TBL->inst_var) ) { + fatal ("Could not allocate enough memory"); + } + } + list_of_tables + ; + +list_of_tables : table + | list_of_tables table + ; + +table : TOK_NAME_TABLE + {context.table = TBL_NAME;} + name_table + | TOK_PORT_TABLE + {context.table = TBL_PORT; + did_default_type = FALSE; + did_allowed_types = FALSE; + INIT (TBL->num_conn);} + port_table + {TBL->num_conn = num_items;} + | TOK_PARAMETER_TABLE + {context.table = TBL_PARAMETER; + INIT (TBL->num_param);} + parameter_table + {TBL->num_param = num_items;} + | TOK_STATIC_VAR_TABLE + {context.table = TBL_STATIC_VAR; + INIT (TBL->num_inst_var);} + static_var_table + {TBL->num_inst_var = num_items;} + ; + +name_table : /* empty */ + | name_table name_table_item + ; + +name_table_item : TOK_C_FUNCTION_NAME identifier + {TBL->name.c_fcn_name =strdup (ifs_yytext); + saw_function_name = TRUE; + if (parser_just_names && saw_model_name) return 0;} + | TOK_SPICE_MODEL_NAME identifier + {TBL->name.model_name = strdup (ifs_yytext); + saw_model_name = TRUE; + if (parser_just_names && saw_function_name) return 0;} + | TOK_DESCRIPTION string + {TBL->name.description = strdup (ifs_yytext);} + ; + +port_table : /* empty */ + | port_table port_table_item + ; + +port_table_item : TOK_PORT_NAME list_of_ids + {int i; + END; + FOR_ITEM (i) { + TBL->conn[i].name = ITEM_BUF(i).str; + }} + | TOK_DESCRIPTION list_of_strings + {int i; + END; + FOR_ITEM (i) { + TBL->conn[i].description = ITEM_BUF(i).str; + }} + | TOK_DIRECTION list_of_directions + {int i; + END; + FOR_ITEM (i) { + TBL->conn[i].direction = ITEM_BUF(i).dir; + }} + | TOK_DEFAULT_TYPE list_of_ctypes + {int i; + END; + did_default_type = TRUE; + FOR_ITEM (i) { + TBL->conn[i].default_port_type = + ITEM_BUF(i).ctype.kind; + TBL->conn[i].default_type = ITEM_BUF(i).ctype.id; + if (did_allowed_types) { + check_default_type (TBL->conn[i]); + } + }} + | TOK_ALLOWED_TYPES list_of_ctype_lists + {int i; + END; + did_allowed_types = TRUE; + FOR_ITEM (i) { + assign_ctype_list (&TBL->conn[i], + ITEM_BUF(i).ctype_list); + if (did_default_type) { + check_default_type (TBL->conn[i]); + } + }} + | TOK_ARRAY list_of_bool + {int i; + END; + FOR_ITEM (i) { + TBL->conn[i].is_array = ITEM_BUF(i).bool; + }} + | TOK_ARRAY_BOUNDS list_of_array_bounds + {int i; + END; + FOR_ITEM (i) { + ASSIGN_BOUNDS (conn, i); + assert (!TBL->conn[i].has_conn_ref); + }} + | TOK_NULL_ALLOWED list_of_bool + {int i; + END; + FOR_ITEM (i) { + TBL->conn[i].null_allowed = ITEM_BUF(i).bool; + }} + ; + +parameter_table : /* empty */ + | parameter_table parameter_table_item + ; + +parameter_table_item : TOK_PARAMETER_NAME list_of_ids + {int i; + END; + FOR_ITEM (i) { + TBL->param[i].name = ITEM_BUF(i).str; + }} + | TOK_DESCRIPTION list_of_strings + {int i; + END; + FOR_ITEM (i) { + TBL->param[i].description = ITEM_BUF(i).str; + }} + | TOK_DATA_TYPE list_of_dtypes + {int i; + END; + FOR_ITEM (i) { + check_dtype_not_pointer (ITEM_BUF(i).dtype); + TBL->param[i].type = ITEM_BUF(i).dtype; + }} + | TOK_DEFAULT_VALUE list_of_values + {int i; + END; + FOR_ITEM (i) { + TBL->param[i].has_default = + ITEM_BUF(i).value.has_value; + if (TBL->param[i].has_default) { + assign_value (TBL->param[i].type, + &TBL->param[i].default_value, + ITEM_BUF(i).value); + } + }} + | TOK_LIMITS list_of_ranges + {int i; + END; + FOR_ITEM (i) { + assign_limits (TBL->param[i].type, + &TBL->param[i], + ITEM_BUF(i).range); + }} + | TOK_ARRAY list_of_bool + {int i; + END; + FOR_ITEM (i) { + TBL->param[i].is_array = ITEM_BUF(i).bool; + }} + | TOK_ARRAY_BOUNDS list_of_array_bounds + {int i; + END; + FOR_ITEM (i) { + ASSIGN_BOUNDS (param, i); + }} + | TOK_NULL_ALLOWED list_of_bool + {int i; + END; + FOR_ITEM (i) { + TBL->param[i].null_allowed = ITEM_BUF(i).bool; + }} + ; + +static_var_table : /* empty */ + | static_var_table static_var_table_item + ; + +static_var_table_item : TOK_STATIC_VAR_NAME list_of_ids + {int i; + END; + FOR_ITEM (i) { + TBL->inst_var[i].name = ITEM_BUF(i).str; + }} + | TOK_DESCRIPTION list_of_strings + {int i; + END; + FOR_ITEM (i) { + TBL->inst_var[i].description = ITEM_BUF(i).str; + }} + | TOK_DATA_TYPE list_of_dtypes + {int i; + END; + FOR_ITEM (i) { + TBL->inst_var[i].type = ITEM_BUF(i).dtype; + }} + | TOK_ARRAY list_of_bool + {int i; + END; + FOR_ITEM (i) { + TBL->inst_var[i].is_array = ITEM_BUF(i).bool; + }} + ; + +list_of_ids : /* empty */ + | list_of_ids identifier {ITEM; BUF.str = $2;} + ; + +list_of_array_bounds : /* empty */ + | list_of_array_bounds int_range + {ITEM; + BUF.range = $2;} + | list_of_array_bounds identifier + {ITEM; + BUF.range.is_named = TRUE; + BUF.range.u.name = $2;} + ; + +list_of_strings : /* empty */ + | list_of_strings string {ITEM; BUF.str = $2;} + ; + +list_of_directions : /* empty */ + | list_of_directions direction {ITEM; BUF.dir = $2;} + ; + +direction : TOK_DIR_IN {$$ = IN;} + | TOK_DIR_OUT {$$ = OUT;} + | TOK_DIR_INOUT {$$ = INOUT;} + ; + +list_of_bool : /* empty */ + | list_of_bool bool {ITEM; BUF.bool = $2;} + ; + +list_of_ctypes : /* empty */ + | list_of_ctypes ctype {ITEM; BUF.ctype = $2;} + ; + +ctype : TOK_CTYPE_V {$$.kind = VOLTAGE;} + | TOK_CTYPE_VD {$$.kind = DIFF_VOLTAGE;} + | TOK_CTYPE_VNAM {$$.kind = VSOURCE_CURRENT;} + | TOK_CTYPE_I {$$.kind = CURRENT;} + | TOK_CTYPE_ID {$$.kind = DIFF_CURRENT;} + | TOK_CTYPE_G {$$.kind = CONDUCTANCE;} + | TOK_CTYPE_GD {$$.kind = DIFF_CONDUCTANCE;} + | TOK_CTYPE_H {$$.kind = RESISTANCE;} + | TOK_CTYPE_HD {$$.kind = DIFF_RESISTANCE;} + | TOK_CTYPE_D {$$.kind = DIGITAL;} + | identifier {$$.kind = USER_DEFINED; + $$.id = $1;} + ; + +list_of_dtypes : /* empty */ + | list_of_dtypes dtype {ITEM; BUF.dtype = $2;} + ; + +dtype : TOK_DTYPE_REAL {$$ = REAL;} + | TOK_DTYPE_INT {$$ = INTEGER;} + | TOK_DTYPE_BOOLEAN {$$ = BOOLEAN;} + | TOK_DTYPE_COMPLEX {$$ = COMPLEX;} + | TOK_DTYPE_STRING {$$ = STRING;} + | TOK_DTYPE_POINTER {$$ = POINTER;} + ; + +list_of_ranges : /* empty */ + | list_of_ranges range {ITEM; BUF.range = $2;} + ; + +int_range : TOK_DASH {$$.is_named = FALSE; + $$.u.bounds.lower.has_bound = FALSE; + $$.u.bounds.upper.has_bound = FALSE;} + | TOK_LBRACKET int_or_dash maybe_comma int_or_dash + TOK_RBRACKET + {$$.is_named = FALSE; + $$.u.bounds.lower = $2; + $$.u.bounds.upper = $4;} + ; + +maybe_comma : /* empty */ + | TOK_COMMA + ; + +int_or_dash : TOK_DASH {$$.has_bound = FALSE;} + | integer_value {$$.has_bound = TRUE; + $$.bound = $1;} + ; + +range : TOK_DASH {$$.is_named = FALSE; + $$.u.bounds.lower.has_bound = FALSE; + $$.u.bounds.upper.has_bound = FALSE;} + | TOK_LBRACKET number_or_dash maybe_comma + number_or_dash TOK_RBRACKET + {$$.is_named = FALSE; + $$.u.bounds.lower = $2; + $$.u.bounds.upper = $4;} + ; + +number_or_dash : TOK_DASH {$$.has_bound = FALSE;} + | number {$$.has_bound = TRUE; + $$.bound = $1;} + ; + +list_of_values : /* empty */ + | list_of_values value_or_dash {ITEM; BUF.value = $2;} + ; + +value_or_dash : TOK_DASH {$$.has_value = FALSE;} + | value + ; + +value : string {$$.has_value = TRUE; + $$.kind = STRING; + $$.u.svalue = $1;} + | bool {$$.has_value = TRUE; + $$.kind = BOOLEAN; + $$.u.bvalue = $1;} + | complex {$$.has_value = TRUE; + $$.kind = COMPLEX; + $$.u.cvalue = $1;} + | number + ; + +complex : TOK_LANGLE real maybe_comma real TOK_RANGLE + {$$.real = $2; + $$.imag = $4;} + ; + +list_of_ctype_lists : /* empty */ + | list_of_ctype_lists delimited_ctype_list + {ITEM; BUF.ctype_list = $2;} + ; + +delimited_ctype_list : TOK_LBRACKET ctype_list TOK_RBRACKET {$$ = $2;} + ; + +ctype_list : ctype + {$$ = (Ctype_List_t*)calloc (1, + sizeof (Ctype_List_t)); + if (!$$) { + fatal ("Error allocating memory"); + } + $$->ctype = $1; + $$->next = (Ctype_List_t*)0;} + | ctype_list maybe_comma ctype + {$$ = (Ctype_List_t*)calloc (1, + sizeof (Ctype_List_t)); + if (!$$) { + fatal ("Error allocating memory"); + } + $$->ctype = $3; + $$->next = $1; + /*$$->next = (Ctype_List_t*)0; + assert ($1); + $1->next = $$;*/} + ; + +bool : TOK_BOOL_YES {$$ = TRUE;} + | TOK_BOOL_NO {$$ = FALSE;} + ; + +string : TOK_STRING_LITERAL {$$ = strdup(ifs_yytext);} + ; + +identifier : TOK_IDENTIFIER {$$ = strdup(ifs_yytext);} + ; + +number : real {$$.has_value = TRUE; + $$.kind = REAL; + $$.u.rvalue = $1;} + | integer_value + ; + +integer_value : integer {$$.has_value = TRUE; + $$.kind = INTEGER; + $$.u.ivalue = $1;} + ; + +real : TOK_REAL_LITERAL {$$ = yydval;} + ; + +integer : TOK_INT_LITERAL {$$ = yyival;} + ; + +%% diff --git a/src/xspice/cmpp/main.c b/src/xspice/cmpp/main.c new file mode 100755 index 000000000..20d796fb2 --- /dev/null +++ b/src/xspice/cmpp/main.c @@ -0,0 +1,125 @@ +/*============================================================================ +FILE main.c + +MEMBER OF process cmpp + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the top-level function for the Code Model + PreProcessor (cmpp). It handles reading the command-line + arguments, and then vectors to an appropriate function. + +INTERFACES + + main() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include "cmpp.h" + + +#define USAGE_MSG "Usage: cmpp [-ifs] [-mod []] [-lst]" +#define TOO_FEW_ARGS "ERROR - Too few arguments" +#define TOO_MANY_ARGS "ERROR - Too many arguments" +#define UNRECOGNIZED_ARGS "ERROR - Unrecognized argument" + + + +/* *********************************************************************** */ + + +/* +main + +Function main checks the validity of the command-line arguments +supplied when the program is invoked and calls one of the three +major functions as appropriate: + + preprocess_ifs_file Process Interface Specification File. + preprocess_mod_file Process Model Definition File. + preprocess_lst_file Process Pathname List Files. + +depending on the argument. +*/ + +main( + int argc, /* Number of command line arguments */ + char *argv[]) /* Command line argument text */ +{ + + init_error (argv[0]); + + /* Process command line arguments and vector to appropriate function */ + + if(argc < 2) { + print_error(TOO_FEW_ARGS); + print_error(USAGE_MSG); + exit(1); + } + + if(strcmp(argv[1],"-ifs") == 0) { + if(argc == 2) { + preprocess_ifs_file(); + } + else { + print_error(TOO_MANY_ARGS); + print_error(USAGE_MSG); + exit(1); + } + } + else if(strcmp(argv[1],"-lst") == 0) { + if(argc == 2) { + preprocess_lst_files(); + } + else { + print_error(TOO_MANY_ARGS); + print_error(USAGE_MSG); + exit(1); + } + } + else if(strcmp(argv[1],"-mod") == 0) { + if(argc == 2) { + preprocess_mod_file("cfunc.mod"); + } + else if(argc == 3) { + preprocess_mod_file(argv[2]); + } + else { + print_error(TOO_MANY_ARGS); + print_error(USAGE_MSG); + exit(1); + } + } + else { + print_error(UNRECOGNIZED_ARGS); + print_error(USAGE_MSG); + exit(1); + } + + exit(0); +} + diff --git a/src/xspice/cmpp/mod_lex.l b/src/xspice/cmpp/mod_lex.l new file mode 100755 index 000000000..bccbb33aa --- /dev/null +++ b/src/xspice/cmpp/mod_lex.l @@ -0,0 +1,107 @@ +%option yylineno +%option noyywrap +%{ /* $Id$ */ + +/*============================================================================ +FILE mod_lex.l + +MEMBER OF process cmpp + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Steve Tynor + +MODIFICATIONS + + + +SUMMARY + + This file defines tokens applicable to parsing the cfunc.mod + file, and actions to be taken on encountering those tokens. + +INTERFACES + + None. + +REFERENCED FILES + + mod_yacc.y + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include "mod_yacc.h" +#include "mod_tok.h" + +%} + +I [A-Za-z_] +Z [0-9A-Za-z_] + +%% + +"/*" {char ch, last_ch; + ECHO; /* a comment - repeat it */ + ch = '\0'; + do { + last_ch = ch; + ch = input(); + fputc(ch,mod_yyout); + } while (ch && !((last_ch == '*') && (ch == '/'))); + if (!ch) {mod_yyerror ("Unterminated comment");}} + +ARGS {return TOK_ARGS;} +INIT {return TOK_INIT;} +ANALYSIS {return TOK_ANALYSIS;} +NEW_TIMEPOINT {return TOK_NEW_TIMEPOINT;} +CALL_TYPE {return TOK_CALL_TYPE;} +TIME {return TOK_TIME;} +RAD_FREQ {return TOK_RAD_FREQ;} +TEMPERATURE {return TOK_TEMPERATURE;} +T {return TOK_T;} +LOAD {return TOK_LOAD;} +TOTAL_LOAD {return TOK_TOTAL_LOAD;} +MESSAGE {return TOK_MESSAGE;} +PARAM {return TOK_PARAM;} +PARAM_SIZE {return TOK_PARAM_SIZE;} +PARAM_NULL {return TOK_PARAM_NULL;} +PORT_SIZE {return TOK_PORT_SIZE;} +PORT_NULL {return TOK_PORT_NULL;} +PARTIAL {return TOK_PARTIAL;} +AC_GAIN {return TOK_AC_GAIN;} +OUTPUT_DELAY {return TOK_OUTPUT_DELAY;} +STATIC_VAR {return TOK_STATIC_VAR;} +STATIC_VAR_SIZE {return TOK_STATIC_VAR_SIZE;} +INPUT {return TOK_INPUT;} +INPUT_STATE {return TOK_INPUT_STATE;} +INPUT_TYPE {return TOK_INPUT_TYPE;} +INPUT_STRENGTH {return TOK_INPUT_STRENGTH;} +OUTPUT {return TOK_OUTPUT;} +OUTPUT_STATE {return TOK_OUTPUT_STATE;} +OUTPUT_STRENGTH {return TOK_OUTPUT_STRENGTH;} +OUTPUT_TYPE {return TOK_OUTPUT_TYPE;} +OUTPUT_CHANGED {return TOK_OUTPUT_CHANGED;} + +"(" {return TOK_LPAREN;} +")" {return TOK_RPAREN;} +"[" {return TOK_LBRACKET;} +"]" {return TOK_RBRACKET;} +"," {return TOK_COMMA;} + +{I}+{Z}* {return TOK_IDENTIFIER;} +[ \t] ECHO; /* just eat non-newline whitespace */ +\n ECHO; /* echo newlines */ +. {return TOK_MISC_C;} + +%% diff --git a/src/xspice/cmpp/mod_yacc.h b/src/xspice/cmpp/mod_yacc.h new file mode 100755 index 000000000..4e1a1bca4 --- /dev/null +++ b/src/xspice/cmpp/mod_yacc.h @@ -0,0 +1,49 @@ +/* $Id$ */ + +/*============================================================================ +FILE mod_yacc.h + +MEMBER OF process cmpp + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Steve Tynor + +MODIFICATIONS + + + +SUMMARY + + Typedefs needed by the YYSTYPE union (%union operator) in the yacc + file. These are only used in the yacc file, but must be defined here since + the generated token.h file includes a definition of the union YYSTYPE. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include "cmpp.h" + +typedef struct { + char *id; + Boolean_t has_subscript; + char *subscript; +} Sub_Id_t; diff --git a/src/xspice/cmpp/mod_yacc.y b/src/xspice/cmpp/mod_yacc.y new file mode 100755 index 000000000..2a6e88f6c --- /dev/null +++ b/src/xspice/cmpp/mod_yacc.y @@ -0,0 +1,559 @@ +%{ /* $Id$ */ + +/*============================================================================ +FILE mod_yacc.y + +MEMBER OF process cmpp + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Steve Tynor + +MODIFICATIONS + + + +SUMMARY + + This file contains a BNF specification of the translation of + cfunc.mod files to cfunc.c files, together with various support + functions. + +INTERFACES + + mod_yyparse() - Function 'yyparse()' is generated automatically + by UNIX 'yacc' utility and then converted to + 'mod_yyparse()' by UNIX 'sed' utility under + direction of Makefile. + +REFERENCED FILES + + mod_lex.l + +NON-STANDARD FEATURES + + Names of functions generated by 'yacc' are translated by 'sed' + under direction of the Makefile to prevent collisions with + functions generated from ifs_yacc.y. + +============================================================================*/ + + +#include +#include +#include "mod_yacc.h" + +Ifs_Table_t *mod_ifs_table; + +extern char *mod_yytext; +extern FILE* mod_yyout; + +#include +#include + +int mod_num_errors; + +#define BUFFER_SIZE 3000 +static char buffer [BUFFER_SIZE]; +static int buf_len; + +typedef enum {CONN, PARAM, STATIC_VAR} Id_Kind_t; + +/*--------------------------------------------------------------------------*/ +static char *subscript (Sub_Id_t sub_id) +{ + if (sub_id.has_subscript) { + return sub_id.subscript; + } else { + return "0"; + } +} + +/*--------------------------------------------------------------------------*/ +int strcmpi(s, t) + char *s; + char *t; + /* string compare - case insensitive */ +{ + for (; *s && t && tolower(*s) == tolower(*t); s++, t++); + if (*s && !*t) { + return 1; + } + if (!*s && *t) { + return -1; + } + if (! (*s || *t)) { + return 0; + } + return (tolower(*s) - tolower(*t)); +} + +/*---------------------------------------------------------------------------*/ +static void put_type (FILE *fp, Data_Type_t type) +{ + char ch; + + switch (type) { + case INTEGER: + ch = 'i'; + break; + case REAL: + ch = 'r'; + break; + case COMPLEX: + ch = 'c'; + break; + case BOOLEAN: + ch = 'b'; + break; + case STRING: + ch = 's'; + break; + case POINTER: + ch = 'p'; + break; + } + fprintf (fp, ".%cvalue", ch); +} + +/*---------------------------------------------------------------------------*/ +static void put_conn_type (FILE *fp, Port_Type_t type) +{ + char ch; + + switch (type) { + case USER_DEFINED: + ch = 'p'; + break; + case DIGITAL: + ch = 'p'; + break; + default: + ch = 'r'; + break; + } + fprintf (fp, ".%cvalue", ch); +} + +/*---------------------------------------------------------------------------*/ +static void check_dir (int conn_number, Dir_t dir, char *context) +{ + Dir_t conn_dir; + + if (conn_number >= 0) { + /* + * If negative, this is an invalid port ID and we've already issued + * an error. + */ + conn_dir = mod_ifs_table->conn[conn_number].direction; + if ((conn_dir != dir) && (conn_dir != INOUT)) { + char error_str[200]; + + sprintf (error_str, + "Direction of port `%s' in %s() is not %s or INOUT", + mod_ifs_table->conn[conn_number].name, context, + (dir == IN) ? "IN" : "OUT"); + yyerror (error_str); + mod_num_errors++; + } + } +} + +/*---------------------------------------------------------------------------*/ +static void check_subscript (Boolean_t formal, Boolean_t actual, + Boolean_t missing_actual_ok, + char *context, char *id) +{ + char error_str[200]; + + if ((formal && !actual) && !missing_actual_ok) { + sprintf (error_str, + "%s `%s' is an array - subscript required", + context, id); + yyerror (error_str); + mod_num_errors++; + return; + } else if (!formal && actual) { + sprintf (error_str, + "%s `%s' is not an array - subscript prohibited", + context, id); + yyerror (error_str); + mod_num_errors++; + return; + } +} + +/*---------------------------------------------------------------------------*/ +static int check_id (Sub_Id_t sub_id, Id_Kind_t kind, Boolean_t do_subscript) +{ + int i; + char error_str[200]; + + switch (kind) { + case CONN: + for (i = 0; i < mod_ifs_table->num_conn; i++) { + if (0 == strcmpi (sub_id.id, mod_ifs_table->conn[i].name)) { + if (do_subscript) { + check_subscript (mod_ifs_table->conn[i].is_array, + sub_id.has_subscript, FALSE, "Port", + sub_id.id); + } + return i; + } + } + break; + case PARAM: + for (i = 0; i < mod_ifs_table->num_param; i++) { + if (0 == strcmpi (sub_id.id, mod_ifs_table->param[i].name)) { + if (do_subscript) { + check_subscript (mod_ifs_table->param[i].is_array, + sub_id.has_subscript, FALSE, "Parameter", + sub_id.id); + } + return i; + } + } + break; + case STATIC_VAR: + for (i = 0; i < mod_ifs_table->num_inst_var; i++) { + if (0 == strcmpi (sub_id.id, mod_ifs_table->inst_var[i].name)) { + if (do_subscript) { + check_subscript (mod_ifs_table->inst_var[i].is_array, + sub_id.has_subscript, TRUE, + "Static Variable", + sub_id.id); + } + return i; + } + } + break; + } + + sprintf (error_str, "No %s named '%s'", + ((kind==CONN) + ? "port" + : ((kind==PARAM) + ? "parameter" + :"static variable")), + sub_id.id); + yyerror (error_str); + mod_num_errors++; + return -1; +} + +/*---------------------------------------------------------------------------*/ +static int valid_id (Sub_Id_t sub_id, Id_Kind_t kind) +{ + return check_id (sub_id, kind, FALSE); +} + +/*---------------------------------------------------------------------------*/ +static int valid_subid (Sub_Id_t sub_id, Id_Kind_t kind) +{ + return check_id (sub_id, kind, TRUE); +} + +/*---------------------------------------------------------------------------*/ +static init_buffer () +{ + buf_len = 0; + buffer[0] = '\0'; +} + +/*---------------------------------------------------------------------------*/ +static append (char *str) +{ + int len = strlen (str); + if (len + buf_len > BUFFER_SIZE) { + yyerror ("Buffer overflow - try reducing the complexity of CM-macro array subscripts"); + exit (1); + } + (void)strcat (buffer,str); +} + +%} + +%union { + char *str; + Sub_Id_t sub_id; +} + +%type buffered_c_code +%type subscriptable_id id + +%token TOK_ARGS +%token TOK_INIT +%token TOK_ANALYSIS +%token TOK_NEW_TIMEPOINT +%token TOK_TIME +%token TOK_RAD_FREQ +%token TOK_TEMPERATURE +%token TOK_T +%token TOK_PARAM +%token TOK_PARAM_SIZE +%token TOK_PARAM_NULL +%token TOK_PORT_SIZE +%token TOK_PORT_NULL +%token TOK_PARTIAL +%token TOK_AC_GAIN +%token TOK_CHANGED +%token TOK_OUTPUT_DELAY +%token TOK_STATIC_VAR +%token TOK_STATIC_VAR_SIZE +%token TOK_INPUT +%token TOK_INPUT_STRENGTH +%token TOK_INPUT_STATE +%token TOK_INPUT_TYPE +%token TOK_OUTPUT +%token TOK_OUTPUT_CHANGED +%token TOK_OUTPUT_STRENGTH +%token TOK_OUTPUT_STATE +%token TOK_OUTPUT_TYPE +%token TOK_COMMA +%token TOK_LPAREN +%token TOK_RPAREN +%token TOK_LBRACKET +%token TOK_RBRACKET +%token TOK_MISC_C +%token TOK_IDENTIFIER +%token TOK_LOAD +%token TOK_TOTAL_LOAD +%token TOK_MESSAGE +%token TOK_CALL_TYPE + +%start mod_file + +%% + +mod_file : /* empty */ + | mod_file c_code + ; + +c_code : /* empty */ + | c_code c_char + | c_code macro + /*| TOK_RPAREN {yyerror ("Unmatched )"); YYERROR;} + | TOK_RBRACKET {yyerror ("Unmatched ]"); YYERROR;}*/ + ; + +buffered_c_code : {init_buffer();} buffered_c_code2 + {$$ = strdup (buffer);} + ; + +buffered_c_code2 : /* empty */ + | buffered_c_code2 buffered_c_char + ; + +buffered_c_char : TOK_IDENTIFIER {append (mod_yytext);} + | TOK_MISC_C {append (mod_yytext);} + | TOK_COMMA {append (mod_yytext);} + | TOK_LBRACKET + {append("[");} + buffered_c_code2 TOK_RBRACKET + {append("]");} + | TOK_LPAREN + {append("(");} + buffered_c_code2 TOK_RPAREN + {append(")");} + ; + +c_char : TOK_IDENTIFIER {fputs (mod_yytext, mod_yyout);} + | TOK_MISC_C {fputs (mod_yytext, mod_yyout);} + | TOK_COMMA {fputs (mod_yytext, mod_yyout);} + | TOK_LBRACKET + {putc ('[', mod_yyout);} + c_code TOK_RBRACKET + {putc (']', mod_yyout);} + | TOK_LPAREN + {putc ('(', mod_yyout);} + c_code TOK_RPAREN + {putc (')', mod_yyout);} + ; + +macro : TOK_INIT + {fprintf (mod_yyout, "private->circuit.init");} + | TOK_ARGS + {fprintf (mod_yyout, "Mif_Private_t *private");} + | TOK_ANALYSIS + {fprintf (mod_yyout, "private->circuit.anal_type");} + | TOK_NEW_TIMEPOINT + {fprintf (mod_yyout, "private->circuit.anal_init");} + | TOK_CALL_TYPE + {fprintf (mod_yyout, "private->circuit.call_type");} + | TOK_TIME + {fprintf (mod_yyout, "private->circuit.time");} + | TOK_RAD_FREQ + {fprintf (mod_yyout, "private->circuit.frequency");} + | TOK_TEMPERATURE + {fprintf (mod_yyout, "private->circuit.temperature");} + | TOK_T TOK_LPAREN buffered_c_code TOK_RPAREN + {fprintf (mod_yyout, "private->circuit.t[%s]", $3);} + | TOK_PARAM TOK_LPAREN subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, PARAM); + fprintf (mod_yyout, "private->param[%d]->element[%s]", + i, subscript ($3)); + put_type (mod_yyout, mod_ifs_table->param[i].type); + } + | TOK_PARAM_SIZE TOK_LPAREN id TOK_RPAREN + {int i = valid_id ($3, PARAM); + fprintf (mod_yyout, "private->param[%d]->size", i);} + | TOK_PARAM_NULL TOK_LPAREN id TOK_RPAREN + {int i = valid_id ($3, PARAM); + fprintf (mod_yyout, "private->param[%d]->is_null", i);} + | TOK_PORT_SIZE TOK_LPAREN id TOK_RPAREN + {int i = valid_id ($3, CONN); + fprintf (mod_yyout, "private->conn[%d]->size", i);} + | TOK_PORT_NULL TOK_LPAREN id TOK_RPAREN + {int i = valid_id ($3, CONN); + fprintf (mod_yyout, "private->conn[%d]->is_null", i);} + | TOK_PARTIAL TOK_LPAREN subscriptable_id TOK_COMMA + subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, CONN); + int j = valid_subid ($5, CONN); + check_dir (i, OUT, "PARTIAL"); + check_dir (j, IN, "PARTIAL"); + fprintf (mod_yyout, "private->conn[%d]->port[%s]->partial[%d].port[%s]", + i, subscript($3), j, subscript($5));} + | TOK_AC_GAIN TOK_LPAREN subscriptable_id TOK_COMMA + subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, CONN); + int j = valid_subid ($5, CONN); + check_dir (i, OUT, "AC_GAIN"); + check_dir (j, IN, "AC_GAIN"); + fprintf (mod_yyout, + "private->conn[%d]->port[%s]->ac_gain[%d].port[%s]", + i, subscript($3), j, subscript($5));} + | TOK_STATIC_VAR TOK_LPAREN subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, STATIC_VAR); + fprintf (mod_yyout, + "private->inst_var[%d]->element[%s]", + i, subscript($3)); + if (mod_ifs_table->inst_var[i].is_array + && !($3.has_subscript)) { + /* null - eg. for malloc lvalue */ + } else { + put_type (mod_yyout, + mod_ifs_table->inst_var[i].type); + } } + | TOK_STATIC_VAR_SIZE TOK_LPAREN id TOK_RPAREN + {int i = valid_subid ($3, STATIC_VAR); + fprintf (mod_yyout, "private->inst_var[%d]->size", + i, subscript($3));} + | TOK_OUTPUT_DELAY TOK_LPAREN subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, CONN); + check_dir (i, OUT, "OUTPUT_DELAY"); + fprintf (mod_yyout, + "private->conn[%d]->port[%s]->delay", i, + subscript($3));} + | TOK_CHANGED TOK_LPAREN subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, CONN); + check_dir (i, OUT, "CHANGED"); + fprintf (mod_yyout, + "private->conn[%d]->port[%s]->changed", i, + subscript($3));} + | TOK_INPUT TOK_LPAREN subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, CONN); + check_dir (i, IN, "INPUT"); + fprintf (mod_yyout, + "private->conn[%d]->port[%s]->input", + i, subscript($3)); + put_conn_type (mod_yyout, + mod_ifs_table->conn[i].allowed_port_type[0]);} + | TOK_INPUT_TYPE TOK_LPAREN subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, CONN); + check_dir (i, IN, "INPUT_TYPE"); + fprintf (mod_yyout, + "private->conn[%d]->port[%s]->type_str", + i, subscript($3)); } + | TOK_OUTPUT_TYPE TOK_LPAREN subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, CONN); + check_dir (i, OUT, "OUTPUT_TYPE"); + fprintf (mod_yyout, + "private->conn[%d]->port[%s]->type_str", + i, subscript($3)); } + | TOK_INPUT_STRENGTH TOK_LPAREN subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, CONN); + check_dir (i, IN, "INPUT_STRENGTH"); + fprintf (mod_yyout, + "((Digital_t*)(private->conn[%d]->port[%s]->input", + i, subscript($3)); + put_conn_type (mod_yyout, + mod_ifs_table->conn[i].allowed_port_type[0]); + fprintf (mod_yyout, "))->strength");} + | TOK_INPUT_STATE TOK_LPAREN subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, CONN); + check_dir (i, IN, "INPUT_STATE"); + fprintf (mod_yyout, + "((Digital_t*)(private->conn[%d]->port[%s]->input", + i, subscript($3)); + put_conn_type (mod_yyout, + mod_ifs_table->conn[i].allowed_port_type[0]); + fprintf (mod_yyout, "))->state");} + | TOK_OUTPUT TOK_LPAREN subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, CONN); + check_dir (i, OUT, "OUTPUT"); + fprintf (mod_yyout, + "private->conn[%d]->port[%s]->output", + i, subscript($3)); + put_conn_type (mod_yyout, + mod_ifs_table->conn[i].allowed_port_type[0]);} + | TOK_OUTPUT_STRENGTH TOK_LPAREN subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, CONN); + check_dir (i, OUT, "OUTPUT_STRENGTH"); + fprintf (mod_yyout, + "((Digital_t*)(private->conn[%d]->port[%s]->output", + i, subscript($3)); + put_conn_type (mod_yyout, + mod_ifs_table->conn[i].allowed_port_type[0]); + fprintf (mod_yyout, "))->strength");} + | TOK_OUTPUT_STATE TOK_LPAREN subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, CONN); + check_dir (i, OUT, "OUTPUT_STATE"); + fprintf (mod_yyout, + "((Digital_t*)(private->conn[%d]->port[%s]->output", + i, subscript($3)); + put_conn_type (mod_yyout, + mod_ifs_table->conn[i].allowed_port_type[0]); + fprintf (mod_yyout, "))->state");} + | TOK_OUTPUT_CHANGED TOK_LPAREN subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, CONN); + fprintf (mod_yyout, + "private->conn[%d]->port[%s]->changed", i, + subscript($3));} + | TOK_LOAD TOK_LPAREN subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, CONN); + fprintf (mod_yyout, + "private->conn[%d]->port[%s]->load", i, + subscript($3));} + | TOK_TOTAL_LOAD TOK_LPAREN subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, CONN); + fprintf (mod_yyout, + "private->conn[%d]->port[%s]->total_load", i, + subscript($3));} + | TOK_MESSAGE TOK_LPAREN subscriptable_id TOK_RPAREN + {int i = valid_subid ($3, CONN); + fprintf (mod_yyout, + "private->conn[%d]->port[%s]->msg", i, + subscript($3));} + ; + +subscriptable_id : id + | id TOK_LBRACKET buffered_c_code TOK_RBRACKET + {$$ = $1; + $$.has_subscript = TRUE; + $$.subscript = $3;} + ; + +id : TOK_IDENTIFIER + {$$.has_subscript = FALSE; + $$.id = strdup (mod_yytext);} + ; + +%% diff --git a/src/xspice/cmpp/pp_ifs.c b/src/xspice/cmpp/pp_ifs.c new file mode 100755 index 000000000..c92d5adb7 --- /dev/null +++ b/src/xspice/cmpp/pp_ifs.c @@ -0,0 +1,88 @@ +/*============================================================================ +FILE pp_ifs.c + +MEMBER OF process cmpp + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the main function for processing an Interface Spec + File (ifspec.ifs). + +INTERFACES + + preprocess_ifs_file() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include "cmpp.h" + + + +/* *********************************************************************** */ + + +/* +preprocess_ifs_file + +Function preprocess_ifs_file is the top-level driver function for +preprocessing an Interface Specification file (ifspec.ifs). This +function calls read_ifs_file() requesting it to read and parse +the Interface Specification file and place the information +contained in it into an internal data structure. Then +write_ifs_c_file() is called to write the information out in a C +file that will be compiled and linked with the simulator. +*/ + + +void preprocess_ifs_file(void) +{ + + Ifs_Table_t ifs_table; /* Repository for info read from ifspec.ifs file */ + + Status_t status; /* Return status */ + + + /* Read the entire ifspec.ifs file and load the data into ifs_table */ + + status = read_ifs_file(IFSPEC_FILENAME,GET_IFS_TABLE,&ifs_table); + + if(status != OK) { + exit(1); + } + + + /* Write the ifspec.c file required by the spice simulator */ + + status = write_ifs_c_file("ifspec.c",&ifs_table); + + if(status != OK) { + exit(1); + } + +} + + + diff --git a/src/xspice/cmpp/pp_lst.c b/src/xspice/cmpp/pp_lst.c new file mode 100755 index 000000000..69a9b091c --- /dev/null +++ b/src/xspice/cmpp/pp_lst.c @@ -0,0 +1,1082 @@ +/*============================================================================ +FILE pp_lst.c + +MEMBER OF process cmpp + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains functions used in processing the files: + + modpath.lst + udnpath.lst + + The files 'modpath.lst' and 'udnpath.lst' are read to get + the pathnames to directories containing the models and node types + desired in the simulator to be built. Files in each of these + directories are then examined to get the names of functions and/or + data structures that the simulator must know about. The names + are then checked for uniqueness, and finally, a collection of + files needed in the 'make' for the simulator are written. + +INTERFACES + + preprocess_lst_files() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include "cmpp.h" +#include +#include + + +void *malloc(unsigned size); +void *realloc(void *ptr, unsigned size); + +/* *********************************************************************** */ + +/* + * Information for processing the pathname files + */ + +typedef struct { + char *path_name; /* Pathname read from model path file */ + char *spice_name; /* Name of model from ifspec.ifs */ + char *cfunc_name; /* Name of C fcn from ifspec.ifs */ + Boolean_t spice_unique; /* True if spice name unique */ + Boolean_t cfunc_unique; /* True if C fcn name unique */ +} Model_Info_t; + + +typedef struct { + char *path_name; /* Pathname read from udn path file */ + char *node_name; /* Name of node type */ + Boolean_t unique; /* True if node type name unique */ +} Node_Info_t; + + + +/* *********************************************************************** */ + + +static Status_t read_modpath(int *num_models, Model_Info_t **model_info); +static Status_t read_udnpath(int *num_nodes, Node_Info_t **node_info); +static Status_t read_model_names(int num_models, Model_Info_t *model_info); +static Status_t read_node_names(int num_nodes, Node_Info_t *node_info); +static Status_t check_uniqueness(int num_models, Model_Info_t *model_info, + int num_nodes, Node_Info_t *node_info); +static Status_t write_CMextrn(int num_models, Model_Info_t *model_info); +static Status_t write_CMinfo(int num_models, Model_Info_t *model_info); +static Status_t write_UDNextrn(int num_nodes, Node_Info_t *node_info); +static Status_t write_UDNinfo(int num_nodes, Node_Info_t *node_info); +static Status_t write_objects_inc(int num_models, Model_Info_t *model_info, + int num_nodes, Node_Info_t *node_info); +static Status_t read_udn_type_name(char *path, char **node_name); + + +/* *********************************************************************** */ + + +/* +preprocess_lst_files + +Function preprocess_lst_files is the top-level driver function for +preprocessing a simulator model path list file (modpath.lst). +This function calls read_ifs_file() requesting it to read and +parse the Interface Specification file (ifspec.ifs) to extract +the model name and associated C function name and place this +information into an internal data structure. It then calls +check_uniqueness() to verify that the model names and function +names are unique with respect to each other and to the models and +functions internal to SPICE 3C1. Following this check, it calls +write_CMextrn(), write_CMinfo(), and write_make_include() to +write out the C include files CMextrn.h and CMinfo.h required by +SPICE function SPIinit.c to define the models known to the +simulator, and to write out a make include file used by the make +utility to locate the object modules necessary to link the models +into the simulator. +*/ + +void preprocess_lst_files(void) +{ + Status_t status; /* Return status */ + Model_Info_t *model_info; /* Info about each model */ + Node_Info_t *node_info; /* Info about each user-defined node type */ + int num_models; /* The number of models */ + int num_nodes; /* The number of user-defined nodes */ + + + /* Get list of models from model pathname file */ + status = read_modpath(&num_models, &model_info); + if(status != OK) { + exit(1); + } + + /* Get list of node types from udn pathname file */ + status = read_udnpath(&num_nodes, &node_info); + if(status != OK) { + exit(1); + } + + + /* Get the spice and C function names from the ifspec.ifs files */ + status = read_model_names(num_models, model_info); + if(status != OK) { + exit(1); + } + + /* Get the user-defined node type names */ + status = read_node_names(num_nodes, node_info); + if(status != OK) { + exit(1); + } + + + /* Check to be sure the names are unique */ + status = check_uniqueness(num_models, model_info, + num_nodes, node_info); + if(status != OK) { + exit(1); + } + + + /* Write out the CMextrn.h file used to compile SPIinit.c */ + status = write_CMextrn(num_models, model_info); + if(status != OK) { + exit(1); + } + + /* Write out the CMinfo.h file used to compile SPIinit.c */ + status = write_CMinfo(num_models, model_info); + if(status != OK) { + exit(1); + } + + + /* Write out the UDNextrn.h file used to compile SPIinit.c */ + status = write_UDNextrn(num_nodes, node_info); + if(status != OK) { + exit(1); + } + + /* Write out the UDNinfo.h file used to compile SPIinit.c */ + status = write_UDNinfo(num_nodes, node_info); + if(status != OK) { + exit(1); + } + + + /* Write the make_include file used to link the models and */ + /* user-defined node functions with the simulator */ + status = write_objects_inc(num_models, model_info, + num_nodes, node_info); + if(status != OK) { + exit(1); + } + +} + + + +/* *********************************************************************** */ + + +/* +read_modpath + +This function opens the modpath.lst file, reads the pathnames from the +file, and puts them into an internal data structure for future +processing. +*/ + + +static Status_t read_modpath( + int *num_models, /* Number of model pathnames found */ + Model_Info_t **model_info /* Info about each model */ +) +{ + FILE *fp; /* Model pathname file pointer */ + char msg[MAX_PATH_LEN+257]; /* space for an error message */ + char path[MAX_PATH_LEN+2]; /* space to read pathnames into */ + Model_Info_t *model; /* temporary pointer to model info */ + + int n; + int i; + int j; + int len; + int line_num; + + static char *filename = MODPATH_FILENAME; + + + /* Initialize number of models to zero in case of error */ + *num_models = 0; + + /* Open the model pathname file */ + fp = fopen(filename, "r"); + + if(fp == NULL) { + sprintf(msg, "ERROR - File not found: %s", filename); + print_error(msg); + return(ERROR); + } + + /* Read the pathnames from the file, one line at a time until EOF */ + n = 0; + line_num = 0; + while( fgets(path, sizeof(path), fp) ) { + + line_num++; + len = strlen(path); + + /* If line was too long for buffer, exit with error */ + if(len > MAX_PATH_LEN) { + sprintf(msg, "ERROR - Line %d of %s exceeds %d characters", + line_num, filename, MAX_PATH_LEN); + print_error(msg); + return(ERROR); + } + + /* Strip white space including newline */ + for(i = 0, j = 0; i < len; ) { + if(isspace(path[i])) { + i++; + } + else { + path[j] = path[i]; + i++; + j++; + } + } + path[j] = '\0'; + len = j; + + /* Strip trailing '/' if any */ + if(path[len - 1] == '/') + path[--len] = '\0'; + + /* If blank line, continue */ + if(len == 0) + continue; + + /* Make sure pathname is short enough to add a filename at the end */ + if(len > (MAX_PATH_LEN - (MAX_FN_LEN + 1)) ) { + sprintf(msg, "ERROR - Pathname on line %d of %s exceeds %d characters", + line_num, filename, (MAX_PATH_LEN - (MAX_FN_LEN + 1))); + print_error(msg); + return(ERROR); + } + + /* Allocate and initialize a new model info structure */ + if(n == 0) + model = (void *) malloc(sizeof(Model_Info_t)); + else + model = (void *) realloc(model, (n + 1) * sizeof(Model_Info_t)); + model[n].path_name = NULL; + model[n].spice_name = NULL; + model[n].cfunc_name = NULL; + model[n].spice_unique = TRUE; + model[n].cfunc_unique = TRUE; + + /* Put pathname into info structure */ + model[n].path_name = malloc(len); + strcpy(model[n].path_name, path); + + /* Increment count of paths read */ + n++; + } + + + /* Close model pathname file and return data read */ + fclose(fp); + + *num_models = n; + *model_info = model; + + return(OK); + +} + + +/* *********************************************************************** */ + + +/* +read_udnpath + +This function opens the udnpath.lst file, reads the pathnames from the +file, and puts them into an internal data structure for future +processing. +*/ + + +static Status_t read_udnpath( + int *num_nodes, /* Number of node pathnames found */ + Node_Info_t **node_info /* Info about each node */ +) +{ + FILE *fp; /* Udn pathname file pointer */ + char msg[MAX_PATH_LEN+257]; /* space for an error message */ + char path[MAX_PATH_LEN+2]; /* space to read pathnames into */ + Node_Info_t *node; /* temporary pointer to node info */ + + int n; + int i; + int j; + int len; + int line_num; + + static char *filename = UDNPATH_FILENAME; + + + /* Initialize number of nodes to zero in case of error */ + *num_nodes = 0; + + /* Open the node pathname file */ + fp = fopen(filename, "r"); + + /* For backward compatibility, return with WARNING only if file not found */ + if(fp == NULL) { + sprintf(msg, "WARNING - File not found: %s", filename); + print_error(msg); + return(OK); + } + + /* Read the pathnames from the file, one line at a time until EOF */ + n = 0; + line_num = 0; + while( fgets(path, sizeof(path), fp) ) { + + line_num++; + len = strlen(path); + + /* If line was too long for buffer, exit with error */ + if(len > MAX_PATH_LEN) { + sprintf(msg, "ERROR - Line %d of %s exceeds %d characters", + line_num, filename, MAX_PATH_LEN); + print_error(msg); + return(ERROR); + } + + /* Strip white space including newline */ + for(i = 0, j = 0; i < len; ) { + if(isspace(path[i])) { + i++; + } + else { + path[j] = path[i]; + i++; + j++; + } + } + path[j] = '\0'; + len = j; + + /* Strip trailing '/' if any */ + if(path[len - 1] == '/') + path[--len] = '\0'; + + /* If blank line, continue */ + if(len == 0) + continue; + + /* Make sure pathname is short enough to add a filename at the end */ + if(len > (MAX_PATH_LEN - (MAX_FN_LEN + 1)) ) { + sprintf(msg, "ERROR - Pathname on line %d of %s exceeds %d characters", + line_num, filename, (MAX_PATH_LEN - (MAX_FN_LEN + 1))); + print_error(msg); + return(ERROR); + } + + /* Allocate and initialize a new node info structure */ + if(n == 0) + node = (void *) malloc(sizeof(Node_Info_t)); + else + node = (void *) realloc(node, (n + 1) * sizeof(Node_Info_t)); + node[n].path_name = NULL; + node[n].node_name = NULL; + node[n].unique = TRUE; + + /* Put pathname into info structure */ + node[n].path_name = malloc(len); + strcpy(node[n].path_name, path); + + /* Increment count of paths read */ + n++; + } + + + /* Close node pathname file and return data read */ + fclose(fp); + + *num_nodes = n; + *node_info = node; + + return(OK); + +} + + + +/* *********************************************************************** */ + + +/* +read_model_names + +This function opens each of the models and gets the names of the model +and the C function into the internal model information structure. +*/ + +static Status_t read_model_names( + int num_models, /* Number of model pathnames */ + Model_Info_t *model_info /* Info about each model */ +) +{ + Boolean_t all_found; /* True if all ifspec files read */ + int i; /* A temporary counter */ + char msg[MAX_PATH_LEN+257]; /* space for an error message */ + char path[MAX_PATH_LEN+1]; /* full pathname to ifspec file */ + Status_t status; /* Return status */ + Ifs_Table_t ifs_table; /* Repository for info read from ifspec file */ + + + /* For each model found in model pathname file, read the interface */ + /* spec file to get the SPICE and C function names into model_info */ + + for(i = 0, all_found = TRUE; i < num_models; i++) { + + /* Form the full pathname to the interface spec file */ + strcpy(path, model_info[i].path_name); + strcat(path, "/"); + strcat(path, IFSPEC_FILENAME); + + /* Read the SPICE and C function names from the interface spec file */ + status = read_ifs_file(path, GET_IFS_NAME, &ifs_table); + + /* Transfer the names into the model_info structure */ + if(status == OK) { + model_info[i].spice_name = ifs_table.name.model_name; + model_info[i].cfunc_name = ifs_table.name.c_fcn_name; + } + else { + all_found = FALSE; + sprintf(msg, "ERROR - Problems reading %s in directory %s", + IFSPEC_FILENAME, model_info[i].path_name); + print_error(msg); + } + } + + if(all_found) + return(OK); + else + return(ERROR); + +} + + +/* *********************************************************************** */ + + +/* +read_node_names + +This function opens each of the user-defined node definition files +and gets the names of the node into the internal information structure. +*/ + + +static Status_t read_node_names( + int num_nodes, /* Number of node pathnames */ + Node_Info_t *node_info /* Info about each node */ +) +{ + Boolean_t all_found; /* True if all files read OK */ + int i; /* A temporary counter */ + char msg[MAX_PATH_LEN+257]; /* space for an error message */ + char path[MAX_PATH_LEN+1]; /* full pathname to file */ + Status_t status; /* Return status */ + char *node_name; /* Name of node type read from file */ + + /* For each node found in node pathname file, read the udnfunc.c */ + /* file to get the node type names into node_info */ + + for(i = 0, all_found = TRUE; i < num_nodes; i++) { + + /* Form the full pathname to the interface spec file */ + strcpy(path, node_info[i].path_name); + strcat(path, "/"); + strcat(path, UDNFUNC_FILENAME); + + /* Read the udn node type name from the file */ + status = read_udn_type_name(path, &node_name); + + /* Transfer the names into the node_info structure */ + if(status == OK) { + node_info[i].node_name = node_name; + } + else { + all_found = FALSE; + sprintf(msg, "ERROR - Problems reading %s in directory %s", + UDNFUNC_FILENAME, node_info[i].path_name); + print_error(msg); + } + } + + if(all_found) + return(OK); + else + return(ERROR); + +} + + + +/* *********************************************************************** */ + + +/* +check_uniqueness + +Function check_uniqueness determines if model names and function +names are unique with respect to each other and to the models and +functions internal to SPICE 3C1. +*/ + +static Status_t check_uniqueness( + int num_models, /* Number of model pathnames */ + Model_Info_t *model_info, /* Info about each model */ + int num_nodes, /* Number of node type pathnames */ + Node_Info_t *node_info /* Info about each node type */ +) +{ + int i; /* A temporary counter */ + int j; /* A temporary counter */ + char msg[MAX_PATH_LEN+257]; /* space for an error message */ + Boolean_t all_unique; /* true if names are unique */ + + /* Define a list of model names used internally by XSPICE */ + /* These names (except 'poly') are defined in src/sim/INP/INPdomodel.c and */ + /* are case insensitive */ + static char *SPICEmodel[] = { "npn", + "pnp", + "d", + "njf", + "pjf", + "nmf", + "pmf", + "urc", + "nmos", + "pmos", + "r", + "c", + "sw", + "csw", + "poly" }; + static int numSPICEmodels = sizeof(SPICEmodel) / sizeof(char *); + + + /* Define a list of node type names used internally by the simulator */ + /* These names are defined in src/sim/include/MIFtypes.h and are case */ + /* insensitive */ + static char *UDNidentifier[] = { "v", + "vd", + "i", + "id", + "vnam", + "g", + "gd", + "h", + "hd", + "d" }; + static int numUDNidentifiers = sizeof(UDNidentifier) / sizeof(char *); + + + + /* First, normalize case of all model and node names to lower since */ + /* SPICE is case insensitive in parsing decks */ + for(i = 0; i < num_models; i++) + str_to_lower(model_info[i].spice_name); + for(i = 0; i < num_nodes; i++) + str_to_lower(node_info[i].node_name); + + /* Then, loop through all model names and report errors if same as SPICE */ + /* model name or same as another model name in list */ + for(i = 0, all_unique = TRUE; i < num_models; i++) { + + /* First check against list of SPICE internal names */ + for(j = 0; j < numSPICEmodels; j++) { + if(strcmp(model_info[i].spice_name, SPICEmodel[j]) == 0) { + all_unique = FALSE; + sprintf(msg, + "ERROR - Model name '%s' is same as internal SPICE model name\n", + model_info[i].spice_name); + print_error(msg); + } + } + + /* Skip if already seen once */ + if(model_info[i].spice_unique == FALSE) + continue; + + /* Then check against other names in list */ + for(j = 0; j < num_models; j++) { + + /* Skip checking against itself */ + if(i == j) + continue; + + /* Compare the names */ + if(strcmp(model_info[i].spice_name, model_info[j].spice_name) == 0) { + all_unique = FALSE; + model_info[i].spice_unique = FALSE; + model_info[j].spice_unique = FALSE; + sprintf(msg, + "ERROR - Model name '%s' in directory: %s", + model_info[i].spice_name, + model_info[i].path_name); + print_error(msg); + print_error(" is same as"); + sprintf(msg, + " model name '%s' in directory: %s\n", + model_info[j].spice_name, + model_info[j].path_name); + print_error(msg); + } + } + + } + + /* Loop through all C function names and report errors if duplicates found */ + for(i = 0; i < num_models; i++) { + + /* Skip if already seen once */ + if(model_info[i].cfunc_unique == FALSE) + continue; + + /* Check against other names in list only, not against SPICE identifiers */ + /* Let linker catch collisions with SPICE identifiers for now */ + for(j = 0; j < num_models; j++) { + + /* Skip checking against itself */ + if(i == j) + continue; + + /* Compare the names */ + if(strcmp(model_info[i].cfunc_name, model_info[j].cfunc_name) == 0) { + all_unique = FALSE; + model_info[i].cfunc_unique = FALSE; + model_info[j].cfunc_unique = FALSE; + sprintf(msg, + "ERROR - C function name '%s' in directory: %s", + model_info[i].cfunc_name, + model_info[i].path_name); + print_error(msg); + print_error(" is same as"); + sprintf(msg, + " C function name '%s' in directory: %s\n", + model_info[j].cfunc_name, + model_info[j].path_name); + print_error(msg); + } + } + + } + + /* Loop through all node type names and report errors if same as internal */ + /* name or same as another name in list */ + for(i = 0; i < num_nodes; i++) { + + /* First check against list of internal names */ + for(j = 0; j < numUDNidentifiers; j++) { + if(strcmp(node_info[i].node_name, UDNidentifier[j]) == 0) { + all_unique = FALSE; + sprintf(msg, + "ERROR - Node type '%s' is same as internal node type\n", + node_info[i].node_name); + print_error(msg); + } + } + + /* Skip if already seen once */ + if(node_info[i].unique == FALSE) + continue; + + /* Then check against other names in list */ + for(j = 0; j < num_nodes; j++) { + + /* Skip checking against itself */ + if(i == j) + continue; + + /* Compare the names */ + if(strcmp(node_info[i].node_name, node_info[j].node_name) == 0) { + all_unique = FALSE; + node_info[i].unique = FALSE; + node_info[j].unique = FALSE; + sprintf(msg, + "ERROR - Node type '%s' in directory: %s", + node_info[i].node_name, + node_info[i].path_name); + print_error(msg); + print_error(" is same as"); + sprintf(msg, + " node type '%s' in directory: %s\n", + node_info[j].node_name, + node_info[j].path_name); + print_error(msg); + } + } + } + + + /* Return error status */ + if(all_unique) + return(OK); + else + return(ERROR); + +} + + +/* *********************************************************************** */ + + +/* +write_CMextrn + +Function write_CMextrn writes the CMextrn.h file used in +compiling SPIinit.c immediately prior to linking the simulator +and code models. This SPICE source file uses the structures +mentioned in the include file to define the models known to the +simulator. +*/ + +static Status_t write_CMextrn( + int num_models, /* Number of model pathnames */ + Model_Info_t *model_info /* Info about each model */ +) +{ + int i; /* A temporary counter */ + FILE *fp; /* File pointer for writing CMextrn.h */ + + + /* Open the file to be written */ + fp = fopen("cmextrn.h", "w"); + if(fp == NULL) { + print_error("ERROR - Problems opening CMextrn.h for write"); + return(ERROR); + } + + /* Write out the data */ + for(i = 0; i < num_models; i++) { + fprintf(fp, "extern SPICEdev %s_info;\n", model_info[i].cfunc_name); + } + + /* Close the file and return */ + fclose(fp); + return(OK); +} + + +/* *********************************************************************** */ + + +/* +write_CMinfo + +Function write_CMinfo writes the CMinfo.h file used in compiling +SPIinit.c immediately prior to linking the simulator and code +models. This SPICE source file uses the structures mentioned in +the include file to define the models known to the simulator. +*/ + + +static Status_t write_CMinfo( + int num_models, /* Number of model pathnames */ + Model_Info_t *model_info /* Info about each model */ +) +{ + int i; /* A temporary counter */ + FILE *fp; /* File pointer for writing CMinfo.h */ + + + /* Open the file to be written */ + fp = fopen("cminfo.h", "w"); + if(fp == NULL) { + print_error("ERROR - Problems opening CMinfo.h for write"); + return(ERROR); + } + + /* Write out the data */ + for(i = 0; i < num_models; i++) { + fprintf(fp, "&%s_info,\n", model_info[i].cfunc_name); + } + + /* Close the file and return */ + fclose(fp); + return(OK); +} + + + +/* *********************************************************************** */ + + + +/* +write_UDNextrn + +Function write_UDNextrn writes the UDNextrn.h file used in +compiling SPIinit.c immediately prior to linking the simulator +and user-defined nodes. This SPICE source file uses the structures +mentioned in the include file to define the node types known to the +simulator. +*/ + + + +static Status_t write_UDNextrn( + int num_nodes, /* Number of node pathnames */ + Node_Info_t *node_info /* Info about each node */ +) +{ + int i; /* A temporary counter */ + FILE *fp; /* File pointer for writing UDNextrn.h */ + + + /* Open the file to be written */ + fp = fopen("udnextrn.h", "w"); + if(fp == NULL) { + print_error("ERROR - Problems opening UDNextrn.h for write"); + return(ERROR); + } + + /* Write out the data */ + for(i = 0; i < num_nodes; i++) { + fprintf(fp, "extern Evt_Udn_Info_t udn_%s_info;\n", node_info[i].node_name); + } + + /* Close the file and return */ + fclose(fp); + return(OK); +} + + +/* *********************************************************************** */ + + +/* +write_UDNinfo + +Function write_UDNinfo writes the UDNinfo.h file used in +compiling SPIinit.c immediately prior to linking the simulator +and user-defined nodes. This SPICE source file uses the structures +mentioned in the include file to define the node types known to the +simulator. +*/ + + + +static Status_t write_UDNinfo( + int num_nodes, /* Number of node pathnames */ + Node_Info_t *node_info /* Info about each node */ +) +{ + int i; /* A temporary counter */ + FILE *fp; /* File pointer for writing UDNinfo.h */ + + + /* Open the file to be written */ + fp = fopen("udninfo.h", "w"); + if(fp == NULL) { + print_error("ERROR - Problems opening UDNinfo.h for write"); + return(ERROR); + } + + /* Write out the data */ + for(i = 0; i < num_nodes; i++) { + fprintf(fp, "&udn_%s_info,\n", node_info[i].node_name); + } + + /* Close the file and return */ + fclose(fp); + return(OK); +} + + +/* *********************************************************************** */ + +/* +write_objects_inc + +Function write_objects_inc writes a make include file used by +the make utility to locate the object modules needed to link the +simulator with the code models and user-defined node types. +*/ + +static Status_t write_objects_inc( + int num_models, /* Number of model pathnames */ + Model_Info_t *model_info, /* Info about each model */ + int num_nodes, /* Number of udn pathnames */ + Node_Info_t *node_info /* Info about each node type */ +) +{ + int i; /* A temporary counter */ + FILE *fp; /* File pointer for writing make_include */ + + + /* Open the file to be written */ + fp = fopen("objects.inc", "w"); + if(fp == NULL) { + print_error("ERROR - Problems opening objects.inc file for write"); + return(ERROR); + } + + /* Write out the data */ + for(i = 0; i < num_models; i++) { + fprintf(fp, "%s/*.o", model_info[i].path_name); + if((i < (num_models - 1)) || (num_nodes > 0)) + fprintf(fp, " \\\n"); + else + fprintf(fp, "\n"); + } + for(i = 0; i < num_nodes; i++) { + fprintf(fp, "%s/*.o", node_info[i].path_name); + if(i < (num_nodes - 1)) + fprintf(fp, " \\\n"); + else + fprintf(fp, "\n"); + } + + /* Close the file and return */ + fclose(fp); + return(OK); +} + + +/* +read_udn_type_name + +This function reads a User-Defined Node Definition File until +the definition of the Evt_Udn_Info_t struct +is found, and then gets the name of the node type from the first +member of the structure. +*/ + +static Status_t read_udn_type_name( + char *path, /* the path to the node definition file */ + char **node_name /* the node type name found in the file */ +) +{ + FILE *fp; /* file pointer for opened file */ + char msg[MAX_PATH_LEN+257]; /* space for an error message */ + Boolean_t found; /* true if name found successfully */ + Boolean_t in_struct; /* true if found struct with name */ + char name[MAX_NAME_LEN + 1]; /* temporary storage for name read */ + int c; /* a character read from the file */ + int i; /* a counter */ + + static char *struct_type = "Evt_Udn_Info_t"; + + /* Open the file from which the node type name will be read */ + fp = fopen(path, "r"); + if(fp == NULL) + return(ERROR); + + /* Read the file until the definition of the Evt_Udn_Info_t struct */ + /* is found, then get the name of the node type from the first */ + /* member of the structure */ + found = FALSE; + do { + /* read the next character */ + c = fgetc(fp); + + /* check for and gobble up comments */ + if(c == '/') { + c = fgetc(fp); + if(c == '*') { + do { + c = fgetc(fp); + if(c == '*') { + c = fgetc(fp); + if((c == '/') || (c == EOF)) + break; + else + ungetc(c, fp); + } + } while(c != EOF); + } + } + if(c == EOF) + break; + + /* read until "Evt_Udn_Info_t" is encountered */ + for(i = 0, in_struct = FALSE; ; i++) { + if(c != struct_type[i]) + break; + else if(i == (sizeof(struct_type) - 2)) { + in_struct = TRUE; + break; + } + else + c = fgetc(fp); + } + /* if found it, read until open quote found */ + /* and then read the name until the closing quote is found */ + if(in_struct) { + do { + c = fgetc(fp); + if(c == '"') { + i = 0; + do { + c = fgetc(fp); + if(c == '"') { + found = TRUE; + name[i] = '\0'; + } + else if(c != EOF) + name[i++] = c; + } while((c != EOF) && (! found)); + } + } while((c != EOF) && (! found)); + } + + } while((c != EOF) && (! found)); + + /* Close the file and return */ + fclose(fp); + + if(found) { + *node_name = malloc(strlen(name) + 1); + strcpy(*node_name, name); + return(OK); + } + else { + *node_name = NULL; + return(ERROR); + } +} + diff --git a/src/xspice/cmpp/pp_mod.c b/src/xspice/cmpp/pp_mod.c new file mode 100755 index 000000000..722529b7f --- /dev/null +++ b/src/xspice/cmpp/pp_mod.c @@ -0,0 +1,181 @@ +/*============================================================================ +FILE pp_mod.c + +MEMBER OF process cmpp + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Steve Tynor + +MODIFICATIONS + + + +SUMMARY + + This file contains the top-level driver function for preprocessing the + "cfunc.mod" file. First, the "ifspec.ifs" file is opened and parsed to + get the data that will be needed in the .mod to .c translation (See + read_ifs.c). Then the .mod file is translated. Most of the work of the + translation is handled by the UNIX 'lex' and 'yacc' utilities. This + translation is begun at the call to mod_yyparse() below. See also files: + + mod_lex.l + mod_yacc.y + + Note that to allow lex/yacc to be used twice (once for the ifspec.ifs + file, and then again for the cfunc.mod file), the functions created by + lex/yacc for the latter are translated using the UNIX text editor 'sed' + under the direction of the Makefile and the following 'sed scripts': + + mod_lex.sed + mod_yacc.sed + + Hence the call to 'mod_yyparse()' rather than 'yyparse()' below. + +INTERFACES + + preprocess_mod_file() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include "cmpp.h" + +/*---------------------------------------------------------------------------*/ +static void change_extension (char *filename, char *ext, char *new_filename) +{ + int i = strlen (filename); + + strcpy (new_filename, filename); + + for (; i >= 0; i--) { + if (new_filename[i] == '.') { + new_filename[i+1] = '\0'; + break; + } + } + strcat (new_filename, ext); +} + +/*---------------------------------------------------------------------------*/ + +/* +preprocess_mod_file + +Function preprocess_mod_file is the top-level driver function for +preprocessing a code model file (cfunc.mod). This function calls +read_ifs_file() requesting it to read and parse the Interface +Specification file (ifspec.ifs) and place the information +contained in it into an internal data structure. It then calls +mod_yyparse() to read the cfunc.mod file and translate it +according to the Interface Specification information. Function +mod_yyparse() is automatically generated by UNIX lex/yacc +utilities. +*/ + + +void preprocess_mod_file ( + char *filename) /* The file to read */ +{ + extern FILE *mod_yyin; + extern FILE *mod_yyout; + extern char *current_filename; + extern int mod_yylineno; + extern int mod_num_errors; + extern Ifs_Table_t *mod_ifs_table; + + Ifs_Table_t ifs_table; /* info read from ifspec.ifs file */ + Status_t status; /* Return status */ + char error_str[200]; + char output_filename[200]; + + /* + * Read the entire ifspec.ifs file and load the data into ifs_table + */ + + status = read_ifs_file (IFSPEC_FILENAME, GET_IFS_TABLE, &ifs_table); + + if (status != OK) { + exit(1); + } + + mod_yyin = fopen (filename, "r"); + if (mod_yyin == NULL) { + sprintf(error_str, "ERROR - Could not open input .mod file: %s", + filename); + print_error(error_str); + return; + } + + current_filename = filename; + + change_extension (filename, "c", output_filename); + mod_yyout = fopen (output_filename, "w"); + + if (mod_yyout == NULL) { + sprintf(error_str, "ERROR - Could not open output .c: %s", + output_filename); + print_error(error_str); + return; + } + + mod_ifs_table = &ifs_table; + mod_num_errors = 0; + + fprintf (mod_yyout, "#line 1 \"%s\"\n", filename); + fprintf (mod_yyout, "#include \"cm.h\"\n"); + fprintf (mod_yyout, "#line 1 \"%s\"\n", filename); + + mod_yylineno = 1; + if (!mod_yyin) { + sprintf (error_str, "Could not open .mod file: \"%s\"", filename); + print_error (error_str); + unlink (output_filename); + exit(1); + } + if (!mod_yyout) { + sprintf (error_str, "Could not create .c file: \"%s\"", + output_filename); + print_error (error_str); + unlink (output_filename); + exit(1); + } + + if (mod_yyparse() || (mod_num_errors > 0)) { + sprintf (error_str, "Error parsing .mod file: \"%s\"", filename); + print_error (error_str); + unlink (output_filename); + exit (1); + } + fclose (mod_yyout); + mod_yyrestart(NULL); +} + +/*---------------------------------------------------------------------------*/ +int mod_yyerror (str) + char *str; +{ + extern int mod_yylineno; + extern char *mod_yytext; + extern char *current_filename; + extern char *prog_name; + + fprintf (stderr, "%s: Error: \"%s\": line %d (near \'%s\'):\n\t%s.\n", + prog_name, current_filename, mod_yylineno, mod_yytext, str); +} + diff --git a/src/xspice/cmpp/read_ifs.c b/src/xspice/cmpp/read_ifs.c new file mode 100755 index 000000000..abcb6c5b6 --- /dev/null +++ b/src/xspice/cmpp/read_ifs.c @@ -0,0 +1,175 @@ +/*============================================================================ +FILE read_ifs.c + +MEMBER OF process cmpp + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn and Steve Tynor + +MODIFICATIONS + + + +SUMMARY + + This file contains top-level functions used in reading information + from the ifspec.ifs file and building an internal data structure that + holds the information. Most of the work in parsing of the + ifspec.ifs file and in building the structure is handled by + the UNIX 'lex' and 'yacc' utilities. This processing is begun + at the call to yyparse() in read_ifs_table() below. See also files: + + ifs_lex.l + ifs_yacc.y + +INTERFACES + + read_ifs_file() + yywrap() + yyerror() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include "cmpp.h" + +extern char *prog_name; + +void *malloc(unsigned size); + +static Status_t read_ifs_table(FILE *fp, int mode, Ifs_Table_t *ifs_table); + +char *current_filename; + +/* *********************************************************************** */ + + +/* + NOTE + + The following function may be called either by cmpp -ifs or cmpp -lst with + mode set to GET_IFS_TABLE or GET_IFS_NAME respectively. + */ + + +/* +read_ifs_file + +Function read_ifs_file() opens the Interface Specification file +(ifspec.ifs) for read access and calls read_ifs_table() with the +assigned file pointer to read and parse the file. Upon return +from read_ifs_table(), the file is closed. +*/ + + + +Status_t read_ifs_file( + char *filename, /* File to read */ + int mode, /* Get names only or get everything? */ + Ifs_Table_t *ifs_table) /* Table to put info in */ +{ + + FILE *fp; /* Ifs file pointer */ + + char msg[MAX_PATH_LEN+257]; /* space for an error message */ + + Status_t status; /* returned status from function */ + + + /* Open the ifs file for read access */ + + fp = fopen(filename, "r"); + + if(fp == NULL) { + perror (prog_name); + sprintf(msg, "ERROR - File not found: %s", filename); + print_error(msg); + return(ERROR); + } + + current_filename = filename; + + /* Get the stuff from the file into the ifs_table struct */ + + status = read_ifs_table(fp, mode, ifs_table); + + /* Close file and return */ + + fclose(fp); + + return(status); + +} + + + + +/* *********************************************************************** */ + + +/* +read_ifs_table + +Function read_ifs_table() calls yyparse() to read and parse the +Interface Specification file contents and place the information +into an internal data structure. Function yyparse() is +automatically generated by UNIX lex/yacc. +*/ + + + +static Status_t read_ifs_table( + FILE *fp, /* File to read from */ + int mode, /* Get names only or get everything? */ + Ifs_Table_t *ifs_table) /* Table to put info in */ +{ + + extern FILE *ifs_yyin; + extern Ifs_Table_t *parser_ifs_table; + extern Boolean_t parser_just_names; + extern int ifs_yylineno; + + assert (ifs_table); + assert (fp); + + ifs_yylineno = 1; + ifs_yyin = fp; + parser_just_names = (mode == GET_IFS_NAME); + parser_ifs_table = ifs_table; + + if (ifs_yyparse()) { + print_error ("Error parsing interface specification file"); + ifs_yyrestart(NULL); + return ERROR; + } + ifs_yyrestart(NULL); + return OK; +} + +/*---------------------------------------------------------------------------*/ +int ifs_yyerror (str) + char *str; +{ + extern int ifs_yylineno; + extern char *ifs_yytext; + + fprintf (stderr, "%s: Error: \"%s\": line %d (near \'%s\'):\n\t%s.\n", + prog_name, current_filename, ifs_yylineno, ifs_yytext, str); +} + diff --git a/src/xspice/cmpp/util.c b/src/xspice/cmpp/util.c new file mode 100755 index 000000000..9ff8b6dd1 --- /dev/null +++ b/src/xspice/cmpp/util.c @@ -0,0 +1,87 @@ +/*============================================================================ +FILE util.c + +MEMBER OF process cmpp + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn and Steve Tynor + +MODIFICATIONS + + + +SUMMARY + + This file contains miscellaneous utility functions used in cmpp. + +INTERFACES + + init_error() + print_error() + str_to_lower() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include "cmpp.h" +#include +#include +#include + + + +/* *********************************************************************** */ + +char *prog_name; + + +/* Initialize print_error() with the name of the program */ + +void init_error (char *program_name) +{ + prog_name = program_name; +} + + + +/* Print an error message to stderr */ + +void print_error( + char *msg) /* The message to write */ +{ + fprintf(stderr, "%s: %s\n", prog_name, msg); +} + + + +/* Convert a string to all lower case */ + +str_to_lower(s) + +char *s; /* The string to convert */ +{ + int i; + char c; + + for(i = 0; (c = s[i]) != '\0'; i++) + if(isalpha(c)) + if(isupper(c)) + s[i] = tolower(c); +} + + diff --git a/src/xspice/cmpp/writ_ifs.c b/src/xspice/cmpp/writ_ifs.c new file mode 100755 index 000000000..1d239d02e --- /dev/null +++ b/src/xspice/cmpp/writ_ifs.c @@ -0,0 +1,1304 @@ +/*============================================================================ +FILE writ_ifs.c + +MEMBER OF process cmpp + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains functions used to write out the file "ifspec.c" + based on information read from "ifspec.ifs", which is now held in + structure 'ifs_table' passed to write_ifs_c_file(). + +INTERFACES + + write_ifs_c_file() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +#include +#include "cmpp.h" + +/* Explicitly prototype malloc and free. BSD has no header file for this */ + +void *malloc(unsigned size); +void *realloc(void *ptr, unsigned size); +void free(void *ptr); + + +/* Local function prototypes */ + +static void write_comment(FILE *fp, Ifs_Table_t *ifs_table); + +static void write_includes(FILE *fp); + +static void write_mPTable(FILE *fp, Ifs_Table_t *ifs_table); + +static void write_pTable(FILE *fp, Ifs_Table_t *ifs_table); + +static void write_conn_info(FILE *fp, Ifs_Table_t *ifs_table); + +static void write_param_info(FILE *fp, Ifs_Table_t *ifs_table); + +static void write_inst_var_info(FILE *fp, Ifs_Table_t *ifs_table); + +static void write_SPICEdev(FILE *fp, Ifs_Table_t *ifs_table); + + + +static char *data_type_to_str(Data_Type_t type); + +static char *port_type_to_str(Port_Type_t port); + +static char *dir_to_str(Dir_t dir); + +static char *value_to_str(Data_Type_t type, Value_t value); + +static char *no_value_to_str(void); + +static char *boolean_to_str(Boolean_t value); + +static char *integer_to_str(int value); + +static char *gen_port_type_str(Port_Type_t port); + + + +/* *********************************************************************** */ + + + +/* +write_ifs_c_file + +Function write_ifs_c_file is a top-level driver function for +creating a C file (ifspec.c) that defines the model Interface +Specification in a form usable by the simulator. The ifspec.c +output file is opened for writing, and then each of the following +functions is called in order to write the necessary statements +and data structures into the file: + + write_comment + write_includes + write_mPTable + write_conn_info + write_param_info + write_inst_var_info + write_SPICEdev + +The output file is then closed. +*/ + + + +Status_t write_ifs_c_file( + char *filename, /* File to write to */ + Ifs_Table_t *ifs_table) /* Table of Interface Specification data */ +{ + FILE *fp; /* File pointer */ + char msg[MAX_PATH_LEN+257]; /* space for an error message */ + int int_status; /* returned status from fclose */ + + + /* Open the ifspec.c file for write access */ + + fp = fopen(filename, "w"); + + if(fp == NULL) { + sprintf(msg, "ERROR - Can't create file: %s", filename); + print_error(msg); + return(ERROR); + } + + + /* Write out a comment section at the top of the file */ + write_comment(fp, ifs_table); + + /* Put in the # includes */ + write_includes(fp); + + /* Write the SPICE3 required XXXmPTable structure */ + write_mPTable(fp, ifs_table); + + /* Write the SPICE3 required XXXpTable structure */ + write_pTable(fp, ifs_table); + + /* Write out the connector table required for the code model element parser */ + write_conn_info(fp, ifs_table); + + /* Write out the parameter table required for the code model element parser */ + write_param_info(fp, ifs_table); + + /* Write out the instance variable table required for the code model element parser */ + write_inst_var_info(fp, ifs_table); + + /* Write out the externally visible structure for this model */ + write_SPICEdev(fp, ifs_table); + + + /* Close the ifspec.c file and return */ + + int_status = fclose(fp); + + if(int_status == 0) + return(OK); + else + return(ERROR); +} + + + +/* *********************************************************************** */ + + +/* +write_comment + +Function write_comment places a comment at the top of the +ifspec.c file warning the user that this file is automatically +generated and should not be edited. +*/ + + +static void write_comment( + FILE *fp, /* File to write to */ + Ifs_Table_t *ifs_table) /* Table of Interface Specification data */ +{ + fprintf(fp, "\n"); + fprintf(fp, "/*\n"); + fprintf(fp, " * Structures for model: %s\n", ifs_table->name.model_name); + fprintf(fp, " *\n"); + fprintf(fp, " * Automatically generated by cmpp preprocessor\n"); + fprintf(fp, " *\n"); + fprintf(fp, " * !!! DO NOT EDIT !!!\n"); + fprintf(fp, " *\n"); + fprintf(fp, " */\n"); + fprintf(fp, "\n"); +} + + +/* *********************************************************************** */ + +/* +write_includes + +Function write_includes writes the C header files required in +ifspec.c. +*/ + + +static void write_includes( + FILE *fp) /* File to write to */ +{ + fprintf(fp, "\n"); + fprintf(fp, "#include \"ngspice.h\" \n"); +/* fprintf(fp, "#include \"prefix.h\" \n");*/ + fprintf(fp, "#include \n"); + fprintf(fp, "#include \"devdefs.h\" \n"); + fprintf(fp, "#include \"ifsim.h\" \n"); + fprintf(fp, "#include \"mifdefs.h\" \n"); + fprintf(fp, "#include \"mifproto.h\" \n"); + fprintf(fp, "#include \"mifparse.h\" \n"); +/* fprintf(fp, "#include \"suffix.h\" \n");*/ + fprintf(fp, "\n"); +} + + +/* *********************************************************************** */ + + + +/* +write_pTable + +Function write_pTable writes the instance parameter information +using SPICE's IFparm structure type. This table defines the +parameters as output only variables using SPICE's ``OP'' macro. +These instance parameters are derived from the Interface +Specification file's STATIC_VAR table and define the parameters +that can be queried using the SPICE 3C1 .save feature. +*/ + + +static void write_pTable( + FILE *fp, /* File to write to */ + Ifs_Table_t *ifs_table) /* Table of Interface Specification data */ +{ + + int i; + char str[80]; + Boolean_t is_array; + Data_Type_t type; + + + /* Only write the pTable if there is something to put in it. */ + /* Otherwise, we will put NULL in the SPICEdev structure in its slot */ + + if(ifs_table->num_inst_var == 0) + return; + + + /* Write the structure beginning */ + + fprintf(fp, "\n"); + fprintf(fp, "static IFparm MIFpTable[] = {\n"); + + + /* Write out an entry for each instance variable in the table */ + + /* Use the index of the element in the instance variable info array */ + /* ADDED TO the number of parameters as the SPICE3 integer tag. */ + + for(i = 0; i < ifs_table->num_inst_var; i++) { + + /* Use the SPICE3 OP macro since instance vars are output-only */ + + fprintf(fp, " OP("); + + /* Put in the name of the parameter and the integer tag */ + + fprintf(fp, "\"%s\", ", ifs_table->inst_var[i].name); + fprintf(fp, "%d, ", i + ifs_table->num_param); + + /* Format SPICE3 type according to parameter type field */ + + type = ifs_table->inst_var[i].type; + is_array = ifs_table->inst_var[i].is_array; + + strcpy(str,""); + + if(is_array == TRUE) { + strcat(str,"("); + } + + if(type == BOOLEAN) { + strcat(str,"IF_FLAG"); /* There is no BOOLEAN in SPICE3 */ + } + else if(type == INTEGER) { + strcat(str,"IF_INTEGER"); + } + else if(type == REAL) { + strcat(str,"IF_REAL"); + } + else if(type == COMPLEX) { + strcat(str,"IF_COMPLEX"); + } + else if(type == STRING) { + strcat(str,"IF_STRING"); + } + else if(type == POINTER) { + strcat(str,"IF_STRING"); + } + else { + print_error("INTERNAL ERROR - write_pTable() - Impossible data type."); + } + + if(is_array == TRUE) { + strcat(str,"|IF_VECTOR)"); + } + + fprintf(fp, "%s, ", str); + + /* Put in the description string and finish this line off */ + + fprintf(fp, "\"%s\"", ifs_table->inst_var[i].description); + fprintf(fp, "),\n"); + + } + + /* Finish off the structure */ + + fprintf(fp, "};\n"); + fprintf(fp, "\n"); +} + + +/* *********************************************************************** */ + + +/* +write_mPTable + +Function write_mPTable writes the model parameter information +using SPICE's IFparm structure type. This table defines the +parameters to be input/output variables using SPICE's ``IOP'' macro +so that these variables can be set or queried from SPICE. These +model parameters are derived from the Interface Specification's +PARAMETER table. +*/ + +static void write_mPTable( + FILE *fp, /* File to write to */ + Ifs_Table_t *ifs_table) /* Table of Interface Specification data */ +{ + + int i; + char str[80]; + Boolean_t is_array; + Data_Type_t type; + + + /* Only write the mPTable if there is something to put in it. */ + /* Otherwise, we will put NULL in the SPICEdev structure in its slot */ + + if(ifs_table->num_param == 0) + return; + + + /* Write the structure beginning */ + + fprintf(fp, "\n"); + fprintf(fp, "static IFparm MIFmPTable[] = {\n"); + + + /* Write out an entry for each parameter in the table */ + + /* Use the index of the element in the parameter info array */ + /* as the SPICE3 integer tag. */ + + for(i = 0; i < ifs_table->num_param; i++) { + + /* Use the SPICE3 IOP macro since model parameters are input/output */ + + fprintf(fp, " IOP("); + + /* Put in the name of the parameter and the integer tag */ + + fprintf(fp, "\"%s\", ", ifs_table->param[i].name); + fprintf(fp, "%d, ", i); + + /* Format SPICE3 type according to parameter type field */ + + type = ifs_table->param[i].type; + is_array = ifs_table->param[i].is_array; + + strcpy(str,""); + + if(is_array == TRUE) { + strcat(str,"("); + } + + if(type == BOOLEAN) { + strcat(str,"IF_FLAG"); /* There is no BOOLEAN in SPICE3 */ + } + else if(type == INTEGER) { + strcat(str,"IF_INTEGER"); + } + else if(type == REAL) { + strcat(str,"IF_REAL"); + } + else if(type == COMPLEX) { + strcat(str,"IF_COMPLEX"); + } + else if(type == STRING) { + strcat(str,"IF_STRING"); + } + else { + print_error("INTERNAL ERROR - write_mPTable() - Impossible data type."); + } + + if(is_array == TRUE) { + strcat(str,"|IF_VECTOR)"); + } + + fprintf(fp, "%s, ", str); + + /* Put in the description string and finish this line off */ + + fprintf(fp, "\"%s\"", ifs_table->param[i].description); + fprintf(fp, "),\n"); + + } + + /* Finish off the structure */ + + fprintf(fp, "};\n"); + fprintf(fp, "\n"); + +} + + +/* *********************************************************************** */ + + +/* +write_conn_info + +Function write_conn_info writes information used by the +Simulator's new MIF package to interpret and error check a +model's connection list in a SPICE deck. This information is +derived from the Interface Specification file's PORT table. +*/ + + + +static void write_conn_info( + FILE *fp, /* File to write to */ + Ifs_Table_t *ifs_table) /* Table of Interface Specification data */ +{ + + int i; + int j; + char *str; + Boolean_t is_array; + Data_Type_t type; + + + /* Only write the connTable if there is something to put in it. */ + /* Otherwise, we will put NULL in the SPICEdev structure in its slot */ + + if(ifs_table->num_conn == 0) /* An unlikely condition for sure ... */ + return; + + /* First, we must define arrays of port types */ + + /* Note that there should be always at least one allowed port type */ + /* so we don't have to worry about arrays with no elements */ + + for(i = 0; i < ifs_table->num_conn; i++) { + + fprintf(fp, "\n"); + fprintf(fp, "static Mif_Port_Type_t MIFportEnum%d[] = {\n", i); + + if(ifs_table->conn[i].num_allowed_types < 1) + print_error("ERROR - write_conn_info() - Number of allowed types cannot be zero"); + + for(j = 0; j < ifs_table->conn[i].num_allowed_types; j++) { + + str = port_type_to_str(ifs_table->conn[i].allowed_port_type[j]); + fprintf(fp, "\t%s,\n", str); + + } /* for number of allowed types */ + + fprintf(fp, "};\n"); + fprintf(fp, "\n"); + + + fprintf(fp, "\n"); + fprintf(fp, "static char *MIFportStr%d[] = {\n", i); + + for(j = 0; j < ifs_table->conn[i].num_allowed_types; j++) { + if(ifs_table->conn[i].allowed_port_type[j] == USER_DEFINED) + fprintf(fp, "\t\"%s\",\n", ifs_table->conn[i].allowed_type[j]); + else { + str = gen_port_type_str(ifs_table->conn[i].allowed_port_type[j]); + fprintf(fp, "\t\"%s\",\n", str); + } + } /* for number of allowed types */ + + fprintf(fp, "};\n"); + fprintf(fp, "\n"); + + } /* for number of connections */ + + + + /* Now write the structure */ + + fprintf(fp, "\n"); + fprintf(fp, "static Mif_Conn_Info_t MIFconnTable[] = {\n"); + + + /* Write out an entry for each parameter in the table */ + + for(i = 0; i < ifs_table->num_conn; i++) { + + fprintf(fp, " {\n"); + fprintf(fp, " \"%s\",\n",ifs_table->conn[i].name); + fprintf(fp, " \"%s\",\n",ifs_table->conn[i].description); + + str = dir_to_str(ifs_table->conn[i].direction); + fprintf(fp, " %s,\n", str); + + str = port_type_to_str(ifs_table->conn[i].default_port_type); + fprintf(fp, " %s,\n", str); + + fprintf(fp, " \"%s\",\n", + (ifs_table->conn[i].default_port_type == USER_DEFINED) + ? ifs_table->conn[i].default_type + : gen_port_type_str (ifs_table->conn[i].default_port_type)); + + fprintf(fp," %d,\n",ifs_table->conn[i].num_allowed_types); + + fprintf(fp, " MIFportEnum%d,\n", i); + fprintf(fp, " MIFportStr%d,\n", i); + + + str = boolean_to_str(ifs_table->conn[i].is_array); + fprintf(fp, " %s,\n", str); + + if(ifs_table->conn[i].is_array == FALSE) { + + str = boolean_to_str(FALSE); /* has_lower_bound */ + fprintf(fp, " %s,\n", str); + + str = integer_to_str(0); /* lower_bound */ + fprintf(fp, " %s,\n", str); + + str = boolean_to_str(FALSE); /* has_upper_bound */ + fprintf(fp, " %s,\n", str); + + str = integer_to_str(0); /* upper_bound */ + fprintf(fp, " %s,\n", str); + } + else { /* is_array == TRUE */ + + str = boolean_to_str(ifs_table->conn[i].has_lower_bound); + fprintf(fp, " %s,\n", str); + + if(ifs_table->conn[i].has_lower_bound == TRUE) + str = integer_to_str(ifs_table->conn[i].lower_bound); + else + str = integer_to_str(0); + fprintf(fp, " %s,\n", str); + + str = boolean_to_str(ifs_table->conn[i].has_upper_bound); + fprintf(fp, " %s,\n", str); + + if(ifs_table->conn[i].has_upper_bound == TRUE) + str = integer_to_str(ifs_table->conn[i].upper_bound); + else + str = integer_to_str(0); + fprintf(fp, " %s,\n", str); + + } /* if is_array */ + + str = boolean_to_str(ifs_table->conn[i].null_allowed); + fprintf(fp, " %s,\n", str); + + fprintf(fp, " },\n"); + + } /* for number of parameters */ + + + /* Finish off the structure */ + + fprintf(fp, "};\n"); + fprintf(fp, "\n"); +} + + +/* *********************************************************************** */ + + +/* +write_param_info + +Function write_param_info writes information used by the +Simulator's new MIF package to interpret and error check a +model's parameter list in an XSPICE deck. This information is +derived from the Interface Specification file's PARAMETER table. +It is essentially a superset of the IFparm information written by +write_mPTable(). The IFparm information written by +write_mPTable() is required to work with SPICE's device set and +query functions. The information written by write_param_info is +more extensive and is required to parse and error check the XSPICE +input deck. +*/ + + + +static void write_param_info( + FILE *fp, /* File to write to */ + Ifs_Table_t *ifs_table) /* Table of Interface Specification data */ +{ + + int i; + char *str; + Boolean_t is_array; + Data_Type_t type; + + + /* Only write the paramTable if there is something to put in it. */ + /* Otherwise, we will put NULL in the SPICEdev structure in its slot */ + + if(ifs_table->num_param == 0) + return; + + + /* Write the structure beginning */ + + fprintf(fp, "\n"); + fprintf(fp, "static Mif_Param_Info_t MIFparamTable[] = {\n"); + + + /* Write out an entry for each parameter in the table */ + + for(i = 0; i < ifs_table->num_param; i++) { + + fprintf(fp, " {\n"); + fprintf(fp, " \"%s\",\n",ifs_table->param[i].name); + fprintf(fp, " \"%s\",\n",ifs_table->param[i].description); + + str = data_type_to_str(ifs_table->param[i].type); + fprintf(fp, " %s,\n", str); + + str = boolean_to_str(ifs_table->param[i].has_default); + fprintf(fp, " %s,\n", str); + + if(ifs_table->param[i].has_default == TRUE) + str = value_to_str(ifs_table->param[i].type, ifs_table->param[i].default_value); + else + str = no_value_to_str(); + fprintf(fp, " %s,\n", str); + + str = boolean_to_str(ifs_table->param[i].has_lower_limit); + fprintf(fp, " %s,\n", str); + + if(ifs_table->param[i].has_lower_limit == TRUE) + str = value_to_str(ifs_table->param[i].type, ifs_table->param[i].lower_limit); + else + str = no_value_to_str(); + fprintf(fp, " %s,\n", str); + + str = boolean_to_str(ifs_table->param[i].has_upper_limit); + fprintf(fp, " %s,\n", str); + + if(ifs_table->param[i].has_upper_limit == TRUE) + str = value_to_str(ifs_table->param[i].type, ifs_table->param[i].upper_limit); + else + str = no_value_to_str(); + fprintf(fp, " %s,\n", str); + + str = boolean_to_str(ifs_table->param[i].is_array); + fprintf(fp, " %s,\n", str); + + if(ifs_table->param[i].is_array == FALSE) { + + str = boolean_to_str(FALSE); /* has_conn_ref */ + fprintf(fp, " %s,\n", str); + + str = integer_to_str(0); /* conn_ref */ + fprintf(fp, " %s,\n", str); + + str = boolean_to_str(FALSE); /* has_lower_bound */ + fprintf(fp, " %s,\n", str); + + str = integer_to_str(0); /* lower_bound */ + fprintf(fp, " %s,\n", str); + + str = boolean_to_str(FALSE); /* has_upper_bound */ + fprintf(fp, " %s,\n", str); + + str = integer_to_str(0); /* upper_bound */ + fprintf(fp, " %s,\n", str); + } + else { /* is_array == TRUE */ + + str = boolean_to_str(ifs_table->param[i].has_conn_ref); + fprintf(fp, " %s,\n", str); + + if(ifs_table->param[i].has_conn_ref == TRUE) { + + str = integer_to_str(ifs_table->param[i].conn_ref); + fprintf(fp, " %s,\n", str); + + str = boolean_to_str(FALSE); /* has_lower_bound */ + fprintf(fp, " %s,\n", str); + + str = integer_to_str(0); /* lower_bound */ + fprintf(fp, " %s,\n", str); + + str = boolean_to_str(FALSE); /* has_upper_bound */ + fprintf(fp, " %s,\n", str); + + str = integer_to_str(0); /* upper_bound */ + fprintf(fp, " %s,\n", str); + } + else { /* has_conn_ref == FALSE */ + + str = integer_to_str(0); /* conn_ref */ + fprintf(fp, " %s,\n", str); + + str = boolean_to_str(ifs_table->param[i].has_lower_bound); + fprintf(fp, " %s,\n", str); + + if(ifs_table->param[i].has_lower_bound == TRUE) + str = integer_to_str(ifs_table->param[i].lower_bound); + else + str = integer_to_str(0); + fprintf(fp, " %s,\n", str); + + str = boolean_to_str(ifs_table->param[i].has_upper_bound); + fprintf(fp, " %s,\n", str); + + if(ifs_table->param[i].has_upper_bound == TRUE) + str = integer_to_str(ifs_table->param[i].upper_bound); + else + str = integer_to_str(0); + fprintf(fp, " %s,\n", str); + + } /* if has_conn_ref */ + + } /* if is_array */ + + str = boolean_to_str(ifs_table->param[i].null_allowed); + fprintf(fp, " %s,\n", str); + + fprintf(fp, " },\n"); + + } /* for number of parameters */ + + + /* Finish off the structure */ + + fprintf(fp, "};\n"); + fprintf(fp, "\n"); + +} + + + +/* *********************************************************************** */ + + +/* +write_inst_var_info + +Function write_inst_var_info writes information used by the +Simulator's new MIF package to allocate space for and to output +(using SPICE's .save feature) variables defined in the Interface +Specification file's STATIC_VAR table. It is essentially a +superset of the IFparm information written by write_mPTable(). +The IFparm information written by write_pTable() is required to +work with SPICE's device query functions. The information +written by write_inst_var_info is more extensive. +*/ + + + +static void write_inst_var_info( + FILE *fp, /* File to write to */ + Ifs_Table_t *ifs_table) /* Table of Interface Specification data */ +{ + + int i; + char *str; + Boolean_t is_array; + Data_Type_t type; + + + /* Only write the inst_varTable if there is something to put in it. */ + /* Otherwise, we will put NULL in the SPICEdev structure in its slot */ + + if(ifs_table->num_inst_var == 0) + return; + + + /* Write the structure beginning */ + + fprintf(fp, "\n"); + fprintf(fp, "static Mif_Inst_Var_Info_t MIFinst_varTable[] = {\n"); + + + /* Write out an entry for each parameter in the table */ + + for(i = 0; i < ifs_table->num_inst_var; i++) { + + fprintf(fp, " {\n"); + fprintf(fp, " \"%s\",\n",ifs_table->inst_var[i].name); + fprintf(fp, " \"%s\",\n",ifs_table->inst_var[i].description); + + str = data_type_to_str(ifs_table->inst_var[i].type); + fprintf(fp, " %s,\n", str); + + str = boolean_to_str(ifs_table->inst_var[i].is_array); + fprintf(fp, " %s,\n", str); + + fprintf(fp, " },\n"); + + } /* for number of parameters */ + + + /* Finish off the structure */ + + fprintf(fp, "};\n"); + fprintf(fp, "\n"); + +} + + + + +/* *********************************************************************** */ + + +/* +write_SPICEdev + +Function write_SPICEdev writes the global XXX_info structure used +by SPICE to define a model. Here ``XXX'' is the name of the code +model. This structure contains the name of the +model, a pointer to the C function that implements the model, and +pointers to all of the above data structures. +*/ + + + +static void write_SPICEdev( + FILE *fp, /* File to write to */ + Ifs_Table_t *ifs_table) /* Table of Interface Specification data */ +{ + + /* Extern the code model function name */ + fprintf(fp, "\n"); + fprintf(fp, "extern void %s(Mif_Private_t *);\n", + ifs_table->name.c_fcn_name); + + /* SPICE now needs these static integers */ + fprintf(fp, "\n"); + fprintf(fp, "static int val_terms = 0;\n"); + fprintf(fp, "static int val_numNames = 0;\n"); + fprintf(fp, "static int val_numInstanceParms = %d;\n",ifs_table->num_inst_var); + fprintf(fp, "static int val_numModelParms = %d;\n",ifs_table->num_param); + fprintf(fp, "static int val_sizeofMIFinstance = sizeof(MIFinstance);\n"); + fprintf(fp, "static int val_sizeofMIFmodel = sizeof(MIFmodel);\n"); + + /* Write out the structure beginning */ + + /* Use the c function external identifier appended with _info as the */ + /* external identifier for the structure. */ + + fprintf(fp, "\n"); + fprintf(fp, "SPICEdev %s_info = {\n", ifs_table->name.c_fcn_name); + + /* Write the IFdevice structure */ + + fprintf(fp, " { \"%s\",\n", ifs_table->name.model_name); + fprintf(fp, " \"%s\",\n", ifs_table->name.description); + fprintf(fp, " &val_terms,\n"); + fprintf(fp, " &val_numNames,\n"); + fprintf(fp, " NULL,\n"); + + fprintf(fp, " &val_numInstanceParms,\n"); + if(ifs_table->num_inst_var > 0) + fprintf(fp, " MIFpTable,\n"); + else + fprintf(fp, " NULL,\n"); + + fprintf(fp, " &val_numModelParms,\n"); + if(ifs_table->num_param > 0) + fprintf(fp, " MIFmPTable,\n"); + else + fprintf(fp, " NULL,\n"); + + fprintf(fp, " %s,\n", ifs_table->name.c_fcn_name); + + fprintf(fp, " %d,\n", ifs_table->num_conn); + if(ifs_table->num_conn > 0) + fprintf(fp, " MIFconnTable,\n"); + else + fprintf(fp, " NULL,\n"); + + fprintf(fp, " %d,\n", ifs_table->num_param); + if(ifs_table->num_param > 0) + fprintf(fp, " MIFparamTable,\n"); + else + fprintf(fp, " NULL,\n"); + + fprintf(fp, " %d,\n", ifs_table->num_inst_var); + if(ifs_table->num_inst_var > 0) + fprintf(fp, " MIFinst_varTable,\n"); + else + fprintf(fp, " NULL,\n"); + + fprintf(fp, " },\n"); + + /* Write the names of the generic code model functions */ + + fprintf(fp, "NULL, \n"); /* DEVparam */ + fprintf(fp, "MIFmParam, \n"); /* DEVmodParam */ + fprintf(fp, "MIFload, \n"); /* DEVload */ + fprintf(fp, "MIFsetup, \n"); /* DEVsetup */ + fprintf(fp, "NULL, \n"); /* DEVunsetup */ + fprintf(fp, "NULL, \n"); /* DEVpzSetup */ + fprintf(fp, "NULL, \n"); /* DEVtemperature */ + fprintf(fp, "MIFtrunc, \n"); /* DEVtrunc */ + fprintf(fp, "NULL, \n"); /* DEVfindBranch */ + fprintf(fp, "MIFload, \n"); /* DEVacLoad */ + fprintf(fp, "NULL, \n"); /* DEVaccept */ + fprintf(fp, "MIFdestroy, \n"); /* DEVdestroy */ + fprintf(fp, "MIFmDelete, \n"); /* DEVmodDelete */ + fprintf(fp, "MIFdelete, \n"); /* DEVdelete */ + fprintf(fp, "NULL, \n"); /* DEVsetic */ + fprintf(fp, "MIFask, \n"); /* DEVask */ + fprintf(fp, "MIFmAsk, \n"); /* DEVmodAsk */ + fprintf(fp, "NULL, \n"); /* DEVpzLoad */ + fprintf(fp, "MIFconvTest, \n"); /* DEVconvTest */ + fprintf(fp, "NULL, \n"); /* DEVsenSetup */ + fprintf(fp, "NULL, \n"); /* DEVsenLoad */ + fprintf(fp, "NULL, \n"); /* DEVSenUpdate */ + fprintf(fp, "NULL, \n"); /* DEVsenAcLoad */ + fprintf(fp, "NULL, \n"); /* DEVsenPrint */ + fprintf(fp, "NULL, \n"); /* DEVsenTrunc */ + fprintf(fp, "NULL, \n"); /* DEVdisto */ + fprintf(fp, "NULL, \n"); /* DEVnoise */ + + + /* Write the sizeof stuff used in dynamic allocation of inst/model structs */ + + fprintf(fp, "&val_sizeofMIFinstance,\n"); + fprintf(fp, "&val_sizeofMIFmodel,\n"); + fprintf(fp, "\n"); + fprintf(fp, "};\n"); + fprintf(fp, "\n"); + +} + + + + +/* *********************************************************************** */ + + +/* +The following functions are utility routines used to convert internal +enums and data to ASCII form for placing into the .c file +being created. +*/ + + +#define BASE_STR_LEN 80 + + +static char *data_type_to_str(Data_Type_t type) +{ + static char *str = NULL; + + if(str == NULL) + str = malloc(BASE_STR_LEN+1); + + switch(type) { + + case BOOLEAN: + strcpy(str,"MIF_BOOLEAN"); + break; + + case INTEGER: + strcpy(str,"MIF_INTEGER"); + break; + + case REAL: + strcpy(str,"MIF_REAL"); + break; + + case COMPLEX: + strcpy(str,"MIF_COMPLEX"); + break; + + case STRING: + strcpy(str,"MIF_STRING"); + break; + + case POINTER: + strcpy(str,"MIF_STRING"); + break; + + default: + print_error("INTERNAL ERROR - data_type_to_str() - Impossible data type."); + } + + return(str); +} + + +/* *********************************************************************** */ + +static char *port_type_to_str(Port_Type_t port) +{ + static char *str = NULL; + + if(str == NULL) + str = malloc(BASE_STR_LEN+1); + + switch(port) { + + case VOLTAGE: + strcpy(str,"MIF_VOLTAGE"); + break; + + case DIFF_VOLTAGE: + strcpy(str,"MIF_DIFF_VOLTAGE"); + break; + + case CURRENT: + strcpy(str,"MIF_CURRENT"); + break; + + case DIFF_CURRENT: + strcpy(str,"MIF_DIFF_CURRENT"); + break; + + case VSOURCE_CURRENT: + strcpy(str,"MIF_VSOURCE_CURRENT"); + break; + + case CONDUCTANCE: + strcpy(str,"MIF_CONDUCTANCE"); + break; + + case DIFF_CONDUCTANCE: + strcpy(str,"MIF_DIFF_CONDUCTANCE"); + break; + + case RESISTANCE: + strcpy(str,"MIF_RESISTANCE"); + break; + + case DIFF_RESISTANCE: + strcpy(str,"MIF_DIFF_RESISTANCE"); + break; + + case DIGITAL: + strcpy(str,"MIF_DIGITAL"); + break; + + case USER_DEFINED: + strcpy(str,"MIF_USER_DEFINED"); + break; + + default: + print_error("INTERNAL ERROR - port_type_to_str() - Impossible port type."); + } + + return(str); + +} + +/* *********************************************************************** */ + +static char *gen_port_type_str(Port_Type_t port) +{ + static char *str = NULL; + + if(str == NULL) + str = malloc(BASE_STR_LEN+1); + + switch(port) { + + case VOLTAGE: + strcpy(str,"v"); + break; + + case DIFF_VOLTAGE: + strcpy(str,"vd"); + break; + + case CURRENT: + strcpy(str,"i"); + break; + + case DIFF_CURRENT: + strcpy(str,"id"); + break; + + case VSOURCE_CURRENT: + strcpy(str,"vnam"); + break; + + case CONDUCTANCE: + strcpy(str,"g"); + break; + + case DIFF_CONDUCTANCE: + strcpy(str,"gd"); + break; + + case RESISTANCE: + strcpy(str,"h"); + break; + + case DIFF_RESISTANCE: + strcpy(str,"hd"); + break; + + case DIGITAL: + strcpy(str,"d"); + break; + + case USER_DEFINED: + strcpy(str,""); + break; + + default: + print_error("INTERNAL ERROR - gen_port_type_str() - Impossible port type."); + } + + return(str); + +} + + +/* *********************************************************************** */ + +static char *dir_to_str(Dir_t dir) +{ + static char *str = NULL; + + if(str == NULL) + str = malloc(BASE_STR_LEN+1); + + switch(dir) { + + case IN: + strcpy(str,"MIF_IN"); + break; + + case OUT: + strcpy(str,"MIF_OUT"); + break; + + case INOUT: + strcpy(str,"MIF_INOUT"); + break; + + default: + print_error("INTERNAL ERROR - dir_to_str() - Impossible direction type."); + } + + return(str); +} + + + +/* *********************************************************************** */ + +static char *value_to_str(Data_Type_t type, Value_t value) +{ + static char *str = NULL; + static int max_len = 0; + + char *bool_str; + int str_len; + + + if(str == NULL) { + str = malloc(2 * BASE_STR_LEN + 1); + max_len = 2 * BASE_STR_LEN; + } + + switch(type) { + + case BOOLEAN: + bool_str = boolean_to_str(value.bvalue); + sprintf(str, "{%s, 0, 0.0, {0.0, 0.0}, NULL}", bool_str); + break; + + case INTEGER: + sprintf(str, "{MIF_FALSE, %d, 0.0, {0.0, 0.0}, NULL}", value.ivalue); + break; + + case REAL: + sprintf(str, "{MIF_FALSE, 0, %e, {0.0, 0.0}, NULL}", value.rvalue); + break; + + case COMPLEX: + sprintf(str, "{MIF_FALSE, 0, 0.0, {%e, %e}, NULL}", + value.cvalue.real, value.cvalue.imag); + break; + + case STRING: + /* be careful, the string could conceivably be very long... */ + str_len = strlen(value.svalue); + if((str_len + BASE_STR_LEN) > max_len) { + str = realloc(str, (max_len + str_len +1)); + max_len += str_len; + } + sprintf(str, "{MIF_FALSE, 0, 0.0, {0.0, 0.0}, \"%s\"}", value.svalue); + break; + + default: + print_error("INTERNAL ERROR - value_to_str() - Impossible data type."); + + } + + return(str); +} + + +/* *********************************************************************** */ + +static char *boolean_to_str(Boolean_t value) +{ + static char *str = NULL; + + if(str == NULL) + str = malloc(BASE_STR_LEN+1); + + switch(value) { + + case TRUE: + strcpy(str,"MIF_TRUE"); + break; + + case FALSE: + strcpy(str,"MIF_FALSE"); + break; + + default: + print_error("INTERNAL ERROR - boolean_to_str() - Impossible boolean value."); + { + char *p = 0; *p = 0; + } + } + + return(str); +} + + +/* *********************************************************************** */ + +static char *integer_to_str(int value) +{ + static char *str = NULL; + + if(str == NULL) { + str = malloc(BASE_STR_LEN + 1); + } + + sprintf(str, "%d", value); + + return(str); +} + + +/* *********************************************************************** */ + +static char *no_value_to_str(void) +{ + static char *str = NULL; + + if(str == NULL) { + str = malloc(BASE_STR_LEN + 1); + } + + sprintf(str, "{MIF_FALSE, 0, 0.0, {0.0, 0.0}, NULL}"); + + return(str); +} + + + diff --git a/src/xspice/enh/Makefile.am b/src/xspice/enh/Makefile.am new file mode 100755 index 000000000..827d4675d --- /dev/null +++ b/src/xspice/enh/Makefile.am @@ -0,0 +1,13 @@ +## Process this file with automake to produce Makefile.in +# +# JW 3/9/01 - had a go and makeing an autoconf script. + +noinst_LIBRARIES = libenhxsp.a + +libenhxsp_a_SOURCES = \ +enh.c \ +enhtrans.c + +INCLUDES = -I$(top_srcdir)/src/include + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/xspice/enh/enh.c b/src/xspice/enh/enh.c new file mode 100755 index 000000000..6286cc80c --- /dev/null +++ b/src/xspice/enh/enh.c @@ -0,0 +1,99 @@ +/*============================================================================ +FILE ENH.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains routines used for general enhancements made + to the Berkeley SPICE3 core. + +INTERFACES + + ENHreport_conv_prob() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/*=== INCLUDE FILES ===*/ + + +#include +#include "enh.h" + +/* +ENHreport_conv_prob() + +Report convergence problem messages from nodes, branch currents, +or instances. This function is setup to allow providing the SI +with information identifying the type of convergence problem. +For now, it simply writes to stdout. +*/ + + +void ENHreport_conv_prob( + Enh_Conv_Source_t type, /* node, branch, or instance */ + char *name, /* the name of the node/branch/instance */ + char *msg) /* an optional message */ +{ + + char *type_str; + char *msg_str; + + /* Convert the type enum to a string for printing */ + switch(type) { + + case ENH_ANALOG_NODE: + case ENH_EVENT_NODE: + type_str = "node"; + break; + + case ENH_ANALOG_BRANCH: + type_str = "branch current"; + break; + + case ENH_ANALOG_INSTANCE: + case ENH_EVENT_INSTANCE: + case ENH_HYBRID_INSTANCE: + type_str = "instance"; + break; + + default: + printf("\nERROR: Internal error in ENHreport_conv_prob - impossible type\n"); + return; + } + + /* Check for msg == NULL and turn into null string */ + if(msg) + msg_str = msg; + else + msg_str = ""; + + /* Print the convergence problem report */ + printf("\nWARNING: Convergence problems at %s (%s). %s\n", + type_str, name, msg_str); + +} /* ENHreport_conv_prob */ + diff --git a/src/xspice/enh/enhtrans.c b/src/xspice/enh/enhtrans.c new file mode 100755 index 000000000..53cca2d49 --- /dev/null +++ b/src/xspice/enh/enhtrans.c @@ -0,0 +1,536 @@ +/* =========================================================================== +FILE ENHtranslate_poly.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains functions used by the simulator in + calling the internal "poly" code model to substitute for + SPICE 2G6 style poly sources found in the input deck. + +INTERFACES + + ENHtranslate_poly() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +/*=== FUNCTION PROTOTYPES ===*/ + +//void free(void *); //ka removed compiler error +/* int atoi(char *); */ + + +/*=== INCLUDE FILES ===*/ + + +/* #include "prefix.h" */ + +#include "ngspice.h" +//#include "misc.h" + +#include "fteinp.h" +#include "enh.h" +#include "cpdefs.h" +#include "ftedefs.h" +#include "mifproto.h" + +/* #include "suffix.h" */ + + +/*=== FUNCTION PROTOTYPES ===*/ +static int needs_translating(char *card); +static int count_tokens(char *card); +static char *two2three_translate(char *orig_card, char **inst_card, + char **mod_card); +static int get_poly_dimension(char *card); + +/* +ENHtranslate_poly() + +Translate all 2G6 style polynomial controlled sources in the deck +to new polynomial controlled source code model syntax. +*/ + +/*---------------------------------------------------------------------*/ +/* ENHtranslate_poly takes (a pointer to) the SPICE deck as argument. */ +/* It loops through the deck, and translates all POLY statements */ +/* in dependent sources into a .model conformant with the needs of */ +/* XSPICE. It splices the new statements in the deck, and comments */ +/* out the old dependent source. */ +/* It returns (a pointer to) the processed deck. */ +/*---------------------------------------------------------------------*/ +struct line * ENHtranslate_poly( + struct line *deck) /* Linked list of lines in input deck */ +{ + struct line *d; + struct line *l1; + struct line *l2; + + char *card; + + /* Iterate through each card in the deck and translate as needed */ + for(d = deck; d; d = d->li_next) + { + +#ifdef TRACE + /* SDB debug statement */ + printf("In ENHtranslate_poly, now examining card %s . . . \n", d->li_line); +#endif + + /* If doesn't need to be translated, continue to next card */ + if(! needs_translating(d->li_line)) { + +#ifdef TRACE + /* SDB debug statement */ + /* printf("Card doesn't need translating. Continuing . . . .\n"); */ +#endif + + continue; + } + +#ifdef TRACE + /* SDB debug statement */ + printf("Found a card to translate . . . .\n"); +#endif + +/* Create two new line structs and splice into deck */ +/* l1 = alloc(line); */ /* jgroves */ +/* l2 = alloc(line); */ /* jgroves */ + l1 = alloc(struct line); + l2 = alloc(struct line); + l2->li_next = d->li_next; + l1->li_next = l2; + d->li_next = l1; + + /* Create the translated cards */ + d->li_error = two2three_translate(d->li_line, &(l1->li_line), &(l2->li_line)); + + /* Comment out the original line */ + card = (void *) MALLOC(strlen(d->li_line) + 2); + strcpy(card,"*"); + strcat(card, d->li_line); + d->li_line = card; + +#ifdef TRACE + /* SDB debug statement */ + printf("In ENHtranslate_poly, translated card = %s . . . \n", card); +#endif + + /* Advance deck pointer to last line added */ + d = l2; + } + + /* Return head of deck */ + return(deck); + +} /* ENHtranslate_poly */ + + +/*---------------------------------------------------------------------*/ +/* +needs_translating() + +Test to see if card needs translating. Return true if card defines +an e,f,g, or h controlled source and has too many tokens to be +a simple linear dependent source. Otherwise return false. +*/ + + +static int needs_translating( + char *card) /* the card text to check */ +{ + +#ifdef TRACE + /* SDB debug statement */ + /* printf("In needs_translating, examining card %s . . . \n", card); */ +#endif + + switch(*card) { + + case 'e': case 'E': + case 'g': case 'G': + if(count_tokens(card) <=6) + return(0); + else + return(1); + + case 'f': case 'F': + case 'h': case 'H': + if(count_tokens(card) <= 5) + return(0); + else + return(1); + + default: + return(0); + } + +} /* needs_translating */ + + + +/*---------------------------------------------------------------------*/ +/* +count_tokens() + +Count and return the number of tokens on the card. +*/ +static int count_tokens( + char *card) /* the card text on which to count tokens */ +{ + int i; + + /* Get and count tokens until end of line reached */ + for(i = 0; *card != '\0'; i++) + txfree(MIFgettok(&card)); + + return(i); + +} /* count_tokens */ + + + +/********************************************************************/ +/*==================================================================== + +two2three_translate() + +Do the syntax translation of the 2G6 source to the new code model +syntax. The translation proceeds according to the template below. + +-------------------------------------------- +VCVS: +ename N+ N- POLY(dim) NC+ NC- P0 P1 P2 . . . +N+ N- = outputs +NC+ NC- = inputs + +aname %vd[NC+ NC-] %vd[N+ N-] pname +.model pname spice2poly(coef=P0 P1 P2 . . . ) +%vd[NC+ NC-] = inputs +%vd[N+ N-] = outputs +-------------------------------------------- +CCCS +fname N+ N- POLY(dim) Vname P0 P1 P2 . . . +N+ N- = outputs +Vname = input voltage source (measures current) + +aname %vnam[Vname] %id[N+ N-] pname +.model pname spice2poly(coef=P0 P1 P2 . . . ) +%vnam[Vname] = input +%id[N+ N-] = output +-------------------------------------------- +VCCS +gname N+ N- POLY(dim) NC+ NC- P0 P1 P2 , , , +N+ N- = outputs +NC+ NC- = inputs + +aname %vd[NC+ NC-] %id[N+ N-] pname +.model pname spice2poly(coef=P0 P1 P2 . . . ) +%vd[NC+ NC-] = inputs +%id[N+ N-] = outputs +-------------------------------------------- +CCVS +hname N+ N- POLY(dim) Vname P0 P1 P2 . . . +N+ N- = outputs +Vname = input voltage source (measures current) + +aname %vnam[Vname] %vd[N+ N-] pname +.model pname spice2poly(coef=P0 P1 P2 . . . ) +%vnam[Vname] = input +%vd[N+ N-] = output + +====================================================================*/ +/********************************************************************/ + +static char *two2three_translate( + char *orig_card, /* the original untranslated card */ + char **inst_card, /* the instance card created by the translation */ + char **mod_card) /* the model card created by the translation */ +{ + int dim; + int num_tokens; + + int num_conns; + int num_coefs; + int inst_card_len; + int mod_card_len; + + int i; + + char type; + + char *tok; + char *name; + char **out_conn; + char **in_conn; + char **coef; + + char *card; + + +#ifdef TRACE + /* SDB debug statement */ + printf("In two2three_translate, card to translate = %s . . .\n", orig_card); +#endif + + /* Put the first character into local storage for checking type */ + type = *orig_card; + + /* Count the number of tokens for use in parsing */ + num_tokens = count_tokens(orig_card); + + /* Determine the dimension of the poly source */ + /* Note that get_poly_dimension returns 0 for "no poly", -1 for + invalid dimensiion, otherwise returns numeric value of POLY */ + dim = get_poly_dimension(orig_card); + if(dim == -1) { + printf("ERROR in two2three_translate -- Argument to poly() is not an integer\n"); + return("ERROR - Argument to poly() is not an integer\n"); + } + + /* Compute number of output connections based on type and dimension */ + switch(type) { + case 'E': + case 'e': + case 'G': + case 'g': + num_conns = 2 * dim; + break; + + default: + num_conns = dim; + } + + /* Compute number of coefficients. Return error if less than one. */ + if(dim == 0) + num_coefs = num_tokens - num_conns - 3; /* no POLY token */ + else + num_coefs = num_tokens - num_conns - 5; /* POLY token present */ + +#ifdef TRACE + /* SDB debug statement */ + printf("In two2three_translate, num_tokens=%d, num_conns=%d, num_coefs=%d . . .\n", num_tokens, num_conns, num_coefs); +#endif + + + if(num_coefs < 1) + return("ERROR - Number of connections differs from poly dimension\n"); + + /* Split card into name, output connections, input connections, */ + /* and coefficients */ + + card = orig_card; + name = MIFgettok(&card); + + /* Get output connections (2 netnames) */ + out_conn = (void *) MALLOC(2 * sizeof(char *)); + for(i = 0; i < 2; i++) + out_conn[i] = MIFgettok(&card); + + /* check for POLY, and ignore it if present */ + if (dim > 0) { + +#ifdef TRACE + /* SDB debug statement */ + printf("In two2three_translate, found poly!!! dim = %d \n", dim); +#endif + + tok = MIFgettok(&card); /* read and discard POLY */ + tok = MIFgettok(&card); /* read and discard dimension */ + } + + + /* Get input connections (2 netnames per dimension) */ + in_conn = (void *) MALLOC(num_conns * sizeof(char *)); + for(i = 0; i < num_conns; i++) + in_conn[i] = MIFgettok(&card); + + /* The remainder of the line are the poly coeffs. */ + coef = (void *) MALLOC(num_coefs * sizeof(char *)); + for(i = 0; i < num_coefs; i++) + coef[i] = MIFgettok(&card); + + /* Compute the size needed for the new cards to be created */ + /* Allow a fair amount of extra space for connection types, etc. */ + /* to be safe... */ + + inst_card_len = 70; + inst_card_len += 2 * (strlen(name) + 1); + for(i = 0; i < 2; i++) + inst_card_len += strlen(out_conn[i]) + 1; + for(i = 0; i < num_conns; i++) + inst_card_len += strlen(in_conn[i]) + 1; + + mod_card_len = 70; + mod_card_len += strlen(name) + 1; + for(i = 0; i < num_coefs; i++) + mod_card_len += strlen(coef[i]) + 1; + + /* Allocate space for the cards and write them into the strings */ + + *inst_card = (void *) MALLOC(inst_card_len); + *mod_card = (void *) MALLOC(mod_card_len); + + strcpy(*inst_card, "a$poly$"); + sprintf(*inst_card + strlen(*inst_card), "%s ", name); + + /* Write input nets/sources */ + if((type == 'e') || (type == 'g') || + (type == 'E') || (type == 'G')) + sprintf(*inst_card + strlen(*inst_card), "%%vd [ "); + else + sprintf(*inst_card + strlen(*inst_card), "%%vnam [ "); + + for(i = 0; i < num_conns; i++) + sprintf(*inst_card + strlen(*inst_card), "%s ", in_conn[i]); + + sprintf(*inst_card + strlen(*inst_card), "] "); + + + /* Write output nets */ + if((type == 'e') || (type == 'h') || + (type == 'E') || (type == 'H')) + sprintf(*inst_card + strlen(*inst_card), "%%vd [ "); + else + sprintf(*inst_card + strlen(*inst_card), "%%id [ "); + + for(i = 0; i < 2; i++) + sprintf(*inst_card + strlen(*inst_card), "%s ", out_conn[i]); + + sprintf(*inst_card + strlen(*inst_card), "] "); + + + /* Write model name */ + sprintf(*inst_card + strlen(*inst_card), "a$poly$%s", name); + + + /* Now create model card */ + sprintf(*mod_card, ".model a$poly$%s spice2poly coef = [ ", name); + for(i = 0; i < num_coefs; i++) + sprintf(*mod_card + strlen(*mod_card), "%s ", coef[i]); + sprintf(*mod_card + strlen(*mod_card), "]"); + +#ifdef TRACE + /* SDB debug statement */ + printf("In two2three_translate, translated statements:\n%s \n%s \n", *inst_card, *mod_card); +#endif + + /* Free the temporary space */ + FREE(name); + name = NULL; + + for(i = 0; i < 2; i++) + { + FREE(out_conn[i]); + out_conn[i] = NULL; + } + + FREE(out_conn); + out_conn = NULL; + + for(i = 0; i < num_conns; i++) + { + FREE(in_conn[i]); + in_conn[i] = NULL; + } + + FREE(in_conn); + in_conn = NULL; + + for(i = 0; i < num_coefs; i++) + { + FREE(coef[i]); + coef[i] = NULL; + } + + FREE(coef); + + coef = NULL; + + /* Return NULL to indicate no error */ + return(NULL); + +} /* two2three_translate */ + + + +/*--------------------------------------------------------------------*/ +/* +get_poly_dimension() + +Get the poly source dimension from the token immediately following +the 'poly' if any. Return values changed by SDB on 5.23.2003 to be: +If "poly" is not present, return 0. +If the dimension token following "poly" is invalid, return -1. +Otherwise, return the integer dimension. +Note that the dimension may only be 1 or 2. Is this correct SPICE? +*/ + + +static int get_poly_dimension( + char *card) /* the card text */ +{ + + int i; + int dim; + char *local_tok; + + + /* Skip over name and output connections */ + for(i = 0; i < 3; i++) + txfree(MIFgettok(&card)); + + /* Check the next token to see if it is "poly" */ + /* If not, return 0 */ + local_tok = MIFgettok(&card); + if( strcmp(local_tok, "poly") && + strcmp(local_tok, "POLY") ) /* check that local_tok is *not* poly */ + { + FREE(local_tok); + local_tok = NULL; + return(0); + } + + FREE(local_tok); + + /* Must have been "poly", so next line must be a number */ + /* Try to convert it. If successful, return the number */ + /* else, return -1 to indicate an error... */ + local_tok = MIFgettok(&card); + dim = atoi(local_tok); + FREE(local_tok); + + /* This is stupid, but it works. . . . */ + if ( (dim == 0) || (dim == 1) || (dim == 2) ) { + return(dim); + } + else { + return(-1); + } + +} /* get_poly_dimension */ + diff --git a/src/xspice/evt/Makefile.am b/src/xspice/evt/Makefile.am new file mode 100755 index 000000000..d03788259 --- /dev/null +++ b/src/xspice/evt/Makefile.am @@ -0,0 +1,27 @@ +## Process this file with automake to produce Makefile.in +# +# JW 3/9/01 - had a go and makeing an autoconf script. + +noinst_LIBRARIES = libevtxsp.a + +libevtxsp_a_SOURCES = \ +evtaccept.c \ +evtcall_hybrids.c \ +evtdump.c \ +evtiter.c \ +evtnext_time.c \ +evtop.c \ +evtprint.c \ +evtsetup.c \ +evtbackup.c \ +evtdeque.c \ +evtinit.c \ +evtload.c \ +evtnode_copy.c \ +evtplot.c \ +evtqueue.c \ +evttermi.c + +INCLUDES = -I$(top_srcdir)/src/include + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/xspice/evt/evtaccept.c b/src/xspice/evt/evtaccept.c new file mode 100755 index 000000000..16973ed44 --- /dev/null +++ b/src/xspice/evt/evtaccept.c @@ -0,0 +1,170 @@ +/*============================================================================ +FILE EVTaccept.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains a function called at the end of a + successful (accepted) analog timepoint. It saves pointers + to the states of the queues and data at this accepted time. + +INTERFACES + + void EVTaccept(CKTcircuit *ckt, double time) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/*=== INCLUDE FILES ===*/ + +#include +#include +#include "cktdefs.h" + +#include "mif.h" +#include "evt.h" + + + +/* +EVTaccept() + +This function is called at the end of a successful (accepted) +analog timepoint. It saves pointers to the states of the +queues and data at this accepted time. +*/ + + + +void EVTaccept( + CKTcircuit *ckt, /* main circuit struct */ + double time) /* time at which analog soln was accepted */ +{ + + int i; + int index; + int num_modified; + + Evt_Inst_Queue_t *inst_queue; + Evt_Output_Queue_t *output_queue; + + Evt_Node_Data_t *node_data; + Evt_State_Data_t *state_data; + Evt_Msg_Data_t *msg_data; + + + /* Exit if no event instances */ + if(ckt->evt->counts.num_insts == 0) + return; + + /* Get often used pointers */ + inst_queue = &(ckt->evt->queue.inst); + output_queue = &(ckt->evt->queue.output); + + node_data = ckt->evt->data.node; + state_data = ckt->evt->data.state; + msg_data = ckt->evt->data.msg; + + + /* Process the inst queue */ + num_modified = inst_queue->num_modified; + /* Loop through list of items modified since last time */ + for(i = 0; i < num_modified; i++) { + /* Get the index of the inst modified */ + index = inst_queue->modified_index[i]; + /* Update last_step for this index */ + inst_queue->last_step[index] = inst_queue->current[index]; + /* Reset the modified flag */ + inst_queue->modified[index] = MIF_FALSE; + } + /* Record the new last_time and reset number modified to zero */ + inst_queue->last_time = time; + inst_queue->num_modified = 0; + + + /* Process the output queue */ + num_modified = output_queue->num_modified; + /* Loop through list of items modified since last time */ + for(i = 0; i < num_modified; i++) { + /* Get the index of the output modified */ + index = output_queue->modified_index[i]; + /* Update last_step for this index */ + output_queue->last_step[index] = output_queue->current[index]; + /* Reset the modified flag */ + output_queue->modified[index] = MIF_FALSE; + } + /* Record the new last_time and reset number modified to zero */ + output_queue->last_time = time; + output_queue->num_modified = 0; + + + /* Process the node data */ + num_modified = node_data->num_modified; + /* Loop through list of items modified since last time */ + for(i = 0; i < num_modified; i++) { + /* Get the index of the node modified */ + index = node_data->modified_index[i]; + /* Update last_step for this index */ + node_data->last_step[index] = node_data->tail[index]; + /* Reset the modified flag */ + node_data->modified[index] = MIF_FALSE; + } + /* Reset number modified to zero */ + node_data->num_modified = 0; + + + /* Process the state data */ + num_modified = state_data->num_modified; + /* Loop through list of items modified since last time */ + for(i = 0; i < num_modified; i++) { + /* Get the index of the state modified */ + index = state_data->modified_index[i]; + /* Update last_step for this index */ + state_data->last_step[index] = state_data->tail[index]; + /* Reset the modified flag */ + state_data->modified[index] = MIF_FALSE; + } + /* Reset number modified to zero */ + state_data->num_modified = 0; + + + /* Process the msg data */ + num_modified = msg_data->num_modified; + /* Loop through list of items modified since last time */ + for(i = 0; i < num_modified; i++) { + /* Get the index of the msg modified */ + index = msg_data->modified_index[i]; + /* Update last_step for this index */ + msg_data->last_step[index] = msg_data->tail[index]; + /* Reset the modified flag */ + msg_data->modified[index] = MIF_FALSE; + } + /* Reset number modified to zero */ + msg_data->num_modified = 0; + +} /* EVTaccept */ + + diff --git a/src/xspice/evt/evtbackup.c b/src/xspice/evt/evtbackup.c new file mode 100755 index 000000000..6ddc24cc6 --- /dev/null +++ b/src/xspice/evt/evtbackup.c @@ -0,0 +1,645 @@ +/*============================================================================ +FILE EVTbackup.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains a function that resets the queues and data + structures to their state at the new analog simulation time specified + following the rejection of an analog timestep by the DCtran routine. + +INTERFACES + + void EVTbackup(CKTcircuit *ckt, double new_time) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +/*=== INCLUDE FILES ===*/ +#include +#include "ngspice.h" + +#include "cktdefs.h" +//#include "util.h" + +#include "mif.h" +#include "evt.h" + +#include "evtproto.h" + + + +/*=== FUNCTION PROTOTYPES ===*/ + + +static void EVTbackup_node_data(CKTcircuit *ckt, double new_time); +static void EVTbackup_state_data(CKTcircuit *ckt, double new_time); +static void EVTbackup_msg_data(CKTcircuit *ckt, double new_time); +static void EVTbackup_inst_queue(CKTcircuit *ckt, double new_time); +static void EVTbackup_output_queue(CKTcircuit *ckt, double new_time); + + + +/* +EVTbackup() + +This function resets the queues and data structures to their state +at the new analog simulation time specified. The algorithms in this file +assume the following timestep coordination between +analog and event-driven algorithms: + + while(not end of analysis) { + + while (next event time <= next analog time) { + do event solution with call_type = event_driven + if any instance set analog breakpoint < next analog time + set next analog time to breakpoint + } + + do analog timestep solution with call_type = analog + call all hybrid models with call_type = event_driven + + if(analog solution doesn't converge) + Call EVTbackup + else + Call EVTaccept + } +*/ + + +void EVTbackup( + CKTcircuit *ckt, /* the main circuit structure */ + double new_time) /* the time to backup to */ +{ + + + /* Backup the node data */ + EVTbackup_node_data(ckt, new_time); + + /* Backup the state data */ + EVTbackup_state_data(ckt, new_time); + + /* Backup the msg data */ + EVTbackup_msg_data(ckt, new_time); + + /* Backup the inst queue */ + EVTbackup_inst_queue(ckt, new_time); + + /* Backup the output queue */ + EVTbackup_output_queue(ckt, new_time); + + /* Record statistics */ + (ckt->evt->data.statistics->tran_time_backups)++; + +} /* EVTbackup */ + + + + +/* +EVTbackup_node_data() + +Reset the node structure data. +*/ + + +static void EVTbackup_node_data( + CKTcircuit *ckt, /* the main circuit structure */ + double new_time) /* the time to backup to */ +{ + + int i; + int j; + + int num_modified; + int node_index; + + Evt_Node_Info_t **node_table; + Evt_Node_Data_t *node_data; + Evt_Node_t **node_ptr; + Evt_Node_t *node; + Evt_Node_t *from_node; + Evt_Node_t *to_node; + Evt_Node_t *head; + Evt_Node_t *tail; + Evt_Node_t *free_head; + + /* Get pointers for quick access */ + node_data = ckt->evt->data.node; + node_table = ckt->evt->info.node_table; + + /* Loop through list of indexes modified since last accepted timepoint */ + num_modified = node_data->num_modified; + for(i = 0; i < num_modified; i++) { + + /* Get the needed node and udn indexes */ + node_index = node_data->modified_index[i]; + + /* Scan data for this node from last_step to determine new setting */ + /* for tail, and splice later data into the free list */ + node_ptr = node_data->last_step[node_index]; + node = *node_ptr; + while(1) { + if((node->next == NULL) || (node->next->step > new_time)) { + + /* Splice rest of list, if any, into free list */ + head = node->next; + if(head) { + tail = *(node_data->tail[node_index]); + free_head = node_data->free[node_index]; + node_data->free[node_index] = head; + tail->next = free_head; + } + + /* Set the tail */ + node_data->tail[node_index] = node_ptr; + node->next = NULL; + + break; + } + node_ptr = &(node->next); + node = node->next; + } + + /* Copy data from the location at tail to rhs and rhsold */ + from_node = *(node_data->tail[node_index]); + to_node = &(node_data->rhs[node_index]); + EVTnode_copy(ckt, node_index, from_node, &to_node); + to_node = &(node_data->rhsold[node_index]); + EVTnode_copy(ckt, node_index, from_node, &to_node); + + } /* end for number modified */ + + /* Update/compact the modified list */ + for(i = 0, j = 0; i < num_modified; i++) { + node_index = node_data->modified_index[i]; + /* If nothing after last_step, remove this index from the modified list */ + if((*(node_data->last_step[node_index]))->next == NULL) { + node_data->modified[node_index] = MIF_FALSE; + (node_data->num_modified)--; + } + /* else, keep the index */ + else { + node_data->modified_index[j] = node_data->modified_index[i]; + j++; + } + } + +} /* EVTbackup_node_data */ + + + +/* +EVTbackup_state_data() + +Reset the state structure data. +*/ + + +static void EVTbackup_state_data( + CKTcircuit *ckt, /* the main circuit structure */ + double new_time) /* the time to backup to */ +{ + int i; + int j; + + int num_modified; + int inst_index; + + Evt_State_Data_t *state_data; + + Evt_State_t **state_ptr; + Evt_State_t *state; + Evt_State_t *head; + Evt_State_t *tail; + Evt_State_t *free_head; + + /* Get pointers for quick access */ + state_data = ckt->evt->data.state; + + /* Loop through list of indexes modified since last accepted timepoint */ + num_modified = state_data->num_modified; + for(i = 0; i < num_modified; i++) { + + /* Get the inst index */ + inst_index = state_data->modified_index[i]; + + /* Scan data for this inst from last_step to determine new setting */ + /* for tail, and splice later data into the free list */ + state_ptr = state_data->last_step[inst_index]; + state = *state_ptr; + while(1) { + if((state->next == NULL) || (state->next->step > new_time)) { + + /* Splice rest of list, if any, into free list */ + head = state->next; + if(head) { + tail = *(state_data->tail[inst_index]); + free_head = state_data->free[inst_index]; + state_data->free[inst_index] = head; + tail->next = free_head; + } + + /* Set the tail */ + state_data->tail[inst_index] = state_ptr; + state->next = NULL; + + break; + } + state_ptr = &(state->next); + state = state->next; + } + } /* end for number modified */ + + /* Update/compact the modified list */ + for(i = 0, j = 0; i < num_modified; i++) { + inst_index = state_data->modified_index[i]; + /* If nothing after last_step, remove this index from the modified list */ + if((*(state_data->last_step[inst_index]))->next == NULL) { + state_data->modified[inst_index] = MIF_FALSE; + (state_data->num_modified)--; + } + /* else, keep the index */ + else { + state_data->modified_index[j] = state_data->modified_index[i]; + j++; + } + } + +} /* EVTbackup_state_data */ + + + +/* +EVTbackup_msg_data() + +Backup the message data. +*/ + + +static void EVTbackup_msg_data( + CKTcircuit *ckt, /* the main circuit structure */ + double new_time) /* the time to backup to */ +{ + int i; + int j; + + int num_modified; + int port_index; + + Evt_Msg_Data_t *msg_data; + + Evt_Msg_t **msg_ptr; + Evt_Msg_t *msg; + Evt_Msg_t *head; + Evt_Msg_t *tail; + Evt_Msg_t *free_head; + + /* Get pointers for quick access */ + msg_data = ckt->evt->data.msg; + + /* Loop through list of indexes modified since last accepted timepoint */ + num_modified = msg_data->num_modified; + for(i = 0; i < num_modified; i++) { + + /* Get the port index */ + port_index = msg_data->modified_index[i]; + + /* Scan data for this port from last_step to determine new setting */ + /* for tail, and splice later data into the free list */ + msg_ptr = msg_data->last_step[port_index]; + msg = *msg_ptr; + while(1) { + if((msg->next == NULL) || (msg->next->step > new_time)) { + + /* Splice rest of list, if any, into free list */ + head = msg->next; + if(head) { + tail = *(msg_data->tail[port_index]); + free_head = msg_data->free[port_index]; + msg_data->free[port_index] = head; + tail->next = free_head; + } + + /* Set the tail */ + msg_data->tail[port_index] = msg_ptr; + msg->next = NULL; + + break; + } + msg_ptr = &(msg->next); + msg = msg->next; + } + + } /* end for number modified */ + + /* Update/compact the modified list */ + for(i = 0, j = 0; i < num_modified; i++) { + port_index = msg_data->modified_index[i]; + /* If nothing after last_step, remove this index from the modified list */ + if((*(msg_data->last_step[port_index]))->next == NULL) { + msg_data->modified[port_index] = MIF_FALSE; + (msg_data->num_modified)--; + } + /* else, keep the index */ + else { + msg_data->modified_index[j] = msg_data->modified_index[i]; + j++; + } + } +} + + + +/* +EVTbackup_inst_queue() + +Backup data in inst queue. +*/ + + +static void EVTbackup_inst_queue( + CKTcircuit *ckt, /* the main circuit structure */ + double new_time) /* the time to backup to */ +{ + + int i; + int j; + + int num_modified; + int num_pending; + int inst_index; + + Evt_Inst_Queue_t *inst_queue; + + Evt_Inst_Event_t **inst_ptr; + Evt_Inst_Event_t *inst; + + double next_time; + double event_time; + + + /* Get pointers for quick access */ + inst_queue = &(ckt->evt->queue.inst); + + /* Loop through list of indexes modified since last accepted timepoint */ + /* and remove events with posted time > new_time */ + num_modified = inst_queue->num_modified; + for(i = 0; i < num_modified; i++) { + + /* Get the inst index */ + inst_index = inst_queue->modified_index[i]; + + /* Scan forward from last_step and cut out data with posted time */ + /* > new_time and add it to the free list */ + + inst_ptr = inst_queue->last_step[inst_index]; + inst = *inst_ptr; + + while(inst) { + if(inst->posted_time > new_time) { + *inst_ptr = inst->next; + inst->next = inst_queue->free[inst_index]; + inst_queue->free[inst_index] = inst; + inst = *inst_ptr; + } + else { + inst_ptr = &(inst->next); + inst = *inst_ptr; + } + } + + /* Scan forward from last_step and set current to first */ + /* event with event_time > new_time */ + + inst_ptr = inst_queue->last_step[inst_index]; + inst = *inst_ptr; + + while(inst) { + if(inst->event_time > new_time) + break; + inst_ptr = &((*inst_ptr)->next); + inst = *inst_ptr; + } + inst_queue->current[inst_index] = inst_ptr; + } + + /* Add set of items modified to set of items pending before updating the */ + /* pending list because things may have been pulled from the pending list */ + for(i = 0; i < num_modified; i++) { + j = inst_queue->modified_index[i]; + if(! inst_queue->pending[j]) { + inst_queue->pending[j] = MIF_TRUE; + inst_queue->pending_index[(inst_queue->num_pending)++] = j; + } + } + + /* Update the pending list and the next time by seeing if there */ + /* is anything at the location pointed to by current */ + next_time = 1e30; + num_pending = inst_queue->num_pending; + for(i = 0, j = 0; i < num_pending; i++) { + inst_index = inst_queue->pending_index[i]; + inst = *(inst_queue->current[inst_index]); + /* If nothing in queue at last_step, remove this index from the pending list */ + if(! inst) { + inst_queue->pending[inst_index] = MIF_FALSE; + (inst_queue->num_pending)--; + } + /* else, keep the index and update the next time */ + else { + inst_queue->pending_index[j] = inst_queue->pending_index[i]; + j++; + event_time = inst->event_time; + if(event_time < next_time) + next_time = event_time; + } + } + inst_queue->next_time = next_time; + + /* Update the modified list by looking for any queued events */ + /* with posted time > last_time */ + for(i = 0, j = 0; i < num_modified; i++) { + + inst_index = inst_queue->modified_index[i]; + inst = *(inst_queue->last_step[inst_index]); + + while(inst) { + if(inst->posted_time > inst_queue->last_time) + break; + inst = inst->next; + } + + if(! inst) { + inst_queue->modified[inst_index] = MIF_FALSE; + (inst_queue->num_modified)--; + } + else { + inst_queue->modified_index[j] = inst_queue->modified_index[i]; + j++; + } + } +} + + + + + +/* +EVTbackup_output_queue() + +Backup data in output queue. +*/ + + + +static void EVTbackup_output_queue( + CKTcircuit *ckt, /* the main circuit structure */ + double new_time) /* the time to backup to */ +{ + + int i; + int j; + + int num_modified; + int num_pending; + + int output_index; + + Evt_Output_Queue_t *output_queue; + + Evt_Output_Event_t **output_ptr; + Evt_Output_Event_t *output; + + double next_time; + double event_time; + + + /* Get pointers for quick access */ + output_queue = &(ckt->evt->queue.output); + + /* Loop through list of indexes modified since last accepted timepoint */ + /* and remove events with posted time > new_time */ + num_modified = output_queue->num_modified; + for(i = 0; i < num_modified; i++) { + + /* Get the output index */ + output_index = output_queue->modified_index[i]; + + /* Scan forward from last_step and cut out data with posted time */ + /* > new_time and add it to the free list */ + /* Also, unremove anything with removed time > new_time */ + + output_ptr = output_queue->last_step[output_index]; + output = *output_ptr; + + while(output) { + if(output->posted_time > new_time) { + *output_ptr = output->next; + output->next = output_queue->free[output_index]; + output_queue->free[output_index] = output; + output = *output_ptr; + } + else { + if(output->removed && (output->removed_time > new_time)) + output->removed = MIF_FALSE; + output_ptr = &(output->next); + output = *output_ptr; + } + } + + /* Scan forward from last_step and set current to first */ + /* event with event_time > new_time */ + + output_ptr = output_queue->last_step[output_index]; + output = *output_ptr; + + while(output) { + if(output->event_time > new_time) + break; + output_ptr = &((*output_ptr)->next); + output = *output_ptr; + } + output_queue->current[output_index] = output_ptr; + } + + /* Add set of items modified to set of items pending before updating the */ + /* pending list because things may have been pulled from the pending list */ + for(i = 0; i < num_modified; i++) { + j = output_queue->modified_index[i]; + if(! output_queue->pending[j]) { + output_queue->pending[j] = MIF_TRUE; + output_queue->pending_index[(output_queue->num_pending)++] = j; + } + } + + /* Update the pending list and the next time by seeing if there */ + /* is anything at the location pointed to by current */ + next_time = 1e30; + num_pending = output_queue->num_pending; + for(i = 0, j = 0; i < num_pending; i++) { + output_index = output_queue->pending_index[i]; + output = *(output_queue->current[output_index]); + /* If nothing in queue at last_step, remove this index from the pending list */ + if(! output) { + output_queue->pending[output_index] = MIF_FALSE; + (output_queue->num_pending)--; + } + /* else, keep the index and update the next time */ + else { + output_queue->pending_index[j] = output_queue->pending_index[i]; + j++; + event_time = output->event_time; + if(event_time < next_time) + next_time = event_time; + } + } + output_queue->next_time = next_time; + + /* Update the modified list by looking for any queued events */ + /* with posted time > last_time */ + for(i = 0, j = 0; i < num_modified; i++) { + + output_index = output_queue->modified_index[i]; + output = *(output_queue->last_step[output_index]); + + while(output) { + if(output->posted_time > output_queue->last_time) + break; + output = output->next; + } + + if(! output) { + output_queue->modified[output_index] = MIF_FALSE; + (output_queue->num_modified)--; + } + else { + output_queue->modified_index[j] = output_queue->modified_index[i]; + j++; + } + } +} diff --git a/src/xspice/evt/evtcall_hybrids.c b/src/xspice/evt/evtcall_hybrids.c new file mode 100755 index 000000000..f44146090 --- /dev/null +++ b/src/xspice/evt/evtcall_hybrids.c @@ -0,0 +1,78 @@ +/*============================================================================ +FILE EVTcall_hybrids.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTcall_hybrids which calls all models + which have both analog and event-driven ports. It is called following + successful evaluation of an analog iteration attempt to allow + events to be scheduled by the hybrid models. The 'CALL_TYPE' is set + to 'EVENT_DRIVEN' when the model is called from this function. + +INTERFACES + + void EVTcall_hybrids(CKTcircuit *ckt) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +#include "ngspice.h" +#include "cktdefs.h" + +#include "evt.h" + +#include "evtproto.h" + + +/* +EVTcall_hybrids + +This function calls all the hybrid instances. It is called following +the successful evaluation of an analog iteration. +*/ + + +void EVTcall_hybrids( + CKTcircuit *ckt) /* the main circuit structure */ +{ + + int i; + int num_hybrids; + + int *hybrid_index; + + + /* Get needed data for fast access */ + num_hybrids = ckt->evt->counts.num_hybrids; + hybrid_index = ckt->evt->info.hybrid_index; + + /* Call EVTload for all hybrids */ + for(i = 0; i < num_hybrids; i++) + EVTload(ckt, hybrid_index[i]); + +} diff --git a/src/xspice/evt/evtdeque.c b/src/xspice/evt/evtdeque.c new file mode 100755 index 000000000..3f70ce18b --- /dev/null +++ b/src/xspice/evt/evtdeque.c @@ -0,0 +1,366 @@ +/*============================================================================ +FILE EVTdequeue.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTdequeue which removes any items on the + output and instance queues with event times matching the specified + simulation time. + +INTERFACES + + void EVTdequeue(CKTcircuit *ckt, double time) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include + +#include "ngspice.h" + +#include "cktdefs.h" +//#include "util.h" + +#include "mif.h" +#include "evt.h" +#include "evtudn.h" + +#include "evtproto.h" + + +static void EVTdequeue_output(CKTcircuit *ckt, double time); +static void EVTdequeue_inst(CKTcircuit *ckt, double time); + +static void EVTprocess_output( + CKTcircuit *ckt, + int output_index, + void *value); + + +/* +EVTdequeue + +This function removes any items on the output and instance queues +with event times matching the specified simulation time. EVTiter +is then called to determine which instances need to be called. + +*/ + + +void EVTdequeue( + CKTcircuit *ckt, /* The circuit structure */ + double time) /* The event time of the events to dequeue */ +{ + + /* Take all items on output queue with matching time */ + /* and set changed flags in output queue */ + EVTdequeue_output(ckt, time); + + /* Take all items on inst queue with matching time */ + /* and set to_call flags in inst queue */ + EVTdequeue_inst(ckt, time); + +} + + +/* +EVTdequeue_output + +This function de-queues output events with times matching the +specified time. +*/ + +static void EVTdequeue_output( + CKTcircuit *ckt, /* The circuit structure */ + double time) /* The event time of the events to dequeue */ +{ + + int i; + int j; + + int num_pending; + int index; + int output_index; + + double next_time; + double event_time; + + Evt_Output_Queue_t *output_queue; + + Evt_Output_Event_t *output; + Evt_Output_Event_t **output_ptr; + + + /* Get pointers for fast access */ + output_queue = &(ckt->evt->queue.output); + + /* Exit if nothing pending on output queue or if next_time */ + /* != specified time */ + if(output_queue->num_pending == 0) + return; + if(output_queue->next_time != time) + return; + + /* Scan the list of outputs pending */ + num_pending = output_queue->num_pending; + for(i = 0; i < num_pending; i++) { + + /* Get the index of the output */ + index = output_queue->pending_index[i]; + + /* Get pointer to next event in queue at this index */ + output = *(output_queue->current[index]); + + /* If event time does not match current time, skip */ + if(output->event_time != time) + continue; + + /* It must match, so pull the event from the queue and process it */ + EVTprocess_output(ckt, index, output->value); + + /* Move current to point to next non-removed item in list */ + output_ptr = &(output->next); + output = *output_ptr; + while(output) { + if(! output->removed) + break; + output_ptr = &(output->next); + output = *output_ptr; + } + output_queue->current[index] = output_ptr; + + /* Mark that this index in the queue has been modified */ + if(! output_queue->modified[index]) { + output_queue->modified[index] = MIF_TRUE; + output_queue->modified_index[(output_queue->num_modified)++] = index; + } + } + + + /* Update/compact the pending list and update the next_time */ + next_time = 1e30; + for(i = 0, j = 0; i < num_pending; i++) { + output_index = output_queue->pending_index[i]; + output = *(output_queue->current[output_index]); + /* If nothing in queue at last_step, remove this index from the pending list */ + if(! output) { + output_queue->pending[output_index] = MIF_FALSE; + (output_queue->num_pending)--; + } + /* else, keep the index and update the next time */ + else { + output_queue->pending_index[j] = output_queue->pending_index[i]; + j++; + event_time = output->event_time; + if(event_time < next_time) + next_time = event_time; + } + } + output_queue->next_time = next_time; + + +} + + + +/* +EVTdequeue_inst + +This function de-queues instance events with times matching the +specified time. +*/ + + +void EVTdequeue_inst( + CKTcircuit *ckt, /* The circuit structure */ + double time) /* The event time of the events to dequeue */ +{ + + int i; + int j; + + int num_pending; + int index; + int inst_index; + + double next_time; + double event_time; + + Evt_Inst_Queue_t *inst_queue; + + Evt_Inst_Event_t *inst; + + + /* Get pointers for fast access */ + inst_queue = &(ckt->evt->queue.inst); + + /* Exit if nothing pending on inst queue or if next_time */ + /* != specified time */ + if(inst_queue->num_pending == 0) + return; + if(inst_queue->next_time != time) + return; + + /* Scan the list of insts pending */ + num_pending = inst_queue->num_pending; + for(i = 0; i < num_pending; i++) { + + /* Get the index of the inst */ + index = inst_queue->pending_index[i]; + + /* Get pointer to next event in queue at this index */ + inst = *(inst_queue->current[index]); + + /* If event time does not match current time, skip */ + if(inst->event_time != time) + continue; + + /* It must match, so pull the event from the queue and process it */ + if(! inst_queue->to_call[index]) { + inst_queue->to_call[index] = MIF_TRUE; + inst_queue->to_call_index[(inst_queue->num_to_call)++] = + index; + } + + /* Move current to point to next item in list */ + inst_queue->current[index] = &(inst->next); + + /* Mark that this index in the queue has been modified */ + if(! inst_queue->modified[index]) { + inst_queue->modified[index] = MIF_TRUE; + inst_queue->modified_index[(inst_queue->num_modified)++] = index; + } + } + + + /* Update/compact the pending list and update the next_time */ + next_time = 1e30; + for(i = 0, j = 0; i < num_pending; i++) { + inst_index = inst_queue->pending_index[i]; + inst = *(inst_queue->current[inst_index]); + /* If nothing in queue at last_step, remove this index from the pending list */ + if(! inst) { + inst_queue->pending[inst_index] = MIF_FALSE; + (inst_queue->num_pending)--; + } + /* else, keep the index and update the next time */ + else { + inst_queue->pending_index[j] = inst_queue->pending_index[i]; + j++; + event_time = inst->event_time; + if(event_time < next_time) + next_time = event_time; + } + } + inst_queue->next_time = next_time; + + + +} + + + +/* +EVTprocess_output + +This function processes a specified output after it is pulled +from the queue. +*/ + + +static void EVTprocess_output( + CKTcircuit *ckt, /* The circuit structure */ + int output_index, /* The index of the output to process */ + void *value) /* The output value */ +{ + + int num_outputs; + int node_index; + int udn_index; + int output_subindex; + + Evt_Output_Info_t **output_table; + Evt_Node_Info_t **node_table; + + Evt_Node_t *rhs; + Evt_Node_t *rhsold; + + Evt_Output_Queue_t *output_queue; + + Mif_Boolean_t equal; + + + output_table = ckt->evt->info.output_table; + node_table = ckt->evt->info.node_table; + + node_index = output_table[output_index]->node_index; + num_outputs = node_table[node_index]->num_outputs; + udn_index = node_table[node_index]->udn_index; + + rhs = ckt->evt->data.node->rhs; + rhsold = ckt->evt->data.node->rhsold; + + /* Determine if output is different from rhsold value */ + /* and copy it to rhs AND rhsold if so */ + /* This is somewhat inefficient, but that's the way */ + /* we have setup the structures (rhs and rhsold must match)... */ + if(num_outputs > 1) { + output_subindex = output_table[output_index]->output_subindex; + (*(g_evt_udn_info[udn_index]->compare)) + (value, + rhsold[node_index].output_value[output_subindex], + &equal); + if(! equal) { + (*(g_evt_udn_info[udn_index]->copy)) + (value, rhs[node_index].output_value[output_subindex]); + (*(g_evt_udn_info[udn_index]->copy)) + (value, rhsold[node_index].output_value[output_subindex]); + } + } + else { + (*(g_evt_udn_info[udn_index]->compare)) + (value, + rhsold[node_index].node_value, + &equal); + if(! equal) { + (*(g_evt_udn_info[udn_index]->copy)) + (value, rhs[node_index].node_value); + (*(g_evt_udn_info[udn_index]->copy)) + (value, rhsold[node_index].node_value); + } + } + + /* If different, put in changed list of output queue */ + if(! equal) { + output_queue = &(ckt->evt->queue.output); + if(! output_queue->changed[output_index]) { + output_queue->changed[output_index] = MIF_TRUE; + output_queue->changed_index[(output_queue->num_changed)++] = + output_index; + } + } +} diff --git a/src/xspice/evt/evtdump.c b/src/xspice/evt/evtdump.c new file mode 100755 index 000000000..6bb016791 --- /dev/null +++ b/src/xspice/evt/evtdump.c @@ -0,0 +1,350 @@ +/*============================================================================ +FILE EVTdump.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 6/15/92 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains functions used + to send event-driven node results data to the IPC channel when + the simulator is used with CAE software. + +INTERFACES + + EVTdump() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +#include +#include + +#include "ngspice.h" +//#include "misc.h" + +#include "cktdefs.h" +//#include "util.h" +#include "sperror.h" + +#include "mif.h" +#include "evt.h" +#include "evtproto.h" +#include "evtudn.h" + +#include "ipc.h" +#include "ipctiein.h" +#include "ipcproto.h" + + + +static void EVTsend_line( + int ipc_index, /* The index used in the dictionary */ + double step, /* The analysis step */ + void *node_value, /* The node value */ + int udn_index); /* The user-defined node index */ + + + +/* +EVTdump + +This function is called to send event-driven node data to the IPC +channel. A ``mode'' argument determines how the data is located in +the event data structure and what data is sent. + +If the mode is DCOP, then this is necessarily the first call to +the function. In this case, the set of event-driven nodes is +scanned to determine which should be sent. Only nodes that are +not inside subcircuits are sent. Next, the function sends +a ``dictionary'' of node names/types vs. node indexes. +Finally, the function sends the DC operating point solutions +for the event-driven nodes in the dictionary. + +If the mode is DCTRCURVE, it is assumed that the function has +already been called with mode = DCOP. The function scans the solution +vector and sends data for any nodes that have changed. + +If the mode is TRAN, it is assumed that the function has already +been called once with mode = DCOP. The function scans the +event data for nodes that have changed since the last accepted +analog timepoint and sends the new data. + +Note: This function must be called BEFORE calling EVTop_save or +EVTaccept() so that the state of the node data structure will allow +it to determine what has changed. +*/ + + +typedef struct evtdump_s { + Mif_Boolean_t send; /* True if this node should be sent */ + int ipc_index; /* Index for this node in dict sent to CAE system */ + char *node_name_str; /* Node name */ + char *udn_type_str; /* UDN type */ +} evtdump_dict_t; + + + +void EVTdump( + CKTcircuit *ckt, /* The circuit structure */ + Ipc_Anal_t mode, /* The analysis mode for this call */ + double step) /* The sweep step for a DCTRCURVE analysis, or */ + /* 0.0 for DCOP and TRAN */ +{ + static evtdump_dict_t *node_dict = NULL; + static int num_send_nodes; + + int i; + int j; + int num_nodes; + int num_modified; + int index; + + char *name; + int name_len; + + Mif_Boolean_t firstcall; + + Evt_Node_Data_t *node_data; + + Evt_Node_t *rhsold; + Evt_Node_t **head; + Evt_Node_t *here; + + Evt_Node_Info_t **node_table; + + char buff[10000]; + + Mif_Boolean_t equal; + + + /* Return immediately if IPC is not enabled */ + if(! g_ipc.enabled) + return; + + /* Get number of event-driven nodes */ + num_nodes = ckt->evt->counts.num_nodes; + + /* Exit immediately if no event-driven nodes in circuit */ + if(num_nodes <= 0) + return; + + + /* Get pointers for fast access to event data */ + node_data = ckt->evt->data.node; + node_table = ckt->evt->info.node_table; + rhsold = node_data->rhsold; + head = node_data->head; + + + /* Determine if this is the first call */ + if(node_dict == NULL) + firstcall = MIF_TRUE; + else + firstcall = MIF_FALSE; + + + /* If this is the first call, get the dictionary info */ + if(firstcall) { + + /* Allocate local data structure used to process nodes */ + node_dict = (void *) MALLOC(num_nodes * sizeof(evtdump_dict_t)); + + /* Loop through all nodes to determine which nodes should be sent. */ + /* Only nodes not within subcircuits qualify. */ + + num_send_nodes = 0; + for(i = 0; i < num_nodes; i++) { + + /* Get the name of the node. */ + name = node_table[i]->name; + + /* If name is in a subcircuit, mark that node should not be sent */ + /* and continue to next node. */ + name_len = strlen(name); + for(j = 0; j < name_len; j++) { + if(name[j] == ':') + break; + } + if(j < name_len) { + node_dict[i].send = MIF_FALSE; + continue; + } + + /* Otherwise, fill in info in dictionary. */ + node_dict[i].send = MIF_TRUE; + node_dict[i].ipc_index = num_send_nodes; + node_dict[i].node_name_str = name; + node_dict[i].udn_type_str = g_evt_udn_info[node_table[i]->udn_index]->name; + + /* Increment the count of nodes to be sent. */ + num_send_nodes++; + } /* end for */ + } /* end if first call */ + + + /* Exit if there are no nodes to be sent */ + if(num_send_nodes <= 0) + return; + + + /* If this is the first call, send the dictionary */ + if(firstcall) { + ipc_send_evtdict_prefix(); + for(i = 0; i < num_nodes; i++) { + if(node_dict[i].send) { + sprintf(buff, "%d %s %s", node_dict[i].ipc_index, + node_dict[i].node_name_str, + node_dict[i].udn_type_str); + ipc_send_line(buff); + } + } + ipc_send_evtdict_suffix(); + } + + /* If this is the first call, send the operating point solution */ + /* and return. */ + if(firstcall) { + ipc_send_evtdata_prefix(); + for(i = 0; i < num_nodes; i++) { + if(node_dict[i].send) { + EVTsend_line(node_dict[i].ipc_index, + step, + rhsold[i].node_value, + node_table[i]->udn_index); + } + } + ipc_send_evtdata_suffix(); + return; + } + + /* Otherwise, this must be DCTRCURVE or TRAN mode and we need to */ + /* send only stuff that has changed since the last call. */ + /* The determination of what to send is modeled after code in */ + /* EVTop_save() for DCTRCURVE and EVTaccept() for TRAN. */ + + if(mode == IPC_ANAL_DCTRCURVE) { + /* Send data prefix */ + ipc_send_evtdata_prefix(); + /* Loop through event nodes */ + for(i = 0; i < num_nodes; i++) { + /* If dictionary indicates this node should be sent */ + if(node_dict[i].send) { + /* Locate end of node data */ + here = head[i]; + for(;;) { + if(here->next) + here = here->next; + else + break; + } + /* Compare entry at end of list to rhsold */ + (*(g_evt_udn_info[node_table[i]->udn_index]->compare)) ( + rhsold[i].node_value, + here->node_value, + &equal); + /* If value in rhsold is different, send it */ + if(!equal) { + EVTsend_line(node_dict[i].ipc_index, + step, + rhsold[i].node_value, + node_table[i]->udn_index); + } + } + } + /* Send data suffix and return */ + ipc_send_evtdata_suffix(); + return; + } + + + if(mode == IPC_ANAL_TRAN) { + /* Send data prefix */ + ipc_send_evtdata_prefix(); + /* Loop through list of nodes modified since last time */ + num_modified = node_data->num_modified; + for(i = 0; i < num_modified; i++) { + /* Get the index of the node modified */ + index = node_data->modified_index[i]; + /* If dictionary indicates this node should be sent */ + if(node_dict[index].send) { + /* Scan through new events and send the data for each event */ + here = *(node_data->last_step[index]); + while((here = here->next)) { + EVTsend_line(node_dict[index].ipc_index, + here->step, + here->node_value, + node_table[index]->udn_index); + } + } + } + /* Send data suffix and return */ + ipc_send_evtdata_suffix(); + return; + } + +} + + + +/* +EVTsend_line + +This function formats the event node data and sends it to the IPC channel. +*/ + + +static void EVTsend_line( + int ipc_index, /* The index used in the dictionary */ + double step, /* The analysis step */ + void *node_value, /* The node value */ + int udn_index) /* The user-defined node index */ +{ + double dvalue; + char *svalue; + void *pvalue; + int len; + + /* Get the data to send */ + if(g_evt_udn_info[udn_index]->plot_val) + (*(g_evt_udn_info[udn_index]->plot_val)) (node_value, "", &dvalue); + else + dvalue = 0.0; + + if(g_evt_udn_info[udn_index]->print_val) + (*(g_evt_udn_info[udn_index]->print_val)) (node_value, "", &svalue); + else + svalue = ""; + + if(g_evt_udn_info[udn_index]->ipc_val) + (*(g_evt_udn_info[udn_index]->ipc_val)) (node_value, &pvalue, &len); + else { + pvalue = NULL; + len = 0; + } + + /* Send it to the IPC channel */ + ipc_send_event(ipc_index, step, dvalue, svalue, pvalue, len); +} diff --git a/src/xspice/evt/evtinit.c b/src/xspice/evt/evtinit.c new file mode 100755 index 000000000..1c59e01b1 --- /dev/null +++ b/src/xspice/evt/evtinit.c @@ -0,0 +1,437 @@ +/*============================================================================ +FILE EVTinit.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTinit which allocates and initializes + evt structure elements after the number of instances, nodes, etc. + have been determined in parsing during INPpas2. EVTinit also checks + to be sure no nodes have been used for both analog and event-driven + algorithms simultaneously. + +INTERFACES + + int EVTinit(CKTcircuit *ckt) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include +#include "ngspice.h" +#include "cktdefs.h" +//#include "util.h" +#include "sperror.h" + +#include "evtproto.h" + + + +static int EVTcheck_nodes(CKTcircuit *ckt); +static int EVTcount_hybrids(CKTcircuit *ckt); +static int EVTinit_info(CKTcircuit *ckt); +static int EVTinit_queue(CKTcircuit *ckt); +static int EVTinit_limits(CKTcircuit *ckt); + + + +/* Allocation macro with built-in check for out-of-memory */ +/* Adapted from SPICE 3C1 code in CKTsetup.c */ +#define CKALLOC(var,size,type) \ + if(size) { \ + if(!(var = (void *) MALLOC((size) * sizeof(type)))) \ + return(E_NOMEM); \ + } + + +/* +EVTinit + +Allocate and initialize additional evt structure elements now that +we can determine the number of instances, nodes, etc. + +Also check to be sure that no nodes have been used in both event-driven +and analog domains. + +In this version, we also report an error if there are no hybrids in the +circuit. This restriction may be removed in the future to allow the +simulator to be used with digital only circuits... +*/ + + +int EVTinit( + CKTcircuit *ckt) /* the circuit structure */ +{ + + int err; /* SPICE error return code 0 = OK */ + + /* static char *err_no_hybrids = "ERROR - no hybrids found in input deck";*/ + + + /* Exit immediately if there are no event-driven instances */ + /* but don't complain */ + if(ckt->evt->counts.num_insts == 0) + return(OK); + + /* Count the number of hybrids and hybrid outputs */ + err = EVTcount_hybrids(ckt); + if(err) + return(err); + + /* Exit with error if there are no hybrids in the circuit. */ + /* Will probably remove this restriction later... */ +/* + if(ckt->evt->counts.num_hybrids == 0) { + errMsg = MALLOC(strlen(err_no_hybrids) + 1); + strcpy(errMsg, err_no_hybrids); + return(E_PRIVATE); + } +*/ + /* Check that event nodes have not been used as analog nodes also */ + err = EVTcheck_nodes(ckt); + if(err) + return(err); + + /* Create info table arrays */ + err = EVTinit_info(ckt); + if(err) + return(err); + + /* Setup queues */ + err = EVTinit_queue(ckt); + if(err) + return(err); + + /* Initialize limits */ + err = EVTinit_limits(ckt); + if(err) + return(err); + + /* Note: Options were initialized in CKTinit so that INPpas2 */ + /* could set values according to .options cards in deck. The */ + /* structure 'jobs' will be setup immediately prior to each */ + /* simulation job. The results data structure is also */ + /* allocated immediately prior to each simulation job. */ + + /* Return */ + return(OK); +} + + +/* +EVTcount_hybrids + +Count the number of hybrids and the number of outputs on all hybrids. +*/ + +static int EVTcount_hybrids( + CKTcircuit *ckt) /* The circuit structure */ +{ + + int i; + int j; + + int num_hybrids; + int num_hybrid_outputs; + int num_conn; + int num_port; + + MIFinstance *fast; + + Evt_Inst_Info_t *inst; + + + /* Count number of hybrids and hybrid outputs in the inst list */ + /* created during parsing. Note: other counts */ + /* are created during parsing, but these were */ + /* too difficult to do until now... */ + num_hybrids = 0; + num_hybrid_outputs = 0; + inst = ckt->evt->info.inst_list; + while(inst) { + fast = inst->inst_ptr; + if(fast->analog && fast->event_driven) { + num_hybrids++; + num_conn = fast->num_conn; + for(i = 0; i < num_conn; i++) { + if((! fast->conn[i]->is_null) && (fast->conn[i]->is_output)) { + num_port = fast->conn[i]->size; + for(j = 0; j < num_port; j++) + if(! fast->conn[i]->port[j]->is_null) + num_hybrid_outputs++; + } + } + } + inst = inst->next; + } + ckt->evt->counts.num_hybrids = num_hybrids; + ckt->evt->counts.num_hybrid_outputs = num_hybrid_outputs; + + return(OK); +} + + +/* +EVTcheck_nodes + +Report error if any event node name is also used as an analog node. +*/ + + +static int EVTcheck_nodes( + CKTcircuit *ckt) /* The circuit structure */ +{ + + CKTnode *analog_node; + Evt_Node_Info_t *event_node; + + static char *err_prefix = "ERROR - node "; + static char *err_collide = " cannot be both analog and digital"; + + + /* Report error if any analog node name matches any event node name */ + event_node = ckt->evt->info.node_list; + while(event_node) { + analog_node = ckt->CKTnodes; + while(analog_node) { + if(strcmp(event_node->name, analog_node->name) == 0) { + errMsg = MALLOC(strlen(err_prefix) + strlen(event_node->name) + + strlen(err_collide) + 1); + sprintf(errMsg, "%s%s%s", err_prefix, + event_node->name, + err_collide); + + fprintf(stdout,errMsg); + + return(E_PRIVATE); + } + analog_node = analog_node->next; + } + event_node = event_node->next; + } + + /* Return */ + return(OK); +} + + +/* +EVTinit_info + +This function creates the ``info'' pointer tables used in the +event-driven circuit representation. These arrays allow faster +access to data associated with instances, nodes, ports, and +outputs than could be provided by having to scan the linked-list +representations created during parsing. +*/ + + +static int EVTinit_info( + CKTcircuit *ckt) /* the circuit structure */ +{ + + int i; + int j; + + int num_insts; + int num_nodes; + int num_ports; + int num_outputs; + + Evt_Inst_Info_t *inst; + Evt_Node_Info_t *node; + Evt_Port_Info_t *port; + Evt_Output_Info_t *output; + + Evt_Inst_Info_t **inst_table; + Evt_Node_Info_t **node_table; + Evt_Port_Info_t **port_table; + Evt_Output_Info_t **output_table; + + int *hybrid_index; + + int num_hybrids; + + + /* Allocate and initialize table of inst pointers */ + num_insts = ckt->evt->counts.num_insts; + CKALLOC(inst_table, num_insts, void *) + inst = ckt->evt->info.inst_list; + for(i = 0; i < num_insts; i++) { + inst_table[i] = inst; + inst = inst->next; + } + ckt->evt->info.inst_table = inst_table; + + /* Allocate and initialize table of node pointers */ + num_nodes = ckt->evt->counts.num_nodes; + CKALLOC(node_table, num_nodes, void *) + node = ckt->evt->info.node_list; + for(i = 0; i < num_nodes; i++) { + node_table[i] = node; + node = node->next; + } + ckt->evt->info.node_table = node_table; + + /* Allocate and initialize table of port pointers */ + num_ports = ckt->evt->counts.num_ports; + CKALLOC(port_table, num_ports, void *) + port = ckt->evt->info.port_list; + for(i = 0; i < num_ports; i++) { + port_table[i] = port; + port = port->next; + } + ckt->evt->info.port_table = port_table; + + /* Allocate and initialize table of output pointers */ + num_outputs = ckt->evt->counts.num_outputs; + CKALLOC(output_table, num_outputs, void *) + output = ckt->evt->info.output_list; + for(i = 0; i < num_outputs; i++) { + output_table[i] = output; + output = output->next; + } + ckt->evt->info.output_table = output_table; + + + /* Allocate and create table of indexes into inst_table for hybrids */ + num_hybrids = ckt->evt->counts.num_hybrids; + CKALLOC(hybrid_index, num_hybrids, int) + for(i = 0, j = 0; i < num_insts; i++) { + if(inst_table[i]->inst_ptr->analog) + hybrid_index[j++] = i; + } + ckt->evt->info.hybrid_index = hybrid_index; + + + /* Return */ + return(OK); +} + + + +/* +EVTinit_queue + +This function prepares the event-driven queues for simulation. +*/ + + +static int EVTinit_queue( + CKTcircuit *ckt) /* the circuit structure */ +{ + + int num_insts; + int num_nodes; + int num_outputs; + + Evt_Inst_Queue_t *inst_queue; + Evt_Node_Queue_t *node_queue; + Evt_Output_Queue_t *output_queue; + + + /* Allocate elements in the inst queue */ + + num_insts = ckt->evt->counts.num_insts; + inst_queue = &(ckt->evt->queue.inst); + + CKALLOC(inst_queue->head, num_insts, void *) + CKALLOC(inst_queue->current, num_insts, void *) + CKALLOC(inst_queue->last_step, num_insts, void *) + CKALLOC(inst_queue->free, num_insts, void *) + CKALLOC(inst_queue->modified_index, num_insts, int) + CKALLOC(inst_queue->modified, num_insts, Mif_Boolean_t) + CKALLOC(inst_queue->pending_index, num_insts, int) + CKALLOC(inst_queue->pending, num_insts, Mif_Boolean_t) + CKALLOC(inst_queue->to_call_index, num_insts, int) + CKALLOC(inst_queue->to_call, num_insts, Mif_Boolean_t) + + + /* Allocate elements in the node queue */ + + num_nodes = ckt->evt->counts.num_nodes; + node_queue = &(ckt->evt->queue.node); + + CKALLOC(node_queue->to_eval_index, num_nodes, int) + CKALLOC(node_queue->to_eval, num_nodes, Mif_Boolean_t) + CKALLOC(node_queue->changed_index, num_nodes, int) + CKALLOC(node_queue->changed, num_nodes, Mif_Boolean_t) + + + /* Allocate elements in the output queue */ + + num_outputs = ckt->evt->counts.num_outputs; + output_queue = &(ckt->evt->queue.output); + + CKALLOC(output_queue->head, num_outputs, void *) + CKALLOC(output_queue->current, num_outputs, void *) + CKALLOC(output_queue->last_step, num_outputs, void *) + CKALLOC(output_queue->free, num_outputs, void *) + CKALLOC(output_queue->modified_index, num_outputs, int) + CKALLOC(output_queue->modified, num_outputs, Mif_Boolean_t) + CKALLOC(output_queue->pending_index, num_outputs, int) + CKALLOC(output_queue->pending, num_outputs, Mif_Boolean_t) + CKALLOC(output_queue->changed_index, num_outputs, int) + CKALLOC(output_queue->changed, num_outputs, Mif_Boolean_t) + + + /* Return */ + return(OK); +} + + + + +/* +EVTinit_limits + +This function initializes the iteration limits applicable to the +event-driven algorithm. +*/ + + +static int EVTinit_limits( + CKTcircuit *ckt) /* the circuit structure */ +{ + + /* Set maximum number of event load calls within a single event iteration */ + /* to the number of event outputs. This should allow for the */ + /* maximum possible number of events that can trickle through any */ + /* circuit that does not contain loops. */ + + ckt->evt->limits.max_event_passes = ckt->evt->counts.num_outputs + 1; + + + /* Set maximum number of alternations between analog and event-driven */ + /* iterations to the number of event outputs on hybrids. */ + + ckt->evt->limits.max_op_alternations = ckt->evt->counts.num_hybrid_outputs + 1; + + + /* Return */ + return(OK); +} diff --git a/src/xspice/evt/evtiter.c b/src/xspice/evt/evtiter.c new file mode 100755 index 000000000..d53f0a651 --- /dev/null +++ b/src/xspice/evt/evtiter.c @@ -0,0 +1,300 @@ +/*============================================================================ +FILE EVTiter.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTiter which iterates through + event-driven outputs and instances until the outputs no longer change. + + +INTERFACES + + int EVTiter(CKTcircuit *ckt) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include + +#include "ngspice.h" +//#include "misc.h" +#include "cktdefs.h" +//#include "util.h" +#include "sperror.h" + +#include "mif.h" +#include "evt.h" +#include "evtudn.h" + +#include "evtproto.h" + + +/* +EVTiter + +This function iterates through event-driven outputs and instances +until the outputs no longer change. The general algorithm used +is: + +Do: + + Scan list of changed outputs + Put items on the node queue 'to_eval' list for + each changed output. + + Scan list of changed nodes + Resolve nodes with multiple outputs posted. + Create inverted state for nodes with attached + inverted inputs. + Put items on the instance queue 'to_call' list + for each changed node. + If transient analysis, put state of the node + into the node data structure. + + Scan instance to_call list + Call EVTload for each instance on list. + +While there are changed outputs + +*/ + + +int EVTiter( + CKTcircuit *ckt) /* the circuit structure */ +{ + + int i; + int num_changed; + + int num_to_eval; + int num_to_call; + + int output_index; + /* int output_subindex;*/ + int inst_index; + int node_index; + int port_index; + + int num_outputs; + int udn_index; + + int passes; + + Evt_Ckt_Data_t *evt; + + Evt_Output_Queue_t *output_queue; + Evt_Node_Queue_t *node_queue; + Evt_Inst_Queue_t *inst_queue; + + Evt_Output_Info_t **output_table; + Evt_Node_Info_t **node_table; + Evt_Port_Info_t **port_table; + + Evt_Inst_Index_t *inst_list; + + Evt_Node_Data_t *node_data; + + Evt_Node_t *rhs; + Evt_Node_t *rhsold; + + Evt_Node_t *node; + + Mif_Boolean_t equal; + + char *err_msg; + + + /* Get temporary pointers for fast access */ + evt = ckt->evt; + + output_queue = &(evt->queue.output); + node_queue = &(evt->queue.node); + inst_queue = &(evt->queue.inst); + + output_table = evt->info.output_table; + node_table = evt->info.node_table; + port_table = evt->info.port_table; + + node_data = evt->data.node; + rhs = node_data->rhs; + rhsold = node_data->rhsold; + + + /* Loop until no more output change, or too many passes through loop */ + for(passes = 0; passes < evt->limits.max_event_passes; passes++) { + + + /* Create list of nodes to evaluate from list of changed outputs */ + num_changed = output_queue->num_changed; + for(i = 0; i < num_changed; i++) { + + /* Get index of node that output is connected to */ + output_index = output_queue->changed_index[i]; + node_index = output_table[output_index]->node_index; + + /* If not already on list of nodes to evaluate, add it */ + if(! node_queue->to_eval[node_index]) { + node_queue->to_eval[node_index] = MIF_TRUE; + node_queue->to_eval_index[(node_queue->num_to_eval)++] + = node_index; + } + + /* Reset the changed flag on the output queue */ + output_queue->changed[output_index] = MIF_FALSE; + + } + output_queue->num_changed = 0; + + + + /* Evaluate nodes and for any which have changed, enter */ + /* the instances that receive inputs from them on the list */ + /* of instances to call */ + + num_to_eval = node_queue->num_to_eval; + for(i = 0; i < num_to_eval; i++) { + + /* Get the node index, udn index and number of outputs */ + node_index = node_queue->to_eval_index[i]; + udn_index = node_table[node_index]->udn_index; + num_outputs = node_table[node_index]->num_outputs; + + /* Resolve the node value if multiple outputs on it */ + /* and test if new node value is different than old value */ + if(num_outputs > 1) { + (*(g_evt_udn_info[udn_index]->resolve)) + (num_outputs, + rhs[node_index].output_value, + rhs[node_index].node_value); + (*(g_evt_udn_info[udn_index]->compare)) + (rhs[node_index].node_value, + rhsold[node_index].node_value, + &equal); + if(! equal) { + (*(g_evt_udn_info[udn_index]->copy)) + (rhs[node_index].node_value, + rhsold[node_index].node_value); + } + } + /* Else, load function has already determined that they were */ + /* not equal */ + else + equal = MIF_FALSE; + + /* If not equal, make inverted copy in rhsold if */ + /* needed, and place indexes of instances with inputs connected */ + /* to the node in the to_call list of inst queue */ + if(! equal) { + if(node_table[node_index]->invert) { + (*(g_evt_udn_info[udn_index]->copy)) + (rhsold[node_index].node_value, + rhsold[node_index].inverted_value); + (*(g_evt_udn_info[udn_index]->invert)) + (rhsold[node_index].inverted_value); + } + inst_list = node_table[node_index]->inst_list; + while(inst_list) { + inst_index = inst_list->index; + if(! inst_queue->to_call[inst_index]) { + inst_queue->to_call[inst_index] = MIF_TRUE; + inst_queue->to_call_index[(inst_queue->num_to_call)++] + = inst_index; + } + inst_list = inst_list->next; + } /* end while instances with inputs on node */ + } /* end if not equal */ + + /* If transient analysis mode */ + /* Save the node data onto the node results list and mark */ + /* that it has been modified, even if the */ + /* resolved node value has not changed */ + if(g_mif_info.circuit.anal_type == MIF_TRAN) { + + node = *(node_data->tail[node_index]); + node_data->tail[node_index] = &(node->next); + EVTnode_copy(ckt, node_index, &(rhsold[node_index]), &(node->next)); + node->next->step = g_mif_info.circuit.evt_step; + + if(! node_data->modified[node_index]) { + node_data->modified[node_index] = MIF_TRUE; + node_data->modified_index[(node_data->num_modified)++] = node_index; + } + } + + /* Reset the to_eval flag on the node queue */ + node_queue->to_eval[node_index] = MIF_FALSE; + + } /* end for number of nodes to evaluate */ + node_queue->num_to_eval = 0; + + + + /* Call the instances with inputs on nodes that have changed */ + num_to_call = inst_queue->num_to_call; + for(i = 0; i < num_to_call; i++) { + inst_index = inst_queue->to_call_index[i]; + inst_queue->to_call[inst_index] = MIF_FALSE; + EVTload(ckt, inst_index); + } + inst_queue->num_to_call = 0; + + + /* Record statistics */ + if(g_mif_info.circuit.anal_type == MIF_DC) + (ckt->evt->data.statistics->op_event_passes)++; + + + /* If no outputs changed, iteration is over, so return with success! */ + if(output_queue->num_changed == 0) + return(0); + + } /* end for */ + + + /* Too many passes through loop, report problems and exit with error */ + + err_msg = MALLOC(10000); + for(i = 0; i < output_queue->num_changed; i++) { + output_index = output_queue->changed_index[i]; + port_index = output_table[output_index]->port_index; + sprintf(err_msg, "\n Instance: %s\n Connection: %s\n Port: %d", + port_table[port_index]->inst_name, + port_table[port_index]->conn_name, + port_table[port_index]->port_num); + ENHreport_conv_prob(ENH_EVENT_NODE, + port_table[port_index]->node_name, + err_msg); + } + FREE(err_msg); + + (*(SPfrontEnd->IFerror)) (ERR_WARNING, + "Too many iteration passes in event-driven circuits", + (IFuid *) NULL); + return(E_ITERLIM); + +} diff --git a/src/xspice/evt/evtload.c b/src/xspice/evt/evtload.c new file mode 100755 index 000000000..606ec4cf2 --- /dev/null +++ b/src/xspice/evt/evtload.c @@ -0,0 +1,613 @@ +/*============================================================================ +FILE EVTload.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTload which is used to call a + specified event-driven or hybrid code model during an event-driven + iteration. The 'CALL_TYPE' is set to 'EVENT_DRIVEN' when the + model is called from this function. + +INTERFACES + + int EVTload(CKTcircuit *ckt, int inst_index) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include + +#include "ngspice.h" +#include "cktdefs.h" +//#include "util.h" +#include "devdefs.h" +#include "sperror.h" + +#include "mif.h" +#include "evt.h" +#include "evtudn.h" + +#include "mifproto.h" +#include "evtproto.h" + + + + +extern SPICEdev **DEVices; + + + +static void EVTcreate_state( + CKTcircuit *ckt, + int inst_index); + +static void EVTadd_msg( + CKTcircuit *ckt, + int port_index, + char *msg_text); + +static void EVTcreate_output_event( + CKTcircuit *ckt, + int node_index, + int output_index, + void **value_ptr); + +static void EVTprocess_output( + CKTcircuit *ckt, + Mif_Boolean_t changed, + int output_index, + Mif_Boolean_t invert, + double delay); + + + +/* +EVTload + +This function calls the code model function for the specified +instance with CALL_TYPE set to EVENT_DRIVEN. Event outputs, +messages, etc. are processed on return from the code model. +Analog outputs, partials, etc. should not be computed by the +code model when the call type is event-driven and are +ignored. +*/ + +int EVTload( + CKTcircuit *ckt, /* The circuit structure */ + int inst_index) /* The instance to call code model for */ +{ + + int i; + int j; + + int num_conn; + int num_port; + int mod_type; + + Mif_Conn_Data_t *conn; + Mif_Port_Data_t *port; + + Mif_Private_t cm_data; + + MIFinstance *inst; + + Evt_Node_Data_t *node_data; + + void *value_ptr; + + + /* ***************************** */ + /* Prepare the code model inputs */ + /* ***************************** */ + + /* Get pointer to instance data structure and other data */ + /* needed for fast access */ + inst = ckt->evt->info.inst_table[inst_index]->inst_ptr; + node_data = ckt->evt->data.node; + + /* Setup circuit data in struct to be passed to code model function */ + + if(inst->initialized) + cm_data.circuit.init = MIF_FALSE; + else + cm_data.circuit.init = MIF_TRUE; + + cm_data.circuit.anal_init = MIF_FALSE; + cm_data.circuit.anal_type = g_mif_info.circuit.anal_type; + + if(g_mif_info.circuit.anal_type == MIF_TRAN) + cm_data.circuit.time = g_mif_info.circuit.evt_step; + else + cm_data.circuit.time = 0.0; + + cm_data.circuit.call_type = MIF_EVENT_DRIVEN; + cm_data.circuit.temperature = ckt->CKTtemp - 273.15; + + + /* Setup data needed by cm_... functions */ + + g_mif_info.ckt = ckt; + g_mif_info.instance = inst; + g_mif_info.errmsg = ""; + g_mif_info.circuit.call_type = MIF_EVENT_DRIVEN; + + if(inst->initialized) + g_mif_info.circuit.init = MIF_FALSE; + else + g_mif_info.circuit.init = MIF_TRUE; + + + /* If after initialization and in transient analysis mode */ + /* create a new state for the instance */ + + if((g_mif_info.circuit.anal_type == MIF_TRAN) && inst->initialized) + EVTcreate_state(ckt, inst_index); + + + /* Loop through all connections on the instance and setup */ + /* load, total_load, and msg on all ports, and changed flag */ + /* and output pointer on all outputs */ + + num_conn = inst->num_conn; + for(i = 0; i < num_conn; i++) { + + conn = inst->conn[i]; + + /* if connection is null, continue to next */ + if(conn->is_null) + continue; + + /* Loop through each port on the connection */ + num_port = conn->size; + for(j = 0; j < num_port; j++) { + + port = conn->port[j]; + + /* Skip if port is null */ + if(port->is_null) + continue; + + /* If port type is Digital or User-Defined */ + if((port->type == MIF_DIGITAL) || (port->type == MIF_USER_DEFINED)) { + + /* Initialize the msg pointer on the port to NULL, */ + /* initialize the load value to zero, and get the total load */ + port->msg = NULL; + port->load = 0.0; + port->total_load = node_data->total_load[port->evt_data.node_index]; + + /* If connection is an output, initialize changed to true */ + /* and create a new output event object in the free list */ + /* if transient analysis mode */ + if(conn->is_output) { + port->changed = MIF_TRUE; + if(g_mif_info.circuit.anal_type == MIF_TRAN) { + EVTcreate_output_event(ckt, + port->evt_data.node_index, + port->evt_data.output_index, + &value_ptr); + port->output.pvalue = value_ptr; + } + } + } + else { + /* Get the analog input value. All we need to do is */ + /* set it to zero if mode is INITJCT. Otherwise, value */ + /* should still be around from last successful analog call */ + if(ckt->CKTmode & MODEINITJCT) + port->input.rvalue = 0.0; + } + + } /* end for number of ports */ + } /* end for number of connections */ + + + /* Prepare the structure to be passed to the code model */ + cm_data.num_conn = inst->num_conn; + cm_data.conn = inst->conn; + cm_data.num_param = inst->num_param; + cm_data.param = inst->param; + cm_data.num_inst_var = inst->num_inst_var; + cm_data.inst_var = inst->inst_var; + + + /* ******************* */ + /* Call the code model */ + /* ******************* */ + + mod_type = inst->MIFmodPtr->MIFmodType; + (*(DEVices[mod_type]->DEVpublic.cm_func)) (&cm_data); + + + /* ****************************** */ + /* Process the code model outputs */ + /* ****************************** */ + + /* Loop through all connections and ports and process the msgs */ + /* and event outputs */ + + num_conn = inst->num_conn; + for(i = 0; i < num_conn; i++) { + + conn = inst->conn[i]; + if(conn->is_null) + continue; + + /* Loop through each port on the connection */ + num_port = conn->size; + for(j = 0; j < num_port; j++) { + + port = conn->port[j]; + + /* Skip if port is null */ + if(port->is_null) + continue; + + /* Process the message if any */ + if(port->msg) + EVTadd_msg(ckt, port->evt_data.port_index, port->msg); + + /* If this is the initialization pass, process the load factor */ + if(! inst->initialized) { + node_data->total_load[port->evt_data.node_index] += + port->load; + } + + /* If connection is not an event output, continue to next port */ + if(! conn->is_output) + continue; + if((port->type != MIF_DIGITAL) && (port->type != MIF_USER_DEFINED)) + continue; + + /* If output changed, process it */ + EVTprocess_output(ckt, port->changed, + port->evt_data.output_index, + port->invert, port->delay); + + /* And prevent erroneous models from overwriting it during */ + /* analog iterations */ + if(g_mif_info.circuit.anal_type == MIF_TRAN) + port->output.pvalue = NULL; + + } /* end for number of ports */ + } /* end for number of connections */ + + + /* Record statistics */ + if(g_mif_info.circuit.anal_type == MIF_DC) + (ckt->evt->data.statistics->op_load_calls)++; + else if(g_mif_info.circuit.anal_type == MIF_TRAN) + (ckt->evt->data.statistics->tran_load_calls)++; + + /* Mark that the instance has been called once */ + inst->initialized = MIF_TRUE; + + return(OK); +} + + + +/* +EVTcreate_state + +This function creates a new state storage area for a particular instance +during an event-driven simulation. New states must be created so +that old states are saved and can be accessed by code models in the +future. The new state is initialized to the previous state value. +*/ + + +static void EVTcreate_state( + CKTcircuit *ckt, /* The circuit structure */ + int inst_index) /* The instance to create state for */ +{ + int i; + int total_size; + + Evt_State_Data_t *state_data; + + Evt_State_t *new_state; + Evt_State_t *prev_state; + + char *from; + char *to; + + + /* Get variables for fast access */ + state_data = ckt->evt->data.state; + + /* Exit immediately if no states on this instance */ + if(state_data->desc[inst_index] == NULL) + return; + + /* Get size of state block to be allocated */ + total_size = state_data->total_size[inst_index]; + + /* Allocate a new state for the instance */ + if(state_data->free[inst_index]) + { + new_state = state_data->free[inst_index]; + state_data->free[inst_index] = new_state->next; + } + else + { + + new_state = (void *) MALLOC(sizeof(Evt_State_t)); + new_state->block = (void *) MALLOC(total_size); + + } + + /* Splice the new state into the state data linked list */ + /* and update the tail pointer */ + prev_state = *(state_data->tail[inst_index]); + prev_state->next = new_state; + new_state->prev = prev_state; + state_data->tail[inst_index] = &(prev_state->next); + + /* Copy the old state to the new state and set the step */ + from = prev_state->block; + to = new_state->block; + for(i = 0; i < total_size; i++) + to[i] = from[i]; + new_state->step = g_mif_info.circuit.evt_step; + + /* Mark that the state data on the instance has been modified */ + if(! state_data->modified[inst_index]) { + state_data->modified[inst_index] = MIF_TRUE; + state_data->modified_index[(state_data->num_modified)++] = inst_index; + } +} + + +/* +EVTcreate_output_event + +This function creates a new output event. +*/ + +static void EVTcreate_output_event( + CKTcircuit *ckt, /* The circuit structure */ + int node_index, /* The node type port is on */ + int output_index, /* The output index for this port */ + void **value_ptr) /* The event created */ +{ + int udn_index; + Evt_Node_Info_t **node_table; + Evt_Output_Queue_t *output_queue; + Evt_Output_Event_t *event; + + + /* Check the output queue free list and use the structure */ + /* at the head of the list if non-null. Otherwise, create a new one. */ + output_queue = &(ckt->evt->queue.output); + if(output_queue->free[output_index]) { + *value_ptr = output_queue->free[output_index]->value; + } + else { + /* Create a new event */ + event = (void *) MALLOC(sizeof(Evt_Output_Event_t)); + event->next = NULL; + + /* Initialize the value */ + node_table = ckt->evt->info.node_table; + udn_index = node_table[node_index]->udn_index; + (*(g_evt_udn_info[udn_index]->create)) (&(event->value)); + + /* Put the event onto the free list and return the value pointer */ + output_queue->free[output_index] = event; + *value_ptr = event->value; + } +} + + + +/* +EVTadd_msg + +This function records a message output by a code model into the +message results data structure. +*/ + + +static void EVTadd_msg( + CKTcircuit *ckt, /* The circuit structure */ + int port_index, /* The port to add message to */ + char *msg_text) /* The message text */ +{ + + Evt_Msg_Data_t *msg_data; + + Evt_Msg_t **msg_ptr; + Evt_Msg_t *msg; + + + /* Get pointers for fast access */ + msg_data = ckt->evt->data.msg; + msg_ptr = msg_data->tail[port_index]; + + /* Set pointer to location at which to add, and update tail */ + if(*msg_ptr != NULL) { + msg_ptr = &((*msg_ptr)->next); + msg_data->tail[port_index] = msg_ptr; + } + + /* Add a new entry in the list of messages for this port */ + if(msg_data->free[port_index]) { + *msg_ptr = msg_data->free[port_index]; + msg_data->free[port_index] = msg_data->free[port_index]->next; + } + else { + *msg_ptr = (void *) MALLOC(sizeof(Evt_Msg_t)); + } + + /* Fill in the values */ + msg = *msg_ptr; + msg->next = NULL; + if((ckt->CKTmode & MODEDCOP) == MODEDCOP) + msg->op = MIF_TRUE; + else + msg->step = g_mif_info.circuit.evt_step; + msg->text = MIFcopy(msg_text); + + /* Update the modified indexes */ + if(g_mif_info.circuit.anal_type == MIF_TRAN) { + if(! msg_data->modified[port_index]) { + msg_data->modified[port_index] = MIF_TRUE; + msg_data->modified_index[(msg_data->num_modified)++] = port_index; + } + } + +} + + +/* +EVTprocess_output + +This function processes an event-driven output produced by a code +model. If transient analysis mode, the event is placed into the +output queue according to its (non-zero) delay. If DC analysis, +the event is processed immediately. +*/ + + +static void EVTprocess_output( + CKTcircuit *ckt, /* The circuit structure */ + Mif_Boolean_t changed, /* Has output changed? */ + int output_index, /* The output of interest */ + Mif_Boolean_t invert, /* Does output need to be inverted? */ + double delay) /* The output delay in transient analysis */ +{ + + int num_outputs; + int node_index; + int udn_index; + int output_subindex; + + Evt_Output_Info_t **output_table; + Evt_Node_Info_t **node_table; + + Evt_Node_t *rhs; + Evt_Node_t *rhsold; + + Evt_Output_Queue_t *output_queue; + Evt_Output_Event_t *output_event; + + Mif_Boolean_t equal; + + + output_queue = &(ckt->evt->queue.output); + output_table = ckt->evt->info.output_table; + node_table = ckt->evt->info.node_table; + + node_index = output_table[output_index]->node_index; + udn_index = node_table[node_index]->udn_index; + + + /* if transient analysis, just put the output event on the queue */ + /* to be processed at a later time */ + if(g_mif_info.circuit.anal_type == MIF_TRAN) { + /* If model signaled that output was not posted, */ + /* leave the event struct on the free list and return */ + if((! changed) || (delay <= 0.0)) { + if(changed && (delay <= 0.0)) + printf("\nERROR - Output delay <= 0 not allowed - output ignored!\n"); + return; + } + /* Remove the (now used) struct from the head of the free list */ + output_event = output_queue->free[output_index]; + output_queue->free[output_index] = output_event->next; + /* Invert the output value if necessary */ + if(invert) + (*(g_evt_udn_info[udn_index]->invert)) + (output_event->value); + /* Add it to the queue */ + EVTqueue_output(ckt, output_index, udn_index, output_event, + g_mif_info.circuit.evt_step, + g_mif_info.circuit.evt_step + delay); + return; + } + /* If not transient analysis, process immediately. */ + /* Determine if output has changed from rhsold value */ + /* and put entry in output queue changed list if so */ + else { + + /* If model signaled that output was not posted, */ + /* just return */ + if(! changed) + return; +/* + if(delay > 0.0) + printf("\nWARNING - Non-zero output delay not allowed in DCOP - delay ignored!\n"); +*/ + rhs = ckt->evt->data.node->rhs; + rhsold = ckt->evt->data.node->rhsold; + + /* Determine if changed */ + num_outputs = node_table[node_index]->num_outputs; + if(num_outputs > 1) { + output_subindex = output_table[output_index]->output_subindex; + if(invert) + (*(g_evt_udn_info[udn_index]->invert)) + (rhs[node_index].output_value[output_subindex]); + (*(g_evt_udn_info[udn_index]->compare)) + (rhs[node_index].output_value[output_subindex], + rhsold[node_index].output_value[output_subindex], + &equal); + if(! equal) { + (*(g_evt_udn_info[udn_index]->copy)) + (rhs[node_index].output_value[output_subindex], + rhsold[node_index].output_value[output_subindex]); + } + } + else { + if(invert) + (*(g_evt_udn_info[udn_index]->invert)) + (rhs[node_index].node_value); + (*(g_evt_udn_info[udn_index]->compare)) + (rhs[node_index].node_value, + rhsold[node_index].node_value, + &equal); + if(! equal) { + (*(g_evt_udn_info[udn_index]->copy)) + (rhs[node_index].node_value, + rhsold[node_index].node_value); + } + } + + /* If changed, put in changed list of output queue */ + if(! equal) { + if(! output_queue->changed[output_index]) { + output_queue->changed[output_index] = MIF_TRUE; + output_queue->changed_index[(output_queue->num_changed)++] = + output_index; + } + } + + return; + + } /* end else process immediately */ +} diff --git a/src/xspice/evt/evtnext_time.c b/src/xspice/evt/evtnext_time.c new file mode 100755 index 000000000..b2f3c35e9 --- /dev/null +++ b/src/xspice/evt/evtnext_time.c @@ -0,0 +1,93 @@ +/*============================================================================ +FILE EVTnext_time.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTnext_time which determines and + returns the time of the next scheduled event on the inst and output + queues. + +INTERFACES + + double EVTnext_time(CKTcircuit *ckt) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include + +#include "ngspice.h" +#include "cktdefs.h" +//#include "util.h" + +#include "mif.h" +#include "evt.h" + +#include "evtproto.h" + + + +/* +EVTnext_time + +Get the next event time as the minimum of the next times +in the inst and output queues. If no next time in either, +return machine infinity. +*/ + + +double EVTnext_time( + CKTcircuit *ckt) /* The circuit structure */ +{ + + double next_time; + + Evt_Inst_Queue_t *inst_queue; + Evt_Output_Queue_t *output_queue; + + + /* Initialize next time to machine infinity */ + next_time = 1e30; + + /* Get pointers for fast access */ + inst_queue = &(ckt->evt->queue.inst); + output_queue = &(ckt->evt->queue.output); + + /* If anything pending in inst queue, set next time */ + /* to minimum of itself and the inst queue next time */ + if(inst_queue->num_pending) + if(inst_queue->next_time < next_time) + next_time = inst_queue->next_time; + + /* If anything pending in output queue, set next time */ + /* to minimum of itself and the output queue next time */ + if(output_queue->num_pending) + if(output_queue->next_time < next_time) + next_time = output_queue->next_time; + + return(next_time); +} diff --git a/src/xspice/evt/evtnode_copy.c b/src/xspice/evt/evtnode_copy.c new file mode 100755 index 000000000..6690b9224 --- /dev/null +++ b/src/xspice/evt/evtnode_copy.c @@ -0,0 +1,159 @@ +/*============================================================================ +FILE EVTnode_copy.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTnode_copy which copies the state + of a node structure. + +INTERFACES + + void EVTnode_copy(CKTcircuit *ckt, int node_index, Evt_Node_t *from, + Evt_Node_t **to) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include "ngspice.h" +#include "cktdefs.h" +//#include "util.h" + +#include "mif.h" +#include "evt.h" +#include "evtudn.h" + +#include "mifproto.h" +#include "evtproto.h" +#include "cm.h" + +/* +EVTnode_copy + +This function copies the state of a node structure. + +If the destination is NULL, it is allocated before the copy. This is the +case when EVTiter copies a node during a transient analysis to +save the state of an element of rhsold into the node data structure +lists. + +If the destination is non-NULL, only the internal elements of the node +structure are copied. This is the case when EVTbackup restores that state +of nodes that existed at a certain timestep back into rhs and rhsold. +*/ + + +void EVTnode_copy( + CKTcircuit *ckt, /* The circuit structure */ + int node_index, /* The node to copy */ + Evt_Node_t *from, /* Location to copy from */ + Evt_Node_t **to) /* Location to copy to */ +{ + + int i; + + int udn_index; + int num_outputs; + Mif_Boolean_t invert; + + Evt_Node_Data_t *node_data; + Evt_Node_Info_t **node_table; + + Evt_Node_t *here; + + /* Digital_t *dummy;*/ + + /* char buff[128];*/ + + /* Get data for fast access */ + node_data = ckt->evt->data.node; + node_table = ckt->evt->info.node_table; + + udn_index = node_table[node_index]->udn_index; + num_outputs = node_table[node_index]->num_outputs; + invert = node_table[node_index]->invert; + + + /* If destination is not allocated, allocate it */ + /* otherwise we just copy into the node struct */ + here = *to; + + if(here == NULL) + { + /* Use allocated structure on free list if available */ + /* Otherwise, allocate a new one */ + here = node_data->free[node_index]; + if(here) + { + *to = here; + node_data->free[node_index] = here->next; + here->next = NULL; + } + else + { + here = (void *) MALLOC(sizeof(Evt_Node_t)); + *to = here; + /* Allocate/initialize the data in the new node struct */ + if(num_outputs > 1) + { + here->output_value = (void *) MALLOC(num_outputs * sizeof(void *)); + + for(i = 0; i < num_outputs; i++) + { + (*(g_evt_udn_info[udn_index]->create)) + ( &(here->output_value[i]) ); + } + } + + here->node_value = NULL; + + (*(g_evt_udn_info[udn_index]->create)) ( &(here->node_value) ); + + if(invert) + (*(g_evt_udn_info[udn_index]->create)) ( &(here->inverted_value) ); + + + } + } + + /* Copy the node data */ + here->op = from->op; + here->step = from->step; + if(num_outputs > 1) + { + for(i = 0; i < num_outputs; i++) + { + (*(g_evt_udn_info[udn_index]->copy)) (from->output_value[i], + here->output_value[i]); + } + } + (*(g_evt_udn_info[udn_index]->copy)) (from->node_value, here->node_value); + if(invert) + { + (*(g_evt_udn_info[udn_index]->copy)) (from->inverted_value, + here->inverted_value); + } +} diff --git a/src/xspice/evt/evtop.c b/src/xspice/evt/evtop.c new file mode 100755 index 000000000..fa431ecd8 --- /dev/null +++ b/src/xspice/evt/evtop.c @@ -0,0 +1,321 @@ +/*============================================================================ +FILE EVTop.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTop which is used to perform an + operating point analysis in place of CKTop when there are + event-driven instances in the circuit. It alternates between doing + event-driven iterations with EVTiter and doing analog iterations with + NIiter/CKTop until no more event-driven outputs change. + +INTERFACES + + EVTop() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include "ngspice.h" +#include "cktdefs.h" +//#include "util.h" +#include "sperror.h" + +#include "mif.h" +#include "evt.h" +#include "evtproto.h" +#include "evtudn.h" + + +static void EVTnode_compare( + CKTcircuit *ckt, + int node_index, + Evt_Node_t *node1, + Evt_Node_t *node2, + Mif_Boolean_t *equal); + + + +/* +EVTop + +This function is used to perform an operating point analysis +in place of CKTop when there are event-driven instances in the +circuit. It alternates between doing event-driven iterations +with EVTiter and doing analog iterations with NIiter/CKTop +until no more event-driven outputs change. +*/ + + +int EVTop( + CKTcircuit *ckt, /* The circuit structure */ + long firstmode, /* The SPICE 3C1 CKTop() firstmode parameter */ + long continuemode, /* The SPICE 3C1 CKTop() continuemode paramter */ + int max_iter, /* The SPICE 3C1 CKTop() max iteration parameter */ + Mif_Boolean_t first_call) /* Is this the first time through? */ +{ + + int i; + int num_insts; + int converged; + int output_index; + int port_index; + + char *err_msg; + + Mif_Boolean_t firstime; + + Evt_Inst_Queue_t *inst_queue; + Evt_Output_Queue_t *output_queue; + + Evt_Output_Info_t **output_table; + Evt_Port_Info_t **port_table; + + + /* get data to local storage for fast access */ + num_insts = ckt->evt->counts.num_insts; + inst_queue = &(ckt->evt->queue.inst); + + /* Initialize to_call entries in event inst queue */ + /* to force calling all event/hybrid instance the first */ + /* time through */ + + if(first_call) { + for(i = 0; i < num_insts; i++) { + inst_queue->to_call[i] = MIF_TRUE; + inst_queue->to_call_index[i] = i; + } + inst_queue->num_to_call = num_insts; + } + + + /* Alternate between event-driven and analog solutions until */ + /* there are no changed event-driven outputs */ + + firstime = MIF_TRUE; + for(;;) { + + /* Call EVTiter to establish initial outputs from */ + /* event/hybrid instances with states (e.g. flip-flops) */ + + ckt->CKTmode = firstmode; + converged = EVTiter(ckt); + if(converged != 0) + return(converged); + + /* Now do analog solution for current state of hybrid outputs */ + + /* If first analog solution, call CKTop */ + if(firstime) { + firstime = MIF_FALSE; + converged = CKTop(ckt, + firstmode, + continuemode, + max_iter); + if(converged != 0) + return(converged); + } + /* Otherwise attempt to converge with mode = continuemode */ + else { + ckt->CKTmode = continuemode; + converged = NIiter(ckt,max_iter); + if(converged != 0) { + converged = CKTop(ckt, + firstmode, + continuemode, + max_iter); + if(converged != 0) + return(converged); + } + } + + /* Call all hybrids to allow new event outputs to be posted */ + EVTcall_hybrids(ckt); + + /* Increment count of successful alternations */ + (ckt->evt->data.statistics->op_alternations)++; + + /* If .option card specified not to alternate solutions, exit */ + /* immediately with this first pass solution */ + if(! ckt->evt->options.op_alternate) + return(0); + + /* If no hybrid instances produced different event outputs, */ + /* alternation is completed, so exit */ + if(ckt->evt->queue.output.num_changed == 0) + return(0); + + /* If too many alternations, exit with error */ + if(ckt->evt->data.statistics->op_alternations >= + ckt->evt->limits.max_op_alternations) { + + (*(SPfrontEnd->IFerror)) (ERR_WARNING, + "Too many analog/event-driven solution alternations", + (IFuid *) NULL); + + err_msg = MALLOC(10000); + output_queue = &(ckt->evt->queue.output); + output_table = ckt->evt->info.output_table; + port_table = ckt->evt->info.port_table; + + for(i = 0; i < output_queue->num_changed; i++) { + output_index = output_queue->changed_index[i]; + port_index = output_table[output_index]->port_index; + sprintf(err_msg, "\n Instance: %s\n Connection: %s\n Port: %d", + port_table[port_index]->inst_name, + port_table[port_index]->conn_name, + port_table[port_index]->port_num); + ENHreport_conv_prob(ENH_EVENT_NODE, + port_table[port_index]->node_name, + err_msg); + } + FREE(err_msg); + + return(E_ITERLIM); + } + + } /* end forever */ +} + + + +/* +EVTop_save + +Save result from operating point iteration into the node data area. +*/ + +void EVTop_save( + CKTcircuit *ckt, /* The circuit structure */ + Mif_Boolean_t op, /* True if from a DCOP analysis, false if TRANOP, etc. */ + double step) +{ + + int i; + int num_nodes; + + Mif_Boolean_t equal; + + Evt_Node_Data_t *node_data; + + Evt_Node_t *rhsold; + Evt_Node_t **head; + Evt_Node_t **here; + + /* char buff[128];*/ + + + /* Get pointers for fast access */ + node_data = ckt->evt->data.node; + rhsold = node_data->rhsold; + head = node_data->head; + + /* For number of event nodes, copy rhsold to node data */ + /* and set the op member if appropriate */ + num_nodes = ckt->evt->counts.num_nodes; + + for(i = 0; i < num_nodes; i++) + { + /* if head is null, just copy there */ + if(head[i] == NULL) + { + EVTnode_copy(ckt, i, &(rhsold[i]), &(head[i])); + + head[i]->op = op; + head[i]->step = step; + + } + /* Otherwise, add to the end of the list */ + else + { + /* Locate end of list */ + here = &(head[i]); + for(;;) + { + if((*here)->next) + here = &((*here)->next); + else + break; + } + /* Compare entry at end of list to rhsold */ + + EVTnode_compare(ckt, i, &(rhsold[i]), *here, &equal); + + /* If new value in rhsold is different, add it to the list */ + if(!equal) + { + here = &((*here)->next); + EVTnode_copy(ckt, i, &(rhsold[i]), here); + (*here)->op = op; + (*here)->step = step; + } + } /* end else add to end of list */ + } /* end for number of nodes */ + +} + + + +/* ************************************************************ */ + + +/* +EVTnode_compare + +This function compares the resolved values of the old and +new states on a node. The actual comparison is done by +calling the appropriate user-defined node compare function. +*/ + + +static void EVTnode_compare( + CKTcircuit *ckt, /* The circuit structure */ + int node_index, /* The index for the node in question */ + Evt_Node_t *node1, /* The first value */ + Evt_Node_t *node2, /* The second value */ + Mif_Boolean_t *equal) /* The computed result */ +{ + + Evt_Node_Data_t *node_data; + Evt_Node_Info_t **node_table; + + int udn_index; + + + /* Get data for fast access */ + node_data = ckt->evt->data.node; + node_table = ckt->evt->info.node_table; + udn_index = node_table[node_index]->udn_index; + + + /* Do compare based on changes in resolved node value only */ + (*(g_evt_udn_info[udn_index]->compare)) ( + node1->node_value, + node2->node_value, + equal); +} diff --git a/src/xspice/evt/evtplot.c b/src/xspice/evt/evtplot.c new file mode 100755 index 000000000..60d47fefc --- /dev/null +++ b/src/xspice/evt/evtplot.c @@ -0,0 +1,218 @@ +/*============================================================================ +FILE EVTplot.c + +MEMBER OF process XSPICE + +Copyright 1992 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 5/7/92 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTplot which is used to provide basic + plotting of event driven nodes through SPICE3's 'plot' command. + +INTERFACES + + void EVTplot() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include + +#include "ngspice.h" +//nclude "misc.h" +#include "evt.h" +#include "evtudn.h" +#include "evtproto.h" +#include "mif.h" +#include "mifproto.h" + +/*saj for output */ +#include "sim.h" +#include "dvec.h" +//#include "ftedata.h" +//#include "fteconstant.h" +//#include "util.h" +#include "cpstd.h" + + +/* + +EVTfindvec() + +This function is called from FTE/vectors.c:findvec() when a node specified +for plotting cannot be located in the analog plot data. It scans the +event driven data structures looking for the node, and if found, returns +a new 'dvec' structure holding the data to be plotted. The dvec struct +is created with it's own v_scale member holding the event time vector +for this node since the time vector is not aligned with the analog data +points or with other event vectors. + +The node name supplied as argument can either be a simple node name, or a +name of the form (), where is the member of +the event-driven structure to be plotted. These member names are defined +by the individual "user-defined node" plot_val routines for the node +type in question. If the simple node name form is used, the special +keyword "all" is supplied to the plot_val routine for the member name. + +*/ + + +struct dvec *EVTfindvec( + char *node) /* The node name (and optional member name) */ +{ + char *name; + char *member = "all"; + char *ptr; + + int i; + int len; + int num_nodes; + int udn_index; + int num_events; + + Mif_Boolean_t found; + Evt_Node_Info_t **node_table; + Evt_Node_t *head; + Evt_Node_t *event; + + double *anal_point_vec; + double *value_vec; + double value; + + struct dvec *d; + struct dvec *scale; + + /* Exit immediately if event-driven stuff not allocated yet, */ + /* or if number of event nodes is zero. */ + if(! g_mif_info.ckt) + return(NULL); + if(! g_mif_info.ckt->evt) + return(NULL); + if(g_mif_info.ckt->evt->counts.num_nodes == 0) + return(NULL); + + /* Make a copy of the node name. */ + /* Do not free this string. It is assigned into the dvec structure below. */ + name = MIFcopy(node); + + /* Convert to all lower case */ + len = strlen(name); + for(i = 0; i < len; i++) + if(isupper(name[i])) + name[i] = tolower(name[i]); + + /* Divide into the node name and member name */ + for(ptr = name; *ptr != '\0'; ptr++) + if(*ptr == '(') + break; + + if(*ptr == '(') { + *ptr = '\0'; + ptr++; + member = ptr; + for( ; *ptr != '\0'; ptr++) + if(*ptr == ')') + break; + *ptr = '\0'; + } + + /* Look for node name in the event-driven node list */ + num_nodes = g_mif_info.ckt->evt->counts.num_nodes; + node_table = g_mif_info.ckt->evt->info.node_table; + + for(i = 0, found = MIF_FALSE; i < num_nodes; i++) { + if(cieq(name, node_table[i]->name)) { + found = MIF_TRUE; + break; + } + } + + if(! found) + return(NULL); + + /* Get the UDN type index */ + udn_index = node_table[i]->udn_index; + + /* Count the number of events */ + head = g_mif_info.ckt->evt->data.node->head[i]; + + for(event = head, num_events = 0; event; event = event->next) + num_events++; + + /* Allocate arrays to hold the analysis point and node value vectors */ + anal_point_vec = (double *) MALLOC(2 * (num_events + 2) * sizeof(double)); + value_vec = (double *) MALLOC(2 * (num_events + 2) * sizeof(double)); + + /* Iterate through the events and fill the arrays. */ + /* Note that we create vertical segments every time an event occurs. */ + /* Need to modify this in the future to complete the vector out to the */ + /* last analysis point... */ + for(i = 0, event = head; event; event = event->next) { + + /* If not first point, put the second value of the horizontal line in the vectors */ + if(i > 0) { + anal_point_vec[i] = event->step; + value_vec[i] = value; + i++; + } + + /* Get the next value by calling the appropriate UDN plot_val function */ + value = 0.0; + (*(g_evt_udn_info[udn_index]->plot_val)) (event->node_value, + member, + &value); + + /* Put the first value of the horizontal line in the vector */ + anal_point_vec[i] = event->step; + value_vec[i] = value; + i++; + + } + + /* Allocate dvec structures and assign the vectors into them. */ + /* See FTE/OUTinterface.c:plotInit() for initialization example. */ + + scale = (void *) MALLOC(sizeof(struct dvec)); + + scale->v_name = MIFcopy("step"); + scale->v_type = SV_TIME; + scale->v_flags = VF_REAL & ~VF_PERMANENT; + scale->v_length = i; + scale->v_realdata = anal_point_vec; + scale->v_scale = NULL; + + d = (void *) MALLOC(sizeof(struct dvec)); + + d->v_name = name; + d->v_type = SV_VOLTAGE; + d->v_flags = VF_REAL & ~VF_PERMANENT; + d->v_length = i; + d->v_realdata = value_vec; + d->v_scale = scale; + + + /* Return the dvec */ + return(d); +} diff --git a/src/xspice/evt/evtprint.c b/src/xspice/evt/evtprint.c new file mode 100755 index 000000000..2c82c2ddb --- /dev/null +++ b/src/xspice/evt/evtprint.c @@ -0,0 +1,369 @@ +/*============================================================================ +FILE EVTprint.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTprint which is used to provide a simple + tabular output of event-driven node data. This printout is invoked + through a new nutmeg command called 'eprint' which takes event-driven + node names as argument. + +INTERFACES + + void EVTprint(wordlist *wl) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include + +#include "ngspice.h" +//#include "misc.h" + +#include "cpstd.h" + +#include "mif.h" +#include "evt.h" +#include "evtudn.h" + +#include "evtproto.h" + + + +static int get_index(char *node_name); + +static void print_data( + Mif_Boolean_t dcop, + double step, + char **node_value, + int nargs); + +/* +EVTprint + +This function implements the 'eprint' command used to print +event-driven node values and messages from the latest simulation. + +This is a simple prototype implementation of the +eprint command for testing purposes. It is currently lacking +in the following areas: + +1) It accepts only up to 16 nodes. + +2) It does not support the selected printing of different + members of a user-defined data struct. + +3) It is dumb in its output formatting - just tries to print + everything on a line with 4 space separators. + +4) It works only for the latest simulation - i.e. it does not + use the evt jobs structure to find old results. + +5) It does not allow a range of timesteps to be selected. + +*/ + + + +#define EPRINT_MAXARGS 16 + + +void EVTprint( + wordlist *wl) /* The command line entered by user */ +{ + + int i; + int nargs; + int num_ports; + + wordlist *w; + + char *node_name[EPRINT_MAXARGS]; + int node_index[EPRINT_MAXARGS]; + int udn_index[EPRINT_MAXARGS]; + Evt_Node_t *node_data[EPRINT_MAXARGS]; + char *node_value[EPRINT_MAXARGS]; + + CKTcircuit *ckt; + + Evt_Node_Info_t **node_table; + Evt_Port_Info_t **port_table; + + Mif_Boolean_t more; + Mif_Boolean_t dcop; + + double step; + double next_step; + double this_step; + + char *value; + + Evt_Msg_t *msg_data; + Evt_Statistic_t *statistics; + + + /* Count the number of arguments to the command */ + nargs = 0; + w = wl; + while(w) { + nargs++; + w = w->wl_next; + } + + if(nargs < 1) { + printf("Usage: eprint ...\n"); + return; + } + if(nargs > EPRINT_MAXARGS) { + printf("ERROR - eprint currently limited to 16 arguments\n"); + return; + } + + /* Get needed pointers */ + ckt = g_mif_info.ckt; + node_table = ckt->evt->info.node_table; + + /* Get data for each argument */ + w = wl; + for(i = 0; i < nargs; i++) { + node_name[i] = w->wl_word; + node_index[i] = get_index(node_name[i]); + if(node_index[i] < 0) { + printf("ERROR - Node %s is not an event node.\n", node_name[i]); + return; + } + udn_index[i] = node_table[node_index[i]]->udn_index; + node_data[i] = ckt->evt->data.node->head[node_index[i]]; + node_value[i] = ""; + w = w->wl_next; + } + + + /* Print results data */ + printf("\n**** Results Data ****\n\n"); + + /* Print the column identifiers */ + printf("Time or Step\n"); + for(i = 0; i < nargs; i++) + printf("%s\n",node_name[i]); + printf("\n\n"); + + /* Scan the node data and determine if the first vector */ + /* is for a DCOP analysis or the first step in a swept DC */ + /* or transient analysis. Also, determine if there is */ + /* more data following it and if so, what the next step */ + /* is. */ + more = MIF_FALSE; + dcop = MIF_FALSE; + next_step = 1e30; + for(i = 0; i < nargs; i++) { + if(node_data[i]->op) + dcop = MIF_TRUE; + else + step = node_data[i]->step; + (*(g_evt_udn_info[udn_index[i]]->print_val)) + (node_data[i]->node_value, "all", &value); + node_value[i] = value; + node_data[i] = node_data[i]->next; + if(node_data[i]) { + more = MIF_TRUE; + if(node_data[i]->step < next_step) + next_step = node_data[i]->step; + } + } + + /* Print the data */ + print_data(dcop, step, node_value, nargs); + + /* While there is more data, get the next values and print */ + while(more) { + + more = MIF_FALSE; + this_step = next_step; + next_step = 1e30; + + for(i = 0; i < nargs; i++) { + + if(node_data[i]) { + if(node_data[i]->step == this_step) { + (*(g_evt_udn_info[udn_index[i]]->print_val)) + (node_data[i]->node_value, "all", &value); + node_value[i] = value; + node_data[i] = node_data[i]->next; + } + if(node_data[i]) { + more = MIF_TRUE; + if(node_data[i]->step < next_step) + next_step = node_data[i]->step; + } + + } /* end if node_data not NULL */ + + } /* end for number of args */ + + print_data(MIF_FALSE, this_step, node_value, nargs); + + } /* end while there is more data */ + printf("\n\n"); + + + /* Print messages for all ports */ + printf("\n**** Messages ****\n\n"); + + num_ports = ckt->evt->counts.num_ports; + port_table = ckt->evt->info.port_table; + + for(i = 0; i < num_ports; i++) { + + /* Get pointer to messages for this port */ + msg_data = ckt->evt->data.msg->head[i]; + + /* If no messages on this port, skip */ + if(! msg_data) + continue; + + /* Print the port description */ + printf("Node: %s Inst: %s Conn: %s Port: %d\n\n", + port_table[i]->node_name, + port_table[i]->inst_name, + port_table[i]->conn_name, + port_table[i]->port_num); + + /* Print the messages on this port */ + while(msg_data) { + if(msg_data->op) + printf("DCOP "); + else + printf("%-16.9e", msg_data->step); + printf("%s\n", msg_data->text); + msg_data = msg_data->next; + } + printf("\n\n"); + + } /* end for number of ports */ + + + /* Print statistics */ + printf("\n**** Statistics ****\n\n"); + + statistics = ckt->evt->data.statistics; + printf("Operating point analog/event alternations: %d\n", + statistics->op_alternations); + printf("Operating point load calls: %d\n", + statistics->op_load_calls); + printf("Operating point event passes: %d\n", + statistics->op_event_passes); + printf("Transient analysis load calls: %d\n", + statistics->tran_load_calls); + printf("Transient analysis timestep backups: %d\n", + statistics->tran_time_backups); + + printf("\n\n"); +} + + + + +/* +get_index + +This function determines the index of a specified event-driven node. +*/ + + +static int get_index( + char *node_name /* The name of the node to search for */ +) +{ + + /* Get the event-driven node index for the specified name */ + + int index; + + Mif_Boolean_t found; + Evt_Node_Info_t *node; + CKTcircuit *ckt; + + + /* Scan list of nodes in event structure to see if there */ + + found = MIF_FALSE; + index = 0; + + ckt = g_mif_info.ckt; + node = ckt->evt->info.node_list; + + while(node) { + if(strcmp(node_name, node->name) == 0) { + found = MIF_TRUE; + break; + } + else { + index++; + node = node->next; + } + } + + /* Return the index or -1 if not found */ + if(! found) + return(-1); + else + return(index); +} + + + + +/* +print_data + +This function prints the values of one or more nodes to +standard output. +*/ + + +static void print_data( + Mif_Boolean_t dcop, /* Is this the operating point data */ + double step, /* The analysis step if dcop */ + char **node_value, /* The array of values to be printed */ + int nargs) /* The size of the value array */ +{ + + int i; + char step_str[100]; + + if(dcop) + strcpy(step_str, "DCOP "); + else + sprintf(step_str, "%-16.9e", step); + + printf("%s", step_str); + for(i = 0; i < nargs; i++) + printf(" %s", node_value[i]); + printf("\n"); +} diff --git a/src/xspice/evt/evtqueue.c b/src/xspice/evt/evtqueue.c new file mode 100755 index 000000000..1de0b9937 --- /dev/null +++ b/src/xspice/evt/evtqueue.c @@ -0,0 +1,252 @@ +/*============================================================================ +FILE EVTqueue.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains functions that place new events into the output and + instance queues. + +INTERFACES + + void EVTqueue_output( + CKTcircuit *ckt, + int output_index, + int udn_index, + Evt_Output_Event_t *new_event, + double posted_time, + double event_time) + + void EVTqueue_inst( + CKTcircuit *ckt, + int inst_index, + double posted_time, + double event_time) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +#include + +#include "ngspice.h" +#include "cktdefs.h" +//#include "util.h" + +#include "mif.h" +#include "evt.h" +#include "evtudn.h" + +#include "evtproto.h" + + + +/* +EVTqueue_output + +This function places the specified output event onto the output +queue. It is called by EVTload during a transient analysis. + +The linked list in the queue for the specified output is +searched beginning at the current head of the pending events +to find the location at which to insert the new event. The +events are ordered in the list by event_time. If the event +is placed before the end of the list, subsequent events are +removed from the list by marking them as 'removed' and +recording the time of removal. This allows efficient backup +of the state of the queue if a subsequent analog timestep +fails. +*/ + + +void EVTqueue_output( + CKTcircuit *ckt, /* The circuit structure */ + int output_index, /* The output in question */ + int udn_index, /* The associated user-defined node type */ + Evt_Output_Event_t *new_event, /* The event to queue */ + double posted_time, /* The current time */ + double event_time) /* The time the event should happen */ +{ + + Evt_Output_Queue_t *output_queue; + Evt_Output_Event_t **here; + Evt_Output_Event_t *next; + + Mif_Boolean_t splice; + + + /* Get pointers for fast access */ + output_queue = &(ckt->evt->queue.output); + + /* Put the times into the event struct */ + new_event->event_time = event_time; + new_event->posted_time = posted_time; + new_event->removed = MIF_FALSE; + + /* Update next_time in output queue */ + if((output_queue->num_pending <= 0) || + (event_time < output_queue->next_time)) + output_queue->next_time = event_time; + + /* Find location at which to insert event */ + splice = MIF_FALSE; + here = output_queue->current[output_index]; + while(*here) { + if(event_time <= (*here)->event_time) { + splice = MIF_TRUE; + break; + } + here = &((*here)->next); + } + + /* If needs to be spliced into middle of existing list */ + if(splice) { + /* splice it in */ + next = *here; + *here = new_event; + new_event->next = next; + /* mark later events as removed */ + while(next) { + if(! next->removed) { + next->removed = MIF_TRUE; + next->removed_time = posted_time; + } + next = next->next; + } + } + /* else, just put it at the end */ + else { + *here = new_event; + new_event->next = NULL; + } + + /* Add to the list of outputs modified since last accepted timestep */ + if(! output_queue->modified[output_index]) { + output_queue->modified[output_index] = MIF_TRUE; + output_queue->modified_index[(output_queue->num_modified)++] = + output_index; + } + + /* Add to the list of outputs with events pending */ + if(! output_queue->pending[output_index]) { + output_queue->pending[output_index] = MIF_TRUE; + output_queue->pending_index[(output_queue->num_pending)++] = + output_index; + } +} + + + +/* +EVTqueue_inst + +This function places the specified inst event onto the inst +queue. + +The linked list in the queue for the specified inst is +searched beginning at the current head of the pending events +to find the location at which to insert the new event. The +events are ordered in the list by event_time. +*/ + + + + +void EVTqueue_inst( + CKTcircuit *ckt, /* The circuit structure */ + int inst_index, /* The instance in question */ + double posted_time, /* The current time */ + double event_time) /* The time the event should happen */ +{ + + Evt_Inst_Queue_t *inst_queue; + Evt_Inst_Event_t **here; + Evt_Inst_Event_t *new_event; + Evt_Inst_Event_t *next; + + Mif_Boolean_t splice; + + + /* Get pointers for fast access */ + inst_queue = &(ckt->evt->queue.inst); + + /* Update next_time in inst queue */ + if((inst_queue->num_pending <= 0) || + (event_time < inst_queue->next_time)) + inst_queue->next_time = event_time; + + /* Create a new event or get one from the free list and copy in data */ + if(inst_queue->free[inst_index]) { + new_event = inst_queue->free[inst_index]; + inst_queue->free[inst_index] = new_event->next; + } + else { + new_event = (void *) MALLOC(sizeof(Evt_Inst_Event_t)); + } + new_event->event_time = event_time; + new_event->posted_time = posted_time; + + /* Find location at which to insert event */ + splice = MIF_FALSE; + here = inst_queue->current[inst_index]; + while(*here) { + /* If there's an event with the same time, don't duplicate it */ + if(event_time == (*here)->event_time) + return; + else if(event_time < (*here)->event_time) { + splice = MIF_TRUE; + break; + } + here = &((*here)->next); + } + + /* If needs to be spliced into middle of existing list */ + if(splice) { + /* splice it in */ + next = *here; + *here = new_event; + new_event->next = next; + } + /* else, just put it at the end */ + else { + *here = new_event; + new_event->next = NULL; + } + + /* Add to the list of insts modified since last accepted timestep */ + if(! inst_queue->modified[inst_index]) { + inst_queue->modified[inst_index] = MIF_TRUE; + inst_queue->modified_index[(inst_queue->num_modified)++] = + inst_index; + } + + /* Add to the list of insts with events pending */ + if(! inst_queue->pending[inst_index]) { + inst_queue->pending[inst_index] = MIF_TRUE; + inst_queue->pending_index[(inst_queue->num_pending)++] = + inst_index; + } +} diff --git a/src/xspice/evt/evtsetup.c b/src/xspice/evt/evtsetup.c new file mode 100755 index 000000000..59aef7343 --- /dev/null +++ b/src/xspice/evt/evtsetup.c @@ -0,0 +1,578 @@ +/*============================================================================ +FILE EVTsetup.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTsetup which clears/allocates the + event-driven queues and data structures immediately prior to a new + analysis. In addition, it places entries in the job list so that + results data from multiple analysis can be retrieved similar to + SPICE3C1 saving multiple 'plots'. + +INTERFACES + + int EVTsetup(CKTcircuit *ckt) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include + +#include "ngspice.h" +//#include "misc.h" + +#include "cktdefs.h" +#include "sperror.h" +//#include "util.h" + +#include "mif.h" +#include "evt.h" +#include "evtudn.h" +#include "mifproto.h" +#include "evtproto.h" + + + + +static int EVTsetup_queues(CKTcircuit *ckt); +static int EVTsetup_data(CKTcircuit *ckt); +static int EVTsetup_jobs(CKTcircuit *ckt); +static int EVTsetup_load_ptrs(CKTcircuit *ckt); + + + + +/* Allocation macros with built-in check for out-of-memory */ +/* Adapted from SPICE 3C1 code in CKTsetup.c */ + +#define CKALLOC(var,size,type) \ + if(size) { \ + if(!(var = (void *) MALLOC((size) * sizeof(type)))) \ + return(E_NOMEM); \ + } + +#define CKREALLOC(var,size,type) \ + if((size) == 1) { \ + if(!(var = (void *) MALLOC((size) * sizeof(type)))) \ + return(E_NOMEM); \ + } else if((size) > 1) { \ + if(!(var = (void *) REALLOC((void *) (var), (size) * sizeof(type)))) \ + return(E_NOMEM); \ + } + + +/* +EVTsetup + +This function clears/allocates the event-driven queues and data structures +immediately prior to a new analysis. In addition, it places entries +in the job list so that results data from multiple analysis can be +retrieved similar to SPICE3C1 saving multiple 'plots'. +*/ + + +int EVTsetup( + CKTcircuit *ckt) /* The circuit structure */ +{ + + int err; + + + /* Exit immediately if no event-driven instances in circuit */ + if(ckt->evt->counts.num_insts == 0) + return(OK); + + /* Clear the inst, node, and output queues, and initialize the to_call */ + /* elements in the instance queue to call all event-driven instances */ + err = EVTsetup_queues(ckt); + if(err) + return(err); + + /* Allocate and initialize the node, state, message, and statistics data */ + err = EVTsetup_data(ckt); + if(err) + return(err); + + /* Set the job pointers to the allocated results, states, messages, */ + /* and statistics so that data will be accessable after run */ + err = EVTsetup_jobs(ckt); + if(err) + return(err); + + /* Setup the pointers in the MIFinstance structure for inputs, outputs, */ + /* and total loads */ + err = EVTsetup_load_ptrs(ckt); + if(err) + return(err); + + /* Initialize additional event data */ + g_mif_info.circuit.evt_step = 0.0; + + /* Return OK */ + return(OK); +} + + + + +/* +EVTsetup_queues + +This function clears the event-driven queues in preparation for +a new simulation. +*/ + + +static int EVTsetup_queues( + CKTcircuit *ckt) /* The circuit structure */ +{ + + int i; + int num_insts; + int num_nodes; + int num_outputs; + + Evt_Inst_Queue_t *inst_queue; + Evt_Node_Queue_t *node_queue; + Evt_Output_Queue_t *output_queue; + + Evt_Inst_Event_t *inst_event; + Evt_Output_Event_t *output_event; + + void *ptr; + + /* ************************ */ + /* Clear the instance queue */ + /* ************************ */ + + num_insts = ckt->evt->counts.num_insts; + inst_queue = &(ckt->evt->queue.inst); + + for(i = 0; i < num_insts; i++) { + inst_event = inst_queue->head[i]; + while(inst_event) { + ptr = inst_event; + inst_event = inst_event->next; + FREE(ptr); + } + inst_event = inst_queue->free[i]; + while(inst_event) { + ptr = inst_event; + inst_event = inst_event->next; + FREE(ptr); + } + inst_queue->head[i] = NULL; + inst_queue->current[i] = &(inst_queue->head[i]); + inst_queue->last_step[i] = &(inst_queue->head[i]); + inst_queue->free[i] = NULL; + } + + inst_queue->next_time = 0.0; + inst_queue->last_time = 0.0; + + inst_queue->num_modified = 0; + inst_queue->num_pending = 0; + inst_queue->num_to_call = 0; + + for(i = 0; i < num_insts; i++) { + inst_queue->modified[i] = MIF_FALSE; + inst_queue->pending[i] = MIF_FALSE; + inst_queue->to_call[i] = MIF_FALSE; + } + + + /* ******************** */ + /* Clear the node queue */ + /* ******************** */ + + num_nodes = ckt->evt->counts.num_nodes; + node_queue = &(ckt->evt->queue.node); + + node_queue->num_changed = 0; + node_queue->num_to_eval = 0; + + for(i = 0; i < num_nodes; i++) { + node_queue->changed[i] = MIF_FALSE; + node_queue->to_eval[i] = MIF_FALSE; + } + + /* ********************** */ + /* Clear the output queue */ + /* ********************** */ + + num_outputs = ckt->evt->counts.num_outputs; + output_queue = &(ckt->evt->queue.output); + + for(i = 0; i < num_outputs; i++) { + output_event = output_queue->head[i]; + while(output_event) { + ptr = output_event; + output_event = output_event->next; + FREE(ptr); + } + output_event = output_queue->free[i]; + while(output_event) { + ptr = output_event; + output_event = output_event->next; + FREE(ptr); + } + output_queue->head[i] = NULL; + output_queue->current[i] = &(output_queue->head[i]); + output_queue->last_step[i] = &(output_queue->head[i]); + output_queue->free[i] = NULL; + } + + output_queue->next_time = 0.0; + output_queue->last_time = 0.0; + + output_queue->num_modified = 0; + output_queue->num_pending = 0; + output_queue->num_changed = 0; + + for(i = 0; i < num_outputs; i++) { + output_queue->modified[i] = MIF_FALSE; + output_queue->pending[i] = MIF_FALSE; + output_queue->changed[i] = MIF_FALSE; + } + + return(OK); +} + + + +/* +EVTsetup_data + +This function sets up the event-driven node, state, and +message data runtime structures in preparation for +a new simulation. +*/ + + +static int EVTsetup_data( + CKTcircuit *ckt) /* The circuit structure */ +{ + + Evt_Data_t *data; + + int i; + int j; + + int num_insts; + int num_ports; + int num_nodes; + + int udn_index; + int num_outputs; + + Mif_Boolean_t invert; + + Evt_Node_Data_t *node_data; + Evt_State_Data_t *state_data; + Evt_Msg_Data_t *msg_data; + /* Evt_Statistic_t *statistics_data;*/ + + Evt_Node_t *rhs; + Evt_Node_t *rhsold; + + Evt_Node_Info_t *node_info; + + + /* Allocate main substructures of data */ + /* Note that we don't free any old structures */ + /* since they are pointed to by jobs and need */ + /* to be maintained so that results from multiple */ + /* jobs are kept around like SPICE does */ + + data = &(ckt->evt->data); + CKALLOC(data->node, 1, Evt_Node_Data_t) + CKALLOC(data->state, 1, Evt_State_Data_t) + CKALLOC(data->msg, 1, Evt_Msg_Data_t) + CKALLOC(data->statistics, 1, Evt_Statistic_t) + + /* Allocate node data */ + + num_nodes = ckt->evt->counts.num_nodes; + node_data = data->node; + + CKALLOC(node_data->head, num_nodes, void *) + CKALLOC(node_data->tail, num_nodes, void *) + CKALLOC(node_data->last_step, num_nodes, void *) + CKALLOC(node_data->free, num_nodes, void *) + CKALLOC(node_data->modified_index, num_nodes, int) + CKALLOC(node_data->modified, num_nodes, Mif_Boolean_t) + CKALLOC(node_data->rhs, num_nodes, Evt_Node_t) + CKALLOC(node_data->rhsold, num_nodes, Evt_Node_t) + CKALLOC(node_data->total_load, num_nodes, double) + + /* Initialize the node data */ + + for(i = 0; i < num_nodes; i++) { + node_data->tail[i] = &(node_data->head[i]); + node_data->last_step[i] = &(node_data->head[i]); + } + + for(i = 0; i < num_nodes; i++) { + + /* Get pointers to rhs & rhsold, the user-defined node type index, */ + /* the number of outputs on the node and the invert flag */ + rhs = &(node_data->rhs[i]); + rhsold = &(node_data->rhsold[i]); + node_info = ckt->evt->info.node_table[i]; + udn_index = node_info->udn_index; + num_outputs = node_info->num_outputs; + invert = node_info->invert; + + /* Initialize the elements within rhs and rhsold */ + rhs->step = 0.0; + rhsold->step = 0.0; + if(num_outputs > 1) { + CKALLOC(rhs->output_value, num_outputs, void *) + CKALLOC(rhsold->output_value, num_outputs, void *) + for(j = 0; j < num_outputs; j++) { + (*(g_evt_udn_info[udn_index]->create)) (&(rhs->output_value[j])); + (*(g_evt_udn_info[udn_index]->initialize)) (rhs->output_value[j]); + (*(g_evt_udn_info[udn_index]->create)) (&(rhsold->output_value[j])); + (*(g_evt_udn_info[udn_index]->initialize)) (rhsold->output_value[j]); + } + } + (*(g_evt_udn_info[udn_index]->create)) (&(rhs->node_value)); + (*(g_evt_udn_info[udn_index]->initialize)) (rhs->node_value); + (*(g_evt_udn_info[udn_index]->create)) (&(rhsold->node_value)); + (*(g_evt_udn_info[udn_index]->initialize)) (rhsold->node_value); + if(invert) { + (*(g_evt_udn_info[udn_index]->create)) (&(rhs->inverted_value)); + (*(g_evt_udn_info[udn_index]->initialize)) (rhs->inverted_value); + (*(g_evt_udn_info[udn_index]->create)) (&(rhsold->inverted_value)); + (*(g_evt_udn_info[udn_index]->initialize)) (rhsold->inverted_value); + } + + /* Initialize the total load value to zero */ + node_data->total_load[i] = 0.0; + } + + + /* Allocate and initialize state data */ + + num_insts = ckt->evt->counts.num_insts; + state_data = data->state; + + CKALLOC(state_data->head, num_insts, void *) + CKALLOC(state_data->tail, num_insts, void *) + CKALLOC(state_data->last_step, num_insts, void *) + CKALLOC(state_data->free, num_insts, void *) + CKALLOC(state_data->modified_index, num_insts, int) + CKALLOC(state_data->modified, num_insts, Mif_Boolean_t) + CKALLOC(state_data->total_size, num_insts, int) + CKALLOC(state_data->desc, num_insts, void *) + + for(i = 0; i < num_insts; i++) { + state_data->tail[i] = &(state_data->head[i]); + state_data->last_step[i] = &(state_data->head[i]); + } + + + /* Allocate and initialize msg data */ + + num_ports = ckt->evt->counts.num_ports; + msg_data = data->msg; + + CKALLOC(msg_data->head, num_ports, void *) + CKALLOC(msg_data->tail, num_ports, void *) + CKALLOC(msg_data->last_step, num_ports, void *) + CKALLOC(msg_data->free, num_ports, void *) + CKALLOC(msg_data->modified_index, num_ports, int) + CKALLOC(msg_data->modified, num_ports, Mif_Boolean_t) + + for(i = 0; i < num_ports; i++) { + msg_data->tail[i] = &(msg_data->head[i]); + msg_data->last_step[i] = &(msg_data->head[i]); + } + + /* Don't need to initialize statistics since they were */ + /* calloc'ed above */ + + return(OK); +} + + + + +/* +EVTsetup_jobs + +This function prepares the jobs data for a new simulation. +*/ + + +static int EVTsetup_jobs( + CKTcircuit *ckt) /* The circuit structure */ +{ + + int i; + int num_jobs; + + Evt_Job_t *jobs; + Evt_Data_t *data; + + + jobs = &(ckt->evt->jobs); + data = &(ckt->evt->data); + + /* Increment the number of jobs */ + num_jobs = ++(jobs->num_jobs); + + /* Allocate/reallocate necessary pointers */ + CKREALLOC(jobs->job_name, num_jobs, void *) + CKREALLOC(jobs->node_data, num_jobs, void *) + CKREALLOC(jobs->state_data, num_jobs, void *) + CKREALLOC(jobs->msg_data, num_jobs, void *) + CKREALLOC(jobs->statistics, num_jobs, void *) + + /* Fill in the pointers, etc. for this new job */ + i = num_jobs - 1; + jobs->job_name[i] = MIFcopy((char *) ckt->CKTcurJob->JOBname); + jobs->node_data[i] = data->node; + jobs->state_data[i] = data->state; + jobs->msg_data[i] = data->msg; + jobs->statistics[i] = data->statistics; + + return(OK); +} + + + +/* +EVTsetup_load_ptrs + +This function setups up the required data in the MIFinstance +structure of event-driven and hybrid instances. +*/ + + +static int EVTsetup_load_ptrs( + CKTcircuit *ckt) /* The circuit structure */ +{ + + int i; + int j; + int k; + + int num_insts; + int num_conn; + int num_port; + int num_outputs; + + int node_index; + int output_subindex; + + MIFinstance *fast; + + Mif_Conn_Data_t *conn; + + Mif_Port_Type_t type; + Mif_Port_Data_t *port; + + Evt_Node_Data_t *node_data; + + + /* This function setups up the required data in the MIFinstance */ + /* structure of event-driven and hybrid instances */ + + /* Loop through all event-driven and hybrid instances */ + num_insts = ckt->evt->counts.num_insts; + for(i = 0; i < num_insts; i++) { + + /* Get the MIFinstance pointer */ + fast = ckt->evt->info.inst_table[i]->inst_ptr; + + /* Loop through all connections */ + num_conn = fast->num_conn; + for(j = 0; j < num_conn; j++) { + + /* Skip if connection is null */ + if(fast->conn[j]->is_null) + continue; + + conn = fast->conn[j]; + + /* Loop through all ports */ + num_port = conn->size; + for(k = 0; k < num_port; k++) { + + /* Get port data pointer for quick access */ + port = conn->port[k]; + + if(port->is_null) + continue; + + /* Skip if port is not digital or user-defined type */ + type = port->type; + if((type != MIF_DIGITAL) && (type != MIF_USER_DEFINED)) + continue; + + /* Set input.pvalue to point to rhsold.node_value or to */ + /* rhsold.inverted_value as appropriate */ + node_index = port->evt_data.node_index; + node_data = ckt->evt->data.node; + if(conn->is_input) { + if(port->invert) { + port->input.pvalue = node_data->rhsold[node_index]. + inverted_value; + } + else { + port->input.pvalue = node_data->rhsold[node_index]. + node_value; + } + } + + /* Set output.pvalue to point to rhs.node_value or rhs.output_value[i] */ + /* where i is given by the output_subindex in output info */ + /* depending on whether more than one output is connected to the node. */ + /* Note that this is only for the DCOP analysis. During a transient */ + /* analysis, new structures will be created and the pointers will */ + /* be set by EVTload */ + if(conn->is_output) { + num_outputs = ckt->evt->info.node_table[node_index]->num_outputs; + if(num_outputs <= 1) { + port->output.pvalue = node_data->rhs[node_index]. + node_value; + } + else { + output_subindex = port->evt_data.output_subindex; + port->output.pvalue = node_data->rhs[node_index]. + output_value[output_subindex]; + } + } + + } /* end for number of ports */ + } /* end for number of connections */ + } /* end for number of insts */ + + return(OK); +} diff --git a/src/xspice/evt/evttermi.c b/src/xspice/evt/evttermi.c new file mode 100755 index 000000000..7922ad817 --- /dev/null +++ b/src/xspice/evt/evttermi.c @@ -0,0 +1,512 @@ +/*============================================================================ +FILE EVTtermInsert.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains function EVTtermInsert which is called by + MIF_INP2A during the parsing of the input deck. EVTtermInsert is + similar to SPICE3's INPtermInsert except that it is used when the node + type is event-driven. Calls to this function build the info lists + for instances, nodes, outputs, and ports. The completion of the info + struct is carried out by EVTinit following the parsing of all + instances in the deck. + +INTERFACES + + void EVTtermInsert( + CKTcircuit *ckt, + MIFinstance *fast, + char *node_name, + char *type_name, + int conn_num, + int port_num, + char **err_msg) + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include + +#include "ngspice.h" +//#include "misc.h" + +#include "cktdefs.h" +//#include "util.h" + +#include "mif.h" +#include "evt.h" +#include "evtudn.h" + +#include "mifproto.h" +#include "evtproto.h" + + +static void EVTinst_insert( + CKTcircuit *ckt, + MIFinstance *fast, + int *inst_index, + char **err_msg); + +static void EVTnode_insert( + CKTcircuit *ckt, + MIFinstance *fast, + int inst_index, + char *node_name, + char *type_name, + int conn_num, + int port_num, + int *node_index, + int *output_subindex, + char **err_msg); + +static void EVTport_insert( + CKTcircuit *ckt, + MIFinstance *fast, + int inst_index, + int node_index, + char *node_name, + int conn_num, + int port_num, + int *port_index, + char **err_msg); + +static void EVToutput_insert( + CKTcircuit *ckt, + MIFinstance *fast, + int inst_index, + int node_index, + int port_index, + int output_subindex, + int conn_num, + int port_num, + char **err_msg); + + +/* +EVTtermInsert + +This function is called by MIF_INP2A during the parsing of the input +deck. EVTtermInsert is similar to 3C1's INPtermInsert except that it is +used when the node type is event-driven. Calls to this function build +the info lists for instances, nodes, outputs, and ports. The +completion of the info struct is carried out by EVTinit following +the parsing of all instances in the deck. +*/ + + +void EVTtermInsert( + CKTcircuit *ckt, /* The circuit structure */ + MIFinstance *fast, /* The instance being parsed */ + char *node_name, /* The node name */ + char *type_name, /* The type of node */ + int conn_num, /* The port connection number */ + int port_num, /* The sub-port number - 0 for scalar ports */ + char **err_msg) /* Returned error message if any */ +{ + + int inst_index; + int node_index; + int port_index; + + int output_subindex; + + + /* Get the instance index and create new entry in inst */ + /* info list if this is a new instance. */ + EVTinst_insert(ckt, fast, &inst_index, err_msg); + if(*err_msg) + return; + + /* Get the node index and create new entry in node info */ + /* list if this is a new node */ + EVTnode_insert(ckt, fast, inst_index, node_name, type_name, + conn_num, port_num, &node_index, &output_subindex, + err_msg); + if(*err_msg) + return; + + /* Create new entry in port info list and return port index */ + EVTport_insert(ckt, fast, inst_index, node_index, node_name, conn_num, + port_num, &port_index, err_msg); + if(*err_msg) + return; + + /* Create new entry in output info list if appropriate */ + if(fast->conn[conn_num]->is_output) { + EVToutput_insert(ckt, fast, inst_index, node_index, port_index, + output_subindex, conn_num, port_num, err_msg); + if(*err_msg) + return; + } +} + + + + +/* +EVTinst_insert + +This function locates or creates a new entry for the specified +instance in the event-driven ``info'' structures during parsing. +*/ + + +static void EVTinst_insert( + CKTcircuit *ckt, /* The circuit structure */ + MIFinstance *fast, /* The instance being parsed */ + int *inst_index, /* The index found or added */ + char **err_msg) /* Error message if any */ +{ + + Mif_Boolean_t found; + int index; + + Evt_Inst_Info_t *inst; + Evt_Inst_Info_t **inst_ptr; + + + /* Scan list of instances in event structure to see if already there */ + /* and get the index */ + found = MIF_FALSE; + index = 0; + inst = ckt->evt->info.inst_list; + inst_ptr = &(ckt->evt->info.inst_list); + + while(inst) { + if(inst->inst_ptr == fast) { + found = MIF_TRUE; + break; + } + else { + index++; + inst_ptr = &(inst->next); + inst = inst->next; + } + } + + + /* If not found, create a new entry in list and increment the */ + /* instance count in the event structure */ + if(! found) { + *inst_ptr = (void *) MALLOC(sizeof(Evt_Inst_Info_t)); + inst = *inst_ptr; + inst->next = NULL; + inst->inst_ptr = fast; + index = ckt->evt->counts.num_insts; + (ckt->evt->counts.num_insts)++; + } + + /* Record the inst index in the MIFinstance structure and return it */ + fast->inst_index = index; + *inst_index = index; +} + + + +/* +EVTnode_insert + +This function locates or creates a new entry for the specified +node in the event-driven ``info'' structures during parsing. +*/ + + +static void EVTnode_insert( + CKTcircuit *ckt, /* The circuit structure */ + MIFinstance *fast, /* The instance being parsed */ + int inst_index, /* The index of inst in evt structures */ + char *node_name, /* The node name */ + char *type_name, /* The node type specified */ + int conn_num, /* The port connection number */ + int port_num, /* The sub-port number - 0 if scalar port */ + int *node_index, /* The node index found or added */ + int *output_subindex, /* The output number on this node */ + char **err_msg) /* Error message text if any */ +{ + + int i; + int udn_index=0; + Mif_Boolean_t found; + + int index; + + Evt_Node_Info_t *node; + Evt_Node_Info_t **node_ptr; + + Evt_Inst_Index_t *inst; + Evt_Inst_Index_t **inst_ptr; + + + /* *************************************** */ + /* Get and check the node type information */ + /* *************************************** */ + + /* Scan the list of user-defined node types and get the index */ + found = MIF_FALSE; + for(i = 0; i < g_evt_num_udn_types; i++) { + if(strcmp(type_name, g_evt_udn_info[i]->name) == 0) { + udn_index = i; + found = MIF_TRUE; + break; + } + } + + /* Report error if not recognized */ + if(! found) { + *err_msg = "Unrecognized connection type"; + return; + } + + /* If inverted, check to be sure invert function exists for type */ + if(fast->conn[conn_num]->port[port_num]->invert) { + if(g_evt_udn_info[udn_index]->invert == NULL) { + *err_msg = "Connection type cannot be inverted"; + return; + } + } + + + /* ******************************************* */ + /* Find/create entry in event-driven node list */ + /* ******************************************* */ + + /* Scan list of nodes in event structure to see if already there */ + /* and get the index */ + found = MIF_FALSE; + index = 0; + node = ckt->evt->info.node_list; + node_ptr = &(ckt->evt->info.node_list); + + while(node) { + if(strcmp(node_name, node->name) == 0) { + found = MIF_TRUE; + break; + } + else { + index++; + node_ptr = &(node->next); + node = node->next; + } + } + + + /* If found, verify that connection type is same as type of node */ + if(found) { + if(udn_index != node->udn_index) { + *err_msg = "Node cannot have two different types"; + return; + } + } + + /* If not found, create a new entry in list and increment the */ + /* node count in the event structure */ + if(! found) { + *node_ptr = (void *) MALLOC(sizeof(Evt_Node_Info_t)); + node = *node_ptr; + node->next = NULL; + node->name = MIFcopy(node_name); + node->udn_index = udn_index; + index = ckt->evt->counts.num_nodes; + (ckt->evt->counts.num_nodes)++; + } + + + /* ******************************************* */ + /* Update remaining items in node list struct */ + /* ******************************************* */ + + /* Update flag on node that indicates if inversion is used by any */ + /* instance inputs */ + if(fast->conn[conn_num]->is_input) + if(! node->invert) + node->invert = fast->conn[conn_num]->port[port_num]->invert; + + /* Increment counts of ports, outputs connected to node */ + (node->num_ports)++; + if(fast->conn[conn_num]->is_output) + (node->num_outputs)++; + + /* If this is an input, add instance to list if not already there */ + if(fast->conn[conn_num]->is_input) { + + found = MIF_FALSE; + inst = node->inst_list; + inst_ptr = &(node->inst_list); + + while(inst) { + if(inst_index == inst->index) { + found = MIF_TRUE; + break; + } + else { + inst_ptr = &(inst->next); + inst = inst->next; + } + } + + if(! found) { + (node->num_insts)++; + *inst_ptr = (void *) MALLOC(sizeof(Evt_Inst_Index_t)); + inst = *inst_ptr; + inst->next = NULL; + inst->index = inst_index; + } + } + + /* Record the node index in the MIFinstance structure */ + fast->conn[conn_num]->port[port_num]->evt_data.node_index = index; + + /* Return the node index */ + *node_index = index; + if(fast->conn[conn_num]->is_output) + *output_subindex = node->num_outputs - 1; + else + *output_subindex = 0; /* just for safety - shouldn't need this */ +} + + + +/* +EVTport_insert + +This function locates or creates a new entry for the specified +port in the event-driven ``info'' structures during parsing. +*/ + + +static void EVTport_insert( + CKTcircuit *ckt, /* The circuit structure */ + MIFinstance *fast, /* The instance being parsed */ + int inst_index, /* The index of inst in evt structures */ + int node_index, /* The index of the node in evt structures */ + char *node_name, /* The node name */ + int conn_num, /* The port connection number */ + int port_num, /* The sub-port number - 0 if scalar port */ + int *port_index, /* The port index found or added */ + char **err_msg) /* Error message text if any */ +{ + + Evt_Port_Info_t *port; + Evt_Port_Info_t **port_ptr; + + int index; + + /* Find the end of the port info list */ + port = ckt->evt->info.port_list; + port_ptr = &(ckt->evt->info.port_list); + + index = 0; + while(port) { + port_ptr = &(port->next); + port = port->next; + index++; + } + + + /* Update the port count and create a new entry in the list */ + + (ckt->evt->counts.num_ports)++; + + *port_ptr = (void *) MALLOC(sizeof(Evt_Port_Info_t)); + port = *port_ptr; + + /* Fill in the elements */ + port->next = NULL; + port->inst_index = inst_index; + port->node_index = node_index; + port->node_name = MIFcopy(node_name); + port->inst_name = MIFcopy((char *) fast->MIFname); + port->conn_name = MIFcopy((char *) fast->conn[conn_num]->name); + port->port_num = port_num; + + /* Record the port index in the MIFinstance structure */ + fast->conn[conn_num]->port[port_num]->evt_data.port_index = index; + + /* Return the port index */ + *port_index = index; +} + + + + +/* +EVToutput_insert + +This function locates or creates a new entry for the specified +output in the event-driven ``info'' structures during parsing. +*/ + + +static void EVToutput_insert( + CKTcircuit *ckt, /* The circuit structure */ + MIFinstance *fast, /* The instance being parsed */ + int inst_index, /* The index of inst in evt structures */ + int node_index, /* The index of the node in evt structures */ + int port_index, /* The index of the port in the evt structures */ + int output_subindex, /* The output on this node */ + int conn_num, /* The port connection number */ + int port_num, /* The sub-port number - 0 if scalar port */ + char **err_msg) /* Error message text if any */ +{ + Evt_Output_Info_t *output; + Evt_Output_Info_t **output_ptr; + + int index; + + /* Find the end of the port info list */ + output = ckt->evt->info.output_list; + output_ptr = &(ckt->evt->info.output_list); + + index = 0; + while(output) { + output_ptr = &(output->next); + output = output->next; + index++; + } + + + /* Update the port count and create a new entry in the list */ + + (ckt->evt->counts.num_outputs)++; + + *output_ptr = (void *) MALLOC(sizeof(Evt_Output_Info_t)); + output = *output_ptr; + + /* Fill in the elements */ + output->next = NULL; + output->inst_index = inst_index; + output->node_index = node_index; + output->port_index = port_index; + output->output_subindex = output_subindex; + + /* Record the output index and subindex in the MIFinstance structure */ + fast->conn[conn_num]->port[port_num]->evt_data.output_index = index; + fast->conn[conn_num]->port[port_num]->evt_data.output_subindex + = output_subindex; + +} diff --git a/src/xspice/examples/analog_models1_ac.deck b/src/xspice/examples/analog_models1_ac.deck new file mode 100755 index 000000000..6467c2bd2 --- /dev/null +++ b/src/xspice/examples/analog_models1_ac.deck @@ -0,0 +1,67 @@ +Code Model Test - AC: gain, summer, mult, divide, pwl +* +* +*** analysis type *** +.ac dec 10 10 1000 +* +*** input sources *** +* +v1 1 0 1.0 AC 1.0 0.0 +* +v2 2 0 1.0 AC 1.0 0.0 +* +v3 3 0 DC 2.0 +* +v4 4 0 0.5 AC 0.5 0.0 +* +*** gain block *** +a1 1 10 gain1 +.model gain1 gain (in_offset=0.0 gain=2.0 out_offset=0.0) +* +* +*** summer block *** +a2 [1 2] 20 summer1 +.model summer1 summer (in_offset=[0.0 0.0] in_gain=[1.0 1.0] ++ out_gain=1.0 out_offset=0.0) +* +* +*** mult block *** +a3 [1 3] 30 mult1 +.model mult1 mult (in_offset=[0.0 0.0] in_gain=[1.0 1.0] ++ out_gain=1.0 out_offset=0.0) +* +* +*** divider block *** +a4 1 3 40 divide1 +.model divide1 divide (num_offset=0.0 num_gain=1.0 den_offset=0.0 den_gain=1.0 ++ den_lower_limit=1.0e-10 den_domain=1.0e-16 ++ fraction=false out_gain=1.0 out_offset=0.0) +* +* +*** pwl block *** +a5 4 50 pwl1 +.model pwl1 pwl (x_array=[-1.0 0.0 1.0 2.0 3.0 4.0 5.0] ++ y_array=[-1.0 0.0 1.0 4.0 4.5 5.0 5.0] ++ input_domain=0.01 fraction=TRUE) +* +* +*** resistors to ground *** +r1 1 0 1k +r2 2 0 1k +r3 3 0 1k +r4 4 0 1k +* +r10 10 0 1k +r20 20 0 1k +r30 30 0 1k +r40 40 0 1k +r50 50 0 1k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models1_dc.deck b/src/xspice/examples/analog_models1_dc.deck new file mode 100755 index 000000000..4ad6f0c83 --- /dev/null +++ b/src/xspice/examples/analog_models1_dc.deck @@ -0,0 +1,61 @@ +Code Model Test - DC: gain, summer, mult, divide, pwl +* +* +*** analysis type *** +.op +* +*** input sources *** +v1 1 0 DC 2 +* +v2 2 0 DC 2 +* +*** gain block *** +a1 1 10 gain1 +.model gain1 gain (in_offset=0.0 gain=2.0 out_offset=0.0) +* +* +*** summer block *** +a2 [1 2] 20 summer1 +.model summer1 summer (in_offset=[0.0 0.0] in_gain=[1.0 1.0] ++ out_gain=1.0 out_offset=0.0) +* +* +*** mult block *** +a3 [1 1] 30 mult1 +.model mult1 mult (in_offset=[0.0 0.0] in_gain=[1.0 1.0] ++ out_gain=0.1 out_offset=0.0) +* +* +*** divider block *** +a4 2 1 40 divide1 +.model divide1 divide (num_offset=0.0 num_gain=1.0 den_offset=0.0 den_gain=1.0 ++ den_lower_limit=1.0e-10 den_domain=1.0e-16 ++ fraction=false out_gain=1.0 out_offset=0.0) +* +* +*** pwl block *** +a5 1 50 pwl1 +.model pwl1 pwl (x_array=[-1.0 0.0 1.0 2.0 3.0 4.0 5.0] ++ y_array=[ 0.0 0.0 1.0 4.0 4.5 5.0 5.0] ++ input_domain=0.01 fraction=TRUE) +* +* +*** resistors to ground *** +r1 1 0 1k +r2 2 0 1k +r3 3 0 1k +* +r10 10 0 1k +r20 20 0 1k +r30 30 0 1k +r40 40 0 1k +r50 50 0 1k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models1_swept_dc.deck b/src/xspice/examples/analog_models1_swept_dc.deck new file mode 100755 index 000000000..e211325bd --- /dev/null +++ b/src/xspice/examples/analog_models1_swept_dc.deck @@ -0,0 +1,61 @@ +Code Model Test - Swept DC: gain, summer, mult, divide, pwl +* +* +*** analysis type *** +.dc v1 .1 10 .1 +* +*** input sources *** +v1 1 0 DC 2 +* +v2 2 0 DC 2 +* +*** gain block *** +a1 1 10 gain1 +.model gain1 gain (in_offset=0.0 gain=2.0 out_offset=0.0) +* +* +*** summer block *** +a2 [1 2] 20 summer1 +.model summer1 summer (in_offset=[0.0 0.0] in_gain=[1.0 1.0] ++ out_gain=1.0 out_offset=0.0) +* +* +*** mult block *** +a3 [1 1] 30 mult1 +.model mult1 mult (in_offset=[0.0 0.0] in_gain=[1.0 1.0] ++ out_gain=0.1 out_offset=0.0) +* +* +*** divider block *** +a4 2 1 40 divide1 +.model divide1 divide (num_offset=0.0 num_gain=1.0 den_offset=0.0 den_gain=1.0 ++ den_lower_limit=1.0e-10 den_domain=1.0e-16 ++ fraction=false out_gain=1.0 out_offset=0.0) +* +* +*** pwl block *** +a5 1 50 pwl1 +.model pwl1 pwl (x_array=[-1.0 0.0 1.0 2.0 3.0 4.0 5.0] ++ y_array=[ 0.0 0.0 1.0 4.0 4.5 5.0 5.0] ++ input_domain=0.01 fraction=TRUE) +* +* +*** resistors to ground *** +r1 1 0 1k +r2 2 0 1k +r3 3 0 1k +* +r10 10 0 1k +r20 20 0 1k +r30 30 0 1k +r40 40 0 1k +r50 50 0 1k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models1_transient.deck b/src/xspice/examples/analog_models1_transient.deck new file mode 100755 index 000000000..a4967d275 --- /dev/null +++ b/src/xspice/examples/analog_models1_transient.deck @@ -0,0 +1,62 @@ +Code Model Test - Transient: gain, summer, mult, divide, pwl +* +* +*** analysis type *** +.tran .1s 10s +* +*** input sources *** +* +v1 1 0 DC PWL(0 0 10 10) +* +v2 2 0 DC 2 +* +*** gain block *** +a1 1 10 gain1 +.model gain1 gain (in_offset=0.0 gain=2.0 out_offset=0.0) +* +* +*** summer block *** +a2 [1 2] 20 summer1 +.model summer1 summer (in_offset=[0.0 0.0] in_gain=[1.0 1.0] ++ out_gain=1.0 out_offset=0.0) +* +* +*** mult block *** +a3 [1 1] 30 mult1 +.model mult1 mult (in_offset=[0.0 0.0] in_gain=[1.0 1.0] ++ out_gain=0.1 out_offset=0.0) +* +* +*** divider block *** +a4 2 1 40 divide1 +.model divide1 divide (num_offset=0.0 num_gain=1.0 den_offset=0.0 den_gain=1.0 ++ den_lower_limit=0.1 den_domain=1.0e-16 ++ fraction=false out_gain=1.0 out_offset=0.0) +* +* +*** pwl block *** +a5 1 50 pwl1 +.model pwl1 pwl (x_array=[-1.0 0.0 1.0 2.0 3.0 4.0 5.0] ++ y_array=[ 0.0 0.0 1.0 4.0 4.5 5.0 5.0] ++ input_domain=0.01 fraction=TRUE) +* +* +*** resistors to ground *** +r1 1 0 1k +r2 2 0 1k +r3 3 0 1k +* +r10 10 0 1k +r20 20 0 1k +r30 30 0 1k +r40 40 0 1k +r50 50 0 1k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models2_ac.deck b/src/xspice/examples/analog_models2_ac.deck new file mode 100755 index 000000000..0b450c809 --- /dev/null +++ b/src/xspice/examples/analog_models2_ac.deck @@ -0,0 +1,66 @@ +Code Model Test - Swept DC: int, d_dt, s_xfer, core, lcouple +* +* +*** analysis type *** +.ac dec 10 10 1000 +* +* +*** input sources *** +* +v1 1 0 1.0 AC 1.0 0.0 +* +* +*** integrator block *** +a1 1 10 int1 +.model int1 int (in_offset=0.0 gain=2.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6 out_ic=0.0) +* +* +*** differentiator block *** +a2 1 20 d_dt1 +.model d_dt1 d_dt (out_offset=0.0 gain=1.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6) +* +* +*** s_xfer block *** +a3 1 30 filter1 +.model filter1 s_xfer (in_offset=0.0 gain=1.0 num_coeff=[1.0] ++ den_coeff=[1.0 1.414214 1.0] int_ic=[0.0 0.0] ++ denormalized_freq=628.0) +* +* +* +*** magnetic core & inductive coupling *** +v40 45 46 0.0 +a4 40 45 core1 +.model core1 core (H_array=[-2.0 -1.0 1.0 2.0] ++ B_array=[-2.0 -1.0 1.0 2.0] ++ area=1.0 length=1.0 input_domain=1.0e-6 ++ fraction=TRUE mode=1 ++ in_low=-1.0 in_high=1.0 hyst=0.5 ++ out_lower_limit=-1.0 out_upper_limit=1.0) +* +* +r5 1 50 100.0 +a5 (50 0) (40 46) inductor1 +.model inductor1 lcouple (num_turns=10) +* +* +*** resistors to ground *** +r1 1 0 1k +* +r10 10 0 1e12 +r20 20 0 1e12 +r30 30 0 1e12 +r40 40 0 1e12 +r45 45 0 1e12 +r50 50 0 1e12 +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models2_dc.deck b/src/xspice/examples/analog_models2_dc.deck new file mode 100755 index 000000000..66a612e46 --- /dev/null +++ b/src/xspice/examples/analog_models2_dc.deck @@ -0,0 +1,63 @@ +Code Model Test - DC: int, d_dt, s_xfer, core, lcouple +* +* +*** analysis type *** +.op +* +*** input sources *** +v1 1 0 DC 1.0 +* +* +* +* +*** integrator block *** +a1 1 10 int1 +.model int1 int (in_offset=0.0 gain=2.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6 out_ic=0.0) +* +* +*** differentiator block *** +a2 1 20 d_dt1 +.model d_dt1 d_dt (out_offset=0.0 gain=1.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6) +* +* +*** s_xfer block *** +a3 1 30 filter1 +.model filter1 s_xfer (in_offset=0.0 gain=1.0 num_coeff=[1.0] ++ den_coeff=[1.0 1.425625 1.516203] int_ic=[0.0 0.0] ++ denormalized_freq=6283.2) +* +* +*** magnetic core & inductive coupling *** +a4 40 45 core1 +.model core1 core (H_array=[-2.0 -1.0 1.0 2.0] ++ B_array=[-2.0 -1.0 1.0 2.0] ++ area=1.0 length=1.0 input_domain=1.0e-6 ++ fraction=TRUE mode=1 ++ in_low=-1.0 in_high=1.0 hyst=0.5 ++ out_lower_limit=-1.0 out_upper_limit=1.0) +* +* +r5 1 50 100.0 +a5 (50 0) (40 45) inductor1 +.model inductor1 lcouple (num_turns=10) +* +* +*** resistors to ground *** +r1 1 0 1k +* +r10 10 0 1k +r20 20 0 1k +r30 30 0 1k +r40 40 0 1k +r50 50 0 1k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models2_swept_dc.deck b/src/xspice/examples/analog_models2_swept_dc.deck new file mode 100755 index 000000000..470d11c81 --- /dev/null +++ b/src/xspice/examples/analog_models2_swept_dc.deck @@ -0,0 +1,64 @@ +Code Model Test - Swept DC: int, d_dt, s_xfer, core, lcouple +* +* +*** analysis type *** +.dc v1 .1 10 .5 +* +*** input sources *** +v1 1 0 DC 2 +* +* +*** integrator block *** +a1 1 10 int1 +.model int1 int (in_offset=0.0 gain=2.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6 out_ic=0.0) +* +* +*** differentiator block *** +a2 1 20 d_dt1 +.model d_dt1 d_dt (out_offset=0.0 gain=1.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6) +* +* +*** s_xfer block *** +a3 1 30 filter1 +.model filter1 s_xfer (in_offset=0.0 gain=1.0 num_coeff=[1.0] ++ den_coeff=[1.0 1.414214 1.0] int_ic=[0.0 0.0] ++ denormalized_freq=1.0) +* +* +* +*** magnetic core & inductive coupling *** +v40 45 46 0.0 +a4 40 45 core1 +.model core1 core (H_array=[-2.0 -1.0 1.0 2.0] ++ B_array=[-2.0 -1.0 1.0 2.0] ++ area=1.0 length=1.0 input_domain=1.0e-6 ++ fraction=TRUE mode=1 ++ in_low=-1.0 in_high=1.0 hyst=0.5 ++ out_lower_limit=-1.0 out_upper_limit=1.0) +* +* +r5 1 50 100.0 +a5 (50 0) (40 46) inductor1 +.model inductor1 lcouple (num_turns=10) +* +* +*** resistors to ground *** +r1 1 0 1k +* +r10 10 0 1e12 +r20 20 0 1e12 +r30 30 0 1e12 +r40 40 0 1e12 +r45 45 0 1e12 +r50 50 0 1e12 +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models2_transient.deck b/src/xspice/examples/analog_models2_transient.deck new file mode 100755 index 000000000..c9efac726 --- /dev/null +++ b/src/xspice/examples/analog_models2_transient.deck @@ -0,0 +1,67 @@ +Code Model Test - Transient: int, d_dt, s_xfer, core, lcouple +* +* +*** analysis type *** +.tran .1s 10s +* +*** input sources *** +* +v1 1 0 DC PWL(0 0 10 10) +* +v2 2 0 DC PWL(0 0 0.1 0.1 0.2 0.9 0.3 1.0 10 1.0) +* +* +* +*** integrator block *** +a1 1 10 int1 +.model int1 int (in_offset=0.0 gain=2.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6 out_ic=0.0) +* +* +*** differentiator block *** +a2 1 20 d_dt1 +.model d_dt1 d_dt (out_offset=0.0 gain=1.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6) +* +* +*** s_xfer block *** +a3 2 30 filter1 +.model filter1 s_xfer (in_offset=0.0 gain=1.0 num_coeff=[1.0] ++ den_coeff=[1.0 1.414214 1.0] int_ic=[0.0 0.0] ++ denormalized_freq=1.0) +* +* +* +*** magnetic core & inductive coupling *** +a4 40 45 core1 +.model core1 core (H_array=[-2.0 -1.0 1.0 2.0] ++ B_array=[-2.0 -1.0 1.0 2.0] ++ area=1.0 length=1.0 input_domain=1.0e-6 ++ fraction=TRUE mode=1 ++ in_low=-1.0 in_high=1.0 hyst=0.5 ++ out_lower_limit=-1.0 out_upper_limit=1.0) +* +* +r5 1 50 100.0 +a5 (50 0) (40 45) inductor1 +.model inductor1 lcouple (num_turns=10) +* +* +*** resistors to ground *** +r1 1 0 1k +r2 2 0 1k +* +r10 10 0 1k +r20 20 0 1k +r30 30 0 1k +r40 40 0 1k +r50 50 0 1k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models3_ac.deck b/src/xspice/examples/analog_models3_ac.deck new file mode 100755 index 000000000..3c4d7fa96 --- /dev/null +++ b/src/xspice/examples/analog_models3_ac.deck @@ -0,0 +1,81 @@ +Code Model Test - AC: hyst, limit, ilimit, climit, cmeter, lmeter +* +* +*** analysis type *** +.ac dec 10 10 1000 +* +* +*** input sources *** +* +v1 1 0 1.0 AC 1.0 0.0 +* +v2 2 0 DC 10.0 +* +v3 3 0 DC -10.0 +* +* +* +*** hyst block *** +a1 1 10 hyst1 +.model hyst1 hyst (in_low=0.0 in_high=1.0 hyst=0.1 out_lower_limit=0.0 ++ out_upper_limit=1.0 input_domain=0.01 fraction=TRUE) +* +* +*** limit block *** +a2 1 20 limit1 +.model limit1 limit (in_offset=0.0 gain=1.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6 fraction=FALSE) +* +* +*** ilimit block *** +a3 1 2 3 30 ilimit1 +.model ilimit1 ilimit (in_offset=0.0 gain=1.0 r_out_source=1.0 ++ r_out_sink=1.0 i_limit_source=1.0 ++ i_limit_sink=1.0 v_pwr_range=1.0e-3 ++ i_source_range=1.0e-6 i_sink_range=1.0e-6 ++ r_out_domain=1.0e-6) +* +* +*** climit block *** +a4 1 2 3 40 climit1 +.model climit1 climit (in_offset=0.0 gain=1.0 upper_delta=0.0 ++ lower_delta=0.0 limit_range=1.0e-6 ++ fraction=FALSE) +* +* +*** cmeter block *** +c5 51 0 1.0e-6 +a5 51 50 cmeter1 +.model cmeter1 cmeter (gain=1.0) +* +* +* +*** lmeter block *** +l6 61 0 1.0e-6 +a6 61 60 lmeter1 +.model lmeter1 lmeter (gain=1.0) +* +* +* +*** resistors to ground *** +r1 1 0 10k +r2 2 0 10k +r3 3 0 10k +* +r10 10 0 10k +r20 20 0 10k +r30 30 0 10k +r40 40 0 10k +r50 50 0 10k +r51 51 0 10k +r60 60 0 10k +r61 61 0 10k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models3_dc.deck b/src/xspice/examples/analog_models3_dc.deck new file mode 100755 index 000000000..cff617cdc --- /dev/null +++ b/src/xspice/examples/analog_models3_dc.deck @@ -0,0 +1,79 @@ +Code Model Test - DC: hyst, limit, ilimit, climit, cmeter, lmeter +* +* +*** analysis type *** +.op +* +*** input sources *** +v1 1 0 DC 1.0 +* +v2 2 0 DC 10.0 +* +v3 3 0 DC -10.0 +* +* +* +*** hyst block *** +a1 1 10 hyst1 +.model hyst1 hyst (in_low=0.0 in_high=1.0 hyst=0.1 out_lower_limit=0.0 ++ out_upper_limit=1.0 input_domain=0.01 fraction=TRUE) +* +* +*** limit block *** +a2 1 20 limit1 +.model limit1 limit (in_offset=0.0 gain=1.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6 fraction=FALSE) +* +* +*** ilimit block *** +a3 1 2 3 30 ilimit1 +.model ilimit1 ilimit (in_offset=0.0 gain=1.0 r_out_source=1.0 ++ r_out_sink=1.0 i_limit_source=1.0 ++ i_limit_sink=1.0 v_pwr_range=1.0e-3 ++ i_source_range=1.0e-3 i_sink_range=1.0e-3 ++ r_out_domain=1.0e-3) +* +* +*** climit block *** +a4 1 2 3 40 climit1 +.model climit1 climit (in_offset=0.0 gain=1.0 upper_delta=0.0 ++ lower_delta=0.0 limit_range=1.0e-6 ++ fraction=FALSE) +* +* +*** cmeter block *** +c5 51 0 1.0e-6 +a5 51 50 cmeter1 +.model cmeter1 cmeter (gain=1.0) +* +* +* +*** lmeter block *** +l6 61 0 1.0e-6 +a6 61 60 lmeter1 +.model lmeter1 lmeter (gain=1.0) +* +* +* +*** resistors to ground *** +r1 1 0 10k +r2 2 0 10k +r3 3 0 10k +* +r10 10 0 10k +r20 20 0 10k +r30 30 0 10k +r40 40 0 10k +r50 50 0 10k +r51 51 0 10k +r60 60 0 10k +r61 61 0 10k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models3_swept_dc.deck b/src/xspice/examples/analog_models3_swept_dc.deck new file mode 100755 index 000000000..394e7aac1 --- /dev/null +++ b/src/xspice/examples/analog_models3_swept_dc.deck @@ -0,0 +1,79 @@ +Code Model Test - DC: hyst, limit, ilimit, climit, cmeter, lmeter +* +* +*** analysis type *** +.dc v1 .1 15 .5 +* +*** input sources *** +v1 1 0 DC 1.0 +* +v2 2 0 DC 10.0 +* +v3 3 0 DC -10.0 +* +* +* +*** hyst block *** +a1 1 10 hyst1 +.model hyst1 hyst (in_low=0.0 in_high=1.0 hyst=0.1 out_lower_limit=0.0 ++ out_upper_limit=1.0 input_domain=0.01 fraction=TRUE) +* +* +*** limit block *** +a2 1 20 limit1 +.model limit1 limit (in_offset=0.0 gain=1.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6 fraction=FALSE) +* +* +*** ilimit block *** +a3 1 2 3 30 ilimit1 +.model ilimit1 ilimit (in_offset=0.0 gain=1.0 r_out_source=1.0 ++ r_out_sink=1.0 i_limit_source=1.0 ++ i_limit_sink=1.0 v_pwr_range=1.0e-3 ++ i_source_range=1.0e-6 i_sink_range=1.0e-6 ++ r_out_domain=1.0e-6) +* +* +*** climit block *** +a4 1 2 3 40 climit1 +.model climit1 climit (in_offset=0.0 gain=1.0 upper_delta=0.0 ++ lower_delta=0.0 limit_range=1.0e-6 ++ fraction=FALSE) +* +* +*** cmeter block *** +c5 51 0 1.0e-6 +a5 51 50 cmeter1 +.model cmeter1 cmeter (gain=1.0) +* +* +* +*** lmeter block *** +l6 61 0 1.0e-6 +a6 61 60 lmeter1 +.model lmeter1 lmeter (gain=1.0) +* +* +* +*** resistors to ground *** +r1 1 0 10k +r2 2 0 10k +r3 3 0 10k +* +r10 10 0 10k +r20 20 0 10k +r30 30 0 10k +r40 40 0 10k +r50 50 0 10k +r51 51 0 10k +r60 60 0 10k +r61 61 0 10k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models3_transient.deck b/src/xspice/examples/analog_models3_transient.deck new file mode 100755 index 000000000..c92ffff96 --- /dev/null +++ b/src/xspice/examples/analog_models3_transient.deck @@ -0,0 +1,80 @@ +Code Model Test - Transient: hyst, limit, ilimit, climit, cmeter, lmeter +* +* +*** analysis type *** +.tran .1s 15s +* +*** input sources *** +* +v1 1 0 DC PWL(0 0 15 15) +* +v2 2 0 DC 10.0 +* +v3 3 0 DC -10.0 +* +* +* +*** hyst block *** +a1 1 10 hyst1 +.model hyst1 hyst (in_low=0.0 in_high=1.0 hyst=0.1 out_lower_limit=0.0 ++ out_upper_limit=1.0 input_domain=0.01 fraction=TRUE) +* +* +*** limit block *** +a2 1 20 limit1 +.model limit1 limit (in_offset=0.0 gain=1.0 out_lower_limit=-1.0e6 ++ out_upper_limit=1.0e6 limit_range=1.0e-6 fraction=FALSE) +* +* +*** ilimit block *** +a3 1 2 3 30 ilimit1 +.model ilimit1 ilimit (in_offset=0.0 gain=1.0 r_out_source=1.0 ++ r_out_sink=1.0 i_limit_source=1.0 ++ i_limit_sink=1.0 v_pwr_range=1.0e-3 ++ i_source_range=1.0e-6 i_sink_range=1.0e-6 ++ r_out_domain=1.0e-6) +* +* +*** climit block *** +a4 1 2 3 40 climit1 +.model climit1 climit (in_offset=0.0 gain=1.0 upper_delta=0.0 ++ lower_delta=0.0 limit_range=1.0e-6 ++ fraction=FALSE) +* +* +*** cmeter block *** +c5 51 0 1.0e-6 +a5 51 50 cmeter1 +.model cmeter1 cmeter (gain=1.0) +* +* +* +*** lmeter block *** +l6 61 0 1.0e-6 +a6 61 60 lmeter1 +.model lmeter1 lmeter (gain=1.0) +* +* +* +*** resistors to ground *** +r1 1 0 10k +r2 2 0 10k +r3 3 0 10k +* +r10 10 0 10k +r20 20 0 10k +r30 30 0 10k +r40 40 0 10k +r50 50 0 10k +r51 51 0 10k +r60 60 0 10k +r61 61 0 10k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models4_ac.deck b/src/xspice/examples/analog_models4_ac.deck new file mode 100755 index 000000000..70413e1ad --- /dev/null +++ b/src/xspice/examples/analog_models4_ac.deck @@ -0,0 +1,78 @@ +Code Model Test - DC: sine, triangle, aswitch, zener, oneshot +* +* +*** analysis type *** +.ac dec 10 10 1000 +* +* +*** input sources *** +* +v1 1 0 1.0 AC 1.0 0.0 +* +v2 2 0 DC 1.0 +* +v3 3 0 DC 1.0 +* +* +* +* +*** sine block *** +a1 1 10 sine1 +.model sine1 sine (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0 1.0 100.0 100.0] ++ out_low=-1.0 out_high=1.0) +* +* +*** triangle block *** +a2 1 20 triangle1 +.model triangle1 triangle (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0 1.0 100.0 100.0] ++ out_low=-1.0 out_high=1.0 duty_cycle=0.8) +* +* +*** aswitch block *** +a3 1 (2 30) aswitch1 +.model aswitch1 aswitch (cntl_off=0.0 cntl_on=1.0 log=TRUE ++ r_off=1.0e12 r_on=10.0) +* +* +*** zener diode *** +r4 1 40 10K +a4 (40 0) zener1 +.model zener1 zener (v_breakdown=10.0 i_breakdown=2.0e-2 ++ r_breakdown=1.0 i_rev=1.0e-6 i_sat=1.0e-12 ++ n_forward=1.0 limit_switch=FALSE) +* +* +*** oneshot block *** +a5 3 1 2 50 oneshot1 +.model oneshot1 oneshot (cntl_array=[-1.0 0.0 1.0 2.0] ++ pw_array=[1.0 1.0 0.1 0.1] clk_trig=0.5 ++ pos_edge_trig=TRUE out_low=0.0 out_high=1.0 ++ rise_time=1.0e-6 rise_delay=1.0e-9 ++ fall_delay=1.0e-9 fall_time=1.0e-6 ++ retrig=FALSE) +* +* +* +* +*** resistors to ground *** +r1 1 0 10k +r2 2 0 10k +r3 3 0 10k +* +r10 10 0 10k +r20 20 0 10k +r30 30 0 10k +r40 40 0 10k +r50 50 0 10k +r60 60 0 10k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models4_dc.deck b/src/xspice/examples/analog_models4_dc.deck new file mode 100755 index 000000000..b3254ea5e --- /dev/null +++ b/src/xspice/examples/analog_models4_dc.deck @@ -0,0 +1,74 @@ +Code Model Test - DC: sine triangle aswitch zener oneshot +* +* +*** analysis type *** +.op +* +*** input sources *** +v1 1 0 DC 1.0 +* +v2 2 0 DC 1.0 +* +v3 3 0 DC 1.0 +* +* +*** sine block *** +a1 1 10 sine1 +.model sine1 sine (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0 1.0 100.0 100.0] ++ out_low=-1.0 out_high=1.0) +* +* +*** triangle block *** +a2 1 20 triangle1 +.model triangle1 triangle (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0 1.0 100.0 100.0] ++ out_low=-1.0 out_high=1.0 duty_cycle=0.8) +* +* +*** aswitch block *** +a3 1 (2 30) aswitch1 +.model aswitch1 aswitch (cntl_off=0.0 cntl_on=1.0 log=TRUE ++ r_off=1.0e12 r_on=10.0) +* +* +*** zener diode *** +r4 1 40 10K +a4 (40 0) zener1 +.model zener1 zener (v_breakdown=10.0 i_breakdown=2.0e-2 ++ r_breakdown=1.0 i_rev=1.0e-6 i_sat=1.0e-12 ++ n_forward=1.0 limit_switch=FALSE) +* +* +*** oneshot block *** +a5 3 1 2 50 oneshot1 +.model oneshot1 oneshot (cntl_array=[-1.0 0.0 1.0 2.0] ++ pw_array=[1.0 1.0 0.1 0.1] clk_trig=0.5 ++ pos_edge_trig=TRUE out_low=0.0 out_high=1.0 ++ rise_time=1.0e-6 rise_delay=1.0e-9 ++ fall_delay=1.0e-9 fall_time=1.0e-6 ++ retrig=FALSE) +* +* +* +* +*** resistors to ground *** +r1 1 0 10k +r2 2 0 10k +r3 3 0 10k +* +r10 10 0 10k +r20 20 0 10k +r30 30 0 10k +r40 40 0 10k +r50 50 0 10k +r60 60 0 10k +* +* +.end + + + + + + diff --git a/src/xspice/examples/analog_models4_swept_dc.deck b/src/xspice/examples/analog_models4_swept_dc.deck new file mode 100755 index 000000000..d06fe05f8 --- /dev/null +++ b/src/xspice/examples/analog_models4_swept_dc.deck @@ -0,0 +1,72 @@ +Code Model Test - Swept DC: sine, triangle, aswitch, zener, oneshot +* +* +*** analysis type *** +.dc v1 .1 15 .5 +* +*** input sources *** +v1 1 0 DC 1.0 +* +v2 2 0 DC 1.0 +* +v3 3 0 DC 1.0 +* +* +*** sine block *** +a1 1 10 sine1 +.model sine1 sine (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0 1.0 100.0 100.0] ++ out_low=-1.0 out_high=1.0) +* +* +*** triangle block *** +a2 1 20 triangle1 +.model triangle1 triangle (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0 1.0 100.0 100.0] ++ out_low=-1.0 out_high=1.0 duty_cycle=0.8) +* +* +*** aswitch block *** +a3 1 (2 30) aswitch1 +.model aswitch1 aswitch (cntl_off=0.0 cntl_on=1.0 log=TRUE ++ r_off=1.0e12 r_on=10.0) +* +* +*** zener diode *** +r4 1 40 100 +a4 (0 40) zener1 +.model zener1 zener (v_breakdown=8.0 i_breakdown=2.0e-2 ++ r_breakdown=1.0 i_rev=1.0e-6 i_sat=1.0e-12 ++ n_forward=1.0 limit_switch=FALSE) +* +* +*** oneshot block *** +a5 3 1 2 50 oneshot1 +.model oneshot1 oneshot (cntl_array=[-1.0 0.0 1.0 2.0] ++ pw_array=[1.0 1.0 0.1 0.1] clk_trig=0.5 ++ pos_edge_trig=TRUE out_low=0.0 out_high=1.0 ++ rise_time=1.0e-6 rise_delay=1.0e-9 ++ fall_delay=1.0e-9 fall_time=1.0e-6 ++ retrig=FALSE) +* +* +* +* +*** resistors to ground *** +r1 1 0 10k +r2 2 0 10k +r3 3 0 10k +* +r10 10 0 10k +r20 20 0 10k +r30 30 0 10k +r40 40 0 10k +r50 50 0 10k +r60 60 0 10k +* +* +.end + + + + diff --git a/src/xspice/examples/analog_models4_transient.deck b/src/xspice/examples/analog_models4_transient.deck new file mode 100755 index 000000000..9ad60efee --- /dev/null +++ b/src/xspice/examples/analog_models4_transient.deck @@ -0,0 +1,76 @@ +Code Model Test - Transient: sine, triangle, aswitch, zener, oneshot +* +* +*** analysis type *** +.tran .01ms 2ms +* +*** input sources *** +* +v1 1 0 DC 0.0 PWL(0 0 2e-3 2) +* +v2 2 0 DC 0.0 PWL(0 0 2e-3 10) +* +v3 3 0 DC 0.0 PWL(0 0.0 0.9e-3 0.0 1e-3 1.0 1.9e-3 1.0 2e-3 0.0 2.9e-3 0.0) +* +v4 4 0 DC 1.0 +* +* +*** sine block *** +a1 1 10 sine1 +.model sine1 sine (cntl_array=[-1.0 0.0 10.0 20.0] ++ freq_array=[500 500 2000 2000] ++ out_low=-1.0 out_high=1.0) +* +* +*** triangle block *** +a2 1 20 triangle1 +.model triangle1 triangle (cntl_array=[-1.0 0.0 10.0 20.0] ++ freq_array=[500 500 10000 10000] ++ out_low=-1.0 out_high=1.0 duty_cycle=0.5) +* +* +*** aswitch block *** +a3 1 (4 30) aswitch1 +.model aswitch1 aswitch (cntl_off=0.0 cntl_on=1.0 log=TRUE ++ r_off=1.0e12 r_on=10.0) +* +* +*** zener diode *** +rzener 2 40 100 +a4 (0 40) zener1 +.model zener1 zener (v_breakdown=9.0 i_breakdown=2.0e-2 ++ r_breakdown=1.0 i_rev=1.0e-6 i_sat=1.0e-12 ++ n_forward=1.0 limit_switch=FALSE) +* +* +*** oneshot block *** +a5 3 1 0 50 oneshot1 +.model oneshot1 oneshot (cntl_array=[-1.0 0.0 1.0 2.0] ++ pw_array=[2.0e-3 2.0e-3 0.1e-3 0.1e-3] clk_trig=0.5 ++ pos_edge_trig=TRUE out_low=0.0 out_high=1.0 ++ rise_time=1.0e-6 rise_delay=1.0e-9 ++ fall_delay=1.0e-9 fall_time=1.0e-6 ++ retrig=FALSE) +* +* +* +* +*** resistors to ground *** +r1 1 0 10k +r2 2 0 10k +r3 3 0 10k +r4 4 0 10k +* +r10 10 0 10k +r20 20 0 10k +r30 30 0 10k +r40 40 0 10k +r50 50 0 10k +r60 60 0 10k +* +* +.end + + + + diff --git a/src/xspice/examples/arbitrary_phase.deck b/src/xspice/examples/arbitrary_phase.deck new file mode 100755 index 000000000..3ef68bc29 --- /dev/null +++ b/src/xspice/examples/arbitrary_phase.deck @@ -0,0 +1,17 @@ +Arbitrary Phase SIN and PULSE Sources +* +* This circuit generates two cycles of sine and square waves +* beginning at +45 degrees. +* +* Phase shift is specified after Berkeley defined parameters +* on the independent source cards. +* +.tran 2e-5 2e-3 +* +v1 1 0 0.0 sin(0 1 1k 0 0 45.0) +r1 1 0 1k +* +v2 2 0 0.0 pulse(-1 1 0 1e-5 1e-5 5e-4 1e-3 45.0) +r2 2 0 1k +* +.end diff --git a/src/xspice/examples/bad_io.deck b/src/xspice/examples/bad_io.deck new file mode 100755 index 000000000..74678115c --- /dev/null +++ b/src/xspice/examples/bad_io.deck @@ -0,0 +1,17 @@ +Invalid number of inputs/outputs +* +* This circuit contains a simple gain block to demonstrate +* that the simulator reports an error if the number of +* connections on the code model is incorrect. +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +r1 1 0 1k +* +a1 1 2 3 gain_block +a2 1 gain_block +.model gain_block gain (gain=10) +r2 2 0 1k +* +.end diff --git a/src/xspice/examples/bad_io_type.deck b/src/xspice/examples/bad_io_type.deck new file mode 100755 index 000000000..41e64ad59 --- /dev/null +++ b/src/xspice/examples/bad_io_type.deck @@ -0,0 +1,25 @@ +Invalid input/output type +* +* This circuit contains a simple gain block to demonstrate +* that the simulator reports an error if an invalid type +* is used with the code model connections. +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +r1 1 0 1k +* +* Both connections on the gain block must be analog, but +* the second is specified as digital +* +a1 1 %d 2 gain_block +.model gain_block gain (gain=10) +r2 2 0 1k +* +* Node 1 below should be a digital node, but is an analog node +* +a2 [1] [3] dac +.model dac dac_bridge +r3 3 0 1k +* +.end diff --git a/src/xspice/examples/bad_name.deck b/src/xspice/examples/bad_name.deck new file mode 100755 index 000000000..0380cac2e --- /dev/null +++ b/src/xspice/examples/bad_name.deck @@ -0,0 +1,16 @@ +Unknown code model name +* +* This circuit contains a simple gain block to demonstrate +* that the simulator reports an error if the code model name +* is incorrect. +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +r1 1 0 1k +* +a1 1 2 gain_block +.model gain_block this_model_doesnt_exist (gain=10) +r2 2 0 1k +* +.end diff --git a/src/xspice/examples/bad_param.deck b/src/xspice/examples/bad_param.deck new file mode 100755 index 000000000..22a2aa2f0 --- /dev/null +++ b/src/xspice/examples/bad_param.deck @@ -0,0 +1,16 @@ +Unknown code model parameter +* +* This circuit contains a simple gain block to demonstrate +* that the simulator reports an error if the .model card +* references a parameter that doesn't exist +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +r1 1 0 1k +* +a1 1 2 gain_block +.model gain_block gain (this_parameter_doesnt_exist=2 gain=10) +r2 2 0 1k +* +.end diff --git a/src/xspice/examples/bad_param_type.deck b/src/xspice/examples/bad_param_type.deck new file mode 100755 index 000000000..f110feda5 --- /dev/null +++ b/src/xspice/examples/bad_param_type.deck @@ -0,0 +1,16 @@ +Invalid parameter type +* +* This circuit contains a simple gain block to demonstrate +* that the simulator reports an error if the parameter value +* is invalid. +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +r1 1 0 1k +* +a1 1 2 gain_block +.model gain_block gain (gain=false) +r2 2 0 1k +* +.end diff --git a/src/xspice/examples/d_to_real/Makefile b/src/xspice/examples/d_to_real/Makefile new file mode 100755 index 000000000..d2d2af97e --- /dev/null +++ b/src/xspice/examples/d_to_real/Makefile @@ -0,0 +1,40 @@ +# $Id$ +# +# Makefile for Code Model directories +# + +# Include global XSPICE selections for CC and other macros +include /usr/local/xspice-1-0/include/make.include + +INCLUDE = -I. -I$(ROOT)/include/sim + +CFLAGS = -g + +#----------------------------------------------------------------------------- +# Edit the following definition to specify the object files that comprise +# your code model. If your code model is completely specified in the +# cfunc.mod file, there is no need to edit this definition. +# DO NOT include the ifspec.o file. + +CODE_MODEL_OBJECTS = cfunc.o + +#----------------------------------------------------------------------------- +# DO NOT MODIFY THE FOLLOWING DEFINITIONS: + +.SUFFIXES: $(SUFFIXES) .mod .ifs + +.mod.c: + $(BINDIR)/cmpp -mod $< + +.ifs.c: + $(BINDIR)/cmpp -ifs + +.c.o: $*.c + ${CC} ${CFLAGS} ${INCLUDE} -c $*.c + +all : ifspec.o $(CODE_MODEL_OBJECTS) + +cfunc.o : cfunc.c +ifspec.o : ifspec.c + + diff --git a/src/xspice/examples/d_to_real/cfunc.mod b/src/xspice/examples/d_to_real/cfunc.mod new file mode 100755 index 000000000..079fc78ca --- /dev/null +++ b/src/xspice/examples/d_to_real/cfunc.mod @@ -0,0 +1,43 @@ +/* $Id$ */ + +void ucm_d_to_real (ARGS) +{ + + Digital_State_t in; + + double *out; + double delay; + double zero; + double one; + double ena; + + + in = INPUT_STATE(in); + if(PORT_NULL(enable)) + ena = 1.0; + else if(INPUT_STATE(enable) == ONE) + ena = 1.0; + else + ena = 0.0; + out = OUTPUT(out); + + zero = PARAM(zero); + one = PARAM(one); + delay = PARAM(delay); + + + if(in == ZERO) + *out = zero * ena; + else if(in == UNKNOWN) + *out = (zero + one) / 2.0 * ena; + else + *out = one * ena; + + if(TIME > 0.0) + OUTPUT_DELAY(out) = delay; + +} + + + + diff --git a/src/xspice/examples/d_to_real/ifspec.ifs b/src/xspice/examples/d_to_real/ifspec.ifs new file mode 100755 index 000000000..eb2300c82 --- /dev/null +++ b/src/xspice/examples/d_to_real/ifspec.ifs @@ -0,0 +1,32 @@ +/* $Id$ */ + +NAME_TABLE: + +Spice_Model_Name: d_to_real +C_Function_Name: ucm_d_to_real +Description: "Node bridge from digital to real with enable" + + +PORT_TABLE: + +Port_Name: in enable out +Description: "input" "enable" "output" +Direction: in in out +Default_Type: d d real +Allowed_Types: [d] [d] [real] +Vector: no no no +Vector_Bounds: - - - +Null_Allowed: no yes no + + +PARAMETER_TABLE: + +Parameter_Name: zero one delay +Description: "value for 0" "value for 1" "delay" +Data_Type: real real real +Default_Value: 0.0 1.0 1e-9 +Limits: - - [1e-15 -] +Vector: no no no +Vector_Bounds: - - - +Null_Allowed: yes yes yes + diff --git a/src/xspice/examples/diffpair.in b/src/xspice/examples/diffpair.in new file mode 100755 index 000000000..da321ce89 --- /dev/null +++ b/src/xspice/examples/diffpair.in @@ -0,0 +1,28 @@ +difpair ckt - simple differential pair +*.width in=72 +.opt acct list node lvlcod=2 +*.tf v(5) vin +*.dc vin -0.25 0.25 0.005 +*.ac dec 10 1 10ghz +.tran 5ns 500ns +vin 1 0 sin(0 0.1 5meg) ac 1 +vcc 8 0 12 +vee 9 0 -12 +q1 4 2 6 qnl +q2 5 3 6 qnl +rs1 1 2 1k +rs2 3 0 1k +rc1 4 8 10k +rc2 5 8 10k +q3 6 7 9 qnl +q4 7 7 9 qnl +rbias 7 8 20k +.model qnl npn(bf=80 rb=100 ccs=2pf tf=0.3ns tr=6ns cje=3pf cjc=2pf ++ va=50) +.print dc v(4) v(5) +.plot dc v(5) +.print ac vm(5) vp(5) +.plot ac vm(5) vp(5) +.print tran v(4) v(5) +.plot tran v(5) +.end diff --git a/src/xspice/examples/digital_invert.deck b/src/xspice/examples/digital_invert.deck new file mode 100755 index 000000000..08d7f6e91 --- /dev/null +++ b/src/xspice/examples/digital_invert.deck @@ -0,0 +1,23 @@ +Digital inversions +* +.tran 1e-8 1e-6 +* +v1 1 0 0.0 pulse(0 1 0 1e-8 1e-8 0.25e-6 0.5e-6) +r1 1 0 1k +* +a1 [1] [2] adc +.model adc adc_bridge +* +a2 2 3 inv +a3 2 ~4 inv +a4 ~2 5 inv +a5 ~2 ~6 inv +.model inv d_inverter +* +a6 [2 ~4] 7 nand +.model nand d_nand +* +a8 [2 3 4 5 6 7] [12 13 14 15 16 17] dac +.model dac dac_bridge +* +.end diff --git a/src/xspice/examples/digital_models.deck b/src/xspice/examples/digital_models.deck new file mode 100755 index 000000000..39f737278 --- /dev/null +++ b/src/xspice/examples/digital_models.deck @@ -0,0 +1,20 @@ +Digital models +* +* This circuit contains a nand gate oscillator enabled by +* a pulse input after 20nS. Node 1 is an analog node. +* Nodes 2 and 3 are digital nodes. +* +.tran 1e-8 1e-7 +* +v1 1 0 0.0 pulse(0 1 2e-8 1e-9 1e-9) +* +r1 1 0 1k +* +a1 [1] [2] atod1 +.model atod1 adc_bridge (in_low=0.25 in_high=0.75 ++ rise_delay=1e-9 fall_delay=1e-9) +* +a2 [2 3] 3 nand +.model nand d_nand (rise_delay=1e-9 fall_delay=1e-9) +* +.end diff --git a/src/xspice/examples/digital_models1.deck b/src/xspice/examples/digital_models1.deck new file mode 100755 index 000000000..c78fa45ad --- /dev/null +++ b/src/xspice/examples/digital_models1.deck @@ -0,0 +1,77 @@ +Code Model Test: buffer, inverter, and, nand, or, nor, xor, xnor +* +* +*** analysis type *** +.tran .01s 4s +* +*** input sources *** +* +v2 200 0 DC PWL( (0 0.0) (2 0.0) (2.0000000001 1.0) (3 1.0) ) +* +v1 100 0 DC PWL( (0 0.0) (1.0 0.0) (1.0000000001 1.0) (2 1.0) ++ (2.0000000001 0.0) (3 0.0) (3.0000000001 1.0) (4 1.0) ) +* +* +*** adc_bridge blocks *** +aconverter [200 100] [2 1] adc_bridge1 +.model adc_bridge1 adc_bridge (in_low=0.1 in_high=0.9 ++ rise_delay=1.0e-12 fall_delay=1.0e-12) +* +* +* +*** buffer block *** +a1 1 10 d_buffer1 +.model d_buffer1 d_buffer (rise_delay=1.0e-6 fall_delay=2.0e-6 ++ input_load=1.0e-12) +* +* +*** inverter block *** +a2 1 20 d_inv1 +.model d_inv1 d_inverter (rise_delay=1.0e-6 fall_delay=2.0e-6 ++ input_load=1.0e-12) +* +* +*** and block *** +a3 [1 2] 30 d_and1 +.model d_and1 d_and (rise_delay=1.0e-6 fall_delay=2.0e-6 ++ input_load=1.0e-12) +* +* +*** nand block *** +a4 [1 2] 40 d_nand1 +.model d_nand1 d_nand (rise_delay=1.0e-6 fall_delay=2.0e-6 ++ input_load=1.0e-12) +* +* +*** or block *** +a5 [1 2] 50 d_or1 +.model d_or1 d_or (rise_delay=1.0e-6 fall_delay=2.0e-6 ++ input_load=1.0e-12) +* +* +*** nor block *** +a6 [1 2] 60 d_nor1 +.model d_nor1 d_nor (rise_delay=1.0e-6 fall_delay=2.0e-6 ++ input_load=1.0e-12) +* +* +*** xor block *** +a7 [1 2] 70 d_xor1 +.model d_xor1 d_xor (rise_delay=1.0e-6 fall_delay=2.0e-6 ++ input_load=1.0e-12) +* +* +*** xnor block *** +a8 [1 2] 80 d_xnor1 +.model d_xnor1 d_xnor (rise_delay=1.0e-6 fall_delay=2.0e-6 ++ input_load=1.0e-12) +* +* +* +*** resistors to ground *** +r1 100 0 1k +r2 200 0 1k +* +* +* +.end diff --git a/src/xspice/examples/digital_models2.deck b/src/xspice/examples/digital_models2.deck new file mode 100755 index 000000000..9f6244073 --- /dev/null +++ b/src/xspice/examples/digital_models2.deck @@ -0,0 +1,91 @@ +Code Model Test: d flip-flop, jk flip-flop, toggle ff, set-reset ff +* +* +*** analysis type *** +.tran .01s 4s +* +*** input sources *** +* +vdata1 100 0 DC PWL( (0 0.0) (2 0.0) (2.0000000001 1.0) (3 1.0) ) +* +* +vdata2 200 0 DC PWL( (0 0.0) (1.0 0.0) (1.0000000001 1.0) (2 1.0) ++ (2.0000000001 0.0) (3 0.0) (3.0000000001 1.0) (4 1.0) ) +* +* +vclk 300 0 DC PWL( (0 0.0) (0.5 0.0) (0.50000000001 1.0) ++ (1.0 1.0) (1.00000000001 0.0) ++ (1.5 0.0) (1.50000000001 1.0) ++ (2.0 1.0) (2.00000000001 0.0) ++ (2.5 0.0) (2.50000000001 1.0) ++ (3.0 1.0) (3.00000000001 0.0) ++ (3.5 0.0) (3.50000000001 1.0) (4.0 1.0) ) +* +* +vset 400 0 DC 0.0 +* +* +vreset 500 0 DC PWL( (0 0.0) (3.8 0.0) (3.80000000001 1.0) (4 1.0) ) +* +* +*** adc_bridge blocks *** +aconverter [100 200 300 400 500] [1 2 3 4 5] adc_bridge1 +.model adc_bridge1 adc_bridge (in_low=0.1 in_high=0.9 ++ rise_delay=1.0e-12 fall_delay=1.0e-12) +* +* +* +*** d flip-flop block *** +a1 1 3 4 5 10 11 d_dff1 +.model d_dff1 d_dff (clk_delay=1.0e-6 set_delay=2.0e-6 ++ reset_delay=3.0e-6 ic=0 ++ rise_delay=4.0e-6 fall_delay=5.0e-6 ++ data_load=1.0e-12 clk_load=1.0e-12 ++ set_load=1.0e-12 reset_load=1.0e-12) +* +* +*** jk flip-flop block *** +a2 1 2 3 4 5 20 21 d_jkff1 +.model d_jkff1 d_jkff (clk_delay=1.0e-6 set_delay=2.0e-6 ++ reset_delay=3.0e-6 ic=0 ++ rise_delay=4.0e-6 fall_delay=5.0e-6 ++ jk_load=1.0e-12 clk_load=1.0e-12 ++ set_load=1.0e-12 reset_load=1.0e-12) +* +* +*** toggle flip-flop block *** +a3 1 3 4 5 30 31 d_tff1 +.model d_tff1 d_tff (clk_delay=1.0e-6 set_delay=2.0e-6 ++ reset_delay=3.0e-6 ic=0 ++ rise_delay=4.0e-6 fall_delay=5.0e-6 ++ t_load=1.0e-12 clk_load=1.0e-12 ++ set_load=1.0e-12 reset_load=1.0e-12) +* +* +*** set-reset flip-flop block *** +a4 1 2 3 4 5 40 41 d_srff1 +.model d_srff1 d_srff (clk_delay=1.0e-6 set_delay=2.0e-6 ++ reset_delay=3.0e-6 ic=0 ++ rise_delay=4.0e-6 fall_delay=5.0e-6 ++ sr_load=1.0e-12 clk_load=1.0e-12 ++ set_load=1.0e-12 reset_load=1.0e-12) +* +* +* +* +*** resistors to ground *** +r1 100 0 1k +r2 200 0 1k +r3 300 0 1k +r4 400 0 1k +r5 500 0 1k +* +* +* +.end + + + + + + diff --git a/src/xspice/examples/digital_models3.deck b/src/xspice/examples/digital_models3.deck new file mode 100755 index 000000000..b9173ece7 --- /dev/null +++ b/src/xspice/examples/digital_models3.deck @@ -0,0 +1,92 @@ +Code Model Test: d latch, set-reset latch, frequency divider +* +* +*** analysis type *** +.tran .01s 8s +* +*** input sources *** +* +vdata1 100 0 DC PWL( (0 0.0) (2 0.0) (2.0000000001 1.0) (3 1.0) ) +* +* +vdata2 200 0 DC PWL( (0 0.0) (1.0 0.0) (1.0000000001 1.0) (2 1.0) ++ (2.0000000001 0.0) (3 0.0) (3.0000000001 1.0) (4 1.0) ) +* +* +vclk 300 0 DC PWL( (0 0.0) (0.5 0.0) (0.50000000001 1.0) ++ (1.0 1.0) (1.00000000001 0.0) ++ (1.5 0.0) (1.50000000001 1.0) ++ (2.0 1.0) (2.00000000001 0.0) ++ (2.5 0.0) (2.50000000001 1.0) ++ (3.0 1.0) (3.00000000001 0.0) ++ (3.5 0.0) (3.50000000001 1.0) ++ (4.0 1.0) (4.00000000001 0.0) ++ (4.5 0.0) (4.50000000001 1.0) ++ (5.0 1.0) (5.00000000001 0.0) ++ (5.5 0.0) (5.50000000001 1.0) ++ (6.0 1.0) (6.00000000001 0.0) ++ (6.5 0.0) (6.50000000001 1.0) ++ (7.0 1.0) (7.00000000001 0.0) ++ (7.5 0.0) (7.50000000001 1.0) (4.0 1.0) ) +* +* +vset 400 0 DC 0.0 +* +* +vreset 500 0 DC PWL( (0 0.0) (3.8 0.0) (3.80000000001 1.0) (4 1.0) ) +* +* +*** adc_bridge block *** +aconverter [100 200 300 400 500] [1 2 3 4 5] adc_bridge1 +.model adc_bridge1 adc_bridge (in_low=0.1 in_high=0.9 ++ rise_delay=1.0e-12 fall_delay=1.0e-12) +* +* +* +*** d latch block *** +a1 1 3 4 5 10 11 d_dlatch1 +.model d_dlatch1 d_dlatch (data_delay=1.0e-6 enable_delay=2.0e-6 ++ set_delay=3.0e-6 reset_delay=4.0e-6 ++ ic=0 ++ rise_delay=5.0e-6 fall_delay=6.0e-6 ++ data_load=1.0e-12 enable_load=1.0e-12 ++ set_load=1.0e-12 reset_load=1.0e-12) +* +* +*** set-reset latch block *** +a2 1 2 3 4 5 20 21 d_srlatch1 +.model d_srlatch1 d_srlatch (sr_delay=1.0e-6 enable_delay=2.0e-6 ++ set_delay=3.0e-6 reset_delay=4.0e-6 ++ ic=0 ++ rise_delay=5.0e-6 fall_delay=6.0e-6 ++ sr_load=1.0e-12 enable_load=1.0e-12 ++ set_load=1.0e-12 reset_load=1.0e-12) +* +* +*** frequency divider block *** +a3 3 30 d_fdiv1 +.model d_fdiv1 d_fdiv (div_factor=3 high_cycles=2 ++ i_count=0 rise_delay=1.0e-6 fall_delay=2.0e-6 ++ freq_in_load=1.0e-12) +* +* +* +* +* +* +*** resistors to ground *** +r1 100 0 1k +r2 200 0 1k +r3 300 0 1k +r4 400 0 1k +r5 500 0 1k +* +* +* +.end + + + + + + diff --git a/src/xspice/examples/digital_models4.deck b/src/xspice/examples/digital_models4.deck new file mode 100755 index 000000000..8a328c922 --- /dev/null +++ b/src/xspice/examples/digital_models4.deck @@ -0,0 +1,91 @@ +Code Model Test: State Machine, RAM +* +* +*** analysis type *** +.tran .01s 8s +* +*** input sources *** +* +vdata1 100 0 DC PWL( (0 0.0) (2 0.0) (2.0000000001 1.0) (3 1.0) ++ (3.5000000001 0.0) (4 0.0) ) +* +* +vdata2 200 0 DC PWL( (0 0.0) (1.0 0.0) (1.0000000001 1.0) (2 1.0) ++ (2.0000000001 0.0) (3 0.0) (3.0000000001 1.0) (4 1.0) ) +* +* +vclk 300 0 DC PWL( (0 0.0) (0.5 0.0) (0.50000000001 1.0) ++ (1.0 1.0) (1.00000000001 0.0) ++ (1.5 0.0) (1.50000000001 1.0) ++ (2.0 1.0) (2.00000000001 0.0) ++ (2.5 0.0) (2.50000000001 1.0) ++ (3.0 1.0) (3.00000000001 0.0) ++ (3.5 0.0) (3.50000000001 1.0) ++ (4.0 1.0) (4.00000000001 0.0) ++ (4.5 0.0) (4.50000000001 1.0) ++ (5.0 1.0) (5.00000000001 0.0) ++ (5.5 0.0) (5.50000000001 1.0) ++ (6.0 1.0) (6.00000000001 0.0) ++ (6.5 0.0) (6.50000000001 1.0) ++ (7.0 1.0) (7.00000000001 0.0) ++ (7.5 0.0) (7.50000000001 1.0) (4.0 1.0) ) +* +vaddr1 400 0 DC 0 +* +* +vaddr2 500 0 DC PWL( (0 0.0) (0.6 0.0) (0.60000000001 1.0) ++ (0.9 1.0) (0.90000000001 0.0) ++ (2.6 0.0) (2.60000000001 1.0) ++ (2.9 1.0) (2.90000000001 0.0) (3.0 0.0) ) +* +* +* +vselect 600 0 DC PWL( (0 0.0) (1.0 0.0) (2.0000000001 1.0) (2 1.0) ) +* +* +* +* +* +*** adc_bridge block *** +aconverter [100 200 300 400 500 600] [1 2 3 4 5 6] adc_bridge1 +.model adc_bridge1 adc_bridge (in_low=0.1 in_high=0.9 ++ rise_delay=1.0e-12 fall_delay=1.0e-12) +* +* +* +*** state machine block *** +a1 [1 2] 3 4 [10 11] d_state1 +.model d_state1 d_state (clk_delay=1.0e-6 reset_delay=2.0e-6 ++ state_file=state.txt reset_state=0 ++ input_load=1.0e-12 clk_load=1.0e-12 ++ reset_load=1.0e-12) +* +* +*** RAM block *** +a2 [1 2] [20 21] [3 4] 5 [6] d_ram1 +.model d_ram1 d_ram (select_value=1 ic=0 ++ read_delay=1.0e-6 data_load=1.0e-12 ++ address_load=1.0e-12 select_load=1.0e-12 ++ enable_load=1.0e-12) +* +* +* +* +* +*** resistors to ground *** +r1 100 0 10k +r2 200 0 10k +r3 300 0 10k +r4 400 0 10k +r5 500 0 10k +r6 600 0 10k +* +* +* +.end + + + + + + diff --git a/src/xspice/examples/dot_model_ref.deck b/src/xspice/examples/dot_model_ref.deck new file mode 100755 index 000000000..159d144f8 --- /dev/null +++ b/src/xspice/examples/dot_model_ref.deck @@ -0,0 +1,19 @@ +Model card reference +* +* This circuit contains simple gain blocks that share a +* single .model card. +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +r1 1 0 1k +* +a1 1 2 gain_block +r2 2 0 1k +* +a2 1 3 gain_block +r3 3 0 1k +* +.model gain_block gain (in_offset = 1 gain=10) +* +.end diff --git a/src/xspice/examples/hybrid_models1_dc.deck b/src/xspice/examples/hybrid_models1_dc.deck new file mode 100755 index 000000000..190fb0be6 --- /dev/null +++ b/src/xspice/examples/hybrid_models1_dc.deck @@ -0,0 +1,46 @@ +Code Model Test - DC: d_osc, dac_bridge, adc_bridge +* +* +*** analysis type *** +.op +* +*** input sources *** +v1 1 0 DC 2 +* +v2 2 0 DC 2 +* +*** d_osc block *** +a1 1 10 d_osc1 +.model d_osc1 d_osc (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[100 100 1000 1000] ++ duty_cycle=0.5 init_phase=0.0 ++ rise_delay=1.0e-6 fall_delay=2.0e-6) +* +*** dac_bridge block *** +a2 [10] [20] dac_bridge1 +.model dac_bridge1 dac_bridge (out_low=0.5 out_high=4.5 out_undef=1.8 ++ input_load=1.0e-12 ++ t_rise=1.0e-6 t_fall=2.0e-6) +* +* +*** adc_bridge block *** +a3 [2] [30] adc_bridge1 +.model adc_bridge1 adc_bridge (in_low=0.7 in_high=2.4 ++ rise_delay=1.0e-12 fall_delay=2.0e-12) +* +* +* +*** resistors to ground *** +r1 1 0 1k +r2 2 0 1k +* +r20 20 0 1k +* +* +.end + + + + + + diff --git a/src/xspice/examples/hybrid_models1_transient.deck b/src/xspice/examples/hybrid_models1_transient.deck new file mode 100755 index 000000000..e09b15ad4 --- /dev/null +++ b/src/xspice/examples/hybrid_models1_transient.deck @@ -0,0 +1,48 @@ +Code Model Test - Transient: d_osc, dac_bridge, adc_bridge +* +* +*** analysis type *** +.tran .01s 1s +* +*** input sources *** +* +v1 1 0 DC PWL( (0 0.0) (1 1.0) ) +* +v2 2 0 DC PWL( (0 0.0) (1 5.0) ) +* +* +*** d_osc block *** +a1 1 10 d_osc1 +.model d_osc1 d_osc (cntl_array=[-1.0 0.0 1.0 2.0] ++ freq_array=[1.0 1.0 8.0 8.0] ++ duty_cycle=0.5 init_phase=0.0 ++ rise_delay=1.0e-6 fall_delay=2.0e-6) +* +*** dac_bridge block *** +a2 [10] [20] dac_bridge1 +.model dac_bridge1 dac_bridge (out_low=0.5 out_high=4.5 out_undef=1.8 ++ input_load=1.0e-12 ++ t_rise=1.0e-6 t_fall=2.0e-6) +* +* +*** adc_bridge block *** +a3 [2] [30] adc_bridge1 +.model adc_bridge1 adc_bridge (in_low=0.7 in_high=2.4 ++ rise_delay=1.0e-12 fall_delay=2.0e-12) +* +* +* +*** resistors to ground *** +r1 1 0 1k +r2 2 0 1k +* +r20 20 0 1k +* +* +.end + + + + + + diff --git a/src/xspice/examples/initial_conditions.deck b/src/xspice/examples/initial_conditions.deck new file mode 100755 index 000000000..610383625 --- /dev/null +++ b/src/xspice/examples/initial_conditions.deck @@ -0,0 +1,19 @@ +Capacitor and inductor with natural initial conditions +* +* This circuit contains a capacitor and an inductor with +* initial conditions on them. Each of the components +* has a parallel resistor so that an exponential decay +* of the initial condition occurs with a time constant of +* 1 second. +* +.tran 0.1 5 +* +a1 1 0 cap +.model cap capacitor (c=1000uf ic=1) +r1 1 0 1k +* +a2 2 0 ind +.model ind inductor (l=1H ic=1) +r2 2 0 1.0 +* +.end diff --git a/src/xspice/examples/io_ordering.deck b/src/xspice/examples/io_ordering.deck new file mode 100755 index 000000000..8cc245526 --- /dev/null +++ b/src/xspice/examples/io_ordering.deck @@ -0,0 +1,17 @@ +IO ordering +* +* This circuit contains a simple gain block. The order of +* the nodes listed on the instance line follows the order +* of the connections defined in the 'ifspec.ifs' file for +* the model. Refer to /atesse-su/src/cml/gain/ifspec.ifs . +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +r1 1 0 1k +* +a1 1 2 gain_block +.model gain_block gain (gain=10) +r2 2 0 1k +* +.end diff --git a/src/xspice/examples/io_types.deck b/src/xspice/examples/io_types.deck new file mode 100755 index 000000000..0d792ab73 --- /dev/null +++ b/src/xspice/examples/io_types.deck @@ -0,0 +1,34 @@ +IO types +* +* This circuit contains a mix of input output types including +* voltages, currents, digital signals, and user defined +* signals. +* +.tran 1e-6 1e-4 +* +v1 1 0 0.0 pulse(0 1 2e-5) +r1 1 0 1k +* +abridge1 [1] [enable] node_bridge1 +.model node_bridge1 adc_bridge +* +aclk [enable clk] clk nand +.model nand d_nand (rise_delay=1e-5 fall_delay=1e-5) +* +abridge2 clk enable real_node1 node_bridge2 +.model node_bridge2 d_to_real (zero=-1 one=1) +* +again real_node1 real_node2 times10 +.model times10 real_gain (gain=10) +* +abridge3 real_node2 analog_node node_bridge3 +.model node_bridge3 real_to_v +* +rout analog_node 0 1k +* +again %vnam v1 %i i_out gain_block +.model gain_block gain (gain=10) +ri_out i_out 0 1k +* +* +.end diff --git a/src/xspice/examples/long_names.deck b/src/xspice/examples/long_names.deck new file mode 100755 index 000000000..6a0fecada --- /dev/null +++ b/src/xspice/examples/long_names.deck @@ -0,0 +1,19 @@ +Long names +* +* This circuit contains a sine wave source followed by a +* gain block code model with a gain of 10. Long names +* are used for instances, models, and nodes. +* +.tran 1e-5 1e-3 +* +v1_123456789_123456789_1234 1 0 0.0 sin(0 1 2k) +* +r1_123456789_123456789_1234 1 0 1k +* +a1_123456789_123456789_1234 1 out_123456789_123456789_1234 ++ gain_block_123456789_123456789_1234 +* +.model gain_block_123456789_123456789_1234 gain (gain=10) +r2_123456789_123456789_1234 out_123456789_123456789_1234 0 1k +* +.end diff --git a/src/xspice/examples/mixed_case.deck b/src/xspice/examples/mixed_case.deck new file mode 100755 index 000000000..1a1006492 --- /dev/null +++ b/src/xspice/examples/mixed_case.deck @@ -0,0 +1,15 @@ +MiXeD CaSe +* +* This circuit contains a simple gain block to demonstrate +* that the simulator deck parsing code is case-insensitive. +* +.TrAn 1E-5 1e-3 +* +V1 1 0 0.0 sIn(0 1 1k) +r1 1 0 1k +* +A1 1 2 GaIn_BlOcK +.MODel gAiN_bLoCk GAin (gaIN=10) +r2 2 0 1K +* +.eNd diff --git a/src/xspice/examples/mixed_io_size.deck b/src/xspice/examples/mixed_io_size.deck new file mode 100755 index 000000000..824cdeb4b --- /dev/null +++ b/src/xspice/examples/mixed_io_size.deck @@ -0,0 +1,33 @@ +Mixed IO sizes +* +* This circuit contains a collection of digital and analog +* models with saclar and vector inputs of varying sizes. +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 pulse(0 1 1e-4) +r1 1 0 1k +* +v2 2 0 0.0 sin(0 1 2k) +r2 2 0 1k +* +abridge1 [1] [enable] atod +.model atod adc_bridge +* +aosc [enable clk] clk nand +.model nand d_nand (rise_delay=1e-4 fall_delay=1e-4) +* +ainv clk clk_bar inv +.model inv d_inverter (rise_delay=1e-5 fall_delay=1e-5) +* +adac [clk clk_bar] [3 4] dac +.model dac dac_bridge (t_rise=1e-5 t_fall=1e-5) +* +asum [1 2 3 4] 5 sum +.model sum summer +* +r3 3 0 1k +r4 4 0 1k +r5 5 0 1k +* +.end diff --git a/src/xspice/examples/mixed_mode.deck b/src/xspice/examples/mixed_mode.deck new file mode 100755 index 000000000..59e4745ba --- /dev/null +++ b/src/xspice/examples/mixed_mode.deck @@ -0,0 +1,98 @@ +Mixed IO types +* +* This circuit contains a mixture of IO types, including +* analog, digital, user-defined (real), and 'null'. +* +* The circuit demonstrates the use of the digital and +* user-defined node capability to model system-level designs +* such as sampled-data filters. The simulated circuit +* contains a digital oscillator enabled after 100us. The +* square wave oscillator output is divided by 8 with a +* ripple counter. The result is passed through a digital +* filter to convert it to a sine wave. +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 pulse(0 1 1e-4 1e-6) +r1 1 0 1k +* +abridge1 [1] [enable] atod +.model atod adc_bridge +* +aclk [enable clk] clk nand +.model nand d_nand (rise_delay=1e-5 fall_delay=1e-5) +* +adiv2 div2_out clk NULL NULL NULL div2_out dff +adiv4 div4_out div2_out NULL NULL NULL div4_out dff +adiv8 div8_out div4_out NULL NULL NULL div8_out dff +.model dff d_dff +* +abridge2 div8_out enable filt_in node_bridge2 +.model node_bridge2 d_to_real (zero=-1 one=1) +* +xfilter filt_in clk filt_out dig_filter +* +abridge3 filt_out a_out node_bridge3 +.model node_bridge3 real_to_v +* +rlpf1 a_out oa_minus 10k +* +xlpf 0 oa_minus lpf_out opamp +* +rlpf2 oa_minus lpf_out 10k +clpf lpf_out oa_minus 0.01uF +* +* +.subckt dig_filter filt_in clk filt_out +* +.model n0 real_gain (gain=1.0) +.model n1 real_gain (gain=2.0) +.model n2 real_gain (gain=1.0) +.model g1 real_gain (gain=0.125) +.model zm1 real_delay +.model d0a real_gain (gain=-0.75) +.model d1a real_gain (gain=0.5625) +.model d0b real_gain (gain=-0.3438) +.model d1b real_gain (gain=1.0) +* +an0a filt_in x0a n0 +an1a filt_in x1a n1 +an2a filt_in x2a n2 +* +az0a x0a clk x1a zm1 +az1a x1a clk x2a zm1 +* +ad0a x2a x0a d0a +ad1a x2a x1a d1a +* +az2a x2a filt1_out g1 +az3a filt1_out clk filt2_in zm1 +* +an0b filt2_in x0b n0 +an1b filt2_in x1b n1 +an2b filt2_in x2b n2 +* +az0b x0b clk x1b zm1 +az1b x1b clk x2b zm1 +* +ad0 x2b x0b d0b +ad1 x2b x1b d1b +* +az2b x2b clk filt_out zm1 +* +.ends dig_filter +* +* +.subckt opamp plus minus out +* +r1 plus minus 300k +a1 %vd (plus minus) outint lim +.model lim limit (out_lower_limit = -12 out_upper_limit = 12 ++ fraction = true limit_range = 0.2 gain=300e3) +r3 outint out 50.0 +r2 out 0 1e12 +* +.ends opamp +* +* +.end diff --git a/src/xspice/examples/mixed_ref.deck b/src/xspice/examples/mixed_ref.deck new file mode 100755 index 000000000..475e0f655 --- /dev/null +++ b/src/xspice/examples/mixed_ref.deck @@ -0,0 +1,41 @@ +Mixed references +* +* This circuit demonstrates the use of single-ended and +* differential inputs and outputs. +* +* Note that digital models reference a single node for +* their inputs and output (i.e. they are single-ended) +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 5k) +v2 2 0 0.0 sin(0 1 1k) +* +r1 1 0 1k +r2 2 0 1k +* +* +a1 %v 1 %i 10 times10 +r10 10 0 1k +* +* +a2 %vd (1 2) %id(11 12) times10 +r11 11 0 1k +r12 12 0 1k +r11_12 11 12 1.0 +* +* +r3 2 3 1k +a3 %i 3 %v 13 times10 +r13 13 0 1k +* +a4 [1] [digital_node1] adc +.model adc adc_bridge +* +a5 digital_node1 digital_node2 inv +.model inv d_inverter +* +* +.model times10 gain (gain=10) +* +.end diff --git a/src/xspice/examples/mosamp2.in b/src/xspice/examples/mosamp2.in new file mode 100755 index 000000000..d8029ad77 --- /dev/null +++ b/src/xspice/examples/mosamp2.in @@ -0,0 +1,42 @@ +mosamp2 - mos amplifier - transient +.options acct abstol=10n vntol=10n +.tran 0.1us 10us +m1 15 15 1 32 m w=88.9u l=25.4u +m2 1 1 2 32 m w=12.7u l=266.7u +m3 2 2 30 32 m w=88.9u l=25.4u +m4 15 5 4 32 m w=12.7u l=106.7u +m5 4 4 30 32 m w=88.9u l=12.7u +m6 15 15 5 32 m w=44.5u l=25.4u +m7 5 20 8 32 m w=482.6u l=12.7u +m8 8 2 30 32 m w=88.9u l=25.4u +m9 15 15 6 32 m w=44.5u l=25.4u +m10 6 21 8 32 m w=482.6u l=12.7u +m11 15 6 7 32 m w=12.7u l=106.7u +m12 7 4 30 32 m w=88.9u l=12.7u +m13 15 10 9 32 m w=139.7u l=12.7u +m14 9 11 30 32 m w=139.7u l=12.7u +m15 15 15 12 32 m w=12.7u l=207.8u +m16 12 12 11 32 m w=54.1u l=12.7u +m17 11 11 30 32 m w=54.1u l=12.7u +m18 15 15 10 32 m w=12.7u l=45.2u +m19 10 12 13 32 m w=270.5u l=12.7u +m20 13 7 30 32 m w=270.5u l=12.7u +m21 15 10 14 32 m w=254u l=12.7u +m22 14 11 30 32 m w=241.3u l=12.7u +m23 15 20 16 32 m w=19u l=38.1u +m24 16 14 30 32 m w=406.4u l=12.7u +m25 15 15 20 32 m w=38.1u l=42.7u +m26 20 16 30 32 m w=381u l=25.4u +m27 20 15 66 32 m w=22.9u l=7.6u +cc 7 9 40pf +cl 66 0 70pf +vin 21 0 pulse(0 5 1ns 1ns 1ns 5us 10us) +vccp 15 0 dc +15 +vddn 30 0 dc -15 +vb 32 0 dc -20 +.model m nmos(nsub=2.2e15 uo=575 ucrit=49k uexp=0.1 tox=0.11u xj=2.95u ++ level=2 cgso=1.5n cgdo=1.5n cbd=4.5f cbs=4.5f ld=2.4485u nss=3.2e10 ++ kp=2e-5 phi=0.6 ) +.print tran v(20) v(66) +.plot tran v(20) v(66) +.end diff --git a/src/xspice/examples/mosmem.in b/src/xspice/examples/mosmem.in new file mode 100755 index 000000000..c2803445b --- /dev/null +++ b/src/xspice/examples/mosmem.in @@ -0,0 +1,27 @@ +mosmem - mos memory cell +.width in=72 +.opt abstol=1u +.opt acct list node +.tran 20ns 2us +vdd 9 0 dc 5 +vs 7 0 pulse(2 0 520ns 20ns 20ns 500ns 2000ns) +vw 1 0 pulse(0 2 20ns 20ns 500ns 200ns) +vwb 2 0 pulse(2 0 20ns 20ns 20ns 2000ns 2000ns) +m1 3 1 0 0 mod w=250u l=5u +m2 4 2 0 0 mod w=250u l=5u +m3 9 9 3 0 mod w=5u l=5u +m4 9 9 4 0 mod w=5u l=5u +m5 5 7 3 0 mod w=50u l=5u +m6 6 7 4 0 mod w=50u l=5u +m7 5 6 0 0 mod w=250u l=5u +m8 6 5 0 0 mod w=250u l=5u +m9 9 9 5 0 mod w=5u l=5u +m10 9 9 6 0 mod w=5u l=5u +m11 8 4 0 0 mod w=250u l=5u +m12 9 9 8 0 mod w=5u l=5u +.model mod nmos(vto=0.5 phi=0.7 kp=1.0e-6 gamma=1.83 lambda=0.115 ++ level=1 cgso=1u cgdo=1u cbd=50p cbs=50p) +.print dc v(5) v(6) +.plot dc v(6) +.plot tran v(6) v(5) v(7) v(1) v(2) +.end diff --git a/src/xspice/examples/nco/Makefile b/src/xspice/examples/nco/Makefile new file mode 100755 index 000000000..d2d2af97e --- /dev/null +++ b/src/xspice/examples/nco/Makefile @@ -0,0 +1,40 @@ +# $Id$ +# +# Makefile for Code Model directories +# + +# Include global XSPICE selections for CC and other macros +include /usr/local/xspice-1-0/include/make.include + +INCLUDE = -I. -I$(ROOT)/include/sim + +CFLAGS = -g + +#----------------------------------------------------------------------------- +# Edit the following definition to specify the object files that comprise +# your code model. If your code model is completely specified in the +# cfunc.mod file, there is no need to edit this definition. +# DO NOT include the ifspec.o file. + +CODE_MODEL_OBJECTS = cfunc.o + +#----------------------------------------------------------------------------- +# DO NOT MODIFY THE FOLLOWING DEFINITIONS: + +.SUFFIXES: $(SUFFIXES) .mod .ifs + +.mod.c: + $(BINDIR)/cmpp -mod $< + +.ifs.c: + $(BINDIR)/cmpp -ifs + +.c.o: $*.c + ${CC} ${CFLAGS} ${INCLUDE} -c $*.c + +all : ifspec.o $(CODE_MODEL_OBJECTS) + +cfunc.o : cfunc.c +ifspec.o : ifspec.c + + diff --git a/src/xspice/examples/nco/cfunc.mod b/src/xspice/examples/nco/cfunc.mod new file mode 100755 index 000000000..993248863 --- /dev/null +++ b/src/xspice/examples/nco/cfunc.mod @@ -0,0 +1,90 @@ +/* $Id$ */ + +void *malloc(unsigned); + +#define OUT_STATE 0 +#define NXT_TIME 1 +#define NUM_NOTES 128 + + +/* A numerically controlled oscillator. Output frequencies */ +/* are determined according to the MIDI note number at input */ + +void ucm_nco (ARGS) +{ + + double *freq; + + int *output_state; + double *next_time; + + int i; + int index; + int scale_factor; + + double half_period; + + + if(INIT) { + + /* Setup storage for the toggled output state */ + output_state = (int *) cm_event_alloc(OUT_STATE, sizeof(int)); + next_time = (double *) cm_event_alloc(NXT_TIME, sizeof(double)); + + /* Allocate storage for frequencies */ + STATIC_VAR(freq) = malloc(NUM_NOTES * sizeof(double)); + freq = STATIC_VAR(freq); + + /* Initialize the frequency array */ + for(i = 0; i < NUM_NOTES; i++) { + if(i == 0) + freq[0] = 8.17578 * PARAM(mult_factor); + else + freq[i] = freq[i-1] * 1.059463094; + } + } + else { + + /* Get old output state */ + output_state = (int *) cm_event_get_ptr(OUT_STATE, 0); + next_time = (double *) cm_event_get_ptr(NXT_TIME, 0); + } + + + /* Convert the input bits to an integer */ + index = 0; + scale_factor = 64; + for(i = 0; i < 7; i++) { + if(INPUT_STATE(in[i]) == ONE) + index += scale_factor; + scale_factor /= 2; + } + + /* Look up the frequency and compute half its period */ + freq = STATIC_VAR(freq); + half_period = 1.0 / freq[index]; + + + /* Queue up events and output the new state */ + if(TIME == 0.0) { + *next_time = half_period; + cm_event_queue(*next_time); + OUTPUT_STATE(out) = *output_state; + } + else { + if(TIME == *next_time) { + *next_time = TIME + half_period; + cm_event_queue(*next_time); + *output_state = 1 - *output_state; + OUTPUT_STATE(out) = *output_state; + OUTPUT_DELAY(out) = PARAM(delay); + } + else + OUTPUT_CHANGED(out) = FALSE; + } + +} + + + + diff --git a/src/xspice/examples/nco/ifspec.ifs b/src/xspice/examples/nco/ifspec.ifs new file mode 100755 index 000000000..30cdba5d7 --- /dev/null +++ b/src/xspice/examples/nco/ifspec.ifs @@ -0,0 +1,38 @@ +/* $Id$ */ + +NAME_TABLE: + +Spice_Model_Name: nco +C_Function_Name: ucm_nco +Description: "A simple MIDI numerically controlled oscillator" + + +PORT_TABLE: + +Port_Name: in out +Description: "program input" "oscillator output" +Direction: in out +Default_Type: d d +Allowed_Types: [d] [d] +Vector: yes no +Vector_Bounds: [7 7] - +Null_Allowed: no no + + +PARAMETER_TABLE: + +Parameter_Name: delay mult_factor +Description: "output delay" "freq multiplier" +Data_Type: real real +Default_Value: 1e-9 1 +Limits: [1e-15 -] [1e-9 -] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + + +STATIC_VAR_TABLE: + +Static_Var_Name: freq +Data_Type: pointer +Description: "frequencies of notes" diff --git a/src/xspice/examples/param_defaults.deck b/src/xspice/examples/param_defaults.deck new file mode 100755 index 000000000..75e3542be --- /dev/null +++ b/src/xspice/examples/param_defaults.deck @@ -0,0 +1,16 @@ +Parameter defaults +* +* This circuit contains a code model with +* parameters of various types, which are all defaulted, +* and prints the default values. +* +.op +* +r1 1 0 1k +r2 2 0 1k +r3 1 2 1k +* +a1 [1 2] mod +.model mod print_param_types +* +.end diff --git a/src/xspice/examples/param_types.deck b/src/xspice/examples/param_types.deck new file mode 100755 index 000000000..84a43ef9e --- /dev/null +++ b/src/xspice/examples/param_types.deck @@ -0,0 +1,23 @@ +Parameter types +* +* This circuit contains a code model which accepts several +* parameters of various types and prints them. +* +.op +* +r1 1 0 1k +r2 2 0 1k +r3 1 2 1k +* +a1 [1 2] mod +.model mod print_param_types ++ integer=2 ++ real=3.0 ++ complex=<4.0 5.0> ++ string=six ++ integer_array=[7 8] ++ real_array=[9.0 10.0] ++ complex_array=[< 11.0 12.0 > < 13.0 14.0 >] ++ string_array=[fifteen sixteen] +* +.end diff --git a/src/xspice/examples/parsing.deck b/src/xspice/examples/parsing.deck new file mode 100755 index 000000000..b5de5f594 --- /dev/null +++ b/src/xspice/examples/parsing.deck @@ -0,0 +1,16 @@ +Parsing +* +* This circuit contains a simple gain block to demonstrate +* that the simulator parses the syntax used to reference +* code models. +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +r1 1 0 1k +* +a1 1 2 gain_block +.model gain_block gain (gain=10) +r2 2 0 1k +* +.end diff --git a/src/xspice/examples/polarity.deck b/src/xspice/examples/polarity.deck new file mode 100755 index 000000000..7488e182a --- /dev/null +++ b/src/xspice/examples/polarity.deck @@ -0,0 +1,26 @@ +Polarity of voltages and currents +* +* This circuit contains a set of gain blocks to evaluate +* the polarity of voltages and currents on code models +* +.tran 1e-5 1e-3 +* +v1 1 0 0.0 sin(0 1 1k) +* +r1 1 0 1k +* +* +a1 %v 1 %v 10 times10 +r10 10 0 1k +* +r1_2 1 2 1k +a2 %i 2 %v 11 times10 +r11 11 0 1k +* +a3 1 %i 12 times10 +r12 12 0 1k +* +* +.model times10 gain (gain=10) +* +.end diff --git a/src/xspice/examples/print_param_types/Makefile b/src/xspice/examples/print_param_types/Makefile new file mode 100755 index 000000000..d2d2af97e --- /dev/null +++ b/src/xspice/examples/print_param_types/Makefile @@ -0,0 +1,40 @@ +# $Id$ +# +# Makefile for Code Model directories +# + +# Include global XSPICE selections for CC and other macros +include /usr/local/xspice-1-0/include/make.include + +INCLUDE = -I. -I$(ROOT)/include/sim + +CFLAGS = -g + +#----------------------------------------------------------------------------- +# Edit the following definition to specify the object files that comprise +# your code model. If your code model is completely specified in the +# cfunc.mod file, there is no need to edit this definition. +# DO NOT include the ifspec.o file. + +CODE_MODEL_OBJECTS = cfunc.o + +#----------------------------------------------------------------------------- +# DO NOT MODIFY THE FOLLOWING DEFINITIONS: + +.SUFFIXES: $(SUFFIXES) .mod .ifs + +.mod.c: + $(BINDIR)/cmpp -mod $< + +.ifs.c: + $(BINDIR)/cmpp -ifs + +.c.o: $*.c + ${CC} ${CFLAGS} ${INCLUDE} -c $*.c + +all : ifspec.o $(CODE_MODEL_OBJECTS) + +cfunc.o : cfunc.c +ifspec.o : ifspec.c + + diff --git a/src/xspice/examples/print_param_types/cfunc.mod b/src/xspice/examples/print_param_types/cfunc.mod new file mode 100755 index 000000000..8cf7d02ca --- /dev/null +++ b/src/xspice/examples/print_param_types/cfunc.mod @@ -0,0 +1,33 @@ +/* $Id$ */ + +void ucm_print_param_types (ARGS) +{ + int i; + + if(INIT) { + /* Print scalar parameters */ + printf("\nScalar parameters\n\n"); + printf("integer = %d\n", PARAM(integer)); + printf("real = %e\n", PARAM(real)); + printf("complex = <%e %e>\n", PARAM(complex).real, + PARAM(complex).imag); + printf("string = %s\n", PARAM(string)); + + /* Print vector parameters */ + printf("\nVector parameters\n\n"); + for(i = 0; i < PARAM_SIZE(integer_array); i++) + printf("integer = %d\n", PARAM(integer_array[i])); + for(i = 0; i < PARAM_SIZE(real_array); i++) + printf("real = %e\n", PARAM(real_array[i])); + for(i = 0; i < PARAM_SIZE(complex_array); i++) + printf("complex = <%e %e>\n", PARAM(complex_array[i]).real, + PARAM(complex_array[i]).imag); + for(i = 0; i < PARAM_SIZE(string_array); i++) + printf("string = %s\n", PARAM(string_array[i])); + + } +} + + + + diff --git a/src/xspice/examples/print_param_types/ifspec.ifs b/src/xspice/examples/print_param_types/ifspec.ifs new file mode 100755 index 000000000..e836df007 --- /dev/null +++ b/src/xspice/examples/print_param_types/ifspec.ifs @@ -0,0 +1,112 @@ +/* $Id$ */ + +NAME_TABLE: + +Spice_Model_Name: print_param_types +C_Function_Name: ucm_print_param_types +Description: "ignores its input, but prints its parameters" + + +PORT_TABLE: + + +Port_Name: in +Description: "input" +Direction: in +Default_Type: v +Allowed_Types: [v,vd,i,id,vnam] +Vector: yes +Vector_Bounds: - +Null_Allowed: no + + + +PARAMETER_TABLE: + +Parameter_Name: integer +Description: "integer parameter" +Data_Type: int +Default_Value: 1 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: real +Description: "real parameter" +Data_Type: real +Default_Value: 1 +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: complex +Description: "complex parameter" +Data_Type: complex +Default_Value: <1.0, 1.0> +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: string +Description: "string parameter" +Data_Type: string +Default_Value: "one" +Limits: - +Vector: no +Vector_Bounds: - +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: integer_array +Description: "integer array parameter" +Data_Type: int +Default_Value: 1 +Limits: - +Vector: yes +Vector_Bounds: in +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: real_array +Description: "real array parameter" +Data_Type: real +Default_Value: 1 +Limits: - +Vector: yes +Vector_Bounds: in +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: complex_array +Description: "complex array parameter" +Data_Type: complex +Default_Value: <1.0 1.0> +Limits: - +Vector: yes +Vector_Bounds: in +Null_Allowed: yes + +PARAMETER_TABLE: + +Parameter_Name: string_array +Description: "string array parameter" +Data_Type: string +Default_Value: "one" +Limits: - +Vector: yes +Vector_Bounds: in +Null_Allowed: yes + + diff --git a/src/xspice/examples/rca3040.in b/src/xspice/examples/rca3040.in new file mode 100755 index 000000000..6b6e52048 --- /dev/null +++ b/src/xspice/examples/rca3040.in @@ -0,0 +1,33 @@ +rca3040 ckt - rca 3040 wideband amplifier +.ac dec 10 1 10ghz +.dc vin -0.25 0.25 0.005 +.tran 2.0ns 200ns +vin 1 0 sin(0 0.1 50meg 0.5ns) ac 1 +vcc 2 0 15.0 +vee 3 0 -15.0 +rs1 30 1 1k +rs2 31 0 1k +r1 5 3 4.8k +r2 6 3 4.8k +r3 9 3 811 +r4 8 3 2.17k +r5 8 0 820 +r6 2 14 1.32k +r7 2 12 4.5k +r8 2 15 1.32k +r9 16 0 5.25k +r10 17 0 5.25k +q1 2 30 5 qnl +q2 2 31 6 qnl +q3 10 5 7 qnl +q4 11 6 7 qnl +q5 14 12 10 qnl +q6 15 12 11 qnl +q7 12 12 13 qnl +q8 13 13 0 qnl +q9 7 8 9 qnl +q10 2 15 16 qnl +q11 2 14 17 qnl +.model qnl npn bf=80 rb=100 ccs=2pf tf=0.3ns tr=6ns cje=3pf ++ cjc=2pf va 50 +.end diff --git a/src/xspice/examples/real_delay/Makefile b/src/xspice/examples/real_delay/Makefile new file mode 100755 index 000000000..d2d2af97e --- /dev/null +++ b/src/xspice/examples/real_delay/Makefile @@ -0,0 +1,40 @@ +# $Id$ +# +# Makefile for Code Model directories +# + +# Include global XSPICE selections for CC and other macros +include /usr/local/xspice-1-0/include/make.include + +INCLUDE = -I. -I$(ROOT)/include/sim + +CFLAGS = -g + +#----------------------------------------------------------------------------- +# Edit the following definition to specify the object files that comprise +# your code model. If your code model is completely specified in the +# cfunc.mod file, there is no need to edit this definition. +# DO NOT include the ifspec.o file. + +CODE_MODEL_OBJECTS = cfunc.o + +#----------------------------------------------------------------------------- +# DO NOT MODIFY THE FOLLOWING DEFINITIONS: + +.SUFFIXES: $(SUFFIXES) .mod .ifs + +.mod.c: + $(BINDIR)/cmpp -mod $< + +.ifs.c: + $(BINDIR)/cmpp -ifs + +.c.o: $*.c + ${CC} ${CFLAGS} ${INCLUDE} -c $*.c + +all : ifspec.o $(CODE_MODEL_OBJECTS) + +cfunc.o : cfunc.c +ifspec.o : ifspec.c + + diff --git a/src/xspice/examples/real_delay/cfunc.mod b/src/xspice/examples/real_delay/cfunc.mod new file mode 100755 index 000000000..5f3c41cbe --- /dev/null +++ b/src/xspice/examples/real_delay/cfunc.mod @@ -0,0 +1,46 @@ +/* $Id$ */ + + +#define CLK_STATE 0 + + +void ucm_real_delay (ARGS) +{ + + double *in; + double *out; + + Digital_State_t *state; + Digital_State_t *old_state; + + + if(INIT) { + state = (void *) cm_event_alloc(CLK_STATE, sizeof(Digital_State_t)); + old_state = state; + *state = INPUT_STATE(clk); + } + else { + state = (void *) cm_event_get_ptr(CLK_STATE, 0); + old_state = (void *) cm_event_get_ptr(CLK_STATE, 1); + } + + if(ANALYSIS != TRANSIENT) + OUTPUT_CHANGED(out) = FALSE; + else { + *state = INPUT_STATE(clk); + if(*state == *old_state) + OUTPUT_CHANGED(out) = FALSE; + else if(*state != ONE) + OUTPUT_CHANGED(out) = FALSE; + else { + in = INPUT(in); + out = OUTPUT(out); + *out = *in; + OUTPUT_DELAY(out) = PARAM(delay); + } + } +} + + + + diff --git a/src/xspice/examples/real_delay/ifspec.ifs b/src/xspice/examples/real_delay/ifspec.ifs new file mode 100755 index 000000000..603bbee27 --- /dev/null +++ b/src/xspice/examples/real_delay/ifspec.ifs @@ -0,0 +1,33 @@ +/* $Id$ */ + +NAME_TABLE: + +Spice_Model_Name: real_delay +C_Function_Name: ucm_real_delay +Description: "A Z ** -1 block working on real data" + + +PORT_TABLE: + +Port_Name: in clk out +Description: "input" "clock" "output" +Direction: in in out +Default_Type: real d real +Allowed_Types: [real] [d] [real] +Vector: no no no +Vector_Bounds: - - - +Null_Allowed: no no no + + +PARAMETER_TABLE: + +Parameter_Name: delay +Description: "delay from clk to out" +Data_Type: real +Default_Value: 1e-9 +Limits: [1e-15 -] +Vector: no +Vector_Bounds: - +Null_Allowed: yes + + diff --git a/src/xspice/examples/real_gain/Makefile b/src/xspice/examples/real_gain/Makefile new file mode 100755 index 000000000..d2d2af97e --- /dev/null +++ b/src/xspice/examples/real_gain/Makefile @@ -0,0 +1,40 @@ +# $Id$ +# +# Makefile for Code Model directories +# + +# Include global XSPICE selections for CC and other macros +include /usr/local/xspice-1-0/include/make.include + +INCLUDE = -I. -I$(ROOT)/include/sim + +CFLAGS = -g + +#----------------------------------------------------------------------------- +# Edit the following definition to specify the object files that comprise +# your code model. If your code model is completely specified in the +# cfunc.mod file, there is no need to edit this definition. +# DO NOT include the ifspec.o file. + +CODE_MODEL_OBJECTS = cfunc.o + +#----------------------------------------------------------------------------- +# DO NOT MODIFY THE FOLLOWING DEFINITIONS: + +.SUFFIXES: $(SUFFIXES) .mod .ifs + +.mod.c: + $(BINDIR)/cmpp -mod $< + +.ifs.c: + $(BINDIR)/cmpp -ifs + +.c.o: $*.c + ${CC} ${CFLAGS} ${INCLUDE} -c $*.c + +all : ifspec.o $(CODE_MODEL_OBJECTS) + +cfunc.o : cfunc.c +ifspec.o : ifspec.c + + diff --git a/src/xspice/examples/real_gain/cfunc.mod b/src/xspice/examples/real_gain/cfunc.mod new file mode 100755 index 000000000..f6934ccc3 --- /dev/null +++ b/src/xspice/examples/real_gain/cfunc.mod @@ -0,0 +1,39 @@ +/* $Id$ */ + +void ucm_real_gain (ARGS) +{ + double *in; + double *out; + + double in_offset; + double gain; + double out_offset; + double delay; + double ic; + + + /* Get the input and output pointers */ + in = INPUT(in); + out = OUTPUT(out); + + /* Get the parameters */ + in_offset = PARAM(in_offset); + gain = PARAM(gain); + out_offset = PARAM(out_offset); + delay = PARAM(delay); + ic = PARAM(ic); + + + /* Assign the output and delay */ + if(ANALYSIS == DC) { + *out = ic; + if(INIT) + cm_event_queue(delay); + } + else { + *out = gain * (*in + in_offset) + out_offset; + OUTPUT_DELAY(out) = delay; + } +} + + diff --git a/src/xspice/examples/real_gain/ifspec.ifs b/src/xspice/examples/real_gain/ifspec.ifs new file mode 100755 index 000000000..886acfdcb --- /dev/null +++ b/src/xspice/examples/real_gain/ifspec.ifs @@ -0,0 +1,45 @@ +/* $Id$ */ + +NAME_TABLE: + +Spice_Model_Name: real_gain +C_Function_Name: ucm_real_gain +Description: "A gain block for event-driven real data" + + +PORT_TABLE: + +Port_Name: in out +Description: "input" "output" +Direction: in out +Default_Type: real real +Allowed_Types: [real] [real] +Vector: no no +Vector_Bounds: - - +Null_Allowed: no no + + +PARAMETER_TABLE: + +Parameter_Name: in_offset gain out_offset +Description: "input offset" "gain" "output offset" +Data_Type: real real real +Default_Value: 0.0 1.0 0.0 +Limits: - - - +Vector: no no no +Vector_Bounds: - - - +Null_Allowed: yes yes yes + + +PARAMETER_TABLE: + +Parameter_Name: delay ic +Description: "delay" "initial condition" +Data_Type: real real +Default_Value: 1.0e-9 0.0 +Limits: - - +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + + diff --git a/src/xspice/examples/real_to_v/Makefile b/src/xspice/examples/real_to_v/Makefile new file mode 100755 index 000000000..d2d2af97e --- /dev/null +++ b/src/xspice/examples/real_to_v/Makefile @@ -0,0 +1,40 @@ +# $Id$ +# +# Makefile for Code Model directories +# + +# Include global XSPICE selections for CC and other macros +include /usr/local/xspice-1-0/include/make.include + +INCLUDE = -I. -I$(ROOT)/include/sim + +CFLAGS = -g + +#----------------------------------------------------------------------------- +# Edit the following definition to specify the object files that comprise +# your code model. If your code model is completely specified in the +# cfunc.mod file, there is no need to edit this definition. +# DO NOT include the ifspec.o file. + +CODE_MODEL_OBJECTS = cfunc.o + +#----------------------------------------------------------------------------- +# DO NOT MODIFY THE FOLLOWING DEFINITIONS: + +.SUFFIXES: $(SUFFIXES) .mod .ifs + +.mod.c: + $(BINDIR)/cmpp -mod $< + +.ifs.c: + $(BINDIR)/cmpp -ifs + +.c.o: $*.c + ${CC} ${CFLAGS} ${INCLUDE} -c $*.c + +all : ifspec.o $(CODE_MODEL_OBJECTS) + +cfunc.o : cfunc.c +ifspec.o : ifspec.c + + diff --git a/src/xspice/examples/real_to_v/cfunc.mod b/src/xspice/examples/real_to_v/cfunc.mod new file mode 100755 index 000000000..f6b699391 --- /dev/null +++ b/src/xspice/examples/real_to_v/cfunc.mod @@ -0,0 +1,75 @@ +/* $Id$ */ + + +#define TS 0 +#define VS 1 + + +void ucm_real_to_v (ARGS) +{ + + double *t, *v; + double *in; + + double out; + + + in = INPUT(in); + + if(INIT) { + t = (void *) cm_event_alloc(TS, 2 * sizeof(double)); + v = (void *) cm_event_alloc(VS, 2 * sizeof(double)); + t[0] = -2.0; + t[1] = -1.0; + v[0] = *in; + v[1] = *in; + } + else { + t = (void *) cm_event_get_ptr(TS, 0); + v = (void *) cm_event_get_ptr(VS, 0); + } + + switch(CALL_TYPE) { + + case ANALOG: + if(TIME == 0.0) { + OUTPUT(out) = *in; + v[0] = *in; + v[1] = *in; + } + else { + if(TIME <= t[0]) + OUTPUT(out) = v[0]; + else if(TIME >= t[1]) + OUTPUT(out) = v[1]; + else { + OUTPUT(out) = v[0] + (v[1] - v[0]) * + (TIME - t[0]) / (t[1] - t[0]); + } + } + break; + + case EVENT: + if(TIME == 0.0) + return; + if(TIME >= t[1]) { + v[0] = v[1]; + v[1] = *in; + t[0] = TIME; + t[1] = TIME + PARAM(transition_time); + } + else { + v[0] = v[0] + (v[1] - v[0]) * + (TIME - t[0]) / (t[1] - t[0]); + v[1] = *in; + t[0] = TIME; + t[1] = TIME + PARAM(transition_time); + } + break; + + } +} + + + + diff --git a/src/xspice/examples/real_to_v/ifspec.ifs b/src/xspice/examples/real_to_v/ifspec.ifs new file mode 100755 index 000000000..9b06bf137 --- /dev/null +++ b/src/xspice/examples/real_to_v/ifspec.ifs @@ -0,0 +1,33 @@ +/* $Id$ */ + +NAME_TABLE: + +Spice_Model_Name: real_to_v +C_Function_Name: ucm_real_to_v +Description: "Node bridge from real to analog voltage" + + +PORT_TABLE: + +Port_Name: in out +Description: "input" "output" +Direction: in out +Default_Type: real v +Allowed_Types: [real] [v, vd, i, id] +Vector: no no +Vector_Bounds: - - +Null_Allowed: no no + + +PARAMETER_TABLE: + +Parameter_Name: gain transition_time +Description: "gain" "output transition time" +Data_Type: real real +Default_Value: 1.0 1e-9 +Limits: - [1e-15 -] +Vector: no no +Vector_Bounds: - - +Null_Allowed: yes yes + + diff --git a/src/xspice/examples/rtlinv.in b/src/xspice/examples/rtlinv.in new file mode 100755 index 000000000..8632a17e8 --- /dev/null +++ b/src/xspice/examples/rtlinv.in @@ -0,0 +1,20 @@ +rtlinv ckt - cascaded rtl inverters +.width in=72 +.opt acct list node lvlcod=2 +.dc vin 0.0 2.5 0.025 +.tran 2ns 200ns +vcc 6 0 5 +vin 1 0 pulse(0 5 2ns 2ns 2ns 80ns) +rb1 1 2 10k +rc1 6 3 1k +q1 3 2 0 qnd +rb2 3 4 10k +q2 5 4 0 qnd +rc2 6 5 1k +.model qnd npn(bf=50 rb=70 rc=40 ccs=2pf tf=0.1ns tr=10ns cje=0.9pf ++ cjc=1.5pf pc=0.85 va=50) +.print dc v(3) v(5) +.plot dc v(3) +.print tran v(3) v(5) +.plot tran v(3) v(5) v(1) +.end diff --git a/src/xspice/examples/schmitt.in b/src/xspice/examples/schmitt.in new file mode 100755 index 000000000..6a7732027 --- /dev/null +++ b/src/xspice/examples/schmitt.in @@ -0,0 +1,24 @@ +schmitt ckt - ecl compatible schmitt trigger +.width in=72 +.opt acct list node lvlcod=2 +.tran 10ns 1000ns +vin 1 0 pulse(-1.6 -1.2 10ns 400ns 400ns 100ns 10000ns) +vee 8 0 -5 +rin 1 2 50 +rc1 0 3 50 +r1 3 5 185 +r2 5 8 760 +rc2 0 6 100 +re 4 8 260 +rth1 7 8 125 +rth2 7 0 85 +cload 7 0 5pf +q1 3 2 4 qstd off +q2 6 5 4 qstd +q3 0 6 7 qstd +q4 0 6 7 qstd +.model qstd npn(is=1.0e-16 bf=50 br=0.1 rb=50 rc=10 tf=0.12ns tr=5ns ++ cje=0.4pf pe=0.8 me=0.4 cjc=0.5pf pc=0.8 mc=0.333 ccs=1pf va=50) +.print tran v(1) v(3) v(5) v(6) +.plot tran v(3) v(5) v(6) v(1) +.end diff --git a/src/xspice/examples/spice3.deck b/src/xspice/examples/spice3.deck new file mode 100755 index 000000000..1055c69b8 --- /dev/null +++ b/src/xspice/examples/spice3.deck @@ -0,0 +1,25 @@ +A Berkeley SPICE3 compatible circuit +* +* This circuit contains only Berkeley SPICE3 components. +* +* The circuit is an AC coupled transistor amplifier with +* a sinewave input at node "1", a gain of approximately -3.9, +* and output on node "coll". +* +.tran 1e-5 2e-3 +* +vcc vcc 0 12.0 +vin 1 0 0.0 ac 1.0 sin(0 1 1k) +* +ccouple 1 base 10uF +* +rbias1 vcc base 100k +rbias2 base 0 24k +* +q1 coll base emit generic +.model generic npn +* +rcollector vcc coll 3.9k +remitter emit 0 1k +* +.end diff --git a/src/xspice/examples/suffixes.deck b/src/xspice/examples/suffixes.deck new file mode 100755 index 000000000..81b9bec75 --- /dev/null +++ b/src/xspice/examples/suffixes.deck @@ -0,0 +1,25 @@ +Engineering suffixes +* +* This circuit contains a code model which accepts several +* parameters of various types and prints them. The values +* specified on the .model card use engineering suffixes on +* the numeric parameters. +* +.op +* +r1 1 0 1k +r2 2 0 1k +r3 1 2 1k +* +a1 [1 2] mod +.model mod print_param_types ++ integer=2k ++ real=3.0u ++ complex=< 4.0f 5.0mil > ++ string=six ++ integer_array=[7meg 8] ++ real_array=[9.0n 10.0p] ++ complex_array=[< 11.0t 12.0g > < 13.0m 14.0 >] ++ string_array=[fifteen sixteen] +* +.end diff --git a/src/xspice/examples/supply_ramping.deck b/src/xspice/examples/supply_ramping.deck new file mode 100755 index 000000000..c2efd8050 --- /dev/null +++ b/src/xspice/examples/supply_ramping.deck @@ -0,0 +1,30 @@ +Supply ramping option +* +* This circuit demonstrates the use of the option +* "ramptime" which ramps independent sources and the +* capacitor and inductor initial conditions from +* zero to their final value during the time period +* specified. +* +* +.tran 0.1 5 +.option ramptime=0.2 +* +a1 1 0 cap +.model cap capacitor (c=1000uf ic=1) +r1 1 0 1k +* +a2 2 0 ind +.model ind inductor (l=1H ic=1) +r2 2 0 1.0 +* +v1 3 0 1.0 +r3 3 0 1k +* +i1 4 0 1e-3 +r4 4 0 1k +* +v2 5 0 0.0 sin(0 1 0.3 0 0 45.0) +r5 5 0 1k +* +.end diff --git a/src/xspice/examples/user_defined_nodes.deck b/src/xspice/examples/user_defined_nodes.deck new file mode 100755 index 000000000..9fb9ca1a4 --- /dev/null +++ b/src/xspice/examples/user_defined_nodes.deck @@ -0,0 +1,30 @@ +User defined nodes +* +* This circuit contains a mix of node types including +* two 'real' type user-defined nodes and associated +* node bridges. +* +.tran 1e-6 1e-4 +* +v1 1 0 0.0 pulse(0 1 2e-5) +r1 1 0 1k +* +abridge1 [1] [enable] node_bridge1 +.model node_bridge1 adc_bridge +* +aclk [enable clk] clk nand +.model nand d_nand (rise_delay=1e-5 fall_delay=1e-5) +* +abridge2 clk enable real_node1 node_bridge2 +.model node_bridge2 d_to_real (zero=-1 one=1) +* +again real_node1 real_node2 times10 +.model times10 real_gain (gain=10) +* +abridge3 real_node2 analog_node node_bridge3 +.model node_bridge3 real_to_v +* +rout analog_node 0 1k +* +* +.end diff --git a/src/xspice/examples/xspice.deck b/src/xspice/examples/xspice.deck new file mode 100755 index 000000000..121ef20b8 --- /dev/null +++ b/src/xspice/examples/xspice.deck @@ -0,0 +1,22 @@ +A simple XSPICE amplifier circuit +* +* This uses an XSPICE "gain" code model to substitute for +* the transistor amplifier circuit in spice3.deck. +* +.tran 1e-5 2e-3 +* +vin 1 0 0.0 ac 1.0 sin(0 1 1k) +* +ccouple 1 in 10uF +* +* +rzin in 0 19.35k +* +aamp in coll gain_block +.model gain_block gain (gain = -3.9 out_offset = 7.003) +* +rzout out coll 3.9k +rbig coll 0 1e12 +* +* +.end diff --git a/src/xspice/icm/Makefile.am b/src/xspice/icm/Makefile.am new file mode 100755 index 000000000..fbdd5a786 --- /dev/null +++ b/src/xspice/icm/Makefile.am @@ -0,0 +1,194 @@ +## Process this file with automake to produce Makefile.in +# +# Hacked on 5.23.2003 by SDB + + +SUBDIRS = icm_spice2poly + +INCLUDES = -I$(top_srcdir)/src/include + +MAINTAINERCLEANFILES = Makefile.in + +EXTRA_DIST = README modpath.lst udnpath.lst spice2poly.cm dlmain.c + + +# Edit these to point to your Opus installation +CMPPDIR = $(top_srcdir)/src/xspice/cmpp +OPUS_INCLUDE_DIR = $(top_srcdir)/src/include + + +##------ automake definitions below. ------ +## + +## Yes, listing all this stuff is ugly, but I wasn't clever enough to +## find a better way to make it work. + +SYSTEM_LIBS = -lm -lncurses -L/usr/X11R6/lib -lX11 -lXt -lXext -lXmu -lXaw -lSM -lICE + +LOCAL_LIBS = -lcmxsp,-levtxsp,-lidnxsp,-lipcxsp,-lenhxsp,-lmifxsp,-ldev,-lckt,-linp,-lparser,-lhlp,-linp,-lderiv,-lcmaths,-lpoly,-lni,-lsparse,-lmisc,-lfte,-lplotting + +DEVICE_LIBS = -lasrc,-lbjt,-lbsim1,-lbsim2,-lbsim3,-lbsim3v1,-lbsim3v2,-lbsim4,-lcap,-lbsim3soipd,-lbsim3soifd,-lbsim3soidd,-lcccs,-lccvs,-lccvs,-lcpl,-lcsw,-ldio,-lind,-lisrc,-lhfet,-lhfet2,-ljfet,-ljfet2,-lltra,-lmes,-lmesa,-lmos1,-lmos2,-lmos3,-lmos6,-lmos9,-lres,-lsoi3,-lsw,-ltxl,-ltra,-lurc,-lvccs,-lvcvs,-lvsrc + + + +LOCAL_LIB_DIRS = \ + -L$(top_srcdir)/src/xspice/cm/ \ + -L$(top_srcdir)/src/xspice/enh/ \ + -L$(top_srcdir)/src/xspice/evt/ \ + -L$(top_srcdir)/src/xspice/idn/ \ + -L$(top_srcdir)/src/xspice/ipc/ \ + -L$(top_srcdir)/src/xspice/mif/ \ + -L$(top_srcdir)/src/spicelib/devices/ \ + -L$(top_srcdir)/src/spicelib/analysis/ \ + -L$(top_srcdir)/src/spicelib/parser/ \ + \ + -L$(top_srcdir)/src/frontend/parser/ \ + -L$(top_srcdir)/src/frontend/help/ \ + -L$(top_srcdir)/src/maths/deriv/ \ + -L$(top_srcdir)/src/maths/cmaths/ \ + -L$(top_srcdir)/src/maths/poly/ \ + -L$(top_srcdir)/src/maths/ni/ \ + -L$(top_srcdir)/src/maths/sparse/ \ + -L$(top_srcdir)/src/misc/ \ + \ + -L$(top_srcdir)/src/frontend/ \ + -L$(top_srcdir)/src/frontend/plotting/ \ + \ + -L$(top_srcdir)/src/spicelib/devices/asrc/ \ + -L$(top_srcdir)/src/spicelib/devices/bjt/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim1/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim2/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3v1/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3v2/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim4/ \ + -L$(top_srcdir)/src/spicelib/devices/cap/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3soi_pd/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3soi_fd/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3soi_dd/ \ + -L$(top_srcdir)/src/spicelib/devices/cccs/ \ + -L$(top_srcdir)/src/spicelib/devices/ccvs/ \ + -L$(top_srcdir)/src/spicelib/devices/ccvs/ \ + -L$(top_srcdir)/src/spicelib/devices/cpl/ \ + -L$(top_srcdir)/src/spicelib/devices/csw/ \ + -L$(top_srcdir)/src/spicelib/devices/dio/ \ + \ + -L$(top_srcdir)/src/spicelib/devices/ind/ \ + -L$(top_srcdir)/src/spicelib/devices/isrc/ \ + -L$(top_srcdir)/src/spicelib/devices/hfet1/ \ + -L$(top_srcdir)/src/spicelib/devices/hfet2/ \ + -L$(top_srcdir)/src/spicelib/devices/jfet/ \ + -L$(top_srcdir)/src/spicelib/devices/jfet2/ \ + -L$(top_srcdir)/src/spicelib/devices/ltra/ \ + -L$(top_srcdir)/src/spicelib/devices/mes/ \ + -L$(top_srcdir)/src/spicelib/devices/mesa/ \ + -L$(top_srcdir)/src/spicelib/devices/mos1/ \ + -L$(top_srcdir)/src/spicelib/devices/mos2/ \ + -L$(top_srcdir)/src/spicelib/devices/mos3/ \ + -L$(top_srcdir)/src/spicelib/devices/mos6/ \ + -L$(top_srcdir)/src/spicelib/devices/mos9/ \ + -L$(top_srcdir)/src/spicelib/devices/res/ \ + -L$(top_srcdir)/src/spicelib/devices/soi3/ \ + -L$(top_srcdir)/src/spicelib/devices/sw/ \ + -L$(top_srcdir)/src/spicelib/devices/txl/ \ + -L$(top_srcdir)/src/spicelib/devices/tra/ \ + -L$(top_srcdir)/src/spicelib/devices/urc/ \ + -L$(top_srcdir)/src/spicelib/devices/vccs/ \ + -L$(top_srcdir)/src/spicelib/devices/vcvs/ \ + -L$(top_srcdir)/src/spicelib/devices/vsrc/ + + +EXTRA_LIB_DIRS = \ + -L$(top_srcdir)/src/xspice/cm/.libs/ \ + -L$(top_srcdir)/src/xspice/enh/.libs/ \ + -L$(top_srcdir)/src/xspice/evt/.libs/ \ + -L$(top_srcdir)/src/xspice/idn/.libs/ \ + -L$(top_srcdir)/src/xspice/ipc/.libs/ \ + -L$(top_srcdir)/src/xspice/mif/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/.libs/ \ + -L$(top_srcdir)/src/spicelib/analysis/.libs/ \ + -L$(top_srcdir)/src/spicelib/parser/.libs/ \ + \ + -L$(top_srcdir)/src/frontend/parser/.libs/ \ + -L$(top_srcdir)/src/frontend/help/.libs/ \ + -L$(top_srcdir)/src/maths/deriv/.libs/ \ + -L$(top_srcdir)/src/maths/cmaths/.libs/ \ + -L$(top_srcdir)/src/maths/poly/.libs/ \ + -L$(top_srcdir)/src/maths/ni/.libs/ \ + -L$(top_srcdir)/src/maths/sparse/.libs \ + -L$(top_srcdir)/src/misc/.libs/ \ + \ + -L$(top_srcdir)/src/frontend/.libs/ \ + -L$(top_srcdir)/src/frontend/plotting/.libs/ \ + \ + -L$(top_srcdir)/src/spicelib/devices/asrc/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bjt/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim1/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim2/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3v1/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3v2/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim4/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/cap/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3soi_pd/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3soi_fd/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3soi_dd/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/cccs/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/ccvs/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/ccvs/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/cpl/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/csw/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/dio/.libs/ \ + \ + -L$(top_srcdir)/src/spicelib/devices/ind/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/isrc/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/hfet1/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/hfet2/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/jfet/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/jfet2/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/ltra/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/mes/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/mesa/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/mos1/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/mos2/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/mos3/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/mos6/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/mos9/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/res/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/soi3/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/sw/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/txl/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/tra/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/urc/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/vccs/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/vcvs/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/vsrc/.libs/ + +clean: clean-recursive + rm -f cmextrn.h cminfo.h udnextrn.h udninfo.h *.o + + +cmextrn.h: modpath.lst $(SUBDIRS)/cfunc.c $(SUBDIRS)/ifspec.c + $(CMPPDIR)/cmpp -lst + +cminfo.h: modpath.lst $(SUBDIRS)/cfunc.c $(SUBDIRS)/ifspec.c + $(CMPPDIR)/cmpp -lst + +udnextrn.h: udnpath.lst $(SUBDIRS)/cfunc.c $(SUBDIRS)/ifspec.c + $(CMPPDIR)/cmpp -lst + +udninfo.h: udnpath.lst $(SUBDIRS)/cfunc.c $(SUBDIRS)/ifspec.c + $(CMPPDIR)/cmpp -lst + +all: spice2poly.cm + +spice2poly.cm: $(SUBDIRS)/cfunc.o $(SUBDIRS)/ifspec.o dlmain.o + $(top_srcdir)/libtool --mode=link $(CC) -module -shared -fPIC -o spice2poly.cm \ + $(SUBDIRS)/cfunc.o $(SUBDIRS)/ifspec.o dlmain.o \ + $(SYSTEM_LIBS) $(LOCAL_LIB_DIRS) \ + -Wl,--start-group -Wl,$(LOCAL_LIBS) -Wl,--end-group -ldl \ + -Wl,--rpath,/usr/local/geda/lib/ng-spice-rework + +dlmain.o: dlmain.c cmextrn.h udnextrn.h cminfo.h udninfo.h + $(CC) -fPIC -I$(OPUS_INCLUDE_DIR) $(INCLUDES) $(CFLAGS) -DCM_LINUX -c dlmain.c + diff --git a/src/xspice/icm/README b/src/xspice/icm/README new file mode 100644 index 000000000..a969bb159 --- /dev/null +++ b/src/xspice/icm/README @@ -0,0 +1,52 @@ +This directory holds a codemodel which enables ngspice to handle SPICE +2 POLY attributes on controlled sources. In short, when a SPICE 2 +netlist is read in, any controlled sources with POLY attributes are +translated into codemodel devices with an associated .model which +invokes spice2poly to evaluate the polynomial. + +To use this model, you need to do the following: + +1. Compile the rest of ngspice/tclspice in the usual way from the +base directory. Make sure you do configure --enable-xspice when +configuring. + +2. Download and install SPICE Opus (available from +http://www.fe.uni-lj.si/). From this you need the program cmpp, as +well as some of the include files. + +3. Edit the Makefile in this directory and make the variable CMPPDIR +point to the base location of your Opus installation. (Hint: I +place it in /usr/local/opus.) + +4. Edit the Makefile in the directory below (icm_spice2poly/) and +make the variable CMPDIR point to the base location of your Opus +installation. + +5. Do "make" in this directory. The makefiles are set up +to do all the necessary stuff to turn the spice2poly sources into a +shared object named spice2poly.cm which you can load into ngspice. +(Alternatively, you can do "make codemodels" from $(top_srcdir); I +have included codemodels as a target which cd's into this directory +and does "make".) + +6. Read the codemodel into ngspice in the following way: + +ngspice 1 -> codemodel /usr/local/src/tclspice-0.2.10/src/xspice/icm/spice2poly.cm + +(Of course, you should point to the location where *you* built +spice2poly.cm!) You might want to put this invocation into your +spinit file (which usually lives in $(top_srcdir)/src/). + +7. Then read in your SPICE netlist. SPICE 2 POLY attributes in +controlled sources will be translated into .models invoking the +spice2poly codemodel. You should be able to run ngspice and simulate +in the usual way! + +---------------------------------------------------------------------- +Please send any comments/questions/bug reports to: + +Stuart Brorson +sdb@cloud9.net + +-- SDB 6.19.2003 + diff --git a/src/xspice/icm/dlinfo.h b/src/xspice/icm/dlinfo.h new file mode 100644 index 000000000..f79261781 --- /dev/null +++ b/src/xspice/icm/dlinfo.h @@ -0,0 +1,9 @@ +////////////////////////////////////////////////////////////////////////////// +// Modify these to include general information about the library +////////////////////////////////////////////////////////////////////////////// +char inf_Title[]="icm_poly.cm"; +char inf_Version[]="0.1"; +char inf_Description[]="Code model implementation of SPICE2 poly."; +char inf_Author[]="Hacked together by SDB, June 2003."; +char inf_Copyright[]="Released under GPL"; + diff --git a/src/xspice/icm/dlmain.c b/src/xspice/icm/dlmain.c new file mode 100644 index 000000000..9472aab6c --- /dev/null +++ b/src/xspice/icm/dlmain.c @@ -0,0 +1,408 @@ +////////////////////////////////////////////////////////////////////////////// +// Build cmextrn.h, cminfo.h, udnextrn.h and udninfo.h from udnpath.lst and +// modpath.lst using 'cmpp -lst'. Then compile this file and link it with +// cm and udn object files to produce a dll that can be loaded by the +// spice opus simulator at run-time. +// +// Author: Arpad Buermen +////////////////////////////////////////////////////////////////////////////// +#include "dlinfo.h" + +#include +#include +#include "port.h" +#include "misc.h" +#include "cmextrn.h" +#include "udnextrn.h" +#include "dllitf.h" + +// This one is automatically set by the compiler +char inf_Date[]=__DATE__; + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// Do not modify anything below this line +////////////////////////////////////////////////////////////////////////////// + +SPICEdev *cmDEVices[] = { +#include "cminfo.h" + NULL +}; + +int cmDEVicesCNT = sizeof(cmDEVices)/sizeof(SPICEdev *)-1; + +Evt_Udn_Info_t *cmEVTudns[] = { +#include "udninfo.h" + NULL +}; + +int cmEVTudnCNT = sizeof(cmEVTudns)/sizeof(Evt_Udn_Info_t *)-1; + +// Pointer to core info structure containing pointers to core functions. +struct coreInfo_t *coreitf; + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// Functions that return pointers to structures. +////////////////////////////////////////////////////////////////////////////// +#ifdef CM_WINDOWS +#undef CM_EXPORT +#define CM_EXPORT __declspec(dllexport) +#endif + +#ifdef CM_LINUX +#undef CM_EXPORT +#define CM_EXPORT extern +#endif + +int cmopusvers = 2; + +// This one checks the opus dynamic link version. +CM_EXPORT void *CMsysvers() { + return (void *)&cmopusvers; +} + +// This one returns the title +CM_EXPORT void *CMtitle() { + return (void *)inf_Title; +} + +// This one returns the version +CM_EXPORT void *CMversion() { + return (void *)inf_Version; +} + +// This one returns the date +CM_EXPORT void *CMdate() { + return (void *)inf_Date; +} + +// This one returns the description +CM_EXPORT void *CMdescription() { + return (void *)inf_Description; +} + +// This one returns the author +CM_EXPORT void *CMauthor() { + return (void *)inf_Author; +} + +// This one returns the copyright +CM_EXPORT void *CMcopyright() { + return (void *)inf_Copyright; +} + +// This one returns the device table +CM_EXPORT void *CMdevs() { + return (void *)cmDEVices; +} + +// This one returns the device count +CM_EXPORT void *CMdevNum() { + return (void *)&cmDEVicesCNT; +} + +// This one returns the UDN table +CM_EXPORT void *CMudns() { + return (void *)cmEVTudns; +} + +// This one returns the UDN count +CM_EXPORT void *CMudnNum() { + return (void *)&cmEVTudnCNT; +} + +// This one returns the pointer to the pointer to the core interface structure +CM_EXPORT void *CMgetCoreItfPtr() { + return (void *)(&coreitf); +} + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +// These functions call the real core functions of SPICE OPUS using the +// pointers in coreitf structure. +////////////////////////////////////////////////////////////////////////////// +void MIF_INP2A( + GENERIC *ckt, /* circuit structure to put mod/inst structs in */ + INPtables *tab, /* symbol table for node names, etc. */ + card *current /* the card we are to parse */ + ) { + (coreitf->dllitf_MIF_INP2A)(ckt,tab,current); +} + +char * MIFgetMod( + GENERIC *ckt, + char **name, + INPmodel **model, + INPtables *tab + ) { + return (coreitf->dllitf_MIFgetMod)(ckt,name,model,tab); +} + +IFvalue * MIFgetValue( + GENERIC *ckt, + char **line, + int type, + INPtables *tab, + char **err + ) { + return (coreitf->dllitf_MIFgetValue)(ckt,line,type,tab,err); +} + + +int MIFsetup( + SMPmatrix *matrix, + GENmodel *inModel, + CKTcircuit *ckt, + int *state + ) { + return (coreitf->dllitf_MIFsetup)(matrix,inModel,ckt,state); +} + +int MIFunsetup( + GENmodel *inModel, + CKTcircuit *ckt +) { + return (coreitf->dllitf_MIFunsetup)(inModel,ckt); +} + +int MIFload( + GENmodel *inModel, + CKTcircuit *ckt + ) { + return (coreitf->dllitf_MIFload)(inModel,ckt); +} + + +int MIFmParam( + int param_index, + IFvalue *value, + GENmodel *inModel + ) { + return (coreitf->dllitf_MIFmParam)(param_index,value,inModel); +} + +int MIFask( + CKTcircuit *ckt, + GENinstance *inst, + int param_index, + IFvalue *value, + IFvalue *select + ) { + return (coreitf->dllitf_MIFask)(ckt,inst,param_index,value,select); +} + +int MIFmAsk( + CKTcircuit *ckt, + GENmodel *inModel, + int param_index, + IFvalue *value + ) { + return (coreitf->dllitf_MIFmAsk)(ckt,inModel,param_index,value); +} + +int MIFtrunc( + GENmodel *inModel, + CKTcircuit *ckt, + double *timeStep + ) { + return (coreitf->dllitf_MIFtrunc)(inModel,ckt,timeStep); +} + +int MIFconvTest( + GENmodel *inModel, + CKTcircuit *ckt + ) { + return (coreitf->dllitf_MIFconvTest)(inModel,ckt); +} + +int MIFdelete( + GENmodel *inModel, + IFuid name, + GENinstance **inst + ) { + return (coreitf->dllitf_MIFdelete)(inModel,name,inst); +} + +int MIFmDelete( + GENmodel **inModel, + IFuid modname, + GENmodel *model + ) { + return (coreitf->dllitf_MIFmDelete)(inModel,modname,model); +} + +void MIFdestroy( + GENmodel **inModel + ) { + (coreitf->dllitf_MIFdestroy)(inModel); +} + +char *MIFgettok( + char **s + ) { + return (coreitf->dllitf_MIFgettok)(s); +} + + +char *MIFget_token( + char **s, + Mif_Token_Type_t *type + ) { + return (coreitf->dllitf_MIFget_token)(s,type); +} + + +Mif_Cntl_Src_Type_t MIFget_cntl_src_type( + Mif_Port_Type_t in_port_type, + Mif_Port_Type_t out_port_type + ) { + return (coreitf->dllitf_MIFget_cntl_src_type)(in_port_type,out_port_type); +} + +char *MIFcopy(char *c) { + return (coreitf->dllitf_MIFcopy)(c); +} + + +void cm_climit_fcn(double in, double in_offset, double cntl_upper, + double cntl_lower, double lower_delta, + double upper_delta, double limit_range, + double gain, int percent, double *out_final, + double *pout_pin_final, double *pout_pcntl_lower_final, + double *pout_pcntl_upper_final) { + (coreitf->dllitf_cm_climit_fcn)(in,in_offset,cntl_upper,cntl_lower,lower_delta, + upper_delta,limit_range,gain,percent,out_final, + pout_pin_final,pout_pcntl_lower_final, + pout_pcntl_upper_final); +} + + + +void cm_smooth_corner(double x_input, double x_center, double y_center, + double domain, double lower_slope, double upper_slope, + double *y_output, double *dy_dx) { + (coreitf->dllitf_cm_smooth_corner)(x_input,x_center,y_center,domain,lower_slope, + upper_slope,y_output,dy_dx); +} + +void cm_smooth_discontinuity(double x_input, double x_lower, double y_lower, + double x_upper, double y_upper, + double *y_output, double *dy_dx) { + (coreitf->dllitf_cm_smooth_discontinuity)(x_input,x_lower,y_lower,x_upper,y_upper, + y_output,dy_dx); +} + +double cm_smooth_pwl(double x_input, double *x, double *y, int size, + double input_domain, double *dout_din) { + return (coreitf->dllitf_cm_smooth_pwl)(x_input,x,y,size,input_domain,dout_din); +} + +double cm_analog_ramp_factor(void) { + return (coreitf->dllitf_cm_analog_ramp_factor)(); +} + +void *cm_analog_alloc(int tag, int bytes) { + return (coreitf->dllitf_cm_analog_alloc)(tag,bytes); +} + +void *cm_analog_get_ptr(int tag, int timepoint) { + return (coreitf->dllitf_cm_analog_get_ptr)(tag,timepoint); +} + +int cm_analog_integrate(double integrand, double *integral, double *partial) { + return (coreitf->dllitf_cm_analog_integrate)(integrand,integral,partial); +} + +int cm_analog_converge(double *state) { + return (coreitf->dllitf_cm_analog_converge)(state); +} + +int cm_analog_set_temp_bkpt(double time) { + return (coreitf->dllitf_cm_analog_set_temp_bkpt)(time); +} + +int cm_analog_set_perm_bkpt(double time) { + return (coreitf->dllitf_cm_analog_set_perm_bkpt)(time); +} + +void cm_analog_not_converged(void) { + (coreitf->dllitf_cm_analog_not_converged)(); +} + +void cm_analog_auto_partial(void) { + (coreitf->dllitf_cm_analog_auto_partial)(); +} + +void *cm_event_alloc(int tag, int bytes){ + return (coreitf->dllitf_cm_event_alloc)(tag,bytes); +} + +void *cm_event_get_ptr(int tag, int timepoint) { + return (coreitf->dllitf_cm_event_get_ptr)(tag,timepoint); +} + +int cm_event_queue(double time) { + return (coreitf->dllitf_cm_event_queue)(time); +} + +char *cm_message_get_errmsg(void) { + return (coreitf->dllitf_cm_message_get_errmsg)(); +} + +int cm_message_send(char *msg) { + return (coreitf->dllitf_cm_message_send)(msg); +} + +double cm_netlist_get_c(void) { + return (coreitf->dllitf_cm_netlist_get_c)(); +} + +double cm_netlist_get_l(void) { + return (coreitf->dllitf_cm_netlist_get_l)(); +} + +Complex_t cm_complex_set(double real, double imag) { + return (coreitf->dllitf_cm_complex_set)(real,imag); +} + +Complex_t cm_complex_add(Complex_t x, Complex_t y) { + return (coreitf->dllitf_cm_complex_add)(x,y); +} + +Complex_t cm_complex_subtract(Complex_t x, Complex_t y) { + return (coreitf->dllitf_cm_complex_subtract)(x,y); +} + +Complex_t cm_complex_multiply(Complex_t x, Complex_t y) { + return (coreitf->dllitf_cm_complex_multiply)(x,y); +} + +Complex_t cm_complex_divide(Complex_t x, Complex_t y) { + return (coreitf->dllitf_cm_complex_divide)(x,y); +} + +FILE * cm_stream_out(void) { + return (coreitf->dllitf_cm_stream_out)(); +} + +FILE * cm_stream_in(void) { + return (coreitf->dllitf_cm_stream_in)(); +} + +FILE * cm_stream_err(void) { + return (coreitf->dllitf_cm_stream_err)(); +} + +char * tmalloc_internal(size_t s, int clean, const char *f, int l, int sw) { + return (coreitf->dllitf_tmalloc)(s,clean,f,l,sw); +} + +char * trealloc_internal(char *ptr, size_t s, const char *f, int l, int sw) { + return (coreitf->dllitf_trealloc)(ptr,s,f,l,sw); +} + +void tfree_internal(char *ptr, const char *f, int l, int sw) { + (coreitf->dllitf_tfree)(ptr,f,l,sw); +} diff --git a/src/xspice/icm/icm_spice2poly/Makefile.am b/src/xspice/icm/icm_spice2poly/Makefile.am new file mode 100644 index 000000000..8c7ad1e4c --- /dev/null +++ b/src/xspice/icm/icm_spice2poly/Makefile.am @@ -0,0 +1,193 @@ +## Process this file with automake to produce Makefile.in +# +# Makefile.am hand-crafted by SDB on 5.25.2003 to create POLY +# codemodel shared object. To enable simulation of POLY controlled sources, +# do the following: +# + +# Edit this to point to cmpp in your installation. +CMPPDIR = $(top_srcdir)/src/xspice/cmpp + +##------ automake definitions below. ------ +## + +## Yes, listing all this stuff is ugly, but I wasn't clever enough to +## find a better way to make it work. + +SYSTEM_LIBS = -lm -lncurses -L/usr/X11R6/lib -lX11 -lXt -lXext -lXmu -lXaw -lSM -lICE + +LOCAL_LIBS = -lcmxsp,-levtxsp,-lidnxsp,-lipcxsp,-lenhxsp,-lmifxsp,-ldev,-lckt,-linp,-lparser,-lhlp,-linp,-lderiv,-lcmaths,-lpoly,-lni,-lsparse,-lmisc,-lfte,-lplotting + +DEVICE_LIBS = -lasrc,-lbjt,-lbsim1,-lbsim2,-lbsim3,-lbsim3v1,-lbsim3v2,-lbsim4,-lcap,-lbsim3soipd,-lbsim3soifd,-lbsim3soidd,-lcccs,-lccvs,-lccvs,-lcpl,-lcsw,-ldio,-lind,-lisrc,-lhfet,-lhfet2,-ljfet,-ljfet2,-lltra,-lmes,-lmesa,-lmos1,-lmos2,-lmos3,-lmos6,-lmos9,-lres,-lsoi3,-lsw,-ltxl,-ltra,-lurc,-lvccs,-lvcvs,-lvsrc + + +LOCAL_LIB_DIRS = \ + -L$(top_srcdir)/src/xspice/cm/ \ + -L$(top_srcdir)/src/xspice/enh/ \ + -L$(top_srcdir)/src/xspice/evt/ \ + -L$(top_srcdir)/src/xspice/idn/ \ + -L$(top_srcdir)/src/xspice/ipc/ \ + -L$(top_srcdir)/src/xspice/mif/ \ + -L$(top_srcdir)/src/spicelib/devices/ \ + -L$(top_srcdir)/src/spicelib/analysis/ \ + -L$(top_srcdir)/src/spicelib/parser/ \ + \ + -L$(top_srcdir)/src/frontend/parser/ \ + -L$(top_srcdir)/src/frontend/help/ \ + -L$(top_srcdir)/src/maths/deriv/ \ + -L$(top_srcdir)/src/maths/cmaths/ \ + -L$(top_srcdir)/src/maths/poly/ \ + -L$(top_srcdir)/src/maths/ni/ \ + -L$(top_srcdir)/src/maths/sparse/ \ + -L$(top_srcdir)/src/misc/ \ + \ + -L$(top_srcdir)/src/frontend/ \ + -L$(top_srcdir)/src/frontend/plotting/ \ + \ + -L$(top_srcdir)/src/spicelib/devices/asrc/ \ + -L$(top_srcdir)/src/spicelib/devices/bjt/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim1/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim2/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3v1/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3v2/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim4/ \ + -L$(top_srcdir)/src/spicelib/devices/cap/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3soi_pd/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3soi_fd/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3soi_dd/ \ + -L$(top_srcdir)/src/spicelib/devices/cccs/ \ + -L$(top_srcdir)/src/spicelib/devices/ccvs/ \ + -L$(top_srcdir)/src/spicelib/devices/ccvs/ \ + -L$(top_srcdir)/src/spicelib/devices/cpl/ \ + -L$(top_srcdir)/src/spicelib/devices/csw/ \ + -L$(top_srcdir)/src/spicelib/devices/dio/ \ + \ + -L$(top_srcdir)/src/spicelib/devices/ind/ \ + -L$(top_srcdir)/src/spicelib/devices/isrc/ \ + -L$(top_srcdir)/src/spicelib/devices/hfet1/ \ + -L$(top_srcdir)/src/spicelib/devices/hfet2/ \ + -L$(top_srcdir)/src/spicelib/devices/jfet/ \ + -L$(top_srcdir)/src/spicelib/devices/jfet2/ \ + -L$(top_srcdir)/src/spicelib/devices/ltra/ \ + -L$(top_srcdir)/src/spicelib/devices/mes/ \ + -L$(top_srcdir)/src/spicelib/devices/mesa/ \ + -L$(top_srcdir)/src/spicelib/devices/mos1/ \ + -L$(top_srcdir)/src/spicelib/devices/mos2/ \ + -L$(top_srcdir)/src/spicelib/devices/mos3/ \ + -L$(top_srcdir)/src/spicelib/devices/mos6/ \ + -L$(top_srcdir)/src/spicelib/devices/mos9/ \ + -L$(top_srcdir)/src/spicelib/devices/res/ \ + -L$(top_srcdir)/src/spicelib/devices/soi3/ \ + -L$(top_srcdir)/src/spicelib/devices/sw/ \ + -L$(top_srcdir)/src/spicelib/devices/txl/ \ + -L$(top_srcdir)/src/spicelib/devices/tra/ \ + -L$(top_srcdir)/src/spicelib/devices/urc/ \ + -L$(top_srcdir)/src/spicelib/devices/vccs/ \ + -L$(top_srcdir)/src/spicelib/devices/vcvs/ \ + -L$(top_srcdir)/src/spicelib/devices/vsrc/ + + +EXTRA_LIB_DIRS = \ + -L$(top_srcdir)/src/xspice/cm/.libs/ \ + -L$(top_srcdir)/src/xspice/enh/.libs/ \ + -L$(top_srcdir)/src/xspice/evt/.libs/ \ + -L$(top_srcdir)/src/xspice/idn/.libs/ \ + -L$(top_srcdir)/src/xspice/ipc/.libs/ \ + -L$(top_srcdir)/src/xspice/mif/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/.libs/ \ + -L$(top_srcdir)/src/spicelib/analysis/.libs/ \ + -L$(top_srcdir)/src/spicelib/parser/.libs/ \ + \ + -L$(top_srcdir)/src/frontend/parser/.libs/ \ + -L$(top_srcdir)/src/frontend/help/.libs/ \ + -L$(top_srcdir)/src/maths/deriv/.libs/ \ + -L$(top_srcdir)/src/maths/cmaths/.libs/ \ + -L$(top_srcdir)/src/maths/poly/.libs/ \ + -L$(top_srcdir)/src/maths/ni/.libs/ \ + -L$(top_srcdir)/src/maths/sparse/.libs \ + -L$(top_srcdir)/src/misc/.libs/ \ + \ + -L$(top_srcdir)/src/frontend/.libs/ \ + -L$(top_srcdir)/src/frontend/plotting/.libs/ \ + \ + -L$(top_srcdir)/src/spicelib/devices/asrc/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bjt/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim1/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim2/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3v1/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3v2/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim4/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/cap/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3soi_pd/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3soi_fd/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/bsim3soi_dd/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/cccs/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/ccvs/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/ccvs/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/cpl/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/csw/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/dio/.libs/ \ + \ + -L$(top_srcdir)/src/spicelib/devices/ind/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/isrc/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/hfet1/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/hfet2/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/jfet/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/jfet2/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/ltra/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/mes/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/mesa/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/mos1/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/mos2/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/mos3/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/mos6/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/mos9/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/res/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/soi3/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/sw/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/txl/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/tra/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/urc/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/vccs/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/vcvs/.libs/ \ + -L$(top_srcdir)/src/spicelib/devices/vsrc/.libs/ + + +INCLUDES = -I$(top_srcdir)/src/include -I$(top_srcdir) + +EXTRA_DIST = cfunc.mod ifspec.ifs README cfunc.c ifspec.c README + +MAINTAINERCLEANFILES = Makefile.in + +##------ Make targets listed below. ------ + +all: cfunc.o ifspec.o + +clean: + rm -f *.o *.cm *.la + rm -fR .libs + +cfunc.o: cfunc.c + $(CC) -fPIC $(INCLUDES) $(CFLAGS) -c cfunc.c + +ifspec.o: ifspec.c + $(CC) -fPIC $(INCLUDES) $(CFLAGS) -c ifspec.c + +# Don't use the below targets unless you know what you are doing! +# +# Hint: if you create ifspec.c using cmpp, you need to replace MIFunsetup +# with NULL in ifspec.c before making ifspec.o (compiling with gcc) because +# MIFunsetup is apparently unimplemented. +# +#cclean: clean +# rm -f cfunc.c ifspec.c +# +#cfunc.c: cfunc.mod +# $(CMPPDIR)/cmpp -mod cfunc.mod +# +#ifspec.c: ifspec.ifs +# $(CMPPDIR)/cmpp -ifs + + diff --git a/src/xspice/icm/icm_spice2poly/README b/src/xspice/icm/icm_spice2poly/README new file mode 100644 index 000000000..c850128a6 --- /dev/null +++ b/src/xspice/icm/icm_spice2poly/README @@ -0,0 +1,49 @@ +This directory holds a codemodel which enables ngspice to handle SPICE +2 POLY attributes on controlled sources. In short, when a SPICE 2 +netlist is read in, any controlled sources with POLY attributes are +translated into codemodel devices with an associated .model which +invokes spice2poly to evaluate the polynomial. + +To use this model, you need to do the following: + +1. Compile the rest of ngspice/tclspice in the usual way from the +base directory. + +2. Download and install SPICE Opus (available from +http://www.fe.uni-lj.si/ ). From this you need the program cmpp, as +well as some of the include files. + +3. Edit the Makefile in this directory and make the variable CMPPDIR +point to the base location of your Opus installation. + +4. Edit the Makefile in the directory above (..) and make the +variable CMPDIR point to the base location of your Opus installation. + +5. Do "make" in the directory above (..). The makefiles are set up +to do all the necessary stuff to turn the spice2poly sources into a +shared object named spice2poly.cm which you can load into ngspice. +(Alternatively, you can do "make codemodels" from $(top_srcdir); I +have included codemodels as a target which cd's into the directory +below and does "make".) + +6. Read the codemodel into ngspice in the following way: + +ngspice 1 -> codemodel /usr/local/src/tclspice-0.2.10/src/xspice/icm/spice2poly.cm + +(Of course, you should point to the location where *you* built +spice2poly.cm!) You might want to put this invocation into your +spinit file (which usually lives in $(top_srcdir)/src/). + +7. Then read in your SPICE netlist. SPICE 2 POLY attributes in +controlled sources will be translated into .models invoking the +spice2poly codemodel. You should be able to run ngspice and simulate +in the usual way! + +---------------------------------------------------------------------- +Please send any comments/questions/bug reports to: + +Stuart Brorson +sdb@cloud9.net + +-- SDB 6.19.2003 + diff --git a/src/xspice/icm/icm_spice2poly/cfunc.c b/src/xspice/icm/icm_spice2poly/cfunc.c new file mode 100644 index 000000000..6afbb77ba --- /dev/null +++ b/src/xspice/icm/icm_spice2poly/cfunc.c @@ -0,0 +1,305 @@ +#line 1 "cfunc.mod" +#include "cm.h" +#line 1 "cfunc.mod" +/* =========================================================================== +FILE cfunc.mod + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the definition of a code model polynomial controlled + source compatible with SPICE 2G6 poly sources. + +INTERFACES + + spice2poly() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +/* + +This code model implements the non-linear polynomial controlled sources +available in SPICE 2G6. An automatic translator added into the simulator +front end is used to map 2G6 syntax into a call to this model in the +required syntax. + +This model may also be called directly as follows: + + a1 [ ] xxx + .model xxx spice2poly ( coef = [ ] ) + +Refer to the 2G6 User Guide for an explanation of the coefficients. + + +This model is patterned after the FORTRAN code used in the 2G6 simulator. +Function cm_poly() below performs the functions of subroutines NLCSRC and +EVPOLY. Function evterm() performs the function of subroutine EVTERM, +and function nxtpwr() performs the function of subroutine NXTPWR. + +*/ + + + + +void *malloc(int); +void free(void *); + +/* SPICE 2G6 type utility functions */ +static double evterm(double x, int n); +static void nxtpwr(int *pwrseq, int pdim); + + + + +void spice2poly (Mif_Private_t *private) +{ + int num_inputs; /* Number of inputs to model */ + int num_coefs; /* Number of coefficients */ + int *exp; /* List of exponents in products */ + /* One for each input */ + + int i; /* Counter */ + int j; /* Counter */ + int k; /* Counter */ + + double *in; /* Values of inputs to model */ + double *coef; /* Values of coefficients */ + + double sum; /* Temporary for accumulating sum of terms */ + double product; /* Temporary for accumulating product */ + + double *acgains; /* Static variable holding AC gains for AC analysis */ + + + /* Get number of input values */ + + num_inputs = private->conn[0]->size; + + /* If this is the first call to the model, allocate the static variable */ + /* array */ + + if(private->circuit.init) { + acgains = malloc(num_inputs * sizeof(double)); + for(i = 0; i < num_inputs; i++) + acgains[i] = 0.0; + private->inst_var[0]->element[0].pvalue = acgains; + } + else + acgains = private->inst_var[0]->element[0].pvalue; + + /* If analysis type is AC, use the previously computed DC partials */ + /* for the AC gains */ + + if(private->circuit.anal_type == MIF_AC) { + for(i = 0; i < num_inputs; i++) { + acgains = private->inst_var[0]->element[0].pvalue; + private->conn[1]->port[0]->ac_gain[0].port[i].real = acgains[i]; + private->conn[1]->port[0]->ac_gain[0].port[i].imag = 0.0; + } + return; + } + + /* Get input values and coefficients to local storage for faster access */ + + in = malloc(num_inputs * sizeof(double)); + for(i = 0; i < num_inputs; i++) + in[i] = private->conn[0]->port[i]->input.rvalue; + + num_coefs = private->param[0]->size; + + coef = malloc(num_coefs * sizeof(double)); + for(i = 0; i < num_coefs; i++) + coef[i] = private->param[0]->element[i].rvalue; + + + /* Allocate the array of exponents used in computing the poly terms */ + exp = malloc(num_inputs * sizeof(int)); + + /* Initialize the exponents to zeros */ + for(i = 0; i < num_inputs; i++) + exp[i] = 0; + + + /* Compute the output of the source by summing the required products */ + for(i = 1, sum = coef[0]; i < num_coefs; i++) { + + /* Get the list of powers for the product terms in this term of the sum */ + nxtpwr(exp, num_inputs); + + /* Form the product of the inputs taken to the required powers */ + for(j = 0, product = 1.0; j < num_inputs; j++) + product *= evterm(in[j], exp[j]); + + /* Add the product times the appropriate coefficient into the sum */ + sum += coef[i] * product; + } + private->conn[1]->port[0]->output.rvalue = sum; + + + /* Compute and output the partials for each input */ + for(i = 0; i < num_inputs; i++) { + + /* Reinitialize the exponent list to zeros */ + for(j = 0; j < num_inputs; j++) + exp[j] = 0; + + /* Compute the partials by summing the required products */ + for(j = 1, sum = 0.0; j < num_coefs; j++) { + + /* Get the list of powers for the product terms in this term of the sum */ + nxtpwr(exp, num_inputs); + + /* If power for input for which partial is being evaluated */ + /* is zero, the term is a constant, so the partial is zero */ + if(exp[i] == 0) + continue; + + /* Form the product of the inputs taken to the required powers */ + for(k = 0, product = 1.0; k < num_inputs; k++) { + /* If input is not the one for which the partial is being taken */ + /* take the term to the specified exponent */ + if(k != i) + product *= evterm(in[k], exp[k]); + /* else, take the derivative of this term as n*x**(n-1) */ + else + product *= exp[k] * evterm(in[k], exp[k] - 1); + } + + /* Add the product times the appropriate coefficient into the sum */ + sum += coef[j] * product; + } + + private->conn[1]->port[0]->partial[0].port[i] = sum; + + /* If this is DC analysis, save the partial for use as AC gain */ + /* value in an AC analysis */ + + if(private->circuit.anal_type == MIF_DC) + acgains[i] = sum; + } + + /* Free the allocated items and return */ + free(in); + free(coef); + free(exp); + + return; +} + + +/* Function evterm computes the value of x**n */ + +static double evterm( + double x, + int n) +{ + double product; /* Temporary accumlator for forming the product */ + + product = 1.0; + while(n > 0) { + product *= x; + n--; + } + + return(product); +} + + + +/* + +This function is a literal translation of subroutine NXTPWR in SPICE 2G6. +This was done to guarantee compatibility with the ordering of +coefficients used by 2G6. The 2G6 User Guide does not completely define +the algorithm used and the GOTO loaded FORTRAN code is difficult to unravel. +Therefore, a one-to-one translation was deemed the safest approach. + +No attempt is made to document the function statements since no documentaton +is available in the 2G6 code. However, it can be noted that the code +appears to generate the exponents of the product terms in the sum-of-products +produced by the following expansion for two and three dimensional polynomials: + + 2D (a + b) ** n + 3D (a + (b + c)) ** n + +where n begins at 1 and increments as needed for as many terms as there are +coefficients on the polynomial source SPICE deck card, and where terms that +are identical under the laws of associativity are dropped. Thus, for example, +the exponents for the following sums are produced: + + 2D a + b + a**2 + ab + b**2 + c**3 + ... + 3D a + b + c + a**2 + a*b + a*c + b**2 + bc + c**2 + a**3 + ... + +*/ + +/* Define a macro to tranlate between FORTRAN-style array references */ +/* and C-style array references */ + +#define PWRSEQ(x) pwrseq[x - 1] + + +static void nxtpwr( + int *pwrseq, /* Array of exponents */ + int pdim) +{ + int i; + int k; + int km1; + int psum; + + if(pdim == 1) goto stmt80; + k = pdim; +stmt10: if(PWRSEQ(k) != 0) goto stmt20; + k = k - 1; + if(k != 0) goto stmt10; + goto stmt80; +stmt20: if(k == pdim) goto stmt30; + PWRSEQ(k) = PWRSEQ(k) - 1; + PWRSEQ(k+1) = PWRSEQ(k+1) + 1; + goto stmt100; +stmt30: km1 = k - 1; + for(i = 1; i <= km1; i++) + if(PWRSEQ(i) != 0) goto stmt50; +stmt40: PWRSEQ(1) = PWRSEQ(pdim) + 1; + PWRSEQ(pdim) = 0; + goto stmt100; +stmt50: psum = 1; + k = pdim; +stmt60: if(PWRSEQ(k-1) >= 1) goto stmt70; + psum = psum + PWRSEQ(k); + PWRSEQ(k) = 0; + k = k - 1; + goto stmt60; +stmt70: PWRSEQ(k) = PWRSEQ(k) + psum; + PWRSEQ(k-1) = PWRSEQ(k-1) - 1; + goto stmt100; +stmt80: PWRSEQ(1) = PWRSEQ(1) + 1; + +stmt100: return; + +} + diff --git a/src/xspice/icm/icm_spice2poly/cfunc.mod b/src/xspice/icm/icm_spice2poly/cfunc.mod new file mode 100644 index 000000000..4bf354e20 --- /dev/null +++ b/src/xspice/icm/icm_spice2poly/cfunc.mod @@ -0,0 +1,302 @@ +/* =========================================================================== +FILE cfunc.mod + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the definition of a code model polynomial controlled + source compatible with SPICE 2G6 poly sources. + +INTERFACES + + spice2poly() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +/* + +This code model implements the non-linear polynomial controlled sources +available in SPICE 2G6. An automatic translator added into the simulator +front end is used to map 2G6 syntax into a call to this model in the +required syntax. + +This model may also be called directly as follows: + + a1 [ ] xxx + .model xxx spice2poly ( coef = [ ] ) + +Refer to the 2G6 User Guide for an explanation of the coefficients. + + +This model is patterned after the FORTRAN code used in the 2G6 simulator. +Function cm_poly() below performs the functions of subroutines NLCSRC and +EVPOLY. Function evterm() performs the function of subroutine EVTERM, +and function nxtpwr() performs the function of subroutine NXTPWR. + +*/ + + + + +void *malloc(int); +void free(void *); + +/* SPICE 2G6 type utility functions */ +static double evterm(double x, int n); +static void nxtpwr(int *pwrseq, int pdim); + + + + +void spice2poly (ARGS) +{ + int num_inputs; /* Number of inputs to model */ + int num_coefs; /* Number of coefficients */ + int *exp; /* List of exponents in products */ + /* One for each input */ + + int i; /* Counter */ + int j; /* Counter */ + int k; /* Counter */ + + double *in; /* Values of inputs to model */ + double *coef; /* Values of coefficients */ + + double sum; /* Temporary for accumulating sum of terms */ + double product; /* Temporary for accumulating product */ + + double *acgains; /* Static variable holding AC gains for AC analysis */ + + + /* Get number of input values */ + + num_inputs = PORT_SIZE(in); + + /* If this is the first call to the model, allocate the static variable */ + /* array */ + + if(INIT) { + acgains = malloc(num_inputs * sizeof(double)); + for(i = 0; i < num_inputs; i++) + acgains[i] = 0.0; + STATIC_VAR(acgains) = acgains; + } + else + acgains = STATIC_VAR(acgains); + + /* If analysis type is AC, use the previously computed DC partials */ + /* for the AC gains */ + + if(ANALYSIS == MIF_AC) { + for(i = 0; i < num_inputs; i++) { + acgains = STATIC_VAR(acgains); + AC_GAIN(out,in[i]).real = acgains[i]; + AC_GAIN(out,in[i]).imag = 0.0; + } + return; + } + + /* Get input values and coefficients to local storage for faster access */ + + in = malloc(num_inputs * sizeof(double)); + for(i = 0; i < num_inputs; i++) + in[i] = INPUT(in[i]); + + num_coefs = PARAM_SIZE(coef); + + coef = malloc(num_coefs * sizeof(double)); + for(i = 0; i < num_coefs; i++) + coef[i] = PARAM(coef[i]); + + + /* Allocate the array of exponents used in computing the poly terms */ + exp = malloc(num_inputs * sizeof(int)); + + /* Initialize the exponents to zeros */ + for(i = 0; i < num_inputs; i++) + exp[i] = 0; + + + /* Compute the output of the source by summing the required products */ + for(i = 1, sum = coef[0]; i < num_coefs; i++) { + + /* Get the list of powers for the product terms in this term of the sum */ + nxtpwr(exp, num_inputs); + + /* Form the product of the inputs taken to the required powers */ + for(j = 0, product = 1.0; j < num_inputs; j++) + product *= evterm(in[j], exp[j]); + + /* Add the product times the appropriate coefficient into the sum */ + sum += coef[i] * product; + } + OUTPUT(out) = sum; + + + /* Compute and output the partials for each input */ + for(i = 0; i < num_inputs; i++) { + + /* Reinitialize the exponent list to zeros */ + for(j = 0; j < num_inputs; j++) + exp[j] = 0; + + /* Compute the partials by summing the required products */ + for(j = 1, sum = 0.0; j < num_coefs; j++) { + + /* Get the list of powers for the product terms in this term of the sum */ + nxtpwr(exp, num_inputs); + + /* If power for input for which partial is being evaluated */ + /* is zero, the term is a constant, so the partial is zero */ + if(exp[i] == 0) + continue; + + /* Form the product of the inputs taken to the required powers */ + for(k = 0, product = 1.0; k < num_inputs; k++) { + /* If input is not the one for which the partial is being taken */ + /* take the term to the specified exponent */ + if(k != i) + product *= evterm(in[k], exp[k]); + /* else, take the derivative of this term as n*x**(n-1) */ + else + product *= exp[k] * evterm(in[k], exp[k] - 1); + } + + /* Add the product times the appropriate coefficient into the sum */ + sum += coef[j] * product; + } + + PARTIAL(out,in[i]) = sum; + + /* If this is DC analysis, save the partial for use as AC gain */ + /* value in an AC analysis */ + + if(ANALYSIS == MIF_DC) + acgains[i] = sum; + } + + /* Free the allocated items and return */ + free(in); + free(coef); + free(exp); + + return; +} + + +/* Function evterm computes the value of x**n */ + +static double evterm( + double x, + int n) +{ + double product; /* Temporary accumlator for forming the product */ + + product = 1.0; + while(n > 0) { + product *= x; + n--; + } + + return(product); +} + + + +/* + +This function is a literal translation of subroutine NXTPWR in SPICE 2G6. +This was done to guarantee compatibility with the ordering of +coefficients used by 2G6. The 2G6 User Guide does not completely define +the algorithm used and the GOTO loaded FORTRAN code is difficult to unravel. +Therefore, a one-to-one translation was deemed the safest approach. + +No attempt is made to document the function statements since no documentaton +is available in the 2G6 code. However, it can be noted that the code +appears to generate the exponents of the product terms in the sum-of-products +produced by the following expansion for two and three dimensional polynomials: + + 2D (a + b) ** n + 3D (a + (b + c)) ** n + +where n begins at 1 and increments as needed for as many terms as there are +coefficients on the polynomial source SPICE deck card, and where terms that +are identical under the laws of associativity are dropped. Thus, for example, +the exponents for the following sums are produced: + + 2D a + b + a**2 + ab + b**2 + c**3 + ... + 3D a + b + c + a**2 + a*b + a*c + b**2 + bc + c**2 + a**3 + ... + +*/ + +/* Define a macro to tranlate between FORTRAN-style array references */ +/* and C-style array references */ + +#define PWRSEQ(x) pwrseq[x - 1] + + +static void nxtpwr( + int *pwrseq, /* Array of exponents */ + int pdim) +{ + int i; + int k; + int km1; + int psum; + + if(pdim == 1) goto stmt80; + k = pdim; +stmt10: if(PWRSEQ(k) != 0) goto stmt20; + k = k - 1; + if(k != 0) goto stmt10; + goto stmt80; +stmt20: if(k == pdim) goto stmt30; + PWRSEQ(k) = PWRSEQ(k) - 1; + PWRSEQ(k+1) = PWRSEQ(k+1) + 1; + goto stmt100; +stmt30: km1 = k - 1; + for(i = 1; i <= km1; i++) + if(PWRSEQ(i) != 0) goto stmt50; +stmt40: PWRSEQ(1) = PWRSEQ(pdim) + 1; + PWRSEQ(pdim) = 0; + goto stmt100; +stmt50: psum = 1; + k = pdim; +stmt60: if(PWRSEQ(k-1) >= 1) goto stmt70; + psum = psum + PWRSEQ(k); + PWRSEQ(k) = 0; + k = k - 1; + goto stmt60; +stmt70: PWRSEQ(k) = PWRSEQ(k) + psum; + PWRSEQ(k-1) = PWRSEQ(k-1) - 1; + goto stmt100; +stmt80: PWRSEQ(1) = PWRSEQ(1) + 1; + +stmt100: return; + +} + diff --git a/src/xspice/icm/icm_spice2poly/ifspec.c b/src/xspice/icm/icm_spice2poly/ifspec.c new file mode 100644 index 000000000..016e00fde --- /dev/null +++ b/src/xspice/icm/icm_spice2poly/ifspec.c @@ -0,0 +1,187 @@ + +/* + * Structures for model: spice2poly + * + * Automatically generated by cmpp preprocessor + * + * !!! DO NOT EDIT !!! + * + */ + + +// #include "prefix.h" +#include +#include "ngspice.h" +#include "devdefs.h" +#include "ifsim.h" +#include "mifdefs.h" +#include "mifproto.h" +#include "mifparse.h" +// #include "suffix.h" + + +static IFparm MIFmPTable[] = { + IOP("coef", 0, (IF_REAL|IF_VECTOR), "2g6 compatible spice card coefficient list"), +}; + + +static IFparm MIFpTable[] = { + OP("acgains", 1, IF_STRING, "partial derivatives from dc analysis used for ac gains"), +}; + + +static Mif_Port_Type_t MIFportEnum0[] = { + MIF_DIFF_VOLTAGE, + MIF_DIFF_CURRENT, + MIF_VSOURCE_CURRENT, +}; + + +static char *MIFportStr0[] = { + "vd", + "id", + "vnam", +}; + + +static Mif_Port_Type_t MIFportEnum1[] = { + MIF_DIFF_VOLTAGE, + MIF_DIFF_CURRENT, +}; + + +static char *MIFportStr1[] = { + "vd", + "id", +}; + + +static Mif_Conn_Info_t MIFconnTable[] = { + { + "in", + "input", + MIF_IN, + MIF_DIFF_VOLTAGE, + "vd", + 3, + MIFportEnum0, + MIFportStr0, + MIF_TRUE, + MIF_TRUE, + 1, + MIF_FALSE, + 0, + MIF_FALSE, + }, + { + "out", + "output", + MIF_OUT, + MIF_DIFF_VOLTAGE, + "vd", + 2, + MIFportEnum1, + MIFportStr1, + MIF_FALSE, + MIF_FALSE, + 0, + MIF_FALSE, + 0, + MIF_FALSE, + }, +}; + + +static Mif_Param_Info_t MIFparamTable[] = { + { + "coef", + "2g6 compatible spice card coefficient list", + MIF_REAL, + MIF_FALSE, + {MIF_FALSE, 0, 0.0, {0.0, 0.0}, NULL}, + MIF_FALSE, + {MIF_FALSE, 0, 0.0, {0.0, 0.0}, NULL}, + MIF_FALSE, + {MIF_FALSE, 0, 0.0, {0.0, 0.0}, NULL}, + MIF_TRUE, + MIF_FALSE, + 0, + MIF_TRUE, + 2, + MIF_FALSE, + 0, + MIF_FALSE, + }, +}; + + +static Mif_Inst_Var_Info_t MIFinst_varTable[] = { + { + "acgains", + "partial derivatives from dc analysis used for ac gains", + MIF_STRING, + MIF_FALSE, + }, +}; + + +extern void spice2poly(Mif_Private_t *); + +static int val_terms = 0; +static int val_numNames = 0; +static int val_numInstanceParms = 1; +static int val_numModelParms = 1; +static int val_sizeofMIFinstance = sizeof(MIFinstance); +static int val_sizeofMIFmodel = sizeof(MIFmodel); + +SPICEdev spice2poly_info = { + { "spice2poly", + "2g6 compatible polynomial controlled source", + &val_terms, + &val_numNames, + NULL, + &val_numInstanceParms, + MIFpTable, + &val_numModelParms, + MIFmPTable, + spice2poly, + 2, + MIFconnTable, + 1, + MIFparamTable, + 1, + MIFinst_varTable, + }, +NULL, +MIFmParam, +MIFload, +MIFsetup, + NULL, /* removed by sdb MIFunsetup, */ +NULL, +NULL, +MIFtrunc, +NULL, +MIFload, +NULL, +MIFdestroy, +MIFmDelete, +MIFdelete, +NULL, +MIFask, +MIFmAsk, +NULL, +MIFconvTest, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +&val_sizeofMIFinstance, +&val_sizeofMIFmodel, + +}; + diff --git a/src/xspice/icm/icm_spice2poly/ifspec.ifs b/src/xspice/icm/icm_spice2poly/ifspec.ifs new file mode 100644 index 000000000..33da52236 --- /dev/null +++ b/src/xspice/icm/icm_spice2poly/ifspec.ifs @@ -0,0 +1,75 @@ +/* =========================================================================== +FILE ifspec.ifs + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the definition of a code model polynomial controlled + source compatible with SPICE 2G6 poly sources. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +NAME_TABLE: + +Spice_Model_Name: spice2poly +C_Function_Name: spice2poly +Description: "2G6 compatible polynomial controlled source" + + +PORT_TABLE: + +Port_Name: in out +Description: "input" "output" +Direction: in out +Default_Type: vd vd +Allowed_Types: [vd,id,vnam] [vd,id] +Vector: yes no +Vector_Bounds: [1 -] - +Null_Allowed: no no + + +PARAMETER_TABLE: + +Parameter_Name: coef +Description: "2G6 compatible spice card coefficient list" +Data_Type: real +Default_Value: - +Limits: - +Vector: yes +Vector_Bounds: [2 -] +Null_Allowed: no + + +STATIC_VAR_TABLE: + +Static_Var_Name: acgains +Data_Type: pointer +Description: "Partial derivatives from DC analysis used for AC gains" diff --git a/src/xspice/icm/icm_spice2poly/make.bat b/src/xspice/icm/icm_spice2poly/make.bat new file mode 100644 index 000000000..19a341b81 --- /dev/null +++ b/src/xspice/icm/icm_spice2poly/make.bat @@ -0,0 +1,3 @@ +#!/bin/sh +cmpp -mod cfunc.mod +cmpp -ifs diff --git a/src/xspice/icm/modpath.lst b/src/xspice/icm/modpath.lst new file mode 100644 index 000000000..9ad9131c9 --- /dev/null +++ b/src/xspice/icm/modpath.lst @@ -0,0 +1,2 @@ +icm_spice2poly + diff --git a/src/xspice/icm/objects.inc b/src/xspice/icm/objects.inc new file mode 100644 index 000000000..fc2fd16c8 --- /dev/null +++ b/src/xspice/icm/objects.inc @@ -0,0 +1 @@ +icm_spice2poly/*.o diff --git a/src/xspice/icm/poly/Makefile.am b/src/xspice/icm/poly/Makefile.am new file mode 100755 index 000000000..2490dc745 --- /dev/null +++ b/src/xspice/icm/poly/Makefile.am @@ -0,0 +1,18 @@ +## Process this file with automake to produce Makefile.in +# +# JW 3/9/01 - had a go and makeing an autoconf script. + +noinst_LIBRARIES = libidnxsp.a + +libidnxsp_a_SOURCES = \ + ifspec.c \ + cfunc.c + +INCLUDES = -I$(top_srcdir)/src/include + +MAINTAINERCLEANFILES = Makefile.in + +ifspec.c: + cmpp -ifs +cfunc.c: + cmpp -mod cfunc.mod diff --git a/src/xspice/icm/poly/cfunc.c b/src/xspice/icm/poly/cfunc.c new file mode 100755 index 000000000..c529d3851 --- /dev/null +++ b/src/xspice/icm/poly/cfunc.c @@ -0,0 +1,307 @@ +#line 1 "cfunc.mod" +#include "cm.h" +#line 1 "cfunc.mod" +/* =========================================================================== +FILE cfunc.mod + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the definition of a code model polynomial controlled + source compatible with SPICE 2G6 poly sources. + +INTERFACES + + icm_poly() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +/* + +This code model implements the non-linear polynomial controlled sources +available in SPICE 2G6. An automatic translator added into the simulator +front end is used to map 2G6 syntax into a call to this model in the +required syntax. + +This model may also be called directly as follows: + + a1 [ ] xxx + .model xxx poly ( coef = [ ] ) + +Refer to the 2G6 User Guide for an explanation of the coefficients. + + +This model is patterned after the FORTRAN code used in the 2G6 simulator. +Function cm_poly() below performs the functions of subroutines NLCSRC and +EVPOLY. Function evterm() performs the function of subroutine EVTERM, +and function nxtpwr() performs the function of subroutine NXTPWR. + +*/ + + + + +void *malloc(int); +void free(void *); + +/* SPICE 2G6 type utility functions */ +static double evterm(double x, int n); +static void nxtpwr(int *pwrseq, int pdim); + + + + +void icm_poly (Mif_Private_t *private) +{ + int num_inputs; /* Number of inputs to model */ + int num_coefs; /* Number of coefficients */ + int *exp; /* List of exponents in products */ + /* One for each input */ + + int i; /* Counter */ + int j; /* Counter */ + int k; /* Counter */ + + double *in; /* Values of inputs to model */ + double *coef; /* Values of coefficients */ + + double sum; /* Temporary for accumulating sum of terms */ + double product; /* Temporary for accumulating product */ + + double *acgains; /* Static variable holding AC gains for AC analysis */ + + /* debug statement */ + printf("In icm_poly!!! . . . .\n"); + + /* Get number of input values */ + + num_inputs = private->conn[0]->size; + + /* If this is the first call to the model, allocate the static variable */ + /* array */ + + if(private->circuit.init) { + acgains = malloc(num_inputs * sizeof(double)); + for(i = 0; i < num_inputs; i++) + acgains[i] = 0.0; + private->inst_var[0]->element[0].pvalue = acgains; + } + else + acgains = private->inst_var[0]->element[0].pvalue; + + /* If analysis type is AC, use the previously computed DC partials */ + /* for the AC gains */ + + if(private->circuit.anal_type == MIF_AC) { + for(i = 0; i < num_inputs; i++) { + acgains = private->inst_var[0]->element[0].pvalue; + private->conn[1]->port[0]->ac_gain[0].port[i].real = acgains[i]; + private->conn[1]->port[0]->ac_gain[0].port[i].imag = 0.0; + } + return; + } + + /* Get input values and coefficients to local storage for faster access */ + + in = malloc(num_inputs * sizeof(double)); + for(i = 0; i < num_inputs; i++) + in[i] = private->conn[0]->port[i]->input.rvalue; + + num_coefs = private->param[0]->size; + + coef = malloc(num_coefs * sizeof(double)); + for(i = 0; i < num_coefs; i++) + coef[i] = private->param[0]->element[i].rvalue; + + + /* Allocate the array of exponents used in computing the poly terms */ + exp = malloc(num_inputs * sizeof(int)); + + /* Initialize the exponents to zeros */ + for(i = 0; i < num_inputs; i++) + exp[i] = 0; + + + /* Compute the output of the source by summing the required products */ + for(i = 1, sum = coef[0]; i < num_coefs; i++) { + + /* Get the list of powers for the product terms in this term of the sum */ + nxtpwr(exp, num_inputs); + + /* Form the product of the inputs taken to the required powers */ + for(j = 0, product = 1.0; j < num_inputs; j++) + product *= evterm(in[j], exp[j]); + + /* Add the product times the appropriate coefficient into the sum */ + sum += coef[i] * product; + } + private->conn[1]->port[0]->output.rvalue = sum; + + + /* Compute and output the partials for each input */ + for(i = 0; i < num_inputs; i++) { + + /* Reinitialize the exponent list to zeros */ + for(j = 0; j < num_inputs; j++) + exp[j] = 0; + + /* Compute the partials by summing the required products */ + for(j = 1, sum = 0.0; j < num_coefs; j++) { + + /* Get the list of powers for the product terms in this term of the sum */ + nxtpwr(exp, num_inputs); + + /* If power for input for which partial is being evaluated */ + /* is zero, the term is a constant, so the partial is zero */ + if(exp[i] == 0) + continue; + + /* Form the product of the inputs taken to the required powers */ + for(k = 0, product = 1.0; k < num_inputs; k++) { + /* If input is not the one for which the partial is being taken */ + /* take the term to the specified exponent */ + if(k != i) + product *= evterm(in[k], exp[k]); + /* else, take the derivative of this term as n*x**(n-1) */ + else + product *= exp[k] * evterm(in[k], exp[k] - 1); + } + + /* Add the product times the appropriate coefficient into the sum */ + sum += coef[j] * product; + } + + private->conn[1]->port[0]->partial[0].port[i] = sum; + + /* If this is DC analysis, save the partial for use as AC gain */ + /* value in an AC analysis */ + + if(private->circuit.anal_type == MIF_DC) + acgains[i] = sum; + } + + /* Free the allocated items and return */ + free(in); + free(coef); + free(exp); + + return; +} + + +/* Function evterm computes the value of x**n */ + +static double evterm( + double x, + int n) +{ + double product; /* Temporary accumlator for forming the product */ + + product = 1.0; + while(n > 0) { + product *= x; + n--; + } + + return(product); +} + + + +/* + +This function is a literal translation of subroutine NXTPWR in SPICE 2G6. +This was done to guarantee compatibility with the ordering of +coefficients used by 2G6. The 2G6 User Guide does not completely define +the algorithm used and the GOTO loaded FORTRAN code is difficult to unravel. +Therefore, a one-to-one translation was deemed the safest approach. + +No attempt is made to document the function statements since no documentaton +is available in the 2G6 code. However, it can be noted that the code +appears to generate the exponents of the product terms in the sum-of-products +produced by the following expansion for two and three dimensional polynomials: + + 2D (a + b) ** n + 3D (a + (b + c)) ** n + +where n begins at 1 and increments as needed for as many terms as there are +coefficients on the polynomial source SPICE deck card, and where terms that +are identical under the laws of associativity are dropped. Thus, for example, +the exponents for the following sums are produced: + + 2D a + b + a**2 + ab + b**2 + c**3 + ... + 3D a + b + c + a**2 + a*b + a*c + b**2 + bc + c**2 + a**3 + ... + +*/ + +/* Define a macro to tranlate between FORTRAN-style array references */ +/* and C-style array references */ + +#define PWRSEQ(x) pwrseq[x - 1] + + +static void nxtpwr( + int *pwrseq, /* Array of exponents */ + int pdim) +{ + int i; + int k; + int km1; + int psum; + + if(pdim == 1) goto stmt80; + k = pdim; +stmt10: if(PWRSEQ(k) != 0) goto stmt20; + k = k - 1; + if(k != 0) goto stmt10; + goto stmt80; +stmt20: if(k == pdim) goto stmt30; + PWRSEQ(k) = PWRSEQ(k) - 1; + PWRSEQ(k+1) = PWRSEQ(k+1) + 1; + goto stmt100; +stmt30: km1 = k - 1; + for(i = 1; i <= km1; i++) + if(PWRSEQ(i) != 0) goto stmt50; +stmt40: PWRSEQ(1) = PWRSEQ(pdim) + 1; + PWRSEQ(pdim) = 0; + goto stmt100; +stmt50: psum = 1; + k = pdim; +stmt60: if(PWRSEQ(k-1) >= 1) goto stmt70; + psum = psum + PWRSEQ(k); + PWRSEQ(k) = 0; + k = k - 1; + goto stmt60; +stmt70: PWRSEQ(k) = PWRSEQ(k) + psum; + PWRSEQ(k-1) = PWRSEQ(k-1) - 1; + goto stmt100; +stmt80: PWRSEQ(1) = PWRSEQ(1) + 1; + +stmt100: return; + +} + diff --git a/src/xspice/icm/poly/cfunc.mod b/src/xspice/icm/poly/cfunc.mod new file mode 100755 index 000000000..d7ec837ed --- /dev/null +++ b/src/xspice/icm/poly/cfunc.mod @@ -0,0 +1,302 @@ +/* =========================================================================== +FILE cfunc.mod + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the definition of a code model polynomial controlled + source compatible with SPICE 2G6 poly sources. + +INTERFACES + + icm_poly() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +/* + +This code model implements the non-linear polynomial controlled sources +available in SPICE 2G6. An automatic translator added into the simulator +front end is used to map 2G6 syntax into a call to this model in the +required syntax. + +This model may also be called directly as follows: + + a1 [ ] xxx + .model xxx poly ( coef = [ ] ) + +Refer to the 2G6 User Guide for an explanation of the coefficients. + + +This model is patterned after the FORTRAN code used in the 2G6 simulator. +Function cm_poly() below performs the functions of subroutines NLCSRC and +EVPOLY. Function evterm() performs the function of subroutine EVTERM, +and function nxtpwr() performs the function of subroutine NXTPWR. + +*/ + + + + +void *malloc(int); +void free(void *); + +/* SPICE 2G6 type utility functions */ +static double evterm(double x, int n); +static void nxtpwr(int *pwrseq, int pdim); + + + + +void icm_poly (ARGS) +{ + int num_inputs; /* Number of inputs to model */ + int num_coefs; /* Number of coefficients */ + int *exp; /* List of exponents in products */ + /* One for each input */ + + int i; /* Counter */ + int j; /* Counter */ + int k; /* Counter */ + + double *in; /* Values of inputs to model */ + double *coef; /* Values of coefficients */ + + double sum; /* Temporary for accumulating sum of terms */ + double product; /* Temporary for accumulating product */ + + double *acgains; /* Static variable holding AC gains for AC analysis */ + + + /* Get number of input values */ + + num_inputs = PORT_SIZE(in); + + /* If this is the first call to the model, allocate the static variable */ + /* array */ + + if(INIT) { + acgains = malloc(num_inputs * sizeof(double)); + for(i = 0; i < num_inputs; i++) + acgains[i] = 0.0; + STATIC_VAR(acgains) = acgains; + } + else + acgains = STATIC_VAR(acgains); + + /* If analysis type is AC, use the previously computed DC partials */ + /* for the AC gains */ + + if(ANALYSIS == MIF_AC) { + for(i = 0; i < num_inputs; i++) { + acgains = STATIC_VAR(acgains); + AC_GAIN(out,in[i]).real = acgains[i]; + AC_GAIN(out,in[i]).imag = 0.0; + } + return; + } + + /* Get input values and coefficients to local storage for faster access */ + + in = malloc(num_inputs * sizeof(double)); + for(i = 0; i < num_inputs; i++) + in[i] = INPUT(in[i]); + + num_coefs = PARAM_SIZE(coef); + + coef = malloc(num_coefs * sizeof(double)); + for(i = 0; i < num_coefs; i++) + coef[i] = PARAM(coef[i]); + + + /* Allocate the array of exponents used in computing the poly terms */ + exp = malloc(num_inputs * sizeof(int)); + + /* Initialize the exponents to zeros */ + for(i = 0; i < num_inputs; i++) + exp[i] = 0; + + + /* Compute the output of the source by summing the required products */ + for(i = 1, sum = coef[0]; i < num_coefs; i++) { + + /* Get the list of powers for the product terms in this term of the sum */ + nxtpwr(exp, num_inputs); + + /* Form the product of the inputs taken to the required powers */ + for(j = 0, product = 1.0; j < num_inputs; j++) + product *= evterm(in[j], exp[j]); + + /* Add the product times the appropriate coefficient into the sum */ + sum += coef[i] * product; + } + OUTPUT(out) = sum; + + + /* Compute and output the partials for each input */ + for(i = 0; i < num_inputs; i++) { + + /* Reinitialize the exponent list to zeros */ + for(j = 0; j < num_inputs; j++) + exp[j] = 0; + + /* Compute the partials by summing the required products */ + for(j = 1, sum = 0.0; j < num_coefs; j++) { + + /* Get the list of powers for the product terms in this term of the sum */ + nxtpwr(exp, num_inputs); + + /* If power for input for which partial is being evaluated */ + /* is zero, the term is a constant, so the partial is zero */ + if(exp[i] == 0) + continue; + + /* Form the product of the inputs taken to the required powers */ + for(k = 0, product = 1.0; k < num_inputs; k++) { + /* If input is not the one for which the partial is being taken */ + /* take the term to the specified exponent */ + if(k != i) + product *= evterm(in[k], exp[k]); + /* else, take the derivative of this term as n*x**(n-1) */ + else + product *= exp[k] * evterm(in[k], exp[k] - 1); + } + + /* Add the product times the appropriate coefficient into the sum */ + sum += coef[j] * product; + } + + PARTIAL(out,in[i]) = sum; + + /* If this is DC analysis, save the partial for use as AC gain */ + /* value in an AC analysis */ + + if(ANALYSIS == MIF_DC) + acgains[i] = sum; + } + + /* Free the allocated items and return */ + free(in); + free(coef); + free(exp); + + return; +} + + +/* Function evterm computes the value of x**n */ + +static double evterm( + double x, + int n) +{ + double product; /* Temporary accumlator for forming the product */ + + product = 1.0; + while(n > 0) { + product *= x; + n--; + } + + return(product); +} + + + +/* + +This function is a literal translation of subroutine NXTPWR in SPICE 2G6. +This was done to guarantee compatibility with the ordering of +coefficients used by 2G6. The 2G6 User Guide does not completely define +the algorithm used and the GOTO loaded FORTRAN code is difficult to unravel. +Therefore, a one-to-one translation was deemed the safest approach. + +No attempt is made to document the function statements since no documentaton +is available in the 2G6 code. However, it can be noted that the code +appears to generate the exponents of the product terms in the sum-of-products +produced by the following expansion for two and three dimensional polynomials: + + 2D (a + b) ** n + 3D (a + (b + c)) ** n + +where n begins at 1 and increments as needed for as many terms as there are +coefficients on the polynomial source SPICE deck card, and where terms that +are identical under the laws of associativity are dropped. Thus, for example, +the exponents for the following sums are produced: + + 2D a + b + a**2 + ab + b**2 + c**3 + ... + 3D a + b + c + a**2 + a*b + a*c + b**2 + bc + c**2 + a**3 + ... + +*/ + +/* Define a macro to tranlate between FORTRAN-style array references */ +/* and C-style array references */ + +#define PWRSEQ(x) pwrseq[x - 1] + + +static void nxtpwr( + int *pwrseq, /* Array of exponents */ + int pdim) +{ + int i; + int k; + int km1; + int psum; + + if(pdim == 1) goto stmt80; + k = pdim; +stmt10: if(PWRSEQ(k) != 0) goto stmt20; + k = k - 1; + if(k != 0) goto stmt10; + goto stmt80; +stmt20: if(k == pdim) goto stmt30; + PWRSEQ(k) = PWRSEQ(k) - 1; + PWRSEQ(k+1) = PWRSEQ(k+1) + 1; + goto stmt100; +stmt30: km1 = k - 1; + for(i = 1; i <= km1; i++) + if(PWRSEQ(i) != 0) goto stmt50; +stmt40: PWRSEQ(1) = PWRSEQ(pdim) + 1; + PWRSEQ(pdim) = 0; + goto stmt100; +stmt50: psum = 1; + k = pdim; +stmt60: if(PWRSEQ(k-1) >= 1) goto stmt70; + psum = psum + PWRSEQ(k); + PWRSEQ(k) = 0; + k = k - 1; + goto stmt60; +stmt70: PWRSEQ(k) = PWRSEQ(k) + psum; + PWRSEQ(k-1) = PWRSEQ(k-1) - 1; + goto stmt100; +stmt80: PWRSEQ(1) = PWRSEQ(1) + 1; + +stmt100: return; + +} + diff --git a/src/xspice/icm/poly/ifspec.c b/src/xspice/icm/poly/ifspec.c new file mode 100755 index 000000000..707859a58 --- /dev/null +++ b/src/xspice/icm/poly/ifspec.c @@ -0,0 +1,194 @@ + +/* + * Structures for model: poly + * + * Automatically generated by cmpp preprocessor + * + * !!! DO NOT EDIT !!! + * + */ + + +// #include "prefix.h" +#include +#include "spice.h" +#include "devdefs.h" +#include "ifsim.h" +#include "mifdefs.h" +#include "mifproto.h" +#include "mifparse.h" +// #include "suffix.h" + + +static IFparm MIFmPTable[] = { + IOP("coef", 0, (IF_REAL|IF_VECTOR), "2g6 compatible spice card coefficient list"), +}; + + +static IFparm MIFpTable[] = { + OP("acgains", 1, IF_STRING, "partial derivatives from dc analysis used for ac gains"), +}; + + +static Mif_Port_Type_t MIFportEnum0[] = { + MIF_VOLTAGE, + MIF_DIFF_VOLTAGE, + MIF_CURRENT, + MIF_DIFF_CURRENT, + MIF_VSOURCE_CURRENT, +}; + + +static char *MIFportStr0[] = { + "v", + "vd", + "i", + "id", + "vnam", +}; + + +static Mif_Port_Type_t MIFportEnum1[] = { + MIF_VOLTAGE, + MIF_DIFF_VOLTAGE, + MIF_CURRENT, + MIF_DIFF_CURRENT, +}; + + +static char *MIFportStr1[] = { + "v", + "vd", + "i", + "id", +}; + + +static Mif_Conn_Info_t MIFconnTable[] = { + { + "in", + "input", + MIF_IN, + MIF_VOLTAGE, + "v", + 5, + MIFportEnum0, + MIFportStr0, + MIF_TRUE, + MIF_TRUE, + 1, + MIF_FALSE, + 0, + MIF_FALSE, + }, + { + "out", + "output", + MIF_OUT, + MIF_VOLTAGE, + "v", + 4, + MIFportEnum1, + MIFportStr1, + MIF_FALSE, + MIF_FALSE, + 0, + MIF_FALSE, + 0, + MIF_FALSE, + }, +}; + + +static Mif_Param_Info_t MIFparamTable[] = { + { + "coef", + "2g6 compatible spice card coefficient list", + MIF_REAL, + MIF_FALSE, + {MIF_FALSE, 0, 0.0, {0.0, 0.0}, NULL}, + MIF_FALSE, + {MIF_FALSE, 0, 0.0, {0.0, 0.0}, NULL}, + MIF_FALSE, + {MIF_FALSE, 0, 0.0, {0.0, 0.0}, NULL}, + MIF_TRUE, + MIF_FALSE, + 0, + MIF_TRUE, + 2, + MIF_FALSE, + 0, + MIF_FALSE, + }, +}; + + +static Mif_Inst_Var_Info_t MIFinst_varTable[] = { + { + "acgains", + "partial derivatives from dc analysis used for ac gains", + MIF_STRING, + MIF_FALSE, + }, +}; + + +extern void icm_poly(Mif_Private_t *); + +static int val_terms = 0; +static int val_numNames = 0; +static int val_numInstanceParms = 1; +static int val_numModelParms = 1; +static int val_sizeofMIFinstance = sizeof(MIFinstance); +static int val_sizeofMIFmodel = sizeof(MIFmodel); + +SPICEdev icm_poly_info = { + { "poly", + "2g6 compatible polynomial controlled source", + &val_terms, + &val_numNames, + NULL, + &val_numInstanceParms, + MIFpTable, + &val_numModelParms, + MIFmPTable, + icm_poly, + 2, + MIFconnTable, + 1, + MIFparamTable, + 1, + MIFinst_varTable, + }, +NULL, +MIFmParam, +MIFload, +MIFsetup, +MIFunsetup, +NULL, +NULL, +MIFtrunc, +NULL, +MIFload, +NULL, +MIFdestroy, +MIFmDelete, +MIFdelete, +NULL, +MIFask, +MIFmAsk, +NULL, +MIFconvTest, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +NULL, +&val_sizeofMIFinstance, +&val_sizeofMIFmodel, + +}; + diff --git a/src/xspice/icm/poly/ifspec.ifs b/src/xspice/icm/poly/ifspec.ifs new file mode 100755 index 000000000..39a2aaef8 --- /dev/null +++ b/src/xspice/icm/poly/ifspec.ifs @@ -0,0 +1,75 @@ +/* =========================================================================== +FILE ifspec.ifs + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the definition of a code model polynomial controlled + source compatible with SPICE 2G6 poly sources. + +INTERFACES + + None. + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +=========================================================================== */ + +NAME_TABLE: + +Spice_Model_Name: poly +C_Function_Name: icm_poly +Description: "2G6 compatible polynomial controlled source" + + +PORT_TABLE: + +Port_Name: in out +Description: "input" "output" +Direction: in out +Default_Type: v v +Allowed_Types: [v,vd,i,id,vnam] [v,vd,i,id] +Vector: yes no +Vector_Bounds: [1 -] - +Null_Allowed: no no + + +PARAMETER_TABLE: + +Parameter_Name: coef +Description: "2G6 compatible spice card coefficient list" +Data_Type: real +Default_Value: - +Limits: - +Vector: yes +Vector_Bounds: [2 -] +Null_Allowed: no + + +STATIC_VAR_TABLE: + +Static_Var_Name: acgains +Data_Type: pointer +Description: "Partial derivatives from DC analysis used for AC gains" diff --git a/src/xspice/icm/poly/make.bat b/src/xspice/icm/poly/make.bat new file mode 100755 index 000000000..19a341b81 --- /dev/null +++ b/src/xspice/icm/poly/make.bat @@ -0,0 +1,3 @@ +#!/bin/sh +cmpp -mod cfunc.mod +cmpp -ifs diff --git a/src/xspice/icm/spice2poly.cm b/src/xspice/icm/spice2poly.cm new file mode 100755 index 0000000000000000000000000000000000000000..1e67d1e959327de7c7533d1ed73827d9cf46e0ba GIT binary patch literal 18495 zcmeHO4{%(?dEb*{5JrFiB?vIifekT_(5w;|pfPFsQJxPb}A9weJ z-9k}DFy(rvaZ{2w9g@<75SpQhJJ6O1CK1HJc5o(|#Lc*1QztQr6se5UI@CiPuD{>D z-S_s+5`U7+WTunVtiJvB`?KGE`{(w(x8MDN_SHV0Pv}%G$_2678X=kho0mA0CgBHV zfw)3kCh3}=uqfkP>^7?Dt{O-qRIqok?W8XM39yt-zG~!+zME)%Ig(00fz1WP4c5Bn2Ix|>t~=` zq3SUsb_uFBUbc(sBX0T-)sDQHH;y3$Gme?*m?RWHO;xl>UtxJqIBxf*u}DXed%a=B7r z&zUqx~!z@s`62X=8tq*S`R`rf$^Vk}8uRh*fJY#&=R@Tz^cCxY zsT%Y78_>7O(Dymie-`@m{JjW{X@o!Z{Ek51OY=SY=g|Hk_)E{%PeJ~)VGs9Vy+2)q z{izA<_1x_F;~CKJ!uadD_6A#f+85~k0%qsOg5Cx27yKs(X0se0e}?V z9%3;Ri~gA1h8z)?o~x^>7?I(Zh^aYFXa}B+%1kM-r@dN=|JuWoHJ_m}u$9?9ipGw45y$o;NbmMwk4Ui)@J)UvuP1VF`Dh8i-K#{ zcQ&qM2`n8+>2H*JLNQzV)K0kGM|CzS{fbf$MJu7?g5g9q7R;o>!R(Hd6r*kEb1)o> z#-rI_UpPT56Hg|y13@&OuwaE-5s79nMA>MLg6fi z!K-y9lZVr`6#MAPMH5-8KkbZ?*2NiNrTaN>x(fXaW-T<)yDgP1sZUwycxininMF?= zH4%d!!8# zu<)!ZtQTY*R}-QawNuLBRRrOWzfQU3t=YWLK6%f|WR| z(4J}qYZPo&uvNk13XUtt^E%}!6s%ORO2HZhYZaWMpkKi{1)CIHuHZ8Y&Ij~Ga1JMI z!#fqiE<9%_*h$za#8yHauv3J7oNEbh7h*eMFP<9+agcg|5C`Um32~6-`PYYs{{1WP zJ_=`pgM?S(oI;4R*&#w4Kn@eaS4RkO<~c^V3g;uj0M3Pkcxay@+=O#8VIR&Xgm(#X ziZG7zBw-2;gO0P(coqPDHeLwrA%xs0;Z`B`5<;#!_xkWu<@jbu>>U3Y{*S+b6WM|9 z6rScOsPJX_o~iI zvUfl{ADDXKSb698H{X^9P@Jd+CLrSLA0GnKV*UoiEF8sK`|;&UGz@0qm+F*d_&EeBq}Rc^~Yqq}_n>jf%8? zB17MKzWDlYl`jqz8(KDb0~=oM_`*Kqw3M%u);vhH`IWW#it2pl9GIM|Sdr^mQQ>&# zdC8k)Un+8eaoO0obo@%)*qGVaI2^x}dYhca?i*7I*jP^XWo4z;@#}M4*Q=IBbw|oN z$6v?9!K}&!j+?qUsFxcygX#>bW9qBm#iIiTWQ@a!!P}qfJ_!Ds0^|42n)q9guqF%k zRKY$z{(2d(z18~yN7(&+fl&wS#TIZ&K5)dDK`Vx*Fe*nzY8)R0ju)CRKY%-Z3mhtB zt1D19-r;CF8W?@;z|qa4BY`8BT+d2V731Fj5^%3 zKi_>Yw{k)5wz}fZ&zg7|-BT93Ho_r>fv!Vn0333J4%rvj@AU28#jYUi%M6YV>=g!!GsMl@p0HcmhsGP;l9O6uW8+sD*zxfi z2OF_Y-mxW!O&fdR@Kh}=>aNNKPUX5!?+cu8Jb#i?bojn%aX)seqk$7gI!+zkd}1U} zU8tNK&j(KAyH8@F=DMej2F4vtJ;PJg!}nGBFqB*1^n3s+Pv!$BUpQ7)j@@m%f_rM& ze&Emww$ktS!(bYT9TbVsog z{5aW`3!K;&I0fA>;?zxn6Ms=Q2@jqsF{E)Ma0=7+NWL4|s$61TG_x(Q zVjmqMCO(6I`vOyr){?IA=K_t?71>-p0&dK7&jF4m+5gEGT*643*H_u^sq(U%&m(u9 za?8WxmC_ZH=gKjw%wM!BSB?3&#TkK(`X}udSE1{}epq}*3FZ5evTPd4(~{CK4dwHa zf+Nux+U}Q>x@jnuq%4?*@-a#2oQASkQktfrTp}rRrlI`yO7?XAG?br7O8YdF1Cr8e zP_O}L*F6Oy_k&^5^P*r^7i_F6_`NX3jY}SOcm^?Kwz~=`P~*uoS9n4tGOeBe(aZ17&blqr)khVO8$YnqY!&|J1q%)M+z?k6Po8NFWa36%CaJH15n%o3}nv8dH! zcRbXp*JTa3^$*pFTsMALs`K4Atn()*AE?cD&&lEB*L^%6@aMbd=K_aw-S}ZykngU` z1;%pSIE*&ryPNc3ZCSp1c`h)T>)x9WH0QfpbAe}b-Fu4sv#DTr73|J}-CnTU3ih6Y z{cOSBTd+q9_Q8U^A3wDPd#qp|DcFZ`(ks}<Hy}H` z#MUz;+#ar?CF#yJo@|hAtLjvzb+~IV5 z>-P5v$e3eOBrm-+^uRRoZjbvYuX{Z7kYZ)0OZUy?(z>Ei9D2A&b*aScBHcURLHDs* z(+a*>&`zaJz67b(%k5vtic?OYD&LIT}E3Rl~oGRFAo9oFB?NVCjG})yoPzBx-72ViRH>^o)Ja>lwyO7|?Xh>$_N_}~ zx8Iv>kH4F?)ceu)Jeu=x5B)QyaMHqC+nG~i|586c z9fh*d-k9Z=pFsTKP&$HItFJE_j^bOFpI@d#DBK^4;=^ZAQlG{A5i1=Xgpxrk!H9@P#9#uSh4G~+nn>Z}6F)#9vuJ!K%ALRW`1Nd z>HAz2n8D3>ExQewy#8vuPDc6Pkomgyd82H~zli$pA~R8r{IsJDcJUht>vTrZO)rD* z2gsU6xiV0@Aj5Ac?={hW+D+L%R$?D?V!6~7`joz`&`bvWG3t5Y=uY%`tLk?Ikl&jI zkcU(qb&jG;{rm<*$ZtP?j1*HgvF#{6$#T-xhvTB5o;O2^Q%f>p`Ekj?iut4b{2Rw| z#4<}J{K-E5#!h_yZ1hX{VlfwM3x&5~1!R->`aEEz{k=Q<8(UY^`PbPA%iq}0u%upW zz;%ldSE!x9D)z7GY!{&V8yh7Zn#}Pj9UIc_$G{Ng6#Tq`Ur}(kg3l{>K*4`k@R)*A z3i7)nZ#!N(%ejlhJ8JmYDDNtPBzPB%+3yF;|sk6eR% zAu@iSGyPAK-{B>RMoImtVtcnz7n8u)unkS~x|Lzai| zZBD1x(BK=$ZzG>i<9?sCrlsXZ|AIB^yRY>xsh7jn*lKjO3cs`FZI~t2DTHLg3X=B5}=#phi7p>}E+upjUpVu4~Hr6-R->_s+ zIAaS8zklUMKW;26slTD2z5zEOd_#`f^QGFm|8Q!37mDh$F0Z`0@?x&TVhkUJ(KGtM z=ljk1xTWiaXZXaTY&^9n98L_Z4o7iS0}M#B%6J2@e6R4$t(!ad_PJ$eLjbk(XZaFT z-dJ{)Q$x>|U#{39k-EBtH_k1?bq3!$sClOb@9tZ-zGs`)Acvs*N*|49x=EXr$r z3a`)Xi1SKWJ#NA{a6p#^LYVa)1#BI@N;yZUre-cqS|{#ygVKHQ74r2bGm8W#1uAC&>_fV;wMGYN6rY5_4B=WhbY zRIL_1 zmbrg3H=xWmsLNS>LdKq)1Dt%^|Czb}6K5h{C2|k4=G%$+!R$vJ`B<;{f+$lze}_0{ znen{mCf)8QP^0-6Gr-*Gayk0Q%Q4Y>obk+K$U13#+YCOgAZEOOa+Bt}Tk+9up7ogd zeN!jDD(nDcHT~6$^9kdhmJ?CQui*WFnvbyx_b&8&OBUU34rTg;D)_s{cqzt6X_e%o zpFfSP^)a?#7cR51UMH>Z^Qh2#jCpu$2ENA(KE^^k2|lfZI(7Lg1|QD}Pl0a}8B`)4 z^Vg7R5AEjN>yT2Ws(LL7taGt`$-Akr@{Aa3aF&{upHbmBI&k9A^uD85NJL^y=GXy|9Z?*EDX-U zR%H5xenM4|zFjMY_$nucN?M=BO~`$Q=})me92LxE%u}Xvd203U#+S>}t1~|xy0KwM ze!MR*((e-6V~C@D?&Mx7mr;r)KF6IWtgXP^eSJ0}UWU_p@m}?-!iQ1M_0QzP%()Jb z>zv7llY0xGj*apmHj4YR_J8+fTb zp8@?*xBY7ICGxxYg@WtdwC8!?racFMKeGV;kl3C%&*_i3-mk8F(FVzANB+mU-X-ov{=bV6)od_hKb30dB^g zT?yQbJ-ZgT8GE)AxEXu461aYzV{8gz+sxRrF5qVDSs1t(dlm<7#-42l{zce}pCM^) z9=I8M_8H)2?AcSm&DgVNfSa*r&jIIMR{q{FR^(;iX6)H3z|GjRao}d`*{_JhU*A*q z6o8wtXXloSqS&(!0yksN<^wll&pry=j6G`s&a)B|W6#zCH)GF2z|GjRIB+xe>^|U3 zI`-^Sz>lH7IJU_C{ylIr_G~wBGxqFTz|GjR?*lhu&t3z55c)VD=|9G9nXzZ5fSa*r z=YY|SJ-ZmV8GCjaa5MIdv45k`&-F?D^}x;8vsU1B^SoH09^hu|*;e3Y>{$}H8GD91 zSVggC!@$khv(EuHW6!<{+>AZrbzC#{?Aw&T!ixpl58RABdjjrMdo<)J1v1b|JX6)Gm zz|H>Wf%9C*#P$6Tz?q&y=JWnP12<#O{uQ_xdo~K(j6E9zZpNOy3fzo6`zdfU_UxCy z&DgWkzzxr%-FLlj5C!VJd1&wv1iS|>p;M~Uk1Xl&ttX(e|*0s3Ysrm-QJ@KVz){5ZOX)qW`2K!^l-cT$kpOu3l zdpm;C@Gu>W*zx!d-CAIMtJj!DoQ5>UW^~!pDj&q9)^H!<)?j~Pdp31XT3CY!iovIh zzD&wuHKW)%yF&P)!eYuRCDYjeqSP8>HIR+jv|A=&u|be!e8ot3;*6tnF?@^31_wiF zr(zJjrV$yz5>%~6zg=W4cteS>g-AS)1HoZd%&W;D(l6Nud#RYrQj=RNUqXvWERK&r za^xbJWN;vqh&Y{ObdwW7hBw+OzRQc#f>s$o=Edo?bU4v*Zk!$C=)k4}0o~X|FQCqd zUSiM_n9(RB+`@4;{LX7!M{gOwYh zN8`NEIL9_8=8h4GD%OzPUJxRf7}n^STpWOg))9~CQ5nwWg{?uDJ(NL^Egf)#5e?{s zYBe|O7^Vg%tkG!tjA$-2qk1KSy^15bKrD_%W0!hVP$MOsC_ok5XRPP-v} zP^cn4$yyYSsl(r<4gTc_du=v2fpJiVErG$eP^LQN#5sD4LWkVykkd2c06p2_2ux^q u;(ezJCx(z2Hwj)2dT|)4Udc|#t?q4ctSM$0{Y-~@QN4~f#=4foJpUhgNEgWf literal 0 HcmV?d00001 diff --git a/src/xspice/idn/Makefile.am b/src/xspice/idn/Makefile.am new file mode 100755 index 000000000..1d1ebc2f9 --- /dev/null +++ b/src/xspice/idn/Makefile.am @@ -0,0 +1,12 @@ +## Process this file with automake to produce Makefile.in +# +# JW 3/9/01 - had a go and makeing an autoconf script. + +noinst_LIBRARIES = libidnxsp.a + +libidnxsp_a_SOURCES = \ + idndig.c + +INCLUDES = -I$(top_srcdir)/src/include + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/xspice/idn/idndig.c b/src/xspice/idn/idndig.c new file mode 100755 index 000000000..0dd55ba97 --- /dev/null +++ b/src/xspice/idn/idndig.c @@ -0,0 +1,346 @@ +/*============================================================================ +FILE IDNdig.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the definition of the 'digital' node type + used by 12-state digital models in the code model library. + These functions are called exclusively through function + pointers in an Evt_Udn_Info_t data structure. + +INTERFACES + + Evt_Udn_Info_t idn_digital_info + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include "ngspice.h" + +#include "cm.h" + +#include "evtudn.h" + +/* ************************************************************************ */ + +void idn_digital_create(void **evt_struct) +{ + /* Malloc space for a digital struct */ + + *evt_struct = tmalloc(sizeof(Digital_t)); +} + + +/* ************************************************************************ */ + +void idn_digital_dismantle(void *evt_struct) +{ + /* Do nothing. There are no internally malloc'ed things to dismantle */ +} + + +/* ************************************************************************ */ + +void idn_digital_initialize(void *evt_struct) +{ + Digital_t *dig_struct = evt_struct; + + + /* Initialize to unknown state and strength */ + dig_struct->state = ZERO; + dig_struct->strength = UNDETERMINED; +} + + +/* ************************************************************************ */ + +void idn_digital_invert(void *evt_struct) +{ + Digital_t *dig_struct = evt_struct; + + + /* Invert the state */ + switch(dig_struct->state) { + + case ZERO: + dig_struct->state = ONE; + return; + + case ONE: + dig_struct->state = ZERO; + return; + + default: + return; + } + +} + + +/* ************************************************************************ */ + +void idn_digital_copy(void *evt_from_struct, void *evt_to_struct) +{ + Digital_t *dig_from_struct = evt_from_struct; + Digital_t *dig_to_struct = evt_to_struct; + + /* Copy the structure */ + dig_to_struct->state = dig_from_struct->state; + dig_to_struct->strength = dig_from_struct->strength; +} + + +/* ************************************************************************ */ + +void idn_digital_resolve(int num_struct, + void **evt_struct_array, void *evt_struct) +{ + Digital_t **dig_struct_array; + Digital_t *dig_struct; + + static int map[12][12] = { + { 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2}, + { 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1}, + { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, + { 0, 1, 2, 3, 5, 5, 3, 3, 3, 9, 11, 11}, + { 0, 1, 2, 5, 4, 5, 4, 4, 4, 11, 10, 11}, + { 0, 1, 2, 5, 5, 5, 5, 5, 5, 10, 11, 11}, + { 0, 1, 2, 3, 4, 5, 6, 8, 8, 9, 11, 11}, + { 0, 1, 2, 3, 4, 5, 8, 7, 8, 11, 10, 11}, + { 0, 1, 2, 3, 4, 5, 8, 8, 8, 11, 11, 11}, + { 0, 2, 2, 9, 11, 11, 9, 11, 11, 9, 11, 11}, + { 2, 1, 2, 11, 10, 11, 11, 10, 11, 11, 10, 11}, + { 2, 1, 2, 11, 11, 11, 11, 11, 11, 11, 11, 11} }; + + int i; + + int index1; + int index2; + + /* Cast the input void pointers to pointers of the digital type */ + dig_struct = evt_struct; + dig_struct_array = (Digital_t **)evt_struct_array; + + /* Copy the first member of the array directly to the output */ + dig_struct->state = dig_struct_array[0]->state; + dig_struct->strength = dig_struct_array[0]->strength; + + /* Convert struct to index into map */ + index1 = dig_struct->state + ((int)dig_struct->strength) * 3; + + + /* For the remaining members, perform the resolution algorithm */ + for(i = 1; i < num_struct; i++) { + + /* Convert struct to index into map */ + index2 = dig_struct_array[i]->state + + ((int)dig_struct_array[i]->strength) * 3; + + /* Compute the result */ + index1 = map[index1][index2]; + } + + /* Convert result back to state and strength */ + dig_struct->state = index1 % 3; + dig_struct->strength = index1 / 3; +} + + +/* ************************************************************************ */ + +void idn_digital_compare(void *evt_struct1, void *evt_struct2, + Boolean_t *equal) +{ + Digital_t *dig_struct1 = evt_struct1; + Digital_t *dig_struct2 = evt_struct2; + + /* Compare the structures in order of most likely differences */ + if(dig_struct1->state != dig_struct2->state) + *equal = FALSE; + else if(dig_struct1->strength != dig_struct2->strength) + *equal = FALSE; + else + *equal = TRUE; +} + + +/* ************************************************************************ */ + +void idn_digital_plot_val(void *evt_struct, char *member, double *val) +{ + Digital_t *dig_struct = evt_struct; + + + /* Output a value for the requested member of the digital struct */ + if(strcmp(member,"strength") == 0) { + + /* Choose values that will not make plots lie on state plots */ + switch(dig_struct->strength) { + + case STRONG: + *val = 0.1; + return; + + case RESISTIVE: + *val = 0.6; + return; + + case HI_IMPEDANCE: + *val = 1.1; + return; + + case UNDETERMINED: + *val = -0.4; + return; + } + } + else { + /* member = "state" or anything else */ + + /* Pick reasonable values */ + switch(dig_struct->state) { + + case ZERO: + *val = 0.0; + return; + + case ONE: + *val = 1.0; + return; + + case UNKNOWN: + *val = 0.5; + return; + } + } +} + + +/* ************************************************************************ */ + +void idn_digital_print_val(void *evt_struct, char *member, char **val) +{ + Digital_t *dig_struct = evt_struct; + + int index; + + static char *map[] = { "0s", "1s", "Us", + "0r", "1r", "Ur", + "0z", "1z", "Uz", + "0u", "1u", "Uu" }; + + + /* Output a value for the requested member of the digital struct */ + + if(strcmp(member,"state") == 0) { + + /* Pick reasonable values */ + switch(dig_struct->state) { + + case ZERO: + *val = "0"; + return; + + case ONE: + *val = "1"; + return; + + case UNKNOWN: + *val = "U"; + return; + + default: + *val = "?"; + return; + } + } + else if(strcmp(member,"strength") == 0) { + + /* Choose values that will not make plots lie on state plots */ + switch(dig_struct->strength) { + + case STRONG: + *val = "s"; + return; + + case RESISTIVE: + *val = "r"; + return; + + case HI_IMPEDANCE: + *val = "z"; + return; + + case UNDETERMINED: + *val = "u"; + return; + + default: + *val = "?"; + return; + } + } + else { + index = dig_struct->state + ((int)dig_struct->strength) * 3; + if((index < 0) || (index > 11)) + *val = "??"; + else + *val = map[index]; + return; + } +} + + + +/* ************************************************************************ */ + +void idn_digital_ipc_val(void *evt_struct, void **ipc_val, int *ipc_val_size) +{ + /* Return the digital data structure and its size */ + *ipc_val = evt_struct; + *ipc_val_size = sizeof(Digital_t); +} + + + +Evt_Udn_Info_t idn_digital_info = { + +"d", +"12 state digital data", +idn_digital_create, +idn_digital_dismantle, +idn_digital_initialize, +idn_digital_invert, +idn_digital_copy, +idn_digital_resolve, +idn_digital_compare, +idn_digital_plot_val, +idn_digital_print_val, +idn_digital_ipc_val + +}; + diff --git a/src/xspice/ipc/Makefile.am b/src/xspice/ipc/Makefile.am new file mode 100755 index 000000000..dc19a55e9 --- /dev/null +++ b/src/xspice/ipc/Makefile.am @@ -0,0 +1,16 @@ +## Process this file with automake to produce Makefile.in +# +# JW 3/9/01 - had a go and makeing an autoconf script. + +noinst_LIBRARIES = libipcxsp.a + +libipcxsp_a_SOURCES = \ +ipcaegis.c \ +ipc.c \ +ipcsockets.c \ +ipcstdio.c \ +ipctiein.c + +INCLUDES = -I$(top_srcdir)/src/include -I$(top_srcdir)/src/spicelib/devices + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/xspice/ipc/ipc.c b/src/xspice/ipc/ipc.c new file mode 100755 index 000000000..03be29fb3 --- /dev/null +++ b/src/xspice/ipc/ipc.c @@ -0,0 +1,987 @@ +/*============================================================================ +FILE IPC.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Steve Tynor + +MODIFICATIONS + + 6/13/92 Bill Kuhn Added some comments + +SUMMARY + + Provides compatibility for the new SPICE simulator to both the MSPICE user + interface and BCP (via ATESSE v.1 style AEGIS mailboxes) and the new ATESSE + v.2 Simulator Interface and BCP (via Bsd Sockets). + + The Interprocess Communications package provides functions + called to receive XSPICE decks from the ATESSE Simulator Interface + or Batch Control processes, and to return results to those + processes. Functions callable from the simulator packages include: + + ipc_initialize_server + ipc_terminate_server + ipc_get_line + ipc_send_line + ipc_send_data_prefix + ipc_send_data_suffix + ipc_send_dcop_prefix + ipc_send_dcop_suffix + ipc_send_evtdict_prefix + ipc_send_evtdict_suffix + ipc_send_evtdata_prefix + ipc_send_evtdata_suffix + ipc_send_errchk + ipc_send_end + ipc_send_boolean + ipc_send_int + ipc_send_double + ipc_send_complex + ipc_send_event + ipc_flush + + These functions communicate with a set of transport-level functions + that implement the interprocess communications under one of + the following protocol types determined by a compile-time option: + + BSD UNIX Sockets + HP/Apollo Mailboxes + + For each transport protocol, the following functions are written: + + ipc_transport_initialize_server + ipc_transport_get_line + ipc_transport_terminate_server + ipc_transport_send_line + + + +============================================================================*/ + +#ifndef NDEBUG +#include +#endif +#include /* Specific to BSD - Use sys/fcntl.h for sys5 */ +#include +#include +#include + +#include +#include +#include +#include /* NOTE: I think this is a Sys5ism (there is not man + * page for it under Bsd, but it's in /usr/include + * and it has a BSD copyright header. Go figure. + */ + +#include "ipc.h" +#include "ipctiein.h" +#include "ipcproto.h" + + +/* + * Conditional compilation sanity check: + */ + +/* +#if !defined (IPC_AEGIS_MAILBOXES) && !defined (IPC_UNIX_SOCKETS)\ + && !defined (IPC_DEBUG_VIA_STDIO) +" compiler error - must specify a transport mechanism"; +#endif + +*///ka removed + +/* + * static 'globals' + */ + +/*typedef unsigned char Buffer_Char_t;*/ +typedef char Buffer_Char_t; + +#define OUT_BUFFER_SIZE 1000 +#define MAX_NUM_RECORDS 200 +static int end_of_record_index [MAX_NUM_RECORDS]; +static int num_records; +static Buffer_Char_t out_buffer [OUT_BUFFER_SIZE]; +static int fill_count; + +static Ipc_Mode_t mode; +static Ipc_Protocol_t protocol; +static Ipc_Boolean_t end_of_deck_seen; +static int batch_fd; + +#define FMT_BUFFER_SIZE 80 +static char fmt_buffer [FMT_BUFFER_SIZE]; + +/*---------------------------------------------------------------------------*/ +Ipc_Boolean_t kw_match (keyword, str) + char *keyword; + char *str; + /* + * returns IPC_TRUE if the first `strlen(keyword)' characters of `str' match + * the ones in `keyword' - case sensitive + */ +{ + char *k = keyword; + char *s = str; + + /* + * quit if we run off the end of either string: + */ + while (*s && *k) { + if (*s != *k) { + return IPC_FALSE; + } + s++; + k++; + } + /* + * if we get this far, it sould be because we ran off the end of the + * keyword else we didn't match: + */ + return (*k == '\0'); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_initialize_server + +This function creates the interprocess communication channel +server mailbox or socket. +*/ + + +Ipc_Status_t ipc_initialize_server (server_name, m, p) + char *server_name; /* Mailbox path or host/portnumber pair */ + Ipc_Mode_t m; /* Interactive or batch */ + Ipc_Protocol_t p; /* Type of IPC protocol */ + /* + * For mailboxes, `server_name' would be the mailbox pathname; for + * sockets, this needs to be a host/portnumber pair. Maybe this should be + * automatically generated by the routine... + */ +{ + Ipc_Status_t status; + char batch_filename [1025]; + + mode = m; + protocol = p; + end_of_deck_seen = IPC_FALSE; + + num_records = 0; + fill_count = 0; + + status = ipc_transport_initialize_server (server_name, m, p, + batch_filename); + + if (status != IPC_STATUS_OK) { + fprintf (stderr, "ERROR: IPC: error initializing server\n"); + return IPC_STATUS_ERROR; + } + + if (mode == IPC_MODE_BATCH) { +#ifdef IPC_AEGIS_MAILBOXES + strcat (batch_filename, ".log"); +#endif + batch_fd = open (batch_filename, O_WRONLY | O_CREAT, 0666); + if (batch_fd < 0) { + // fprintf (stderr, "ERROR: IPC: Error opening batch output file: %s\n",batch_filename); + perror ("IPC"); + return IPC_STATUS_ERROR; + } + } + return status; +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_terminate_server + +This function deallocates the interprocess communication channel +mailbox or socket. +*/ + +Ipc_Status_t ipc_transport_terminate_server (); + +Ipc_Status_t ipc_terminate_server () +{ + return ipc_transport_terminate_server (); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_get_line + +This function gets a SPICE deck input line from the interprocess +communication channel. Any special control commands in the deck +beginning with a ``>'' or ``#'' character are processed internally by +this function and not returned to SPICE. +*/ + +Ipc_Status_t ipc_get_line (str, len, wait) + char *str; /* Text retrieved from IPC channel */ + int *len; /* Length of text string */ + Ipc_Wait_t wait; /* Select blocking or non-blocking */ + /* + * Reads one SPICE line from the connection. Strips any control lines + * which cannot be interpretted by the simulator (e.g. >INQCON) and + * processes them. If such a line is read, it is processed and the next + * line is read. `ipc_get_line' does not return until a non-interceptable + * line is read or end of file. + * + * If `wait' is IPC_NO_WAIT and there is no data available on the + * connection, `ipc_get_line' returns IPC_STATUS_NO_DATA. If `wait' is + * IPC_WAIT, `ipc_get_line' will not return until there is data available + * or and end of file condition is reached or an error occurs. + * + * Intercepts and processes the following commands: + * #RETURNI, #MINTIME, #VTRANS, + * >PAUSE, >CONT, >STOP, >INQCON, >NETLIST, >ENDNET + * Other > records are silently ignored. + * + * Intercepts old-style .TEMP card generated by MSPICE + * + * Returns: + * IPC_STATUS_OK - for successful reads + * IPC_STATUS_NO_DATA - when NO_WAIT and no data available + * IPC_STATUS_END_OF_DECK - at end of deck (>ENDNET seen) + * IPC_STATUS_ERROR - otherwise + */ +{ + Ipc_Status_t status; + Ipc_Boolean_t need_another = IPC_TRUE; + + do { + + status = ipc_transport_get_line (str, len, wait); + + switch (status) { + case IPC_STATUS_NO_DATA: + case IPC_STATUS_ERROR: + need_another = IPC_FALSE; + break; + case IPC_STATUS_END_OF_DECK: + assert (0); /* should never get this from the low-level get-line */ + status = IPC_STATUS_ERROR; + need_another = IPC_FALSE; + break; + case IPC_STATUS_OK: + /* + * Got a good line - check to see if it's one of the ones we need to + * intercept + */ + if (str[0] == '>') { + if (kw_match (">STOP", str)) { + ipc_handle_stop(); + } else if (kw_match (">PAUSE", str)) { + /* assert (need_another); */ + /* + * once more around the loop to do a blocking wait for the >CONT + */ + need_another = IPC_TRUE; + wait = IPC_WAIT; + } else if (kw_match (">INQCON", str)) { + ipc_send_line (">ABRTABL"); + ipc_send_line (">PAUSABL"); + ipc_send_line (">KEEPABL"); + status = ipc_flush (); + if (IPC_STATUS_OK != status) { + need_another = IPC_FALSE; + } + } else if (kw_match (">ENDNET", str)) { + end_of_deck_seen = IPC_TRUE; + need_another = IPC_FALSE; + status = IPC_STATUS_END_OF_DECK; + } else { + /* silently ignore */ + } + } else if (str[0] == '#') { + if (kw_match ("#RETURNI", str)) { + ipc_handle_returni (); + } else if (kw_match ("#MINTIME", str)) { + double d1/*,d2*/; + if (1 != sscanf (&str[8], "%lg", &d1)) { + status = IPC_STATUS_ERROR; + need_another = IPC_FALSE; + } else { + ipc_handle_mintime (d1); + } + } else if (kw_match ("#VTRANS", str)) { + char *tok1; + char *tok2; + char *tok3; + + tok1 = &str[8]; + for (tok2 = tok1; *tok2; tok2++) { + if (isspace(*tok2)) { + *tok2 = '\0'; + tok2++; + break; + } + } + for(tok3 = tok2; *tok3; tok3++) { + if(isspace(*tok3)) { + *tok3 = '\0'; + break; + } + } + ipc_handle_vtrans (tok1, tok2); + } else { + /* silently ignore */ + } + } else if (str[0] == '.') { + if (kw_match (".TEMP", str)) { + /* don't pass .TEMP card to caller */ + printf("Old-style .TEMP card found - ignored\n"); + } + else { + /* pass all other . cards to the caller */ + need_another = IPC_FALSE; + } + } else { + /* + * Not a '>' or '#' record - let the caller deal with it + */ + need_another = IPC_FALSE; + } + break; + default: + /* + * some unknown status value! + */ + assert (0); + status = IPC_STATUS_ERROR; + need_another = IPC_FALSE; + break; + } + } while (need_another); + + return status; +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_flush + +This function flushes the interprocess communication channel +buffer contents. +*/ + +Ipc_Status_t ipc_flush () + /* + * Flush all buffered messages out the connection. + */ +{ + Ipc_Status_t status; + int last = 0; + /*int bytes;*/ + int i; + + /* if batch mode */ + if (mode == IPC_MODE_BATCH) { + + assert (batch_fd >= 0); + + /* for number of records in buffer */ + for (i = 0; i < num_records; i++) { + + /* write the records to the .log file */ + if ((end_of_record_index [i] - last) != + write (batch_fd, &out_buffer[last], end_of_record_index [i] - last)) { + // fprintf (stderr,"ERROR: IPC: Error writing to batch output file\n"); + perror ("IPC"); + return IPC_STATUS_ERROR; + } + + /* If the record is one of the batch simulation status messages, */ + /* send it over the ipc channel too */ + if( kw_match("#ERRCHK", &out_buffer[last]) || + kw_match(">ENDANAL", &out_buffer[last]) || + kw_match(">ABORTED", &out_buffer[last]) ) { + + status = ipc_transport_send_line (&out_buffer[last], + end_of_record_index [i] - last); + if (IPC_STATUS_OK != status) { + return status; + } + } + last = end_of_record_index [i]; + } + + /* else, must be interactive mode */ + } else { + /* send the full buffer over the ipc channel */ + status = ipc_transport_send_line (&out_buffer[0], + end_of_record_index [num_records - 1]); + if (IPC_STATUS_OK != status) { + return status; + } + } + + /* reset counts to zero and return */ + num_records = 0; + fill_count = 0; + return IPC_STATUS_OK; +} + +/*---------------------------------------------------------------------------*/ +Ipc_Status_t ipc_send_line_binary (str, len) + char *str; + int len; + /* + * Same as `ipc_send_line' except does not expect the str to be null + * terminated. Sends exactly `len' characters. Use this for binary data + * strings that may have embedded nulls. + * + * Modified by wbk to append newlines for compatibility with + * ATESSE 1.0 + * + */ +{ + int length = len + 1; + /*int diff;*/ + Ipc_Status_t status; + + /* + * If we can't add the whole str to the buffer, or if there are no more + * record indices free, flush the buffer: + */ + if (((fill_count + length) >= OUT_BUFFER_SIZE) || + (num_records >= MAX_NUM_RECORDS)) { + status = ipc_flush (); + if (IPC_STATUS_OK != status) { + return status; + } + } + + /* + * make sure that the str will fit: + */ + if (length + fill_count > OUT_BUFFER_SIZE) { + // fprintf (stderr,"ERROR: IPC: String too long to fit in output buffer (> %d bytes) - truncated\n",OUT_BUFFER_SIZE); + length = OUT_BUFFER_SIZE - fill_count; + } + + /* + * finally, concatenate the str to the end of the buffer and add the newline: + */ + memcpy (&out_buffer[fill_count], str, len); + fill_count += len; + + out_buffer[fill_count] = '\n'; + fill_count++; + + end_of_record_index [num_records++] = fill_count; + + return IPC_STATUS_OK; +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_line + +This function sends a line of text over the interprocess +communication channel. +*/ + + +Ipc_Status_t ipc_send_line (str) + char *str; /* The text to send */ +{ + int len; + int send_len; + + char *s; + + Ipc_Status_t status= IPC_STATUS_OK; + + + len = strlen(str); + + /* if short string, send it immediately */ + if(len < 80) + status = ipc_send_line_binary (str, len); + else { + /* otherwise, we have to send it as multiple strings */ + /* because Mspice cannot handle things longer than 80 chars */ + s = str; + while(len > 0) { + if(len < 80) + send_len = len; + else + send_len = 79; + status = ipc_send_line_binary (str, send_len); + if(status != IPC_STATUS_OK) + break; + s += send_len; + len -= send_len; + } + } + + return(status); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_data_prefix + +This function sends a ``>DATAB'' line over the interprocess +communication channel to signal that this is the beginning of a +results dump for the current analysis point. +*/ + +Ipc_Status_t ipc_send_data_prefix (time) + double time; /* The analysis point for this data set */ +{ + char buffer[40]; + + sprintf (buffer, ">DATAB %.5E", time); + return ipc_send_line (buffer); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_data_suffix + +This function sends a ``>ENDDATA'' line over the interprocess +communication channel to signal that this is the end of a results +dump from a particular analysis point. +*/ + + +Ipc_Status_t ipc_send_data_suffix () +{ + Ipc_Status_t status; + + status = ipc_send_line (">ENDDATA"); + + if(status != IPC_STATUS_OK) + return(status); + + return(ipc_flush()); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_dcop_prefix + +This function sends a ``>DCOPB'' line over the interprocess +communication channel to signal that this is the beginning of a +results dump from a DC operating point analysis. +*/ + +Ipc_Status_t ipc_send_dcop_prefix () +{ + return ipc_send_line (">DCOPB"); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_dcop_suffix + +This function sends a ``>ENDDATA'' line over the interprocess +communication channel to signal that this is the end of a results +dump from a particular analysis point. +*/ + + +Ipc_Status_t ipc_send_dcop_suffix () +{ + Ipc_Status_t status; + + status = ipc_send_line (">ENDDCOP"); + + if(status != IPC_STATUS_OK) + return(status); + + return(ipc_flush()); +} + + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_evtdict_prefix + +This function sends a ``>EVTDICT'' line over the interprocess +communication channel to signal that this is the beginning of an +event-driven node dictionary. + +The line is sent only if the IPC is configured +for UNIX sockets, indicating use with the V2 ATESSE SI process. +*/ + +Ipc_Status_t ipc_send_evtdict_prefix () +{ +#ifdef IPC_AEGIS_MAILBOXES + return IPC_STATUS_OK; +#else + return ipc_send_line (">EVTDICT"); +#endif +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_evtdict_suffix + +This function sends a ``>ENDDICT'' line over the interprocess +communication channel to signal that this is the end of an +event-driven node dictionary. + +The line is sent only if the IPC is configured +for UNIX sockets, indicating use with the V2 ATESSE SI process. +*/ + + +Ipc_Status_t ipc_send_evtdict_suffix () +{ +#ifdef IPC_AEGIS_MAILBOXES + return IPC_STATUS_OK; +#else + Ipc_Status_t status; + + status = ipc_send_line (">ENDDICT"); + + if(status != IPC_STATUS_OK) + return(status); + + return(ipc_flush()); +#endif +} + + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_evtdata_prefix + +This function sends a ``>EVTDATA'' line over the interprocess +communication channel to signal that this is the beginning of an +event-driven node data block. + +The line is sent only if the IPC is configured +for UNIX sockets, indicating use with the V2 ATESSE SI process. +*/ + +Ipc_Status_t ipc_send_evtdata_prefix () +{ +#ifdef IPC_AEGIS_MAILBOXES + return IPC_STATUS_OK; +#else + return ipc_send_line (">EVTDATA"); +#endif +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_evtdata_suffix + +This function sends a ``>ENDDATA'' line over the interprocess +communication channel to signal that this is the end of an +event-driven node data block. + +The line is sent only if the IPC is configured +for UNIX sockets, indicating use with the V2 ATESSE SI process. +*/ + + +Ipc_Status_t ipc_send_evtdata_suffix () +{ +#ifdef IPC_AEGIS_MAILBOXES + return IPC_STATUS_OK; +#else + Ipc_Status_t status; + + status = ipc_send_line (">ENDDATA"); + + if(status != IPC_STATUS_OK) + return(status); + + return(ipc_flush()); +#endif +} + + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_errchk + +This function sends a ``\ERRCHK [GO|NOGO]'' message over the +interprocess communication channel to signal that the initial +parsing of the input deck has been completed and to indicate +whether or not errors were detected. +*/ + + +Ipc_Status_t ipc_send_errchk() +{ + char str[IPC_MAX_LINE_LEN+1]; + Ipc_Status_t status; + + if(g_ipc.errchk_sent) + return(IPC_STATUS_OK); + + if(g_ipc.syntax_error) + sprintf(str, "#ERRCHK NOGO"); + else + sprintf(str, "#ERRCHK GO"); + + g_ipc.errchk_sent = IPC_TRUE; + + status = ipc_send_line(str); + if(status != IPC_STATUS_OK) + return(status); + + return(ipc_flush()); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_end + +This function sends either an ``>ENDANAL'' or an ``>ABORTED'' message +over the interprocess communication channel together with the +total CPU time used to indicate whether or not the simulation +completed normally. +*/ + + +Ipc_Status_t ipc_send_end() +{ + char str[IPC_MAX_LINE_LEN+1]; + Ipc_Status_t status; + + if(g_ipc.syntax_error || g_ipc.run_error) + sprintf(str, ">ABORTED %.4f", g_ipc.cpu_time); + else + sprintf(str, ">ENDANAL %.4f", g_ipc.cpu_time); + + status = ipc_send_line(str); + if(status != IPC_STATUS_OK) + return(status); + + return(ipc_flush()); +} + + +/*---------------------------------------------------------------------------*/ +int stuff_binary_v1 (d1, d2, n, buf, pos) + double d1, d2; /* doubles to be stuffed */ + int n; /* how many of d1, d2 ( 1 <= n <= 2 ) */ + char *buf; /* buffer to stuff to */ + int pos; /* index at which to stuff */ +{ + union { + float float_val[2]; + char ch[32]; + } trick; + int i, j; + + assert (protocol == IPC_PROTOCOL_V1); + assert (sizeof(float) == 4); + assert (sizeof(char) == 1); + assert ((n >= 1) && (n <= 2)); + + trick.float_val[0] = d1; + if (n > 1) { + trick.float_val[1] = d2; + } + for (i = 0, j = pos; i < n*sizeof(float); j++, i++) + buf[j] = trick.ch[i]; + i = sizeof(float)*n + pos; + buf[0] = 'A' + i - 1; + return i; +} + +/*---------------------------------------------------------------------------*/ + + +/* +ipc_send_double + +This function sends a double data value over the interprocess +communication channel preceded by a character string that +identifies the simulation variable. +*/ + +Ipc_Status_t ipc_send_double (tag, value) + char *tag; /* The node or instance */ + double value; /* The data value to send */ +{ + int i; + int len = 0; + int fmt_buffer_len; + + switch (protocol) { + case IPC_PROTOCOL_V1: + strcpy (fmt_buffer, " "); /* save room for the length byte */ + strcat (fmt_buffer, tag); + strcat (fmt_buffer, " "); + + /* If talking to Mentor tools, must force upper case for Mspice 7.0 */ + fmt_buffer_len = strlen(fmt_buffer); + for(i = 0; i < fmt_buffer_len; i++) { + if(islower(fmt_buffer[i])) + fmt_buffer[i] = toupper(fmt_buffer[i]); + } + + len = stuff_binary_v1 (value, 0.0, 1, fmt_buffer, strlen(fmt_buffer)); + break; + case IPC_PROTOCOL_V2: + break; + } + return ipc_send_line_binary (fmt_buffer, len); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_complex + +This function sends a complex data value over the interprocess +communication channel preceded by a character string that +identifies the simulation variable. +*/ + + +Ipc_Status_t ipc_send_complex (tag, value) + char *tag; /* The node or instance */ + Ipc_Complex_t value; /* The data value to send */ +{ + int i; + int len=0; + int fmt_buffer_len; + + switch (protocol) { + case IPC_PROTOCOL_V1: + strcpy (fmt_buffer, " "); /* save room for the length byte */ + strcat (fmt_buffer, tag); + strcat (fmt_buffer, " "); + + /* If talking to Mentor tools, must force upper case for Mspice 7.0 */ + fmt_buffer_len = strlen(fmt_buffer); + for(i = 0; i < fmt_buffer_len; i++) { + if(islower(fmt_buffer[i])) + fmt_buffer[i] = toupper(fmt_buffer[i]); + } + + len = stuff_binary_v1 (value.real, value.imag, 2, fmt_buffer, + strlen(fmt_buffer)); + break; + case IPC_PROTOCOL_V2: + break; + } + return ipc_send_line_binary (fmt_buffer, len); +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_send_event + +This function sends data from an event-driven node over the interprocess +communication channel. The data is sent only if the IPC is configured +for UNIX sockets, indicating use with the V2 ATESSE SI process. +*/ + + +Ipc_Status_t ipc_send_event(ipc_index, step, plot_val, print_val, ipc_val, len) + int ipc_index; /* Index used in EVTDICT */ + double step; /* Analysis point or timestep (0.0 for DC) */ + double plot_val; /* The value for plotting purposes */ + char *print_val; /* The value for printing purposes */ + void *ipc_val; /* The binary representation of the node data */ + int len; /* The length of the binary representation */ +{ +#ifdef IPC_AEGIS_MAILBOXES + return IPC_STATUS_OK; +#else + char buff[OUT_BUFFER_SIZE]; + int i; + int buff_len; + char *buff_ptr; + char *temp_ptr; + float fvalue; + + /* Report error if size of data is too big for IPC channel block size */ + if((len + strlen(print_val) + 100) >= OUT_BUFFER_SIZE) { + printf("ERROR - Size of event-driven data too large for IPC channel\n"); + return IPC_STATUS_ERROR; + } + + /* Place the index into the buffer with a trailing space */ + sprintf(buff, "%d ", ipc_index); + + assert(sizeof(float) == 4); + assert(sizeof(int) == 4); + + /* Put the analysis step bytes in */ + buff_len = strlen(buff); + buff_ptr = buff + buff_len; + fvalue = step; + temp_ptr = (char *) &fvalue; + for(i = 0; i < 4; i++) { + *buff_ptr = temp_ptr[i]; + buff_ptr++; + buff_len++; + } + + /* Put the plot value in */ + fvalue = plot_val; + temp_ptr = (char *) &fvalue; + for(i = 0; i < 4; i++) { + *buff_ptr = temp_ptr[i]; + buff_ptr++; + buff_len++; + } + + /* Put the length of the binary representation in */ + temp_ptr = (char *) &len; + for(i = 0; i < 4; i++) { + *buff_ptr = temp_ptr[i]; + buff_ptr++; + buff_len++; + } + + /* Put the binary representation bytes in last */ + temp_ptr = ipc_val; + for(i = 0; i < len; i++) + buff_ptr[i] = temp_ptr[i]; + buff_ptr += len; + buff_len += len; + + /* Put the print value in */ + strcpy(buff_ptr, print_val); + buff_ptr += strlen(print_val); + buff_len += strlen(print_val); + + /* Send the data to the IPC channel */ + return ipc_send_line_binary(buff, buff_len); + +#endif +} + + diff --git a/src/xspice/ipc/ipcaegis.c b/src/xspice/ipc/ipcaegis.c new file mode 100755 index 000000000..c86cb7216 --- /dev/null +++ b/src/xspice/ipc/ipcaegis.c @@ -0,0 +1,307 @@ +/*============================================================================ +FILE IPCaegis.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Steve Tynor + +MODIFICATIONS + + + +SUMMARY + + Provides compatibility for the new XSPICE simulator to both the MSPICE user + interface and BCP via ATESSE v.1 style AEGIS mailboxes. + +INTERFACES + + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#ifdef IPC_AEGIS_MAILBOXES + +#include +#include +#include +#include +#include +#include + +#include "ipc.h" + + +typedef unsigned char Buffer_char_t; + +static status_$t status; +typedef enum { + IPC_MBX_UNINITIALIZED, + IPC_MBX_INITIALIZED, + IPC_MBX_CONNECTED_TO_CLIENT, +} Ipc_Mbx_State_t; + +static void *mbx_handle; +static Ipc_Mbx_State_t mbx_state = IPC_MBX_UNINITIALIZED; +static mbx_$server_msg_t mbx_send_msg_buf; +static mbx_$server_msg_t mbx_recieve_msg_buf; +static mbx_$server_msg_t *mbx_ret_ptr; +static int mbx_ret_len; +static short mbx_chan; + +#include "ipcproto.h" + +/*---------------------------------------------------------------------------*/ + +/* +ipc_transport_initialize_server + +This function creates an Aegis mailbox, and if successful, +calls ipc_get_line to wait for the first record sent which is +assumed to be the batch output filename. +*/ + + + +Ipc_Status_t ipc_transport_initialize_server (server_name, m, p, + batch_filename) + char *server_name; /* The mailbox pathname */ + Ipc_Mode_t m; /* Mode - interactive or batch */ + Ipc_Protocol_t p; /* Protocol type */ + char *batch_filename; /* Batch filename returned */ +{ + int len; + // extern void *malloc(); + + assert (p == IPC_PROTOCOL_V1); + + mbx_$create_server (server_name, strlen (server_name), mbx_$serv_msg_max, + 1, &mbx_handle, &status); + + if (status.all != status_$ok) { + fprintf (stderr, + "ERROR: IPC: Error creating mailbox server \"%s\"\n", + server_name); + error_$print (status); + mbx_state = IPC_MBX_UNINITIALIZED; + return IPC_STATUS_ERROR; + } else { + mbx_state = IPC_MBX_INITIALIZED; + /* + * First record is the name of the batch filename - whether we're in + * batch mode or not: + */ + return ipc_get_line (batch_filename, &len, IPC_WAIT); + } + /* + * shouldn't get here + */ + assert (0); + return IPC_STATUS_ERROR; +} +/*---------------------------------------------------------------------------*/ +Ipc_Status_t extract_msg (str, len) + char *str; + int *len; +{ + *len = mbx_ret_len - mbx_$serv_msg_hdr_len; + assert (*len >= 0); + + /* + * null terminate before copy: + */ + mbx_ret_ptr->data [*len] = '\0'; + strcpy (str, mbx_ret_ptr->data); + + return IPC_STATUS_OK; +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_transport_get_line + +This function reads data sent by a client over the mailbox +channel. It also handles the initial opening of the +mailbox channel when requested by a client. +*/ + + + +Ipc_Status_t ipc_transport_get_line (str, len, wait) + char *str; /* The string text read from IPC channel */ + int *len; /* The length of str */ + Ipc_Wait_t wait; /* Blocking or non-blocking */ +{ + if (mbx_state == IPC_MBX_UNINITIALIZED) { + fprintf (stderr, + "ERROR: IPC: Attempted to read from non-initialized mailbox\n"); + return IPC_STATUS_ERROR; + } + + assert ((mbx_state == IPC_MBX_CONNECTED_TO_CLIENT) || + (mbx_state == IPC_MBX_INITIALIZED)); + + do { + if (wait == IPC_WAIT) { + mbx_$get_rec (mbx_handle, &mbx_recieve_msg_buf, mbx_$serv_msg_max, + &mbx_ret_ptr, &mbx_ret_len, &status); + } else { + mbx_$get_conditional (mbx_handle, &mbx_recieve_msg_buf, + mbx_$serv_msg_max, &mbx_ret_ptr, &mbx_ret_len, + &status); + if (status.all == mbx_$channel_empty) { + return IPC_STATUS_NO_DATA; + } + } + + if (status.all != status_$ok) { + fprintf (stderr, "ERROR: IPC: Error reading from mailbox\n"); + error_$print (status); + return IPC_STATUS_ERROR; + } + + switch (mbx_ret_ptr->mt) { + case mbx_$channel_open_mt: + if (mbx_state == IPC_MBX_CONNECTED_TO_CLIENT) { + /* + * we're already connected to a client... refuse the connection + */ + mbx_send_msg_buf.mt = mbx_$reject_open_mt; + } else { + mbx_send_msg_buf.mt = mbx_$accept_open_mt; + mbx_state = IPC_MBX_CONNECTED_TO_CLIENT; + } + mbx_send_msg_buf.cnt = mbx_$serv_msg_hdr_len; + mbx_chan = mbx_ret_ptr->chan; + mbx_send_msg_buf.chan = mbx_chan; + + mbx_$put_rec (mbx_handle, &mbx_send_msg_buf, mbx_$serv_msg_hdr_len, + &status); + + if (status.all != status_$ok) { + fprintf (stderr, "ERROR: IPC: Error writing to mailbox\n"); + error_$print (status); + return IPC_STATUS_ERROR; + } + + /* + * check to see if there was a message buried in the open request: + */ + if (mbx_ret_len > mbx_$serv_msg_hdr_len) { + return extract_msg (str, len); + } + break; + case mbx_$eof_mt: + mbx_chan = mbx_ret_ptr->chan; + mbx_$deallocate(mbx_handle, mbx_chan, &status); + + if (status.all != status_$ok) { + fprintf (stderr, "ERROR: IPC: Error deallocating mailbox\n"); + error_$print (status); + return IPC_STATUS_ERROR; + } + + mbx_state = IPC_MBX_INITIALIZED; + return IPC_STATUS_EOF; + break; + case mbx_$data_mt: + assert (mbx_state == IPC_MBX_CONNECTED_TO_CLIENT); + return extract_msg (str, len); + break; + case mbx_$data_partial_mt: + fprintf (stderr, "ERROR: IPC: Recieved partial data message - ignored\n"); + break; + default: + fprintf (stderr, "ERROR: IPC: Bad message type (0x%x) recieved\n", + mbx_ret_ptr->mt); + } + } while (1); + return IPC_STATUS_ERROR; +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_transport_terminate_server + +This function calls ipc\_transport\_get\_line until it +receives an EOF from the client, which concludes the +communication. +*/ + + +Ipc_Status_t ipc_transport_terminate_server () +{ + char buffer[300]; + int len; + Ipc_Status_t status; + + do { + status = ipc_transport_get_line (buffer, &len, IPC_WAIT); + } while ((status =! IPC_STATUS_ERROR) && + (status =! IPC_STATUS_EOF)); + return status; +} + +/*---------------------------------------------------------------------------*/ + +/* +ipc_transport_send_line + +This function sends a message to the current client through +the mailbox channel. +*/ + + +Ipc_Status_t ipc_transport_send_line (str, len) + char *str; /* The bytes to send */ + int len; /* The number of bytes from str to send */ +{ + long cnt; + + if (mbx_state != IPC_MBX_CONNECTED_TO_CLIENT) { + fprintf (stderr, + "ERROR: IPC: Attempted to write to non-open mailbox\n"); + return IPC_STATUS_ERROR; + } + + mbx_send_msg_buf.mt = mbx_$data_mt; + if (mbx_$serv_msg_hdr_len + len > mbx_$serv_msg_max) { + fprintf (stderr, + "ERROR: IPC: send_line message too long - truncating\n"); + len = mbx_$serv_msg_max - mbx_$serv_msg_hdr_len; + } + + mbx_send_msg_buf.cnt = mbx_$serv_msg_hdr_len + len; + mbx_send_msg_buf.chan = mbx_chan; + memcpy (mbx_send_msg_buf.data, str, len); + + cnt = mbx_send_msg_buf.cnt; + mbx_$put_rec (mbx_handle, &mbx_send_msg_buf, cnt, &status); + + if (status.all != status_$ok) { + fprintf (stderr, "ERROR: IPC: Error writing to mailbox\n"); + error_$print (status); + return IPC_STATUS_ERROR; + } + return IPC_STATUS_OK; +} + +#endif /* IPC_AEGIS_MAILBOXES */ diff --git a/src/xspice/ipc/ipcsockets.c b/src/xspice/ipc/ipcsockets.c new file mode 100755 index 000000000..8c4efeba9 --- /dev/null +++ b/src/xspice/ipc/ipcsockets.c @@ -0,0 +1,744 @@ +/*============================================================================= + + FILE IPCsockets.c + + Copyright 1991 + Georgia Tech Research Corporation, Atlanta, Georgia 30332 + All Rights Reserved + + + PROJECT ATESSE A-8503 + + AUTHOR + Stefan Roth July 1991 + + MODIFICATIONS + none + + SUMMARY + Generic Interprocess Communication module + Provides compatibility for the new SPICE simulator to both the MSPICE user + interface and BCP (via ATESSE v.1 style AEGIS mailboxes) and the new ATESSE + v.2 Simulator Interface and BCP (via BSD Sockets). This file contains the + BSD sockets version. + The Simulator is the server, while the SI and BCP will be the clients. + + + INTERFACES + + FILE ROUTINE CALLED + + IPC.c ipc_get_line(); + + + REFERENCED FILES + + Outputs to stderr. + + +=============================================================================*/ + + +/*============================================================================= + + DESCRIPTION OF FUNCTIONALITY: + + Outline of Initialize_Server function: + create socket; + bind name to socket; + getsockname; + listen; + sock_state = IPC_SOCK_INITIALIZED; + return ipc_get_line (); + + + Format of a message line: + bytes description + ----- ------------------- + 0 recognition character for beginning of line; value is BOL_CHAR. + 1-4 message length (not including bytes 0-4); 32 bits in htonl + format; + if value = -1, then EOF and socket should be closed. + 5-N+5 message body of length specified in bytes 1-4. + + The bytes before the message body are the message header. The header + length is specified as SOCK_MSG_HDR_LEN bytes. + + + Outline of Get_Line function: + read 5 characters; + verify that first char is BOL_CHAR; + interpret message length (N) from bytes 1-4; + do error checking on message header bytes; + read N characters as message body; + do error checking on message body read; + + + Outline of Send_Line function: + write BOL_CHAR; + write 4-byte message body length + write message body (N bytes) + do error checking after each write operation + + + Outline of Terminate_Server function: + Continue to read lines (with ipc_transport_get_line) and ignore + them until socket EOF is reached; + Close the socket. + + +=============================================================================*/ + + +/*#ifdef IPC_UNIX_SOCKETS */ + +/*=== INCLUDE FILES ===*/ +#include "ngspice.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipc.h" +#include "ipctiein.h" + + + + +/*=== TYPE DEFINITIONS ===*/ + +typedef enum { + IPC_SOCK_UNINITIALIZED, + IPC_SOCK_INITIALIZED, + IPC_SOCK_CONNECTED_TO_CLIENT +} Ipc_Sock_State_t; + + +/*=== LOCAL VARIABLES ===*/ + +static int sock_desc; /* socket descriptor */ +static int msg_stream; /* socket stream */ +static Ipc_Sock_State_t sock_state = IPC_SOCK_UNINITIALIZED; + + +/*=== INCLUDE FILES ===*/ + +#include "ipcproto.h" + +/*============================================================================= + +FUNCTION ipc_transport_initialize_server + +AUTHORS + + July 1991 Stefan Roth + +MODIFICATIONS + + NONE + +SUMMARY + + Creates and opens the BSD socket of the server. Listens for requests + by a client and then reads the first line message. + +INTERFACES + + Called by: (IPC.c) ipc_initialize_server(); + +RETURNED VALUE + + Ipc_Status_t - returns status of the socket connection. + +GLOBAL VARIABLES + + NONE + +NON-STANDARD FEATURES + + NONE + +=============================================================================*/ + +Ipc_Status_t ipc_transport_initialize_server (server_name, mode, protocol, + batch_filename) + char *server_name; /* not used */ + Ipc_Mode_t mode; /* not used */ + Ipc_Protocol_t protocol; /* IN - only used in assert */ + char *batch_filename; /* OUT - returns a value */ + /* Note that unused parameters are required to maintain compatibility */ + /* with version 1 (mailboxes) functions of the same names. */ +{ + struct sockaddr_in server; /* Server specifications for socket*/ + int server_length; /* Size of server structure */ + unsigned int port_num; /* Port number converted from server_name */ + + Ipc_Status_t ipc_get_line (); + + /* assert (protocol == IPC_PROTOCOL_V2); */ /* allow v1 protocol - wbk */ + assert (sock_state == IPC_SOCK_UNINITIALIZED); + + /* convert server_name (from atesse_xspice invocation line) to a port */ + /* number */ + port_num = atoi(server_name); + if((port_num > 0) && (port_num < 1024)) { + /* Reserved port number */ + perror ("ERROR: IPC Port numbers below 1024 are reserved"); + sock_state = IPC_SOCK_UNINITIALIZED; + return IPC_STATUS_ERROR; + } + + + sock_desc = socket (AF_INET, SOCK_STREAM, 0); + + if (sock_desc < 0) { + /* Unsuccessful socket opening */ + perror ("ERROR: IPC Creating socket"); + sock_state = IPC_SOCK_UNINITIALIZED; + return IPC_STATUS_ERROR; + } + + /* Socket opened successfully */ + + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = port_num; /* SOCKET_PORT; */ + + server_length = sizeof (server); + if (bind (sock_desc, (struct sockaddr *)&server, server_length) + < 0) { + fprintf (stderr, "ERROR: IPC: Bind unsuccessful\n"); + perror ("ERROR: IPC"); + sock_state = IPC_SOCK_UNINITIALIZED; + return IPC_STATUS_ERROR; + } + + if (getsockname (sock_desc, (struct sockaddr *)&server, &server_length) + < 0) { + fprintf (stderr, "ERROR: IPC: getting socket name\n"); + perror ("ERROR: IPC"); + sock_state = IPC_SOCK_UNINITIALIZED; + return IPC_STATUS_ERROR; + } + + fprintf (stderr, "Socket port %d.\n", ntohs(server.sin_port)); + + listen (sock_desc, 5); + + sock_state = IPC_SOCK_INITIALIZED; + + /* Socket ok to use now */ + + /* + * First record is the name of the batch filename if we're in batch mode. + */ + + if(g_ipc.mode == IPC_MODE_BATCH) { + int len; + return ipc_get_line (batch_filename, &len, IPC_WAIT); + } + + /* Return success */ + return IPC_STATUS_OK; + +} /* end ipc_transport_initialize_server */ + + + +/*============================================================================= + +FUNCTION bytes_to_integer + +AUTHORS + + July 1991 Stefan Roth + +MODIFICATIONS + + NONE + +SUMMARY + + Convert four bytes at START in the string STR + to a 32-bit unsigned integer. The string is assumed + to be in network byte order and the returned value + is converted to host byte order (with ntohl). + +INTERFACES + + Local to this file. + Called by: ipc_transport_get_line(); + +RETURNED VALUE + + u_long - unsigned 32 bit integer + +GLOBAL VARIABLES + + NONE + +NON-STANDARD FEATURES + + NONE + +=============================================================================*/ + +static u_long bytes_to_integer (str, start) + char *str; /* IN - string that contains the bytes to convert */ + int start; /* IN - index into string where bytes are */ +{ + u_long u; /* Value to be returned */ + char buff[4]; /* Transfer str into buff to word align reqd data */ + int index; /* Index into str and buff for transfer */ + + /* Get the str+start character and cast it into a u_long and put + the value through the network-to-host-short converter and store + it in the variable u. */ + + index = 0; + while (index < sizeof(u)) { + buff[index] = str[index+start]; + index++; + } + u = ntohl (*((u_long *) buff)); + + return u; +} /* end bytes_to_integer */ + + + +/*============================================================================= + +FUNCTION handle_socket_eof + +AUTHORS + + July 1991 Stefan Roth + +MODIFICATIONS + + NONE + +SUMMARY + + Do processing when the socket reaches EOF or when a message from the + client states that EOF has been reached. + +INTERFACES + + Local to this file. + Called by: ipc_transport_get_line(); + +RETURNED VALUE + + Ipc_Status_t - always IPC_STATUS_EOF + +GLOBAL VARIABLES + + NONE + +NON-STANDARD FEATURES + + NONE + +=============================================================================*/ + + +static Ipc_Status_t handle_socket_eof () +{ + close (msg_stream); + close (sock_desc); + + sock_state = IPC_SOCK_UNINITIALIZED; + + return IPC_STATUS_EOF; +} /* handle_socket_eof */ + + + +/*============================================================================= + +FUNCTION read_sock + +AUTHORS + + July 1991 Stefan Roth + +MODIFICATIONS + + NONE + +SUMMARY + + Read N bytes from a socket. Only returns when the read had an error, + when 0 bytes (EOF) could be read, or LENGTH bytes are read. + +INTERFACES + + Local to this file. + Called by: ipc_transport_get_line(); + +RETURNED VALUE + + int - Returns the total number of bytes read. + +GLOBAL VARIABLES + + NONE + +NON-STANDARD FEATURES + + NONE + +=============================================================================*/ + + +static int read_sock (stream, buffer, length, wait, flags) + int stream; /* IN - Socket stream */ + char *buffer; /* OUT - buffer to store incoming data */ + int length; /* IN - Number of bytes to be read */ + Ipc_Wait_t wait; /* IN - type of read operation */ + int flags; /* IN - Original socket flags for blocking read */ +{ + int count; /* Number of bytes read with last `read` */ + int totalcount; /* total number of bytes read */ + char *buf2; + +/* count = 0; */ +/* while (count < length) { */ +/* buffer[count] = 'x'; */ +/* count++; */ +/* } */ + count = read (stream, buffer, length); + if (wait == IPC_NO_WAIT) { + fcntl (stream, F_SETFL, flags); /* Revert to blocking read */ + } + if ((count <= 0) || (count == length)) { + /* If error or if read in reqd number of bytes: */ + return count; + } else { + /* Only got some of the bytes requested */ + totalcount = count; + buf2 = &buffer[totalcount]; + length = length - count; + while (length > 0) { + count = read (stream, buf2, length); + if (count <= 0) /* EOF or read error */ + break; + totalcount = totalcount + count; + buf2 = &buffer[totalcount]; + length = length - count; + } + if (length != 0) { + fprintf (stderr, "WARNING: READ_SOCK read %d bytes instead of %d\n", + totalcount, totalcount + length); + } + return totalcount; + } +} /* end read_sock */ + + + +/*============================================================================= + +FUNCTION ipc_transport_get_line + +AUTHORS + + July 1991 Stefan Roth + +MODIFICATIONS + + NONE + +SUMMARY + + Main function for reading one line from a socket. Requires that the + socket be open. Lines are mostly SPICE code, but commands may also + be embedded in the socket data and they are interpreted by this function. + Therefore, this function may cause the socket to be closed. + +INTERFACES + + Called by: ipc_transport_terminate_server(); + (IPC.c) ipc_get_line(); + +RETURNED VALUE + + Ipc_Status_t - returns status of the read operation + +GLOBAL VARIABLES + + NONE + +NON-STANDARD FEATURES + + NONE + +=============================================================================*/ + + +Ipc_Status_t ipc_transport_get_line (str, len, wait) + char *str; /* returns the result, null terminated */ + int *len; /* length of str passed IN and passed OUT */ + Ipc_Wait_t wait; /* IN - wait or dont wait on incoming msg */ +{ + int count; /* number of bytes read */ + int message_length; /* extracted from message header */ + + if (sock_state == IPC_SOCK_UNINITIALIZED) { + fprintf (stderr, + "ERROR: IPC: Attempted to read from uninitialized socket\n"); + return IPC_STATUS_ERROR; + } + + assert ((sock_state == IPC_SOCK_CONNECTED_TO_CLIENT) || + (sock_state == IPC_SOCK_INITIALIZED)); + + if (sock_state == IPC_SOCK_INITIALIZED) { + /* We have an open socket but have not connected to a client. */ + /* Accept a connection from a client. */ + msg_stream = accept (sock_desc, (struct sockaddr *)0, (int *)0); + + if (msg_stream == -1) { + fprintf (stderr, "ERROR: IPC: Server accepting request\n"); + perror ("ERROR: IPC"); + return IPC_STATUS_ERROR; + } + sock_state = IPC_SOCK_CONNECTED_TO_CLIENT; + } + /*-----------------------------------------------------------------------*/ + /* First read in the message header. */ + { + int flags; + flags = fcntl(msg_stream, F_GETFL, NULL); /* Blocking read mode */ + + if (wait == IPC_WAIT) { + /* Block here and wait for the next message */ + count = read_sock (msg_stream, str, SOCK_MSG_HDR_LEN, wait, flags); + if (count == 0) { + /* EOF, will this ever happen? */ + /* fprintf (stderr, "WARNING: IPC: Reached eof on socket\n"); */ + return handle_socket_eof (); + } + } else if (wait == IPC_NO_WAIT) { + /* Read message, but do not wait if none available. */ + + fcntl (msg_stream, F_SETFL, flags | O_NDELAY); + count = read_sock (msg_stream, str, SOCK_MSG_HDR_LEN, wait, flags); + + if (count == 0) { + /* EOF, will this ever happen? */ + /* fprintf (stderr, "WARNING: IPC: Reached eof on socket\n"); */ + return handle_socket_eof (); + } else if (count == -1) { + if (errno == EWOULDBLOCK) { + return IPC_STATUS_NO_DATA; + } + } + + } else { + /* Serious problem, since it is not reading anything. */ + fprintf (stderr, + "ERROR: IPC: invalid wait arg to ipc_transport_get_line\n"); + } + } + + /* Do more error checking on the read in values of the message header: */ + if (count == -1) { + fprintf (stderr, "ERROR: IPC: Reading from socket\n"); + perror ("ERROR: IPC"); + return IPC_STATUS_ERROR; + } else if (str[0] != BOL_CHAR) { + fprintf (stderr, + "ERROR: IPC: Did not find beginning of message header (%c)\n", + str[0]); + return IPC_STATUS_ERROR; + } else if ((message_length = bytes_to_integer (str, 1)) == -1) { + /* fprintf (stderr, "WARNING: IPC: Reached eof on socket\n"); */ + return handle_socket_eof (); + } else if (message_length == 0) { + *len = 0; + return IPC_STATUS_NO_DATA; + +/* Invalid test... delete - wbk + } else if (message_length > *len) { + fprintf (stderr, + "ERROR: IPC: Buffer (%d) is too short for message (%d)\n", + *len, message_length); + return IPC_STATUS_ERROR; +*/ + + } + + /*-----------------------------------------------------------------------*/ + /* Now read in the message body. */ + /* Always block here since the message header was already read and */ + /* we must get the body. */ + + *len = message_length; + count = read_sock (msg_stream, str, message_length); + if (count == 0) { + /* EOF, will this ever happen? */ + /* fprintf (stderr, */ + /* "WARNING: IPC: Reached eof in message body on socket\n");*/ + return handle_socket_eof (); + } else if (count == -1) { + fprintf (stderr, "ERROR: IPC: reading message body from socket\n"); + perror ("ERROR: IPC"); + return IPC_STATUS_ERROR; + } + + /* Looks like we have a valid message here. Put in the string terminator. */ + *len = count; + str[count] = '\0'; + + return IPC_STATUS_OK; + +} /* end ipc_transport_get_line */ + + + +/*============================================================================= + +FUNCTION ipc_transport_send_line + +AUTHORS + + July 1991 Stefan Roth + +MODIFICATIONS + + NONE + +SUMMARY + Send a line of information. First sends a message header and + then the actual message body. + Error checking is done to make reasonably sure that the data was sent. + + +INTERFACES + + Called by: (IPC.c) ipc_flush (); + +RETURNED VALUE + + Ipc_Status_t - returns status of the send operation (typically + IPC_STATUS_ERROR or IPC_STATUS_OK). + + +GLOBAL VARIABLES + + NONE + +NON-STANDARD FEATURES + + NONE + +=============================================================================*/ + + +Ipc_Status_t ipc_transport_send_line (str, len) + char *str; /* IN - String to write */ + int len; /* IN - Number of characters out of STR to write */ +{ + int count; /* Counts how many bytes were actually written */ + u_long u; /* 32-bit placeholder for transmission of LEN */ + char hdr_buff[5]; /* Buffer for building header message in */ + int i; /* Temporary counter */ + char *char_ptr; /* Pointer for int to bytes conversion */ + + if (sock_state != IPC_SOCK_CONNECTED_TO_CLIENT) { + fprintf (stderr, "ERROR: IPC: Attempt to write to non-open socket\n"); + return IPC_STATUS_ERROR; + } + + /* Write message body header with length: */ + hdr_buff[0] = BOL_CHAR; + u = htonl (len); + char_ptr = (char *) &u; + for(i = 0; i < 4; i++) + hdr_buff[i+1] = char_ptr[i]; + + count = write (msg_stream, hdr_buff, 5); + if (count != 5) { + fprintf (stderr, "ERROR: IPC: (%d) send line error 1\n", count); + return IPC_STATUS_ERROR; + } + + /* Write message body: */ + count = write (msg_stream, str, len); + if (count != len) { + fprintf (stderr, "ERROR: IPC: (%d) send line error 2\n", count); + return IPC_STATUS_ERROR; + } + + return IPC_STATUS_OK; +} /* end ipc_transport_send_line */ + + + +/*============================================================================= + +FUNCTION ipc_transport_terminate_server + +AUTHORS + + July 1991 Stefan Roth + +MODIFICATIONS + + NONE + +SUMMARY + + This function reads all pending incoming messages and discards them. + Reading continues until a read error occurs or EOF is reached, at which + time the socket is closed. + Note that this function does not actually close the socket. This is + done in ipc_transport_get_line, which is called in this function. + + In this function, the incoming line length is limited. See buffer below. + +INTERFACES + + Called by: (IPC.c) ipc_terminate_server(); + +RETURNED VALUE + + Ipc_Status_t - returns status of last read operation (always + IPC_STATUS_ERROR or IPC_STATUS_EOF). + +GLOBAL VARIABLES + + NONE + +NON-STANDARD FEATURES + + NONE + +=============================================================================*/ + + +Ipc_Status_t ipc_transport_terminate_server () +{ + char buffer[17000]; /* temp buffer for incoming data */ + int len; /* placeholder var to as arg to function */ + Ipc_Status_t status; /* value to be returned from function */ + int max_size; /* Max length of buffer */ + + max_size = sizeof (buffer); + do { + len = max_size; + status = ipc_transport_get_line (buffer, &len, IPC_WAIT); + } while ((status != IPC_STATUS_ERROR) && + (status != IPC_STATUS_EOF)); + return status; +} + +/*#endif IPC_UNIX_SOCKETS */ diff --git a/src/xspice/ipc/ipcstdio.c b/src/xspice/ipc/ipcstdio.c new file mode 100755 index 000000000..52598b240 --- /dev/null +++ b/src/xspice/ipc/ipcstdio.c @@ -0,0 +1,69 @@ +/* + * Steve Tynor + * + * Generic Interprocess Communication module + * + * Used for debugging in absense of IPC interface. + * + */ + +//#include + +#ifdef IPC_DEBUG_VIA_STDIO + +#include + + +#include "ipc.h" + +#include "ipcproto.h" + +#include /* 12/1/97 jg */ + +/*---------------------------------------------------------------------------*/ +Ipc_Status_t ipc_transport_initialize_server (server_name, m, p, + batch_filename) + char *server_name; + Ipc_Mode_t m; + Ipc_Protocol_t p; + char *batch_filename; +{ + assert (m == IPC_MODE_INTERACTIVE); + printf ("INITIALIZE_SERVER\n"); + return IPC_STATUS_OK; +} + +/*---------------------------------------------------------------------------*/ +Ipc_Status_t ipc_transport_get_line (str, len, wait) + char *str; + int *len; + Ipc_Wait_t wait; +{ + printf ("GET_LINE\n"); + gets (str); + *len = strlen (str); + return IPC_STATUS_OK; +} + +/*---------------------------------------------------------------------------*/ +Ipc_Status_t ipc_transport_send_line (str, len) + char *str; + int len; +{ + int i; + + printf ("SEND_LINE: /"); + for (i = 0; i < len; i++) + putchar (str[i]); + printf ("/\n"); + return IPC_STATUS_OK; +} + +/*---------------------------------------------------------------------------*/ +Ipc_Status_t ipc_transport_terminate_server () +{ +return IPC_STATUS_OK; +} + + +#endif /* IPC_DEBUG_VIA_STDIO */ diff --git a/src/xspice/ipc/ipctiein.c b/src/xspice/ipc/ipctiein.c new file mode 100755 index 000000000..2980241ab --- /dev/null +++ b/src/xspice/ipc/ipctiein.c @@ -0,0 +1,530 @@ +/*============================================================================ +FILE IPCtiein.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + Provides a protocol independent interface between the simulator + and the IPC method used to interface to CAE packages. + +INTERFACES + + g_ipc (global variable) + + ipc_handle_stop() + ipc_handle_returni() + ipc_handle_mintime() + ipc_handle_vtrans() + ipc_send_stdout() + ipc_send_stderr() + ipc_send_std_files() + ipc_screen_name() + ipc_get_devices() + ipc_free_devices() + ipc_check_pause_stop() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + + +#include "ngspice.h" +#include +#include +#include +//#include "util.h" +#include "inpdefs.h" +#include "gendefs.h" +#include "cktdefs.h" +#include "bjt/bjtdefs.h" +#include "jfet/jfetdefs.h" +#include "mos1/mos1defs.h" +#include "mos2/mos2defs.h" +#include "mos3/mos3defs.h" +#include "mifproto.h" +#include "ipc.h" +#include "ipctiein.h" + + + +/* +Global variable g_ipc is used by the SPICE mods that take care of +interprocess communications activities. +*/ + + +Ipc_Tiein_t g_ipc = { + IPC_FALSE, /* enabled */ + IPC_MODE_INTERACTIVE, /* mode */ + IPC_ANAL_DCOP, /* analysis mode */ + IPC_FALSE, /* parse_error */ + IPC_FALSE, /* run_error */ + IPC_FALSE, /* errchk_sent */ + IPC_FALSE, /* returni */ + 0.0, /* mintime */ + 0.0, /* lasttime */ + 0.0, /* cpu time */ + NULL, /* send array */ + NULL, /* log file */ + { /* vtrans struct */ + 0, /* size */ + NULL, /* vsrc_name array */ + NULL, /* device_name array */ + }, + IPC_FALSE, /* stop analysis */ +}; + + + +/* +ipc_handle_stop + +This function sets a flag in the g_ipc variable to signal that +a stop message has been received over the IPC channel. +*/ + +void ipc_handle_stop(void) +{ + g_ipc.stop_analysis = IPC_TRUE; +} + + +/* +ipc_handle_returni + +This function sets a flag in the g_ipc variable to signal that +a message has been received over the IPC channel specifying that +current values are to be returned in the results data sets. +*/ + +void ipc_handle_returni(void) +{ + g_ipc.returni = IPC_TRUE; +} + + +/* +ipc_handle_mintime + +This function sets a value in the g_ipc variable that specifies +how often data is to be returned as it is computed. If the +simulator takes timestep backups, data may still be returned +more often that that specified by 'mintime' so that glitches +are not missed. +*/ + +void ipc_handle_mintime(double time) +{ + g_ipc.mintime = time; +} + + + +/* +ipc_handle_vtrans + +This function processes arguments from a #VTRANS card received over +the IPC channel. The data on the card specifies that a particular +zero-valued voltage source name should be translated to the specified +instance name for which it was setup to monitor currents. +*/ + +void ipc_handle_vtrans( + char *vsrc, /* The name of the voltage source to be translated */ + char *dev) /* The device name the vsource name should be translated to */ +{ + int i; + int size; + + + if(g_ipc.vtrans.size == 0) { + g_ipc.vtrans.size = 1; + g_ipc.vtrans.vsrc_name = (void *) MALLOC(sizeof(char *)); + g_ipc.vtrans.device_name = (void *) MALLOC(sizeof(char *)); + g_ipc.vtrans.vsrc_name[0] = MIFcopy(vsrc); + g_ipc.vtrans.device_name[0] = MIFcopy(dev); + } + else { + g_ipc.vtrans.size++; + + size = g_ipc.vtrans.size; + i = g_ipc.vtrans.size - 1; + + g_ipc.vtrans.vsrc_name = (void *) REALLOC(g_ipc.vtrans.vsrc_name, + size * sizeof(char *)); + g_ipc.vtrans.device_name = (void *) REALLOC(g_ipc.vtrans.device_name, + size * sizeof(char *)); + g_ipc.vtrans.vsrc_name[i] = MIFcopy(vsrc); + g_ipc.vtrans.device_name[i] = MIFcopy(dev); + } +} + + + +/* +ipc_send_stdout + +This function sends the data written to stdout over the IPC channel. +This stream was previously redirected to a temporary file during +the simulation. +*/ + +void ipc_send_stdout(void) +{ + int c; + int len; + + char buf[IPC_MAX_LINE_LEN+1]; + + /* rewind the redirected stdout stream */ + rewind(stdout); + + /* Begin reading from the top of file and send lines */ + /* over the IPC channel. */ + + /* Don't send newlines. Also, if line is > IPC_MAX_LINE_LEN chars */ + /* we must wrap it because Mspice can't handle it */ + + len = 0; + while( (c=fgetc(stdout)) != EOF) { + if(c != '\n') { + buf[len] = c; + len++; + } + if((c == '\n') || (len == IPC_MAX_LINE_LEN)) { + buf[len] = '\0'; + ipc_send_line(buf); + len = 0; + } + } + if(len > 0) { + buf[len] = '\0'; + ipc_send_line(buf); + } + + /* Finally, rewind file again to discard the data already sent */ + rewind(stdout); +} + + +/* +ipc_send_stderr + +This function sends the data written to stderr over the IPC channel. +This stream was previously redirected to a temporary file during +the simulation. +*/ + +void ipc_send_stderr(void) +{ + int c; + int len; + + char buf[IPC_MAX_LINE_LEN+1]; + + /* rewind the redirected stderr stream */ + rewind(stderr); + + /* Begin reading from the top of file and send lines */ + /* over the IPC channel. */ + + /* Don't send newlines. Also, if line is > IPC_MAX_LINE_LEN chars */ + /* we must wrap it because Mspice can't handle it */ + + len = 0; + while( (c=fgetc(stderr)) != EOF) { + if(c != '\n') { + buf[len] = c; + len++; + } + if((c == '\n') || (len == IPC_MAX_LINE_LEN)) { + buf[len] = '\0'; + ipc_send_line(buf); + len = 0; + } + } + if(len > 0) { + buf[len] = '\0'; + ipc_send_line(buf); + } + + /* Finally, rewind file again to discard the data already sent */ + rewind(stderr); +} + + +/* +ipc_send_std_files + +This function sends the data written to stdout and stderr over the +IPC channel. These streams were previously redirected to temporary +files during the simulation. +*/ + +Ipc_Status_t ipc_send_std_files() +{ + ipc_send_stdout(); + ipc_send_stderr(); + + return(ipc_flush()); +} + + + +/* +ipc_screen_name + +This function screens names of instances and nodes to limit the +data returned over the IPC channel. +*/ + +Ipc_Boolean_t ipc_screen_name(char *name, char *mapped_name) +{ + char *endp; + int i; + int len; + long l; + + /* Return FALSE if name is in a subcircuit */ + for(i = 0; name[i] != '\0'; i++) { + if(name[i] == ':') + return(IPC_FALSE); + } + + /* Determine if name is numeric and what value is */ + l = strtol(name, &endp, 10); + + /* If numeric */ + if(*endp == '\0') { + /* Return FALSE if >100,000 -> added by ms_server in ATESSE 1.0 */ + if(l >= 100000) + return(IPC_FALSE); + /* Otherwise, copy name to mapped name and return true */ + else { + strcpy(mapped_name,name); + return(IPC_TRUE); + } + } + + /* If node is an internal node from a semiconductor (indicated by a */ + /* trailing #collector, #source, ...), do not return its current. */ + /* Otherwise, map to upper case and eliminate trailing "#branch" if any. */ + for(i = 0; name[i]; i++) { + if(name[i] == '#') { + if(strcmp(name + i, "#branch") == 0) + break; + else + return(IPC_FALSE); + } + else { + if(islower(name[i])) + mapped_name[i] = toupper(name[i]); + else + mapped_name[i] = name[i]; + } + } + mapped_name[i] = '\0'; + len = i; + + /* If len != 8 or 6'th char not equal to $, then doesn't need vtrans */ + /* Otherwise, translate to device name that it monitors */ + if(len != 8) + return(IPC_TRUE); + else if(name[5] != '$') + return(IPC_TRUE); + else { + /* Scan list of prefixes in VTRANS table and convert name */ + for(i = 0; i < g_ipc.vtrans.size; i++) { + if(strncmp(mapped_name, g_ipc.vtrans.vsrc_name[i], 5) == 0) { + strcpy(mapped_name, g_ipc.vtrans.device_name[i]); + return(IPC_TRUE); + } + } + return(IPC_TRUE); + } + + return(IPC_TRUE); +} + + + +/* +ipc_get_devices + +This function is used to setup the OUTinterface data structure that +determines what instances will have data returned over the IPC channel. +*/ + + +int ipc_get_devices( + void *circuit, /* The circuit structure */ + char *device, /* The device name as it appears in the info struct */ + char ***names, /* Array of name strings to be built */ + double **modtypes) /* Array of types to be built */ +{ + int index; + int num_instances; + GENmodel *model; + GENinstance *here; + CKTcircuit *ckt; + char *inst_name; + int inst_name_len; + int i; + + BJTmodel *BJTmod; + JFETmodel *JFETmod; + MOS1model *MOS1mod; + MOS2model *MOS2mod; + MOS3model *MOS3mod; + + /* Initialize local variables */ + ckt = (CKTcircuit *) circuit; + num_instances = 0; + + /* Get the index into the circuit structure linked list of models */ + index = INPtypelook(device); + + /* Iterate through all models of this type */ + for(model = ckt->CKThead[index]; model; model = model->GENnextModel) { + + /* Iterate through all instance of this model */ + for(here = model->GENinstances; here; here = here->GENnextInstance) { + + /* Get the name of the instance */ + inst_name = here->GENname; + inst_name_len = strlen(inst_name); + + /* Skip if it is a inside a subcircuit */ + for(i = 0; i < inst_name_len; i++) + if(inst_name[i] == ':') + break; + if(i < inst_name_len) + continue; + + /* Otherwise, add the name to the list */ + num_instances++; + if(num_instances == 1) + *names = (char **) MALLOC(sizeof(char *)); + else + *names = (char **) REALLOC(*names, num_instances * sizeof(char *)); + (*names)[num_instances-1] = MIFcopy(inst_name); + + /* Then get the type if it is a Q J or M */ + if(num_instances == 1) + *modtypes = (double *) MALLOC(sizeof(double)); + else + *modtypes = (double *) REALLOC((char *) *modtypes, + num_instances * sizeof(double)); + + if(strcmp(device,"BJT") == 0) { + BJTmod = (BJTmodel *) model; + (*modtypes)[num_instances-1] = BJTmod->BJTtype; + } + else if(strcmp(device,"JFET") == 0) { + JFETmod = (JFETmodel *) model; + (*modtypes)[num_instances-1] = JFETmod->JFETtype; + } + else if(strcmp(device,"Mos1") == 0) { + MOS1mod = (MOS1model *) model; + (*modtypes)[num_instances-1] = MOS1mod->MOS1type; + } + else if(strcmp(device,"Mos2") == 0) { + MOS2mod = (MOS2model *) model; + (*modtypes)[num_instances-1] = MOS2mod->MOS2type; + } + else if(strcmp(device,"Mos3") == 0) { + MOS3mod = (MOS3model *) model; + (*modtypes)[num_instances-1] = MOS3mod->MOS3type; + } + else { + (*modtypes)[num_instances-1] = 1.0; + } + + } /* end for all instances */ + } /* end for all models */ + + return(num_instances); +} + + + +/* +ipc_free_devices + +This function frees temporary data created by ipc_get_devices(). +*/ + + +void ipc_free_devices( + int num_items, /* Number of things to free */ + char **names, /* Array of name strings to be built */ + double *modtypes) /* Array of types to be built */ +{ + int i; + + for(i = 0; i < num_items; i++) + { + FREE(names[i]); + names[i] = 0; + } + + if(num_items > 0) + { + FREE(names); + FREE(modtypes); + + names = NULL; + modtypes = NULL; + } +} + + +/* +ipc_check_pause_stop + +This function is called at various times during a simulation to check +for incoming messages of the form >STOP or >PAUSE signaling that +simulation should be stopped or paused. Processing of the messages +is handled by ipc_get_line(). +*/ + +void ipc_check_pause_stop(void) +{ + char buf[1025]; + int len; + + /* If already seen stop analysis, don't call ipc_get_line, just return. */ + /* This is provided so that the function can be called multiple times */ + /* during the process of stopping */ + if(g_ipc.stop_analysis) + return; + + /* Otherwise do a non-blocking call to ipc_get_line() to check for messages. */ + /* We assume that the only possible messages at this point are >PAUSE */ + /* and >STOP, so we don't do anything with the returned text if any */ + ipc_get_line(buf, &len, IPC_NO_WAIT); +} + diff --git a/src/xspice/mif/Makefile.am b/src/xspice/mif/Makefile.am new file mode 100755 index 000000000..757d3dcd8 --- /dev/null +++ b/src/xspice/mif/Makefile.am @@ -0,0 +1,26 @@ +## Process this file with automake to produce Makefile.in +# +# JW 3/9/01 - had a go and makeing an autoconf script. + +noinst_LIBRARIES = libmifxsp.a + +libmifxsp_a_SOURCES = \ + mif_inp2.c \ + mifgetmod.c \ + mifgetvalue.c \ + mifload.c \ + mifmpara.c \ + mifsetup.c \ + mifutil.c \ + mifask.c \ + mifmask.c \ + miftrunc.c \ + mifconvt.c \ + mifdelete.c \ + mifmdelete.c \ + mifdestr.c \ + mif.c + +INCLUDES = -I$(top_srcdir)/src/include + +MAINTAINERCLEANFILES = Makefile.in diff --git a/src/xspice/mif/mif.c b/src/xspice/mif/mif.c new file mode 100755 index 000000000..9cf711db9 --- /dev/null +++ b/src/xspice/mif/mif.c @@ -0,0 +1,60 @@ +/*============================================================================ +FILE MIF.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file allocates globals used by various packages, including MIF. + +INTERFACES + + g_mif_info + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +#include "mif.h" + +int MIFiSize = sizeof(MIFinstance); +int MIFmSize = sizeof(MIFmodel); + + +/* Allocate global used to pass info on analysis type, etc. from */ +/* SPICE to the MIF load routine */ + + +/* This must be initialized so that EVTfindvec can check for */ +/* NULL pointer in g_mif_info.ckt */ + +Mif_Info_t g_mif_info = { + { MIF_FALSE, MIF_FALSE, MIF_DC, MIF_ANALOG, 0.0,}, + NULL, + NULL, + NULL, + { 0.0, 0.0,}, + { MIF_FALSE, MIF_FALSE,}, +}; diff --git a/src/xspice/mif/mif_inp2.c b/src/xspice/mif/mif_inp2.c new file mode 100755 index 000000000..55f6ac1d4 --- /dev/null +++ b/src/xspice/mif/mif_inp2.c @@ -0,0 +1,941 @@ +/*============================================================================ +FILE MIF_INP2A.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the main routine for parsing code model lines + in the SPICE circuit description input deck. + +INTERFACES + + MIF_INP2A() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ /* jgroves */ +#include "ngspice.h" +#include +//#include "util.h" +#include "ifsim.h" +#include "inpdefs.h" +#include "devdefs.h" +#include "inpmacs.h" +#include "fteext.h" + +#include "mifproto.h" +#include "mifparse.h" +#include "mifdefs.h" +#include "mifcmdat.h" + +#include "evt.h" +#include "evtproto.h" + +/* #include "suffix.h" */ /* jgroves */ + + + +/*#define NUM_SPICE3_MODELS 40 (was 23 - jgroves Number of Berkeley models in DEVices array. */ + /* See CKT/SPIinit.c */ + +extern int *DEVicesfl; /*flags for the devices */ +extern SPICEdev **DEVices; /* info about all device types */ +extern int DEVmaxnum; /* size of DEVices array */ + + +static void MIFinit_inst(MIFmodel *mdfast, MIFinstance *fast); + +static void MIFget_port_type( + void *ckt, /* circuit structure to put mod/inst structs in */ + INPtables *tab, /* symbol table for node names, etc. */ + card *current, /* MUST be named 'current' for spice macros */ + char **line, + char **next_token, + Mif_Token_Type_t *next_token_type, + Mif_Port_Type_t *port_type, + char **port_type_str, + Mif_Conn_Info_t *conn_info, /* for faster access to conn info struct */ + Mif_Status_t *status); + + +static void MIFget_port( + void *ckt, /* circuit structure to put mod/inst structs in */ + INPtables *tab, /* symbol table for node names, etc. */ + card *current, /* MUST be named 'current' for spice macros */ + MIFinstance *fast, /* pointer to instance struct */ + char **line, + char **next_token, + Mif_Token_Type_t *next_token_type, + Mif_Port_Type_t def_port_type, + char *def_port_type_str, + Mif_Conn_Info_t *conn_inf, /* for faster access to conn info struct */ + int conn_num, + int port_num, + Mif_Status_t *status); + + + + +/* ********************************************************************* */ + + + +/* +MIF_INP2A + +This function is called by INPpas2() in SPICE to parse the new +``a'' type element cards and build the required circuit structures +for the associated code model device and instance. It first +checks the model name at the end of the element card to be sure +the model was found in pass 1 of the parser. If so, MIFgetMod is +called to process the .model card, creating the necessary +internal model structure and filling in the parameter value +information. Next, the instance structure is allocated. +Finally, the connections on the element card are scanned and the +connection information is filled-in on the instance structure, +and error checks are performed. +*/ + + +void +MIF_INP2A(ckt,tab,current) + +void *ckt; /* circuit structure to put mod/inst structs in */ +INPtables *tab; /* symbol table for node names, etc. */ +card *current; /* the card we are to parse */ + /* Must be called "current" for compatibility */ + /* with macros */ +{ + +/* parse a code model instance card */ +/* Aname */ + + char *line; /* the text line for this card */ + char *name; /* the name of the instance */ + char *model=NULL; /* the name of the model */ + + char *def_port_type_str; /* The default port type in string form */ + char *next_token; /* a token string */ + + int i; /* a loop counter */ + int j; /* a loop counter */ + int type; /* the type of the model for this instance */ + /* int num_conn; number of connections for this model */ + int error; /* for the IFC macro */ + + MIFmodel *mdfast; /* pointer to model struct */ + MIFinstance *fast; /* pointer to instance struct */ + + INPmodel *thismodel; /* pointer to model struct */ + + Mif_Conn_Info_t *conn_info; /* for faster access to conn info struct */ + Mif_Param_Info_t *param_info; /* for faster access to param info struct */ + Mif_Port_Type_t def_port_type; /* the default port type */ + Mif_Status_t status; /* return status */ + Mif_Token_Type_t next_token_type; /* the type of the next token */ + +#ifdef TRACE + /* SDB debug statement */ + printf("In MIF_INP2A, line to process = %s . . . \n", current->line); +#endif + + /* get the line text from the card struct */ + + line = current->line; + + + /* get the name of the instance and add it to the symbol table */ + + name = MIFgettok(&line); + INPinsert(&name, tab); + + /* locate the last token on the line and put it into "model" */ + + while(*line != '\0') + model = MIFgettok(&line); + + if(model == NULL) { + LITERR("Missing model on A type device"); + return; + } + + /* Locate model from pass 1. If it hasn't been processed yet, */ + /* allocate a structure in ckt for it, process its parameters */ + /* and return a pointer to its structure in 'thismodel' */ + + current->error = MIFgetMod(ckt, model, &thismodel, tab); + tfree(name); + + if(current->error) { + return; + } + +#ifdef TRACE + /* SDB debug statement */ + printf("In MIF_INP2A, about to get ports on line %s\n", + current->line); +#endif + + + + /* get the integer index into the DEVices data array for this */ + /* model */ + + type = thismodel->INPmodType; + + if((type >= DEVmaxnum) || DEVicesfl[type] == 0) { + LITERR("Invalid model type for A type device"); + return; + } + + /* create a new structure for this instance in ckt */ + + mdfast = thismodel->INPmodfast; + IFC(newInstance, (ckt, mdfast,(void **)&fast, name)) + + + /* initialize the code model specific elements of the inst struct */ + + MIFinit_inst(mdfast, fast); + + + /* *********************** */ + /* Process the connections */ + /* *********************** */ + + /* get the line text from the card struct */ + /* skipping over the name of the instance */ + /* and reading the first token following */ + + line = current->line; + MIFgettok(&line); /* read instance name again . . . .*/ + + /******* loop through the fixed number of connections expected *******/ + for(i = 0; i < DEVices[type]->DEVpublic.num_conn; i++) { + + /* there better be at least one more token besides the model name */ + if(*line == '\0') { + LITERR("Encountered end of line before all connections were found in model."); + return; + } + + /* now get next token, should be a % after this statement */ + next_token = MIFget_token(&line,&next_token_type); + + + /* prepare a pointer for fast access to info about this connection */ + conn_info = &(DEVices[type]->DEVpublic.conn[i]); + + /* get the default port type for this connection */ + def_port_type = conn_info->default_port_type; + def_port_type_str = conn_info->default_type; + + + /* Now get real info about connection type (instead of default info) */ + if(next_token_type == MIF_PERCENT_TOK) { /* next_token_type should be a % */ + + /* get the port type identifier and check it for validity */ + next_token = MIFget_token(&line,&next_token_type); + MIFget_port_type(ckt, + tab, + current, + &line, + &next_token, + &next_token_type, + &def_port_type, + &def_port_type_str, + conn_info, + &status); + if(status == MIF_ERROR) + return; + } + else { + LITERR("Non percent token encountered when expecting MIF_PERCENT_TOK"); + return; + } + + + /* At this point, next_token should hold the token *after* the port type + identified (%v, %id, etc). */ + + /* set analog and event_driven flags on instance and model */ + if((def_port_type == MIF_DIGITAL) || (def_port_type == MIF_USER_DEFINED)) { + fast->event_driven = MIF_TRUE; + mdfast->event_driven = MIF_TRUE; + } + else { + fast->analog = MIF_TRUE; + mdfast->analog = MIF_TRUE; + } + + + /* check for a null connection and continue to next connection if found */ + if(next_token_type == MIF_NULL_TOK) { + + /* make sure null is allowed */ + if(! conn_info->null_allowed) { + LITERR("NULL connection found where not allowed"); + return; + } + + /* set the null flag to true */ + fast->conn[i]->is_null = MIF_TRUE; + fast->conn[i]->size = 0; + + /* eat the null token and continue to next connection */ + next_token = MIFget_token(&line,&next_token_type); + continue; + } + else { + /* set the null flag to false */ + fast->conn[i]->is_null = MIF_FALSE; + } + + + + /* ===== process connection as appropriate for scalar or array ====== */ + if(! conn_info->is_array) { /* a scalar connection - the simpler case */ + + /* At this point, next_token should hold the first netname in the port netlist. */ + + /* do a couple of error checks */ + if(next_token_type == MIF_LARRAY_TOK) { + LITERR("ERROR - Scalar connection expected, [ found"); + return; + } + if(next_token_type == MIF_RARRAY_TOK) { + LITERR("ERROR - Unexpected ]"); + return; + } + + /* If all OK, get the port data into the instance struct */ + /* allocating the port member of the instance struct as needed */ + MIFget_port(ckt, + tab, + current, + fast, + &line, + &next_token, + &next_token_type, + def_port_type, + def_port_type_str, + conn_info, + i, /* connection index */ + 0, /* port index for scalar connection */ + &status); + + /* upon leaving MIFget_port, we should be *ready* to read a % with the next + get_token */ + + if(status == MIF_ERROR) + return; + + fast->conn[i]->size = 1; + } + else { /* ====== the connection is an array - much to be done ... ====== */ + + /* At this point, the next_token should be a [ */ + + /* check for required leading array delim character and eat it if found */ + if(next_token_type != MIF_LARRAY_TOK) { + LITERR("Missing [, an array connection was expected"); + return; + } + else /* eat the [ */ + next_token = MIFget_token(&line,&next_token_type); + + /*------ get and process ports until ] is encountered ------*/ + + for(j = 0; + (next_token_type != MIF_RARRAY_TOK) && + (*line != '\0'); + j++) { + + /* First, do some error checks */ + + /* check for required leading array delim character */ + if(next_token_type == MIF_LARRAY_TOK) { + LITERR("ERROR - Unexpected [ - Arrays of arrays not allowed"); + return; + } + + /* If all OK, get the port nodes into the instance struct */ + /* allocating the port member of the instance struct as needed */ + + MIFget_port(ckt, + tab, + current, + fast, + &line, + &next_token, + &next_token_type, + def_port_type, + def_port_type_str, + conn_info, + i, /* connection index */ + j, /* port index */ + &status); + + if(status == MIF_ERROR) + return; + } /*------ end of for loop until ] is encountered ------*/ + + /* At this point, next_token should hold the next token after the + port netnames. This token should be a ]. */ + + /* make sure we exited because the end of the array connection */ + /* was reached. If so, eat the closing array delimiter ] */ + if(*line == '\0') { + LITERR("Missing ] in array connection"); + return; + } + + /* else { + * next_token = MIFget_token(&line,&next_token_type); + * } + */ + + /* At this point, the next time we get_token, we should get a % */ + + /* record the number of ports found for this connection */ + if(j < 1) { + LITERR("Array connection must have at least one port"); + return; + } + fast->conn[i]->size = j; + + } /* ====== array connection processing ====== */ + + /* be careful about putting stuff here, there is a 'continue' used */ + /* in the processing of NULL connections above */ + + + } /******* for number of connections *******/ + + + /* *********************** */ + /* Error Checks */ + /* *********************** */ + + /* check for too many connections */ + + /* At this point, we should have eaten all the net connections. At + this point, line should hold only the remainder of the model line (i.e. + the model name). */ + + if(strcmp(line,name) != 0) { + LITERR("Too many connections -- expecting model name but encountered other tokens."); + return; + } + + /* check connection constraints */ + + for(i = 0; i < DEVices[type]->DEVpublic.num_conn; i++) { + + conn_info = &(DEVices[type]->DEVpublic.conn[i]); + + if( (fast->conn[i]->is_null) && + (! conn_info->null_allowed) ) { + LITERR("Null found for connection where not allowed"); + return; + } + + if(conn_info->has_lower_bound) { + if(fast->conn[i]->size < conn_info->lower_bound) { + LITERR("Too few ports in connection"); + return; + } + } + + if(conn_info->has_upper_bound) { + if(fast->conn[i]->size > conn_info->upper_bound) { + LITERR("Too many ports in connection"); + return; + } + } + } + + /* check model parameter constraints */ + /* some of these should probably be done in MIFgetMod() */ + /* to prevent multiple error messages */ + + for(i = 0; i < DEVices[type]->DEVpublic.num_param; i++) { + + param_info = &(DEVices[type]->DEVpublic.param[i]); + + if(mdfast->param[i]->is_null) { + if(! param_info->has_default) { + LITERR("Parameter on model has no default"); + return; + } + else if((param_info->is_array) && (! param_info->has_conn_ref)) { + LITERR("Defaulted array parameter must have associated array connection"); + return; + } + } + if((! mdfast->param[i]->is_null) && (param_info->is_array)) { + if(param_info->has_conn_ref) { + if(fast->conn[param_info->conn_ref]->size != fast->param[i]->size) { + LITERR("Array parameter size on model does not match connection size"); + return; + } + } + } + } +} + + +/* ********************************************************************* */ + + + +/* +MIFinit_inst + +This function initializes the code model specific elements of the inst struct. +*/ + + +static void MIFinit_inst( + MIFmodel *mdfast, /* The model the instance is derived from */ + MIFinstance *fast) /* The instance to initialize */ +{ + + int mod_type; /* type of this model */ + + Mif_Conn_Info_t *conn_info; + + int i; + + + /* get an index into the DEVices information structure */ + + mod_type = mdfast->MIFmodType; + + + /* allocate code model connector data in instance struct */ + + fast->num_conn = DEVices[mod_type]->DEVpublic.num_conn; + fast->conn = (void *) tmalloc(fast->num_conn * sizeof(void *)); + + for(i = 0; i < fast->num_conn; i++) + fast->conn[i] = (void *) tmalloc(sizeof(Mif_Conn_Data_t)); + + /* initialize code model connector data */ + for(i = 0; i < fast->num_conn; i++) { + conn_info = &(DEVices[mod_type]->DEVpublic.conn[i]); + fast->conn[i]->name = conn_info->name; + fast->conn[i]->description = conn_info->description; + fast->conn[i]->is_null = MIF_TRUE; + fast->conn[i]->size = 0; + fast->conn[i]->port = NULL; + switch(conn_info->direction) { + case MIF_INOUT: + fast->conn[i]->is_input = MIF_TRUE; + fast->conn[i]->is_output = MIF_TRUE; + break; + case MIF_IN: + fast->conn[i]->is_input = MIF_TRUE; + fast->conn[i]->is_output = MIF_FALSE; + break; + case MIF_OUT: + fast->conn[i]->is_input = MIF_FALSE; + fast->conn[i]->is_output = MIF_TRUE; + break; + default: + printf("\nERROR - Impossible direction type in MIFinit_inst\n"); + exit(1); + } + } + + + /* allocate and copy instance variable data to the instance */ + + fast->num_inst_var = DEVices[mod_type]->DEVpublic.num_inst_var; + fast->inst_var = (void *) tmalloc(fast->num_inst_var * sizeof(void *)); + + for(i = 0; i < fast->num_inst_var; i++) { + + fast->inst_var[i] = (void *) tmalloc(sizeof(Mif_Inst_Var_Data_t)); + + if(DEVices[mod_type]->DEVpublic.inst_var[i].is_array) { + fast->inst_var[i]->size = 0; + fast->inst_var[i]->element = NULL; + /* The code model allocates space for the data and sets the size */ + } + else { + fast->inst_var[i]->size = 1; + fast->inst_var[i]->element = (void *) tmalloc(sizeof(Mif_Value_t)); + } + } + + + /* copy model parameter data to the instance */ + + fast->num_param = mdfast->num_param; + fast->param = mdfast->param; + + /* initialize any additional instance data */ + fast->initialized = MIF_FALSE; + fast->analog = MIF_FALSE; + fast->event_driven = MIF_FALSE; + fast->inst_index = 0; +} + + + +/* ********************************************************************* */ + + + +/* +MIFget_port_type + +This function gets the port type identifier and checks it for validity. It also +replaces false default information in conn_info with the real info based upon +the discovered port type string. + +When we call it, we expect that the next token type (sent from above) +should be the port type (MIF_STRING_TOK type). That is, we should be sitting right +on the def of the port type (i.e. %vnam, %vd, %id, etc.) Note that the parser +should have stripped the % already. + +Upon return, this fcn should leave next_token holding the token *after* the +port type (i.e. the thing after vnam, v, vd, id, etc). +*/ + + +static void +MIFget_port_type( + void *ckt, /* circuit structure to put mod/inst structs in */ + INPtables *tab, /* symbol table for node names, etc. */ + card *current, /* MUST be named 'current' for spice macros */ + char **line, + char **next_token, + Mif_Token_Type_t *next_token_type, + Mif_Port_Type_t *port_type, + char **port_type_str, + Mif_Conn_Info_t *conn_info, /* for faster access to conn info struct */ + Mif_Status_t *status) +{ + + Mif_Boolean_t found_type; + char *temp; + + int i; + + if(**line == '\0') { + LITERR("Missing connections on A device"); + *status = MIF_ERROR; + return; + } + + if(*next_token_type != MIF_STRING_TOK) { + LITERR("Invalid port type specifier"); + *status = MIF_ERROR; + return; + } + + /* OK, so get the port type string from the token and read next token */ + + temp = *next_token; + *next_token = MIFget_token(line, next_token_type); + + /* check port type for validity */ + + found_type = MIF_FALSE; + + for(i = 0; i < conn_info->num_allowed_types; i++) { + if(strcmp(temp, conn_info->allowed_type_str[i]) == 0) { + found_type = MIF_TRUE; + *port_type = conn_info->allowed_type[i]; + *port_type_str = temp; + break; + } + } + + if(! found_type) { + LITERR("Port type is invalid"); + *status = MIF_ERROR; + } + else { + + /* Fix by SDB so that the netlist parser uses the actual nature + of the port instead of the default state to decide if it is an array. */ + if ( (*port_type == MIF_DIFF_VOLTAGE) || + (*port_type == MIF_DIFF_CURRENT) || + (*port_type == MIF_DIFF_CONDUCTANCE) || + (*port_type == MIF_DIFF_RESISTANCE) ) { + conn_info->is_array = 1; + } + + *status = MIF_OK; + } + +} + + + + +/* ********************************************************************* */ + + +/* +MIFget_port + +This function processes a port being parsed, either single ended, +or both connections of a differential. + +When we call this fcn, next_token should be the *first* netname in the port +net list. Depending upon the type of port, this fcn should eat the appropriate +number of net tokens. + +When we leave this fcn, next_token should hold the next token after the last +netname processed. +*/ + + + + +static void +MIFget_port( + void *ckt, /* circuit structure to put mod/inst structs in */ + INPtables *tab, /* symbol table for node names, etc. */ + card *current, /* MUST be named 'current' for spice macros */ + MIFinstance *fast, /* pointer to instance struct */ + char **line, + char **next_token, + Mif_Token_Type_t *next_token_type, + Mif_Port_Type_t def_port_type, + char *def_port_type_str, + Mif_Conn_Info_t *conn_info, /* for faster access to conn info struct */ + int conn_num, + int port_num, + Mif_Status_t *status) + +{ + + + CKTnode *pos_node; /* positive connection node */ + CKTnode *neg_node; /* negative connection node */ + + char *node; + + + /* allocate space in the instance data struct for this port */ + if(port_num == 0) { + fast->conn[conn_num]->port = (void *) tmalloc(sizeof(void *)); + fast->conn[conn_num]->port[0] = (void *) tmalloc(sizeof(Mif_Port_Data_t)); + } + else { + fast->conn[conn_num]->port = (void *) REALLOC( + fast->conn[conn_num]->port, + ((port_num + 1) * sizeof(void *)) ); + fast->conn[conn_num]->port[port_num] = (void *) tmalloc(sizeof(Mif_Port_Data_t)); + } + + + /* store the port type information in the instance struct */ + fast->conn[conn_num]->port[port_num]->type = def_port_type; + fast->conn[conn_num]->port[port_num]->type_str = def_port_type_str; + + /* check for a leading tilde on digital ports */ + if(*next_token_type == MIF_TILDE_TOK) { + if((def_port_type != MIF_DIGITAL) && (def_port_type != MIF_USER_DEFINED)) { + LITERR("ERROR - Tilde not allowed on analog nodes"); + *status = MIF_ERROR; + return; + } + fast->conn[conn_num]->port[port_num]->invert = MIF_TRUE; + + /* eat the tilde and get the next token */ + *next_token = MIFget_token(line, next_token_type); + if(**line == '\0') { + LITERR("ERROR - Not enough ports"); + *status = MIF_ERROR; + return; + } + } + else + fast->conn[conn_num]->port[port_num]->invert = MIF_FALSE; + + + /* check for null port */ + if(*next_token_type == MIF_NULL_TOK) { + + /* make sure null is allowed */ + if(! conn_info->null_allowed) { + LITERR("NULL connection found where not allowed"); + *status = MIF_ERROR; + return; + } + + /* set the (port specific) null flag to true */ + fast->conn[conn_num]->port[port_num]->is_null = MIF_TRUE; + + /* set input value to zero in case user code model refers to it */ + fast->conn[conn_num]->port[port_num]->input.rvalue = 0.0; + + /* eat the null token and return */ + *next_token = MIFget_token(line, next_token_type); + *status = MIF_OK; + return; + } + else { + /* set the (port specific) null flag to false */ + fast->conn[conn_num]->port[port_num]->is_null = MIF_FALSE; + } + + + /* next token must be a node/instance identifier ... */ + if(*next_token_type != MIF_STRING_TOK) { + LITERR("ERROR - Expected node/instance identifier"); + *status = MIF_ERROR; + return; + } + + /* Get the first connection or the voltage source name */ + + switch(def_port_type) { + + case MIF_VOLTAGE: + case MIF_DIFF_VOLTAGE: + case MIF_CURRENT: + case MIF_DIFF_CURRENT: + case MIF_CONDUCTANCE: + case MIF_DIFF_CONDUCTANCE: + case MIF_RESISTANCE: + case MIF_DIFF_RESISTANCE: + + /* Call the spice3c1 function to put this node in the node list in ckt */ + INPtermInsert(ckt, next_token, tab,(void **)&pos_node); + + /* store the equation number and node identifier */ + /* This is the equivalent of what CKTbindNode() does in 3C1 */ + fast->conn[conn_num]->port[port_num]->pos_node_str = *next_token; + fast->conn[conn_num]->port[port_num]->smp_data.pos_node = pos_node->number; + + break; + + case MIF_VSOURCE_CURRENT: + + /* Call the spice3c1 function to put this vsource instance name in */ + /* the symbol table */ + INPinsert(next_token, tab); + + /* Now record the name of the vsource instance for processing */ + /* later by MIFsetup. This is equivalent to what INPpName */ + /* does in 3C1. Checking to see if the source is present in */ + /* the circuit is deferred to MIFsetup as is done in 3C1. */ + fast->conn[conn_num]->port[port_num]->vsource_str = *next_token; + + break; + + case MIF_DIGITAL: + case MIF_USER_DEFINED: + /* Insert data into event-driven info structs */ + EVTtermInsert(ckt, + fast, + *next_token, + def_port_type_str, + conn_num, + port_num, + &(current->error)); + if(current->error) { + *status = MIF_ERROR; + return; + } + break; + + default: + + /* impossible connection type */ + LITERR("INTERNAL ERROR - Impossible connection type"); + *status = MIF_ERROR; + return; + } + + /* get the next token */ + *next_token = MIFget_token(line, next_token_type); + + /* get other node if appropriate */ + switch(def_port_type) { + + case MIF_VOLTAGE: + case MIF_CURRENT: + case MIF_CONDUCTANCE: + case MIF_RESISTANCE: + /* These are single ended types, so default other node to ground */ + // This don't work dickhead, INPtermInsert tries to FREE(&node) K.A. Feb 27, 2000 + // which was not allocted + node = (char*)malloc(2);// added by K.A. march 5th 2000 + + *node = '0'; // added by K.A. March 5th 2000 + node[1] ='\0'; // added by K.A. March 5th 2000 +// node = "0"; // deleted by K.A. March 5th 2000, this is incorrect, it creates a new pointer + // that cause a crash in INPtermInsert() + + INPtermInsert(ckt, &node, tab,(void **)&neg_node); + + fast->conn[conn_num]->port[port_num]->neg_node_str = node; + fast->conn[conn_num]->port[port_num]->smp_data.neg_node = neg_node->number; + break; + + case MIF_DIFF_VOLTAGE: + case MIF_DIFF_CURRENT: + case MIF_DIFF_CONDUCTANCE: + case MIF_DIFF_RESISTANCE: + /* These are differential types, so get the other node */ + if((**line == '\0') || (*next_token_type != MIF_STRING_TOK)) { + LITERR("ERROR - Expected node identifier"); + *status = MIF_ERROR; + return; + } + INPtermInsert(ckt, next_token, tab,(void **)&neg_node); + fast->conn[conn_num]->port[port_num]->neg_node_str = *next_token; + fast->conn[conn_num]->port[port_num]->smp_data.neg_node = neg_node->number; + *next_token = MIFget_token(line, next_token_type); + break; + + default: + /* must be vsource name, digital, or user defined, so there is no other node */ + break; + + } + + *status = MIF_OK; + return; +} + + + + + + + + + diff --git a/src/xspice/mif/mifask.c b/src/xspice/mif/mifask.c new file mode 100755 index 000000000..7ff22d171 --- /dev/null +++ b/src/xspice/mif/mifask.c @@ -0,0 +1,231 @@ +/*============================================================================ +FILE MIFask.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function used by nutmeg to request the + value of a code model static var. + +INTERFACES + + MIFask() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" +#include +//#include "CONST.h" +//#include "util.h" +#include "ifsim.h" +#include "devdefs.h" +#include "sperror.h" + +#include + +#include "mifproto.h" +#include "mifdefs.h" + +/* #include "suffix.h" */ + + + +extern SPICEdev **DEVices; +extern int DEVmaxnum; + + + + + +/* +MIFask + +This function is called by SPICE/Nutmeg to query the value of a +parameter on an instance. It locates the value of the parameter +in the instance structure and converts the value into the IFvalue +structure understood by Nutmeg. For code models, this function +is provided to allow output of Instance Variables defined in the +code model's Interface Specification. +*/ + + +int MIFask( + CKTcircuit *ckt, /* The circuit structure */ + GENinstance *inInst, /* The instance to get the value from */ + int which, /* The parameter to get */ + IFvalue *value, /* The value returned */ + IFvalue *select) /* Unused */ +{ + + MIFinstance *inst; + MIFmodel *model; + int mod_type; + int value_type; + int i; + int size; + + int inst_index; + + Mif_Boolean_t is_array; + + + /* Arrange for access to MIF specific data in the instance */ + inst = (MIFinstance *) inInst; + + /* Arrange for access to MIF specific data in the model */ + model = inst->MIFmodPtr; + + + /* Get model type */ + mod_type = model->MIFmodType; + if((mod_type < 0) || (mod_type >= DEVmaxnum)) + return(E_BADPARM); + + /* Adjust parameter tag to actual index into inst info array */ + inst_index = which - model->num_param; + + /* Check instance index for validity */ + if((inst_index < 0) || (inst_index >= inst->num_inst_var)) + return(E_BADPARM); + + /* get value type to know which members of unions to access */ + value_type = DEVices[mod_type]->DEVpublic.instanceParms[inst_index].dataType; + value_type &= IF_VARTYPES; + + + /* determine if the parameter is an array or not */ + is_array = value_type & IF_VECTOR; + + + /* Transfer the values to the SPICE3C1 value union from the param elements */ + /* This is analagous to what SPICE3 does with other device types */ + + + if(! is_array) { + + switch(value_type) { + + case IF_FLAG: + value->iValue = inst->inst_var[inst_index]->element[0].bvalue; + break; + + case IF_INTEGER: + value->iValue = inst->inst_var[inst_index]->element[0].ivalue; + break; + + case IF_REAL: + value->rValue = inst->inst_var[inst_index]->element[0].rvalue; + break; + + case IF_STRING: + /* Make copy of string. We don't trust caller to not free it */ + /* These copies could get expensive! */ + value->sValue = MIFcopy(inst->inst_var[inst_index]->element[0].svalue); + break; + + case IF_COMPLEX: + /* we don't trust the caller to have a parallel complex structure */ + /* so copy the real and imaginary parts explicitly */ + value->cValue.real = inst->inst_var[inst_index]->element[0].cvalue.real; + value->cValue.imag = inst->inst_var[inst_index]->element[0].cvalue.imag; + break; + + default: + return(E_BADPARM); + + } + } + else { /* it is an array */ + + size = inst->inst_var[inst_index]->size; + if(size < 0) + size = 0; + + value->v.numValue = size; + + switch(value_type) { + + /* Note that we malloc space each time this function is called. */ + /* This is what TRAask.c does, so we do it too, even though */ + /* we don't know if it is ever freed... */ + + case IF_FLAGVEC: + if(size <= 0) + break; + value->v.vec.iVec = (void *) MALLOC(size * sizeof(int)); + for(i = 0; i < size; i++) + value->v.vec.iVec[i] = inst->inst_var[inst_index]->element[i].bvalue; + break; + + case IF_INTVEC: + if(size <= 0) + break; + value->v.vec.iVec = (void *) MALLOC(size * sizeof(int)); + for(i = 0; i < size; i++) + value->v.vec.iVec[i] = inst->inst_var[inst_index]->element[i].ivalue; + break; + + case IF_REALVEC: + if(size <= 0) + break; + value->v.vec.rVec = (void *) MALLOC(size * sizeof(double)); + for(i = 0; i < size; i++) + value->v.vec.rVec[i] = inst->inst_var[inst_index]->element[i].rvalue; + break; + + case IF_STRINGVEC: + if(size <= 0) + break; + value->v.vec.sVec = (void *) MALLOC(size * sizeof(char *)); + for(i = 0; i < size; i++) + /* Make copy of string. We don't trust caller to not free it */ + /* These copies could get expensive! */ + value->v.vec.sVec[i] = MIFcopy(inst->inst_var[inst_index]->element[i].svalue); + break; + + case IF_CPLXVEC: + if(size <= 0) + break; + /* we don't trust the caller to have a parallel complex structure */ + /* so copy the real and imaginary parts explicitly */ + value->v.vec.cVec = (void *) MALLOC(size * sizeof(IFcomplex)); + for(i = 0; i < size; i++) { + value->v.vec.cVec[i].real = inst->inst_var[inst_index]->element[i].cvalue.real; + value->v.vec.cVec[i].imag = inst->inst_var[inst_index]->element[i].cvalue.imag; + } + break; + + default: + return(E_BADPARM); + + } /* end switch */ + + } /* end else */ + + return(OK); +} diff --git a/src/xspice/mif/mifconvt.c b/src/xspice/mif/mifconvt.c new file mode 100755 index 000000000..93d483b11 --- /dev/null +++ b/src/xspice/mif/mifconvt.c @@ -0,0 +1,145 @@ +/*============================================================================ +FILE MIFconvTest.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function used to check that internal + states of a code model have converged. These internal states + are typically integration states. + +INTERFACES + + MIFconvTest() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" +#include +#include "cktdefs.h" +#include "sperror.h" + +//#include "util.h" +#include "devdefs.h" +//#include "CONST.h" +#include "trandefs.h" +#include + +#include "mifproto.h" +#include "mifparse.h" +#include "mifdefs.h" +#include "mifcmdat.h" + +/* #include "suffix.h" */ + + + + +/* +MIFconvTest + +This function is called by the CKTconvTest() driver function to +check convergence of any states owned by instances of a +particular code model type. It loops through all models of that +type and all instances of each model. For each instance, it +looks in the instance structure to determine if any variables +allocated by cm_analog_alloc() have been registered by a call to +cm_analog_converge() to have their convergence tested. If so, the value +of the function at the last iteration is compared with the value +at the current iteration to see if it has converged to within the +same delta amount used in node convergence checks (as defined by +SPICE 3C1). +*/ + + +int MIFconvTest( + GENmodel *inModel, /* The head of the model list */ + CKTcircuit *ckt) /* The circuit structure */ +{ + + MIFmodel *model; + MIFinstance *here; + + int i; + + double value; + double last_value; + + char *byte_aligned_double_ptr; + double *double_ptr; + + double tol; + + Mif_Boolean_t gotone = MIF_FALSE; + + + /* Setup for access into MIF specific model data */ + model = (MIFmodel *) inModel; + + /* loop through all models of this type */ + for( ; model != NULL; model = model->MIFnextModel) { + + /* Loop through all instances of this model */ + for(here = model->MIFinstances; here != NULL; here = here->MIFnextInstance) { + + /* Loop through all items registered for convergence */ + for(i = 0; i < here->num_conv; i++) { + + /* Get the current value and the last value */ + byte_aligned_double_ptr = (char *) ckt->CKTstate0; + byte_aligned_double_ptr += here->conv[i].byte_index; + double_ptr = (double *) byte_aligned_double_ptr; + value = *double_ptr; + + last_value = here->conv[i].last_value; + + /* If none have failed so far, check convergence */ + if(! gotone) { + + tol = ckt->CKTreltol * MAX(fabs(value), fabs(last_value)) + + ckt->CKTabstol; + if (fabs(value - last_value) > tol) { + if(ckt->enh->conv_debug.report_conv_probs) { + ENHreport_conv_prob(ENH_ANALOG_INSTANCE, + (char *) here->MIFname, + ""); + } + ckt->CKTnoncon++; + gotone = MIF_TRUE; + } + } + + /* Rotate the current value to last_value */ + here->conv[i].last_value = value; + + } /* end for number of conv items */ + } /* end for all instances */ + } /* end for all models of this type */ + + return(OK); +} diff --git a/src/xspice/mif/mifdelete.c b/src/xspice/mif/mifdelete.c new file mode 100755 index 000000000..56db26b32 --- /dev/null +++ b/src/xspice/mif/mifdelete.c @@ -0,0 +1,199 @@ +/*============================================================================ +FILE MIFdelete.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function used by SPICE to delete an + instance and its allocated data structures from the internal + circuit description data structures. + +INTERFACES + + MIFdelete() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +#include +#include "ngspice.h" +#include "sperror.h" +#include "gendefs.h" + +#include "mifproto.h" +#include "mifdefs.h" + +#include "suffix.h" + + + +/* +MIFdelete + +This function deletes a particular instance from the linked list +of instance structures, freeing all dynamically allocated memory +used by the instance structure. +*/ + + +int +MIFdelete( + GENmodel *inModel, /* The head of the model list */ + IFuid name, /* The name of the instance to delete */ + GENinstance **inst /* The instance structure to delete */ +) +{ + MIFmodel *model; + MIFinstance **fast; + MIFinstance **prev; + MIFinstance *here=NULL; + + Mif_Boolean_t found; + + int i; + int j; + int k; + + int num_conn; + int num_port; + int num_inst_var; + + + /* Convert generic pointers in arg list to MIF specific pointers */ + model = (MIFmodel *) inModel; + fast = (MIFinstance **) inst; + + /*******************************************/ + /* Cut the instance out of the linked list */ + /*******************************************/ + + /* Loop through all models */ + for(found = MIF_FALSE; model; model = model->MIFnextModel) { + prev = &(model->MIFinstances); + /* Loop through all instances of this model */ + for(here = *prev; here; here = here->MIFnextInstance) { + /* If name or pointer matches, cut it out and mark that its found */ + if(here->MIFname == name || (fast && here == *fast) ) { + *prev= here->MIFnextInstance; + found = MIF_TRUE; + break; + } + prev = &(here->MIFnextInstance); + } + if(found) + break; + } + + /* Return error if not found */ + if(!found) + return(E_NODEV); + + + /*******************************/ + /* Free the instance structure */ + /*******************************/ + + /* Loop through all connections on the instance */ + /* and dismantle the stuff allocated during readin/setup */ + /* in MIFinit_inst, MIFget_port, and MIFsetup */ + + num_conn = here->num_conn; + for(i = 0; i < num_conn; i++) { + + /* If connection never used, skip it */ + if(here->conn[i]->is_null) + continue; + + /* If analog output, lots to free... */ + if(here->conn[i]->is_output && here->analog) { + num_port = here->conn[i]->size; + /* For each port on the connector */ + for(j = 0; j < num_port; j++) { + /* Free the partial/ac_gain/smp stuff allocated in MIFsetup */ + for(k = 0; k < num_conn; k++) { + if((here->conn[k]->is_null) || (! here->conn[k]->is_input) ) + continue; + FREE(here->conn[i]->port[j]->partial[k].port); + FREE(here->conn[i]->port[j]->ac_gain[k].port); + FREE(here->conn[i]->port[j]->smp_data.input[k].port); + } + FREE(here->conn[i]->port[j]->partial); + FREE(here->conn[i]->port[j]->ac_gain); + FREE(here->conn[i]->port[j]->smp_data.input); + /* but don't free strings. They are either not owned */ + /* by the inst or are part of tokens. SPICE3C1 never */ + /* frees tokens, so we don't either... */ + } + } + /* Free the basic port structure allocated in MIFget_port */ + num_port = here->conn[i]->size; + for(j = 0; j < num_port; j++) + FREE(here->conn[i]->port[j]); + FREE(here->conn[i]->port); + } + + /* Free the connector stuff allocated in MIFinit_inst */ + /* Don't free name/description! They are not owned */ + /* by the instance */ + for(i = 0; i < num_conn; i++) { + FREE(here->conn[i]); + } + FREE(here->conn); + + /* Loop through all instance variables on the instance */ + /* and free stuff */ + + num_inst_var = here->num_inst_var; + for(i = 0; i < num_inst_var; i++) { + if(here->inst_var[i]->element != NULL) { + FREE(here->inst_var[i]->element); + } + FREE(here->inst_var[i]); + } + FREE(here->inst_var); + + /* ************************************************************* */ + /* Dont free params here. They are not currently implemented on */ + /* a per-instance basis, so their allocated space is owned by */ + /* the parent model, not the instance. Param stuff will be freed */ + /* by MIFmDelete */ + /* ************************************************************* */ + + /* Free the stuff used by the cm_... functions */ + + if(here->num_state && here->state) + FREE(here->state); + if(here->num_intgr && here->intgr) + FREE(here->intgr); + if(here->num_conv && here->conv) + FREE(here->conv); + + + /* Finally, free the instance struct itself */ + FREE(here); + + return(OK); +} diff --git a/src/xspice/mif/mifdestr.c b/src/xspice/mif/mifdestr.c new file mode 100755 index 000000000..c637623ea --- /dev/null +++ b/src/xspice/mif/mifdestr.c @@ -0,0 +1,72 @@ +/*============================================================================ +FILE MIFdestroy.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains a function that deletes all models of a particular + device (code model) type from the circuit description structures. + +INTERFACES + + MIFdestroy() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" +#include + +#include "mifproto.h" + +/* #include "suffix.h" */ + + + +/* +MIFdestroy + +This function deletes all models and all instances of a specified +device type. It traverses the linked list of model structures +for that type and calls MIFmDelete on each model. +*/ + +void MIFdestroy( + GENmodel **inModel) /* The head of the list of models to delete */ +{ + + /* Free all models of this device type by removing */ + /* models from the head of the linked list until */ + /* the head is null */ + + while(*inModel) { + MIFmDelete(inModel, + (*inModel)->GENmodName, + *inModel); + } + +} diff --git a/src/xspice/mif/mifgetmod.c b/src/xspice/mif/mifgetmod.c new file mode 100755 index 000000000..3b257651b --- /dev/null +++ b/src/xspice/mif/mifgetmod.c @@ -0,0 +1,265 @@ +/*============================================================================ +FILE + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + + * + * Copyright (c) 1985 Thomas L. Quarles + * + * NOTE: Portions of this code are Copyright Thomas L. Quarles and University of + * California at Berkeley. Other portions are modified and added by + * the Georgia Tech Research Institute. + * + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the routine that allocates a new model structure and + parses the .model card parameters. + +INTERFACES + + MIFgetMod() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ + +#include "ngspice.h" + + +#include +#include "inpdefs.h" /* maschmann : kleinbuchstaben */ +#include "devdefs.h" /* maschmann : kleinbuchstaben */ +//#include "util.h" +#include "ifsim.h" /* maschmann : kleinbuchstaben */ +#include "cpstd.h" /* maschmann : kleinbuchstaben */ +#include "fteext.h" /* maschmann : kleinbuchstaben */ + +#include "mifproto.h" +#include "mifdefs.h" +#include "mifcmdat.h" + +#include "suffix.h" + +/* This is the table of all models known to the program. */ +/* It is now defined in inpdefs.h 6.19.2003 -- SDB. */ +extern INPmodel *modtab; + +extern SPICEdev **DEVices; /* info about all device types */ + +/* +MIFgetMod + +This function is a modified version of SPICE 3C1 INPgetMod(). +MIFgetMod looks in the table of model information created on the +first pass of the parser to find the text of the .model card. It +then checks to see if the .model card has already been processed +by a previous element card reference. If so, it returns a +pointer to the previously created model structure. If not, it +allocates a new model structure and processes the parameters on +the .model card. Parameter values for parameters not found on +the .model card are not filled in by this function. They are +defaulted later by MIFsetup(). The function returns NULL when +successful, and an error string on failure. +*/ + +/* char *MIFgetMod(ckt,name,model,tab) */ /* former buggy calling method */ + +char *MIFgetMod( + void *ckt, /* The circuit structure */ + char *name, /* The name of the model to look for */ + INPmodel **model, /* The model found/created */ + INPtables *tab /* Table of model info from first pass */ + ) +{ + INPmodel *modtmp; + IFvalue * val; + register int j; + char * line; + char *parm; + char *err = NULL; + char *temp; + int error; + + int i; + + char *err1; + char *err2; + + MIFmodel *mdfast; + /* Mif_Param_Info_t *param_info;*/ + + + /* =========== First locate the named model in the modtab list ================= */ + +#ifdef TRACE + /* SDB debug statement */ + printf("In MIFgetMod, looking for model name = %s . . .\n", name); +#endif + + /* maschmann : remove : from name + * char *pos; + * if((pos=strstr(name,":"))!=NULL) *pos=0; + */ + + /*------------------------------------ + for (i = &modtab; *i != (INPmodel *) NULL; i = &((*i)->INPnextModel)) { + if (strcmp((*i)->INPmodName, token) == 0) { + return (OK); + } + } + --------------------------*/ + + /* loop through modtable looking for this model (*name) */ + for (modtmp = modtab; modtmp != NULL; modtmp = ((modtmp)->INPnextModel) ) { + +#ifdef TRACE + /* SDB debug statement */ + printf("In MIFgetMod, checking model against stored model = %s . . .\n", (modtmp)->INPmodName ); +#endif + + if (strcmp((modtmp)->INPmodName,name) == 0) { + +#ifdef TRACE + /* SDB debug statement */ + printf("In MIFgetMod, found model!!!\n"); +#endif + + /* ========= found the model in question - now instantiate if necessary ========== */ + /* ============== and return an appropriate pointer to it ===================== */ + + /* make sure the type is valid before proceeding */ + if( (modtmp)->INPmodType < 0) { + /* illegal device type, so can't handle */ + *model = NULL; + + /* fixed by SDB -- magic number is 39, not 35. + * Also needed parens to correctly compute # of bytes to malloc + */ + err = (char *)tmalloc( (39+strlen(name)) * sizeof(char) ); + + sprintf(err, "MIF: Unknown device type for model %s \n",name); + return(err); + } + + /* check to see if this model's parameters have been processed */ + if(! ((modtmp)->INPmodUsed )) { + + /* not already processed, so create data struct */ + error = (*(ft_sim->newModel))( ckt,(modtmp)->INPmodType, + &((modtmp)->INPmodfast), (modtmp)->INPmodName); + if(error) + return(INPerror(error)); + + /* gtri modification: allocate and initialize MIF specific model struct items */ + mdfast = modtmp->INPmodfast; + mdfast->num_param = DEVices[modtmp->INPmodType]->DEVpublic.num_param; + mdfast->param = (void *) tmalloc(mdfast->num_param * sizeof(void *)); + for(i = 0; i < mdfast->num_param; i++) { + mdfast->param[i] = (void *) tmalloc(sizeof(Mif_Param_Data_t)); + mdfast->param[i]->is_null = MIF_TRUE; + mdfast->param[i]->size = 0; + mdfast->param[i]->element = NULL; + } + /* remaining initializations will be done by MIFmParam() and MIFsetup() */ + + /* parameter isolation, identification, binding */ + line = ((modtmp)->INPmodLine)->line; + INPgetTok(&line,&parm,1); /* throw away '.model' */ + INPgetTok(&line,&parm,1); /* throw away 'modname' */ + + /* throw away the modtype - we don't treat it as a parameter */ + /* like SPICE does */ + INPgetTok(&line,&parm,1); /* throw away 'modtype' */ + + while(*line != 0) { + INPgetTok(&line,&parm,1); + for(j=0;j<*((*(ft_sim->devices)[(modtmp)->INPmodType]).numModelParms); j++) { + if (strcmp(parm,((*(ft_sim->devices) [ (modtmp)-> + INPmodType ]).modelParms[j].keyword)) == 0) { + /* gtri modification: call MIFgetValue instead of INPgetValue */ + err1 = NULL; + val = MIFgetValue(ckt,&line, + ((*(ft_sim->devices)[(modtmp)-> + INPmodType ]).modelParms[j]. + dataType),tab,&err1); + if(err1) { + err2 = (void *) tmalloc(25 + strlen(name) + strlen(err1)); + sprintf(err2, "MIF-ERROR - model: %s - %s\n", name, err1); + return(err2); + } + error = (*(ft_sim->setModelParm))(ckt, + ((modtmp)->INPmodfast), + (*(ft_sim->devices)[(modtmp)->INPmodType ]). + modelParms[j].id,val,(IFvalue*)NULL); + if(error) + return(INPerror(error)); + break; + } + } + /* gtri modification: processing of special parameter "level" removed */ + if(j >= *((*(ft_sim->devices)[(modtmp)->INPmodType]).numModelParms)) + { + //err has not been allocated, but free() in INPerrCat() + + // This did not allocate enough memory you wanker, K.A. replaced 5 March 2000 + // temp = (char *)tmalloc((40+strlen(parm)) * sizeof(char)); + temp = (char *)tmalloc((42+strlen(parm)) * sizeof(char));// K.A. replaced 5 March 2000 + + sprintf(temp, "MIF: unrecognized parameter (%s) - ignored\n", parm); + + fprintf(stdout,temp); + err = (char *)tmalloc( (2*strlen(temp) +2)*sizeof(char));// K.A. added 5 March 2000 + + *err = '\0';// K.A. added 5 March 2000 + + err = INPerrCat(err,temp); + } + FREE(parm); + + } /* end while end of line not reached */ + + (modtmp)->INPmodUsed=1; + (modtmp)->INPmodLine->error = err; + + } /* end if model parameters not processed yet */ + + *model = modtmp; + return((char *)NULL); + + } /* end if name matches */ + + } /* end for all models in modtab linked list */ + + + /* didn't find model - ERROR - return NULL model */ + *model = (INPmodel *)NULL; + err = (char *)tmalloc((60+strlen(name)) * sizeof(char)); + sprintf(err, " MIF-ERROR - unable to find definition of model %s\n",name); + + return(err); +} + diff --git a/src/xspice/mif/mifgetvalue.c b/src/xspice/mif/mifgetvalue.c new file mode 100755 index 000000000..bd3ad6695 --- /dev/null +++ b/src/xspice/mif/mifgetvalue.c @@ -0,0 +1,368 @@ +/*============================================================================ +FILE MIFgetValue.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function called to read parameter values from a + .model card. + +INTERFACES + + MIFgetValue() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" +#include +#include "ifsim.h" +//#include "util.h" +#include "inpdefs.h" +#include "inpptree.h" + +/* #include */ +#include + +#include "mifproto.h" +#include "mifparse.h" +#include "mifdefs.h" +#include "mifcmdat.h" + +/* #include "suffix.h" */ + + + +static int MIFget_boolean(char *token, char **err); + +static int MIFget_integer(char *token, char **err); + +static double MIFget_real(char *token, char **err); + +static char *MIFget_string(char *token, char **err); + +static IFcomplex MIFget_complex(char *token, Mif_Token_Type_t token_type, + char **line, char **err); + + + +/* +MIFgetValue + +This function gets a parameter value from the .model text line +into an IFvalue structure. The parameter type is specified in +the argument list and is used to determine how to parse the text +on the .model line. If the parameter is an array, the entire +array is parsed and placed in the IFvalue structure along with +the number of elements found. +*/ + + +IFvalue * +MIFgetValue(ckt,line,type,tab,err) + void *ckt; /* The circuit structure */ + char **line; /* The text line to read value from */ + int type; /* The type of data to read */ + INPtables *tab; /* Unused */ + char **err; /* Error string text */ +{ + static IFvalue val; + + int btemp; + int itemp; + double rtemp; + char *stemp; + IFcomplex ctemp; + + char *token; + Mif_Token_Type_t token_type; + + int value_type; + int is_array; + + + /* Mask off non-type bits */ + value_type = type & IF_VARTYPES; + + /* Setup array boolean */ + is_array = value_type & IF_VECTOR; + + + /* initialize stuff if array */ + if(is_array) { + token = MIFget_token(line, &token_type); + if(token_type != MIF_LARRAY_TOK) { + *err = "Array parameter expected - No array delimiter found"; + return(NULL); + } + val.v.numValue = 0; + val.v.vec.iVec = (void *) MALLOC(1); /* just so that realloc doesn't bomb */ + } + + + /* now get the values into val */ + + while(1) { + + token = MIFget_token(line, &token_type); + + /* exit if no more tokens */ + if(token_type == MIF_NO_TOK) { + *err = "Unexpected end of model card"; + return(NULL); + } + + /* exit if end of array found */ + if(is_array && (token_type == MIF_RARRAY_TOK)) { + if(val.v.numValue == 0) { + *err = "Array parameter must have at least one value"; + return(NULL); + } + break; + } + + /* process the token to extract a value */ + switch(value_type) { + + case IF_FLAG: + val.iValue = MIFget_boolean(token, err); + break; + + case IF_INTEGER: + val.iValue = MIFget_integer(token, err); + break; + + case IF_REAL: + val.rValue = MIFget_real(token, err); + break; + + case IF_STRING: + val.sValue = MIFget_string(token, err); + break; + + case IF_COMPLEX: + val.cValue = MIFget_complex(token, token_type, line, err); + break; + + + case IF_FLAGVEC: + btemp = MIFget_boolean(token, err); + val.v.vec.iVec = (void *) REALLOC(val.v.vec.iVec, + (val.v.numValue + 1) * sizeof(int)); + val.v.vec.iVec[val.v.numValue] = btemp; + val.v.numValue++; + break; + + case IF_INTVEC: + itemp = MIFget_integer(token, err); + val.v.vec.iVec = (void *) REALLOC(val.v.vec.iVec, + (val.v.numValue + 1) * sizeof(int)); + val.v.vec.iVec[val.v.numValue] = itemp; + val.v.numValue++; + break; + + case IF_REALVEC: + rtemp = MIFget_real(token, err); + val.v.vec.rVec = (void *) REALLOC(val.v.vec.rVec, + (val.v.numValue + 1) * sizeof(double)); + val.v.vec.rVec[val.v.numValue] = rtemp; + val.v.numValue++; + break; + + case IF_STRINGVEC: + stemp = MIFget_string(token, err); + val.v.vec.sVec = (void *) REALLOC(val.v.vec.sVec, + (val.v.numValue + 1) * sizeof(char *)); + val.v.vec.sVec[val.v.numValue] = stemp; + val.v.numValue++; + break; + + case IF_CPLXVEC: + ctemp = MIFget_complex(token, token_type, line, err); + val.v.vec.cVec = (void *) REALLOC(val.v.vec.cVec, + (val.v.numValue + 1) * sizeof(IFcomplex)); + val.v.vec.cVec[val.v.numValue] = ctemp; + val.v.numValue++; + break; + + + default: + *err = "Internal error - unexpected value type in MIFgetValue()"; + return(NULL); + + } + + if(*err) + return(NULL); + + /* exit after this single pass if not array */ + if(! is_array) + break; + + } /* end forever loop */ + + + return(&val); +} + + +/* *************************************************************** */ + + +static int MIFget_boolean(char *token, char **err) +{ + *err = NULL; + + if((strcmp(token, "t") == 0) || (strcmp(token, "true") == 0)) + return(1); + else if((strcmp(token, "f") == 0) || (strcmp(token, "false") == 0)) + return(0); + else + *err = "Bad boolean value"; + + return(-1); +} + + +/* *************************************************************** */ + +static int MIFget_integer(char *token, char **err) +{ + int error; + long l; + double dtemp; + char *endp; +/* long strtol(char *, char **, int); */ + + *err = NULL; + + l = strtol(token, &endp, 0); /* handles base 8, 10, 16 automatically */ + + /* if error, probably caused by engineering suffixes, */ + /* so try parsing with INPevaluate */ + if(errno || (*endp != '\0')) { + dtemp = INPevaluate(&token, &error, 1); + if(error) { + *err = "Bad integer, octal, or hex value"; + l = 0; + } + else if(dtemp > 0.0) + l = dtemp + 0.5; + else + l = dtemp - 0.5; + } + + return((int) l); +} + + +/* *************************************************************** */ + +static double MIFget_real(char *token, char **err) +{ + double dtemp; + int error; + + *err = NULL; + + dtemp = INPevaluate(&token, &error, 1); + + if(error) + *err = "Bad real value"; + + return(dtemp); +} + + +/* *************************************************************** */ + +static char *MIFget_string(char *token, char **err) +{ + *err = NULL; + + return(token); +} + + +/* *************************************************************** */ + +static IFcomplex MIFget_complex(char *token, Mif_Token_Type_t token_type, + char **line, char **err) +{ + static char *msg = "Bad complex value"; + + IFcomplex ctemp; + + double dtemp; + int error; + + *err = NULL; + + ctemp.real = 0.0; + ctemp.imag = 0.0; + + /* Complex values must be of form < > */ + if(token_type != MIF_LCOMPLEX_TOK) { + *err = msg; + return(ctemp); + } + + /* get the real part */ + token = MIFget_token(line, &token_type); + if(token_type != MIF_STRING_TOK) { + *err = msg; + return(ctemp); + } + dtemp = INPevaluate(&token, &error, 1); + if(error) { + *err = msg; + return(ctemp); + } + ctemp.real = dtemp; + + /* get the imaginary part */ + token = MIFget_token(line, &token_type); + if(token_type != MIF_STRING_TOK) { + *err = msg; + return(ctemp); + } + dtemp = INPevaluate(&token, &error, 1); + if(error) { + *err = msg; + return(ctemp); + } + ctemp.imag = dtemp; + + /* eat the closing > delimiter */ + token = MIFget_token(line, &token_type); + if(token_type != MIF_RCOMPLEX_TOK) { + *err = msg; + return(ctemp); + } + + return(ctemp); +} diff --git a/src/xspice/mif/mifload.c b/src/xspice/mif/mifload.c new file mode 100755 index 000000000..8202b1ca4 --- /dev/null +++ b/src/xspice/mif/mifload.c @@ -0,0 +1,898 @@ +/*============================================================================ +FILE MIFload.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the driver function for calling code model evaluation + functions. This is one of the most important, complex, and often called + functions in the model interface package. It iterates through all models + and all instances of a specified code model device type, fills in the + inputs for the model, calls the model, and then uses the outputs and + partials returned by the model to load the matrix. + +INTERFACES + + MIFload() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +/* #include "prefix.h" */ +#include "ngspice.h" + +#include +#include + +#include "cktdefs.h" +#include "devdefs.h" +#include "sperror.h" + +#include "mifproto.h" +#include "mifparse.h" +#include "mifdefs.h" +#include "mifcmdat.h" +#include "mif.h" + +#include "enh.h" +#include "cm.h" + +/* #include "suffix.h" */ + + + +extern SPICEdev **DEVices; /* info about all device types */ + + + +static void MIFauto_partial( + MIFinstance *here, + void (*cm_func)(), + Mif_Private_t *cm_data +); + + + + + + +/* +MIFload + +This function is called by the CKTload() driver function to call +the C function for each instance of a code model type. It loops +through all models of that type and all instances of each model. +For each instance, it prepares the structure that is passed to +the code model by filling it with the input values for that +instance. The code model's C function is then called, and the +outputs and partial derivatives computed by the C function are +used to fill the matrix for the next solution attempt. +*/ + + +int +MIFload( + GENmodel *inModel, /* The head of the model list */ + CKTcircuit *ckt) /* The circuit structure */ +{ + + MIFmodel *model; + MIFinstance *here; + + Mif_Private_t cm_data; /* data to be passed to/from code model */ + Mif_Port_Type_t type; + Mif_Port_Data_t *fast; + + Mif_Smp_Ptr_t *smp_data_out; + + Mif_Port_Ptr_t *smp_ptr; + + Mif_Port_Type_t in_type; + Mif_Port_Type_t out_type; + + Mif_Boolean_t is_input; + Mif_Boolean_t is_output; + + Mif_Cntl_Src_Type_t cntl_src_type; + + Mif_Analysis_t anal_type; + + Mif_Complex_t czero; + Mif_Complex_t ac_gain; + + int mod_type; + int num_conn; + int num_port; + int num_port_k; + int i; + int j; + int k; + int l; + + /*int tag;*/ + + double *rhs; + double *rhsOld; + double partial; + double temp; + + double *double_ptr0; + double *double_ptr1; + + /*double *input;*/ + /* double *oldinput;*/ + + char *byte_ptr0; + char *byte_ptr1; + + double last_input; + double conv_limit; + + double cntl_input; + + + Evt_Node_Data_t *node_data; + + + /* Prepare a zero complex number for AC gain initializations */ + czero.real = 0.0; + czero.imag = 0.0; + + /* Setup for access into MIF specific model data */ + model = (MIFmodel *) inModel; + mod_type = model->MIFmodType; + + /* Setup pointers for fast access to rhs and rhsOld elements of ckt struct */ + rhs = ckt->CKTrhs; + rhsOld = ckt->CKTrhsOld; + + node_data = ckt->evt->data.node; + + /* *********************************************************************** */ + /* Setup the circuit data in the structure to be passed to the code models */ + /* *********************************************************************** */ + + /* anal_init is set if this is the first iteration at any step in */ + /* an analysis */ + if(!(ckt->CKTmode & MODEINITFLOAT)) + g_mif_info.circuit.anal_init = MIF_TRUE; + cm_data.circuit.anal_init = g_mif_info.circuit.anal_init; + + /* anal_type is determined by CKTload */ + anal_type = g_mif_info.circuit.anal_type; + cm_data.circuit.anal_type = anal_type; + + /* get the analysis freq from the ckt struct if this is an AC analysis */ + /* otherwise, set the freq to zero */ + if(anal_type == MIF_AC) + cm_data.circuit.frequency = ckt->CKTomega; + else + cm_data.circuit.frequency = 0.0; + + /* get the analysis times from the ckt struct if this is a transient analysis */ + /* otherwise, set the times to zero */ + if(anal_type == MIF_TRAN) { + cm_data.circuit.time = ckt->CKTtime; + cm_data.circuit.t[0] = ckt->CKTtime; + for(i = 1; i < 8; i++) { + cm_data.circuit.t[i] = cm_data.circuit.t[i-1] - ckt->CKTdeltaOld[i-1]; + if(cm_data.circuit.t[i] < 0.0) + cm_data.circuit.t[i] = 0.0; + } + } + else { + cm_data.circuit.time = 0.0; + for(i = 0; i < 8; i++) { + cm_data.circuit.t[i] = 0.0; + } + } + + cm_data.circuit.call_type = MIF_ANALOG; + cm_data.circuit.temperature = ckt->CKTtemp - 273.15; + + g_mif_info.circuit.call_type = MIF_ANALOG; + g_mif_info.ckt = ckt; + + + /* ***************************************************************** */ + /* loop through all models of this type */ + /* ***************************************************************** */ + for( ; model != NULL; model = model->MIFnextModel) { + + /* If not an analog or hybrid model, continue to next */ + if(! model->analog) + continue; + + /* ***************************************************************** */ + /* loop through all instances of this model */ + /* ***************************************************************** */ + for(here = model->MIFinstances; here != NULL; here = here->MIFnextInstance) { + /* If not an analog or hybrid instance, continue to next */ + if(! here->analog) + continue; + + /* ***************************************************************** */ + /* Prepare the data needed by the cm_.. functions */ + /* ***************************************************************** */ + g_mif_info.instance = here; + g_mif_info.errmsg = ""; + + if(here->initialized) { + cm_data.circuit.init = MIF_FALSE; + g_mif_info.circuit.init = MIF_FALSE; + } + else { + cm_data.circuit.init = MIF_TRUE; + g_mif_info.circuit.init = MIF_TRUE; + } + + + /* ***************************************************************** */ + /* if tran analysis and anal_init is true, copy state 1 to state 0 */ + /* Otherwise the data in state 0 would be invalid */ + /* ***************************************************************** */ + + if((anal_type == MIF_TRAN) && g_mif_info.circuit.anal_init) { + for(i = 0; i < here->num_state; i++) { + double_ptr0 = ckt->CKTstate0 + here->state[i].index; + double_ptr1 = ckt->CKTstate1 + here->state[i].index; + byte_ptr0 = (char *) double_ptr0; + byte_ptr1 = (char *) double_ptr1; + for(j = 0; j < here->state[i].bytes; j++) + byte_ptr0[j] = byte_ptr1[j]; + } + } + + /* ***************************************************************** */ + /* If not AC analysis, loop through all connections on this instance */ + /* and load the input values for each input port of each connection */ + /* ***************************************************************** */ + + num_conn = here->num_conn; + for(i = 0; i < num_conn; i++) { + + /* If AC analysis, skip getting input values. The input values */ + /* should stay the same as they were at the last iteration of */ + /* the operating point analysis */ + if(anal_type == MIF_AC) + break; + + /* if the connection is null, skip to next connection */ + if(here->conn[i]->is_null) + continue; + + /* if this connection is not an input, skip to next connection */ + if(! here->conn[i]->is_input) + continue; + + /* Get number of ports on this connection */ + num_port = here->conn[i]->size; + + /* loop through all ports on this connection */ + for(j = 0; j < num_port; j++) { + + /*setup a pointer for fast access to port data */ + fast = here->conn[i]->port[j]; + + /* skip if this port is null */ + if(fast->is_null) + continue; + + /* determine the type of this port */ + type = fast->type; + + /* If port type is Digital or User-Defined, we only need */ + /* to get the total load. The input values are pointers */ + /* already set by EVTsetup() */ + if((type == MIF_DIGITAL) || (type == MIF_USER_DEFINED)) { + fast->total_load = + node_data->total_load[fast->evt_data.node_index]; + } + /* otherwise, it is an analog node and we get the input value */ + else { + /* load the input values based on type and mode */ + if(ckt->CKTmode & MODEINITJCT) + /* first iteration step for DC */ + fast->input.rvalue = 0.0; + else if((ckt->CKTmode & MODEINITTRAN) || + (ckt->CKTmode & MODEINITPRED)) + /* first iteration step at timepoint */ + fast->input.rvalue = *(ckt->CKTstate1 + fast->old_input); + else { + /* subsequent iterations */ + + /* record last iteration's input value for convergence limiting */ + last_input = fast->input.rvalue; + + /* get the new input value */ + switch(type) { + case MIF_VOLTAGE: + case MIF_DIFF_VOLTAGE: + case MIF_CONDUCTANCE: + case MIF_DIFF_CONDUCTANCE: + fast->input.rvalue = rhsOld[fast->smp_data.pos_node] - + rhsOld[fast->smp_data.neg_node]; + break; + case MIF_CURRENT: + case MIF_DIFF_CURRENT: + case MIF_VSOURCE_CURRENT: + case MIF_RESISTANCE: + case MIF_DIFF_RESISTANCE: + fast->input.rvalue = rhsOld[fast->smp_data.ibranch]; + break; + case MIF_DIGITAL: + case MIF_USER_DEFINED: + break; + } /* end switch on type of port */ + + /* If convergence limiting enabled, limit maximum input change */ + if(ckt->enh->conv_limit.enabled) { + /* compute the maximum the input is allowed to change */ + conv_limit = fabs(last_input) * ckt->enh->conv_limit.step; + if(conv_limit < ckt->enh->conv_limit.abs_step) + conv_limit = ckt->enh->conv_limit.abs_step; + /* if input has changed too much, limit it and signal not converged */ + if(fabs(fast->input.rvalue - last_input) > conv_limit) { + if((fast->input.rvalue - last_input) > 0.0) + fast->input.rvalue = last_input + conv_limit; + else + fast->input.rvalue = last_input - conv_limit; + (ckt->CKTnoncon)++; + /* report convergence problem if last call */ + if(ckt->enh->conv_debug.report_conv_probs) { + ENHreport_conv_prob(ENH_ANALOG_INSTANCE, + (char *) here->MIFname, ""); + } + } + } + + } /* end else */ + + /* Save value of input for use with MODEINITTRAN */ + *(ckt->CKTstate0 + fast->old_input) = fast->input.rvalue; + + } /* end else analog type */ + } /* end for number of ports */ + } /* end for number of connections */ + + /* ***************************************************************** */ + /* loop through all connections on this instance and zero out all */ + /* outputs/partials/AC gains for each output port of each connection */ + /* ***************************************************************** */ + num_conn = here->num_conn; + for(i = 0; i < num_conn; i++) { + + /* if the connection is null or is not an output */ + /* skip to next connection */ + if(here->conn[i]->is_null || (! here->conn[i]->is_output)) + continue; + + /* loop through all ports on this connection */ + num_port = here->conn[i]->size; + for(j = 0; j < num_port; j++) { + + /*setup a pointer for fast access to port data */ + fast = here->conn[i]->port[j]; + + /* skip if this port is null */ + if(fast->is_null) + continue; + + /* determine the type of this port */ + type = fast->type; + + /* If not an analog node, continue to next port */ + if((type == MIF_DIGITAL) || (type == MIF_USER_DEFINED)) + continue; + + /* initialize the output to zero */ + fast->output.rvalue = 0.0; + + /* loop through all connections and ports that */ + /* could be inputs for this port and zero the partials */ + for(k = 0; k < num_conn; k++) { + if(here->conn[k]->is_null || (! here->conn[k]->is_input)) + continue; + num_port_k = here->conn[k]->size; + for(l = 0; l < num_port_k; l++) { + /* skip if this port is null */ + if(here->conn[k]->port[l]->is_null) + continue; + fast->partial[k].port[l] = 0.0; + fast->ac_gain[k].port[l] = czero; + } /* end for number of ports */ + } /* end for number of connections */ + } /* end for number of ports */ + } /* end for number of connections */ + + + /* ***************************************************************** */ + /* Prepare the structure to be passed to the code model */ + /* ***************************************************************** */ + cm_data.num_conn = here->num_conn; + cm_data.conn = here->conn; + cm_data.num_param = here->num_param; + cm_data.param = here->param; + cm_data.num_inst_var = here->num_inst_var; + cm_data.inst_var = here->inst_var; + + /* Initialize the auto_partial flag to false */ + g_mif_info.auto_partial.local = MIF_FALSE; + + /* ******************* */ + /* Call the code model */ + /* ******************* */ + (*(DEVices[mod_type]->DEVpublic.cm_func)) (&cm_data); + + /* Automatically compute partials if requested by .options auto_partial */ + /* or by model through call to cm_analog_auto_partial() in DC or TRAN analysis */ + if((anal_type != MIF_AC) && + (g_mif_info.auto_partial.global || g_mif_info.auto_partial.local)) + MIFauto_partial(here, DEVices[mod_type]->DEVpublic.cm_func, &cm_data); + + /* ***************************************************************** */ + /* Loop through all connections on this instance and */ + /* load the data into the matrix for each output port */ + /* and for each V source associated with a current input. */ + /* For AC analysis, we only load the +-1s required to satisfy */ + /* KCL and KVL in the matrix equations. */ + /* ***************************************************************** */ + + num_conn = here->num_conn; + for(i = 0; i < num_conn; i++) { + + /* if the connection is null, skip to next connection */ + if(here->conn[i]->is_null) + continue; + + /* prepare things for convenient access later */ + is_input = here->conn[i]->is_input; + is_output = here->conn[i]->is_output; + + /* loop through all ports on this connection */ + num_port = here->conn[i]->size; + for(j = 0; j < num_port; j++) { + + /*setup a pointer for fast access to port data */ + fast = here->conn[i]->port[j]; + + /* skip if this port is null */ + if(fast->is_null) + continue; + + /* determine the type of this port */ + type = fast->type; + + /* If not an analog node, continue to next port */ + if((type == MIF_DIGITAL) || (type == MIF_USER_DEFINED)) + continue; + + /* create a pointer to the smp data for quick access */ + smp_data_out = &(fast->smp_data); + + /* if it is a current input */ + /* load the matrix data needed for the associated zero-valued V source */ + if(is_input && (type == MIF_CURRENT || type == MIF_DIFF_CURRENT)) { + *(smp_data_out->pos_ibranch) += 1.0; + *(smp_data_out->neg_ibranch) -= 1.0; + *(smp_data_out->ibranch_pos) += 1.0; + *(smp_data_out->ibranch_neg) -= 1.0; + /* rhs[smp_data_out->ibranch] += 0.0; */ + } /* end if current input */ + + /* if it has a voltage source output, */ + /* load the matrix with the V source output data */ + if( (is_output && (type == MIF_VOLTAGE || type == MIF_DIFF_VOLTAGE)) || + (type == MIF_RESISTANCE || type == MIF_DIFF_RESISTANCE) ) { + *(smp_data_out->pos_branch) += 1.0; + *(smp_data_out->neg_branch) -= 1.0; + *(smp_data_out->branch_pos) += 1.0; + *(smp_data_out->branch_neg) -= 1.0; + if(anal_type != MIF_AC) + rhs[smp_data_out->branch] += fast->output.rvalue; + } /* end if V source output */ + + /* if it has a current source output, */ + /* load the matrix with the V source output data */ + if( (is_output && (type == MIF_CURRENT || type == MIF_DIFF_CURRENT)) || + (type == MIF_CONDUCTANCE || type == MIF_DIFF_CONDUCTANCE) ) { + if(anal_type != MIF_AC) { + rhs[smp_data_out->pos_node] -= fast->output.rvalue; + rhs[smp_data_out->neg_node] += fast->output.rvalue; + } + } /* end if current output */ + + } /* end for number of ports */ + } /* end for number of connections */ + + + /* ***************************************************************** */ + /* loop through all output connections on this instance and */ + /* load the partials/AC gains into the matrix */ + /* ***************************************************************** */ + for(i = 0; i < num_conn; i++) { + + /* if the connection is null or is not an output */ + /* skip to next connection */ + if((here->conn[i]->is_null) || (! here->conn[i]->is_output)) + continue; + + /* loop through all ports on this connection */ + num_port = here->conn[i]->size; + for(j = 0; j < num_port; j++) { + + /*setup a pointer for fast access to port data */ + fast = here->conn[i]->port[j]; + + /* skip if this port is null */ + if(fast->is_null) + continue; + + /* determine the type of this output port */ + out_type = fast->type; + + /* If not an analog node, continue to next port */ + if((out_type == MIF_DIGITAL) || (out_type == MIF_USER_DEFINED)) + continue; + + /* create a pointer to the smp data for quick access */ + smp_data_out = &(fast->smp_data); + + /* for this port, loop through all connections */ + /* and all ports to touch on each possible input */ + for(k = 0; k < num_conn; k++) { + + /* if the connection is null or is not an input */ + /* skip to next connection */ + if((here->conn[k]->is_null) || (! here->conn[k]->is_input)) + continue; + + num_port_k = here->conn[k]->size; + /* loop through all the ports of this connection */ + for(l = 0; l < num_port_k; l++) { + + /* skip if this port is null */ + if(here->conn[k]->port[l]->is_null) + continue; + + /* determine the type of this input port */ + in_type = here->conn[k]->port[l]->type; + + /* If not an analog node, continue to next port */ + if((in_type == MIF_DIGITAL) || (in_type == MIF_USER_DEFINED)) + continue; + + /* get the partial to local variable for fast access */ + partial = fast->partial[k].port[l]; + ac_gain = fast->ac_gain[k].port[l]; + + /* create a pointer to the matrix pointer data for quick access */ + smp_ptr = &(smp_data_out->input[k].port[l]); + + /* get the input value */ + cntl_input = here->conn[k]->port[l]->input.rvalue; + + /* determine type of controlled source according */ + /* to input and output types */ + cntl_src_type = MIFget_cntl_src_type(in_type, out_type); + + switch(cntl_src_type) { + case MIF_VCVS: + if(anal_type == MIF_AC) { + *(smp_ptr->e.branch_poscntl) -= ac_gain.real; + *(smp_ptr->e.branch_negcntl) += ac_gain.real; + *(smp_ptr->e.branch_poscntl+1) -= ac_gain.imag; + *(smp_ptr->e.branch_negcntl+1) += ac_gain.imag; + } + else { + *(smp_ptr->e.branch_poscntl) -= partial; + *(smp_ptr->e.branch_negcntl) += partial; + rhs[smp_data_out->branch] -= partial * cntl_input; + } + break; + case MIF_ICIS: + if(anal_type == MIF_AC) { + *(smp_ptr->f.pos_ibranchcntl) += ac_gain.real; + *(smp_ptr->f.neg_ibranchcntl) -= ac_gain.real; + *(smp_ptr->f.pos_ibranchcntl+1) += ac_gain.imag; + *(smp_ptr->f.neg_ibranchcntl+1) -= ac_gain.imag; + } + else { + *(smp_ptr->f.pos_ibranchcntl) += partial; + *(smp_ptr->f.neg_ibranchcntl) -= partial; + temp = partial * cntl_input; + rhs[smp_data_out->pos_node] += temp; + rhs[smp_data_out->neg_node] -= temp; + } + break; + case MIF_VCIS: + if(anal_type == MIF_AC) { + *(smp_ptr->g.pos_poscntl) += ac_gain.real; + *(smp_ptr->g.pos_negcntl) -= ac_gain.real; + *(smp_ptr->g.neg_poscntl) -= ac_gain.real; + *(smp_ptr->g.neg_negcntl) += ac_gain.real; + *(smp_ptr->g.pos_poscntl+1) += ac_gain.imag; + *(smp_ptr->g.pos_negcntl+1) -= ac_gain.imag; + *(smp_ptr->g.neg_poscntl+1) -= ac_gain.imag; + *(smp_ptr->g.neg_negcntl+1) += ac_gain.imag; + } + else { + *(smp_ptr->g.pos_poscntl) += partial; + *(smp_ptr->g.pos_negcntl) -= partial; + *(smp_ptr->g.neg_poscntl) -= partial; + *(smp_ptr->g.neg_negcntl) += partial; + temp = partial * cntl_input; + rhs[smp_data_out->pos_node] += temp; + rhs[smp_data_out->neg_node] -= temp; + } + break; + case MIF_ICVS: + if(anal_type == MIF_AC) { + *(smp_ptr->h.branch_ibranchcntl) -= ac_gain.real; + *(smp_ptr->h.branch_ibranchcntl+1) -= ac_gain.imag; + } + else { + *(smp_ptr->h.branch_ibranchcntl) -= partial; + rhs[smp_data_out->branch] -= partial * cntl_input; + } + break; + } /* end switch on controlled source type */ + } /* end for number of input ports */ + } /* end for number of input connections */ + } /* end for number of output ports */ + } /* end for number of output connections */ + + here->initialized = MIF_TRUE; + + } /* end for all instances */ + + } /* end for all models */ + + return(OK); +} + + + + +/* +MIFauto_partial + +This function is called by MIFload() when a code model requests +that partial derivatives be computed automatically. It calls +the code model additional times with an individual input to the +model varied by a small amount at each call. Partial +derivatives of each output with respect to the varied input +are then computed by divided differences. +*/ + + +static void MIFauto_partial( + MIFinstance *here, /* The instance structure */ + void (*cm_func)(), /* The code model function to be called */ + Mif_Private_t *cm_data) /* The data to be passed to the code model */ +{ + + Mif_Port_Data_t *fast; + Mif_Port_Data_t *out_fast; + + Mif_Port_Type_t type; + Mif_Port_Type_t out_type; + + int num_conn; + int num_port; + int num_port_k; + + int i; + int j; + int k; + int l; + + double epsilon; + double nominal_input; + + + /* Reset init and anal_init flags before making additional calls */ + /* to the model */ + cm_data->circuit.init = MIF_FALSE; + g_mif_info.circuit.init = MIF_FALSE; + + cm_data->circuit.anal_init = MIF_FALSE; + g_mif_info.circuit.anal_init = MIF_FALSE; + + + /* *************************** */ + /* Save nominal analog outputs */ + /* *************************** */ + + /* loop through all connections */ + num_conn = here->num_conn; + for(i = 0; i < num_conn; i++) { + + /* if the connection is null or is not an output */ + /* skip to next connection */ + if(here->conn[i]->is_null || (! here->conn[i]->is_output)) + continue; + + /* loop through all ports on this connection */ + num_port = here->conn[i]->size; + for(j = 0; j < num_port; j++) { + + /*setup a pointer for fast access to port data */ + fast = here->conn[i]->port[j]; + + /* skip if this port is null */ + if(fast->is_null) + continue; + + /* determine the type of this port */ + type = fast->type; + + /* If not an analog port, continue to next port */ + if((type == MIF_DIGITAL) || (type == MIF_USER_DEFINED)) + continue; + + /* copy the output for use in computing output deltas */ + fast->nominal_output = fast->output.rvalue; + + } /* end for number of output ports */ + } /* end for number of output connections */ + + + /* ***************************************************************** */ + /* Change each analog input by a small amount and call the model to */ + /* compute new outputs. */ + /* ***************************************************************** */ + + /* loop through all connections */ + num_conn = here->num_conn; + for(i = 0; i < num_conn; i++) { + + /* if the connection is null, skip to next connection */ + if(here->conn[i]->is_null) + continue; + + /* if this connection is not an input, skip to next connection */ + if(! here->conn[i]->is_input) + continue; + + /* Get number of ports on this connection */ + num_port = here->conn[i]->size; + + /* loop through all ports on this connection */ + for(j = 0; j < num_port; j++) { + + /*setup a pointer for fast access to port data */ + fast = here->conn[i]->port[j]; + + /* skip if this port is null */ + if(fast->is_null) + continue; + + /* determine the type of this port */ + type = fast->type; + + /* If port type is Digital or User-Defined, skip it */ + if((type == MIF_DIGITAL) || (type == MIF_USER_DEFINED)) + continue; + + /* otherwise, it is an analog port and we need to perturb it and */ + /* then call the model */ + + /* compute the perturbation amount depending on type of input */ + switch(type) { + case MIF_VOLTAGE: + case MIF_DIFF_VOLTAGE: + case MIF_CONDUCTANCE: + case MIF_DIFF_CONDUCTANCE: + epsilon = 1.0e-6; + break; + + case MIF_CURRENT: + case MIF_DIFF_CURRENT: + case MIF_VSOURCE_CURRENT: + case MIF_RESISTANCE: + case MIF_DIFF_RESISTANCE: + epsilon = 1.0e-12; + break; + + default: + printf("INTERNAL ERROR - MIFauto_partial. Invalid port type\n"); + epsilon = 1.0e-30; + break; + } /* end switch on type of port */ + + /* record and perturb input value */ + nominal_input = fast->input.rvalue; + fast->input.rvalue += epsilon; + + + /* call model to compute new outputs */ + (*cm_func)(cm_data); + + + /* ******************************************************* */ + /* Compute the partials of each output with respect to the */ + /* perturbed input by divided differences. */ + /* ******************************************************* */ + + /* loop through all analog output connections */ + for(k = 0; k < num_conn; k++) { + + /* if the connection is null or is not an output */ + /* skip to next connection */ + if((here->conn[k]->is_null) || (! here->conn[k]->is_output)) + continue; + + /* loop through all the ports of this connection */ + num_port_k = here->conn[k]->size; + for(l = 0; l < num_port_k; l++) { + + /*setup a pointer for out_fast access to port data */ + out_fast = here->conn[k]->port[l]; + + /* skip if this port is null */ + if(out_fast->is_null) + continue; + + /* determine the out_type of this port */ + out_type = out_fast->type; + + /* If port type is Digital or User-Defined, skip it */ + if((out_type == MIF_DIGITAL) || (out_type == MIF_USER_DEFINED)) + continue; + + /* compute partial by divided differences */ + out_fast->partial[i].port[j] = + (out_fast->output.rvalue - out_fast->nominal_output) / epsilon; + + /* zero the output in preparation for next call */ + out_fast->output.rvalue = 0.0; + + } /* end for number of output ports */ + } /* end for number of output connections */ + + /* restore nominal input value */ + fast->input.rvalue = nominal_input; + + } /* end for number of input ports */ + } /* end for number of input connections */ + + + /* *************************************************** */ + /* Call model one last time to recompute nominal case. */ + /* *************************************************** */ + + /* This is needed even though the outputs are recorded, because */ + /* the model may compute other state values that cannot be restored */ + /* to the nominal condition from here */ + + (*cm_func)(cm_data); + +} + + + diff --git a/src/xspice/mif/mifmask.c b/src/xspice/mif/mifmask.c new file mode 100755 index 000000000..3004fd491 --- /dev/null +++ b/src/xspice/mif/mifmask.c @@ -0,0 +1,220 @@ +/*============================================================================ +FILE MIFmAsk.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function called by nutmeg to get the value + of a specified code model parameter. + +INTERFACES + + MIFmAsk() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" +#include +//#include "CONST.h" +//#include "util.h" +#include "ifsim.h" +#include "devdefs.h" +#include "sperror.h" + +#include + +#include "mifproto.h" +#include "mifdefs.h" + +/* #include "suffix.h" */ + + + +extern SPICEdev **DEVices; +extern int DEVmaxnum; + + + + +/* +MIFmAsk + +This function is called by SPICE/Nutmeg to query the value of a +parameter on a model. It is essentially the opposite of +MIFmParam, taking the index of the parameter, locating the value +of the parameter in the model structure, and converting that +value into the IFvalue structure understood by Nutmeg. +*/ + + +int MIFmAsk( + CKTcircuit *ckt, /* The circuit structure */ + GENmodel *inModel, /* The model to get the value from */ + int param_index, /* The parameter to get */ + IFvalue *value) /* The value returned */ +{ + + MIFmodel *model; + int mod_type; + int value_type; + int i; + int size; + + Mif_Boolean_t is_array; + + + /* Arrange for access to MIF specific data in the model */ + model = (MIFmodel *) inModel; + + + /* Get model type */ + mod_type = model->MIFmodType; + if((mod_type < 0) || (mod_type >= DEVmaxnum)) + return(E_BADPARM); + + + /* Check parameter index for validity */ + if((param_index < 0) || (param_index >= model->num_param)) + return(E_BADPARM); + + /* get value type to know which members of unions to access */ + value_type = DEVices[mod_type]->DEVpublic.modelParms[param_index].dataType; + value_type &= IF_VARTYPES; + + + /* determine if the parameter is an array or not */ + is_array = value_type & IF_VECTOR; + + + /* Transfer the values to the SPICE3C1 value union from the param elements */ + /* This is analagous to what SPICE3 does with other device types */ + + + if(! is_array) { + + switch(value_type) { + + case IF_FLAG: + value->iValue = model->param[param_index]->element[0].bvalue; + break; + + case IF_INTEGER: + value->iValue = model->param[param_index]->element[0].ivalue; + break; + + case IF_REAL: + value->rValue = model->param[param_index]->element[0].rvalue; + break; + + case IF_STRING: + /* Make copy of string. We don't trust caller to not free it */ + /* These copies could get expensive! */ + value->sValue = MIFcopy(model->param[param_index]->element[0].svalue); + break; + + case IF_COMPLEX: + /* we don't trust the caller to have a parallel complex structure */ + /* so copy the real and imaginary parts explicitly */ + value->cValue.real = model->param[param_index]->element[0].cvalue.real; + value->cValue.imag = model->param[param_index]->element[0].cvalue.imag; + break; + + default: + return(E_BADPARM); + + } + } + else { /* it is an array */ + + size = model->param[param_index]->size; + if(size < 0) + size = 0; + + value->v.numValue = size; + + switch(value_type) { + + /* Note that we malloc space each time this function is called. */ + /* This is what TRAask.c does, so we do it too, even though */ + /* we don't know if it is ever freed... */ + + case IF_FLAGVEC: + if(size <= 0) + break; + value->v.vec.iVec = (void *) MALLOC(size * sizeof(int)); + for(i = 0; i < size; i++) + value->v.vec.iVec[i] = model->param[param_index]->element[i].bvalue; + break; + + case IF_INTVEC: + if(size <= 0) + break; + value->v.vec.iVec = (void *) MALLOC(size * sizeof(int)); + for(i = 0; i < size; i++) + value->v.vec.iVec[i] = model->param[param_index]->element[i].ivalue; + break; + + case IF_REALVEC: + if(size <= 0) + break; + value->v.vec.rVec = (void *) MALLOC(size * sizeof(double)); + for(i = 0; i < size; i++) + value->v.vec.rVec[i] = model->param[param_index]->element[i].rvalue; + break; + + case IF_STRINGVEC: + if(size <= 0) + break; + value->v.vec.sVec = (void *) MALLOC(size * sizeof(char *)); + for(i = 0; i < size; i++) + /* Make copy of string. We don't trust caller to not free it */ + /* These copies could get expensive! */ + value->v.vec.sVec[i] = MIFcopy(model->param[param_index]->element[i].svalue); + break; + + case IF_CPLXVEC: + if(size <= 0) + break; + /* we don't trust the caller to have a parallel complex structure */ + /* so copy the real and imaginary parts explicitly */ + value->v.vec.cVec = (void *) MALLOC(size * sizeof(IFcomplex)); + for(i = 0; i < size; i++) { + value->v.vec.cVec[i].real = model->param[param_index]->element[i].cvalue.real; + value->v.vec.cVec[i].imag = model->param[param_index]->element[i].cvalue.imag; + } + break; + + default: + return(E_BADPARM); + + } /* end switch */ + + } /* end else */ + + return(OK); +} diff --git a/src/xspice/mif/mifmdelete.c b/src/xspice/mif/mifmdelete.c new file mode 100755 index 000000000..57c268b7a --- /dev/null +++ b/src/xspice/mif/mifmdelete.c @@ -0,0 +1,123 @@ +/*============================================================================ +FILE MIFmDelete.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function called by SPICE to delete a model + structure and all instances of that model. + +INTERFACES + + MIFmDelete() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" +#include +//#include "util.h" +#include "sperror.h" +#include "gendefs.h" + +#include "mifproto.h" +#include "mifdefs.h" + +/* #include "suffix.h" */ + + + + +/* +MIFmDelete + +This function deletes a particular model defined by a .model card +from the linked list of model structures of a particular code +model type, freeing all dynamically allocated memory used by the +model structure. It calls MIFdelete as needed to delete all +instances of the specified model. +*/ + + +int MIFmDelete( + GENmodel **inModel, /* The head of the model list */ + IFuid modname, /* The name of the model to delete */ + GENmodel *kill /* The model structure to be deleted */ +) +{ + MIFmodel **model; + MIFmodel *modfast; + MIFmodel **oldmod; + MIFmodel *here=NULL; + + Mif_Boolean_t found; + + int i; + + + /* Convert the generic pointers to MIF specific pointers */ + model = (MIFmodel **) inModel; + modfast = (MIFmodel *) kill; + + /* Locate the model by name or pointer and cut it out of the list */ + oldmod = model; + for(found = MIF_FALSE; *model; model = &((*model)->MIFnextModel)) { + if( (*model)->MIFmodName == modname || + (modfast && *model == modfast) ) { + here = *model; + *oldmod = (*model)->MIFnextModel; + found = MIF_TRUE; + break; + } + oldmod = model; + } + + if(! found) + return(E_NOMOD); + + /* Free the instances under this model if any */ + /* by removing from the head of the linked list */ + /* until the head is null */ + while(here->MIFinstances) { + MIFdelete((GENmodel *) here, + here->MIFinstances->MIFname, + (GENinstance **) &(here->MIFinstances)); + } + + /* Free the model params stuff allocated in MIFget_mod */ + for(i = 0; i < here->num_param; i++) { + if(here->param[i]->element) + FREE(here->param[i]->element); + FREE(here->param[i]); + } + FREE(here->param); + + /* Free the model and return */ + FREE(here); + return(OK); + +} diff --git a/src/xspice/mif/mifmpara.c b/src/xspice/mif/mifmpara.c new file mode 100755 index 000000000..f7bc28b64 --- /dev/null +++ b/src/xspice/mif/mifmpara.c @@ -0,0 +1,211 @@ +/*============================================================================ +FILE MIFmParam.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function used to assign the value of a parameter + read from the .model card into the appropriate structure in the model. + +INTERFACES + + MIFmParam() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" +#include +//#include "CONST.h" +//#include "util.h" +#include "ifsim.h" +//#include "resdefs.h" +#include "devdefs.h" +#include "sperror.h" + +#include + +#include "mifproto.h" +#include "mifparse.h" +#include "mifdefs.h" +#include "mifcmdat.h" + +/* #include "suffix.h" */ + + + +extern SPICEdev **DEVices; +extern int DEVmaxnum; + + + + +/* +MIFmParam + +This function is called by SPICE/Nutmeg to set the value of a +parameter on a model according to information parsed from a +.model card or information supplied interactively by a user. It +takes the value of the parameter input in an IFvalue structure +and sets the parameter on the specified model structure. Unlike +the procedure for SPICE 3C1 devices, MIFmParam does not use +enumerations for identifying the parameter to set. Instead, the +parameter is identified directly by the index value of the +parameter in the SPICEdev.DEVpublic.modelParms array. +*/ + +int MIFmParam( + int param_index, /* The parameter to set */ + IFvalue *value, /* The value of the parameter */ + GENmodel *inModel) /* The model structure on which to set the value */ +{ + + MIFmodel *model; + int mod_type; + int value_type; + int i; + + Mif_Boolean_t is_array; + + + /* Arrange for access to MIF specific data in the model */ + model = (MIFmodel *) inModel; + + + /* Get model type */ + mod_type = model->MIFmodType; + if((mod_type < 0) || (mod_type >= DEVmaxnum)) + return(E_BADPARM); + + + /* Check parameter index for validity */ + if((param_index < 0) || (param_index >= model->num_param)) + return(E_BADPARM); + + /* get value type to know which members of unions to access */ + value_type = DEVices[mod_type]->DEVpublic.modelParms[param_index].dataType; + value_type &= IF_VARTYPES; + + + /* determine if the parameter is an array or not */ + is_array = value_type & IF_VECTOR; + + /* initialize the parameter is_null and size elements and allocate elements */ + model->param[param_index]->is_null = MIF_FALSE; + if(is_array) { + model->param[param_index]->size = value->v.numValue; + model->param[param_index]->element = (void *) MALLOC(value->v.numValue * + sizeof(Mif_Value_t)); + } + else { + model->param[param_index]->size = 1; + model->param[param_index]->element = (void *) MALLOC(sizeof(Mif_Value_t)); + } + + + /* Transfer the values from the SPICE3C1 value union to the param elements */ + /* This is analagous to what SPICE3 does with other device types */ + + + if(! is_array) { + + switch(value_type) { + + case IF_FLAG: + model->param[param_index]->element[0].bvalue = value->iValue; + break; + + case IF_INTEGER: + model->param[param_index]->element[0].ivalue = value->iValue; + break; + + case IF_REAL: + model->param[param_index]->element[0].rvalue = value->rValue; + break; + + case IF_STRING: + /* we don't trust the caller to keep the string alive, so copy it */ + model->param[param_index]->element[0].svalue = + (void *) MALLOC(1 + strlen(value->sValue)); + strcpy(model->param[param_index]->element[0].svalue, value->sValue); + break; + + case IF_COMPLEX: + /* we don't trust the caller to have a parallel complex structure */ + /* so copy the real and imaginary parts explicitly */ + model->param[param_index]->element[0].cvalue.real = value->cValue.real; + model->param[param_index]->element[0].cvalue.imag = value->cValue.imag; + break; + + default: + return(E_BADPARM); + + } + } + else { /* it is an array */ + + for(i = 0; i < value->v.numValue; i++) { + + switch(value_type) { + + case IF_FLAGVEC: + model->param[param_index]->element[i].bvalue = value->v.vec.iVec[i]; + break; + + case IF_INTVEC: + model->param[param_index]->element[i].ivalue = value->v.vec.iVec[i]; + break; + + case IF_REALVEC: + model->param[param_index]->element[i].rvalue = value->v.vec.rVec[i]; + break; + + case IF_STRINGVEC: + /* we don't trust the caller to keep the string alive, so copy it */ + model->param[param_index]->element[i].svalue = + (void *) MALLOC(1 + strlen(value->v.vec.sVec[i])); + strcpy(model->param[param_index]->element[i].svalue, value->v.vec.sVec[i]); + break; + + case IF_CPLXVEC: + /* we don't trust the caller to have a parallel complex structure */ + /* so copy the real and imaginary parts explicitly */ + model->param[param_index]->element[i].cvalue.real = value->v.vec.cVec[i].real; + model->param[param_index]->element[i].cvalue.imag = value->v.vec.cVec[i].imag; + break; + + default: + return(E_BADPARM); + + } /* end switch */ + + } /* end for number of elements of vector */ + + } /* end else */ + + return(OK); +} diff --git a/src/xspice/mif/mifsetup.c b/src/xspice/mif/mifsetup.c new file mode 100755 index 000000000..11708aa44 --- /dev/null +++ b/src/xspice/mif/mifsetup.c @@ -0,0 +1,456 @@ +/*============================================================================ +FILE MIFsetup.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function called by SPICE to setup data structures + of a code model after parsing, but prior to beginning a simulation. The + major responsibilities of this function are to default values for model + parameters not given on the .model card, create equations in the matrix + for any voltage sources, and setup the matrix pointers used during + simulation to load the matrix. + +INTERFACES + + MIFsetup() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + + +/* #include "prefix.h" */ +#include "ngspice.h" +#include +//#include "util.h" +#include "smpdefs.h" +#include "devdefs.h" +#include "sperror.h" + +#include "mifproto.h" +#include "mifparse.h" +#include "mifdefs.h" +#include "mifcmdat.h" + +/* #include "suffix.h" */ + + + +extern SPICEdev **DEVices; /* info about all device types */ + + + +/* define macro for easy creation of matrix entries/pointers for outputs */ +#define TSTALLOC(ptr,first,second) \ + if((smp_data_out->ptr = \ + SMPmakeElt(matrix, smp_data_out->first, smp_data_out->second)) == NULL) { \ + return(E_NOMEM); \ + } + +/* define macro for easy creation of matrix entries/pointers for inputs */ +#define CTSTALLOC(ptr,first,second) \ + if((smp_data_out->input[k].port[l].ptr = \ + SMPmakeElt(matrix, smp_data_out->first, smp_data_cntl->second)) == NULL) { \ + return(E_NOMEM); \ + } + + + +/* +MIFsetup + +This function is called by the CKTsetup() driver function to +prepare all code model structures and all code model instance +structures for simulation. It loops through all models of a +particular code model type and provides defaults for any +parameters not specified on a .model card. It loops through all +instances of the model and prepares the instance structures for +simulation. The most important setup task is the creation of +entries in the SPICE matrix and the storage of pointers to +locations of the matrix used by MIFload during a simulation. +*/ + + +int +MIFsetup( + SMPmatrix *matrix, /* The analog simulation matrix structure */ + GENmodel *inModel, /* The head of the model list */ + CKTcircuit *ckt, /* The circuit structure */ + int *states) /* The states vector */ +{ + MIFmodel *model; + MIFinstance *here; + + int mod_type; + int max_size; + int size; + int error; + + int num_conn; + int num_port; + int num_port_k; + int i; + int j; + int k; + int l; + + Mif_Port_Type_t type; + Mif_Port_Type_t in_type; + Mif_Port_Type_t out_type; + + Mif_Cntl_Src_Type_t cntl_src_type; + + Mif_Smp_Ptr_t *smp_data_out; + Mif_Smp_Ptr_t *smp_data_cntl; + + Mif_Param_Info_t *param_info; + /* Mif_Conn_Info_t *conn_info;*/ + + Mif_Boolean_t is_input; + Mif_Boolean_t is_output; + + char *suffix; + CKTnode *tmp; + + + /* Setup for access into MIF specific model data */ + model = (MIFmodel *) inModel; + mod_type = model->MIFmodType; + + + /* loop through all models of this type */ + + for( ; model != NULL; model = model->MIFnextModel) { + + + /* For each parameter not given explicitly on the .model */ + /* card, default it */ + + for(i = 0; i < model->num_param; i++) { + + if(model->param[i]->is_null) { + + /* setup a pointer for quick access */ + param_info = &(DEVices[mod_type]->DEVpublic.param[i]); + + /* determine the size and allocate the parameter element(s) */ + if(! param_info->is_array) { + model->param[i]->size = 1; + model->param[i]->element = (void *) MALLOC(sizeof(Mif_Value_t)); + } + else { /* parameter is an array */ + /* MIF_INP2A() parser assures that there is an associated array connection */ + /* Since several instances may share this model, we have to create an array */ + /* big enough for the instance with the biggest connection array */ + max_size = 0; + for(here = model->MIFinstances; here != NULL; here = here->MIFnextInstance) { + size = here->conn[param_info->conn_ref]->size; + if(size > max_size) + max_size = size; + } + model->param[i]->size = max_size; + model->param[i]->element = (void *) MALLOC(max_size * sizeof(Mif_Value_t)); + } /* end if parameter is an array */ + + /* set the parameter element(s) to default value */ + for(j = 0; j < model->param[i]->size; j++) { + + switch(param_info->type) { + + case MIF_BOOLEAN: + model->param[i]->element[j].bvalue = param_info->default_value.bvalue; + break; + + case MIF_INTEGER: + model->param[i]->element[j].ivalue = param_info->default_value.ivalue; + break; + + case MIF_REAL: + model->param[i]->element[j].rvalue = param_info->default_value.rvalue; + break; + + case MIF_COMPLEX: + model->param[i]->element[j].cvalue = param_info->default_value.cvalue; + break; + + case MIF_STRING: + model->param[i]->element[j].svalue = param_info->default_value.svalue; + break; + + default: + return(E_BADPARM); + } + + } /* end for number of elements in param array */ + + } /* end if null */ + + } /* end for number of parameters */ + + + /* For each instance, initialize stuff used by cm_... functions */ + + for(here = model->MIFinstances; here != NULL; here = here->MIFnextInstance) { + + here->num_state = 0; + here->state = NULL; + + here->num_intgr = 0; + here->intgr = NULL; + + here->num_conv = 0; + here->conv = NULL; + } + + + /* For each instance, allocate runtime structs for output connections/ports */ + /* and grab a place in the state vector for all input connections/ports */ + + for(here = model->MIFinstances; here != NULL; here = here->MIFnextInstance) { + /* Skip these expensive allocations if the instance is not analog */ + if(! here->analog) + continue; + + num_conn = here->num_conn; + for(i = 0; i < num_conn; i++) { + if((here->conn[i]->is_null) || (! here->conn[i]->is_output) ) + continue; + num_port = here->conn[i]->size; + for(j = 0; j < num_port; j++) { + here->conn[i]->port[j]->partial = + (void *) MALLOC(num_conn * sizeof(Mif_Partial_t)); + here->conn[i]->port[j]->ac_gain = + (void *) MALLOC(num_conn * sizeof(Mif_AC_Gain_t)); + here->conn[i]->port[j]->smp_data.input = + (void *) MALLOC(num_conn * sizeof(Mif_Conn_Ptr_t)); + for(k = 0; k < num_conn; k++) { + if((here->conn[k]->is_null) || (! here->conn[k]->is_input) ) + continue; + num_port_k = here->conn[k]->size; + here->conn[i]->port[j]->partial[k].port = + (void *) MALLOC(num_port_k * sizeof(double)); + here->conn[i]->port[j]->ac_gain[k].port = + (void *) MALLOC(num_port_k * sizeof(Mif_Complex_t)); + here->conn[i]->port[j]->smp_data.input[k].port = + (void *) MALLOC(num_port_k * sizeof(Mif_Port_Ptr_t)); + } + } + } + + num_conn = here->num_conn; + for(i = 0; i < num_conn; i++) { + if((here->conn[i]->is_null) || (! here->conn[i]->is_input) ) + continue; + num_port = here->conn[i]->size; + for(j = 0; j < num_port; j++) { + here->conn[i]->port[j]->old_input = *states; + (*states)++; + } + } + } + + + /* Loop through all instances of this model and for each port of each connection */ + /* create current equations, matrix entries, and matrix pointers as necessary. */ + + for(here = model->MIFinstances; here != NULL; here = here->MIFnextInstance) { + /* Skip these expensive allocations if the instance is not analog */ + if(! here->analog) + continue; + + num_conn = here->num_conn; + + /* loop through all connections on this instance */ + /* and create matrix data needed for outputs and */ + /* V sources associated with I inputs */ + for(i = 0; i < num_conn; i++) { + + /* if the connection is null, skip to next connection */ + if(here->conn[i]->is_null) + continue; + + /* prepare things for convenient access later */ + is_input = here->conn[i]->is_input; + is_output = here->conn[i]->is_output; + num_port = here->conn[i]->size; + + /* loop through all ports on this connection */ + for(j = 0; j < num_port; j++) { + + /* if port is null, skip to next */ + if(here->conn[i]->port[j]->is_null) + continue; + + /* determine the type of this port */ + type = here->conn[i]->port[j]->type; + + /* create a pointer to the smp data for quick access */ + smp_data_out = &(here->conn[i]->port[j]->smp_data); + + /* if it has a voltage source output, */ + /* create the matrix data needed */ + if( (is_output && (type == MIF_VOLTAGE || type == MIF_DIFF_VOLTAGE)) || + (type == MIF_RESISTANCE || type == MIF_DIFF_RESISTANCE) ) { + + /* first, make the current equation */ + suffix = (void *) MALLOC(strlen((char *) here->MIFname) + 100); + sprintf(suffix, "branch_%d_%d", i, j); + error = CKTmkCur(ckt, &tmp, here->MIFname, suffix); + FREE(suffix); + if(error) + return(error); + smp_data_out->branch = tmp->number; + + /* ibranch is needed to find the input equation for RESISTANCE type */ + smp_data_out->ibranch = tmp->number; + + /* then make the matrix pointers */ + TSTALLOC(pos_branch, pos_node, branch); + TSTALLOC(neg_branch, neg_node, branch); + TSTALLOC(branch_pos, branch, pos_node); + TSTALLOC(branch_neg, branch, neg_node); + } /* end if current input */ + + /* if it is a current input */ + /* create the matrix data needed for the associated zero-valued V source */ + if(is_input && (type == MIF_CURRENT || type == MIF_DIFF_CURRENT)) { + + /* first, make the current equation */ + suffix = (void *) MALLOC(strlen((char *) here->MIFname) + 100); + sprintf(suffix, "ibranch_%d_%d", i, j); + error = CKTmkCur(ckt, &tmp, here->MIFname, suffix); + FREE(suffix); + if(error) + return(error); + smp_data_out->ibranch = tmp->number; + + /* then make the matrix pointers */ + TSTALLOC(pos_ibranch, pos_node, ibranch); + TSTALLOC(neg_ibranch, neg_node, ibranch); + TSTALLOC(ibranch_pos, ibranch, pos_node); + TSTALLOC(ibranch_neg, ibranch, neg_node); + } /* end if current input */ + + /* if it is a vsource current input (refers to a vsource elsewhere */ + /* in the circuit), locate the source and get its equation number */ + if(is_input && (type == MIF_VSOURCE_CURRENT)) { + smp_data_out->ibranch = CKTfndBranch(ckt, + here->conn[i]->port[j]->vsource_str); + if(smp_data_out->ibranch == (int)NULL) { + IFuid names[2]; + names[0] = here->MIFname; + names[1] = (IFuid) here->conn[i]->port[j]->vsource_str; + (*(SPfrontEnd->IFerror))(ERR_FATAL, + "%s: unknown controlling source %s",names); + return(E_BADPARM); + } + } /* end if vsource current input */ + + } /* end for number of ports */ + } /* end for number of connections */ + + /* now loop through all connections on the instance and create */ + /* matrix data needed for partial derivatives of outputs */ + for(i = 0; i < num_conn; i++) { + + /* if the connection is null or is not an output */ + /* skip to next connection */ + if((here->conn[i]->is_null) || (! here->conn[i]->is_output)) + continue; + + /* loop through all ports on this connection */ + + num_port = here->conn[i]->size; + for(j = 0; j < num_port; j++) { + + /* if port is null, skip to next */ + if(here->conn[i]->port[j]->is_null) + continue; + + /* determine the type of this output port */ + out_type = here->conn[i]->port[j]->type; + + /* create a pointer to the smp data for quick access */ + smp_data_out = &(here->conn[i]->port[j]->smp_data); + + /* for this port, loop through all connections */ + /* and all ports to touch on each possible input */ + for(k = 0; k < num_conn; k++) { + + /* if the connection is null or is not an input */ + /* skip to next connection */ + if((here->conn[k]->is_null) || (! here->conn[k]->is_input)) + continue; + + num_port_k = here->conn[k]->size; + /* loop through all the ports of this connection */ + for(l = 0; l < num_port_k; l++) { + + /* if port is null, skip to next */ + if(here->conn[k]->port[l]->is_null) + continue; + + /* determine the type of this input port */ + in_type = here->conn[k]->port[l]->type; + + /* create a pointer to the smp data for quick access */ + smp_data_cntl = &(here->conn[k]->port[l]->smp_data); + + /* determine type of controlled source according */ + /* to input and output types */ + cntl_src_type = MIFget_cntl_src_type(in_type, out_type); + + switch(cntl_src_type) { + case MIF_VCVS: + CTSTALLOC(e.branch_poscntl, branch, pos_node); + CTSTALLOC(e.branch_negcntl, branch, neg_node); + break; + case MIF_ICIS: + CTSTALLOC(f.pos_ibranchcntl, pos_node, ibranch); + CTSTALLOC(f.neg_ibranchcntl, neg_node, ibranch); + break; + case MIF_VCIS: + CTSTALLOC(g.pos_poscntl, pos_node, pos_node); + CTSTALLOC(g.pos_negcntl, pos_node, neg_node); + CTSTALLOC(g.neg_poscntl, neg_node, pos_node); + CTSTALLOC(g.neg_negcntl, neg_node, neg_node); + break; + case MIF_ICVS: + CTSTALLOC(h.branch_ibranchcntl, branch, ibranch); + break; + } /* end switch on controlled source type */ + } /* end for number of input ports */ + } /* end for number of input connections */ + } /* end for number of output ports */ + } /* end for number of output connections */ + + } /* end for all instances */ + + + } /* end for all models of this type */ + + return(OK); +} diff --git a/src/xspice/mif/miftrunc.c b/src/xspice/mif/miftrunc.c new file mode 100755 index 000000000..0b03fcaf6 --- /dev/null +++ b/src/xspice/mif/miftrunc.c @@ -0,0 +1,224 @@ +/*============================================================================ +FILE MIFtrunc.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains the function called by SPICE to check truncation + error of an integration state used by a code model. + +INTERFACES + + MIFtrunc() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" +#include +#include "cktdefs.h" +#include "sperror.h" + +//#include "util.h" +#include + +#include "mifproto.h" +#include "mifparse.h" +#include "mifdefs.h" +#include "mifcmdat.h" + +/* #include "suffix.h" */ + + + +static void MIFterr(Mif_Intgr_t *intgr, CKTcircuit *ckt, double *timeStep); + + + +/* +MIFtrunc + +This function is called by the CKTtrunc() driver function to +check numerical integration truncation error of any integrals +associated with instances of a particular code model type. It +traverses all models of that type and all instances of each +model. For each instance, it looks in the instance structure to +determine if any variables allocated by cm_analog_alloc() have been used +in a call to cm_analog_integrate(). If so, the truncation error of that +integration is computed and used to set the maximum delta allowed +for the current timestep. +*/ + + +int +MIFtrunc( + GENmodel *inModel, /* The head of the model list */ + CKTcircuit *ckt, /* The circuit structure */ + double *timeStep) /* The timestep delta */ +{ + + MIFmodel *model; + MIFinstance *here; + + int i; + + + /* Setup for access into MIF specific model data */ + model = (MIFmodel *) inModel; + + + /* loop through all models of this type */ + for( ; model != NULL; model = model->MIFnextModel) { + + /* Loop through all instances of this model */ + for(here = model->MIFinstances; here != NULL; here = here->MIFnextInstance) { + + /* Loop through all integration states on the instance */ + for(i = 0; i < here->num_intgr; i++) { + + /* Limit timeStep according to truncation error */ + MIFterr(&(here->intgr[i]), ckt, timeStep); + + } /* end for number of integration states */ + } /* end for all instances */ + } /* end for all models of this type */ + + + return(OK); +} + + + +/* + * Copyright (c) 1985 Thomas L. Quarles + * + * This is a modified version of the function CKTterr(). It limits + * timeStep according to computed truncation error. + * + * Modifications are Copyright 1991 Georgia Tech Research Institute + * + */ + + +static void MIFterr( + Mif_Intgr_t *intgr, + CKTcircuit *ckt, + double *timeStep) +{ + double volttol; + double chargetol; + double tol; + double del; + double diff[8]; + double deltmp[8]; + double factor; + + int i; + int j; + + static double gearCoeff[] = { + .5, + .2222222222, + .1363636364, + .096, + .07299270073, + .05830903790 + }; + static double trapCoeff[] = { + .5, + .08333333333 + }; + + /* Define new local variables. Dimension = number of states in ckt struct */ + char *byte_aligned_state_ptr; + double *state_ptr[8]; + + + /* Set state pointers to the (possibly byte-aligned) states */ + for(i = 0; i < 8; i++) { + byte_aligned_state_ptr = (char *) ckt->CKTstates[i]; + byte_aligned_state_ptr += intgr->byte_index; + state_ptr[i] = (double *) byte_aligned_state_ptr; + } + + /* Modify computation of volttol to not include current from previous timestep */ + /* which is unavailable in this implementation. Note that this makes the */ + /* the overall trunction error timestep smaller (which is better accuracy) */ + + /* Old code */ +/* + volttol = ckt->CKTabstol + ckt->CKTreltol * + MAX( fabs(*(ckt->CKTstate0+ccap)), fabs(*(ckt->CKTstate1+ccap))); +*/ + + /* New code */ + volttol = ckt->CKTabstol + ckt->CKTreltol * fabs(*(state_ptr[0]) - *(state_ptr[1])) + / ckt->CKTdelta; + + /* Modify remaining references to qcap to access byte-aligned MIF state */ + /* Otherwise, remaining code is same as SPICE3C1 ... */ + + chargetol = MAX(fabs(*(state_ptr[0])),fabs(*(state_ptr[1]))); + chargetol = ckt->CKTreltol * MAX(chargetol,ckt->CKTchgtol)/ckt->CKTdelta; + tol = MAX(volttol,chargetol); + /* now divided differences */ + for(i=ckt->CKTorder+1;i>=0;i--) { + diff[i] = *(state_ptr[i]); + } + for(i=0 ; i <= ckt->CKTorder ; i++) { + deltmp[i] = ckt->CKTdeltaOld[i]; + } + j = ckt->CKTorder; + while(1) { + for(i=0;i <= j;i++) { + diff[i] = (diff[i] - diff[i+1])/deltmp[i]; + } + if (--j < 0) break; + for(i=0;i <= j;i++) { + deltmp[i] = deltmp[i+1] + ckt->CKTdeltaOld[i]; + } + } + switch(ckt->CKTintegrateMethod) { + case GEAR: + default: + factor = gearCoeff[ckt->CKTorder-1]; + break; + + case TRAPEZOIDAL: + factor = trapCoeff[ckt->CKTorder - 1] ; + break; + } + del = ckt->CKTtrtol * tol/MAX(ckt->CKTabstol,factor * fabs(diff[0])); + if(ckt->CKTorder == 2) { + del = sqrt(del); + } else if (ckt->CKTorder > 2) { + del = exp(log(del)/ckt->CKTorder); + } + *timeStep = MIN(*timeStep,del); + return; + +} diff --git a/src/xspice/mif/mifutil.c b/src/xspice/mif/mifutil.c new file mode 100755 index 000000000..8e69a04e7 --- /dev/null +++ b/src/xspice/mif/mifutil.c @@ -0,0 +1,324 @@ +/*============================================================================ +FILE MIFutil.c + +MEMBER OF process XSPICE + +Copyright 1991 +Georgia Tech Research Corporation +Atlanta, Georgia 30332 +All Rights Reserved + +PROJECT A-8503 + +AUTHORS + + 9/12/91 Bill Kuhn + +MODIFICATIONS + + + +SUMMARY + + This file contains various utility routines used by the MIF package. + +INTERFACES + + MIFgettok() + MIFget_token() + MIFget_cntl_src_type() + MIFcopy() + +REFERENCED FILES + + None. + +NON-STANDARD FEATURES + + None. + +============================================================================*/ + +/* #include "prefix.h" */ +#include "ngspice.h" + +//#include "util.h" +#include "cpstd.h" +#include +#include "miftypes.h" +#include "mifproto.h" + +/* #include "suffix.h" */ + + + + +/* + +MIFgettok + +Get the next token from the input string. The input string pointer +is advanced to the following token and the token from the input +string is copied to malloced storage and a pointer to that storage +is returned. The original input string is undisturbed. + +MIFgettok treats ( and ) like whitespace. + +*/ + +char *MIFgettok(char **s) +{ + + char *buf; /* temporary storage to copy token into */ + char *ret_str; /* storage for returned string */ + + int i; + + /* allocate space big enough for the whole string */ + + buf = (void *) MALLOC(strlen(*s) + 1); + + /* skip over any white space */ + + while(isspace(**s) || (**s == '=') || + (**s == '(') || (**s == ')') || (**s == ',')) + (*s)++; + + /* isolate the next token */ + + switch(**s) { + + case '\0': + FREE(buf); + return(NULL); + + case '<': + case '>': + case '[': + case ']': + case '~': + case '%': + buf[0] = **s; + buf[1] = '\0'; + (*s)++; + break; + + default: + i = 0; + /* if first character is a quote, read until the closing */ + /* quote, or the end of string, discarding the quotes */ + if(**s == '"') { + (*s)++; + while( (**s != '\0') && (**s != '"') ) { + buf[i] = **s; + i++; + (*s)++; + } + if(**s == '"') + (*s)++; + } + /* else, read until the next delimiter */ + else { + while( (**s != '\0') && + (! ( isspace(**s) || (**s == '=') || (**s == '%') || + (**s == '(') || (**s == ')') || (**s == ',') || + (**s == '[') || (**s == ']') || + (**s == '<') || (**s == '>') || (**s == '~') + ) ) ) { + buf[i] = **s; + i++; + (*s)++; + } + } + + buf[i] = '\0'; + break; + } + + /* skip over white space up to next token */ + + while(isspace(**s) || (**s == '=') || + (**s == '(') || (**s == ')') || (**s == ',')) + (*s)++; + + /* make a copy using only the space needed by the string length */ + /* Changed from copy to MIFcopy by SDB on 6.22.2003 */ + ret_str = MIFcopy(buf); + FREE(buf); + + return(ret_str); +} + + + + +/* + +MIFget_token + +Get the next token from the input string together with its type. +The input string pointer +is advanced to the following token and the token from the input +string is copied to malloced storage and a pointer to that storage +is returned. The original input string is undisturbed. + +*/ + +char *MIFget_token( + char **s, /* The text line to get the token from */ + Mif_Token_Type_t *type) /* The type of token found */ +{ + + char *ret_str; /* storage for returned string */ + + /* get the token from the input line */ + + ret_str = MIFgettok(s); + + + /* if no next token, return */ + + if(ret_str == NULL) { + *type = MIF_NO_TOK; + return(NULL); + } + + /* else, determine and return token type */ + + switch(*ret_str) { + + case '[': + *type = MIF_LARRAY_TOK; + break; + + case ']': + *type = MIF_RARRAY_TOK; + break; + + case '<': + *type = MIF_LCOMPLEX_TOK; + break; + + case '>': + *type = MIF_RCOMPLEX_TOK; + break; + + case '%': + *type = MIF_PERCENT_TOK; + break; + + case '~': + *type = MIF_TILDE_TOK; + break; + + default: + if(strcmp(ret_str, "null") == 0) + *type = MIF_NULL_TOK; + else + *type = MIF_STRING_TOK; + break; + + } + + return(ret_str); +} + + + +/* +MIFget_cntl_src_type + +This function takes an input connection/port type and an output +connection/port type (MIF_VOLTAGE, MIF_CURRENT, etc.) and maps +this pair to one of the four controlled source types used in +SPICE (VCVS, VCIS, ICVS, ICIS). +*/ + + +Mif_Cntl_Src_Type_t MIFget_cntl_src_type( + Mif_Port_Type_t in_port_type, /* The type of the input port */ + Mif_Port_Type_t out_port_type) /* The type of the output port */ +{ + + switch(in_port_type) { + + case MIF_VOLTAGE: + case MIF_DIFF_VOLTAGE: + case MIF_CONDUCTANCE: + case MIF_DIFF_CONDUCTANCE: + + switch(out_port_type) { + + case MIF_VOLTAGE: + case MIF_DIFF_VOLTAGE: + case MIF_RESISTANCE: + case MIF_DIFF_RESISTANCE: + return(MIF_VCVS); + break; + + case MIF_CURRENT: + case MIF_DIFF_CURRENT: + case MIF_CONDUCTANCE: + case MIF_DIFF_CONDUCTANCE: + return(MIF_VCIS); + break; + + default: + break; + + } + break; + + case MIF_CURRENT: + case MIF_DIFF_CURRENT: + case MIF_VSOURCE_CURRENT: + case MIF_RESISTANCE: + case MIF_DIFF_RESISTANCE: + + switch(out_port_type) { + + case MIF_VOLTAGE: + case MIF_DIFF_VOLTAGE: + case MIF_RESISTANCE: + case MIF_DIFF_RESISTANCE: + return(MIF_ICVS); + break; + + case MIF_CURRENT: + case MIF_DIFF_CURRENT: + case MIF_CONDUCTANCE: + case MIF_DIFF_CONDUCTANCE: + return(MIF_ICIS); + break; + + default: + break; + + } + break; + + default: + break; + + } + + return(-1); +} + + +/* +MIFcopy + +This function allocates a new copy of a string. +*/ + +char *MIFcopy(char *str) +{ + char *temp; + + /* Allocate space for the string and then copy it */ + temp = MALLOC(strlen(str) + 1); + strcpy(temp,str); + + return(temp); +} diff --git a/src/xspice/xspice.c b/src/xspice/xspice.c new file mode 100755 index 000000000..3c89f0097 --- /dev/null +++ b/src/xspice/xspice.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include + + +/*how annoying!, needed for structure below*/ +void *tcalloc(size_t a, size_t b){ + return tmalloc(a*b); +} + +static void *empty(void){ + return NULL; +} + +struct coreInfo_t coreInfo = +{ + MIF_INP2A, + MIFgetMod, + MIFgetValue, + MIFsetup, + (int (*)(GENmodel *, CKTcircuit *))empty, + MIFload, + MIFmParam, + MIFask, + MIFmAsk, + MIFtrunc, + MIFconvTest, + MIFdelete, + MIFmDelete, + MIFdestroy, + MIFgettok, + MIFget_token, + MIFget_cntl_src_type, + MIFcopy, + cm_climit_fcn, + cm_smooth_corner, + cm_smooth_discontinuity, + cm_smooth_pwl, + cm_analog_ramp_factor, + cm_analog_alloc, + cm_analog_get_ptr, + cm_analog_integrate, + cm_analog_converge, + cm_analog_set_temp_bkpt, + cm_analog_set_perm_bkpt, + cm_analog_not_converged, + cm_analog_auto_partial, + cm_event_alloc, + cm_event_get_ptr, + cm_event_queue, + cm_message_get_errmsg, + cm_message_send, + cm_netlist_get_c, + cm_netlist_get_l, + cm_complex_set, + cm_complex_add, + cm_complex_subtract, + cm_complex_multiply, + cm_complex_divide, + (FILE *(*)(void))empty, + (FILE *(*)(void))empty, + (FILE *(*)(void))empty, +#ifndef HAVE_LIBGC + tmalloc, + tcalloc, + trealloc, + txfree, + (char *(*)(int))tmalloc, + (char *(*)(char *,int))trealloc, + (void (*)(char *))txfree +#else + GC_malloc, + tcalloc, + GC_realloc, + (void (*)(void *))empty, + (char *(*)(int))GC_malloc, + (char *(*)(char *,int))GC_realloc, + (void (*)(char *))empty +#endif +};