Merge branch 'pre-master-42' into bt_dev

This commit is contained in:
Brian Taylor 2023-12-01 12:01:56 -08:00
commit df38aa24ce
77 changed files with 2514 additions and 229 deletions

View File

@ -16,7 +16,8 @@
# external OpenVAF Verilig-A compiler, will allow access to advanced compact
# device models writen in Verilog-A.
# Please see the ngspice manual, chapt. 13, for more info on using OSDI/OpenVAF.
# CIDER, XSPICE, and OpenMP may be selected at will.
# --enable-klu will add the new matrix solver in addition to Sparse 1.3.
# CIDER, XSPICE, KLU, and OpenMP may be selected at will.
# --disable-debug will give O2 optimization (versus O0 for debug) and removes all debugging info.
# ngspice as shared library:
@ -48,13 +49,13 @@ if test "$1" = "d"; then
if [ $? -ne 0 ]; then echo "cd debug failed"; exit 1 ; fi
echo "configuring for 64 bit debug"
echo
../configure --with-x --enable-xspice --enable-cider --enable-predictor --enable-osdi --with-readline=yes --enable-openmp CFLAGS="-g -m64 -O0 -Wall -Wno-unused-but-set-variable" LDFLAGS="-m64 -g"
../configure --with-x --enable-xspice --enable-cider --enable-predictor --enable-osdi --enable-klu --with-readline=yes --enable-openmp --prefix="/usr/local" --libdir="/usr/local/lib" CFLAGS="-g -m64 -O0 -Wall -Wno-unused-but-set-variable" LDFLAGS="-m64 -g"
else
cd release
if [ $? -ne 0 ]; then echo "cd release failed"; exit 1 ; fi
echo "configuring for 64 bit release"
echo
../configure --with-x --enable-xspice --enable-cider --enable-predictor --enable-osdi --with-readline=yes --enable-openmp --disable-debug CFLAGS="-m64 -O2" LDFLAGS="-m64 -s"
../configure --with-x --enable-xspice --enable-cider --enable-predictor --enable-osdi --enable-klu --with-readline=yes --enable-openmp --disable-debug --prefix="/usr/local" --libdir="/usr/local/lib" CFLAGS="-m64 -O2" LDFLAGS="-m64 -s"
fi
if [ $? -ne 0 ]; then echo "../configure failed"; exit 1 ; fi

View File

@ -11,7 +11,7 @@
# --enable-osdi will add the osdi interface which allows to dynamically load compiled Verilog-A
# compact models. Compiling the VA code of the models is done by the OpenVAF compiler.
# Please see the ngspice manual, chapt. 13, for more info on OSDI/OpenVAF.
# CIDER, XSPICE, and OpenMP may be selected at will.
# CIDER, XSPICE, KLU, and OpenMP may be selected at will.
# --disable-debug will give O2 optimization (versus O0 for debug) and removes all debugging info.
# Add (optionally) --enable-relpath to avoid absolute paths when searching for code models.
@ -44,7 +44,7 @@ if test "$1" = "d"; then
echo
# The --prefix (and perhaps --libdir) may be used to determine a different install location
# (depending on the Linux distribution, and on the calling programs search path).
../configure --with-ngshared --enable-xspice --enable-cider --enable-openmp --enable-osdi --prefix=/usr CFLAGS="-g -m64 -O0 -Wall" LDFLAGS="-m64 -g"
../configure --with-ngshared --enable-xspice --enable-cider --enable-openmp --enable-osdi --enable-klu --prefix=/usr CFLAGS="-g -m64 -O0 -Wall" LDFLAGS="-m64 -g"
else
cd releasesh
if [ $? -ne 0 ]; then echo "cd releasesh failed"; exit 1 ; fi
@ -52,7 +52,7 @@ else
echo
# The --prefix (and perhaps --libdir) may be used to determine a different install location
# (depending on the Linux distribution, and on the calling programs search path).
../configure --with-ngshared --enable-xspice --enable-cider --enable-openmp --disable-debug --enable-osdi --prefix=/usr CFLAGS="-m64 -O2" LDFLAGS="-m64 -s"
../configure --with-ngshared --enable-xspice --enable-cider --enable-openmp --enable-osdi --enable-klu --disable-debug --prefix=/usr CFLAGS="-m64 -O2" LDFLAGS="-m64 -s"
fi
if [ $? -ne 0 ]; then echo "../configure failed"; exit 1 ; fi

View File

@ -10,10 +10,8 @@
# for debug version of shared ngspice
# Options:
# --adms and --enable-adms will install extra HICUM, EKV and MEXTRAM models via the
# adms interface.
# Please see http://ngspice.sourceforge.net/admshowto.html for more info on adms.
# CIDER, XSPICE, and OpenMP may be selected at will.
# CIDER, XSPICE, KLU, and OpenMP may be selected at will.
# --disable-debug will give O2 optimization (versus O0 for debug) and removes all debugging info.
# To obtain a 32 bit executable, replace -m64 by -m32 ./configure lines.
@ -38,27 +36,19 @@ fi
./autogen.sh
if [ $? -ne 0 ]; then echo "./autogen.sh failed"; exit 1 ; fi
# Alternatively, if compiling sources from git, and want to add adms created devices,
# you may need to uncomment the following two lines (and don't forget to add adms option
# to the ../configure statement):
#./autogen.sh --adms
#if [ $? -ne 0 ]; then echo "./autogen.sh failed"; exit 1 ; fi
echo
if test "$1" = "d"; then
cd debug-sh
if [ $? -ne 0 ]; then echo "cd debug-sh failed"; exit 1 ; fi
echo "configuring for 64 bit debug"
echo
# You may add --enable-adms to the following command for adding adms generated devices
../configure --with-ngshared --enable-xspice --enable-cider --enable-openmp --enable-osdi --enable-relpath --disable-debug prefix="C:/Spice64d" CFLAGS="-m64 -g -O0 -Wall" LDFLAGS="-m64"
../configure --with-ngshared --enable-xspice --enable-cider --enable-openmp --enable-osdi --enable-klu --enable-relpath prefix="C:/Spice64d" CFLAGS="-m64 -g -O0 -Wall" LDFLAGS="-m64"
else
cd release-sh
if [ $? -ne 0 ]; then echo "cd release-sh failed"; exit 1 ; fi
echo "configuring for 64 bit release"
echo
# You may add --enable-adms to the following command for adding adms generated devices
../configure --with-ngshared --enable-xspice --enable-cider --enable-openmp --enable-osdi --enable-relpath --disable-debug prefix="C:/Spice64" CFLAGS="-m64 -O2" LDFLAGS="-m64 -s"
../configure --with-ngshared --enable-xspice --enable-cider --enable-openmp --enable-osdi --enable-klu --enable-relpath --disable-debug prefix="C:/Spice64" CFLAGS="-m64 -O2" LDFLAGS="-m64 -s"
fi
if [ $? -ne 0 ]; then echo "../configure failed"; exit 1 ; fi

View File

@ -19,10 +19,10 @@ q4 7 7 9 qnl
rbias 7 8 20k
.model qnl npn(bf=80 rb=100 ccs=2pf tf=0.3ns tr=6ns cje=3pf cjc=2pf
+ va=50)
.print dc v(4) v(5)
.plot dc v(5)
.print ac vm(5) vp(5)
.plot ac vm(5) vp(5)
*.print dc v(4) v(5)
*.plot dc v(5)
*.print ac vm(5) vp(5)
*.plot ac vm(5) vp(5)
.print tran v(4) v(5)
.plot tran v(5)
.end

View File

@ -21,7 +21,7 @@ m11 8 4 0 0 mod w=250u l=5u
m12 9 9 8 0 mod w=5u l=5u
.model mod nmos(vto=0.5 phi=0.7 kp=1.0e-6 gamma=1.83 lambda=0.115
+ level=1 cgso=1u cgdo=1u cbd=50p cbs=50p)
.print dc v(5) v(6)
.plot dc v(6)
*.print dc v(5) v(6)
*.plot dc v(6)
.plot tran v(6) v(5) v(7) v(1) v(2)
.end

View File

@ -0,0 +1,10 @@
The circuit adc.cir in this directory illustrates the use of the d_cosim
XSPICE code model as a container for a Verilog simulation. Before the
simulation can be run, the Verilog code must be compiled by Verilator
using the command:
ngspice vlnggen adc.v
That should create a shared library file, adc.so (or adc.DLL on Windows)
that will be loaded by the d_cosim code model. The compiled Verilog code that
it contains will be executed during simulation.

View File

@ -0,0 +1,103 @@
Simulation of a switched-capacitor SAR ADC with Verilator and d_cosim
.subckt sar_adc input vref start valid d5 d4 d3 d2 d1 d0 clk
* A transmission gate connects the input to the capacitor set.
xsample input iin sample vref tgate
rin iin test_v 1k
* Capacitors and controlling inverters
xb5 test_v vref d5 ccap c=1p
xb4 test_v vref d4 ccap c={1p / 2}
xb3 test_v vref d3 ccap c={1p / 4}
xb2 test_v vref d2 ccap c={1p / 8}
xb1 test_v vref d1 ccap c={1p / 16}
xb0 test_v vref d0 ccap c={1p / 32}
clast test_v 0 {1p / 32}
* An XSPICE ADC bridge functions as a comparator.
acomp [%vd(test_v vref)] [comp] comparator
.model comparator adc_bridge in_low=0 in_high=0
* The digital portion of the circuit is specified in compiled Verilog.
* Outputs inverted to cancel the inverter in subcircuit ccap,
* and produce the correct numerical output value.
adut [ Clk Comp Start] [Sample Valid ~d5 ~d4 ~d3 ~d2 ~d1 ~d0] null dut
.model dut d_cosim simulation="./adc.so"
.ends // SUBCKT sar_adc
* Some MOS transistors complete the circuit.
* Models from https://homepages.rpi.edu/~sawyes/AIMSPICE_TutorialManual.pdf
.model p1 pmos
+ level=2 vto=-0.5 kp=8.5e-6 gamma=0.4 phi=0.65 lambda=0.05 xj=0.5e-6
.model n1 nmos
+ level=2 vto=0.5 kp=24e-6 gamma=0.15 phi=0.65 lambda=0.015 xj=0.5e-6
* Use those for an inverter.
.subckt ainv in out vdd
mn out in 0 0 n1
mp out in vdd vdd p1
.ends
* A transmission gate modelled by a switch.
.subckt mos_tgate a b ctl vdd
mn a ctl b b n1
xinv ctl ictl vdd ainv
mp b ictl a a p1
.ends
.subckt tgate a b ctl vdd
switch a b ctl 0 tg
.model tg sw vt=1.5 ron=2k
.ends
* The per-bit subcircuit in the adc
.subckt ccap in vcc ctl c=10p
xinv ctl tail vcc ainv
cb in tail {c}
.ends
**** End of the ADC and its subcircuits. Begin test circuit ****
.param vcc=3.3
vcc vcc 0 {vcc}
* Digital clock signal
aclock 0 clk clock
.model clock d_osc cntl_array=[-1 1] freq_array=[1Meg 1Meg]
* A simple DAC so that the result may be compared to the input.
r5 d5 sum 2
r4 d4 sum 4
r3 d3 sum 8
r2 d2 sum 16
r1 d1 sum 32
r0 d0 sum 64
vamm sum 0 0
* Pulse the Start signal high for 1.3uS each 10uS
Vpulse Start 0 PULSE 0 {vcc} 0.2u 10n 10n 1.3u 10u
Vtest input 0 PULSE 0 3 0 200u 200u 1u 401u
* The ADC for testing
xtest input vcc start valid d5 d4 d3 d2 d1 d0 clk sar_adc
.control
tran 100n 250u
plot input xtest.test_v vamm#branch clk/2 start/3 xtest.sample/3 valid
.endc
.end

View File

@ -0,0 +1,45 @@
// Digital control for a successive approximation ADC with switched capacitors.
module adc(Clk, Comp, Start, Sample, Done, Result);
input wire Clk, Comp, Start;
output reg Sample, Done;
output reg [Bits - 1 : 0] Result;
parameter Bits=6;
reg [Bits - 1 : 0] SR;
reg Running;
initial begin
$display("ADC simulation starting");
Done = 0;
Sample = 0;
Result = 0;
Running = 0;
end
always @(posedge(Clk)) begin
if (Running) begin
if (Sample) begin
Sample <= 0;
SR[Bits - 1] = 1;
Result[Bits - 1] = 1;
end else if (SR != 0) begin
if (Comp)
Result &= ~SR;
SR >>= 1;
Result |= SR;
if (SR == 0) begin
Running <= 0;
Done <= 1;
end
end
end else if (Start) begin
Running <= 1;
Sample <= 1;
Done <= 0;
SR = 0;
Result = 0;
end
end
endmodule

View File

@ -34,6 +34,8 @@ libfte_la_SOURCES = \
com_dump.h \
com_echo.c \
com_echo.h \
com_fileio.c \
com_fileio.h \
com_ghelp.c \
com_ghelp.h \
com_gnuplot.h \

181
src/frontend/com_fileio.c Normal file
View File

@ -0,0 +1,181 @@
/* Commands for opening and reading arbitrary files. */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "ngspice/ngspice.h"
#include "ngspice/bool.h"
#include "ngspice/wordlist.h"
#include "com_strcmp.h"
#include "variable.h"
/* Track open files with these structures, indexed by the underlying
* descriptor. Not many should be needed.
*/
#define MAX_OPEN_FILES 20
#define MAX_TEXT_LINE 8192
static struct {
FILE *fp;
char *name;
} Open_Files[MAX_OPEN_FILES];
/* Check whether error messages should be suppressed. That is useful when
* opening a file to see if it exists.
*/
static int verbose(void)
{
return !cp_getvar("silent_fileio", CP_BOOL, NULL, 0);
}
/* fopen handle file_name [mode]
*
* For example: fopen handle result.txt r
*
* The underlying file descriptor (or -1) is returned in the variable "handle".
*/
void com_fopen(wordlist *wl)
{
char *var, *file_name, *mode;
FILE *fp;
int fd;
var = wl->wl_word;
wl = wl->wl_next;
file_name = cp_unquote(wl->wl_word);
wl = wl->wl_next;
mode = wl ? cp_unquote(wl->wl_word) : "r";
fp = fopen(file_name, mode);
if (fp) {
fd = fileno(fp);
if (fd < MAX_OPEN_FILES) {
if (Open_Files[fd].fp) // Not expected!
fclose(Open_Files[fd].fp);
if (Open_Files[fd].name)
tfree(Open_Files[fd].name);
Open_Files[fd].fp = fp;
Open_Files[fd].name = copy(file_name);
} else {
fclose(fp);
fprintf(stderr,
"com_fopen() cannot open %s: too many open files\n",
file_name);
fd = -1;
}
} else {
fd = -1;
if (verbose()) {
fprintf(stderr, "com_fopen() cannot open %s: %s\n",
file_name, strerror(errno));
}
}
tfree(file_name);
if (wl)
tfree(mode);
cp_vset(var, CP_NUM, &fd);
}
/* Command looks like:
* fread result handle [length]
* where handle is a small positive integer, result names a variable
* and length is the name of a variable used to return the length of the line.
* The returned length is -1 at EOF, -2 on failure.
*/
void com_fread(wordlist *wl)
{
char *handle, *result, *lvar;
int fd, length;
char buf[MAX_TEXT_LINE];
result = cp_unquote(wl->wl_word);
wl = wl->wl_next;
handle = cp_unquote(wl->wl_word);
fd = atoi(handle);
tfree(handle);
wl = wl->wl_next;
if (wl)
lvar = cp_unquote(wl->wl_word);
else
lvar = NULL;
if (fd >= 0 && fd < MAX_OPEN_FILES) {
if (!Open_Files[fd].fp) {
/* Allow stdin, for example. */
Open_Files[fd].fp = fdopen(fd, "r");
if (!Open_Files[fd].fp && verbose()) {
fprintf(stderr, "com_fread() cannot open handle %d\n", fd);
goto err;
}
}
if (fgets(buf, sizeof buf, Open_Files[fd].fp)) {
length = strlen(buf);
if (length > 0 && buf[length - 1] == '\n') {
--length;
if (length > 0 && buf[length - 1] == '\r') {
/* Windows CRLF line termination. */
--length;
}
buf[length] = '\0';
} else if (verbose()) {
fprintf(stderr,
"com_fread() found line in %s "
"too long for buffer\n",
Open_Files[fd].name);
}
} else {
if (feof(Open_Files[fd].fp)) {
length = -1;
} else if (verbose()) {
fprintf(stderr,
"com_fread() error reading %s: %s\n",
Open_Files[fd].name, strerror(errno));
length = -2;
}
*buf = '\0';
}
} else if (verbose()) {
fprintf(stderr,
"com_fread(): file handle %d is not in accepted range.\n",
fd);
err:
length = -1;
*buf = '\0';
}
cp_vset(result, CP_STRING, buf);
tfree(result);
if (lvar) {
cp_vset(lvar, CP_NUM, &length);
tfree(lvar);
}
}
void com_fclose(wordlist *wl)
{
char *handle;
int fd;
handle = cp_unquote(wl->wl_word);
fd = atoi(handle);
tfree(handle);
if (fd <= 2 || fd >= MAX_OPEN_FILES)
return;
if (Open_Files[fd].fp) {
fclose(Open_Files[fd].fp);
Open_Files[fd].fp = NULL;
}
if (Open_Files[fd].name)
tfree(Open_Files[fd].name);
}

