#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <float.h>

#define EXIT_SUCCESS    0
#define EXIT_FAILURE    1
#define EXIT_HELP       2
#define TRUE            1
#define FALSE           0
#define NMOS            1
#define PMOS            0
#define LengthOfNodeName  100
#define LengthOfLine    200
#define MaxNumberOfInputs 100
#define MaxNumberOfOutputs 100
#define NumberOfUniqueConnections 6

/* getopt stuff */
extern  int     optind, getopt();
extern  char    *optarg;

struct Buffers {
        struct Buffers *next;
        char in[LengthOfLine],out[LengthOfLine];
};

struct Clocks {
        struct Clocks *next;
        char in[LengthOfLine],out[LengthOfLine];
};

struct InputPorts {
        struct InputPorts *next;
        char port[LengthOfLine];
};

struct FlopCell {
	char *name;
	char *pin_in;
	char *pin_out;
	char *pin_clock;
};

struct BufCell {
	char *name;
	char *pin_in;
	char *pin_out;
};

int getline( char s[], int lim, FILE *fp);
struct Buffers *BufferAlloc();
struct Clocks *ClockAlloc();
struct InputPorts *CIAlloc();
void AddClocks( FILE *BDNETFILE ,struct Clocks *Clock, struct Buffers *Buffer,
	int, int, struct InputPorts *, struct BufCell *, struct FlopCell *);
void ReadClockInput(FILE *INPUT, struct InputPorts *ClockedInput);
void ReadGenlib(char *, struct BufCell *, struct FlopCell *);
void helpmessage();

int   NoClockInputs = TRUE;

main ( int argc, char *argv[])
{

        FILE *NET1, *NET2, *OUT, *INPUTFILE;
	struct Buffers *Buffer;
	struct Clocks *Clock;
	struct InputPorts *ClockedInput;
        int i,AllMatched,NetsEqual,ImplicitPower, BuffersOn;

        char Net1name[LengthOfNodeName];
	char *ClockedInputsFile = NULL;
	char *TechFile = NULL;
	struct BufCell bufCell;
	struct FlopCell flopCell;

	bufCell.name = NULL;
	flopCell.name = NULL;

	BuffersOn=TRUE;
        while( (i = getopt( argc, argv, "c:b:f:t:nxhH" )) != EOF ) {
           switch( i ) {
	   case 't':
	       TechFile = strdup(optarg);
	   case 'b':
	       bufCell.name = strdup(optarg);
	       break;
	   case 'f':
	       flopCell.name = strdup(optarg);
	       break;
	   case 'n':
	       BuffersOn=FALSE;
	       break;
	   case 'c':
	       ClockedInputsFile = strdup(optarg);
	       break;
	   case 'x':
	       NoClockInputs=FALSE;
	       break;
           case 'h':
           case 'H':
               helpmessage();
               break;
           default:
               fprintf(stderr,"\nbad switch %d\n", i );
               helpmessage();
               break;
           }
        }

        if( optind < argc )     {
           strcpy(Net1name,argv[optind]);
           optind++;
        }
        else    {
           fprintf(stderr,"Couldn't find a filename as input\n");
           exit(EXIT_FAILURE);
        }
        optind++;
        NET1=fopen(Net1name,"r");
        if (NET1 == NULL ) {
                fprintf(stderr,"Couldn't open %s for read\n",Net1name);
                exit(EXIT_FAILURE);
        }
	ClockedInput=CIAlloc();
	ClockedInput->next=FALSE;
	if (ClockedInputsFile) {
	   INPUTFILE=fopen(ClockedInputsFile,"r");
	   if(INPUTFILE == NULL) {
		fprintf(stderr,"Couldn't find file with inputs to clock: %s\n",
				ClockedInputsFile);
		exit(EXIT_FAILURE);
	   }
	   else {
	      NoClockInputs=FALSE;
	      ReadClockInput(INPUTFILE,ClockedInput);
	   }
	}
        OUT=stdout;
	Clock=ClockAlloc();
	Buffer=BufferAlloc();

	if (!TechFile) TechFile = strdup("/pub/tech/Jazz/verilog/jazzA35.genlib");

	ReadGenlib(TechFile, &bufCell, &flopCell);

	AddClocks(NET1, Clock, Buffer, BuffersOn, (ClockedInputsFile) ? 1 : 0,
		ClockedInput, &bufCell, &flopCell);

	if (ClockedInputsFile)	free(ClockedInputsFile);
	if (TechFile)		free(TechFile);

	if (bufCell.name)	free(bufCell.name);
	if (bufCell.pin_in)	free(bufCell.pin_in);
	if (bufCell.pin_out)	free(bufCell.pin_out);

	if (flopCell.name)	free(flopCell.name);
	if (flopCell.pin_in)	free(flopCell.pin_in);
	if (flopCell.pin_out)	free(flopCell.pin_out);
	if (flopCell.pin_clock)	free(flopCell.pin_clock);

        return 0;

}

