Browse Source
Add an extended shared library test program with additional
Add an extended shared library test program with additional
local commands to exercise the API.pre-master-46
committed by
Holger Vogt
4 changed files with 979 additions and 0 deletions
-
93examples/shared/README
-
829examples/shared/shx.c
-
34examples/shared/t.cir
-
23examples/shared/t.shx
@ -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 |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue