You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							2231 lines
						
					
					
						
							56 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							2231 lines
						
					
					
						
							56 KiB
						
					
					
				| /** | |
|   @file | |
|  | |
|   @ingroup nanotrav | |
|  | |
|   @brief Functions to read in a boolean network. | |
|  | |
|   @author Fabio Somenzi | |
|  | |
|   @copyright@parblock | |
|   Copyright (c) 1995-2015, Regents of the University of Colorado | |
|  | |
|   All rights reserved. | |
|  | |
|   Redistribution and use in source and binary forms, with or without | |
|   modification, are permitted provided that the following conditions | |
|   are met: | |
|  | |
|   Redistributions of source code must retain the above copyright | |
|   notice, this list of conditions and the following disclaimer. | |
|  | |
|   Redistributions in binary form must reproduce the above copyright | |
|   notice, this list of conditions and the following disclaimer in the | |
|   documentation and/or other materials provided with the distribution. | |
|  | |
|   Neither the name of the University of Colorado nor the names of its | |
|   contributors may be used to endorse or promote products derived from | |
|   this software without specific prior written permission. | |
|  | |
|   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
|   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
|   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
|   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
|   COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
|   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
|   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
|   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
|   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
|   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
|   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
|   POSSIBILITY OF SUCH DAMAGE. | |
|   @endparblock | |
|  | |
| */ | |
| 
 | |
| #include "cuddInt.h" | |
| #include "bnet.h" | |
|  | |
| /*---------------------------------------------------------------------------*/ | |
| /* Constant declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| #define MAXLENGTH 131072 | |
|  | |
| /*---------------------------------------------------------------------------*/ | |
| /* Stucture declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Type declarations                                                         */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Variable declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| static	char	BuffLine[MAXLENGTH]; | |
| static	char	*CurPos; | |
| static	int	newNameNumber = 0; | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Macro declarations                                                        */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| /** \cond */ | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Static function prototypes                                                */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| static char * readString (FILE *fp); | |
| static char ** readList (FILE *fp, int *n); | |
| static void printList (char **list, int n); | |
| static char ** bnetGenerateNewNames (st_table *hash, int n); | |
| static int bnetDumpReencodingLogic (DdManager *dd, char *mname, int noutputs, DdNode **outputs, char **inames, char **altnames, char **onames, FILE *fp); | |
| #if 0 | |
| static int bnetBlifXnorTable (FILE *fp, int n); | |
| #endif | |
| static int bnetBlifWriteReencode (DdManager *dd, char *mname, char **inames, char **altnames, int *support, FILE *fp); | |
| static int * bnetFindVectorSupport (DdManager *dd, DdNode **list, int n); | |
| static int buildExorBDD (DdManager *dd, BnetNode *nd, st_table *hash, int params, int nodrop); | |
| static int buildMuxBDD (DdManager * dd, BnetNode * nd, st_table * hash, int  params, int  nodrop); | |
| static int bnetSetLevel (BnetNetwork *net); | |
| static int bnetLevelDFS (BnetNetwork *net, BnetNode *node); | |
| static BnetNode ** bnetOrderRoots (BnetNetwork *net, int *nroots); | |
| static int bnetLevelCompare (BnetNode **x, BnetNode **y); | |
| static int bnetDfsOrder (DdManager *dd, BnetNetwork *net, BnetNode *node); | |
| 
 | |
