Merge branch 'pre-master-45' into bt_dev
This commit is contained in:
commit
fdf1a7d791
|
|
@ -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)
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
15
src/main.c
15
src/main.c
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue