techmap: Add support for extracting init values of ports
authorMarcin Kościelnicki <koriakin@0x04.net>
Fri, 16 Aug 2019 03:14:03 +0000 (03:14 +0000)
committerMarcin Kościelnicki <koriakin@0x04.net>
Sat, 7 Sep 2019 14:30:43 +0000 (16:30 +0200)
CHANGELOG
passes/techmap/techmap.cc
tests/techmap/wireinit.ys [new file with mode: 0644]

index c2942929561c7700cb92ab5c52bdc6ada51ea625..e416d152c97bcf0a7c51d3464f328f2e64c7cf97 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -38,6 +38,7 @@ Yosys 0.9 .. Yosys 0.9-dev
     - Improvements in pmgen: slices, choices, define, generate
     - Added "xilinx_srl" for Xilinx shift register extraction
     - Removed "shregmap -tech xilinx" (superseded by "xilinx_srl")
+    - Added "_TECHMAP_WIREINIT_*_" attribute and "_TECHMAP_REMOVEINIT_*_" wire for "techmap" pass
 
 Yosys 0.8 .. Yosys 0.9
 ----------------------
index 5ce1bf7d68c32caec13f4f5d6a66f911cbbf7c92..51a65aea6ac5731ebca27677be27f4c0cc3f6898 100644 (file)
@@ -424,6 +424,18 @@ struct TechmapWorker
 
                SigMap sigmap(module);
 
+               dict<SigBit, State> init_bits;
+               pool<SigBit> remove_init_bits;
+
+               for (auto wire : module->wires()) {
+                       if (wire->attributes.count("\\init")) {
+                               Const value = wire->attributes.at("\\init");
+                               for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++)
+                                       if (value[i] != State::Sx)
+                                               init_bits[sigmap(SigBit(wire, i))] = value[i];
+                       }
+               }
+
                TopoSort<RTLIL::Cell*, RTLIL::IdString::compare_ptr_by_name<RTLIL::Cell>> cells;
                std::map<RTLIL::Cell*, std::set<RTLIL::SigBit>> cell_to_inbit;
                std::map<RTLIL::SigBit, std::set<RTLIL::Cell*>> outbit_to_cell;
@@ -661,6 +673,17 @@ struct TechmapWorker
                                                                        bit = RTLIL::SigBit(RTLIL::State::Sx);
                                                        parameters[stringf("\\_TECHMAP_CONSTVAL_%s_", RTLIL::id2cstr(conn.first))] = RTLIL::SigSpec(v).as_const();
                                                }
+                                               if (tpl->avail_parameters.count(stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))) != 0) {
+                                                       auto sig = sigmap(conn.second);
+                                                       RTLIL::Const value(State::Sx, sig.size());
+                                                       for (int i = 0; i < sig.size(); i++) {
+                                                               auto it = init_bits.find(sig[i]);
+                                                               if (it != init_bits.end()) {
+                                                                       value[i] = it->second;
+                                                               }
+                                                       }
+                                                       parameters[stringf("\\_TECHMAP_WIREINIT_%s_", RTLIL::id2cstr(conn.first))] = value;
+                                               }
                                        }
 
                                        int unique_bit_id_counter = 0;
@@ -861,12 +884,25 @@ struct TechmapWorker
 
                                        TechmapWires twd = techmap_find_special_wires(tpl);
                                        for (auto &it : twd) {
-                                               if (it.first != "_TECHMAP_FAIL_" && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_")
+                                               if (it.first != "_TECHMAP_FAIL_" && (it.first.substr(0, 20) != "_TECHMAP_REMOVEINIT_" || it.first[it.first.size()-1] != '_') && it.first.substr(0, 12) != "_TECHMAP_DO_" && it.first.substr(0, 14) != "_TECHMAP_DONE_")
                                                        log_error("Techmap yielded unknown config wire %s.\n", it.first.c_str());
                                                if (techmap_do_cache[tpl])
                                                        for (auto &it2 : it.second)
                                                                if (!it2.value.is_fully_const())
                                                                        log_error("Techmap yielded config wire %s with non-const value %s.\n", RTLIL::id2cstr(it2.wire->name), log_signal(it2.value));
+                                               if (it.first.substr(0, 20) == "_TECHMAP_REMOVEINIT_" && techmap_do_cache[tpl]) {
+                                                       for (auto &it2 : it.second) {
+                                                               auto val = it2.value.as_const();
+                                                               auto wirename = RTLIL::escape_id(it.first.substr(20, it.first.size() - 20 - 1));
+                                                               auto it = cell->connections().find(wirename);
+                                                               if (it != cell->connections().end()) {
+                                                                       auto sig = sigmap(it->second);
+                                                                       for (int i = 0; i < sig.size(); i++)
+                                                                               if (val[i] == State::S1)
+                                                                                       remove_init_bits.insert(sig[i]);
+                                                               }
+                                                       }
+                                               }
                                                techmap_wire_names.erase(it.first);
                                        }
 
@@ -935,6 +971,25 @@ struct TechmapWorker
                        handled_cells.insert(cell);
                }
 
