/* Copyright (c) 1995-2003 Pragmatic C Software Corp. */ /* * for every port in design check various acess methods * * FIXME - there are port expression cases for which this will core dump */ #include #include #include "vpi_user.h" #include "cv_vpi_user.h" #define TRUE 1 #define FALSE 0 unsigned mcd; int (*iproc_rtn)(); /* local function prototypes */ static void process_inst(vpiHandle); static int obj_in_iterator(vpiHandle, vpiHandle); static char *bld_varbitnam(char *, vpiHandle); static int is_lvalue(vpiHandle); static vpiHandle map_lvalue_to_var(vpiHandle); static void my_prt_vlog_info(void); static void print_nest_argv(char **, int); /* global function prototypes */ extern int chk_ports(vpiHandle); extern int my_error_handler(struct t_cb_data *); extern void register_scan_cb(void); /* * process all instances calling processing routine for each */ int process_all_insts(struct t_cb_data *cbp) { int isiz; vpiHandle topiter, topiref; /* build the iterator for each module */ topiter = vpi_iterate(vpiModule, NULL); isiz = vpi_get(vpiSize, topiter); vpi_mcd_printf(mcd, " There are %d top level modules.\n", isiz); for (;;) { if ((topiref = vpi_scan(topiter)) == NULL) break; process_inst(topiref); } vpi_mcd_printf(mcd, "name of extra output file is [%s].\n", vpi_mcd_name(mcd & ~0x1)); vpi_mcd_printf(mcd, " >>> All instances processed - done - using Cver PLI only for checking.\n"); vpi_mcd_close(mcd & ~0x1); vpi_sim_control(vpiFinish, 0); return(0); } /* * process one instance and recursively process all under instances * * processing is top down depth first */ static void process_inst(vpiHandle up_ihref) { int isiz; vpiHandle iter, ihref; iproc_rtn(up_ihref); /* ignore all but bottom (no containing instances) */ if ((iter = vpi_iterate(vpiModule, up_ihref)) == NULL) return; isiz = vpi_get(vpiSize, iter); vpi_mcd_printf(mcd, " There are %d instances in %s.\n", isiz, vpi_get_str(vpiFullName, up_ihref)); for (;;) { if ((ihref = vpi_scan(iter)) == NULL) break; process_inst(ihref); } } /* * from instance if has ports check cross and back for same object */ int chk_ports(vpiHandle ihref) { int iconn_siz, mdprt_siz, pndx, bi, prtwid; int ntyp; vpiHandle iter, iter2, ihref2, pref, icref, mpref, varef; vpiHandle pbref, p2ref; char nnam[1025], s1[1025], s2[1025]; /* case 1: no ports */ if ((iter = vpi_iterate(vpiPort, ihref)) == NULL) return(0); /* case 2: has ports */ for (;;) { if ((pref = vpi_scan(iter)) == NULL) break; pndx = vpi_get(vpiPortIndex, pref); strcpy(s1, vpi_get_str(vpiFullName, ihref)); vpi_mcd_printf(mcd, " Processing port %s.%s (index %d):\n", s1, vpi_get_str(vpiName, pref), pndx); ihref2 = vpi_handle(vpiModule, pref); if (ihref2 == NULL || !vpi_compare_objects(ihref, ihref2)) { vpi_mcd_printf(mcd, "**ERR: port %s (index %d) vpiModule (in 1-to-1 method) failed.\n", vpi_get_str(vpiName, pref), pndx); } /* this is up instance connection */ icref = vpi_handle(vpiHighConn, pref); /* this is down module port expression */ mpref = vpi_handle(vpiLowConn, pref); /* DBG remove --- if ((ntyp = vpi_get(vpiType, icref)) == vpiNet || ntyp == vpiNetBit) vpi_printf("Net type %d\n", vpi_get(vpiNetType, icref)); if ((ntyp = vpi_get(vpiType, mpref)) == vpiNet || ntyp == vpiNetBit) vpi_printf("Net type %d\n", vpi_get(vpiNetType, mpref)); --- */ if ((ntyp = vpi_get(vpiType, icref)) == vpiNet || ntyp == vpiNetBit) { vpi_printf(" high conn: %s; net type: %d\n", vpi_get_str(vpiFullName, icref), vpi_get(vpiNetType, icref)); } if ((ntyp = vpi_get(vpiType, mpref)) == vpiNet || ntyp == vpiNetBit) { vpi_printf(" low conn: %s; net type: %d\n", vpi_get_str(vpiFullName, mpref), vpi_get(vpiNetType, mpref)); } /* check for width mismatch */ iconn_siz = vpi_get(vpiSize, icref); mdprt_siz = vpi_get(vpiSize, mpref); if (iconn_siz != mdprt_siz) { vpi_mcd_printf(mcd, "--WARN: port %s (index %d) width %d but connection width %d.\n", vpi_get_str(vpiName, pref), pndx, mdprt_siz, iconn_siz); } /* get up net if lvalue - check to make sure net connects to port */ if (!is_lvalue(icref)) { vpi_mcd_printf(mcd, "++INFORM: port %s (index %d) connection not simple lvalue.\n", vpi_get_str(vpiName, pref), pndx); } else { varef = map_lvalue_to_var(icref); /* iterator for High Conn wire instance ports it connects to */ /* notice in looping scanning iter, if used here will over-write */ iter2 = vpi_iterate(vpiPortInst, varef); if (iter2 == NULL || !obj_in_iterator(iter2, pref)) { strcpy(nnam, vpi_get_str(vpiName, icref)); vpi_mcd_printf(mcd, "**ERR: port %s (index %d) variable %s no vpiPortInst connection.\n", vpi_get_str(vpiName, pref), pndx, nnam); } else { /* in Cver, this frees handle (standard needs to define) */ if (iter2 != NULL) vpi_free_object(iter2); /* since for non nil case (usual), traversed iterator, must */ /* recreate the iterator */ iter2 = vpi_iterate(vpiPortInst, varef); for (;;) { if ((p2ref = vpi_scan(iter2)) == NULL) break; if (vpi_compare_objects(p2ref, pref)) continue; strcpy(s1, vpi_get_str(vpiFullName, ihref)); vpi_mcd_printf(mcd, "Also port %s.%s is a vpiPortInst.\n", s1, vpi_get_str(vpiName, p2ref)); } } } /* get down net (ignore concatenate) - check to make sure net connects */ if (mpref == NULL || !is_lvalue(mpref)) { vpi_mcd_printf(mcd, "**ERR: port %s (index %d) expression not simple lvalue.\n", vpi_get_str(vpiName, pref), pndx); } else { varef = map_lvalue_to_var(mpref); /* get net's module connecting ports (usually just 1) */ iter2 = vpi_iterate(vpiPort, varef); if (iter2 == NULL || !obj_in_iterator(iter2, pref)) { if (mpref == NULL) strcpy(nnam, "*none*"); else strcpy(nnam, vpi_get_str(vpiName, varef)); vpi_mcd_printf(mcd, "**ERR: port %s (index %d) variable %s no vpiPortInst connection.\n", vpi_get_str(vpiName, pref), pndx, nnam); } else { /* in Cver, this frees handle (standard needs to define) */ if (iter2 != NULL) vpi_free_object(iter2); /* since for non nil case (usual), traversed iterator, must */ /* recreate the iterator */ iter2 = vpi_iterate(vpiPort, varef); for (;;) { if ((p2ref = vpi_scan(iter2)) == NULL) break; if (vpi_compare_objects(p2ref, pref)) continue; vpi_mcd_printf(mcd, "Also port %s is a vpiPort.\n", vpi_get_str(vpiName, p2ref)); } } } /* final test - for every vector port - get bit high and low conns */ if (vpi_get(vpiScalar, pref)) continue; vpi_printf("... processing port %s\n", vpi_get_str(vpiName, pref)); if ((iter2 = vpi_iterate(vpiBit, pref)) == NULL) { vpi_mcd_printf(mcd, "**ERR: vector port %s (index %d) vpiBit iterator failed.\n", vpi_get_str(vpiName, pref), pndx); continue; } /* just assuming high to low for port bits (not port connecting net) */ prtwid = vpi_get(vpiSize, pref); /* bit iterators always go from lsb to msb - needed for unc. */ for (bi = 0; bi < prtwid; bi++) { if ((pbref = vpi_scan(iter2)) == NULL) break; /* notice ref's here either variable or vpiNetBit or vpiRegBit */ /* but for vpiPort (not port bit) conn's are expressions */ if ((icref = vpi_handle(vpiHighConn, pbref)) == NULL) continue; if ((mpref = vpi_handle(vpiLowConn, pbref)) == NULL) continue; vpi_mcd_printf(mcd, "Port %d bit %d vpiLowConn %s connects to vpiHighConn %s.\n", pndx, bi, bld_varbitnam(s1, mpref), bld_varbitnam(s2, icref)); } } return(0); } /* * return T if object is simple lvalue (non concatenate) * can be reg */ static int is_lvalue(vpiHandle xref) { int xtyp; xtyp = vpi_get(vpiType, xref); switch (xtyp) { case vpiNet: case vpiReg: case vpiIntegerVar: case vpiTimeVar: case vpiNetBit: case vpiRegBit: case vpiVarSelect: case vpiMemoryWord: case vpiPartSelect: break; default: return(FALSE); } return(TRUE); } /* * map lvalue to variable * * for variables returns argument else for selects returns parent */ static vpiHandle map_lvalue_to_var(vpiHandle lvalref) { int lvtyp; vpiHandle varef; lvtyp = vpi_get(vpiType, lvalref); switch (lvtyp) { case vpiNet: case vpiReg: case vpiIntegerVar: case vpiTimeVar: break; case vpiNetBit: case vpiRegBit: case vpiVarSelect: case vpiMemoryWord: case vpiPartSelect: varef = vpi_handle(vpiParent, lvalref); return(varef); default: /* this is error that should never happen - will catch later */ break; } return(lvalref); } /* * return T if object in iterator * notice this scans so must free iterator and use vpi_iterate to scan again */ static int obj_in_iterator(vpiHandle iter, vpiHandle obj) { vpiHandle iobj; for (;;) { if ((iobj = vpi_scan(iter)) == NULL) break; if (vpi_compare_objects(iobj, obj)) return(TRUE); } /* notice T and F constants come from veriuser.h include */ return(FALSE); } /* * build a 1 bit scalar or variable bit handle full (qualified) name */ static char *bld_varbitnam(char *s, vpiHandle href) { int i1; int otyp = vpi_get(vpiType, href); vpiHandle ndxref, varef; s_vpi_value tmpval; char s1[1025]; varef = map_lvalue_to_var(href); strcpy(s, vpi_get_str(vpiFullName, varef)); if (otyp == vpiRegBit || otyp == vpiNetBit) { strcpy(s1, s); ndxref = vpi_handle(vpiIndex, href); tmpval.format = vpiIntVal; vpi_get_value(ndxref, &tmpval); i1 = tmpval.value.integer; sprintf(s, "%s[%d]", s1, i1); } return(s); } /* * routine to build an error indication string */ int my_error_handler(struct t_cb_data *cbp) { struct t_vpi_error_info einfotab; struct t_vpi_error_info *einfop; char s1[128]; einfop = &einfotab; vpi_chk_error(einfop); if (einfop->state == vpiCompile) strcpy(s1, "vpiCompile"); else if (einfop->state == vpiPLI) strcpy(s1, "vpiPLI"); else if (einfop->state == vpiRun) strcpy(s1, "vpiRun"); else strcpy(s1, "**unknown**"); vpi_printf("**ERR(%s) %s (level %d) at **%s(%d):\n %s\n", einfop->code, s1, einfop->level, einfop->file, einfop->line, einfop->message); /* if fatal error give up - continue after vpiError */ if (einfop->level == vpiSystem || einfop->level == vpiInternal) { vpi_printf("**FATAL: encountered System/Internal error - giving up\n"); vpi_sim_control(vpiFinish, 0); } return(0); } /* Template functin table for added user systf tasks and functions. See file vpi_user.h for structure definition Note only vpi_register_systf and vpi_ or tf_ utility routines that do not access the simulation data base may be called from these routines */ /* all routines are called to register cbs */ /* called just after all PLI 1.0 tf_ veriusertfs table routines are set up */ /* before source is read */ void (*vlog_startup_routines[]) () = { register_scan_cb, 0 }; /* routine to do the systf registering - probably should go in other file */ /* usually only vpi_ PLI 2.0 systf or cb registering is done here */ /* * register the start of sim scan call back and set up error handling * * notice making version of Cver that prints some stuff to start but * is a normal Cver * * since handle not save (passed back?), no way to find out cb info */ void register_scan_cb(void) { vpiHandle href, href2; struct t_cb_data *ecbp, *cbp; struct t_cb_data cbrec, cbrec2; /* notice cb records must be in global storage */ ecbp = &cbrec; ecbp->reason = cbPLIError; ecbp->cb_rtn = my_error_handler; ecbp->obj = NULL; ecbp->time = NULL; ecbp->value = NULL; ecbp->user_data = NULL; /* probably should check for error here */ if ((href = vpi_register_cb(ecbp)) == NULL) vpi_printf("**ERR: PLI 2.0 can not register error handler callback.\n"); cbp = &cbrec2; cbp->reason = cbStartOfSimulation; cbp->cb_rtn = process_all_insts; cbp->obj = NULL; cbp->time = NULL; cbp->value = NULL; cbp->user_data = NULL; /* probably should check for error here */ if ((href2 = vpi_register_cb(cbp)) == NULL) vpi_printf("**ERR: PLI 2.0 can not register start of sim setup callback.\n"); /* open a file to write to in addition to stdout and log */ if ((mcd = vpi_mcd_open("fff9")) == 0) vpi_printf("***ERR: unable to open additional output file fff9\n"); mcd |= 5; /* set the processing routine */ iproc_rtn = chk_ports; /* print out verilog information - mostly simulator specific */ my_prt_vlog_info(); } /* * print out simulator information to stdout and log file */ static void my_prt_vlog_info(void) { register int i; s_vpi_vlog_info vlog_inf; p_vpi_vlog_info infp; /* user must provide record filled by routine */ infp = &vlog_inf; if (!vpi_get_vlog_info(infp)) { vpi_printf("**ERR: call to vpi_get_vlog_info failed.\n"); return; } vpi_printf("--------------------------------------------------------------\n"); vpi_printf("VLOG PRODUCT: %s - VERSION: %s\n", infp->product, infp->version); if (infp->argc == 0) vpi_printf(" There are no command line arguments.\n"); else { for (i = 0; i < infp->argc; i++) { if (strcmp(infp->argv[i], "-f") == 0) { vpi_printf(" Argument %d: %s\n", i, infp->argv[i]); vpi_printf(" Argument %d: NESTED ARGV\n", i + 1); if (i + 1 < infp->argc) { print_nest_argv((char **) infp->argv[i + 1], 1); /* move past nested **argv structure */ i++; } } else vpi_printf(" Argument %d: %s\n", i, infp->argv[i]); } } vpi_printf("--------------------------------------------------------------\n"); } /* * print nested argument list (recursive) * * format is -f followed by pointer to argv list with no argc but nil end */ static void print_nest_argv(char **argv, int lev) { register int i; if (argv[0] == NULL) { vpi_printf(" Level %d: No Arguments\n", lev); return; } for (i = 0;; i++) { if (argv[i] == NULL) { vpi_printf(" Level %d: Argument %d: **NULL**\n", lev, i); break; } if (strcmp(argv[i], "-f") == 0) { vpi_printf(" Level %d: Argument %d: %s\n", lev, i, argv[i]); vpi_printf(" Level %d: Argument %d: NESTED ARGV\n", lev, i + 1); print_nest_argv((char **) argv[i + 1], lev + 1); i++; } else vpi_printf(" Level %d: Argument %d: %s\n", lev, i, argv[i]); } } /* dummy +loadvpi= boostrap routine - mimics old style exec all routines */ /* in standard PLI vlog_startup_routines table */ void vpi_compat_bootstrap(void) { int i; for (i = 0;; i++) { if (vlog_startup_routines[i] == NULL) break; vlog_startup_routines[i](); } }