Merge branch 'pre-master-45' into bt_dev

This commit is contained in:
Brian Taylor 2025-04-18 08:55:47 -07:00
commit fdf1a7d791
28 changed files with 2083 additions and 333 deletions

93
examples/shared/README Normal file
View File

@ -0,0 +1,93 @@
This directory contains the source code (shx.c) for a simple utility
for exercising the Ngspice shared library API. The path to the ngspice
include file directory must be specified when compiling. When compiling
in a complete source tree:
cc -I../../src/include/ngspice -o shx shx.c
or for Windows with VisualC++:
CL.EXE /O2 /I..\..\src\include\ngspice shx.c
If a shared library binary package is installed, the inclusion option
may be unnecessary.
When run, the program dynamically loads the shared library. It is assumed to
be located in the current directory (./libngspice.so or libngspice.DLL).
The full path to the library may be specified by the "-l" option, so if the
library was built by the usual method:
./shx -l ../../releasesh/src/.libs/libngspice.so
or
shx -l ..\..\visualc\sharedspice\Release.x64\ngspice.dll
If the '-a" option is given, the program exercises some example circuits
and exits. That assumes that the current directory is the one containing this
file. Otherwise the program is interactive and prompts for a command.
If the input line starts with "/" an internal command for exercising the API
will be executed. Other lines are passed to the shared library's command
interpreter.
These internal commands are available:
/avec <plot_name>
Lists the vectors in the named plot, using ngSpice_AllVecs().
/aevt
Lists all XSPICE event nodes, using ngSpice_AllEvtNodes().
/aplot
Lists all plot names, using ngSpice_AllPlots().
/bgr
Shows the state of the background thread, using ngSpice_running().
/cplot
Show the name of the current plot, using ngSpice_CurPlot().
/dlim <limit>
To reduce repetitive output, this command sets a limit for the number
of SendData callbacks that will be reported. The parameter should be
a positive integer, 0 to report all, or -1 for unlimited output.
The initial value is 10.
/elim <limit>
Like /dlim but for XSPICE event data callbacks.
/help
Shows a summary of minternal commands.
/lvals
Toggles a flag that causes all node values to be listed for
SendData callbacks.
/reset
Resets Ngspice (call to ngSpice_Reset()) and internal variables.
/sask
Toggles a flag that control interactive prompting for
EXTERNAL source values. As a special case, the /sask command
is accepted at the prompt.
/slim <limit>
Like /dlim, but sets an independent limit for reporting callbacks that
request an new value for EXTERNAL sources.
/sramp [new_val] [interval end_val]
Stores data used to genarate an automatic response to EXTERNAL queries.
A single parameter sets a new value for the future (step change),
two values define a ramp to a specified value at a future time,
three values do both. If there is more than one such source, the same
value is used for all.
/vec <vector>
Query a spacific data vector, using ngSpice_GVI().
/xnode <node ...>
Requests raw event callbacks for an event node. That reports all node
events, not just the value when a timestep ends. Uses ngSpice_Raw_Evt().
Also found here are an example circuit, t.cir, with analog and event nodes
and an EXTERNAL voltage source. File t.shx contains inputs to shx that
run the circuit and use most of the internal commands. Run as:
./shx < t.shx
or for Windows
shx < t.txt
To view the results, run Ngspice as a program and enter:
load t.raw
plot v(ctl) v(div)

829
examples/shared/shx.c Normal file
View File

