<commit_before>/*
 * Copyright 2020-2022 F4PGA Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "kernel/log.h"
#include "kernel/register.h"
#include "kernel/rtlil.h"
#include "kernel/sigtools.h"

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") {}

    void help() override
    {
        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");
    }

    // ..........................................

    /// Describes DSP config unique to a whole DSP cell
    struct DspConfig {

        // 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.

        DspConfig() = default;

        DspConfig(const DspConfig &ref) = default;
        DspConfig(DspConfig &&ref) = default;

        unsigned int hash() const { return connections.hash(); }

        bool operator==(const DspConfig &ref) const { return connections == ref.connections && use_cfg_params == ref.use_cfg_params; }
    };

    // ..........................................

    // DSP control and config ports to consider and how to map them to ports
    // of the target DSP cell
    const std::vector<std::pair<std::string, std::string>> m_DspCfgPorts = {std::make_pair("clock_i", "clk"),
                                                                            std::make_pair("reset_i", "reset"),

                                                                            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<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 = {
      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"),
    };

    // DSP parameters
    const std::vector<std::string> 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";

    // 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;

    // ..........................................

    void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) override
    {
        log_header(a_Design, "Executing QL_DSP_SIMD pass.\n");

        // Parse args
        extra_args(a_Args, 1, a_Design);

        // Process modules
        for (auto module : a_Design->selected_modules()) {

            // Setup the SigMap
            m_SigMap.clear();
            m_SigMap.set(module);

            // Assemble DSP cell groups
            dict<DspConfig, std::vector<RTLIL::Cell *>> 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) {
                    continue;
                }

                // Skip if it has the (* keep *) attribute set
                if (cell->has_keep_attr()) {
                    continue;
                }

                // Add to a group
                const auto key = getDspConfig(cell);
                groups[key].push_back(cell);
            }

            std::vector<const RTLIL::Cell *> 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)
                    count--;

                // 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_%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(),
                        SimdDspType.c_str());

                    // Create the new cell
                    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()) {<commit_msg>ql-qlf: k6n10f: ql_dsp_simd: change naming scheme<commit_after>/*
 * Copyright 2020-2022 F4PGA Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "kernel/log.h"
#include "kernel/register.h"
#include "kernel/rtlil.h"
#include "kernel/sigtools.h"

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") {}

    void help() override
    {
        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");
    }

    // ..........................................

    /// Describes DSP config unique to a whole DSP cell
    struct DspConfig {

        // 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.

        DspConfig() = default;

        DspConfig(const DspConfig &ref) = default;
        DspConfig(DspConfig &&ref) = default;

        unsigned int hash() const { return connections.hash(); }

        bool operator==(const DspConfig &ref) const { return connections == ref.connections && use_cfg_params == ref.use_cfg_params; }
    };

    // ..........................................

    // DSP control and config ports to consider and how to map them to ports
    // of the target DSP cell
    const std::vector<std::pair<std::string, std::string>> m_DspCfgPorts = {std::make_pair("clock_i", "clk"),
                                                                            std::make_pair("reset_i", "reset"),

                                                                            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<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 = {
      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"),
    };

    // DSP parameters
    const std::vector<std::string> 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";

    // 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;

    // ..........................................

    void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) override
    {
        log_header(a_Design, "Executing QL_DSP_SIMD pass.\n");

        // Parse args
        extra_args(a_Args, 1, a_Design);

        // Process modules
        for (auto module : a_Design->selected_modules()) {

            // Setup the SigMap
            m_SigMap.clear();
            m_SigMap.set(module);

            // Assemble DSP cell groups
            dict<DspConfig, std::vector<RTLIL::Cell *>> 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) {
                    continue;
                }

                // Skip if it has the (* keep *) attribute set
                if (cell->has_keep_attr()) {
                    continue;
                }

                // Add to a group
                const auto key = getDspConfig(cell);
                groups[key].push_back(cell);
            }

            std::vector<const RTLIL::Cell *> 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)
                    count--;

                // 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());

                    // Create the new cell
                    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()) {
{'fraction_non_alphanumeric': 0.13893280632411067, 'fraction_numerical': 0.006324110671936759, 'mean_word_length': 1.9154983675821011, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 36, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7459549', 'n_tokens_mistral': 4560, 'n_tokens_neox': 4332, 'n_words': 1117}