/****************************************************************************
    Copyright (C) 1987-2005 by Jeffery P. Hansen

    This program is free software; you can redistribute it and/or modify
    it 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., 675 Mass Ave, Cambridge, MA 02139, USA.
****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pwd.h>
#include "tkgate.h"

int yyparse();
void BeginVR();

#ifndef yyrestart
void yyrestart(FILE*);
#endif

#define WENDSBLOCKSIZE	64

static int libraryMode;

GModuleDef *topModule = 0;

int VerilogErrorCount = 0;

/*
   Data structure used to keep intermediate parser state.
*/
static struct {
  Version	gate_ver;	/* Version number of tkgate */
  Version	ver;		/* Version number of current file */

  GModuleDef	*env;		/* Current module being processed */
  GNet		*net;		/* Current net being processed */
  GCElement	*gate;		/* Current gate being processed */
  int		vnum;		/* Variant number of gate */
  GWire		*wire;
  GWireNode	*node;

  int		del_mod;	/* Delete module in VerEndModule */

  int		numAllocedWends;/* Number of wends elements allocated */
  GWire		**wends;	/* Array of wire ends */
  int		numWEnds;	/* Number of wire ends */

  SHash		*netWEnds;

  int		numAllocedgPin;	/* Number of gPin/gNet/gComp elements allocated */
  const char	**gPin;		/* Array of pin names */
  GNet		**gNet;		/* Array of net objects */
  int		*gComp;		/* Array of complement flags */
  int		numPins;	/* Number of pins */
  int		pidx;		/* Current pin index */
} cur = {{0}};

static void cur_init()
{
  cur.net = 0;
  cur.gate = 0;
  cur.wire = 0;
  cur.node = 0;
  cur.del_mod = 0;
  cur.numWEnds = 0;
  cur.numPins = 0;
  cur.wends = 0;
  cur.gPin = 0;
  cur.gNet = 0;
  cur.gComp = 0;
}

/*
 * Ensure that element n of wends has been allocated. 
 */
static void cur_wends_extend(int n)
{
  if (!cur.wends) {
    cur.wends = (GWire**)calloc(WENDSBLOCKSIZE,sizeof(GWire*));
    cur.numAllocedWends = WENDSBLOCKSIZE;
  }
  if (n >= cur.numAllocedWends) {
    while (n >= cur.numAllocedWends)
      cur.numAllocedWends += WENDSBLOCKSIZE;
    cur.wends = (GWire**)realloc(cur.wends,cur.numAllocedWends*sizeof(GWire*));
  }
}

static void cur_gpin_extend(int n)
{
  if (!cur.gPin) {
    cur.gPin = (const char**)calloc(WENDSBLOCKSIZE,sizeof(char*));
    cur.gNet = (GNet**)calloc(WENDSBLOCKSIZE,sizeof(GNet*));
    cur.gComp = (int*)calloc(WENDSBLOCKSIZE,sizeof(int));
    cur.numAllocedgPin = WENDSBLOCKSIZE;
  }
  if (n >= cur.numAllocedgPin) {
    while (n >= cur.numAllocedgPin)
      cur.numAllocedgPin += WENDSBLOCKSIZE;
    cur.gPin = (const char**)realloc(cur.wends,cur.numAllocedgPin*sizeof(char*));
    cur.gNet = (GNet**)realloc(cur.gNet,cur.numAllocedgPin*sizeof(GNet*));
    cur.gComp = (int*)realloc(cur.gComp,cur.numAllocedgPin*sizeof(int));
  }
}

static unsigned spread(unsigned M,unsigned x)
{
  unsigned R = 0;
  int i,j;

  j = 0;
  for (i = 0;i < 32;i++) 
    if ((M & (1 << i)) && (x & (1 << j++)))
      R |= (1 << i);

  return R;
}

char *getTypeName(GGateInfo *gi,GCElement *g)
{
  static char buf[32];
  unsigned imask = 0;
  unsigned M = gi->vmask;
  int i,j;
  char *p;

  if (!M) return gi->vnames;

  j = 0;
  for (i = 0;i < 32 && M;i++) {
    unsigned Q = (1 << i);
    if ((M & Q)) {
      M &= ~Q;
      if (g->wires[i]->invert)
	imask |= (1 << j);
      j++;
    }
  }

  p = gi->vnames;
  for (;imask;imask--) {
    p += strcspn(p,":");
    if (*p == ':') p++;
  }

  for (i = 0;i < 31 && *p && *p != ':';)
    buf[i++] = *p++;
  buf[i] = 0;

  return buf;
}

int VerilogBasicGateCall(FILE *f,GCElement *g)
{
  static int gateID = 0;
  GGateInfo *gi = g->typeinfo;
  char *typeName = getTypeName(gi,g);

  fprintf(f,"  %s",typeName);
  if (g->ename)
    fprintf(f," %s",g->ename);
  else {
    fprintf(f," g%d",gateID++);
    logError(ERL_WARN,"Saved gate with no name.");
  }
  return 0;
}

