Finish dedicated interconnect implementation.
Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
This commit is contained in:
parent
5574455d2a
commit
184665652e
@ -767,32 +767,45 @@ 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") {
|
||||||
|
IdString gnd_net_name(chip_info->constants->gnd_net_name);
|
||||||
|
|
||||||
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;
|
||||||
port_info.net = nullptr;
|
port_info.net = nullptr;
|
||||||
|
|
||||||
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);
|
||||||
|
} else {
|
||||||
connectPort(IdString(chip_info->constants->gnd_net_name), cell->name, bel_pin);
|
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") {
|
||||||
|
IdString vcc_net_name(chip_info->constants->vcc_net_name);
|
||||||
|
|
||||||
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;
|
||||||
port_info.net = nullptr;
|
port_info.net = nullptr;
|
||||||
|
|
||||||
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);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
cell->cell_bel_pins[bel_pin].push_back(bel_pin);
|
|
||||||
|
|
||||||
connectPort(IdString(chip_info->constants->vcc_net_name), cell->name, bel_pin);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -976,6 +989,8 @@ void Arch::merge_constant_nets() {
|
|||||||
disconnectPort(cell, port_ref.port);
|
disconnectPort(cell, port_ref.port);
|
||||||
connectPort(gnd_net_name, cell, port_ref.port);
|
connectPort(gnd_net_name, cell, port_ref.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(net->driver.cell->type == vcc_cell_type) {
|
if(net->driver.cell->type == vcc_cell_type) {
|
||||||
@ -1003,11 +1018,23 @@ void Arch::merge_constant_nets() {
|
|||||||
for(IdString other_gnd_net : other_gnd_nets) {
|
for(IdString other_gnd_net : other_gnd_nets) {
|
||||||
NetInfo * net = getNetByAlias(other_gnd_net);
|
NetInfo * net = getNetByAlias(other_gnd_net);
|
||||||
NPNR_ASSERT(net->users.empty());
|
NPNR_ASSERT(net->users.empty());
|
||||||
|
if(net->driver.cell) {
|
||||||
|
PortRef driver = net->driver;
|
||||||
|
IdString cell_to_remove = driver.cell->name;
|
||||||
|
disconnectPort(driver.cell->name, driver.port);
|
||||||
|
NPNR_ASSERT(cells.erase(cell_to_remove));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(IdString other_vcc_net : other_vcc_nets) {
|
for(IdString other_vcc_net : other_vcc_nets) {
|
||||||
NetInfo * net = getNetByAlias(other_vcc_net);
|
NetInfo * net = getNetByAlias(other_vcc_net);
|
||||||
NPNR_ASSERT(net->users.empty());
|
NPNR_ASSERT(net->users.empty());
|
||||||
|
if(net->driver.cell) {
|
||||||
|
PortRef driver = net->driver;
|
||||||
|
IdString cell_to_remove = driver.cell->name;
|
||||||
|
disconnectPort(driver.cell->name, driver.port);
|
||||||
|
NPNR_ASSERT(cells.erase(cell_to_remove));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(IdString other_gnd_net : other_gnd_nets) {
|
for(IdString other_gnd_net : other_gnd_nets) {
|
||||||
|
@ -24,178 +24,6 @@
|
|||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
void DedicatedInterconnect::init(const Context *ctx) {
|
|
||||||
this->ctx = ctx;
|
|
||||||
|
|
||||||
if(ctx->debug) {
|
|
||||||
log_info("Finding dedicated interconnect!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
find_dedicated_interconnect();
|
|
||||||
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 {
|
|
||||||
// FIXME: Implement.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo* cell) const {
|
|
||||||
NPNR_ASSERT(bel != BelId());
|
|
||||||
|
|
||||||
Loc bel_loc = ctx->getBelLocation(bel);
|
|
||||||
|
|
||||||
const auto &bel_data = bel_info(ctx->chip_info, bel);
|
|
||||||
|
|
||||||
for(const auto &port_pair : cell->ports) {
|
|
||||||
IdString port_name = port_pair.first;
|
|
||||||
NetInfo *net = port_pair.second.net;
|
|
||||||
if(net == nullptr) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only check sink BELs.
|
|
||||||
if(net->driver.cell == cell && net->driver.port == port_name) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This net doesn't have a driver, probably not valid?
|
|
||||||
NPNR_ASSERT(net->driver.cell != nullptr);
|
|
||||||
|
|
||||||
BelId driver_bel = net->driver.cell->bel;
|
|
||||||
if(driver_bel == BelId()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto &driver_bel_data = bel_info(ctx->chip_info, driver_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));
|
|
||||||
|
|
||||||
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 = pins_with_dedicate_interconnect.find(type_bel_pin);
|
|
||||||
if(iter == pins_with_dedicate_interconnect.end()) {
|
|
||||||
// This BEL pin doesn't have a dedicate interconnect.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do detailed routing check to ensure driver can reach sink.
|
|
||||||
//
|
|
||||||
// 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) {
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DedicatedInterconnect::print_dedicated_interconnect() const {
|
|
||||||
log_info("Found %zu sinks with dedicated interconnect\n", pins_with_dedicate_interconnect.size());
|
|
||||||
std::vector<TileTypeBelPin> sorted_keys;
|
|
||||||
for(const auto & sink_to_srcs : pins_with_dedicate_interconnect) {
|
|
||||||
sorted_keys.push_back(sink_to_srcs.first);
|
|
||||||
}
|
|
||||||
std::sort(sorted_keys.begin(), sorted_keys.end());
|
|
||||||
|
|
||||||
for(const auto & dst : sorted_keys) {
|
|
||||||
for(const auto & src : pins_with_dedicate_interconnect.at(dst)) {
|
|
||||||
const TileTypeInfoPOD & src_tile_type = ctx->chip_info->tile_types[src.type_bel_pin.tile_type];
|
|
||||||
const BelInfoPOD & src_bel_info = src_tile_type.bel_data[src.type_bel_pin.bel_index];
|
|
||||||
IdString src_site_type = IdString(src_tile_type.site_types[src_bel_info.site]);
|
|
||||||
IdString src_bel_pin = src.type_bel_pin.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];
|
|
||||||
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/%s/%s (%d, %d) -> %s.%s/%s/%s\n",
|
|
||||||
IdString(src_tile_type.name).c_str(ctx),
|
|
||||||
src_site_type.c_str(ctx),
|
|
||||||
IdString(src_bel_info.name).c_str(ctx),
|
|
||||||
src_bel_pin.c_str(ctx),
|
|
||||||
src.delta_x,
|
|
||||||
src.delta_y,
|
|
||||||
IdString(dst_tile_type.name).c_str(ctx),
|
|
||||||
dst_site_type.c_str(ctx),
|
|
||||||
IdString(dst_bel_info.name).c_str(ctx),
|
|
||||||
dst_bel_pin.c_str(ctx));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(size_t i = 0; i < bel_data.num_bel_wires; ++i) {
|
|
||||||
if(bel_data.types[i] != PORT_IN) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
WireId wire;
|
|
||||||
wire.tile = bel.tile;
|
|
||||||
wire.index = bel_data.wires[i];
|
|
||||||
|
|
||||||
expand_bel(bel, IdString(bel_data.ports[i]), wire);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All legal routes involved at most 2 sites, the source site and the sink
|
// All legal routes involved at most 2 sites, the source site and the sink
|
||||||
// site. The source site and sink sites may be the same, but that is not
|
// site. The source site and sink sites may be the same, but that is not
|
||||||
// dedicated routing, that is intra site routing.
|
// dedicated routing, that is intra site routing.
|
||||||
@ -222,22 +50,481 @@ struct WireNode {
|
|||||||
// interconnect.
|
// interconnect.
|
||||||
constexpr int kMaxDepth = 20;
|
constexpr int kMaxDepth = 20;
|
||||||
|
|
||||||
void DedicatedInterconnect::expand_bel(BelId bel, IdString pin, WireId wire) {
|
void DedicatedInterconnect::init(const Context *ctx) {
|
||||||
|
this->ctx = ctx;
|
||||||
|
|
||||||
|
if(ctx->debug) {
|
||||||
|
log_info("Finding dedicated interconnect!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
find_dedicated_interconnect();
|
||||||
|
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 {
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
NPNR_ASSERT(dst_wire_data.site != -1);
|
||||||
|
|
||||||
|
WireNode wire_node;
|
||||||
|
wire_node.wire = src_wire;
|
||||||
|
wire_node.state = IN_SOURCE_SITE;
|
||||||
|
wire_node.depth = 0;
|
||||||
|
|
||||||
|
nodes_to_expand.push_back(wire_node);
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
WireId wire = ctx->getPipDstWire(pip);
|
||||||
|
if(wire == WireId()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_EXPANSION
|
||||||
|
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) {
|
||||||
|
// Dedicated routing should reach sources by kMaxDepth (with
|
||||||
|
// tuning).
|
||||||
|
//
|
||||||
|
// FIXME: Consider removing kMaxDepth and use kMaxSources?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
#ifdef DEBUG_EXPANSION
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next_node.state = node_to_expand.state;
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
type_bel_pin.tile_type = ctx->chip_info->tiles[driver_bel.tile].type;
|
||||||
|
type_bel_pin.bel_index = driver_bel.index;
|
||||||
|
|
||||||
|
Loc driver_loc = ctx->getBelLocation(driver_bel);
|
||||||
|
|
||||||
|
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()) {
|
||||||
|
// This BEL pin doesn't have a dedicate interconnect.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const PortRef & port_ref : net->users) {
|
||||||
|
NPNR_ASSERT(port_ref.cell != nullptr);
|
||||||
|
|
||||||
|
if(port_ref.cell->bel == BelId()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// This is a site local routing, even though this is a sink
|
||||||
|
// with a dedicated interconnect.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeltaTileTypeBelPin sink_type_bel_pin;
|
||||||
|
sink_type_bel_pin.delta_x = sink_loc.x - driver_loc.x;
|
||||||
|
sink_type_bel_pin.delta_y = sink_loc.y - driver_loc.y;
|
||||||
|
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)) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do detailed routing check to ensure driver can reach sink.
|
||||||
|
//
|
||||||
|
// 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) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
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()) {
|
||||||
|
// This BEL pin doesn't have a dedicate interconnect.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do detailed routing check to ensure driver can reach sink.
|
||||||
|
//
|
||||||
|
// 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) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DedicatedInterconnect::isBelLocationValid(BelId bel, const CellInfo* cell) const {
|
||||||
NPNR_ASSERT(bel != BelId());
|
NPNR_ASSERT(bel != BelId());
|
||||||
|
|
||||||
|
for(const auto &port_pair : cell->ports) {
|
||||||
|
IdString port_name = port_pair.first;
|
||||||
|
NetInfo *net = port_pair.second.net;
|
||||||
|
if(net == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This net doesn't have a driver, probably not valid?
|
||||||
|
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)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(!is_sink_on_net_valid(bel, cell, port_name, net)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
sorted_keys.push_back(sink_to_srcs.first);
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
auto iter = sinks.find(key);
|
||||||
|
if(iter != sinks.end()) {
|
||||||
|
auto dst = key;
|
||||||
|
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];
|
||||||
|
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];
|
||||||
|
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));
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto src = 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];
|
||||||
|
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];
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < bel_data.num_bel_wires; ++i) {
|
||||||
|
if(bel_data.types[i] != PORT_IN) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
WireId wire;
|
||||||
|
wire.tile = bel.tile;
|
||||||
|
wire.index = bel_data.wires[i];
|
||||||
|
|
||||||
|
expand_sink_bel(bel, IdString(bel_data.ports[i]), wire);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<TileTypeBelPin> seen_pins;
|
||||||
|
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) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(bel_data.synthetic) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Don't visit src pins already handled in the sink expansion!
|
||||||
|
if(seen_pins.count(type_bel_pin)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
WireId wire;
|
||||||
|
wire.tile = bel.tile;
|
||||||
|
wire.index = bel_data.wires[i];
|
||||||
|
|
||||||
|
expand_source_bel(bel, bel_pin, 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));
|
||||||
|
#endif
|
||||||
|
|
||||||
std::vector<WireNode> nodes_to_expand;
|
std::vector<WireNode> nodes_to_expand;
|
||||||
|
|
||||||
const auto & src_wire_data = ctx->wire_info(wire);
|
const auto & sink_wire_data = ctx->wire_info(sink_wire);
|
||||||
NPNR_ASSERT(src_wire_data.site != -1);
|
NPNR_ASSERT(sink_wire_data.site != -1);
|
||||||
|
|
||||||
WireNode wire_node;
|
WireNode wire_node;
|
||||||
wire_node.wire = wire;
|
wire_node.wire = sink_wire;
|
||||||
wire_node.state = IN_SINK_SITE;
|
wire_node.state = IN_SINK_SITE;
|
||||||
wire_node.depth = 0;
|
wire_node.depth = 0;
|
||||||
|
|
||||||
nodes_to_expand.push_back(wire_node);
|
nodes_to_expand.push_back(wire_node);
|
||||||
|
|
||||||
Loc sink_loc = ctx->getBelLocation(bel);
|
Loc sink_loc = ctx->getBelLocation(sink_bel);
|
||||||
std::unordered_set<DeltaTileTypeBelPin> srcs;
|
std::unordered_set<DeltaTileTypeBelPin> srcs;
|
||||||
|
|
||||||
while(!nodes_to_expand.empty()) {
|
while(!nodes_to_expand.empty()) {
|
||||||
@ -254,6 +541,11 @@ void DedicatedInterconnect::expand_bel(BelId bel, IdString pin, WireId wire) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_EXPANSION
|
||||||
|
log_info(" - At wire %s via %s\n",
|
||||||
|
ctx->nameOfWire(wire), ctx->nameOfPip(pip));
|
||||||
|
#endif
|
||||||
|
|
||||||
WireNode next_node;
|
WireNode next_node;
|
||||||
next_node.wire = wire;
|
next_node.wire = wire;
|
||||||
next_node.depth = node_to_expand.depth += 1;
|
next_node.depth = node_to_expand.depth += 1;
|
||||||
@ -277,9 +569,12 @@ void DedicatedInterconnect::expand_bel(BelId bel, IdString pin, WireId wire) {
|
|||||||
break;
|
break;
|
||||||
case IN_ROUTING:
|
case IN_ROUTING:
|
||||||
NPNR_ASSERT(wire_data.site != -1);
|
NPNR_ASSERT(wire_data.site != -1);
|
||||||
if(wire_data.site == src_wire_data.site) {
|
if(wire.tile == sink_wire.tile && wire_data.site == sink_wire_data.site) {
|
||||||
// Dedicated routing won't have straight loops,
|
// Dedicated routing won't have straight loops,
|
||||||
// general routing looks like that.
|
// general routing looks like that.
|
||||||
|
#ifdef DEBUG_EXPANSION
|
||||||
|
log_info(" - Not dedicated site routing because loop!");
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
next_node.state = IN_SOURCE_SITE;
|
next_node.state = IN_SOURCE_SITE;
|
||||||
@ -307,7 +602,6 @@ void DedicatedInterconnect::expand_bel(BelId bel, IdString pin, WireId wire) {
|
|||||||
for(BelPin bel_pin : ctx->getWireBelPins(wire)) {
|
for(BelPin bel_pin : ctx->getWireBelPins(wire)) {
|
||||||
BelId src_bel = bel_pin.bel;
|
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);
|
||||||
NPNR_ASSERT(bel_data.site != src_wire_data.site);
|
|
||||||
|
|
||||||
if(bel_data.category != BEL_CATEGORY_LOGIC) {
|
if(bel_data.category != BEL_CATEGORY_LOGIC) {
|
||||||
continue;
|
continue;
|
||||||
@ -319,11 +613,15 @@ void DedicatedInterconnect::expand_bel(BelId bel, IdString pin, WireId wire) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_EXPANSION
|
||||||
|
log_info(" - Reached %s/%s\n", ctx->nameOfBel(bel_pin.bel), bel_pin.pin.c_str(ctx));
|
||||||
|
#endif
|
||||||
|
|
||||||
Loc src_loc = ctx->getBelLocation(src_bel);
|
Loc src_loc = ctx->getBelLocation(src_bel);
|
||||||
|
|
||||||
DeltaTileTypeBelPin delta_type_bel_pin;
|
DeltaTileTypeBelPin delta_type_bel_pin;
|
||||||
delta_type_bel_pin.delta_x = src_loc.x - sink_loc.x;
|
delta_type_bel_pin.delta_x = src_loc.x - sink_loc.x;
|
||||||
delta_type_bel_pin.delta_x = src_loc.y - sink_loc.y;
|
delta_type_bel_pin.delta_y = src_loc.y - sink_loc.y;
|
||||||
delta_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[src_bel.tile].type;
|
delta_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[src_bel.tile].type;
|
||||||
delta_type_bel_pin.type_bel_pin.bel_index = src_bel.index;
|
delta_type_bel_pin.type_bel_pin.bel_index = src_bel.index;
|
||||||
delta_type_bel_pin.type_bel_pin.bel_pin = bel_pin.pin;
|
delta_type_bel_pin.type_bel_pin.bel_pin = bel_pin.pin;
|
||||||
@ -334,11 +632,11 @@ void DedicatedInterconnect::expand_bel(BelId bel, IdString pin, WireId wire) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TileTypeBelPin type_bel_pin;
|
TileTypeBelPin type_bel_pin;
|
||||||
type_bel_pin.tile_type = ctx->chip_info->tiles[bel.tile].type;
|
type_bel_pin.tile_type = ctx->chip_info->tiles[sink_bel.tile].type;
|
||||||
type_bel_pin.bel_index = bel.index;
|
type_bel_pin.bel_index = sink_bel.index;
|
||||||
type_bel_pin.bel_pin = pin;
|
type_bel_pin.bel_pin = sink_pin;
|
||||||
|
|
||||||
auto result = pins_with_dedicate_interconnect.emplace(type_bel_pin, srcs);
|
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
|
// type_bel_pin was already present! Add any new sources from this
|
||||||
// sink type (if any);
|
// sink type (if any);
|
||||||
@ -348,4 +646,144 @@ void DedicatedInterconnect::expand_bel(BelId bel, IdString pin, WireId 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));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::vector<WireNode> nodes_to_expand;
|
||||||
|
|
||||||
|
const auto & src_wire_data = ctx->wire_info(src_wire);
|
||||||
|
NPNR_ASSERT(src_wire_data.site != -1);
|
||||||
|
|
||||||
|
WireNode wire_node;
|
||||||
|
wire_node.wire = src_wire;
|
||||||
|
wire_node.state = IN_SOURCE_SITE;
|
||||||
|
wire_node.depth = 0;
|
||||||
|
|
||||||
|
nodes_to_expand.push_back(wire_node);
|
||||||
|
|
||||||
|
Loc src_loc = ctx->getBelLocation(src_bel);
|
||||||
|
std::unordered_set<DeltaTileTypeBelPin> dsts;
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
WireId wire = ctx->getPipDstWire(pip);
|
||||||
|
if(wire == WireId()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_EXPANSION
|
||||||
|
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) {
|
||||||
|
// Dedicated routing should reach sources by kMaxDepth (with
|
||||||
|
// tuning).
|
||||||
|
//
|
||||||
|
// FIXME: Consider removing kMaxDepth and use kMaxSources?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
#ifdef DEBUG_EXPANSION
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next_node.state = node_to_expand.state;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
BelId sink_bel = bel_pin.bel;
|
||||||
|
auto const & bel_data = bel_info(ctx->chip_info, sink_bel);
|
||||||
|
|
||||||
|
if(bel_data.category != BEL_CATEGORY_LOGIC) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(bel_data.synthetic) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(ctx->getBelPinType(bel_pin.bel, bel_pin.pin) != PORT_IN) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_EXPANSION
|
||||||
|
log_info(" - Reached %s/%s\n", ctx->nameOfBel(bel_pin.bel), bel_pin.pin.c_str(ctx));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Loc sink_loc = ctx->getBelLocation(sink_bel);
|
||||||
|
|
||||||
|
DeltaTileTypeBelPin delta_type_bel_pin;
|
||||||
|
delta_type_bel_pin.delta_x = sink_loc.x - src_loc.x;
|
||||||
|
delta_type_bel_pin.delta_y = sink_loc.y - src_loc.y;
|
||||||
|
delta_type_bel_pin.type_bel_pin.tile_type = ctx->chip_info->tiles[sink_bel.tile].type;
|
||||||
|
delta_type_bel_pin.type_bel_pin.bel_index = sink_bel.index;
|
||||||
|
delta_type_bel_pin.type_bel_pin.bel_pin = bel_pin.pin;
|
||||||
|
dsts.emplace(delta_type_bel_pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TileTypeBelPin type_bel_pin;
|
||||||
|
type_bel_pin.tile_type = ctx->chip_info->tiles[src_bel.tile].type;
|
||||||
|
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) {
|
||||||
|
// type_bel_pin was already present! Add any new sources from this
|
||||||
|
// sink type (if any);
|
||||||
|
for(auto dst : dsts) {
|
||||||
|
result.first->second.emplace(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -108,7 +108,8 @@ struct Context;
|
|||||||
struct DedicatedInterconnect {
|
struct DedicatedInterconnect {
|
||||||
const Context *ctx;
|
const Context *ctx;
|
||||||
|
|
||||||
std::unordered_map<TileTypeBelPin, std::unordered_set<DeltaTileTypeBelPin>> pins_with_dedicate_interconnect;
|
std::unordered_map<TileTypeBelPin, std::unordered_set<DeltaTileTypeBelPin>> sinks;
|
||||||
|
std::unordered_map<TileTypeBelPin, std::unordered_set<DeltaTileTypeBelPin>> sources;
|
||||||
|
|
||||||
void init(const Context *ctx);
|
void init(const Context *ctx);
|
||||||
|
|
||||||
@ -123,7 +124,13 @@ struct DedicatedInterconnect {
|
|||||||
bool check_routing(
|
bool check_routing(
|
||||||
BelId src_bel, IdString src_bel_pin,
|
BelId src_bel, IdString src_bel_pin,
|
||||||
BelId dst_bel, IdString dst_bel_pin) const;
|
BelId dst_bel, IdString dst_bel_pin) const;
|
||||||
void expand_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);
|
||||||
|
|
||||||
|
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
|
NEXTPNR_NAMESPACE_END
|
||||||
|
Loading…
Reference in New Issue
Block a user