Additions and changes for using VVP as DLL with an analog simulator.

This commit is contained in:
Kevin Cameron 2008-11-13 01:09:20 -08:00 committed by Stephen Williams
parent e0a1b41b37
commit 4ee9cc6247
10 changed files with 1267 additions and 12 deletions

458
vpi/bindsigs.cc Normal file
View File

@ -0,0 +1,458 @@
/*
* Copyright (c) 2008 True Circuits Inc.
*
* Author: Kevin Cameron
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
# include <dlfcn.h>
# include <math.h>
# include "vpi_user.h"
# include "vpi_priv.h"
# include "vpi_user.h"
# include "acc_user.h"
# define DECL__IVL_PARM_NM
# include "extpwl.h"
static int bs_debug;
static PLI_INT32 bindsigs_compiletf(PLI_BYTE8 *user_data)
{
static int shown = 0;
if (!bs_debug) {
char *env = getenv("BS_DEBUG");
if (env && !(bs_debug = atoi(env))) {
bs_debug = 1;
} else {
bs_debug = -1;
}
}
if (bs_debug > 0 && !shown++) {
fprintf(stderr,"Bindsigs compiled in.\n");
}
return 0;
}
static SpcIvlCB **(*get_lists)();
extern double SimTimeD;
static void print_inst(FILE *fp,struct __vpiScope *scope)
{
if (scope->scope) {
print_inst(fp,scope->scope);
fputs(".",fp);
}
fputs(scope->name,fp);
}
extern "C" int BSgetPWR(vpiHandle argh,SpcIvlCB *cb)
{
struct __vpiScope *scp = (struct __vpiScope *)acc_handle_scope(argh);
for (; scp ; scp = scp->scope) {
vpiHandle neth,net_iter;
eBLtype bl = BL_REG;
for (net_iter = vpi_iterate(vpiReg, &scp->base)
; bl >= BL_NET ; ((int)bl)--,
net_iter = vpi_iterate(vpiNet, &scp->base)) {
if (net_iter) while (neth = vpi_scan(net_iter)) {
struct __vpiSignal *sgp = (struct __vpiSignal*)neth;
const char *nm = sgp->id.name;
int i;
if (((i=0,0 == strcmp("vss",nm)) ||
(i=1,0 == strcmp("vdd",nm)) ||
(i=0,0 == strcmp("vss__i",nm)) ||
(i=1,0 == strcmp("vdd__i",nm))) && !cb->pwr[i]) {
for (SpcIvlCB *ub = (*get_lists)()[bl]; ub ; ub = ub->next) {
if (ub->sig == sgp) {
cb->pwr[i] = ub; goto next;
}
}
next:;
}
}
if (cb->pwr[0] && cb->pwr[1]) {
if (bs_debug > 0) {
fprintf(stderr,"Power scope: ");
print_inst(stderr,scp);
fprintf(stderr,"\n");
}
return 1;
}
}
}
return 0;
}
static PLI_INT32 bindsigs_calltf(PLI_BYTE8 *user_data)
{
if (!get_lists) {
get_lists = (typeof(get_lists))dlsym(0,"spicenets");
if (!get_lists) {
fprintf(stderr,"Bindsigs can't find support function: spicenets\n");
return -1;
}
}
SpcIvlCB **lists = (*get_lists)();
vpiHandle systfref, args_iter, argh, scope;
struct t_vpi_value argval;
char *value;
// Obtain a handle to the argument list
systfref = vpi_handle(vpiSysTfCall, NULL);
args_iter = vpi_iterate(vpiArgument, systfref);
// Grab the value of the first argument
while (argh = vpi_scan(args_iter)) {
struct __vpiSignal *rfp = 0;
int reg = 1;
switch (argh->vpi_type->type_code) {
case vpiNet: reg = 0;
case vpiReg: rfp = (struct __vpiSignal*)argh;
}
assert(rfp);
struct __vpiScope *scope = (struct __vpiScope *)acc_handle_scope(argh),
*up;
if (bs_debug > 0) {
fprintf(stderr,"Binding ");
print_inst(stderr,scope);
fprintf(stderr,".%s",rfp->id.name);
}
SpcIvlCB *ub,**ubp = &lists[0];
while (ub = *ubp) {
const char *spec = ub->spec,
*name = rfp->id.name;
int hier = 0;
while (*spec) if ('.' == *spec++) hier++;
if (hier) {
int ln = strlen(name),
ls = 0;
for (up = scope ;;) {
for (ls =0; spec-- > ub->spec && '.' != *spec; ls++);
if (!(ln == ls && (0 == strncmp(spec+1,name,ls)
|| ('_' == *name && 0 == hier)))) goto next;
if (hier-- <= 0) break;
if (name == up->name) {
up = up->scope;
}
ln = strlen(name = up->name);
}
break;
} else {
if (0 == strcmp(ub->spec,name)) {
break;
}
}
next:
ubp = &ub->next;
}
assert(ub);
if (bs_debug > 0) {
fprintf(stderr," to %s\n",ub->spec);
}
rfp->ext_bound = 1 + reg; // eBLtype
*ubp = ub->next;
ub->next = lists[rfp->ext_bound];
lists[rfp->ext_bound] = ub;
ub->sig = rfp;
__vpiScope *scp = rfp->within.scope;
vpiHandle prmh, prm_iter = vpi_iterate(vpiParameter, &scp->base);
if (prm_iter) {
struct __vpiRealVar *parm;
int nml = strlen(rfp->id.name),
i,p = sizeof(ub->parm_nm)/sizeof(*ub->parm_nm),
mode = 0;
double defaults[p];
for (i = p; i-- > 0;) defaults[i] = nan("");
while (prmh = vpi_scan(prm_iter)) {
char *pnm = vpi_get_str(vpiName,prmh);
if (0 == strcmp(pnm,"mode")) {
argval.format = vpiIntVal;
vpi_get_value(prmh, &argval);
mode = argval.value.integer;
} else for (i = p; i-- > 0;) {
if (0 == strcmp(pnm,SpcIvlCB::parm_nm[i])) {
argval.format = vpiRealVal;
vpi_get_value(prmh, &argval);
defaults[i] = argval.value.real;
}
}
}
ub->mode = mode;
ub->set_parms(defaults);
prm_iter = vpi_iterate(vpiParameter, &scp->base);
while (prmh = vpi_scan(prm_iter)) {
char *pnm = vpi_get_str(vpiName,prmh);
if (0 == strncmp(pnm,rfp->id.name,nml)) {
const char *sub = pnm + nml;
if ('_' == *sub++) {
if (0 == strcmp(sub,"mode")) {
argval.format = vpiIntVal;
vpi_get_value(prmh, &argval);
ub->mode = argval.value.integer;
} else for (i = p; i-- > 0;) {
if (0 == strcmp(sub,ub->parm_nm[i])) {
argval.format = vpiRealVal;
vpi_get_value(prmh, &argval);
ub->parms()[i] = argval.value.real;
}
}
}
}
}
set_if_not(ub->lo, ub->vss);
set_if_not(ub->hi, ub->vdd);
set_if_not(ub->fall, ub->rise);
set_if_not(ub->thrshld[0],(ub->hi + ub->lo)/2);
set_if_not(ub->thrshld[0],0.5);
set_if_not(ub->thrshld[1],ub->thrshld[0]);
set_if_not(ub->last_value,ub->init_v);
set_if_not(ub->prec, pow(10,scp->time_precision));
// constrain first timestep
if (ub->coeffs[1] == ub->init_v) {
ub->coeffs[2] = SimTimeA + ub->prec/2;
ub->coeffs[3] = ub->fillEoT(4,ub->init_v);
} else {
ub->coeffs[2] = SimTimeA;
ub->coeffs[4] = SimTimeA + ub->prec/2;
ub->coeffs[5] = ub->fillEoT(6,ub->init_v);
}
if (3 == mode && !((0 == strncmp("vss",rfp->id.name,3) ||
(0 == strncmp("vdd",rfp->id.name,3))))) {
if (!BSgetPWR(argh,ub)) {
fprintf(stderr,"Power not found for: ");
print_inst(stderr,scp);
fprintf(stderr,".%s\n",rfp->id.name);
}
}
}
next_arg:;
}
return 0;
}
static void bindsigs_register()
{
s_vpi_systf_data tf_data;
tf_data.type = vpiSysTask;
tf_data.tfname = "$bindsigs";
tf_data.calltf = bindsigs_calltf;
tf_data.compiletf = bindsigs_compiletf;
tf_data.sizetf = 0;
tf_data.user_data = 0;
vpi_register_systf(&tf_data);
}
static PLI_INT32 sync_out_calltf(PLI_BYTE8 *user_data)
{
if (!get_lists) return 0;
vpiHandle systfref, args_iter, argh;
struct t_vpi_value argval;
char *value;
// Obtain a handle to the argument list
systfref = vpi_handle(vpiSysTfCall, NULL);
args_iter = vpi_iterate(vpiArgument, systfref);
// Grab the value of the first argument
argh = vpi_scan(args_iter);
struct __vpiSignal *rfp = 0;
int reg = 1;
switch (argh->vpi_type->type_code) {
case vpiNet: reg = 0;
case vpiReg: rfp = (struct __vpiSignal*)argh;
}
assert(rfp);
argval.format = vpiIntVal;
vpi_get_value(argh, &argval);
SpcIvlCB *scan = (*get_lists)()[BL_NET];
double v;
char *fmt;
for (; scan; scan = scan->next) {
if (scan->sig == rfp) {
fmt = "Syncing net %s=%d\n"; goto found;
}
}
for (scan = get_lists()[BL_REG]; scan; scan = scan->next) {
if (scan->sig == rfp) {
fmt = "Syncing reg %s=%d\n"; goto found;
}
}
goto done;
found:
switch (scan->mode) {
case 3: {
scan->lo = scan->Pwr(0,"BSgetPWR")->last_value;
scan->hi = scan->Pwr(1,"BSgetPWR")->last_value;
}
case 2:
if (!isnan(scan->coeffs[4])) { // check on ramp
int s = 0;
double now = SimTimeA;
while (now >= scan->coeffs[2+s]) { s += 2; }
double slew = scan->coeffs[3+s] - scan->coeffs[1+s];
if (0.0 == slew) {
slew = scan->coeffs[1+s] - ((scan->hi + scan->lo)/2);
}
if ((slew > 0) != argval.value.integer) {
scan->go2 = argval.value.integer;
scan->last_error = scan->last_time;
if (scan->reported = (bs_debug > 0)) {
fprintf(stderr,"Warning: PWL/logic mismatch on ");
print_inst(stderr,rfp->within.scope);
fprintf(stderr,".%s (->%d @ %g)\n",
rfp->id.name,argval.value.integer,now);
scan->dumpPWL(stderr,now);
}
} else if (scan->last_error >= 0.0) {
double dt = now-scan->last_error;
if (bs_debug > 0) {
fprintf(stderr,"Info: PWL OK ");
print_inst(stderr,rfp->within.scope);
fprintf(stderr,".%s (->%d @ %g, dt: %g)\n",
rfp->id.name,argval.value.integer,now,dt);
scan->dumpPWL(stderr,now);
} else if (dt > scan->prec/2) {
fprintf(stderr,"Warning: PWL/logic mismatch on ");
print_inst(stderr,rfp->within.scope);
fprintf(stderr,".%s (->%d @ %g for %g)\n",
rfp->id.name,argval.value.integer,now-dt,dt);
}
scan->last_error = -1;
}
break;
}
default:
sync:
v = scan->lo + argval.value.integer * (scan->hi - scan->lo);
if (v != scan->coeffs[1]) {
if (bs_debug > 0) {
fprintf(stderr,fmt,rfp->id.name,argval.value.integer);
}
// scan->coeffs[0] = 0.0;
scan->coeffs[1] = v;
scan->coeffs[2] = EndOfTimeD;
scan->coeffs[3] = v;
}
goto done;
}
done:
// Cleanup and return
vpi_free_object(args_iter);
return 0;
}
static void sync_out_register()
{
s_vpi_systf_data tf_data;
tf_data.type = vpiSysTask;
tf_data.tfname = "$sync_out";
tf_data.calltf = sync_out_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = 0;
vpi_register_systf(&tf_data);
}
static PLI_INT32 sync_in_calltf(PLI_BYTE8 *user_data)
{
if (!get_lists) return 0;
vpiHandle systfref, args_iter, argh;
struct t_vpi_value argval;
char *value;
// Obtain a handle to the argument list
systfref = vpi_handle(vpiSysTfCall, NULL);
args_iter = vpi_iterate(vpiArgument, systfref);
// Grab the value of the first argument
argh = vpi_scan(args_iter);
struct __vpiSignal *rfp = 0;
int reg = 1;
switch (argh->vpi_type->type_code) {
case vpiNet: reg = 0;
case vpiReg: rfp = (struct __vpiSignal*)argh; break;
}
// if (!reg) rfp = findReg(rfp);
assert(rfp);
if (bs_debug > 0) {
fprintf(stderr,"Syncing %s\n",rfp->id.name);
}
// Cleanup and return
vpi_free_object(args_iter);
return 0;
}
static void sync_in_register()
{
s_vpi_systf_data tf_data;
tf_data.type = vpiSysTask;
tf_data.tfname = "$sync_in";
tf_data.calltf = sync_in_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = 0;
vpi_register_systf(&tf_data);
}
extern "C" {
void (*vlog_startup_routines[])() = {bindsigs_register,
sync_out_register,
sync_in_register,
0};
}

View File

@ -47,7 +47,9 @@ MAN = @MAN@
PS2PDF = @PS2PDF@ PS2PDF = @PS2PDF@
CPPFLAGS = @ident_support@ -I. -I.. -I $(srcdir) -I$(srcdir)/.. @CPPFLAGS@ @DEFS@ CPPFLAGS = @ident_support@ -I. -I.. -I $(srcdir) -I$(srcdir)/.. @CPPFLAGS@ @DEFS@
CXXFLAGS = -Wall @CXXFLAGS@ CFLAGS = -Wall -g -O2 $(CSHRDFLAGS)
CXXFLAGS = $(CFLAGS)
LDFLAGS_SO = @rdynamic@ -shared
LDFLAGS = @rdynamic@ @LDFLAGS@ LDFLAGS = @rdynamic@ @LDFLAGS@
LIBS = @LIBS@ @EXTRALIBS@ LIBS = @LIBS@ @EXTRALIBS@
@ -58,7 +60,17 @@ ifneq (x@vpidir2@,x)
MDIR2 = -DMODULE_DIR2=\"$(libdir)/ivl/@vpidir2@\" MDIR2 = -DMODULE_DIR2=\"$(libdir)/ivl/@vpidir2@\"
endif endif
ifdef VVP_SHARED
ENTRY_OBJ=main-shrd.o
EXES=
all: dep libvvp.so
SHARED_LIBS=$(libdir)/libvvp.so
CSHRDFLAGS=-fPIC
else
ENTRY_OBJ=main.o
EXES=$(bindir)/vvp@EXEEXT@
all: dep vvp@EXEEXT@ libvpi.a all: dep vvp@EXEEXT@ libvpi.a
endif
clean: clean:
rm -f *.o *~ parse.cc parse.cc.output parse.h lexor.cc tables.cc rm -f *.o *~ parse.cc parse.cc.output parse.h lexor.cc tables.cc
@ -75,12 +87,12 @@ vpi_priv.o vpi_scope.o vpi_real.o vpi_signal.o vpi_tasks.o vpi_time.o \
vpi_vthr_vector.o vpip_bin.o vpip_hex.o vpip_oct.o \ vpi_vthr_vector.o vpip_bin.o vpip_hex.o vpip_oct.o \
vpip_to_dec.o vpip_format.o vvp_vpi.o vpip_to_dec.o vpip_format.o vvp_vpi.o
O = main.o parse.o parse_misc.o lexor.o arith.o array.o bufif.o compile.o \ O = $(ENTRY_OBJ) parse.o parse_misc.o lexor.o arith.o array.o bufif.o compile.o \
concat.o \ concat.o \
dff.o extend.o npmos.o part.o reduce.o resolv.o sfunc.o stop.o symbols.o \ dff.o extend.o npmos.o part.o reduce.o resolv.o sfunc.o stop.o symbols.o \
ufunc.o codes.o \ ufunc.o codes.o \
vthread.o schedule.o statistics.o tables.o udp.o vvp_island.o vvp_net.o \ vthread.o schedule.o statistics.o tables.o udp.o vvp_island.o vvp_net.o \
event.o logic.o delay.o words.o $V event.o logic.o delay.o words.o spice.o $V
ifeq (@WIN32@,yes) ifeq (@WIN32@,yes)
# Under Windows (mingw) I need to make the ivl.exe in two steps. # Under Windows (mingw) I need to make the ivl.exe in two steps.
@ -101,6 +113,12 @@ libvpi.a: libvpi.c
vvp@EXEEXT@: $O vvp@EXEEXT@: $O
$(CXX) $(LDFLAGS) -o vvp@EXEEXT@ $O $(LIBS) $(dllib) $(CXX) $(LDFLAGS) -o vvp@EXEEXT@ $O $(LIBS) $(dllib)
libvvp.so: $O
$(CXX) $(LDFLAGS_SO) -o $@ $O $(LIBS) $(dllib)
$(libdir)/%.so: %.so
endif endif
dep: dep:
@ -110,6 +128,10 @@ dep:
$(CXX) $(CPPFLAGS) $(MDIR1) $(MDIR2) $(CXXFLAGS) -MD -c $< -o $*.o $(CXX) $(CPPFLAGS) $(MDIR1) $(MDIR2) $(CXXFLAGS) -MD -c $< -o $*.o
mv $*.d dep/$*.d mv $*.d dep/$*.d
%-shrd.o: %.cc
$(CXX) $(CPPFLAGS) $(MDIR1) $(MDIR2) $(CXXFLAGS) -DVVP_SHARED -MD -c $< -o $@
mv $*-shrd.d dep/$*.d
%.o: %.c %.o: %.c
$(CC) $(CPPFLAGS) $(MDIR1) $(MDIR2) $(CFLAGS) -MD -c $< -o $*.o $(CC) $(CPPFLAGS) $(MDIR1) $(MDIR2) $(CFLAGS) -MD -c $< -o $*.o
mv $*.d dep/$*.d mv $*.d dep/$*.d
@ -158,7 +180,8 @@ Makefile: Makefile.in config.status
./config.status ./config.status
install: all installdirs $(bindir)/vvp@EXEEXT@ $(libdir)/libvpi.a $(INSTALL_DOC) install: all installdirs $(EXES) $(libdir)/libvpi.a $(INSTALL_DOC)\
$(SHARED_LIBS)
$(bindir)/vvp@EXEEXT@: ./vvp@EXEEXT@ $(bindir)/vvp@EXEEXT@: ./vvp@EXEEXT@
$(INSTALL_PROGRAM) ./vvp@EXEEXT@ $(DESTDIR)$(bindir)/vvp@EXEEXT@ $(INSTALL_PROGRAM) ./vvp@EXEEXT@ $(DESTDIR)$(bindir)/vvp@EXEEXT@
@ -166,6 +189,9 @@ $(bindir)/vvp@EXEEXT@: ./vvp@EXEEXT@
$(libdir)/libvpi.a : ./libvpi.a $(libdir)/libvpi.a : ./libvpi.a
$(INSTALL_DATA) libvpi.a $(DESTDIR)$(libdir)/libvpi.a $(INSTALL_DATA) libvpi.a $(DESTDIR)$(libdir)/libvpi.a
$(libdir)/libvvp.so : ./libvvp.so
$(INSTALL_DATA) libvvp.so $(DESTDIR)$(libdir)/libvvp.so
$(mandir)/man1/vvp.1: $(srcdir)/vvp.man $(mandir)/man1/vvp.1: $(srcdir)/vvp.man
$(INSTALL_DATA) $(srcdir)/vvp.man $(DESTDIR)$(mandir)/man1/vvp.1 $(INSTALL_DATA) $(srcdir)/vvp.man $(DESTDIR)$(mandir)/man1/vvp.1

222
vvp/extpwl.h Normal file
View File

@ -0,0 +1,222 @@
/*
* Copyright (c) 2008 True Circuits Inc.
*
* Author: Kevin Cameron
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <dlfcn.h>
int vvp_main(int,char **);
enum eCBmode { // !!! keep in sync with Spice
CB_LOAD = 0,
CB_TRUNC,
CB_ACCEPT
};
const double EndOfTimeD = 1e99;
template<int S>
struct SpiceCallback
{
typedef double *(*SpiceCB)(SpiceCallback *,double,eCBmode,...);
double coeffs[S];
SpiceCB eval;
char *spec;
void *dll_ref;
void (*set_active)(void *,void *,double time);
inline int Slots() {return S;}
void setCoeffs(double *cof) {
int t = S;
while (t-- > 0) coeffs[t] = cof[t];
}
inline double fillEoT(int t,double val = 0.0) {
assert(t < S);
while (t < S) {
coeffs[t++] = EndOfTimeD;
coeffs[t++] = val;
}
return val;
}
SpiceCallback(double val = 0.0,double dt0 = EndOfTimeD) {
coeffs[0] = 0.0;
coeffs[1] = val;
coeffs[2] = dt0;
coeffs[3] = val;
fillEoT(4,val);
}
void dumpPWL(FILE *fp = stderr,double now = 0.0);
inline void checkPWL(double now = 0.0,double end = 0.0,
FILE *fp = stderr) {
for (int i = 2; i < S; i += 2) {
if (coeffs[i-2] > coeffs[i]) {
dumpPWL(fp);
assert(!"Bad waveform");
}
if (end > 0.0 && coeffs[i] >= end) { break; }
}
}
};
enum eBLtype {
BL_UNBOUND = 0,
BL_NET,
BL_REG,
BL_LAST
};
extern "C" void *bindnet(char *spec,char T,int *coeffs,
void *,void (*set_active)(void *,void *,double));
#ifdef __vpi_priv_H
#define IVL_PWL_SLOTS 16 /* 8 pairs */
struct SpcIvlCB : SpiceCallback<IVL_PWL_SLOTS> {
SpcIvlCB *next,
*active; // non-local drive
__vpiSignal *sig; // boundary or driven signal
// Params - keep together (see below)
double vss,vdd;
double lo,hi; // drive levels
double thrshld[2]; // rising,falling
double rise,fall; // times
double init_v; // initial voltage
double prec; // local precision
double last_time,
last_value,
last_error;
SpcIvlCB *pwr[2]; // vss,vdd
char set, // value is known
others, // has other drivers
non_local, // > 0 => not boundary
mode; // 0 - discrete
// 1 - ramp over NBA
// 2 - sync event
// 3 - + tie to local vss/vdd
char go2, // error fix
reported;
static const char *parm_nm[]; // SAME ORDER as fields
inline double *parms() {return &vss;}
double *set_parms(double *);
SpcIvlCB(double dt0 = EndOfTimeD,double val = 0.0,
const char *nan_tag = "")
: SpiceCallback <IVL_PWL_SLOTS>(val,dt0) {
next = 0;
sig = 0;
active = 0;
vss = nan(nan_tag);
vdd = nan(nan_tag);
hi = nan(nan_tag);
lo = nan(nan_tag);
thrshld[0] = nan(nan_tag);
thrshld[1] = nan(nan_tag);
rise = nan(nan_tag);
fall = nan(nan_tag);
init_v = nan(nan_tag);
prec = nan(nan_tag);
set = 0;
others = 0;
non_local = 0;
mode = 0;
last_time = -1;
last_value = 0;
last_error = -1;
pwr[0] = 0;
pwr[1] = 0;
}
typedef int (*pwr_fn)(vpiHandle argh,SpcIvlCB *);
inline SpcIvlCB *Pwr(int i,const char *pfn_nm) {
if (!pwr[i]) {
pwr_fn pfn;
if (pfn_nm) {
pfn = (pwr_fn)dlsym(0,pfn_nm);
}
if (pfn) {
// (*pfn)(sig,this);
}
if (!pwr[i] && non_local) {
pwr[i] = next->pwr[i];
}
assert(pwr[i]);
}
return pwr[i];
}
inline double Rise() { if (isnan(rise) && non_local > 0) {
rise = next->rise;
assert(!isnan(rise)); }
return rise; }
inline double Fall() { if (isnan(fall) && non_local > 0) {
fall = next->fall;
assert(!isnan(fall)); }
return fall; }
inline double LastValue() { return non_local > 0 ? next->last_value
: last_value; }
};
struct SpcDllData {
char active;
double next_time;
void (*activate)(SpcDllData *,SpcIvlCB *,double);
};
void ActivateCB(double time,SpcIvlCB *);
#ifdef DECL__IVL_PARM_NM
inline void set_if_not(double &prm,double val) {
if (isnan(prm)) prm = val;
}
const char *SpcIvlCB::parm_nm[] = // SAME ORDER as fields above
{"vss","vdd",
"lo","hi",
"thrsh0","thrsh1",
"rise","fall",
"init_v",
"prec"};
double *SpcIvlCB::set_parms(double *from)
{
int p = sizeof(parm_nm)/sizeof(*parm_nm);
while (p--) parms()[p] = from[p];
}
#endif
extern "C" SpcIvlCB **spicenets();
#endif