int VerilogBasicGateParmList(FILE *f,GCElement *g)
{
  GGateInfo *gi = g->typeinfo;
  GWire *w;
  int i,j;
  int first_pin;

  first_pin = 1;
  fprintf(f," (");
  for (i = 0;i < gi->NumPads;i++) {
    for (w = g->wires[i], j=0;w;w = w->next, j++) {
      char *inv = "";

      if (!first_pin)
	fprintf(f,", ");
      first_pin = 0;

      if (!(gi->vmask & (1<<i)) && w->invert)
	inv = "!";

      if (gi->Pad[i].CanAdd)
	fprintf(f,".%s%d",gi->Pad[i].Name,j);
      else
	fprintf(f,".%s",gi->Pad[i].Name);
      if (w->net->signame)
	fprintf(f,"(%s%s)",inv,w->net->signame);
      else
	fprintf(f,"(%sw%x)",inv,(size_t)w->net);
    } 
  }
  fprintf(f,");");
  return 0;
}

void VerilogBasicGateComment(FILE *f,GCElement *g,int doComMark)
{
  GGateInfo *gi = g->typeinfo;
  GWire *w;
  int i,j;

  if (doComMark)
    fprintf(f,"   //:");
  fprintf(f," @(%d,%d)",g->xpos,g->ypos);
  if (!g->show_name)
    fprintf(f," /sn:0");
  if (g->orient != 0)
    fprintf(f," /R:%d",g->orient);
  if (g->anchored != 0)
    fprintf(f," /anc:1");
  if (g->cpath_cut != 0)
    fprintf(f," /cpc:1");

  if (g->cust_delay) {
    int i;

    fprintf(f," /delay:\"");
    for (i = 0;gi->delayNames[i];i++) {
      fprintf(f," %d",g->delays[i]);
    }
    fprintf(f,"\"");
  } else {
    if (strcmp(g->tech,"default") != 0)
      fprintf(f," /tech:%s",g->tech);
  }

  if (gi->NumPads > 0) {
    fprintf(f," /w:[");
    for (i = 0;i < gi->NumPads;i++) {
      for (w = g->wires[i], j=0;w;w = w->next, j++) {
	fprintf(f," %d",w->nidx);
      } 
    }
    fprintf(f," ]");
  }
}

static void VerilogSaveWire(FILE *f,GWire *w,int doComment)
{
  GWireNode *n;

  if (doComment)
    fprintf(f,"//:");

  fprintf(f," {%d}",w->nidx);

  n = w->nodes;

  if (n->out) {
    for (;n;n = n->out) {
      fprintf(f,"(%d,%d)",n->x,n->y);
      w = n->end;
    }
  } else {
    for (;n;n = n->in) {
      fprintf(f,"(%d,%d)",n->x,n->y);
      w = n->end;
    }
  }

  if (w) {
    GCElement *g;
    GGateInfo *gi;
    int i;

    fprintf(f,"{%d}",w->nidx);
    fprintf(f,"\n");

    g = w->gate;
    if (!g) return;
    gi = w->gate->typeinfo;

    switch (gi->Code) {
    case JOINT :
      for (i = 0;i < gi->NumPads;i++)
	if (g->wires[i] && g->wires[i] != w)
	  VerilogSaveWire(f,g->wires[i],1);
      break;
    case TAP :
      if (g->wires[TAP_OUT] == w)
	VerilogSaveWire(f,g->wires[TAP_IN],1);
      if (g->wires[TAP_IN] == w)
	VerilogSaveWire(f,g->wires[TAP_OUT],1);
      break;
    }
  }
}

static void ClearNetMarks(GModuleDef *env)
{
  HashElem *nl;

  for (nl = Hash_first(env->nets);nl;nl = Hash_next(env->nets,nl)) {
    GNet *n = (GNet*) HashElem_obj(nl);
    n->mark = 0;
  }
}

static void VerilogSaveOneNet(FILE *f,GNet *n)
{
  if (n->ionet)
    fprintf(f,"%s ",n->ionet->typeinfo->vnames);
  else
    fprintf(f,"wire ");
  if (n->nbits > 1)
    fprintf(f,"[%d:0] ",n->nbits-1);

  if (n->signame)
    fprintf(f,"%s;    ",n->signame);
  else
    fprintf(f,"w%x;    ",(size_t)n);

  fprintf(f,"//:");
  if (!n->show_name)
    fprintf(f," /sn:0");
  if (n->decoration != 0)
    fprintf(f," /dp:%d",n->decoration);

  VerilogSaveWire(f,wire_sigroot(n->driver),0);
}

