This patch makes it possible to compile ngspice as a library for wasm target.
First, download emsdk. Using the sdk install the latest target, as well as well as binaryen.
To compile ngspice library, run:
emconfigure ./configure --with-ngshared --disable-debug emmake make
This will produce src/.libs/libngspice.so.0.0.0
. Despite what the extension would suggest, this is not a shared object, but a wasm static library that can be statically linked to other wasm code.
// sim.cpp #include <stdio.h> #include <string.h> #include <stdlib.h> #include "sharedspice.h" using namespace std; int recieve_char(char * str, int id, void* p){ printf("recieved %s\n", str); return 0; } int recieve_stat(char* status, int id, void* p){ printf("status: %s\n", status); return 0; } int ngexit(int status, bool unload, bool exit, int id, void* p){ printf("exit: %d\n", status); return 0; } int recieve_data(vecvaluesall* data, int numstructs, int id, void* p){ printf("data recieved: %f\n", data->vecsa[0]->creal); return 0; } int recieve_init_data(vecinfoall* data, int id, void* p){ printf("init data recieved from: %d\n", id); return 0; } int ngrunning(bool running, int id, void* p){ if(running){ printf("ng is running\n"); }else{ printf("ng is not running\n"); } return 0; } int main(){ ngSpice_Init(&recieve_char, &recieve_stat, &ngexit, &recieve_data, &recieve_init_data, &ngrunning, (void*) NULL); char** circarray = (char**)malloc(sizeof(char*) * 7); circarray[0] = strdup("test array"); circarray[1] = strdup("V1 1 0 1"); circarray[2] = strdup("R1 1 2 1"); circarray[3] = strdup("C1 2 0 1 ic=0"); circarray[4] = strdup(".tran 10u 3 uic"); circarray[5] = strdup(".end"); circarray[6] = NULL; ngSpice_Circ(circarray); ngSpice_Command("run"); return 0; }
To complile, run:
em++ -o sim.html sim.cpp lib/libngspice.so.0.0.0
where libngspice.so.0.0.0
is the library produced in the previous step.
frontend/outitf.c
: Signatures of sh_vecinit
, didn't match up, leading to a runtime error.
main.c
: for some reason even when compiling with flag --with-ngshared
, the library file contained the main function, leading to a duplicate symbol error, so the main function needed to be removed by macros.
misc/misc_time.c
: getrusage
doesn't work properly with wasm/emscripten. It runs into an error with memset, but even if it worked, it would only return fake values. FYI, this is how it is implemented:
function ___sys_getrusage(who, usage) {try { _memset(usage, 0, 136); HEAP32[((usage)>>2)]=1; // fake some values HEAP32[(((usage)+(4))>>2)]=2; HEAP32[(((usage)+(8))>>2)]=3; HEAP32[(((usage)+(12))>>2)]=4; return 0; } catch (e) { if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e); return -e.errno; } }
sharedspice.c
: wasm couldn't possibly access user configuration files, so that section had to be removed by a macro.
branch pre-master:
sh_vecinit is fixed
compilation of main.c is suppressed, when SHARED_MODULE is set
getrusage is used at other places in the code as well. It might be better to not set HAVE_GETRUSAGE already during ./configure , that is we might need some entry in configure.ac. Is there an alternative time information available?
There is a ngspice install time configuration file spinit. Is this read? It is used to enable the XSPICE code models. If not available, the commands to load the code models may be sent via the interface, e.g. as
codemodel <path>/analog.com
Code models may be required when applying standard OpAmp subcircuit models containing poly source statements.
The user defined initialization files may contain commands like
set ngbehavior=ps
which is required when PSPICE OpAmp models are to bew used. This command may be sent via the interface as well.
Your patch does not show any additions/changes to configure.ac. Should we declare emscripten as a target of its own?
The shared library doesn't seem to work. I have followed this here:
https://sourceforge.net/p/ngspice/patches/99/
Last edit: Danial Chitnis 2020-10-19