View File

@ -138,7 +138,13 @@ const char*module_tab[64];
extern void vpi_mcd_init(FILE *log); extern void vpi_mcd_init(FILE *log);
extern void vvp_vpi_init(void); extern void vvp_vpi_init(void);
#ifdef VVP_SHARED
int vvp_master = 0;
int vvp_main(int argc, char*argv[])
#else
int vvp_master = 1;
int main(int argc, char*argv[]) int main(int argc, char*argv[])
#endif
{ {
int opt; int opt;
unsigned flag_errors = 0; unsigned flag_errors = 0;
@ -303,6 +309,8 @@ int main(int argc, char*argv[])
} }
#ifndef VVP_SHARED
schedule_simulate(); schedule_simulate();
if (verbose_flag) { if (verbose_flag) {
@ -328,5 +336,7 @@ int main(int argc, char*argv[])
count_gen_events, count_gen_pool()); count_gen_events, count_gen_pool());
} }
#endif
return vvp_return_value; return vvp_return_value;
} }

View File

@ -20,12 +20,14 @@
# include "schedule.h" # include "schedule.h"
# include "vthread.h" # include "vthread.h"
# include "slab.h" # include "slab.h"
# include "vpi_priv.h"
# include <new> # include <new>
# include <signal.h> # include <signal.h>
# include <stdlib.h> # include <stdlib.h>
# include <assert.h> # include <assert.h>
# include <stdio.h> # include <stdio.h>
# include "extpwl.h"
unsigned long count_assign_events = 0; unsigned long count_assign_events = 0;
unsigned long count_gen_events = 0; unsigned long count_gen_events = 0;
@ -722,8 +724,243 @@ static void run_rosync(struct event_time_s*ctim)
} }
} }
void schedule_simulate(void) inline double getdtime(struct event_time_s *et)
{ {
return et->delay * pow(10.0,vpip_get_time_precision());
}
double SimTimeD = 0.0;
double SimTimeA = 0.0;
double SimTimeDlast = 0.0;
void schedule_assign_pv_sync(vvp_net_t *node,int lgc, double time)
{
double d_dly = time - SimTimeD;
int i_dly = d_dly / pow(10.0,vpip_get_time_precision());
vvp_time64_t dly(i_dly);
vvp_vector4_t val(1,lgc);
vvp_sub_pointer_t<vvp_net_t> ptr(node,0);
schedule_assign_plucked_vector(ptr,dly,val,0,1);
}
static SpcIvlCB *reattach(SpcIvlCB **nodes,vvp_net_t *primary,
vvp_net_ptr_t ptr)
{
vvp_net_t *cur;
vvp_net_ptr_t next;
for (; cur = ptr.ptr(); ptr = next) {
int port = ptr.port();
next = cur->port[port];
SpcIvlCB *scn_n;
for (int l = BL_NET; l <= BL_REG ; l++) {
SpcIvlCB **scn_p = &nodes[l];
for (; scn_n = *scn_p ; scn_p = &scn_n->next) {
if (scn_n->sig->node == cur) {
scn_n->others = 1;
SpcIvlCB *sub = new SpcIvlCB;
sub->next = scn_n;
sub->mode = scn_n->mode;
sub->sig = new __vpiSignal();
sub->sig->node = primary;
sub->non_local = 1;
return sub;
}
}
}
if (scn_n = reattach(nodes,primary,ptr.ptr()->out)) {
return scn_n;
}
}
return 0;
}
static PLI_INT32 extMarker(struct t_cb_data *cb)
{
return 0;
}
static void doExtPwl(struct event_s *nbas,struct event_time_s *et)
{
struct event_s *scan = nbas;
SpcIvlCB **nodes = spicenets();
static t_vpi_time never = {vpiSuppressTime};
while (scan) {
assign_vector4_event_s *v4 = dynamic_cast<assign_vector4_event_s *>(scan);
while (v4) {
SpcIvlCB *scn_n;
vvp_net_t *node = v4->ptr.ptr();
vvp_fun_signal_base *sig_fun = dynamic_cast<vvp_fun_signal_base*>(node->fun);
__vpiCallback *cb = sig_fun->has_vpi_callback(extMarker);
if (cb) {
if (scn_n = (SpcIvlCB *)cb->cb_data.user_data) goto propagate;
goto next_nba;
}
for (scn_n = nodes[2]; scn_n ; scn_n = scn_n->next) {
if (scn_n->sig->node == node) {
setup:
cb = new_vpi_callback();
cb->cb_data.cb_rtn = extMarker;
cb->cb_data.user_data = (char *)scn_n;
cb->cb_data.value = 0;
cb->cb_data.time = &never;
sig_fun->add_vpi_callback(cb);
propagate:
static vvp_vector4_t lgc1(1,true),lgc0(1,false),lgcx(1);
switch (scn_n->mode) {
case 1: {
scn_n->coeffs[0] = SimTimeD;
// scn_n->coeffs[1] = as is
scn_n->coeffs[2] = SimTimeD + getdtime(et);
scn_n->coeffs[3] = scn_n->lo
+ v4->val.eeq(lgc1)
* (scn_n->hi - scn_n->lo);
} break;
case 3:
scn_n->lo = scn_n->Pwr(0,"BSgetPWR")->last_value;
scn_n->hi = scn_n->Pwr(1,"BSgetPWR")->last_value;
case 2: {
int go2;
if (v4->val.has_xz()) {
if (0 == SimTimeD) goto next_nba;
go2 = v4->val.eeq(lgcx) - 2;
} else {
go2 = v4->val.eeq(lgc1);
}
double dt_abs = SimTimeD + getdtime(et);
double rft;
switch (go2) {
case 1: rft = scn_n->Rise(); break;
case 0: rft = scn_n->Fall(); break;
/* z */ case -2: scn_n->set = -1;
scn_n->coeffs[6] = dt_abs; // for checks
/* x */ case -1: goto next_nba;
}
double rstart = dt_abs - (rft/2);
if (rstart < SimTimeA) {
rstart = SimTimeA;
}
double rend = rstart + rft;
scn_n->set = 1;
double lv = scn_n->LastValue(),
gv = go2 ? scn_n->hi
: scn_n->lo,
fv,
split;
int s = 0;
while (scn_n->last_time > scn_n->coeffs[2+s]) {
s += 2;
}
if (s) {
int i = 0;
for (; i + s < scn_n->Slots(); i++) {
scn_n->coeffs[i] = scn_n->coeffs[i+s];
}
scn_n->fillEoT(i,scn_n->coeffs[i-1]);
}
int off = 0;
double slew = nan(""),
t2;
if (rstart >= scn_n->coeffs[0]) {
while (rstart > (t2 = scn_n->coeffs[2+off])) off += 2;
slew = (fv = scn_n->coeffs[3+off]) - scn_n->coeffs[1+off];
if (fv == gv) { // destination matches current
if (t2 < rend || 0.0 == slew) { // current arrives faster
goto next_nba; // no change
}
} else if (0.0 == slew) { // starting on flat
goto tail;
}
split:
split = (rstart - scn_n->coeffs[off])
/((t2 = scn_n->coeffs[2+off]) - scn_n->coeffs[off]);
if (split >= 1.0) {
if (1.0 == split) {
lv = scn_n->coeffs[3+off];
} else {
assert(0);
}
} else {
lv = scn_n->coeffs[1+off]
+ (split * (scn_n->coeffs[3+off] - scn_n->coeffs[1+off]));
if (gv == fv && rend > t2) {
goto next_nba; // no change
}
}
} else {
if (gv == lv) {
if (gv == scn_n->coeffs[1]) {
scn_n->fillEoT(2,gv);
goto check;
}
fv = scn_n->coeffs[3];
goto split;
}
off = -2;
goto tail;
}
knee:
scn_n->coeffs[3+off] = lv;
tail:
scn_n->fillEoT(6+off,gv);
scn_n->coeffs[5+off] = gv;
assert(scn_n->coeffs[5+off] != scn_n->coeffs[3+off]);
scn_n->coeffs[4+off] = rend;
scn_n->coeffs[2+off] = rstart;
check:
scn_n->checkPWL();
if (scn_n->non_local) {
SpcIvlCB **p_act = &scn_n->next->active,
*active = *p_act;
if (active != scn_n) {
if (active && active->set > 0) {
active->set = 0;
}
*p_act = scn_n;
}
scn_n->next->setCoeffs(scn_n->coeffs);
}
} break;
}
goto next_nba;
}
}
if (scn_n = reattach(nodes,node,v4->ptr)) {
goto setup;
}
cb = new_vpi_callback();
cb->cb_data.cb_rtn = extMarker;
cb->cb_data.user_data = 0;
cb->cb_data.value = 0;
cb->cb_data.time = &never;
sig_fun->add_vpi_callback(cb);
break;
}
next_nba:
scan = scan->next;
if (scan == nbas) break;
}
}
static double SimDelayD = -1.0;
static sim_mode SimState = SIM_ALL;
inline static sim_mode schedule_simulate_m(sim_mode mode)
{
struct event_s *cur = 0;
struct event_time_s *ctim = 0;
double d_dly;
switch (mode) {
case SIM_CONT0: if (ctim = sched_list) goto sim_cont0;
goto done;
case SIM_CONT1: goto sim_cont1;
}
schedule_time = 0; schedule_time = 0;
// Execute end of compile callbacks // Execute end of compile callbacks
@ -750,15 +987,32 @@ void schedule_simulate(void)
stop_handler(0); stop_handler(0);
// You can finish from the debugger without a time change. // You can finish from the debugger without a time change.
if (!schedule_runnable) break; if (!schedule_runnable) break;
continue; goto cycle_done;
} }
/* ctim is the current time step. */ /* ctim is the current time step. */
struct event_time_s* ctim = sched_list; ctim = sched_list;
/* If the time is advancing, then first run the /* If the time is advancing, then first run the
postponed sync events. Run them all. */ postponed sync events. Run them all. */
if (ctim->delay > 0) { if (ctim->delay > 0) {
switch (mode) {
case SIM_CONT0:
case SIM_CONT1:
case SIM_INIT: d_dly = getdtime(ctim);
if (d_dly > 0) {
doExtPwl(sched_list->nbassign,ctim);
SimDelayD = d_dly; return SIM_CONT0;
sim_cont0:
double dly = getdtime(ctim),
te = SimTimeDlast + dly;
if (te > SimTimeA) {
SimDelayD = te - SimTimeA;
return SIM_PREM;
}
SimTimeD = SimTimeDlast + dly;
}
}
if (!schedule_runnable) break; if (!schedule_runnable) break;
schedule_time += ctim->delay; schedule_time += ctim->delay;
@ -785,8 +1039,24 @@ void schedule_simulate(void)
if (ctim->active == 0) { if (ctim->active == 0) {
run_rosync(ctim); run_rosync(ctim);
sched_list = ctim->next; sched_list = ctim->next;
switch (mode) {
case SIM_CONT0:
case SIM_CONT1:
case SIM_INIT:
d_dly = getdtime(ctim);
if (d_dly > 0) {
doExtPwl(sched_list->nbassign,ctim);
SimDelayD = d_dly;
delete ctim; delete ctim;
continue; return SIM_CONT1;
sim_cont1:
// SimTimeD += ???;
goto cycle_done;
}
}
delete ctim;
goto cycle_done;
} }
} }
} }
@ -794,7 +1064,7 @@ void schedule_simulate(void)
/* Pull the first item off the list. If this is the last /* Pull the first item off the list. If this is the last
cell in the list, then clear the list. Execute that cell in the list, then clear the list. Execute that
event type, and delete it. */ event type, and delete it. */
struct event_s*cur = ctim->active->next; cur = ctim->active->next;
if (cur->next == cur) { if (cur->next == cur) {
ctim->active = 0; ctim->active = 0;
} else { } else {
@ -804,11 +1074,62 @@ void schedule_simulate(void)
cur->run_run(); cur->run_run();
delete (cur); delete (cur);
cycle_done:;
} }
if (SIM_ALL == mode) {
signals_revert(); signals_revert();
// Execute post-simulation callbacks // Execute post-simulation callbacks
vpiPostsim(); vpiPostsim();
}
done:
return SIM_DONE;
} }
void schedule_simulate()
{
schedule_simulate_m(SIM_ALL);
}
static SpcDllData *SpcControl;
void ActivateCB(double when,SpcIvlCB *cb)
{
(*SpcControl->activate)(SpcControl,cb,when);
}
extern "C" double startsim(char *analysis,SpcDllData *control)
{
SimDelayD = -1;
SpcControl = control;
if (0 == strcmp("TRAN",analysis)) {
SimState = schedule_simulate_m(SIM_INIT);
}
SimTimeDlast = SimTimeD;
return SimDelayD;
}
extern "C" double contsim(char *analysis,double time)
{
SimTimeA = time;
SimDelayD = -1;
if (0 == strcmp("TRAN",analysis)) {
SimState = SIM_CONT0;
while (SimTimeDlast < time) {
SimState = schedule_simulate_m(SimState);
SimTimeDlast = SimTimeD;
if (SIM_PREM <= SimState) break;
}
}
return SimDelayD;
}