static void VerilogSaveNets(FILE *f,GModuleDef *env)
{
  GWireList *wl;
  HashElem *gl;
  HashElem *nl;

  /* Unmark all gates */
  for (gl = Hash_first(env->gates);gl;gl = Hash_next(env->gates,gl)) {
    GCElement *g = (GCElement*)HashElem_obj(gl);
    g->mark = 0;
  }

  /*
     Make sure all nets are finalized.
   */
  for (wl = env->wires;wl;wl = wl->cdr) {
    wire_finalizeNet(wl->car);
  }

  for (nl = Hash_first(env->nets);nl;nl = Hash_next(env->nets,nl)) {
    GNet *n = (GNet*) HashElem_obj(nl);

    if (n->ionet)
      VerilogSaveOneNet(f,n);
  }
  for (nl = Hash_first(env->nets);nl;nl = Hash_next(env->nets,nl)) {
    GNet *n = (GNet*) HashElem_obj(nl);

    if (!n->ionet)
      VerilogSaveOneNet(f,n);
  }
  fprintf(f,"//: enddecls\n\n");
}

/*
   Save the parameter list for a module.
*/
static void VerilogSaveModDecl(FILE *f,GModuleDef *M)
{
  HashElem *gl;
  int did_first = 0;

  if (M->is_top) {
    if (M == XGate.circuit->root_mod)
      fprintf(f,";    //: root_module\n");
    else
      fprintf(f,";\n");
    return;
  }

  fprintf(f,"(");
  for (gl = Hash_first(M->gates);gl;gl = Hash_next(M->gates,gl)) {
    GCElement *g = (GCElement*) HashElem_obj(gl);
    switch (g->typeinfo->Code) {
    case LOGICIN :
    case LOGICOUT :
    case LOGICTRI :
      if (did_first) fprintf(f,", ");
      did_first = 1;
      fprintf(f,"%s",g->wires[0]->net->signame); break;
      break;
    }
  }
  fprintf(f,");\n");
}

/*
   Save comment for block descriptor.
*/
static void VerilogSaveModBlockDesc(FILE *f,GModuleDef *M)
{
  GCElement *g = M->blockdescript;
  GGateInfo *gi;
  GWire *w;
  int i,j;
  static char dirchar[] = "?=><";

  if (!g) return;

  gi = g->typeinfo;
  fprintf(f,"//: interface ");
  fprintf(f," /sz:(%d, %d) /bd:[",g->u.block.gwidth,g->u.block.gheight);
  for (i = 0;i < gi->NumPads;i++) {
    for (w = g->wires[i], j=0;w;w = w->next, j++) {
      fprintf(f," %s%d%c",
	      gi->Pad[i].Name,j,
	      dirchar[gi->Pad[i].Dir]);
      if (w->net->nbits == 1)
	fprintf(f,"%s",w->name);
      else
	fprintf(f,"%s[%d:0]",w->name,w->net->nbits-1);
      fprintf(f,"(%d/%d)",w->offset.num,w->offset.den);
    } 
  }
  fprintf(f," ]\n");
}

static void VerilogSaveGates(FILE *f,GModuleDef *M)
{
  HashElem *gl;

  for (gl = Hash_first(M->gates);gl;gl = Hash_next(M->gates,gl)) {
    GCElement *g = (GCElement*) HashElem_obj(gl);
    assert(g->typeinfo->VerSave);
    (*g->typeinfo->VerSave)(f,g);
  }
  fprintf(f,"\n");
}

static void VerilogSaveModule(FILE *f,GModuleDef *M)
{
  ClearNetMarks(M);

  fprintf(f,"\n");
  fprintf(f,"module %s",M->Name);
  VerilogSaveModDecl(f,M);
  if (!M->is_top)
    VerilogSaveModBlockDesc(f,M);
  VerilogSaveNets(f,M);
  VerilogSaveGates(f,M);
  fprintf(f,"endmodule\n");
}


/*****************************************************************************
 *  Raw save of file in verilog format.
 *****************************************************************************/
int VerilogWriteModules(const char *name,int writeLibBlocks)
{
  FILE *f;
  HashElem *E;
  int i;
  int error_status;

  if (!(f = fopen(name,"w"))) {
    return -1;
  }

  fprintf(f,"//: version \"%s\"\n",VERSIONS[0].name);

  if (!XGate.circuit->useExtBars)
    fprintf(f,"//: property useExtBars = 0\n");

  if (XGate.circuit->discardChanges)
    fprintf(f,"//: property discardChanges = 1\n");

  if (strcmp(tkg_defaultTech,"default") != 0)
    fprintf(f,"//: property technology = %s\n",tkg_defaultTech);

  if (XGate.circuit->numInitScripts > 0) {
    char buf[STRMAX];

    for (i = 0;i < XGate.circuit->numInitScripts;i++)
      fprintf(f,"//: script \"%s\"\n",quoteChars(buf,XGate.circuit->initScripts[i],"\"\\"));
    fprintf(f,"\n");
  }    

  for (E = Hash_first(XGate.circuit->moduleTable);E;E = Hash_next(XGate.circuit->moduleTable,E)) {
    GModuleDef *M = (GModuleDef*) HashElem_obj(E);
    if (writeLibBlocks || !M->is_lib)
      VerilogSaveModule(f,M);
  }

  error_status = ferror(f);
  fclose(f);

  return error_status;
}

/*****************************************************************************
 * Copy a file and return 0 on success or non-zero on failure.
 *****************************************************************************/
