struct ShregmapTech
{
virtual ~ShregmapTech() { }
- virtual void init(const Module * /*module*/, const SigMap &/*sigmap*/) {}
- virtual void non_chain_user(const SigBit &/*bit*/, const Cell* /*cell*/, IdString /*port*/) {}
- virtual bool analyze_first(const Cell* /*first_cell*/, const SigMap &/*sigmap*/) { return true; }
- virtual bool analyze(vector<int> &taps, const vector<SigBit> &qbits) = 0;
- virtual Cell* fixup(Cell *cell, const vector<int> &taps, const vector<SigBit> &qbits) = 0;
+ virtual bool analyze(vector<int> &taps) = 0;
+ virtual bool fixup(Cell *cell, dict<int, SigBit> &taps) = 0;
};
struct ShregmapOptions
struct ShregmapTechGreenpak4 : ShregmapTech
{
- virtual bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/) override
+ bool analyze(vector<int> &taps)
{
if (GetSize(taps) > 2 && taps[0] == 0 && taps[2] < 17) {
taps.clear();
return true;
}
- virtual Cell* fixup(Cell *cell, const vector<int> &taps, const vector<SigBit> &qbits) override
+ bool fixup(Cell *cell, dict<int, SigBit> &taps)
{
- auto D = cell->getPort("\\D");
- auto C = cell->getPort("\\C");
+ auto D = cell->getPort(ID(D));
+ auto C = cell->getPort(ID(C));
- auto newcell = cell->module->addCell(NEW_ID, "\\GP_SHREG");
- newcell->setPort("\\nRST", State::S1);
- newcell->setPort("\\CLK", C);
- newcell->setPort("\\IN", D);
+ auto newcell = cell->module->addCell(NEW_ID, ID(GP_SHREG));
+ newcell->setPort(ID(nRST), State::S1);
+ newcell->setPort(ID(CLK), C);
+ newcell->setPort(ID(IN), D);
int i = 0;
for (auto tap : taps) {
- newcell->setPort(i ? "\\OUTB" : "\\OUTA", qbits[tap]);
- newcell->setParam(i ? "\\OUTB_TAP" : "\\OUTA_TAP", tap + 1);
+ newcell->setPort(i ? ID(OUTB) : ID(OUTA), tap.second);
+ newcell->setParam(i ? ID(OUTB_TAP) : ID(OUTA_TAP), tap.first + 1);
i++;
}
- cell->setParam("\\OUTA_INVERT", 0);
- return newcell;
+ cell->setParam(ID(OUTA_INVERT), 0);
+ return false;
}
};
-struct ShregmapTechXilinx7Static : ShregmapTech
-{
- dict<SigBit, Cell*> sigbit_to_cell;
- const ShregmapOptions &opts;
-
- virtual void init(const Module* module, const SigMap &sigmap) override
- {
- for (const auto &i : module->cells_) {
- auto cell = i.second;
- if (!cell->type.in("\\FDRE", "\\FDRE_1","\\FDSE", "\\FDSE_1",
- "\\FDCE", "\\FDCE_1", "\\FDPE", "\\FDPE_1"))
- continue;
-
- sigbit_to_cell[sigmap(cell->getPort("\\Q"))] = cell;
- }
- }
-
- ShregmapTechXilinx7Static(const ShregmapOptions &opts) : opts(opts) {}
-
- virtual bool analyze_first(const Cell* first_cell, const SigMap &sigmap) override
- {
- if (first_cell->type.in("\\FDRE", "\\FDRE_1")) {
- bool is_R_inverted = false;
- if (first_cell->hasParam("\\IS_R_INVERTED"))
- is_R_inverted = first_cell->getParam("\\IS_R_INVERTED").as_bool();
- SigBit R = sigmap(first_cell->getPort("\\R"));
- if (R != RTLIL::S0 && R != RTLIL::S1)
- return false;
- if ((!is_R_inverted && R != RTLIL::S0) || (is_R_inverted && R != RTLIL::S1))
- return false;
- return true;
- }
- if (first_cell->type.in("\\FDSE", "\\FDSE_1")) {
- bool is_S_inverted = false;
- if (first_cell->hasParam("\\IS_S_INVERTED"))
- is_S_inverted = first_cell->getParam("\\IS_S_INVERTED").as_bool();
- SigBit S = sigmap(first_cell->getPort("\\S"));
- if (S != RTLIL::S0 && S != RTLIL::S1)
- return false;
- if ((!is_S_inverted && S != RTLIL::S0) || (is_S_inverted && S != RTLIL::S1))
- return false;
- return true;
- }
- if (first_cell->type.in("\\FDCE", "\\FDCE_1")) {
- bool is_CLR_inverted = false;
- if (first_cell->hasParam("\\IS_CLR_INVERTED"))
- is_CLR_inverted = first_cell->getParam("\\IS_CLR_INVERTED").as_bool();
- SigBit CLR = sigmap(first_cell->getPort("\\CLR"));
- if (CLR != RTLIL::S0 && CLR != RTLIL::S1)
- return false;
- if ((!is_CLR_inverted && CLR != RTLIL::S0) || (is_CLR_inverted && CLR != RTLIL::S1))
- return false;
- return true;
- }
- if (first_cell->type.in("\\FDPE", "\\FDPE_1")) {
- bool is_PRE_inverted = false;
- if (first_cell->hasParam("\\IS_PRE_INVERTED"))
- is_PRE_inverted = first_cell->getParam("\\IS_PRE_INVERTED").as_bool();
- SigBit PRE = sigmap(first_cell->getPort("\\PRE"));
- if (PRE != RTLIL::S0 && PRE != RTLIL::S1)
- return false;
- if ((!is_PRE_inverted && PRE != RTLIL::S0) || (is_PRE_inverted && PRE != RTLIL::S1))
- return false;
- return true;
- }
- return true;
- }
-
- virtual bool analyze(vector<int> &taps, const vector<SigBit> &/*qbits*/) override
- {
- return GetSize(taps) == 1 && taps[0] >= opts.minlen-1;
- }
-
- virtual Cell* fixup(Cell *cell, const vector<int> &/*taps*/, const vector<SigBit> &qbits) override
- {
- auto newcell = cell->module->addCell(NEW_ID, "$__SHREG_");
- newcell->set_src_attribute(cell->get_src_attribute());
- newcell->setParam("\\DEPTH", cell->getParam("\\DEPTH"));
-
- if (cell->type.in("$__SHREG_DFF_N_", "$__SHREG_DFF_P_",
- "$__SHREG_DFFE_NN_", "$__SHREG_DFFE_NP_", "$__SHREG_DFFE_PN_", "$__SHREG_DFFE_PP_")) {
- int param_clkpol = -1;
- int param_enpol = 2;
- if (cell->type == "$__SHREG_DFF_N_") param_clkpol = 0;
- else if (cell->type == "$__SHREG_DFF_P_") param_clkpol = 1;
- else if (cell->type == "$__SHREG_DFFE_NN_") param_clkpol = 0, param_enpol = 0;
- else if (cell->type == "$__SHREG_DFFE_NP_") param_clkpol = 0, param_enpol = 1;
- else if (cell->type == "$__SHREG_DFFE_PN_") param_clkpol = 1, param_enpol = 0;
- else if (cell->type == "$__SHREG_DFFE_PP_") param_clkpol = 1, param_enpol = 1;
- else log_abort();
-
- log_assert(param_clkpol >= 0);
- newcell->setParam("\\CLKPOL", param_clkpol);
- newcell->setParam("\\ENPOL", param_enpol);
- newcell->setParam("\\INIT", cell->getParam("\\INIT"));
-
- if (cell->hasPort("\\E"))
- newcell->setPort("\\E", cell->getPort("\\E"));
- }
- else if (cell->type.in("$__SHREG_FDRE", "$__SHREG_FDRE_1","$__SHREG_FDSE", "$__SHREG_FDSE_1",
- "$__SHREG_FDCE", "$__SHREG_FDCE_1", "$__SHREG_FDPE", "$__SHREG_FDPE_1")) {
- int param_clkpol = 1;
- if (cell->hasParam("\\IS_C_INVERTED") && cell->getParam("\\IS_C_INVERTED").as_bool())
- param_clkpol = 0;
- newcell->setParam("\\CLKPOL", param_clkpol);
- newcell->setParam("\\ENPOL", 1);
- log_assert(cell->getParam("\\INIT").is_fully_undef());
- SigSpec INIT;
- for (auto q : qbits) {
- Cell* reg = sigbit_to_cell.at(q);
- INIT.append(SigBit(reg->getParam("\\INIT").as_bool()));
- }
-
- newcell->setPort("\\E", cell->getPort("\\CE"));
- }
- else log_abort();
-
- newcell->setParam("\\ENPOL", 1);
-
- newcell->setPort("\\C", cell->getPort("\\C"));
- newcell->setPort("\\D", cell->getPort("\\D"));
- newcell->setPort("\\Q", cell->getPort("\\Q"));
-
- return newcell;
- }
-};
-
-struct ShregmapTechXilinx7Dynamic : ShregmapTechXilinx7Static
-{
- dict<SigBit, std::tuple<Cell*,int,int>> sigbit_to_shiftx_offset;
-
- ShregmapTechXilinx7Dynamic(const ShregmapOptions &opts) : ShregmapTechXilinx7Static(opts) {}
-
- virtual void init(const Module* module, const SigMap &sigmap) override
- {
- for (const auto &i : module->cells_) {
- auto cell = i.second;
- if (cell->type == "$shiftx") {
- if (cell->getParam("\\Y_WIDTH") != 1) continue;
- int j = 0;
- for (auto bit : sigmap(cell->getPort("\\A")))
- sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, j++, 0);
- log_assert(j == cell->getParam("\\A_WIDTH").as_int());
- }
- else if (cell->type == "$mux") {
- int j = 0;
- for (auto bit : sigmap(cell->getPort("\\A")))
- sigbit_to_shiftx_offset[bit] = std::make_tuple(cell, 0, j++);
- j = 0;
- for (auto bit : sigmap(cell->getPort("\\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 == "$shiftx" && port == "\\A")
- return;
- if (cell->type == "$mux" && (port == "\\A" || port == "\\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) {
- // Check taps are sequential
- if (i != taps[i])
- return false;
-
- auto it = sigbit_to_shiftx_offset.find(qbits[i]);
- if (it == sigbit_to_shiftx_offset.end())
- 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 == "$shiftx") {
- if (GetSize(taps) > shiftx->getParam("\\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("\\A_WIDTH").as_int()) {
- const SigSpec A = shiftx->getPort("\\A");
- const int A_width = shiftx->getParam("\\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("\\A_WIDTH").as_int())
- return false;
- }
- else if (shiftx->type == "$mux") {
- if (GetSize(taps) != 2)
- return false;
- }
- else log_abort();
-
- return true;
- }
-
- virtual Cell* fixup(Cell *cell, const vector<int> &taps, const vector<SigBit> &qbits) override
- {
- auto bit = qbits[taps.front()];
-
- auto it = sigbit_to_shiftx_offset.find(bit);
- log_assert(it != sigbit_to_shiftx_offset.end());
-
- Cell* newcell = ShregmapTechXilinx7Static::fixup(cell, taps, qbits);
- log_assert(newcell);
- log_assert(newcell->type == "$__SHREG_");
- newcell->type = "$__XILINX_SHREG_";
-
- Cell* shiftx = std::get<0>(it->second);
- RTLIL::SigSpec l_wire;
- if (shiftx->type == "$shiftx")
- l_wire = shiftx->getPort("\\B");
- else if (shiftx->type == "$mux")
- l_wire = shiftx->getPort("\\S");
- else log_abort();
-
- newcell->setPort("\\L", l_wire);
- newcell->setPort("\\Q", shiftx->getPort("\\Y"));
- shiftx->setPort("\\Y", cell->module->addWire(NEW_ID));
-
- return newcell;
- }
-};
-
-
struct ShregmapWorker
{
Module *module;
{
for (auto wire : module->wires())
{
- if (wire->port_output || wire->get_bool_attribute("\\keep")) {
- for (auto bit : sigmap(wire)) {
+ if (wire->port_output || wire->get_bool_attribute(ID::keep)) {
+ for (auto bit : sigmap(wire))
sigbit_with_non_chain_users.insert(bit);
- if (opts.tech) opts.tech->non_chain_user(bit, nullptr, {});
- }
}
- if (wire->attributes.count("\\init")) {
+ if (wire->attributes.count(ID(init))) {
SigSpec initsig = sigmap(wire);
- Const initval = wire->attributes.at("\\init");
+ Const initval = wire->attributes.at(ID(init));
for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
if (initval[i] == State::S0 && !opts.zinit)
sigbit_init[initsig[i]] = false;
for (auto cell : module->cells())
{
- if (opts.ffcells.count(cell->type) && !cell->get_bool_attribute("\\keep"))
+ if (opts.ffcells.count(cell->type) && !cell->get_bool_attribute(ID::keep))
{
IdString d_port = opts.ffcells.at(cell->type).first;
IdString q_port = opts.ffcells.at(cell->type).second;
if (opts.init || sigbit_init.count(q_bit) == 0)
{
- if (sigbit_chain_next.count(d_bit)) {
+ auto r = sigbit_chain_next.insert(std::make_pair(d_bit, cell));
+ if (!r.second) {
+ // Insertion not successful means that d_bit is already
+ // connected to another register, thus mark it as a
+ // non chain user ...
sigbit_with_non_chain_users.insert(d_bit);
- } else
- sigbit_chain_next[d_bit] = cell;
+ // ... and clone d_bit into another wire, and use that
+ // wire as a different key in the d_bit-to-cell dictionary
+ // so that it can be identified as another chain
+ // (omitting this common flop)
+ // Link: https://github.com/YosysHQ/yosys/pull/1085
+ Wire *wire = module->addWire(NEW_ID);
+ module->connect(wire, d_bit);
+ sigmap.add(wire, d_bit);
+ sigbit_chain_next.insert(std::make_pair(wire, cell));
+ }
sigbit_chain_prev[q_bit] = cell;
continue;
for (auto conn : cell->connections())
if (cell->input(conn.first))
- for (auto bit : sigmap(conn.second)) {
+ for (auto bit : sigmap(conn.second))
sigbit_with_non_chain_users.insert(bit);
- if (opts.tech) opts.tech->non_chain_user(bit, cell, conn.first);
- }
}
}
IdString q_port = opts.ffcells.at(c1->type).second;
auto c1_conn = c1->connections();
- auto c2_conn = c1->connections();
+ auto c2_conn = c2->connections();
c1_conn.erase(d_port);
c1_conn.erase(q_port);
Cell *first_cell = chain[cursor];
IdString q_port = opts.ffcells.at(first_cell->type).second;
- vector<SigBit> qbits;
- vector<int> taps;
+ dict<int, SigBit> taps_dict;
if (opts.tech)
{
- if (!opts.tech->analyze_first(first_cell, sigmap)) {
- cursor += depth;
- continue;
- }
+ vector<SigBit> qbits;
+ vector<int> taps;
for (int i = 0; i < depth; i++)
{
if (taps.empty() || taps.back() < depth-1)
taps.push_back(depth-1);
- if (opts.tech->analyze(taps, qbits))
+ if (opts.tech->analyze(taps))
break;
taps.pop_back();
depth = 0;
for (auto tap : taps) {
+ taps_dict[tap] = qbits.at(tap);
log_assert(depth < tap+1);
depth = tap+1;
}
initval.push_back(State::S0);
remove_init.insert(bit);
}
- first_cell->setParam("\\INIT", initval);
+ first_cell->setParam(ID(INIT), initval);
}
if (opts.zinit)
int param_clkpol = -1;
int param_enpol = 2;
- if (first_cell->type == "$_DFF_N_") param_clkpol = 0;
- if (first_cell->type == "$_DFF_P_") param_clkpol = 1;
+ if (first_cell->type == ID($_DFF_N_)) param_clkpol = 0;
+ if (first_cell->type == ID($_DFF_P_)) param_clkpol = 1;
- if (first_cell->type == "$_DFFE_NN_") param_clkpol = 0, param_enpol = 0;
- if (first_cell->type == "$_DFFE_NP_") param_clkpol = 0, param_enpol = 1;
- if (first_cell->type == "$_DFFE_PN_") param_clkpol = 1, param_enpol = 0;
- if (first_cell->type == "$_DFFE_PP_") param_clkpol = 1, param_enpol = 1;
+ if (first_cell->type == ID($_DFFE_NN_)) param_clkpol = 0, param_enpol = 0;
+ if (first_cell->type == ID($_DFFE_NP_)) param_clkpol = 0, param_enpol = 1;
+ if (first_cell->type == ID($_DFFE_PN_)) param_clkpol = 1, param_enpol = 0;
+ if (first_cell->type == ID($_DFFE_PP_)) param_clkpol = 1, param_enpol = 1;
log_assert(param_clkpol >= 0);
- first_cell->setParam("\\CLKPOL", param_clkpol);
- if (opts.ffe) first_cell->setParam("\\ENPOL", param_enpol);
+ first_cell->setParam(ID(CLKPOL), param_clkpol);
+ if (opts.ffe) first_cell->setParam(ID(ENPOL), param_enpol);
}
first_cell->type = shreg_cell_type_str;
first_cell->setPort(q_port, last_cell->getPort(q_port));
- first_cell->setParam("\\DEPTH", depth);
+ first_cell->setParam(ID(DEPTH), depth);
- if (opts.tech != nullptr && opts.tech->fixup(first_cell, taps, qbits))
+ if (opts.tech != nullptr && !opts.tech->fixup(first_cell, taps_dict))
remove_cells.insert(first_cell);
for (int i = 1; i < depth; i++)
for (auto wire : module->wires())
{
- if (wire->attributes.count("\\init") == 0)
+ if (wire->attributes.count(ID(init)) == 0)
continue;
SigSpec initsig = sigmap(wire);
- Const &initval = wire->attributes.at("\\init");
+ Const &initval = wire->attributes.at(ID(init));
for (int i = 0; i < GetSize(initsig) && i < GetSize(initval); i++)
if (remove_init.count(initsig[i]))
initval[i] = State::Sx;
if (SigSpec(initval).is_fully_undef())
- wire->attributes.erase("\\init");
+ wire->attributes.erase(ID(init));
}
remove_cells.clear();
ShregmapWorker(Module *module, const ShregmapOptions &opts) :
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();
find_chain_start_cells();
log(" -tech greenpak4\n");
log(" map to greenpak4 shift registers.\n");
log("\n");
- log(" -tech xilinx\n");
- log(" map to xilinx dynamic-length shift registers.\n");
- log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
clkpol = "pos";
opts.zinit = true;
opts.tech = new ShregmapTechGreenpak4;
- }
- else if (tech == "xilinx_static" || tech == "xilinx_dynamic") {
- opts.init = true;
- opts.ffcells["$_DFF_P_"] = make_pair(IdString("\\D"), IdString("\\Q"));
- opts.ffcells["$_DFF_N_"] = make_pair(IdString("\\D"), IdString("\\Q"));
- opts.ffcells["$_DFFE_PP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
- opts.ffcells["$_DFFE_PN_"] = make_pair(IdString("\\D"), IdString("\\Q"));
- opts.ffcells["$_DFFE_NP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
- opts.ffcells["$_DFFE_NN_"] = make_pair(IdString("\\D"), IdString("\\Q"));
- opts.ffcells["\\FDRE"] = make_pair(IdString("\\D"), IdString("\\Q"));
- opts.ffcells["\\FDRE_1"] = make_pair(IdString("\\D"), IdString("\\Q"));
- opts.ffcells["\\FDSE"] = make_pair(IdString("\\D"), IdString("\\Q"));
- opts.ffcells["\\FDSE_1"] = make_pair(IdString("\\D"), IdString("\\Q"));
- opts.ffcells["\\FDCE"] = make_pair(IdString("\\D"), IdString("\\Q"));
- opts.ffcells["\\FDCE_1"] = make_pair(IdString("\\D"), IdString("\\Q"));
- opts.ffcells["\\FDPE"] = make_pair(IdString("\\D"), IdString("\\Q"));
- opts.ffcells["\\FDPE_1"] = make_pair(IdString("\\D"), IdString("\\Q"));
- if (tech == "xilinx_static")
- opts.tech = new ShregmapTechXilinx7Static(opts);
- else if (tech == "xilinx_dynamic")
- opts.tech = new ShregmapTechXilinx7Dynamic(opts);
} else {
argidx--;
break;
bool en_neg = enpol == "neg" || enpol == "any" || enpol == "any_or_none";
if (clk_pos && en_none)
- opts.ffcells["$_DFF_P_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ opts.ffcells[ID($_DFF_P_)] = make_pair(IdString(ID(D)), IdString(ID(Q)));
if (clk_neg && en_none)
- opts.ffcells["$_DFF_N_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ opts.ffcells[ID($_DFF_N_)] = make_pair(IdString(ID(D)), IdString(ID(Q)));
if (clk_pos && en_pos)
- opts.ffcells["$_DFFE_PP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ opts.ffcells[ID($_DFFE_PP_)] = make_pair(IdString(ID(D)), IdString(ID(Q)));
if (clk_pos && en_neg)
- opts.ffcells["$_DFFE_PN_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ opts.ffcells[ID($_DFFE_PN_)] = make_pair(IdString(ID(D)), IdString(ID(Q)));
if (clk_neg && en_pos)
- opts.ffcells["$_DFFE_NP_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ opts.ffcells[ID($_DFFE_NP_)] = make_pair(IdString(ID(D)), IdString(ID(Q)));
if (clk_neg && en_neg)
- opts.ffcells["$_DFFE_NN_"] = make_pair(IdString("\\D"), IdString("\\Q"));
+ opts.ffcells[ID($_DFFE_NN_)] = make_pair(IdString(ID(D)), IdString(ID(Q)));
if (en_pos || en_neg)
opts.ffe = true;