iverilog/vpi/bindsigs.cc

459 lines
12 KiB
C++

/*
* 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};
}