static int copy_file(const char *src,const char *dst)
{
  FILE *inf = 0,*outf = 0;
  int r = 0;
  int c;

  inf = fopen(src,"r");
  if (inf)
    outf = fopen(dst,"w");

  if (inf && outf) {
    while ((c = fgetc(inf)) != EOF) {
      if (fputc(c,outf) == EOF) break;
    }

    if (ferror(inf)) r = -1;
    if (ferror(outf)) r = -1;
  } else
    r = -1;

  if (inf)  fclose(inf);
  if (outf) fclose(outf);
  return r;
}

/*****************************************************************************
 * Check a file to see if it is OK by invoking tkgate in verify mode on the
 * file.
 *****************************************************************************/
static int check_file(const char *name)
{
  extern char tkgate_homedir[];
  char cmd[STRMAX];
  int pid = 0,status = 0;

#if 0
  /* Mess with the file to simulate corruption. */
  sprintf(cmd,"xemacs %s",name);
  system(cmd);
#endif

  sprintf(cmd,"%s/libexec/tkgate",tkgate_homedir);
  if (!(pid = fork())) {
    execl(cmd,cmd,"-Vq",name,0);
    exit(1);
  }

  waitpid(pid,&status,0);

  return status;
}


/*****************************************************************************
 *
 * Save a file without confirmation check.
 *
 *****************************************************************************/
int VerilogQuickSave(const char *name)
{
  int r;

  r = VerilogWriteModules(name,0);
  if (r != 0) {
    message(1,msgLookup("err.badsave"),name);
    return r;
  }
  if (check_file(name))
    message(1,msgLookup("msg.corruptnewsave"),name);
  else {
    message(0,msgLookup("msg.save"),name);
    ClearModified();
  }
  return 0;
}

/*****************************************************************************
 *
 * Safely save a file in verilog format.
 *
 *****************************************************************************/
int VerilogSave(const char *name)
{
  char tempName1[STRMAX],tempName2[STRMAX];
  extern int doBackupOnSave;
  int file_exists = 0;
  FILE *f;

  if (!*name) return -1;

  /*
   * Check to see if we are overwriting an existing file.
   */
  file_exists = ((f = fopen(name,"r")) != 0);
  if (f) fclose(f);


  /*
   * If we are overwriting an existing file and safe saving is enabled,
   * we will first write the circuit to a temporary file, check the file
   * to see if it was saved correctly, and only then move it to the
   * target save file.
   *
   */
  if (file_exists) {
    /*
     * If this is the first time we have saved and the file exists, make a backup. 
     */
    if (doBackupOnSave) {
      char buf[STRMAX];
      char *p;

      strcpy(buf,name);
      p = buf+strlen(buf);
      if (p[-1] != '~') {			/* Don't create backup when editing a backup file */
	strcpy(p,"~");
	if (copy_file(name,buf) != 0)
	  message(1,msgLookup("msg.backupfail"),p);
      }
      doBackupOnSave = 0;
    }

    sprintf(tempName1,"%s#save-%d#",name,getpid());
    sprintf(tempName2,"%s#backup-%d#",name,getpid());

    if (copy_file(name,tempName2) != 0 || VerilogWriteModules(tempName1,0) != 0) {
      unlink(tempName1);
      unlink(tempName2);
      DoTcl("yesno [format [m err.nosafesave] %s]",name);
      if (strcmp(XGate.tcl->result,"yes") == 0)
	VerilogQuickSave(name);
      return 0;
    }

    if (check_file(tempName1)) {
      unlink(tempName2);
      message(1,msgLookup("err.corruptsave"),name,tempName1);
      return 0;
    }

    if (rename(tempName1,name) != 0) {
      message(1,msgLookup("err.badrename"),tempName1,name);
      return 0;
    }

    unlink(tempName2);
    message(0,msgLookup("msg.save"),name);
    ClearModified();

    return 0;
  } else {
    VerilogQuickSave(name);
    return 0;
  }
}

int VerilogOpen(EditState **es,const char *name)
{
  FILE *f;

  libraryMode = 0;
  XGate.circuit->discardChanges = 0;
  XGate.circuit->useExtBars = 1;

  if (!(f = fopen(name,"r"))) {
    return -1;
  }

  Circuit_setCurrentFile(name);

  editstate_flushModules(es);
  topModule = 0;

  ycFileName = name;
  ycLineNumber = 1;
  yc_setup();
  BeginVR();
  yyrestart(f);
  yc_pushpool();
  yyparse();
  yc_poppool();
  fclose(f);

  if (!topModule)
    topModule = env_findAdd("main",1);

  editstate_push(es,topModule,0);
  editstate_setCurrent(*es);
  XGate.circuit->root_mod = (*es)->env;

  return 0;
}

int VerilogOpenLibrary(EditState **es,const char *name)
{
  FILE *f;

  libraryMode = 1;

  if (!(f = fopen(name,"r"))) {
    return -1;
  }

  topModule = 0;

  ycFileName = name;
  ycLineNumber = 1;
  yc_setup();
  BeginVR();
  yyrestart(f);
  yc_pushpool();
  yyparse();
  yc_poppool();
  fclose(f);

  return 0;
}

