/* * 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/sigtools.h" #include "kernel/yosys.h" 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 // ============================================================================ struct QlDspIORegs : public Pass { const std::vector ports2del_mult = {"load_acc", "subtract", "acc_fir", "dly_b"}; 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; // .......................................... QlDspIORegs() : Pass("ql_dsp_io_regs", "Changes types of QL_DSP2/QL_DSP3 depending on their configuration.") {} void help() override { 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"); } void execute(std::vector a_Args, RTLIL::Design *a_Design) override { log_header(a_Design, "Executing QL_DSP_IO_REGS pass.\n"); size_t argidx; for (argidx = 1; argidx < a_Args.size(); argidx++) { break; } extra_args(a_Args, argidx, a_Design); for (auto module : a_Design->selected_modules()) { ql_dsp_io_regs_pass(module); } } // 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); 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; // 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; } bool del_clk = true; bool use_dsp_cfg_params = (cell_type == RTLIL::escape_id("QL_DSP3")); int reg_in_i; int out_sel_i; // 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(); 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(); } // 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; 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()); } 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)); } } } // Clear the sigmap m_SigMap.clear(); } } QlDspIORegs; PRIVATE_NAMESPACE_END