| /** \endcond */ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of exported functions                                          */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Reads boolean network from blif file. | |
|  | |
|   @details A very restricted subset of blif is supported. Specifically: | |
|   <ul> | |
|   <li> The only directives recognized are: | |
|     <ul> | |
|     <li> .model | |
|     <li> .inputs | |
|     <li> .outputs | |
|     <li> .latch | |
|     <li> .names | |
|     <li> .exdc | |
|     <li> .wire_load_slope | |
|     <li> .end | |
|     </ul> | |
|   <li> Latches must have an initial values and no other parameters | |
|        specified. | |
|   <li> Lines must not exceed MAXLENGTH-1 characters, and individual names must | |
|        not exceed 1023 characters. | |
|   </ul> | |
|   Caveat emptor: There may be other limitations as well. One should | |
|   check the syntax of the blif file with some other tool before relying | |
|   on this parser. | |
|  | |
|   @return a pointer to the network if successful; NULL otherwise. | |
|    | |
|   @sideeffect None | |
|  | |
|   @see Bnet_PrintNetwork Bnet_FreeNetwork | |
|  | |
| */ | |
| BnetNetwork * | |
| Bnet_ReadNetwork( | |
|   FILE * fp /**< pointer to the blif file */, | |
|   int  pr /**< verbosity level */) | |
| { | |
|     char *savestring; | |
|     char **list; | |
|     int i, j, n; | |
|     BnetNetwork *net; | |
|     BnetNode *newnode; | |
|     BnetNode *lastnode = NULL; | |
|     BnetTabline *newline; | |
|     BnetTabline *lastline; | |
|     char ***latches = NULL; | |
|     int maxlatches = 0; | |
|     int exdc = 0; | |
|     BnetNode	*node; | |
|     int	count; | |
| 
 | |
|     /* Allocate network object and initialize symbol table. */ | |
|     net = ALLOC(BnetNetwork,1); | |
|     if (net == NULL) goto failure; | |
|     memset((char *) net, 0, sizeof(BnetNetwork)); | |
|     net->hash = st_init_table((st_compare_t) strcmp, st_strhash); | |
|     if (net->hash == NULL) goto failure; | |
| 
 | |
|     savestring = readString(fp); | |
|     if (savestring == NULL) goto failure; | |
|     net->nlatches = 0; | |
|     while (strcmp(savestring, ".model") == 0 || | |
| 	strcmp(savestring, ".inputs") == 0 || | |
| 	strcmp(savestring, ".outputs") == 0 || | |
| 	strcmp(savestring, ".latch") == 0 || | |
| 	strcmp(savestring, ".wire_load_slope") == 0 || | |
| 	strcmp(savestring, ".exdc") == 0 || | |
| 	strcmp(savestring, ".names") == 0 || strcmp(savestring,".end") == 0) { | |
| 	if (strcmp(savestring, ".model") == 0) { | |
| 	    /* Read .model directive. */ | |
| 	    FREE(savestring); | |
| 	    /* Read network name. */ | |
| 	    savestring = readString(fp); | |
| 	    if (savestring == NULL) goto failure; | |
| 	    if (savestring[0] == '.') { | |
| 		net->name = ALLOC(char,  1); | |
| 		if (net->name == NULL) goto failure; | |
|                 net->name[0] = '\0'; | |
| 	    } else { | |
| 		net->name = savestring; | |
| 	    } | |
| 	} else if (strcmp(savestring, ".inputs") == 0) { | |
| 	    /* Read .inputs directive. */ | |
| 	    FREE(savestring); | |
| 	    /* Read input names. */ | |
| 	    list = readList(fp,&n); | |
| 	    if (list == NULL) goto failure; | |
| 	    if (pr > 2) printList(list,n); | |
| 	    /* Expect at least one input. */ | |
| 	    if (n < 1) { | |
| 		(void) fprintf(stdout,"Empty input list.\n"); | |
| 		goto failure; | |
| 	    } | |
| 	    if (exdc) { | |
| 		for (i = 0; i < n; i++) | |
| 		    FREE(list[i]); | |
| 		FREE(list); | |
| 		savestring = readString(fp); | |
| 		if (savestring == NULL) goto failure; | |
| 		continue; | |
| 	    } | |
| 	    if (net->ninputs) { | |
| 		net->inputs = REALLOC(char *, net->inputs, | |
| 		    (net->ninputs + n) * sizeof(char *)); | |
| 		for (i = 0; i < n; i++) | |
| 		    net->inputs[net->ninputs + i] = list[i]; | |
| 	    } | |
| 	    else | |
| 		net->inputs = list; | |
| 	    /* Create a node for each primary input. */ | |
| 	    for (i = 0; i < n; i++) { | |
| 		newnode = ALLOC(BnetNode,1); | |
| 		memset((char *) newnode, 0, sizeof(BnetNode)); | |
| 		if (newnode == NULL) goto failure; | |
| 		newnode->name = list[i]; | |
| 		newnode->inputs = NULL; | |
| 		newnode->type = BNET_INPUT_NODE; | |
| 		newnode->active = FALSE; | |
| 		newnode->nfo = 0; | |
| 		newnode->ninp = 0; | |
| 		newnode->f = NULL; | |
| 		newnode->polarity = 0; | |
| 		newnode->dd = NULL; | |
| 		newnode->next = NULL; | |
| 		if (lastnode == NULL) { | |
| 		    net->nodes = newnode; | |
| 		} else { | |
| 		    lastnode->next = newnode; | |
| 		} | |
| 		lastnode = newnode; | |
| 	    } | |
| 	    net->npis += n; | |
| 	    net->ninputs += n; | |
| 	} else if (strcmp(savestring, ".outputs") == 0) { | |
| 	    /* Read .outputs directive. We do not create nodes for the primary | |
| 	    ** outputs, because the nodes will be created when the same names | |
| 	    ** appear as outputs of some gates. | |
| 	    */ | |
| 	    FREE(savestring); | |
| 	    /* Read output names. */ | |
| 	    list = readList(fp,&n); | |
| 	    if (list == NULL) goto failure; | |
| 	    if (pr > 2) printList(list,n); | |
| 	    if (n < 1) { | |
| 		(void) fprintf(stdout,"Empty .outputs list.\n"); | |
| 		goto failure; | |
| 	    } | |
| 	    if (exdc) { | |
| 		for (i = 0; i < n; i++) | |
| 		    FREE(list[i]); | |
| 		FREE(list); | |
| 		savestring = readString(fp); | |
| 		if (savestring == NULL) goto failure; | |
| 		continue; | |
| 	    } | |
| 	    if (net->noutputs) { | |
| 		net->outputs = REALLOC(char *, net->outputs, | |
| 		    (net->noutputs + n) * sizeof(char *)); | |
| 		for (i = 0; i < n; i++) | |
| 		    net->outputs[net->noutputs + i] = list[i]; | |
| 	    } else { | |
| 		net->outputs = list; | |
| 	    } | |
| 	    net->npos += n; | |
| 	    net->noutputs += n; | |
| 	} else if (strcmp(savestring,".wire_load_slope") == 0) { | |
| 	    FREE(savestring); | |
| 	    savestring = readString(fp); | |
| 	    net->slope = savestring; | |
| 	} else if (strcmp(savestring,".latch") == 0) { | |
| 	    FREE(savestring); | |
| 	    newnode = ALLOC(BnetNode,1); | |
| 	    if (newnode == NULL) goto failure; | |
| 	    memset((char *) newnode, 0, sizeof(BnetNode)); | |
| 	    newnode->type = BNET_PRESENT_STATE_NODE; | |
| 	    list = readList(fp,&n); | |
| 	    if (list == NULL) goto failure; | |
| 	    if (pr > 2) printList(list,n); | |
| 	    /* Expect three names. */ | |
| 	    if (n != 3) { | |
| 		(void) fprintf(stdout, | |
| 			       ".latch not followed by three tokens.\n"); | |
| 		goto failure; | |
| 	    } | |
| 	    newnode->name = list[1]; | |
| 	    newnode->inputs = NULL; | |
| 	    newnode->ninp = 0; | |
| 	    newnode->f = NULL; | |
| 	    newnode->active = FALSE; | |
| 	    newnode->nfo = 0; | |
| 	    newnode->polarity = 0; | |
| 	    newnode->dd = NULL; | |
| 	    newnode->next = NULL; | |
| 	    if (lastnode == NULL) { | |
| 		net->nodes = newnode; | |
| 	    } else { | |
| 		lastnode->next = newnode; | |
| 	    } | |
| 	    lastnode = newnode; | |
| 	    /* Add next state variable to list. */ | |
| 	    if (maxlatches == 0) { | |
| 		maxlatches = 20; | |
| 		latches = ALLOC(char **,maxlatches); | |
| 	    } else if (maxlatches <= net->nlatches) { | |
| 		maxlatches += 20; | |
| 		latches = REALLOC(char **,latches,maxlatches); | |
| 	    } | |
| 	    latches[net->nlatches] = list; | |
| 	    net->nlatches++; | |
| 	    savestring = readString(fp); | |
| 	    if (savestring == NULL) goto failure; | |
| 	} else if (strcmp(savestring,".names") == 0) { | |
| 	    FREE(savestring); | |
| 	    newnode = ALLOC(BnetNode,1); | |
| 	    memset((char *) newnode, 0, sizeof(BnetNode)); | |
| 	    if (newnode == NULL) goto failure; | |
| 	    list = readList(fp,&n); | |
| 	    if (list == NULL) goto failure; | |
| 	    if (pr > 2) printList(list,n); | |
| 	    /* Expect at least one name (the node output). */ | |
| 	    if (n < 1) { | |
| 		(void) fprintf(stdout,"Missing output name.\n"); | |
| 		goto failure; | |
| 	    } | |
| 	    newnode->name = list[n-1]; | |
| 	    newnode->inputs = list; | |
| 	    newnode->ninp = n-1; | |
| 	    newnode->active = FALSE; | |
| 	    newnode->nfo = 0; | |
| 	    newnode->polarity = 0; | |
| 	    if (newnode->ninp > 0) { | |
| 		newnode->type = BNET_INTERNAL_NODE; | |
| 		for (i = 0; i < net->noutputs; i++) { | |
| 		    if (strcmp(net->outputs[i], newnode->name) == 0) { | |
| 			newnode->type = BNET_OUTPUT_NODE; | |
| 			break; | |
| 		    } | |
| 		} | |
| 	    } else { | |
| 		newnode->type = BNET_CONSTANT_NODE; | |
| 	    } | |
| 	    newnode->dd = NULL; | |
| 	    newnode->next = NULL; | |
| 	    if (lastnode == NULL) { | |
| 		net->nodes = newnode; | |
| 	    } else { | |
| 		lastnode->next = newnode; | |
| 	    } | |
| 	    lastnode = newnode; | |
| 	    /* Read node function. */ | |
| 	    newnode->f = NULL; | |
| 	    if (exdc) { | |
| 		newnode->exdc_flag = 1; | |
| 		node = net->nodes; | |
| 		while (node) { | |
| 		    if (node->type == BNET_OUTPUT_NODE && | |
| 			strcmp(node->name, newnode->name) == 0) { | |
| 			node->exdc = newnode; | |
| 			break; | |
| 		    } | |
| 		    node = node->next; | |
| 		} | |
| 	    } | |
| 	    savestring = readString(fp); | |
| 	    if (savestring == NULL) goto failure; | |
| 	    lastline = NULL; | |
| 	    while (savestring[0] != '.') { | |
| 		/* Reading a table line. */ | |
| 		newline = ALLOC(BnetTabline,1); | |
| 		if (newline == NULL) goto failure; | |
| 		newline->next = NULL; | |
| 		if (lastline == NULL) { | |
| 		    newnode->f = newline; | |
| 		} else { | |
| 		    lastline->next = newline; | |
| 		} | |
| 		lastline = newline; | |
| 		if (newnode->type == BNET_INTERNAL_NODE || | |
| 		    newnode->type == BNET_OUTPUT_NODE) { | |
| 		    newline->values = savestring; | |
| 		    /* Read output 1 or 0. */ | |
| 		    savestring = readString(fp); | |
| 		    if (savestring == NULL) goto failure; | |
| 		} else { | |
| 		    newline->values = NULL; | |
| 		} | |
| 		if (savestring[0] == '0') newnode->polarity = 1; | |
| 		FREE(savestring); | |
| 		savestring = readString(fp); | |
| 		if (savestring == NULL) goto failure; | |
| 	    } | |
| 	} else if (strcmp(savestring,".exdc") == 0) { | |
| 	    FREE(savestring); | |
| 	    exdc = 1; | |
| 	} else if (strcmp(savestring,".end") == 0) { | |
| 	    FREE(savestring); | |
| 	    break; | |
| 	} | |
| 	if ((!savestring) || savestring[0] != '.') | |
| 	    savestring = readString(fp); | |
| 	if (savestring == NULL) goto failure; | |
|     } | |
| 
 | |
|     /* Put nodes in symbol table. */ | |
|     newnode = net->nodes; | |
|     while (newnode != NULL) { | |
| 	int retval = st_insert(net->hash,newnode->name,(char *) newnode); | |
| 	if (retval == ST_OUT_OF_MEM) { | |
| 	    goto failure; | |
| 	} else if (retval == 1) { | |
| 	    printf("Error: Multiple drivers for node %s\n", newnode->name); | |
| 	    goto failure; | |
| 	} else { | |
| 	    if (pr > 2) printf("Inserted %s\n",newnode->name); | |
| 	} | |
| 	newnode = newnode->next; | |
|     } | |
| 
 | |
|     if (latches) { | |
| 	net->latches = latches; | |
| 
 | |
| 	count = 0; | |
| 	net->outputs = REALLOC(char *, net->outputs, | |
| 	    (net->noutputs + net->nlatches) * sizeof(char *)); | |
| 	for (i = 0; i < net->nlatches; i++) { | |
| 	    for (j = 0; j < net->noutputs; j++) { | |
| 		if (strcmp(latches[i][0], net->outputs[j]) == 0) | |
| 		    break; | |
| 	    } | |
| 	    if (j < net->noutputs) | |
| 		continue; | |
| 	    savestring = ALLOC(char, strlen(latches[i][0]) + 1); | |
| 	    strcpy(savestring, latches[i][0]); | |
| 	    net->outputs[net->noutputs + count] = savestring; | |
| 	    count++; | |
| 	    if (st_lookup(net->hash, savestring, (void **) &node)) { | |
| 		if (node->type == BNET_INTERNAL_NODE) { | |
| 		    node->type = BNET_OUTPUT_NODE; | |
| 		} | |
| 	    } | |
| 	} | |
| 	net->noutputs += count; | |
| 
 | |
| 	net->inputs = REALLOC(char *, net->inputs, | |
| 	    (net->ninputs + net->nlatches) * sizeof(char *)); | |
| 	for (i = 0; i < net->nlatches; i++) { | |
| 	    savestring = ALLOC(char, strlen(latches[i][1]) + 1); | |
| 	    strcpy(savestring, latches[i][1]); | |
| 	    net->inputs[net->ninputs + i] = savestring; | |
| 	} | |
| 	net->ninputs += net->nlatches; | |
|     } | |
| 
 | |
|     /* Compute fanout counts. For each node in the linked list, fetch | |
|     ** all its fanins using the symbol table, and increment the fanout of | |
|     ** each fanin. | |
|     */ | |
|     newnode = net->nodes; | |
|     while (newnode != NULL) { | |
| 	BnetNode *auxnd; | |
| 	for (i = 0; i < newnode->ninp; i++) { | |
| 	    if (!st_lookup(net->hash,newnode->inputs[i],(void **)&auxnd)) { | |
| 		(void) fprintf(stdout,"%s not driven\n", newnode->inputs[i]); | |
| 		goto failure; | |
| 	    } | |
| 	    auxnd->nfo++; | |
| 	} | |
| 	newnode = newnode->next; | |
|     } | |
| 
 | |
|     if (!bnetSetLevel(net)) goto failure; | |
| 
 | |
|     return(net); | |
| 
 | |
| failure: | |
|     /* Here we should clean up the mess. */ | |
|     (void) fprintf(stdout,"Error in reading network from file.\n"); | |
|     return(NULL); | |
| 
 | |
| } /* end of Bnet_ReadNetwork */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Prints to stdout a boolean network created by Bnet_ReadNetwork. | |
|  | |
|   @details Uses the blif format; this way, one can verify the | |
|   equivalence of the input and the output with, say, sis. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Bnet_ReadNetwork | |
|  | |
| */ | |
| void | |
| Bnet_PrintNetwork( | |
|   BnetNetwork * net /**< boolean network */) | |
| { | |
|     BnetNode *nd; | |
|     BnetTabline *tl; | |
|     int i; | |
| 
 | |
|     if (net == NULL) return; | |
| 
 | |
|     (void) fprintf(stdout,".model %s\n", net->name); | |
|     (void) fprintf(stdout,".inputs"); | |
|     printList(net->inputs,net->npis); | |
|     (void) fprintf(stdout,".outputs"); | |
|     printList(net->outputs,net->npos); | |
|     for (i = 0; i < net->nlatches; i++) { | |
| 	(void) fprintf(stdout,".latch"); | |
| 	printList(net->latches[i],3); | |
|     } | |
|     nd = net->nodes; | |
|     while (nd != NULL) { | |
| 	if (nd->type != BNET_INPUT_NODE && nd->type != BNET_PRESENT_STATE_NODE) { | |
| 	    (void) fprintf(stdout,".names"); | |
| 	    for (i = 0; i < nd->ninp; i++) { | |
| 		(void) fprintf(stdout," %s",nd->inputs[i]); | |
| 	    } | |
| 	    (void) fprintf(stdout," %s\n",nd->name); | |
| 	    tl = nd->f; | |
| 	    while (tl != NULL) { | |
| 		if (tl->values != NULL) { | |
| 		    (void) fprintf(stdout,"%s %d\n",tl->values, | |
| 		    1 - nd->polarity); | |
| 		} else { | |
| 		    (void) fprintf(stdout,"%d\n", 1 - nd->polarity); | |
| 		} | |
| 		tl = tl->next; | |
| 	    } | |
| 	} | |
| 	nd = nd->next; | |
|     } | |
|     (void) fprintf(stdout,".end\n"); | |
| 
 | |
| } /* end of Bnet_PrintNetwork */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Frees a boolean network created by Bnet_ReadNetwork. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Bnet_ReadNetwork | |
|  | |
| */ | |
| void | |
| Bnet_FreeNetwork( | |
|   BnetNetwork * net) | |
| { | |
|     BnetNode *node, *nextnode; | |
|     BnetTabline *line, *nextline; | |
|     int i; | |
| 
 | |
|     FREE(net->name); | |
|     /* The input name strings are already pointed by the input nodes. | |
|     ** Here we only need to free the latch names and the array that | |
|     ** points to them. | |
|     */ | |
|     for (i = 0; i < net->nlatches; i++) { | |
| 	FREE(net->inputs[net->npis + i]); | |
|     } | |
|     FREE(net->inputs); | |
|     /* Free the output name strings and then the array pointing to them.  */ | |
|     for (i = 0; i < net->noutputs; i++) { | |
| 	FREE(net->outputs[i]); | |
|     } | |
|     FREE(net->outputs); | |
| 
 | |
|     for (i = 0; i < net->nlatches; i++) { | |
| 	FREE(net->latches[i][0]); | |
| 	FREE(net->latches[i][1]); | |
| 	FREE(net->latches[i][2]); | |
| 	FREE(net->latches[i]); | |
|     } | |
|     if (net->nlatches) FREE(net->latches); | |
|     node = net->nodes; | |
|     while (node != NULL) { | |
| 	nextnode = node->next; | |
| 	if (node->type != BNET_PRESENT_STATE_NODE) | |
| 	    FREE(node->name); | |
| 	for (i = 0; i < node->ninp; i++) { | |
| 	    FREE(node->inputs[i]); | |
| 	} | |
| 	if (node->inputs != NULL) { | |
| 	    FREE(node->inputs); | |
| 	} | |
| 	/* Free the function table. */ | |
| 	line = node->f; | |
| 	while (line != NULL) { | |
| 	    nextline = line->next; | |
| 	    FREE(line->values); | |
| 	    FREE(line); | |
| 	    line = nextline; | |
| 	} | |
| 	FREE(node); | |
| 	node = nextnode; | |
|     } | |
|     st_free_table(net->hash); | |
|     if (net->slope != NULL) FREE(net->slope); | |
|     FREE(net); | |
| 
 | |
| } /* end of Bnet_FreeNetwork */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Builds the %BDD for the function of a node. | |
|  | |
|   @details Builds the %BDD for the function of a node and stores a | |
|   pointer to it in the dd field of the node itself. The reference count | |
|   of the %BDD is incremented. If params is BNET_LOCAL_DD, then the %BDD is | |
|   built in terms of the local inputs to the node; otherwise, if params | |
|   is BNET_GLOBAL_DD, the %BDD is built in terms of the network primary | |
|   inputs. To build the global %BDD of a node, the BDDs for its local | |
|   inputs must exist. If that is not the case, Bnet_BuildNodeBDD | |
|   recursively builds them. Likewise, to create the local %BDD for a node, | |
|   the local inputs must have variables assigned to them. If that is not | |
|   the case, Bnet_BuildNodeBDD recursively assigns variables to nodes. | |
|  | |
|   @return 1 in case of success; 0 otherwise. | |
|  | |
|   @sideeffect Sets the dd field of the node. | |
|  | |
| */ | |
| int | |
| Bnet_BuildNodeBDD( | |
|   DdManager * dd /**< %DD manager */, | |
|   BnetNode * nd /**< node of the boolean network */, | |
|   st_table * hash /**< symbol table of the boolean network */, | |
|   int  params /**< type of %DD to be built */, | |
|   int  nodrop /**< retain the intermediate node DDs until the end */) | |
| { | |
|     DdNode *func; | |
|     BnetNode *auxnd; | |
|     DdNode *tmp; | |
|     DdNode *prod, *var; | |
|     BnetTabline *line; | |
|     int i; | |
| 
 | |
|     if (nd->dd != NULL) return(1); | |
| 
 | |
|     if (nd->type == BNET_CONSTANT_NODE) { | |
| 	if (nd->f == NULL) { /* constant 0 */ | |
| 	    func = Cudd_ReadLogicZero(dd); | |
| 	} else { /* either constant depending on the polarity */ | |
| 	    func = Cudd_ReadOne(dd); | |
| 	} | |
| 	Cudd_Ref(func); | |
|     } else if (nd->type == BNET_INPUT_NODE || | |
| 	       nd->type == BNET_PRESENT_STATE_NODE) { | |
| 	if (nd->active == TRUE) { /* a variable is already associated: use it */ | |
| 	    func = Cudd_ReadVars(dd,nd->var); | |
| 	    if (func == NULL) goto failure; | |
| 	} else { /* no variable associated: get a new one */ | |
| 	    func = Cudd_bddNewVar(dd); | |
| 	    if (func == NULL) goto failure; | |
| 	    nd->var = func->index; | |
| 	    nd->active = TRUE; | |
| 	} | |
| 	Cudd_Ref(func); | |
|     } else if (buildExorBDD(dd,nd,hash,params,nodrop)) { | |
| 	func = nd->dd; | |
|     } else if (buildMuxBDD(dd,nd,hash,params,nodrop)) { | |
| 	func = nd->dd; | |
|     } else { /* type == BNET_INTERNAL_NODE or BNET_OUTPUT_NODE */ | |
| 	/* Initialize the sum to logical 0. */ | |
| 	func = Cudd_ReadLogicZero(dd); | |
| 	Cudd_Ref(func); | |
| 
 | |
| 	/* Build a term for each line of the table and add it to the | |
| 	** accumulator (func). | |
| 	*/ | |
| 	line = nd->f; | |
| 	while (line != NULL) { | |
| #ifdef BNET_DEBUG | |
| 	    (void) fprintf(stdout,"line = %s\n", line->values); | |
| #endif | |
| 	    /* Initialize the product to logical 1. */ | |
| 	    prod = Cudd_ReadOne(dd); | |
| 	    Cudd_Ref(prod); | |
| 	    /* Scan the table line. */ | |
| 	    for (i = 0; i < nd->ninp; i++) { | |
| 		if (line->values[i] == '-') continue; | |
| 		if (!st_lookup(hash,nd->inputs[i],(void **)&auxnd)) { | |
| 		    goto failure; | |
| 		} | |
| 		if (params == BNET_LOCAL_DD) { | |
| 		    if (auxnd->active == FALSE) { | |
| 			if (!Bnet_BuildNodeBDD(dd,auxnd,hash,params,nodrop)) { | |
| 			    goto failure; | |
| 			} | |
| 		    } | |
| 		    var = Cudd_ReadVars(dd,auxnd->var); | |
| 		    if (var == NULL) goto failure; | |
| 		    Cudd_Ref(var); | |
| 		    if (line->values[i] == '0') { | |
| 			var = Cudd_Not(var); | |
| 		    } | |
| 		} else { /* params == BNET_GLOBAL_DD */ | |
| 		    if (auxnd->dd == NULL) { | |
| 			if (!Bnet_BuildNodeBDD(dd,auxnd,hash,params,nodrop)) { | |
| 			    goto failure; | |
| 			} | |
| 		    } | |
| 		    if (line->values[i] == '1') { | |
| 			var = auxnd->dd; | |
| 		    } else { /* line->values[i] == '0' */ | |
| 			var = Cudd_Not(auxnd->dd); | |
| 		    } | |
| 		} | |
| 		tmp = Cudd_bddAnd(dd,prod,var); | |
| 		if (tmp == NULL) goto failure; | |
| 		Cudd_Ref(tmp); | |
| 		Cudd_IterDerefBdd(dd,prod); | |
| 		if (params == BNET_LOCAL_DD) { | |
| 		    Cudd_IterDerefBdd(dd,var); | |
| 		} | |
| 		prod = tmp; | |
| 	    } | |
| 	    tmp = Cudd_bddOr(dd,func,prod); | |
| 	    if (tmp == NULL) goto failure; | |
| 	    Cudd_Ref(tmp); | |
| 	    Cudd_IterDerefBdd(dd,func); | |
| 	    Cudd_IterDerefBdd(dd,prod); | |
| 	    func = tmp; | |
| 	    line = line->next; | |
| 	} | |
| 	/* Associate a variable to this node if local BDDs are being | |
| 	** built. This is done at the end, so that the primary inputs tend | |
| 	** to get lower indices. | |
| 	*/ | |
| 	if (params == BNET_LOCAL_DD && nd->active == FALSE) { | |
| 	    DdNode *auxfunc = Cudd_bddNewVar(dd); | |
| 	    if (auxfunc == NULL) goto failure; | |
| 	    Cudd_Ref(auxfunc); | |
| 	    nd->var = auxfunc->index; | |
| 	    nd->active = TRUE; | |
| 	    Cudd_IterDerefBdd(dd,auxfunc); | |
| 	} | |
|     } | |
|     if (nd->polarity == 1) { | |
| 	nd->dd = Cudd_Not(func); | |
|     } else { | |
| 	nd->dd = func; | |
|     } | |
| 
 | |
|     if (params == BNET_GLOBAL_DD && nodrop == FALSE) { | |
| 	/* Decrease counters for all faninis. | |
| 	** When count reaches 0, the DD is freed. | |
| 	*/ | |
| 	for (i = 0; i < nd->ninp; i++) { | |
| 	    if (!st_lookup(hash,nd->inputs[i],(void **)&auxnd)) { | |
| 		goto failure; | |
| 	    } | |
| 	    auxnd->count--; | |
| 	    if (auxnd->count == 0) { | |
| 		Cudd_IterDerefBdd(dd,auxnd->dd); | |
| 		if (auxnd->type == BNET_INTERNAL_NODE || | |
| 		    auxnd->type == BNET_CONSTANT_NODE) auxnd->dd = NULL; | |
| 	    } | |
| 	} | |
|     } | |
|     return(1); | |
| 
 | |
| failure: | |
|     /* Here we should clean up the mess. */ | |
|     return(0); | |
| 
 | |
| } /* end of Bnet_BuildNodeBDD */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Orders the %BDD variables by DFS. | |
|  | |
|   @return 1 in case of success; 0 otherwise. | |
|  | |
|   @sideeffect Uses the visited flags of the nodes. | |
|  | |
| */ | |
| int | |
| Bnet_DfsVariableOrder( | |
|   DdManager * dd, | |
|   BnetNetwork * net) | |
| { | |
|     BnetNode **roots; | |
|     BnetNode *node; | |
|     int nroots; | |
|     int i; | |
| 
 | |
|     roots = bnetOrderRoots(net,&nroots); | |
|     if (roots == NULL) return(0); | |
|     for (i = 0; i < nroots; i++) { | |
| 	if (!bnetDfsOrder(dd,net,roots[i])) { | |
| 	    FREE(roots); | |
| 	    return(0); | |
| 	} | |
|     } | |
|     /* Clear visited flags. */ | |
|     node = net->nodes; | |
|     while (node != NULL) { | |
| 	node->visited = 0; | |
| 	node = node->next; | |
|     } | |
|     FREE(roots); | |
|     return(1); | |
| 
 | |
| } /* end of Bnet_DfsVariableOrder */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Writes the network BDDs to a file in dot, blif, or daVinci | |
|   format. | |
|  | |
|   @details If "-" is passed as file name, the BDDs are dumped to the | |
|   standard output. | |
|  | |
|   @return 1 in case of success; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| int | |
| Bnet_bddDump( | |
|   DdManager * dd /**< %DD manager */, | |
|   BnetNetwork * network /**< network whose BDDs should be dumped */, | |
|   char * dfile /**< file name */, | |
|   int  dumpFmt /**< 0 -> dot */, | |
|   int  reencoded /**< whether variables have been reencoded */) | |
| { | |
|     int noutputs; | |
|     FILE *dfp = NULL; | |
|     DdNode **outputs = NULL; | |
|     char **inames = NULL; | |
|     char **onames = NULL; | |
|     char **altnames = NULL; | |
|     BnetNode *node; | |
|     int i; | |
|     int retval = 0; /* 0 -> failure; 1 -> success */ | |
| 
 | |
|     /* Open dump file. */ | |
|     if (strcmp(dfile, "-") == 0) { | |
| 	dfp = stdout; | |
|     } else { | |
| 	dfp = fopen(dfile,"w"); | |
|     } | |
|     if (dfp == NULL) goto endgame; | |
| 
 | |
|     /* Initialize data structures. */ | |
|     noutputs = network->noutputs; | |
|     outputs = ALLOC(DdNode *,noutputs); | |
|     if (outputs == NULL) goto endgame; | |
|     onames = ALLOC(char *,noutputs); | |
|     if (onames == NULL) goto endgame; | |
|     inames = ALLOC(char *,Cudd_ReadSize(dd)); | |
|     if (inames == NULL) goto endgame; | |
| 
 | |
|     /* Find outputs and their names. */ | |
|     for (i = 0; i < network->nlatches; i++) { | |
| 	onames[i] = network->latches[i][0]; | |
| 	if (!st_lookup(network->hash,network->latches[i][0],(void **)&node)) { | |
| 	    goto endgame; | |
| 	} | |
| 	outputs[i] = node->dd; | |
|     } | |
|     for (i = 0; i < network->npos; i++) { | |
| 	onames[i + network->nlatches] = network->outputs[i]; | |
| 	if (!st_lookup(network->hash,network->outputs[i],(void **)&node)) { | |
| 	    goto endgame; | |
| 	} | |
| 	outputs[i + network->nlatches] = node->dd; | |
|     } | |
| 
 | |
|     /* Find the input names. */ | |
|     for (i = 0; i < network->ninputs; i++) { | |
| 	if (!st_lookup(network->hash,network->inputs[i],(void **)&node)) { | |
| 	    goto endgame; | |
| 	} | |
| 	inames[node->var] = network->inputs[i]; | |
|     } | |
|     for (i = 0; i < network->nlatches; i++) { | |
| 	if (!st_lookup(network->hash,network->latches[i][1],(void **)&node)) { | |
| 	    goto endgame; | |
| 	} | |
| 	inames[node->var] = network->latches[i][1]; | |
|     } | |
| 
 | |
|     if (reencoded == 1 && dumpFmt == 1) { | |
| 	altnames = bnetGenerateNewNames(network->hash,network->ninputs); | |
| 	if (altnames == NULL) { | |
| 	    retval = 0; | |
| 	    goto endgame; | |
| 	} | |
| 	retval = bnetDumpReencodingLogic(dd,network->name,noutputs,outputs, | |
| 		 inames,altnames,onames,dfp); | |
| 	for (i = 0; i < network->ninputs; i++) { | |
| 	    FREE(altnames[i]); | |
| 	} | |
| 	FREE(altnames); | |
| 	if (retval == 0) goto endgame; | |
|     } | |
| 
 | |
|     /* Dump the BDDs. */ | |
|     if (dumpFmt == 1) { | |
| 	retval = Cudd_DumpBlif(dd,noutputs,outputs, | |
| 			       (char const * const *) inames, | |
| 			       (char const * const *) onames, | |
| 			       network->name,dfp,0); | |
|     } else if (dumpFmt == 2) { | |
| 	retval = Cudd_DumpDaVinci(dd,noutputs,outputs, | |
| 				  (char const * const *) inames, | |
| 				  (char const * const *) onames,dfp); | |
|     } else if (dumpFmt == 3) { | |
| 	retval = Cudd_DumpDDcal(dd,noutputs,outputs, | |
| 				(char const * const *) inames, | |
| 				(char const * const *) onames,dfp); | |
|     } else if (dumpFmt == 4) { | |
| 	retval = Cudd_DumpFactoredForm(dd,noutputs,outputs, | |
| 				       (char const * const *) inames, | |
| 				       (char const * const *) onames,dfp); | |
|     } else if (dumpFmt == 5) { | |
| 	retval = Cudd_DumpBlif(dd,noutputs,outputs, | |
| 			       (char const * const *) inames, | |
| 			       (char const * const *) onames, | |
| 			       network->name,dfp,1); | |
|     } else { | |
| 	retval = Cudd_DumpDot(dd,noutputs,outputs, | |
| 			      (char const * const *) inames, | |
| 			      (char const * const *) onames,dfp); | |
|     } | |
| 
 | |
| endgame: | |
|     if (dfp != stdout && dfp != NULL) { | |
| 	if (fclose(dfp) == EOF) retval = 0; | |
|     } | |
|     if (outputs != NULL) FREE(outputs); | |
|     if (onames  != NULL) FREE(onames); | |
|     if (inames  != NULL) FREE(inames); | |
| 
 | |
|     return(retval); | |
| 
 | |
| } /* end of Bnet_bddDump */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Writes an array of BDDs to a file in dot, blif, DDcal, | |
|   factored-form, daVinci, or blif-MV format. | |
|  | |
|   @details The BDDs and their names are passed as arguments.  The | |
|   inputs and their names are taken from the network. If "-" is passed | |
|   as file name, the BDDs are dumped to the standard output.  The encoding | |
|   of the format is: | |
|   <ul> | |
|   <li>0: dot | |
|   <li>1: blif | |
|   <li>2: da Vinci | |
|   <li>3: ddcal | |
|   <li>4: factored form | |
|   <li>5: blif-MV | |
|   </ul> | |
|  | |
|   @return 1 in case of success; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| int | |
| Bnet_bddArrayDump( | |
|   DdManager * dd /**< %DD manager */, | |
|   BnetNetwork * network /**< network whose BDDs should be dumped */, | |
|   char * dfile /**< file name */, | |
|   DdNode ** outputs /**< BDDs to be dumped */, | |
|   char ** onames /**< names of the BDDs to be dumped */, | |
|   int  noutputs /**< number of BDDs to be dumped */, | |
|   int  dumpFmt /**< 0 -> dot */) | |
| { | |
|     FILE *dfp = NULL; | |
|     char **inames = NULL; | |
|     BnetNode *node; | |
|     int i; | |
|     int retval = 0; /* 0 -> failure; 1 -> success */ | |
| 
 | |
|     /* Open dump file. */ | |
|     if (strcmp(dfile, "-") == 0) { | |
| 	dfp = stdout; | |
|     } else { | |
| 	dfp = fopen(dfile,"w"); | |
|     } | |
|     if (dfp == NULL) goto endgame; | |
| 
 | |
|     /* Initialize data structures. */ | |
|     inames = ALLOC(char *,Cudd_ReadSize(dd)); | |
|     if (inames == NULL) goto endgame; | |
|     for (i = 0; i < Cudd_ReadSize(dd); i++) { | |
| 	inames[i] = NULL; | |
|     } | |
| 
 | |
|     /* Find the input names. */ | |
|     for (i = 0; i < network->ninputs; i++) { | |
| 	if (!st_lookup(network->hash,network->inputs[i],(void **)&node)) { | |
| 	    goto endgame; | |
| 	} | |
| 	inames[node->var] = network->inputs[i]; | |
|     } | |
|     for (i = 0; i < network->nlatches; i++) { | |
| 	if (!st_lookup(network->hash,network->latches[i][1],(void **)&node)) { | |
| 	    goto endgame; | |
| 	} | |
| 	inames[node->var] = network->latches[i][1]; | |
|     } | |
| 
 | |
|     /* Dump the BDDs. */ | |
|     if (dumpFmt == 1) { | |
| 	retval = Cudd_DumpBlif(dd,noutputs,outputs, | |
| 			       (char const * const *) inames, | |
| 			       (char const * const *) onames, | |
| 			       network->name,dfp,0); | |
|     } else if (dumpFmt == 2) { | |
| 	retval = Cudd_DumpDaVinci(dd,noutputs,outputs, | |
| 				  (char const * const *) inames, | |
| 				  (char const * const *) onames,dfp); | |
|     } else if (dumpFmt == 3) { | |
| 	retval = Cudd_DumpDDcal(dd,noutputs,outputs, | |
| 				(char const * const *) inames, | |
| 				(char const * const *) onames,dfp); | |
|     } else if (dumpFmt == 4) { | |
| 	retval = Cudd_DumpFactoredForm(dd,noutputs,outputs, | |
| 				       (char const * const *) inames, | |
| 				       (char const * const *) onames,dfp); | |
|     } else if (dumpFmt == 5) { | |
| 	retval = Cudd_DumpBlif(dd,noutputs,outputs, | |
| 			       (char const * const *) inames, | |
| 			       (char const * const *) onames, | |
| 			       network->name,dfp,1); | |
|     } else { | |
| 	retval = Cudd_DumpDot(dd,noutputs,outputs, | |
| 			      (char const * const *) inames, | |
| 			      (char const * const *) onames,dfp); | |
|     } | |
| 
 | |
| endgame: | |
|     if (dfp != stdout && dfp != NULL) { | |
| 	if (fclose(dfp) == EOF) retval = 0; | |
|     } | |
|     if (inames  != NULL) FREE(inames); | |
| 
 | |
|     return(retval); | |
| 
 | |
| } /* end of Bnet_bddArrayDump */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Reads the variable order from a file. | |
|  | |
|   @return 1 if successful; 0 otherwise. | |
|  | |
|   @sideeffect The BDDs for the primary inputs and present state variables | |
|   are built. | |
|  | |
| */ | |
| int | |
| Bnet_ReadOrder( | |
|   DdManager * dd, | |
|   char * ordFile, | |
|   BnetNetwork * net, | |
|   int  locGlob, | |
|   int  nodrop) | |
| { | |
|     FILE *fp; | |
|     st_table *dict; | |
|     int result; | |
|     BnetNode *node; | |
|     char name[MAXLENGTH]; | |
| 
 | |
|     if (ordFile == NULL) { | |
| 	return(0); | |
|     } | |
| 
 | |
|     dict = st_init_table((st_compare_t) strcmp,st_strhash); | |
|     if (dict == NULL) { | |
| 	return(0); | |
|     } | |
| 
 | |
|     if ((fp = fopen(ordFile,"r")) == NULL) { | |
| 	(void) fprintf(stderr,"Unable to open %s\n",ordFile); | |
| 	st_free_table(dict); | |
| 	return(0); | |
|     } | |
| 
 | |
|     while (!feof(fp)) { | |
| 	result = fscanf(fp, "%s", name); | |
| 	if (result == EOF) { | |
| 	    break; | |
| 	} else if (result != 1) { | |
| 	    st_free_table(dict); | |
| 	    return(0); | |
| 	} else if (strlen(name) > MAXLENGTH) { | |
| 	    st_free_table(dict); | |
| 	    return(0); | |
| 	} | |
| 	/* There should be a node named "name" in the network. */ | |
| 	if (!st_lookup(net->hash,name,(void **)&node)) { | |
| 	    (void) fprintf(stderr,"Unknown name in order file (%s)\n", name); | |
| 	    st_free_table(dict); | |
| 	    return(0); | |
| 	} | |
| 	/* A name should not appear more than once in the order. */ | |
| 	if (st_is_member(dict,name)) { | |
| 	    (void) fprintf(stderr,"Duplicate name in order file (%s)\n", name); | |
| 	    st_free_table(dict); | |
| 	    return(0); | |
| 	} | |
| 	/* The name should correspond to a primary input or present state. */ | |
| 	if (node->type != BNET_INPUT_NODE && | |
| 	    node->type != BNET_PRESENT_STATE_NODE) { | |
| 	    (void) fprintf(stderr,"%s has the wrong type (%d)\n", name, | |
| 			   node->type); | |
| 	    st_free_table(dict); | |
| 	    return(0); | |
| 	} | |
| 	/* Insert in table. Use node->name rather than name, because the | |
| 	** latter gets overwritten. | |
| 	*/ | |
| 	if (st_insert(dict,node->name,NULL) == ST_OUT_OF_MEM) { | |
| 	    (void) fprintf(stderr,"Out of memory in Bnet_ReadOrder\n"); | |
| 	    st_free_table(dict); | |
| 	    return(0); | |
| 	} | |
| 	result = Bnet_BuildNodeBDD(dd,node,net->hash,locGlob,nodrop); | |
| 	if (result == 0) { | |
| 	    (void) fprintf(stderr,"Construction of BDD failed\n"); | |
| 	    st_free_table(dict); | |
| 	    return(0); | |
| 	} | |
|     } /* while (!feof(fp)) */ | |
|     result = fclose(fp); | |
|     if (result == EOF) { | |
| 	(void) fprintf(stderr,"Error closing order file %s\n", ordFile); | |
| 	st_free_table(dict); | |
| 	return(0); | |
|     } | |
| 
 | |
|     /* The number of names in the order file should match exactly the | |
|     ** number of primary inputs and present states. | |
|     */ | |
|     if (st_count(dict) != net->ninputs) { | |
| 	(void) fprintf(stderr,"Order incomplete: %d names instead of %d\n", | |
| 		       st_count(dict), net->ninputs); | |
| 	st_free_table(dict); | |
| 	return(0); | |
|     } | |
| 
 | |
|     st_free_table(dict); | |
|     return(1); | |
| 
 | |
| } /* end of Bnet_ReadOrder */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Prints the order of the %DD variables of a network. | |
|  | |
|   @details Only primary inputs and present states are printed. | |
|  | |
|   @return 1 if successful; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| int | |
| Bnet_PrintOrder( | |
|   BnetNetwork * net, | |
|   DdManager *dd) | |
| { | |
|     char **names;		/* array used to print variable orders */ | |
|     int	level;			/* position of a variable in current order */ | |
|     BnetNode *node;		/* auxiliary pointer to network node */ | |
|     int i,j; | |
|     int retval; | |
|     int nvars; | |
| 
 | |
|     nvars = Cudd_ReadSize(dd); | |
|     names = ALLOC(char *, nvars); | |
|     if (names == NULL) return(0); | |
|     for (i = 0; i < nvars; i++) { | |
| 	names[i] = NULL; | |
|     } | |
|     for (i = 0; i < net->npis; i++) { | |
| 	if (!st_lookup(net->hash,net->inputs[i],(void **)&node)) { | |
| 	    FREE(names); | |
| 	    return(0); | |
| 	} | |
| 	if (node->dd == NULL) { | |
| 	    FREE(names); | |
| 	    return(0); | |
| 	} | |
| 	level = Cudd_ReadPerm(dd,node->var); | |
| 	names[level] = node->name; | |
|     } | |
|     for (i = 0; i < net->nlatches; i++) { | |
| 	if (!st_lookup(net->hash,net->latches[i][1],(void **)&node)) { | |
| 	    FREE(names); | |
| 	    return(0); | |
| 	} | |
| 	if (node->dd == NULL) { | |
| 	    FREE(names); | |
| 	    return(0); | |
| 	} | |
| 	level = Cudd_ReadPerm(dd,node->var); | |
| 	names[level] = node->name; | |
|     } | |
|     for (i = 0, j = 0; i < nvars; i++) { | |
| 	if (names[i] == NULL) continue; | |
| 	if ((j%8 == 0)&&j) { | |
| 	    retval = printf("\n"); | |
| 	    if (retval == EOF) { | |
| 		FREE(names); | |
| 		return(0); | |
| 	    } | |
| 	} | |
| 	retval = printf("%s ",names[i]); | |
| 	if (retval == EOF) { | |
| 	    FREE(names); | |
| 	    return(0); | |
| 	} | |
| 	j++; | |
|     } | |
|     FREE(names); | |
|     retval = printf("\n"); | |
|     if (retval == EOF) { | |
| 	return(0); | |
|     } | |
|     return(1); | |
| 
 | |
| } /* end of Bnet_PrintOrder */ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of internal functions                                          */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of static functions                                            */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Reads a string from a file. | |
|  | |
|   @details The string can be MAXLENGTH-1 characters at | |
|   most. readString allocates memory to hold the string. | |
|  | |
|   @return a pointer to the result string if successful. It returns | |
|   NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see readList | |
|  | |
| */ | |
| static char * | |
| readString( | |
|   FILE * fp /**< pointer to the file from which the string is read */) | |
| { | |
|     char *savestring; | |
|     int length; | |
| 
 | |
|     while (!CurPos) { | |
| 	if (!fgets(BuffLine, MAXLENGTH, fp)) | |
| 	    return(NULL); | |
| 	BuffLine[strlen(BuffLine) - 1] = '\0'; | |
| 	CurPos = strtok(BuffLine, " \t"); | |
| 	if (CurPos && CurPos[0] == '#') CurPos = (char *)NULL; | |
|     } | |
|     length = strlen(CurPos); | |
|     savestring = ALLOC(char,length+1); | |
|     if (savestring == NULL) | |
| 	return(NULL); | |
|     strcpy(savestring,CurPos); | |
|     CurPos = strtok(NULL, " \t"); | |
|     return(savestring); | |
| 
 | |
| } /* end of readString */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Reads a list of strings from a line of a file. | |
|  | |
|   @details The strings are sequences of characters separated by spaces | |
|   or tabs.  The total length of the list, white space included, must | |
|   not exceed MAXLENGTH-1 characters.  readList allocates memory for | |
|   the strings and creates an array of pointers to the individual | |
|   lists. Only two pieces of memory are allocated by readList: One to | |
|   hold all the strings, and one to hold the pointers to | |
|   them. Therefore, when freeing the memory allocated by readList, only | |
|   the pointer to the list of pointers, and the pointer to the | |
|   beginning of the first string should be freed. | |
|  | |
|   @return the pointer to the list of pointers if successful; NULL | |
|   otherwise. | |
|  | |
|   @sideeffect n is set to the number of strings in the list. | |
|  | |
|   @see readString printList | |
|  | |
| */ | |
| static char ** | |
| readList( | |
|   FILE * fp /**< pointer to the file from which the list is read */, | |
|   int * n /**< on return, number of strings in the list */) | |
| { | |
|     char	*savestring; | |
|     int		length; | |
|     char	*stack[8192]; | |
|     char	**list; | |
|     int		i, count = 0; | |
| 
 | |
|     while (CurPos) { | |
| 	if (strcmp(CurPos, "\\") == 0) { | |
| 	    CurPos = (char *)NULL; | |
| 	    while (!CurPos) { | |
| 		if (!fgets(BuffLine, MAXLENGTH, fp)) return(NULL); | |
| 		BuffLine[strlen(BuffLine) - 1] = '\0'; | |
| 		CurPos = strtok(BuffLine, " \t"); | |
| 	    } | |
| 	} | |
| 	length = strlen(CurPos); | |
| 	savestring = ALLOC(char,length+1); | |
| 	if (savestring == NULL) return(NULL); | |
| 	strcpy(savestring,CurPos); | |
| 	stack[count] = savestring; | |
| 	count++; | |
| 	CurPos = strtok(NULL, " \t"); | |
|     } | |
|     list = ALLOC(char *, count); | |
|     for (i = 0; i < count; i++) | |
| 	list[i] = stack[i]; | |
|     *n = count; | |
|     return(list); | |
| 
 | |
| } /* end of readList */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Prints a list of strings to the standard output. | |
|  | |
|   @details The list is in the format created by readList. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see readList Bnet_PrintNetwork | |
|  | |
| */ | |
| static void | |
| printList( | |
|   char ** list /**< list of pointers to strings */, | |
|   int  n /**< length of the list */) | |
| { | |
|     int i; | |
| 
 | |
|     for (i = 0; i < n; i++) { | |
| 	(void) fprintf(stdout," %s",list[i]); | |
|     } | |
|     (void) fprintf(stdout,"\n"); | |
| 
 | |
| } /* end of printList */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Generates n names not currently in a symbol table. | |
|  | |
|   @details The pointer to the symbol table may be NULL, in which case | |
|   no test is made. The names generated by the procedure are | |
|   unique. So, if there is no possibility of conflict with pre-existing | |
|   names, NULL can be passed for the hash table. | |
|  | |
|   @return an array of names if succesful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see  | |
|  | |
| */ | |
| static char ** | |
| bnetGenerateNewNames( | |
|   st_table * hash /* table of existing names (or NULL) */, | |
|   int  n /* number of names to be generated */) | |
| { | |
|     char **list; | |
|     char name[256]; | |
|     int i; | |
| 
 | |
|     if (n < 1) return(NULL); | |
| 
 | |
|     list = ALLOC(char *,n); | |
|     if (list == NULL) return(NULL); | |
|     for (i = 0; i < n; i++) { | |
| 	do { | |
| 	    sprintf(name, "var%d", newNameNumber); | |
| 	    newNameNumber++; | |
| 	} while (hash != NULL && st_is_member(hash,name)); | |
| 	list[i] = util_strsav(name); | |
|     } | |
| 
 | |
|     return(list); | |
| 
 | |
| } /* bnetGenerateNewNames */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Writes blif for the reencoding logic. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static int | |
| bnetDumpReencodingLogic( | |
|   DdManager * dd /**< %DD manager */, | |
|   char * mname /**< model name */, | |
|   int  noutputs /**< number of outputs */, | |
|   DdNode ** outputs /**< array of network outputs */, | |
|   char ** inames /**< array of network input names */, | |
|   char ** altnames /**< array of names of reencoded inputs */, | |
|   char ** onames /**< array of network output names */, | |
|   FILE * fp /**< file pointer */) | |
| { | |
|     int i; | |
|     int retval; | |
|     int nvars = Cudd_ReadSize(dd); | |
|     int *support = NULL; | |
| 
 | |
|     support = bnetFindVectorSupport(dd,outputs,noutputs); | |
|     if (support == NULL) return(0); | |
| 
 | |
|     /* Write the header (.model .inputs .outputs). */ | |
|     retval = fprintf(fp,".model %s.global\n.inputs",mname); | |
|     if (retval == EOF) goto failure; | |
| 
 | |
|     for (i = 0; i < nvars; i++) { | |
| 	if ((i%8 == 0)&&i) { | |
| 	    retval = fprintf(fp," \\\n"); | |
| 	    if (retval == EOF) goto failure; | |
| 	} | |
| 	retval = fprintf(fp," %s", inames[i]); | |
| 	if (retval == EOF) goto failure; | |
|     } | |
| 
 | |
|     /* Write the .output line. */ | |
|     retval = fprintf(fp,"\n.outputs"); | |
|     if (retval == EOF) goto failure; | |
|     for (i = 0; i < noutputs; i++) { | |
| 	if ((i%8 == 0)&&i) { | |
| 	    retval = fprintf(fp," \\\n"); | |
| 	    if (retval == EOF) goto failure; | |
| 	} | |
| 	retval = fprintf(fp," %s", onames[i]); | |
| 	if (retval == EOF) goto failure; | |
|     } | |
|     retval = fprintf(fp,"\n"); | |
|     if (retval == EOF) goto failure; | |
| 
 | |
|     /* Instantiate main subcircuit. */ | |
|     retval = fprintf(fp,"\n.subckt %s", mname); | |
|     if (retval == EOF) goto failure; | |
|     for (i = 0; i < nvars; i++) { | |
| 	if ((i%8 == 0)&&i) { | |
| 	    retval = fprintf(fp," \\\n"); | |
| 	    if (retval == EOF) goto failure; | |
| 	} | |
| 	if (support[i] == 1) { | |
| 	    retval = fprintf(fp," %s=%s", inames[i], altnames[i]); | |
| 	    if (retval == EOF) goto failure; | |
| 	} | |
|     } | |
|     for (i = 0; i < noutputs; i++) { | |
| 	if ((i%8 == 0)&&i) { | |
| 	    retval = fprintf(fp," \\\n"); | |
| 	    if (retval == EOF) goto failure; | |
| 	} | |
| 	retval = fprintf(fp," %s=%s", onames[i], onames[i]); | |
| 	if (retval == EOF) goto failure; | |
|     } | |
|     retval = fprintf(fp,"\n"); | |
|     if (retval == EOF) goto failure; | |
| 
 | |
|     /* Instantiate reencoding subcircuit. */ | |
|     retval = fprintf(fp,"\n.subckt %s.reencode",mname); | |
|     if (retval == EOF) goto failure; | |
|     for (i = 0; i < nvars; i++) { | |
| 	if ((i%8 == 0)&&i) { | |
| 	    retval = fprintf(fp," \\\n"); | |
| 	    if (retval == EOF) goto failure; | |
| 	} | |
| 	retval = fprintf(fp," %s=%s", inames[i], inames[i]); | |
| 	if (retval == EOF) goto failure; | |
|     } | |
|     retval = fprintf(fp," \\\n"); | |
|     if (retval == EOF) goto failure; | |
|     for (i = 0; i < nvars; i++) { | |
| 	if ((i%8 == 0)&&i) { | |
| 	    retval = fprintf(fp," \\\n"); | |
| 	    if (retval == EOF) goto failure; | |
| 	} | |
| 	if (support[i] == 1) { | |
| 	    retval = fprintf(fp," %s=%s", altnames[i],altnames[i]); | |
| 	    if (retval == EOF) goto failure; | |
| 	} | |
|     } | |
|     retval = fprintf(fp,"\n"); | |
|     if (retval == EOF) goto failure; | |
| 
 | |
|     /* Write trailer. */ | |
|     retval = fprintf(fp,".end\n\n"); | |
|     if (retval == EOF) goto failure; | |
| 
 | |
|     /* Write reencoding subcircuit. */ | |
|     retval = bnetBlifWriteReencode(dd,mname,inames,altnames,support,fp); | |
|     if (retval == EOF) goto failure; | |
| 
 | |
|     FREE(support); | |
|     return(1); | |
| 
 | |
| failure: | |
|     if (support != NULL) FREE(support); | |
|     return(0); | |
| 
 | |
| } /* end of bnetDumpReencodingLogic */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Writes blif for the truth table of an n-input xnor. | |
|  | |
|   @return 1 if successful; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| #if 0 | |
| static int | |
| bnetBlifXnorTable( | |
|   FILE * fp /**< file pointer */, | |
|   int  n /**< number of inputs */) | |
| { | |
|     int power;	/* 2 to the power n */ | |
|     int i,j,k; | |
|     int nzeroes; | |
|     int retval; | |
|     char *line; | |
|  | |
|     line = ALLOC(char,n+1); | |
|     if (line == NULL) return(0); | |
|     line[n] = '\0'; | |
|  | |
|     for (i = 0, power = 1; i < n; i++) { | |
| 	power *= 2; | |
|     } | |
|  | |
|     for (i = 0; i < power; i++) { | |
| 	k = i; | |
| 	nzeroes = 0; | |
| 	for (j = 0; j < n; j++) { | |
| 	    if (k & 1) { | |
| 		line[j] = '1'; | |
| 	    } else { | |
| 		line[j] = '0'; | |
| 		nzeroes++; | |
| 	    } | |
| 	    k >>= 1; | |
| 	} | |
| 	if ((nzeroes & 1) == 0) { | |
| 	    retval = fprintf(fp,"%s 1\n",line); | |
| 	    if (retval == 0) return(0); | |
| 	} | |
|     } | |
|     return(1); | |
|  | |
| } /* end of bnetBlifXnorTable */ | |
| #endif | |
|  | |
| 
 | |