/*--------------------------------------------------------------------------------*/
/* Parse the genlib file to get the pin names for the buffer and flop/latch cells */
/*--------------------------------------------------------------------------------*/

void ReadGenlib(char *techName, struct BufCell *bufCell, struct FlopCell *flopCell)
{
    FILE *inFile;
    char line[LengthOfLine];
    char type[LengthOfLine];
    char cellname[LengthOfLine];
    char pin1[LengthOfLine];
    char pin2[LengthOfLine];
    char *scptr;
    int have_buffer = FALSE;
    int have_flop = FALSE;
    int need_control = FALSE;

    inFile = fopen(techName, "r");
    if (inFile == NULL) {
	fprintf(stderr, "Error: Genlib file '%s' not found\n", techName);
	return;
    }

    while (getline(line, sizeof(line), inFile) > 0) {
	if (sscanf(line, "%s %s", type, cellname) == 2) {
	    if (!have_buffer && !strcmp(type, "GATE")) {
	       if (need_control) need_control = FALSE;
	       if (!strcmp(cellname, bufCell->name)) {
		   if (sscanf(line, "%*s %*s %*g %s = %s", pin1, pin2) == 2) {
		      bufCell->pin_out = strdup(pin1);
		      if ((scptr = strchr(pin2, ';')) != NULL) *scptr = '\0';
		      bufCell->pin_in = strdup(pin2);
		      have_buffer = TRUE;
		   }
		   else {
		      fprintf(stderr, "Error:  Gate %s found in %s, but definition:\n",
				bufCell->name, techName);
		      fprintf(stderr, "  '%s'\n", line);
		      fprintf(stderr, "doesn't match expected syntax:\n");
		      fprintf(stderr, "  'GATE %s <value> <pin_out> = <pin_in>;\n",
				bufCell->name);
		      break;
		   }
	       }
	    }
	    else if (!strcmp(type, "LATCH")) {
	       if (need_control) need_control = FALSE;
	       if (!have_flop && !strcmp(cellname, flopCell->name)) {
		   if (sscanf(line, "%*s %*s %*g %s = %s", pin1, pin2) == 2) {
		      flopCell->pin_out = strdup(pin1);
		      if ((scptr = strchr(pin2, ';')) != NULL) *scptr = '\0';
		      flopCell->pin_in = strdup(pin2);
		      need_control = TRUE;
		   }
		   else {
		      fprintf(stderr, "Error:  Latch %s found in %s, but definition:\n",
				flopCell->name, techName);
		      fprintf(stderr, "  '%s'\n", line);
		      fprintf(stderr, "doesn't match expected syntax:\n");
		      fprintf(stderr, "  'LATCH %s <value> <pin_out> = <pin_in>;\n",
				bufCell->name);
		      break;
		   }
	       }
	    }
	    else if (!strcmp(type, "CONTROL")) {
	    	if (need_control) {
		    flopCell->pin_clock = strdup(cellname);
		    need_control = FALSE;
		    have_flop = TRUE;
		}
	    }
	}
    }

    if (!have_buffer) {
	free(bufCell->name);
	bufCell->name = NULL;
    }
    if (!have_flop) {
	free(flopCell->name);
	flopCell->name = NULL;
    }
}