View File

@ -52,6 +52,10 @@ extern void schedule_assign_plucked_vector(vvp_net_ptr_t ptr,
const vvp_vector4_t&val, const vvp_vector4_t&val,
unsigned adr, unsigned wid); unsigned adr, unsigned wid);
extern void schedule_assign_pv_sync(vvp_net_t *node,
int lgc,
double time);
extern void schedule_assign_array_word(vvp_array_t mem, extern void schedule_assign_array_word(vvp_array_t mem,
unsigned word_address, unsigned word_address,
unsigned off, unsigned off,
@ -115,6 +119,11 @@ struct vvp_gen_event_s
* This runs the simulator. It runs until all the functors run out or * This runs the simulator. It runs until all the functors run out or
* the simulation is otherwise finished. * the simulation is otherwise finished.
*/ */
enum sim_mode {SIM_ALL,
SIM_INIT,SIM_CONT0,SIM_CONT1,
SIM_PREM,SIM_DONE};
extern double SimTimeD, // time in digital
SimTimeA; // time in analog
extern void schedule_simulate(void); extern void schedule_simulate(void);
/* /*

196
vvp/spice.cc Normal file
View File

@ -0,0 +1,196 @@
/*
* Copyright (c) 2008 True Circuits Inc.
*
* Author: Kevin Cameron
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
# include "config.h"
# include "parse_misc.h"
# include "compile.h"
# include "schedule.h"
# include "vpi_priv.h"
# include "statistics.h"
# include "extpwl.h"
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
static SpcIvlCB *bnets[BL_LAST]; // unbound & bound lists
extern "C" SpcIvlCB **spicenets()
{
return bnets;
}
double *EvalVPI(SpcIvlCB *cb,double sim_time,eCBmode mode,
int (*fn)(void *,...),void *handle,void *xtra)
{
double data[4],
thresh;
switch (mode) {
case CB_LOAD: {
cb->checkPWL();
} break;
case CB_ACCEPT:
case CB_TRUNC:
if (fn) { // null current voltage probe
struct t_vpi_value argval;
argval.format = vpiIntVal;
vpi_get_value(&cb->sig->base,&argval);
int l,i = (*fn)(handle,xtra,data),
leave = cb->set;
if (argval.value.integer) { // falling
l = 1;
thresh = cb->thrshld[1];
if (data[0] < thresh) {l = 0; leave = 0;}
} else { // rising
l = 0;
thresh = cb->thrshld[0];
if (data[0] > thresh) {l = 1; leave = 0;}
}
if (!leave) {
double cross = sim_time,
split;
if (cb->last_time >= 0) {
double span = cb->last_value - data[0];
if (span > 0.0) {
split = (cb->last_value - thresh)/span;
if (split < 0.0) {
split = 0.0;
if (CB_ACCEPT == mode) {
fprintf(stderr,
"Warning: overran threshold on %s @ %g\n",
cb->spec,sim_time);
}
}
cross = cb->last_time +
split * (sim_time - cb->last_time);
} else {
cross = cb->last_time;
}
}
switch (mode) {
case CB_TRUNC:
if (!isnan(cb->prec)) {
double tol = cb->prec/4;
cross += tol;
if (cross < sim_time) {
assert(cross > cb->last_time);
cb->coeffs[2] = cross;
cb->coeffs[3] = cb->fillEoT(4,cb->coeffs[0]);
}
}
break;
case CB_ACCEPT:
schedule_assign_pv_sync(cb->sig->node,l,cross);
ActivateCB(cross,cb);
(*cb->set_active)(cb->dll_ref,cb,0.0);
cb->set = 1;
cb->fillEoT(2,cb->coeffs[0]);
}
}
if (CB_ACCEPT == mode) {
cb->last_time = sim_time;
cb->last_value = data[0];
}
} else { // voltage
if (CB_ACCEPT == mode) {
cb->last_time = sim_time;
if (cb->last_error >= 0.0) {
double dt = sim_time-cb->last_error;
if (dt > cb->prec && !cb->reported) {
cb->reported = 1;
fprintf(stderr,
"Warning: possible PWL/logic mismatch on %s @ %g\n",
cb->spec,sim_time-dt);
// schedule_assign_pv_sync(cb->sig->node,cb->go2,SimTimeD);
// ActivateCB(SimTimeD,cb);
}
}
}
}
}
return cb->coeffs;
}
extern "C" void *bindnet(char *spec, char T, int *slots, void *dll_ref,
void (*set_active)(void *,void *,double))
{
char *sig = spec,
des[2000], *dp = des;
int sts = 0;
while (*sig && *sig != ':') { *dp++ = *sig++; }
if (*sig++) {
*dp = '\0';
} else {
sig = spec;
}
#ifdef VVP_SHARED
static int loaded = 0;
if (!loaded) {
loaded++;
char *argv[] = {"libvvp.so","-M.","-mbindsigs",*des ? des : 0, 0};
sts = vvp_main(*des ? 4 : 3, argv);
}
#endif
SpcIvlCB *cb = new SpcIvlCB(1e-15);
cb->eval = (SpcIvlCB::SpiceCB)EvalVPI;
cb->spec = strdup(sig);
cb->dll_ref = dll_ref;
cb->set_active = set_active;
*slots = cb->Slots();
cb->next = bnets[BL_UNBOUND];
bnets[BL_UNBOUND] = cb;
return cb;
}
extern "C" void endsim()
{
}
template<int S>
void SpiceCallback<S>::dumpPWL(FILE *fp,double now) {
for (int i = 0; i < S ; i += 2) {
fprintf(fp,"%8g\t%8g %c\n",coeffs[i],coeffs[i+1],
now < coeffs[i] ? '+' : '-');
if (EndOfTimeD == coeffs[i]) break;
}
}
template struct SpiceCallback<IVL_PWL_SLOTS>;

View File

@ -515,6 +515,16 @@ void vvp_vpi_callback::add_vpi_callback(__vpiCallback*cb)
vpi_callbacks_ = cb; vpi_callbacks_ = cb;
} }
__vpiCallback *vvp_vpi_callback::has_vpi_callback(PLI_INT32(* cb_rtn)(struct t_cb_data *cb))
{
struct __vpiCallback *cur = vpi_callbacks_;
for (; cur ; cur = cur->next) {
if (cb_rtn == cur->cb_data.cb_rtn) return cur;
}
return 0;
}
/* /*
* A vvp_fun_signal uses this method to run its callbacks whenever it * A vvp_fun_signal uses this method to run its callbacks whenever it
* has a value change. If the cb_rtn is non-nil, then call the * has a value change. If the cb_rtn is non-nil, then call the

View File

@ -223,6 +223,7 @@ struct __vpiSignal {
unsigned signed_flag : 1; unsigned signed_flag : 1;
unsigned isint_ : 1; // original type was integer unsigned isint_ : 1; // original type was integer
unsigned is_netarray : 1; // This is word of a net array unsigned is_netarray : 1; // This is word of a net array
unsigned ext_bound : 2; // Bound to an external signal
/* The represented value is here. */ /* The represented value is here. */
vvp_net_t*node; vvp_net_t*node;
}; };

View File

@ -1197,6 +1197,8 @@ class vvp_vpi_callback {
virtual void get_value(struct t_vpi_value*value) =0; virtual void get_value(struct t_vpi_value*value) =0;
__vpiCallback *has_vpi_callback(PLI_INT32(* cb_rtn)(struct t_cb_data *cb));
private: private:
struct __vpiCallback*vpi_callbacks_; struct __vpiCallback*vpi_callbacks_;
}; };