diff options
Diffstat (limited to 'passes/pmgen')
-rw-r--r-- | passes/pmgen/Makefile.inc | 6 | ||||
-rw-r--r-- | passes/pmgen/xilinx_srl.cc | 237 | ||||
-rw-r--r-- | passes/pmgen/xilinx_srl.pmg | 211 |
3 files changed, 454 insertions, 0 deletions
diff --git a/passes/pmgen/Makefile.inc b/passes/pmgen/Makefile.inc index 8e0cbdca8..e73a7b1c9 100644 --- a/passes/pmgen/Makefile.inc +++ b/passes/pmgen/Makefile.inc @@ -30,3 +30,9 @@ PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN) $(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^) + +# -------------------------------------- + +OBJS += passes/pmgen/xilinx_srl.o +passes/pmgen/xilinx_srl.o: passes/pmgen/xilinx_srl_pm.h +$(eval $(call add_extra_objs,passes/pmgen/xilinx_srl_pm.h)) diff --git a/passes/pmgen/xilinx_srl.cc b/passes/pmgen/xilinx_srl.cc new file mode 100644 index 000000000..22fb93e18 --- /dev/null +++ b/passes/pmgen/xilinx_srl.cc @@ -0,0 +1,237 @@ +/* + * 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 + +// for peepopt_pm +bool did_something; + +#include "passes/pmgen/xilinx_srl_pm.h" +#include "passes/pmgen/ice40_dsp_pm.h" +#include "passes/pmgen/peepopt_pm.h" + +void run_fixed(xilinx_srl_pm &pm) +{ + auto &st = pm.st_fixed; + auto &ud = pm.ud_fixed; + auto param_def = [&ud](Cell *cell, IdString param) { + auto def = ud.default_params.at(std::make_pair(cell->type,param)); + return cell->parameters.at(param, def); + }; + + log("Found fixed chain of length %d (%s):\n", GetSize(ud.longest_chain), log_id(st.first->type)); + + auto last_cell = ud.longest_chain.back(); + + 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()) { + initval.append(it->second[Q.offset]); + } + else + initval.append(State::Sx); + } + else if (cell->type.in(ID(FDRE), ID(FDRE_1))) + initval.append(param_def(cell, ID(INIT))); + else + log_abort(); + if (cell != last_cell) + pm.autoremove(cell); + } + + Cell *c = last_cell; + SigBit Q = st.first->getPort(ID(Q)); + c->setPort(ID(Q), Q); + + if (c->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->parameters.clear(); + c->setParam(ID(DEPTH), GetSize(ud.longest_chain)); + c->setParam(ID(INIT), initval.as_const()); + if (c->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_))) + c->setParam(ID(CLKPOL), 1); + else if (c->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_), ID(FDRE_1))) + c->setParam(ID(CLKPOL), 0); + else if (c->type.in(ID(FDRE))) + c->setParam(ID(CLKPOL), param_def(c, ID(IS_C_INVERTED)).as_bool() ? 0 : 1); + else + log_abort(); + if (c->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_))) + c->setParam(ID(ENPOL), 1); + else if (c->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_))) + c->setParam(ID(ENPOL), 0); + else + c->setParam(ID(ENPOL), 2); + if (c->type.in(ID($_DFF_N_), ID($_DFF_P_))) + c->setPort(ID(E), State::S1); + c->setPort(ID(L), GetSize(ud.longest_chain)-1); + c->type = ID($__XILINX_SHREG_); + } + 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)); + + auto last_cell = ud.chain.back(); + + SigSpec initval; + for (auto cell : ud.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()) { + initval.append(it->second[Q.offset]); + } + else + initval.append(State::Sx); + } + else + log_abort(); + if (cell != last_cell) + pm.autoremove(cell); + } + pm.autoremove(st.shiftx); + + Cell *c = last_cell; + SigBit Q = st.first->getPort(ID(Q)); + c->setPort(ID(Q), Q); + + if (c->type.in(ID($_DFF_N_), ID($_DFF_P_), ID($_DFFE_NN_), ID($_DFFE_NP_), ID($_DFFE_PN_), ID($_DFFE_PP_))) { + c->parameters.clear(); + c->setParam(ID(DEPTH), GetSize(ud.chain)); + c->setParam(ID(INIT), initval.as_const()); + if (c->type.in(ID($_DFF_P_), ID($_DFFE_PN_), ID($_DFFE_PP_))) + c->setParam(ID(CLKPOL), 1); + else if (c->type.in(ID($_DFF_N_), ID($DFFE_NN_), ID($_DFFE_NP_), ID(FDRE_1))) + c->setParam(ID(CLKPOL), 0); + else + log_abort(); + if (c->type.in(ID($_DFFE_NP_), ID($_DFFE_PP_))) + c->setParam(ID(ENPOL), 1); + else if (c->type.in(ID($_DFFE_NN_), ID($_DFFE_PN_))) + c->setParam(ID(ENPOL), 0); + else + c->setParam(ID(ENPOL), 2); + if (c->type.in(ID($_DFF_N_), ID($_DFF_P_))) + c->setPort(ID(E), State::S1); + c->setPort(ID(L), st.shiftx->getPort(ID(B))); + c->setPort(ID(Q), st.shiftx->getPort(ID(Y))); + c->type = ID($__XILINX_SHREG_); + } + 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 ($_DFF_[NP]_, $_DFFE_*) as well as\n"); + log("Xilinx flops (FDRE, FDRE_1) into a $__XILINX_SHREG cell. Chains must be of the\n"); + log("same type, clock, clock polarity, enable, enable polarity (when 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) { + // TODO: How to get these automatically? + pm.ud_fixed.default_params[std::make_pair(ID(FDRE),ID(INIT))] = State::S0; + pm.ud_fixed.default_params[std::make_pair(ID(FDRE),ID(IS_C_INVERTED))] = State::S0; + pm.ud_fixed.default_params[std::make_pair(ID(FDRE),ID(IS_D_INVERTED))] = State::S0; + pm.ud_fixed.default_params[std::make_pair(ID(FDRE),ID(IS_R_INVERTED))] = State::S0; + pm.run_fixed(run_fixed); + } + if (variable) + pm.run_variable(run_variable); + } + } +} XilinxSrlPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/pmgen/xilinx_srl.pmg b/passes/pmgen/xilinx_srl.pmg new file mode 100644 index 000000000..0cc551e92 --- /dev/null +++ b/passes/pmgen/xilinx_srl.pmg @@ -0,0 +1,211 @@ +pattern fixed + +udata <vector<Cell*>> chain longest_chain +udata <pool<Cell*>> non_first_cells +udata <int> minlen +udata <dict<std::pair<IdString,IdString>,Const>> default_params + +code + non_first_cells.clear(); + subpattern(setup); +endcode + +match first + select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1) + select !first->has_keep_attr() + filter !non_first_cells.count(first) +//generate +// SigSpec A = module->addWire(NEW_ID); +// SigSpec B = module->addWire(NEW_ID); +// SigSpec Y = module->addWire(NEW_ID); +// switch (rng(3)) +// { +// case 0: +// module->addAndGate(NEW_ID, A, B, Y); +// break; +// case 1: +// module->addOrGate(NEW_ID, A, B, Y); +// break; +// case 2: +// module->addXorGate(NEW_ID, A, B, Y); +// break; +// } +endmatch + +code + longest_chain.clear(); + chain.push_back(first); + subpattern(tail); +finally + chain.pop_back(); + log_assert(chain.empty()); + if (GetSize(longest_chain) >= minlen) + accept; +endcode + +// ------------------------------------------------------------------ + +subpattern setup + +match first + select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1) + select !first->has_keep_attr() +endmatch + +code + if (first->type.in(\FDRE, \FDRE_1)) { + SigBit R = port(first, \R); + if (first->type == \FDRE) { + auto inverted = first->parameters.at(\IS_R_INVERTED, default_params.at(std::make_pair(first->type,\IS_R_INVERTED))).as_bool(); + if (!inverted && R != State::S0) + reject; + if (inverted && R != State::S1) + reject; + } + else if (first->type == \FDRE_1) { + if (R == State::S0) + reject; + } + else log_abort(); + } +endcode + +match next + select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1) + select !next->has_keep_attr() + select !port(next, \D)[0].wire->get_bool_attribute(\keep) + select nusers(port(next, \Q)) == 2 + index <IdString> next->type === first->type + index <SigBit> port(next, \Q) === port(first, \D) +endmatch + +code + if (next->type.in(\FDRE, \FDRE_1)) { + for (auto p : { \R }) + if (port(next, p) != port(first, p)) + reject; + + if (next->type == \FDRE) { + for (auto p : { \IS_C_INVERTED, \IS_D_INVERTED, \IS_R_INVERTED }) { + auto n = next->parameters.at(p, default_params.at(std::make_pair(next->type,p))); + auto f = first->parameters.at(p, default_params.at(std::make_pair(first->type,p))); + if (n != f) + reject; + } + } + } + non_first_cells.insert(next); +endcode + +// ------------------------------------------------------------------ + +subpattern tail +arg first + +match next + semioptional + select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1) + select !next->has_keep_attr() + select !port(next, \D)[0].wire->get_bool_attribute(\keep) + select nusers(port(next, \Q)) == 2 + index <IdString> next->type === chain.back()->type + index <SigBit> port(next, \Q) === port(chain.back(), \D) +//generate 10 +// SigSpec A = module->addWire(NEW_ID); +// SigSpec B = module->addWire(NEW_ID); +// SigSpec Y = port(chain.back().first, chain.back().second); +// Cell *c = module->addAndGate(NEW_ID, A, B, Y); +// c->type = chain.back().first->type; +endmatch + +code + if (next) { + if (next->type.in(\FDRE, \FDRE_1)) { + for (auto p : { \R }) + if (port(next, p) != port(first, p)) + reject; + + if (next->type == \FDRE) { + for (auto p : { \IS_C_INVERTED, \IS_D_INVERTED, \IS_R_INVERTED }) { + auto n = next->parameters.at(p, default_params.at(std::make_pair(next->type,p))); + auto f = first->parameters.at(p, default_params.at(std::make_pair(first->type,p))); + if (n != f) + reject; + } + } + } + + chain.push_back(next); + subpattern(tail); + } else { + if (GetSize(chain) > GetSize(longest_chain)) + longest_chain = chain; + } +finally + if (next) + chain.pop_back(); +endcode + +// ----------- + +pattern variable + +state <int> shiftx_width +udata <int> minlen +udata <vector<Cell*>> chain + +match shiftx + select shiftx->type.in($shiftx) + select !shiftx->has_keep_attr() + select param(shiftx, \Y_WIDTH).as_int() == 1 + filter param(shiftx, \A_WIDTH).as_int() >= minlen +endmatch + +code shiftx_width + shiftx_width = param(shiftx, \A_WIDTH).as_int(); +endcode + +match first + select first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_) + select nusers(port(first, \Q)) == 2 + index <SigBit> port(first, \Q) === port(shiftx, \A)[shiftx_width-1] +endmatch + +code + chain.push_back(first); + subpattern(tail); +finally + if (GetSize(chain) == shiftx_width) + accept; + chain.clear(); +endcode + +// ------------------------------------------------------------------ + +subpattern tail +arg shiftx +arg shiftx_width + +match next + semioptional + select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_) + select !next->has_keep_attr() + select !port(next, \D)[0].wire->get_bool_attribute(\keep) + select nusers(port(next, \Q)) == 3 + index <IdString> next->type === chain.back()->type + index <SigBit> port(next, \Q) === port(chain.back(), \D) + index <SigBit> port(next, \Q) === port(shiftx, \A)[shiftx_width-1-GetSize(chain)] +endmatch + +code + if (next) { + auto sig = port(next, \Q); + log_warning("nusers of '%s'\n", log_signal(sig)); + for (auto bit : sigmap(sig)) + for (auto user : sigusers[bit]) + log_warning("\t%s\n", log_id(user)); + chain.push_back(next); + if (GetSize(chain) < shiftx_width) + subpattern(tail); + } +endcode |