int yyerror(char *err,...)
{
  char buf[STRMAX];
  extern int quietMode;

  va_list ap;

  VerilogErrorCount++;

  va_start(ap, err);

  if (XGate.tcl) {
    sprintf(buf,"tkg_verilogErr %s \"Line %d, ",ycFileName,ycLineNumber);
    vsprintf(buf+strlen(buf),err,ap);
    va_end(ap);
    strcat(buf,"\"");
    Tcl_Eval(XGate.tcl,buf);
  } else {
    if (!quietMode) {
      sprintf(buf,"%s,%d: ",ycFileName,ycLineNumber);
      vsprintf(buf+strlen(buf),err,ap);
      printf("%s\n",buf);
    }
  }

  return 0;
}

/*************************************************************/
/*
   Functions called by parser to construct modules
*/


int ParseVersion(const char *version,Version *V)
{
  char p,x;
  int n;

  if (*version == 'V') version++;	/* Ignore leading V if present */

  if (sscanf(version,"%d.%d.%d",&V->major,&V->minor,&V->revision) == 3)
    return 0;

  n = sscanf(version,"%d.%d%c%c",&V->major,&V->minor,&p,&x);

  switch (n) {
  case 3 :
    V->revision = p - 'a';
    break;
  case 2 :
    V->revision = 0;
    break;
  default :
    return -1;
  }
  return 0;
}

/*
  Compare versions
 */
int VersionCmp(Version *V1,Version *V2)
{
  if (V1->major < V2->major) return -1;
  if (V1->major > V2->major) return 1;

  if (V1->minor < V2->minor) return -1;
  if (V1->minor > V2->minor) return 1;

  if (V1->revision < V2->revision) return -1;
  if (V1->revision > V2->revision) return 1;

  return 0;
}

void VerCheckVersion(const char *version)
{
  int i;
  unsigned flags = VF_UNKNOWN;
  Version V;

  ParseVersion(VERSIONS[0].name, &V);
  cur.gate_ver = V;

  Circuit_setCurrentFileVersion(version);

  if (ParseVersion(version, &V) < 0) {
    message(1,msgLookup("err.badversion"));
    return;
  }

  cur.ver = V;

  for (i = 0;i < numVersions;i++) {
    Version x_V;

    ParseVersion(VERSIONS[i].name, &x_V);

    if (VersionCmp(&x_V,&V) < 0)
      break;

    flags = VERSIONS[i].flags;
  }

  /*
   * Check version number to set default value for extension bar use.
   */
  {
    extern char *extBarVersion;

    Version x_V;
    ParseVersion(extBarVersion,&x_V);
    if (VersionCmp(&V,&x_V) >= 0)
      XGate.circuit->useExtBars = 1;
    else
      XGate.circuit->useExtBars = 0;
  }

  /* Loaded file with outdated version... */
  if ((flags & VF_OLD))
    message(1,msgLookup("err.oldversion"),version,VERSIONS[0].name);

  /* Loaded file with no backwards compatability */
  if ((flags & VF_NOBACK))
    message(1,msgLookup("err.noback"),version,VERSIONS[0].name);

  /* Loaded file with future? version number */
  if ((flags & VF_UNKNOWN))
    message(1,msgLookup("err.futureversion"),version,VERSIONS[0].name);

}

void VerCircuitProp(const char *name,const void *value,int ptype)
{
  if (strcmp(name,"discardChanges") == 0) {
    if (ptype == TYPE_INT) {
      if (!libraryMode) {
	XGate.circuit->discardChanges = *(int*)value;
      }
    }
  } else if (strcmp(name,"technology") == 0) {
    if (ptype == TYPE_STR) {
      if (tkg_defaultTech)
	Tcl_Free(tkg_defaultTech);
      tkg_defaultTech = Tcl_Alloc(strlen(value)+1);
      strcpy(tkg_defaultTech,(char*)value);
      DoTcl("catch { resetTechList }");
    }
  } else if (strcmp(name,"useExtBars") == 0) {
    if (ptype == TYPE_INT) {
      if (!libraryMode) {
	XGate.circuit->useExtBars = *(int*)value;
      }
    }
  }
}

void VerAddScript(const char *name)
{
  const char *argv[3];

  argv[1] = "-script";
  argv[2] = name;
  if (XGate.tcl)
    gat_setCircProp(0,XGate.tcl,3,argv);
}

void VerNewModule(const char *name,int isMain)
{
  GModuleDef *M;
  static int hash_init = 0;

  if (!hash_init) {
    cur.netWEnds = new_SHash();
    hash_init = 1;
  }

  cur_init();

  if (libraryMode) {
    M = env_findModule(name);
    if (!M || M->is_lib ||  (!M->is_top && Hash_numElems(M->gates) == 0)) {
      M = env_removeModule(name);
      if (!M) M = new_GModuleDef(name,isMain);
    } else {
      M = new_GModuleDef(name,isMain);
      cur.del_mod = 1;
    }
    if (M->is_top)
      cur.del_mod = 1;
    M->is_lib = 1;	/* Flag as library module */
  } else {
    M = env_removeModule(name);
    if (!M) M = new_GModuleDef(name,isMain);
  }

  env_clear(M);
  cur.env = M;
  if (isMain && !topModule) topModule = M;

  if (!cur.del_mod)
    env_insertModule(cur.env);
}

