Added extractinv pass
authorMarcin Kościelnicki <koriakin@0x04.net>
Wed, 28 Aug 2019 14:58:14 +0000 (14:58 +0000)
committerMarcin Kościelnicki <koriakin@0x04.net>
Thu, 19 Sep 2019 02:02:48 +0000 (04:02 +0200)
CHANGELOG
README.md
passes/techmap/Makefile.inc
passes/techmap/extractinv.cc [new file with mode: 0644]
tests/techmap/extractinv.ys [new file with mode: 0644]

index 890fad9781318101207cea1fa3cf2fd8ed221552..2e73d58951a5661c4bcbef49037a8f5e34f1de4a 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -27,6 +27,7 @@ Yosys 0.9 .. Yosys 0.9-dev
     - Improve attribute and parameter encoding in JSON to avoid ambiguities between
       bit vectors and strings containing [01xz]*
     - Added "clkbufmap" pass
+    - Added "extractinv" pass and "invertible_pin" attribute
     - Added "synth_xilinx -family xc6s" for Spartan 6 support (experimental)
     - Added "synth_xilinx -ise" (experimental)
     - Added "synth_xilinx -iopad"
index af3333e1d7722d08101ac5114af21554ae9392e4..fdd4bb410d54f59f30016c0d990a459f27112b74 100644 (file)
--- a/README.md
+++ b/README.md
@@ -347,6 +347,12 @@ Verilog Attributes and non-standard features
   automatic clock buffer insertion by ``clkbufmap``. This behaviour can be
   overridden by providing a custom selection to ``clkbufmap``.
 
+- The ``invertible_pin`` attribute can be set on a port to mark it as
+  invertible via a cell parameter.  The name of the inversion parameter
+  is specified as the value of this attribute.  The value of the inversion
+  parameter must be of the same width as the port, with 1 indicating
+  an inverted bit and 0 indicating a non-inverted bit.
+
 - The ``iopad_external_pin`` attribute on a blackbox module's port marks
   it as the external-facing pin of an I/O pad, and prevents ``iopadmap``
   from inserting another pad cell on it.
index 631a80aa5e34c12b68746e47b6abbffe976fa3ce..cd357d72a263be1e66e38f3f06a373c950eb64eb 100644 (file)
@@ -40,6 +40,7 @@ OBJS += passes/techmap/attrmap.o
 OBJS += passes/techmap/zinit.o
 OBJS += passes/techmap/dff2dffs.o
 OBJS += passes/techmap/flowmap.o
+OBJS += passes/techmap/extractinv.o
 endif
 
 GENFILES += passes/techmap/techmap.inc
