/********************************************************************* NAME prime_implicants_by_recursive_partitioning PURPOSE Allocate some working vectors and call the recursive prime implicants function to generate the prime implicants of a boolean function. In fact this function is only an entry for the recursive process performed by the recursive function that will be called. SYNOPSIS struct node *prime_implicants_by_recursive_partitioning(list) struct node *list; DESCRIPTION We allocate a vector that contains all the input variables. Each time we partition the function along one input variable, we place this variable at the end of the vector and decrement the number of input of the partitioned function such that this input variable is no longer available to the partitioned function. So this function initializes the recursive process by allocating a vector containing all the input variables before calling the recursive function to generate the prime implicants. COORDINATES McGill University Electrical Engineering VLSI lab MONTREAL CANADA 14 juillet 1984 AUTHOR Michel DAGENAIS ***********************************************************************/ #include "cubes.h" #include struct node *common_list, /* list when converting a tree in a list */ *dont_care_list, /* list to put the dont care cover */ **point_to_merge, /* pointer to node to merge */ *node_to_merge; /* node that we currently try to merge */ struct binary *common_binary; /* branch to merge with */ int *nb0_at_input, /* number of cubes with 0 for each input */ *nb1_at_input, /* number of cubes with 1 for each input */ *nbx_at_input, /* number of cubes with x for each input */ *unused_input, /* input var not taken by partitions */ nbinput; /* number of input var not taken for partition*/ struct node *prime_implicants_by_recursive_partitioning(list) struct node *list; { struct branch branch; /* branch sent to the recursive function */ struct node *temp_node, /* node in the list */ **prev_list; /* pointer to node in the list */ /* We first screen the list to see if any node has an output part composed exclusively of 0; in such a case, this node is removed from the list. */ prev_list = &list; for(; (temp_node = *prev_list) != NULL ;) { if(empty_output(temp_node->cube)) { *prev_list = temp_node->next_node; free_node(temp_node); } else prev_list = &(temp_node->next_node); } /* we first allocate integer vectors to contain the list of input var, and for each passible code (0, 1 or x) a working vector used in the input var selection for partitioning. */ unused_input = (int *)calloc((unsigned)input_number,sizeof(int)); nb0_at_input = (int *)calloc((unsigned)input_number,sizeof(int)); nb1_at_input = (int *)calloc((unsigned)input_number,sizeof(int)); nbx_at_input = (int *)calloc((unsigned)input_number,sizeof(int)); if(unused_input == NULL || nb0_at_input == NULL || nb1_at_input == NULL || nbx_at_input == NULL) fatal_system_error("unable to alloc vector for inputs"); /* we initialize unused_input with the list of input var which is 0 to (input_number - 1) when we start here with the unpartitioned function. nbinput is initialized to input_number the number of input variables available in the vector unused input before we start the partitioning. */ for(nbinput = 0 ; nbinput < input_number ; nbinput++) { unused_input[nbinput] = nbinput; } dont_care_list = NULL; /* we now call the recursive function that will return the list of prime implicants of the function described by the list of disjoint cubes sent */ branch.status = LEAF; branch._.leaf = list; recursive_prime_implicants(&branch,1); /* the nodes in the dont care cover were kept in a different list, we will return them in the same list as the prime implicants. */ list = merge_node_lists(dont_care_list,branch._.leaf); /* we now simply free the vectors allocated, the job is done */ flush_binary(); free((char *)unused_input); free((char *)nb0_at_input); free((char *)nb1_at_input); free((char *)nbx_at_input); return(list); } /********************************************************************** NAME recursive_prime_implicants PURPOSE Generate the list of prime implicants of a boolean function from a list of disjoint cubes. These prime implicants will be linked together in a covering graph. SYNOPSIS recursive_prime_implicants(branch,return_a_list) struct branch *branch; int return_a_list; DESCRIPTION This function generates the prime implicants using a recursive partitioning up to unate functions. The partitions are then merged together and the new implicants formed are checked for absorbtion. Each partition is performed on an input variable, all the cubes with a 0 go on one side, all the cubes with a 1 on the other and the cubes with a x go on both sides. We then make both sides prime and we finally merge both together along the partitioning variable. The parameter return_a_list tells that the 0 side of the tree should be returned in a list instead of a subtree. COORDINATES McGill University Electrical Engineering VLSI lab MONTREAL CANADA 13 july 1984 AUTHOR Michel DAGENAIS ************************************************************************/ recursive_prime_implicants(branch,return_a_list) struct branch *branch; int return_a_list; { int current_code, /* code for the node at current_input */ current_input; /* input variable selected */ struct node *temp_node, /* temp node to place in the list 1 or 0 */ *start_list1, /* list of nodes with 1 in current input */ *start_list0, /* list of nodes with a 0 in current input */ *next_node, /* next node in the list to partition */ *list; /* list of nodes received */ struct binary *temp_binary; /* new node for this partition */ /* we select the input for which we will partition. A negative value tells that only one cube is left in the list and that no partitioning is needed any more; in this case the only cube in this partition is necessarily prime and the function terminates here. */ current_input = select_input(&(branch->_.leaf)); if(current_input < 0) return; list = branch->_.leaf; /* we need to partition; the input is already selected, all the cubes with a 0 for this input will be put in start_list0, all the cubes with a 1 in start_list1. The cubes with a x for this input will be splitted in two cube, one with a 0 to put in start_list 0 and one with a 1 to put in start_list1. */ define_current_var(current_input); start_list0 = NULL; start_list1 = NULL; for(temp_node = list ; temp_node != NULL ; temp_node = next_node) { next_node = temp_node->next_node; current_code = extract_current_var(temp_node->cube); switch(current_code) { case _1_ : /* we have a 1 for the selected input place the cube in start_list1 */ temp_node->next_node = start_list1; start_list1 = temp_node; break; case _0_ : /* we have a 0 for the selected input place the cube in start_list0 */ temp_node->next_node = start_list0; start_list0 = temp_node; break; case _X_ : /* we have a x split the cube and place it in both lists */ temp_node->next_node = start_list0; start_list0 = temp_node; temp_node = copy_and_alloc_node(temp_node->cube); temp_node->next_node = start_list1; start_list1 = temp_node; /* place respectively 0 and 1 in the splitted cubes for current_input*/ set_current_var(start_list0->cube,mask10); set_current_var(start_list1->cube,mask01); break; } } /* for each partition we generate the prime implicants by calling the recursive function. */ temp_binary = alloc_binary(); temp_binary->var = current_input; temp_binary->is0.status = LEAF; temp_binary->is0._.leaf = start_list0; temp_binary->is1.status = LEAF; temp_binary->is1._.leaf = start_list1; if(return_a_list) recursive_prime_implicants(&(temp_binary->is0),1); else recursive_prime_implicants(&(temp_binary->is0),0); recursive_prime_implicants(&(temp_binary->is1),0); nbinput++; define_current_var(current_input); /* we have two lists of cubes prime in their respective partition, when we will put the list together they will still be prime except maybe for the input var along which they were partition. For this reason, we will try the star product between each cube of a partition with each cube of the other partition. The new cubes formed may absorb the cubes used for their formation and the new cubes must also be checked for absorbtion with the other new cubes. */ common_binary = temp_binary; scan_node_to_merge(&(temp_binary->is0)); /* if we have to return a list, we simply merge the subtrees in one list */ if(return_a_list) { common_list = merge_node_lists(temp_binary->isx,temp_binary->is0._.leaf); put_in_common_list(&(temp_binary->is1)); free_binary(temp_binary); branch->_.leaf = common_list; if(VERBOSIS) { sprintf(error_buffer,"leftmost subtree merged at level %d (%d)", input_number - nbinput,nb_alloc_nodes - 1); send_user_message(error_buffer); } return; } /* both subtrees are empty, we can then simply return a list and release the binary structure that holds the subtrees. */ if(temp_binary->is0.status == 0 && temp_binary->is1.status == 0) { branch->status = LEAF; branch->_.leaf = temp_binary->isx; free_binary(temp_binary); } else { branch->status = SUBTREE; branch->_.subtree = temp_binary; } if(VERY_VERBOSIS) { sprintf(error_buffer,"subtree merged at level %d (%d)", input_number - nbinput,nb_alloc_nodes - 1); send_user_message(error_buffer); } } /********************************************************************** NAME scan_node_to_merge PURPOSE Find all the nodes in a branch and try to merge them with the rest of the nodes that are in the other side of the common branch. SYNOPSIS scan_node_to_merge(branch) struct branch *branch; DESCRIPTION When we start the merging at a binary node, we scan to merge all the nodes in the 0 branch and we try to merge them with the nodes in the 1 branch. The 1 branch for this purpose can be accessed from the common binary. When the branch points to a leaf, we take all the nodes in the leaf; when it points to a subtree, we take all the nodes in the isx list and scan recursively the 0 and 1 branches. COORDINATES McGill University Electrical Engineering VLSI lab MONTREAL CANADA 8 august 1984 AUTHOR Michel DAGENAIS ************************************************************************/ scan_node_to_merge(branch) struct branch *branch; { struct binary *temp_binary; /* temp pointer to a subtree */ int status; status = branch->status; if(status == 0) return; if(status & SUBTREE) { temp_binary = branch->_.subtree; scan_node_to_merge(&(temp_binary->is0)); scan_node_to_merge(&(temp_binary->is1)); point_to_merge = &(temp_binary->isx); } else point_to_merge = &(branch->_.leaf); /* all the nodes in the list to merge will be star produc with all the other nodes in the 1 branch of the common_binary. When no merge covers the node to merge we take the next node in the list. If the node is covered, the point_to_merge is already updated to point to the next node */ for(; (node_to_merge = *point_to_merge) != NULL ;) { if(merge_with_rest(&(common_binary->is1)) == 0) point_to_merge = &(node_to_merge->next_node); } /* We look if this branch of the tree is not empty in which case we cut it to help restrict the search further on. */ if(status & SUBTREE) { if(temp_binary->is0.status == 0 && temp_binary->is1.status == 0) { if(temp_binary->isx == NULL) branch->status = 0; else { branch->status = LEAF; branch->_.leaf = temp_binary->isx; } free_binary(temp_binary); } } else if(branch->_.leaf == NULL) branch->status = 0; } /*********************************************************************** NAME merge_with_rest PURPOSE We scan the specified branch to find some nodes that might merge with the node_to_merge. The candidates are merged and placed in the graph. SYNOPSIS int merge_with_rest(branch) struct branch *branch; DESCRIPTION If the branch points to a leaf, we try to merge the nodes in it with the current node to merge. If it points to a subtree, we try to merge the nodes in the isx list and recursively merge with the 0 and 1 branches. When a merge is successfull, the new node is placed in the graph and we check that it is not absorbed by the nodes already in the common_binary in the isx list where the new nodes merged are put. When the node to merge is absorbed, we remove it from the list to merge, update the pointer to merge and return 1; when the node to merge is absorbed we know that no other merge will give a bigger cube and it is why we stop the merging for that node. Otherwise, we return 0. COORDINATES McGill University Electrical Engineering VLSI lab MONTREAL CANADA 8 august 1984 AUTHOR Michel DAGENAIS ************************************************************************/ int merge_with_rest(branch) struct branch *branch; { struct node *temp_node, /* temp pointer in the list to merge */ **point_temp_node, /* pointer to temp_node */ *temp_nodex, /* temp pointer in the list to absorb */ **point_to_nodex, /* pointer to temp_nodex */ *new_node; /* pointer to the result of the merge and link*/ struct binary *temp_binary; /* pointer to the subtree of branch */ int status, /* temp storage for the branch status */ code; /* value of the var for the node to merge */ status = branch->status; if(status == 0) return(0); if(status & SUBTREE) { temp_binary = branch->_.subtree; point_temp_node = &(temp_binary->isx); } else point_temp_node = &(branch->_.leaf); /* we will try to merge the node with all the nodes in the list */ for(; (temp_node = *point_temp_node) != NULL ;) { #ifdef DEBUG foutput_cube(stdout,node_to_merge->cube); foutput_cube(stdout,temp_node->cube); printf("Above two cubes to merge"); #endif if(star_product(node_to_merge->cube,temp_node->cube,spare_node->cube)) { new_node = merge_and_link(point_to_merge,point_temp_node,spare_node); #ifdef DEBUG printf(", merge successfull gives:\n"); foutput_cube(stdout,new_node->cube); #endif /* a merge occured, we will see if the new node is absorbed by nodes already merged and put in the listx of the common binary. */ point_to_nodex = &(common_binary->isx); for(; (temp_nodex = *point_to_nodex) != NULL ;) { code = absorb(new_node->cube,temp_nodex->cube); #ifdef DEBUG printf("Absorbtion code: %d \n",code); #endif switch(code) { case 0 : absorb_and_unlink(temp_nodex,&new_node); break; case 1 : absorb_and_unlink(new_node,point_to_nodex); break; case 2 : absorb_and_unlink(temp_nodex,&new_node); break; } if(new_node == NULL) break; /* if the node in the listx was not absorbed, we update the point_to_nodex */ if(temp_nodex == *point_to_nodex) point_to_nodex = &(temp_nodex->next_node); } /* the new node was not absorbed, it will be put in the listx. */ if(new_node != NULL) { new_node->next_node = common_binary->isx; common_binary->isx = new_node; } /* the node to merge was absorbed, it is the best possible merge and we stop */ if(*point_to_merge != node_to_merge) return(1); } if(temp_node == *point_temp_node)point_temp_node = &(temp_node->next_node); } /* We will now merge recursively with the subtrees of this branch. If the node to merge has a 0 for the variable of this binary it can only merge with the subtree 0 and the same if it has 1 with the subtree 1; this way important savings on useless attempt to star product, are achieved. */ if(status & SUBTREE) { code = extract_var(node_to_merge->cube,temp_binary->var); switch (code) { case _0_ : if(merge_with_rest(&(temp_binary->is0))) return(1); break; case _1_ : if(merge_with_rest(&(temp_binary->is1))) return(1); break; case _X_ : if(merge_with_rest(&(temp_binary->is0))) return(1); if(merge_with_rest(&(temp_binary->is1))) return(1); break; } /* all the merging are done, we will now look if this branch is not empty in which case we will cut it from the tree. */ if(temp_binary->is0.status == 0 && temp_binary->is1.status == 0) { if(temp_binary->isx == NULL) branch->status = 0; else { branch->status = LEAF; branch->_.leaf = temp_binary->isx; } free_binary(temp_binary); } } else if(branch->_.leaf == NULL) branch->status = 0; return(0); }