| /* | |
| * yosys -- Yosys Open SYnthesis Suite | |
| * | |
| * Copyright (C) 2012 Clifford Wolf <clifford@clifford.at> | |
| * (C) 2019 Eddie Hung <eddie@fpgeh.com> | |
| * | |
| * Permission to use, copy, modify, and/or distribute this software for any | |
| * purpose with or without fee is hereby granted, provided that the above | |
| * copyright notice and this permission notice appear in all copies. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
| * | |
| */ | |
| #include "kernel/yosys.h" | |
| #include "kernel/sigtools.h" | |
| USING_YOSYS_NAMESPACE | |
| PRIVATE_NAMESPACE_BEGIN | |
| #include "passes/pmgen/xilinx_srl_pm.h" | |
| void run_fixed(xilinx_srl_pm &pm) | |
| { | |
| auto &st = pm.st_fixed; | |
| auto &ud = pm.ud_fixed; | |
| log("Found fixed chain of length %d (%s):\n", GetSize(ud.longest_chain), log_id(st.first->type)); | |
| SigSpec initval; | |
| for (auto cell : ud.longest_chain) { | |
| log_debug(" %s\n", log_id(cell)); | |
| if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) { | |
| SigBit Q = cell->getPort(ID(Q)); | |
| log_assert(Q.wire); | |
| auto it = Q.wire->attributes.find(ID(init)); | |
| if (it != Q.wire->attributes.end()) { | |
| auto &i = it->second[Q.offset]; | |
| initval.append(i); | |
| i = State::Sx; | |
| } | |
| else | |
| initval.append(State::Sx); | |
| } | |
| else if (cell->type.in(ID(FDRE), ID(FDRE_1))) { | |
| if (cell->parameters.at(ID(INIT), State::S0).as_bool()) | |
| initval.append(State::S1); | |
| else | |
| initval.append(State::S0); | |
| } | |
| else | |
| log_abort(); | |
| pm.autoremove(cell); | |
| } | |
| auto first_cell = ud.longest_chain.back(); | |
| auto last_cell = ud.longest_chain.front(); | |
| Cell *c = pm.module->addCell(NEW_ID, ID($__XILINX_SHREG_)); | |
| pm.module->swap_names(c, first_cell); | |
| if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID(FDRE), ID(FDRE_1))) { | |
| c->setParam(ID(DEPTH), GetSize(ud.longest_chain)); | |
| c->setParam(ID(INIT), initval.as_const()); | |
| if (first_cell->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_))) | |
| c->setParam(ID(CLKPOL), 1); | |
| else if (first_cell->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_), ID(FDRE_1))) | |
| c->setParam(ID(CLKPOL), 0); | |
| else if (first_cell->type.in(ID(FDRE))) { | |
| if (!first_cell->parameters.at(ID(IS_C_INVERTED), State::S0).as_bool()) | |
| c->setParam(ID(CLKPOL), 1); | |
| else | |
| c->setParam(ID(CLKPOL), 0); | |
| } | |
| else | |
| log_abort(); | |
| if (first_cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_))) | |
| c->setParam(ID(ENPOL), 1); | |
| else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_))) | |
| c->setParam(ID(ENPOL), 0); | |
| else | |
| c->setParam(ID(ENPOL), 2); | |
| c->setPort(ID(C), first_cell->getPort(ID(C))); | |
| c->setPort(ID(D), first_cell->getPort(ID(D))); | |
| c->setPort(ID(Q), last_cell->getPort(ID(Q))); | |
| c->setPort(ID(L), GetSize(ud.longest_chain)-1); | |
| if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_))) | |
| c->setPort(ID(E), State::S1); | |
| else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) | |
| c->setPort(ID(E), first_cell->getPort(ID(E))); | |
| else if (first_cell->type.in(ID(FDRE), ID(FDRE_1))) | |
| c->setPort(ID(E), first_cell->getPort(ID(CE))); | |
| else | |
| log_abort(); | |
| } | |
| else | |
| log_abort(); | |
| log(" -> %s (%s)\n", log_id(c), log_id(c->type)); | |
| } | |
| void run_variable(xilinx_srl_pm &pm) | |
| { | |
| auto &st = pm.st_variable; | |
| auto &ud = pm.ud_variable; | |
| log("Found variable chain of length %d (%s):\n", GetSize(ud.chain), log_id(st.first->type)); | |
| SigSpec initval; | |
| for (const auto &i : ud.chain) { | |
| auto cell = i.first; | |
| auto slice = i.second; | |
| log_debug(" %s\n", log_id(cell)); | |
| if (cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID($dff), ID($dffe))) { | |
| SigBit Q = cell->getPort(ID(Q))[slice]; | |
| log_assert(Q.wire); | |
| auto it = Q.wire->attributes.find(ID(init)); | |
| if (it != Q.wire->attributes.end()) { | |
| auto &i = it->second[Q.offset]; | |
| initval.append(i); | |
| i = State::Sx; | |
| } | |
| else | |
| initval.append(State::Sx); | |
| } | |
| else | |
| log_abort(); | |
| } | |
| pm.autoremove(st.shiftx); | |
| auto first_cell = ud.chain.back().first; | |
| auto first_slice = ud.chain.back().second; | |
| Cell *c = pm.module->addCell(NEW_ID, ID($__XILINX_SHREG_)); | |
| pm.module->swap_names(c, first_cell); | |
| if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_), ID($dff), ID($dffe))) { | |
| c->setParam(ID(DEPTH), GetSize(ud.chain)); | |
| c->setParam(ID(INIT), initval.as_const()); | |
| Const clkpol, enpol; | |
| if (first_cell->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_))) | |
| clkpol = 1; | |
| else if (first_cell->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_))) | |
| clkpol = 0; | |
| else if (first_cell->type.in(ID($dff), ID($dffe))) | |
| clkpol = first_cell->getParam(ID(CLK_POLARITY)); | |
| else | |
| log_abort(); | |
| if (first_cell->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_))) | |
| enpol = 1; | |
| else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_))) | |
| enpol = 0; | |
| else if (first_cell->type.in(ID($dffe))) | |
| enpol = first_cell->getParam(ID(EN_POLARITY)); | |
| else | |
| enpol = 2; | |
| c->setParam(ID(CLKPOL), clkpol); | |
| c->setParam(ID(ENPOL), enpol); | |
| if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) | |
| c->setPort(ID(C), first_cell->getPort(ID(C))); | |
| else if (first_cell->type.in(ID($dff), ID($dffe))) | |
| c->setPort(ID(C), first_cell->getPort(ID(CLK))); | |
| else | |
| log_abort(); | |
| c->setPort(ID(D), first_cell->getPort(ID(D))[first_slice]); | |
| c->setPort(ID(Q), st.shiftx->getPort(ID(Y))); | |
| c->setPort(ID(L), st.shiftx->getPort(ID(B))); | |
| if (first_cell->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($dff))) | |
| c->setPort(ID(E), State::S1); | |
| else if (first_cell->type.in(ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) | |
| c->setPort(ID(E), first_cell->getPort(ID(E))); | |
| else if (first_cell->type.in(ID($dffe))) | |
| c->setPort(ID(E), first_cell->getPort(ID(EN))); | |
| else | |
| log_abort(); | |
| } | |
| else | |
| log_abort(); | |
| log(" -> %s (%s)\n", log_id(c), log_id(c->type)); | |
| } | |
| struct XilinxSrlPass : public Pass { | |
| XilinxSrlPass() : Pass("xilinx_srl", "Xilinx shift register extraction") { } | |
| void help() YS_OVERRIDE | |
| { | |
| // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| | |
| log("\n"); | |
| log(" xilinx_srl [options] [selection]\n"); | |
| log("\n"); | |
| log("This pass converts chains of built-in flops (bit-level: $_DFF_[NP]_, $_DFFE_*\n"); | |
| log("and word-level: $dff, $dffe) as well as Xilinx flops (FDRE, FDRE_1) into a\n"); | |
| log("$__XILINX_SHREG cell. Chains must be of the same cell type, clock, clock polarity,\n"); | |
| log("enable, and enable polarity (where relevant).\n"); | |
| log("Flops with resets cannot be mapped to Xilinx devices and will not be inferred."); | |
| log("\n"); | |
| log(" -minlen N\n"); | |
| log(" min length of shift register (default = 3)\n"); | |
| log("\n"); | |
| log(" -fixed\n"); | |
| log(" infer fixed-length shift registers.\n"); | |
| log("\n"); | |
| log(" -variable\n"); | |
| log(" infer variable-length shift registers (i.e. fixed-length shifts where\n"); | |
| log(" each element also fans-out to a $shiftx cell).\n"); | |
| log("\n"); | |
| } | |
| void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE | |
| { | |
| log_header(design, "Executing XILINX_SRL pass (Xilinx shift register extraction).\n"); | |
| bool fixed = false; | |
| bool variable = false; | |
| int minlen = 3; | |
| size_t argidx; | |
| for (argidx = 1; argidx < args.size(); argidx++) | |
| { | |
| if (args[argidx] == "-minlen" && argidx+1 < args.size()) { | |
| minlen = atoi(args[++argidx].c_str()); | |
| continue; | |
| } | |
| if (args[argidx] == "-fixed") { | |
| fixed = true; | |
| continue; | |
| } | |
| if (args[argidx] == "-variable") { | |
| variable = true; | |
| continue; | |
| } | |
| break; | |
| } | |
| extra_args(args, argidx, design); | |
| if (!fixed && !variable) | |
| log_cmd_error("'-fixed' and/or '-variable' must be specified.\n"); | |
| for (auto module : design->selected_modules()) { | |
| auto pm = xilinx_srl_pm(module, module->selected_cells()); | |
| pm.ud_fixed.minlen = minlen; | |
| pm.ud_variable.minlen = minlen; | |
| if (fixed) | |
| pm.run_fixed(run_fixed); | |
| if (variable) | |
| pm.run_variable(run_variable); | |
| } | |
| } | |
| } XilinxSrlPass; | |
| PRIVATE_NAMESPACE_END |