diff --git a/techlibs/quicklogic/ql_dsp_io_regs.cc b/techlibs/quicklogic/ql_dsp_io_regs.cc index 217a5aa55..efb1ad4d5 100644 --- a/techlibs/quicklogic/ql_dsp_io_regs.cc +++ b/techlibs/quicklogic/ql_dsp_io_regs.cc @@ -30,26 +30,24 @@ PRIVATE_NAMESPACE_BEGIN // ============================================================================ struct QlDspIORegs : public Pass { - - const std::vector ports2del_mult = {"load_acc", "subtract", "acc_fir", "dly_b"}; + const std::vector ports2del_mult = {"load_acc", "subtract", "acc_fir", "dly_b", + "saturate_enable", "shift_right", "round"}; const std::vector ports2del_mult_acc = {"acc_fir", "dly_b"}; - const std::vector ports2del_mult_add = {"dly_b"}; - const std::vector ports2del_extension = {"saturate_enable", "shift_right", "round"}; - /// Temporary SigBit to SigBit helper map. - SigMap m_SigMap; + SigMap sigmap; // .......................................... - QlDspIORegs() : Pass("ql_dsp_io_regs", "Changes types of QL_DSP2/QL_DSP3 depending on their configuration.") {} + QlDspIORegs() : Pass("ql_dsp_io_regs", "change types of QL_DSP2 depending on configuration") {} void help() override { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" ql_dsp_io_regs [options] [selection]\n"); log("\n"); - log("Looks for QL_DSP2/QL_DSP3 cells and changes their types depending\n"); - log("on their configuration.\n"); + log("This pass looks for QL_DSP2 cells and changes their cell type depending on their\n"); + log("configuration.\n"); } void execute(std::vector a_Args, RTLIL::Design *a_Design) override @@ -67,178 +65,92 @@ struct QlDspIORegs : public Pass { } } - // Returns a pair of mask and value describing constant bit connections of - // a SigSpec - std::pair get_constant_mask_value(const RTLIL::SigSpec *sigspec) - { - uint32_t mask = 0L; - uint32_t value = 0L; - - auto sigbits = sigspec->bits(); - for (ssize_t i = (sigbits.size() - 1); i >= 0; --i) { - auto other = m_SigMap(sigbits[i]); - - mask <<= 1; - value <<= 1; - - // A known constant - if (!other.is_wire() && other.data != RTLIL::Sx) { - mask |= 0x1; - value |= (other.data == RTLIL::S1); - } - } - - return std::make_pair(mask, value); - } - void ql_dsp_io_regs_pass(RTLIL::Module *module) { - // Setup the SigMap - m_SigMap.clear(); - m_SigMap.set(module); + sigmap.set(module); - for (auto cell : module->cells_) { - std::string cell_type = cell.second->type.str(); - if (cell_type == RTLIL::escape_id("QL_DSP2") || cell_type == RTLIL::escape_id("QL_DSP3")) { - auto dsp = cell.second; + for (auto cell : module->cells()) { + if (cell->type != ID(QL_DSP2)) + continue; - // If the cell does not have the "is_inferred" attribute set - // then don't touch it. - if (!dsp->has_attribute(RTLIL::escape_id("is_inferred")) || dsp->get_bool_attribute(RTLIL::escape_id("is_inferred")) == false) { - continue; - } + // If the cell does not have the "is_inferred" attribute set + // then don't touch it. + if (!cell->get_bool_attribute(ID(is_inferred))) + continue; - bool del_clk = true; - bool use_dsp_cfg_params = (cell_type == RTLIL::escape_id("QL_DSP3")); + // Get DSP configuration + for (auto cfg_port : {ID(register_inputs), ID(output_select)}) + if (!cell->hasPort(cfg_port) || sigmap(cell->getPort(cfg_port)).is_fully_const()) + log_error("Missing or non-constant '%s' port on DSP cell %s\n", + log_id(cfg_port), log_id(cell)); + int reg_in_i = sigmap(cell->getPort(ID(register_inputs))).as_int(); + int out_sel_i = sigmap(cell->getPort(ID(output_select))).as_int(); - int reg_in_i; - int out_sel_i; + // Get the feedback port + if (!cell->hasPort(ID(feedback))) + log_error("Missing 'feedback' port on %s", log_id(cell)); + SigSpec feedback = sigmap(cell->getPort(ID(feedback))); - // Get DSP configuration - 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(); + // Check the top two bits on 'feedback' to be constant zero. + // That's what we are expecting from inference. + if (feedback.extract(1, 2) != SigSpec(0, 2)) + log_error("Unexpected feedback configuration on %s\n", log_id(cell)); - 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(); + // Build new type name + std::string new_type = "QL_DSP2_MULT"; - 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(); - } + // Decide if we should be deleting the clock port + bool del_clk = true; - // Get the feedback port - const RTLIL::SigSpec *feedback; - feedback = &dsp->getPort(RTLIL::escape_id("feedback")); - if (!feedback) - log_error("feedback port not found!"); - - // Check if feedback is or can be set to 0 which implies MACC - auto feedback_con = get_constant_mask_value(feedback); - bool have_macc = (feedback_con.second == 0x0); - // log("mask=0x%08X value=0x%08X\n", consts.first, consts.second); - // log_error("=== END HERE ===\n"); - - // Build new type name - std::string new_type = cell_type; - new_type += "_MULT"; - - if (have_macc) { - switch (out_sel_i) { - case 1: - case 2: - case 3: - case 5: - case 7: - del_clk = false; - new_type += "ACC"; - break; - default: - break; - } - } else { - switch (out_sel_i) { - case 1: - case 2: - case 3: - case 5: - case 7: - new_type += "ADD"; - break; - default: - break; - } - } - - if (reg_in_i) { - del_clk = false; - new_type += "_REGIN"; - } - - if (out_sel_i > 3) { - del_clk = false; - new_type += "_REGOUT"; - } - - // Set new type name - dsp->type = RTLIL::IdString(new_type); - - std::vector ports2del; - - if (del_clk) - ports2del.push_back("clk"); - - switch (out_sel_i) { - case 0: - case 4: - case 6: - ports2del.insert(ports2del.end(), ports2del_mult.begin(), ports2del_mult.end()); - // Mark for deleton additional configuration ports - if (!use_dsp_cfg_params) { - ports2del.insert(ports2del.end(), ports2del_extension.begin(), ports2del_extension.end()); - } - break; + switch (out_sel_i) { case 1: case 2: case 3: case 5: case 7: - if (have_macc) { - ports2del.insert(ports2del.end(), ports2del_mult_acc.begin(), ports2del_mult_acc.end()); - } else { - ports2del.insert(ports2del.end(), ports2del_mult_add.begin(), ports2del_mult_add.end()); - } + del_clk = false; + new_type += "ACC"; break; - } + default: + break; + } - for (auto portname : ports2del) { - const RTLIL::SigSpec *port = &dsp->getPort(RTLIL::escape_id(portname)); - if (!port) - log_error("%s port not found!", portname.c_str()); - dsp->connections_.erase(RTLIL::escape_id(portname)); - } + if (reg_in_i) { + del_clk = false; + new_type += "_REGIN"; + } + + if (out_sel_i > 3) { + del_clk = false; + new_type += "_REGOUT"; + } + + // Set new type name + cell->type = RTLIL::IdString(new_type); + + std::vector ports2del; + + if (del_clk) + cell->unsetPort(ID(clk)); + + switch (out_sel_i) { + case 0: + case 4: + case 6: + for (auto port : ports2del_mult) + cell->unsetPort(port); + break; + case 1: + case 2: + case 3: + case 5: + case 7: + for (auto port : ports2del_mult_acc) + cell->unsetPort(port); + break; } } - - // Clear the sigmap - m_SigMap.clear(); } - } QlDspIORegs; PRIVATE_NAMESPACE_END diff --git a/techlibs/quicklogic/ql_dsp_simd.cc b/techlibs/quicklogic/ql_dsp_simd.cc index 5213aa1c4..153f3995f 100644 --- a/techlibs/quicklogic/ql_dsp_simd.cc +++ b/techlibs/quicklogic/ql_dsp_simd.cc @@ -24,39 +24,30 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN -#define MODE_BITS_BASE_SIZE 80 -#define MODE_BITS_EXTENSION_SIZE 13 // ============================================================================ struct QlDspSimdPass : public Pass { - QlDspSimdPass() : Pass("ql_dsp_simd", "Infers QuickLogic k6n10f DSP pairs that can operate in SIMD mode") {} + QlDspSimdPass() : Pass("ql_dsp_simd", "merge QuickLogic K6N10f DSP pairs to operate in SIMD mode") {} void help() override { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| log("\n"); log(" ql_dsp_simd [selection]\n"); log("\n"); - log(" This pass identifies k6n10f DSP cells with identical configuration\n"); - log(" and packs pairs of them together into other DSP cells that can\n"); - log(" perform SIMD operation.\n"); + log("This pass identifies K6N10f DSP cells with identical configuration and pack pairs\n"); + log("of them together into other DSP cells that can perform SIMD operation.\n"); } // .......................................... /// Describes DSP config unique to a whole DSP cell struct DspConfig { - // Port connections dict 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. - DspConfig() = default; DspConfig(const DspConfig &ref) = default; @@ -64,50 +55,50 @@ struct QlDspSimdPass : public Pass { unsigned int hash() const { return connections.hash(); } - bool operator==(const DspConfig &ref) const { return connections == ref.connections && use_cfg_params == ref.use_cfg_params; } + bool operator==(const DspConfig &ref) const { return connections == ref.connections; } }; // .......................................... // DSP control and config ports to consider and how to map them to ports // of the target DSP cell - const std::vector> m_DspCfgPorts = {std::make_pair("clock_i", "clk"), - std::make_pair("reset_i", "reset"), + const std::vector> m_DspCfgPorts = { + std::make_pair(ID(clock_i), ID(clk)), + std::make_pair(ID(reset_i), ID(reset)), + std::make_pair(ID(feedback_i), ID(feedback)), + std::make_pair(ID(load_acc_i), ID(load_acc)), + std::make_pair(ID(unsigned_a_i), ID(unsigned_a)), + std::make_pair(ID(unsigned_b_i), ID(unsigned_b)), + std::make_pair(ID(subtract_i), ID(subtract)), + std::make_pair(ID(output_select_i), ID(output_select)), + std::make_pair(ID(saturate_enable_i), ID(saturate_enable)), + std::make_pair(ID(shift_right_i), ID(shift_right)), + std::make_pair(ID(round_i), ID(round)), + std::make_pair(ID(register_inputs_i), ID(register_inputs)) + }; - std::make_pair("feedback_i", "feedback"), - std::make_pair("load_acc_i", "load_acc"), - std::make_pair("unsigned_a_i", "unsigned_a"), - std::make_pair("unsigned_b_i", "unsigned_b"), - - std::make_pair("subtract_i", "subtract")}; - // For QL_DSP2 expand with configuration ports - const std::vector> 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 m_DspParams2Mode = {"OUTPUT_SELECT", "SATURATE_ENABLE", "SHIFT_RIGHT", "ROUND", "REGISTER_INPUTS"}; + const int m_ModeBitsSize = 80; // DSP data ports and how to map them to ports of the target DSP cell - const std::vector> m_DspDataPorts = { - std::make_pair("a_i", "a"), std::make_pair("b_i", "b"), std::make_pair("acc_fir_i", "acc_fir"), - std::make_pair("z_o", "z"), std::make_pair("dly_b_o", "dly_b"), + const std::vector> m_DspDataPorts = { + std::make_pair(ID(a_i), ID(a)), + std::make_pair(ID(b_i), ID(b)), + std::make_pair(ID(acc_fir_i), ID(acc_fir)), + std::make_pair(ID(z_o), ID(z)), + std::make_pair(ID(dly_b_o), ID(dly_b)) }; // DSP parameters const std::vector m_DspParams = {"COEFF_3", "COEFF_2", "COEFF_1", "COEFF_0"}; // Source DSP cell type (SISD) - const std::string m_SisdDspType = "dsp_t1_10x9x32"; - // Suffix for DSP cell with configuration parameters - const std::string m_SisdDspType_cfg_params_suffix = "_cfg_params"; + const IdString m_SisdDspType = ID(dsp_t1_10x9x32); // 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"; + const IdString m_SimdDspType = ID(QL_DSP2); /// Temporary SigBit to SigBit helper map. - SigMap m_SigMap; + SigMap sigmap; // .......................................... @@ -120,38 +111,32 @@ struct QlDspSimdPass : public Pass { // Process modules for (auto module : a_Design->selected_modules()) { - // Setup the SigMap - m_SigMap.clear(); - m_SigMap.set(module); + sigmap.set(module); // Assemble DSP cell groups dict> groups; for (auto cell : module->selected_cells()) { - // 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) { + if (cell->type != m_SisdDspType) continue; - } // Skip if it has the (* keep *) attribute set - if (cell->has_keep_attr()) { + if (cell->has_keep_attr()) continue; - } // Add to a group const auto key = getDspConfig(cell); groups[key].push_back(cell); } - std::vector cellsToRemove; + std::vector cellsToRemove; // Map cell pairs to the target DSP SIMD cell for (const auto &it : groups) { 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) @@ -159,66 +144,45 @@ struct QlDspSimdPass : public Pass { // Map SIMD pairs for (size_t i = 0; i < count; i += 2) { - const RTLIL::Cell *dsp_a = group[i]; - const RTLIL::Cell *dsp_b = group[i + 1]; - - std::string name = stringf("simd%ld", i / 2); - 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(), - SimdDspType.c_str()); + Cell *dsp_a = group[i]; + Cell *dsp_b = group[i + 1]; // Create the new cell - RTLIL::Cell *simd = module->addCell(RTLIL::escape_id(name), RTLIL::escape_id(SimdDspType)); + Cell *simd = module->addCell(NEW_ID, m_SimdDspType); + + log(" SIMD: %s (%s) + %s (%s) => %s (%s)\n", log_id(dsp_a), log_id(dsp_a->type), + log_id(dsp_b), log_id(dsp_b->type), log_id(simd), log_id(simd->type)); // 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!", SimdDspType.c_str()); - } - - std::vector> DspCfgPorts = m_DspCfgPorts; - if (!use_cfg_params) - DspCfgPorts.insert(DspCfgPorts.end(), m_DspCfgPorts_expand.begin(), m_DspCfgPorts_expand.end()); + if (!simd->known()) + log_error(" The target cell type '%s' is not known!", log_id(simd)); // Connect common ports - for (const auto &it : DspCfgPorts) { - auto sport = RTLIL::escape_id(it.first); - auto dport = RTLIL::escape_id(it.second); - - simd->setPort(dport, config.connections.at(sport)); - } + for (const auto &it : m_DspCfgPorts) + simd->setPort(it.first, config.connections.at(it.second)); // Connect data ports for (const auto &it : m_DspDataPorts) { - auto sport = RTLIL::escape_id(it.first); - auto dport = RTLIL::escape_id(it.second); - size_t width; bool isOutput; - std::tie(width, isOutput) = getPortInfo(simd, dport); + std::tie(width, isOutput) = getPortInfo(simd, it.second); auto getConnection = [&](const RTLIL::Cell *cell) { RTLIL::SigSpec sigspec; - if (cell->hasPort(sport)) { - const auto &sig = cell->getPort(sport); + if (cell->hasPort(it.first)) { + const auto &sig = cell->getPort(it.first); sigspec.append(sig); } - if (sigspec.bits().size() < width / 2) { - if (isOutput) { - for (size_t i = 0; i < width / 2 - sigspec.bits().size(); ++i) { - sigspec.append(RTLIL::SigSpec()); - } - } else { - sigspec.append(RTLIL::SigSpec(RTLIL::Sx, width / 2 - sigspec.bits().size())); - } + + int padding = width / 2 - sigspec.bits().size(); + + if (padding) { + if (!isOutput) + sigspec.append(RTLIL::SigSpec(RTLIL::Sx, padding)); + else + sigspec.append(module->addWire(NEW_ID, padding)); } return sigspec; }; @@ -226,49 +190,31 @@ struct QlDspSimdPass : public Pass { RTLIL::SigSpec sigspec; sigspec.append(getConnection(dsp_a)); sigspec.append(getConnection(dsp_b)); - simd->setPort(dport, sigspec); + simd->setPort(it.second, sigspec); } // Concatenate FIR coefficient parameters into the single // MODE_BITS parameter - std::vector mode_bits; + Const mode_bits; for (const auto &it : m_DspParams) { - auto val_a = dsp_a->getParam(RTLIL::escape_id(it)); - auto val_b = dsp_b->getParam(RTLIL::escape_id(it)); + auto val_a = dsp_a->getParam(it); + auto val_b = dsp_b->getParam(it); - mode_bits.insert(mode_bits.end(), val_a.begin(), val_a.end()); - mode_bits.insert(mode_bits.end(), val_b.begin(), val_b.end()); + mode_bits.bits.insert(mode_bits.end(), val_a.begin(), val_a.end()); + mode_bits.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() == mode_bits_size); + + // Enable the fractured mode by connecting the control + // port. + simd->setPort(ID(f_mode), State::S1); + simd->setParam(ID(MODE_BITS), mode_bits); + log_assert(mode_bits.size() == m_ModeBitsSize); // Handle the "is_inferred" attribute. If one of the fragments // is not inferred mark the whole DSP as not inferred - bool is_inferred_a = - dsp_a->has_attribute(RTLIL::escape_id("is_inferred")) ? dsp_a->get_bool_attribute(RTLIL::escape_id("is_inferred")) : false; - bool is_inferred_b = - dsp_b->has_attribute(RTLIL::escape_id("is_inferred")) ? dsp_b->get_bool_attribute(RTLIL::escape_id("is_inferred")) : false; - - simd->set_bool_attribute(RTLIL::escape_id("is_inferred"), is_inferred_a && is_inferred_b); + bool is_inferred_a = dsp_a->get_bool_attribute(ID(is_inferred)); + bool is_inferred_b = dsp_b->get_bool_attribute(ID(is_inferred)); + simd->set_bool_attribute(ID(is_inferred), is_inferred_a && is_inferred_b); // Mark DSP parts for removal cellsToRemove.push_back(dsp_a); @@ -277,13 +223,9 @@ struct QlDspSimdPass : public Pass { } // Remove old cells - for (const auto &cell : cellsToRemove) { - module->remove(const_cast(cell)); - } + for (auto cell : cellsToRemove) + module->remove(cell); } - - // Clear - m_SigMap.clear(); } // .......................................... @@ -317,43 +259,18 @@ struct QlDspSimdPass : public Pass { { DspConfig config; - 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> 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); + for (const auto &it : m_DspCfgPorts) { + auto port = it.first; // Port unconnected - if (!a_Cell->hasPort(port)) { - config.connections[port] = RTLIL::SigSpec(RTLIL::Sx); + if (!a_Cell->hasPort(port)) continue; - } - // Get the port connection and map it to unique SigBits - const auto &orgSigSpec = a_Cell->getPort(port); - const auto &orgSigBits = orgSigSpec.bits(); - - RTLIL::SigSpec newSigSpec; - for (size_t i = 0; i < orgSigBits.size(); ++i) { - auto newSigBit = m_SigMap(orgSigBits[i]); - newSigSpec.append(newSigBit); - } - - // Store - config.connections[port] = newSigSpec; + config.connections[port] = sigmap(a_Cell->getPort(port)); } return config; } - } QlDspSimdPass; PRIVATE_NAMESPACE_END