dfflibmap: Refactor to use dfflegalize internally.
authorMarcelina Kościelnicka <mwk@0x04.net>
Thu, 2 Jul 2020 16:22:43 +0000 (18:22 +0200)
committerMarcelina Kościelnicka <mwk@0x04.net>
Thu, 9 Jul 2020 16:51:03 +0000 (18:51 +0200)
passes/techmap/dfflibmap.cc
tests/liberty/run-test.sh
tests/techmap/dfflibmap-sim.v [new file with mode: 0644]
tests/techmap/dfflibmap.lib [new file with mode: 0644]
tests/techmap/dfflibmap.ys [new file with mode: 0644]

index c189d649b68bd972e63b8427d9f8a39672e5865a..78a6f1c0d570234807d24d75854ced5cadd40878 100644 (file)
@@ -115,7 +115,7 @@ static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name,
        return false;
 }
 
-static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval, bool prepare_mode)
+static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval)
 {
        LibertyAst *best_cell = nullptr;
        std::map<std::string, char> best_cell_ports;
@@ -222,21 +222,12 @@ static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has
        if (best_cell != nullptr) {
                log("  cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s.\n",
                                best_cell->args[0].c_str(), best_cell_noninv ? "non" : "", best_cell_pins, best_cell_area, cell_type.c_str());
-               if (prepare_mode) {
-                       cell_mappings[cell_type].cell_name = cell_type;
-                       cell_mappings[cell_type].ports["C"] = 'C';
-                       if (has_reset)
-                               cell_mappings[cell_type].ports["R"] = 'R';
-                       cell_mappings[cell_type].ports["D"] = 'D';
-                       cell_mappings[cell_type].ports["Q"] = 'Q';
-               } else {
-                       cell_mappings[cell_type].cell_name = RTLIL::escape_id(best_cell->args[0]);
-                       cell_mappings[cell_type].ports = best_cell_ports;
-               }
+               cell_mappings[cell_type].cell_name = RTLIL::escape_id(best_cell->args[0]);
+               cell_mappings[cell_type].ports = best_cell_ports;
        }
 }
 
-static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool setpol, bool clrpol, bool prepare_mode)
+static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool setpol, bool clrpol)
 {
        LibertyAst *best_cell = nullptr;
        std::map<std::string, char> best_cell_ports;
@@ -339,141 +330,12 @@ static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool
        if (best_cell != nullptr) {
                log("  cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s.\n",
                                best_cell->args[0].c_str(), best_cell_noninv ? "non" : "", best_cell_pins, best_cell_area, cell_type.c_str());
-               if (prepare_mode) {
-                       cell_mappings[cell_type].cell_name = cell_type;
-                       cell_mappings[cell_type].ports["C"] = 'C';
-                       cell_mappings[cell_type].ports["S"] = 'S';
-                       cell_mappings[cell_type].ports["R"] = 'R';
-                       cell_mappings[cell_type].ports["D"] = 'D';
-                       cell_mappings[cell_type].ports["Q"] = 'Q';
-               } else {
-                       cell_mappings[cell_type].cell_name = RTLIL::escape_id(best_cell->args[0]);
-                       cell_mappings[cell_type].ports = best_cell_ports;
-               }
+               cell_mappings[cell_type].cell_name = RTLIL::escape_id(best_cell->args[0]);
+               cell_mappings[cell_type].ports = best_cell_ports;
        }
 }
 