void ReadClockInput(FILE *INPUT, struct InputPorts *ClockedInput)
{
	struct InputPorts *ClockedInputPresent;
	char line[LengthOfLine];
	char port[LengthOfLine];

	ClockedInputPresent=ClockedInput;
        while(getline(line, sizeof(line), INPUT)>0 ) {
	   if(line[0] != '*') {
	      if(sscanf(line,"%s",port) != 1) {
	         fprintf(stderr,"problem reading clock input file :%s\n",line);
	      }
	      ClockedInputPresent->next=CIAlloc();
	      strcpy(ClockedInputPresent->port,port);
	      ClockedInputPresent=ClockedInputPresent->next;
	      ClockedInputPresent->next=NULL;
	   }
	}
}





/*
void AddBuffers( FILE *BDNETFILE, struct Buffers *Buffer )
{
	struct Buffers *BuffPresent;
	char line[LengthOfLine];
	char outnode[LengthOfLine],outlabel[LengthOfLine];
	int OUTPUTSection;


	BuffPresent=Buffer;
        while(getline(line, sizeof(line), BDNETFILE)>0 ) {
	   if(strcmp(line,"OUTPUT") ==0 ) OUTPUTSection=TRUE;
	   if(OUTPUTSection == TRUE) {
	      if( sscanf(line,"%s : %s",outlabel,outnode) ==2) {
	         BuffPresent->next=BuffAlloc();
	         strcpy(BuffPresent->in,outnode);
	         strcpy(BuffPresent->out,outlabel);
	         BuffPresent=BuffPresent->next;
	         BuffPresent->next=NULL;
	         fprintf(stdout,"%s : %s",outlabel,outlabel);
	      }
	      else fprintf(stderr,"Read Error in output \n"); 
	   else fprintf(stdout,"%s",line);

} */

/* Check for net names changed due to an inserted flop */

void CheckClock( char *name, struct Clocks *Clock)
{
 	struct Clocks *ClockPresent;
	int nlen, NeedSemi = FALSE;

	nlen = strlen(name);
	if (name[nlen - 1] == ';') {
	   NeedSemi = TRUE;
	   name[nlen - 1] = '\0';
	}
	    
	    
	for (ClockPresent = Clock; ClockPresent != NULL; ClockPresent =
		ClockPresent->next) {
	    if (!strncmp(name, ClockPresent->in, nlen)) {
		sprintf(name, ClockPresent->out);
		if (NeedSemi) strcat(name, ";");
		break;
	    }
	}
}

/* Add output and (optionally) input buffers buffers */

void AddClocks( FILE *BDNETFILE ,struct Clocks *Clock, struct Buffers *Buffer,
	int BuffersOn, int ClockSomeInputs, struct InputPorts *ClockedInput,
	struct BufCell *bufCell, struct FlopCell *flopCell)
{
	struct InputPorts *ClockedInputPresent;
	struct Clocks *ClockPresent;	
	struct Buffers *BuffPresent;
	int INPUTSection,OUTPUTSection, ADDIO;
	int AddClock2ThisOne;
	int LastInput = FALSE;
	int LastOutput = FALSE;
        char line[LengthOfLine];
	char inlabel[LengthOfLine],innode[LengthOfLine];
	char outlabel[LengthOfLine],outnode[LengthOfLine];

