/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 David Shah * * 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 "cells.h" #include "design_utils.h" #include "log.h" #include "nextpnr.h" #include "timing.h" #include "util.h" NEXTPNR_NAMESPACE_BEGIN inline NetInfo *port_or_nullptr(const CellInfo *cell, IdString name) { auto found = cell->ports.find(name); if (found == cell->ports.end()) return nullptr; return found->second.net; } bool Arch::slicesCompatible(const std::vector &cells) const { // TODO: allow different LSR/CLK and MUX/SRMODE settings once // routing details are worked out IdString clk_sig, lsr_sig; IdString CLKMUX, LSRMUX, SRMODE; bool first = true; for (auto cell : cells) { if (cell->sliceInfo.using_dff) { if (first) { clk_sig = cell->sliceInfo.clk_sig; lsr_sig = cell->sliceInfo.lsr_sig; CLKMUX = cell->sliceInfo.clkmux; LSRMUX = cell->sliceInfo.lsrmux; SRMODE = cell->sliceInfo.srmode; } else { if (cell->sliceInfo.clk_sig != clk_sig) return false; if (cell->sliceInfo.lsr_sig != lsr_sig) return false; if (cell->sliceInfo.clkmux != CLKMUX) return false; if (cell->sliceInfo.lsrmux != LSRMUX) return false; if (cell->sliceInfo.srmode != SRMODE) return false; } first = false; } } return true; } bool Arch::isBelLocationValid(BelId bel) const { if (getBelType(bel) == id_TRELLIS_SLICE) { std::vector bel_cells; Loc bel_loc = getBelLocation(bel); for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) { CellInfo *cell_other = getBoundBelCell(bel_other); if (cell_other != nullptr) { bel_cells.push_back(cell_other); } } if (getBoundBelCell(bel) != nullptr && getBoundBelCell(bel)->sliceInfo.has_l6mux && ((bel_loc.z % 2) == 1)) return false; return slicesCompatible(bel_cells); } else { CellInfo *cell = getBoundBelCell(bel); if (cell == nullptr) return true; else return isValidBelForCell(cell, bel); } } bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const { if (cell->type == id_TRELLIS_SLICE) { NPNR_ASSERT(getBelType(bel) == id_TRELLIS_SLICE); std::vector bel_cells; Loc bel_loc = getBelLocation(bel); if (cell->sliceInfo.has_l6mux && ((bel_loc.z % 2) == 1)) return false; for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) { CellInfo *cell_other = getBoundBelCell(bel_other); if (cell_other != nullptr && bel_other != bel) { bel_cells.push_back(cell_other); } } bel_cells.push_back(cell); return slicesCompatible(bel_cells); } else if (cell->type == id_DCUA || cell->type == id_EXTREFB || cell->type == id_PCSCLKDIV) { return args.type != ArchArgs::LFE5U_25F && args.type != ArchArgs::LFE5U_45F && args.type != ArchArgs::LFE5U_85F; } else { // other checks return true; } } void Arch::permute_luts() { NetCriticalityMap nc; get_criticalities(getCtx(), &nc); std::unordered_map port_to_user; for (auto net : sorted(nets)) { NetInfo *ni = net.second; for (size_t i = 0; i < ni->users.size(); i++) { auto &usr = ni->users.at(i); port_to_user[&(usr.cell->ports.at(usr.port))] = i; } } auto proc_lut = [&](CellInfo *ci, int lut) { std::vector port_names; for (int i = 0; i < 4; i++) port_names.push_back(id(std::string("ABCD").substr(i, 1) + std::to_string(lut))); std::vector> inputs; std::vector orig_nets; for (int i = 0; i < 4; i++) { if (!ci->ports.count(port_names.at(i))) { ci->ports[port_names.at(i)].name = port_names.at(i); ci->ports[port_names.at(i)].type = PORT_IN; } auto &port = ci->ports.at(port_names.at(i)); float crit = 0; if (port.net != nullptr && nc.count(port.net->name)) { auto &n = nc.at(port.net->name); size_t usr = port_to_user.at(&port); if (usr < n.criticality.size()) crit = n.criticality.at(usr); } orig_nets.push_back(port.net); inputs.emplace_back(crit, i); } // Least critical first (A input is slowest) // Avoid permuting locked LUTs (e.g. from an OOC submodule) if (ci->belStrength <= STRENGTH_STRONG) std::sort(inputs.begin(), inputs.end()); for (int i = 0; i < 4; i++) { IdString p = port_names.at(i); // log_info("%s %s %f\n", p.c_str(ctx), port_names.at(inputs.at(i).second).c_str(ctx), inputs.at(i).first); disconnect_port(getCtx(), ci, p); ci->ports.at(p).net = nullptr; if (orig_nets.at(inputs.at(i).second) != nullptr) { connect_port(getCtx(), orig_nets.at(inputs.at(i).second), ci, p); ci->params[id(p.str(this) + "MUX")] = p.str(this); } else { ci->params[id(p.str(this) + "MUX")] = std::string("1"); } } // Rewrite function int old_init = int_or_default(ci->params, id("LUT" + std::to_string(lut) + "_INITVAL"), 0); int new_init = 0; for (int i = 0; i < 16; i++) { int old_index = 0; for (int k = 0; k < 4; k++) { if (i & (1 << k)) old_index |= (1 << inputs.at(k).second); } if (old_init & (1 << old_index)) new_init |= (1 << i); } ci->params[id("LUT" + std::to_string(lut) + "_INITVAL")] = Property(new_init, 16); }; for (auto cell : sorted(cells)) { CellInfo *ci = cell.second; if (ci->type == id_TRELLIS_SLICE && str_or_default(ci->params, id("MODE"), "LOGIC") == "LOGIC") { proc_lut(ci, 0); proc_lut(ci, 1); } } } NEXTPNR_NAMESPACE_END