/* ifdef.c - a program for processing Verilog ifdefs By Jeff Winston http://www.kwcpa.com/tools Rev. 1.0, 11/15/02 Copyright (C) 2002 IFDEF is a program that processes Verilog ifdefs, producing an output file that looks like the input file with ifdefs processed. Type ifdef -h for more information --------------------------------------------------------------------- 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. or check out http://www.gnu.org/copyleft/gpl.html --------------------------------------------------------------------- To compile: gcc ifdef.c -o ifdef */ # include # include #define MAXVAR 10000 /* maximum number of ifdef variables */ char instr[511], /* input string */ tokens[64][64], /* tokens from string */ infile[64], /* input filename */ optfile[64], /* options file */ outfile[64], /* output filename */ var[MAXVAR][32], /* ifdef variables */ if_str[32], /* compare strings */ ifdef_str[32], ifndef_str[32], else_str[32], def_str[32], endif_str[32]; int ntk, /* token count */ debug, /* debug on */ cmode, /* cmode on */ collect, /* enables collection of defines from input files */ ignore, /* forces ignore of all undefined flags */ x,y, /* loop vars */ args_processed, /* counts arguments */ lcnt, /* line count */ nest, /* nest level */ vcnt, /* variable count */ long_com, /* static for parser routine - long-comment flag */ vartype[MAXVAR]; /* 1= keep, 0 = process */ /* make the code more readable */ #define ENABLED 0 #define DIS_UNTIL_ELSE 1 #define DIS_UNTIL_ENDIF 2 #define RETAIN 3 #define NEG_DEFINED 2 FILE *infile_ptr,*optfile_ptr, *outfile_ptr; /* file pointers */ void proc_if_def(int cst); int get_tokens(char *str,int special); void add_var(char *tk0,char *tk1,char *tk2,int cnt); main(argc,argv) int argc; /* Parse input file from command line */ char *argv[]; { printf("\nIFDEF Rev 1.0 by J. Winston\n"); if (argc == 2) { ++argv; strcpy(instr,*argv); /* help information */ if (strcmp(instr,"-h")==0) { printf("\nUse: ifdef [-cmode] [-collect] [-retain] \\ \n"); printf(" [-o optsfile] [-f ifdef variable list file] \\ \n"); printf(" [ ... ]\n\n"); printf("-cmode The input is C code. If this is excluded, the input is assumed\n"); printf(" to be verilog code.\n\n"); printf("-collect Causes IFDEF to collect more variable definitions as it processes \n"); printf(" the input file (recommended for verilog).\n\n"); printf("-retain Causes IFDEF to retain (i.e., not process) ifdefs for any \n"); printf(" undefined variables. This limits ifdef processing to \n"); printf(" variables defined in the variable lists. (See below to\n"); printf(" see how to define variables as negative.\n\n"); printf(" -o optsfile IFDEF can read a simulator options file as a source of IFDEF variables.\n\n"); printf(" -f filename IFDEF can read a file (see below) as a source of IFDEF variables.\n\n"); printf("IFDEF takes a C or verilog file as input, and produces as output what the file\n"); printf("would look like if all the ifdefs were executed. It processes files according\n"); printf("to a list of annotated variables, which indicate which ifdefs to process. The\n"); printf("variable list can be in a file (one variable per line), or can be\n"); printf("appended to the command line. Prepending a + to a variable (either in\n"); printf("the variable list file or on the command line) causes IFDEF to skip\n"); printf("processing of ifdefs for that variable. Prepending a - to a variable\n"); printf("(in the variable list, or variable list file) causes IFDEF to consider \n"); printf("that variable as defined but never true (used with the -retain option\n"); printf("above). IFDEF can also read a simulator options file (like the ncopts\n"); printf("file from NC Verilog).\n\n"); printf("IFDEF will also report mismatched ifdef/endif pairs.\n\n"); printf("in C mode, IFDEF recognizes #ifdef , #if , #ifndef , #if defined & #if !defined.\n\n"); printf("The output is a verilog or C file with IFDEFs processed. It is interesting\n"); printf("to review the result by comparing it to the original using tkdiff.\n\n"); exit(0); } } if (argc < 3) /* print help message if too few input parameters */ { printf("Error: Incorrect number of parameters on command line\n"); printf("Type ifdef -h for help\n"); exit(0); } /* read input line */ ++argv; /* open input file based on filename */ strcpy(infile,*argv); if ( (infile_ptr=fopen(infile, "r")) ==NULL ) { printf("Error: Error opening input file /%s/\n\n",infile); exit(1); } ++argv; /* open output file based on filename */ strcpy(outfile,*argv); if ((outfile_ptr=fopen(outfile, "w")) ==NULL) { printf("Error: Error opening output file /%s/\n\n",outfile); exit(1); } args_processed = 2; vcnt=0; debug=0; cmode=0; /* parse command line */ ignore = 0; collect = 0; while (args_processed < (argc-1)) { ++argv; args_processed++; strcpy(instr,*argv); if (strcmp(instr,"-o")==0) /* Read NC verilog options file */ { ++argv; args_processed++; if (args_processed > (argc-1)) { printf("Error: No argument for -o\n\n"); exit(1); } strcpy(optfile,*argv); if ( (optfile_ptr=fopen(optfile, "r")) ==NULL ) { printf("Error: Error opening input file /%s/\n\n",optfile); exit(1); } printf("Reading options from %s\n",optfile); while (fgets(instr,255,optfile_ptr) != NULL) /* Keep getting lines until no more data */ { ntk = get_tokens(instr,1); /* parse line with special delimiters */ if (ntk > 0) { if (strcmp(tokens[0],"define")==0) /* see if this is a define statement */ { add_var(tokens[1],tokens[2],tokens[3],ntk-1); } } } close (optfile_ptr); } else if (strncmp(instr,"-debug",2)==0) /* debug flag */ { debug=1; } else if (strncmp(instr,"-cmode",3)==0) /* c mode flag */ { cmode=1; } else if (strncmp(instr,"-collect",3)==0) /* collection enable flag */ { collect=1; } else if (strncmp(instr,"-ignore",2)==0) /* collection enable flag */ { ignore=1; } else if (strncmp(instr,"-file",2)==0) /* read variables file */ { ++argv; args_processed++; if (args_processed > (argc-1)) { printf("Error: No argument for -f\n\n"); exit(1); } strcpy(optfile,*argv); if ( (optfile_ptr=fopen(optfile, "r")) ==NULL ) { printf("Error: Error opening input file /%s/\n\n",optfile); exit(1); } printf("Reading variables from %s\n",optfile); while (fgets(instr,255,optfile_ptr) != NULL) /* Keep getting lines until no more data */ { ntk = get_tokens(instr,0); add_var(tokens[0],tokens[1],tokens[2],ntk); } } else /* read variables off command line */ { if (vcnt > 9999) { printf("Error: Too many ifdef variables.\n"); exit(1); } add_var(*argv,*argv,*argv,1); } } /* initialize global variables */ long_com = 0; nest=0; lcnt=0; if (cmode == 1) /* for c*/ { strcpy(if_str,"#if"); strcpy(ifdef_str,"#ifdef"); strcpy(ifndef_str,"#ifndef"); strcpy(else_str,"#else"); strcpy(endif_str,"#endif"); strcpy(def_str,"#define"); /* not filtered in c mode */ } else /* for verilog */ { strcpy(if_str,"`ifdef"); /* same as ifdef */ strcpy(ifdef_str,"`ifdef"); strcpy(ifndef_str,"(&^%$"); strcpy(else_str,"`else"); strcpy(endif_str,"`endif"); strcpy(def_str,"`define"); } /* call processing routine */ proc_if_def(0); /* we're done, print final messages */ if (nest !=0) printf ("Error: Bad nesting, missing %d endif(s)\n",nest); printf("%s written\n",outfile); fclose(outfile_ptr); if ((outfile_ptr=fopen("ifdef_args.log", "w")) ==NULL) { printf("Error: Error opening output file /ifdef_args.log/\n\n"); exit(1); } for (x=0;x 0) /* don't process lines with no tokens */ { if (((cst == ENABLED) || (cst == RETAIN)) && ((strcmp(tokens[0],ifdef_str)==0) || ((cmode == 1) && ((strcmp(tokens[0],if_str)==0) || (strcmp(tokens[0],ifndef_str)==0))))) /* if printing enabled and an ifdef */ { /* if c mode if-defined construct - move token into right spot */ if ((cmode ==1) && (strcmp(tokens[0],if_str)==0) && (strcmp(tokens[1],"defined")==0)) strcpy(tokens[2],tokens[1]); next_cst = DIS_UNTIL_ELSE; /* set default next state to disabled-until-else */ if (ignore == 1) next_cst = RETAIN; /* unless ignore is enabled, then set default next state to retain */ found_action = ENABLED; /* set default 'if-found' next state to print-enabled */ /* if c- mode if-not-defined construct, set default 'if-found' to print-disabled */ if ((cmode ==1) && (((strcmp(tokens[0],if_str)==0) && (strcmp(tokens[1],"!defined")==0)) || (strcmp(tokens[0],ifndef_str)==0))) { found_action= DIS_UNTIL_ELSE; } for (x=0; x< vcnt; x++) { if (strcmp(tokens[1],var[x])==0) /* if variable found */ if (vartype[x] == ENABLED) next_cst=found_action; /* if normal variable, set state to default if-found state */ else if (vartype[x] == NEG_DEFINED) /* if variable negated, set state to opposite of default if_found state */ { if (found_action == ENABLED) next_cst = DIS_UNTIL_ELSE; else next_cst = ENABLED; } else /* if (vartype[x]) == retain */ next_cst = RETAIN; /* if variable set as retained, keep it */ } nest++; /* call proc_if_def and increment nesting level */ if (next_cst == RETAIN) fprintf(outfile_ptr,"%s",instr); /* print it if a keeper */ proc_if_def(next_cst); } else if (cst == RETAIN) /* if type is retain, always print the line */ { fprintf(outfile_ptr,"%s",instr); if (strcmp(tokens[0],endif_str)==0) { nest--; return; /* and return if we're done */ } } else if (((cst == DIS_UNTIL_ELSE) || (cst == DIS_UNTIL_ENDIF)) && ((strcmp(tokens[0],ifdef_str)==0) || ((cmode == 1) && ((strcmp(tokens[0],if_str)==0) || (strcmp(tokens[0],ifndef_str)==0))))) /* if printing disabled and an ifdef */ { if (debug == 1) printf("Not processing %s",instr); nest++; /* increment nesting level, and call proc_if_def */ proc_if_def(2); /* with next state disabled-until-endif */ } else if ((cst == DIS_UNTIL_ELSE) && (strcmp(tokens[0],else_str)==0)) /* if disabled-until-else, and we find an else */ { cst = ENABLED; /* set state to enabled and check else count */ if (elscnt > 0) { printf("Error: Extra Else on line %d\n",lcnt); } else elscnt++; } else if ((cst == ENABLED) && (strcmp(tokens[0],else_str)==0)) /* if enabled, and we find an else */ { cst = DIS_UNTIL_ENDIF; /* set state to disabled-until-endif */ if (elscnt > 0) /* and check else count */ { printf("Error: Extra Else on line %d\n",lcnt); } else elscnt++; } else if (strcmp(tokens[0],endif_str)==0) /* at endif, decrease nest count and return */ { nest--; return; } else if ( (collect == 1) && (cst == ENABLED) && (strcmp(tokens[0],def_str)==0)) /* if enabled, and we see a define */ { fprintf(outfile_ptr,"%s",instr); /* print line */ add_var(tokens[1],tokens[2],tokens[3],ntk-1); } else if (cst==ENABLED) fprintf(outfile_ptr,"%s",instr); /* if not a control line and state=enabled, print line */ } else if ((cst==ENABLED) || (cst == RETAIN)) fprintf(outfile_ptr,"%s",instr); } } void add_var(char *tk0,char *tk1,char *tk2,int cnt) /* add a variable to the list */ { char tmp[32]; int keep,val,y,processed; if (cnt == 0) return; /* nothing to do */ processed = 0; /* init variables */ keep = 0; /* parse variable name */ if (tk0[0] == '+') /* a + predecessor means keep */ { strcpy(tmp,&tk0[1]); keep=1; } else if (tk0[0] == '-') /* a - predecessor means defined as 0 */ { strcpy(tmp,&tk0[1]); keep=2; } else { strcpy(tmp,tk0); } /* try to parse the real value */ if (cnt == 1) val=1; else { if (strcmp(tk1,"1'b0")==0) val=0; else if (strcmp(tk1,"1'b1")==0) val=1; else if ((strcmp(tk1,"1'b")==0) && (cnt > 2)) sscanf(tk2,"%d",&val); else sscanf(tk1,"%d",&val); } for(y=0;y 9999) { printf("Error: Too many ifdef variables.\n"); exit(1); } strcpy(var[vcnt],tmp); vartype[vcnt] = keep; vcnt++; } } int get_tokens(char *str,int special) /* get tokens on line. Put in global tokens[][], return count */ /* special adds + and = to the list of possible separators */ { int len, loc, cnt, start, waiting_for_token; /* waiting_for_token is a state variable that indicates if we're collecting characters into a token, or waiting for a valid token to start */ len = strlen(str); waiting_for_token=1; cnt=0; for (loc=0; loc <= len; loc++) { /* process comment conditions first */ if (loc <= len - 1) { if ((long_com==0) && (str[loc] == '/') && (str[loc+1] == '/')) /* the start of a single line comment */ { if (waiting_for_token==0) /* if state = running, append good characters to string and we're done */ { strncpy(tokens[cnt-1],&str[start],loc-start); strncpy(&tokens[cnt-1][loc-start],"\0",1); } return(cnt); } else if ((long_com==0) && (str[loc] == '/') && (str[loc+1] == '*')) /* the start of a long comment */ { long_com = 1; /* set flag, advance past comment identifier */ if (waiting_for_token==0) /* if state = running, append good characters to string and change state to waiting */ { waiting_for_token=1; strncpy(tokens[cnt-1],&str[start],loc-start); strncpy(&tokens[cnt-1][loc-start],"\0",1); } loc=loc+2; } else if ((long_com==1) && (str[loc] == '*') && (str[loc+1] == '/')) /* the end of a long comment */ { long_com = 0; /* clear flag and advance past comment identifier */ loc=loc+2; } } if ((long_com==0) && (loc <=len)) /* if not a comment, process string */ { /* if not a terminating character */ if ((loc < len) && (str[loc] != ' ') && (str[loc] != ' ') && (str[loc] != '\n') && (str[loc] != '(') && (str[loc] != ')') && (str[loc] != '\r') && ((special==0) || ((special==1) && (str[loc] != '=') && (str[loc] != '+')))) { if (waiting_for_token==1) /* and state = waiting */ { cnt++; /* incr token count, flip state to running, assign start location */ waiting_for_token=0; start=loc; } } else /* if a terminating character */ { if (waiting_for_token==0) /* and state = running, flip state to waiting, append good characters to token string */ { waiting_for_token=1; strncpy(tokens[cnt-1],&str[start],loc-start); strncpy(&tokens[cnt-1][loc-start],"\0",1); } } } } return(cnt); }