|  | #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<std::string> ports2del_mult = {"load_acc", "subtract", "acc_fir", "dly_b"}; | 
|  | const std::vector<std::string> ports2del_mult_acc = {"acc_fir", "dly_b"}; | 
|  | const std::vector<std::string> ports2del_mult_add = {"dly_b"}; | 
|  | const std::vector<std::string> 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<std::string> 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<uint32_t, uint32_t> 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<std::string> 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 |