mirror of https://github.com/YosysHQ/abc.git
630 lines
17 KiB
C
630 lines
17 KiB
C
/*
|
||
* Revision Control Information
|
||
*
|
||
* $Source$
|
||
* $Author$
|
||
* $Revision$
|
||
* $Date$
|
||
*
|
||
*/
|
||
#include "espresso.h"
|
||
|
||
ABC_NAMESPACE_IMPL_START
|
||
|
||
|
||
/*
|
||
* Phase assignment technique (T. Sasao):
|
||
*
|
||
* 1. create a function with 2*m outputs which implements the
|
||
* original function and its complement for each output
|
||
*
|
||
* 2. minimize this function
|
||
*
|
||
* 3. choose the minimum number of prime implicants from the
|
||
* result of step 2 which are needed to realize either a function
|
||
* or its complement for each output
|
||
*
|
||
* Step 3 is performed in a rather crude way -- by simply multiplying
|
||
* out a large expression of the form:
|
||
*
|
||
* I = (ab + cdef)(acd + bgh) ...
|
||
*
|
||
* which is a product of m expressions where each expression has two
|
||
* product terms -- one representing which primes are needed for the
|
||
* function, and one representing which primes are needed for the
|
||
* complement. The largest product term resulting shows which primes
|
||
* to keep to implement one function or the other for each output.
|
||
* For problems with many outputs, this may grind to a
|
||
* halt.
|
||
*
|
||
* Untried: form complement of I and use unate_complement ...
|
||
*
|
||
* I have unsuccessfully tried several modifications to the basic
|
||
* algorithm. The first is quite simple: use Sasao's technique, but
|
||
* only commit to a single output at a time (rather than all
|
||
* outputs). The goal would be that the later minimizations can "take
|
||
* into account" the partial assignment at each step. This is
|
||
* expensive (m+1 minimizations rather than 2), and the results are
|
||
* discouraging.
|
||
*
|
||
* The second modification is rather complicated. The result from the
|
||
* minimization in step 2 is guaranteed to be minimal. Hence, for
|
||
* each output, the set of primes with a 1 in that output are both
|
||
* necessary and sufficient to implement the function. Espresso
|
||
* achieves the minimality using the routine MAKE_SPARSE. The
|
||
* modification is to prevent MAKE_SPARSE from running. Hence, there
|
||
* are potentially many subsets of the set of primes with a 1 in a
|
||
* column which can be used to implement that output. We use
|
||
* IRREDUNDANT to enumerate all possible subsets and then proceed as
|
||
* before.
|
||
*/
|
||
|
||
static int opo_no_make_sparse;
|
||
static int opo_repeated;
|
||
static int opo_exact;
|
||
static void minimize();
|
||
|
||
void phase_assignment(PLA, opo_strategy)
|
||
pPLA PLA;
|
||
int opo_strategy;
|
||
{
|
||
opo_no_make_sparse = opo_strategy % 2;
|
||
skip_make_sparse = opo_no_make_sparse;
|
||
opo_repeated = (opo_strategy / 2) % 2;
|
||
opo_exact = (opo_strategy / 4) % 2;
|
||
|
||
/* Determine a phase assignment */
|
||
if (PLA->phase != NULL) {
|
||
FREE(PLA->phase);
|
||
}
|
||
|
||
if (opo_repeated) {
|
||
PLA->phase = set_save(cube.fullset);
|
||
repeated_phase_assignment(PLA);
|
||
} else {
|
||
PLA->phase = find_phase(PLA, 0, (pcube) NULL);
|
||
}
|
||
|
||
/* Now minimize with this assignment */
|
||
skip_make_sparse = FALSE;
|
||
(void) set_phase(PLA);
|
||
minimize(PLA);
|
||
}
|
||
|
||
/*
|
||
* repeated_phase_assignment -- an alternate strategy which commits
|
||
* to a single phase assignment a step at a time. Performs m + 1
|
||
* minimizations !
|
||
*/
|
||
void repeated_phase_assignment(PLA)
|
||
pPLA PLA;
|
||
{
|
||
int i;
|
||
pcube phase;
|
||
|
||
for(i = 0; i < cube.part_size[cube.output]; i++) {
|
||
|
||
/* Find best assignment for all undecided outputs */
|
||
phase = find_phase(PLA, i, PLA->phase);
|
||
|
||
/* Commit for only a single output ... */
|
||
if (! is_in_set(phase, cube.first_part[cube.output] + i)) {
|
||
set_remove(PLA->phase, cube.first_part[cube.output] + i);
|
||
}
|
||
|
||
if (trace || summary) {
|
||
printf("\nOPO loop for output #%d\n", i);
|
||
printf("PLA->phase is %s\n", pc1(PLA->phase));
|
||
printf("phase is %s\n", pc1(phase));
|
||
}
|
||
set_free(phase);
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* find_phase -- find a phase assignment for the PLA for all outputs starting
|
||
* with output number first_output.
|
||
*/
|
||
pcube find_phase(PLA, first_output, phase1)
|
||
pPLA PLA;
|
||
int first_output;
|
||
pcube phase1;
|
||
{
|
||
pcube phase;
|
||
pPLA PLA1;
|
||
|
||
phase = set_save(cube.fullset);
|
||
|
||
/* setup the double-phase characteristic function, resize the cube */
|
||
PLA1 = new_PLA();
|
||
PLA1->F = sf_save(PLA->F);
|
||
PLA1->R = sf_save(PLA->R);
|
||
PLA1->D = sf_save(PLA->D);
|
||
if (phase1 != NULL) {
|
||
PLA1->phase = set_save(phase1);
|
||
(void) set_phase(PLA1);
|
||
}
|
||
EXEC_S(output_phase_setup(PLA1, first_output), "OPO-SETUP ", PLA1->F);
|
||
|
||
/* minimize the double-phase function */
|
||
minimize(PLA1);
|
||
|
||
/* set the proper phases according to what gives a minimum solution */
|
||
EXEC_S(PLA1->F = opo(phase, PLA1->F, PLA1->D, PLA1->R, first_output),
|
||
"OPO ", PLA1->F);
|
||
free_PLA(PLA1);
|
||
|
||
/* set the cube structure to reflect the old size */
|
||
setdown_cube();
|
||
cube.part_size[cube.output] -=
|
||
(cube.part_size[cube.output] - first_output) / 2;
|
||
cube_setup();
|
||
|
||
return phase;
|
||
}
|
||
|
||
/*
|
||
* opo -- multiply the expression out to determine a minimum subset of
|
||
* primes.
|
||
*/
|
||
|
||
/*ARGSUSED*/
|
||
pcover opo(phase, T, D, R, first_output)
|
||
pcube phase;
|
||
pcover T, D, R;
|
||
int first_output;
|
||
{
|
||
int offset, output, i, last_output, ind;
|
||
pset pdest, select, p, p1, last, last1, not_covered, tmp;
|
||
pset_family temp, T1, T2;
|
||
|
||
/* must select all primes for outputs [0 .. first_output-1] */
|
||
select = set_full(T->count);
|
||
for(output = 0; output < first_output; output++) {
|
||
ind = cube.first_part[cube.output] + output;
|
||
foreachi_set(T, i, p) {
|
||
if (is_in_set(p, ind)) {
|
||
set_remove(select, i);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Recursively perform the intersections */
|
||
offset = (cube.part_size[cube.output] - first_output) / 2;
|
||
last_output = first_output + offset - 1;
|
||
temp = opo_recur(T, D, select, offset, first_output, last_output);
|
||
|
||
/* largest set is on top -- select primes which are inferred from it */
|
||
pdest = temp->data;
|
||
T1 = new_cover(T->count);
|
||
foreachi_set(T, i, p) {
|
||
if (! is_in_set(pdest, i)) {
|
||
T1 = sf_addset(T1, p);
|
||
}
|
||
}
|
||
|
||
set_free(select);
|
||
sf_free(temp);
|
||
|
||
/* finding phases is difficult -- see which functions are not covered */
|
||
T2 = complement(cube1list(T1));
|
||
not_covered = new_cube();
|
||
tmp = new_cube();
|
||
foreach_set(T, last, p) {
|
||
foreach_set(T2, last1, p1) {
|
||
if (cdist0(p, p1)) {
|
||
(void) set_or(not_covered, not_covered, set_and(tmp, p, p1));
|
||
}
|
||
}
|
||
}
|
||
free_cover(T);
|
||
free_cover(T2);
|
||
set_free(tmp);
|
||
|
||
/* Now reflect the phase choice in a single cube */
|
||
for(output = first_output; output <= last_output; output++) {
|
||
ind = cube.first_part[cube.output] + output;
|
||
if (is_in_set(not_covered, ind)) {
|
||
if (is_in_set(not_covered, ind + offset)) {
|
||
fatal("error in output phase assignment");
|
||
} else {
|
||
set_remove(phase, ind);
|
||
}
|
||
}
|
||
}
|
||
set_free(not_covered);
|
||
return T1;
|
||
}
|
||
|
||
pset_family opo_recur(T, D, select, offset, first, last)
|
||
pcover T, D;
|
||
pcube select;
|
||
int offset, first, last;
|
||
{
|
||
static int level = 0;
|
||
int middle;
|
||
pset_family sl, sr, temp;
|
||
|
||
level++;
|
||
if (first == last) {
|
||
#if 0
|
||
if (opo_no_make_sparse) {
|
||
temp = form_cover_table(T, D, select, first, first + offset);
|
||
} else {
|
||
temp = opo_leaf(T, select, first, first + offset);
|
||
}
|
||
#else
|
||
temp = opo_leaf(T, select, first, first + offset);
|
||
#endif
|
||
} else {
|
||
middle = (first + last) / 2;
|
||
sl = opo_recur(T, D, select, offset, first, middle);
|
||
sr = opo_recur(T, D, select, offset, middle+1, last);
|
||
temp = unate_intersect(sl, sr, level == 1);
|
||
if (trace) {
|
||
printf("# OPO[%d]: %4d = %4d x %4d, time = %s\n", level - 1,
|
||
temp->count, sl->count, sr->count, print_time(ptime()));
|
||
(void) fflush(stdout);
|
||
}
|
||
free_cover(sl);
|
||
free_cover(sr);
|
||
}
|
||
level--;
|
||
return temp;
|
||
}
|
||
|
||
|
||
pset_family opo_leaf(T, select, out1, out2)
|
||
register pcover T;
|
||
pset select;
|
||
int out1, out2;
|
||
{
|
||
register pset_family temp;
|
||
register pset p, pdest;
|
||
register int i;
|
||
|
||
out1 += cube.first_part[cube.output];
|
||
out2 += cube.first_part[cube.output];
|
||
|
||
/* Allocate space for the result */
|
||
temp = sf_new(2, T->count);
|
||
|
||
/* Find which primes are needed for the ON-set of this fct */
|
||
pdest = GETSET(temp, temp->count++);
|
||
(void) set_copy(pdest, select);
|
||
foreachi_set(T, i, p) {
|
||
if (is_in_set(p, out1)) {
|
||
set_remove(pdest, i);
|
||
}
|
||
}
|
||
|
||
/* Find which primes are needed for the OFF-set of this fct */
|
||
pdest = GETSET(temp, temp->count++);
|
||
(void) set_copy(pdest, select);
|
||
foreachi_set(T, i, p) {
|
||
if (is_in_set(p, out2)) {
|
||
set_remove(pdest, i);
|
||
}
|
||
}
|
||
|
||
return temp;
|
||
}
|
||
|
||
#if 0
|
||
pset_family form_cover_table(F, D, select, f, fbar)
|
||
pcover F, D;
|
||
pset select;
|
||
int f, fbar; /* indices of f and fbar in the output part */
|
||
{
|
||
register int i;
|
||
register pcube p;
|
||
pset_family f_table, fbar_table;
|
||
|
||
/* setup required for fcube_is_covered */
|
||
Rp_size = F->count;
|
||
Rp_start = set_new(Rp_size);
|
||
foreachi_set(F, i, p) {
|
||
PUTSIZE(p, i);
|
||
}
|
||
foreachi_set(D, i, p) {
|
||
RESET(p, REDUND);
|
||
}
|
||
|
||
f_table = find_covers(F, D, select, f);
|
||
fbar_table = find_covers(F, D, select, fbar);
|
||
f_table = sf_append(f_table, fbar_table);
|
||
|
||
set_free(Rp_start);
|
||
return f_table;
|
||
}
|
||
|
||
|
||
pset_family find_covers(F, D, select, n)
|
||
pcover F, D;
|
||
register pset select;
|
||
int n;
|
||
{
|
||
register pset p, last, new;
|
||
pcover F1;
|
||
pcube *Flist;
|
||
pset_family f_table, table;
|
||
int i;
|
||
|
||
n += cube.first_part[cube.output];
|
||
|
||
/* save cubes in this output, and remove the output variable */
|
||
F1 = new_cover(F->count);
|
||
foreach_set(F, last, p)
|
||
if (is_in_set(p, n)) {
|
||
new = GETSET(F1, F1->count++);
|
||
set_or(new, p, cube.var_mask[cube.output]);
|
||
PUTSIZE(new, SIZE(p));
|
||
SET(new, REDUND);
|
||
}
|
||
|
||
/* Find ways (sop form) to fail to cover output indexed by n */
|
||
Flist = cube2list(F1, D);
|
||
table = sf_new(10, Rp_size);
|
||
foreach_set(F1, last, p) {
|
||
set_fill(Rp_start, Rp_size);
|
||
set_remove(Rp_start, SIZE(p));
|
||
table = sf_append(table, fcube_is_covered(Flist, p));
|
||
RESET(p, REDUND);
|
||
}
|
||
set_fill(Rp_start, Rp_size);
|
||
foreach_set(table, last, p) {
|
||
set_diff(p, Rp_start, p);
|
||
}
|
||
|
||
/* complement this to get possible ways to cover the function */
|
||
for(i = 0; i < Rp_size; i++) {
|
||
if (! is_in_set(select, i)) {
|
||
p = set_new(Rp_size);
|
||
set_insert(p, i);
|
||
table = sf_addset(table, p);
|
||
set_free(p);
|
||
}
|
||
}
|
||
f_table = unate_compl(table);
|
||
|
||
/* what a pain, but we need bitwise complement of this */
|
||
set_fill(Rp_start, Rp_size);
|
||
foreach_set(f_table, last, p) {
|
||
set_diff(p, Rp_start, p);
|
||
}
|
||
|
||
free_cubelist(Flist);
|
||
sf_free(F1);
|
||
return f_table;
|
||
}
|
||
#endif
|
||
|
||
/*
|
||
* Take a PLA (ON-set, OFF-set and DC-set) and create the
|
||
* "double-phase characteristic function" which is merely a new
|
||
* function which has twice as many outputs and realizes both the
|
||
* function and the complement.
|
||
*
|
||
* The cube structure is assumed to represent the PLA upon entering.
|
||
* It will be modified to represent the double-phase function upon
|
||
* exit.
|
||
*
|
||
* Only the outputs numbered starting with "first_output" are
|
||
* duplicated in the output part
|
||
*/
|
||
|
||
void output_phase_setup(PLA, first_output)
|
||
INOUT pPLA PLA;
|
||
int first_output;
|
||
{
|
||
pcover F, R, D;
|
||
pcube mask, mask1, last;
|
||
int first_part, offset;
|
||
bool save;
|
||
register pcube p, pr, pf;
|
||
register int i, last_part;
|
||
|
||
if (cube.output == -1)
|
||
fatal("output_phase_setup: must have an output");
|
||
|
||
F = PLA->F;
|
||
D = PLA->D;
|
||
R = PLA->R;
|
||
first_part = cube.first_part[cube.output] + first_output;
|
||
last_part = cube.last_part[cube.output];
|
||
offset = cube.part_size[cube.output] - first_output;
|
||
|
||
/* Change the output size, setup the cube structure */
|
||
setdown_cube();
|
||
cube.part_size[cube.output] += offset;
|
||
cube_setup();
|
||
|
||
/* Create a mask to select that part of the cube which isn't changing */
|
||
mask = set_save(cube.fullset);
|
||
for(i = first_part; i < cube.size; i++)
|
||
set_remove(mask, i);
|
||
mask1 = set_save(mask);
|
||
for(i = cube.first_part[cube.output]; i < first_part; i++) {
|
||
set_remove(mask1, i);
|
||
}
|
||
|
||
PLA->F = new_cover(F->count + R->count);
|
||
PLA->R = new_cover(F->count + R->count);
|
||
PLA->D = new_cover(D->count);
|
||
|
||
foreach_set(F, last, p) {
|
||
pf = GETSET(PLA->F, (PLA->F)->count++);
|
||
pr = GETSET(PLA->R, (PLA->R)->count++);
|
||
INLINEset_and(pf, mask, p);
|
||
INLINEset_and(pr, mask1, p);
|
||
for(i = first_part; i <= last_part; i++)
|
||
if (is_in_set(p, i))
|
||
set_insert(pf, i);
|
||
save = FALSE;
|
||
for(i = first_part; i <= last_part; i++)
|
||
if (is_in_set(p, i))
|
||
save = TRUE, set_insert(pr, i+offset);
|
||
if (! save) PLA->R->count--;
|
||
}
|
||
|
||
foreach_set(R, last, p) {
|
||
pf = GETSET(PLA->F, (PLA->F)->count++);
|
||
pr = GETSET(PLA->R, (PLA->R)->count++);
|
||
INLINEset_and(pf, mask1, p);
|
||
INLINEset_and(pr, mask, p);
|
||
save = FALSE;
|
||
for(i = first_part; i <= last_part; i++)
|
||
if (is_in_set(p, i))
|
||
save = TRUE, set_insert(pf, i+offset);
|
||
if (! save) PLA->F->count--;
|
||
for(i = first_part; i <= last_part; i++)
|
||
if (is_in_set(p, i))
|
||
set_insert(pr, i);
|
||
}
|
||
|
||
foreach_set(D, last, p) {
|
||
pf = GETSET(PLA->D, (PLA->D)->count++);
|
||
INLINEset_and(pf, mask, p);
|
||
for(i = first_part; i <= last_part; i++)
|
||
if (is_in_set(p, i)) {
|
||
set_insert(pf, i);
|
||
set_insert(pf, i+offset);
|
||
}
|
||
}
|
||
|
||
free_cover(F);
|
||
free_cover(D);
|
||
free_cover(R);
|
||
set_free(mask);
|
||
set_free(mask1);
|
||
}
|
||
|
||
/*
|
||
* set_phase -- given a "cube" which describes which phases of the output
|
||
* are to be implemented, compute the appropriate on-set and off-set
|
||
*/
|
||
pPLA set_phase(PLA)
|
||
INOUT pPLA PLA;
|
||
{
|
||
pcover F1, R1;
|
||
register pcube last, p, outmask;
|
||
register pcube temp=cube.temp[0], phase=PLA->phase, phase1=cube.temp[1];
|
||
|
||
outmask = cube.var_mask[cube.num_vars - 1];
|
||
set_diff(phase1, outmask, phase);
|
||
set_or(phase1, set_diff(temp, cube.fullset, outmask), phase1);
|
||
F1 = new_cover((PLA->F)->count + (PLA->R)->count);
|
||
R1 = new_cover((PLA->F)->count + (PLA->R)->count);
|
||
|
||
foreach_set(PLA->F, last, p) {
|
||
if (! setp_disjoint(set_and(temp, p, phase), outmask))
|
||
set_copy(GETSET(F1, F1->count++), temp);
|
||
if (! setp_disjoint(set_and(temp, p, phase1), outmask))
|
||
set_copy(GETSET(R1, R1->count++), temp);
|
||
}
|
||
foreach_set(PLA->R, last, p) {
|
||
if (! setp_disjoint(set_and(temp, p, phase), outmask))
|
||
set_copy(GETSET(R1, R1->count++), temp);
|
||
if (! setp_disjoint(set_and(temp, p, phase1), outmask))
|
||
set_copy(GETSET(F1, F1->count++), temp);
|
||
}
|
||
free_cover(PLA->F);
|
||
free_cover(PLA->R);
|
||
PLA->F = F1;
|
||
PLA->R = R1;
|
||
return PLA;
|
||
}
|
||
|
||
#define POW2(x) (1 << (x))
|
||
|
||
void opoall(PLA, first_output, last_output, opo_strategy)
|
||
pPLA PLA;
|
||
int first_output, last_output;
|
||
int opo_strategy;
|
||
{
|
||
pcover F, D, R, best_F, best_D, best_R;
|
||
int i, j, ind, num;
|
||
pcube bestphase;
|
||
|
||
opo_exact = opo_strategy;
|
||
|
||
if (PLA->phase != NULL) {
|
||
set_free(PLA->phase);
|
||
}
|
||
|
||
bestphase = set_save(cube.fullset);
|
||
best_F = sf_save(PLA->F);
|
||
best_D = sf_save(PLA->D);
|
||
best_R = sf_save(PLA->R);
|
||
|
||
for(i = 0; i < POW2(last_output - first_output + 1); i++) {
|
||
|
||
/* save the initial PLA covers */
|
||
F = sf_save(PLA->F);
|
||
D = sf_save(PLA->D);
|
||
R = sf_save(PLA->R);
|
||
|
||
/* compute the phase cube for this iteration */
|
||
PLA->phase = set_save(cube.fullset);
|
||
num = i;
|
||
for(j = last_output; j >= first_output; j--) {
|
||
if (num % 2 == 0) {
|
||
ind = cube.first_part[cube.output] + j;
|
||
set_remove(PLA->phase, ind);
|
||
}
|
||
num /= 2;
|
||
}
|
||
|
||
/* set the phase and minimize */
|
||
(void) set_phase(PLA);
|
||
printf("# phase is %s\n", pc1(PLA->phase));
|
||
summary = TRUE;
|
||
minimize(PLA);
|
||
|
||
/* see if this is the best so far */
|
||
if (PLA->F->count < best_F->count) {
|
||
/* save new best solution */
|
||
set_copy(bestphase, PLA->phase);
|
||
sf_free(best_F);
|
||
sf_free(best_D);
|
||
sf_free(best_R);
|
||
best_F = PLA->F;
|
||
best_D = PLA->D;
|
||
best_R = PLA->R;
|
||
} else {
|
||
/* throw away the solution */
|
||
free_cover(PLA->F);
|
||
free_cover(PLA->D);
|
||
free_cover(PLA->R);
|
||
}
|
||
set_free(PLA->phase);
|
||
|
||
/* restore the initial PLA covers */
|
||
PLA->F = F;
|
||
PLA->D = D;
|
||
PLA->R = R;
|
||
}
|
||
|
||
/* one more minimization to restore the best answer */
|
||
PLA->phase = bestphase;
|
||
sf_free(PLA->F);
|
||
sf_free(PLA->D);
|
||
sf_free(PLA->R);
|
||
PLA->F = best_F;
|
||
PLA->D = best_D;
|
||
PLA->R = best_R;
|
||
}
|
||
|
||
static void minimize(PLA)
|
||
pPLA PLA;
|
||
{
|
||
if (opo_exact) {
|
||
EXEC_S(PLA->F = minimize_exact(PLA->F,PLA->D,PLA->R,1), "EXACT", PLA->F);
|
||
} else {
|
||
EXEC_S(PLA->F = espresso(PLA->F, PLA->D, PLA->R), "ESPRESSO ",PLA->F);
|
||
}
|
||
}
|
||
ABC_NAMESPACE_IMPL_END
|
||
|