	INPUTSection=FALSE;
        OUTPUTSection=FALSE;
	ADDIO=FALSE;
	ClockPresent=Clock;
	BuffPresent=Buffer;
	if(ClockSomeInputs) ClockedInputPresent=ClockedInput;
        while(getline(line, sizeof(line), BDNETFILE)>0 ) {
	   if(strncmp(line,"INPUT",5) ==0 ) {
	        fprintf(stdout,"INPUT\n");
	        INPUTSection=TRUE;
	        OUTPUTSection=FALSE;
	   }
	   if(strncmp(line,"OUTPUT",6) ==0) {
	        fprintf(stdout,"OUTPUT\n");
		INPUTSection=FALSE;
                OUTPUTSection=TRUE;
	   }
	   if(strncmp(line,"INSTANCE",8) ==0) {
                INPUTSection=FALSE;
                OUTPUTSection=FALSE;
           }
	   if(strncmp(line,"ENDMODEL",8) ==0 ) {
		if (bufCell->name != NULL && flopCell->name != NULL)
	            ADDIO=TRUE;
		else
		    fprintf(stderr,
			"Warning:  No techfile information; cannot add buffers!\n");
	   }
	   if(INPUTSection == TRUE) {
	      AddClock2ThisOne = (NoClockInputs) ? FALSE : TRUE;
	      if( sscanf(line,"%s : %s",inlabel,innode) ==2) {
	         if(ClockSomeInputs) {
	            ClockedInputPresent=ClockedInput;
	            AddClock2ThisOne=TRUE;
	            while(ClockedInputPresent->next != NULL) {
	               if(strstr(inlabel,ClockedInputPresent->port) != NULL) {
	                  AddClock2ThisOne=FALSE;
	               }
	               ClockedInputPresent=ClockedInputPresent->next;
	            }
	         }
	         if(strcmp(inlabel,"\"clock\"") !=0 && AddClock2ThisOne) {
	            ClockPresent->next=ClockAlloc();
	            if( innode[strlen(innode)-1] == ';' ) {
		       LastInput = TRUE;
	               innode[strlen(innode)-1]='\0';
		    }

		    if (!strcmp(inlabel, innode)) {
			/* Input node and label are the same.  Change	*/
			/* the input node whereever it occurs.		*/
			char tempnode[256];
			sprintf(tempnode, "\"int_%s", innode + 1);
			strcpy(innode, tempnode);
		    }

	            fprintf(stdout,"        %s\t :\t %s",inlabel,inlabel); 
		    if (LastInput) fprintf(stdout, ";");
		    fprintf(stdout, "\n");

	            strcpy(ClockPresent->in,inlabel);
	            strcpy(ClockPresent->out,innode);
	            ClockPresent=ClockPresent->next;
	            ClockPresent->next=NULL;
	         }
	         else fprintf(stdout,"%s",line);
	      }
	      else if( sscanf(line,"%s",inlabel) ==1) ;
	   //   else fprintf(stderr,"Read Error in input \n"); 
	   }
	   else if(OUTPUTSection == TRUE && BuffersOn) {
              if( sscanf(line,"%s : %s",outlabel,outnode) ==2) {
	         if( outnode[strlen(outnode)-1] == ';') LastOutput = TRUE;
		 CheckClock(outnode, Clock);
                 BuffPresent->next=BufferAlloc();
                 strcpy(BuffPresent->in,outnode);
                 strcpy(BuffPresent->out,outlabel);
                 BuffPresent=BuffPresent->next;
                 BuffPresent->next=NULL;
	         fprintf(stdout,"        %s\t :\t %s", outlabel,outlabel);
		 if (LastOutput) fprintf(stdout, ";\n");
                 fprintf(stdout,"\n");
              }
	      else if( sscanf(line,"%s",outlabel) ==1) ;
           //   else fprintf(stderr,"Read Error in output \n");
	   }
	   else if(ADDIO == TRUE) {
	      BuffPresent=Buffer;
              while(BuffPresent->next != NULL) {
                 fprintf(stdout,"INSTANCE \"%s\":\"physical\"\n", bufCell->name);
                 fprintf(stdout,"\t\"%s\"\t : \t%s;\n",
			bufCell->pin_in, BuffPresent->in);
                 fprintf(stdout,"\t\"%s\"\t : \t%s;\n",
			bufCell->pin_out, BuffPresent->out);
                 fprintf(stdout,"\n");
                 BuffPresent=BuffPresent->next;
              }
	      if( NoClockInputs == FALSE) {
		  ClockPresent=Clock;
		  while(ClockPresent->next != NULL) {
		      fprintf(stdout,"INSTANCE \"%s\":\"physical\"\n", flopCell->name);
		      fprintf(stdout,"\t\"%s\"\t : \t%s;\n",
				flopCell->pin_in, ClockPresent->in);
		      fprintf(stdout,"\t\"%s\"\t : \t\"clock\";\n",
				flopCell->pin_clock);
		      fprintf(stdout,"\t\"%s\"\t : \t%s;\n",
				flopCell->pin_out, ClockPresent->out);
		      fprintf(stdout,"\n");
		      ClockPresent=ClockPresent->next;
		  }
	      }
	      fprintf(stdout,"ENDMODEL;\n");
	   } 
	   else {
              if( sscanf(line,"%s : %s",outlabel,outnode) == 2) {
		 CheckClock(outnode, Clock);
		 fprintf(stdout,"%s : %s\n",outlabel, outnode);
	      }
	      else
		 fprintf(stdout,"%s",line);
	   }
	}	

}