void VerModParm(const char *name)
{
}

void VerEndModule()
{
  HashElem *E;
  GWireList *wl;
  NHash *bad_wires = new_NHash();
  NHash *bad_nets = new_NHash();

  /*
     Fix up wire nets.  There may be redunant processing here,
     but I'm too lazy to worry about it right now.
   */
  for (E = Hash_first(cur.env->nets);E;E = Hash_next(cur.env->nets,E)) {
    GNet *n = (GNet*) HashElem_obj(E);
    if (n->driver) {
      wire_finalizeNet(n->driver);
      wire_snap(n->driver->nodes);
    } else
      message(1,msgLookup("err.nodrive"),n->signame,cur.env->Name); /* Net %s has no driver in module %s. */
  }

  for (E = Hash_first(cur.netWEnds);E;E = Hash_next(cur.netWEnds,E)) {
    GWire **wends = (GWire**) HashElem_obj(E);
    ob_free(wends);
  }
  SHash_flush(cur.netWEnds);

  for (wl = cur.env->wires;wl;wl = wl->cdr) {
    GWire *w1 = wl->car;
    GWire *w2 = wire_driver(w1);

    if (w1 == w2) continue;

    if (!w1->gate && !w2->gate) {
      NHash_insert(bad_wires,(nkey_t)w2,w2);
      NHash_insert(bad_wires,(nkey_t)w1,w1);
      NHash_insert(bad_nets,(nkey_t)w1->net,w1->net);
    }
  }

  if (Hash_numElems(bad_wires) > 0) {
    for (E = Hash_first(bad_wires);E;E = Hash_next(bad_wires,E)) {
      GWire *W = (GWire*) HashElem_obj(E);

      message(1,msgLookup("err.noconn"),W->net->signame,W->nidx); /* Wire %s{%d} has no connections - deleted. */
      cur.env->wires = wire_unlink(cur.env->wires,W);
    }
    for (E = Hash_first(bad_nets);E;E = Hash_next(bad_nets,E)) {
      GNet *n = (GNet*) HashElem_obj(E);

      net_delete(n);
    }
  }

  /*
   * If file version does not match current version, apply the version
   * delta function to all gates in the module.
   */
  if (VersionCmp(&cur.gate_ver,&cur.ver) != 0) {
    for (E = Hash_first(cur.env->gates);E;E = Hash_next(cur.env->gates,E)) {
      GCElement *g = (GCElement*) HashElem_obj(E);
      GGateInfo *gi = g->typeinfo;
      if (gi->VersionDelta)
	(*gi->VersionDelta)(g,&cur.ver);
    }
  }

  if (cur.del_mod) {
    delete_GModuleDef(cur.env);
    cur.env = 0;
  }

  delete_NHash(bad_wires);
  delete_NHash(bad_nets);

  cur.env = 0;
}

void VerSetRootMod()
{
  topModule = cur.env;
}

void VerFinishNet()
{
  GWire **wends;
  int i;

  if (!cur.net) return;

  wends = (GWire**) ob_calloc(cur.numWEnds,sizeof(GWire*),"GWire*[]");
  for (i = 0;i < cur.numWEnds;i++)
    wends[i] = cur.wends[i];

  SHash_insert(cur.netWEnds,cur.net->signame,wends);
  cur.net = 0;
}

void VerEndDecls()
{
  VerFinishNet();
}

void VerNewNet(const char *name,int nbits)
{
  VerFinishNet();

  cur.net = (GNet*) net_new(name,cur.env);
  net_setSize(cur.net,nbits);
  cur.numWEnds = 0;
}

void VerGate(const char *func,const char *name)
{
  GGateInfo *gi = gateinfo_lookup(func);
  int vnum = gi ? gateinfo_variantNum(gi,func) : 0;
  GCElement *g;

  if (gi) {
    g = (*gi->MakeFunction)(0,cur.env,gi->Code,0,0,0,name,1,0,0);
  } else {
    gi = gateinfo_lookup("block");
    g = (*gi->MakeFunction)(0,cur.env,gi->Code,0,0,0,name,1,0,0);
    g->u.block.BlockFunction = ob_strdup(func);
  }
  cur.gate = g;
  cur.vnum = vnum;
  cur.numPins = 0;
  cur.pidx = 0;
}

void VerEndGate()
{
  int i;
  unsigned M;
  GGateInfo *gi;

  if (!cur.gate || !cur.vnum) return;
  gi = cur.gate->typeinfo;
  M = spread(gi->vmask,cur.vnum);

  for (i = 0;i < gi->NumPads;i++) {
    GWire *w;
    if (!(M & (1<<i))) continue;

    for (w = cur.gate->wires[i];w;w = w->next)
      w->invert = 1;
  }

  cur.gate = 0;
}

