| #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 |