Fix assorted bugs in FPGA interchange.

Fixes:
 - Only use map constant pins during routing, and not during placement.
 - Unmapped cell ports have no BEL pins.
 - Fix SiteRouter congestion not taking into account initial expansion.
 - Fix psuedo-site pip output.

Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
This commit is contained in:
Keith Rothman 2021-02-23 13:35:45 -08:00
parent 184665652e
commit a30043c8da
8 changed files with 624 additions and 420 deletions

View File

@ -195,10 +195,7 @@ Arch::Arch(ArchArgs args) : args(args)
default_tags.resize(max_tag_count); 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); 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") { if (placer == "heap") {
PlacerHeapCfg cfg(getCtx()); PlacerHeapCfg cfg(getCtx());
cfg.criticalityExponent = 7; cfg.criticalityExponent = 7;
@ -644,6 +649,14 @@ bool Arch::route()
{ {
std::string router = str_or_default(settings, id("router"), defaultRouter); 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; bool result;
if (router == "router1") { if (router == "router1") {
result = router1(getCtx(), Router1Cfg(getCtx())); 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 delay_t Arch::estimateDelay(WireId src, WireId dst) const
{ {
// FIXME: Implement something to push the A* router in the right direction. // 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 delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
{ {
// FIXME: Implement when adding timing-driven place and route. // 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 bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const
@ -755,20 +788,25 @@ const std::vector<std::string> Arch::availablePlacers = {"sa",
const std::string Arch::defaultRouter = "router2"; const std::string Arch::defaultRouter = "router2";
const std::vector<std::string> Arch::availableRouters = {"router1", "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_mapping = mapping;
cell->cell_bel_pins.clear(); 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]; 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) { for (const auto &pin_map : cell_pin_map.common_pins) {
IdString cell_pin(pin_map.cell_pin); IdString cell_pin(pin_map.cell_pin);
IdString bel_pin(pin_map.bel_pin); IdString bel_pin(pin_map.bel_pin);
if (cell_pin.str(this) == "GND") { if (cell_pin.str(this) == "GND") {
IdString gnd_net_name(chip_info->constants->gnd_net_name); if (bind_constants) {
PortInfo port_info; PortInfo port_info;
port_info.name = bel_pin; port_info.name = bel_pin;
port_info.type = PORT_IN; port_info.type = PORT_IN;
@ -778,18 +816,19 @@ void Arch::map_cell_pins(CellInfo *cell, int32_t mapping)
if (result.second) { if (result.second) {
cell->cell_bel_pins[bel_pin].push_back(bel_pin); cell->cell_bel_pins[bel_pin].push_back(bel_pin);
connectPort(gnd_net_name, cell->name, bel_pin); connectPort(gnd_net_name, cell->name, bel_pin);
cell->const_ports.emplace(bel_pin);
} else { } else {
NPNR_ASSERT(result.first->second.net == getNetByAlias(gnd_net_name)); NPNR_ASSERT(result.first->second.net == getNetByAlias(gnd_net_name));
auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector<IdString>({bel_pin})); 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.at(0) == bel_pin);
NPNR_ASSERT(result2.first->second.size() == 1); NPNR_ASSERT(result2.first->second.size() == 1);
} }
}
continue; continue;
} }
if (cell_pin.str(this) == "VCC") { if (cell_pin.str(this) == "VCC") {
IdString vcc_net_name(chip_info->constants->vcc_net_name); if (bind_constants) {
PortInfo port_info; PortInfo port_info;
port_info.name = bel_pin; port_info.name = bel_pin;
port_info.type = PORT_IN; port_info.type = PORT_IN;
@ -799,13 +838,14 @@ void Arch::map_cell_pins(CellInfo *cell, int32_t mapping)
if (result.second) { if (result.second) {
cell->cell_bel_pins[bel_pin].push_back(bel_pin); cell->cell_bel_pins[bel_pin].push_back(bel_pin);
connectPort(vcc_net_name, cell->name, bel_pin); connectPort(vcc_net_name, cell->name, bel_pin);
cell->const_ports.emplace(bel_pin);
} else { } else {
NPNR_ASSERT(result.first->second.net == getNetByAlias(vcc_net_name)); NPNR_ASSERT(result.first->second.net == getNetByAlias(vcc_net_name));
auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector<IdString>({bel_pin})); 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.at(0) == bel_pin);
NPNR_ASSERT(result2.first->second.size() == 1); NPNR_ASSERT(result2.first->second.size() == 1);
} }
}
continue; continue;
} }
@ -830,30 +870,44 @@ void Arch::map_cell_pins(CellInfo *cell, int32_t mapping)
IdString bel_pin(pin_map.bel_pin); IdString bel_pin(pin_map.bel_pin);
if (cell_pin.str(this) == "GND") { if (cell_pin.str(this) == "GND") {
if (bind_constants) {
PortInfo port_info; PortInfo port_info;
port_info.name = bel_pin; port_info.name = bel_pin;
port_info.type = PORT_IN; port_info.type = PORT_IN;
auto result = cell->ports.emplace(bel_pin, port_info); auto result = cell->ports.emplace(bel_pin, port_info);
NPNR_ASSERT(result.second); if (result.second) {
cell->cell_bel_pins[bel_pin].push_back(bel_pin); cell->cell_bel_pins[bel_pin].push_back(bel_pin);
connectPort(gnd_net_name, cell->name, bel_pin);
connectPort(IdString(chip_info->constants->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; continue;
} }
if (cell_pin.str(this) == "VCC") { if (cell_pin.str(this) == "VCC") {
if (bind_constants) {
PortInfo port_info; PortInfo port_info;
port_info.name = bel_pin; port_info.name = bel_pin;
port_info.type = PORT_IN; port_info.type = PORT_IN;
auto result = cell->ports.emplace(bel_pin, port_info); auto result = cell->ports.emplace(bel_pin, port_info);
NPNR_ASSERT(result.second); if (result.second) {
cell->cell_bel_pins[bel_pin].push_back(bel_pin); cell->cell_bel_pins[bel_pin].push_back(bel_pin);
connectPort(vcc_net_name, cell->name, bel_pin);
connectPort(IdString(chip_info->constants->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; continue;
} }
@ -912,7 +966,8 @@ size_t Arch::get_cell_type_index(IdString cell_type) const
return cell_offset; return cell_offset;
} }
void Arch::merge_constant_nets() { void Arch::merge_constant_nets()
{
NetInfo *gnd_net = nullptr; NetInfo *gnd_net = nullptr;
NetInfo *vcc_net = nullptr; NetInfo *vcc_net = nullptr;
@ -1062,6 +1117,24 @@ void Arch::merge_constant_nets() {
} }
} }
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. // Instance constraint templates.
template void Arch::ArchConstraints::bindBel(Arch::ArchConstraints::TagState *, const Arch::ConstraintRange); template void Arch::ArchConstraints::bindBel(Arch::ArchConstraints::TagState *, const Arch::ConstraintRange);
template void Arch::ArchConstraints::unbindBel(Arch::ArchConstraints::TagState *, const Arch::ConstraintRange); template void Arch::ArchConstraints::unbindBel(Arch::ArchConstraints::TagState *, const Arch::ConstraintRange);

View File

@ -30,6 +30,7 @@
#include "constraints.h" #include "constraints.h"
#include "dedicated_interconnect.h" #include "dedicated_interconnect.h"
#include "site_router.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
@ -773,7 +774,14 @@ struct ArchRanges
using BucketBelRangeT = FilteredBelRange; 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> struct Arch : ArchAPI<ArchRanges>
{ {
@ -787,31 +795,6 @@ struct Arch : ArchAPI<ArchRanges>
std::unordered_map<WireId, NetInfo *> wire_to_net; std::unordered_map<WireId, NetInfo *> wire_to_net;
std::unordered_map<PipId, NetInfo *> pip_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; DedicatedInterconnect dedicated_interconnect;
std::unordered_map<int32_t, TileStatus> tileStatus; 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; } 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; void map_port_pins(BelId bel, CellInfo *cell) const;
TileStatus &get_tile_status(int32_t tile) TileStatus &get_tile_status(int32_t tile)
@ -931,10 +914,13 @@ struct Arch : ArchAPI<ArchRanges>
if (io_port_types.count(cell->type) == 0) { if (io_port_types.count(cell->type) == 0) {
int32_t mapping = bel_info(chip_info, bel).pin_map[get_cell_type_index(cell->type)]; 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); NPNR_ASSERT(mapping >= 0);
if (cell->cell_mapping != mapping) { 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)); constraints.bindBel(tile_status.tags.data(), get_cell_constraints(bel, cell->type));
} else { } else {
@ -1078,10 +1064,7 @@ struct Arch : ArchAPI<ArchRanges>
return str_range; return str_range;
} }
const std::vector<IdString> &getBelPinsForCellPin(const CellInfo *cell_info, IdString pin) const override const std::vector<IdString> &getBelPinsForCellPin(const CellInfo *cell_info, IdString pin) const override;
{
return cell_info->cell_bel_pins.at(pin);
}
// ------------------------------------------------- // -------------------------------------------------
@ -1718,6 +1701,11 @@ struct Arch : ArchAPI<ArchRanges>
} }
void merge_constant_nets(); 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 NEXTPNR_NAMESPACE_END

View File

@ -243,7 +243,9 @@ void Arch::pack_ports()
for (CellInfo *cell : placed_cells) { for (CellInfo *cell : placed_cells) {
NPNR_ASSERT(cell->bel != BelId()); 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));
}
} }
} }
} }

View File

@ -18,8 +18,8 @@
* *
*/ */
#include "nextpnr.h"
#include "log.h" #include "log.h"
#include "nextpnr.h"
#include "util.h" #include "util.h"
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
@ -32,13 +32,15 @@ NEXTPNR_NAMESPACE_BEGIN
// terminate at another site. Routing that "flys" over a site is expressed as // 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 // a psuedo-pip connected the relevant site pin wires, rather than traversing
// the site. // the site.
enum WireNodeState { enum WireNodeState
{
IN_SINK_SITE = 0, IN_SINK_SITE = 0,
IN_ROUTING = 1, IN_ROUTING = 1,
IN_SOURCE_SITE = 2 IN_SOURCE_SITE = 2
}; };
struct WireNode { struct WireNode
{
WireId wire; WireId wire;
WireNodeState state; WireNodeState state;
int depth; int depth;
@ -50,7 +52,8 @@ struct WireNode {
// interconnect. // interconnect.
constexpr int kMaxDepth = 20; constexpr int kMaxDepth = 20;
void DedicatedInterconnect::init(const Context *ctx) { void DedicatedInterconnect::init(const Context *ctx)
{
this->ctx = ctx; this->ctx = ctx;
if (ctx->debug) { if (ctx->debug) {
@ -63,9 +66,9 @@ void DedicatedInterconnect::init(const Context *ctx) {
} }
} }
bool DedicatedInterconnect::check_routing( bool DedicatedInterconnect::check_routing(BelId src_bel, IdString src_bel_pin, BelId dst_bel,
BelId src_bel, IdString src_bel_pin, IdString dst_bel_pin) const
BelId dst_bel, IdString dst_bel_pin) const { {
std::vector<WireNode> nodes_to_expand; std::vector<WireNode> nodes_to_expand;
WireId src_wire = ctx->getBelPinWire(src_bel, src_bel_pin); WireId src_wire = ctx->getBelPinWire(src_bel, src_bel_pin);
@ -100,8 +103,7 @@ bool DedicatedInterconnect::check_routing(
} }
#ifdef DEBUG_EXPANSION #ifdef DEBUG_EXPANSION
log_info(" - At wire %s via %s\n", log_info(" - At wire %s via %s\n", ctx->nameOfWire(wire), ctx->nameOfPip(pip));
ctx->nameOfWire(wire), ctx->nameOfPip(pip));
#endif #endif
WireNode next_node; WireNode next_node;
@ -159,12 +161,9 @@ bool DedicatedInterconnect::check_routing(
if (next_node.state == IN_SINK_SITE) { if (next_node.state == IN_SINK_SITE) {
for (BelPin bel_pin : ctx->getWireBelPins(wire)) { for (BelPin bel_pin : ctx->getWireBelPins(wire)) {
if (bel_pin.bel == dst_bel && bel_pin.pin == dst_bel_pin) { if (bel_pin.bel == dst_bel && bel_pin.pin == dst_bel_pin) {
if(ctx->verbose) { if (ctx->debug) {
log_info("Valid dedicated interconnect from %s/%s to %s/%s\n", log_info("Valid dedicated interconnect from %s/%s to %s/%s\n", ctx->nameOfBel(src_bel),
ctx->nameOfBel(src_bel), src_bel_pin.c_str(ctx), ctx->nameOfBel(dst_bel), dst_bel_pin.c_str(ctx));
src_bel_pin.c_str(ctx),
ctx->nameOfBel(dst_bel),
dst_bel_pin.c_str(ctx));
} }
return true; return true;
} }
@ -176,8 +175,9 @@ bool DedicatedInterconnect::check_routing(
return false; return false;
} }
bool DedicatedInterconnect::is_driver_on_net_valid(BelId driver_bel, bool DedicatedInterconnect::is_driver_on_net_valid(BelId driver_bel, const CellInfo *cell, IdString driver_port,
const CellInfo* cell, IdString driver_port, NetInfo *net) const { NetInfo *net) const
{
const auto &driver_bel_data = bel_info(ctx->chip_info, driver_bel); const auto &driver_bel_data = bel_info(ctx->chip_info, driver_bel);
TileTypeBelPin type_bel_pin; TileTypeBelPin type_bel_pin;
@ -199,7 +199,14 @@ bool DedicatedInterconnect::is_driver_on_net_valid(BelId driver_bel,
NPNR_ASSERT(port_ref.cell != nullptr); NPNR_ASSERT(port_ref.cell != nullptr);
if (port_ref.cell->bel == BelId()) { if (port_ref.cell->bel == BelId()) {
return true; // 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; BelId sink_bel = port_ref.cell->bel;
@ -224,12 +231,9 @@ bool DedicatedInterconnect::is_driver_on_net_valid(BelId driver_bel,
// Do fast routing check to see if the pair of driver and sink // Do fast routing check to see if the pair of driver and sink
// every are valid. // every are valid.
if (iter->second.count(sink_type_bel_pin) == 0) { if (iter->second.count(sink_type_bel_pin) == 0) {
if(ctx->verbose) { if (ctx->debug) {
log_info("BEL %s is not valid because pin %s cannot reach %s/%s\n", log_info("BEL %s is not valid because pin %s cannot reach %s/%s\n", ctx->nameOfBel(driver_bel),
ctx->nameOfBel(driver_bel), driver_bel_pin.c_str(ctx), ctx->nameOfBel(sink_bel), sink_bel_pin.c_str(ctx));
driver_bel_pin.c_str(ctx),
ctx->nameOfBel(sink_bel),
sink_bel_pin.c_str(ctx));
} }
return false; return false;
} }
@ -239,14 +243,10 @@ bool DedicatedInterconnect::is_driver_on_net_valid(BelId driver_bel,
// FIXME: This might be too slow, but it handles a case on // FIXME: This might be too slow, but it handles a case on
// SLICEL.COUT -> SLICEL.CIN has delta_y = {1, 2}, but the // SLICEL.COUT -> SLICEL.CIN has delta_y = {1, 2}, but the
// delta_y=2 case is rare. // delta_y=2 case is rare.
if(!check_routing( if (!check_routing(driver_bel, driver_bel_pin, sink_bel, sink_bel_pin)) {
driver_bel, driver_bel_pin, if (ctx->debug) {
sink_bel, sink_bel_pin)) {
if(ctx->verbose) {
log_info("BEL %s is not valid because pin %s cannot be reach %s/%s (via detailed check)\n", log_info("BEL %s is not valid because pin %s cannot be reach %s/%s (via detailed check)\n",
ctx->nameOfBel(driver_bel), ctx->nameOfBel(driver_bel), driver_bel_pin.c_str(ctx), ctx->nameOfBel(sink_bel),
driver_bel_pin.c_str(ctx),
ctx->nameOfBel(sink_bel),
sink_bel_pin.c_str(ctx)); sink_bel_pin.c_str(ctx));
} }
return false; return false;
@ -258,24 +258,13 @@ bool DedicatedInterconnect::is_driver_on_net_valid(BelId driver_bel,
return true; return true;
} }
bool DedicatedInterconnect::is_sink_on_net_valid(BelId bel, const CellInfo* cell, IdString port_name, NetInfo *net) const { bool DedicatedInterconnect::is_sink_on_net_valid(BelId bel, const CellInfo *cell, IdString port_name,
BelId driver_bel = net->driver.cell->bel; NetInfo *net) const
if(driver_bel == BelId()) { {
return true;
}
const auto &bel_data = bel_info(ctx->chip_info, bel); 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 bel_loc = ctx->getBelLocation(bel);
Loc driver_loc = ctx->getBelLocation(driver_bel);
DeltaTileTypeBelPin driver_type_bel_pin; BelId driver_bel = net->driver.cell->bel;
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));
for (IdString bel_pin : ctx->getBelPinsForCellPin(cell, port_name)) { for (IdString bel_pin : ctx->getBelPinsForCellPin(cell, port_name)) {
TileTypeBelPin type_bel_pin; TileTypeBelPin type_bel_pin;
@ -289,20 +278,41 @@ bool DedicatedInterconnect::is_sink_on_net_valid(BelId bel, const CellInfo* cell
continue; continue;
} }
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) { 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 // This is a site local routing, even though this is a sink
// with a dedicated interconnect. // with a dedicated interconnect.
continue; 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 // Do fast routing check to see if the pair of driver and sink
// every are valid. // every are valid.
if (iter->second.count(driver_type_bel_pin) == 0) { if (iter->second.count(driver_type_bel_pin) == 0) {
if(ctx->verbose) { if (ctx->debug) {
log_info("BEL %s is not valid because pin %s cannot be driven by %s/%s\n", log_info("BEL %s is not valid because pin %s cannot be driven by %s/%s\n", ctx->nameOfBel(bel),
ctx->nameOfBel(bel), bel_pin.c_str(ctx), ctx->nameOfBel(driver_bel),
bel_pin.c_str(ctx),
ctx->nameOfBel(driver_bel),
driver_type_bel_pin.type_bel_pin.bel_pin.c_str(ctx)); driver_type_bel_pin.type_bel_pin.bel_pin.c_str(ctx));
} }
return false; return false;
@ -313,14 +323,10 @@ bool DedicatedInterconnect::is_sink_on_net_valid(BelId bel, const CellInfo* cell
// FIXME: This might be too slow, but it handles a case on // FIXME: This might be too slow, but it handles a case on
// SLICEL.COUT -> SLICEL.CIN has delta_y = {1, 2}, but the // SLICEL.COUT -> SLICEL.CIN has delta_y = {1, 2}, but the
// delta_y=2 case is rare. // delta_y=2 case is rare.
if(!check_routing( if (!check_routing(driver_bel, driver_type_bel_pin.type_bel_pin.bel_pin, bel, bel_pin)) {
driver_bel, driver_type_bel_pin.type_bel_pin.bel_pin, if (ctx->debug) {
bel, bel_pin)) {
if(ctx->verbose) {
log_info("BEL %s is not valid because pin %s cannot be driven by %s/%s (via detailed check)\n", log_info("BEL %s is not valid because pin %s cannot be driven by %s/%s (via detailed check)\n",
ctx->nameOfBel(bel), ctx->nameOfBel(bel), bel_pin.c_str(ctx), ctx->nameOfBel(driver_bel),
bel_pin.c_str(ctx),
ctx->nameOfBel(driver_bel),
driver_type_bel_pin.type_bel_pin.bel_pin.c_str(ctx)); driver_type_bel_pin.type_bel_pin.bel_pin.c_str(ctx));
} }
return false; return false;
@ -330,7 +336,8 @@ bool DedicatedInterconnect::is_sink_on_net_valid(BelId bel, const CellInfo* cell
return true; return true;
} }
bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo* cell) const { bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo *cell) const
{
NPNR_ASSERT(bel != BelId()); NPNR_ASSERT(bel != BelId());
for (const auto &port_pair : cell->ports) { for (const auto &port_pair : cell->ports) {
@ -358,7 +365,8 @@ bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo* cell)
return true; 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 sinks with dedicated interconnect\n", sinks.size());
log_info("Found %zu sources with dedicated interconnect\n", sources.size()); log_info("Found %zu sources with dedicated interconnect\n", sources.size());
std::vector<TileTypeBelPin> sorted_keys; std::vector<TileTypeBelPin> sorted_keys;
@ -389,20 +397,11 @@ void DedicatedInterconnect::print_dedicated_interconnect() const {
IdString dst_site_type = IdString(dst_tile_type.site_types[dst_bel_info.site]); IdString dst_site_type = IdString(dst_tile_type.site_types[dst_bel_info.site]);
IdString dst_bel_pin = dst.bel_pin; IdString dst_bel_pin = dst.bel_pin;
log_info("%s.%s[%d]/%s/%s (%d, %d) -> %s.%s[%d]/%s/%s\n", log_info("%s.%s[%d]/%s/%s (%d, %d) -> %s.%s[%d]/%s/%s\n", IdString(src_tile_type.name).c_str(ctx),
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_site_type.c_str(ctx), src_bel_pin.c_str(ctx), delta_x, delta_y, IdString(dst_tile_type.name).c_str(ctx),
src_bel_info.site, dst_site_type.c_str(ctx), dst_bel_info.site, IdString(dst_bel_info.name).c_str(ctx),
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)); dst_bel_pin.c_str(ctx));
} }
} else { } else {
auto src = key; auto src = key;
@ -421,26 +420,18 @@ void DedicatedInterconnect::print_dedicated_interconnect() const {
IdString dst_site_type = IdString(dst_tile_type.site_types[dst_bel_info.site]); IdString dst_site_type = IdString(dst_tile_type.site_types[dst_bel_info.site]);
IdString dst_bel_pin = dst.bel_pin; IdString dst_bel_pin = dst.bel_pin;
log_info("%s.%s[%d]/%s/%s -> %s.%s[%d]/%s/%s (%d, %d)\n", log_info("%s.%s[%d]/%s/%s -> %s.%s[%d]/%s/%s (%d, %d)\n", IdString(src_tile_type.name).c_str(ctx),
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_site_type.c_str(ctx), src_bel_pin.c_str(ctx), IdString(dst_tile_type.name).c_str(ctx), dst_site_type.c_str(ctx),
src_bel_info.site, dst_bel_info.site, IdString(dst_bel_info.name).c_str(ctx), dst_bel_pin.c_str(ctx), delta_x,
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); delta_y);
} }
} }
} }
} }
void DedicatedInterconnect::find_dedicated_interconnect() { void DedicatedInterconnect::find_dedicated_interconnect()
{
for (BelId bel : ctx->getBels()) { for (BelId bel : ctx->getBels()) {
const auto &bel_data = bel_info(ctx->chip_info, bel); const auto &bel_data = bel_info(ctx->chip_info, bel);
if (bel_data.category != BEL_CATEGORY_LOGIC) { if (bel_data.category != BEL_CATEGORY_LOGIC) {
@ -484,7 +475,6 @@ void DedicatedInterconnect::find_dedicated_interconnect() {
continue; continue;
} }
IdString bel_pin(bel_data.ports[i]); IdString bel_pin(bel_data.ports[i]);
TileTypeBelPin type_bel_pin; TileTypeBelPin type_bel_pin;
@ -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()); NPNR_ASSERT(sink_bel != BelId());
#ifdef DEBUG_EXPANSION #ifdef DEBUG_EXPANSION
log_info("Expanding from %s/%s\n", ctx->nameOfBel(sink_bel), pin.c_str(ctx)); log_info("Expanding from %s/%s\n", ctx->nameOfBel(sink_bel), pin.c_str(ctx));
@ -542,8 +533,7 @@ void DedicatedInterconnect::expand_sink_bel(BelId sink_bel, IdString sink_pin, W
} }
#ifdef DEBUG_EXPANSION #ifdef DEBUG_EXPANSION
log_info(" - At wire %s via %s\n", log_info(" - At wire %s via %s\n", ctx->nameOfWire(wire), ctx->nameOfPip(pip));
ctx->nameOfWire(wire), ctx->nameOfPip(pip));
#endif #endif
WireNode next_node; WireNode next_node;
@ -555,6 +545,9 @@ void DedicatedInterconnect::expand_sink_bel(BelId sink_bel, IdString sink_pin, W
// tuning). // tuning).
// //
// FIXME: Consider removing kMaxDepth and use kMaxSources? // FIXME: Consider removing kMaxDepth and use kMaxSources?
#ifdef DEBUG_EXPANSION
log_info(" - Exceeded max depth!\n");
#endif
return; return;
} }
@ -646,10 +639,11 @@ void DedicatedInterconnect::expand_sink_bel(BelId sink_bel, IdString sink_pin, W
} }
} }
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()); NPNR_ASSERT(src_bel != BelId());
#ifdef DEBUG_EXPANSION #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 #endif
std::vector<WireNode> nodes_to_expand; std::vector<WireNode> nodes_to_expand;
@ -682,8 +676,7 @@ void DedicatedInterconnect::expand_source_bel(BelId src_bel, IdString src_pin, W
} }
#ifdef DEBUG_EXPANSION #ifdef DEBUG_EXPANSION
log_info(" - At wire %s via %s\n", log_info(" - At wire %s via %s\n", ctx->nameOfWire(wire), ctx->nameOfPip(pip));
ctx->nameOfWire(wire), ctx->nameOfPip(pip));
#endif #endif
WireNode next_node; WireNode next_node;
@ -695,6 +688,9 @@ void DedicatedInterconnect::expand_source_bel(BelId src_bel, IdString src_pin, W
// tuning). // tuning).
// //
// FIXME: Consider removing kMaxDepth and use kMaxSources? // FIXME: Consider removing kMaxDepth and use kMaxSources?
#ifdef DEBUG_EXPANSION
log_info(" - Exceeded max depth!\n");
#endif
return; return;
} }
@ -776,7 +772,7 @@ void DedicatedInterconnect::expand_source_bel(BelId src_bel, IdString src_pin, W
type_bel_pin.bel_index = src_bel.index; type_bel_pin.bel_index = src_bel.index;
type_bel_pin.bel_pin = src_pin; type_bel_pin.bel_pin = src_pin;
auto result = sinks.emplace(type_bel_pin, dsts); auto result = sources.emplace(type_bel_pin, dsts);
if (!result.second) { if (!result.second) {
// type_bel_pin was already present! Add any new sources from this // type_bel_pin was already present! Add any new sources from this
// sink type (if any); // sink type (if any);

View File

@ -24,12 +24,14 @@
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
struct TileTypeBelPin { struct TileTypeBelPin
{
int32_t tile_type; int32_t tile_type;
int32_t bel_index; int32_t bel_index;
IdString bel_pin; IdString bel_pin;
bool operator < (const TileTypeBelPin &other) const { bool operator<(const TileTypeBelPin &other) const
{
if (tile_type >= other.tile_type) { if (tile_type >= other.tile_type) {
return false; return false;
} }
@ -41,23 +43,28 @@ struct TileTypeBelPin {
return bel_pin < other.bel_pin; 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; 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; 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_x;
int32_t delta_y; int32_t delta_y;
TileTypeBelPin type_bel_pin; 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; 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; 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 class discovers dedicated interconnect by examing the routing graph.
// This discovery make be expensive, and require caching to accelerate // This discovery make be expensive, and require caching to accelerate
// startup. // startup.
struct DedicatedInterconnect { struct DedicatedInterconnect
{
const Context *ctx; const Context *ctx;
std::unordered_map<TileTypeBelPin, std::unordered_set<DeltaTileTypeBelPin>> sinks; std::unordered_map<TileTypeBelPin, std::unordered_set<DeltaTileTypeBelPin>> sinks;
@ -121,16 +129,12 @@ struct DedicatedInterconnect {
void find_dedicated_interconnect(); void find_dedicated_interconnect();
void print_dedicated_interconnect() const; void print_dedicated_interconnect() const;
bool check_routing( bool check_routing(BelId src_bel, IdString src_bel_pin, BelId dst_bel, IdString dst_bel_pin) const;
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_sink_bel(BelId bel, IdString pin, WireId wire);
void expand_source_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, bool is_driver_on_net_valid(BelId driver_bel, const CellInfo *cell, IdString driver_port, NetInfo *net) const;
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_sink_on_net_valid(BelId bel, const CellInfo* cell,
IdString port_name, NetInfo *net) const;
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -111,20 +111,37 @@ static PhysicalNetlist::PhysNetlist::RouteBranch::Builder emit_branch(
if(bel_data.category == BEL_CATEGORY_LOGIC) { if(bel_data.category == BEL_CATEGORY_LOGIC) {
// This is a psuedo site-pip. // This is a psuedo site-pip.
auto in_bel_pin = branch.getRouteSegment().initBelPin(); auto in_bel_pin = branch.getRouteSegment().initBelPin();
IdString src_wire_name = IdString(tile_type.wire_data[pip_data.src_index].name); WireId src_wire = ctx->getPipSrcWire(pip);
IdString dst_wire_name = IdString(tile_type.wire_data[pip_data.dst_index].name); 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)); int bel_idx = strings->get_index(bel_name[1].str(ctx));
in_bel_pin.setSite(site_idx); in_bel_pin.setSite(site_idx);
in_bel_pin.setBel(bel_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 subbranch = branch.initBranches(1);
auto bel_pin_branch = subbranch[0]; auto bel_pin_branch = subbranch[0];
auto out_bel_pin = bel_pin_branch.getRouteSegment().initBelPin(); auto out_bel_pin = bel_pin_branch.getRouteSegment().initBelPin();
out_bel_pin.setSite(site_idx); out_bel_pin.setSite(site_idx);
out_bel_pin.setBel(bel_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; return bel_pin_branch;
} else if(bel_data.category == BEL_CATEGORY_ROUTING) { } else if(bel_data.category == BEL_CATEGORY_ROUTING) {
@ -614,6 +631,8 @@ struct ModuleReader {
ModuleReader(const LogicalNetlistImpl *root, ModuleReader(const LogicalNetlistImpl *root,
LogicalNetlist::Netlist::CellInstance::Reader cell_inst, bool is_top); LogicalNetlist::Netlist::CellInstance::Reader cell_inst, bool is_top);
size_t translate_port_index(LogicalNetlist::Netlist::PortInstance::Reader port_inst) const;
}; };
struct PortReader { struct PortReader {
@ -850,8 +869,8 @@ struct LogicalNetlistImpl
bool is_vector_bit_constant(const std::vector<int32_t> &bits, int i) const 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 // Note: This appears weird, but is correct. This is because VCC/GND
// emitted for GND and VCC, e.g. VCC vcc(.P(vcc_net)). // nets are not handled in frontend_base for FPGA interchange.
return false; return false;
} }
@ -929,11 +948,8 @@ ModuleReader::ModuleReader(const LogicalNetlistImpl *root,
PortKey port_key(inst_idx, port_inst.getPort()); PortKey port_key(inst_idx, port_inst.getPort());
std::vector<int32_t> & port_connections = connections.at(port_key); std::vector<int32_t> & port_connections = connections.at(port_key);
if(port_inst.getBusIdx().isSingleBit()) { size_t port_idx = translate_port_index(port_inst);
port_connections[0] = net_idx; port_connections.at(port_idx) = net_idx;
} else {
port_connections.at(port_inst.getBusIdx().getIdx()) = 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)(); 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 NEXTPNR_NAMESPACE_END

View File

@ -22,9 +22,9 @@
NEXTPNR_NAMESPACE_BEGIN 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); auto result = cells_in_site.emplace(cell);
NPNR_ASSERT(result.second); NPNR_ASSERT(result.second);
@ -32,7 +32,7 @@ void Arch::SiteRouter::bindBel(CellInfo *cell)
dirty = true; dirty = true;
} }
void Arch::SiteRouter::unbindBel(CellInfo *cell) void SiteRouter::unbindBel(CellInfo *cell)
{ {
NPNR_ASSERT(cells_in_site.erase(cell) == 1); NPNR_ASSERT(cells_in_site.erase(cell) == 1);
@ -56,6 +56,22 @@ struct RouteNode
PipId pip; // What pip was taken to reach this node. PipId pip; // What pip was taken to reach this node.
WireId wire; // What wire is this routing node located at? 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 struct RouteNodeStorage
@ -261,8 +277,7 @@ struct SiteInformation
// Conflict, this wire is already in use and it's not // Conflict, this wire is already in use and it's not
// doesn't match! // doesn't match!
if (verbose_site_router(ctx)) { if (verbose_site_router(ctx)) {
log_info("Cannot select route because net %s != net %s\n", log_info("Cannot select route because net %s != net %s\n", result.first->second->name.c_str(ctx),
result.first->second->name.c_str(ctx),
net->name.c_str(ctx)); net->name.c_str(ctx));
} }
@ -309,6 +324,8 @@ struct SiteInformation
std::unordered_set<const NetInfo *> nets_fully_within_site; 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); } bool is_net_within_site(const NetInfo *net) const { return nets_fully_within_site.count(net); }
void print_current_state() const;
}; };
struct SiteExpansionLoop 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; 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) { for (auto &expansion_wire : wire_to_expansion) {
auto &expansion = *expansion_wire.second; auto &expansion = *expansion_wire.second;
@ -642,8 +663,15 @@ bool route_site(const Context *ctx, SiteInformation *site_info)
if (uncongestion_route != RouteNode::Node()) { if (uncongestion_route != RouteNode::Node()) {
// Select a trivially uncongested route if possible. // Select a trivially uncongested route if possible.
NPNR_ASSERT(site_info->select_route(expansion.first_wire, uncongestion_route, expansion.net_for_wire, if (!site_info->select_route(expansion.first_wire, uncongestion_route, expansion.net_for_wire,
&newly_consumed_wires)); &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); completed_wires.push_back(expansion.first_wire);
} }
} }
@ -676,7 +704,7 @@ bool route_site(const Context *ctx, SiteInformation *site_info)
return true; 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) { if (!dirty) {
return site_ok; return site_ok;
@ -753,4 +781,42 @@ bool Arch::SiteRouter::checkSiteRouting(const Context *ctx, const Arch::TileStat
return site_ok; 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 NEXTPNR_NAMESPACE_END

View File

@ -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