Added tribuf command
authorClifford Wolf <clifford@clifford.at>
Sun, 16 Aug 2015 10:55:25 +0000 (12:55 +0200)
committerClifford Wolf <clifford@clifford.at>
Sun, 16 Aug 2015 10:55:25 +0000 (12:55 +0200)
passes/techmap/Makefile.inc
passes/techmap/tribuf.cc [new file with mode: 0644]
techlibs/ice40/synth_ice40.cc

index f1c987ccdf210dc88dbdb02daca4571caed85e5b..af4816a72b25b9c5981a0f7009e0380a5932f8f1 100644 (file)
@@ -19,6 +19,7 @@ OBJS += passes/techmap/dffinit.o
 OBJS += passes/techmap/pmuxtree.o
 OBJS += passes/techmap/muxcover.o
 OBJS += passes/techmap/aigmap.o
+OBJS += passes/techmap/tribuf.o
 endif
 
 GENFILES += passes/techmap/techmap.inc
diff --git a/passes/techmap/tribuf.cc b/passes/techmap/tribuf.cc
new file mode 100644 (file)
index 0000000..d0564b4
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ *  yosys -- Yosys Open SYnthesis Suite
+ *
+ *  Copyright (C) 2012  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"
+
+USING_YOSYS_NAMESPACE
+PRIVATE_NAMESPACE_BEGIN
+
+struct TribufConfig {
+       bool merge_mode;
+       bool logic_mode;
+
+       TribufConfig() {
+               merge_mode = false;
+               logic_mode = false;
+       }
+};
+
+struct TribufWorker {
+       Module *module;
+       SigMap sigmap;
+       const TribufConfig &config;
+
+       TribufWorker(Module *module, const TribufConfig &config) : module(module), sigmap(module), config(config)
+       {
+       }
+
+       static bool is_all_z(SigSpec sig)
+       {
+               for (auto bit : sig)
+                       if (bit != State::Sz)
+                               return false;
+               return true;
+       }
+
+       void run()
+       {
+               dict<SigSpec, vector<Cell*>> tribuf_cells;
+               pool<SigBit> output_bits;
+
+               if (config.logic_mode)
+                       for (auto wire : module->wires())
+                               if (wire->port_output)
+                                       for (auto bit : sigmap(wire))
+                                               output_bits.insert(bit);
+
+               for (auto cell : module->selected_cells())
+               {
+                       if (cell->type == "$tribuf")
+                               tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell);
+
+                       if (cell->type == "$_TBUF_")
+                               tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell);
+
+                       if (cell->type.in("$mux", "$_MUX_"))
+                       {
+                               IdString en_port = cell->type == "$mux" ? "\\EN" : "\\E";
+                               IdString tri_type = cell->type == "$mux" ? "$tribuf" : "$_TBUF_";
+
+                               if (is_all_z(cell->getPort("\\A")) && is_all_z(cell->getPort("\\B"))) {
+                                       module->remove(cell);
+                                       continue;
+                               }
+
+                               if (is_all_z(cell->getPort("\\A"))) {
+                                       cell->setPort("\\A", cell->getPort("\\B"));
+                                       cell->setPort(en_port, cell->getPort("\\S"));
+                                       cell->unsetPort("\\B");
+                                       cell->unsetPort("\\S");
+                                       cell->type = tri_type;
+                                       tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell);
+                                       continue;
+                               }
+
+                               if (is_all_z(cell->getPort("\\B"))) {
+                                       cell->setPort(en_port, module->Not(NEW_ID, cell->getPort("\\S")));
+                                       cell->unsetPort("\\B");
+                                       cell->unsetPort("\\S");
+                                       cell->type = tri_type;
+                                       tribuf_cells[sigmap(cell->getPort("\\Y"))].push_back(cell);
+                                       continue;
+                               }
+                       }
+               }
+
+               if (config.merge_mode || config.logic_mode)
+               {
+                       for (auto &it : tribuf_cells)
+                       {
+                               bool no_tribuf = false;
+
+                               if (config.logic_mode) {
+                                       no_tribuf = true;
+                                       for (auto bit : it.first)
+                                               if (output_bits.count(bit))
+                                                       no_tribuf = false;
+                               }
+
+                               if (GetSize(it.second) <= 1 && !no_tribuf)
+                                       continue;
+
+                               SigSpec pmux_b, pmux_s;
+                               for (auto cell : it.second) {
+                                       if (cell->type == "$tribuf")
+                                               pmux_s.append(cell->getPort("\\EN"));
+                                       else
+                                               pmux_s.append(cell->getPort("\\E"));
+                                       pmux_b.append(cell->getPort("\\A"));
+                                       module->remove(cell);
+                               }
+
+                               SigSpec muxout = GetSize(pmux_s) > 1 ? module->Pmux(NEW_ID, SigSpec(State::Sx, GetSize(it.first)), pmux_b, pmux_s) : pmux_b;
+
+                               if (no_tribuf)
+                                       module->connect(it.first, muxout);
+                               else
+                                       module->addTribuf(NEW_ID, muxout, module->ReduceOr(NEW_ID, pmux_s), it.first);
+                       }
+               }
+       }
+};
+
+struct TribufPass : public Pass {
+       TribufPass() : Pass("tribuf", "infer tri-state buffers") { }
+       virtual void help()
+       {
+               //   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+               log("\n");
+               log("    tribuf [options] [selection]\n");
+               log("\n");
+               log("This pass transforms $mux cells with 'z' inputs to tristate buffers.\n");
+               log("\n");
+               log("    -merge\n");
+               log("        merge multiple tri-state buffers driving the same net\n");
+               log("        into a single buffer.\n");
+               log("\n");
+               log("    -logic\n");
+               log("        convert tri-state buffers that do not drive output ports\n");
+               log("        to non-tristate logic. this option implies -merge.\n");
+               log("\n");
+       }
+       virtual void execute(std::vector<std::string> args, RTLIL::Design *design)
+       {
+               TribufConfig config;
+
+               log_header("Executing TRIBUF pass.\n");
+
+               size_t argidx;
+               for (argidx = 1; argidx < args.size(); argidx++) {
+                       if (args[argidx] == "-merge") {
+                               config.merge_mode = true;
+                               continue;
+                       }
+                       if (args[argidx] == "-logic") {
+                               config.logic_mode = true;
+                               continue;
+                       }
+                       break;
+               }
+               extra_args(args, argidx, design);
+
+               for (auto module : design->selected_modules()) {
+                       TribufWorker worker(module, config);
+                       worker.run();
+               }
+       }
+} TribufPass;
+
+PRIVATE_NAMESPACE_END
index 4499263a3ea1c7227de7f09a3bc1854f21fa0a36..788835f1d3bc28a4d97e4c605281e5eedc724087 100644 (file)
@@ -82,6 +82,7 @@ struct SynthIce40Pass : public Pass {
                log("    flatten:         (unless -noflatten)\n");
                log("        proc\n");
                log("        flatten\n");
+               log("        tribuf -logic\n");
                log("\n");
                log("    coarse:\n");
                log("        synth -run coarse\n");
@@ -201,6 +202,7 @@ struct SynthIce40Pass : public Pass {
                {
                        Pass::call(design, "proc");
                        Pass::call(design, "flatten");
+                       Pass::call(design, "tribuf -logic");
                }
 
                if (check_label(active, run_from, run_to, "coarse"))