View File

@ -0,0 +1,9 @@
#ifndef ngspice_COM_FILEIO_H
#define ngspice_COM_FILEIO_H
extern void com_fopen(wordlist *wl);
extern void com_fread(wordlist *wl);
extern void com_fclose(wordlist *wl);
#endif

View File

@ -400,6 +400,11 @@ com_measure_when(
return MEASUREMENT_FAILURE;
}
if (dScale->v_realdata ==NULL && dScale->v_compdata == NULL) {
fprintf(cp_err, "Error: scale vector time, frequency or dc has no data.\n");
return MEASUREMENT_FAILURE;
}
prevValue = 0.;
prevValue2 = 0.;
prevScaleValue = 0.;
@ -658,6 +663,11 @@ measure_at(
return MEASUREMENT_FAILURE;
}
if (dScale->v_realdata == NULL && dScale->v_compdata == NULL) {
fprintf(cp_err, "Error: scale vector time, frequency or dc has no data.\n");
return MEASUREMENT_FAILURE;
}
/* -----------------------------------------------------------------
* Take the string tests outside of the loop for speed.
* ----------------------------------------------------------------- */
@ -768,7 +778,12 @@ measure_minMaxAvg(
}
if (dScale == NULL) {
fprintf(cp_err, "Error: no such vector as time, frquency or v-sweep.\n");
fprintf(cp_err, "Error: no such vector as time, frequency or v-sweep.\n");
return MEASUREMENT_FAILURE;
}
if (dScale->v_realdata == NULL && dScale->v_compdata == NULL) {
fprintf(cp_err, "Error: scale vector time, frequency or v-sweep has no data.\n");
return MEASUREMENT_FAILURE;
}
@ -944,7 +959,12 @@ measure_rms_integral(
}
if (xScale == NULL) {
fprintf(cp_err, "Error: no such vector as time.\n");
fprintf(cp_err, "Error: no such vector as time, frequency or v-sweep.\n");
return MEASUREMENT_FAILURE;
}
if (xScale->v_realdata == NULL && xScale->v_compdata == NULL) {
fprintf(cp_err, "Error: scale vector time, frequency or v-sweep has no data.\n");
return MEASUREMENT_FAILURE;
}

View File

@ -23,3 +23,54 @@ com_strcmp(wordlist *wl)
tfree(s2);
cp_vset(var, CP_NUM, &i);
}
/* These must be more evil still. */
void com_strstr(wordlist *wl)
{
char *var, *s1, *s2;
int i;
s1 = cp_unquote(wl->wl_next->wl_word);
s2 = cp_unquote(wl->wl_next->wl_next->wl_word);
if (*s2) {
var = strstr(s1, s2); // Search for s2 in s1
if (var)
i = var - s1; // Offset to match
else
i = -1;
} else {
i = strlen(s1); // Length
}
tfree(s1);
tfree(s2);
cp_vset(wl->wl_word, CP_NUM, &i);
}
void com_strslice(wordlist *wl)
{
char *var, *s1, *tp, tmp;
int offset, length, actual;
var = wl->wl_word;
wl = wl->wl_next;
s1 = cp_unquote(wl->wl_word);
wl = wl->wl_next;
offset = atoi(wl->wl_word);
length = atoi(wl->wl_next->wl_word);
actual = strlen(s1);
if (offset < 0)
offset = actual + offset;
if (length + offset > actual)
length = actual - offset;
if (length > 0 && offset >= 0) {
tp = s1 + offset + length;
tmp = *tp;
*tp = '\0';
cp_vset(var, CP_STRING, s1 + offset);
*tp = tmp;
} else {
cp_vset(var, CP_STRING, "");
}
tfree(s1);
}

View File

@ -1,7 +1,8 @@
#ifndef ngspice_COM_STRCMP_H
#define ngspice_COM_STRCMP_H
void com_strcmp(wordlist *wl);
extern void com_strcmp(wordlist *wl);
extern void com_strstr(wordlist *wl);
extern void com_strslice(wordlist *wl);
#endif

View File

@ -82,6 +82,7 @@
#include "resource.h"
#include "diff.h"
#include "com_strcmp.h"
#include "com_fileio.h"
#include "ngspice/randnumb.h"
#include "../spicelib/analysis/com_optran.h"
#include "com_wr_ic.h"
@ -613,6 +614,30 @@ struct comm spcp_coms[] = {
{ 0, 0, 0, 0 }, E_DEFHMASK, 3, 3,
NULL,
"varname s1 s2 : Set $varname to strcmp(s1, s2)." } ,
{ "strstr", com_strstr, FALSE, FALSE,
{ 0, 0, 0, 0 }, E_DEFHMASK, 3, 3,
NULL,
"varname s1 s2 : Set $varname to strstr(s1, s2)." } ,
{ "strslice", com_strslice, FALSE, FALSE,
{ 0, 0, 0, 0 }, E_DEFHMASK, 4, 4,
NULL,
"varname s1 offset length : "
"Set $varname to s1[offset ... offset+length]" } ,
{ "fopen", com_fopen, FALSE, FALSE,
{ 0, 0, 0, 0 }, E_DEFHMASK, 2, 3,
NULL,
"handle file_name [mode] : "
"Open file_name with mode, return handle in $handle" } ,
{ "fread", com_fread, FALSE, FALSE,
{ 0, 0, 0, 0 }, E_DEFHMASK, 2, 3,
NULL,
"handle result [length] : "
"Read a line from open file handle, "
"data in $result, status in $length" } ,
{ "fclose", com_fclose, FALSE, FALSE,
{ 0, 0, 0, 0 }, E_DEFHMASK, 1, 1,
NULL,
"handle : Close open file" } ,
{ "linearize", com_linearize, FALSE, FALSE,
{ 040000, 040000, 040000, 040000 }, E_DEFHMASK, 0, LOTS,
NULL,
@ -1033,6 +1058,30 @@ struct comm nutcp_coms[] = {
{ 0, 0, 0, 0 }, E_DEFHMASK, 3, 3,
NULL,
"varname s1 s2 : Set $varname to strcmp(s1, s2)." } ,
{ "strstr", com_strstr, FALSE, FALSE,
{ 0, 0, 0, 0 }, E_DEFHMASK, 3, 3,
NULL,
"varname s1 s2 : Set $varname to strstr(s1, s2)." } ,
{ "strslice", com_strslice, FALSE, FALSE,
{ 0, 0, 0, 0 }, E_DEFHMASK, 4, 4,
NULL,
"varname s1 offset length : "
"Set $varname to s1[offset ... offset+length]" } ,
{ "fopen", com_fopen, FALSE, FALSE,
{ 0, 0, 0, 0 }, E_DEFHMASK, 2, 3,
NULL,
"handle file_name [mode] : "
"Open file_name with mode, return handle in $handle" } ,
{ "fread", com_fread, FALSE, FALSE,
{ 0, 0, 0, 0 }, E_DEFHMASK, 2, 3,
NULL,
"handle result [length] : "
"Read a line from open file handle, "
"data in $result, status in $length" } ,
{ "fclose", com_fclose, FALSE, FALSE,
{ 0, 0, 0, 0 }, E_DEFHMASK, 1, 1,
NULL,
"handle : Close open file" } ,
{ "linearize", com_linearize, TRUE, FALSE,
{ 040000, 040000, 040000, 040000 }, E_DEFHMASK, 0, LOTS,
NULL,

View File

@ -1,6 +1,54 @@
#if defined(__MINGW32__) || defined(_MSC_VER)
#include <windows.h>
#endif
#include "ngspice/ngspice.h"
#include "ngspice/dvec.h"
#if defined SHARED_MODULE
/*Use Windows threads if on W32 without pthreads*/
#ifndef HAVE_LIBPTHREAD
#if defined(__MINGW32__) || defined(_MSC_VER)
//#if defined(_MSC_VER)
#ifdef SRW
#define mutex_lock(a) AcquireSRWLockExclusive(a)
#define mutex_unlock(a) ReleaseSRWLockExclusive(a)
typedef SRWLOCK mutexType;
#else
#define mutex_lock(a) EnterCriticalSection(a)
#define mutex_unlock(a) LeaveCriticalSection(a)
typedef CRITICAL_SECTION mutexType;
#endif
#define thread_self() GetCurrentThread()
#define threadid_self() GetCurrentThreadId()
typedef HANDLE threadId_t;
#define WIN_THREADS
#define THREADS
#endif
#else
#include <pthread.h>
#define mutex_lock(a) pthread_mutex_lock(a)
#define mutex_unlock(a) pthread_mutex_unlock(a)
#define thread_self() pthread_self()
#define threadid_self() 0 //FIXME t.b.d.
typedef pthread_mutex_t mutexType;
typedef pthread_t threadId_t;
#define THREADS
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static bool cont_condition;
#endif
extern mutexType vecreallocMutex;
#endif
struct dvec *dvec_alloc(/* NOT const -- assigned to char */ char *name,
int type, short flags, int length, void *storage)
@ -58,7 +106,7 @@ struct dvec *dvec_alloc(/* NOT const -- assigned to char */ char *name,
} /* end of function dvec_alloc */
/* Resize dvec to length if storage is NULL orr replace
/* Resize dvec to length if storage is NULL or replace
* its existing allocation with storage if not
*/
void dvec_realloc(struct dvec *v, int length, void *storage)
@ -86,17 +134,26 @@ void dvec_realloc(struct dvec *v, int length, void *storage)
v->v_alloc_length = length;
} /* end of function dvec_realloc */
/* called from plotAddReal(Complex)Value, to increase
storage for result vectors.
In shared ngspice this may be locked, e.g. during plotting in the primary
thread, while the simulation is running in the background thread. Locking and unlocking
is done by API functions ngSpice_LockRealloc(), ngSpice_UnlockRealloc(). */
void dvec_extend(struct dvec *v, int length)
{
#if defined SHARED_MODULE
mutex_lock(&vecreallocMutex);
#endif
if (isreal(v)) {
v->v_realdata = TREALLOC(double, v->v_realdata, length);
}
else {
v->v_compdata = TREALLOC(ngcomplex_t, v->v_compdata, length);
}
v->v_alloc_length = length;
#if defined SHARED_MODULE
mutex_unlock(&vecreallocMutex);
#endif
} /* end of function dvec_extend */

View File

@ -32,6 +32,7 @@ Author: 1985 Wayne A. Christopher
#include "subckt.h"
#include "spiceif.h"
#include "com_let.h"
#include "com_set.h"
#include "com_commands.h"
#ifdef XSPICE
@ -57,8 +58,9 @@ static wordlist *inp_savecurrents(struct card *deck, struct card *options,
wordlist *wl, wordlist *controls);
static void recifeval(struct card *pdeck);
static char *upper(register char *string);
#ifdef REM_UNUSED
static void rem_unused_mos_models(struct card* deck);
#endif
extern void com_optran(wordlist * wl);
extern void tprint(struct card *deck);
@ -88,6 +90,17 @@ extern void exec_controls(wordlist *controls);
extern void SetSource(char *Name);
#endif
#if defined (_MSC_VER)
typedef struct timeval {
long tv_sec;
long tv_usec;
} timeval;
#endif
#if defined (_MSC_VER) || defined (__MINGW32__)
extern int gettimeofday(struct timeval* tp, void* unused);
#endif
/* structure used to save expression parse trees for .model and
* device instance lines
*/
@ -100,6 +113,7 @@ struct pt_temper {
struct pt_temper *next;
};
static int inp_parse_temper(struct card *deck,
struct pt_temper **motdlist_p,
struct pt_temper **devtlist_p);
@ -438,9 +452,10 @@ eval_opt(struct card* deck)
char* token = gettok(&begtok);
/* option seed=random [seed='random'] */
if (eq(token, "random") || eq(token, "{random}")) {
time_t acttime = time(NULL);
/* get random value from time in seconds since 1.1.1970 */
int rseed = (int)(acttime - 1600000000);
struct timeval tv;
gettimeofday(&tv, NULL);
/* get random value from current timestamp microseconds */
int rseed = (int)(tv.tv_usec);
cp_vset("rndseed", CP_NUM, &rseed);
com_sseed(NULL);
has_seed = TRUE;
@ -623,10 +638,71 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile)
* the cards .control and .endc, unless comfile is TRUE, in which
* case every line must be a front-end command. There are too
* many problems with matching the first word on the line. */
ld = deck;
if (comfile) {
bool with_params = FALSE;
#ifndef SHARED_MODULE
if (ciprefix("*ng_script_with_params", deck->line)) {
extern char **Copy_of_argv; // main.c
extern int optind; // Library function getopt()
static const char header[] = "argc = %u argv = ( ";
wordlist *setarg;
unsigned int argc, size;
char *p_buf_active; /* buffer in use */
char buf[BSIZE_SP];
/* Not just a command script, but one requesting arguments
* from the program's command line.
* This is similar to cp_oddcomm() (cpitf.c),
* but arguments are taken from the original program command.
*/
with_params = TRUE;
size = sizeof header + 10; // Allow for %u and close.
for (argc = 0; Copy_of_argv[optind + argc]; ++argc)
size += strlen(Copy_of_argv[optind + argc]);
size += 3 * argc; // Spaces and quotes.
if (size <= sizeof buf)
p_buf_active = buf;
else
p_buf_active = TMALLOC(char, size);
/* Fill the buffer. */
size = sprintf(p_buf_active, header, argc);
while (Copy_of_argv[optind]) {
char c, *fmt;
c = Copy_of_argv[optind][0];
if ((c >= '0' && c <= '9') || c == '+' || c == '-' || !c) {
/* Looks like a number or empty string - quote it. */
fmt = " \"%s\"";
} else {
fmt = " %s";
}
size += sprintf(p_buf_active + size, fmt,
Copy_of_argv[optind++]);
}
strcpy(p_buf_active + size, " )");
/* Treat the buffer as a "set" command to create argv and argc. */
setarg = cp_lexer(p_buf_active);
com_set(setarg);
wl_free(setarg);
/* Free buffer allocation if made */
if (p_buf_active != buf)
txfree(p_buf_active);
}
#endif
/* Process each command, except 'option' which is assembled
in a list and ingnored here */
for (dd = deck; dd; dd = ld) {
ld = dd->nextcard;
if ((dd->line[0] == '*') && (dd->line[1] != '#'))
@ -641,6 +717,12 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile)
cp_evloop(dd->line);
}
}
if (with_params) {
cp_remvar("argc");
cp_remvar("argv");
}
/* free the control deck */
line_free(deck, TRUE);
/* set to NULL to allow generation of a new dbs */
@ -929,10 +1011,11 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile)
}
/* merge the two option line structs
com_options (comfile == TRUE, filled in from spinit, .spiceinit, and *ng_sript), and
options (comfile == FALSE, filled in from circuit with .OPTIONS)
com_options (comfile == TRUE, filled in from spinit,
.spiceinit, and *ng_script), and options
(comfile == FALSE, filled in from circuit with .OPTIONS)
into options, thus keeping com_options,
options is loaded into circuit and freed when circuit is removed */
options is loaded into circuit and freed when circuit is removed */
options = line_reverse(line_nconc(options, inp_deckcopy(com_options)));
/* List of all expressions found in instance and .model lines */
@ -955,14 +1038,14 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile)
/* If user wants all currents saved (.options savecurrents), add .save
to wl_first with all terminal currents available on selected devices */
wl_first = inp_savecurrents(deck, options, wl_first, controls);
#ifdef REM_UNUSED
/* Circuit is flat, all numbers expanded.
So again try to remove unused MOS models.
All binning models are still here when w or l have been
determined by an expression. */
// if (newcompat.hs || newcompat.spe)
// rem_unused_mos_models(deck->nextcard);
if (newcompat.hs || newcompat.spe)
rem_unused_mos_models(deck->nextcard);
#endif
/* now load deck into ft_curckt -- the current circuit. */
if(inp_dodeck(deck, tt, wl_first, FALSE, options, filename) != 0)
return 1;
@ -2541,6 +2624,7 @@ struct mlist {
bool checked;
};
#ifdef REM_UNUSED
/* Finally get rid of unused MOS models */
static void rem_unused_mos_models(struct card* deck) {
struct card *tmpc, *tmppc = NULL;
@ -2744,3 +2828,4 @@ static void rem_unused_mos_models(struct card* deck) {
modellist = tlist;
}
}
#endif

View File

