ql-qlf: k6n10f: DSP: adjust custom passes Signed-off-by: Pawel Czarnecki <pczarnecki@antmicro.com>
diff --git a/ql-qlf-plugin/ql-dsp-io-regs.cc b/ql-qlf-plugin/ql-dsp-io-regs.cc index 7161203..4befecf 100644 --- a/ql-qlf-plugin/ql-dsp-io-regs.cc +++ b/ql-qlf-plugin/ql-dsp-io-regs.cc
@@ -4,34 +4,56 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +#define MODE_BITS_REGISTER_INPUTS_ID 92 +#define MODE_BITS_OUTPUT_SELECT_START_ID 81 +#define MODE_BITS_OUTPUT_SELECT_WIDTH 3 + // ============================================================================ -const std::vector<std::string> ports2del_mult = {"feedback", "load_acc", "saturate_enable", "shift_right", "round", "subtract", "acc_fir", "dly_b"}; -const std::vector<std::string> ports2del_mult_add_acc = {"saturate_enable", "shift_right", "round", "acc_fir", "dly_b"}; +const std::vector<std::string> ports2del_mult = {"feedback", "load_acc", "subtract", "acc_fir", "dly_b"}; +const std::vector<std::string> ports2del_mult_add_acc = {"acc_fir", "dly_b"}; +const std::vector<std::string> ports2del_extension = {"saturate_enable", "shift_right", "round"}; void ql_dsp_io_regs_pass(RTLIL::Module *module) { for (auto cell : module->cells_) { std::string cell_type = cell.second->type.str(); - if (cell_type == RTLIL::escape_id("QL_DSP2")) { + if (cell_type == RTLIL::escape_id("QL_DSP2") || cell_type == RTLIL::escape_id("QL_DSP3")) { auto dsp = cell.second; bool del_clk = false; + bool use_dsp_cfg_params = cell_type == RTLIL::escape_id("QL_DSP3"); + + int reg_in_i; + int out_sel_i; // Get DSP configuration - const RTLIL::SigSpec *register_inputs; - register_inputs = &dsp->getPort(RTLIL::escape_id("register_inputs")); - if (!register_inputs) - log_error("register_inputs port not found!"); - auto reg_in_c = register_inputs->as_const(); - int reg_in_i = reg_in_c.as_int(); + if (use_dsp_cfg_params) { + // Read MODE_BITS at correct indexes + auto mode_bits = &dsp->getParam(RTLIL::escape_id("MODE_BITS")); + RTLIL::Const register_inputs; + register_inputs = mode_bits->bits.at(MODE_BITS_REGISTER_INPUTS_ID); + reg_in_i = register_inputs.as_int(); - const RTLIL::SigSpec *output_select; - output_select = &dsp->getPort(RTLIL::escape_id("output_select")); - if (!output_select) - log_error("output_select port not found!"); - auto out_sel_c = output_select->as_const(); - int out_sel_i = out_sel_c.as_int(); + RTLIL::Const output_select; + output_select = mode_bits->extract(MODE_BITS_OUTPUT_SELECT_START_ID, MODE_BITS_OUTPUT_SELECT_WIDTH); + out_sel_i = output_select.as_int(); + } else { + // Read dedicated configuration ports + const RTLIL::SigSpec *register_inputs; + register_inputs = &dsp->getPort(RTLIL::escape_id("register_inputs")); + if (!register_inputs) + log_error("register_inputs port not found!"); + auto reg_in_c = register_inputs->as_const(); + reg_in_i = reg_in_c.as_int(); + + const RTLIL::SigSpec *output_select; + output_select = &dsp->getPort(RTLIL::escape_id("output_select")); + if (!output_select) + log_error("output_select port not found!"); + auto out_sel_c = output_select->as_const(); + out_sel_i = out_sel_c.as_int(); + } // Build new type name std::string new_type = cell_type; @@ -79,6 +101,11 @@ ports2del.insert(ports2del.end(), ports2del_mult_add_acc.begin(), ports2del_mult_add_acc.end()); } + // Mark for deleton additional configuration ports + if (!use_dsp_cfg_params) { + ports2del.insert(ports2del.end(), ports2del_extension.begin(), ports2del_extension.end()); + } + for (auto portname : ports2del) { const RTLIL::SigSpec *port = &dsp->getPort(RTLIL::escape_id(portname)); if (!port)
diff --git a/ql-qlf-plugin/ql-dsp-macc.cc b/ql-qlf-plugin/ql-dsp-macc.cc index 4738634..7f865b5 100644 --- a/ql-qlf-plugin/ql-dsp-macc.cc +++ b/ql-qlf-plugin/ql-dsp-macc.cc
@@ -8,7 +8,9 @@ // ============================================================================ -void create_ql_macc_dsp(ql_dsp_macc_pm &pm) +bool use_dsp_cfg_params; + +static void create_ql_macc_dsp(ql_dsp_macc_pm &pm) { auto &st = pm.st_ql_dsp_macc; @@ -78,16 +80,21 @@ size_t tgt_b_width; size_t tgt_z_width; + string cell_base_name = "dsp_t1"; + string cell_size_name = ""; + string cell_cfg_name = ""; + string cell_full_name = ""; + if (min_width <= 2 && max_width <= 2 && z_width <= 4) { // Too narrow return; } else if (min_width <= 9 && max_width <= 10 && z_width <= 19) { - type = RTLIL::escape_id("dsp_t1_10x9x32"); + cell_size_name = "_10x9x32"; tgt_a_width = 10; tgt_b_width = 9; tgt_z_width = 19; } else if (min_width <= 18 && max_width <= 20 && z_width <= 38) { - type = RTLIL::escape_id("dsp_t1_20x18x64"); + cell_size_name = "_20x18x64"; tgt_a_width = 20; tgt_b_width = 18; tgt_z_width = 38; @@ -96,6 +103,14 @@ return; } + if (use_dsp_cfg_params) + cell_cfg_name = "_cfg_params"; + else + cell_cfg_name = "_cfg_ports"; + + cell_full_name = cell_base_name + cell_size_name + cell_cfg_name; + + type = RTLIL::escape_id(cell_full_name); log("Inferring MACC %zux%zu->%zu as %s from:\n", a_width, b_width, z_width, RTLIL::unescape_id(type).c_str()); for (auto cell : {st.mul, st.add, st.mux, st.ff}) { @@ -199,19 +214,26 @@ cell->setPort(RTLIL::escape_id("unsigned_a_i"), RTLIL::SigSpec(a_signed ? RTLIL::S0 : RTLIL::S1)); cell->setPort(RTLIL::escape_id("unsigned_b_i"), RTLIL::SigSpec(b_signed ? RTLIL::S0 : RTLIL::S1)); - // Connect config ports - cell->setPort(RTLIL::escape_id("saturate_enable_i"), RTLIL::SigSpec(RTLIL::S0)); - cell->setPort(RTLIL::escape_id("shift_right_i"), RTLIL::SigSpec(RTLIL::S0, 6)); - cell->setPort(RTLIL::escape_id("round_i"), RTLIL::SigSpec(RTLIL::S0)); - cell->setPort(RTLIL::escape_id("register_inputs_i"), RTLIL::SigSpec(RTLIL::S0)); + // Connect config bits + if (use_dsp_cfg_params) { + cell->setParam(RTLIL::escape_id("SATURATE_ENABLE"), RTLIL::Const(RTLIL::S0)); + cell->setParam(RTLIL::escape_id("SHIFT_RIGHT"), RTLIL::Const(RTLIL::S0, 6)); + cell->setParam(RTLIL::escape_id("ROUND"), RTLIL::Const(RTLIL::S0)); + cell->setParam(RTLIL::escape_id("REGISTER_INPUTS"), RTLIL::Const(RTLIL::S0)); + // 3 - output post acc; 1 - output pre acc + cell->setParam(RTLIL::escape_id("OUTPUT_SELECT"), out_ff ? RTLIL::Const(1, 3) : RTLIL::Const(3, 3)); + } else { + cell->setPort(RTLIL::escape_id("saturate_enable_i"), RTLIL::SigSpec(RTLIL::S0)); + cell->setPort(RTLIL::escape_id("shift_right_i"), RTLIL::SigSpec(RTLIL::S0, 6)); + cell->setPort(RTLIL::escape_id("round_i"), RTLIL::SigSpec(RTLIL::S0)); + cell->setPort(RTLIL::escape_id("register_inputs_i"), RTLIL::SigSpec(RTLIL::S0)); + // 3 - output post acc; 1 - output pre acc + cell->setPort(RTLIL::escape_id("output_select_i"), out_ff ? RTLIL::Const(1, 3) : RTLIL::Const(3, 3)); + } bool subtract = (st.add->type == RTLIL::escape_id("$sub")); cell->setPort(RTLIL::escape_id("subtract_i"), RTLIL::SigSpec(subtract ? RTLIL::S1 : RTLIL::S0)); - // 3 - output post acc - // 1 - output pre acc - cell->setPort(RTLIL::escape_id("output_select_i"), out_ff ? RTLIL::Const(1, 3) : RTLIL::Const(3, 3)); - // Mark the cells for removal pm.autoremove(st.mul); pm.autoremove(st.add); @@ -230,14 +252,25 @@ log("\n"); log(" ql_dsp_macc [options] [selection]\n"); log("\n"); + log(" -use_dsp_cfg_params\n"); + log(" By default use DSP blocks with configuration bits available at module ports.\n"); + log(" Specifying this forces usage of DSP block with configuration bits available as module parameters\n"); + log("\n"); } + void clear_flags() override { use_dsp_cfg_params = false; } + void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) override { log_header(a_Design, "Executing QL_DSP_MACC pass.\n"); size_t argidx; for (argidx = 1; argidx < a_Args.size(); argidx++) { + if (a_Args[argidx] == "-use_dsp_cfg_params") { + use_dsp_cfg_params = true; + continue; + } + break; } extra_args(a_Args, argidx, a_Design); @@ -246,6 +279,7 @@ ql_dsp_macc_pm(module, module->selected_cells()).run_ql_dsp_macc(create_ql_macc_dsp); } } + } QlDspMacc; PRIVATE_NAMESPACE_END
diff --git a/ql-qlf-plugin/ql-dsp-simd.cc b/ql-qlf-plugin/ql-dsp-simd.cc index edf432a..b2935a9 100644 --- a/ql-qlf-plugin/ql-dsp-simd.cc +++ b/ql-qlf-plugin/ql-dsp-simd.cc
@@ -24,6 +24,9 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +#define MODE_BITS_BASE_SIZE 80 +#define MODE_BITS_EXTENSION_SIZE 13 + // ============================================================================ struct QlDspSimdPass : public Pass { @@ -48,6 +51,9 @@ // Port connections dict<RTLIL::IdString, RTLIL::SigSpec> connections; + // Whether DSPs pass configuration bits through ports of parameters + bool use_cfg_params; + // TODO: Possibly include parameters here. For now we have just // connections. @@ -58,7 +64,7 @@ unsigned int hash() const { return connections.hash(); } - bool operator==(const DspConfig &ref) const { return connections == ref.connections; } + bool operator==(const DspConfig &ref) const { return connections == ref.connections && use_cfg_params == ref.use_cfg_params; } }; // .......................................... @@ -73,12 +79,14 @@ std::make_pair("unsigned_a_i", "unsigned_a"), std::make_pair("unsigned_b_i", "unsigned_b"), - std::make_pair("output_select_i", "output_select"), - std::make_pair("saturate_enable_i", "saturate_enable"), - std::make_pair("shift_right_i", "shift_right"), - std::make_pair("round_i", "round"), - std::make_pair("subtract_i", "subtract"), - std::make_pair("register_inputs_i", "register_inputs")}; + std::make_pair("subtract_i", "subtract")}; + // For QL_DSP2 expand with configuration ports + const std::vector<std::pair<std::string, std::string>> m_DspCfgPorts_expand = { + std::make_pair("output_select_i", "output_select"), std::make_pair("saturate_enable_i", "saturate_enable"), + std::make_pair("shift_right_i", "shift_right"), std::make_pair("round_i", "round"), std::make_pair("register_inputs_i", "register_inputs")}; + + // For QL_DSP3 use parameters instead + const std::vector<std::string> m_DspParams2Mode = {"OUTPUT_SELECT", "SATURATE_ENABLE", "SHIFT_RIGHT", "ROUND", "REGISTER_INPUTS"}; // DSP data ports and how to map them to ports of the target DSP cell const std::vector<std::pair<std::string, std::string>> m_DspDataPorts = { @@ -91,8 +99,12 @@ // Source DSP cell type (SISD) const std::string m_SisdDspType = "dsp_t1_10x9x32"; - // Target DSP cell type for the SIMD mode - const std::string m_SimdDspType = "QL_DSP2"; + // Suffix for DSP cell with configuration parameters + const std::string m_SisdDspType_cfg_params_suffix = "_cfg_params"; + + // Target DSP cell types for the SIMD mode + const std::string m_SimdDspType_cfg_ports = "QL_DSP2"; + const std::string m_SimdDspType_cfg_params = "QL_DSP3"; /// Temporary SigBit to SigBit helper map. SigMap m_SigMap; @@ -117,8 +129,8 @@ dict<DspConfig, std::vector<RTLIL::Cell *>> groups; for (auto cell : module->selected_cells()) { - // Check if this is a DSP cell - if (cell->type != RTLIL::escape_id(m_SisdDspType)) { + // Check if this is a DSP cell we are looking for (type starts with m_SisdDspType) + if (strncmp(cell->type.c_str(), RTLIL::escape_id(m_SisdDspType).c_str(), RTLIL::escape_id(m_SisdDspType).size()) != 0) { continue; } @@ -139,6 +151,7 @@ const auto &group = it.second; const auto &config = it.first; + bool use_cfg_params = config.use_cfg_params; // Ensure an even number size_t count = group.size(); if (count & 1) @@ -150,22 +163,32 @@ const RTLIL::Cell *dsp_b = group[i + 1]; std::string name = stringf("simd_%s_%s", RTLIL::unescape_id(dsp_a->name).c_str(), RTLIL::unescape_id(dsp_b->name).c_str()); + std::string SimdDspType; + + if (use_cfg_params) + SimdDspType = m_SimdDspType_cfg_params; + else + SimdDspType = m_SimdDspType_cfg_ports; log(" SIMD: %s (%s) + %s (%s) => %s (%s)\n", RTLIL::unescape_id(dsp_a->name).c_str(), RTLIL::unescape_id(dsp_a->type).c_str(), RTLIL::unescape_id(dsp_b->name).c_str(), RTLIL::unescape_id(dsp_b->type).c_str(), RTLIL::unescape_id(name).c_str(), - m_SimdDspType.c_str()); + SimdDspType.c_str()); // Create the new cell - RTLIL::Cell *simd = module->addCell(RTLIL::escape_id(name), RTLIL::escape_id(m_SimdDspType)); + RTLIL::Cell *simd = module->addCell(RTLIL::escape_id(name), RTLIL::escape_id(SimdDspType)); // Check if the target cell is known (important to know // its port widths) if (!simd->known()) { - log_error(" The target cell type '%s' is not known!", m_SimdDspType.c_str()); + log_error(" The target cell type '%s' is not known!", SimdDspType.c_str()); } + std::vector<std::pair<std::string, std::string>> DspCfgPorts = m_DspCfgPorts; + if (!use_cfg_params) + DspCfgPorts.insert(DspCfgPorts.end(), m_DspCfgPorts_expand.begin(), m_DspCfgPorts_expand.end()); + // Connect common ports - for (const auto &it : m_DspCfgPorts) { + for (const auto &it : DspCfgPorts) { auto sport = RTLIL::escape_id(it.first); auto dport = RTLIL::escape_id(it.second); @@ -216,12 +239,27 @@ mode_bits.insert(mode_bits.end(), val_a.begin(), val_a.end()); mode_bits.insert(mode_bits.end(), val_b.begin(), val_b.end()); } + long unsigned int mode_bits_size = MODE_BITS_BASE_SIZE; + if (use_cfg_params) { + // Add additional config parameters if necessary + mode_bits.push_back(RTLIL::S1); // MODE_BITS[80] == F_MODE : Enable fractured mode + for (const auto &it : m_DspParams2Mode) { + log_assert(dsp_a->getParam(RTLIL::escape_id(it)) == dsp_b->getParam(RTLIL::escape_id(it))); + auto param = dsp_a->getParam(RTLIL::escape_id(it)); + if (param.size() > 1) { + mode_bits.insert(mode_bits.end(), param.bits.begin(), param.bits.end()); + } else { + mode_bits.push_back(param.bits[0]); + } + } + mode_bits_size += MODE_BITS_EXTENSION_SIZE; + } else { + // Enable the fractured mode by connecting the control + // port. + simd->setPort(RTLIL::escape_id("f_mode"), RTLIL::S1); + } simd->setParam(RTLIL::escape_id("MODE_BITS"), RTLIL::Const(mode_bits)); - log_assert(mode_bits.size() == 80); - - // Enable the fractured mode by connecting the control - // port. - simd->setPort(RTLIL::escape_id("f_mode"), RTLIL::S1); + log_assert(mode_bits.size() == mode_bits_size); // Mark DSP parts for removal cellsToRemove.push_back(dsp_a); @@ -270,7 +308,18 @@ { DspConfig config; - for (const auto &it : m_DspCfgPorts) { + string cell_type = a_Cell->type.str(); + string suffix = m_SisdDspType_cfg_params_suffix; + + bool use_cfg_params = cell_type.size() >= suffix.size() && 0 == cell_type.compare(cell_type.size() - suffix.size(), suffix.size(), suffix); + + std::vector<std::pair<std::string, std::string>> DspCfgPorts = m_DspCfgPorts; + if (!use_cfg_params) + DspCfgPorts.insert(DspCfgPorts.end(), m_DspCfgPorts_expand.begin(), m_DspCfgPorts_expand.end()); + + config.use_cfg_params = use_cfg_params; + + for (const auto &it : DspCfgPorts) { auto port = RTLIL::escape_id(it.first); // Port unconnected
diff --git a/ql-qlf-plugin/synth_quicklogic.cc b/ql-qlf-plugin/synth_quicklogic.cc index 303b1de..5be10ec 100644 --- a/ql-qlf-plugin/synth_quicklogic.cc +++ b/ql-qlf-plugin/synth_quicklogic.cc
@@ -73,6 +73,10 @@ log(" By default use DSP blocks in output netlist.\n"); log(" do not use DSP blocks to implement multipliers and associated logic\n"); log("\n"); + log(" -use_dsp_cfg_params\n"); + log(" By default use DSP blocks with configuration bits available at module ports.\n"); + log(" Specifying this forces usage of DSP block with configuration bits available as module parameters\n"); + log("\n"); log(" -no_adder\n"); log(" By default use adder cells in output netlist.\n"); log(" Specifying this switch turns it off.\n"); @@ -95,7 +99,7 @@ log("\n"); } - string top_opt, edif_file, blif_file, family, currmodule, verilog_file; + string top_opt, edif_file, blif_file, family, currmodule, verilog_file, use_dsp_cfg_params; bool nodsp; bool inferAdder; bool inferBram; @@ -119,6 +123,7 @@ noffmap = false; nodsp = false; nosdff = false; + use_dsp_cfg_params = ""; } void execute(std::vector<std::string> args, RTLIL::Design *design) override @@ -164,6 +169,10 @@ nodsp = true; continue; } + if (args[argidx] == "-use_dsp_cfg_params") { + use_dsp_cfg_params = " -use_dsp_cfg_params"; + continue; + } if (args[argidx] == "-no_adder") { inferAdder = false; continue; @@ -223,8 +232,13 @@ void script() override { if (check_label("begin")) { + std::string family_path = " +/quicklogic/" + family; std::string readVelArgs; - readVelArgs = " +/quicklogic/" + family + "/cells_sim.v"; + + // Read simulation library + readVelArgs = family_path + "/cells_sim.v"; + if (family == "qlf_k6n10f") + readVelArgs += family_path + "/dsp_sim.v"; // Use -nomem2reg here to prevent Yosys from complaining about // some block ram cell models. After all the only part of the cells @@ -295,18 +309,22 @@ }; if (help_mode) { - run("wreduce t:$mul", " (for qlf_k6n10f if not -no_dsp)"); - run("ql_dsp_macc", " (for qlf_k6n10f if not -no_dsp)"); - run("techmap -map +/mul2dsp.v [...]", "(for qlf_k6n10f if not -no_dsp)"); - run("chtype -set $mul t:$__soft_mul", "(for qlf_k6n10f if not -no_dsp)"); + run("wreduce t:$mul", " (for qlf_k6n10f if not -no_dsp)"); + run("ql_dsp_macc" + use_dsp_cfg_params, "(for qlf_k6n10f if not -no_dsp)"); + run("techmap -map +/mul2dsp.v [...]", " (for qlf_k6n10f if not -no_dsp)"); + run("chtype -set $mul t:$__soft_mul", " (for qlf_k6n10f if not -no_dsp)"); run("techmap -map +/quicklogic/" + family + "/dsp_map.v", "(for qlf_k6n10f if not -no_dsp)"); + if (use_dsp_cfg_params == "") + run("techmap -map +/quicklogic/" + family + "/dsp_map.v -D USE_DSP_CFG_PARAMS=0", "(for qlf_k6n10f if not -no_dsp)"); + else + run("techmap -map +/quicklogic/" + family + "/dsp_map.v -D USE_DSP_CFG_PARAMS=1", "(for qlf_k6n10f if not -no_dsp)"); run("ql_dsp_simd ", "(for qlf_k6n10f if not -no_dsp)"); run("techmap -map +/quicklogic/" + family + "/dsp_final_map.v", "(for qlf_k6n10f if not -no_dsp)"); run("ql_dsp_io_regs"); } else if (!nodsp) { run("wreduce t:$mul"); - run("ql_dsp_macc"); + run("ql_dsp_macc" + use_dsp_cfg_params); for (const auto &rule : dsp_rules) { run(stringf("techmap -map +/mul2dsp.v " @@ -316,7 +334,10 @@ rule.a_maxwidth, rule.b_maxwidth, rule.a_minwidth, rule.b_minwidth, rule.type.c_str())); run("chtype -set $mul t:$__soft_mul"); } - run("techmap -map +/quicklogic/" + family + "/dsp_map.v"); + if (use_dsp_cfg_params == "") + run("techmap -map +/quicklogic/" + family + "/dsp_map.v -D USE_DSP_CFG_PARAMS=0"); + else + run("techmap -map +/quicklogic/" + family + "/dsp_map.v -D USE_DSP_CFG_PARAMS=1"); run("ql_dsp_simd"); run("techmap -map +/quicklogic/" + family + "/dsp_final_map.v"); run("ql_dsp_io_regs");