| /** | |
|   @brief Writes blif for the reencoding logic. | |
|  | |
|   @details Exclusive NORs with more than two inputs are decomposed | |
|   into cascaded two-input gates. | |
|  | |
|   @return 1 if successful; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static int | |
| bnetBlifWriteReencode( | |
|   DdManager * dd, | |
|   char * mname, | |
|   char ** inames, | |
|   char ** altnames, | |
|   int * support, | |
|   FILE * fp) | |
| { | |
|     int retval; | |
|     int nvars = Cudd_ReadSize(dd); | |
|     int i,j; | |
|     int ninp; | |
| 
 | |
|     /* Write the header (.model .inputs .outputs). */ | |
|     retval = fprintf(fp,".model %s.reencode\n.inputs",mname); | |
|     if (retval == EOF) return(0); | |
| 
 | |
|     for (i = 0; i < nvars; i++) { | |
| 	if ((i%8 == 0)&&i) { | |
| 	    retval = fprintf(fp," \\\n"); | |
| 	    if (retval == EOF) goto failure; | |
| 	} | |
| 	retval = fprintf(fp," %s", inames[i]); | |
| 	if (retval == EOF) goto failure; | |
|     } | |
| 
 | |
|     /* Write the .output line. */ | |
|     retval = fprintf(fp,"\n.outputs"); | |
|     if (retval == EOF) goto failure; | |
|     for (i = 0; i < nvars; i++) { | |
| 	if ((i%8 == 0)&&i) { | |
| 	    retval = fprintf(fp," \\\n"); | |
| 	    if (retval == EOF) goto failure; | |
| 	} | |
| 	if (support[i] == 1) { | |
| 	    retval = fprintf(fp," %s", altnames[i]); | |
| 	    if (retval == EOF) goto failure; | |
| 	} | |
|     } | |
|     retval = fprintf(fp,"\n"); | |
|     if (retval == EOF) goto failure; | |
| 
 | |