void VerAttach(const char *pin,const char *net,int isComp)
{
  int p = cur.numPins++;

  cur_gpin_extend(p);
  cur.gPin[p] = pin;
  cur.gNet[p] = (GNet*) SHash_find(cur.env->nets,net);
  cur.gComp[p] = isComp;
  if (!cur.gNet[p])
    yyerror("Undeclared net '%s'.",net);
}

void VerTranDup()
{
  int s = cur.numPins-1;
  int d = cur.numPins++;

  cur_gpin_extend(d);
  cur.gPin[d] = "D";
  cur.gNet[d] = cur.gNet[s];
  cur.gComp[d] = 0;
}

void VerTranRange(int msb,int lsb)
{
  cur.gate->u.tap.msb = msb;
  cur.gate->u.tap.lsb = lsb;
}

void VerJointNet(const char *nname)
{
  static GGateInfo *pi = 0;
  GNet *n;
  int i;

  if (!pi) pi = gateinfo_lookup("joint");
  n = (GNet*) SHash_find(cur.env->nets,nname); 
  for (i = 0;i < pi->NumPads;i++) {
    cur_gpin_extend(i);
    cur.gPin[i] = pi->Pad[i].Name;
    cur.gNet[i] = n;
    cur.gComp[i] = 0;
  }
  cur.numPins = pi->NumPads;
}

void VerMakeNode(int x,int y)
{
  GWireNode *n = wire_newnode();
  n->x = x;
  n->y = y;

  if (!cur.node) {
    cur.wire->nodes = n;
    n->end = cur.wire;
  } else {
    cur.node->out = n;
    n->in = cur.node;
  }
  cur.node = n;
}

void VerMakeWire(int p)
{
  GWire *w;

  while (cur.numWEnds <= p) {
    cur_wends_extend(cur.numWEnds);
    cur.wends[cur.numWEnds++] = 0;
  }

  w = wire_newend(cur.env,cur.net,0);
  cur.wends[p] = w;
  w->nidx = p;

  if (p == 0)
    w->net->driver = w;

  if (cur.wire) {
    if (cur.node) {
      w->nodes = cur.node;
      w->nodes->end = w;
      w->driver = cur.wire;
    }
    cur.wire = 0;
  } else {
    cur.wire = w;
    w->driver = w;
  }
  cur.node = 0;
}

void VerSetPos(int x,int y)
{
  if (cur.gate) {
    cur.gate->xpos = x;
    cur.gate->ypos = y;
  }
}

void VerSetSize(int w,int h)
{
  if (cur.gate && cur.gate->typeinfo->Code == BLOCK) {
    cur.gate->u.block.gwidth = w;
    cur.gate->u.block.gheight = h;
  }
}

void VerSetRot(int r)
{
  if (cur.gate) 
    cur.gate->orient = r;
}

void VerSetShowName(int sn)
{
  if (cur.gate)
    cur.gate->show_name = sn;
  else if (cur.net) {
    cur.net->show_name = sn;
    net_update(cur.net);
  }
}

void VerSetWireDecorationPos(int dp)
{
  if (cur.net)
    cur.net->decoration = dp;
}


void VerSetState(int state)
{
  if (!cur.gate) return;
  switch (cur.gate->typeinfo->Code) {
  case SWITCH :
  case DIP :
    cur.gate->u.sw.dipval = cur.gate->u.sw.perm_dipval = state;
    break;
  }
}

void VerSetProperty(const char *prop,int n)
{
  if (strcmp(prop,"/anc") == 0)
    cur.gate->anchored = (n != 0);

  if (strcmp(prop,"/cpc") == 0)
    cur.gate->cpath_cut = (n != 0);

  if (cur.gate->typeinfo->SetProp)
    (*cur.gate->typeinfo->SetProp)(cur.gate,prop,&n);
}

void VerSetStrProperty(const char *prop,const char *value)
{
  if (strcmp(prop,"/tech") == 0) {
    cur.gate->cust_delay = 0; 
    if (cur.gate->tech) ob_free(cur.gate->tech);
    cur.gate->tech = ob_strdup(value);
  }
  if (strcmp(prop,"/delay") == 0) {
    char buf[STRMAX],*p;
    int i;

    cur.gate->cust_delay = 1; 
    strcpy(buf,value);
    for (i = 0, p = strtok(buf," ");p;i++, p = strtok(0," ")) {
      int d;
      sscanf(p,"%d",&d);
      cur.gate->delays[i] = d;
    }
  }

  if (cur.gate->typeinfo->SetProp)
    (*cur.gate->typeinfo->SetProp)(cur.gate,prop,value);
}

static GWire *InsertPadWire(GWire *w,GWire *r)
{
  if (!r) {
    r = w;
  } else if (w == r) {
    yyerror("bogus save data detected. circuit may be damaged.");
  } else if (w->PadNum < r->PadNum) {
    w->next = r;
    r = w;
  } else {
    r->next = InsertPadWire(w,r->next);
  }
  return r;
}