diff --git a/passes/techmap/extractinv.cc b/passes/techmap/extractinv.cc
new file mode 100644 (file)
index 0000000..dda71f1
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ *  yosys -- Yosys Open SYnthesis Suite
+ *
+ *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
+ *  Copyright (C) 2019  Marcin Kościelnicki <mwk@0x04.net>
+ *
+ *  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
+
+void split_portname_pair(std::string &port1, std::string &port2)
+{
+       size_t pos = port1.find_first_of(':');
+       if (pos != std::string::npos) {
+               port2 = port1.substr(pos+1);
+               port1 = port1.substr(0, pos);
+       }
+}
+
+struct ExtractinvPass : public Pass {
+       ExtractinvPass() : Pass("extractinv", "extract explicit inverter cells for invertible cell pins") { }
+       void help() YS_OVERRIDE
+       {
+               //   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
+               log("\n");
+               log("    extractinv [options] [selection]\n");
+               log("\n");
+               log("Searches the design for all cells with invertible pins controlled by a cell\n");
+               log("parameter (eg. IS_CLK_INVERTED on many Xilinx cells) and removes the parameter.\n");
+               log("If the parameter was set to 1, inserts an explicit inverter cell in front of\n");
+               log("the pin instead.  Normally used for output to ISE, which does not support the\n");
+               log("inversion parameters.\n");
+               log("\n");
+               log("To mark a cell port as invertible, use (* invertible_pin = \"param_name\" *)\n");
+               log("on the wire in the blackbox module.  The parameter value should have\n");
+               log("the same width as the port, and will be effectively XORed with it.\n");
+               log("\n");
+               log("    -inv <celltype> <portname_out>:<portname_in>\n");
+               log("        Specifies the cell type to use for the inverters and its port names.\n");
+               log("        This option is required.\n");
+               log("\n");
+       }
+
+       void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
+       {
+               log_header(design, "Executing EXTRACTINV pass (extracting pin inverters).\n");
+
+               std::string inv_celltype, inv_portname, inv_portname2;
+
+               size_t argidx;
+               for (argidx = 1; argidx < args.size(); argidx++)
+               {
+                       std::string arg = args[argidx];
+                       if (arg == "-inv" && argidx+2 < args.size()) {
+                               inv_celltype = args[++argidx];
+                               inv_portname = args[++argidx];
+                               split_portname_pair(inv_portname, inv_portname2);
+                               continue;
+                       }
+                       break;
+               }
+               extra_args(args, argidx, design);
+
+               if (inv_celltype.empty())
+                       log_error("The -inv option is required.\n");
+
+               for (auto module : design->selected_modules())
+               {
+                       for (auto cell : module->selected_cells())
+                       for (auto port : cell->connections()) {
+                               auto cell_module = design->module(cell->type);
+                               if (!cell_module)
+                                       continue;
+                               auto cell_wire = cell_module->wire(port.first);
+                               if (!cell_wire)
+                                       continue;
+                               auto it = cell_wire->attributes.find("\\invertible_pin");
+                               if (it == cell_wire->attributes.end())
+                                       continue;
+                               IdString param_name = RTLIL::escape_id(it->second.decode_string());
+                               auto it2 = cell->parameters.find(param_name);
+                               // Inversion not used -- skip.
+                               if (it2 == cell->parameters.end())
+                                       continue;
+                               SigSpec sig = port.second;
+                               if (it2->second.size() != sig.size())
+                                       log_error("The inversion parameter needs to be the same width as the port (%s.%s port %s parameter %s)", log_id(module->name), log_id(cell->type), log_id(port.first), log_id(param_name));
+                               RTLIL::Const invmask = it2->second;
+                               cell->parameters.erase(param_name);
+                               if (invmask.is_fully_zero())
+                                       continue;
+                               Wire *iwire = module->addWire(NEW_ID, sig.size());
+                               for (int i = 0; i < sig.size(); i++)
+                                       if (invmask[i] == State::S1) {
+                                               RTLIL::Cell *icell = module->addCell(NEW_ID, RTLIL::escape_id(inv_celltype));
+                                               icell->setPort(RTLIL::escape_id(inv_portname), SigSpec(iwire, i));
+                                               icell->setPort(RTLIL::escape_id(inv_portname2), sig[i]);
+                                               log("Inserting %s on %s.%s.%s[%d].\n", inv_celltype.c_str(), log_id(module), log_id(cell->type), log_id(port.first), i);
+                                               sig[i] = SigBit(iwire, i);
+                                       }
+                               cell->setPort(port.first, sig);
+                       }
+               }
+       }
+} ExtractinvPass;
+
+PRIVATE_NAMESPACE_END
diff --git a/tests/techmap/extractinv.ys b/tests/techmap/extractinv.ys
new file mode 100644 (file)
index 0000000..6146f82
--- /dev/null
@@ -0,0 +1,41 @@
+read_verilog << EOT
+
+module ff4(...);
+parameter [0:0] CLK_INV = 1'b0;
+parameter [3:0] DATA_INV = 4'b0000;
+(* invertible_pin = "CLK_INV" *)
+input clk;
+(* invertible_pin = "DATA_INV" *)
+input [3:0] d;
+output [3:0] q;
+endmodule
+
+module inv(...);
+output o;
+input i;
+endmodule
+
+module top(...);
+input d0, d1, d2, d3;
+input clk;
+output q;
+ff4 #(.DATA_INV(4'h5)) ff_inst (.clk(clk), .d({d3, d2, d1, d0}), .q(q));
+endmodule
+
+EOT
+
+extractinv -inv inv o:i
+clean
+
+select -assert-count 2 top/t:inv
+select -assert-count 2 top/t:inv top/t:ff4 %ci:+[d] %ci:+[o] %i
+
+select -assert-count 1 top/t:inv top/w:d0 %co:+[i] %i
+select -assert-count 0 top/t:inv top/w:d1 %co:+[i] %i
+select -assert-count 1 top/t:inv top/w:d2 %co:+[i] %i
+select -assert-count 0 top/t:inv top/w:d3 %co:+[i] %i
+
+select -assert-count 0 top/t:ff4 top/w:d0 %co:+[d] %i
+select -assert-count 1 top/t:ff4 top/w:d1 %co:+[d] %i
+select -assert-count 0 top/t:ff4 top/w:d2 %co:+[d] %i
+select -assert-count 1 top/t:ff4 top/w:d3 %co:+[d] %i