|     /* Instantiate exclusive nors. */ | |
|     for (i = 0; i < nvars; i++) { | |
| 	char *in1 = NULL; | |
| 	char *in2 = NULL; | |
| 	char **oname; | |
| 	if (support[i] == 0) continue; | |
| 	ninp = 0; | |
| 	for (j = 0; j < nvars; j++) { | |
| 	    if (Cudd_ReadLinear(dd,i,j)) { | |
| 		switch (ninp) { | |
| 		case 0: | |
| 		    in1 = inames[j]; | |
| 		    ninp++; | |
| 		    break; | |
| 		case 1: | |
| 		    in2 = inames[j]; | |
| 		    ninp++; | |
| 		    break; | |
| 		case 2: | |
| 		    oname = bnetGenerateNewNames(NULL,1); | |
| 		    retval = fprintf(fp,".names %s %s %s\n11 1\n00 1\n", | |
| 			     in1, in2, oname[0]); | |
| 		    if (retval == EOF) goto failure; | |
| 		    in1 = oname[0]; | |
| 		    in2 = inames[j]; | |
| 		    FREE(oname); | |
| 		    break; | |
| 		default: | |
| 		    goto failure; | |
| 		} | |
| 	    } | |
| 	} | |
| 	switch (ninp) { | |
| 	case 1: | |
| 	    retval = fprintf(fp,".names %s %s\n1 1\n", in1, altnames[i]); | |
| 	    if (retval == EOF) goto failure; | |
| 	    break; | |
| 	case 2: | |
| 	    retval = fprintf(fp,".names %s %s %s\n11 1\n00 1\n", | |
| 		     in1, in2, altnames[i]); | |
| 	    if (retval == EOF) goto failure; | |
| 	    break; | |
| 	default: | |
| 	    goto failure; | |
| 	} | |
|     } | |
| 
 | |
