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 */
|