Additions and changes for using VVP as DLL with an analog simulator.
This commit is contained in:
parent
e0a1b41b37
commit
4ee9cc6247
|
|
@ -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};
|
||||
}
|
||||
|
|
@ -47,7 +47,9 @@ MAN = @MAN@
|
|||
PS2PDF = @PS2PDF@
|
||||
|
||||
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@
|
||||
LIBS = @LIBS@ @EXTRALIBS@
|
||||
|
||||
|
|
@ -58,7 +60,17 @@ ifneq (x@vpidir2@,x)
|
|||
MDIR2 = -DMODULE_DIR2=\"$(libdir)/ivl/@vpidir2@\"
|
||||
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
|
||||
endif
|
||||
|
||||
clean:
|
||||
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 \
|
||||
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 \
|
||||
dff.o extend.o npmos.o part.o reduce.o resolv.o sfunc.o stop.o symbols.o \
|
||||
ufunc.o codes.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)
|
||||
# Under Windows (mingw) I need to make the ivl.exe in two steps.
|
||||
|
|
@ -101,6 +113,12 @@ libvpi.a: libvpi.c
|
|||
|
||||
vvp@EXEEXT@: $O
|
||||
$(CXX) $(LDFLAGS) -o vvp@EXEEXT@ $O $(LIBS) $(dllib)
|
||||
|
||||
libvvp.so: $O
|
||||
$(CXX) $(LDFLAGS_SO) -o $@ $O $(LIBS) $(dllib)
|
||||
|
||||
$(libdir)/%.so: %.so
|
||||
|
||||
endif
|
||||
|
||||
dep:
|
||||
|
|
@ -110,6 +128,10 @@ dep:
|
|||
$(CXX) $(CPPFLAGS) $(MDIR1) $(MDIR2) $(CXXFLAGS) -MD -c $< -o $*.o
|
||||
mv $*.d dep/$*.d
|
||||
|
||||
%-shrd.o: %.cc
|
||||
$(CXX) $(CPPFLAGS) $(MDIR1) $(MDIR2) $(CXXFLAGS) -DVVP_SHARED -MD -c $< -o $@
|
||||
mv $*-shrd.d dep/$*.d
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CPPFLAGS) $(MDIR1) $(MDIR2) $(CFLAGS) -MD -c $< -o $*.o
|
||||
mv $*.d dep/$*.d
|
||||
|
|
@ -158,7 +180,8 @@ Makefile: Makefile.in 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@
|
||||
$(INSTALL_PROGRAM) ./vvp@EXEEXT@ $(DESTDIR)$(bindir)/vvp@EXEEXT@
|
||||
|
|
@ -166,6 +189,9 @@ $(bindir)/vvp@EXEEXT@: ./vvp@EXEEXT@
|
|||
$(libdir)/libvpi.a : ./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
|
||||
$(INSTALL_DATA) $(srcdir)/vvp.man $(DESTDIR)$(mandir)/man1/vvp.1
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
10
vvp/main.cc
10
vvp/main.cc
|
|
@ -138,7 +138,13 @@ const char*module_tab[64];
|
|||
extern void vpi_mcd_init(FILE *log);
|
||||
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[])
|
||||
#endif
|
||||
{
|
||||
int opt;
|
||||
unsigned flag_errors = 0;
|
||||
|
|
@ -303,6 +309,8 @@ int main(int argc, char*argv[])
|
|||
}
|
||||
|
||||
|
||||
#ifndef VVP_SHARED
|
||||
|
||||
schedule_simulate();
|
||||
|
||||
if (verbose_flag) {
|
||||
|
|
@ -328,5 +336,7 @@ int main(int argc, char*argv[])
|
|||
count_gen_events, count_gen_pool());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return vvp_return_value;
|
||||
}
|
||||
|
|
|
|||
337
vvp/schedule.cc
337
vvp/schedule.cc
|
|
@ -20,12 +20,14 @@
|
|||
# include "schedule.h"
|
||||
# include "vthread.h"
|
||||
# include "slab.h"
|
||||
# include "vpi_priv.h"
|
||||
# include <new>
|
||||
# include <signal.h>
|
||||
# include <stdlib.h>
|
||||
# include <assert.h>
|
||||
|
||||
# include <stdio.h>
|
||||
# include "extpwl.h"
|
||||
|
||||
unsigned long count_assign_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;
|
||||
|
||||
// Execute end of compile callbacks
|
||||
|
|
@ -750,15 +987,32 @@ void schedule_simulate(void)
|
|||
stop_handler(0);
|
||||
// You can finish from the debugger without a time change.
|
||||
if (!schedule_runnable) break;
|
||||
continue;
|
||||
goto cycle_done;
|
||||
}
|
||||
|
||||
/* 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
|
||||
postponed sync events. Run them all. */
|
||||
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;
|
||||
schedule_time += ctim->delay;
|
||||
|
|
@ -785,8 +1039,24 @@ void schedule_simulate(void)
|
|||
if (ctim->active == 0) {
|
||||
run_rosync(ctim);
|
||||
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;
|
||||
return SIM_CONT1;
|
||||
sim_cont1:
|
||||
// SimTimeD += ???;
|
||||
goto cycle_done;
|
||||
}
|
||||
}
|
||||
delete ctim;
|
||||
continue;
|
||||
goto cycle_done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -794,7 +1064,7 @@ void schedule_simulate(void)
|
|||
/* Pull the first item off the list. If this is the last
|
||||
cell in the list, then clear the list. Execute that
|
||||
event type, and delete it. */
|
||||
struct event_s*cur = ctim->active->next;
|
||||
cur = ctim->active->next;
|
||||
if (cur->next == cur) {
|
||||
ctim->active = 0;
|
||||
} else {
|
||||
|
|
@ -804,11 +1074,62 @@ void schedule_simulate(void)
|
|||
cur->run_run();
|
||||
|
||||
delete (cur);
|
||||
|
||||
cycle_done:;
|
||||
}
|
||||
|
||||
if (SIM_ALL == mode) {
|
||||
|
||||
signals_revert();
|
||||
signals_revert();
|
||||
|
||||
// Execute post-simulation callbacks
|
||||
vpiPostsim();
|
||||
// Execute post-simulation callbacks
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,10 @@ extern void schedule_assign_plucked_vector(vvp_net_ptr_t ptr,
|
|||
const vvp_vector4_t&val,
|
||||
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,
|
||||
unsigned word_address,
|
||||
unsigned off,
|
||||
|
|
@ -115,6 +119,11 @@ struct vvp_gen_event_s
|
|||
* This runs the simulator. It runs until all the functors run out or
|
||||
* 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);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
@ -515,6 +515,16 @@ void vvp_vpi_callback::add_vpi_callback(__vpiCallback*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
|
||||
* has a value change. If the cb_rtn is non-nil, then call the
|
||||
|
|
|
|||
|
|
@ -223,6 +223,7 @@ struct __vpiSignal {
|
|||
unsigned signed_flag : 1;
|
||||
unsigned isint_ : 1; // original type was integer
|
||||
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. */
|
||||
vvp_net_t*node;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1197,6 +1197,8 @@ class vvp_vpi_callback {
|
|||
|
||||
virtual void get_value(struct t_vpi_value*value) =0;
|
||||
|
||||
__vpiCallback *has_vpi_callback(PLI_INT32(* cb_rtn)(struct t_cb_data *cb));
|
||||
|
||||
private:
|
||||
struct __vpiCallback*vpi_callbacks_;
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue