aboutsummaryrefslogtreecommitdiffstats
path: root/fpga_interchange
diff options
context:
space:
mode:
Diffstat (limited to 'fpga_interchange')
-rw-r--r--fpga_interchange/arch.cc243
-rw-r--r--fpga_interchange/arch.h54
-rw-r--r--fpga_interchange/arch_pack_io.cc4
-rw-r--r--fpga_interchange/dedicated_interconnect.cc524
-rw-r--r--fpga_interchange/dedicated_interconnect.h40
-rw-r--r--fpga_interchange/fpga_interchange.cpp52
-rw-r--r--fpga_interchange/site_router.cc86
-rw-r--r--fpga_interchange/site_router.h45
8 files changed, 626 insertions, 422 deletions
diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc
index 776fbdb0..dc99f1cd 100644
--- a/fpga_interchange/arch.cc
+++ b/fpga_interchange/arch.cc
@@ -195,10 +195,7 @@ Arch::Arch(ArchArgs args) : args(args)
default_tags.resize(max_tag_count);
}
-
-void Arch::init() {
- dedicated_interconnect.init(getCtx());
-}
+void Arch::init() { dedicated_interconnect.init(getCtx()); }
// -----------------------------------------------------------------------
@@ -615,6 +612,14 @@ bool Arch::place()
{
std::string placer = str_or_default(settings, id("placer"), defaultPlacer);
+ // Re-map BEL pins without constant pins
+ for (BelId bel : getBels()) {
+ CellInfo *cell = getBoundBelCell(bel);
+ if (cell != nullptr && cell->cell_mapping != -1) {
+ map_cell_pins(cell, cell->cell_mapping, /*bind_constants=*/false);
+ }
+ }
+
if (placer == "heap") {
PlacerHeapCfg cfg(getCtx());
cfg.criticalityExponent = 7;
@@ -644,6 +649,14 @@ bool Arch::route()
{
std::string router = str_or_default(settings, id("router"), defaultRouter);
+ // Re-map BEL pins with constant pins
+ for (BelId bel : getBels()) {
+ CellInfo *cell = getBoundBelCell(bel);
+ if (cell != nullptr && cell->cell_mapping != -1) {
+ map_cell_pins(cell, cell->cell_mapping, /*bind_constants=*/true);
+ }
+ }
+
bool result;
if (router == "router1") {
result = router1(getCtx(), Router1Cfg(getCtx()));
@@ -683,13 +696,33 @@ DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; };
delay_t Arch::estimateDelay(WireId src, WireId dst) const
{
// FIXME: Implement something to push the A* router in the right direction.
- return 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);
+
+ delay_t base = 30 * std::min(std::abs(dst_x - src_x), 18) + 10 * std::max(std::abs(dst_x - src_x) - 18, 0) +
+ 60 * std::min(std::abs(dst_y - src_y), 6) + 20 * std::max(std::abs(dst_y - src_y) - 6, 0) + 300;
+
+ base = (base * 3) / 2;
+ return base;
}
delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
{
// FIXME: Implement when adding timing-driven place and route.
- return 0;
+ int src_x, src_y;
+ get_tile_x_y(net_info->driver.cell->bel.tile, &src_x, &src_y);
+
+ int dst_x, dst_y;
+ get_tile_x_y(sink.cell->bel.tile, &dst_x, &dst_y);
+
+ delay_t base = 30 * std::min(std::abs(dst_x - src_x), 18) + 10 * std::max(std::abs(dst_x - src_x) - 18, 0) +
+ 60 * std::min(std::abs(dst_y - src_y), 6) + 20 * std::max(std::abs(dst_y - src_y) - 6, 0) + 300;
+
+ base = (base * 3) / 2;
+ return base;
}
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const
@@ -755,57 +788,64 @@ const std::vector<std::string> Arch::availablePlacers = {"sa",
const std::string Arch::defaultRouter = "router2";
const std::vector<std::string> Arch::availableRouters = {"router1", "router2"};
-void Arch::map_cell_pins(CellInfo *cell, int32_t mapping)
+void Arch::map_cell_pins(CellInfo *cell, int32_t mapping, bool bind_constants)
{
cell->cell_mapping = mapping;
cell->cell_bel_pins.clear();
+ for (IdString const_port : cell->const_ports) {
+ NPNR_ASSERT(cell->ports.erase(const_port));
+ }
const CellBelMapPOD &cell_pin_map = chip_info->cell_map->cell_bel_map[mapping];
+ IdString gnd_net_name(chip_info->constants->gnd_net_name);
+ IdString vcc_net_name(chip_info->constants->vcc_net_name);
+
for (const auto &pin_map : cell_pin_map.common_pins) {
IdString cell_pin(pin_map.cell_pin);
IdString bel_pin(pin_map.bel_pin);
if (cell_pin.str(this) == "GND") {
- IdString gnd_net_name(chip_info->constants->gnd_net_name);
-
- PortInfo port_info;
- port_info.name = bel_pin;
- port_info.type = PORT_IN;
- port_info.net = nullptr;
+ if (bind_constants) {
+ PortInfo port_info;
+ port_info.name = bel_pin;
+ port_info.type = PORT_IN;
+ port_info.net = nullptr;
- auto result = cell->ports.emplace(bel_pin, port_info);
- if(result.second) {
- cell->cell_bel_pins[bel_pin].push_back(bel_pin);
- connectPort(gnd_net_name, cell->name, bel_pin);
- } else {
- NPNR_ASSERT(result.first->second.net == getNetByAlias(gnd_net_name));
- auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector<IdString>({bel_pin}));
- NPNR_ASSERT(result2.first->second.at(0) == bel_pin);
- NPNR_ASSERT(result2.first->second.size() == 1);
+ auto result = cell->ports.emplace(bel_pin, port_info);
+ if (result.second) {
+ cell->cell_bel_pins[bel_pin].push_back(bel_pin);
+ connectPort(gnd_net_name, cell->name, bel_pin);
+ cell->const_ports.emplace(bel_pin);
+ } else {
+ NPNR_ASSERT(result.first->second.net == getNetByAlias(gnd_net_name));
+ auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector<IdString>({bel_pin}));
+ NPNR_ASSERT(result2.first->second.at(0) == bel_pin);
+ NPNR_ASSERT(result2.first->second.size() == 1);
+ }
}
continue;
}
if (cell_pin.str(this) == "VCC") {
- IdString vcc_net_name(chip_info->constants->vcc_net_name);
-
- PortInfo port_info;
- port_info.name = bel_pin;
- port_info.type = PORT_IN;
- port_info.net = nullptr;
+ if (bind_constants) {
+ PortInfo port_info;
+ port_info.name = bel_pin;
+ port_info.type = PORT_IN;
+ port_info.net = nullptr;
- auto result = cell->ports.emplace(bel_pin, port_info);
- if(result.second) {
- cell->cell_bel_pins[bel_pin].push_back(bel_pin);
- connectPort(vcc_net_name, cell->name, bel_pin);
- } else {
- NPNR_ASSERT(result.first->second.net == getNetByAlias(vcc_net_name));
- auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector<IdString>({bel_pin}));
- NPNR_ASSERT(result2.first->second.at(0) == bel_pin);
- NPNR_ASSERT(result2.first->second.size() == 1);
+ auto result = cell->ports.emplace(bel_pin, port_info);
+ if (result.second) {
+ cell->cell_bel_pins[bel_pin].push_back(bel_pin);
+ connectPort(vcc_net_name, cell->name, bel_pin);
+ cell->const_ports.emplace(bel_pin);
+ } else {
+ NPNR_ASSERT(result.first->second.net == getNetByAlias(vcc_net_name));
+ auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector<IdString>({bel_pin}));
+ NPNR_ASSERT(result2.first->second.at(0) == bel_pin);
+ NPNR_ASSERT(result2.first->second.size() == 1);
+ }
}
-
continue;
}
@@ -830,30 +870,44 @@ void Arch::map_cell_pins(CellInfo *cell, int32_t mapping)
IdString bel_pin(pin_map.bel_pin);
if (cell_pin.str(this) == "GND") {
- PortInfo port_info;
- port_info.name = bel_pin;
- port_info.type = PORT_IN;
-
- auto result = cell->ports.emplace(bel_pin, port_info);
- NPNR_ASSERT(result.second);
-
- cell->cell_bel_pins[bel_pin].push_back(bel_pin);
-
- connectPort(IdString(chip_info->constants->gnd_net_name), cell->name, bel_pin);
+ if (bind_constants) {
+ PortInfo port_info;
+ port_info.name = bel_pin;
+ port_info.type = PORT_IN;
+
+ auto result = cell->ports.emplace(bel_pin, port_info);
+ if (result.second) {
+ cell->cell_bel_pins[bel_pin].push_back(bel_pin);
+ connectPort(gnd_net_name, cell->name, bel_pin);
+ cell->const_ports.emplace(bel_pin);
+ } else {
+ NPNR_ASSERT(result.first->second.net == getNetByAlias(gnd_net_name));
+ auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector<IdString>({bel_pin}));
+ NPNR_ASSERT(result2.first->second.at(0) == bel_pin);
+ NPNR_ASSERT(result2.first->second.size() == 1);
+ }
+ }
continue;
}
if (cell_pin.str(this) == "VCC") {
- PortInfo port_info;
- port_info.name = bel_pin;
- port_info.type = PORT_IN;
-
- auto result = cell->ports.emplace(bel_pin, port_info);
- NPNR_ASSERT(result.second);
-
- cell->cell_bel_pins[bel_pin].push_back(bel_pin);
-
- connectPort(IdString(chip_info->constants->vcc_net_name), cell->name, bel_pin);
+ if (bind_constants) {
+ PortInfo port_info;
+ port_info.name = bel_pin;
+ port_info.type = PORT_IN;
+
+ auto result = cell->ports.emplace(bel_pin, port_info);
+ if (result.second) {
+ cell->cell_bel_pins[bel_pin].push_back(bel_pin);
+ connectPort(vcc_net_name, cell->name, bel_pin);
+ cell->const_ports.emplace(bel_pin);
+ } else {
+ NPNR_ASSERT(result.first->second.net == getNetByAlias(vcc_net_name));
+ auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector<IdString>({bel_pin}));
+ NPNR_ASSERT(result2.first->second.at(0) == bel_pin);
+ NPNR_ASSERT(result2.first->second.size() == 1);
+ }
+ }
continue;
}
@@ -912,9 +966,10 @@ size_t Arch::get_cell_type_index(IdString cell_type) const
return cell_offset;
}
-void Arch::merge_constant_nets() {
- NetInfo* gnd_net = nullptr;
- NetInfo* vcc_net = nullptr;
+void Arch::merge_constant_nets()
+{
+ NetInfo *gnd_net = nullptr;
+ NetInfo *vcc_net = nullptr;
bool need_gnd_source = false;
bool need_vcc_source = false;
@@ -924,7 +979,7 @@ void Arch::merge_constant_nets() {
IdString gnd_cell_port(chip_info->constants->gnd_cell_port);
auto gnd_iter = nets.find(gnd_net_name);
- if(gnd_iter != nets.end()) {
+ if (gnd_iter != nets.end()) {
NPNR_ASSERT(gnd_iter->second->driver.cell != nullptr);
NPNR_ASSERT(gnd_iter->second->driver.cell->type == gnd_cell_type);
NPNR_ASSERT(gnd_iter->second->driver.port == gnd_cell_port);
@@ -940,7 +995,7 @@ void Arch::merge_constant_nets() {
IdString vcc_cell_port(chip_info->constants->vcc_cell_port);
auto vcc_iter = nets.find(vcc_net_name);
- if(vcc_iter != nets.end()) {
+ if (vcc_iter != nets.end()) {
NPNR_ASSERT(vcc_iter->second->driver.cell != nullptr);
NPNR_ASSERT(vcc_iter->second->driver.cell->type == vcc_cell_type);
NPNR_ASSERT(vcc_iter->second->driver.port == vcc_cell_port);
@@ -954,28 +1009,28 @@ void Arch::merge_constant_nets() {
std::vector<IdString> other_gnd_nets;
std::vector<IdString> other_vcc_nets;
- for(auto & net_pair : nets) {
- if(net_pair.first == gnd_net_name) {
+ for (auto &net_pair : nets) {
+ if (net_pair.first == gnd_net_name) {
NPNR_ASSERT(net_pair.second.get() == gnd_net);
continue;
}
- if(net_pair.first == vcc_net_name) {
+ if (net_pair.first == vcc_net_name) {
NPNR_ASSERT(net_pair.second.get() == vcc_net);
continue;
}
NetInfo *net = net_pair.second.get();
- if(net->driver.cell == nullptr) {
+ if (net->driver.cell == nullptr) {
continue;
}
- if(net->driver.cell->type == gnd_cell_type) {
+ if (net->driver.cell->type == gnd_cell_type) {
NPNR_ASSERT(net->driver.port == gnd_cell_port);
other_gnd_nets.push_back(net_pair.first);
- if(need_gnd_source) {
+ if (need_gnd_source) {
IdString driver_cell = net->driver.cell->name;
disconnectPort(driver_cell, gnd_cell_port);
connectPort(gnd_net_name, driver_cell, gnd_cell_port);
@@ -984,7 +1039,7 @@ void Arch::merge_constant_nets() {
NPNR_ASSERT(net->driver.port == gnd_cell_port);
std::vector<PortRef> users_copy = net->users;
- for(const PortRef & port_ref : users_copy) {
+ for (const PortRef &port_ref : users_copy) {
IdString cell = port_ref.cell->name;
disconnectPort(cell, port_ref.port);
connectPort(gnd_net_name, cell, port_ref.port);
@@ -993,12 +1048,12 @@ void Arch::merge_constant_nets() {
continue;
}
- if(net->driver.cell->type == vcc_cell_type) {
+ if (net->driver.cell->type == vcc_cell_type) {
NPNR_ASSERT(net->driver.port == vcc_cell_port);
other_vcc_nets.push_back(net_pair.first);
- if(need_vcc_source) {
+ if (need_vcc_source) {
IdString driver_cell = net->driver.cell->name;
disconnectPort(driver_cell, vcc_cell_port);
connectPort(vcc_net_name, driver_cell, vcc_cell_port);
@@ -1007,7 +1062,7 @@ void Arch::merge_constant_nets() {
NPNR_ASSERT(net->driver.port == vcc_cell_port);
std::vector<PortRef> users_copy = net->users;
- for(const PortRef & port_ref : users_copy) {
+ for (const PortRef &port_ref : users_copy) {
IdString cell = port_ref.cell->name;
disconnectPort(cell, port_ref.port);
connectPort(vcc_net_name, cell, port_ref.port);
@@ -1015,10 +1070,10 @@ void Arch::merge_constant_nets() {
}
}
- for(IdString other_gnd_net : other_gnd_nets) {
- NetInfo * net = getNetByAlias(other_gnd_net);
+ for (IdString other_gnd_net : other_gnd_nets) {
+ NetInfo *net = getNetByAlias(other_gnd_net);
NPNR_ASSERT(net->users.empty());
- if(net->driver.cell) {
+ if (net->driver.cell) {
PortRef driver = net->driver;
IdString cell_to_remove = driver.cell->name;
disconnectPort(driver.cell->name, driver.port);
@@ -1026,10 +1081,10 @@ void Arch::merge_constant_nets() {
}
}
- for(IdString other_vcc_net : other_vcc_nets) {
- NetInfo * net = getNetByAlias(other_vcc_net);
+ for (IdString other_vcc_net : other_vcc_nets) {
+ NetInfo *net = getNetByAlias(other_vcc_net);
NPNR_ASSERT(net->users.empty());
- if(net->driver.cell) {
+ if (net->driver.cell) {
PortRef driver = net->driver;
IdString cell_to_remove = driver.cell->name;
disconnectPort(driver.cell->name, driver.port);
@@ -1037,31 +1092,49 @@ void Arch::merge_constant_nets() {
}
}
- for(IdString other_gnd_net : other_gnd_nets) {
+ for (IdString other_gnd_net : other_gnd_nets) {
NPNR_ASSERT(nets.erase(other_gnd_net));
gnd_net->aliases.push_back(other_gnd_net);
net_aliases[other_gnd_net] = gnd_net_name;
}
- for(IdString other_vcc_net : other_vcc_nets) {
+ for (IdString other_vcc_net : other_vcc_nets) {
NPNR_ASSERT(nets.erase(other_vcc_net));
vcc_net->aliases.push_back(other_vcc_net);
net_aliases[other_vcc_net] = vcc_net_name;
}
- if(need_gnd_source) {
- CellInfo * gnd_cell = createCell(gnd_cell_type, gnd_cell_type);
+ if (need_gnd_source) {
+ CellInfo *gnd_cell = createCell(gnd_cell_type, gnd_cell_type);
gnd_cell->addOutput(gnd_cell_port);
connectPort(gnd_net_name, gnd_cell_type, gnd_cell_port);
}
- if(need_vcc_source) {
- CellInfo * vcc_cell = createCell(vcc_cell_type, vcc_cell_type);
+ if (need_vcc_source) {
+ CellInfo *vcc_cell = createCell(vcc_cell_type, vcc_cell_type);
vcc_cell->addOutput(vcc_cell_port);
connectPort(vcc_net_name, vcc_cell_type, vcc_cell_port);
}
}
+const std::vector<IdString> &Arch::getBelPinsForCellPin(const CellInfo *cell_info, IdString pin) const
+{
+ auto iter = cell_info->cell_bel_pins.find(pin);
+ if (iter == cell_info->cell_bel_pins.end()) {
+ return no_pins;
+ } else {
+ return iter->second;
+ }
+}
+
+void Arch::report_invalid_bel(BelId bel, CellInfo *cell) const
+{
+ int32_t mapping = bel_info(chip_info, bel).pin_map[get_cell_type_index(cell->type)];
+ NPNR_ASSERT(mapping < 0);
+ log_error("Cell %s (%s) cannot be placed at BEL %s (mapping %d)\n", cell->name.c_str(this), cell->type.c_str(this),
+ nameOfBel(bel), mapping);
+}
+
// 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);
diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h
index c713ddb9..13cab02f 100644
--- a/fpga_interchange/arch.h
+++ b/fpga_interchange/arch.h
@@ -30,6 +30,7 @@
#include "constraints.h"
#include "dedicated_interconnect.h"
+#include "site_router.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -773,7 +774,14 @@ struct ArchRanges
using BucketBelRangeT = FilteredBelRange;
};
-struct DedicatedInterconnect;
+static constexpr size_t kMaxState = 8;
+
+struct TileStatus
+{
+ std::vector<ExclusiveStateGroup<kMaxState>> tags;
+ std::vector<CellInfo *> boundcells;
+ std::vector<SiteRouter> sites;
+};
struct Arch : ArchAPI<ArchRanges>
{
@@ -787,31 +795,6 @@ struct Arch : ArchAPI<ArchRanges>
std::unordered_map<WireId, NetInfo *> wire_to_net;
std::unordered_map<PipId, NetInfo *> pip_to_net;
- static constexpr size_t kMaxState = 8;
-
- struct TileStatus;
- struct SiteRouter
- {
- SiteRouter(int16_t site) : site(site), dirty(false), site_ok(true) {}
-
- std::unordered_set<CellInfo *> cells_in_site;
- const int16_t site;
-
- mutable bool dirty;
- mutable bool site_ok;
-
- void bindBel(CellInfo *cell);
- void unbindBel(CellInfo *cell);
- bool checkSiteRouting(const Context *ctx, const TileStatus &tile_status) const;
- };
-
- struct TileStatus
- {
- std::vector<ExclusiveStateGroup<kMaxState>> tags;
- std::vector<CellInfo *> boundcells;
- std::vector<SiteRouter> sites;
- };
-
DedicatedInterconnect dedicated_interconnect;
std::unordered_map<int32_t, TileStatus> tileStatus;
@@ -871,7 +854,7 @@ struct Arch : ArchAPI<ArchRanges>
uint32_t getBelChecksum(BelId bel) const override { return bel.index; }
- void map_cell_pins(CellInfo *cell, int32_t mapping);
+ void map_cell_pins(CellInfo *cell, int32_t mapping, bool bind_constants);
void map_port_pins(BelId bel, CellInfo *cell) const;
TileStatus &get_tile_status(int32_t tile)
@@ -931,10 +914,13 @@ struct Arch : ArchAPI<ArchRanges>
if (io_port_types.count(cell->type) == 0) {
int32_t mapping = bel_info(chip_info, bel).pin_map[get_cell_type_index(cell->type)];
+ if (mapping < 0) {
+ report_invalid_bel(bel, cell);
+ }
NPNR_ASSERT(mapping >= 0);
if (cell->cell_mapping != mapping) {
- map_cell_pins(cell, mapping);
+ map_cell_pins(cell, mapping, /*bind_constants=*/false);
}
constraints.bindBel(tile_status.tags.data(), get_cell_constraints(bel, cell->type));
} else {
@@ -1078,10 +1064,7 @@ struct Arch : ArchAPI<ArchRanges>
return str_range;
}
- const std::vector<IdString> &getBelPinsForCellPin(const CellInfo *cell_info, IdString pin) const override
- {
- return cell_info->cell_bel_pins.at(pin);
- }
+ const std::vector<IdString> &getBelPinsForCellPin(const CellInfo *cell_info, IdString pin) const override;
// -------------------------------------------------
@@ -1509,7 +1492,7 @@ struct Arch : ArchAPI<ArchRanges>
if (cell == nullptr) {
return true;
} else {
- if(!dedicated_interconnect.isBelLocationValid(bel, cell)) {
+ if (!dedicated_interconnect.isBelLocationValid(bel, cell)) {
return false;
}
@@ -1718,6 +1701,11 @@ struct Arch : ArchAPI<ArchRanges>
}
void merge_constant_nets();
+ void report_invalid_bel(BelId bel, CellInfo *cell) const;
+
+ std::vector<IdString> no_pins;
+ IdString gnd_cell_pin;
+ IdString vcc_cell_pin;
};
NEXTPNR_NAMESPACE_END
diff --git a/fpga_interchange/arch_pack_io.cc b/fpga_interchange/arch_pack_io.cc
index 6a0ffe0b..06cfa002 100644
--- a/fpga_interchange/arch_pack_io.cc
+++ b/fpga_interchange/arch_pack_io.cc
@@ -243,7 +243,9 @@ void Arch::pack_ports()
for (CellInfo *cell : placed_cells) {
NPNR_ASSERT(cell->bel != BelId());
- NPNR_ASSERT(isBelLocationValid(cell->bel));
+ if (!isBelLocationValid(cell->bel)) {
+ log_error("Tightly bound BEL %s was not valid!\n", nameOfBel(cell->bel));
+ }
}
}
}
diff --git a/fpga_interchange/dedicated_interconnect.cc b/fpga_interchange/dedicated_interconnect.cc
index b9ef93b5..820896a3 100644
--- a/fpga_interchange/dedicated_interconnect.cc
+++ b/fpga_interchange/dedicated_interconnect.cc
@@ -18,8 +18,8 @@
*
*/
-#include "nextpnr.h"
#include "log.h"
+#include "nextpnr.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -32,13 +32,15 @@ NEXTPNR_NAMESPACE_BEGIN
// terminate at another site. Routing that "flys" over a site is expressed as
// a psuedo-pip connected the relevant site pin wires, rather than traversing
// the site.
-enum WireNodeState {
+enum WireNodeState
+{
IN_SINK_SITE = 0,
IN_ROUTING = 1,
IN_SOURCE_SITE = 2
};
-struct WireNode {
+struct WireNode
+{
WireId wire;
WireNodeState state;
int depth;
@@ -50,32 +52,33 @@ struct WireNode {
// interconnect.
constexpr int kMaxDepth = 20;
-void DedicatedInterconnect::init(const Context *ctx) {
+void DedicatedInterconnect::init(const Context *ctx)
+{
this->ctx = ctx;
- if(ctx->debug) {
+ if (ctx->debug) {
log_info("Finding dedicated interconnect!\n");
}
find_dedicated_interconnect();
- if(ctx->debug) {
+ if (ctx->debug) {
print_dedicated_interconnect();
}
}
-bool DedicatedInterconnect::check_routing(
- BelId src_bel, IdString src_bel_pin,
- BelId dst_bel, IdString dst_bel_pin) const {
+bool DedicatedInterconnect::check_routing(BelId src_bel, IdString src_bel_pin, BelId dst_bel,
+ IdString dst_bel_pin) const
+{
std::vector<WireNode> nodes_to_expand;
WireId src_wire = ctx->getBelPinWire(src_bel, src_bel_pin);
- const auto & src_wire_data = ctx->wire_info(src_wire);
+ const auto &src_wire_data = ctx->wire_info(src_wire);
NPNR_ASSERT(src_wire_data.site != -1);
WireId dst_wire = ctx->getBelPinWire(dst_bel, dst_bel_pin);
- const auto & dst_wire_data = ctx->wire_info(dst_wire);
+ const auto &dst_wire_data = ctx->wire_info(dst_wire);
NPNR_ASSERT(dst_wire_data.site != -1);
WireNode wire_node;
@@ -85,30 +88,29 @@ bool DedicatedInterconnect::check_routing(
nodes_to_expand.push_back(wire_node);
- while(!nodes_to_expand.empty()) {
+ while (!nodes_to_expand.empty()) {
WireNode node_to_expand = nodes_to_expand.back();
nodes_to_expand.pop_back();
- for(PipId pip : ctx->getPipsDownhill(node_to_expand.wire)) {
- if(ctx->is_pip_synthetic(pip)) {
+ for (PipId pip : ctx->getPipsDownhill(node_to_expand.wire)) {
+ if (ctx->is_pip_synthetic(pip)) {
continue;
}
WireId wire = ctx->getPipDstWire(pip);
- if(wire == WireId()) {
+ if (wire == WireId()) {
continue;
}
#ifdef DEBUG_EXPANSION
- log_info(" - At wire %s via %s\n",
- ctx->nameOfWire(wire), ctx->nameOfPip(pip));
+ log_info(" - At wire %s via %s\n", ctx->nameOfWire(wire), ctx->nameOfPip(pip));
#endif
WireNode next_node;
next_node.wire = wire;
next_node.depth = node_to_expand.depth += 1;
- if(next_node.depth > kMaxDepth) {
+ if (next_node.depth > kMaxDepth) {
// Dedicated routing should reach sources by kMaxDepth (with
// tuning).
//
@@ -116,55 +118,52 @@ bool DedicatedInterconnect::check_routing(
return false;
}
- auto const & wire_data = ctx->wire_info(wire);
+ auto const &wire_data = ctx->wire_info(wire);
bool expand_node = true;
- if(ctx->is_site_port(pip)) {
- switch(node_to_expand.state) {
- case IN_SOURCE_SITE:
- NPNR_ASSERT(wire_data.site == -1);
- next_node.state = IN_ROUTING;
- break;
- case IN_ROUTING:
- NPNR_ASSERT(wire_data.site != -1);
- if(wire.tile == src_wire.tile && wire_data.site == src_wire_data.site) {
- // Dedicated routing won't have straight loops,
- // general routing looks like that.
+ if (ctx->is_site_port(pip)) {
+ switch (node_to_expand.state) {
+ case IN_SOURCE_SITE:
+ NPNR_ASSERT(wire_data.site == -1);
+ next_node.state = IN_ROUTING;
+ break;
+ case IN_ROUTING:
+ NPNR_ASSERT(wire_data.site != -1);
+ if (wire.tile == src_wire.tile && wire_data.site == src_wire_data.site) {
+ // Dedicated routing won't have straight loops,
+ // general routing looks like that.
#ifdef DEBUG_EXPANSION
- log_info(" - Not dedicated site routing because loop!");
+ log_info(" - Not dedicated site routing because loop!");
#endif
- return false;
- }
- next_node.state = IN_SINK_SITE;
- break;
- case IN_SINK_SITE:
- // Once entering a site, do not leave it again.
- // This path is not a legal route!
- expand_node = false;
- break;
- default:
- // Unreachable!!!
- NPNR_ASSERT(false);
+ return false;
+ }
+ next_node.state = IN_SINK_SITE;
+ break;
+ case IN_SINK_SITE:
+ // Once entering a site, do not leave it again.
+ // This path is not a legal route!
+ expand_node = false;
+ break;
+ default:
+ // Unreachable!!!
+ NPNR_ASSERT(false);
}
} else {
next_node.state = node_to_expand.state;
}
- if(expand_node) {
+ if (expand_node) {
nodes_to_expand.push_back(next_node);
} else {
continue;
}
- if(next_node.state == IN_SINK_SITE) {
- for(BelPin bel_pin : ctx->getWireBelPins(wire)) {
- if(bel_pin.bel == dst_bel && bel_pin.pin == dst_bel_pin) {
- if(ctx->verbose) {
- log_info("Valid dedicated interconnect from %s/%s to %s/%s\n",
- ctx->nameOfBel(src_bel),
- src_bel_pin.c_str(ctx),
- ctx->nameOfBel(dst_bel),
- dst_bel_pin.c_str(ctx));
+ if (next_node.state == IN_SINK_SITE) {
+ for (BelPin bel_pin : ctx->getWireBelPins(wire)) {
+ if (bel_pin.bel == dst_bel && bel_pin.pin == dst_bel_pin) {
+ if (ctx->debug) {
+ log_info("Valid dedicated interconnect from %s/%s to %s/%s\n", ctx->nameOfBel(src_bel),
+ src_bel_pin.c_str(ctx), ctx->nameOfBel(dst_bel), dst_bel_pin.c_str(ctx));
}
return true;
}
@@ -176,8 +175,9 @@ bool DedicatedInterconnect::check_routing(
return false;
}
-bool DedicatedInterconnect::is_driver_on_net_valid(BelId driver_bel,
- const CellInfo* cell, IdString driver_port, NetInfo *net) const {
+bool DedicatedInterconnect::is_driver_on_net_valid(BelId driver_bel, const CellInfo *cell, IdString driver_port,
+ NetInfo *net) const
+{
const auto &driver_bel_data = bel_info(ctx->chip_info, driver_bel);
TileTypeBelPin type_bel_pin;
@@ -186,27 +186,34 @@ bool DedicatedInterconnect::is_driver_on_net_valid(BelId driver_bel,
Loc driver_loc = ctx->getBelLocation(driver_bel);
- for(IdString driver_bel_pin : ctx->getBelPinsForCellPin(cell, driver_port)) {
+ for (IdString driver_bel_pin : ctx->getBelPinsForCellPin(cell, driver_port)) {
type_bel_pin.bel_pin = driver_bel_pin;
auto iter = sources.find(type_bel_pin);
- if(iter == sources.end()) {
+ if (iter == sources.end()) {
// This BEL pin doesn't have a dedicate interconnect.
continue;
}
- for(const PortRef & port_ref : net->users) {
+ for (const PortRef &port_ref : net->users) {
NPNR_ASSERT(port_ref.cell != nullptr);
- if(port_ref.cell->bel == BelId()) {
- return true;
+ if (port_ref.cell->bel == BelId()) {
+ // FIXME: This should actually return "unknown!" because the
+ // sink is unplaced. Once the sink is placed, this constraint
+ // can be evaluated.
+ if (ctx->debug) {
+ log_info("BEL %s is not valid because sink cell %s/%s is not placed\n", ctx->nameOfBel(driver_bel),
+ port_ref.cell->name.c_str(ctx), port_ref.port.c_str(ctx));
+ }
+ return false;
}
BelId sink_bel = port_ref.cell->bel;
const auto &sink_bel_data = bel_info(ctx->chip_info, sink_bel);
Loc sink_loc = ctx->getBelLocation(port_ref.cell->bel);
- if(sink_bel.tile == driver_bel.tile && sink_bel_data.site == driver_bel_data.site) {
+ if (sink_bel.tile == driver_bel.tile && sink_bel_data.site == driver_bel_data.site) {
// This is a site local routing, even though this is a sink
// with a dedicated interconnect.
continue;
@@ -218,18 +225,15 @@ bool DedicatedInterconnect::is_driver_on_net_valid(BelId driver_bel,
sink_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[sink_bel.tile].type;
sink_type_bel_pin.type_bel_pin.bel_index = sink_bel.index;
- for(IdString sink_bel_pin : ctx->getBelPinsForCellPin(port_ref.cell, port_ref.port)) {
+ for (IdString sink_bel_pin : ctx->getBelPinsForCellPin(port_ref.cell, port_ref.port)) {
sink_type_bel_pin.type_bel_pin.bel_pin = sink_bel_pin;
// Do fast routing check to see if the pair of driver and sink
// every are valid.
- if(iter->second.count(sink_type_bel_pin) == 0) {
- if(ctx->verbose) {
- log_info("BEL %s is not valid because pin %s cannot reach %s/%s\n",
- ctx->nameOfBel(driver_bel),
- driver_bel_pin.c_str(ctx),
- ctx->nameOfBel(sink_bel),
- sink_bel_pin.c_str(ctx));
+ if (iter->second.count(sink_type_bel_pin) == 0) {
+ if (ctx->debug) {
+ log_info("BEL %s is not valid because pin %s cannot reach %s/%s\n", ctx->nameOfBel(driver_bel),
+ driver_bel_pin.c_str(ctx), ctx->nameOfBel(sink_bel), sink_bel_pin.c_str(ctx));
}
return false;
}
@@ -239,15 +243,11 @@ bool DedicatedInterconnect::is_driver_on_net_valid(BelId driver_bel,
// FIXME: This might be too slow, but it handles a case on
// SLICEL.COUT -> SLICEL.CIN has delta_y = {1, 2}, but the
// delta_y=2 case is rare.
- if(!check_routing(
- driver_bel, driver_bel_pin,
- sink_bel, sink_bel_pin)) {
- if(ctx->verbose) {
+ if (!check_routing(driver_bel, driver_bel_pin, sink_bel, sink_bel_pin)) {
+ if (ctx->debug) {
log_info("BEL %s is not valid because pin %s cannot be reach %s/%s (via detailed check)\n",
- ctx->nameOfBel(driver_bel),
- driver_bel_pin.c_str(ctx),
- ctx->nameOfBel(sink_bel),
- sink_bel_pin.c_str(ctx));
+ ctx->nameOfBel(driver_bel), driver_bel_pin.c_str(ctx), ctx->nameOfBel(sink_bel),
+ sink_bel_pin.c_str(ctx));
}
return false;
}
@@ -258,52 +258,62 @@ bool DedicatedInterconnect::is_driver_on_net_valid(BelId driver_bel,
return true;
}
-bool DedicatedInterconnect::is_sink_on_net_valid(BelId bel, const CellInfo* cell, IdString port_name, NetInfo *net) const {
- BelId driver_bel = net->driver.cell->bel;
- if(driver_bel == BelId()) {
- return true;
- }
-
+bool DedicatedInterconnect::is_sink_on_net_valid(BelId bel, const CellInfo *cell, IdString port_name,
+ NetInfo *net) const
+{
const auto &bel_data = bel_info(ctx->chip_info, bel);
- const auto &driver_bel_data = bel_info(ctx->chip_info, driver_bel);
-
Loc bel_loc = ctx->getBelLocation(bel);
- Loc driver_loc = ctx->getBelLocation(driver_bel);
- DeltaTileTypeBelPin driver_type_bel_pin;
- driver_type_bel_pin.delta_x = driver_loc.x - bel_loc.x;
- driver_type_bel_pin.delta_y = driver_loc.y - bel_loc.y;
- driver_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[driver_bel.tile].type;
- driver_type_bel_pin.type_bel_pin.bel_index = driver_bel.index;
- driver_type_bel_pin.type_bel_pin.bel_pin = get_only_value(ctx->getBelPinsForCellPin(net->driver.cell, net->driver.port));
+ BelId driver_bel = net->driver.cell->bel;
- for(IdString bel_pin : ctx->getBelPinsForCellPin(cell, port_name)) {
+ for (IdString bel_pin : ctx->getBelPinsForCellPin(cell, port_name)) {
TileTypeBelPin type_bel_pin;
type_bel_pin.tile_type = ctx->chip_info->tiles[bel.tile].type;
type_bel_pin.bel_index = bel.index;
type_bel_pin.bel_pin = bel_pin;
auto iter = sinks.find(type_bel_pin);
- if(iter == sinks.end()) {
+ if (iter == sinks.end()) {
// This BEL pin doesn't have a dedicate interconnect.
continue;
}
- if(bel.tile == driver_bel.tile && bel_data.site == driver_bel_data.site) {
+ if (driver_bel == BelId()) {
+ // FIXME: This should actually return "unknown!" because the
+ // driver is unplaced. Once the driver is placed, this constraint
+ // can be evaluated.
+ if (ctx->debug) {
+ log_info("BEL %s is not valid because driver cell %s/%s is not placed\n", ctx->nameOfBel(bel),
+ net->driver.cell->name.c_str(ctx), net->driver.port.c_str(ctx));
+ }
+ return false;
+ }
+
+ const auto &driver_bel_data = bel_info(ctx->chip_info, driver_bel);
+
+ if (bel.tile == driver_bel.tile && bel_data.site == driver_bel_data.site) {
// This is a site local routing, even though this is a sink
// with a dedicated interconnect.
continue;
}
+ Loc driver_loc = ctx->getBelLocation(driver_bel);
+
+ DeltaTileTypeBelPin driver_type_bel_pin;
+ driver_type_bel_pin.delta_x = driver_loc.x - bel_loc.x;
+ driver_type_bel_pin.delta_y = driver_loc.y - bel_loc.y;
+ driver_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[driver_bel.tile].type;
+ driver_type_bel_pin.type_bel_pin.bel_index = driver_bel.index;
+ driver_type_bel_pin.type_bel_pin.bel_pin =
+ get_only_value(ctx->getBelPinsForCellPin(net->driver.cell, net->driver.port));
+
// Do fast routing check to see if the pair of driver and sink
// every are valid.
- if(iter->second.count(driver_type_bel_pin) == 0) {
- if(ctx->verbose) {
- log_info("BEL %s is not valid because pin %s cannot be driven by %s/%s\n",
- ctx->nameOfBel(bel),
- bel_pin.c_str(ctx),
- ctx->nameOfBel(driver_bel),
- driver_type_bel_pin.type_bel_pin.bel_pin.c_str(ctx));
+ if (iter->second.count(driver_type_bel_pin) == 0) {
+ if (ctx->debug) {
+ log_info("BEL %s is not valid because pin %s cannot be driven by %s/%s\n", ctx->nameOfBel(bel),
+ bel_pin.c_str(ctx), ctx->nameOfBel(driver_bel),
+ driver_type_bel_pin.type_bel_pin.bel_pin.c_str(ctx));
}
return false;
}
@@ -313,15 +323,11 @@ bool DedicatedInterconnect::is_sink_on_net_valid(BelId bel, const CellInfo* cell
// FIXME: This might be too slow, but it handles a case on
// SLICEL.COUT -> SLICEL.CIN has delta_y = {1, 2}, but the
// delta_y=2 case is rare.
- if(!check_routing(
- driver_bel, driver_type_bel_pin.type_bel_pin.bel_pin,
- bel, bel_pin)) {
- if(ctx->verbose) {
+ if (!check_routing(driver_bel, driver_type_bel_pin.type_bel_pin.bel_pin, bel, bel_pin)) {
+ if (ctx->debug) {
log_info("BEL %s is not valid because pin %s cannot be driven by %s/%s (via detailed check)\n",
- ctx->nameOfBel(bel),
- bel_pin.c_str(ctx),
- ctx->nameOfBel(driver_bel),
- driver_type_bel_pin.type_bel_pin.bel_pin.c_str(ctx));
+ ctx->nameOfBel(bel), bel_pin.c_str(ctx), ctx->nameOfBel(driver_bel),
+ driver_type_bel_pin.type_bel_pin.bel_pin.c_str(ctx));
}
return false;
}
@@ -330,13 +336,14 @@ bool DedicatedInterconnect::is_sink_on_net_valid(BelId bel, const CellInfo* cell
return true;
}
-bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo* cell) const {
+bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo *cell) const
+{
NPNR_ASSERT(bel != BelId());
- for(const auto &port_pair : cell->ports) {
+ for (const auto &port_pair : cell->ports) {
IdString port_name = port_pair.first;
NetInfo *net = port_pair.second.net;
- if(net == nullptr) {
+ if (net == nullptr) {
continue;
}
@@ -344,12 +351,12 @@ bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo* cell)
NPNR_ASSERT(net->driver.cell != nullptr);
// Only check sink BELs.
- if(net->driver.cell == cell && net->driver.port == port_name) {
- if(!is_driver_on_net_valid(bel, cell, port_name, net)) {
+ if (net->driver.cell == cell && net->driver.port == port_name) {
+ if (!is_driver_on_net_valid(bel, cell, port_name, net)) {
return false;
}
} else {
- if(!is_sink_on_net_valid(bel, cell, port_name, net)) {
+ if (!is_sink_on_net_valid(bel, cell, port_name, net)) {
return false;
}
}
@@ -358,100 +365,84 @@ bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo* cell)
return true;
}
-void DedicatedInterconnect::print_dedicated_interconnect() const {
+void DedicatedInterconnect::print_dedicated_interconnect() const
+{
log_info("Found %zu sinks with dedicated interconnect\n", sinks.size());
log_info("Found %zu sources with dedicated interconnect\n", sources.size());
std::vector<TileTypeBelPin> sorted_keys;
- for(const auto & sink_to_srcs : sinks) {
+ for (const auto &sink_to_srcs : sinks) {
sorted_keys.push_back(sink_to_srcs.first);
}
- for(const auto & src_to_sinks : sources) {
+ for (const auto &src_to_sinks : sources) {
sorted_keys.push_back(src_to_sinks.first);
}
std::sort(sorted_keys.begin(), sorted_keys.end());
- for(const auto & key : sorted_keys) {
+ for (const auto &key : sorted_keys) {
auto iter = sinks.find(key);
- if(iter != sinks.end()) {
+ if (iter != sinks.end()) {
auto dst = key;
- for(const auto & src_delta : iter->second) {
+ for (const auto &src_delta : iter->second) {
auto src = src_delta.type_bel_pin;
auto delta_x = src_delta.delta_x;
auto delta_y = src_delta.delta_y;
- const TileTypeInfoPOD & src_tile_type = ctx->chip_info->tile_types[src.tile_type];
- const BelInfoPOD & src_bel_info = src_tile_type.bel_data[src.bel_index];
+ const TileTypeInfoPOD &src_tile_type = ctx->chip_info->tile_types[src.tile_type];
+ const BelInfoPOD &src_bel_info = src_tile_type.bel_data[src.bel_index];
IdString src_site_type = IdString(src_tile_type.site_types[src_bel_info.site]);
IdString src_bel_pin = src.bel_pin;
- const TileTypeInfoPOD & dst_tile_type = ctx->chip_info->tile_types[dst.tile_type];
- const BelInfoPOD & dst_bel_info = dst_tile_type.bel_data[dst.bel_index];
+ const TileTypeInfoPOD &dst_tile_type = ctx->chip_info->tile_types[dst.tile_type];
+ const BelInfoPOD &dst_bel_info = dst_tile_type.bel_data[dst.bel_index];
IdString dst_site_type = IdString(dst_tile_type.site_types[dst_bel_info.site]);
IdString dst_bel_pin = dst.bel_pin;
- log_info("%s.%s[%d]/%s/%s (%d, %d) -> %s.%s[%d]/%s/%s\n",
- IdString(src_tile_type.name).c_str(ctx),
- src_site_type.c_str(ctx),
- src_bel_info.site,
- IdString(src_bel_info.name).c_str(ctx),
- src_bel_pin.c_str(ctx),
- delta_x,
- delta_y,
- IdString(dst_tile_type.name).c_str(ctx),
- dst_site_type.c_str(ctx),
- dst_bel_info.site,
- IdString(dst_bel_info.name).c_str(ctx),
- dst_bel_pin.c_str(ctx));
-
+ log_info("%s.%s[%d]/%s/%s (%d, %d) -> %s.%s[%d]/%s/%s\n", IdString(src_tile_type.name).c_str(ctx),
+ src_site_type.c_str(ctx), src_bel_info.site, IdString(src_bel_info.name).c_str(ctx),
+ src_bel_pin.c_str(ctx), delta_x, delta_y, IdString(dst_tile_type.name).c_str(ctx),
+ dst_site_type.c_str(ctx), dst_bel_info.site, IdString(dst_bel_info.name).c_str(ctx),
+ dst_bel_pin.c_str(ctx));
}
} else {
auto src = key;
- for(const auto & dst_delta : sources.at(key)) {
+ for (const auto &dst_delta : sources.at(key)) {
auto dst = dst_delta.type_bel_pin;
auto delta_x = dst_delta.delta_x;
auto delta_y = dst_delta.delta_y;
- const TileTypeInfoPOD & src_tile_type = ctx->chip_info->tile_types[src.tile_type];
- const BelInfoPOD & src_bel_info = src_tile_type.bel_data[src.bel_index];
+ const TileTypeInfoPOD &src_tile_type = ctx->chip_info->tile_types[src.tile_type];
+ const BelInfoPOD &src_bel_info = src_tile_type.bel_data[src.bel_index];
IdString src_site_type = IdString(src_tile_type.site_types[src_bel_info.site]);
IdString src_bel_pin = src.bel_pin;
- const TileTypeInfoPOD & dst_tile_type = ctx->chip_info->tile_types[dst.tile_type];
- const BelInfoPOD & dst_bel_info = dst_tile_type.bel_data[dst.bel_index];
+ const TileTypeInfoPOD &dst_tile_type = ctx->chip_info->tile_types[dst.tile_type];
+ const BelInfoPOD &dst_bel_info = dst_tile_type.bel_data[dst.bel_index];
IdString dst_site_type = IdString(dst_tile_type.site_types[dst_bel_info.site]);
IdString dst_bel_pin = dst.bel_pin;
- log_info("%s.%s[%d]/%s/%s -> %s.%s[%d]/%s/%s (%d, %d)\n",
- IdString(src_tile_type.name).c_str(ctx),
- src_site_type.c_str(ctx),
- src_bel_info.site,
- IdString(src_bel_info.name).c_str(ctx),
- src_bel_pin.c_str(ctx),
- IdString(dst_tile_type.name).c_str(ctx),
- dst_site_type.c_str(ctx),
- dst_bel_info.site,
- IdString(dst_bel_info.name).c_str(ctx),
- dst_bel_pin.c_str(ctx),
- delta_x,
- delta_y);
-
+ log_info("%s.%s[%d]/%s/%s -> %s.%s[%d]/%s/%s (%d, %d)\n", IdString(src_tile_type.name).c_str(ctx),
+ src_site_type.c_str(ctx), src_bel_info.site, IdString(src_bel_info.name).c_str(ctx),
+ src_bel_pin.c_str(ctx), IdString(dst_tile_type.name).c_str(ctx), dst_site_type.c_str(ctx),
+ dst_bel_info.site, IdString(dst_bel_info.name).c_str(ctx), dst_bel_pin.c_str(ctx), delta_x,
+ delta_y);
}
}
}
}
-void DedicatedInterconnect::find_dedicated_interconnect() {
- for(BelId bel : ctx->getBels()) {
- const auto & bel_data = bel_info(ctx->chip_info, bel);
- if(bel_data.category != BEL_CATEGORY_LOGIC) {
+void DedicatedInterconnect::find_dedicated_interconnect()
+{
+ for (BelId bel : ctx->getBels()) {
+ const auto &bel_data = bel_info(ctx->chip_info, bel);
+ if (bel_data.category != BEL_CATEGORY_LOGIC) {
continue;
}
- if(bel_data.synthetic) {
+ if (bel_data.synthetic) {
continue;
}
- for(size_t i = 0; i < bel_data.num_bel_wires; ++i) {
- if(bel_data.types[i] != PORT_IN) {
+ for (size_t i = 0; i < bel_data.num_bel_wires; ++i) {
+ if (bel_data.types[i] != PORT_IN) {
continue;
}
@@ -464,27 +455,26 @@ void DedicatedInterconnect::find_dedicated_interconnect() {
}
std::unordered_set<TileTypeBelPin> seen_pins;
- for(auto sink_pair : sinks) {
- for(auto src : sink_pair.second) {
+ for (auto sink_pair : sinks) {
+ for (auto src : sink_pair.second) {
seen_pins.emplace(src.type_bel_pin);
}
}
- for(BelId bel : ctx->getBels()) {
- const auto & bel_data = bel_info(ctx->chip_info, bel);
- if(bel_data.category != BEL_CATEGORY_LOGIC) {
+ for (BelId bel : ctx->getBels()) {
+ const auto &bel_data = bel_info(ctx->chip_info, bel);
+ if (bel_data.category != BEL_CATEGORY_LOGIC) {
continue;
}
- if(bel_data.synthetic) {
+ if (bel_data.synthetic) {
continue;
}
- for(size_t i = 0; i < bel_data.num_bel_wires; ++i) {
- if(bel_data.types[i] != PORT_OUT) {
+ for (size_t i = 0; i < bel_data.num_bel_wires; ++i) {
+ if (bel_data.types[i] != PORT_OUT) {
continue;
}
-
IdString bel_pin(bel_data.ports[i]);
TileTypeBelPin type_bel_pin;
@@ -493,7 +483,7 @@ void DedicatedInterconnect::find_dedicated_interconnect() {
type_bel_pin.bel_pin = bel_pin;
// Don't visit src pins already handled in the sink expansion!
- if(seen_pins.count(type_bel_pin)) {
+ if (seen_pins.count(type_bel_pin)) {
continue;
}
@@ -506,7 +496,8 @@ void DedicatedInterconnect::find_dedicated_interconnect() {
}
}
-void DedicatedInterconnect::expand_sink_bel(BelId sink_bel, IdString sink_pin, WireId sink_wire) {
+void DedicatedInterconnect::expand_sink_bel(BelId sink_bel, IdString sink_pin, WireId sink_wire)
+{
NPNR_ASSERT(sink_bel != BelId());
#ifdef DEBUG_EXPANSION
log_info("Expanding from %s/%s\n", ctx->nameOfBel(sink_bel), pin.c_str(ctx));
@@ -514,7 +505,7 @@ void DedicatedInterconnect::expand_sink_bel(BelId sink_bel, IdString sink_pin, W
std::vector<WireNode> nodes_to_expand;
- const auto & sink_wire_data = ctx->wire_info(sink_wire);
+ const auto &sink_wire_data = ctx->wire_info(sink_wire);
NPNR_ASSERT(sink_wire_data.site != -1);
WireNode wire_node;
@@ -527,89 +518,91 @@ void DedicatedInterconnect::expand_sink_bel(BelId sink_bel, IdString sink_pin, W
Loc sink_loc = ctx->getBelLocation(sink_bel);
std::unordered_set<DeltaTileTypeBelPin> srcs;
- while(!nodes_to_expand.empty()) {
+ while (!nodes_to_expand.empty()) {
WireNode node_to_expand = nodes_to_expand.back();
nodes_to_expand.pop_back();
- for(PipId pip : ctx->getPipsUphill(node_to_expand.wire)) {
- if(ctx->is_pip_synthetic(pip)) {
+ for (PipId pip : ctx->getPipsUphill(node_to_expand.wire)) {
+ if (ctx->is_pip_synthetic(pip)) {
continue;
}
WireId wire = ctx->getPipSrcWire(pip);
- if(wire == WireId()) {
+ if (wire == WireId()) {
continue;
}
#ifdef DEBUG_EXPANSION
- log_info(" - At wire %s via %s\n",
- ctx->nameOfWire(wire), ctx->nameOfPip(pip));
+ log_info(" - At wire %s via %s\n", ctx->nameOfWire(wire), ctx->nameOfPip(pip));
#endif
WireNode next_node;
next_node.wire = wire;
next_node.depth = node_to_expand.depth += 1;
- if(next_node.depth > kMaxDepth) {
+ if (next_node.depth > kMaxDepth) {
// Dedicated routing should reach sources by kMaxDepth (with
// tuning).
//
// FIXME: Consider removing kMaxDepth and use kMaxSources?
+#ifdef DEBUG_EXPANSION
+ log_info(" - Exceeded max depth!\n");
+#endif
return;
}
- auto const & wire_data = ctx->wire_info(wire);
+ auto const &wire_data = ctx->wire_info(wire);
bool expand_node = true;
- if(ctx->is_site_port(pip)) {
- switch(node_to_expand.state) {
- case IN_SINK_SITE:
- NPNR_ASSERT(wire_data.site == -1);
- next_node.state = IN_ROUTING;
- break;
- case IN_ROUTING:
- NPNR_ASSERT(wire_data.site != -1);
- if(wire.tile == sink_wire.tile && wire_data.site == sink_wire_data.site) {
- // Dedicated routing won't have straight loops,
- // general routing looks like that.
+ if (ctx->is_site_port(pip)) {
+ switch (node_to_expand.state) {
+ case IN_SINK_SITE:
+ NPNR_ASSERT(wire_data.site == -1);
+ next_node.state = IN_ROUTING;
+ break;
+ case IN_ROUTING:
+ NPNR_ASSERT(wire_data.site != -1);
+ if (wire.tile == sink_wire.tile && wire_data.site == sink_wire_data.site) {
+ // Dedicated routing won't have straight loops,
+ // general routing looks like that.
#ifdef DEBUG_EXPANSION
- log_info(" - Not dedicated site routing because loop!");
+ log_info(" - Not dedicated site routing because loop!");
#endif
- return;
- }
- next_node.state = IN_SOURCE_SITE;
- break;
- case IN_SOURCE_SITE:
- // Once entering a site, do not leave it again.
- // This path is not a legal route!
- expand_node = false;
- break;
- default:
- // Unreachable!!!
- NPNR_ASSERT(false);
+ return;
+ }
+ next_node.state = IN_SOURCE_SITE;
+ break;
+ case IN_SOURCE_SITE:
+ // Once entering a site, do not leave it again.
+ // This path is not a legal route!
+ expand_node = false;
+ break;
+ default:
+ // Unreachable!!!
+ NPNR_ASSERT(false);
}
} else {
next_node.state = node_to_expand.state;
}
- if(expand_node) {
+ if (expand_node) {
nodes_to_expand.push_back(next_node);
} else {
continue;
}
- if(next_node.state == IN_SOURCE_SITE) {
- for(BelPin bel_pin : ctx->getWireBelPins(wire)) {
+ if (next_node.state == IN_SOURCE_SITE) {
+ for (BelPin bel_pin : ctx->getWireBelPins(wire)) {
BelId src_bel = bel_pin.bel;
- auto const & bel_data = bel_info(ctx->chip_info, src_bel);
+ auto const &bel_data = bel_info(ctx->chip_info, src_bel);
- if(bel_data.category != BEL_CATEGORY_LOGIC) {
+ if (bel_data.category != BEL_CATEGORY_LOGIC) {
continue;
}
- if(bel_data.synthetic) {
+ if (bel_data.synthetic) {
continue;
}
- if(ctx->getBelPinType(bel_pin.bel, bel_pin.pin) != PORT_OUT) {
+ if (ctx->getBelPinType(bel_pin.bel, bel_pin.pin) != PORT_OUT) {
continue;
}
@@ -637,24 +630,25 @@ void DedicatedInterconnect::expand_sink_bel(BelId sink_bel, IdString sink_pin, W
type_bel_pin.bel_pin = sink_pin;
auto result = sinks.emplace(type_bel_pin, srcs);
- if(!result.second) {
+ if (!result.second) {
// type_bel_pin was already present! Add any new sources from this
// sink type (if any);
- for(auto src : srcs) {
+ for (auto src : srcs) {
result.first->second.emplace(src);
}
}
}
-void DedicatedInterconnect::expand_source_bel(BelId src_bel, IdString src_pin, WireId src_wire) {
+void DedicatedInterconnect::expand_source_bel(BelId src_bel, IdString src_pin, WireId src_wire)
+{
NPNR_ASSERT(src_bel != BelId());
#ifdef DEBUG_EXPANSION
- log_info("Expanding from %s/%s\n", ctx->nameOfBel(src_bel), pin.c_str(ctx));
+ log_info("Expanding from %s/%s\n", ctx->nameOfBel(src_bel), src_pin.c_str(ctx));
#endif
std::vector<WireNode> nodes_to_expand;
- const auto & src_wire_data = ctx->wire_info(src_wire);
+ const auto &src_wire_data = ctx->wire_info(src_wire);
NPNR_ASSERT(src_wire_data.site != -1);
WireNode wire_node;
@@ -667,89 +661,91 @@ void DedicatedInterconnect::expand_source_bel(BelId src_bel, IdString src_pin, W
Loc src_loc = ctx->getBelLocation(src_bel);
std::unordered_set<DeltaTileTypeBelPin> dsts;
- while(!nodes_to_expand.empty()) {
+ while (!nodes_to_expand.empty()) {
WireNode node_to_expand = nodes_to_expand.back();
nodes_to_expand.pop_back();
- for(PipId pip : ctx->getPipsDownhill(node_to_expand.wire)) {
- if(ctx->is_pip_synthetic(pip)) {
+ for (PipId pip : ctx->getPipsDownhill(node_to_expand.wire)) {
+ if (ctx->is_pip_synthetic(pip)) {
continue;
}
WireId wire = ctx->getPipDstWire(pip);
- if(wire == WireId()) {
+ if (wire == WireId()) {
continue;
}
#ifdef DEBUG_EXPANSION
- log_info(" - At wire %s via %s\n",
- ctx->nameOfWire(wire), ctx->nameOfPip(pip));
+ log_info(" - At wire %s via %s\n", ctx->nameOfWire(wire), ctx->nameOfPip(pip));
#endif
WireNode next_node;
next_node.wire = wire;
next_node.depth = node_to_expand.depth += 1;
- if(next_node.depth > kMaxDepth) {
+ if (next_node.depth > kMaxDepth) {
// Dedicated routing should reach sources by kMaxDepth (with
// tuning).
//
// FIXME: Consider removing kMaxDepth and use kMaxSources?
+#ifdef DEBUG_EXPANSION
+ log_info(" - Exceeded max depth!\n");
+#endif
return;
}
- auto const & wire_data = ctx->wire_info(wire);
+ auto const &wire_data = ctx->wire_info(wire);
bool expand_node = true;
- if(ctx->is_site_port(pip)) {
- switch(node_to_expand.state) {
- case IN_SOURCE_SITE:
- NPNR_ASSERT(wire_data.site == -1);
- next_node.state = IN_ROUTING;
- break;
- case IN_ROUTING:
- NPNR_ASSERT(wire_data.site != -1);
- if(wire.tile == src_wire.tile && wire_data.site == src_wire_data.site) {
- // Dedicated routing won't have straight loops,
- // general routing looks like that.
+ if (ctx->is_site_port(pip)) {
+ switch (node_to_expand.state) {
+ case IN_SOURCE_SITE:
+ NPNR_ASSERT(wire_data.site == -1);
+ next_node.state = IN_ROUTING;
+ break;
+ case IN_ROUTING:
+ NPNR_ASSERT(wire_data.site != -1);
+ if (wire.tile == src_wire.tile && wire_data.site == src_wire_data.site) {
+ // Dedicated routing won't have straight loops,
+ // general routing looks like that.
#ifdef DEBUG_EXPANSION
- log_info(" - Not dedicated site routing because loop!");
+ log_info(" - Not dedicated site routing because loop!");
#endif
- return;
- }
- next_node.state = IN_SINK_SITE;
- break;
- case IN_SINK_SITE:
- // Once entering a site, do not leave it again.
- // This path is not a legal route!
- expand_node = false;
- break;
- default:
- // Unreachable!!!
- NPNR_ASSERT(false);
+ return;
+ }
+ next_node.state = IN_SINK_SITE;
+ break;
+ case IN_SINK_SITE:
+ // Once entering a site, do not leave it again.
+ // This path is not a legal route!
+ expand_node = false;
+ break;
+ default:
+ // Unreachable!!!
+ NPNR_ASSERT(false);
}
} else {
next_node.state = node_to_expand.state;
}
- if(expand_node) {
+ if (expand_node) {
nodes_to_expand.push_back(next_node);
} else {
continue;
}
- if(next_node.state == IN_SINK_SITE) {
- for(BelPin bel_pin : ctx->getWireBelPins(wire)) {
+ if (next_node.state == IN_SINK_SITE) {
+ for (BelPin bel_pin : ctx->getWireBelPins(wire)) {
BelId sink_bel = bel_pin.bel;
- auto const & bel_data = bel_info(ctx->chip_info, sink_bel);
+ auto const &bel_data = bel_info(ctx->chip_info, sink_bel);
- if(bel_data.category != BEL_CATEGORY_LOGIC) {
+ if (bel_data.category != BEL_CATEGORY_LOGIC) {
continue;
}
- if(bel_data.synthetic) {
+ if (bel_data.synthetic) {
continue;
}
- if(ctx->getBelPinType(bel_pin.bel, bel_pin.pin) != PORT_IN) {
+ if (ctx->getBelPinType(bel_pin.bel, bel_pin.pin) != PORT_IN) {
continue;
}
@@ -776,11 +772,11 @@ void DedicatedInterconnect::expand_source_bel(BelId src_bel, IdString src_pin, W
type_bel_pin.bel_index = src_bel.index;
type_bel_pin.bel_pin = src_pin;
- auto result = sinks.emplace(type_bel_pin, dsts);
- if(!result.second) {
+ auto result = sources.emplace(type_bel_pin, dsts);
+ if (!result.second) {
// type_bel_pin was already present! Add any new sources from this
// sink type (if any);
- for(auto dst : dsts) {
+ for (auto dst : dsts) {
result.first->second.emplace(dst);
}
}
diff --git a/fpga_interchange/dedicated_interconnect.h b/fpga_interchange/dedicated_interconnect.h
index d603039e..66e1d41b 100644
--- a/fpga_interchange/dedicated_interconnect.h
+++ b/fpga_interchange/dedicated_interconnect.h
@@ -24,40 +24,47 @@
NEXTPNR_NAMESPACE_BEGIN
-struct TileTypeBelPin {
+struct TileTypeBelPin
+{
int32_t tile_type;
int32_t bel_index;
IdString bel_pin;
- bool operator < (const TileTypeBelPin &other) const {
- if(tile_type >= other.tile_type) {
+ bool operator<(const TileTypeBelPin &other) const
+ {
+ if (tile_type >= other.tile_type) {
return false;
}
- if(bel_index >= other.bel_index) {
+ if (bel_index >= other.bel_index) {
return false;
}
return bel_pin < other.bel_pin;
}
- bool operator ==(const TileTypeBelPin &other) const {
+ bool operator==(const TileTypeBelPin &other) const
+ {
return tile_type == other.tile_type && bel_index == other.bel_index && bel_pin == other.bel_pin;
}
- bool operator !=(const TileTypeBelPin &other) const {
+ bool operator!=(const TileTypeBelPin &other) const
+ {
return tile_type != other.tile_type || bel_index != other.bel_index || bel_pin != other.bel_pin;
}
};
-struct DeltaTileTypeBelPin {
+struct DeltaTileTypeBelPin
+{
int32_t delta_x;
int32_t delta_y;
TileTypeBelPin type_bel_pin;
- bool operator ==(const DeltaTileTypeBelPin &other) const {
+ bool operator==(const DeltaTileTypeBelPin &other) const
+ {
return delta_x == other.delta_x && delta_y == other.delta_y && type_bel_pin == other.type_bel_pin;
}
- bool operator !=(const DeltaTileTypeBelPin &other) const {
+ bool operator!=(const DeltaTileTypeBelPin &other) const
+ {
return delta_x != other.delta_x || delta_y != other.delta_y || type_bel_pin != other.type_bel_pin;
}
};
@@ -105,7 +112,8 @@ struct Context;
// This class discovers dedicated interconnect by examing the routing graph.
// This discovery make be expensive, and require caching to accelerate
// startup.
-struct DedicatedInterconnect {
+struct DedicatedInterconnect
+{
const Context *ctx;
std::unordered_map<TileTypeBelPin, std::unordered_set<DeltaTileTypeBelPin>> sinks;
@@ -117,20 +125,16 @@ struct DedicatedInterconnect {
// interconnect?
//
// Note: Only BEL pin sinks are checked.
- bool isBelLocationValid(BelId bel, const CellInfo* cell) const;
+ bool isBelLocationValid(BelId bel, const CellInfo *cell) const;
void find_dedicated_interconnect();
void print_dedicated_interconnect() const;
- bool check_routing(
- BelId src_bel, IdString src_bel_pin,
- BelId dst_bel, IdString dst_bel_pin) const;
+ bool check_routing(BelId src_bel, IdString src_bel_pin, BelId dst_bel, IdString dst_bel_pin) const;
void expand_sink_bel(BelId bel, IdString pin, WireId wire);
void expand_source_bel(BelId bel, IdString pin, WireId wire);
- bool is_driver_on_net_valid(BelId driver_bel,
- const CellInfo* cell, IdString driver_port, NetInfo *net) const;
- bool is_sink_on_net_valid(BelId bel, const CellInfo* cell,
- IdString port_name, NetInfo *net) const;
+ bool is_driver_on_net_valid(BelId driver_bel, const CellInfo *cell, IdString driver_port, NetInfo *net) const;
+ bool is_sink_on_net_valid(BelId bel, const CellInfo *cell, IdString port_name, NetInfo *net) const;
};
NEXTPNR_NAMESPACE_END
diff --git a/fpga_interchange/fpga_interchange.cpp b/fpga_interchange/fpga_interchange.cpp
index 027513c8..a1642789 100644
--- a/fpga_interchange/fpga_interchange.cpp
+++ b/fpga_interchange/fpga_interchange.cpp
@@ -111,20 +111,37 @@ static PhysicalNetlist::PhysNetlist::RouteBranch::Builder emit_branch(
if(bel_data.category == BEL_CATEGORY_LOGIC) {
// This is a psuedo site-pip.
auto in_bel_pin = branch.getRouteSegment().initBelPin();
- IdString src_wire_name = IdString(tile_type.wire_data[pip_data.src_index].name);
- IdString dst_wire_name = IdString(tile_type.wire_data[pip_data.dst_index].name);
+ WireId src_wire = ctx->getPipSrcWire(pip);
+ WireId dst_wire = ctx->getPipDstWire(pip);
+
+ IdString src_pin;
+ IdString dst_pin;
+ for(IdString pin : ctx->getBelPins(bel)) {
+ if(ctx->getBelPinWire(bel, pin) == src_wire) {
+ NPNR_ASSERT(src_pin == IdString());
+ src_pin = pin;
+ }
+
+ if(ctx->getBelPinWire(bel, pin) == dst_wire) {
+ NPNR_ASSERT(dst_pin == IdString());
+ dst_pin = pin;
+ }
+ }
+
+ NPNR_ASSERT(src_pin != IdString());
+ NPNR_ASSERT(dst_pin != IdString());
int bel_idx = strings->get_index(bel_name[1].str(ctx));
in_bel_pin.setSite(site_idx);
in_bel_pin.setBel(bel_idx);
- in_bel_pin.setPin(strings->get_index(src_wire_name.str(ctx)));
+ in_bel_pin.setPin(strings->get_index(src_pin.str(ctx)));
auto subbranch = branch.initBranches(1);
auto bel_pin_branch = subbranch[0];
auto out_bel_pin = bel_pin_branch.getRouteSegment().initBelPin();
out_bel_pin.setSite(site_idx);
out_bel_pin.setBel(bel_idx);
- out_bel_pin.setPin(strings->get_index(dst_wire_name.str(ctx)));
+ out_bel_pin.setPin(strings->get_index(dst_pin.str(ctx)));
return bel_pin_branch;
} else if(bel_data.category == BEL_CATEGORY_ROUTING) {
@@ -614,6 +631,8 @@ struct ModuleReader {
ModuleReader(const LogicalNetlistImpl *root,
LogicalNetlist::Netlist::CellInstance::Reader cell_inst, bool is_top);
+
+ size_t translate_port_index(LogicalNetlist::Netlist::PortInstance::Reader port_inst) const;
};
struct PortReader {
@@ -850,8 +869,8 @@ struct LogicalNetlistImpl
bool is_vector_bit_constant(const std::vector<int32_t> &bits, int i) const
{
- // FIXME: Check if this is right. Assumption is that cells have been
- // emitted for GND and VCC, e.g. VCC vcc(.P(vcc_net)).
+ // Note: This appears weird, but is correct. This is because VCC/GND
+ // nets are not handled in frontend_base for FPGA interchange.
return false;
}
@@ -929,11 +948,8 @@ ModuleReader::ModuleReader(const LogicalNetlistImpl *root,
PortKey port_key(inst_idx, port_inst.getPort());
std::vector<int32_t> & port_connections = connections.at(port_key);
- if(port_inst.getBusIdx().isSingleBit()) {
- port_connections[0] = net_idx;
- } else {
- port_connections.at(port_inst.getBusIdx().getIdx()) = net_idx;
- }
+ size_t port_idx = translate_port_index(port_inst);
+ port_connections.at(port_idx) = net_idx;
}
}
@@ -993,5 +1009,19 @@ void FpgaInterchange::read_logical_netlist(Context * ctx, const std::string &fil
GenericFrontend<LogicalNetlistImpl>(ctx, netlist_reader, /*split_io=*/false)();
}
+size_t ModuleReader::translate_port_index(LogicalNetlist::Netlist::PortInstance::Reader port_inst) const {
+ LogicalNetlist::Netlist::Port::Reader port = root->root.getPortList()[port_inst.getPort()];
+ if(port_inst.getBusIdx().isSingleBit()) {
+ NPNR_ASSERT(port.isBit());
+ return 0;
+ } else {
+ NPNR_ASSERT(port.isBus());
+ uint32_t idx = port_inst.getBusIdx().getIdx();
+ size_t width = get_port_width(port);
+ NPNR_ASSERT(idx >= 0 && idx < width);
+ return width - 1 - idx;
+ }
+}
+
NEXTPNR_NAMESPACE_END
diff --git a/fpga_interchange/site_router.cc b/fpga_interchange/site_router.cc
index a22dfcd3..7232b635 100644
--- a/fpga_interchange/site_router.cc
+++ b/fpga_interchange/site_router.cc
@@ -22,9 +22,9 @@
NEXTPNR_NAMESPACE_BEGIN
-bool verbose_site_router(const Context *ctx) { return ctx->verbose; }
+bool verbose_site_router(const Context *ctx) { return ctx->debug; }
-void Arch::SiteRouter::bindBel(CellInfo *cell)
+void SiteRouter::bindBel(CellInfo *cell)
{
auto result = cells_in_site.emplace(cell);
NPNR_ASSERT(result.second);
@@ -32,7 +32,7 @@ void Arch::SiteRouter::bindBel(CellInfo *cell)
dirty = true;
}
-void Arch::SiteRouter::unbindBel(CellInfo *cell)
+void SiteRouter::unbindBel(CellInfo *cell)
{
NPNR_ASSERT(cells_in_site.erase(cell) == 1);
@@ -56,6 +56,22 @@ struct RouteNode
PipId pip; // What pip was taken to reach this node.
WireId wire; // What wire is this routing node located at?
+
+ void print_route(const Context *ctx) const
+ {
+ log_info(" %s (via %s)\n", ctx->nameOfWire(wire), ctx->nameOfPip(pip));
+
+ Node node = parent;
+ while (node != RouteNode::Node()) {
+ if (node->pip != PipId()) {
+ log_info(" %s (via %s)\n", ctx->nameOfWire(node->wire), ctx->nameOfPip(node->pip));
+ } else {
+ log_info(" %s\n", ctx->nameOfWire(node->wire));
+ }
+
+ node = node->parent;
+ }
+ }
};
struct RouteNodeStorage
@@ -260,10 +276,9 @@ struct SiteInformation
if (!result.second && result.first->second != net) {
// Conflict, this wire is already in use and it's not
// doesn't match!
- if(verbose_site_router(ctx)) {
- log_info("Cannot select route because net %s != net %s\n",
- result.first->second->name.c_str(ctx),
- net->name.c_str(ctx));
+ if (verbose_site_router(ctx)) {
+ log_info("Cannot select route because net %s != net %s\n", result.first->second->name.c_str(ctx),
+ net->name.c_str(ctx));
}
return false;
@@ -309,6 +324,8 @@ struct SiteInformation
std::unordered_set<const NetInfo *> nets_fully_within_site;
bool is_net_within_site(const NetInfo *net) const { return nets_fully_within_site.count(net); }
+
+ void print_current_state() const;
};
struct SiteExpansionLoop
@@ -605,6 +622,10 @@ bool route_site(const Context *ctx, SiteInformation *site_info)
std::unordered_map<WireId, std::unordered_set<const NetInfo *>> wire_congestion;
+ for (auto &consumed_wire : site_info->consumed_wires) {
+ wire_congestion[consumed_wire.first].emplace(consumed_wire.second);
+ }
+
for (auto &expansion_wire : wire_to_expansion) {
auto &expansion = *expansion_wire.second;
@@ -642,8 +663,15 @@ bool route_site(const Context *ctx, SiteInformation *site_info)
if (uncongestion_route != RouteNode::Node()) {
// Select a trivially uncongested route if possible.
- NPNR_ASSERT(site_info->select_route(expansion.first_wire, uncongestion_route, expansion.net_for_wire,
- &newly_consumed_wires));
+ if (!site_info->select_route(expansion.first_wire, uncongestion_route, expansion.net_for_wire,
+ &newly_consumed_wires)) {
+ log_info("Failed to bind uncongested path with wire %s on net %s\n",
+ ctx->nameOfWire(expansion.first_wire), expansion.net_for_wire->name.c_str(ctx));
+ uncongestion_route->print_route(ctx);
+
+ site_info->print_current_state();
+ NPNR_ASSERT(false);
+ }
completed_wires.push_back(expansion.first_wire);
}
}
@@ -676,7 +704,7 @@ bool route_site(const Context *ctx, SiteInformation *site_info)
return true;
}
-bool Arch::SiteRouter::checkSiteRouting(const Context *ctx, const Arch::TileStatus &tile_status) const
+bool SiteRouter::checkSiteRouting(const Context *ctx, const TileStatus &tile_status) const
{
if (!dirty) {
return site_ok;
@@ -753,4 +781,42 @@ bool Arch::SiteRouter::checkSiteRouting(const Context *ctx, const Arch::TileStat
return site_ok;
}
+void SiteInformation::print_current_state() const
+{
+ const CellInfo *cell = *cells_in_site.begin();
+ BelId bel = cell->bel;
+ const auto &bel_data = bel_info(ctx->chip_info, bel);
+ const auto &site_inst = site_inst_info(ctx->chip_info, bel.tile, bel_data.site);
+
+ log_info("Site %s\n", site_inst.name.get());
+
+ log_info(" Cells in site:\n");
+ for (CellInfo *cell : cells_in_site) {
+ log_info(" - %s (%s)\n", cell->name.c_str(ctx), cell->type.c_str(ctx));
+ }
+
+ log_info(" Nets in site:\n");
+ for (auto *net : nets_in_site) {
+ log_info(" - %s, pins in site:\n", net->name.c_str(ctx));
+ if (net->driver.cell && cells_in_site.count(net->driver.cell)) {
+ log_info(" - %s/%s (%s)\n", net->driver.cell->name.c_str(ctx), net->driver.port.c_str(ctx),
+ net->driver.cell->type.c_str(ctx));
+ }
+
+ for (const auto user : net->users) {
+ if (user.cell && cells_in_site.count(user.cell)) {
+ log_info(" - %s/%s (%s)\n", user.cell->name.c_str(ctx), user.port.c_str(ctx),
+ user.cell->type.c_str(ctx));
+ }
+ }
+ }
+
+ log_info(" Consumed wires:\n");
+ for (auto consumed_wire : consumed_wires) {
+ WireId wire = consumed_wire.first;
+ const NetInfo *net = consumed_wire.second;
+ log_info(" - %s is bound to %s\n", ctx->nameOfWire(wire), net->name.c_str(ctx));
+ }
+}
+
NEXTPNR_NAMESPACE_END
diff --git a/fpga_interchange/site_router.h b/fpga_interchange/site_router.h
new file mode 100644
index 00000000..561dae9d
--- /dev/null
+++ b/fpga_interchange/site_router.h
@@ -0,0 +1,45 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2021 Symbiflow Authors
+ *
+ *
+ * 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.
+ *
+ */
+
+#ifndef NEXTPNR_H
+#error Include "site_router.h" via "nextpnr.h" only.
+#endif
+
+NEXTPNR_NAMESPACE_BEGIN
+
+struct Context;
+struct TileStatus;
+
+struct SiteRouter
+{
+ SiteRouter(int16_t site) : site(site), dirty(false), site_ok(true) {}
+
+ std::unordered_set<CellInfo *> cells_in_site;
+ const int16_t site;
+
+ mutable bool dirty;
+ mutable bool site_ok;
+
+ void bindBel(CellInfo *cell);
+ void unbindBel(CellInfo *cell);
+ bool checkSiteRouting(const Context *ctx, const TileStatus &tile_status) const;
+};
+
+NEXTPNR_NAMESPACE_END