/*--------------------------------------------------------------*/
/*C getline: read a line, return length         */
/*                                                              */
/*         ARGS:
        RETURNS: 1 to OS
   SIDE EFFECTS:
\*--------------------------------------------------------------*/
int getline( char s[], int lim, FILE *fp)
{
        int c, i;

        i=0;
        while(--lim > 0 && (c=getc(fp)) != EOF && c != '\n')
                s[i++] = c;
        if (c == '\n');
                s[i++] = c;
        s[i] = '\0';
        if ( c == EOF ) i=0;
        return i;
}



struct Buffers *BufferAlloc()
{
	return (struct Buffers *) malloc( sizeof(struct Buffers));
}

struct Clocks *ClockAlloc()
{
	return (struct Clocks *) malloc(sizeof(struct Clocks));
}


struct InputPorts *CIAlloc()
{
	return (struct InputPorts *) malloc(sizeof(struct InputPorts));
}



void helpmessage()
{
    fprintf(stderr,"AddIO2Bdnet [-options] bdnetfile\n");
    fprintf(stderr,"takes a bdnet file as input and adds double buffers\n");

    fprintf(stderr,"to the outputs and D-flops to all inputs. Output on stdout.\n");
    fprintf(stderr,"\nThe option -b 'buffername' uses the cell named 'buffername'\n");
    fprintf(stderr, "for buffer cells.\n");
    fprintf(stderr,"\nThe option -f 'flopname' uses the cell named 'flopname'\n");
    fprintf(stderr, "for clocked latches or flip-flops.\n");
    fprintf(stderr,"\nThe option -n does not add buffers to the output.\n");
    fprintf(stderr,"The option -x adds clocked latches to all inputs.\n");
    fprintf(stderr,"\nThe option -c 'filename', only clocks those inputs found "
	"in 'filename'\n");
    fprintf(stderr,"The format in filename is one input port per line and "
	"comments are\n");
    fprintf(stderr,"allowed on lines starting with a *\n");
    fprintf(stderr,"Furthermore, an input port found in 'filename' that partially\n");
    fprintf(stderr,"matches an input port in the netlist will not be clocked\n");
    fprintf(stderr,"This means that for buses, only the body need to be in 'filename'\n");
    fprintf(stderr,"and the whole bus will be excluded\n");
    fprintf(stderr,"\nVersion 0.03  SB/TE  9/8/06\n");
    exit(EXIT_HELP);
}