-static bool expand_cellmap_worker(std::string from, std::string to, std::string inv)
-{
-       if (cell_mappings.count(to) > 0)
-               return false;
-
-       log("  create mapping for %s from mapping for %s.\n", to.c_str(), from.c_str());
-       cell_mappings[to].cell_name = cell_mappings[from].cell_name;
-       cell_mappings[to].ports = cell_mappings[from].ports;
-
-       for (auto &it : cell_mappings[to].ports) {
-               char cmp_ch = it.second;
-               if ('a' <= cmp_ch && cmp_ch <= 'z')
-                       cmp_ch -= 'a' - 'A';
-               if (inv.find(cmp_ch) == std::string::npos)
-                       continue;
-               if ('a' <= it.second && it.second <= 'z')
-                       it.second -= 'a' - 'A';
-               else if ('A' <= it.second && it.second <= 'Z')
-                       it.second += 'a' - 'A';
-       }
-       return true;
-}
-
-static bool expand_cellmap(std::string pattern, std::string inv)
-{
-       std::vector<std::pair<std::string, std::string>> from_to_list;
-       bool return_status = false;
-
-       for (auto &it : cell_mappings) {
-               std::string from = it.first.str(), to = it.first.str();
-               if (from.size() != pattern.size())
-                       continue;
-               for (size_t i = 0; i < from.size(); i++) {
-                       if (pattern[i] == '*') {
-                               to[i] = from[i] == 'P' ? 'N' :
-                                       from[i] == 'N' ? 'P' :
-                                       from[i] == '1' ? '0' :
-                                       from[i] == '0' ? '1' : '*';
-                       } else
-                       if (pattern[i] != '?' && pattern[i] != from[i])
-                               goto pattern_failed;
-               }
-               from_to_list.push_back(std::pair<std::string, std::string>(from, to));
-       pattern_failed:;
-       }
-
-       for (auto &it : from_to_list)
-               return_status = return_status || expand_cellmap_worker(it.first, it.second, inv);
-       return return_status;
-}
-
-static void map_sr_to_arst(IdString from, IdString to)
-{
-       if (!cell_mappings.count(from) || cell_mappings.count(to) > 0)
-               return;
-
-       char from_clk_pol = from[8];
-       char from_set_pol = from[9];
-       char from_clr_pol = from[10];
-       char to_clk_pol = to[6];
-       char to_rst_pol = to[7];
-       char to_rst_val = to[8];
-
-       log_assert(from_clk_pol == to_clk_pol);
-       log_assert(to_rst_pol == from_set_pol && to_rst_pol == from_clr_pol);
-
-       log("  create mapping for %s from mapping for %s.\n", to.c_str(), from.c_str());
-       cell_mappings[to].cell_name = cell_mappings[from].cell_name;
-       cell_mappings[to].ports = cell_mappings[from].ports;
-
-       for (auto &it : cell_mappings[to].ports)
-       {
-               bool is_set_pin = it.second == 'S' || it.second == 's';
-               bool is_clr_pin = it.second == 'R' || it.second == 'r';
-
-               if (!is_set_pin && !is_clr_pin)
-                       continue;
-
-               if ((to_rst_val == '0' && is_set_pin) || (to_rst_val == '1' && is_clr_pin))
-               {
-                       // this is the unused set/clr pin -- deactivate it
-                       if (is_set_pin)
-                               it.second = (from_set_pol == 'P') == (it.second == 'S') ? '0' : '1';
-                       else
-                               it.second = (from_clr_pol == 'P') == (it.second == 'R') ? '0' : '1';
-               }
-               else
-               {
-                       // this is the used set/clr pin -- rename it to 'reset'
-                       if (it.second == 'S')
-                               it.second = 'R';
-                       if (it.second == 's')
-                               it.second = 'r';
-               }
-       }
-}
-
-static void map_adff_to_dff(IdString from, IdString to)
-{
-       if (!cell_mappings.count(from) || cell_mappings.count(to) > 0)
-               return;
-
-       char from_clk_pol = from[6];
-       char from_rst_pol = from[7];
-       char to_clk_pol = to[6];
-
-       log_assert(from_clk_pol == to_clk_pol);
-
-       log("  create mapping for %s from mapping for %s.\n", to.c_str(), from.c_str());
-       cell_mappings[to].cell_name = cell_mappings[from].cell_name;
-       cell_mappings[to].ports = cell_mappings[from].ports;
-
-       for (auto &it : cell_mappings[to].ports) {
-               if (it.second == 'S' || it.second == 'R')
-                       it.second = from_rst_pol == 'P' ? '0' : '1';
-               if (it.second == 's' || it.second == 'r')
-                       it.second = from_rst_pol == 'P' ? '1' : '0';
-       }
-}
-
-static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare_mode)
+static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module)
 {
        log("Mapping DFF cells in module `%s':\n", module->name.c_str());
 
@@ -499,7 +361,7 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare
                module->remove(cell);
 
                cell_mapping &cm = cell_mappings[cell_type];
-               RTLIL::Cell *new_cell = module->addCell(cell_name, prepare_mode ? cm.cell_name : cm.cell_name);
+               RTLIL::Cell *new_cell = module->addCell(cell_name, cm.cell_name);
 
                new_cell->set_src_attribute(src);
 
@@ -552,7 +414,7 @@ struct DfflibmapPass : public Pass {
        void help() override
        {
                log("\n");
-               log("    dfflibmap [-prepare] -liberty <file> [selection]\n");
+               log("    dfflibmap [-prepare] [-map-only] [-info] -liberty <file> [selection]\n");
                log("\n");
                log("Map internal flip-flop cells to the flip-flop cells in the technology\n");
                log("library specified in the given liberty file.\n");
@@ -562,15 +424,27 @@ struct DfflibmapPass : public Pass {
                log("\n");
                log("When called with -prepare, this command will convert the internal FF cells\n");
                log("to the internal cell types that best match the cells found in the given\n");
-               log("liberty file.\n");
+               log("liberty file, but won't actually map them to the target cells.\n");
+               log("\n");
+               log("When called with -map-only, this command will only map internal cell\n");
+               log("types that are already of exactly the right type to match the target\n");
+               log("cells, leaving remaining internal cells untouched.\n");
+               log("\n");
+               log("When called with -info, this command will only print the target cell\n");
+               log("list, along with their associated internal cell types, and the arguments");
+               log("that would be passed to the dfflegalize pass.  The design will not be\n");
+               log("changed.\n");
                log("\n");
        }
        void execute(std::vector<std::string> args, RTLIL::Design *design) override
        {
                log_header(design, "Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n");
+               log_push();
 
                std::string liberty_file;
                bool prepare_mode = false;
+               bool map_only_mode = false;
+               bool info_mode = false;
 
                size_t argidx;
                for (argidx = 1; argidx < args.size(); argidx++)
@@ -585,10 +459,28 @@ struct DfflibmapPass : public Pass {
                                prepare_mode = true;
                                continue;
                        }
+                       if (arg == "-map-only") {
+                               map_only_mode = true;
+                               continue;
+                       }
+                       if (arg == "-info") {
+                               info_mode = true;
+                               continue;
+                       }
                        break;
                }
                extra_args(args, argidx, design);
 
+               int modes = 0;
+               if (prepare_mode)
+                       modes++;
+               if (map_only_mode)
+                       modes++;
+               if (info_mode)
+                       modes++;
+               if (modes > 1)
+                       log_cmd_error("Only one of -prepare, -map-only, or -info options should be given!\n");
+
                if (liberty_file.empty())
                        log_cmd_error("Missing `-liberty liberty_file' option!\n");
 
@@ -599,74 +491,49 @@ struct DfflibmapPass : public Pass {
                LibertyParser libparser(f);
                f.close();
 
-               find_cell(libparser.ast, ID($_DFF_N_), false, false, false, false, prepare_mode);
-               find_cell(libparser.ast, ID($_DFF_P_), true, false, false, false, prepare_mode);
-
-               find_cell(libparser.ast, ID($_DFF_NN0_), false, true, false, false, prepare_mode);
-               find_cell(libparser.ast, ID($_DFF_NN1_), false, true, false, true, prepare_mode);
-               find_cell(libparser.ast, ID($_DFF_NP0_), false, true, true, false, prepare_mode);
-               find_cell(libparser.ast, ID($_DFF_NP1_), false, true, true, true, prepare_mode);
-               find_cell(libparser.ast, ID($_DFF_PN0_), true, true, false, false, prepare_mode);
-               find_cell(libparser.ast, ID($_DFF_PN1_), true, true, false, true, prepare_mode);
-               find_cell(libparser.ast, ID($_DFF_PP0_), true, true, true, false, prepare_mode);
-               find_cell(libparser.ast, ID($_DFF_PP1_), true, true, true, true, prepare_mode);
-
-               find_cell_sr(libparser.ast, ID($_DFFSR_NNN_), false, false, false, prepare_mode);
-               find_cell_sr(libparser.ast, ID($_DFFSR_NNP_), false, false, true, prepare_mode);
-               find_cell_sr(libparser.ast, ID($_DFFSR_NPN_), false, true, false, prepare_mode);
-               find_cell_sr(libparser.ast, ID($_DFFSR_NPP_), false, true, true, prepare_mode);
-               find_cell_sr(libparser.ast, ID($_DFFSR_PNN_), true, false, false, prepare_mode);
-               find_cell_sr(libparser.ast, ID($_DFFSR_PNP_), true, false, true, prepare_mode);
-               find_cell_sr(libparser.ast, ID($_DFFSR_PPN_), true, true, false, prepare_mode);
-               find_cell_sr(libparser.ast, ID($_DFFSR_PPP_), true, true, true, prepare_mode);
-
-               // try to implement as many cells as possible just by inverting
-               // the SET and RESET pins. If necessary, implement cell types
-               // by inverting both D and Q. Only invert clock pins if there
-               // is no other way of implementing the cell.
-               while (1)
-               {
-                       if (expand_cellmap("$_DFF_?*?_", "R") ||
-                                       expand_cellmap("$_DFFSR_?*?_", "S") ||
-                                       expand_cellmap("$_DFFSR_??*_", "R"))
-                               continue;
-
-                       if (expand_cellmap("$_DFF_??*_", "DQ"))
-                               continue;
-
-                       if (expand_cellmap("$_DFF_*_", "C") ||
-                                       expand_cellmap("$_DFF_*??_", "C") ||
-                                       expand_cellmap("$_DFFSR_*??_", "C"))
-                               continue;
-
-                       break;
-               }
-
-               map_sr_to_arst(ID($_DFFSR_NNN_), ID($_DFF_NN0_));
-               map_sr_to_arst(ID($_DFFSR_NNN_), ID($_DFF_NN1_));
-               map_sr_to_arst(ID($_DFFSR_NPP_), ID($_DFF_NP0_));
-               map_sr_to_arst(ID($_DFFSR_NPP_), ID($_DFF_NP1_));
-               map_sr_to_arst(ID($_DFFSR_PNN_), ID($_DFF_PN0_));
-               map_sr_to_arst(ID($_DFFSR_PNN_), ID($_DFF_PN1_));
-               map_sr_to_arst(ID($_DFFSR_PPP_), ID($_DFF_PP0_));
-               map_sr_to_arst(ID($_DFFSR_PPP_), ID($_DFF_PP1_));
-
-               map_adff_to_dff(ID($_DFF_NN0_), ID($_DFF_N_));
-               map_adff_to_dff(ID($_DFF_NN1_), ID($_DFF_N_));
-               map_adff_to_dff(ID($_DFF_NP0_), ID($_DFF_N_));
-               map_adff_to_dff(ID($_DFF_NP1_), ID($_DFF_N_));
-               map_adff_to_dff(ID($_DFF_PN0_), ID($_DFF_P_));
-               map_adff_to_dff(ID($_DFF_PN1_), ID($_DFF_P_));
-               map_adff_to_dff(ID($_DFF_PP0_), ID($_DFF_P_));
-               map_adff_to_dff(ID($_DFF_PP1_), ID($_DFF_P_));
+               find_cell(libparser.ast, ID($_DFF_N_), false, false, false, false);
+               find_cell(libparser.ast, ID($_DFF_P_), true, false, false, false);
+
+               find_cell(libparser.ast, ID($_DFF_NN0_), false, true, false, false);
+               find_cell(libparser.ast, ID($_DFF_NN1_), false, true, false, true);
+               find_cell(libparser.ast, ID($_DFF_NP0_), false, true, true, false);
+               find_cell(libparser.ast, ID($_DFF_NP1_), false, true, true, true);
+               find_cell(libparser.ast, ID($_DFF_PN0_), true, true, false, false);
+               find_cell(libparser.ast, ID($_DFF_PN1_), true, true, false, true);
+               find_cell(libparser.ast, ID($_DFF_PP0_), true, true, true, false);
+               find_cell(libparser.ast, ID($_DFF_PP1_), true, true, true, true);
+
+               find_cell_sr(libparser.ast, ID($_DFFSR_NNN_), false, false, false);
+               find_cell_sr(libparser.ast, ID($_DFFSR_NNP_), false, false, true);
+               find_cell_sr(libparser.ast, ID($_DFFSR_NPN_), false, true, false);
+               find_cell_sr(libparser.ast, ID($_DFFSR_NPP_), false, true, true);
+               find_cell_sr(libparser.ast, ID($_DFFSR_PNN_), true, false, false);
+               find_cell_sr(libparser.ast, ID($_DFFSR_PNP_), true, false, true);
+               find_cell_sr(libparser.ast, ID($_DFFSR_PPN_), true, true, false);
+               find_cell_sr(libparser.ast, ID($_DFFSR_PPP_), true, true, true);
 
                log("  final dff cell mappings:\n");
                logmap_all();
 
-               for (auto module : design->selected_modules())
-                       if (!module->get_blackbox_attribute())
-                               dfflibmap(design, module, prepare_mode);
+               if (!map_only_mode) {
+                       std::string dfflegalize_cmd = "dfflegalize";
+                       for (auto it : cell_mappings)
+                               dfflegalize_cmd += stringf(" -cell %s 01", it.first.c_str());
+                       dfflegalize_cmd += " t:$_DFF* t:$_SDFF*";
+                       if (info_mode) {
+                               log("dfflegalize command line: %s\n", dfflegalize_cmd.c_str());
+                       } else {
+                               Pass::call(design, dfflegalize_cmd);
+                       }
+               }
+
+               if (!prepare_mode && !info_mode) {
+                       for (auto module : design->selected_modules())
+                               if (!module->get_blackbox_attribute())
+                                       dfflibmap(design, module);
+               }
 
+               log_pop();
                cell_mappings.clear();
        }
 } DfflibmapPass;
index 7e2ed2370a8a329c489411ca8d99b4c882874136..61f19b09bc7284afa42241208dfc99d2c0be29f8 100755 (executable)
@@ -5,6 +5,6 @@ for x in *.lib; do
        echo "Running $x.."
     echo "read_verilog small.v" > test.ys
     echo "synth -top small" >> test.ys
-    echo "dfflibmap -liberty ${x}" >> test.ys
+    echo "dfflibmap -info -liberty ${x}" >> test.ys
        ../../yosys -ql ${x%.lib}.log -s test.ys
 done
diff --git a/tests/techmap/dfflibmap-sim.v b/tests/techmap/dfflibmap-sim.v
new file mode 100644 (file)
index 0000000..1788a68
--- /dev/null
@@ -0,0 +1,22 @@
+module dffn(input CLK, D, output reg Q, output QN);
+
+always @(negedge CLK)
+       Q <= D;
+
+assign QN = ~Q;
+
+endmodule
+
+module dffsr(input CLK, D, CLEAR, PRESET, output reg Q, output QN);
+
+always @(posedge CLK, posedge CLEAR, posedge PRESET)
+       if (CLEAR)
+               Q <= 0;
+       else if (PRESET)
+               Q <= 1;
+       else
+               Q <= D;
+
+assign QN = ~Q;
+
+endmodule
diff --git a/tests/techmap/dfflibmap.lib b/tests/techmap/dfflibmap.lib
new file mode 100644 (file)
index 0000000..ce46087
--- /dev/null
@@ -0,0 +1,55 @@
+library(test) {
+  /* D-type flip-flop with asynchronous reset and preset */
+  cell (dffn) {
+    area : 6;
+    ff("IQ", "IQN") {
+      next_state : "D";
+      clocked_on : "!CLK";
+    } 
+    pin(D) {
+      direction : input;
+    }
+    pin(CLK) {
+      direction : input;
+    }
+    pin(Q) {
+      direction: output;
+      function : "IQ";
+    }
+    pin(QN) {
+      direction: output;
+      function : "IQN";
+    } 
+  }
+  cell (dffsr) {
+    area : 6;
+    ff("IQ", "IQN") {
+      next_state : "D";
+      clocked_on : "CLK";
+      clear      : "CLEAR";
+      preset     : "PRESET";
+      clear_preset_var1 : L;
+      clear_preset_var2 : L;
+    } 
+    pin(D) {
+      direction : input;
+    }
+    pin(CLK) {
+      direction : input;
+    }
+    pin(CLEAR) {
+      direction : input;
+    }
+    pin(PRESET) {
+      direction : input;
+    }
+    pin(Q) {
+      direction: output;
+      function : "IQ";
+    }
+    pin(QN) {
+      direction: output;
+      function : "IQN";
+    } 
+  }
+}
diff --git a/tests/techmap/dfflibmap.ys b/tests/techmap/dfflibmap.ys
new file mode 100644 (file)
index 0000000..04477eb
--- /dev/null
@@ -0,0 +1,58 @@
+read_verilog -icells <<EOT
+
+module top(input C, D, S, R, output [9:0] Q);
+
+$_DFF_P_ ff0 (.C(C), .D(D), .Q(Q[0]));
+$_DFF_PP0_ ff1 (.C(C), .D(D), .R(R), .Q(Q[1]));
+$_DFF_PP1_ ff2 (.C(C), .D(D), .R(R), .Q(Q[2]));
+$_DFFSR_PPP_ ff3 (.C(C), .D(D), .R(R), .S(S), .Q(Q[3]));
+$_DFFSR_NNN_ ff4 (.C(C), .D(D), .R(R), .S(S), .Q(Q[4]));
+
+assign Q[9:5] = ~Q[4:0];
+
+endmodule
+
+EOT
+
+simplemap
+
+design -save orig
+
+#equiv_opt -map dfflibmap-sim.v -assert -multiclock dfflibmap -liberty dfflibmap.lib
+#equiv_opt -map dfflibmap-sim.v -assert -multiclock dfflibmap -prepare -liberty dfflibmap.lib
+dfflibmap -prepare -liberty dfflibmap.lib
+equiv_opt -map dfflibmap-sim.v -assert -multiclock dfflibmap -map-only -liberty dfflibmap.lib
+
+design -load orig
+dfflibmap -liberty dfflibmap.lib
+clean
+
+select -assert-count 4 t:$_NOT_
+select -assert-count 1 t:dffn
+select -assert-count 4 t:dffsr
+select -assert-none t:dffn t:dffsr t:$_NOT_ %% %n t:* %i
+
+design -load orig
+dfflibmap -prepare -liberty dfflibmap.lib
+
+select -assert-count 9 t:$_NOT_
+select -assert-count 1 t:$_DFF_N_
+select -assert-count 4 t:$_DFFSR_PPP_
+select -assert-none t:$_DFF_N_ t:$_DFFSR_PPP_ t:$_NOT_ %% %n t:* %i
+
+design -load orig
+dfflibmap -map-only -liberty dfflibmap.lib
+
+select -assert-count 5 t:$_NOT_
+select -assert-count 0 t:dffn
+select -assert-count 1 t:dffsr
+
+design -load orig
+dfflibmap -prepare -liberty dfflibmap.lib
+dfflibmap -map-only -liberty dfflibmap.lib
+clean
+
+select -assert-count 4 t:$_NOT_
+select -assert-count 1 t:dffn
+select -assert-count 4 t:dffsr
+select -assert-none t:dffn t:dffsr t:$_NOT_ %% %n t:* %i