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

/* 
 * example illustrating using PLI tf_ utility routines to fill memory
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "veriuser.h"
#include "cv_veriuser.h"

/* define this to use Cver's immediate assign tf_strputp routine */
/* #define __HAS_STRPUTP__ */

int memsiz, memwid, last_i;
FILE *memval_s;

/* local prototypes */
int intsize(int, int);
static void plisetupmemfill(int, int);
static void check_plimemfill(int, int);
static void plimemfill(int, int);

int intsize(int data, int reason)
{
 return(32);
}

/*
 * routine to setup memory filling routine - set param to 0 on error else 1
 * function: $plisetupmemfill(memsiz, memwid)
 */
static void plisetupmemfill(int data, int reason)
{
 char *chp;

 /* get file name as + verilog argument */
 if ((chp = mc_scan_plusargs("memfile+")) == NULL || strcmp(chp, "") == 0)
  {
   tf_error("missing or empty +memfile+[file name] memory file argument");
   tf_putp(0, 0);
   return;
  }
 /* open the file */
 if ((memval_s = fopen(chp, "r")) == NULL)
  {  
   tf_error("unable to open +memfile+ memory file %s", chp); 
   tf_putp(0, 0);
   return;
  }
 /* need memory size for checking */
 memsiz = tf_getp(1);
 memwid = tf_getp(2);
 tf_putp(0, 1);
 /* assume memory goes from 1 to size */
 last_i = 0; 
}

/*
 * check the fill memory user PLI function
 * notice calling tf_error here will inhibit simulation
 * probably sould also check 2nd index argument 
 */
static void check_plimemfill(int data, int reason)
{
 struct t_tfexprinfo xinfo;

 if (tf_nump() != 2)
  {
   tf_error("$pli_memfill has %d arguments - 2 required", tf_nump());
   return;
  }
 tf_exprinfo(1, &xinfo);
 if (xinfo.expr_type != TF_RWMEMSELECT)
  {
   tf_error("$pli_memfill first argument not read/write memory select");
   return;
  }
}

/*
 * routine to set memory 
 * function: $pli_memfill(mem[i], i)
 */
static void plimemfill(int data, int reason)
{
 int i;
 char memval[1024];

 i = tf_getp(2);
 if (i < 0 || i > memsiz) 
  {
   tf_error("cannot fill memory location %d - memory has only %d cells");
   tf_putp(0, 0);
   return;
  }
 if (fscanf(memval_s, "%s", memval) != 1)
  {
   /* problably should access OS error name here */
   tf_error("error reading memory value for cell %d", i);
   tf_putp(0, 0);
   return;
  }
 /* probably should add code to check for memval as legal binary number */
 /* but can be any width since putp assignment will widen or truncate */

 /* make sure index i is legal - since must have used i in memory select */
 /* only for checking */
 if (i != last_i + 1)
  {
   tf_error("memory index %d non in sequence - %d expected", i, last_i + 1);
   tf_putp(0, 0);
   return;
  }
 last_i = i;

 /* this is #0, in Cver you would use extension routine tf_strputp */
 /* it is identical to tf_strdelputp except string assignment immediate */
#ifdef __HAS_STRPUTP__
 /* notice need final delay type parameter since may need to cancel */
 /* events if delay form used elsewhere */
 printf("*** using tf_strputp\n"); 
 if (tf_strputp(1, memwid, 'b', memval) == 0)
#else
 if (tf_strdelputp(1, memwid, 'b', memval, 0, 0) == 0)
#endif
  {
   tf_error("strdelput of index memory failed");
   tf_putp(0, 0);
   return;
  }
 tf_putp(0, 1);
}

/* example that goes in user code */
s_tfcell veriusertfs[] = 
{ 
 { userfunction, 0, 0, (int (*)()) intsize, (int (*)()) plisetupmemfill, 0,
   "$pli_setupmemfill", 0},
 { userfunction, 0, (int (*)()) check_plimemfill, (int (*)()) intsize,
  (int (*)()) plimemfill, 0, "$pli_memfill", 0},
 /* -- add extra entries here -- */
 {0} /* -- this line must always be last -- */
};

/* dummy +loadpli1 boostrap routine - return old style veriusertfs tab */
s_tfcell *pli1_compat_bootstrap(void)
{
 return(veriusertfs);
}
