aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fpga_interchange/arch.cc132
-rw-r--r--fpga_interchange/arch.h14
-rw-r--r--fpga_interchange/chipdb.h3
3 files changed, 143 insertions, 6 deletions
diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc
index 4bb72ecb..1cefb6fe 100644
--- a/fpga_interchange/arch.cc
+++ b/fpga_interchange/arch.cc
@@ -747,6 +747,40 @@ bool Arch::route_vcc_to_unused_lut_pins()
{
std::string router = str_or_default(settings, id("router"), defaultRouter);
+ HashTables::HashMap<WireId, const NetInfo *> bound_wires;
+ for (auto &net_pair : nets) {
+ const NetInfo *net = net_pair.second.get();
+ for (auto &wire_pair : net->wires) {
+ auto result = bound_wires.emplace(wire_pair.first, net);
+ NPNR_ASSERT(result.first->second == net);
+
+ PipId pip = wire_pair.second.pip;
+ if (pip == PipId()) {
+ continue;
+ }
+
+ const PipInfoPOD &pip_data = pip_info(chip_info, pip);
+#ifdef DEBUG_LUT_MAPPING
+ if (getCtx()->verbose) {
+ log_info("Pip %s in use, has %zu pseudo wires!\n", nameOfPip(pip), pip_data.pseudo_cell_wires.size());
+ }
+#endif
+
+ WireId wire;
+ wire.tile = pip.tile;
+ for (int32_t wire_index : pip_data.pseudo_cell_wires) {
+ wire.index = wire_index;
+#ifdef DEBUG_LUT_MAPPING
+ if (getCtx()->verbose) {
+ log_info("Marking wire %s as in use due to pseudo pip\n", nameOfWire(wire));
+ }
+#endif
+ auto result = bound_wires.emplace(wire, net);
+ NPNR_ASSERT(result.first->second == net);
+ }
+ }
+ }
+
// Fixup LUT vcc pins.
IdString vcc_net_name(chip_info->constants->vcc_net_name);
for (BelId bel : getBels()) {
@@ -766,14 +800,22 @@ bool Arch::route_vcc_to_unused_lut_pins()
port_info.net = nullptr;
WireId lut_pin_wire = getBelPinWire(bel, bel_pin);
- auto iter = wire_to_net.find(lut_pin_wire);
- if (iter != wire_to_net.end()) {
- if (iter->second != nullptr) {
- // This pin is now used by a route through.
- continue;
+ auto iter = bound_wires.find(lut_pin_wire);
+ if (iter != bound_wires.end()) {
+#ifdef DEBUG_LUT_MAPPING
+ if (getCtx()->verbose) {
+ log_info("%s is now used as a LUT route-through, not tying to VCC\n", nameOfWire(lut_pin_wire));
}
+#endif
+ continue;
}
+#ifdef DEBUG_LUT_MAPPING
+ if (getCtx()->verbose) {
+ log_info("%s is an unused LUT pin, tying to VCC\n", nameOfWire(lut_pin_wire));
+ }
+#endif
+
auto result = cell->ports.emplace(bel_pin, port_info);
if (result.second) {
cell->cell_bel_pins[bel_pin].push_back(bel_pin);
@@ -1351,6 +1393,55 @@ void Arch::decode_lut_cells()
}
}
+void Arch::remove_pip_pseudo_wires(PipId pip, NetInfo *net)
+{
+ WireId wire;
+ wire.tile = pip.tile;
+ const PipInfoPOD &pip_data = pip_info(chip_info, pip);
+ for (int32_t wire_index : pip_data.pseudo_cell_wires) {
+ NPNR_ASSERT(wire_index != -1);
+ wire.index = wire_index;
+
+ auto iter = wire_to_net.find(wire);
+ NPNR_ASSERT(iter != wire_to_net.end());
+ // This wire better already have been assigned to this net!
+ if (iter->second != net) {
+ if (iter->second == nullptr) {
+ log_error("Wire %s part of pseudo pip %s but net is null\n", nameOfWire(wire), nameOfPip(pip));
+ } else {
+ log_error("Wire %s part of pseudo pip %s but net is '%s' instead of net '%s'\n", nameOfWire(wire),
+ nameOfPip(pip), iter->second->name.c_str(this), net->name.c_str(this));
+ }
+ }
+
+ auto wire_iter = net->wires.find(wire);
+ if (wire_iter != net->wires.end()) {
+#ifdef DEBUG_BINDING
+ if (getCtx()->verbose) {
+ log_info("Removing %s from net %s, but it's in net wires\n", nameOfWire(wire), net->name.c_str(this));
+ }
+#endif
+ // This wire is part of net->wires, make sure it has no pip,
+ // but leave it alone. It will get cleaned up via
+ // unbindWire.
+ if (wire_iter->second.pip != PipId() && wire_iter->second.pip != pip) {
+ log_error("Wire %s report source'd from pip %s, which is not %s\n", nameOfWire(wire),
+ nameOfPip(wire_iter->second.pip), nameOfPip(pip));
+ }
+ NPNR_ASSERT(wire_iter->second.pip == PipId() || wire_iter->second.pip == pip);
+ } else {
+ // This wire is not in net->wires, update wire_to_net.
+#ifdef DEBUG_BINDING
+ if (getCtx()->verbose) {
+ log_info("Removing %s from net %s in remove_pip_pseudo_wires\n", nameOfWire(wire),
+ net->name.c_str(this));
+ }
+#endif
+ iter->second = nullptr;
+ }
+ }
+}
+
void Arch::assign_net_to_wire(WireId wire, NetInfo *net, const char *src, bool require_empty)
{
#ifdef DEBUG_BINDING
@@ -1402,6 +1493,7 @@ void Arch::unassign_wire(WireId wire)
NPNR_ASSERT(pip_iter != pip_to_net.end());
NPNR_ASSERT(pip_iter->second == net);
pip_iter->second = nullptr;
+ remove_pip_pseudo_wires(pip, net);
}
net_wires.erase(it);
@@ -1430,6 +1522,9 @@ void Arch::unbindPip(PipId pip)
WireId dst = getPipDstWire(pip);
auto wire_iter = wire_to_net.find(dst);
NPNR_ASSERT(wire_iter != wire_to_net.end());
+ NPNR_ASSERT(wire_iter->second == net);
+
+ remove_pip_pseudo_wires(pip, net);
// Clear the net now.
pip_iter->second = nullptr;
@@ -1466,6 +1561,7 @@ void Arch::bindPip(PipId pip, NetInfo *net, PlaceStrength strength)
}
assign_net_to_wire(dst, net, "bindPip", /*require_empty=*/true);
+ assign_pip_pseudo_wires(pip, net);
{
auto result = net->wires.emplace(dst, PipMap{pip, strength});
@@ -1516,6 +1612,7 @@ bool Arch::check_pip_avail_for_net(PipId pip, NetInfo *net) const
}
}
+ WireId src = getPipSrcWire(pip);
WireId dst = getPipDstWire(pip);
auto wire_iter = wire_to_net.find(dst);
@@ -1550,7 +1647,28 @@ bool Arch::check_pip_avail_for_net(PipId pip, NetInfo *net) const
}
}
+ // If this pip is a route-though, make sure all of the route-though
+ // wires are unbound.
const PipInfoPOD &pip_data = pip_info(chip_info, pip);
+ WireId wire;
+ wire.tile = pip.tile;
+ for (int32_t wire_index : pip_data.pseudo_cell_wires) {
+ wire.index = wire_index;
+ NPNR_ASSERT(src != wire);
+ NPNR_ASSERT(dst != wire);
+
+ NetInfo *net = getConflictingWireNet(wire);
+ if (net != nullptr) {
+#ifdef DEBUG_BINDING
+ if (getCtx()->verbose) {
+ log_info("Pip %s is not available because wire %s is tied to net %s\n", getCtx()->nameOfPip(pip),
+ getCtx()->nameOfWire(wire), net->name.c_str(getCtx()));
+ }
+#endif
+ return false;
+ }
+ }
+
if (pip_data.site != -1 && net != nullptr) {
NPNR_ASSERT(net->driver.cell != nullptr);
NPNR_ASSERT(net->driver.cell->bel != BelId());
@@ -1600,6 +1718,10 @@ bool Arch::check_pip_avail_for_net(PipId pip, NetInfo *net) const
}
}
+ // FIXME: This pseudo pip check is incomplete, because constraint
+ // failures will not be detected. However the current FPGA
+ // interchange schema does not provide a cell type to place.
+
return true;
}
diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h
index 84c0b7c8..6a3d7ad1 100644
--- a/fpga_interchange/arch.h
+++ b/fpga_interchange/arch.h
@@ -515,6 +515,20 @@ struct Arch : ArchAPI<ArchRanges>
void assign_net_to_wire(WireId wire, NetInfo *net, const char *src, bool require_empty);
+ void assign_pip_pseudo_wires(PipId pip, NetInfo *net)
+ {
+ NPNR_ASSERT(net != nullptr);
+ WireId wire;
+ wire.tile = pip.tile;
+ const PipInfoPOD &pip_data = pip_info(chip_info, pip);
+ for (int32_t wire_index : pip_data.pseudo_cell_wires) {
+ wire.index = wire_index;
+ assign_net_to_wire(wire, net, "pseudo", /*require_empty=*/true);
+ }
+ }
+
+ void remove_pip_pseudo_wires(PipId pip, NetInfo *net);
+
void unassign_wire(WireId wire);
void bindPip(PipId pip, NetInfo *net, PlaceStrength strength) final;
diff --git a/fpga_interchange/chipdb.h b/fpga_interchange/chipdb.h
index 2130f1b6..2f82dcc9 100644
--- a/fpga_interchange/chipdb.h
+++ b/fpga_interchange/chipdb.h
@@ -34,7 +34,7 @@ NEXTPNR_NAMESPACE_BEGIN
* kExpectedChipInfoVersion
*/
-static constexpr int32_t kExpectedChipInfoVersion = 1;
+static constexpr int32_t kExpectedChipInfoVersion = 2;
// Flattened site indexing.
//
@@ -110,6 +110,7 @@ NPNR_PACKED_STRUCT(struct PipInfoPOD {
int16_t site_variant; // site variant index in tile
int16_t bel; // BEL this pip belongs to if site pip.
int16_t extra_data;
+ RelSlice<int32_t> pseudo_cell_wires;
});
NPNR_PACKED_STRUCT(struct ConstraintTagPOD {