Initial version of opt_demorgan is functioning for AND/OR gates. Not the prettiest...
authorAndrew Zonenberg <azonenberg@drawersteak.com>
Tue, 12 Sep 2017 00:18:26 +0000 (17:18 -0700)
committerAndrew Zonenberg <azonenberg@drawersteak.com>
Wed, 13 Sep 2017 01:47:46 +0000 (18:47 -0700)
passes/opt/Makefile.inc
passes/opt/opt_demorgan.cc [new file with mode: 0644]

index a145cf9460aaf222649208b4c6187e175856a155..639dc6590d02f0292a8d098e61be86e0b91db327 100644 (file)
@@ -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)
diff --git a/passes/opt/opt_demorgan.cc b/passes/opt/opt_demorgan.cc
new file mode 100644 (file)
index 0000000..62d65de
--- /dev/null
@@ -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