mirror of https://github.com/YosysHQ/yosys.git
Initial version of opt_demorgan is functioning for AND/OR gates. Not the prettiest results for bus inputs, but this can be improved
This commit is contained in:
parent
f9d023c53f
commit
6da5d36968
|
@ -6,6 +6,7 @@ OBJS += passes/opt/opt_reduce.o
|
|||
OBJS += passes/opt/opt_rmdff.o
|
||||
OBJS += passes/opt/opt_clean.o
|
||||
OBJS += passes/opt/opt_expr.o
|
||||
OBJS += passes/opt/opt_demorgan.o
|
||||
OBJS += passes/opt/rmports.o
|
||||
|
||||
ifneq ($(SMALL),1)
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/yosys.h"
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/modtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
void demorgan_worker(
|
||||
ModIndex& index,
|
||||
Cell *cell,
|
||||
unsigned int& cells_changed)
|
||||
{
|
||||
SigMap& sigmap = index.sigmap;
|
||||
auto m = cell->module;
|
||||
|
||||
//TODO: Add support for reduce_xor
|
||||
//DeMorgan of XOR is either XOR (if even number of inputs) or XNOR (if odd number)
|
||||
|
||||
if( (cell->type != "$reduce_and") && (cell->type != "$reduce_or") )
|
||||
return;
|
||||
|
||||
auto insig = sigmap(cell->getPort("\\A"));
|
||||
log("Inspecting %s cell %s (%d inputs)\n", log_id(cell->type), log_id(cell->name), insig.size());
|
||||
int num_inverted = 0;
|
||||
for(int i=0; i<insig.size(); i++)
|
||||
{
|
||||
auto b = insig[i];
|
||||
|
||||
//See if this bit is driven by a $not cell
|
||||
//TODO: do other stuff like nor/nand?
|
||||
pool<ModIndex::PortInfo> ports = index.query_ports(b);
|
||||
bool inverted = false;
|
||||
for(auto x : ports)
|
||||
{
|
||||
if(x.port == "\\Y" && x.cell->type == "$_NOT_")
|
||||
{
|
||||
inverted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(inverted)
|
||||
num_inverted ++;
|
||||
}
|
||||
|
||||
//Stop if less than half of the inputs are inverted
|
||||
if(num_inverted*2 < insig.size())
|
||||
{
|
||||
log(" %d / %d inputs are inverted, not pushing\n", num_inverted, insig.size());
|
||||
return;
|
||||
}
|
||||
|
||||
//More than half of the inputs are inverted! Push through
|
||||
cells_changed ++;
|
||||
log(" %d / %d inputs are inverted, pushing inverter through reduction\n", num_inverted, insig.size());
|
||||
|
||||
//For each input, either add or remove the inverter as needed
|
||||
//TODO: this duplicates the loop up above, can we refactor it?
|
||||
for(int i=0; i<insig.size(); i++)
|
||||
{
|
||||
auto b = insig[i];
|
||||
|
||||
//See if this bit is driven by a $not cell
|
||||
//TODO: do other stuff like nor/nand?
|
||||
pool<ModIndex::PortInfo> ports = index.query_ports(b);
|
||||
RTLIL::Cell* srcinv = NULL;
|
||||
for(auto x : ports)
|
||||
{
|
||||
if(x.port == "\\Y" && x.cell->type == "$_NOT_")
|
||||
{
|
||||
srcinv = x.cell;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//We are NOT inverted! Add an inverter
|
||||
if(!srcinv)
|
||||
{
|
||||
auto inverted_b = m->addWire(NEW_ID);
|
||||
m->addNot(NEW_ID, RTLIL::SigSpec(b), RTLIL::SigSpec(inverted_b));
|
||||
insig[i] = inverted_b;
|
||||
}
|
||||
|
||||
//We ARE inverted - bypass it
|
||||
//Don't automatically delete the inverter since other stuff might still use it
|
||||
else
|
||||
insig[i] = srcinv->getPort("\\A");
|
||||
}
|
||||
|
||||
//Cosmetic fixup: If our input is just a scrambled version of one bus, rearrange it
|
||||
//Reductions are all commutative, so there's no point in having them in a weird order
|
||||
bool same_signal = true;
|
||||
RTLIL::Wire* srcwire = insig[0].wire;
|
||||
std::map<int, int> seen_bits;
|
||||
for(int i=0; i<insig.size(); i++)
|
||||
seen_bits[i] = 0;
|
||||
for(int i=0; i<insig.size(); i++)
|
||||
{
|
||||
seen_bits[insig[i].offset] ++;
|
||||
if(insig[i].wire != srcwire)
|
||||
{
|
||||
same_signal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(same_signal)
|
||||
{
|
||||
//Make sure we've seen every bit exactly once
|
||||
bool every_bit_once = true;
|
||||
for(int i=0; i<insig.size(); i++)
|
||||
{
|
||||
if(seen_bits[i] != 1)
|
||||
{
|
||||
every_bit_once = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//All good? Just use the whole wire as-is without any reordering
|
||||
//We do have to swap MSB to LSB b/c that's the way the reduction cells seem to work?
|
||||
//Unclear on why this isn't sorting properly
|
||||
//TODO: can we do SigChunks instead of single bits if we have subsets of a bus?
|
||||
if(every_bit_once && (insig.size() == srcwire->width) )
|
||||
{
|
||||
log("Rearranging bits\n");
|
||||
RTLIL::SigSpec newsig;
|
||||
for(int i=0; i<insig.size(); i++)
|
||||
newsig.append(RTLIL::SigBit(srcwire, insig.size() - i - 1));
|
||||
insig = newsig;
|
||||
insig.sort();
|
||||
}
|
||||
}
|
||||
|
||||
//Push the new input signal back to the reduction (after bypassing/adding inverters)
|
||||
cell->setPort("\\A", insig);
|
||||
|
||||
//Change the cell type
|
||||
if(cell->type == "$reduce_and")
|
||||
cell->type = "$reduce_or";
|
||||
else if(cell->type == "$reduce_or")
|
||||
cell->type = "$reduce_and";
|
||||
//don't change XOR
|
||||
|
||||
//Add an inverter to the output
|
||||
auto inverted_output = cell->getPort("\\Y");
|
||||
auto uninverted_output = m->addWire(NEW_ID);
|
||||
m->addNot(NEW_ID, RTLIL::SigSpec(uninverted_output), inverted_output);
|
||||
cell->setPort("\\Y", uninverted_output);
|
||||
}
|
||||
|
||||
struct OptDemorganPass : public Pass {
|
||||
OptDemorganPass() : Pass("opt_demorgan", "Optimize reductions with DeMorgan equivalents") { }
|
||||
virtual void help()
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" opt_demorgan [selection]\n");
|
||||
log("\n");
|
||||
log("This pass pushes inverters through $reduce_* cells if this will reduce the\n");
|
||||
log("overall gate count of the circuit\n");
|
||||
log("\n");
|
||||
}
|
||||
virtual void execute(std::vector<std::string> /*args*/, RTLIL::Design *design)
|
||||
{
|
||||
log_header(design, "Executing OPT_DEMORGAN pass (push inverters through $reduce_* cells).\n");
|
||||
|
||||
//int argidx = 0;
|
||||
//extra_args(args, argidx, design);
|
||||
|
||||
unsigned int cells_changed = 0;
|
||||
for (auto module : design->selected_modules())
|
||||
{
|
||||
ModIndex index(module);
|
||||
for (auto cell : module->selected_cells())
|
||||
demorgan_worker(index, cell, cells_changed);
|
||||
}
|
||||
|
||||
if(cells_changed)
|
||||
log("Pushed inverters through %u reductions\n", cells_changed);
|
||||
}
|
||||
} OptDemorganPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
Loading…
Reference in New Issue