/* Copyright (c) 1995-2003 Pragmatic C Software Corp. */

/*
 * print out all drives and loads for wire and regs
 *
 * this shows the advantage of type information carrying handles - values
 * can be printed without knowing much about Verilog constructs
 */

#include <stdio.h>
#include <string.h>

#include "vpi_user.h"
#include "cv_vpi_user.h"

int (*iproc_rtn)();

/* local function prototypes */
static void process_inst(vpiHandle); 
static void prt_1iter_drvlds(vpiHandle, vpiHandle);
static void prt_iter(vpiHandle, vpiHandle);
static void prt_1task_drvlds(vpiHandle);

/* global function prototypes */
extern int my_prt_vchg();
extern int prt_drvlds(vpiHandle);
extern int my_error_handler(struct t_cb_data *);
extern void register_scan_cb(void);

/*
 * routine to get and zero all delays in design 
 */
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_printf("  There are %d top level modules.\n", isiz);
 for (;;)
  {
   if ((topiref = vpi_scan(topiter)) == NULL) break;
   process_inst(topiref);
  }
 vpi_printf("  >>> All instances processed - continuing with simulation.\n");
 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);
 if ((iter = vpi_iterate(vpiModule, up_ihref)) == NULL) return;
 isiz = vpi_get(vpiSize, iter);
 vpi_printf("  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);
  }
}

/*
 * print the loads and drivers for all nets in instance ihref
 */
int prt_drvlds(vpiHandle ihref)
{
 vpiHandle iter, thref;
 
 /* first all instance regs, wires, and variables */ 
 iter = vpi_iterate(vpiNet, ihref);
 if (iter != NULL) prt_1iter_drvlds(iter, ihref);
 iter = vpi_iterate(vpiReg, ihref);
 if (iter != NULL) prt_1iter_drvlds(iter, ihref);
 iter = vpi_iterate(vpiVariables, ihref);
 if (iter != NULL) prt_1iter_drvlds(iter, ihref);

 /* also monitor in scopes */
 iter = vpi_iterate(vpiInternalScope, ihref);
 for (;;)
  {
   if ((thref = vpi_scan(iter)) == NULL) break;
   prt_1task_drvlds(thref);
  }
 return(0);
}

/*
 * print all loads and drivers for nets in one iterator
 */
static void prt_1iter_drvlds(vpiHandle iter, vpiHandle ihref)
{
 register int bi;
 int ntyp;
 vpiHandle href, bref, bititer, lditer, drviter, ndxref;
 s_vpi_value tmpval;

 for (;;)
  {
   if ((href = vpi_scan(iter)) == NULL) break;
   /* can not pass real variable to load/driver iterator or will get err */  
   /* bits selects form real illegal in Verilog */
   if ((ntyp = vpi_get(vpiType, href)) == vpiRealVar) continue;

   /* for this test ignore scalars */
   if (vpi_get(vpiScalar, href)) continue; 

   /* should never be nil */
   if ((bititer = vpi_iterate(vpiBit, href)) == NULL) continue;
   for (;;)
    {
     if ((bref = vpi_scan(bititer)) == NULL) break;
     
     ndxref = vpi_handle(vpiIndex, bref);
     tmpval.format = vpiIntVal;
     /* need to evalate expr. even though since from iter know constant */
     vpi_get_value(ndxref, &tmpval);
     bi = tmpval.value.integer;

     /* print the drives and loads for 1 net */ 
     vpi_printf("... printing drivers and loads for %s[%d]:\n", 
      vpi_get_str(vpiFullName, bref), bi);

     lditer = vpi_iterate(vpiLocalLoad, bref);
     if (lditer != NULL)
      {
       vpi_printf("  Loads:\n");
       prt_iter(lditer, ihref);
      }    

     /* regs can only have loads because in Cver force/assign properties */
     /* not drivers */
     if (ntyp != vpiNetBit) continue;
  
     drviter = vpi_iterate(vpiLocalDriver, bref);
     if (drviter != NULL)
      {
       vpi_printf("  Drivers:\n");
       prt_iter(drviter, ihref);
      }

     lditer = vpi_iterate(vpiPathTerm, bref);
     if (lditer != NULL)
      {
       vpi_printf("  Path terminals:\n");
       prt_iter(lditer, ihref);
      }

     lditer = vpi_iterate(vpiTchkTerm, bref);
     if (lditer != NULL)
      {
       vpi_printf("  Timing check terminals:\n");
       prt_iter(lditer, ihref);
      }
    }
  }
}

/*
 * print contents of one iterator (any? non NULL)
 */
static void prt_iter(vpiHandle iter, vpiHandle ihref)
{
 int htyp;
 vpiHandle href, portihref;
 char *chp, s1[1025];

 for (;;)
  {
   if ((href = vpi_scan(iter)) == NULL) break;
  
   htyp = vpi_get(vpiType, href);
   /* must handle port as special case because can be module port */
   /* or up instance port connection */
   if (htyp == vpiModPathIn || htyp == vpiModPathOut) strcpy(s1, "**NONE(0)"); 
   else
    {
     if ((chp = vpi_get_str(vpiFile, href)) == NULL)
      strcpy(s1, "**NONE(0)"); 
     else sprintf(s1, "**%s(%d)", chp, vpi_get(vpiLineNo, href));
    } 
   if (htyp == vpiPort)
    {
     /* if ld/drv net in same instance as port then module port */ 
     /* else up instance connection */
     portihref = vpi_handle(vpiModule, href);
     if (vpi_compare_objects(ihref, portihref))
      {
       vpi_printf("   Port (vpiLowConn) object at %s\n", s1);
      }
     else
      {
       sprintf(s1, "**%s(%d)", vpi_get_str(vpiFile, portihref),
        vpi_get(vpiLineNo, portihref));
       vpi_printf("   Port (vpiHighConn) object at %s\n", s1);
      }
    }
   else vpi_printf("    %s object at %s\n", vpi_get_str(vpiType, href), s1);
  }
}

/*
 * print the drivers and loads for one task (should be no drivers?)
 *
 * drive and load print iterator routines passed nil instance context
 * because only needed for ports that are impossible in tasks
 */
static void prt_1task_drvlds(vpiHandle thref)
{
 vpiHandle iter;

 iter = vpi_iterate(vpiReg, NULL);
 if (iter != NULL) prt_1iter_drvlds(iter, NULL);
 iter = vpi_iterate(vpiVariables, thref);
 if (iter != NULL) prt_1iter_drvlds(iter, NULL);

 /* include all contained named blocks */
 iter = vpi_iterate(vpiInternalScope, thref);
 for (;;)
  {
   if ((thref = vpi_scan(iter)) == NULL) break;
   prt_1task_drvlds(thref);
  }
}

/*
 * 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 serious error give up */
 if (einfop->level == vpiError || einfop->level == vpiSystem
  || einfop->level == vpiInternal)
  {
   vpi_printf("**FATAL: encountered 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;

 /* 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 = &cbrec;
 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");
 /* if not registered will be no call backs */

 /* set the processing routine */
 iproc_rtn = prt_drvlds;
}

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