Add actual DSP inference to ice40_dsp pass
authorClifford Wolf <clifford@clifford.at>
Sun, 17 Feb 2019 14:35:48 +0000 (15:35 +0100)
committerClifford Wolf <clifford@clifford.at>
Sun, 17 Feb 2019 14:35:48 +0000 (15:35 +0100)
Signed-off-by: Clifford Wolf <clifford@clifford.at>
passes/pmgen/ice40_dsp.cc
passes/pmgen/ice40_dsp.pmg
passes/pmgen/pmgen.py

index 78b5bad334e232466fae68c130244ad3585a04a4..7463d598f1fd1c8570177f51ca92efbfb2e2c84c 100644 (file)
 USING_YOSYS_NAMESPACE
 PRIVATE_NAMESPACE_BEGIN
 
+void create_ice40_dsp(ice40_dsp_pm &pm)
+{
+#if 0
+       log("\n");
+       log("ffA:   %s\n", log_id(pm.st.ffA, "--"));
+       log("ffB:   %s\n", log_id(pm.st.ffB, "--"));
+       log("mul:   %s\n", log_id(pm.st.mul, "--"));
+       log("ffY:   %s\n", log_id(pm.st.ffY, "--"));
+       log("addAB: %s\n", log_id(pm.st.addAB, "--"));
+       log("muxAB: %s\n", log_id(pm.st.muxAB, "--"));
+       log("ffS:   %s\n", log_id(pm.st.ffS, "--"));
+#endif
+
+       log("Checking %s.%s for iCE40 DSP inference.\n", log_id(pm.module), log_id(pm.st.mul));
+
+       if (GetSize(pm.st.sigA) > 16) {
+               log("  input A (%s) is too large (%d > 16).\n", log_signal(pm.st.sigA), GetSize(pm.st.sigA));
+               return;
+       }
+
+       if (GetSize(pm.st.sigB) > 16) {
+               log("  input B (%s) is too large (%d > 16).\n", log_signal(pm.st.sigB), GetSize(pm.st.sigB));
+               return;
+       }
+
+       if (GetSize(pm.st.sigS) > 32) {
+               log("  accumulator (%s) is too large (%d > 32).\n", log_signal(pm.st.sigS), GetSize(pm.st.sigS));
+               return;
+       }
+
+       if (GetSize(pm.st.sigY) > 32) {
+               log("  output (%s) is too large (%d > 32).\n", log_signal(pm.st.sigY), GetSize(pm.st.sigY));
+               return;
+       }
+
+       log("  replacing $mul with SB_MAC16 cell.\n");
+
+       bool mul_signed = pm.st.mul->getParam("\\A_SIGNED").as_bool();
+
+       Cell *cell = pm.module->addCell(NEW_ID, "\\SB_MAC16");
+       pm.module->swap_names(cell, pm.st.mul);
+
+       // SB_MAC16 Input Interface
+
+       SigSpec A = pm.st.sigA;
+       A.extend_u0(16, mul_signed);
+
+       SigSpec B = pm.st.sigB;
+       B.extend_u0(16, mul_signed);
+
+       SigSpec CD;
+       if (pm.st.muxA)
+               CD = pm.st.muxA->getPort("\\B");
+       if (pm.st.muxB)
+               CD = pm.st.muxB->getPort("\\A");
+       CD.extend_u0(32, mul_signed);
+
+       cell->setPort("\\A", A);
+       cell->setPort("\\B", B);
+       cell->setPort("\\C", CD.extract(0, 16));
+       cell->setPort("\\D", CD.extract(16, 16));
+
+       cell->setParam("\\A_REG", pm.st.ffA ? State::S0 : State::S1);
+       cell->setParam("\\B_REG", pm.st.ffB ? State::S0 : State::S1);
+
+       cell->setPort("\\AHOLD", State::S0);
+       cell->setPort("\\BHOLD", State::S0);
+       cell->setPort("\\CHOLD", State::S0);
+       cell->setPort("\\DHOLD", State::S0);
+
+       cell->setPort("\\IRSTTOP", State::S0);
+       cell->setPort("\\IRSTBOT", State::S0);
+
+       if (pm.st.clock_vld)
+       {
+               cell->setPort("\\CLK", pm.st.clock);
+               cell->setPort("\\CE", State::S1);
+               cell->setParam("\\NEG_TRIGGER", pm.st.clock_pol ? State::S0 : State::S1);
+
+               log("  clock: %s (%s)", log_signal(pm.st.clock), pm.st.clock_pol ? "posedge" : "negedge");
+
+               if (pm.st.ffA)
+                       log(" ffA:%s", log_id(pm.st.ffA));
+
+               if (pm.st.ffB)
+                       log(" ffB:%s", log_id(pm.st.ffB));
+
+               if (pm.st.ffY)
+                       log(" ffY:%s", log_id(pm.st.ffY));
+
+               if (pm.st.ffS)
+                       log(" ffS:%s", log_id(pm.st.ffS));
+
+               log("\n");
+       }
+       else
+       {
+               cell->setPort("\\CLK", State::S0);
+               cell->setPort("\\CE", State::S0);
+               cell->setParam("\\NEG_TRIGGER", State::S0);
+       }
+
+       // SB_MAC16 Cascade Interface
+
+       cell->setPort("\\SIGNEXTIN", State::Sx);
+       cell->setPort("\\SIGNEXTOUT", pm.module->addWire(NEW_ID));
+
+       cell->setPort("\\CI", State::Sx);
+       cell->setPort("\\CO", pm.module->addWire(NEW_ID));
+
+       cell->setPort("\\ACCUMCI", State::Sx);
+       cell->setPort("\\ACCUMCO", pm.module->addWire(NEW_ID));
+
+       // SB_MAC16 Output Interface
+
+       SigSpec O = pm.st.ffS ? pm.st.sigS : pm.st.sigY;
+       if (GetSize(O) < 32)
+               O.append(pm.module->addWire(NEW_ID, 32-GetSize(O)));
+
+       cell->setPort("\\O", O);
+
+       if (pm.st.addAB) {
+               log("  accumulator %s (%s)\n", log_id(pm.st.addAB), log_id(pm.st.addAB->type));
+               cell->setPort("\\ADDSUBTOP", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
+               cell->setPort("\\ADDSUBBOT", pm.st.addAB->type == "$add" ? State::S0 : State::S1);
+       } else {
+               cell->setPort("\\ADDSUBTOP", State::S0);
+               cell->setPort("\\ADDSUBBOT", State::S0);
+       }
+
+       cell->setPort("\\ORTSTOP", State::S0);
+       cell->setPort("\\ORTSBOT", State::S0);
+
+       cell->setPort("\\OHOLDTOP", State::S0);
+       cell->setPort("\\OHOLDBOT", State::S0);
+
+       SigSpec acc_reset = State::S0;
+       if (pm.st.muxA)
+               acc_reset = pm.st.muxA->getPort("\\S");
+       if (pm.st.muxB)
+               acc_reset = pm.module->Not(NEW_ID, pm.st.muxB->getPort("\\S"));
+
+       cell->setPort("\\OLOADTOP", acc_reset);
+       cell->setPort("\\OLOADBOT", acc_reset);
+
+       // SB_MAC16 Remaining Parameters
+
+       cell->setParam("\\C_REG", State::S0);
+       cell->setParam("\\D_REG", State::S0);
+
+       cell->setParam("\\TOP_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
+       cell->setParam("\\BOT_8x8_MULT_REG", pm.st.ffY ? State::S1 : State::S0);
+       cell->setParam("\\PIPELINE_16X16_MULT_REG1", pm.st.ffY ? State::S1 : State::S0);
+       cell->setParam("\\PIPELINE_16X16_MULT_REG2", State::S0);
+
+       cell->setParam("\\TOPOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
+       cell->setParam("\\TOPADDSUB_LOWERINPUT", Const(2, 2));
+       cell->setParam("\\TOPADDSUB_UPPERINPUT", State::S0);
+       cell->setParam("\\TOPADDSUB_CARRYSELECT", Const(3, 2));
+
+       cell->setParam("\\BOTOUTPUT_SELECT", Const(pm.st.ffS ? 1 : 3, 2));
+       cell->setParam("\\BOTADDSUB_LOWERINPUT", Const(2, 2));
+       cell->setParam("\\BOTADDSUB_UPPERINPUT", State::S0);
+       cell->setParam("\\BOTADDSUB_CARRYSELECT", Const(0, 2));
+
+       cell->setParam("\\MODE_8x8", State::S0);
+       cell->setParam("\\A_SIGNED", mul_signed ? State::S1 : State::S0);
+       cell->setParam("\\B_SIGNED", mul_signed ? State::S1 : State::S0);
+
+       pm.autoremove(pm.st.mul);
+       pm.autoremove(pm.st.ffY);
+       pm.autoremove(pm.st.ffS);
+}
+
 struct Ice40DspPass : public Pass {
        Ice40DspPass() : Pass("ice40_dsp", "iCE40: map multipliers") { }
        void help() YS_OVERRIDE
@@ -32,7 +206,7 @@ struct Ice40DspPass : public Pass {
                log("\n");
                log("    ice40_dsp [options] [selection]\n");
                log("\n");
-               log("Map multipliers and iCE40 DSP resources.\n");
+               log("Map multipliers and multiply-accumulate blocks to iCE40 DSP resources.\n");
                log("\n");
        }
        void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
@@ -51,26 +225,7 @@ struct Ice40DspPass : public Pass {
                extra_args(args, argidx, design);
 
                for (auto module : design->selected_modules())
-               {
-                       ice40_dsp_pm pm(module, module->selected_cells());
-                       pm.run([&]()
-                       {
-                               log("\n");
-                               log("ffA:   %s\n", log_id(pm.st.ffA, "--"));
-                               log("ffB:   %s\n", log_id(pm.st.ffB, "--"));
-                               log("mul:   %s\n", log_id(pm.st.mul, "--"));
-                               log("ffY:   %s\n", log_id(pm.st.ffY, "--"));
-                               log("addAB: %s\n", log_id(pm.st.addAB, "--"));
-                               log("muxAB: %s\n", log_id(pm.st.muxAB, "--"));
-                               log("ffS:   %s\n", log_id(pm.st.ffS, "--"));
-
-                               pm.blacklist(pm.st.mul);
-                               pm.blacklist(pm.st.ffA);
-                               pm.blacklist(pm.st.ffB);
-                               pm.blacklist(pm.st.ffY);
-                               pm.blacklist(pm.st.ffS);
-                       });
-               }
+                       ice40_dsp_pm(module, module->selected_cells()).run(create_ice40_dsp);
        }
 } Ice40DspPass;
 
index 1370cb66aa57cfb965d9860684cfe72e95197a33..58e1ce0e0455c5e867409e326e4fe65543456c28 100644 (file)
@@ -63,7 +63,7 @@ code sigY clock clock_pol clock_vld
        sigY = port(mul, \Y);
 
        if (ffY) {
-               sigY = port(ffY, \D);
+               sigY = port(ffY, \Q);
                SigBit c = port(ffY, \CLK).as_bit();
                bool cp = param(ffY, \CLK_POLARITY).as_bool();
 
@@ -77,7 +77,7 @@ code sigY clock clock_pol clock_vld
 endcode
 
 match addA
-       select addA->type.in($add, $sub)
+       select addA->type.in($add)
        select nusers(port(addA, \A)) == 2
        index <SigSpec> port(addA, \A) === sigY
        optional
@@ -134,3 +134,17 @@ match ffS
        index <SigSpec> port(ffS, \D) === port(muxAB, \Y)
        index <SigSpec> port(ffS, \Q) === sigS
 endmatch
+
+code clock clock_pol clock_vld
+       if (ffS) {
+               SigBit c = port(ffS, \CLK).as_bit();
+               bool cp = param(ffS, \CLK_POLARITY).as_bool();
+
+               if (clock_vld && (c != clock || cp != clock_pol))
+                       reject;
+
+               clock = c;
+               clock_pol = cp;
+               clock_vld = true;
+       }
+endcode
index a59cf5a8135b9dce6359b71666c62d90bb7e1be8..e688a4567f52923915804be2acf8ef135a024d7d 100644 (file)
@@ -203,6 +203,7 @@ with open("%s_pm.h" % prefix, "w") as f:
             print("  dict<index_{}_key_type, vector<Cell*>> index_{};".format(index, index), file=f)
     print("  dict<SigBit, pool<Cell*>> sigusers;", file=f)
     print("  pool<Cell*> blacklist_cells;", file=f)
+    print("  pool<Cell*> autoremove_cells;", file=f)
     print("  bool blacklist_dirty;", file=f)
     print("  int rollback;", file=f)
     print("", file=f)
@@ -244,6 +245,15 @@ with open("%s_pm.h" % prefix, "w") as f:
     print("  }", file=f)
     print("", file=f)
 
+    print("  void autoremove(Cell *cell) {", file=f)
+    print("    if (cell != nullptr) {", file=f)
+    print("      if (blacklist_cells.insert(cell).second)", file=f)
+    print("        blacklist_dirty = true;", file=f)
+    print("      autoremove_cells.insert(cell);", file=f)
+    print("    }", file=f)
+    print("  }", file=f)
+    print("", file=f)
+
     print("  void check_blacklist() {", file=f)
     print("    if (!blacklist_dirty)", file=f)
     print("      return;", file=f)
@@ -308,7 +318,13 @@ with open("%s_pm.h" % prefix, "w") as f:
     print("  }", file=f)
     print("", file=f)
 
-    print("  void run(std::function<void()> on_accept_f) {{".format(prefix), file=f)
+    print("  ~{}_pm() {{".format(prefix), file=f)
+    print("    for (auto cell : autoremove_cells)", file=f)
+    print("      module->remove(cell);", file=f)
+    print("  }", file=f)
+    print("", file=f)
+
+    print("  void run(std::function<void()> on_accept_f) {", file=f)
     print("    on_accept = on_accept_f;", file=f)
     print("    rollback = 0;", file=f)
     print("    blacklist_dirty = false;", file=f)
@@ -321,6 +337,11 @@ with open("%s_pm.h" % prefix, "w") as f:
     print("  }", file=f)
     print("", file=f)
 
+    print("  void run(std::function<void({}_pm&)> on_accept_f) {{".format(prefix), file=f)
+    print("    run([&](){on_accept_f(*this);});", file=f)
+    print("  }", file=f)
+    print("", file=f)
+
     for index in range(len(blocks)):
         block = blocks[index]