From 5a853ed46cd3a41df9da4c8206f9416748788487 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Sun, 17 Feb 2019 15:35:48 +0100 Subject: [PATCH] Add actual DSP inference to ice40_dsp pass Signed-off-by: Clifford Wolf --- passes/pmgen/ice40_dsp.cc | 197 +++++++++++++++++++++++++++++++++---- passes/pmgen/ice40_dsp.pmg | 18 +++- passes/pmgen/pmgen.py | 23 ++++- 3 files changed, 214 insertions(+), 24 deletions(-) diff --git a/passes/pmgen/ice40_dsp.cc b/passes/pmgen/ice40_dsp.cc index 78b5bad33..7463d598f 100644 --- a/passes/pmgen/ice40_dsp.cc +++ b/passes/pmgen/ice40_dsp.cc @@ -24,6 +24,180 @@ 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 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; diff --git a/passes/pmgen/ice40_dsp.pmg b/passes/pmgen/ice40_dsp.pmg index 1370cb66a..58e1ce0e0 100644 --- a/passes/pmgen/ice40_dsp.pmg +++ b/passes/pmgen/ice40_dsp.pmg @@ -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 port(addA, \A) === sigY optional @@ -134,3 +134,17 @@ match ffS index port(ffS, \D) === port(muxAB, \Y) index 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 diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py index a59cf5a81..e688a4567 100644 --- a/passes/pmgen/pmgen.py +++ b/passes/pmgen/pmgen.py @@ -203,6 +203,7 @@ with open("%s_pm.h" % prefix, "w") as f: print(" dict> index_{};".format(index, index), file=f) print(" dict> sigusers;", file=f) print(" pool blacklist_cells;", file=f) + print(" pool 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 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 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 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] -- 2.30.2