From b8b83b1601300724790a631b6418f95d2b282acf Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Thu, 27 Mar 2025 08:54:24 +0000 Subject: [PATCH] Add an extended shared library test program with additional local commands to exercise the API. --- examples/shared/README | 93 +++++ examples/shared/shx.c | 829 +++++++++++++++++++++++++++++++++++++++++ examples/shared/t.cir | 34 ++ examples/shared/t.shx | 23 ++ 4 files changed, 979 insertions(+) create mode 100644 examples/shared/README create mode 100644 examples/shared/shx.c create mode 100644 examples/shared/t.cir create mode 100755 examples/shared/t.shx diff --git a/examples/shared/README b/examples/shared/README new file mode 100644 index 000000000..2de2a7350 --- /dev/null +++ b/examples/shared/README @@ -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 + 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 + 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 + 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 + 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 + Query a spacific data vector, using ngSpice_GVI(). + /xnode + 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) + diff --git a/examples/shared/shx.c b/examples/shared/shx.c new file mode 100644 index 000000000..a3388b7a3 --- /dev/null +++ b/examples/shared/shx.c @@ -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 +#include +#include +#include + +#ifndef _MSC_VER +#include +#include + +#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 + +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 /* to load libraries*/ +#include + +#endif /* not Windows */ + +#include + +/* 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 \tList vectors in plot.\n" + " /bgr\t\t\tQuery background thread.\n" + " /cplot\t\tShow name of current plot.\n" + " /dlim \t\tSet output limit for SendData CB.\n" + " /elim \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 \t\tSet output limit for SendxSRCData CB.\n" + " /sramp [new_val] [interval end_val]\n" + "\t\t\tAuto-ramp sources\n" + " /vec \t\tQuery vector.\n" + " /xnode \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; + } +} diff --git a/examples/shared/t.cir b/examples/shared/t.cir new file mode 100644 index 000000000..73929a1c1 --- /dev/null +++ b/examples/shared/t.cir @@ -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 diff --git a/examples/shared/t.shx b/examples/shared/t.shx new file mode 100755 index 000000000..a16d32131 --- /dev/null +++ b/examples/shared/t.shx @@ -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