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

/*
 * test of get_value using value change call back mechanism 
 */

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

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

/* added local record for saving old variable values */
struct fullpath_var_t {
 char *fullpthnam;
 s_vpi_value *oldvalp;
 struct fullpath_var_t *fullpnxt;
};

struct fullpath_var_t *full_path_vars;
int (*iproc_rtn)();

/* local function prototypes */
static void process_inst(vpiHandle); 
static void setup_1iter_chgcbs(vpiHandle);
static void setup_1task_chgcbs(vpiHandle);
static struct fullpath_var_t *find_add_fullpath_var(char *);
static char *vpival_to_str(char *, p_vpi_value, int);

/* global function prototypes */
extern int process_all_insts(struct t_cb_data *);
extern int setup_varchgcbs(vpiHandle);
extern int my_prt3_vchg(p_cb_data);
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);
  }
}

/*
 * simplest processing routine - just print full path name
 */
int setup_varchgcbs(vpiHandle ihref)
{
 vpiHandle iter, thref;
 
 /* first all instance regs, wires, and variables */ 
 iter = vpi_iterate(vpiNet, ihref);
 if (iter != NULL) setup_1iter_chgcbs(iter);
 iter = vpi_iterate(vpiReg, ihref);
 if (iter != NULL) setup_1iter_chgcbs(iter);
 iter = vpi_iterate(vpiVariables, ihref);
 if (iter != NULL) setup_1iter_chgcbs(iter);

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

/*
 * set up change call back for all nets/regs of some type in iter
 * user cb routine handles getting values and time
 */
static void setup_1iter_chgcbs(vpiHandle iter)
{
 vpiHandle href;
 p_cb_data cbp;
 p_vpi_time timp;
 p_vpi_value valp;

 for (;;)
  {
   if ((href = vpi_scan(iter)) == NULL) break;

   /* notice this must persistent until cb removed */
   cbp = (p_cb_data) malloc(sizeof(s_cb_data));
   cbp->reason = cbValueChange;
   cbp->cb_rtn = my_prt3_vchg;  

   /* do not care about time or value - but must set to unused */
   /* nil here according to the standard causes core dump? */
   cbp->obj = href; 
   timp = (p_vpi_time) malloc(sizeof(s_vpi_time));
   timp->type = vpiSuppressTime;
   cbp->time = timp;
   valp = (p_vpi_value) malloc(sizeof(s_vpi_value)); 
   valp->format = vpiSuppressVal;
   cbp->value = valp;
   cbp->index = 0;

   vpi_register_cb(cbp);
  }
}

/*
 * setup variables in task for monitoring
 */
static void setup_1task_chgcbs(vpiHandle thref)
{
 vpiHandle iter;

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

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

/*
 * value change call back routine - print the value information
 *
 * since just print can reuse one handle
 */
int my_prt3_vchg(p_cb_data cbp)
{
 int blen;
 s_vpi_value val;
 s_vpi_time tim;
 struct fullpath_var_t *varp;
 char s1[1024], s2[1024], s3[1024];

 /* convert new value to string */
 val.format = vpiObjTypeVal;
 vpi_get_value(cbp->obj, &val); 
 blen = vpi_get(vpiSize, cbp->obj);
 vpival_to_str(s3, &val, blen);

 strcpy(s1, vpi_get_str(vpiFullName, cbp->obj));

 /* this will create first time variable changes */
 varp = find_add_fullpath_var(s1);   
 /* naybe should use x for first old value */
 if (varp->oldvalp == NULL) strcpy(s2, "<unknown>");
 else vpival_to_str(s2, varp->oldvalp, blen);

 tim.type = vpiScaledRealTime;
 vpi_get_time(cbp->obj, &tim);

 vpi_printf("--> now %g: %s changed from %s to %s.\n", tim.real,  
  s1, s2, s3);

 /* save old value - notice memory leak for malloced fields inside */
 /* s_vpi_val struct - should free */
 if (varp->oldvalp == NULL)
  varp->oldvalp = (p_vpi_value) malloc(sizeof(s_vpi_value));
 *(varp->oldvalp) = val;
 return(0);
}

/*
 * find-add value to symbol table - return NULL if first time seen
 *
 * note: should use hashing or binary search for this symbol table
 */
static struct fullpath_var_t *find_add_fullpath_var(char *s)
{
 register struct fullpath_var_t *varp, *last_varp;

 last_varp = NULL;
 for (varp = full_path_vars; varp != NULL; varp = varp->fullpnxt)
  {
   if (strcmp(varp->fullpthnam, s) == 0)
    return(varp); 
   last_varp = varp;
  }
 varp = (struct fullpath_var_t *) malloc(sizeof(struct fullpath_var_t));
 varp->fullpthnam = malloc(strlen(s) + 1);
 strcpy(varp->fullpthnam, s);
 varp->oldvalp = NULL;
 varp->fullpnxt = NULL;

 if (last_varp == NULL) full_path_vars = varp;
 else last_varp->fullpnxt = varp;
 return(varp);
}

static char *vpival_to_str(char *s, p_vpi_value valp, int blen)
{
 register int i;
 int numvals;
 char s1[1024];

 switch (valp->format) {
  case vpiBinStrVal: case vpiOctStrVal: case vpiDecStrVal: case vpiHexStrVal:
  case vpiStringVal:
   strcpy(s, valp->value.str);
   break;
  case vpiScalarVal:
   if (valp->value.scalar < 2) sprintf(s, "%x", valp->value.scalar);
   else if (valp->value.scalar == 3) strcpy(s, "x");
   else if (valp->value.scalar == 2) strcpy(s, "z");
   else strcpy(s, "?");  
   break;
  case vpiIntVal:
   sprintf(s, "%d", valp->value.integer);
   break;
  case vpiRealVal:
   sprintf(s, "%g", valp->value.real);
   break;
  case vpiVectorVal:
   /* not bothing to build a Verilog style value - just printing pairs */ 
   /* assuming int type always 32 bits */ 
   numvals = (blen + 31) >> 5;
   /* notice t_vpi_vecval is really array of the 2 integer a/b sections */
   strcpy(s, "");
   for (i = numvals - 1; i >= 0; i--)
    {
     sprintf(s1, "(av=%x,bv=%x)", valp->value.vector[i].aval,
      valp->value.vector[i].bval);
     strcat(s, s1);
    }
   break;
  case vpiStrengthVal:
   sprintf(s, "<s0=%d,s1=%d,v=%d>", valp->value.strength->s0,
    valp->value.strength->s1, valp->value.strength->logic);
   break;
  case vpiTimeVal:
   if (valp->value.time->type == vpiScaledRealTime)
    sprintf(s, "%f", valp->value.time->real);
   else sprintf(s, "%d", valp->value.time->low);
   break;
  case vpiObjTypeVal: case vpiSuppressVal:
   vpi_printf(
    "**ERR: can not copy vpiObjTypeVal or vpiSuppressVal formats - not for filled records.\n");
   return(NULL);
  default: 
   vpi_printf(
    "**ERR: can not copy t_vpi_value record - format code %d illegal.\n",
   valp->format);
  return(NULL);
 }
 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 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 = setup_varchgcbs;

 /* my variable initializes */
 full_path_vars = NULL;
}

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