mirror of
https://github.com/YosysHQ/yosys
synced 2026-07-24 16:12:33 +00:00
run_fixed inferred a fixed length shift register from a chain of FDRE flops but assigned it ENPOL 2, which the cells_map template reads as no enable and ties the enable high. FDRE has an active high clock enable, so the shift register shifted every cycle and ignored stalls. Give FDRE and FDRE_1 chains ENPOL 1 so the active high enable is kept. Add tests/arch/xilinx/xilinx_srl_enable.ys covering the FDRE path.
256 lines
8.4 KiB
C++
256 lines
8.4 KiB
C++
/*
|
|
* yosys -- Yosys Open SYnthesis Suite
|
|
*
|
|
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
|
|
* (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 "techlibs/xilinx/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), st.first->type.unescape());
|
|
|
|
SigSpec initval;
|
|
for (auto cell : ud.longest_chain) {
|
|
log_debug(" %s\n", 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]);
|
|
it->second.set(Q.offset, State::Sx);
|
|
}
|
|
else
|
|
initval.append(State::Sx);
|
|
}
|
|
else if (cell->type.in(ID(FDRE), ID(FDRE_1))) {
|
|
if (cell->getParam(ID::INIT).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->getParam(ID(IS_C_INVERTED)).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_), ID(FDRE), ID(FDRE_1)))
|
|
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", c, c->type.unescape());
|
|
}
|
|
|
|
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), st.first->type.unescape());
|
|
|
|
SigSpec initval;
|
|
for (const auto &i : ud.chain) {
|
|
auto cell = i.first;
|
|
auto slice = i.second;
|
|
log_debug(" %s\n", 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()) {
|
|
initval.append(it->second[Q.offset]);
|
|
it->second.set(Q.offset, 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", c, c->type.unescape());
|
|
}
|
|
|
|
struct XilinxSrlPass : public Pass {
|
|
XilinxSrlPass() : Pass("xilinx_srl", "Xilinx shift register extraction") { }
|
|
void help() 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\n");
|
|
log("polarity, enable, and enable polarity (where relevant).\n");
|
|
log("Flops with resets cannot be mapped to Xilinx devices and will not be inferred.\n");
|
|
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) 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
|