@ -0,0 +1,829 @@
/*
Test file for shared ngspice
Copyright Holger Vogt 2017-2024
Local commands: Giles Atkinson 2025
New BSD license
ngspice library loaded dynamically
simple manual input
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#ifndef _MSC_VER
#include <stdbool.h>
#include <pthread.h>
#else
#define bool int
#define true 1
#define false 0
#define strdup _strdup
#endif
#define XSPICE
#include "sharedspice.h"
typedef void *funptr_t;
#if defined(__MINGW32__) || defined(__CYGWIN__) || defined(_MSC_VER)
#undef BOOLEAN
#define LOAD_STRING "libngspice.DLL"
#include <windows.h>
void *dlopen (const char *, int);
funptr_t dlsym (void *, const char *);
int dlclose (void *);
char *dlerror (void);
#define RTLD_LAZY 1 /* lazy function call binding */
#define RTLD_NOW 2 /* immediate function call binding */
#define RTLD_GLOBAL 4 /* Symbols are externally visible. */
static char errstr[128];
#else
#if defined(__APPLE__)
#define LOAD_STRING "./libngspice.dylib"
#else
#define LOAD_STRING "./libngspice.so"
#endif
#include <dlfcn.h> /* to load libraries*/
#include <unistd.h>
#endif /* not Windows */
#include <ctype.h>
/* pointers to functions exported by ngspice */
char ** (*ngSpice_AllEvtNodes_handle)(void);
char ** (*ngSpice_AllPlots_handle)(void);
char ** (*ngSpice_AllVecs_handle)(char*);
int (*ngSpice_Command_handle)(char*);
int (*ngSpice_Circ_handle)(char**);
char * (*ngSpice_CurPlot_handle)(void);
int (*ngSpice_Decode_Evt_handle)(void *, int,
double *, const char **);
pvector_info (*ngSpice_GVI_handle)(char*);
int (*ngSpice_Init_handle)(SendChar*, SendStat*, ControlledExit*,
SendData*, SendInitData*, BGThreadRunning*,
void*);
int (*ngSpice_Init_Evt_handle)(SendEvtData*, SendInitEvtData*, void*);
int (*ngSpice_Init_Sync_handle)(GetVSRCData*, GetISRCData*,
GetSyncData*, int*, void*);
int (*ngSpice_Raw_Evt_handle)(char *, SendRawEvtData *, void *);
int (*ngSpice_Reset_handle)(void);
bool (*ngSpice_running_handle)(void);
bool no_bg = true;
bool not_yet = true;
/* Limit output from SendData, SendEvtData and VSRCData callbacks. */
static unsigned int sd_limit = 10, sd_count, sd_list;
static unsigned int se_limit = 10, se_count;
static unsigned int sq_limit = 10, sq_count, sq_ask;
/* Automatic ramp of source query replies. */
double sr_last_time = -1, sr_target_time = -1;
double sr_last_val, sr_target_val, sim_time;
static int cieq(register char *p, register char *s);
static int ciprefix(const char *p, const char *s);
static int getLine(char *prmpt, char *buff, size_t sz);
/* Callback functions used by ngspice and defined below. */
static int
ng_getchar(char* outputreturn, int ident, void* userdata);
static int
ng_getstat(char* outputreturn, int ident, void* userdata);
static int
ng_thread_runs(bool noruns, int ident, void* userdata);
static int ng_rawevt(double, void *, void *, int);
static ControlledExit ng_exit;
static SendData ng_data;
static SendInitData ng_initdata;
static SendInitEvtData ng_initevtdata;
static SendEvtData ng_evtdata;
static GetVSRCData ng_srcdata;
static GetSyncData ng_syncdata;
char comd[1024];
void * ngdllhandle = NULL;
/* Register call-back functions. */
static void register_cbs(void) {
static int z = 0;
char *bad;
int ret;
ret = ngSpice_Init_handle(ng_getchar, ng_getstat,
ng_exit, ng_data, ng_initdata, ng_thread_runs,
NULL);
if (ret) {
bad = "ngSpice_Init";
goto fail;
}
ret = ngSpice_Init_Evt_handle(ng_evtdata, ng_initevtdata, NULL);
if (!ret) {
bad = "ngSpice_Init_Evt";
goto fail;
}
ret = ngSpice_Init_Sync_handle(ng_srcdata, ng_srcdata, ng_syncdata,
&z, NULL);
if (ret == 0)
return;
bad = "ngSpice_Init_Sync";
fail:
fprintf(stderr, "Init call %s() failed: %d\n", bad, ret);
exit(2);
}
/* Non-interactive test: run a selection of circuits. */
static int auto_test(void)
{
#define SRC_FMT "source ../%s" /* Assume cd is examples/shared. */
static const char * const tests[] = {
"transient-noise/shot_ng.cir",
"soi/inv_tr.sp",
"p-to-n-examples/op-test-adi.cir",
"digital/compare/adder_Xspice.cir",
NULL
};
int ret, i;
char msgbuf[128];
for (i = 0; tests[i]; ++i) {
register_cbs();
snprintf(msgbuf, sizeof msgbuf, "echo run no. %d", i + 1);
ret = ngSpice_Command_handle(msgbuf);
if (ret)
return ret;
snprintf(msgbuf, sizeof msgbuf, SRC_FMT, tests[i]);
ret = ngSpice_Command_handle(msgbuf);
if (ret)
return ret;
ret = ngSpice_Reset_handle();
if (ret)
return ret;
}
return 0;
}
static funptr_t getsym(const char *sym)
{
funptr_t value;
value = dlsym(ngdllhandle, sym);
if (!value) {
fprintf(stderr, "Ngspice symbol %s was not found: %s\n",
sym, dlerror());
exit(1);
}
return value;
}
/* String utilities. */
char *skip(char *cp)
{
while (isspace(*cp))
++cp;
return cp;
}
char *tail(char *cp)
{
while (*cp && !isspace(*cp))
++cp;
return cp;
}
/* Execute a local command, identified by a leading '/'. */
static void help(char *cmd) {
puts("Local commands are:\n"
" /aevt\t\t\tList event nodes.\n"
" /aplot\t\tList plot names.\n"
" /avec <plot_name>\tList vectors in plot.\n"
" /bgr\t\t\tQuery background thread.\n"
" /cplot\t\tShow name of current plot.\n"
" /dlim <limit>\t\tSet output limit for SendData CB.\n"
" /elim <limit>\t\tSet output limit for SendEvtData CB.\n"
" /help\t\t\tPrint this message.\n"
" /lvals\t\tList node values on data callback (toggle).\n"
" /reset\t\tReset Ngspice.\n"
" /sask\t\t\tAsk for V/ISRC values (toggles).\n"
" /slim <limit>\t\tSet output limit for SendxSRCData CB.\n"
" /sramp [new_val] [interval end_val]\n"
"\t\t\tAuto-ramp sources\n"
" /vec <vector>\t\tQuery vector.\n"
" /xnode <node ...>\tRequest raw event callbacks for event node.\n"
"All other input is passed to Ngspice.\n");
}
static void aevt(char *cmd) {
char **cpp;
cpp = ngSpice_AllEvtNodes_handle();
if (!cpp)
return;
printf("Event nodes:\n");
while (*cpp)
printf(" %s\n", *cpp++);
}
static void aplot(char *cmd) {
char **cpp;
cpp = ngSpice_AllPlots_handle();
if (!cpp)
return;
printf("Plots:\n");
while (*cpp)
printf(" %s\n", *cpp++);
}
static void avec(char *cmd) {
char **cpp;
cpp = ngSpice_AllVecs_handle(cmd);
if (!cpp)
return;
printf("Vectors in plot %s:\n", cmd);
while (*cpp)
printf(" %s\n", *cpp++);
}
static void bgr(char *cmd) {
printf("Background thread is %srunning\n",
ngSpice_running_handle() ? "": "not ");
}
static void cplot(char *cmd) {
printf("Current plot: %s\n", ngSpice_CurPlot_handle());
}
static void dlim(char *cmd) {
sd_limit = atoi(cmd);
sd_count = 0;
}
static void elim(char *cmd) {
se_limit = atoi(cmd);
se_count = 0;
}
static void lvals(char *cmd) {
sd_list ^= 1;
printf("Listing node values is now %s.\n",
sd_list ? "on" : "off");
}
static void reset(char *cmd) {
int ret;
ret = ngSpice_Reset_handle();
if (ret) {
fprintf(stderr, "Reset error %d\n", ret);
return;
}
register_cbs();
sd_count = se_count = sq_count = 10;
sr_last_time = -1;
sr_target_time = -1;
sim_time = 0;
}
static void sask(char *cmd) {
pvector_info vp;
sq_ask ^= 1;
printf("Prompting for V/ISRC values is now %s.\n",
sq_ask ? "on" : "off");
}
static void slim(char *cmd) {
sq_limit = atoi(cmd);
sq_count = 0;
}
static void sramp(char *cmd) {
double v[3];
int i;
for (i = 0; i < 3; ++i) {
if (!*cmd)
break;
v[i] = strtod(cmd, NULL);
cmd = skip(tail(cmd));
}
if (sr_last_time < 0.0)
sr_last_time = sim_time;
switch (i) {
case 0:
break;
case 1:
sr_last_val = sr_target_val = v[0];
break;
case 2:
sr_target_time = sr_last_time + v[0];
sr_target_val = v[1];
break;
default:
sr_last_val = sr_target_val = v[0];
sr_target_time = sr_last_time + v[1];
sr_target_val = v[2];
break;
}
if (sr_target_time < 0.0)
sr_target_time = sim_time + 1.0;
}
static void vec(char *cmd) {
pvector_info vp;
vp = ngSpice_GVI_handle(cmd);
if (!vp)
return;
printf("Vector %s: length %d type %d flags %0hx\n",
vp->v_name, vp->v_length, vp->v_type, vp->v_flags);
}
struct raw_cb_ctx {
int type;
char name[1];
};
static void xnode(char *cmd) {
struct raw_cb_ctx *ctx;
char *end, c;
int ret;
while (*cmd) {
/* Memory leak here. */
end = tail(cmd);
ctx = (struct raw_cb_ctx *)malloc(sizeof *ctx + (end - cmd));
c = *end;
*end = '\0';
strcpy(ctx->name, cmd);
ret = ngSpice_Raw_Evt_handle(cmd, ng_rawevt, ctx);
if (ret >= 0) {
ctx->type = ret;
} else {
free(ctx);
fprintf(stderr, "Node name not recognised\n");
}
*end = c;
cmd = skip(end);
}
}
#define E(name) { #name, name }
static void local(char *cmd)
{
static const struct {
const char *cmd;
void (*fn)(char *);
} table[] = { E(help), // First, so that just "/" works.
E(aevt), E(avec), E(aplot), E(bgr), E(cplot),
E(dlim), E(elim), E(lvals), E(reset), E(sask), E(slim),
E(sramp), E(vec), E(xnode),
{ NULL, NULL }};
char *end;
int i, len;
end = tail(cmd);
len = end - cmd;
for (i = 0; table[i].cmd; ++i) {
if (!strncmp(cmd, table[i].cmd, len)) {
table[i].fn(skip(end));
return;
}
}
fprintf(stderr, "No such local command\n");
}
int main(int argc, char **argv)
{
char *libpath = NULL;
int ret, i, do_auto = 0;;
for (i = 1; i < argc; ++i) {
if (argv[i][0] == '-') {
switch (argv[i][1]) {
case 'a':
do_auto = 1;
break;
case 'l':
libpath = argv[i + 1];
break;
default:
fprintf(stderr, "Unknown option: %s\n", argv[i]);
return 2;
}
}
}
if (!libpath)
libpath = LOAD_STRING;
ngdllhandle = dlopen(libpath, RTLD_GLOBAL | RTLD_NOW);
if (ngdllhandle) {
printf("Ngspice library loaded.\n");
} else {
fprintf(stderr, "Ngspice library not loaded!\n %s\n", dlerror());
return 1;
}
ngSpice_AllEvtNodes_handle = getsym("ngSpice_AllEvtNodes");
ngSpice_Command_handle = getsym("ngSpice_Command");
ngSpice_CurPlot_handle = getsym("ngSpice_CurPlot");
ngSpice_AllPlots_handle = getsym("ngSpice_AllPlots");
ngSpice_AllVecs_handle = getsym("ngSpice_AllVecs");
ngSpice_Decode_Evt_handle = getsym("ngSpice_Decode_Evt");
ngSpice_GVI_handle = getsym("ngGet_Vec_Info");
ngSpice_Init_handle = getsym("ngSpice_Init");
ngSpice_Init_Evt_handle = getsym("ngSpice_Init_Evt");
ngSpice_Init_Sync_handle = getsym("ngSpice_Init_Sync");
ngSpice_Raw_Evt_handle = getsym("ngSpice_Raw_Evt");
ngSpice_Reset_handle = getsym("ngSpice_Reset");
ngSpice_running_handle = getsym("ngSpice_running");
register_cbs();
if (do_auto)
return auto_test();
/* Interactive. */
printf("Enter \"/h\" for local command descriptions.\n\n");
for (;;) {
/* get command from stdin */
if (getLine("Command: ", comd, sizeof(comd)))
return 0; // EOF
/* Check for a locally-executed command. */
if (comd[0] == '/') {
local(comd + 1);
continue;
}
/* return upon 'exit' */
if (cieq("exit", comd))
break;
/* If command 'bg_run' is given, ngSpice_Command_handle() will return immediately.
To guarantee that the primary thread here waits until the run is finished, we
may set no_bg to 0 already here. Risk: if starting the simulation fails, we never
may leave the waiting loop. As an alternative callback function ng_thread_runs()
will set no_bg to 0. This has to happen within the first 200ms waiting time. */
if (cieq("bg_run", comd))
no_bg = false;
ret = ngSpice_Command_handle(comd);
if (ret)
fprintf(stderr, "Ngspice command execution error %d\n", ret);
/* wait until simulation finishes */
for (;;) {
#if defined(__MINGW32__) || defined(__CYGWIN__) || defined(_MSC_VER)
Sleep(200);
#else
usleep(200000);
#endif
/* after 200ms the callback function ng_thread_runs() should have
set no_bg to 0, otherwise we would not wait for the end of the
background thread.*/
if (no_bg)
break;
}
}
ret = ngSpice_Reset_handle();
return 0;
}
/* Callback function called from bg thread in ngspice to transfer
any string created by printf or puts. Output to stdout in ngspice is
preceded by token stdout, same with stderr.*/
static int
ng_getchar(char* outputreturn, int ident, void* userdata)
{
printf("%s\n", outputreturn);
return 0;
}
/* Callback function called from bg thread in ngspice to transfer
simulation status (type and progress in percent). */
static int
ng_getstat(char* outputreturn, int ident, void* userdata)
{
printf("Getstat callback: %s\n", outputreturn);
return 0;
}
/* Callback function called from ngspice upon starting (returns true) or
leaving (returns false) the bg thread. */
static int
ng_thread_runs(bool noruns, int ident, void* userdata)
{
no_bg = noruns;
if (noruns)
printf("\nbg not running\n");
else
printf("bg running\n\n");
return 0;
}
/* Callback function called from bg thread in ngspice if fcn controlled_exit()
is hit. Do not exit, but unload ngspice. */
static int
ng_exit(int exitstatus, bool immediate, bool quitexit, int ident, void* userdata)
{
printf("Exit callback status %d immediate %d quit %d\n",
exitstatus, immediate, quitexit);
return exitstatus;
}
/* Callback function called from bg thread in ngspice once per
* accepted data point: Sendata callbick.
*/
static int
ng_data(pvecvaluesall vdata, int numvecs, int ident, void* userdata)
{
if (sd_limit > sd_count) {
++sd_count;
if (numvecs > 0) {
printf("New data %d (%d) vectors, first is %s\n",
numvecs, vdata->veccount, vdata->vecsa[0]->name);
if (sd_list) {
int i;
for (i = 0; i < vdata->veccount; ++i) {
if (vdata->vecsa[i]->is_complex) {
printf("%s: (%g, %g)\n", vdata->vecsa[i]->name,
vdata->vecsa[i]->creal, vdata->vecsa[i]->cimag);
} else {
printf("%s: %g\n",
vdata->vecsa[i]->name, vdata->vecsa[i]->creal);
}
}
}
} else {
printf("New data callback, no data!\n");
}
}
return 0;
}
/* Callback function called from bg thread in ngspice once upon intialization
* of the simulation vectors.
*/
static int
ng_initdata(pvecinfoall intdata, int ident, void* userdata)
{
int i;
int vn = intdata->veccount;
printf("Init data callback:\n");
for (i = 0; i < vn; i++)
printf(" Vector: %s\n", intdata->vecs[i]->vecname);
return 0;
}
static int ng_initevtdata(int idx, int max_idx, char *node, char *type,
int ident, void* userdata)
{
printf("Evt init: node %s type %s\n", node, type);
return 0;
}
static int ng_evtdata(int idx, double time, double pval, char *sval,
void *sp, int sz, int sim_node,
int ident, void* userdata)
{
if (se_limit > se_count) {
++se_count;
printf("Evt val: node %d val %s/%g at time %g\n",
idx, sval, pval, time);
}
return 0;
}
static int ng_rawevt(double time, void *valp, void *userData, int last)
{
struct raw_cb_ctx *ctx = (struct raw_cb_ctx *)userData;
const char *printstr, *typestr;
double plotval;
if (se_limit > se_count) {
++se_count;
ngSpice_Decode_Evt_handle(valp, ctx->type, &plotval, &printstr);
ngSpice_Decode_Evt_handle(NULL, ctx->type, NULL, &typestr);
printf("Raw event: node %s type %s val %s/%g at time %g%s\n",
ctx->name, typestr, printstr, plotval, time,
last ? " is last." : "");
return 0;
} else {
free(ctx);
return 1;
}
}
/* EXTERNAL source callback. */
static int ng_srcdata(double *vp, double time, char *source, int id, void *udp)
{
if (sq_limit > sq_count) {
++sq_count;
printf("V or ISRC request: source %s at time %g\n", source, time);
if (sq_ask) {
getLine("Value: ", comd, sizeof(comd));
if (!strncmp("/s", comd, 2)) {
/* Allow "/sask" as respone. */
sq_ask = 0;
} else {
sr_last_val = *vp = strtod(comd, NULL);
sr_last_time = time;
}
return 0;
}
}
if (sr_last_time >= 0.0) {
/* Provide a value. */
if (sr_target_time >= 0.0) {
if (time < sr_target_time) {
sr_last_val += (sr_target_val - sr_last_val) *
(time - sr_last_time) / (sr_target_time - sr_last_time);
} else {
sr_last_val = sr_target_val;
}
}
*vp = sr_last_val;
sr_last_time = time;
}
return 0;
}
static int ng_syncdata(double time, double *deltap, double old_delta,
int redo, int loc, int id, void *udp)
{
if (sd_limit > sd_count) {
++sd_count;
printf("Sync data redo %d delta %g (old %g) location %d at %g\n",
redo, *deltap, old_delta, loc, time);
}
sim_time = time;
return 0;
}
/* Unify LINUX and Windows dynamic library handling:
Add functions dlopen, dlsym, dlerror, dlclose to Windows by
tranlating to Windows API functions.
*/
#if defined(__MINGW32__) || defined(__CYGWIN__) || defined(_MSC_VER)
void *dlopen(const char *name,int type)
{
return LoadLibrary((LPCSTR)name);
}
funptr_t dlsym(void *hDll, const char *funcname)
{
return GetProcAddress(hDll, funcname);
}
char *dlerror(void)
{
LPVOID lpMsgBuf;
char * testerr;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL
);
testerr = (char*)lpMsgBuf;
strcpy(errstr,lpMsgBuf);
LocalFree(lpMsgBuf);
if (ciprefix("Der Vorgang wurde erfolgreich beendet.", errstr))
return NULL;
else
return errstr;
}
int dlclose (void *lhandle)
{
return (int)FreeLibrary(lhandle);
}
#endif
/* Case insensitive str eq.
Like strcasecmp( ) XXX */
static int
cieq(register char *p, register char *s)
{
while (*p) {
if ((isupper(*p) ? tolower(*p) : *p) !=
(isupper(*s) ? tolower(*s) : *s))
return(false);
p++;
s++;
}
return (*s ? false : true);
}
/* Case insensitive prefix. */
static int
ciprefix(const char *p, const char *s)
{
while (*p) {
if ((isupper(*p) ? tolower(*p) : *p) !=
(isupper(*s) ? tolower(*s) : *s))
return(false);
p++;
s++;
}
return (true);
}
/* read a line from console input
source:
https://stackoverflow.com/questions/4023895/how-to-read-string-entered-by-user-in-c
*/
#define OK 0
#define NO_INPUT 1
static int
getLine(char *prmpt, char *buff, size_t sz)
{
int ch, len, extra;
// Get line with buffer overrun protection.
for (;;) {
if (prmpt != NULL) {
printf("%s", prmpt);
fflush(stdout);
}
if (fgets(buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
len = strlen(buff);
if (buff[len - 1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
if (extra) {
fprintf(stderr,
"Line longer than %zd characters was ignored.\n",
sz - 1);
continue;
}
}
// Otherwise remove newline and give string back to caller.
buff[len - 1] = '\0';
return OK;
}
}

34
examples/shared/t.cir Normal file
View File

@ -0,0 +1,34 @@
Simple test circuit for shx program.
* A typical command sequence in shx:
* t1.cir
* /lvals
* /aevt
* /elim 20
* /dlim 20
* /slim 20
* /xnode clk div
* run
* d_osc controlled by EXTERNAL source
vext ctl 0 0 external
aosc ctl clk osc
.model osc d_osc cntl_array = [0 20] freq_array = [ 100k 1meg ]
# Divide by three so there will be multiple transitions, reported by /aevt
# within each time-step.
adiv clk div div
.model div d_fdiv div_factor = 3
* Add a resistor to convert to analogue and force breakpoints.
r div 0 100
* Use .tran so that /xnode commands can be given after loading.
* Set maximum time step above div cycle time, so breakpoints control
* time steps. Long run time to overcome the 50 steps minimum.
.tran 4u 201u
.end

23
examples/shared/t.shx Executable file
View File

@ -0,0 +1,23 @@
# Input file running shx with t.cir
# An externally controlled source is set to 0.5V from prompted input,
# then prompting is disabled and the source value is ramped after a stop
#
# The result may be plotted with
# load t.raw
# plot v(ctl) v(div)
#
t.cir
/lvals
/aevt
/elim 20
/dlim 20
/slim 20
/xnode clk div
/sask
stop when time = 50u
run
0.5
/sask
/sram 1 0.0001 20
resume
write t.raw

View File

@ -0,0 +1,20 @@
* pll circuit using xspice code models: version with iplot including
* digital nodes.
.include shared-pll-xspice.cir
* An additional node to scale the analog signal.
bdisplay controlX4 0 v=v(cont)*4
.control
save cont controlX4 s1 s2 u1n d1 v.xlf.vdd#branch; to save memory
iplot -o controlx4 d_d+4.5 d_u
tran 0.1n $&simtime uic
rusage
plot cont s1 s2+1.2 u1n+2.4 d1+3.6 xlimit 4u 5u
plot v.xlf.vdd#branch xlimit 4u 5u ylimit -8m 2m
*plot cont
.endc
.end

View File

@ -1,66 +1,8 @@
* pll circuit using xspice code models
* pll circuit using xspice code models: version with analog-only iplot
* output frequency 400 MHz
* locked to a 1 or 10 MHz reference
.param vcc=3.3
.param divisor=40
.param fref=10e6
.csparam simtime=25u
.global d_d0 d_d1
vdd dd 0 dc 'vcc'
*vco cont 0 dc 1.9
*PULSE(V1 V2 TD TR TF PW PER)
* reference frequency selected by param fref
* PULSE(V1 V2 TD TR TF PW PER)
vref ref 0 dc 0 pulse(0 'vcc' 10n 1n 1n '1/fref/2' '1/fref')
abridgeref [ref] [d_ref] adc_vbuf
.model adc_vbuf adc_bridge(in_low = 0.5 in_high = 0.5)
*digital zero
vzero z 0 dc 0
abridgev3 [z] [d_d0] adc_vbuf
.model adc_vbuf adc_bridge(in_low = 'vcc*0.5' in_high = 'vcc*0.5')
*digital one
ainv1 d_d0 d_d1 invd1
.model invd1 d_inverter(rise_delay = 1e-10 fall_delay = 1e-10)
* vco
* buf: analog out
* d_digout: digital out
* cont: analog control voltage
* dd: analog supply voltage
*.include vco_sub.cir
*xvco buf d_digout cont dd ro_vco
.include vco_sub_new.cir
xvco buf d_digout cont dd d_osc_vco
* digital divider
adiv1 d_digout d_divout divider
.model divider d_fdiv(div_factor = 'divisor' high_cycles = 'divisor/2'
+ i_count = 4 rise_delay = 1e-10
+ fall_delay = 1e-10)
* frequency phase detector
.include f-p-det-d-sub.cir
Xfpdet d_divout d_ref d_U d_Un d_D d_Dn f-p-det
* loop filters
*2nd or 3rd order, transistors as switches
.include loop-filter-2.cir
Xlf d_Un d_D cont loopf
* 2nd order, Exxxx voltage controlled current sources as 'switches'
* loop filter current sources as charge pump
*.include loop-filter.cir
*Xlf d_U d_D cont loopfe
* d to a for plotting
abridge-w1 [d_divout d_ref d_Un d_D] [s1 s2 u1n d1] dac1 ; change to d_u or d_Un
.model dac1 dac_bridge(out_low = 0 out_high = 1 out_undef = 0.5
+ input_load = 5.0e-12 t_rise = 1e-10
+ t_fall = 1e-10)
.include shared-pll-xspice.cir
.control
save cont s1 s2 u1n d1 v.xlf.vdd#branch; to save memory
@ -69,76 +11,5 @@ tran 0.1n $&simtime uic
rusage
plot cont s1 s2+1.2 u1n+2.4 d1+3.6 xlimit 4u 5u
plot v.xlf.vdd#branch xlimit 4u 5u ylimit -8m 2m
*plot cont
.endc
*model = bsim3v3
*Berkeley Spice Compatibility
* Lmin= .35 Lmax= 20 Wmin= .6 Wmax= 20
.model N1 NMOS
*+version = 3.2.4
+version = 3.3.0
+Level= 8
+Tnom=27.0
+Nch= 2.498E+17 Tox=9E-09 Xj=1.00000E-07
+Lint=9.36e-8 Wint=1.47e-7
+Vth0= .6322 K1= .756 K2= -3.83e-2 K3= -2.612
+Dvt0= 2.812 Dvt1= 0.462 Dvt2=-9.17e-2
+Nlx= 3.52291E-08 W0= 1.163e-6
+K3b= 2.233
+Vsat= 86301.58 Ua= 6.47e-9 Ub= 4.23e-18 Uc=-4.706281E-11
+Rdsw= 650 U0= 388.3203 wr=1
+A0= .3496967 Ags=.1 B0=0.546 B1= 1
+ Dwg = -6.0E-09 Dwb = -3.56E-09 Prwb = -.213
+Keta=-3.605872E-02 A1= 2.778747E-02 A2= .9
+Voff=-6.735529E-02 NFactor= 1.139926 Cit= 1.622527E-04
+Cdsc=-2.147181E-05
+Cdscb= 0 Dvt0w = 0 Dvt1w = 0 Dvt2w = 0
+ Cdscd = 0 Prwg = 0
+Eta0= 1.0281729E-02 Etab=-5.042203E-03
+Dsub= .31871233
+Pclm= 1.114846 Pdiblc1= 2.45357E-03 Pdiblc2= 6.406289E-03
+Drout= .31871233 Pscbe1= 5000000 Pscbe2= 5E-09 Pdiblcb = -.234
+Pvag= 0 delta=0.01
+ Wl = 0 Ww = -1.420242E-09 Wwl = 0
+ Wln = 0 Wwn = .2613948 Ll = 1.300902E-10
+ Lw = 0 Lwl = 0 Lln = .316394
+ Lwn = 0
+kt1=-.3 kt2=-.051
+At= 22400
+Ute=-1.48
+Ua1= 3.31E-10 Ub1= 2.61E-19 Uc1= -3.42e-10
+Kt1l=0 Prt=764.3
.model P1 PMOS
*+version = 3.2.4
+version = 3.3.0
+Level= 8
+Tnom=27.0
+Nch= 3.533024E+17 Tox=9E-09 Xj=1.00000E-07
+Lint=6.23e-8 Wint=1.22e-7
+Vth0=-.6732829 K1= .8362093 K2=-8.606622E-02 K3= 1.82
+Dvt0= 1.903801 Dvt1= .5333922 Dvt2=-.1862677
+Nlx= 1.28e-8 W0= 2.1e-6
+K3b= -0.24 Prwg=-0.001 Prwb=-0.323
+Vsat= 103503.2 Ua= 1.39995E-09 Ub= 1.e-19 Uc=-2.73e-11
+ Rdsw= 460 U0= 138.7609
+A0= .4716551 Ags=0.12
+Keta=-1.871516E-03 A1= .3417965 A2= 0.83
+Voff=-.074182 NFactor= 1.54389 Cit=-1.015667E-03
+Cdsc= 8.937517E-04
+Cdscb= 1.45e-4 Cdscd=1.04e-4
+ Dvt0w=0.232 Dvt1w=4.5e6 Dvt2w=-0.0023
+Eta0= 6.024776E-02 Etab=-4.64593E-03
+Dsub= .23222404
+Pclm= .989 Pdiblc1= 2.07418E-02 Pdiblc2= 1.33813E-3
+Drout= .3222404 Pscbe1= 118000 Pscbe2= 1E-09
+Pvag= 0
+kt1= -0.25 kt2= -0.032 prt=64.5
+At= 33000
+Ute= -1.5
+Ua1= 4.312e-9 Ub1= 6.65e-19 Uc1= 0
+Kt1l=0
.end

View File

@ -0,0 +1,133 @@
* pll circuit using xspice code models: shared by pll_xspice.cir and
* pll_digital_iplot.cir
* output frequency 400 MHz
* locked to a 1 or 10 MHz reference
.param vcc=3.3
.param divisor=40
.param fref=10e6
.csparam simtime=25u
.global d_d0 d_d1
vdd dd 0 dc 'vcc'
*vco cont 0 dc 1.9
*PULSE(V1 V2 TD TR TF PW PER)
* reference frequency selected by param fref
* PULSE(V1 V2 TD TR TF PW PER)
vref ref 0 dc 0 pulse(0 'vcc' 10n 1n 1n '1/fref/2' '1/fref')
abridgeref [ref] [d_ref] adc_vbuf
.model adc_vbuf adc_bridge(in_low = 0.5 in_high = 0.5)
*digital zero
vzero z 0 dc 0
abridgev3 [z] [d_d0] adc_vbuf
.model adc_vbuf adc_bridge(in_low = 'vcc*0.5' in_high = 'vcc*0.5')
*digital one
ainv1 d_d0 d_d1 invd1
.model invd1 d_inverter(rise_delay = 1e-10 fall_delay = 1e-10)
* vco
* buf: analog out
* d_digout: digital out
* cont: analog control voltage
* dd: analog supply voltage
*.include vco_sub.cir
*xvco buf d_digout cont dd ro_vco
.include vco_sub_new.cir
xvco buf d_digout cont dd d_osc_vco
* digital divider
adiv1 d_digout d_divout divider
.model divider d_fdiv(div_factor = 'divisor' high_cycles = 'divisor/2'
+ i_count = 4 rise_delay = 1e-10
+ fall_delay = 1e-10)
* frequency phase detector
.include f-p-det-d-sub.cir
Xfpdet d_divout d_ref d_U d_Un d_D d_Dn f-p-det
* loop filters
*2nd or 3rd order, transistors as switches
.include loop-filter-2.cir
Xlf d_Un d_D cont loopf
* 2nd order, Exxxx voltage controlled current sources as 'switches'
* loop filter current sources as charge pump
*.include loop-filter.cir
*Xlf d_U d_D cont loopfe
* d to a for plotting
abridge-w1 [d_divout d_ref d_Un d_D] [s1 s2 u1n d1] dac1 ; change to d_u or d_Un
.model dac1 dac_bridge(out_low = 0 out_high = 1 out_undef = 0.5
+ input_load = 5.0e-12 t_rise = 1e-10
+ t_fall = 1e-10)
*model = bsim3v3
*Berkeley Spice Compatibility
* Lmin= .35 Lmax= 20 Wmin= .6 Wmax= 20
.model N1 NMOS
*+version = 3.2.4
+version = 3.3.0
+Level= 8
+Tnom=27.0
+Nch= 2.498E+17 Tox=9E-09 Xj=1.00000E-07
+Lint=9.36e-8 Wint=1.47e-7
+Vth0= .6322 K1= .756 K2= -3.83e-2 K3= -2.612
+Dvt0= 2.812 Dvt1= 0.462 Dvt2=-9.17e-2
+Nlx= 3.52291E-08 W0= 1.163e-6
+K3b= 2.233
+Vsat= 86301.58 Ua= 6.47e-9 Ub= 4.23e-18 Uc=-4.706281E-11
+Rdsw= 650 U0= 388.3203 wr=1
+A0= .3496967 Ags=.1 B0=0.546 B1= 1
+ Dwg = -6.0E-09 Dwb = -3.56E-09 Prwb = -.213
+Keta=-3.605872E-02 A1= 2.778747E-02 A2= .9
+Voff=-6.735529E-02 NFactor= 1.139926 Cit= 1.622527E-04
+Cdsc=-2.147181E-05
+Cdscb= 0 Dvt0w = 0 Dvt1w = 0 Dvt2w = 0
+ Cdscd = 0 Prwg = 0
+Eta0= 1.0281729E-02 Etab=-5.042203E-03
+Dsub= .31871233
+Pclm= 1.114846 Pdiblc1= 2.45357E-03 Pdiblc2= 6.406289E-03
+Drout= .31871233 Pscbe1= 5000000 Pscbe2= 5E-09 Pdiblcb = -.234
+Pvag= 0 delta=0.01
+ Wl = 0 Ww = -1.420242E-09 Wwl = 0
+ Wln = 0 Wwn = .2613948 Ll = 1.300902E-10
+ Lw = 0 Lwl = 0 Lln = .316394
+ Lwn = 0
+kt1=-.3 kt2=-.051
+At= 22400
+Ute=-1.48
+Ua1= 3.31E-10 Ub1= 2.61E-19 Uc1= -3.42e-10
+Kt1l=0 Prt=764.3
.model P1 PMOS
*+version = 3.2.4
+version = 3.3.0
+Level= 8
+Tnom=27.0
+Nch= 3.533024E+17 Tox=9E-09 Xj=1.00000E-07
+Lint=6.23e-8 Wint=1.22e-7
+Vth0=-.6732829 K1= .8362093 K2=-8.606622E-02 K3= 1.82
+Dvt0= 1.903801 Dvt1= .5333922 Dvt2=-.1862677
+Nlx= 1.28e-8 W0= 2.1e-6
+K3b= -0.24 Prwg=-0.001 Prwb=-0.323
+Vsat= 103503.2 Ua= 1.39995E-09 Ub= 1.e-19 Uc=-2.73e-11
+ Rdsw= 460 U0= 138.7609
+A0= .4716551 Ags=0.12
+Keta=-1.871516E-03 A1= .3417965 A2= 0.83
+Voff=-.074182 NFactor= 1.54389 Cit=-1.015667E-03
+Cdsc= 8.937517E-04
+Cdscb= 1.45e-4 Cdscd=1.04e-4
+ Dvt0w=0.232 Dvt1w=4.5e6 Dvt2w=-0.0023
+Eta0= 6.024776E-02 Etab=-4.64593E-03
+Dsub= .23222404
+Pclm= .989 Pdiblc1= 2.07418E-02 Pdiblc2= 1.33813E-3
+Drout= .3222404 Pscbe1= 118000 Pscbe2= 1E-09
+Pvag= 0
+kt1= -0.25 kt2= -0.032 prt=64.5
+At= 33000
+Ute= -1.5
+Ua1= 4.312e-9 Ub1= 6.65e-19 Uc1= 0
+Kt1l=0

View File

@ -10,6 +10,7 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group
#include "ngspice/ngspice.h"
#include "ngspice/cpdefs.h"
#include "ngspice/ftedefs.h"
#include "ngspice/cktdefs.h"
#include "ngspice/dvec.h"
#include "ngspice/ftedebug.h"
#include "breakp.h"
@ -209,7 +210,10 @@ com_trce(wordlist *wl)
}
/* Incrementally plot a value. This is just like trace. */
/* Incrementally plot values. This is just like trace.
* Nodes may be specified with an offset, as name+number, as that is useful
* for separating the graphs of digital nodes. It will be ignored for analogue.
*/
void
com_iplot(wordlist *wl)
@ -224,12 +228,13 @@ com_iplot(wordlist *wl)
return;
}
/* settrace(wl, VF_PLOT); */
struct dbcomm *d, *td, *currentdb = NULL;
double window = 0.0;
int initial_steps = IPOINTMIN;
#ifdef XSPICE
int event_auto_incr = 0;
#endif
char *s;
int initial_steps = IPOINTMIN;
/* Look for "-w window-size" at the front, indicating a windowed iplot
* or "-d steps" to set the initial delay before the window appears.
@ -254,6 +259,12 @@ com_iplot(wordlist *wl)
wl = wl->wl_next;
if (wl)
initial_steps = atoi(wl->wl_word);
#ifdef XSPICE
} else if (wl->wl_word[1] == 'o' && !wl->wl_word[2]) {
/* Automatically offset traces for event nodes. */
event_auto_incr = 1;
#endif
} else {
break;
}
@ -269,8 +280,10 @@ com_iplot(wordlist *wl)
d = TMALLOC(struct dbcomm, 1);
d->db_analysis = NULL;
d->db_number = debugnumber++;
d->db_op = initial_steps; // Field re-use
d->db_value1 = window; // Field re-use
d->db_iteration = event_auto_incr ? DB_AUTO_OFFSET : DB_NORMAL;
d->db_op = initial_steps; // Field re-use
d->db_value1 = window; // Field re-use
if (eq(s, "all")) {
d->db_type = DB_IPLOTALL;
} else {
@ -278,8 +291,14 @@ com_iplot(wordlist *wl)
d->db_nodename1 = copy(s);
}
tfree(s);/*DG: avoid memory leak */
d->db_also = currentdb;
currentdb = d;
/* Chain in expected order. */
if (currentdb)
td->db_also = d;
else
currentdb = d;
td = d;
wl = wl->wl_next;
}
@ -388,7 +407,10 @@ void
dbfree1(struct dbcomm *d)
{
tfree(d->db_nodename1);
tfree(d->db_nodename2);
if (d->db_type != DB_IPLOT && d->db_type != DB_IPLOTALL &&
d->db_type != DB_DEADIPLOT) {
tfree(d->db_nodename2);
}
if (d->db_also)
dbfree(d->db_also);
tfree(d);

View File

@ -428,7 +428,7 @@ struct comm spcp_coms[] = {
{ "iplot", com_iplot, TRUE, TRUE,
{ 0200, 0200, 0200, 0200 }, E_DEFHMASK, 0, LOTS,
NULL,
"[-w width] [-s initial_steps] [all] [node ...] : Incrementally plot nodes." } ,
"[-w width] [-d initial_steps] [-o] [all] [node ...] : Incrementally plot nodes." } ,
{ "status", com_sttus, TRUE, FALSE,
{ 0, 0, 0, 0 }, E_DEFHMASK, 0, 0,
NULL,
@ -897,10 +897,6 @@ struct comm nutcp_coms[] = {
{ 0200, 0200, 0200, 0200 }, E_DEFHMASK, 0, LOTS,
NULL,
"[all] [node ...] : Save a spice output." } ,
{ "iplot", NULL, TRUE, TRUE,
{ 0200, 0200, 0200, 0200 }, E_DEFHMASK, 0, LOTS,
NULL,
"[all] [node ...] : Incrementally plot a node." } ,
{ "status", NULL, TRUE, FALSE,
{ 0, 0, 0, 0 }, E_DEFHMASK, 0, 0,
NULL,

View File

@ -198,6 +198,10 @@ static void utf8_syntax_check(struct card *deck);
int add_to_sourcepath(const char* filepath, const char* path);
#if defined(_WIN32)
static char* get_windows_canonical_path(const char* input_path);
#endif
struct inp_read_t {
struct card *cc;
int line_number;
@ -1969,19 +1973,20 @@ FILE *inp_pathopen(const char *name, const char *mode)
if the file isn't in . and it isn't an abs path name.
*-------------------------------------------------------------------------*/
char *inp_pathresolve(const char *name)
char *inp_pathresolve(const char *cname)
{
struct variable *v;
struct stat st;
char* name;
#if defined(_WIN32)
/* If variable 'mingwpath' is set: convert mingw /d/... to d:/... */
if (cp_getvar("mingwpath", CP_BOOL, NULL, 0) &&
name[0] == DIR_TERM_LINUX && isalpha_c(name[1]) &&
name[2] == DIR_TERM_LINUX) {
cname[0] == DIR_TERM_LINUX && isalpha_c(cname[1]) &&
cname[2] == DIR_TERM_LINUX) {
DS_CREATE(ds, 100);
if (ds_cat_str(&ds, name) != 0) {
if (ds_cat_str(&ds, cname) != 0) {
fprintf(stderr, "Error: Unable to copy string while resolving path");
controlled_exit(EXIT_FAILURE);
}
@ -1993,27 +1998,33 @@ char *inp_pathresolve(const char *name)
return resolved_path;
}
/* Try to overcome MAX_PATH path length limit by removing '/..' */
name = get_windows_canonical_path(cname);
#else
name = copy(cname);
#endif
/* just try it */
if (stat(name, &st) == 0)
return copy(name);
return name;
#if !defined(EXT_ASC) && (defined(__MINGW32__) || defined(_MSC_VER))
wchar_t wname[BSIZE_SP];
if (MultiByteToWideChar(CP_UTF8, 0, name, -1, wname, 2 * (int)strlen(name) + 1) == 0) {
fprintf(stderr, "UTF-8 to UTF-16 conversion failed with 0x%x\n", GetLastError());
fprintf(stderr, "%s could not be converted\n", name);
tfree(name);
return NULL;
}
if (_waccess(wname, 0) == 0)
return copy(name);
#endif
return name;
#endif
/* fail if this was an absolute filename or if there is no sourcepath var
*/
if (is_absolute_pathname(name) ||
!cp_getvar("sourcepath", CP_LIST, &v, 0)) {
tfree(name);
return (char *) NULL;
}
@ -2040,12 +2051,14 @@ char *inp_pathresolve(const char *name)
fprintf(stderr,
"ERROR: enumeration value `CP_BOOL' or `CP_LIST' "
"not handled in inp_pathresolve\nAborting...\n");
tfree(name);
controlled_exit(EXIT_FAILURE);
}
if (rc_ds != 0) { /* unable to build string */
(void) fprintf(cp_err,
"Error: Unable to build path name in inp_pathresolve");
tfree(name);
controlled_exit(EXIT_FAILURE);
}
@ -2056,6 +2069,7 @@ char *inp_pathresolve(const char *name)
char * const buf_cpy = dup_string(
buf, ds_get_length(&ds));
ds_free(&ds);
tfree(name);
return buf_cpy;
}
/* Else contiue with next attempt */
@ -2063,7 +2077,7 @@ char *inp_pathresolve(const char *name)
} /* end of loop over linked variables */
ds_free(&ds);
} /* end of block trying to find a valid name */
tfree(name);
return (char *) NULL;
} /* end of function inp_pathresolve */
@ -8988,7 +9002,10 @@ static void inp_check_syntax(struct card *deck)
if (check_subs != 0) {
fprintf(cp_err,
"\nError: Mismatch of .subckt ... .ends statements!\n");
fprintf(stderr, " in file %s\n", bugcard->linesource);
if (eq("circbyline", bugcard->linesource))
fprintf(stderr, "in the netlist received from the calling program\n");
else
fprintf(stderr, " in file %s\n", bugcard->linesource);
fprintf(cp_err, " This will cause subsequent errors.\n\n");
if (ends > 0)
fprintf(cp_err, "Check .ends in line number %d\n", ends);
@ -9719,8 +9736,8 @@ int add_to_sourcepath(const char* filepath, const char* path)
else
return 1;
startwl = newwl = wl_from_string("sourcepath = ( ");
endwl = wl_from_string(" )");
startwl = newwl = wl_from_string("sourcepath=(");
endwl = wl_from_string(")");
/* add fpath to 'sourcepath' list variable */
if (cp_getvar("sourcepath", CP_LIST, NULL, 0)) {
@ -9746,3 +9763,134 @@ int add_to_sourcepath(const char* filepath, const char* path)
tfree(fpath);
return 0;
}
#if defined(_WIN32)
/**
* @brief Resolves a Windows path to its canonical, absolute form using GetFullPathNameW.
*
* This function takes a path string (assumed to be UTF-8), converts it to
* UTF-16, calls the Windows API GetFullPathNameW to resolve '..' and '.'
* components and make the path absolute, and then converts the result back
* to a newly allocated UTF-8 string.
*
* It handles potential failures during conversion or path resolution.
* It does NOT automatically add the '\\?\' prefix for long paths, but
* GetFullPathNameW can produce paths longer than MAX_PATH. The caller
* might need to add the prefix separately if using the result in APIs
* that require it for long path support.
*
* @param input_path The input path string (UTF-8 encoded). Can be relative or
* absolute, may contain '.' or '..'.
* @return char* A newly allocated UTF-8 string containing the canonical absolute
* path, or NULL on failure. The caller is responsible for
* calling free() on the returned string. On failure, errno is
* set to indicate the error (e.g., ENOMEM, EINVAL, ENOENT).
*/
char* get_windows_canonical_path(const char* input_path) {
wchar_t* wPathInput = NULL;
wchar_t* wPathOutput = NULL;
char* utf8PathOutput = NULL;
DWORD inputLenW = 0;
DWORD outputLenW = 0;
DWORD resultLenW = 0;
int inputLenMB = 0;
int outputLenMB = 0;
int original_errno = errno;
if (input_path == NULL) {
errno = EINVAL;
return NULL;
}
inputLenMB = (int)strlen(input_path);
if (inputLenMB == 0) {
inputLenW = 1;
}
else {
inputLenW = MultiByteToWideChar(CP_UTF8, 0, input_path, inputLenMB, NULL, 0);
if (inputLenW == 0) {
errno = EINVAL;
return NULL;
}
inputLenW++;
}
wPathInput = TMALLOC(wchar_t, inputLenW * sizeof(wchar_t));
if (!wPathInput) {
errno = ENOMEM;
return NULL;
}
if (MultiByteToWideChar(CP_UTF8, 0, input_path, inputLenMB + 1, wPathInput, inputLenW) == 0) {
tfree(wPathInput);
errno = EINVAL;
return NULL;
}
errno = original_errno;
outputLenW = GetFullPathNameW(wPathInput, 0, NULL, NULL);
if (outputLenW == 0) {
DWORD dwError = GetLastError();
if (dwError == ERROR_FILE_NOT_FOUND || dwError == ERROR_PATH_NOT_FOUND)
errno = ENOENT;
else if (dwError == ERROR_ACCESS_DENIED)
errno = EACCES;
else
errno = EINVAL;
tfree(wPathInput);
return NULL;
}
wPathOutput = (wchar_t*)malloc(outputLenW * sizeof(wchar_t));
if (!wPathOutput) {
tfree(wPathInput);
errno = ENOMEM;
return NULL;
}
errno = original_errno;
resultLenW = GetFullPathNameW(wPathInput, outputLenW, wPathOutput, NULL);
free(wPathInput);
if (resultLenW == 0 || resultLenW >= outputLenW) {
DWORD dwError = GetLastError();
if (dwError == ERROR_FILE_NOT_FOUND || dwError == ERROR_PATH_NOT_FOUND)
errno = ENOENT;
else if (dwError == ERROR_ACCESS_DENIED)
errno = EACCES;
else
errno = EINVAL;
tfree(wPathOutput);
return NULL;
}
outputLenMB = WideCharToMultiByte(CP_UTF8, 0, wPathOutput, -1, NULL, 0, NULL, NULL);
if (outputLenMB == 0) {
tfree(wPathOutput);
errno = EINVAL;
return NULL;
}
utf8PathOutput = (char*)malloc(outputLenMB);
if (!utf8PathOutput) {
tfree(wPathOutput);
errno = ENOMEM;
return NULL;
}
if (WideCharToMultiByte(CP_UTF8, 0, wPathOutput, -1, utf8PathOutput, outputLenMB, NULL, NULL) == 0) {
tfree(wPathOutput);
tfree(utf8PathOutput);
errno = EINVAL;
return NULL;
}
tfree(wPathOutput);
errno = original_errno;
return utf8PathOutput;
}
#endif

View File

@ -146,6 +146,7 @@ beginPlot(JOB *analysisPtr, CKTcircuit *circuitPtr, char *cktName, char *analNam
bool saveall = TRUE;
bool savealli = FALSE;
bool savenosub = FALSE;
bool savenointernals = FALSE;
char *an_name;
int initmem;
/*to resume a run saj
@ -223,6 +224,13 @@ beginPlot(JOB *analysisPtr, CKTcircuit *circuitPtr, char *cktName, char *analNam
saves[i].used = 1;
continue;
}
if (cieq(saves[i].name, "nointernals")) {
savenointernals = TRUE;
savesused[i] = TRUE;
saves[i].used = 1;
continue;
}
#ifdef SHARED_MODULE
/* this may happen if shared ngspice*/
if (cieq(saves[i].name, "none")) {
@ -255,7 +263,7 @@ beginPlot(JOB *analysisPtr, CKTcircuit *circuitPtr, char *cktName, char *analNam
/* Pass 1. */
if (numsaves && !saveall && !savenosub) {
if (numsaves && !saveall && !savenosub && !savenointernals) {
for (i = 0; i < numsaves; i++) {
if (!savesused[i]) {
for (j = 0; j < numNames; j++) {
@ -278,15 +286,20 @@ beginPlot(JOB *analysisPtr, CKTcircuit *circuitPtr, char *cktName, char *analNam
} else {
for (i = 0; i < numNames; i++)
if (!refName || !name_eq(dataNames[i], refName))
/* Save the node as long as it's not an internal device node */
if (!(savenosub && strchr(dataNames[i], '.')) && /* don't save subckt nodes */
/* Save the node (with restrictions) */
/* don't save subckt nodes */
if (!(savenosub && strchr(dataNames[i], '.')) &&
/* no internals at all, but still #branch */
(!(savenointernals && strstr(dataNames[i], "#")) || strstr(dataNames[i], "#branch")) &&
/* created by .probe */
!strstr(dataNames[i], "probe_int_") &&
/* don't save internal device nodes */
!strstr(dataNames[i], "#internal") &&
!strstr(dataNames[i], "#source") &&
!strstr(dataNames[i], "#drain") &&
!strstr(dataNames[i], "#collector") &&
!strstr(dataNames[i], "#collCX") &&
!strstr(dataNames[i], "#emitter") &&
!strstr(dataNames[i], "probe_int_") && /* created by .probe */
!strstr(dataNames[i], "#base"))
{
addDataDesc(run, dataNames[i], dataType, i, initmem);
@ -610,6 +623,8 @@ OUTpData(runDesc *plotPtr, IFvalue *refValue, IFvalue *valuePtr)
#endif
/* interpolated batch mode output to file/plot in transient analysis */
if (interpolated && run->circuit->CKTcurJob->JOBtype == 4) {
/* JOBtype == 4 means Transient Analysis. FIX ME */
if (run->writeOut) { /* To file */
InterpFileAdd(run, refValue, valuePtr);
}
@ -617,10 +632,9 @@ OUTpData(runDesc *plotPtr, IFvalue *refValue, IFvalue *valuePtr)
InterpPlotAdd(run, refValue, valuePtr);
}
return OK;
}
} else if (run->writeOut) {
/* standard batch mode output to file */
/* standard batch mode output to file */
else if (run->writeOut) {
if (run->pointCount == 1) {
fileInit_pass2(run);
}
@ -724,7 +738,6 @@ OUTpData(runDesc *plotPtr, IFvalue *refValue, IFvalue *valuePtr)
#ifdef TCL_MODULE
blt_add(i, valuePtr->v.vec.rVec [run->data[i].outIndex]);
#endif
}
fileEndPoint(run->fp, run->binary);
@ -1218,12 +1231,9 @@ vlength2delta(int len)
return 1024;
}
static void
plotAddRealValue(dataDesc *desc, double value)
void
AddRealValueToVector(struct dvec *v, double value)
{
struct dvec *v = desc->vec;
#ifdef SHARED_MODULE
if (savenone)
/* always save new data to same location */
@ -1245,6 +1255,11 @@ plotAddRealValue(dataDesc *desc, double value)
v->v_dims[0] = v->v_length; /* va, must be updated */
}
static void
plotAddRealValue(dataDesc *desc, double value)
{
AddRealValueToVector(desc->vec, value);
}
static void
plotAddComplexValue(dataDesc *desc, IFcomplex value)

View File

@ -71,5 +71,5 @@ void OUTerrorf(int, const char *fmt, ...) __attribute__ ((format (__printf__, 2
void OUTerrorf(int, const char *fmt, ...);
#endif
void AddRealValueToVector(struct dvec *v, double value);
#endif

View File

@ -23,16 +23,19 @@ Author: 1988 Jeffrey M. Hsu
#include "ngspice/grid.h"
#include "ngspice/sim.h"
#include "ngspice/stringskip.h"
#include "ngspice/evtproto.h"
#include "breakp2.h"
#include "display.h"
#include "graf.h"
#include "graphdb.h"
#include "runcoms.h"
#include "terminal.h"
#include "outitf.h"
static void gr_start_internal(struct dvec *dv, bool copyvec);
static void set(struct plot *plot, struct dbcomm *db, bool value, short mode);
static void setflag(struct plot *plot, struct dbcomm *db,
bool value, short mode);
static char *getitright(char *buf, double num);
/* for legends, set in gr_start, reset in gr_iplot and gr_init */
@ -81,7 +84,7 @@ int gr_init(double *xlims, double *ylims, /* The size of the screen. */
const char *commandline, /* For xi_zoomdata() */
int prevgraph) /* plot id, if started from a previous plot*/
{
GRAPH *graph, *pgraph;
GRAPH *graph, *pgraph;
wordlist *wl;
NG_IGNORE(nplots);
@ -309,7 +312,8 @@ static void drawLine(int x1, int y1, int x2, int y2, struct dvec *dv)
{
if (LC.dv) {
if (LC.dv != dv) {
fprintf(cp_err, "LC: DV changed!\n");
fprintf(cp_err, "LC: DV changed from %s to %s!\n",
LC.dv->v_name, dv->v_name);
LC_flush();
LC.dv = dv;
}
@ -540,11 +544,11 @@ static void gr_start_internal(struct dvec *dv, bool copyvec)
/* Do something special with poles and zeros. Poles are 'x's, and
* zeros are 'o's. */
if (dv->v_type == SV_POLE) {
dv->v_linestyle = 'x';
return;
}
else if (dv->v_type == SV_ZERO) {
} else if (dv->v_type == SV_ZERO) {
dv->v_linestyle = 'o';
return;
}
@ -553,35 +557,34 @@ static void gr_start_internal(struct dvec *dv, bool copyvec)
if (currentgraph->plottype == PLOT_POINT) {
if (pointchars[cur.linestyle - 1]) {
cur.linestyle++;
}
else {
} else {
cur.linestyle = 2;
}
}
else if ((cur.linestyle > 0) &&
(++cur.linestyle == dispdev->numlinestyles)) {
} else if ((cur.linestyle > 0) &&
(++cur.linestyle == dispdev->numlinestyles)) {
cur.linestyle = 2;
}
if ((cur.color > 0) && (++cur.color == dispdev->numcolors))
cur.color = (((currentgraph->grid.gridtype == GRID_SMITH ||
currentgraph->grid.gridtype == GRID_SMITHGRID) &&
(dispdev->numcolors > 3)) ? 4 : 2);
currentgraph->grid.gridtype == GRID_SMITHGRID) &&
(dispdev->numcolors > 3)) ? 4 : 2);
if (currentgraph->plottype == PLOT_POINT) {
dv->v_linestyle = pointchars[cur.linestyle - 2];
}
else {
} else {
dv->v_linestyle = cur.linestyle;
}
dv->v_color = cur.color;
/* Save the data so we can refresh */
link = TMALLOC(struct dveclist, 1);
link->next = currentgraph->plotdata;
/* Either reuse input vector or copy depnding on copyvec */
if (copyvec) {
link->vector = vec_copy(dv);
/* vec_copy doesn't set v_color or v_linestyle */
@ -589,8 +592,7 @@ static void gr_start_internal(struct dvec *dv, bool copyvec)
link->vector->v_linestyle = dv->v_linestyle;
link->vector->v_flags |= VF_PERMANENT;
link->f_own_vector = TRUE;
}
else {
} else {
link->vector = dv;
link->f_own_vector = FALSE;
}
@ -610,6 +612,7 @@ static void gr_start_internal(struct dvec *dv, bool copyvec)
}
/* Put the legend entry on the screen. */
if (!currentgraph->nolegend)
drawlegend(currentgraph, cur.plotno++, dv);
}
@ -846,6 +849,7 @@ void gr_restoretext(GRAPH *graph)
* XXX Or maybe even something more drastic ??
* It would be better to associate a color with an instance using a
* vector than the vector itself, for which color is something artificial. */
static int iplot(struct plot *pl, struct dbcomm *db)
{
double window;
@ -890,7 +894,9 @@ static int iplot(struct plot *pl, struct dbcomm *db)
strcpy(commandline, "plot ");
index = 5;
resumption = FALSE;
/* Draw the grid for the first time, and plot everything. */
lims = ft_minmax(xs, TRUE);
xlims[0] = lims[0];
xlims[1] = lims[1];
@ -951,11 +957,10 @@ static int iplot(struct plot *pl, struct dbcomm *db)
for (v = pl->pl_dvecs; v; v = v->v_next) {
if (v->v_flags & VF_PLOT) {
gr_start_internal(v, FALSE);
ft_graf(v, xs, TRUE);
ft_graf(v, v->v_scale ? v->v_scale : xs, TRUE);
}
}
inited = 1;
} else {
if (!currentgraph) /* Window was closed? */
return 0;
@ -1034,11 +1039,14 @@ static int iplot(struct plot *pl, struct dbcomm *db)
/* checking for all y values */
for (v = pl->pl_dvecs; v; v = v->v_next) {
int l;
if (!(v->v_flags & VF_PLOT)) {
continue;
}
dy = (isreal(v) ? v->v_realdata[len - 1] :
realpart(v->v_compdata[len - 1]));
l = v->v_length - 1;
dy = (isreal(v) ? v->v_realdata[l] :
realpart(v->v_compdata[l]));
if (ft_grdb) {
fprintf(cp_err, "y = %G\n", dy);
}
@ -1094,12 +1102,65 @@ static int iplot(struct plot *pl, struct dbcomm *db)
#ifndef X_DISPLAY_MISSING
gr_redraw(currentgraph);
#endif
} else {
/* Draw latest point for each vector. */
for (v = pl->pl_dvecs; v; v = v->v_next) {
if (v->v_flags & VF_PLOT) {
if (v->v_flags & VF_EVENT_NODE) {
int last;
if (xs->v_type != SV_TIME) {
/* There will be only one value, ignore. */
continue;
}
last = v->v_length - 1;
if (len > 2 &&
v->v_scale->v_realdata[last] <=
xs->v_realdata[len - 1]) {
/* No recent additions to this vector:
* draw/extend horizontal line showing last value.
* The rest is drawn in callback new_event().
*/
gr_point(v,
xs->v_realdata[len - 1],
v->v_realdata[last],
v->v_scale->v_realdata[last],
v->v_realdata[last],
len - 1);
}
} else {
/* Just connect the last two points.
* This won't be done with curve interpolation,
* so it might look funny. */
gr_point(v,
(isreal(xs) ? xs->v_realdata[len - 1] :
realpart(xs->v_compdata[len - 1])),
(isreal(v) ? v->v_realdata[len - 1] :
realpart(v->v_compdata[len - 1])),
(isreal(xs) ? xs->v_realdata[len - 2] :
realpart(xs->v_compdata[len - 2])),
(isreal(v) ? v->v_realdata[len - 2] :
realpart(v->v_compdata[len - 2])),
len - 1);
}
LC_flush(); // Disable line compression here ..
#ifdef LINE_COMPRESSION_CHECKS
LC.dv = NULL; // ... and suppress warnings.
#endif
}
}
#if BAD
}
else {
/* Just connect the last two points. This won't be done
* with curve interpolation, so it might look funny. */
for (v = pl->pl_dvecs; v; v = v->v_next) {
if (v->v_flags & VF_PLOT) {
if ((v->v_flags & VF_PLOT) && !(v->v_flags & VF_EVENT_NODE)) {
gr_point(v,
(isreal(xs) ? xs->v_realdata[len - 1] :
realpart(xs->v_compdata[len - 1])),
@ -1116,6 +1177,7 @@ static int iplot(struct plot *pl, struct dbcomm *db)
#endif
}
}
#endif // BAD
}
}
DevUpdate();
@ -1123,7 +1185,8 @@ static int iplot(struct plot *pl, struct dbcomm *db)
}
static void set(struct plot *plot, struct dbcomm *db, bool value, short mode)
static void setflag(struct plot *plot, struct dbcomm *db,
bool value, short mode)
{
struct dvec *v;
struct dbcomm *dc;
@ -1138,16 +1201,22 @@ static void set(struct plot *plot, struct dbcomm *db, bool value, short mode)
}
for (dc = db; dc; dc = dc->db_also) {
if (dc->db_nodename1 == NULL)
continue;
v = vec_fromplot(dc->db_nodename1, plot);
if (!v || v->v_plot != plot) {
if (!eq(dc->db_nodename1, "0") && value) {
fprintf(cp_err, "Warning: node %s non-existent in %s.\n",
dc->db_nodename1, plot->pl_name);
/* note: XXX remove it from dbs, so won't get further errors */
if (db->db_type == DB_IPLOT) { /* Vector cached in db_nodename2. */
v = (struct dvec *)dc->db_nodename2;
if (!v)
continue;
} else {
if (dc->db_nodename1 == NULL)
continue;
v = vec_fromplot(dc->db_nodename1, plot);
if (!v || v->v_plot != plot) {
if (!eq(dc->db_nodename1, "0") && value) {
fprintf(cp_err, "Warning: node %s non-existent in %s.\n",
dc->db_nodename1, plot->pl_name);
/* note: XXX remove it from dbs, so no further errors. */
}
continue;
}
continue;
}
if (value)
v->v_flags |= mode;
@ -1176,16 +1245,73 @@ static char *getitright(char *buf, double num)
}
}
static int hit, hit2;
void reset_trace(void)
{
hit = -1;
hit2 = -1;
}
/* This function is called from XSPICE whan an event node that is
* being i-plotted has a confirmed new value.
*/
#ifdef XSPICE
static Mif_Boolean_t new_event(double when, Mif_Value_t *val,
void *ctx, int is_last)
{
struct dbcomm *db = (struct dbcomm *)ctx;
struct dvec *v;
double value;
int last;
if (db->db_type == DB_DEADIPLOT)
return MIF_TRUE;
v = (struct dvec *)db->db_nodename2; // Struct member re-use.
/* Extend vectors. */
last = v->v_length - 1;
AddRealValueToVector(v->v_scale, when);
AddRealValueToVector(v->v_scale, when);
AddRealValueToVector(v, v->v_realdata[last]);
value = val->rvalue + db->db_value2; // Apply any plotting offset.
AddRealValueToVector(v, value);
if (db->db_graphid) {
GRAPH *gr;
gr = FindGraph(db->db_graphid);
if (gr) {
PushGraphContext(gr);
/* Draw horizontal and vertical lines. */
gr_point(v, when, v->v_realdata[last],
v->v_scale->v_realdata[last], v->v_realdata[last],
last > 0);
gr_point(v, when, value, when, v->v_realdata[last], 1);
if (is_last) {
struct dvec *xs;
/* Extend horizontally to the end of the time-step. */
xs = v->v_plot->pl_scale;
gr_point(v, xs->v_realdata[xs->v_length - 1], value,
when, value, 1);
}
LC_flush();
#ifdef LINE_COMPRESSION_CHECKS
LC.dv = NULL; // ... and suppress warnings.
#endif
DevUpdate();
PopGraphContext();
}
}
return MIF_FALSE;
}
#endif
void gr_iplot(struct plot *plot)
{
@ -1203,6 +1329,124 @@ void gr_iplot(struct plot *plot)
hit = 0;
for (db = dbs; db; db = db->db_next) {
if (db->db_type == DB_IPLOT || db->db_type == DB_IPLOTALL) {
#ifdef XSPICE
if (db->db_iteration > 0) {
double event_node_offset = 0, event_node_spacing;
struct dvec *v;
/* First call: set up event nodes spacing. */
if (db->db_iteration == DB_AUTO_OFFSET) {
/* Magic value: automatically offset traces for
*event nodes.
*/
if (!cp_getvar("event_node_spacing", CP_REAL,
&event_node_spacing, 0)) {
event_node_spacing = 1.5;
}
} else {
event_node_spacing = 0;
}
/* Find any XSPICE event nodes in the node
* list and set up plotting. There is a parallel path
* for pushing new event values into their corresponding
* vectors and plotting them.
*/
for (dc = db; dc; dc = dc->db_also) {
struct dbcomm *dd;
char *offp, save_sign;
int dup = 0;
if (dc->db_nodename1 == NULL)
continue;
/* Duplicated names will corrupt the plot. */
for (dd = db; dd != dc; dd = dd->db_also) {
if (!strcmp(dc->db_nodename1, dd->db_nodename1)) {
dup = 1;
break;
}
}
if (dup)
continue;
/* Check for a nodename that is an expression. */
offp = strchr(dc->db_nodename1, '+');
if (!offp)
offp = strchr(dc->db_nodename1, '-');
if (offp > dc->db_nodename1) {
save_sign = *offp;
*offp = '\0'; // Trim to bare name.
}
v = vec_fromplot(dc->db_nodename1, plot);
if (v) {
dc->db_nodename2 = (char *)v; // Save link to vector.
} else {
fprintf(cp_err,
"Warning: node %s non-existent in %s.\n",
dc->db_nodename1, plot->pl_name);
}
if (v && (v->v_flags & VF_EVENT_NODE)) {
/* Ask event simulator to call back with new values. */
EVTnew_value_call(dc->db_nodename1, new_event,
Evt_Cbt_Plot, dc);
if (offp > dc->db_nodename1) {
*offp = save_sign;
dc->db_value2 = atof(offp); // Offset to value.
event_node_offset = // New auto-offset.
dc->db_value2 + event_node_spacing;
} else if (event_node_spacing) {
char new_name[256];
/* Add offset to vector names.
* Ugly, but only here can event nodes
* be identified.
*/
snprintf(new_name, sizeof new_name, "%s+%g",
dc->db_nodename1, event_node_offset);
tfree(v->v_name);
v->v_name = copy(new_name);
dc->db_value2 = event_node_offset;
event_node_offset += event_node_spacing;
}
if (dc->db_value2) {
int i;
/* Adjust existing values. */
for (i = 0; i < v->v_length; ++i)
v->v_realdata[i] += dc->db_value2;
}
if ((v->v_flags & VF_PERMANENT) == 0) {
vec_new(v);
v->v_flags |= VF_PERMANENT; // Make it findable.
if (v->v_scale) {
v->v_scale->v_flags |= VF_PERMANENT;
vec_new(v->v_scale);
}
}
} else if (offp) {
fprintf(stderr,
"Offset (%s) ignored for analog node %s\n",
offp, dc->db_nodename1);
}
}
db->db_iteration = 0;
}
#endif
if (db->db_graphid) {
GRAPH *gr;
@ -1214,16 +1458,20 @@ void gr_iplot(struct plot *plot)
/* Temporarily set plot flag on matching vector. */
set(plot, db, TRUE, VF_PLOT);
setflag(plot, db, TRUE, VF_PLOT);
dontpop = 0;
if (iplot(plot, db)) {
/* graph just assigned */
db->db_graphid = currentgraph->graphid;
/* Graph just assigned, place id into every struct dbcomm
* as event nodes need that.
*/
for (dc = db; dc; dc = dc->db_also)
dc->db_graphid = currentgraph->graphid;
dontpop = 1;
}
set(plot, db, FALSE, VF_PLOT);
setflag(plot, db, FALSE, VF_PLOT);
if (!dontpop && db->db_graphid)
PopGraphContext();
@ -1233,7 +1481,7 @@ void gr_iplot(struct plot *plot)
struct dvec *v, *u;
int len;
set(plot, db, TRUE, VF_PRINT);
setflag(plot, db, TRUE, VF_PRINT);
len = plot->pl_scale->v_length;
@ -1289,7 +1537,7 @@ void gr_iplot(struct plot *plot)
printf("\n");
}
}
set(plot, db, FALSE, VF_PRINT);
setflag(plot, db, FALSE, VF_PRINT);
}
}
}
@ -1311,6 +1559,7 @@ void gr_end_iplot(void)
prev = NULL;
for (db = dbs; db; prev = db, db = next) {
db->db_nodename2 = NULL; // Forget link.
next = db->db_next;
if (db->db_type == DB_DEADIPLOT) {
if (db->db_graphid) {
@ -1323,6 +1572,7 @@ void gr_end_iplot(void)
}
}
else if (db->db_type == DB_IPLOT || db->db_type == DB_IPLOTALL) {
db->db_iteration = DB_NORMAL; // Reset XSPICE event plotting
if (db->db_graphid) {
/* get private copy of dvecs */

View File

@ -228,8 +228,15 @@ int DestroyGraph(int id)
if (db && (db->db_type == DB_IPLOT ||
db->db_type == DB_IPLOTALL)) {
db->db_type = DB_DEADIPLOT;
/* Delete this later */
/* Delete this later, after marking as dead.
* Entries on the node mlist are marked to terminate
* any XSPICE callbacks for event nodes.
*/
do {
db->db_type = DB_DEADIPLOT;
db = db->db_also;
} while (db);
return 0;
}
@ -370,6 +377,3 @@ void PopGraphContext(void)
gcstacktop = gcstacktop->next;
txfree(dead); /* free allocation */
} /* end of function PopGraphContext */

View File

@ -20,7 +20,7 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group
static void plotinterval(struct dvec *v, double lo, double hi, register double *coeffs,
int degree, bool rotated);
static int get_xdirection(struct dvec *xs, int len, bool mn);
static int get_xdirection(struct dvec *xs, int len, bool mn, bool analog);
/* Plot the vector v, with scale xs. If we are doing curve-fitting, then
* do some tricky stuff.
@ -90,11 +90,11 @@ ft_graf(struct dvec *v, struct dvec *xs, bool nostart)
if (xs) {
/* Check vector lengths. */
if (v->v_length != xs->v_length) {
if (v->v_length != xs->v_length && !v->v_scale) {
fprintf(stderr,
"Warning: length of vector %s and its scale %s do "
"not match, plot may be truncated!\n",
v->v_name, xs->v_name);
"Warning: length of vector %s (%d) and its scale %s (%d) "
"do not match, plot may be truncated!\n",
v->v_name, v->v_length, xs->v_name, xs->v_length);
}
length = MIN(v->v_length, xs->v_length);
} else {
@ -146,7 +146,8 @@ ft_graf(struct dvec *v, struct dvec *xs, bool nostart)
Then everything is plotted. */
bool mono = (currentgraph->plottype != PLOT_RETLIN);
int dir = get_xdirection(xs, length, mono);
int dir = get_xdirection(xs, length, mono,
!(v->v_flags & VF_EVENT_NODE));
for (i = 0; i < length; i++) {
dx = isreal(xs) ? xs->v_realdata[i] :
realpart(xs->v_compdata[i]);
@ -361,7 +362,7 @@ plotinterval(struct dvec *v, double lo, double hi, register double *coeffs, int
If more than 10% of the data points deviate from the majority direction, issue a warning,
if 'retraceplot' is not set.
*/
static int get_xdirection(struct dvec* xs, int len, bool mn) {
static int get_xdirection(struct dvec* xs, int len, bool mn, bool analog) {
int i, dir = 1, inc = 0, dec = 0;
double dx, lx;
static bool msgsent = FALSE;
@ -379,17 +380,32 @@ static int get_xdirection(struct dvec* xs, int len, bool mn) {
lx = dx;
}
if (inc < 2 && dec < 2)
fprintf(stderr, "Warning, (new) x axis seems to have one data point only\n");
/* Event nodes may never change, so no advance is OK. Similarly, vertical
* edges may falsely suggest that "retrace" is needed.
*/
if (mn && !msgsent && (((double)inc / len > 0.1 && inc < dec) || ((double)dec / len > 0.1 && inc > dec))) {
fprintf(stderr, "Warning, more than 10%% of scale vector %s data points are not monotonic.\n", xs->v_name);
fprintf(stderr, " Please consider using the 'retraceplot' flag to the plot command to plot all data.\n");
msgsent = TRUE;
if (analog) {
if ((inc + dec) == 0) {
fprintf(stderr,
"Warning, (new) x axis (%s) seems to have only one "
"data point: %d points %d increasing %d decreasing.\n",
xs->v_name, xs->v_length, inc, dec);
}
if (mn && !msgsent && (((double)inc / len > 0.1 && inc < dec) ||
((double)dec / len > 0.1 && inc > dec))) {
fprintf(stderr,
"Warning, more than 10%% of scale vector %s data points "
"are not monotonic.\n",
xs->v_name);
fprintf(stderr,
" Please consider using the 'retraceplot' "
"flag to the plot command to plot all data.\n");
msgsent = TRUE;
}
}
if (inc < dec)
dir = -1;
return dir;
}

View File

@ -860,11 +860,15 @@ bool plotit(wordlist *wl, const char *hcopy, const char *devname)
/* Add n * spacing (e.g. 1.5) to digital event node based vectors */
if (digitop) {
double spacing = 1.5;
double spacing;
double nn = 0.;
int ii = 0, jj = 0;
for (d = vecs; d; d = d->v_link2) {
if (!cp_getvar("plot_auto_spacing", CP_REAL, &spacing, 0) ||
spacing < 0.0) {
spacing = 1.5;
}
for (d = vecs; d; d = d->v_link2) {
if ((d->v_flags & VF_EVENT_NODE) &&
!(d->v_flags & VF_PERMANENT) &&
d->v_scale && (d->v_scale->v_flags & VF_EVENT_NODE) &&
@ -884,11 +888,11 @@ bool plotit(wordlist *wl, const char *hcopy, const char *devname)
if (!ylim) {
ylim = TMALLOC(double, 2);
ylim[0] = 0;
/* make ylim[1] a multiple of 2*1.5 */
/* make ylim[1] a multiple of 2 * spacing. */
if (jj % 2 == 0)
ylim[1] = nn;
else
ylim[1] = nn + 1.5;
ylim[1] = nn + spacing;
}
/* re-scaled plot */
else {

View File

@ -78,6 +78,14 @@ struct Evt_Inst_Index {
int index; /* the value of the index */
};
struct Evt_Node_Cb {
struct Evt_Node_Cb *next;
Evt_New_Value_Cb_t fn; /* Function to be called. */
Evt_Node_Cb_Type_t type; /* Data type to pass to fn. */
const char *member; /* For event data type's plot fn. */
void *ctx;
};
struct Evt_Node_Info {
Evt_Node_Info_t *next; /* the next in the linked list */
char *name; /* Name of node in deck */
@ -88,6 +96,7 @@ struct Evt_Node_Info {
int num_outputs; /* Number of outputs connected to this node */
int num_insts; /* The number of insts receiving node as input */
Evt_Inst_Index_t *inst_list; /* Linked list of indexes of these instances */
Evt_Node_Cb_t *cbs; /* New value callbacks. */
};
struct Evt_Inst_Info {
@ -195,8 +204,6 @@ struct Evt_Queue {
/* ************** */
struct Evt_Node {
Evt_Node_t *next; /* pointer to next in linked list */
Mif_Boolean_t op; /* true if computed from op analysis */
@ -204,6 +211,7 @@ struct Evt_Node {
void **output_value; /* Array of outputs posted to this node */
void *node_value; /* Resultant computed from output values */
void *inverted_value; /* Inverted copy of node_value */
};
struct Evt_Node_Data {
@ -362,6 +370,4 @@ struct Evt_Ckt_Data {
Evt_Option_t options; /* Data input on .options cards */
};
#endif

View File

@ -136,6 +136,30 @@ bool Evtcheck_nodes(
struct INPtables *stab); /* Symbol table. */
struct dvec *EVTfindvec(char *node);
/* Set and remove call-backs on new node values. */
Mif_Boolean_t EVTnew_value_call(const char *node,
Evt_New_Value_Cb_t fn,
Evt_Node_Cb_Type_t type,
void *ctx);
void EVTcancel_value_call(const char *node,
Evt_New_Value_Cb_t fn,
void *ctx);
/* Parse a node name with member and return the node index and type. */
struct node_parse {
char *node;
char *member;
int udn_index;
};
int Evt_Parse_Node(const char *node, struct node_parse *result);
/* Internal utility functions. */
void Evt_purge_free_outputs(void);
#endif

View File

@ -1,6 +1,6 @@
#ifndef ngspice_EVTTYPES_H
#define ngspice_EVTTYPES_H
#include "miftypes.h"
typedef struct Evt_Output_Info Evt_Output_Info_t;
typedef struct Evt_Port_Info Evt_Port_Info_t;
@ -28,6 +28,11 @@ typedef struct Evt_Limit Evt_Limit_t;
typedef struct Evt_Job Evt_Job_t;
typedef struct Evt_Option Evt_Option_t;
typedef struct Evt_Ckt_Data Evt_Ckt_Data_t;
typedef struct Evt_Node_Cb Evt_Node_Cb_t;
typedef Mif_Boolean_t (*Evt_New_Value_Cb_t)(double when, Mif_Value_t *val_p,
void *ctx, int is_last);
typedef enum Evt_Node_Cb_Type { Evt_Cbt_Raw, Evt_Cbt_Plot} Evt_Node_Cb_Type_t;
#endif

View File

@ -34,7 +34,13 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group
#define DBC_GTE 5 /* >= (ge) */
#define DBC_LTE 6 /* <= (le) */
/* Below, members db_op and db_value1 are re-purposed by iplot options. */
/* Below, members db_nodename2, db_op, db_iteration and db_value1/2
* are re-purposed by iplot optionsf.
* These definitions are used for db_iteration:
*/
#define DB_NORMAL 1
#define DB_AUTO_OFFSET 2
struct dbcomm {
int db_number; /* The number of this debugging command. */

View File

@ -357,6 +357,18 @@ typedef int (SendInitEvtData)(int, int, char*, char*, int, void*);
int identification number of calling ngspice shared lib
void* return pointer received from caller
*/
/* Upon time step finished, all events that occurred on a node.
* A non-zero return value cancels further reports for tis node.
*/
typedef int (SendRawEvtData)(double, void *, void *, int);
/*
double event time
void* pointer to the node's value (a Digital_t for digital nodes)
void* return pointer received from caller
int zero if another event report for the same node follows
*/
#endif
/* ngspice initialization,
@ -415,6 +427,32 @@ userData: pointer to user-defined data, will not be modified, but
handed over back to caller during Callback, e.g. address of calling object */
IMPEXP
int ngSpice_Init_Evt(SendEvtData* sevtdata, SendInitEvtData* sinitevtdata, void* userData);
/* Request callback for every event on a specific XSPICE event node.
* A single callback function pointer is stored, with all calls directed
* to the function specified last.
* The return value identifies the node data type or is -1 on error.
*
* node name of an event node.
* srawevt pointer to callback function.
* userData pointer to user-defined data.
*/
IMPEXP
int ngSpice_Raw_Evt(const char* node, SendRawEvtData* srawevt, void* userData);
/* Decode raw event node data. Return 0 on success.
* evt pointer to event data (a reported node value).
* type node type index, return value from ngSpice_Raw_Evt().
* pplotval pointer to a double, the "plotting value" is returned. NULL OK.
* printval pointer to a char pointer that is updated to point to a
* readonly string describing the value. If evt is NULL, a string
* identifying the node data type is returned. NULL is OK.
*/
IMPEXP
int ngSpice_Decode_Evt(void* evt, int type,
double *pplotval, const char **pprintval);
#endif

View File

@ -1359,6 +1359,16 @@ int main(int argc, char **argv)
#elif defined(WaGauss)
initw();
#endif
/* write out the ngspice start command */
if (ft_ngdebug)
{
int ni;
fprintf(stdout, "\nNote: ngspice start command line:\n");
for (ni = 0; ni < argc; ni++) {
fprintf(stdout, " %s", argv[ni]);
}
fprintf(stdout, "\n\n");
}
if (!ft_servermode) {
@ -1558,8 +1568,9 @@ int main(int argc, char **argv)
}
else {
fprintf(stderr,
"Note: No \".plot\", \".print\", or \".fourier\" lines; "
"no simulations run\n");
"Error: incomplete or empty netlist\n"
" or no \".plot\", \".print\", or \".fourier\" lines in batch mode;\n"
"no simulations run!\n");
sp_shutdown(EXIT_BAD);
}

View File

@ -182,6 +182,8 @@ typedef void (*sighandler)(int);
#ifdef XSPICE
#include "ngspice/evtshared.h"
#include "ngspice/evtproto.h"
#include "ngspice/evtudn.h"
extern bool wantevtdata;
#endif
@ -222,7 +224,7 @@ extern struct comm spcp_coms[];
struct comm* cp_coms = spcp_coms;
/* Main options */
static bool ft_servermode = FALSE;
bool ft_batchmode = FALSE;
bool ft_pipemode = FALSE;
bool rflag = FALSE; /* has rawfile */
@ -341,6 +343,8 @@ void sh_delete_myvec(void);
#ifdef XSPICE
void shared_send_event(int, double, double, char *, void *, int, int);
void shared_send_dict(int, int, char*, char*);
static int evt_shim(double time, Mif_Value_t *vp, void *ctx, int last);
#endif
#if !defined(low_latency)
@ -397,6 +401,7 @@ static int intermj = 1;
#ifdef XSPICE
static SendInitEvtData* sendinitevt;
static SendEvtData* sendevt;
static SendRawEvtData *sendrawevt;
#endif
static void* euserptr;
static wordlist *shcontrols;
@ -1387,6 +1392,41 @@ int ngSpice_Init_Evt(SendEvtData* sevtdata, SendInitEvtData* sinitevtdata, void
return(TRUE);
}
/* Set callback address for raw XSPICE events.
* The return value identifies the node data type or is -1 on error.
*/
IMPEXP
int ngSpice_Raw_Evt(const char* node, SendRawEvtData* srawevt, void* userData)
{
struct node_parse np;
if (Evt_Parse_Node(node, &np) < 0 || np.member)
return -1; // Invalid node name.
sendrawevt = srawevt;
EVTnew_value_call(node, evt_shim, Evt_Cbt_Raw, userData);
return np.udn_index;
}
IMPEXP
int ngSpice_Decode_Evt(void* evt, int type,
double *pplotval, const char **ppprintval)
{
if (type >= g_evt_num_udn_types)
return 1;
if (!evt) {
if (!ppprintval)
return 2;
*ppprintval = g_evt_udn_info[type]->name;
return 0;
}
if (pplotval)
g_evt_udn_info[type]->plot_val(evt, "", pplotval);
if (ppprintval)
g_evt_udn_info[type]->print_val(evt, "", (char **)ppprintval);
return 0;
}
/* Get info about the event node vector.
If node_name is NULL, just delete previous data */
IMPEXP
@ -1467,7 +1507,7 @@ sh_vfprintf(FILE *f, const char *fmt, va_list args)
{
char buf[1024];
char *p/*, *s*/;
int nchars, /*escapes,*/ result;
int nchars;
size_t size;
@ -1547,7 +1587,7 @@ sh_vfprintf(FILE *f, const char *fmt, va_list args)
Spice_Init() from caller of ngspice.dll */
result = sh_fputs(p, f);
sh_fputs(p, f);
if (p != buf)
tfree(p);
@ -1929,7 +1969,6 @@ void SetAnalyse(
|| defined (HAVE_FTIME)
PerfTime timenow; /* actual time stamp */
int diffsec, diffmillisec; /* differences actual minus prev. time stamp */
int result; /* return value from callback function */
char* s; /* outputs to callback function */
int OldPercent; /* Previous progress value */
char OldAn[128]; /* Previous analysis type */
@ -1994,7 +2033,7 @@ void SetAnalyse(
if (!strcmp(Analyse, "tran")) {
if (ckt && (ckt->CKTtime > ckt->CKTfinalTime - ckt->CKTmaxStep)) {
sprintf(s, "--ready--");
result = statfcn(s, ng_ident, userptr);
statfcn(s, ng_ident, userptr);
tfree(s);
return;
}
@ -2007,7 +2046,7 @@ void SetAnalyse(
return;
}
sprintf( s, "--ready--");
result = statfcn(s, ng_ident, userptr);
statfcn(s, ng_ident, userptr);
tfree(s);
return;
}
@ -2056,7 +2095,7 @@ void SetAnalyse(
}
/* ouput only after a change */
if (strcmp(olds, s))
result = statfcn(s, ng_ident, userptr);
statfcn(s, ng_ident, userptr);
if(thread1)
strcpy(olds1, s);
else
@ -2065,11 +2104,10 @@ void SetAnalyse(
tfree(s);
#else
char* s;
int result;
static bool havesent = FALSE;
if (!havesent) {
s = copy("No usage info available");
result = statfcn(s, ng_ident, userptr);
statfcn(s, ng_ident, userptr);
tfree(s);
havesent = TRUE;
}
@ -2433,8 +2471,14 @@ sharedsync(double *pckttime, double *pcktdelta, double olddelta, double finalt,
step if return value from getsync is 1. */
int retval = getsync(*pckttime, pcktdelta, olddelta, redostep, ng_ident, loc, userptr);
/* never move beyond final time */
if (*pckttime + *pcktdelta > finalt)
*pcktdelta = finalt - *pckttime - 1.1 * delmin;
if (*pckttime + *pcktdelta > finalt) {
double newdelta;
newdelta = finalt - *pckttime - 1.1 * delmin;
if (newdelta <= 0.0)
newdelta = finalt - *pckttime;
*pcktdelta = newdelta;
}
/* user has decided to redo the step, ignoring redostep being set to 0
by ngspice. */
@ -2460,6 +2504,13 @@ void shared_send_dict(int index, int no_of_nodes, char* name, char*type)
if (sendinitevt)
sendinitevt(index, no_of_nodes, name, type, ng_ident, euserptr);
}
static int evt_shim(double time, Mif_Value_t *vp, void *ctx, int last)
{
if (sendrawevt)
return sendrawevt(time, vp->pvalue, ctx, last); // Strip Mif_value.
return 1;
}
#endif
static int totalreset(void)

View File

@ -45,6 +45,7 @@ NON-STANDARD FEATURES
#include "ngspice/mif.h"
#include "ngspice/evt.h"
#include "ngspice/evtudn.h"
#include "ngspice/evtproto.h"
@ -160,18 +161,62 @@ void EVTaccept(
num_modified = node_data->num_modified;
/* Loop through list of items modified since last time */
for(i = 0; i < num_modified; i++) {
Evt_Node_t *this;
Evt_Node_Info_t *node_info;
Evt_Node_Cb_t *cb, **cbpp;
int udn_index;
/* Get the index of the node modified */
index = node_data->modified_index[i];
/* Reset the modified flag */
node_data->modified[index] = MIF_FALSE;
/* Call any value-change functions registered for this node. */
node_info = node_table[index];
udn_index = node_info->udn_index;
cbpp = &node_info->cbs;
for (;;) {
Mif_Value_t val;
cb = *cbpp;
if (cb == NULL)
break;
for (this = *node_data->last_step[index];
this;
this = this->next) {
switch (cb->type) {
case Evt_Cbt_Raw:
val.pvalue = this->node_value;
break;
case Evt_Cbt_Plot:
g_evt_udn_info[udn_index]->plot_val(this->node_value,
(char *)cb->member,
&val.rvalue);
break;
}
if ((*cb->fn)(this->step, &val, cb->ctx, !this->next)) {
/* Remove callback from chain. */
*cbpp = cb->next;
txfree(cb);
break;
}
}
if (this == NULL) // Normal loop exit.
cbpp = &cb->next;
}
/* Optionally store node values for later examination.
* The test of CKTtime here is copied from dctran.c.
* CKTinitTime is from the tstart parameter of the "tran"
* command or card.
*/
if (node_table[index]->save && ckt->CKTtime >= ckt->CKTinitTime &&
if (node_info->save && ckt->CKTtime >= ckt->CKTinitTime &&
(ckt->CKTtime > 0 || !(ckt->CKTmode & MODEUIC))) {
/* Update last_step for this index */
node_data->last_step[index] = node_data->tail[index];
@ -239,3 +284,77 @@ void EVTaccept(
} /* EVTaccept */
/* Functions to set-up and cancel value-changed callbacks. */
Mif_Boolean_t EVTnew_value_call(const char *node,
Evt_New_Value_Cb_t fn,
Evt_Node_Cb_Type_t type,
void *ctx)
{
struct node_parse result;
int index;
Evt_Ckt_Data_t *evt;
CKTcircuit *ckt;
Evt_Node_Info_t *node_info;
Evt_Node_Cb_t *cb;
index = Evt_Parse_Node(node, &result);
if (index < 0)
return MIF_FALSE;
ckt = g_mif_info.ckt;
evt = ckt->evt;
node_info = evt->info.node_table[index];
cb = tmalloc(sizeof *cb);
cb->next = node_info->cbs;
node_info->cbs = cb;
cb->fn = fn;
cb->type = type;
cb->member = copy(result.member);
cb->ctx = ctx;
txfree(result.node);
return MIF_TRUE;
}
void EVTcancel_value_call(const char *node,
Evt_New_Value_Cb_t fn,
void *ctx)
{
Evt_Ckt_Data_t *evt;
CKTcircuit *ckt;
Evt_Node_Info_t **node_table, *node_info;
Evt_Node_Cb_t **cbpp, *cb;
int i, num_nodes;
ckt = g_mif_info.ckt;
if (!ckt)
return;
evt = ckt->evt;
if (!evt)
return;
/* Look for node name in the event-driven node list */
node_table = evt->info.node_table;
num_nodes = evt->counts.num_nodes;
for (i = 0; i < num_nodes; i++) {
if (cieq(node, node_table[i]->name))
break;
}
if (i >= num_nodes)
return;
node_info = node_table[i];
cbpp = &node_info->cbs;
cb = node_info->cbs;
while (cb) {
if (cb->fn == fn && cb->ctx == ctx) {
*cbpp = cb->next;
tfree(cb);
} else {
cbpp = &cb->next;
}
cb = *cbpp;
}
}

View File

@ -167,10 +167,12 @@ Evt_Node_Data_destroy(Evt_Ckt_Data_t *evt, Evt_Node_Data_t *node_data)
for (i = 0; i < evt->counts.num_nodes; i++) {
Evt_Node_Info_t *info = evt->info.node_table[i];
Evt_Node_t *node;
Evt_Node_t *node;
node = node_data->head[i];
while (node) {
Evt_Node_t *next = node->next;
Evt_Node_destroy(info, node);
tfree(node);
node = next;
@ -178,6 +180,7 @@ Evt_Node_Data_destroy(Evt_Ckt_Data_t *evt, Evt_Node_Data_t *node_data)
node = node_data->free[i];
while (node) {
Evt_Node_t *next = node->next;
Evt_Node_destroy(info, node);
tfree(node);
node = next;
@ -193,6 +196,7 @@ Evt_Node_Data_destroy(Evt_Ckt_Data_t *evt, Evt_Node_Data_t *node_data)
for (i = 0; i < evt->counts.num_nodes; i++) {
Evt_Node_Info_t *info = evt->info.node_table[i];
Evt_Node_destroy(info, &(node_data->rhs[i]));
Evt_Node_destroy(info, &(node_data->rhsold[i]));
}
@ -287,6 +291,7 @@ static void
Evt_Info_destroy(Evt_Info_t *info)
{
Evt_Inst_Info_t *inst = info->inst_list;
while (inst) {
Evt_Inst_Info_t *next_inst = inst->next;
tfree(inst);
@ -295,14 +300,24 @@ Evt_Info_destroy(Evt_Info_t *info)
tfree(info->inst_table);
Evt_Node_Info_t *nodei = info->node_list;
while (nodei) {
Evt_Node_Info_t *next_nodei = nodei->next;
Evt_Node_Cb_t *cb, *cb_next;
tfree(nodei->name);
for (cb = nodei->cbs; cb; cb = cb_next) {
cb_next = cb->next;
if (cb->member)
txfree(cb->member);
tfree(cb);
}
Evt_Inst_Index_t *p = nodei->inst_list;
while (p) {
Evt_Inst_Index_t *next_p = p->next;
tfree(p);
txfree(p);
p = next_p;
}

View File

@ -45,15 +45,79 @@ NON-STANDARD FEATURES
#include "ngspice/mif.h"
#include "ngspice/mifproto.h"
/*saj for output */
#include "ngspice/sim.h"
#include "ngspice/dvec.h"
//#include "ftedata.h"
//#include "fteconstant.h"
//#include "util.h"
#include "ngspice/cpstd.h"
/* Parse member qualifier from node name and find node index.
* The node name may be qualified by a member name for nodes with
* composite values such as Digital_t, as in "node_name(state)".
*/
int Evt_Parse_Node(const char *node, struct node_parse *result)
{
Evt_Ckt_Data_t *evt;
CKTcircuit *ckt;
Evt_Node_Info_t **node_table;
char *name, *ptr;
int i, num_nodes;
ckt = g_mif_info.ckt;
if (!ckt)
return -1;
evt = ckt->evt;
if (!evt)
return -1;
if (!evt->info.node_table)
return -1;
if (evt->counts.num_nodes == 0)
return -1;
/* Make a copy of the node name. Do not free this string. */
name = MIFcopy((char *)node);
/* Convert to all lower case */
strtolower(name);
/* Divide into the node name and member name */
result->node = name;
for (ptr = name; *ptr != '\0'; ptr++)
if (*ptr == '(')
break;
if (*ptr == '(') {
*ptr = '\0';
ptr++;
result->member = ptr;
for( ; *ptr != '\0'; ptr++)
if (*ptr == ')')
break;
*ptr = '\0';
} else {
result->member = NULL;
}
/* Look for node name in the event-driven node list */
node_table = evt->info.node_table;
num_nodes = evt->counts.num_nodes;
for (i = 0; i < num_nodes; i++) {
if (cieq(name, node_table[i]->name))
break;
}
if (i >= num_nodes) {
tfree(name);
return -1;
}
result->udn_index = node_table[i]->udn_index;
return i;
}
/*
EVTfindvec()
@ -79,21 +143,16 @@ 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;
struct node_parse result;
int index, i;
int udn_index;
int num_events;
int i;
int num_nodes;
int udn_index;
int num_events;
Mif_Boolean_t found;
Evt_Ckt_Data_t *evt;
CKTcircuit *ckt;
Evt_Node_Info_t **node_table;
Evt_Node_t *head;
Evt_Node_t *event;
Evt_Ckt_Data_t *evt;
CKTcircuit *ckt;
Evt_Node_Info_t *node_info;
Evt_Node_t *head;
Evt_Node_t *event;
double *anal_point_vec;
double *value_vec;
@ -106,66 +165,24 @@ struct dvec *EVTfindvec(
/* or if number of event nodes is zero. */
ckt = g_mif_info.ckt;
if(! ckt)
return(NULL);
if (!ckt)
return NULL;
evt = ckt->evt;
if(! evt)
return(NULL);
if(! evt->info.node_table)
return(NULL);
if(evt->counts.num_nodes == 0)
return(NULL);
if (!evt || !evt->data.node)
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 */
strtolower(name);
/* 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 = evt->counts.num_nodes;
node_table = 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) {
tfree(name);
return(NULL);
}
index = Evt_Parse_Node(node, &result);
if (index < 0)
return NULL;
/* Get the UDN type index */
udn_index = node_table[i]->udn_index;
if (!evt->data.node) {
// fprintf(stderr, "Warning: No event data available! \n Simulation not yet run?\n");
tfree(name);
return(NULL);
}
node_info = evt->info.node_table[index];
udn_index = node_info->udn_index;
/* Count the number of events */
head = evt->data.node->head[i];
head = evt->data.node->head[index];
for(event = head, num_events = 0; event; event = event->next)
num_events++;
@ -187,9 +204,9 @@ struct dvec *EVTfindvec(
/* 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);
g_evt_udn_info[udn_index]->plot_val(event->node_value,
result.member ? result.member : "all",
&value);
/* Put the first value of the horizontal line in the vector */
anal_point_vec[i] = event->step;
@ -197,6 +214,7 @@ struct dvec *EVTfindvec(
i++;
}
txfree(result.node);
/* Add one more point so that the line will extend to the end of the plot. */
@ -206,20 +224,18 @@ struct dvec *EVTfindvec(
/* Allocate dvec structures and assign the vectors into them. */
/* See FTE/OUTinterface.c:plotInit() for initialization example. */
ptr = tprintf("%s_steps", name);
scale = dvec_alloc(ptr,
scale = dvec_alloc(tprintf("%s_steps", node),
SV_TIME,
(VF_REAL | VF_EVENT_NODE) & ~VF_PERMANENT,
i, anal_point_vec);
d = dvec_alloc(name,
d = dvec_alloc(copy(node),
SV_VOLTAGE,
(VF_REAL | VF_EVENT_NODE) & ~VF_PERMANENT,
i, value_vec);
d->v_scale = scale;
/* Return the dvec */
return(d);
}

View File

@ -330,6 +330,7 @@ static void EVTnode_insert(
node->name = MIFcopy(node_name);
node->udn_index = udn_index;
node->save = MIF_TRUE; /* Backward compatible behaviour: save all. */
node->cbs = NULL;
index = ckt->evt->counts.num_nodes;
(ckt->evt->counts.num_nodes)++;
}

View File

@ -210,7 +210,7 @@ static void idn_digital_plot_val(void *evt_struct, char *member, double *val)
/* Output a value for the requested member of the digital struct */
if(strcmp(member,"strength") == 0) {
if (member && strcmp(member,"strength") == 0) {
/* Choose values that will not make plots lie on state plots */
switch(dig_struct->strength) {