| /* |
| * nextpnr -- Next Generation Place and Route |
| * |
| * Copyright (C) 2018 David Shah <david@symbioticeda.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 "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<const CellInfo *> &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<const CellInfo *> 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<const CellInfo *> 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<PortInfo *, size_t> 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<IdString> 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<std::pair<float, int>> inputs; |
| std::vector<NetInfo *> 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); |
| } |
| } |
| } |
| |
| void Arch::setupWireLocations() |
| { |
| wire_loc_overrides.clear(); |
| for (auto cell : sorted(cells)) { |
| CellInfo *ci = cell.second; |
| if (ci->bel == BelId()) |
| continue; |
| if (ci->type == id_MULT18X18D || ci->type == id_DCUA) { |
| for (auto &port : ci->ports) { |
| if (port.second.type != PORT_IN || port.second.net == nullptr) |
| continue; |
| WireId pw = getBelPinWire(ci->bel, port.first); |
| if (pw == WireId()) |
| continue; |
| for (auto uh : getPipsUphill(pw)) { |
| WireId pip_src = getPipSrcWire(uh); |
| wire_loc_overrides[pw] = std::make_pair(pip_src.location.x, pip_src.location.y); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| NEXTPNR_NAMESPACE_END |