|     /* Write trailer. */ | |
|     retval = fprintf(fp,"\n.end\n\n"); | |
|     if (retval == EOF) goto failure; | |
| 
 | |
|     return(1); | |
| 
 | |
| failure: | |
|     return(0); | |
| 
 | |
| } /* end of bnetBlifWriteReencode */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Finds the support of a list of DDs. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static int * | |
| bnetFindVectorSupport( | |
|   DdManager * dd, | |
|   DdNode ** list, | |
|   int  n) | |
| { | |
|     DdNode	*support = NULL; | |
|     DdNode	*scan; | |
|     int		*array = NULL; | |
|     int		nvars = Cudd_ReadSize(dd); | |
|     int		i; | |
| 
 | |
|     /* Build an array with the support of the functions in list. */ | |
|     array = ALLOC(int,nvars); | |
|     if (array == NULL) return(NULL); | |
|     for (i = 0; i < nvars; i++) { | |
| 	array[i] = 0; | |
|     } | |
| 
 | |
|     /* Take the union of the supports of each output function. */ | |
|     for (i = 0; i < n; i++) { | |
| 	support = Cudd_Support(dd,list[i]); | |
| 	if (support == NULL) { | |
| 	    FREE(array); | |
| 	    return(NULL); | |
| 	} | |
| 	Cudd_Ref(support); | |
| 	scan = support; | |
| 	while (!Cudd_IsConstant(scan)) { | |
| 	    array[scan->index] = 1; | |
| 	    scan = Cudd_T(scan); | |
| 	} | |
| 	Cudd_IterDerefBdd(dd,support); | |
|     } | |
| 
 | |
|     return(array); | |
| 
 | |
| } /* end of bnetFindVectorSupport */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Builds %BDD for a XOR function. | |
|  | |
|   @details Checks whether a function is a XOR with 2 or 3 inputs. If so, | |
|   it builds the %BDD. | |
|  | |
|   @return 1 if the %BDD has been built; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static int | |
| buildExorBDD( | |
|   DdManager * dd, | |
|   BnetNode * nd, | |
|   st_table * hash, | |
|   int  params, | |
|   int  nodrop) | |
| { | |
|     int check[8]; | |
|     int i; | |
|     int nlines; | |
|     BnetTabline *line; | |
|     DdNode *func, *var, *tmp; | |
|     BnetNode *auxnd; | |
| 
 | |
|     if (nd->ninp < 2 || nd->ninp > 3) return(0); | |
| 
 | |
|     nlines = 1 << (nd->ninp - 1); | |
|     for (i = 0; i < 8; i++) check[i] = 0; | |
|     line = nd->f; | |
|     while (line != NULL) { | |
| 	int num = 0; | |
| 	int count = 0; | |
| 	nlines--; | |
| 	for (i = 0; i < nd->ninp; i++) { | |
| 	    num <<= 1; | |
| 	    if (line->values[i] == '-') { | |
| 		return(0); | |
| 	    } else if (line->values[i] == '1') { | |
| 		count++; | |
| 		num++; | |
| 	    } | |
| 	} | |
| 	if ((count & 1) == 0) return(0); | |
| 	if (check[num]) return(0); | |
| 	line = line->next; | |
|     } | |
|     if (nlines != 0) return(0); | |
| 
 | |
|     /* Initialize the exclusive sum to logical 0. */ | |
|     func = Cudd_ReadLogicZero(dd); | |
|     Cudd_Ref(func); | |
| 
 | |
|     /* Scan the inputs. */ | |
|     for (i = 0; i < nd->ninp; i++) { | |
| 	if (!st_lookup(hash, nd->inputs[i], (void **) &auxnd)) { | |
| 	    goto failure; | |
| 	} | |
| 	if (params == BNET_LOCAL_DD) { | |
| 	    if (auxnd->active == FALSE) { | |
| 		if (!Bnet_BuildNodeBDD(dd,auxnd,hash,params,nodrop)) { | |
| 		    goto failure; | |
| 		} | |
| 	    } | |
| 	    var = Cudd_ReadVars(dd,auxnd->var); | |
| 	    if (var == NULL) goto failure; | |
| 	    Cudd_Ref(var); | |
| 	} else { /* params == BNET_GLOBAL_DD */ | |
| 	    if (auxnd->dd == NULL) { | |
| 		if (!Bnet_BuildNodeBDD(dd,auxnd,hash,params,nodrop)) { | |
| 		    goto failure; | |
| 		} | |
| 	    } | |
| 	    var = auxnd->dd; | |
| 	} | |
| 	tmp = Cudd_bddXor(dd,func,var); | |
| 	if (tmp == NULL) goto failure; | |
| 	Cudd_Ref(tmp); | |
| 	Cudd_IterDerefBdd(dd,func); | |
| 	if (params == BNET_LOCAL_DD) { | |
| 	    Cudd_IterDerefBdd(dd,var); | |
| 	} | |
| 	func = tmp; | |
|     } | |
|     nd->dd = func; | |
| 
 | |
|     /* Associate a variable to this node if local BDDs are being | |
|     ** built. This is done at the end, so that the primary inputs tend | |
|     ** to get lower indices. | |
|     */ | |
|     if (params == BNET_LOCAL_DD && nd->active == FALSE) { | |
| 	DdNode *auxfunc = Cudd_bddNewVar(dd); | |
| 	if (auxfunc == NULL) goto failure; | |
| 	Cudd_Ref(auxfunc); | |
| 	nd->var = auxfunc->index; | |
| 	nd->active = TRUE; | |
| 	Cudd_IterDerefBdd(dd,auxfunc); | |
|     } | |
| 
 | |
|     return(1); | |
| failure: | |
|     return(0); | |
| 
 | |
| } /* end of buildExorBDD */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Builds %BDD for a multiplexer. | |
|  | |
|   @details Checks whether a function is a 2-to-1 multiplexer. If so, | |
|   it builds the %BDD. | |
|  | |
|   @return 1 if the %BDD has been built; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static int | |
| buildMuxBDD( | |
|   DdManager * dd, | |
|   BnetNode * nd, | |
|   st_table * hash, | |
|   int  params, | |
|   int  nodrop) | |
| { | |
|     BnetTabline *line; | |
|     char *values[2]; | |
|     int mux[2] = {0, 0}; | |
|     int phase[2] = {0, 0}; | |
|     int j; | |
|     int nlines = 0; | |
|     int controlC = -1; | |
|     int controlR = -1; | |
|     DdNode *func, *f, *g, *h; | |
|     BnetNode *auxnd; | |
| 
 | |
|     if (nd->ninp != 3 || nd->f == NULL) return(0); | |
| 
 | |
|     for (line = nd->f; line != NULL; line = line->next) { | |
| 	int dc = 0; | |
| 	if (nlines > 1) return(0); | |
| 	values[nlines] = line->values; | |
| 	for (j = 0; j < 3; j++) { | |
| 	    if (values[nlines][j] == '-') { | |
| 		if (dc) return(0); | |
| 		dc = 1; | |
| 	    } | |
| 	} | |
| 	if (!dc) return(0); | |
| 	nlines++; | |
|     } | |
|     if (nlines != 2) return(0); | |
|     /* At this point we know we have: | |
|     **   3 inputs | |
|     **   2 lines | |
|     **   1 dash in each line | |
|     ** If the two dashes are not in the same column, then there is | |
|     ** exaclty one column without dashes: the control column. | |
|     */ | |
|     for (j = 0; j < 3; j++) { | |
| 	if (values[0][j] == '-' && values[1][j] == '-') return(0); | |
| 	if (values[0][j] != '-' && values[1][j] != '-') { | |
| 	    if (values[0][j] == values[1][j]) return(0); | |
| 	    controlC = j; | |
| 	    controlR = values[0][j] == '0'; | |
| 	} | |
|     } | |
|     assert(controlC != -1 && controlR != -1); | |
|     /* At this point we know that there is indeed no column with two | |
|     ** dashes. The control column has been identified, and we know that | |
|     ** its two elelments are different. */ | |
|     for (j = 0; j < 3; j++) { | |
| 	if (j == controlC) continue; | |
| 	if (values[controlR][j] == '1') { | |
| 	    mux[0] = j; | |
| 	    phase[0] = 0; | |
| 	} else if (values[controlR][j] == '0') { | |
| 	    mux[0] = j; | |
| 	    phase[0] = 1; | |
| 	} else if (values[1-controlR][j] == '1') { | |
| 	    mux[1] = j; | |
| 	    phase[1] = 0; | |
| 	} else if (values[1-controlR][j] == '0') { | |
| 	    mux[1] = j; | |
| 	    phase[1] = 1; | |
| 	} | |
|     } | |
| 
 | |
|     /* Get the inputs. */ | |
|     if (!st_lookup(hash, nd->inputs[controlC], (void **) &auxnd)) { | |
| 	goto failure; | |
|     } | |
|     if (params == BNET_LOCAL_DD) { | |
| 	if (auxnd->active == FALSE) { | |
| 	    if (!Bnet_BuildNodeBDD(dd,auxnd,hash,params,nodrop)) { | |
| 		goto failure; | |
| 	    } | |
| 	} | |
| 	f = Cudd_ReadVars(dd,auxnd->var); | |
| 	if (f == NULL) goto failure; | |
| 	Cudd_Ref(f); | |
|     } else { /* params == BNET_GLOBAL_DD */ | |
| 	if (auxnd->dd == NULL) { | |
| 	    if (!Bnet_BuildNodeBDD(dd,auxnd,hash,params,nodrop)) { | |
| 		goto failure; | |
| 	    } | |
| 	} | |
| 	f = auxnd->dd; | |
|     } | |
|     if (!st_lookup(hash, nd->inputs[mux[0]], (void **) &auxnd)) { | |
| 	goto failure; | |
|     } | |
|     if (params == BNET_LOCAL_DD) { | |
| 	if (auxnd->active == FALSE) { | |
| 	    if (!Bnet_BuildNodeBDD(dd,auxnd,hash,params,nodrop)) { | |
| 		goto failure; | |
| 	    } | |
| 	} | |
| 	g = Cudd_ReadVars(dd,auxnd->var); | |
| 	if (g == NULL) goto failure; | |
| 	Cudd_Ref(g); | |
|     } else { /* params == BNET_GLOBAL_DD */ | |
| 	if (auxnd->dd == NULL) { | |
| 	    if (!Bnet_BuildNodeBDD(dd,auxnd,hash,params,nodrop)) { | |
| 		goto failure; | |
| 	    } | |
| 	} | |
| 	g = auxnd->dd; | |
|     } | |
|     g = Cudd_NotCond(g,phase[0]); | |
|     if (!st_lookup(hash, nd->inputs[mux[1]], (void **) &auxnd)) { | |
| 	goto failure; | |
|     } | |
|     if (params == BNET_LOCAL_DD) { | |
| 	if (auxnd->active == FALSE) { | |
| 	    if (!Bnet_BuildNodeBDD(dd,auxnd,hash,params,nodrop)) { | |
| 		goto failure; | |
| 	    } | |
| 	} | |
| 	h = Cudd_ReadVars(dd,auxnd->var); | |
| 	if (h == NULL) goto failure; | |
| 	Cudd_Ref(h); | |
|     } else { /* params == BNET_GLOBAL_DD */ | |
| 	if (auxnd->dd == NULL) { | |
| 	    if (!Bnet_BuildNodeBDD(dd,auxnd,hash,params,nodrop)) { | |
| 		goto failure; | |
| 	    } | |
| 	} | |
| 	h = auxnd->dd; | |
|     } | |
|     h = Cudd_NotCond(h,phase[1]); | |
|     func = Cudd_bddIte(dd,f,g,h); | |
|     if (func == NULL) goto failure; | |
|     Cudd_Ref(func); | |
|     if (params == BNET_LOCAL_DD) { | |
| 	Cudd_IterDerefBdd(dd,f); | |
| 	Cudd_IterDerefBdd(dd,g); | |
| 	Cudd_IterDerefBdd(dd,h); | |
|     } | |
|     nd->dd = func; | |
| 
 | |
|     /* Associate a variable to this node if local BDDs are being | |
|     ** built. This is done at the end, so that the primary inputs tend | |
|     ** to get lower indices. | |
|     */ | |
|     if (params == BNET_LOCAL_DD && nd->active == FALSE) { | |
| 	DdNode *auxfunc = Cudd_bddNewVar(dd); | |
| 	if (auxfunc == NULL) goto failure; | |
| 	Cudd_Ref(auxfunc); | |
| 	nd->var = auxfunc->index; | |
| 	nd->active = TRUE; | |
| 	Cudd_IterDerefBdd(dd,auxfunc); | |
|     } | |
| 
 | |
|     return(1); | |
| failure: | |
|     return(0); | |
| 
 | |
| } /* end of buildExorBDD */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Sets the level of each node. | |
|  | |
|   @return 1 if successful; 0 otherwise. | |
|  | |
|   @sideeffect Changes the level and visited fields of the nodes it | |
|   visits. | |
|  | |
|   @see bnetLevelDFS | |
|  | |
| */ | |
| static int | |
| bnetSetLevel( | |
|   BnetNetwork * net) | |
| { | |
|     BnetNode *node; | |
| 
 | |
|     /* Recursively visit nodes. This is pretty inefficient, because we | |
|     ** visit all nodes in this loop, and most of them in the recursive | |
|     ** calls to bnetLevelDFS. However, this approach guarantees that | |
|     ** all nodes will be reached ven if there are dangling outputs. */ | |
|     node = net->nodes; | |
|     while (node != NULL) { | |
| 	if (!bnetLevelDFS(net,node)) return(0); | |
| 	node = node->next; | |
|     } | |
| 
 | |
|     /* Clear visited flags. */ | |
|     node = net->nodes; | |
|     while (node != NULL) { | |
| 	node->visited = 0; | |
| 	node = node->next; | |
|     } | |
|     return(1); | |
| 
 | |
| } /* end of bnetSetLevel */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Does a DFS from a node setting the level field. | |
|  | |
|   @return 1 if successful; 0 otherwise. | |
|  | |
|   @sideeffect Changes the level and visited fields of the nodes it | |
|   visits. | |
|  | |
|   @see bnetSetLevel | |
|  | |
| */ | |
| static int | |
| bnetLevelDFS( | |
|   BnetNetwork * net, | |
|   BnetNode * node) | |
| { | |
|     int i; | |
|     BnetNode *auxnd; | |
| 
 | |
|     if (node->visited == 1) { | |
| 	return(1); | |
|     } | |
| 
 | |
|     node->visited = 1; | |
| 
 | |
|     /* Graphical sources have level 0.  This is the final value if the | |
|     ** node has no fan-ins. Otherwise the successive loop will | |
|     ** increase the level. */ | |
|     node->level = 0; | |
|     for (i = 0; i < node->ninp; i++) { | |
| 	if (!st_lookup(net->hash, node->inputs[i], (void **) &auxnd)) { | |
| 	    return(0); | |
| 	} | |
| 	if (!bnetLevelDFS(net,auxnd)) { | |
| 	    return(0); | |
| 	} | |
| 	if (auxnd->level >= node->level) node->level = 1 + auxnd->level; | |
|     } | |
|     return(1); | |
| 
 | |
| } /* end of bnetLevelDFS */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Orders network roots for variable ordering. | |
|  | |
|   @return an array with the ordered outputs and next state variables | |
|   if successful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static BnetNode ** | |
| bnetOrderRoots( | |
|   BnetNetwork * net, | |
|   int * nroots) | |
| { | |
|     int i, noutputs; | |
|     BnetNode *node; | |
|     BnetNode **nodes = NULL; | |
| 
 | |
|     /* Initialize data structures. */ | |
|     noutputs = net->noutputs; | |
|     nodes = ALLOC(BnetNode *, noutputs); | |
|     if (nodes == NULL) goto endgame; | |
| 
 | |
|     /* Find output names and levels. */ | |
|     for (i = 0; i < net->noutputs; i++) { | |
| 	if (!st_lookup(net->hash,net->outputs[i],(void **)&node)) { | |
| 	    goto endgame; | |
| 	} | |
| 	nodes[i] = node; | |
|     } | |
| 
 | |
|     util_qsort(nodes, noutputs, sizeof(BnetNode *), | |
|                (DD_QSFP)bnetLevelCompare); | |
|     *nroots = noutputs; | |
|     return(nodes); | |
| 
 | |
| endgame: | |
|     if (nodes != NULL) FREE(nodes); | |
|     return(NULL); | |
| 
 | |
| } /* end of bnetOrderRoots */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Comparison function used by qsort. | |
|  | |
|   @details Used to order the variables according to the number of keys | |
|   in the subtables. | |
|  | |
|   @return the difference in number of keys between the two variables | |
|   being compared. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static int | |
| bnetLevelCompare( | |
|   BnetNode ** x, | |
|   BnetNode ** y) | |
| { | |
|     return((*y)->level - (*x)->level); | |
| 
 | |
| } /* end of bnetLevelCompare */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Does a DFS from a node ordering the inputs. | |
|  | |
|   @return 1 if successful; 0 otherwise. | |
|  | |
|   @sideeffect Changes visited fields of the nodes it visits. | |
|  | |
|   @see Bnet_DfsVariableOrder | |
|  | |
| */ | |
| static int | |
| bnetDfsOrder( | |
|   DdManager * dd, | |
|   BnetNetwork * net, | |
|   BnetNode * node) | |
| { | |
|     int i; | |
|     BnetNode *auxnd; | |
|     BnetNode **fanins; | |
| 
 | |
|     if (node->visited == 1) { | |
| 	return(1); | |
|     } | |
| 
 | |
|     node->visited = 1; | |
|     if (node->type == BNET_INPUT_NODE || | |
| 	node->type == BNET_PRESENT_STATE_NODE) { | |
| 	node->dd = Cudd_bddNewVar(dd); | |
| 	if (node->dd == NULL) return(0); | |
| 	Cudd_Ref(node->dd); | |
| 	node->active = TRUE; | |
| 	node->var = node->dd->index; | |
| 	return(1); | |
|     } | |
| 
 | |
|     fanins = ALLOC(BnetNode *, node->ninp); | |
|     if (fanins == NULL) return(0); | |
| 
 | |
|     for (i = 0; i < node->ninp; i++) { | |
| 	if (!st_lookup(net->hash, node->inputs[i], (void **) &auxnd)) { | |
| 	    FREE(fanins); | |
| 	    return(0); | |
| 	} | |
| 	fanins[i] = auxnd; | |
|     } | |
| 
 | |
|     util_qsort(fanins, node->ninp, sizeof(BnetNode *), | |
|                (DD_QSFP)bnetLevelCompare); | |
|     for (i = 0; i < node->ninp; i++) { | |
| 	/* for (i = node->ninp - 1; i >= 0; i--) { */ | |
| 	int res = bnetDfsOrder(dd,net,fanins[i]); | |
| 	if (res == 0) { | |
| 	    FREE(fanins); | |
| 	    return(0); | |
| 	} | |
|     } | |
|     FREE(fanins); | |
|     return(1); | |
| 
 | |
| } /* end of bnetLevelDFS */
 |