diff --git a/ChangeLog b/ChangeLog index 0a37e02d7..992aa281c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2004-06-02 Adrian Dawe + + * src/frontend/com_dl.c src/frontend/com_dl.c src/frontend/commands.c: + Add function xaltermod + +2004-06-02 Stefan Jones + + * configure.in: + Change to version 16 + Disable GnuReadline in tclspice + + * src/tclspice.c: + Added trigger and step callbacks + Fix redef of sighandler_t + + * src/pkgIndex.tcl.in: + set a global variable spice_version + 2004-05-26 Stefan Jones * src/tclspice.c src/include/ngspice.h src/misc/tilde.c diff --git a/configure.in b/configure.in index 44cf250e9..6f99611e2 100644 --- a/configure.in +++ b/configure.in @@ -5,7 +5,7 @@ dnl Create a configuration header AM_CONFIG_HEADER(config.h) dnl Initialize automake stuff -AM_INIT_AUTOMAKE(tclspice,0.2.15) +AM_INIT_AUTOMAKE(tclspice,0.2.16) dnl --enable-ftedebug : enable frontend debug macros AC_ARG_ENABLE(ftedebug, @@ -435,9 +435,9 @@ if test "$enable_cluster" = "yes"; then LIBS="$LIBS -lpthread" fi - dnl ---- Option to include GNU readline support in ngspice CLI ---- -if test "$with_readline" != "no"; then +if test "$with_readline" != "no" -a \ + \( "x$with_tcl" == "x" -o "$with_tcl" == "no" \) ; then AC_MSG_RESULT(Checking for readline:) AC_CHECK_HEADERS([readline/readline.h readline/history.h], [AC_DEFINE(HAVE_GNUREADLINE)], diff --git a/src/frontend/com_dl.c b/src/frontend/com_dl.c index b219393d5..c3fb3515d 100755 --- a/src/frontend/com_dl.c +++ b/src/frontend/com_dl.c @@ -4,6 +4,18 @@ #include <../spicelib/devices/dev.h> /*for load library commands*/ #ifdef XSPICE + +#include "mifproto.h" +#include "inpdefs.h" +#include "fteext.h" +#include "ifsim.h" +#include "dllitf.h" +#include "iferrmsg.h" +#include + + + + void com_codemodel(wordlist *wl){ wordlist *ww; for(ww = wl;ww;ww = ww->wl_next) @@ -11,6 +23,138 @@ void com_codemodel(wordlist *wl){ fprintf(cp_err,"Error: Library %s couldn't be loaded!\n",ww->wl_word); return; } + + + +void com_xaltermod(wordlist *wl){ + wordlist *ww, *eqword, *words; + int i, err, typecode=-1; + GENinstance *devptr=(GENinstance *)NULL; + GENmodel *modptr=(GENmodel *)NULL; + IFdevice *device; + IFparm *opt; + IFvalue nval; + bool found; + char *dev, *p; + char *param; + + if (!ft_curckt) { + fprintf(cp_err, "Error: no circuit loaded\n"); + return; + } + + + if (!wl) { + fprintf(cp_err, "usage: xaltermod dev param = expression\n"); + return; + } + + + words = wl; + while (words) { + p = words->wl_word; + eqword = words; + words = words->wl_next; + if (eq(p, "=")) { + break; + } + } + if (!words) { + fprintf(cp_err, "usage: xaltermod dev param = expression\n"); + return; + } + + dev = NULL; + param = NULL; + words = wl; + while (words != eqword) { + p = words->wl_word; + if (param) { + fprintf(cp_err, "Error: excess parameter name \"%s\" ignored.\n",p); + } else if (dev) { + param = words->wl_word; + } else { + dev = p; + } + words = words->wl_next; + } + if (!dev) { + fprintf(cp_err, "Error: no model name provided.\n" ); + return; + } + + words = eqword->wl_next; + + INPretrieve(&dev,(INPtables *)ft_curckt->ci_symtab); + + err = (*(ft_sim->findInstance))(ft_curckt->ci_ckt,&typecode,(void **)&devptr,dev,NULL,NULL); + + if (err != OK) { + typecode = -1; + devptr = (void *)NULL; + err = (*(ft_sim->findModel))(ft_curckt->ci_ckt,&typecode,(void **)&modptr,dev); + } + + if (err != OK) { + printf("Error: no such model '%s'.\n",dev); + printf(" use spice::showmod all, to display all models\n"); + return; + } + + device = ft_sim->devices[typecode]; + found = FALSE; + + int id; + int numParams =0; + + for(ww = words;ww;ww = ww->wl_next) + numParams ++; + + for (i = 0; i < *(device->numModelParms); i++) { + opt = &device->modelParms[i]; + + if (strcmp(opt->keyword,param) == 0) { + id = opt->id; + nval.v.numValue = 0; + + if (opt->dataType & IF_VECTOR) { + switch (opt->dataType & (IF_VARTYPES & ~IF_VECTOR)) + { + case IF_STRING: + found = TRUE; + nval.v.vec.sVec = malloc((numParams +1) * sizeof(char *)); + for(ww = words;ww;ww = ww->wl_next) { + nval.v.vec.sVec[nval.v.numValue] = ww->wl_word; + nval.v.numValue++; + } + break; + + default: + printf("Error: xaltermod only supports vectors of strings.\n"); + printf(" use spice::altermod\n"); + return; + break; + } + } else { + printf("Error: xaltermod only supports vectors of strings.\n"); + printf(" use spice::altermod\n"); + return; + } + } + } + + if (found == FALSE) { + printf("Error: no parameter '%s' in model '%s'.\n", param, dev); + printf(" use spice::showmod all, to list all model parameters\n"); + return; + } + + err = (*(ft_sim->setModelParm))((void *)ft_curckt->ci_ckt, (void *)modptr, + id, &nval, (IFvalue *)NULL); + + return; +} + #endif #ifdef DEVLIB void com_use(wordlist *wl){ diff --git a/src/frontend/com_dl.h b/src/frontend/com_dl.h index e8210cefd..d8995e633 100755 --- a/src/frontend/com_dl.h +++ b/src/frontend/com_dl.h @@ -3,6 +3,7 @@ #ifdef XSPICE void com_codemodel(wordlist *wl); +void com_xaltermod(wordlist *wl); #endif #ifdef DEVLIB diff --git a/src/frontend/commands.c b/src/frontend/commands.c index 1666ee85c..093780642 100644 --- a/src/frontend/commands.c +++ b/src/frontend/commands.c @@ -184,6 +184,11 @@ struct comm spcp_coms[] = { { 040000, 040000, 040000, 040000 }, E_BEGINNING, 1, LOTS, (void (*)()) NULL, "library library ... : Loads the opus librarys." } , + { "xaltermod", com_xaltermod, FALSE, FALSE, TRUE, + { 040000, 040000, 040000, 040000 }, E_BEGINNING, 1, LOTS, + (void (*)()) NULL, + "library library ... : Loads the opus librarys." } , + #endif #ifdef DEVLIB { "use", com_use, FALSE, FALSE, TRUE, diff --git a/src/pkgIndex.tcl.in b/src/pkgIndex.tcl.in index eb59cc94e..cdb218fa4 100755 --- a/src/pkgIndex.tcl.in +++ b/src/pkgIndex.tcl.in @@ -79,6 +79,7 @@ proc Loadspice { version dir } { spicewish::init_gui $spicefile } + set ::spice_version $version } proc q { } { exit } diff --git a/src/tclspice.c b/src/tclspice.c index fe3d97c66..5e119b499 100755 --- a/src/tclspice.c +++ b/src/tclspice.c @@ -53,9 +53,8 @@ /* To interupt a spice run */ #include -#ifndef sighandler_t -typedef void (*sighandler_t)(int); -#endif +typedef void (*sighandler)(int); + #include extern sigjmp_buf jbuf; @@ -109,14 +108,14 @@ typedef struct { } vector; /*The current run (to get variable names, etc)*/ -static runDesc *cur_run = NULL; +static runDesc *cur_run; -static vector *vectors = NULL; +static vector *vectors; -static int ownVectors = 0; +static int ownVectors; /* save this each time called */ -static Tcl_Interp *spice_interp=NULL; +static Tcl_Interp *spice_interp; #define save_interp() \ do {\ spice_interp = interp;\ @@ -127,9 +126,9 @@ do {\ /****************************************************************************/ /*this holds the number of time points done (altered by spice)*/ -int steps_completed = 0; +int steps_completed; /* number of bltvectors*/ -static int blt_vnum = 0; +static int blt_vnum; /*Native Tcl functions */ @@ -303,6 +302,55 @@ static int lastVector TCL_CMDPROCARGS(clientData,interp,argc,argv) { return TCL_OK; } +/*agr1: spice variable name + *arg2: index + */ +static int get_value TCL_CMDPROCARGS(clientData,interp,argc,argv) { + char *var; + int i, vindex, j; + double val=0; + if(argc != 3) { + Tcl_SetResult(interp, + "Wrong # args. spice::get_value spice_variable index", + TCL_STATIC); + return TCL_ERROR; + } + var = (char *)argv[1]; + + for(i=0;i < blt_vnum && strcmp(var,vectors[i].name);i++); + + if(i == blt_vnum) { + Tcl_SetResult(interp, "Bad spice variable ",TCL_STATIC); + Tcl_AppendResult(interp, (char *)var, TCL_STATIC); + return TCL_ERROR; + } else vindex = i; + + j = atoi(argv[2]); + +#ifdef HAVE_LIBPTHREAD + pthread_mutex_lock(&vectors[vindex].mutex); +#endif + + if(j < 0 || j >= vectors[vindex].length) { + i = 1; + } else { + i = 0; + val = vectors[vindex].data[j]; + } + +#ifdef HAVE_LIBPTHREAD + pthread_mutex_unlock(&vectors[vindex].mutex); +#endif + + if(i) { + Tcl_SetResult(interp, "Index out of range",TCL_STATIC); + return TCL_ERROR; + } else { + Tcl_SetObjResult(interp,Tcl_NewDoubleObj(val)); + return TCL_OK; + } +} + /*agr1: spice variable name *arg2: blt_vector *arg3: start copy index, optional @@ -310,7 +358,7 @@ static int lastVector TCL_CMDPROCARGS(clientData,interp,argc,argv) { */ static int spicetoblt TCL_CMDPROCARGS(clientData,interp,argc,argv) { Blt_Vector *vec; - int index, i; + int j, i; char *blt, *var; int start=0,end=-1,len; @@ -328,7 +376,7 @@ static int spicetoblt TCL_CMDPROCARGS(clientData,interp,argc,argv) { Tcl_SetResult(interp, "Bad spice variable ",TCL_STATIC); Tcl_AppendResult(interp, (char *)var, TCL_STATIC); return TCL_ERROR; - } else index = i; + } else j = i; if(Blt_GetVector(interp,blt,&vec)){ Tcl_SetResult(interp, "Bad blt vector ",TCL_STATIC); @@ -341,12 +389,12 @@ static int spicetoblt TCL_CMDPROCARGS(clientData,interp,argc,argv) { start = atoi(argv[3]); if(argc == 5) end = atoi(argv[4]); - if(vectors[index].length) { + if(vectors[j].length) { #ifdef HAVE_LIBPTHREAD - pthread_mutex_lock(&vectors[index].mutex); + pthread_mutex_lock(&vectors[j].mutex); #endif - len = vectors[index].length; + len = vectors[j].length; if(start){ start = start % len; @@ -360,11 +408,11 @@ static int spicetoblt TCL_CMDPROCARGS(clientData,interp,argc,argv) { len = abs(end - start + 1); - Blt_ResetVector(vec,(vectors[index].data + start),len, + Blt_ResetVector(vec,(vectors[j].data + start),len, len,TCL_VOLATILE); #ifdef HAVE_LIBPTHREAD - pthread_mutex_unlock(&vectors[index].mutex); + pthread_mutex_unlock(&vectors[j].mutex); #endif } return TCL_OK; @@ -422,7 +470,7 @@ static int _thread_stop(){ static int _run(int argc,char **argv){ char buf[1024] = ""; int i; - sighandler_t oldHandler; + sighandler oldHandler; #ifdef HAVE_LIBPTHREAD char *string; bool fl_bg = FALSE; @@ -1146,8 +1194,8 @@ struct triggerEvent { }; -struct triggerEvent *eventQueue=NULL; -struct triggerEvent *eventQueueEnd=NULL; +struct triggerEvent *eventQueue; +struct triggerEvent *eventQueueEnd; #ifdef HAVE_LIBPTHREAD pthread_mutex_t triggerMutex; @@ -1166,7 +1214,103 @@ struct watch { double oV; }; -struct watch *watches=NULL; +struct watch *watches; + +char *triggerCallback; +unsigned int triggerPollTime=1; + +char *stepCallback; +unsigned int stepPollTime=1; +unsigned int stepCount=1; +int stepCallbackPending; + +void stepEventSetup(ClientData clientData,int flags) { + Tcl_Time t; + if(stepCallbackPending) { + t.sec = 0; + t.usec = 0; + } else { + t.sec = stepPollTime / 1000; + t.usec = (stepPollTime % 1000) * 1000; + } + Tcl_SetMaxBlockTime(&t); +} + +int stepEventHandler(Tcl_Event *evPtr, int flags) { + if(stepCallbackPending) { + stepCallbackPending = 0; + Tcl_Preserve((ClientData)spice_interp); + Tcl_Eval(spice_interp,stepCallback); + Tcl_ResetResult(spice_interp); + Tcl_Release((ClientData)spice_interp); + } + return TCL_OK; +} + +void stepEventCheck(ClientData clientData,int flags) { + if(stepCallbackPending) { + Tcl_Event *tclEvent; + tclEvent = (Tcl_Event *) ckalloc(sizeof(Tcl_Event)); + tclEvent->proc = stepEventHandler; + Tcl_QueueEvent(tclEvent,TCL_QUEUE_TAIL); + } +} + +int triggerEventHandler(Tcl_Event *evPtr, int flags) { + static char buf[512]; + int rtn=TCL_OK; + Tcl_Preserve((ClientData)spice_interp); +#ifdef HAVE_LIBPTHREAD + pthread_mutex_lock(&triggerMutex); +#endif + while(eventQueue) { + struct triggerEvent *event = eventQueue; + eventQueue = eventQueue->next; + + snprintf(buf,512,"%s %s %g %d %d",triggerCallback,vectors[event->vector].name, + event->time,event->stepNumber,event->type); + rtn = Tcl_Eval(spice_interp,buf); + FREE(event); + if(rtn) { + goto quit; + } + } + eventQueueEnd = NULL; +quit: +#ifdef HAVE_LIBPTHREAD + pthread_mutex_unlock(&triggerMutex); +#endif + Tcl_ResetResult(spice_interp); + Tcl_Release((ClientData)spice_interp); + return TCL_OK; +} + +void triggerEventSetup(ClientData clientData,int flags) { + Tcl_Time t; + if(eventQueue) { + t.sec = 0; + t.usec = 0; + } else { + t.sec = triggerPollTime / 1000; + t.usec = (triggerPollTime % 1000) * 1000; + } + Tcl_SetMaxBlockTime(&t); +} + +void triggerEventCheck(ClientData clientData,int flags) { +#ifdef HAVE_LIBPTHREAD + pthread_mutex_lock(&triggerMutex); +#endif + if(eventQueue) { + Tcl_Event *tclEvent; + tclEvent = (Tcl_Event *) ckalloc(sizeof(Tcl_Event)); + tclEvent->proc = triggerEventHandler; + Tcl_QueueEvent(tclEvent,TCL_QUEUE_TAIL); + } +#ifdef HAVE_LIBPTHREAD + pthread_mutex_unlock(&triggerMutex); +#endif +} int Tcl_ExecutePerLoop() { @@ -1225,12 +1369,32 @@ int Tcl_ExecutePerLoop() { pthread_mutex_unlock(&v->mutex); #endif } + + if(stepCallback && vectors[0].length % stepCount == 0) { + stepCallbackPending=1; + } #ifdef HAVE_LIBPTHREAD pthread_mutex_unlock(&triggerMutex); pthread_mutex_unlock(&vectors[0].mutex); + + if(triggerCallback && eventQueue && bgtid != pthread_self()) { + triggerEventHandler(NULL,0); + } + + if(stepCallback && stepCallbackPending && bgtid != pthread_self()) { + stepEventHandler(NULL,0); + } +#else + if(triggerCallback && eventQueue) { + triggerEventHandler(NULL,0); + } + if(stepCallback && stepCallbackPending) { + triggerEventHandler(NULL,0); + } #endif + return 0; } @@ -1261,6 +1425,83 @@ static int resetTriggers() { } +/* Registers a watch for a trigger + *arg0: Callback function (optional - none removes callback) + *arg1: Poll interval usec (optional - defaults to 500000 ) + */ +static int registerTriggerCallback TCL_CMDPROCARGS(clientData,interp,argc,argv){ + + if (argc > 3) { + Tcl_SetResult(interp, + "Wrong # args. spice::registerTriggerCallback ?proc? ?ms?", + TCL_STATIC); + return TCL_ERROR; + } + + if(triggerCallback) { + Tcl_DeleteEventSource(triggerEventSetup,triggerEventCheck,NULL); + free(triggerCallback); + triggerCallback = NULL; + } + + if(argc == 1) { + return TCL_OK; + } + + triggerCallback = strdup(argv[1]); + Tcl_CreateEventSource(triggerEventSetup,triggerEventCheck,NULL); + + if(argc == 3) { + triggerPollTime = atoi(argv[2]); + if(triggerPollTime == 0) + triggerPollTime = 500; + } + + return TCL_OK; +} + +/* Registers step counter callback + *arg0: Callback function (optional - none removes callback) + *arg1: Number of steps per Callback + *arg2: Poll interval usec (optional - defaults to 500000 ) + */ +static int registerStepCallback TCL_CMDPROCARGS(clientData,interp,argc,argv){ + + if (argc > 4) { + Tcl_SetResult(interp, + "Wrong # args. spice::registerStepCallback ?proc? ?steps? ?ms?", + TCL_STATIC); + return TCL_ERROR; + } + + if(stepCallback) { + Tcl_DeleteEventSource(stepEventSetup,stepEventCheck,NULL); + free(stepCallback); + stepCallback = NULL; + } + + if(argc == 1) { + return TCL_OK; + } + + stepCallback = strdup(argv[1]); + Tcl_CreateEventSource(stepEventSetup,stepEventCheck,NULL); + + if(argc >= 3) { + stepCount = atoi(argv[2]); + if(stepCount == 0) + stepCount = 1; + } + + if(argc == 4) { + stepPollTime = atoi(argv[3]); + if(stepPollTime == 0) + stepPollTime = 50; + } + + return TCL_OK; +} + /* Registers a watch for a trigger *arg0: Vector Name to watch *arg1: Vmin @@ -1390,7 +1631,8 @@ static int popTriggerEvent TCL_CMDPROCARGS(clientData,interp,argc,argv){ popedEvent = eventQueue; eventQueue = popedEvent->next; - + if(eventQueue == NULL) + eventQueueEnd = NULL; list = Tcl_NewListObj(0,NULL); @@ -1470,7 +1712,7 @@ int Spice_Init(Tcl_Interp *interp) { char *key; Tcl_CmdInfo infoPtr; char buf[256]; - sighandler_t old_sigint; + sighandler old_sigint; ft_rawfile = NULL; ivars( ); @@ -1563,6 +1805,7 @@ bot: Tcl_CreateCommand(interp, TCLSPICE_prefix "spice_data", spice_data, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, TCLSPICE_prefix "spicetoblt", spicetoblt, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, TCLSPICE_prefix "lastVector", lastVector, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "get_value", get_value, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, TCLSPICE_prefix "spice", _spice_dispatch, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, TCLSPICE_prefix "get_output", get_output, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, TCLSPICE_prefix "get_param", get_param, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); @@ -1576,16 +1819,21 @@ bot: Tcl_CreateCommand(interp, TCLSPICE_prefix "plot_date", plot_date, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, TCLSPICE_prefix "plot_name", plot_name, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, TCLSPICE_prefix "plot_nvars", plot_nvars, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); - Tcl_CreateCommand(interp, TCLSPICE_prefix "registerTrigger", registerTrigger, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); - Tcl_CreateCommand(interp, TCLSPICE_prefix "popTriggerEvent", popTriggerEvent, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + + Tcl_CreateCommand(interp, TCLSPICE_prefix "registerTrigger", registerTrigger, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "registerTriggerCallback", registerTriggerCallback, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "popTriggerEvent", popTriggerEvent, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, TCLSPICE_prefix "unregisterTrigger", unregisterTrigger, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, TCLSPICE_prefix "listTriggers", listTriggers, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, TCLSPICE_prefix "registerStepCallback", registerTriggerCallback, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); #ifdef HAVE_LIBPTHREAD Tcl_CreateCommand(interp, TCLSPICE_prefix "bg", _tcl_dispatch, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, TCLSPICE_prefix "halt", _tcl_dispatch, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, TCLSPICE_prefix "running", running, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); #endif + + Tcl_CreateCommand(interp, TCLSPICE_prefix "registerStepCallback", registerStepCallback, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_LinkVar(interp, TCLSPICE_prefix "steps_completed", (char *)&steps_completed, TCL_LINK_READ_ONLY|TCL_LINK_INT); Tcl_LinkVar(interp, TCLSPICE_prefix "blt_vnum", (char *)&blt_vnum, TCL_LINK_READ_ONLY|TCL_LINK_INT);