mirror of https://github.com/YosysHQ/yosys.git
Merge pull request #1321 from YosysHQ/eddie/xilinx_srl
xilinx_srl pass for shift register extraction
This commit is contained in:
commit
36e38ed46a
|
@ -36,6 +36,8 @@ Yosys 0.9 .. Yosys 0.9-dev
|
||||||
- Added "ice40_wrapcarry" to encapsulate SB_LUT+SB_CARRY pairs for techmapping
|
- Added "ice40_wrapcarry" to encapsulate SB_LUT+SB_CARRY pairs for techmapping
|
||||||
- Removed "ice40_unlut"
|
- Removed "ice40_unlut"
|
||||||
- Improvements in pmgen: slices, choices, define, generate
|
- Improvements in pmgen: slices, choices, define, generate
|
||||||
|
- Added "xilinx_srl" for Xilinx shift register extraction
|
||||||
|
- Removed "shregmap -tech xilinx" (superseded by "xilinx_srl")
|
||||||
|
|
||||||
Yosys 0.8 .. Yosys 0.9
|
Yosys 0.8 .. Yosys 0.9
|
||||||
----------------------
|
----------------------
|
||||||
|
|
|
@ -30,3 +30,9 @@ PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
|
||||||
|
|
||||||
passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
|
passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
|
||||||
$(P) mkdir -p passes/pmgen && python3 $< -o $@ -p peepopt $(filter-out $<,$^)
|
$(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))
|
||||||
|
|
|
@ -28,6 +28,7 @@ bool did_something;
|
||||||
|
|
||||||
#include "passes/pmgen/test_pmgen_pm.h"
|
#include "passes/pmgen/test_pmgen_pm.h"
|
||||||
#include "passes/pmgen/ice40_dsp_pm.h"
|
#include "passes/pmgen/ice40_dsp_pm.h"
|
||||||
|
#include "passes/pmgen/xilinx_srl_pm.h"
|
||||||
#include "passes/pmgen/peepopt_pm.h"
|
#include "passes/pmgen/peepopt_pm.h"
|
||||||
|
|
||||||
void reduce_chain(test_pmgen_pm &pm)
|
void reduce_chain(test_pmgen_pm &pm)
|
||||||
|
@ -180,7 +181,7 @@ void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const
|
||||||
while (modcnt < maxmodcnt && submodcnt < maxsubcnt && itercnt++ < 1000)
|
while (modcnt < maxmodcnt && submodcnt < maxsubcnt && itercnt++ < 1000)
|
||||||
{
|
{
|
||||||
if (timeout++ > 10000)
|
if (timeout++ > 10000)
|
||||||
log_error("pmgen generator is stuck: 10000 iterations an no matching module generated.\n");
|
log_error("pmgen generator is stuck: 10000 iterations with no matching module generated.\n");
|
||||||
|
|
||||||
pm matcher(mod, mod->cells());
|
pm matcher(mod, mod->cells());
|
||||||
|
|
||||||
|
@ -216,7 +217,7 @@ void generate_pattern(std::function<void(pm&,std::function<void()>)> run, const
|
||||||
run(matcher, [](){});
|
run(matcher, [](){});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (submodcnt)
|
if (submodcnt && maxsubcnt < (1 << 16))
|
||||||
maxsubcnt *= 2;
|
maxsubcnt *= 2;
|
||||||
|
|
||||||
design->remove(mod);
|
design->remove(mod);
|
||||||
|
@ -349,13 +350,18 @@ struct TestPmgenPass : public Pass {
|
||||||
if (pattern == "ice40_dsp")
|
if (pattern == "ice40_dsp")
|
||||||
return GENERATE_PATTERN(ice40_dsp_pm, ice40_dsp);
|
return GENERATE_PATTERN(ice40_dsp_pm, ice40_dsp);
|
||||||
|
|
||||||
|
if (pattern == "xilinx_srl.fixed")
|
||||||
|
return GENERATE_PATTERN(xilinx_srl_pm, fixed);
|
||||||
|
if (pattern == "xilinx_srl.variable")
|
||||||
|
return GENERATE_PATTERN(xilinx_srl_pm, variable);
|
||||||
|
|
||||||
if (pattern == "peepopt-muldiv")
|
if (pattern == "peepopt-muldiv")
|
||||||
return GENERATE_PATTERN(peepopt_pm, muldiv);
|
return GENERATE_PATTERN(peepopt_pm, muldiv);
|
||||||
|
|
||||||
if (pattern == "peepopt-shiftmul")
|
if (pattern == "peepopt-shiftmul")
|
||||||
return GENERATE_PATTERN(peepopt_pm, shiftmul);
|
return GENERATE_PATTERN(peepopt_pm, shiftmul);
|
||||||
|
|
||||||
log_cmd_error("Unkown pattern: %s\n", pattern.c_str());
|
log_cmd_error("Unknown pattern: %s\n", pattern.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
|
|
|
@ -0,0 +1,258 @@
|
||||||
|
/*
|
||||||
|
* 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
|
|
@ -0,0 +1,326 @@
|
||||||
|
pattern fixed
|
||||||
|
|
||||||
|
state <IdString> clk_port en_port
|
||||||
|
udata <vector<Cell*>> chain longest_chain
|
||||||
|
udata <pool<Cell*>> non_first_cells
|
||||||
|
udata <int> minlen
|
||||||
|
|
||||||
|
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()
|
||||||
|
select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
|
||||||
|
select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
|
||||||
|
select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero()
|
||||||
|
filter !non_first_cells.count(first)
|
||||||
|
generate
|
||||||
|
SigSpec C = module->addWire(NEW_ID);
|
||||||
|
SigSpec D = module->addWire(NEW_ID);
|
||||||
|
SigSpec Q = module->addWire(NEW_ID);
|
||||||
|
auto r = rng(8);
|
||||||
|
Cell* cell;
|
||||||
|
switch (r)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
cell = module->addCell(NEW_ID, \FDRE);
|
||||||
|
cell->setPort(\C, C);
|
||||||
|
cell->setPort(\D, D);
|
||||||
|
cell->setPort(\Q, Q);
|
||||||
|
cell->setPort(\CE, module->addWire(NEW_ID));
|
||||||
|
if (r & 1)
|
||||||
|
cell->setPort(\R, module->addWire(NEW_ID));
|
||||||
|
else {
|
||||||
|
if (rng(2) == 0)
|
||||||
|
cell->setPort(\R, State::S0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
cell = module->addDffGate(NEW_ID, C, D, Q, r & 1);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
case 6:
|
||||||
|
case 7:
|
||||||
|
cell = module->addDffeGate(NEW_ID, C, module->addWire(NEW_ID), D, Q, r & 1, r & 2);
|
||||||
|
break;
|
||||||
|
default: log_abort();
|
||||||
|
}
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code clk_port en_port
|
||||||
|
if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1))
|
||||||
|
clk_port = \C;
|
||||||
|
else log_abort();
|
||||||
|
if (first->type.in($_DFF_N_, $_DFF_P_))
|
||||||
|
en_port = IdString();
|
||||||
|
else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
|
||||||
|
en_port = \E;
|
||||||
|
else if (first->type.in(\FDRE, \FDRE_1))
|
||||||
|
en_port = \CE;
|
||||||
|
else log_abort();
|
||||||
|
|
||||||
|
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
|
||||||
|
arg clk_port
|
||||||
|
arg en_port
|
||||||
|
|
||||||
|
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()
|
||||||
|
select !first->type.in(\FDRE) || !first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
|
||||||
|
select !first->type.in(\FDRE) || !first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
|
||||||
|
select !first->type.in(\FDRE, \FDRE_1) || first->connections_.at(\R, State::S0).is_fully_zero()
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code clk_port en_port
|
||||||
|
if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, \FDRE, \FDRE_1))
|
||||||
|
clk_port = \C;
|
||||||
|
else log_abort();
|
||||||
|
if (first->type.in($_DFF_N_, $_DFF_P_))
|
||||||
|
en_port = IdString();
|
||||||
|
else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
|
||||||
|
en_port = \E;
|
||||||
|
else if (first->type.in(\FDRE, \FDRE_1))
|
||||||
|
en_port = \CE;
|
||||||
|
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 && !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)
|
||||||
|
filter port(next, clk_port) == port(first, clk_port)
|
||||||
|
filter en_port == IdString() || port(next, en_port) == port(first, en_port)
|
||||||
|
filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool()
|
||||||
|
filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
|
||||||
|
filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
|
||||||
|
filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero()
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code
|
||||||
|
non_first_cells.insert(next);
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
subpattern tail
|
||||||
|
arg first
|
||||||
|
arg clk_port
|
||||||
|
arg en_port
|
||||||
|
|
||||||
|
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 && !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)
|
||||||
|
filter port(next, clk_port) == port(first, clk_port)
|
||||||
|
filter en_port == IdString() || port(next, en_port) == port(first, en_port)
|
||||||
|
filter !first->type.in(\FDRE) || next->parameters.at(\IS_C_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_C_INVERTED, State::S0).as_bool()
|
||||||
|
filter !first->type.in(\FDRE) || next->parameters.at(\IS_D_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_D_INVERTED, State::S0).as_bool()
|
||||||
|
filter !first->type.in(\FDRE) || next->parameters.at(\IS_R_INVERTED, State::S0).as_bool() == first->parameters.at(\IS_R_INVERTED, State::S0).as_bool()
|
||||||
|
filter !first->type.in(\FDRE, \FDRE_1) || next->connections_.at(\R, State::S0).is_fully_zero()
|
||||||
|
generate
|
||||||
|
Cell *cell = module->addCell(NEW_ID, chain.back()->type);
|
||||||
|
cell->setPort(\C, chain.back()->getPort(\C));
|
||||||
|
cell->setPort(\D, module->addWire(NEW_ID));
|
||||||
|
cell->setPort(\Q, chain.back()->getPort(\D));
|
||||||
|
if (cell->type == \FDRE) {
|
||||||
|
if (rng(2) == 0)
|
||||||
|
cell->setPort(\R, chain.back()->connections_.at(\R, State::S0));
|
||||||
|
cell->setPort(\CE, chain.back()->getPort(\CE));
|
||||||
|
}
|
||||||
|
else if (cell->type.begins_with("$_DFFE_"))
|
||||||
|
cell->setPort(\E, chain.back()->getPort(\E));
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code
|
||||||
|
if (next) {
|
||||||
|
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 <IdString> clk_port en_port
|
||||||
|
state <int> shiftx_width
|
||||||
|
state <int> slice
|
||||||
|
udata <int> minlen
|
||||||
|
udata <vector<pair<Cell*,int>>> chain
|
||||||
|
udata <pool<SigBit>> chain_bits
|
||||||
|
|
||||||
|
code
|
||||||
|
chain_bits.clear();
|
||||||
|
endcode
|
||||||
|
|
||||||
|
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
|
||||||
|
generate
|
||||||
|
minlen = 3;
|
||||||
|
module->addShiftx(NEW_ID, module->addWire(NEW_ID, rng(6)+minlen), module->addWire(NEW_ID, 3), module->addWire(NEW_ID));
|
||||||
|
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_, $dff, $dffe)
|
||||||
|
select !first->has_keep_attr()
|
||||||
|
select port(first, \Q)[0].wire && !port(first, \Q)[0].wire->get_bool_attribute(\keep)
|
||||||
|
slice idx GetSize(port(first, \Q))
|
||||||
|
select nusers(port(first, \Q)[idx]) <= 2
|
||||||
|
index <SigBit> port(first, \Q)[idx] === port(shiftx, \A)[shiftx_width-1]
|
||||||
|
set slice idx
|
||||||
|
generate
|
||||||
|
SigSpec C = module->addWire(NEW_ID);
|
||||||
|
auto WIDTH = rng(3)+1;
|
||||||
|
SigSpec D = module->addWire(NEW_ID, WIDTH);
|
||||||
|
SigSpec Q = module->addWire(NEW_ID, WIDTH);
|
||||||
|
auto r = rng(8);
|
||||||
|
Cell *cell = nullptr;
|
||||||
|
switch (r)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
cell = module->addDff(NEW_ID, C, D, Q, r & 1);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
//cell = module->addDffe(NEW_ID, C, module->addWire(NEW_ID), D, Q, r & 1, r & 4);
|
||||||
|
//break;
|
||||||
|
case 6:
|
||||||
|
case 7:
|
||||||
|
WIDTH = 1;
|
||||||
|
cell = module->addDffGate(NEW_ID, C, D[0], Q[0], r & 1);
|
||||||
|
break;
|
||||||
|
default: log_abort();
|
||||||
|
}
|
||||||
|
shiftx->connections_.at(\A)[shiftx_width-1] = port(cell, \Q)[rng(WIDTH)];
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code clk_port en_port
|
||||||
|
if (first->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
|
||||||
|
clk_port = \C;
|
||||||
|
else if (first->type.in($dff, $dffe))
|
||||||
|
clk_port = \CLK;
|
||||||
|
else log_abort();
|
||||||
|
if (first->type.in($_DFF_N_, $_DFF_P_, $dff))
|
||||||
|
en_port = IdString();
|
||||||
|
else if (first->type.in($_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_))
|
||||||
|
en_port = \E;
|
||||||
|
else if (first->type.in($dffe))
|
||||||
|
en_port = \EN;
|
||||||
|
else log_abort();
|
||||||
|
|
||||||
|
chain_bits.insert(port(first, \Q)[slice]);
|
||||||
|
chain.emplace_back(first, slice);
|
||||||
|
subpattern(tail);
|
||||||
|
finally
|
||||||
|
if (GetSize(chain) == shiftx_width)
|
||||||
|
accept;
|
||||||
|
chain.clear();
|
||||||
|
endcode
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
subpattern tail
|
||||||
|
arg first
|
||||||
|
arg shiftx
|
||||||
|
arg shiftx_width
|
||||||
|
arg slice
|
||||||
|
arg clk_port
|
||||||
|
arg en_port
|
||||||
|
|
||||||
|
match next
|
||||||
|
semioptional
|
||||||
|
select next->type.in($_DFF_N_, $_DFF_P_, $_DFFE_NN_, $_DFFE_NP_, $_DFFE_PN_, $_DFFE_PP_, $dff, $dffe)
|
||||||
|
select !next->has_keep_attr()
|
||||||
|
select port(next, \D)[0].wire && !port(next, \D)[0].wire->get_bool_attribute(\keep)
|
||||||
|
slice idx GetSize(port(next, \Q))
|
||||||
|
select nusers(port(next, \Q)[idx]) <= 3
|
||||||
|
index <IdString> next->type === chain.back().first->type
|
||||||
|
index <SigBit> port(next, \Q)[idx] === port(chain.back().first, \D)[chain.back().second]
|
||||||
|
index <SigBit> port(next, \Q)[idx] === port(shiftx, \A)[shiftx_width-1-GetSize(chain)]
|
||||||
|
filter port(next, clk_port) == port(first, clk_port)
|
||||||
|
filter en_port == IdString() || port(next, en_port) == port(first, en_port)
|
||||||
|
filter !next->type.in($dff, $dffe) || param(next, \CLK_POLARITY).as_bool() == param(first, \CLK_POLARITY).as_bool()
|
||||||
|
filter !next->type.in($dffe) || param(next, \EN_POLARITY).as_bool() == param(first, \EN_POLARITY).as_bool()
|
||||||
|
filter !chain_bits.count(port(next, \D)[idx])
|
||||||
|
set slice idx
|
||||||
|
generate
|
||||||
|
if (GetSize(chain) < shiftx_width) {
|
||||||
|
auto back = chain.back().first;
|
||||||
|
auto slice = chain.back().second;
|
||||||
|
if (back->type.in($dff, $dffe)) {
|
||||||
|
auto WIDTH = GetSize(port(back, \D));
|
||||||
|
if (rng(2) == 0 && slice < WIDTH-1) {
|
||||||
|
auto new_slice = slice + rng(WIDTH-1-slice);
|
||||||
|
back->connections_.at(\D)[slice] = port(back, \Q)[new_slice];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto D = module->addWire(NEW_ID, WIDTH);
|
||||||
|
if (back->type == $dff)
|
||||||
|
module->addDff(NEW_ID, port(back, \CLK), D, port(back, \D), param(back, \CLK_POLARITY).as_bool());
|
||||||
|
else if (back->type == $dffe)
|
||||||
|
module->addDffe(NEW_ID, port(back, \CLK), port(back, \EN), D, port(back, \D), param(back, \CLK_POLARITY).as_bool(), param(back, \EN_POLARITY).as_bool());
|
||||||
|
else
|
||||||
|
log_abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (back->type.begins_with("$_DFF_")) {
|
||||||
|
Cell *cell = module->addCell(NEW_ID, back->type);
|
||||||
|
cell->setPort(\C, back->getPort(\C));
|
||||||
|
cell->setPort(\D, module->addWire(NEW_ID));
|
||||||
|
cell->setPort(\Q, back->getPort(\D));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
log_abort();
|
||||||
|
shiftx->connections_.at(\A)[shiftx_width-1-GetSize(chain)] = port(back, \D)[slice];
|
||||||
|
}
|
||||||
|
endmatch
|
||||||
|
|
||||||
|
code
|
||||||
|
if (next) {
|
||||||
|
chain_bits.insert(port(next, \Q)[slice]);
|
||||||
|
chain.emplace_back(next, slice);
|
||||||
|
if (GetSize(chain) < shiftx_width)
|
||||||
|
subpattern(tail);
|
||||||
|
}
|
||||||
|
endcode
|
|
@ -26,9 +26,7 @@ PRIVATE_NAMESPACE_BEGIN
|
||||||
struct ShregmapTech
|
struct ShregmapTech
|
||||||
{
|
{
|
||||||
virtual ~ShregmapTech() { }
|
virtual ~ShregmapTech() { }
|
||||||
virtual void init(const Module * /*module*/, const SigMap &/*sigmap*/) {}
|
virtual bool analyze(vector<int> &taps) = 0;
|
||||||
virtual void non_chain_user(const SigBit &/*bit*/, const Cell* /*cell*/, IdString /*port*/) {}
|
|
||||||
virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) = 0;
|
|
||||||
virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0;
|
virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -56,7 +54,7 @@ struct ShregmapOptions
|
||||||
|
|
||||||
struct ShregmapTechGreenpak4 : ShregmapTech
|
struct ShregmapTechGreenpak4 : ShregmapTech
|
||||||
{
|
{
|
||||||
bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/)
|
bool analyze(vector<int> &taps)
|
||||||
{
|
{
|
||||||
if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {
|
if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {
|
||||||
taps.clear();
|
taps.clear();
|
||||||
|
@ -93,155 +91,6 @@ struct ShregmapTechGreenpak4 : ShregmapTech
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ShregmapTechXilinx7 : ShregmapTech
|
|
||||||
{
|
|
||||||
dict<SigBit, std::tuple<Cell*,int,int>> sigbit_to_shiftx_offset;
|
|
||||||
const ShregmapOptions &opts;
|
|
||||||
|
|
||||||
ShregmapTechXilinx7(const ShregmapOptions &opts) : opts(opts) {}
|
|
||||||
|
|
||||||
virtual void init(const Module* module, const SigMap &sigmap) override
|
|
||||||
{
|
|
||||||
for (const auto &i : module->cells_) {
|
|
||||||
auto cell = i.second;
|
|
||||||
if (cell->type == ID($shiftx)) {
|
|
||||||
if (cell->getParam(ID(Y_WIDTH)) != 1) continue;
|
|
||||||
int j = 0;
|
|
||||||
for (auto bit : sigmap(cell->getPort(ID::A)))
|
|
||||||
sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, j++, 0);
|
|
||||||
log_assert(j == cell->getParam(ID(A_WIDTH)).as_int());
|
|
||||||
}
|
|
||||||
else if (cell->type == ID($mux)) {
|
|
||||||
int j = 0;
|
|
||||||
for (auto bit : sigmap(cell->getPort(ID::A)))
|
|
||||||
sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 0, j++);
|
|
||||||
j = 0;
|
|
||||||
for (auto bit : sigmap(cell->getPort(ID::B)))
|
|
||||||
sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 1, j++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void non_chain_user(const SigBit &bit, const Cell *cell, IdString port) override
|
|
||||||
{
|
|
||||||
auto it = sigbit_to_shiftx_offset.find(bit);
|
|
||||||
if (it == sigbit_to_shiftx_offset.end())
|
|
||||||
return;
|
|
||||||
if (cell) {
|
|
||||||
if (cell->type == ID($shiftx) && port == ID::A)
|
|
||||||
return;
|
|
||||||
if (cell->type == ID($mux) && port.in(ID::A, ID::B))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sigbit_to_shiftx_offset.erase(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) override
|
|
||||||
{
|
|
||||||
if (GetSize(taps) == 1)
|
|
||||||
return taps[0] >= opts.minlen-1 && sigbit_to_shiftx_offset.count(qbits[0]);
|
|
||||||
|
|
||||||
if (taps.back() < opts.minlen-1)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Cell *shiftx = nullptr;
|
|
||||||
int group = 0;
|
|
||||||
for (int i = 0; i < GetSize(taps); ++i) {
|
|
||||||
auto it = sigbit_to_shiftx_offset.find(qbits[i]);
|
|
||||||
if (it == sigbit_to_shiftx_offset.end())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Check taps are sequential
|
|
||||||
if (i != taps[i])
|
|
||||||
return false;
|
|
||||||
// Check taps are not connected to a shift register,
|
|
||||||
// or sequential to the same shift register
|
|
||||||
if (i == 0) {
|
|
||||||
int offset;
|
|
||||||
std::tie(shiftx,offset,group) = it->second;
|
|
||||||
if (offset != i)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Cell *shiftx_ = std::get<0>(it->second);
|
|
||||||
if (shiftx_ != shiftx)
|
|
||||||
return false;
|
|
||||||
int offset = std::get<1>(it->second);
|
|
||||||
if (offset != i)
|
|
||||||
return false;
|
|
||||||
int group_ = std::get<2>(it->second);
|
|
||||||
if (group_ != group)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log_assert(shiftx);
|
|
||||||
|
|
||||||
// Only map if $shiftx exclusively covers the shift register
|
|
||||||
if (shiftx->type == ID($shiftx)) {
|
|
||||||
if (GetSize(taps) > shiftx->getParam(ID(A_WIDTH)).as_int())
|
|
||||||
return false;
|
|
||||||
// Due to padding the most significant bits of A may be 1'bx,
|
|
||||||
// and if so, discount them
|
|
||||||
if (GetSize(taps) < shiftx->getParam(ID(A_WIDTH)).as_int()) {
|
|
||||||
const SigSpec A = shiftx->getPort(ID::A);
|
|
||||||
const int A_width = shiftx->getParam(ID(A_WIDTH)).as_int();
|
|
||||||
for (int i = GetSize(taps); i < A_width; ++i)
|
|
||||||
if (A[i] != RTLIL::Sx) return false;
|
|
||||||
}
|
|
||||||
else if (GetSize(taps) != shiftx->getParam(ID(A_WIDTH)).as_int())
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (shiftx->type == ID($mux)) {
|
|
||||||
if (GetSize(taps) != 2)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else log_abort();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) override
|
|
||||||
{
|
|
||||||
const auto &tap = *taps.begin();
|
|
||||||
auto bit = tap.second;
|
|
||||||
|
|
||||||
auto it = sigbit_to_shiftx_offset.find(bit);
|
|
||||||
log_assert(it != sigbit_to_shiftx_offset.end());
|
|
||||||
|
|
||||||
auto newcell = cell->module->addCell(NEW_ID, ID($__XILINX_SHREG_));
|
|
||||||
newcell->set_src_attribute(cell->get_src_attribute());
|
|
||||||
newcell->setParam(ID(DEPTH), cell->getParam(ID(DEPTH)));
|
|
||||||
newcell->setParam(ID(INIT), cell->getParam(ID(INIT)));
|
|
||||||
newcell->setParam(ID(CLKPOL), cell->getParam(ID(CLKPOL)));
|
|
||||||
newcell->setParam(ID(ENPOL), cell->getParam(ID(ENPOL)));
|
|
||||||
|
|
||||||
newcell->setPort(ID(C), cell->getPort(ID(C)));
|
|
||||||
newcell->setPort(ID(D), cell->getPort(ID(D)));
|
|
||||||
if (cell->hasPort(ID(E)))
|
|
||||||
newcell->setPort(ID(E), cell->getPort(ID(E)));
|
|
||||||
|
|
||||||
Cell* shiftx = std::get<0>(it->second);
|
|
||||||
RTLIL::SigSpec l_wire, q_wire;
|
|
||||||
if (shiftx->type == ID($shiftx)) {
|
|
||||||
l_wire = shiftx->getPort(ID::B);
|
|
||||||
q_wire = shiftx->getPort(ID::Y);
|
|
||||||
shiftx->setPort(ID::Y, cell->module->addWire(NEW_ID));
|
|
||||||
}
|
|
||||||
else if (shiftx->type == ID($mux)) {
|
|
||||||
l_wire = shiftx->getPort(ID(S));
|
|
||||||
q_wire = shiftx->getPort(ID::Y);
|
|
||||||
shiftx->setPort(ID::Y, cell->module->addWire(NEW_ID));
|
|
||||||
}
|
|
||||||
else log_abort();
|
|
||||||
|
|
||||||
newcell->setPort(ID(Q), q_wire);
|
|
||||||
newcell->setPort(ID(L), l_wire);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct ShregmapWorker
|
struct ShregmapWorker
|
||||||
{
|
{
|
||||||
Module *module;
|
Module *module;
|
||||||
|
@ -264,10 +113,8 @@ struct ShregmapWorker
|
||||||
for (auto wire : module->wires())
|
for (auto wire : module->wires())
|
||||||
{
|
{
|
||||||
if (wire->port_output || wire->get_bool_attribute(ID::keep)) {
|
if (wire->port_output || wire->get_bool_attribute(ID::keep)) {
|
||||||
for (auto bit : sigmap(wire)) {
|
for (auto bit : sigmap(wire))
|
||||||
sigbit_with_non_chain_users.insert(bit);
|
sigbit_with_non_chain_users.insert(bit);
|
||||||
if (opts.tech) opts.tech->non_chain_user(bit, nullptr, {});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wire->attributes.count(ID(init))) {
|
if (wire->attributes.count(ID(init))) {
|
||||||
|
@ -317,10 +164,8 @@ struct ShregmapWorker
|
||||||
|
|
||||||
for (auto conn : cell->connections())
|
for (auto conn : cell->connections())
|
||||||
if (cell->input(conn.first))
|
if (cell->input(conn.first))
|
||||||
for (auto bit : sigmap(conn.second)) {
|
for (auto bit : sigmap(conn.second))
|
||||||
sigbit_with_non_chain_users.insert(bit);
|
sigbit_with_non_chain_users.insert(bit);
|
||||||
if (opts.tech) opts.tech->non_chain_user(bit, cell, conn.first);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,7 +270,7 @@ struct ShregmapWorker
|
||||||
if (taps.empty() || taps.back() < depth-1)
|
if (taps.empty() || taps.back() < depth-1)
|
||||||
taps.push_back(depth-1);
|
taps.push_back(depth-1);
|
||||||
|
|
||||||
if (opts.tech->analyze(taps, qbits))
|
if (opts.tech->analyze(taps))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
taps.pop_back();
|
taps.pop_back();
|
||||||
|
@ -544,9 +389,6 @@ struct ShregmapWorker
|
||||||
ShregmapWorker(Module *module, const ShregmapOptions &opts) :
|
ShregmapWorker(Module *module, const ShregmapOptions &opts) :
|
||||||
module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
|
module(module), sigmap(module), opts(opts), dff_count(0), shreg_count(0)
|
||||||
{
|
{
|
||||||
if (opts.tech)
|
|
||||||
opts.tech->init(module, sigmap);
|
|
||||||
|
|
||||||
make_sigbit_chain_next_prev();
|
make_sigbit_chain_next_prev();
|
||||||
find_chain_start_cells();
|
find_chain_start_cells();
|
||||||
|
|
||||||
|
@ -617,11 +459,6 @@ struct ShregmapPass : public Pass {
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" -tech greenpak4\n");
|
log(" -tech greenpak4\n");
|
||||||
log(" map to greenpak4 shift registers.\n");
|
log(" map to greenpak4 shift registers.\n");
|
||||||
log(" this option also implies -clkpol pos -zinit\n");
|
|
||||||
log("\n");
|
|
||||||
log(" -tech xilinx\n");
|
|
||||||
log(" map to xilinx dynamic-length shift registers.\n");
|
|
||||||
log(" this option also implies -params -init\n");
|
|
||||||
log("\n");
|
log("\n");
|
||||||
}
|
}
|
||||||
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
|
||||||
|
@ -676,12 +513,6 @@ struct ShregmapPass : public Pass {
|
||||||
clkpol = "pos";
|
clkpol = "pos";
|
||||||
opts.zinit = true;
|
opts.zinit = true;
|
||||||
opts.tech = new ShregmapTechGreenpak4;
|
opts.tech = new ShregmapTechGreenpak4;
|
||||||
}
|
|
||||||
else if (tech == "xilinx") {
|
|
||||||
opts.init = true;
|
|
||||||
opts.params = true;
|
|
||||||
enpol = "any_or_none";
|
|
||||||
opts.tech = new ShregmapTechXilinx7(opts);
|
|
||||||
} else {
|
} else {
|
||||||
argidx--;
|
argidx--;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -303,9 +303,8 @@ struct SynthXilinxPass : public ScriptPass
|
||||||
if (widemux > 0 || help_mode)
|
if (widemux > 0 || help_mode)
|
||||||
run("muxpack", " ('-widemux' only)");
|
run("muxpack", " ('-widemux' only)");
|
||||||
|
|
||||||
// shregmap -tech xilinx can cope with $shiftx and $mux
|
// xilinx_srl looks for $shiftx cells for identifying variable-length
|
||||||
// cells for identifying variable-length shift registers,
|
// shift registers, so attempt to convert $pmux-es to this
|
||||||
// so attempt to convert $pmux-es to the former
|
|
||||||
// Also: wide multiplexer inference benefits from this too
|
// Also: wide multiplexer inference benefits from this too
|
||||||
if (!(nosrl && widemux == 0) || help_mode) {
|
if (!(nosrl && widemux == 0) || help_mode) {
|
||||||
run("pmux2shiftx", "(skip if '-nosrl' and '-widemux=0')");
|
run("pmux2shiftx", "(skip if '-nosrl' and '-widemux=0')");
|
||||||
|
@ -387,13 +386,8 @@ struct SynthXilinxPass : public ScriptPass
|
||||||
}
|
}
|
||||||
run("opt -full");
|
run("opt -full");
|
||||||
|
|
||||||
if (!nosrl || help_mode) {
|
if (!nosrl || help_mode)
|
||||||
// shregmap operates on bit-level flops, not word-level,
|
run("xilinx_srl -variable -minlen 3", "(skip if '-nosrl')");
|
||||||
// so break those down here
|
|
||||||
run("simplemap t:$dff t:$dffe", " (skip if '-nosrl')");
|
|
||||||
// shregmap with '-tech xilinx' infers variable length shift regs
|
|
||||||
run("shregmap -tech xilinx -minlen 3", "(skip if '-nosrl')");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string techmap_args = " -map +/techmap.v";
|
std::string techmap_args = " -map +/techmap.v";
|
||||||
if (help_mode)
|
if (help_mode)
|
||||||
|
@ -421,6 +415,14 @@ struct SynthXilinxPass : public ScriptPass
|
||||||
run("clean");
|
run("clean");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (check_label("map_ffs")) {
|
||||||
|
if (abc9 || help_mode) {
|
||||||
|
run("techmap -map +/xilinx/ff_map.v", "('-abc9' only)");
|
||||||
|
run("dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT "
|
||||||
|
"-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT", "('-abc9' only)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (check_label("map_luts")) {
|
if (check_label("map_luts")) {
|
||||||
run("opt_expr -mux_undef");
|
run("opt_expr -mux_undef");
|
||||||
if (flatten_before_abc)
|
if (flatten_before_abc)
|
||||||
|
@ -446,10 +448,17 @@ struct SynthXilinxPass : public ScriptPass
|
||||||
// This shregmap call infers fixed length shift registers after abc
|
// This shregmap call infers fixed length shift registers after abc
|
||||||
// has performed any necessary retiming
|
// has performed any necessary retiming
|
||||||
if (!nosrl || help_mode)
|
if (!nosrl || help_mode)
|
||||||
run("shregmap -minlen 3 -init -params -enpol any_or_none", "(skip if '-nosrl')");
|
run("xilinx_srl -fixed -minlen 3", "(skip if '-nosrl')");
|
||||||
run("techmap -map +/xilinx/lut_map.v -map +/xilinx/ff_map.v -map +/xilinx/cells_map.v");
|
|
||||||
|
std::string techmap_args = "-map +/xilinx/lut_map.v -map +/xilinx/cells_map.v";
|
||||||
|
if (help_mode)
|
||||||
|
techmap_args += " [-map +/xilinx/ff_map.v]";
|
||||||
|
else if (!abc9)
|
||||||
|
techmap_args += " -map +/xilinx/ff_map.v";
|
||||||
|
run("techmap " + techmap_args);
|
||||||
|
if (!abc9)
|
||||||
run("dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT "
|
run("dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT -ff FDSE Q INIT "
|
||||||
"-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT");
|
"-ff FDRE_1 Q INIT -ff FDCE_1 Q INIT -ff FDPE_1 Q INIT -ff FDSE_1 Q INIT", "(without '-abc9' only)");
|
||||||
run("clean");
|
run("clean");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,36 +31,3 @@ sat -verify -prove-asserts -show-ports -seq 5 miter
|
||||||
|
|
||||||
#design -load gate
|
#design -load gate
|
||||||
#stat
|
#stat
|
||||||
|
|
||||||
##########
|
|
||||||
|
|
||||||
design -load read
|
|
||||||
design -copy-to model $__XILINX_SHREG_
|
|
||||||
hierarchy -top shregmap_variable_test
|
|
||||||
prep
|
|
||||||
design -save gold
|
|
||||||
|
|
||||||
simplemap t:$dff t:$dffe
|
|
||||||
shregmap -tech xilinx
|
|
||||||
|
|
||||||
#stat
|
|
||||||
# show -width
|
|
||||||
# write_verilog -noexpr -norename
|
|
||||||
select -assert-count 1 t:$_DFF_P_
|
|
||||||
select -assert-count 2 t:$__XILINX_SHREG_
|
|
||||||
|
|
||||||
design -stash gate
|
|
||||||
|
|
||||||
design -import gold -as gold
|
|
||||||
design -import gate -as gate
|
|
||||||
design -copy-from model -as $__XILINX_SHREG_ \$__XILINX_SHREG_
|
|
||||||
prep
|
|
||||||
|
|
||||||
miter -equiv -flatten -make_assert -make_outputs gold gate miter
|
|
||||||
sat -verify -prove-asserts -show-ports -seq 5 miter
|
|
||||||
|
|
||||||
# design -load gold
|
|
||||||
# stat
|
|
||||||
|
|
||||||
# design -load gate
|
|
||||||
# stat
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
/*.log
|
||||||
|
/*.out
|
||||||
|
/run-test.mk
|
|
@ -0,0 +1,57 @@
|
||||||
|
read_verilog -icells <<EOT
|
||||||
|
module \$__XILINX_SHREG_ (input C, input D, input [31:0] L, input E, output Q, output SO);
|
||||||
|
parameter DEPTH = 1;
|
||||||
|
parameter [DEPTH-1:0] INIT = 0;
|
||||||
|
parameter CLKPOL = 1;
|
||||||
|
parameter ENPOL = 2;
|
||||||
|
|
||||||
|
wire pos_clk = C == CLKPOL;
|
||||||
|
reg pos_en;
|
||||||
|
always @(E)
|
||||||
|
if (ENPOL == 2) pos_en = 1'b1;
|
||||||
|
else pos_en = (E == ENPOL[0]);
|
||||||
|
|
||||||
|
reg [DEPTH-1:0] r;
|
||||||
|
always @(posedge pos_clk)
|
||||||
|
if (pos_en)
|
||||||
|
r <= {r[DEPTH-2:0], D};
|
||||||
|
|
||||||
|
assign Q = r[L];
|
||||||
|
assign SO = r[DEPTH-1];
|
||||||
|
endmodule
|
||||||
|
EOT
|
||||||
|
read_verilog +/xilinx/cells_sim.v
|
||||||
|
proc
|
||||||
|
design -save model
|
||||||
|
|
||||||
|
test_pmgen -generate xilinx_srl.fixed
|
||||||
|
hierarchy -top pmtest_xilinx_srl_pm_fixed
|
||||||
|
flatten; opt_clean
|
||||||
|
|
||||||
|
design -save gold
|
||||||
|
xilinx_srl -fixed
|
||||||
|
techmap -autoproc -map %model
|
||||||
|
design -stash gate
|
||||||
|
|
||||||
|
design -copy-from gold -as gold pmtest_xilinx_srl_pm_fixed
|
||||||
|
design -copy-from gate -as gate pmtest_xilinx_srl_pm_fixed
|
||||||
|
dff2dffe -unmap # sat does not support flops-with-enable yet
|
||||||
|
miter -equiv -flatten -make_assert gold gate miter
|
||||||
|
sat -set-init-zero -seq 5 -verify -prove-asserts miter
|
||||||
|
|
||||||
|
design -load model
|
||||||
|
|
||||||
|
test_pmgen -generate xilinx_srl.variable
|
||||||
|
hierarchy -top pmtest_xilinx_srl_pm_variable
|
||||||
|
flatten; opt_clean
|
||||||
|
|
||||||
|
design -save gold
|
||||||
|
xilinx_srl -variable
|
||||||
|
techmap -autoproc -map %model
|
||||||
|
design -stash gate
|
||||||
|
|
||||||
|
design -copy-from gold -as gold pmtest_xilinx_srl_pm_variable
|
||||||
|
design -copy-from gate -as gate pmtest_xilinx_srl_pm_variable
|
||||||
|
dff2dffe -unmap # sat does not support flops-with-enable yet
|
||||||
|
miter -equiv -flatten -make_assert gold gate miter
|
||||||
|
sat -set-init-zero -seq 5 -verify -prove-asserts miter
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
{
|
||||||
|
echo "all::"
|
||||||
|
for x in *.ys; do
|
||||||
|
echo "all:: run-$x"
|
||||||
|
echo "run-$x:"
|
||||||
|
echo " @echo 'Running $x..'"
|
||||||
|
echo " @../../yosys -ql ${x%.ys}.log $x"
|
||||||
|
done
|
||||||
|
for s in *.sh; do
|
||||||
|
if [ "$s" != "run-test.sh" ]; then
|
||||||
|
echo "all:: run-$s"
|
||||||
|
echo "run-$s:"
|
||||||
|
echo " @echo 'Running $s..'"
|
||||||
|
echo " @bash $s"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
} > run-test.mk
|
||||||
|
exec ${MAKE:-make} -f run-test.mk
|
|
@ -0,0 +1,40 @@
|
||||||
|
module xilinx_srl_static_test(input i, clk, output [1:0] q);
|
||||||
|
reg head = 1'b0;
|
||||||
|
reg [3:0] shift1 = 4'b0000;
|
||||||
|
reg [3:0] shift2 = 4'b0000;
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
head <= i;
|
||||||
|
shift1 <= {shift1[2:0], head};
|
||||||
|
shift2 <= {shift2[2:0], head};
|
||||||
|
end
|
||||||
|
|
||||||
|
assign q = {shift2[3], shift1[3]};
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module xilinx_srl_variable_test(input i, clk, input [1:0] l1, l2, output [1:0] q);
|
||||||
|
reg head = 1'b0;
|
||||||
|
reg [3:0] shift1 = 4'b0000;
|
||||||
|
reg [3:0] shift2 = 4'b0000;
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
head <= i;
|
||||||
|
shift1 <= {shift1[2:0], head};
|
||||||
|
shift2 <= {shift2[2:0], head};
|
||||||
|
end
|
||||||
|
|
||||||
|
assign q = {shift2[l2], shift1[l1]};
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module $__XILINX_SHREG_(input C, D, E, input [1:0] L, output Q);
|
||||||
|
parameter CLKPOL = 1;
|
||||||
|
parameter ENPOL = 1;
|
||||||
|
parameter DEPTH = 1;
|
||||||
|
parameter [DEPTH-1:0] INIT = {DEPTH{1'b0}};
|
||||||
|
reg [DEPTH-1:0] r = INIT;
|
||||||
|
wire clk = C ^ CLKPOL;
|
||||||
|
always @(posedge C)
|
||||||
|
if (E)
|
||||||
|
r <= { r[DEPTH-2:0], D };
|
||||||
|
assign Q = r[L];
|
||||||
|
endmodule
|
|
@ -0,0 +1,67 @@
|
||||||
|
read_verilog xilinx_srl.v
|
||||||
|
design -save read
|
||||||
|
|
||||||
|
design -copy-to model $__XILINX_SHREG_
|
||||||
|
hierarchy -top xilinx_srl_static_test
|
||||||
|
prep
|
||||||
|
design -save gold
|
||||||
|
|
||||||
|
techmap
|
||||||
|
xilinx_srl -fixed
|
||||||
|
opt
|
||||||
|
|
||||||
|
# stat
|
||||||
|
# show -width
|
||||||
|
select -assert-count 1 t:$_DFF_P_
|
||||||
|
select -assert-count 2 t:$__XILINX_SHREG_
|
||||||
|
|
||||||
|
design -stash gate
|
||||||
|
|
||||||
|
design -import gold -as gold
|
||||||
|
design -import gate -as gate
|
||||||
|
design -copy-from model -as $__XILINX_SHREG_ \$__XILINX_SHREG_
|
||||||
|
prep
|
||||||
|
|
||||||
|
miter -equiv -flatten -make_assert -make_outputs gold gate miter
|
||||||
|
dump gate
|
||||||
|
sat -verify -prove-asserts -show-ports -seq 5 miter
|
||||||
|
|
||||||
|
#design -load gold
|
||||||
|
#stat
|
||||||
|
|
||||||
|
#design -load gate
|
||||||
|
#stat
|
||||||
|
|
||||||
|
##########
|
||||||
|
|
||||||
|
design -load read
|
||||||
|
design -copy-to model $__XILINX_SHREG_
|
||||||
|
hierarchy -top xilinx_srl_variable_test
|
||||||
|
prep
|
||||||
|
design -save gold
|
||||||
|
|
||||||
|
xilinx_srl -variable
|
||||||
|
opt
|
||||||
|
|
||||||
|
#stat
|
||||||
|
# show -width
|
||||||
|
# write_verilog -noexpr -norename
|
||||||
|
select -assert-count 1 t:$dff
|
||||||
|
select -assert-count 1 t:$dff r:WIDTH=1 %i
|
||||||
|
select -assert-count 2 t:$__XILINX_SHREG_
|
||||||
|
|
||||||
|
design -stash gate
|
||||||
|
|
||||||
|
design -import gold -as gold
|
||||||
|
design -import gate -as gate
|
||||||
|
design -copy-from model -as $__XILINX_SHREG_ \$__XILINX_SHREG_
|
||||||
|
prep
|
||||||
|
|
||||||
|
miter -equiv -flatten -make_assert -make_outputs gold gate miter
|
||||||
|
sat -verify -prove-asserts -show-ports -seq 5 miter
|
||||||
|
|
||||||
|
# design -load gold
|
||||||
|
# stat
|
||||||
|
|
||||||
|
# design -load gate
|
||||||
|
# stat
|
Loading…
Reference in New Issue