Add an extended shared library test program with additional

local commands to exercise the API.
This commit is contained in:
Giles Atkinson 2025-03-27 08:54:24 +00:00 committed by Holger Vogt
parent 16aadef4c7
commit a649514e87
4 changed files with 979 additions and 0 deletions

93
examples/shared/README Normal file
View File

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

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

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

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

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

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

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