+               if (!remove_init_bits.empty()) {
+                       for (auto wire : module->wires())
+                               if (wire->attributes.count("\\init")) {
+                                       Const &value = wire->attributes.at("\\init");
+                                       bool do_cleanup = true;
+                                       for (int i = 0; i < min(GetSize(value), GetSize(wire)); i++) {
+                                               SigBit bit = sigmap(SigBit(wire, i));
+                                               if (remove_init_bits.count(bit))
+                                                       value[i] = State::Sx;
+                                               else if (value[i] != State::Sx)
+                                                       do_cleanup = false;
+                                       }
+                                       if (do_cleanup) {
+                                               log("Removing init attribute from wire %s.%s.\n", log_id(module), log_id(wire));
+                                               wire->attributes.erase("\\init");
+                                       }
+                               }
+               }
+
                if (log_continue) {
                        log_header(design, "Continuing TECHMAP pass.\n");
                        log_continue = false;
@@ -1047,6 +1102,13 @@ struct TechmapPass : public Pass {
                log("\n");
                log("        It is possible to combine both prefixes to 'RECURSION; CONSTMAP; '.\n");
                log("\n");
+               log("    _TECHMAP_REMOVEINIT_<port-name>_\n");
+               log("        When this wire is set to a constant value, the init attribute of the wire(s)\n");
+               log("        connected to this port will be consumed.  This wire must have the same\n");
+               log("        width as the given port, and for every bit that is set to 1 in the value,\n");
+               log("        the corresponding init attribute bit will be changed to 1'bx.  If all\n");
+               log("        bits of an init attribute are left as x, it will be removed.\n");
+               log("\n");
                log("In addition to this special wires, techmap also supports special parameters in\n");
                log("modules in the map file:\n");
                log("\n");
@@ -1060,6 +1122,13 @@ struct TechmapPass : public Pass {
                log("        former has a 1-bit for each constant input bit and the latter has the\n");
                log("        value for this bit. The unused bits of the latter are set to undef (x).\n");
                log("\n");
+               log("    _TECHMAP_WIREINIT_<port-name>_\n");
+               log("        When a parameter with this name exists, it will be set to the initial\n");
+               log("        value of the wire(s) connected to the given port, as specified by the init\n");
+               log("        attribute. If the attribute doesn't exist, x will be filled for the\n");
+               log("        missing bits.  To remove the init attribute bits used, use the\n");
+               log("        _TECHMAP_REMOVEINIT_*_ wires.\n");
+               log("\n");
                log("    _TECHMAP_BITS_CONNMAP_\n");
                log("    _TECHMAP_CONNMAP_<port-name>_\n");
                log("        For an N-bit port, the _TECHMAP_CONNMAP_<port-name>_ parameter, if it\n");
diff --git a/tests/techmap/wireinit.ys b/tests/techmap/wireinit.ys
new file mode 100644 (file)
index 0000000..1396839
--- /dev/null
@@ -0,0 +1,98 @@
+read_verilog <<EOT
+(* techmap_celltype = "$_DFF_P_" *)
+module ffmap(...);
+input D;
+input C;
+output Q;
+parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
+
+ffbb #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_(.D(D), .Q(Q), .C(C));
+
+wire _TECHMAP_FAIL_ = _TECHMAP_WIREINIT_Q_ === 1'b1;
+
+wire _TECHMAP_REMOVEINIT_Q_ = 1'b1;
+
+endmodule
+EOT
+design -stash map
+
+read_verilog <<EOT
+(* techmap_celltype = "$_DFF_P_" *)
+module ffmap(...);
+input D;
+input C;
+output Q;
+parameter [0:0] _TECHMAP_WIREINIT_Q_ = 1'bx;
+
+ffbb #(.INIT(_TECHMAP_WIREINIT_Q_)) _TECHMAP_REPLACE_(.D(D), .Q(Q), .C(C));
+
+wire _TECHMAP_FAIL_ = _TECHMAP_WIREINIT_Q_ === 1'b1;
+
+wire _TECHMAP_REMOVEINIT_Q_ = 1'b0;
+
+endmodule
+EOT
+design -stash map_noremove
+
+read_verilog <<EOT
+module ffbb (...);
+parameter [0:0] INIT = 1'bx;
+input D, C;
+output Q;
+endmodule
+
+module top(...);
+input clk;
+input d;
+output reg q0 = 0;
+output reg q1 = 1;
+output reg qx;
+
+always @(posedge clk) begin
+       q0 <= d;
+       q1 <= d;
+       qx <= d;
+end
+endmodule
+EOT
+
+design -save ref
+
+hierarchy -auto-top
+proc
+simplemap
+techmap -map %map
+clean
+# Make sure the parameter was used properly.
+select -assert-count 2 top/t:ffbb
+select -set ff0 top/w:q0 %ci t:ffbb %i
+select -set ffx top/w:qx %ci t:ffbb %i
+select -assert-count 1 @ff0
+select -assert-count 1 @ffx
+select -assert-count 1 @ff0 r:INIT=1'b0 %i
+select -assert-count 1 @ffx r:INIT=1'bx %i
+select -assert-count 0 top/w:q1 %ci t:ffbb %i
+# Make sure the init values are dropped from the wires iff mapping was performed.
+select -assert-count 0 top/w:q0 a:init %i
+select -assert-count 1 top/w:q1 a:init=1'b1 %i
+select -assert-count 0 top/w:qx a:init %i
+
+design -load ref
+hierarchy -auto-top
+proc
+simplemap
+techmap -map %map_noremove
+clean
+# Make sure the parameter was used properly.
+select -assert-count 2 top/t:ffbb
+select -set ff0 top/w:q0 %ci t:ffbb %i
+select -set ffx top/w:qx %ci t:ffbb %i
+select -assert-count 1 @ff0
+select -assert-count 1 @ffx
+select -assert-count 1 @ff0 r:INIT=1'b0 %i
+select -assert-count 1 @ffx r:INIT=1'bx %i
+select -assert-count 0 top/w:q1 %ci t:ffbb %i
+# Make sure the init values are not dropped from the wires.
+select -assert-count 1 top/w:q0 a:init=1'b0 %i
+select -assert-count 1 top/w:q1 a:init=1'b1 %i
+select -assert-count 0 top/w:qx a:init %i