@ -584,6 +584,7 @@ static void delete_names(struct names *p)
}
#ifndef _MSC_VER
#ifdef CIDER
/* concatenate 2 strings, with space if spa == TRUE,
return malloced string (replacement for tprintf,
which is not efficient enough when reading PDKs
@ -613,6 +614,7 @@ static char *cat2strings(char *s1, char *s2, bool spa)
return strsum;
}
#endif
#endif
/* line1
@ -1589,8 +1591,11 @@ struct inp_read_t inp_read( FILE *fp, int call_depth, const char *dir_name,
/* add Inp_Path to buffer while keeping the sourcepath variable contents */
if (ciprefix("set", buffer)) {
char *p = strstr(buffer, "sourcepath");
if (p) {
char *p;
p = skip_ws(buffer + 3); // Next word
if (strncmp(p, "sourcepath", 10) == 0 &&
skip_non_ws(p) == p + 10) {
p = strchr(buffer, ')');
if (p) {
*p = 0; // clear ) and insert Inp_Path in between
@ -3108,7 +3113,8 @@ void inp_casefix(char *string)
/* Special treatment of code model file input. */
if (ciprefix(".model", string))
tmpstr = strstr(string, "file=");
tmpstr = strstr(string, "file=\"");
#endif
keepquotes = ciprefix(".param", string); // Allow string params
@ -3184,8 +3190,6 @@ static void inp_stripcomments_deck(struct card *c, bool cf)
If there is only white space before the end-of-line comment the
the whole line is converted to a normal comment line (i.e. one that
begins with a '*').
BUG: comment characters in side of string literals are not ignored
('$' outside of .control section is o.k. however).
If the comaptibility mode is PS, LTPS or LTPSA, '$' is treated as a valid
character, not as end-of-line comment delimiter, except for that it is
@ -3202,6 +3206,24 @@ static void inp_stripcomments_line(char *s, bool cs)
/* look for comments */
while ((c = *d) != '\0') {
d++;
/* Skip over single or double-quoted strings. */
if (c == '"') {
while ((c = *d) && (c != '"' || d[-1] == '\\'))
++d;
if (c)
++d;
continue;
}
if (c == '\'') {
while ((c = *d) && (c != '\'' || d[-1] == '\\'))
++d;
if (c)
++d;
continue;
}
if (*d == ';') {
break;
}
@ -3310,7 +3332,8 @@ static char *inp_fix_subckt(struct names *subckt_w_params, char *s)
char *equal, *beg, *buffer, *ptr1, *ptr2, *new_str;
equal = strchr(s, '=');
if (equal && !strstr(s, "params:")) {
if (equal &&
(!strstr(s, "params:") || !isspace_c(s[-1]))) {
/* get subckt name (ptr1 will point to name) */
ptr1 = skip_non_ws(s);
ptr1 = skip_ws(ptr1);
@ -3481,7 +3504,7 @@ static void inp_fix_for_numparam(
if (ciprefix(".subckt", c->line) || ciprefix("x", c->line)) {
/* remove params: */
char *str_ptr = strstr(c->line, "params:");
if (str_ptr)
if (str_ptr && isspace_c(str_ptr[-1]))
memcpy(str_ptr, " ", 7);
}
@ -3782,13 +3805,14 @@ static int inp_fix_subckt_multiplier(struct names *subckt_w_params,
char *subckt_param_names[], char *subckt_param_values[])
{
struct card *card;
char *new_str;
char *new_str, *s;
subckt_param_names[num_subckt_params] = copy("m");
subckt_param_values[num_subckt_params] = copy("1");
num_subckt_params++;
if (!strstr(subckt_card->line, "params:")) {
s = strstr(subckt_card->line, "params:");
if (!s || !isspace_c(s[-1])) {
new_str = tprintf("%s params: m=1", subckt_card->line);
add_name(subckt_w_params, get_subckt_model_name(subckt_card->line));
}
@ -7842,6 +7866,9 @@ static void inp_quote_params(struct card *c, struct card *end_c,
{
bool in_control = FALSE;
if (ft_skywaterpdk)
return;
for (; c && c != end_c; c = c->nextcard) {
int i, j, num_terminals;
@ -8333,6 +8360,7 @@ static void inp_check_syntax(struct card *deck)
bool mwarn = FALSE;
char* subs[10]; /* store subckt lines */
int ends = 0; /* store .ends line numbers */
static bool nesting_once = TRUE;
/* prevent crash in inp.c, fcn inp_spsource: */
if (ciprefix(".param", deck->line) || ciprefix(".meas", deck->line)) {
@ -8405,10 +8433,12 @@ static void inp_check_syntax(struct card *deck)
}
}
// nesting may be critical if params are involved
if (check_subs > 0 && strchr(cut_line, '='))
if (nesting_once && check_subs > 0 && strchr(cut_line, '=')) {
fprintf(cp_err,
"\nWarning: Nesting of subcircuits with parameters "
"is only marginally supported!\n\n");
"\nWarning: Nesting of subcircuits with parameters "
"is only marginally supported!\n\n");
nesting_once = FALSE;
}
if (check_subs < 10)
subs[check_subs] = cut_line;
else

View File

@ -23,7 +23,7 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group
bool ft_acctprint = FALSE, ft_noacctprint = FALSE, ft_listprint = FALSE;
bool ft_nodesprint = FALSE, ft_optsprint = FALSE, ft_noinitprint = FALSE;
bool ft_norefprint = FALSE;
bool ft_norefprint = FALSE, ft_skywaterpdk = FALSE;
bool ft_ngdebug = FALSE, ft_nginfo = FALSE, ft_stricterror = FALSE;
static void setdb(char *str);
@ -307,6 +307,8 @@ cp_usrset(struct variable *var, bool isset)
ft_ngdebug = isset;
} else if (eq(var->va_name, "nginfo")) {
ft_nginfo = isset;
} else if (eq(var->va_name, "skywaterpdk")) {
ft_skywaterpdk = isset;
} else if (eq(var->va_name, "noinit")) {
ft_noinitprint = isset;
} else if (eq(var->va_name, "norefvalue")) {

View File

@ -1542,6 +1542,7 @@ InterpFileAdd(runDesc *run, IFvalue *refValue, IFvalue *valuePtr)
newval = (timestep - run->circuit->CKTstep - timeold)/(timenew - timeold) * (valuenew[i] - valueold[i]) + valueold[i];
fileAddRealValue(run->fp, run->binary, newval);
valueold[i] = valuenew[i];
timeold = refValue->rValue;
}
else if (nodata)
/* Just keep the transient output value corresponding to timeold,
@ -1573,6 +1574,7 @@ InterpFileAdd(runDesc *run, IFvalue *refValue, IFvalue *valuePtr)
newval = (timestep - run->circuit->CKTstep - timeold)/(timenew - timeold) * (valuenew[i] - valueold[i]) + valueold[i];
fileAddRealValue(run->fp, run->binary, newval);
valueold[i] = valuenew[i];
timeold = refValue->rValue;
}
else if (nodata)
/* Just keep the transient output value corresponding to timeold,
@ -1705,6 +1707,7 @@ InterpPlotAdd(runDesc *run, IFvalue *refValue, IFvalue *valuePtr)
newval = (timestep - run->circuit->CKTstep - timeold)/(timenew - timeold) * (valuenew[i] - valueold[i]) + valueold[i];
plotAddRealValue(&run->data[i], newval);
valueold[i] = valuenew[i];
timeold = refValue->rValue;
}
else if (nodata)
/* Just keep the transient output value corresponding to timeold,
@ -1727,6 +1730,7 @@ InterpPlotAdd(runDesc *run, IFvalue *refValue, IFvalue *valuePtr)
newval = (timestep - run->circuit->CKTstep - timeold)/(timenew - timeold) * (valuenew[i] - valueold[i]) + valueold[i];
plotAddRealValue(&run->data[i], newval);
valueold[i] = valuenew[i];
timeold = refValue->rValue;
}
else if (nodata)
/* Just keep the transient output value corresponding to timeold,

View File

@ -61,6 +61,7 @@ Modified: 2000 AlansFixes
#include "ngspice/fteinp.h"
#include "ngspice/stringskip.h"
#include "ngspice/compatmode.h"
#include "ngspice/hash.h"
#include <stdarg.h>
@ -95,14 +96,17 @@ static int translate(struct card *deck, char *formal, int flen, char *actual,
struct bxx_buffer;
static void finishLine(struct bxx_buffer *dst, char *src, char *scname);
static int settrans(char *formal, int flen, char *actual, const char *subname);
static char *gettrans(const char *name, const char *name_end);
static char *gettrans(const char *name, const char *name_end, bool *isglobal);
static int numnodes(const char *line, struct subs *subs, wordlist const *modnames);
static int numdevs(char *s);
static wordlist *modtranslate(struct card *deck, char *subname, wordlist *new_modnames);
static void devmodtranslate(struct card *deck, char *subname, wordlist * const orig_modnames);
static int inp_numnodes(char c);
#define N_GLOBAL_NODES 1005
/* hash table to store the global nodes
* For now its use is limited to avoid double entries in global_nodes[] */
static NGHASHPTR glonodes = NULL;
#define DUMMYDATA ((void *)42)
/*---------------------------------------------------------------------
* table is used in settrans and gettrans -- it holds the netnames used
@ -137,19 +141,16 @@ static bool use_numparams = FALSE;
static char start[32], sbend[32], invoke[32], model[32];
static char *global_nodes[N_GLOBAL_NODES];
static int num_global_nodes;
static void
collect_global_nodes(struct card *c)
{
num_global_nodes = 0;
global_nodes[num_global_nodes++] = copy("0");
/* hash table for global nodes */
glonodes = nghash_init(NGHASH_MIN_SIZE);
/* add 0 and null as global nodes */
nghash_insert(glonodes, "0", DUMMYDATA);
#ifdef XSPICE
global_nodes[num_global_nodes++] = copy("null");
nghash_insert(glonodes, "null", DUMMYDATA);
#endif
for (; c; c = c->nextcard)
@ -157,17 +158,20 @@ collect_global_nodes(struct card *c)
char *s = c->line;
s = nexttok(s);
while (*s) {
if (num_global_nodes == N_GLOBAL_NODES) {
fprintf(stderr, "ERROR, N_GLOBAL_NODES overflow\n");
controlled_exit(EXIT_FAILURE);
}
char *t = skip_non_ws(s);
global_nodes[num_global_nodes++] = copy_substring(s, t);
/* global node name */
char *gnode = copy_substring(s, t);
/* insert only if not yet found in table */
if (gnode && *gnode != '\0' && nghash_find(glonodes, gnode) == NULL) {
nghash_insert(glonodes, gnode, DUMMYDATA);
}
tfree(gnode);
s = skip_ws(t);
}
c->line[0] = '*'; /* comment it out */
}
#ifdef TRACE
{
int i;
@ -184,10 +188,7 @@ collect_global_nodes(struct card *c)
static void
free_global_nodes(void)
{
int i;
for (i = 0; i < num_global_nodes; i++)
tfree(global_nodes[i]);
num_global_nodes = 0;
nghash_free(glonodes, NULL, NULL);
}
@ -1107,14 +1108,19 @@ bxx_buffer(struct bxx_buffer *t)
static void
translate_node_name(struct bxx_buffer *buffer, const char *scname, const char *name, const char *name_e)
{
const char *t;
bool isglobal;
if (!name_e)
name_e = strchr(name, '\0');
t = gettrans(name, name_e);
t = gettrans(name, name_e, &isglobal);
if (t) {
bxx_put_cstring(buffer, t);
/* free only if t is global node, nodes from table[].t_new are freed elsewhere */
if(isglobal)
tfree(t);
} else {
bxx_put_cstring(buffer, scname);
bxx_putc(buffer, '.');
@ -1624,21 +1630,28 @@ eq_substr(const char *str, const char *end, const char *cstring)
* otherwise it returns NULL.
*------------------------------------------------------------------------------*/
static char *
gettrans(const char *name, const char *name_end)
gettrans(const char *name, const char *name_end, bool *isglobal)
{
int i;
*isglobal = FALSE;
if (!name_end)
name_end = strchr(name, '\0');
/* Added by H.Tanaka to translate global nodes */
for (i = 0; i<num_global_nodes; i++)
if (eq_substr(name, name_end, global_nodes[i]))
return (global_nodes[i]);
char* newgl = copy_substring(name, name_end);
if (nghash_find(glonodes, newgl)) {
*isglobal = TRUE;
return newgl;
}
else
tfree(newgl);
for (i = 0; table[i].t_old; i++)
if (eq_substr(name, name_end, table[i].t_old))
return (table[i].t_new);
if (eq_substr(name, name_end, table[i].t_old)) {
*isglobal = FALSE;
return table[i].t_new;
}
return (NULL);
}
@ -1682,6 +1695,9 @@ numnodes(const char *line, struct subs *subs, wordlist const *modnames)
return (nodes);
}
}
/* if we use option skywaterpdk, MOS has four nodes. Required if number of devices is large */
if (ft_skywaterpdk && c == 'm')
return 4;
n = inp_numnodes(c);

View File

@ -791,8 +791,6 @@ cp_getvar(char *name, enum cp_types type, void *retval, size_t rsize)
* variable *, which allows the host program to provide values for
* non-cshpar variables. */
char cp_dol = '$';
/* Non-alphanumeric characters that may appear in variable names. < is very
* special...
*/
@ -808,10 +806,11 @@ char cp_dol = '$';
* Return value
* Address of the first character after the variable name.
*/
char *span_var_expr(char *t)
char *span_var_expr(char *s)
{
int parenthesis = 0;
int brackets = 0;
char *t = s;
int parenthesis = 0;
int brackets = 0;
while (*t && (isalnum_c(*t) || strchr(VALIDCHARS, *t)))
switch (*t++)
@ -834,6 +833,14 @@ char *span_var_expr(char *t)
if (--parenthesis <= 0)
return t;
break;
case '$':
if (brackets <= 0 && parenthesis <= 0) {
if (t == s + 1) // Special case: "$$".
return t;
else
return t-1;
}
break;
default:
break;
}
@ -852,7 +859,7 @@ wordlist *cp_variablesubst(wordlist *wlist)
char *s_dollar;
int i = 0;
while ((s_dollar = strchr(wl->wl_word + i, cp_dol)) != NULL) {
while ((s_dollar = strchr(wl->wl_word + i, '$')) != NULL) {
int prefix_len = (int) (s_dollar - wl->wl_word);
@ -1002,6 +1009,8 @@ wordlist *vareval(/* NOT const */ char *string)
if (eq(v->va_name, string))
break;
if (!v && isdigit_c(*string)) {
/* Treat $i for some integer i as argv[i] - positional parameters. */
for (v = variables; v; v = v->va_next) {
if (eq(v->va_name, "argv")) {
break;

View File

@ -52,6 +52,7 @@ NON-STANDARD FEATURES
#define ANALOG MIF_ANALOG
#define EVENT MIF_EVENT_DRIVEN
#define STEP_PENDING MIF_STEP_PENDING
#endif

View File

@ -97,6 +97,7 @@ int cm_message_printf(const char *fmt, ...);
double cm_netlist_get_c(void);
double cm_netlist_get_l(void);
void cm_irreversible(unsigned int);
const char *cm_get_node_name(const char *, unsigned int);
bool cm_probe_node(unsigned int, unsigned int, void *);
bool cm_schedule_output(unsigned int, unsigned int, double, void *);

View File

@ -0,0 +1,71 @@
/* Header file for the shim code between d_cosim and a co-simulator. */
#if __cplusplus
extern "C" {
#endif
/* A value of this type controls how the step() function is called.
* The normal method is to call step() to advance to the time of a
* queued input, then supply the input, then call step again to
* advance to the next input time or the end of the current SPICE
* timestep. But Verilator does nothing without an input change,
* so step() must be called after input.
*/
typedef enum {Normal, After_input} Cosim_method;
/* Structure used by Cosim_setup() to pass and return
* co-simulation interface information.
*/
struct co_info {
/* The co-simulator must set the number of ports in Cosim_setup(). */
unsigned int in_count;
unsigned int out_count;
unsigned int inout_count;
/* The co-simulator may specify a function to be called just before
* it is unloaded at the end of a simulation run.
*/
void (*cleanup)(struct co_info *);
/* Function called by SPICE to advance the co-simulation.
* A pointer to this structure is passed, so it has access to its handle
* and the target simulation time, vtime. The co-simulator should
* pause the step when output is produced and update vtime.
*/
void (*step)(struct co_info *pinfo); // Advance simulation.
/* Function called by SPICE to pass input to input and inout ports.
* (Inouts after inputs.)
* Called as:
* struct co_info info;
* (*in_fn)(&info, bit_number, &value);
* Function provided by co-simulator.
*/
void (*in_fn)(struct co_info *, unsigned int, Digital_t *);
/* Function called by co-simulator to report output on
* output and inout ports. (Inouts after outputs.)
* Called as:
* struct co_info *p_info;
* (*out_fn)(p_info, bit_number, &value);
* It will usually be called inside a call to step().
*/
void (*out_fn)(struct co_info *, unsigned int, Digital_t *);
void *handle; // Co-simulator's private handle
double vtime; // Time in the co-simulation.
Cosim_method method; // May be set in Cosim_setup;
};
extern void Cosim_setup(struct co_info *pinfo); // This must exist.
extern void Cosim_step(struct co_info *pinfo); // Exists for Verilator.
#if __cplusplus
}
#endif

View File

@ -156,7 +156,6 @@ extern bool cp_ignoreeof;
extern bool cp_noclobber;
extern bool cp_noglob;
extern bool cp_nonomatch;
extern char cp_dol;
extern void cp_remvar(char *varname);
void cp_vset(const char *varname, enum cp_types type, const void *value);
extern struct variable *cp_setparse(wordlist *wl);

View File

@ -59,6 +59,7 @@ struct coreInfo_t {
int ((*dllitf_cm_message_send)(char *));
double ((*dllitf_cm_netlist_get_c)(void));
double ((*dllitf_cm_netlist_get_l)(void));
void ((*dllitf_cm_irreversible)(unsigned int));
const char * ((*dllitf_cm_get_node_name)(const char *, unsigned int));
bool ((*dllitf_cm_probe_node)(unsigned int, unsigned int,
void *));

View File

@ -91,35 +91,27 @@ struct Evt_Node_Info {
};
struct Evt_Inst_Info {
Evt_Inst_Info_t *next; /* the next in the linked list of node info */
Evt_Inst_Info_t *next; /* the next in the linked list */
MIFinstance *inst_ptr; /* Pointer to MIFinstance struct for this instance */
};
struct Evt_Info {
Evt_Inst_Info_t *inst_list; /* static info about event/hybrid instances */
Evt_Node_Info_t *node_list; /* static info about event nodes */
Evt_Port_Info_t *port_list; /* static info about event ports */
Evt_Output_Info_t *output_list; /* static info about event outputs */
int *hybrid_index; /* vector of inst indexs for hybrids */
Evt_Inst_Info_t **inst_table; /* vector of pointers to elements in inst_list */
Evt_Node_Info_t **node_table; /* vector of pointers to elements in node_list */
Evt_Port_Info_t **port_table; /* vector of pointers to elements in port_list */
Evt_Output_Info_t **output_table; /* vector of pointers to elements in output_list */
Evt_Inst_Info_t *inst_list; /* static info about event instances */
Evt_Node_Info_t *node_list; /* static info about event nodes */
Evt_Port_Info_t *port_list; /* static info about event ports */
Evt_Output_Info_t *output_list; /* static info about event outputs */
MIFinstance **hybrids; /* vector of inst pointers for hybrids */
Evt_Inst_Info_t **inst_table; /* vector of pointers to elements in inst_list */
Evt_Node_Info_t **node_table; /* vector of pointers to elements in node_list */
Evt_Port_Info_t **port_table; /* vector of pointers to elements in port_list */
Evt_Output_Info_t **output_table; /* vector of pointers to elements in output_list */
};
/* *************** */
/* Queue structure */
/* *************** */
struct Evt_Inst_Event {
Evt_Inst_Event_t *next; /* the next in the linked list */
double event_time; /* Time for this event to happen */

View File

@ -92,7 +92,9 @@ void EVTqueue_inst(
void EVTdequeue(CKTcircuit *ckt, double time);
int EVTload(CKTcircuit *ckt, int inst_index);
int EVTload(CKTcircuit *ckt, MIFinstance *inst);
int EVTload_with_event(CKTcircuit *ckt, MIFinstance *inst, Mif_Call_Type_t type);
void EVTprint(wordlist *wl);
void EVTprintvcd(wordlist *wl);

View File

@ -268,6 +268,7 @@ extern struct card *inp_getoptsc(char *line, struct card *options);
extern bool ft_ngdebug;
extern bool ft_nginfo;
extern bool ft_stricterror;
extern bool ft_skywaterpdk;
/* parse.c */

View File

@ -80,6 +80,7 @@ struct MIFinstance {
Mif_Boolean_t analog; /* true if this inst is analog or hybrid type */
Mif_Boolean_t event_driven; /* true if this inst is event-driven or hybrid type */
unsigned int irreversible; /* non-zero for special treatment */
int inst_index; /* Index into inst_table in evt struct in ckt */
Mif_Callback_t callback; /* instance callback function */

View File

@ -75,6 +75,7 @@ typedef enum {
typedef enum {
MIF_ANALOG, /* Analog call */
MIF_EVENT_DRIVEN, /* Event-driven call */
MIF_STEP_PENDING, /* Special event call for irreversible Code Models */
} Mif_Call_Type_t;

View File

@ -124,6 +124,7 @@ enum {
#ifdef KLU
OPT_SPARSE,
OPT_KLU,
OPT_KLU_MEMGROW_FACTOR,
#endif

View File

@ -184,7 +184,7 @@ typedef struct linked_lists_of_Bpoint{
} BPOINT, *BPOINTPTR;
typedef struct linked_lists_of_nodeName{
char id[24];
char id[256];
struct linked_lists_of_nodeName *left, *right;
NODE *nd;
} NDname, *NDnamePt;

View File

@ -164,6 +164,8 @@ double EpsNorm, VNorm, NNorm, LNorm, TNorm, JNorm, GNorm, ENorm;
/* end cider globals */
#endif /* CIDER */
char **Copy_of_argv; // Used to recover args for *ng_script_with_params files
struct variable *(*if_getparam)(CKTcircuit *ckt, char **name, char *param, int ind, int do_model);
/* static functions */
@ -1382,13 +1384,16 @@ int main(int argc, char **argv)
while (optind < argc) {
char *arg = argv[optind++];
FILE *tp;
/* Copy the the path of the first filename only */
if (!Infile_Path) {
Infile_Path = ngdirname(arg);
}
/* unquote the input string, needed if it results from double clicking the filename */
#if defined(HAS_WINGUI)
/* Unquote the input string, needed if it results
* from double clicking the filename.
*/
arg = cp_unquote(arg);
#endif
/* Copy all the arguments into the temporary file */
@ -1401,15 +1406,22 @@ int main(int argc, char **argv)
tp = fopen(p, "r");
tfree(p);
}
if (!tp) {
perror(arg);
err = 1;
break;
/* Try and find it in a directory in $sourcepath. */
tp = inp_pathopen(arg, "r");
if (!tp) {
perror(arg);
err = 1;
break;
}
}
}
/* Copy the input file name which otherwise will be lost due to the
temporary file */
/* Copy the input file name which otherwise will be lost
* due to the temporary file.
*/
dname = copy(arg);
#if defined(HAS_WINGUI)
/* write source file name into source window */
@ -1418,11 +1430,32 @@ int main(int argc, char **argv)
tfree(arg);
#endif
if (!gotone) {
char line[256];
/* Check for "*ng_script_with_params" as first line. */
if (fgets(line, sizeof line, tp) &&
ciprefix("*ng_script_with_params", line)) {
/* Special script file: remaining arguments are
* script parameters.
*/
fclose(tempfile);
tempfile = tp;
Copy_of_argv = argv;
break;
} else {
fseek(tp, 0L, SEEK_SET);
gotone = TRUE;
}
}
append_to_stream(tempfile, tp);
fclose(tp);
}
fseek(tempfile, 0L, SEEK_SET);
gotone = FALSE; // Re-use
if (tempfile && (!err || !ft_batchmode)) {
/* Copy the input file name for becoming another file search path */
@ -1450,7 +1483,6 @@ int main(int argc, char **argv)
}
if (ft_batchmode) {
int error3 = 1;
/* If we get back here in batch mode then something is wrong,

View File

@ -23,6 +23,7 @@ void * cx_mean(void *data, short int type, int length, int *newlength, short int
void * cx_stddev(void *data, short int type, int length, int *newlength, short int *newtype);
void * cx_length(void *data, short int type, int length, int *newlength, short int *newtype);
void * cx_vector(void *data, short int type, int length, int *newlength, short int *newtype);
void * cx_cvector(void *data, short int type, int length, int *newlength, short int *newtype);
void * cx_unitvec(void *data, short int type, int length, int *newlength, short int *newtype);
void * cx_plus(void *data1, void *data2, short int datatype1, short int datatype2, int length);
void * cx_minus(void *data1, void *data2, short int datatype1, short int datatype2, int length);

View File

@ -26,6 +26,7 @@ NIconvTest(CKTcircuit *ckt)
double old;
double new;
double tol;
static int nancount = 0;
node = ckt->CKTnodes;
size = SMPmatSize(ckt->CKTmatrix);
@ -41,8 +42,14 @@ NIconvTest(CKTcircuit *ckt)
new = ckt->CKTrhs [i] ;
old = ckt->CKTrhsOld [i] ;
if (isnan(new)) {
if (ft_ngdebug)
if (ft_ngdebug && nancount < 10) {
fprintf(stderr, "Warning: non-convergence, node %s is nan\n", CKTnodName(ckt, i));
nancount++;
}
else if (ft_ngdebug && nancount == 10) {
fprintf(stderr, " non-convergence warnings (nan) limited to 10\n", CKTnodName(ckt, i));
nancount++;
}
return 1;
}
if(node->type == SP_VOLTAGE) {

View File

@ -174,7 +174,14 @@ NIiter(CKTcircuit *ckt, int maxIter)
ckt->CKTstat->STATreorderTime += SPfrontEnd->IFseconds() - startTime;
if (error) {
SMPgetError(ckt->CKTmatrix, &i, &j);
SPfrontEnd->IFerrorf (ERR_WARNING, "singular matrix: check nodes %s and %s\n", NODENAME(ckt, i), NODENAME(ckt, j));
if (ft_ngdebug || msgcount < 6) {
SMPgetError(ckt->CKTmatrix, &i, &j);
if (eq(NODENAME(ckt, i), NODENAME(ckt, j)))
SPfrontEnd->IFerrorf(ERR_WARNING, "singular matrix: check node %s\n", NODENAME(ckt, i));
else
SPfrontEnd->IFerrorf(ERR_WARNING, "singular matrix: check nodes %s and %s\n", NODENAME(ckt, i), NODENAME(ckt, j));
msgcount += 1;
}
/* CKTload(ckt); */
/* SMPprint(ckt->CKTmatrix, stdout); */

View File

@ -5,7 +5,7 @@
#include "polyeval.h"
#include "polyfit.h"
/* Returns thestrchr of the last element that was calculated. oval is
/* Returns the strchr of the last element that was calculated. oval is
* the value of the old scale at the end of the interval that is being
* interpolated from, and sign is 1 if the old scale was increasing,
* and -1 if it was decreasing. */
@ -30,14 +30,17 @@ putinterval(double *poly, int degree, double *nvec,
/* Interpolate data from oscale to nscale. data is assumed to be olen long,
* ndata will be nlen long. Returns FALSE if the scales are too strange
* to deal with. Note that we are guaranteed that either both scales are
* strictly increasing or both are strictly decreasing.
* increasing or both are decreasing.
*/
#define EDGE_FACTOR 1e-3
bool
ft_interpolate(double *data, double *ndata, double *oscale, int olen,
double *nscale, int nlen, int degree)
{
double *result, *scratch, *xdata, *ydata;
int sign, lastone, i, l;
double *result, *scratch, *xdata, *ydata, diff;
int sign, lastone, i, l, middle, tdegree;
if ((olen < 2) || (nlen < 2)) {
fprintf(cp_err, "Error: lengths too small to interpolate.\n");
@ -49,30 +52,72 @@ ft_interpolate(double *data, double *ndata, double *oscale, int olen,
return (FALSE);
}
if (oscale[1] < oscale[0])
sign = -1;
else
sign = 1;
for (i = 0; i < olen - 1; ++i) {
if (oscale[i + 1] < oscale[i]) {
sign = -1;
break;
} else if (oscale[i + 1] > oscale[i]) {
sign = 1;
break;
}
}
if (i >= olen) {
fprintf(cp_err, "Error: bad scale, can't interpolate.\n");
return FALSE;
}
scratch = TMALLOC(double, (degree + 1) * (degree + 2));
result = TMALLOC(double, degree + 1);
xdata = TMALLOC(double, degree + 1);
ydata = TMALLOC(double, degree + 1);
/* Deal with the first degree pieces. */
memcpy(ydata, data, (size_t) (degree + 1) * sizeof (double));
memcpy(xdata, oscale, (size_t) (degree + 1) * sizeof (double));
/* Initial load of the values to be analysed by ft_polyfit(),
* skipping irrelevant points and checking for and fudging vertical edges.
*/
while (!ft_polyfit(xdata, ydata, result, degree, scratch)) {
i = l = 0;
middle = (degree + 1) / 2;
if (sign > 0) {
while (l < olen - degree && oscale[l + middle] < nscale[0])
++l;
} else {
while (l < olen - degree && oscale[l + middle] > nscale[0])
++l;
}
ydata[0] = data[l];
xdata[0] = oscale[l];
do {
if (oscale[l + 1] == oscale[l]) {
if (i == 0) {
ydata[0] = data[++l]; // Ignore first point.
} else {
/* Push the previous x value back, making edge a slope. */
diff = xdata[i] - xdata[i - 1];
xdata[i] -= sign * diff * EDGE_FACTOR;
}
}
xdata[++i] = oscale[++l];
ydata[i] = data[l];
} while (i < degree && l < olen - 1);
if (i < degree) {
fprintf(cp_err, "Error: too few points to calculate polynomial\n");
return FALSE;
}
i = 0;
tdegree = degree;
while (!ft_polyfit(xdata + i, ydata + i, result, tdegree, scratch)) {
/* If it doesn't work this time, bump the interpolation
* degree down by one.
*/
if (--degree == 0) {
if (--tdegree == 0) {
fprintf(cp_err, "ft_interpolate: Internal Error.\n");
return (FALSE);
}
if (tdegree % 2)
++i; // Drop left point.
}
/* Add this part of the curve. What we do is evaluate the polynomial
@ -81,18 +126,19 @@ ft_interpolate(double *data, double *ndata, double *oscale, int olen,
* if the scale is decreasing at the end of the interval we are looking
* at.
*/
lastone = -1;
for (i = 0; i < degree; i++) {
lastone = putinterval(result, degree, ndata, lastone,
nscale, nlen, xdata[i], sign);
}
lastone = putinterval(result, tdegree, ndata, -1,
nscale, nlen, xdata[middle], sign);
/* Now plot the rest, piece by piece. l is the
* last element under consideration.
*/
for (l = degree + 1; l < olen; l++) {
for (++l; l < olen; l++) {
double out;
/* Shift the old stuff by one and get another value. */
out = xdata[0];
for (i = 0; i < degree; i++) {
xdata[i] = xdata[i + 1];
ydata[i] = ydata[i + 1];
@ -100,16 +146,44 @@ ft_interpolate(double *data, double *ndata, double *oscale, int olen,
ydata[i] = data[l];
xdata[i] = oscale[l];
while (!ft_polyfit(xdata, ydata, result, degree, scratch)) {
if (--degree == 0) {
fprintf(cp_err,
"interpolate: Internal Error.\n");
/* Check for vertical edge. */
if (oscale[l] == xdata[i - 1]) {
if (degree == 1)
diff = xdata[0] - out;
else
diff = xdata[i - 1] - xdata[i - 2];
xdata[i - 1] -= sign * diff * EDGE_FACTOR;
}
/* Skip input points until the next output point is framed. */
if (l < olen - degree) {
if (sign > 0 && xdata[middle] < nscale[lastone + 1])
continue;
else if (sign < 0 && xdata[middle] > nscale[lastone + 1])
continue;
}
i = 0;
tdegree = degree;
while (!ft_polyfit(xdata + i, ydata + i, result, tdegree, scratch)) {
/* If it doesn't work this time, bump the interpolation
* degree down by one.
*/
if (--tdegree == 0) {
fprintf(cp_err, "ft_interpolate: Internal Error.\n");
return (FALSE);
}
if (!((degree - tdegree) & 1))
++i; // Drop left point after right.
}
lastone = putinterval(result, degree, ndata, lastone,
nscale, nlen, xdata[i], sign);
lastone = putinterval(result, tdegree, ndata, lastone,
nscale, nlen, xdata[middle], sign);
}
lastone = putinterval(result, degree, ndata, lastone,
nscale, nlen, oscale[olen - 1], sign);
if (lastone < nlen - 1) /* ??? */
ndata[nlen - 1] = data[olen - 1];
tfree(scratch);

32
src/misc/win_time.c Normal file
View File

@ -0,0 +1,32 @@
#define WIN32_LEAN_AND_MEAN
//#include "ngspice/ngspice.h"
#include <Windows.h>
#include <winsock2.h>
#include <stdint.h> // portable: uint64_t MSVC: __int64
/*/ MSVC defines this in winsock2.h!?
typedef struct timeval {
long tv_sec;
long tv_usec;
} timeval;
*/
int gettimeofday(struct timeval * tp, void * unused)
{
// Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's
// This magic number is the number of 100 nanosecond intervals since January 1, 1601 (UTC)
// until 00:00:00 January 1, 1970
static const uint64_t EPOCH = ((uint64_t) 116444736000000000ULL);
SYSTEMTIME system_time;
FILETIME file_time;
uint64_t time;
GetSystemTime( &system_time );
SystemTimeToFileTime( &system_time, &file_time );
time = ((uint64_t)file_time.dwLowDateTime ) ;
time += ((uint64_t)file_time.dwHighDateTime) << 32;
tp->tv_sec = (long) ((time - EPOCH) / 10000000L);
tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
return 0;
}

View File

@ -335,7 +335,7 @@ extern OsdiObjectFile load_object_file(const char *input) {
* multiple times. We use the handle as a key because the same SO will always
* return the SAME pointer as long as dlclose is not called.
* nghash_insert returns NULL if the key (handle) was not already in the table
* and the data (DUMMYDATA) that was previously insered (!= NULL) otherwise*/
* and the data (DUMMYDATA) that was previously inserted (!= NULL) otherwise*/
if (nghash_insert(known_object_files, handle, DUMMYDATA)) {
txfree(path);
return EMPTY_OBJECT;

View File

@ -396,6 +396,7 @@ unsigned int main_id, ng_id, command_id;
mutexType triggerMutex;
mutexType allocMutex;
mutexType fputsMutex;
mutexType vecreallocMutex;
#endif
/* initialization status */
@ -774,6 +775,8 @@ read_initialisation_file(const char *dir, const char *name)
/* The functions exported explicitely from shared ngspice */
/**********************************************************/
#ifdef THREADS
/* Checks if ngspice is running in the background */
@ -857,16 +860,19 @@ ngSpice_Init(SendChar* printfcn, SendStat* statusfcn, ControlledExit* ngspiceexi
pthread_mutex_init(&triggerMutex, NULL);
pthread_mutex_init(&allocMutex, NULL);
pthread_mutex_init(&fputsMutex, NULL);
pthread_mutex_init(&vecreallocMutex, NULL);
cont_condition = FALSE;
#else
#ifdef SRW
InitializeSRWLock(&triggerMutex);
InitializeSRWLock(&allocMutex);
InitializeSRWLock(&fputsMutex);
InitializeSRWLock(&vecreallocMutex);
#else
InitializeCriticalSection(&triggerMutex);
InitializeCriticalSection(&allocMutex);
InitializeCriticalSection(&fputsMutex);
InitializeCriticalSection(&vecreallocMutex);
#endif
#endif
// Id of primary thread
@ -1336,6 +1342,20 @@ char** ngSpice_AllEvtNodes(void)
}
#endif
/* Lock/unlock realloc of result vectors during plotting */
IMPEXP
int ngSpice_LockRealloc(void)
{
mutex_lock(&vecreallocMutex);
return 1;
}
IMPEXP
int ngSpice_UnlockRealloc(void)
{
mutex_unlock(&vecreallocMutex);
return 1;
}
/* add the preliminary breakpoints to the list.
called from dctran.c */

View File

@ -57,11 +57,18 @@ CKTacct(CKTcircuit *ckt, JOB *anal, int which, IFvalue *val)
case OPT_FILLNZ:
if ( ckt->CKTmatrix != NULL ) {
#ifdef KLU
if (ckt->CKTmatrix->CKTkluMODE)
val->iValue = ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric->lnz + ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric->unz
- (int)ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNZ ;
else
if (ckt->CKTmatrix->CKTkluMODE) {
if (!ckt->CKTmatrix->SMPkluMatrix ||
!ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric) {
return -1;
}
val->iValue =
ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric->lnz +
ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric->unz -
(int)ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNZ ;
} else {
val->iValue = spFillinCount(ckt->CKTmatrix->SPmatrix);
}
#else
val->iValue = spFillinCount(ckt->CKTmatrix->SPmatrix);
#endif
@ -72,10 +79,14 @@ CKTacct(CKTcircuit *ckt, JOB *anal, int which, IFvalue *val)
case OPT_TOTALNZ:
if ( ckt->CKTmatrix != NULL ) {
#ifdef KLU
if (ckt->CKTmatrix->CKTkluMODE)
val->iValue = ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric->lnz + ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric->unz ;
else
val->iValue = spElementCount(ckt->CKTmatrix->SPmatrix);
if (ckt->CKTmatrix->CKTkluMODE && ckt->CKTmatrix->SMPkluMatrix &&
ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric) {
val->iValue =
ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric->lnz +
ckt->CKTmatrix->SMPkluMatrix->KLUmatrixNumeric->unz ;
} else {
val->iValue = 0;
}
#else
val->iValue = spElementCount(ckt->CKTmatrix->SPmatrix);
#endif

View File

@ -141,7 +141,7 @@ CKTnewTask(CKTcircuit *ckt, TSKtask **taskPtr, IFuid taskName, TSKtask **defPtr)
tsk->TSKepsmin = 1e-28;
#ifdef KLU
tsk->TSKkluMODE = CKTkluON;
tsk->TSKkluMODE = CKTkluOFF;
tsk->TSKkluMemGrowFactor = 1.2 ;
#endif

View File

@ -180,7 +180,9 @@ CKTsetOpt(CKTcircuit *ckt, JOB *anal, int opt, IFvalue *val)
case OPT_SPARSE:
task->TSKkluMODE = (val->iValue == 0);
break;
case OPT_KLU:
task->TSKkluMODE = (val->iValue != 0);
break;
case OPT_KLU_MEMGROW_FACTOR:
task->TSKkluMemGrowFactor = (val->rValue == 1.2);
break;
@ -348,6 +350,8 @@ static IFparm OPTtbl[] = {
#ifdef KLU
{ "sparse", OPT_SPARSE, IF_SET|IF_FLAG,
"Set SPARSE 1.3 as Direct Linear Solver" },
{ "klu", OPT_KLU, IF_SET|IF_FLAG,
"Set KLU as Direct Linear Solver" },
{ "klu_memgrow_factor", OPT_KLU_MEMGROW_FACTOR, IF_SET|IF_REAL,
"KLU Memory Grow Factor (default is 1.2)" }
#endif

View File

@ -787,14 +787,6 @@ resume:
converged = NIiter(ckt,ckt->CKTtranMaxIter);
#ifdef XSPICE
if(ckt->evt->counts.num_insts > 0) {
g_mif_info.circuit.evt_step = ckt->CKTtime;
EVTcall_hybrids(ckt);
}
/* gtri - end - wbk - Call all hybrids */
#endif
ckt->CKTstat->STATtimePts ++;
ckt->CKTmode = (ckt->CKTmode&MODEUIC)|MODETRAN | MODEINITPRED;
if(firsttime) {
@ -830,8 +822,10 @@ resume:
#ifdef XSPICE
/* gtri - begin - wbk - Add Breakpoint stuff */
/* Force backup if temporary breakpoint is < current time */
} else if(g_mif_info.breakpoint.current < ckt->CKTtime) {
/* Force backup if temporary breakpoint is < current time */
past_breakpoint:
ckt->CKTsaveDelta = ckt->CKTdelta;
ckt->CKTtime -= ckt->CKTdelta;
ckt->CKTdelta = g_mif_info.breakpoint.current - ckt->CKTtime;
@ -879,6 +873,27 @@ resume:
return(error);
}
if (newdelta > .9 * ckt->CKTdelta) {
#if defined(XSPICE)
/* The timestep has succeeded. XSPICE instances with
* both analog and event ports ("hybrids") and others
* that have called cm_irreversible() receive an EVENT
* call here that allows them to capture their final
* port values and advance co-simulations. As this is an EVENT
* call, they are not expected to do any integrations,
* so there is no need for a further convergence test.
*/
if (ckt->evt->counts.num_hybrids > 0) {
g_mif_info.circuit.evt_step = ckt->CKTtime;
EVTcall_hybrids(ckt);
if (g_mif_info.breakpoint.current < ckt->CKTtime) {
/* A hybrid requested a breakpoint in the past. */
goto past_breakpoint;
}
}
#endif
if ((ckt->CKTorder == 1) && (ckt->CKTmaxOrder > 1)) { /* don't rise the order for backward Euler */
newdelta = ckt->CKTdelta;
ckt->CKTorder = 2;
@ -893,6 +908,7 @@ resume:
}
/* time point OK - 630 */
ckt->CKTdelta = newdelta;
#ifdef NDEV
if (!ft_norefprint) {
/* show a time process indicator, by Gong Ding, gdiso@ustc.edu */

View File

@ -126,6 +126,8 @@ void my_key_free(void * key)
void mem_delete(void) {
#ifdef DB
char buf[128];
if (!memory_table)
return;
printf("CPL GC memory allocated %d times, freed %d times\n", mem_in, mem_out);
printf("CPL GC size of hash table to be freed: %d entries.\n", nghash_get_size(memory_table));
#ifdef DB_FULL
@ -142,6 +144,7 @@ void mem_delete(void) {
#endif
gc_is_on = 0;
nghash_free(memory_table, NULL, my_key_free);
memory_table = NULL;
#ifdef DB
/* printf via sh_printf will need some info from variables that have
been deleted already, therefore we use fputs */

View File

@ -1934,7 +1934,8 @@ insert_ND(char* name, NDnamePt* ndn)
memsaved(p);
p->nd = NULL;
p->right = p->left = NULL;
strcpy(p->id, name);
strncpy(p->id, name, 255);
p->id[255] = '\0';
return(p);
}
cmp = strcmp((*ndn)->id, name);

View File

@ -16,6 +16,7 @@ Modified by Paolo Nenzi 2003 and Dietmar Warning 2012
#include "ngspice/sperror.h"
#include "ngspice/suffix.h"
#include "ngspice/fteext.h"
#include "ngspice/compatmode.h"
int
DIOsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states)
@ -195,7 +196,13 @@ DIOsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *states)
}
if((!model->DIOresistGiven) || (model->DIOresist==0)) {
model->DIOconductance = 0.0;
if (newcompat.ps || newcompat.lt) {
model->DIOconductance = 1e4; /* improved convergence */
if (ft_ngdebug)
fprintf(stderr, "Diode series resistance in model %s set to 100 microOhm\n", model->gen.GENmodName);
}
else
model->DIOconductance = 0.0;
} else {
model->DIOconductance = 1/model->DIOresist;
}

View File

@ -1035,7 +1035,8 @@ insert_ND(char *name, NDnamePt *ndn)
p = *ndn = TMALLOC(NDname, 1);
p->nd = NULL;
p->right = p->left = NULL;
strcpy(p->id, name);
strncpy(p->id, name, 255);
p->id[255] = '\0';
return(p);
}
cmp = strcmp((*ndn)->id, name);

View File

@ -84,6 +84,16 @@ VDMOSload(GENmodel *inModel, CKTcircuit *ckt)
else
Check_th = 0;
/* FIXME:
this is not a fix, but a hack:
with selfheat, op and op for ac don't work, NaN in self heating evaluation of
first iteration in CKTop(). Calling CKTop() from acan uses flag MODEDCOP,
changing this to MODETRANOP, as used by CKTop() called from dctran, then op is o.k.
*/
if (selfheat)
if(ckt->CKTmode == 528) /* includes MODEDCOP */
ckt->CKTmode = 544; /* includes MODETRANOP */
/* first, we compute a few useful values - these could be
* pre-computed, but for historical reasons are still done
* here. They may be moved at the expense of instance size

View File

@ -850,7 +850,6 @@ ElementWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
static int
MakeArgcArgv(char *cmdline, int *argc, char ***argv)
{
char *pC1; /* a temporary character pointer */
char *pWorkString = NULL; /* a working copy of cmdline */
int i; /* a loop counter */
int quoteflag = 0; /* for the finite state machine parsing cmdline */
@ -899,13 +898,13 @@ MakeArgcArgv(char *cmdline, int *argc, char ***argv)
#endif
/* If we still have a string left, parse it for all
the arguments. */
if (strlen(pWorkString))
if (pWorkString[0])
{
/* This could probably be done with strtok as well
but strtok is destructive if I wanted to look for " \""
and I couldn't tell what delimiter that I had bumped
against */
for (i = 0; i < (signed)strlen(pWorkString); i++)
for (i = 0; pWorkString[i]; i++)
switch (pWorkString[i])
{
case SPACE:
@ -935,7 +934,7 @@ MakeArgcArgv(char *cmdline, int *argc, char ***argv)
}
}
/* malloc an argv */
tmpargv = (char**) malloc((unsigned)numargs * sizeof(char *));
tmpargv = (char**) malloc((unsigned)(numargs + 1) * sizeof(char *));
if (NULL == tmpargv)
{
status = -1;
@ -949,18 +948,15 @@ MakeArgcArgv(char *cmdline, int *argc, char ***argv)
deli[0] = DELIMITER;
deli[1] = '\0'; /* delimiter for strtok */
pC1 = NULL;
/* Now actually strdup all the arguments out of the string
and store them in the argv */
for (i = 1; i < numargs; i++) {
if (NULL == pC1)
pC1 = pWorkString;
if (i == 1)
tmpargv[i] = copy(strtok(pC1, deli));
tmpargv[i] = copy(strtok(pWorkString, deli));
else
tmpargv[i] = copy(strtok(NULL, deli));
}
tmpargv[i] = NULL;
/* copy the working values over to the arguments */
*argc = numargs;

View File

@ -1,6 +1,8 @@
# Process this file with automake
EXTRA_DIST = README examples icm xspice.c .gitignore
EXTRA_DIST = README examples icm xspice.c .gitignore \
verilog/vlnggen \
verilog/verilator_shim.cpp verilog/verilator_main.cpp
## This is removed because icm relies upon the existance of all other
## libs. It is currently compiled manually, last.
@ -8,7 +10,15 @@ EXTRA_DIST = README examples icm xspice.c .gitignore
SUBDIRS = mif cm enh evt ipc idn cmpp icm
initdatadir = $(pkgdatadir)/scripts
initdata_DATA = verilog/vlnggen
initdata1dir = $(pkgdatadir)/scripts/src
initdata1_DATA = verilog/verilator_shim.cpp verilog/verilator_main.cpp
initdata2dir = $(pkgdatadir)/scripts/src/ngspice
initdata2_DATA = ../include/ngspice/cosim.h ../include/ngspice/miftypes.h \
../include/ngspice/cmtypes.h
dist-hook:
rm -f "$(distdir)/icm/makedefs"
rm -f "$(distdir)/icm/GNUmakefile"

View File

@ -38,6 +38,7 @@ INTERFACES
cm_get_path()
cm_get_circuit()
cm_irreversible()
cm_get_node_name()
cm_probe_node()
@ -57,6 +58,7 @@ NON-STANDARD FEATURES
#include "ngspice/enh.h"
#include "ngspice/mif.h"
#include "ngspice/cktdefs.h"
#include "ngspice/cpextern.h"
//#include "util.h"
@ -730,6 +732,100 @@ CKTcircuit *cm_get_circuit(void)
return(g_mif_info.ckt);
}
/* Set the "irreversible" flag on the current instance and shuffle it to the
* requested position among any other irreversibles in the hybrid_index array.
* Array entries are sorted so that non-zero values of instance->irreversible
* are decreasing: an instance with instance->irreversible == 1 is fully
* protected.
*/
static void duplicate(MIFinstance *instance)
{
fprintf(cp_err,
"Warning: Duplicate value %d in cm_irreversible() "
"for instance %s.\n",
instance->irreversible, instance->gen.GENname);
}
void cm_irreversible(unsigned int place)
{
MIFinstance *instance;
Evt_Ckt_Data_t *evt;
int num_hybrids;
MIFinstance **hybrids;
int old_index, i;
unsigned int value;
instance = g_mif_info.instance;
if (!g_mif_info.circuit.init) {
fprintf(cp_err,
"%s: Ignoring call to cm_irreversible(): not in INIT\n",
instance->gen.GENname);
return;
}
if (instance->irreversible || place == 0) {
if (instance->irreversible != place) {
fprintf(cp_err, "%s: Ignoring new value %d in cm_irreversible()\n",
instance->gen.GENname, place);
}
return;
}
instance->irreversible = place;
evt = g_mif_info.ckt->evt;
num_hybrids = evt->counts.num_hybrids;
hybrids = evt->info.hybrids;
/* Already a hybrid? */
for (old_index = 0; old_index < num_hybrids; ++old_index) {
if (hybrids[old_index] == instance)
break;
}
if (old_index < num_hybrids) {
/* Existing hybrid, move down, shuffling other entries up. */
for (i = old_index + 1; i < num_hybrids; ++i) {
value = hybrids[i]->irreversible;
if (value == 0 || value > place) {
hybrids[i - 1] = hybrids[i];
} else if (value == place) {
duplicate(instance);
break;
} else {
break;
}
}
hybrids[i - 1] = instance;
} else {
/* Instance is not hybrid, add an entry. */
num_hybrids++;
hybrids = TREALLOC(MIFinstance *, hybrids, num_hybrids);
evt->counts.num_hybrids = num_hybrids;
evt->info.hybrids = hybrids;
if (hybrids == NULL) {
fprintf(cp_err, "Allocation failed in cm_irreversible()\n");
abort();
}
/* Shuffle entries down. */
for (i = num_hybrids - 2; i >= 0; --i) {
value = hybrids[i]->irreversible;
if (value != 0 && value < place) {
hybrids[i + 1] = hybrids[i];
} else if (value == place) {
duplicate(instance);
} else {
break;
}
}
hybrids[i + 1] = instance;
}
}
/* Get the name of a circuit node connected to a port. */
const char *cm_get_node_name(const char *port_name, unsigned int index)

View File

@ -3,8 +3,6 @@
#include "ngspice/cm.h"
#include "ngspice/dllitf.h"
extern bool cp_getvar(char *, enum cp_types, void *, size_t);
/*how annoying!, needed for structure below*/
static void *tcalloc(size_t a, size_t b) {
return tmalloc(a*b); /* FIXME, tcalloc must zero !?!? */
@ -60,6 +58,7 @@ struct coreInfo_t coreInfo =
cm_message_send,
cm_netlist_get_c,
cm_netlist_get_l,
cm_irreversible,
cm_get_node_name,
cm_probe_node,
cm_schedule_output,

View File

@ -52,8 +52,6 @@ NON-STANDARD FEATURES
#include <math.h>
#include "ngspice/cm.h"
extern void controlled_exit(const int);
/* Corner Smoothing Function ************************************
* *
* The following function smooths the transition between two *

View File

@ -20,10 +20,12 @@ MODIFICATIONS
SUMMARY
This file contains function EVTcall_hybrids which calls all models
which have both analog and event-driven ports. It is called following
which have both analog and event-driven ports or have declared
themselves to be irreversible (no back-out). It is called following
successful evaluation of an analog iteration attempt to allow
events to be scheduled by the hybrid models. The 'CALL_TYPE' is set
to 'EVENT_DRIVEN' when the model is called from this function.
to 'EVENT_DRIVEN' or 'STEP_PENDING' when the model is called
from this function.
INTERFACES
@ -60,18 +62,18 @@ void EVTcall_hybrids(
CKTcircuit *ckt) /* the main circuit structure */
{
int i;
int num_hybrids;
int *hybrid_index;
int i;
int num_hybrids;
MIFinstance **hybrids;
/* Get needed data for fast access */
num_hybrids = ckt->evt->counts.num_hybrids;
hybrid_index = ckt->evt->info.hybrid_index;
hybrids = ckt->evt->info.hybrids;
/* Call EVTload for all hybrids */
for(i = 0; i < num_hybrids; i++)
EVTload(ckt, hybrid_index[i]);
for(i = 0; i < num_hybrids; i++)
EVTload_with_event(ckt, hybrids[i], MIF_STEP_PENDING);
}

View File

@ -330,5 +330,5 @@ Evt_Info_destroy(Evt_Info_t *info)
}
tfree(info->output_table);
tfree(info->hybrid_index);
tfree(info->hybrids);
}

View File

@ -207,7 +207,7 @@ static int EVTinit_info(
Evt_Port_Info_t **port_table = NULL;
Evt_Output_Info_t **output_table = NULL;
int *hybrid_index = NULL;
MIFinstance **hybrids = NULL;
int num_hybrids;
@ -253,15 +253,14 @@ static int EVTinit_info(
ckt->evt->info.output_table = output_table;
/* Allocate and create table of indexes into inst_table for hybrids */
/* Allocate and create table of hybrids */
num_hybrids = ckt->evt->counts.num_hybrids;
CKALLOC(hybrid_index, num_hybrids, int)
CKALLOC(hybrids, num_hybrids, MIFinstance *)
for(i = 0, j = 0; i < num_insts; i++) {
if(inst_table[i]->inst_ptr->analog)
hybrid_index[j++] = i;
hybrids[j++] = inst_table[i]->inst_ptr;
}
ckt->evt->info.hybrid_index = hybrid_index;
ckt->evt->info.hybrids = hybrids;
/* Return */
return(OK);

View File

@ -256,7 +256,7 @@ int EVTiter(
for(i = 0; i < num_to_call; i++) {
inst_index = inst_queue->to_call_index[i];
inst_queue->to_call[inst_index] = MIF_FALSE;
EVTload(ckt, inst_index);
EVTload(ckt, ckt->evt->info.inst_table[inst_index]->inst_ptr);
}
inst_queue->num_to_call = 0;

View File

@ -82,8 +82,18 @@ ignored.
*/
int EVTload(
CKTcircuit *ckt, /* The circuit structure */
int inst_index) /* The instance to call code model for */
CKTcircuit *ckt, /* The circuit structure */
MIFinstance *inst) /* The instance to call */
{
return EVTload_with_event(ckt, inst, MIF_EVENT_DRIVEN);
}
/* "Internal" version, also used by EVTcall_hybrids(). */
int EVTload_with_event(
CKTcircuit *ckt, /* The circuit structure */
MIFinstance *inst, /* The instance to call */
Mif_Call_Type_t type) /* Type of call (EVENT or STEP_PENDING). */
{
int i;
@ -96,18 +106,16 @@ int EVTload(
Mif_Conn_Data_t *conn;
Mif_Port_Data_t *port;
Evt_Node_Data_t *node_data;
MIFinstance *inst;
Mif_Private_t cm_data;
void *value_ptr;
/* ***************************** */
/* Prepare the code model inputs */
/* ***************************** */
/* Get pointer to instance data structure and other data */
/* needed for fast access */
inst = ckt->evt->info.inst_table[inst_index]->inst_ptr;
/* Get pointer to data structure needed for fast access */
node_data = ckt->evt->data.node;
/* Setup circuit data in struct to be passed to code model function */
@ -125,7 +133,14 @@ int EVTload(
else
cm_data.circuit.time = 0.0;
cm_data.circuit.call_type = MIF_EVENT_DRIVEN;
/* Instances that have declared themselves as irreversible
* are expected to distinguish STEP_PENDING from ordinary events.
*/
if (type == MIF_STEP_PENDING && inst->irreversible)
cm_data.circuit.call_type = MIF_STEP_PENDING;
else
cm_data.circuit.call_type = MIF_EVENT_DRIVEN;
cm_data.circuit.temperature = ckt->CKTtemp - 273.15;
/* Setup data needed by cm_... functions */
@ -142,11 +157,12 @@ int EVTload(
/* If after initialization and in transient analysis mode */
/* create a new state for the instance */
if((g_mif_info.circuit.anal_type == MIF_TRAN) && inst->initialized)
EVTcreate_state(ckt, inst_index);
/* create a new state for the instance, */
/* except analog-only irreversibles. */
if((g_mif_info.circuit.anal_type == MIF_TRAN) && inst->initialized &&
inst->inst_index >= 0)
EVTcreate_state(ckt, inst->inst_index);
/* Loop through all connections on the instance and setup */
/* load, total_load, and msg on all ports, and changed flag */

View File

@ -97,12 +97,12 @@ cm-descr := \
# When recursively making clean, cm-objs and cm-gens do not contain
# the files generated for individual code models, as cmpp has already gone
# and modlist and udnlist are empty. Those files are explicitly removed.
# and modlist and udnlist are empty. The files are explicitly removed.
cm-clean :
-rm -f $(cm)/$(cm).cm
-rm -f $(cm-descr) $(cm-objs) $(cm-gens)
-rm -f $(cm)/*/*.o $(cm)/*/*.c $(cm)/*/.deps/*
-rm -f $(cm)/*/ifspec.c $(cm)/*/cfunc.c $(cm)/*/*.o $(cm)/*/.deps/*
-rm -f $(cm-deps)
cm-distclean :
@ -135,10 +135,10 @@ $(cm-dirs) $(cm-dep-dirs) :
%/cmextrn.h %/cminfo.h %/udnextrn.h %/udninfo.h %/objects.inc : $(srcdir)/%/modpath.lst $(srcdir)/%/udnpath.lst
CMPP_IDIR=$(srcdir)/$(@D) CMPP_ODIR=$(@D) $(CMPP) -lst
%/ifspec.c : $(srcdir)/%/ifspec.ifs
%/ifspec.c : $(srcdir)/%/ifspec.ifs $(cmpp)
CMPP_IDIR=$(srcdir)/$(@D) CMPP_ODIR=$(@D) $(CMPP) -ifs
%/cfunc.c : $(srcdir)/%/cfunc.mod
%/cfunc.c : $(srcdir)/%/cfunc.mod $(cmpp)
CMPP_IDIR=$(srcdir)/$(@D) CMPP_ODIR=$(@D) $(CMPP) -mod

View File

@ -0,0 +1,578 @@
/* Code model d_cosim.
*
* XSPICE code model for running a co-simulation with no support
* for abandoning the current timestep.
*/
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#if defined (__MINGW32__) || defined (__CYGWIN__) || defined (_MSC_VER)
/* MS WINDOWS. */
#undef BOOLEAN
#include <windows.h>
#define dlopen(name, type) LoadLibrary(name)
#define dlsym(handle, name) (void *)GetProcAddress(handle, name)
#define dlclose(handle) FreeLibrary(handle)
char *dlerror(void) // Lifted from dev.c.
{
static const char errstr_fmt[] =
"Unable to find message in dlerr(). System code = %lu";
static char errstr[256];
LPVOID lpMsgBuf;
DWORD rc = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
NULL
);
if (rc == 0) { /* FormatMessage failed */
(void) sprintf(errstr, errstr_fmt, (unsigned long) GetLastError());
} else {
snprintf(errstr, sizeof errstr, lpMsgBuf);
LocalFree(lpMsgBuf);
}
return errstr;
} /* end of function dlerror */
#else
#include <dlfcn.h>
#endif
#include "ngspice/cosim.h"
/* The argument passed to code model functions. */
#define XSPICE_ARG mif_private
#define DBG(...)
//#define DBG(...) cm_message_printf(__VA_ARGS__)
/* Structure used to hold queued inputs. */
struct pend_in {
double when; // Due time.
unsigned int which; // Index of input.
Digital_t what; // The value.
};
/* Structure to maintain context, pointed to by STATIC VAR cosim_instance. */
struct instance {
struct co_info info; // Co-simulation interface - MUST BE FIRST.
int q_index; // Queue index (last active entry).
unsigned int q_length; // Size of input queue.
struct pend_in *q; // The input queue.
unsigned int in_ports; // Number of XSPICE inputs.
unsigned int out_ports; // Number of XSPICE outputs.
unsigned int inout_ports; // Number of XSPICE inout ports.
unsigned int op_pending; // Output is pending.
Digital_t *out_vals; // The new output values.
double extra; // Margin to extend timestep.
void *so_handle; // dlopen() handle to the simulation binary.
};
/* Called at end of simulation run to free memory. */
static void callback(ARGS, Mif_Callback_Reason_t reason)
{
struct instance *ip;
ip = (struct instance *)STATIC_VAR(cosim_instance);
if (reason == MIF_CB_DESTROY) {
if (!ip)
return;
if (ip->info.cleanup)
(*ip->info.cleanup)(&ip->info);
if (ip->so_handle)
dlclose(ip->so_handle);
if (ip->q)
free(ip->q);
if (ip->out_vals)
free(ip->out_vals);
free(ip);
STATIC_VAR(cosim_instance) = NULL;
}
}
/* Function called when a co-simulator output changes.
* Out-of-range values for bit_num must be ignored.
*/
void accept_output(struct co_info *pinfo, unsigned int bit_num, Digital_t *val)
{
struct instance *ip = (struct instance *)pinfo; // First member.
Digital_t *out_vals; // XSPICE rotating memory.
if (bit_num >= ip->out_ports + ip->inout_ports)
return;
out_vals = (Digital_t *)cm_event_get_ptr(1, 0);
DBG("Change %s %d/%d->%d/%d vtime %g",
cm_get_node_name("d_out", bit_num),
out_vals[bit_num].state, out_vals[bit_num].strength,
val->state, val->strength,
ip->info.vtime);
if (ip->op_pending == 0) {
/* Prepare pending output. */
memcpy(ip->out_vals, out_vals, ip->out_ports * sizeof *ip->out_vals);
ip->op_pending = 1;
}
ip->out_vals[bit_num] = *val;
}
/* Push pending outputs, usually sent back from the future.
* It is safe to use OUTPUT() here, although it mays seem that this
* function may be called twice in a single call to cm_d_cosim().
* There will never be any input changes when cm_d_cosim() is called
* with pending output, as all input for the shortened time-step has
* already been processed.
*/
static void output(struct instance *ip, ARGS)
{
double delay;
Digital_t *out_vals; // XSPICE rotating memory
int i, j;
delay = PARAM(delay) - (TIME - ip->info.vtime);
if (delay <= 0) {
cm_message_printf("WARNING: output scheduled with impossible "
"delay (%g) at %g.", delay, TIME);
delay = 1e-12;
}
out_vals = (Digital_t *)cm_event_get_ptr(1, 0);
/* Output to d_out. */
for (i = 0; i < ip->out_ports; ++i) {
if (ip->out_vals[i].state != out_vals[i].state ||
ip->out_vals[i].strength != out_vals[i].strength) {
DBG("%g: OUT %s %d/%d->%d/%d vtime %g with delay %g",
TIME, cm_get_node_name("d_out", i),
out_vals[i].state, out_vals[i].strength,
ip->out_vals[i].state, ip->out_vals[i].strength,
ip->info.vtime, delay);
*(Digital_t *)OUTPUT(d_out[i]) = out_vals[i] = ip->out_vals[i];
OUTPUT_DELAY(d_out[i]) = delay;
OUTPUT_CHANGED(d_out[i]) = TRUE;
} else {
OUTPUT_CHANGED(d_out[i]) = FALSE;
}
}
/* Output to d_inout. */
for (i = 0, j = ip->out_ports; i < ip->inout_ports; ++i, ++j) {
if (ip->out_vals[j].state != out_vals[j].state ||
ip->out_vals[j].strength != out_vals[j].strength) {
DBG("%g: inOUT %s %d/%d->%d/%d vtime %g with delay %g",
TIME, cm_get_node_name("d_inout", i),
out_vals[j].state, out_vals[j].strength,
ip->out_vals[j].state, ip->out_vals[j].strength,
ip->info.vtime, delay);
*(Digital_t *)OUTPUT(d_inout[i]) = out_vals[j] = ip->out_vals[j];
OUTPUT_DELAY(d_inout[i]) = delay;
OUTPUT_CHANGED(d_inout[i]) = TRUE;
} else {
OUTPUT_CHANGED(d_inout[i]) = FALSE;
}
}
ip->op_pending = 0;
}
/* Run the co-simulation. Return 1 if the timestep was truncated. */
static int advance(struct instance *ip, ARGS)
{
/* The co-simulator should advance to the time in ip->info.vtime,
* but should pause when output is generated and update vtime.
*/
(*ip->info.step)(&ip->info);
if (ip->op_pending) {
/* The co-simulator produced some output. */
if (TIME - ip->info.vtime <= PARAM(delay)) {
#if 1
DBG("Direct output with SPICE %.16g CS %.16g",
TIME, ip->info.vtime);
output(ip, XSPICE_ARG); // Normal output, unlikely.
#else
cm_event_queue((TIME + ip->info.vtime + PARAM(delay)) / 2.0);
#endif
} else {
/* Something changed that may alter the future of the
* SPICE simulation. Truncate the current timestep so that
* SPICE will see the pending output, which currently occurred
* in the past.
*/
DBG("Truncating timestep to %.16g", ip->info.vtime + ip->extra);
cm_analog_set_temp_bkpt(ip->info.vtime + ip->extra);
/* Any remaining input events are in an alternate future. */
ip->q_index = -1;
return 1;
}
}
return 0;
}
/* Called from the main function to run the co-simulation. */
static void run(struct instance *ip, ARGS)
{
struct pend_in *rp;
double sim_started;
int i;
if (ip->q_index < 0) {
/* No queued input, advance to current TIME. */
DBG("Advancing vtime without input %.16g -> %.16g",
ip->info.vtime , TIME);
ip->info.vtime = TIME;
advance(ip, XSPICE_ARG);
return;
}
/* Scan the queue. */
DBG("%.16g: Running Q with %d entries", TIME, ip->q_index + 1);
sim_started = ip->info.vtime;
for (i = 0; i <= ip->q_index; ++i) {
rp = ip->q + i;
if (rp->when <= sim_started) {
/* Not expected. */
cm_message_printf("Warning simulated event is in the past:\n"
"XSPICE %.16g\n"
"Event %.16g\n"
"Cosim %.16g",
TIME, rp->when, ip->info.vtime);
cm_message_printf("i=%d index=%d", i, ip->q_index);
continue;
}
/* Step the simulation forward to the input event time. */
ip->info.vtime = rp->when;
if (ip->info.method == Normal && advance(ip, XSPICE_ARG)) {
ip->q_index = -1;
return;
}
/* Pass input change to simulation. */
(*ip->info.in_fn)(&ip->info, rp->which, &rp->what);
while (i < ip->q_index && ip->q[i + 1].when == rp->when) {
/* Another change at the same time. */
++i;
rp = ip->q + i;
(*ip->info.in_fn)(&ip->info, rp->which, &rp->what);
}
/* Simulator requested to run after input change. */
if (ip->info.method == After_input && advance(ip, XSPICE_ARG)) {
ip->q_index = -1;
return;
}
}
/* All input was processed. Advance to end of the timestep. */
ip->q_index = -1;
if (ip->info.method == Normal && TIME > ip->info.vtime) {
ip->info.vtime = TIME;
advance(ip, XSPICE_ARG);
}
}
/* Check whether an input value has changed.
* To reduce the number of arguments, a struture pointer is passed.
*/
static bool check_input(struct instance *ip, Digital_t *ovp,
struct pend_in *rp)
{
if (ovp->state != rp->what.state ||
ovp->strength != rp->what.strength) {
if (++ip->q_index < ip->q_length) {
/* Record this event. */
ip->q[ip->q_index] = *rp;
} else {
/* Queue is full. Handle that by forcing a shorter timestep. */
--ip->q_index;
while (ip->q_index >= 0 && ip->q[ip->q_index].when >= rp->when)
--ip->q_index;
if (ip->q_index >= 0) {
cm_analog_set_temp_bkpt(
(rp->when + ip->q[ip->q_index].when) / 2);
} else {
/* This should never happen. */
cm_message_printf("Error: Event queue overflow at %e.",
rp->when);
}
}
return true;
}
return false;
}
/* The code model's main function. */
void ucm_d_cosim(ARGS)
{
struct instance *ip;
Digital_t *in_vals; // XSPICE rotating memory
int i, index;
if (INIT) {
int ins, outs, inouts;
unsigned int alloc_size;
void *handle;
void (*ifp)(struct co_info *);
char *fn;
/* Initialise outputs. Done early in case of failure. */
outs = PORT_NULL(d_out) ? 0 : PORT_SIZE(d_out);
for (i = 0; i < outs; ++i) {
OUTPUT_STATE(d_out[i]) = ZERO;
OUTPUT_STRENGTH(d_out[i]) = STRONG;
OUTPUT_DELAY(d_out[i]) = PARAM(delay);
}
inouts = PORT_NULL(d_inout) ? 0 : PORT_SIZE(d_inout);
for (i = 0; i < inouts; ++i) {
OUTPUT_STATE(d_inout[i]) = ZERO;
OUTPUT_STRENGTH(d_inout[i]) = STRONG;
OUTPUT_DELAY(d_inout[i]) = PARAM(delay);
}
/* Load the shared library containing the co-simulator. */
fn = PARAM(simulation);
handle = dlopen(fn, RTLD_LAZY | RTLD_LOCAL);
if (!handle) {
cm_message_send("Failed to load simulation binary. "
"Try setting LD_LIBRARY_PATH.");
cm_message_send(dlerror());
return;
}
ifp = dlsym(handle, "Cosim_setup");
if (*ifp == NULL) {
cm_message_printf("ERROR: no entry function in %s", fn);
cm_message_send(dlerror());
dlclose(handle);
return;
}
/* Get the instance data and initialise it. */
ip = (struct instance *)calloc(1, sizeof *ip);
if (!ip)
goto no_ip;
ip->so_handle = handle;
ip->info.vtime = 0.0;
ip->info.out_fn = accept_output;
CALLBACK = callback;
/* Store the simulation interface information. */
(*ifp)(&ip->info);
/* Check lengths. */
ins = PORT_NULL(d_in) ? 0 : PORT_SIZE(d_in);
if (ins != ip->info.in_count) {
cm_message_printf("Warning: mismatched XSPICE/co-simulator "
"input counts: %d/%d.",
ins, ip->info.in_count);
}
if (outs != ip->info.out_count) {
cm_message_printf("Warning: mismatched XSPICE/co-simulator "
"output counts: %d/%d.",
outs, ip->info.out_count);
}
if (inouts != ip->info.inout_count) {
cm_message_printf("Warning: mismatched XSPICE/co-simulator "
"inout counts: %d/%d.",
inouts, ip->info.inout_count);
}
/* Create input queue and output buffer. */
ip->q_index = -1;
ip->q_length = PARAM(queue_size);
ip->in_ports = ins;
ip->out_ports = outs;
ip->inout_ports = inouts;
if (ins + inouts > ip->q_length) {
cm_message_send("WARNING: Input queue size should be greater than "
"number of input ports. Size increased.");
ip->q_length = ins + inouts + 16;
}
alloc_size = ip->q_length * sizeof (struct pend_in);
ip->q = (struct pend_in *)malloc(alloc_size);
if (!ip->q)
goto no_q;
ip->op_pending = 0;
ip->out_vals = (Digital_t *)calloc(outs + inouts, sizeof (Digital_t));
if (!ip->out_vals)
goto no_out_vals;
ip->extra = PARAM(delay) / 3; // FIXME?
STATIC_VAR(cosim_instance) = ip;
/* Allocate XSPICE rotating storage to track changes. */
cm_event_alloc(0, (ins + inouts) * sizeof (Digital_t));
cm_event_alloc(1, (outs + inouts) * sizeof (Digital_t));
/* Declare irreversible. */
if (PARAM(irreversible) > 0)
cm_irreversible(PARAM(irreversible));
return;
/* Handle malloc failures. */
no_out_vals:
free(ip->q);
no_q:
free(ip);
no_ip:
cm_message_send("No memory!");
return;
}
ip = STATIC_VAR(cosim_instance);
if (!ip) {
int ports;
/* Error state. Do nothing at all. */
ports = PORT_NULL(d_out) ? 0 : PORT_SIZE(d_out);
for (i = 0; i < ports; ++i)
OUTPUT_CHANGED(d_out[i]) = FALSE;
ports = PORT_NULL(d_inout) ? 0 : PORT_SIZE(d_inout);
for (i = 0; i < ports; ++i)
OUTPUT_CHANGED(d_inout[i]) = FALSE;
return;
}
in_vals = (Digital_t *)cm_event_get_ptr(0, 0);
if (TIME == 0.0) {
/* Starting, so inputs may be settling. */
for (i = 0; i < ip->in_ports; ++i) {
Digital_t ival;
ival = *(Digital_t *)INPUT(d_in[i]);
(*ip->info.in_fn)(&ip->info, i, &ival);
in_vals[i] = ival;
}
for (i = 0; i < ip->out_ports; ++i)
OUTPUT_CHANGED(d_out[i]) = FALSE;
for (i = 0; i < ip->inout_ports; ++i) {
Digital_t ival;
ival = *(Digital_t *)INPUT(d_inout[i]);
(*ip->info.in_fn)(&ip->info, i + ip->in_ports, &ival);
in_vals[i + ip->in_ports] = ival;
OUTPUT_CHANGED(d_inout[i]) = FALSE;
}
return;
}
if (CALL_TYPE == ANALOG) // Belt and braces
return;
/* Check for pending output. */
if (ip->op_pending) {
output(ip, XSPICE_ARG);
} else {
for (i = 0; i < ip->out_ports; ++i)
OUTPUT_CHANGED(d_out[i]) = FALSE;
for (i = 0; i < ip->inout_ports; ++i)
OUTPUT_CHANGED(d_inout[i]) = FALSE;
}
/* Check TIME as it may have gone backwards after a failed time-step. */
index = ip->q_index;
while (index >= 0 && TIME < ip->q[index].when)
--index;
ip->q_index = index;
if (CALL_TYPE == EVENT) {
struct pend_in input;
unsigned int limit, max;
/* New input is expected here. */
input.when = TIME;
limit = ip->info.in_count + ip->info.inout_count;
max = limit < ip->in_ports ? limit : ip->in_ports;
limit -= max;
for (input.which = 0; input.which < max; ++input.which) {
input.what = *(Digital_t *)INPUT(d_in[input.which]);
if (check_input(ip, in_vals + input.which, &input)) {
DBG("%.16g: IN %s %d/%d->%d/%d",
TIME, cm_get_node_name("d_in", input.which),
in_vals[input.which].state, in_vals[input.which].strength,
input.what.state, input.what.strength);
in_vals[input.which] = input.what;
}
}
if (limit > ip->inout_ports)
limit = ip->inout_ports;
for (i = 0; i < limit; ++i, ++input.which) {
input.what = *(Digital_t *)INPUT(d_inout[i]);
if (check_input(ip, in_vals + input.which, &input)) {
DBG("%.16g: INout %s %d/%d->%d/%d",
TIME, cm_get_node_name("d_inout", i),
in_vals[input.which].state, in_vals[input.which].strength,
input.what.state, input.what.strength);
in_vals[ip->in_ports + i] = input.what;
}
}
} else if (CALL_TYPE == STEP_PENDING) {
/* The current timestep succeeded. Run the co-simulator code
* forward, replaying any saved input events.
*/
if (TIME <= ip->info.vtime)
cm_message_printf("XSPICE time is behind vtime:\n"
"XSPICE %.16g\n"
"Cosim %.16g",
TIME, ip->info.vtime);
run(ip, XSPICE_ARG);
}
}

View File

@ -0,0 +1,102 @@
/* Copyright 2023 Giles Atkinson
SUMMARY
This file contains the interface specification file for the
d_cosim code model for general digital co-simulation.
=============================================================================*/
NAME_TABLE:
Spice_Model_Name: d_cosim
C_Function_Name: ucm_d_cosim
Description: "Bridge to an irreversible digital model"
PORT_TABLE:
Port_Name: d_in
Description: "digital input"
Direction: in
Default_Type: d
Allowed_Types: [d]
Vector: yes
Vector_Bounds: [0 -]
Null_Allowed: yes
PORT_TABLE:
Port_Name: d_out
Description: "digital output"
Direction: out
Default_Type: d
Allowed_Types: [d]
Vector: yes
Vector_Bounds: [0 -]
Null_Allowed: yes
PORT_TABLE:
Port_Name: d_inout
Description: "digital bidirectional port"
Direction: inout
Default_Type: d
Allowed_Types: [d]
Vector: yes
Vector_Bounds: [0 -]
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: delay
Description: "output delay time"
Data_Type: real
Default_Value: 1.0e-9
Limits: [1e-12 -]
Vector: no
Vector_bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: simulation
Description: "A shared library containing a digital model"
Data_Type: string
Default_Value: -
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: no
/* Instances maintain an internal input event queue that should be at least
* as large as the number of inputs. Performance with clocked logic may
* be improved by making it larger than (2 * F) / MTS, where F is
* the clock frequency and MTS is the maximum timestep for .tran.
*/
PARAMETER_TABLE:
Parameter_Name: queue_size
Description: "input queue size"
Data_Type: int
Default_Value: 128
Limits: [1 -]
Vector: no
Vector_bounds: -
Null_Allowed: yes
PARAMETER_TABLE:
Parameter_Name: irreversible
Description: "Parameter passed to library function cm_irreversible()"
Data_Type: int
Default_Value: 1
Limits: -
Vector: no
Vector_Bounds: -
Null_Allowed: yes
STATIC_VAR_TABLE:
Static_Var_Name: cosim_instance
Data_Type: pointer
Description: "Per-instance structure"

View File

@ -29,3 +29,4 @@ d_tff
d_tristate
d_xnor
d_xor
d_cosim

View File

@ -340,6 +340,11 @@ double cm_netlist_get_l(void) {
return (coreitf->dllitf_cm_netlist_get_l)();
}
void cm_irreversible(unsigned int place)
{
(coreitf->dllitf_cm_irreversible)(place);
}
const char *cm_get_node_name(const char *port, unsigned int index) {
return coreitf->dllitf_cm_get_node_name(port, index);
}

View File

@ -0,0 +1 @@
CL /O2 /LD /EHsc /Fe..\adc.DLL /I. /IC:\mingw64\share\verilator\include\vltstd /IC:\mingw64\share\verilator\include Vlng__ALL.cpp verilator_shim.cpp C:\mingw64\share\verilator\include\verilated.cpp C:\mingw64\share\verilator\include\verilated_threads.cpp /link /DLL /EXPORT:Cosim_setup

View File

@ -0,0 +1,3 @@
This directory contains Ngspice scripts and other files used to prepare
Verilog (and possibly VHDL) code to be included in an Ngspice simulation.
An example circuit can be found in examples/xspice/verilator.

View File

@ -0,0 +1,13 @@
/* Dummy main() for Verilator. */
#include "ngspice/cmtypes.h" // For Digital_t
#include "ngspice/cosim.h" // For struct co_info and prototypes
int main(int argc, char** argv, char**) {
struct co_info info;
Cosim_setup(&info);
for (;;)
(*info.step)(&info);
return 0;
}

View File

@ -0,0 +1,157 @@
/* This is a very mangled version of Vadc__main.cpp as generated by Verilator. */
// Verilated -*- C++ -*-
// DESCRIPTION: main() calling loop, created with Verilator --main
#include "verilated.h"
#include "Vlng.h"
#include "ngspice/cmtypes.h" // For Digital_t
#include "ngspice/cosim.h" // For struct co_info and prototypes
//======================
/* Structure for the input table. */
struct input {
const char *name;
unsigned int offset;
unsigned int count;
};
/* This VL_DATA macro is used in header files inputs.h, outputs.h and inouts.h
* to write functions accept_input() and step().
* The macro is used several times with different definitions.
*/
/* Generate the previous values table used by step(). */
#define VL_DATA(size, name, msb, lsb) + msb - lsb + 1
static const unsigned int outs = 0
#include "outputs.h"
;
static const unsigned int inouts = 0
#include "inouts.h"
;
static unsigned char previous_output[outs + inouts];
#undef VL_DATA
/* The input function: it should ignore out-of-range values of index. */
#define VL_DATA(size, name, msb, lsb) \
if (index >= msb - lsb + 1) { \
index -= msb - lsb + 1; \
} else if (msb == 0 && lsb == 0) { \
topp->name = val ? 1 : 0; \
return; \
} else { \
if (val) \
topp->name |= (1 << (msb - index)); \
else \
topp->name &= (1 << (msb - index)); \
return; \
}
static void accept_input(struct co_info *pinfo,
unsigned int index, Digital_t *vp)
{
Vlng *topp = (Vlng *)pinfo->handle;
unsigned int val, offset;
val = vp->state;
if (val == UNKNOWN)
return; // Verilator simulations are two-state.
#include "inputs.h"
/* For inout ports the new value must be stored to detect changes. */
offset = outs;
#undef VL_DATA
#define VL_DATA(size, name, msb, lsb) \
if (index >= msb - lsb + 1) { \
index -= msb - lsb + 1; \
offset += msb - lsb + 1; \
} else if (msb == 0 && lsb == 0) { \
topp->name = val ? 1 : 0; \
previous_output[index + offset] = val; \
return; \
} else { \
if (val) \
topp->name | (1 << (msb - index)); \
else \
topp->name &= (1 << (msb - index)); \
previous_output[index + offset] = val; \
return; \
}
#include "inouts.h"
}
#undef VL_DATA
/* The step function that calls the Verilator code. */
#define VL_DATA(size, name, msb, lsb) \
for (i = msb; i >= lsb; --i) { \
if (topp->name & (1 << i)) \
bit = 1; \
else \
bit = 0; \
if (bit ^ previous_output[index]) { \
previous_output[index] = bit; \
oval.state = (Digital_State_t)bit; \
(*pinfo->out_fn)(pinfo, index, &oval); \
} \
++index; \
}
static void step(struct co_info *pinfo)
{
static Digital_t oval = {ZERO, STRONG};
Vlng *topp;
int index, i;
unsigned char bit;
topp = (Vlng *)pinfo->handle;
topp->eval();
/* Now scan the outputs for changes. */
index = 0;
#include "outputs.h"
#include "inouts.h"
}
#undef VL_DATA
extern "C" void Cosim_setup(struct co_info *pinfo)
{
int i;
// Setup context, and defaults
Verilated::debug(0);
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
// Construct the Verilated model, from Vtop.h generated from Verilating
Vlng *topp{new Vlng{contextp.get()}};
/* Return information to caller. */
pinfo->handle = topp;
pinfo->step = step;
#define VL_DATA(size, name, msb, lsb) i += msb - lsb + 1; // Count ports
i = 0;
#include "inputs.h"
pinfo->in_count = i;
pinfo->out_count = outs;
pinfo->inout_count = inouts;
pinfo->in_fn = accept_input;
pinfo->method = After_input; // Verilator requires input to advance.
}
#undef VL_DATA

316
src/xspice/verilog/vlnggen Normal file
View File

@ -0,0 +1,316 @@
*ng_script_with_params
// This Ngspice interpreter script accepts arbitrary argiments to
// the Verilator compiler (Verilog to C++) and builds a shared library
// or DLL that can be loaded by the d_cosim XSPICE code model.
// Instances of the model are then digital circuit elements whose
// behaviour is controlled by the Verilog source.
set bad=0
if $?argc = 0
set bad=1
end
if $argc <= 0
set bad=1
end
if $bad
echo Arguments acceptable to Verilator are required.
quit
end
// Disable special processing of '{'.
set noglob
// Set parameters for Windows or Unix-like OS.
// For setting CFLAGS (passed to Verilator) it is somewhat arbitrarily
// assumed that if Ngspice was compiled with VisualC++, then that is
// the compiler to be used with Verilator. Edit to change.
// Compilation option for C/C++: -fpic is required by GCC for a shared library
if $oscompiled = 8 // VisualC++ - Verilator is a Perl script
setcs cflags="--CFLAGS -fpic --compiler msvc"
else
setcs cflags="--CFLAGS -fpic" // For g++
end
if $oscompiled = 2 | $oscompiled = 3 | $oscompiled = 8 // Windows
set windows=1
set dirsep1="\\"
set dirsep2="/"
set vloc="C:/mingw64/bin/verilator" // Expected location on Windows
set run_verilator="perl $vloc"
else
set windows=0
set dirsep1="/"
set run_verilator=verilator
end
if $oscompiled = 7 // MacOS
set macos=1
setcs cflags="$cflags --compiler clang"
else
set macos=0
end
// Loop through the arguments to find Verilog source: some_path/xxxx.v
// The output file will have the same base name.
let index=1
set off=1 // Avoid error in dowhile
repeat $argc
set base="$argv[$&index]"
let index = index + 1
strstr l "$base" ""
if $l > 2 // Look for xxxx.v
strslice tail "$base" -2 2
strcmp bad "$tail" ".v"
if $bad <> 0
set base=""
continue
end
let l = $l - 2
strslice base "$base" 0 $&l
else
set base=""
continue
end
dowhile $off >= 0 // Strip leading directories
strstr off "$base" "$dirsep1"
if $windows
if $off < 0
strstr off "$base" "$dirsep2"
end
end
if $off >= 0
let off=$off+1
strslice base "$base" $&off $l
end
end
strstr l "$base" "" // Check for zero-length string
if $l > 0
break
end
end
if index - 1 > $argc
set base=verilated // Default name
end
// Define working directory for Verilator
set tail="_obj_dir"
setcs objdir="$base$tail"
// Default base name of output file.
if $windows
setcs tail=".DLL"
else
setcs tail=".so"
end
setcs soname="$base$tail"
// First convert to C++, PREFIX determines the file names.
setcs prefix="Vlng"
// Run Verilator on the given input files.
shell $run_verilator --Mdir $objdir --prefix $prefix $cflags --cc $argv
// Parse the primary interface Class definition for members representing
// the ports of the top-level Verilog module.
// Example conversion: VL_IN8(&Clk,0,0); ==> VL_DATA(8,Clk,0,0)
cd $objdir
echo "/* Generated code: do not edit. */" > inouts.h
echo "/* Generated code: do not edit. */" > inputs.h
echo "/* Generated code: do not edit. */" > outputs.h
// This loop is intended to have the same effect as:
// sed --quiet -e 's/VL_IN\([0-9]*\)(&\(.*\);/VL_DATA(\1,\2/p' \
// obj_dir/${PREFIX}.h >> inputs.h
set htail=".h"
setcs inout="VL_INOUT"
setcs in="VL_IN"
setcs out="VL_OUT"
set fn="$prefix$htail" // Like foo-obj_dir/Vlng.h
fopen fh $fn
if $fh < 0
quit
end
while 1
fread line $fh l
if $l < 0
break
end
// Does it contain a closing parenthesis?
strstr off "$line" ")"
if $off < 0
continue // No ")", ignore.
end
let off = $off + 1
strslice line "$line" 0 $&off // Slice off tail.
// Is it an inout port definition?
strstr off "$line" $inout
if $off >= 0 // Match found
let off = $off + 8 // strlen("VL_INOUT") == 8
strslice line "$line" $&off $l
strstr off "$line" "("
strslice size "$line" 0 $off
let off = $off + 2 // strlen("(&") == 2
strslice line "$line" $&off $l
echo VL_DATA($size,$line >> inouts.h // New macro invocation
continue
end
// Input port?
strstr off "$line" $in
if $off >= 0 // Match found
let off = $off + 5 // strlen("VL_IN") == 5
strslice line "$line" $&off $l
strstr off "$line" "("
strslice size "$line" 0 $off
let off = $off + 2 // strlen("(&") == 2
strslice line "$line" $&off $l
echo VL_DATA($size,$line >> inputs.h // New macro invocation
continue
end
// Output port?
strstr off "$line" $out
if $off >= 0 // Match found
let off = $off + 6 // strlen("VL_OUT") == 6
strslice line "$line" $&off $l
strstr off "$line" "("
strslice size "$line" 0 $off
let off = $off + 2 // strlen("(&") == 2
strslice line "$line" $&off $l
echo VL_DATA($size,$line >> outputs.h // New macro invocation
continue
end
end
fclose $fh
cd ..
// The shared library/DLL contains some ngspice source code as
// well as that created by Verilator. Find it by scanning $sourcepath.
set shimfile=verilator_shim.cpp
set shimobj=verilator_shim.o
set mainfile=verilator_main.cpp
set srcdir=src
set silent_fileio // Silences fopen complaints
let i=1
repeat $#sourcepath
set stem="$sourcepath[$&i]"
let i = i + 1
set fn="$stem$dirsep1$shimfile"
fopen fh $fn
if $fh > 0
break
end
set stem="$stem$dirsep1$srcdir"
set fn="$stem$dirsep1$shimfile"
fopen fh $fn
if $fh > 0
break
end
end
if $fh > 0
fclose $fh
set fn_main="$stem$dirsep1$mainfile"
else
echo Can not find C++ source file $shimfile
quit
end
if $windows
// Verilator makes a mess of absolute include paths passed by --CFLAGS.
// Copy the files instead.
set incdir=ngspice
shell xcopy /i "$stem$dirsep1$incdir" "$objdir$dirsep1$incdir"
setcs include="--CFLAGS -I."
// Copy verilator_shim.cpp for MSVC.CMD.
shell copy "$fn" "$objdir"
else
// Some header files are with the source.
strstr off "$stem" "."
if $off <> 0
setcs include="--CFLAGS -I$stem"
else
setcs include="--CFLAGS -I..$dirsep1$stem" // Relative path
end
end
// Compile the code. Verilator only does that when building an executable,
// so include verilator_main.cpp.
shell $run_verilator --Mdir $objdir --prefix $prefix $include $cflags
+ --cc --build --exe
+ $fn_main $fn $argv
strcmp bad "$shellstatus" "0"
if $bad = 0
// g++ must be available: make a shared library/DLL.
set v_objs="$objdir$dirsep1$shimobj $objdir/verilated.o $objdir/verilated_threads.o"
setcs tail="__ALL.a"
setcs v_lib="$objdir/$prefix$tail" // Like Vlng__ALL.a
shell g++ --shared $v_objs $v_lib -pthread -lpthread -o $soname
else
// Assume we have CL.EXE and use that. A script avoids multiple \escapes.
if $windows = 0
quit
end
// Look for MSVC.CMD
set msvcfile=MSVC.CMD
let i=1
repeat $#sourcepath
set stem="$sourcepath[$&i]"
let i = i + 1
set fn="$stem$dirsep1$msvcfile"
fopen fh $fn
if $fh > 0
break
end
end
if $fh > 0
fclose $fh
else
echo Can not find bulid file $msvcfile
quit
end
echo Building with MSVC compiler, CL.EXE.
cd $objdir
shell $fn
cd ..
end
quit

View File

@ -12,7 +12,6 @@ set cmsrc=.\codemodels\Win32\Release
mkdir %dst%\bin
mkdir %dst%\lib\ngspice
mkdir %dst%\share\ngspice\scripts
copy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.OPENMP\vcomp140.dll" %dst%\bin\
copy %cmsrc%\analog.cm %dst%\lib\ngspice\analog.cm
@ -21,8 +20,6 @@ copy %cmsrc%\table.cm %dst%\lib\ngspice\table.cm
copy %cmsrc%\xtraevt.cm %dst%\lib\ngspice\xtraevt.cm
copy %cmsrc%\xtradev.cm %dst%\lib\ngspice\xtradev.cm
copy %cmsrc%\spice2poly.cm %dst%\lib\ngspice\spice2poly.cm
copy .\spinit_all %dst%\share\ngspice\scripts\spinit
copy .\spinitr .\spinit
if "%2" == "fftw" goto copy2
if "%3" == "fftw" goto copy2
@ -42,7 +39,6 @@ set cmsrc=.\codemodels\x64\Release
mkdir %dst%\bin
mkdir %dst%\lib\ngspice
mkdir %dst%\share\ngspice\scripts
copy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x64\Microsoft.VC140.OPENMP\vcomp140.dll" %dst%\bin\
copy %cmsrc%\analog64.cm %dst%\lib\ngspice\analog.cm
@ -51,8 +47,6 @@ copy %cmsrc%\table64.cm %dst%\lib\ngspice\table.cm
copy %cmsrc%\xtraevt64.cm %dst%\lib\ngspice\xtraevt.cm
copy %cmsrc%\xtradev64.cm %dst%\lib\ngspice\xtradev.cm
copy %cmsrc%\spice2poly64.cm %dst%\lib\ngspice\spice2poly.cm
copy .\spinit_all %dst%\share\ngspice\scripts\spinit
copy .\spinitr64 .\spinit
if "%2" == "fftw" goto copy2-64
if "%3" == "fftw" goto copy2-64
@ -65,3 +59,18 @@ copy %1\ngspice.exe %dst%\bin\
copy ..\..\fftw-3.3-dll64\libfftw3-3.dll %dst%\bin\
:end
mkdir %dst%\share\ngspice\scripts\src\ngspice
copy .\spinit_all %dst%\share\ngspice\scripts\spinit
copy .\spinitr .\spinit
cd ..\src
copy ciderinit %dst%\share\ngspice\scripts
copy devaxis %dst%\share\ngspice\scripts
copy devload %dst%\share\ngspice\scripts
copy setplot %dst%\share\ngspice\scripts
copy spectrum %dst%\share\ngspice\scripts
copy xspice\verilog\vlnggen %dst%\share\ngspice\scripts
copy xspice\verilog\MSVC.CMD %dst%\share\ngspice\scripts
copy xspice\verilog\*.cpp %dst%\share\ngspice\scripts\src
copy include\ngspice\cosim.h %dst%\share\ngspice\scripts\src\ngspice
copy include\ngspice\miftypes.h %dst%\share\ngspice\scripts\src\ngspice
copy include\ngspice\cmtypes.h %dst%\share\ngspice\scripts\src\ngspice

View File

@ -12,7 +12,6 @@ set cmsrc=.\codemodels\Win32\Debug
mkdir %dst%\bin
mkdir %dst%\lib\ngspice
mkdir %dst%\share\ngspice\scripts
copy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.OPENMP\vcomp140.dll" %dst%\bin\
copy %cmsrc%\analog.cm %dst%\lib\ngspice\analog.cm
@ -21,8 +20,6 @@ copy %cmsrc%\table.cm %dst%\lib\ngspice\table.cm
copy %cmsrc%\xtraevt.cm %dst%\lib\ngspice\xtraevt.cm
copy %cmsrc%\xtradev.cm %dst%\lib\ngspice\xtradev.cm
copy %cmsrc%\spice2poly.cm %dst%\lib\ngspice\spice2poly.cm
copy .\spinit_all %dst%\share\ngspice\scripts\spinit
copy .\spinitd .\spinit
if "%2" == "fftw" goto copy2
if "%3" == "fftw" goto copy2
@ -42,7 +39,6 @@ set cmsrc=.\codemodels\x64\Debug
mkdir %dst%\bin
mkdir %dst%\lib\ngspice
mkdir %dst%\share\ngspice\scripts
copy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x64\Microsoft.VC140.OPENMP\vcomp140.dll" %dst%\bin\
copy %cmsrc%\analog64.cm %dst%\lib\ngspice\analog.cm
@ -51,8 +47,6 @@ copy %cmsrc%\table64.cm %dst%\lib\ngspice\table.cm
copy %cmsrc%\xtraevt64.cm %dst%\lib\ngspice\xtraevt.cm
copy %cmsrc%\xtradev64.cm %dst%\lib\ngspice\xtradev.cm
copy %cmsrc%\spice2poly64.cm %dst%\lib\ngspice\spice2poly.cm
copy .\spinit_all %dst%\share\ngspice\scripts\spinit
copy .\spinitd64 .\spinit
if "%2" == "fftw" goto copy2-64
if "%3" == "fftw" goto copy2-64
@ -65,3 +59,18 @@ copy %1\ngspice.exe %dst%\bin\
copy ..\..\fftw-3.3-dll64\libfftw3-3.dll %dst%\bin\
:end
mkdir %dst%\share\ngspice\scripts\src\ngspice
copy .\spinit_all %dst%\share\ngspice\scripts\spinit
copy .\spinitr .\spinit
cd ..\src
copy ciderinit %dst%\share\ngspice\scripts
copy devaxis %dst%\share\ngspice\scripts
copy devload %dst%\share\ngspice\scripts
copy setplot %dst%\share\ngspice\scripts
copy spectrum %dst%\share\ngspice\scripts
copy xspice\verilog\vlnggen %dst%\share\ngspice\scripts
copy xspice\verilog\MSVC.CMD %dst%\share\ngspice\scripts
copy xspice\verilog\*.cpp %dst%\share\ngspice\scripts\src
copy include\ngspice\cosim.h %dst%\share\ngspice\scripts\src\ngspice
copy include\ngspice\miftypes.h %dst%\share\ngspice\scripts\src\ngspice
copy include\ngspice\cmtypes.h %dst%\share\ngspice\scripts\src\ngspice

View File

@ -395,6 +395,7 @@
<ClInclude Include="..\src\frontend\com_dump.h" />
<ClInclude Include="..\src\frontend\com_echo.h" />
<ClInclude Include="..\src\frontend\com_fft.h" />
<ClInclude Include="..\src\frontend\com_fileio.h" />
<ClInclude Include="..\src\frontend\com_ghelp.h" />
<ClInclude Include="..\src\frontend\com_gnuplot.h" />
<ClInclude Include="..\src\frontend\com_hardcopy.h" />
@ -1007,6 +1008,7 @@
<ClCompile Include="..\src\frontend\com_dump.c" />
<ClCompile Include="..\src\frontend\com_echo.c" />
<ClCompile Include="..\src\frontend\com_fft.c" />
<ClCompile Include="..\src\frontend\com_fileio.c" />
<ClCompile Include="..\src\frontend\com_ghelp.c" />
<ClCompile Include="..\src\frontend\com_gnuplot.c" />
<ClCompile Include="..\src\frontend\com_hardcopy.c" />
@ -1220,6 +1222,7 @@
<ClCompile Include="..\src\misc\string.c" />
<ClCompile Include="..\src\misc\tilde.c" />
<ClCompile Include="..\src\misc\util.c" />
<ClCompile Include="..\src\misc\win_time.c" />
<ClCompile Include="..\src\misc\wlist.c" />
<ClCompile Include="..\src\ngspice.c" />
<ClCompile Include="..\src\osdi\osdiacld.c" />

View File

@ -854,6 +854,7 @@ lib /machine:x64 /def:..\..\fftw-3.3-dll64\libfftw3-3.def /out:$(IntDir)libfftw3
<ClInclude Include="..\src\frontend\com_dump.h" />
<ClInclude Include="..\src\frontend\com_echo.h" />
<ClInclude Include="..\src\frontend\com_fft.h" />
<ClInclude Include="..\src\frontend\com_fileio.h" />
<ClInclude Include="..\src\frontend\com_ghelp.h" />
<ClInclude Include="..\src\frontend\com_gnuplot.h" />
<ClInclude Include="..\src\frontend\com_hardcopy.h" />
@ -1467,6 +1468,7 @@ lib /machine:x64 /def:..\..\fftw-3.3-dll64\libfftw3-3.def /out:$(IntDir)libfftw3
<ClCompile Include="..\src\frontend\com_dump.c" />
<ClCompile Include="..\src\frontend\com_echo.c" />
<ClCompile Include="..\src\frontend\com_fft.c" />
<ClCompile Include="..\src\frontend\com_fileio.c" />
<ClCompile Include="..\src\frontend\com_ghelp.c" />
<ClCompile Include="..\src\frontend\com_gnuplot.c" />
<ClCompile Include="..\src\frontend\com_hardcopy.c" />
@ -1686,6 +1688,7 @@ lib /machine:x64 /def:..\..\fftw-3.3-dll64\libfftw3-3.def /out:$(IntDir)libfftw3
<ClCompile Include="..\src\misc\string.c" />
<ClCompile Include="..\src\misc\tilde.c" />
<ClCompile Include="..\src\misc\util.c" />
<ClCompile Include="..\src\misc\win_time.c" />
<ClCompile Include="..\src\misc\wlist.c" />
<ClCompile Include="..\src\ngspice.c" />
<ClCompile Include="..\src\osdi\osdiacld.c" />

View File

@ -885,6 +885,7 @@
<ClInclude Include="..\src\frontend\com_shift.h" />
<ClInclude Include="..\src\frontend\com_state.h" />
<ClInclude Include="..\src\frontend\com_strcmp.h" />
<ClInclude Include="..\src\frontend\com_fileio.h" />
<ClInclude Include="..\src\frontend\com_unset.h" />
<ClInclude Include="..\src\frontend\com_wr_ic.h" />
<ClInclude Include="..\src\frontend\control.h" />
@ -1498,6 +1499,7 @@
<ClCompile Include="..\src\frontend\com_shift.c" />
<ClCompile Include="..\src\frontend\com_state.c" />
<ClCompile Include="..\src\frontend\com_strcmp.c" />
<ClCompile Include="..\src\frontend\com_fileio.c" />
<ClCompile Include="..\src\frontend\com_sysinfo.c" />
<ClCompile Include="..\src\frontend\com_unset.c" />
<ClCompile Include="..\src\frontend\com_wr_ic.c" />
@ -1700,6 +1702,7 @@
<ClCompile Include="..\src\misc\string.c" />
<ClCompile Include="..\src\misc\tilde.c" />
<ClCompile Include="..\src\misc\util.c" />
<ClCompile Include="..\src\misc\win_time.c" />
<ClCompile Include="..\src\misc\wlist.c" />
<ClCompile Include="..\src\ngspice.c" />
<ClCompile Include="..\src\osdi\osdiacld.c" />
@ -2893,4 +2896,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -322,6 +322,10 @@
<AdditionalIncludeDirectories>..\..\src\xspice\%(RelativeDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<ClCompile Include="icm\digital\d_xor\d_xor-ifspec.c" />
<ClCompile Include="icm\digital\d_cosim\d_cosim-ifspec.c" />
<ClCompile Include="icm\digital\d_cosim\d_cosim-cfunc.c">
<AdditionalIncludeDirectories>..\..\src\xspice\%(RelativeDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<ClCompile Include="..\..\src\xspice\icm\dlmain.c" />
</ItemGroup>
<ItemGroup>