GWire *FastenToGate(GCElement *g,const char *pspec,GNet *n,int p,int bdir)
{
  int pp;			/* Position in pad to connect to */
  int ap;			/* Pad number */
  GGateInfo *gi;		/* Type info for this gate */
  GWire **wends;		/* Wire ends for net n */
  GWire *w;			/* Wire to attach */

  gi = cur.gate->typeinfo;
  wends = (GWire**) SHash_find(cur.netWEnds,n->signame);
  w = wends[p];

  switch (bdir) {
  case '=' : w->WireDir = TRI; break;
  case '>' : w->WireDir = IN; break;
  case '<' : w->WireDir = OUT; break;
  }

  if (pspec)  {
    char pn[STRMAX];		/* Name of pad to connect to */

    switch (sscanf(pspec,"%[^0123456789]%d",pn,&pp)) {
    case 2: 
      break;
    case 1:
      strcpy(pn,pspec);
      pp = 0; 
      break;
    default :
      yyerror("Illegal pin name format '%s'.",pspec);
      return 0;
    }
    for (ap = 0;ap < gi->NumPads;ap++)
      if (strcmp(gi->Pad[ap].Name,pn) == 0)
	break;

    if (ap == gi->NumPads) {
      yyerror("Pin name '%s' not defined for gate type '%s'",
	      pn,gi->englishName);
      return 0;
    }
  } else {
    ap = pp = 0;
  }

  w->PadNum = pp;
  w->next = 0;
  cur.gate->wires[ap] = InsertPadWire(w,cur.gate->wires[ap]);
  w->gate = cur.gate;
  if (gi->Pad[ap].Loc)
    w->orient = gi->Pad[ap].Loc[cur.gate->orient].dir;

  if (gi->Code == BLOCK)
    block_setWireEnd(cur.gate,w,ap);

  return w;
}

void VerPlaceWire(int p)
{
  int x;			/* Position in argument list */ 
  GNet *n;			/* Net for position x */
  const char *pin;		/* Pin name for position x */
  GWire *w;

  x = cur.pidx++;
  if (p < 0) return;
  if (!cur.gate) return;

  n = cur.gNet[x];
  if (n) {
    pin = cur.gPin[x];
    w = FastenToGate(cur.gate,pin,n,p,0);
    if (cur.gComp[x])
      w->invert = 1;
  } else {
    yyerror("No net for position %d on gate %s.",x,cur.gate->ename);
  }
}

void VerBlockPort(const char *pname,int pdir,int widx)
{
  int x;			/* Position in argument list */ 
  GNet *n;			/* Net for position x */
  const char *pin;		/* Pin name for position x */
  GWire *w;

  x = cur.pidx++;
  if (!cur.gate) return;

  n = cur.gNet[x];
  pin = pname;
  w = FastenToGate(cur.gate,pin,n,widx,pdir);
  w->name = ob_strdup(cur.gPin[x]);
}

void VerBeginBD()
{
  GCElement *g = modint_find(cur.env->Name);

  cur.gate = g;
}

void VerEndBD()
{
  cur.gate = 0;
}

void VerBDPort(const char *pname,int pdir,const char *name,int nbits,int onum,int oden)
{
  int x,y;
  GWire *e1,*e2;
  GCElement *g = cur.gate;
  int p;

  x = y = 0;

  p = GGateInfo_getPadNum(g->typeinfo,pname);
  if (p < 0) {
    printf("failed to find pad '%s'\n",pname);
    return;
  }

  wire_new(XGate.circuit->mid_mod,&e1,&e2);

  switch (p) {
  case BLOCK_TIN :
  case BLOCK_TOUT :
  case BLOCK_TTRI :
    x = g->xpos + (g->u.block.gwidth*onum)/oden;
    y = g->ypos;
    break;
  case BLOCK_LIN :
  case BLOCK_LOUT :
  case BLOCK_LTRI :
    x = g->xpos;
    y = g->ypos + (g->u.block.gheight*onum)/oden;
    break;
  case BLOCK_RIN :
  case BLOCK_ROUT :
  case BLOCK_RTRI :
    x = g->xpos + g->u.block.gwidth;
    y = g->ypos + (g->u.block.gheight*onum)/oden;
    break;
  case BLOCK_BIN :
  case BLOCK_BOUT :
  case BLOCK_BTRI :
    x = g->xpos + (g->u.block.gwidth*onum)/oden;
    y = g->ypos + g->u.block.gheight;
    break;
  }
  e1->offset.num = onum;
  e1->offset.den = oden;
  block_attach(XGate.circuit->mid_mod,g,e1,e2,x,y,name,pdir);

  net_setSize(e1->net,nbits);
}

/*
 * Check for trailing ".v"
 */
int isVerilogFile(const char *name)
{
  int l;

  if (!name) return 0;
  l = strlen(name);
  if (l < 2) return 0;

  if (strcmp(name+l-2,".v") == 0)
    return 1;
  else
    return 0;
}

