aboutsummaryrefslogtreecommitdiffstats
path: root/fpga_interchange/arch.cc
diff options
context:
space:
mode:
Diffstat (limited to 'fpga_interchange/arch.cc')
-rw-r--r--fpga_interchange/arch.cc294
1 files changed, 285 insertions, 9 deletions
diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc
index 3839f579..4bb72ecb 100644
--- a/fpga_interchange/arch.cc
+++ b/fpga_interchange/arch.cc
@@ -19,6 +19,8 @@
*
*/
+#include "arch.h"
+
#include <algorithm>
#include <boost/algorithm/string.hpp>
#include <boost/range/adaptor/reversed.hpp>
@@ -37,6 +39,9 @@
#include "util.h"
#include "xdc.h"
+// Include tcl.h late because it messed with defines and let them leave the
+// scope of the header.
+#include <tcl.h>
NEXTPNR_NAMESPACE_BEGIN
struct SiteBelPair
{
@@ -90,6 +95,10 @@ Arch::Arch(ArchArgs args) : args(args)
log_error("Unable to read chipdb %s\n", args.chipdb.c_str());
}
+ if (chip_info->version != kExpectedChipInfoVersion) {
+ log_error("Expected chipdb with version %d found version %d\n", kExpectedChipInfoVersion, chip_info->version);
+ }
+
// Read strings from constids into IdString database, checking that list
// is unique and matches expected constid value.
const RelSlice<RelPtr<char>> &constids = *chip_info->constids;
@@ -254,6 +263,8 @@ IdString Arch::archArgsToId(ArchArgs args) const { return IdString(); }
void Arch::setup_byname() const
{
+ by_name_mutex.lock();
+
if (tile_by_name.empty()) {
for (int i = 0; i < chip_info->tiles.ssize(); i++) {
tile_by_name[id(chip_info->tiles[i].name.get())] = i;
@@ -270,6 +281,8 @@ void Arch::setup_byname() const
}
}
}
+
+ by_name_mutex.unlock();
}
BelId Arch::getBelByName(IdStringList name) const
@@ -416,7 +429,8 @@ PipId Arch::getPipByName(IdStringList name) const
int tile;
int site;
std::tie(tile, site) = site_by_name.at(site_name);
- auto &tile_info = chip_info->tile_types[chip_info->tiles[tile].type];
+ auto tile_type_idx = chip_info->tiles[tile].type;
+ auto &tile_info = chip_info->tile_types[tile_type_idx];
std::array<IdString, 2> ids{name.ids[0], belname};
BelId bel = getBelByName(IdStringList(ids));
@@ -444,7 +458,8 @@ PipId Arch::getPipByName(IdStringList name) const
int tile;
int site;
std::tie(tile, site) = iter->second;
- auto &tile_info = chip_info->tile_types[chip_info->tiles[tile].type];
+ auto tile_type_idx = chip_info->tiles[tile].type;
+ auto &tile_info = chip_info->tile_types[tile_type_idx];
std::string pip_second = name.ids[1].str(this);
auto split = pip_second.find('.');
@@ -500,7 +515,8 @@ PipId Arch::getPipByName(IdStringList name) const
}
} else {
int tile = tile_by_name.at(name.ids[0]);
- auto &tile_info = chip_info->tile_types[chip_info->tiles[tile].type];
+ size_t tile_type_idx = chip_info->tiles[tile].type;
+ auto &tile_info = chip_info->tile_types[tile_type_idx];
std::string pip_second = name.ids[1].str(this);
auto spn = split_identifier_name_dot(pip_second);
@@ -618,11 +634,14 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const
int dst_tile = dst.tile == -1 ? chip_info->nodes[dst.index].tile_wires[0].tile : dst.tile;
int src_tile = src.tile == -1 ? chip_info->nodes[src.index].tile_wires[0].tile : src.tile;
- int x0, x1, y0, y1;
- x0 = src_tile % chip_info->width;
- x1 = x0;
- y0 = src_tile / chip_info->width;
- y1 = y0;
+ int x0 = 0, x1 = 0, y0 = 0, y1 = 0;
+
+ int src_x, src_y;
+ get_tile_x_y(src_tile, &src_x, &src_y);
+
+ int dst_x, dst_y;
+ get_tile_x_y(dst_tile, &dst_x, &dst_y);
+
auto expand = [&](int x, int y) {
x0 = std::min(x0, x);
x1 = std::max(x1, x);
@@ -630,7 +649,8 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const
y1 = std::max(y1, y);
};
- expand(dst_tile % chip_info->width, dst_tile / chip_info->width);
+ expand(src_x, src_y);
+ expand(dst_x, dst_y);
if (source_locs.count(src))
expand(source_locs.at(src).x, source_locs.at(src).y);
@@ -1331,6 +1351,262 @@ void Arch::decode_lut_cells()
}
}
+void Arch::assign_net_to_wire(WireId wire, NetInfo *net, const char *src, bool require_empty)
+{
+#ifdef DEBUG_BINDING
+ if (getCtx()->verbose) {
+ log_info("Assigning wire %s to %s from %s\n", nameOfWire(wire), net->name.c_str(this), src);
+ }
+#endif
+ NPNR_ASSERT(net != nullptr);
+ auto result = wire_to_net.emplace(wire, net);
+ if (!result.second) {
+ // This wire was already in the map, make sure this assignment was
+ // legal.
+ if (require_empty) {
+ NPNR_ASSERT(result.first->second == nullptr);
+ } else {
+ NPNR_ASSERT(result.first->second == nullptr || result.first->second == net);
+ }
+ result.first->second = net;
+ }
+}
+
+void Arch::unassign_wire(WireId wire)
+{
+ NPNR_ASSERT(wire != WireId());
+#ifdef DEBUG_BINDING
+ if (getCtx()->verbose) {
+ log_info("unassign_wire %s\n", nameOfWire(wire));
+ }
+#endif
+
+ auto iter = wire_to_net.find(wire);
+ NPNR_ASSERT(iter != wire_to_net.end());
+
+ NetInfo *net = iter->second;
+ NPNR_ASSERT(net != nullptr);
+
+ auto &net_wires = net->wires;
+ auto it = net_wires.find(wire);
+ NPNR_ASSERT(it != net_wires.end());
+
+ auto pip = it->second.pip;
+ if (pip != PipId()) {
+#ifdef DEBUG_BINDING
+ if (getCtx()->verbose) {
+ log_info("Removing pip %s because it was used to reach wire %s\n", nameOfPip(pip), nameOfWire(wire));
+ }
+#endif
+ auto pip_iter = pip_to_net.find(pip);
+ NPNR_ASSERT(pip_iter != pip_to_net.end());
+ NPNR_ASSERT(pip_iter->second == net);
+ pip_iter->second = nullptr;
+ }
+
+ net_wires.erase(it);
+#ifdef DEBUG_BINDING
+ if (getCtx()->verbose) {
+ log_info("Removing %s from net %s in unassign_wire\n", nameOfWire(wire), net->name.c_str(this));
+ }
+#endif
+ iter->second = nullptr;
+}
+
+void Arch::unbindPip(PipId pip)
+{
+ NPNR_ASSERT(pip != PipId());
+#ifdef DEBUG_BINDING
+ if (getCtx()->verbose) {
+ log_info("unbindPip %s\n", nameOfPip(pip));
+ }
+#endif
+
+ auto pip_iter = pip_to_net.find(pip);
+ NPNR_ASSERT(pip_iter != pip_to_net.end());
+ NetInfo *net = pip_iter->second;
+ NPNR_ASSERT(net != nullptr);
+
+ WireId dst = getPipDstWire(pip);
+ auto wire_iter = wire_to_net.find(dst);
+ NPNR_ASSERT(wire_iter != wire_to_net.end());
+
+ // Clear the net now.
+ pip_iter->second = nullptr;
+#ifdef DEBUG_BINDING
+ if (getCtx()->verbose) {
+ log_info("Removing %s from net %s in unbindPip\n", nameOfWire(dst), net->name.c_str(this));
+ }
+#endif
+ wire_iter->second = nullptr;
+ NPNR_ASSERT(net->wires.erase(dst) == 1);
+
+ refreshUiPip(pip);
+ refreshUiWire(dst);
+}
+
+void Arch::bindPip(PipId pip, NetInfo *net, PlaceStrength strength)
+{
+ NPNR_ASSERT(pip != PipId());
+#ifdef DEBUG_BINDING
+ if (getCtx()->verbose) {
+ log_info("bindPip %s (%d/%d) to net %s\n", nameOfPip(pip), pip.tile, pip.index, net->name.c_str(this));
+ }
+#endif
+ WireId dst = getPipDstWire(pip);
+ NPNR_ASSERT(dst != WireId());
+
+ {
+ // Pip should not already be assigned to anything.
+ auto result = pip_to_net.emplace(pip, net);
+ if (!result.second) {
+ NPNR_ASSERT(result.first->second == nullptr);
+ result.first->second = net;
+ }
+ }
+
+ assign_net_to_wire(dst, net, "bindPip", /*require_empty=*/true);
+
+ {
+ auto result = net->wires.emplace(dst, PipMap{pip, strength});
+ NPNR_ASSERT(result.second);
+ }
+
+ refreshUiPip(pip);
+ refreshUiWire(dst);
+}
+
+void Arch::bindWire(WireId wire, NetInfo *net, PlaceStrength strength)
+{
+ NPNR_ASSERT(wire != WireId());
+#ifdef DEBUG_BINDING
+ if (getCtx()->verbose) {
+ log_info("bindWire %s to net %s\n", nameOfWire(wire), net->name.c_str(this));
+ }
+#endif
+ assign_net_to_wire(wire, net, "bindWire", /*require_empty=*/true);
+ auto &pip_map = net->wires[wire];
+ pip_map.pip = PipId();
+ pip_map.strength = strength;
+ refreshUiWire(wire);
+}
+
+bool Arch::check_pip_avail_for_net(PipId pip, NetInfo *net) const
+{
+ NPNR_ASSERT(pip != PipId());
+ auto pip_iter = pip_to_net.find(pip);
+ if (pip_iter != pip_to_net.end() && pip_iter->second != nullptr) {
+ bool pip_blocked = false;
+ if (net == nullptr) {
+ pip_blocked = true;
+ } else {
+ if (net != pip_iter->second) {
+ pip_blocked = true;
+ }
+ }
+ if (pip_blocked) {
+#ifdef DEBUG_BINDING
+ if (getCtx()->verbose) {
+ log_info("Pip %s (%d/%d) is not available, tied to net %s\n", getCtx()->nameOfPip(pip), pip.tile,
+ pip.index, pip_iter->second->name.c_str(getCtx()));
+ }
+#endif
+ NPNR_ASSERT(pip_iter->first == pip);
+ return false;
+ }
+ }
+
+ WireId dst = getPipDstWire(pip);
+
+ auto wire_iter = wire_to_net.find(dst);
+ if (wire_iter != wire_to_net.end()) {
+ NetInfo *wire_net = wire_iter->second;
+ if (wire_net != nullptr) {
+ auto net_iter = wire_net->wires.find(dst);
+ if (net_iter != wire_net->wires.end()) {
+ if (net == nullptr) {
+#ifdef DEBUG_BINDING
+ if (getCtx()->verbose) {
+ log_info("Pip %s (%d/%d) is not available, dst wire %s is tied to net %s\n",
+ getCtx()->nameOfPip(pip), pip.tile, pip.index, getCtx()->nameOfWire(dst),
+ wire_net->name.c_str(getCtx()));
+ }
+#endif
+ // dst is already driven in this net, do not allow!
+ return false;
+ } else {
+#ifdef DEBUG_BINDING
+ if (getCtx()->verbose && net_iter->second.pip != pip) {
+ log_info("Pip %s (%d/%d) is not available, dst wire %s is tied to net %s\n",
+ getCtx()->nameOfPip(pip), pip.tile, pip.index, getCtx()->nameOfWire(dst),
+ wire_net->name.c_str(getCtx()));
+ }
+#endif
+ // This pip is available if this pip is already bound to
+ // this.
+ return net_iter->second.pip == pip;
+ }
+ }
+ }
+ }
+
+ const PipInfoPOD &pip_data = pip_info(chip_info, pip);
+ if (pip_data.site != -1 && net != nullptr) {
+ NPNR_ASSERT(net->driver.cell != nullptr);
+ NPNR_ASSERT(net->driver.cell->bel != BelId());
+
+ bool valid_pip = false;
+ if (pip.tile == net->driver.cell->bel.tile) {
+ auto &bel_data = bel_info(chip_info, net->driver.cell->bel);
+ if (bel_data.site == pip_data.site) {
+ valid_pip = true;
+ }
+ }
+
+ if (!valid_pip) {
+ // See if one users can enter this site.
+ auto &tile_type = loc_info(chip_info, pip);
+ auto &src_wire_data = tile_type.wire_data[pip_data.src_index];
+ auto &dst_wire_data = tile_type.wire_data[pip_data.dst_index];
+
+ if (dst_wire_data.site == -1) {
+ // This is an output site port, but not for the driver net.
+ // Disallow.
+ NPNR_ASSERT(src_wire_data.site == pip_data.site);
+ } else {
+ // This might be a valid pip, scan users.
+ for (auto &user : net->users) {
+ NPNR_ASSERT(user.cell != nullptr);
+ if (user.cell->bel == BelId()) {
+ continue;
+ }
+
+ auto &bel_data = bel_info(chip_info, user.cell->bel);
+ if (bel_data.site == pip_data.site) {
+ valid_pip = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!valid_pip) {
+#ifdef DEBUG_BINDING
+ if (getCtx()->verbose) {
+ log_info("Pip %s is within a site and not available not right now\n", getCtx()->nameOfPip(pip));
+ }
+#endif
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Arch::checkPipAvail(PipId pip) const { return check_pip_avail_for_net(pip, nullptr); }
+
+Arch::~Arch() {}
+
// Instance constraint templates.
template void Arch::ArchConstraints::bindBel(Arch::ArchConstraints::TagState *, const Arch::ConstraintRange);
template void Arch::ArchConstraints::unbindBel(Arch::ArchConstraints::TagState *, const Arch::ConstraintRange);