Rework FPGA interchange site router.

The new site router should be robust to most situations, and isn't
significantly slower with the use of caching.

Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
This commit is contained in:
Keith Rothman 2021-03-19 18:26:00 -07:00
parent 0f4014615c
commit 32f2ec86c4
12 changed files with 2653 additions and 607 deletions

View File

@ -723,6 +723,36 @@ bool Arch::route()
}
}
HashTables::HashSet<WireId> wires_to_unbind;
for (auto &net_pair : nets) {
for (auto &wire_pair : net_pair.second->wires) {
WireId wire = wire_pair.first;
if (wire_pair.second.strength != STRENGTH_PLACER) {
// Only looking for bound placer wires
continue;
}
const TileWireInfoPOD &wire_data = wire_info(wire);
NPNR_ASSERT(wire_data.site != -1);
wires_to_unbind.emplace(wire);
}
}
for (WireId wire : wires_to_unbind) {
unbindWire(wire);
}
for (auto &tile_pair : tileStatus) {
for (auto &site_router : tile_pair.second.sites) {
if (site_router.cells_in_site.empty()) {
continue;
}
site_router.bindSiteRouting(getCtx());
}
}
bool result;
if (router == "router1") {
result = router1(getCtx(), Router1Cfg(getCtx()));
@ -1727,8 +1757,6 @@ bool Arch::check_pip_avail_for_net(PipId pip, NetInfo *net) const
bool Arch::checkPipAvail(PipId pip) const { return check_pip_avail_for_net(pip, nullptr); }
Arch::~Arch() {}
// Instance constraint templates.
template void Arch::ArchConstraints::bindBel(Arch::ArchConstraints::TagState *, const Arch::ConstraintRange);
template void Arch::ArchConstraints::unbindBel(Arch::ArchConstraints::TagState *, const Arch::ConstraintRange);

View File

@ -37,6 +37,7 @@
#include "chipdb.h"
#include "dedicated_interconnect.h"
#include "site_router.h"
#include "site_routing_cache.h"
NEXTPNR_NAMESPACE_BEGIN
@ -1037,6 +1038,8 @@ struct Arch : ArchAPI<ArchRanges>
std::regex verilog_hex_constant;
void read_lut_equation(DynamicBitarray<> *equation, const Property &equation_parameter) const;
bool route_vcc_to_unused_lut_pins();
mutable RouteNodeStorage node_storage;
mutable SiteRoutingCache site_routing_cache;
};
NEXTPNR_NAMESPACE_END

View File

@ -98,11 +98,14 @@ struct BelBucketId
bool operator<(const BelBucketId &other) const { return name < other.name; }
};
struct SiteExpansionLoop;
struct ArchNetInfo
{
};
virtual ~ArchNetInfo();
struct NetInfo;
SiteExpansionLoop *loop = nullptr;
};
struct ArchCellInfo
{

View File

@ -0,0 +1,121 @@
/*
* 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 FLAT_WIRE_MAP_H_
#define FLAT_WIRE_MAP_H_
#include "context.h"
#include "dynamic_bitarray.h"
#include "nextpnr_namespaces.h"
#include "nextpnr_types.h"
NEXTPNR_NAMESPACE_BEGIN
template <typename Value> class FlatTileWireMap
{
public:
std::pair<Value *, bool> emplace(const Context *ctx, WireId wire, const Value &value)
{
if (values_.empty()) {
if (wire.tile == -1) {
resize(ctx->chip_info->nodes.size());
} else {
resize(loc_info(ctx->chip_info, wire).wire_data.size());
}
}
if (set_.get(wire.index)) {
return std::make_pair(&values_[wire.index], false);
} else {
values_[wire.index] = value;
set_.set(wire.index, true);
return std::make_pair(&values_[wire.index], true);
}
}
const Value &at(WireId wire) const
{
NPNR_ASSERT(!values_.empty());
NPNR_ASSERT(set_.get(wire.index));
return values_.at(wire.index);
}
void clear()
{
if (!values_.empty()) {
set_.fill(false);
}
}
private:
void resize(size_t count)
{
set_.resize(count);
set_.fill(false);
values_.resize(count);
}
DynamicBitarray<> set_;
std::vector<Value> values_;
};
template <typename Value> class FlatWireMap
{
public:
FlatWireMap(const Context *ctx) : ctx_(ctx) { tiles_.resize(ctx->chip_info->tiles.size() + 1); }
std::pair<std::pair<WireId, Value *>, bool> emplace(WireId wire, const Value &value)
{
// Tile = -1 is for node wires.
size_t tile_index = wire.tile + 1;
auto &tile = tiles_.at(tile_index);
auto result = tile.emplace(ctx_, wire, value);
if (result.second) {
size_ += 1;
}
return std::make_pair(std::make_pair(wire, result.first), result.second);
}
const Value &at(WireId wire) const
{
const auto &tile = tiles_.at(wire.tile + 1);
return tile.at(wire);
}
size_t size() const { return size_; }
void clear()
{
for (auto &tile : tiles_) {
tile.clear();
}
size_ = 0;
}
private:
const Context *ctx_;
std::vector<FlatTileWireMap<Value>> tiles_;
size_t size_;
};
NEXTPNR_NAMESPACE_END
#endif /* FLAT_WIRE_MAP_H_ */

View File

@ -0,0 +1,376 @@
/*
* 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.
*
*/
#include "site_arch.h"
#include "site_arch.impl.h"
NEXTPNR_NAMESPACE_BEGIN
SiteInformation::SiteInformation(const Context *ctx, int32_t tile, int32_t site,
const std::unordered_set<CellInfo *> &cells_in_site)
: ctx(ctx), tile(tile), tile_type(ctx->chip_info->tiles[tile].type), site(site), cells_in_site(cells_in_site)
{
}
bool SiteArch::bindPip(const SitePip &pip, SiteNetInfo *net)
{
SiteWire src = getPipSrcWire(pip);
SiteWire dst = getPipDstWire(pip);
if (!bindWire(src, net)) {
return false;
}
if (!bindWire(dst, net)) {
unbindWire(src);
return false;
}
auto result = net->wires.emplace(dst, SitePipMap{pip, 1});
if (!result.second) {
if (result.first->second.pip != pip) {
// Pip conflict!
if (debug()) {
log_info("Pip conflict binding pip %s to wire %s, conflicts with pip %s\n", nameOfPip(pip),
nameOfWire(dst), nameOfPip(result.first->second.pip));
}
unbindWire(src);
unbindWire(dst);
return false;
}
result.first->second.count += 1;
}
return true;
}
void SiteArch::unbindPip(const SitePip &pip)
{
SiteWire src = getPipSrcWire(pip);
SiteWire dst = getPipDstWire(pip);
SiteNetInfo *src_net = unbindWire(src);
SiteNetInfo *dst_net = unbindWire(dst);
NPNR_ASSERT(src_net == dst_net);
auto iter = dst_net->wires.find(dst);
NPNR_ASSERT(iter != dst_net->wires.end());
NPNR_ASSERT(iter->second.count >= 1);
iter->second.count -= 1;
if (iter->second.count == 0) {
dst_net->wires.erase(iter);
}
}
void SiteArch::archcheck()
{
for (SiteWire wire : getWires()) {
for (SitePip pip : getPipsDownhill(wire)) {
SiteWire wire2 = getPipSrcWire(pip);
log_assert(wire == wire2);
}
for (SitePip pip : getPipsUphill(wire)) {
SiteWire wire2 = getPipDstWire(pip);
log_assert(wire == wire2);
}
}
}
SiteArch::SiteArch(const SiteInformation *site_info) : ctx(site_info->ctx), site_info(site_info)
{
// Build list of input and output site ports
//
// FIXME: This doesn't need to be computed over and over, move to
// arch/chip db.
const TileTypeInfoPOD &tile_type = loc_info(&site_info->chip_info(), *site_info);
PipId pip;
pip.tile = site_info->tile;
for (size_t pip_index = 0; pip_index < tile_type.pip_data.size(); ++pip_index) {
if (tile_type.pip_data[pip_index].site != site_info->site) {
continue;
}
pip.index = pip_index;
if (!site_info->is_site_port(pip)) {
continue;
}
WireId src_wire = ctx->getPipSrcWire(pip);
if (site_info->is_wire_in_site(src_wire)) {
output_site_ports.push_back(pip);
} else {
input_site_ports.push_back(pip);
}
}
// Create list of out of site sources and sinks.
for (CellInfo *cell : site_info->cells_in_site) {
for (const auto &pin_pair : cell->cell_bel_pins) {
const PortInfo &port = cell->ports.at(pin_pair.first);
if (port.net != nullptr) {
nets.emplace(port.net, SiteNetInfo{port.net});
}
}
}
for (auto &net_pair : nets) {
NetInfo *net = net_pair.first;
SiteNetInfo &net_info = net_pair.second;
// All nets require drivers
NPNR_ASSERT(net->driver.cell != nullptr);
bool net_driven_out_of_site = false;
if (net->driver.cell->bel == BelId()) {
// The driver of this site hasn't been placed, so treat it as
// out of site.
out_of_site_sources.push_back(SiteWire::make(site_info, PORT_OUT, net));
net_info.driver = out_of_site_sources.back();
net_driven_out_of_site = true;
} else {
if (!site_info->is_bel_in_site(net->driver.cell->bel)) {
// The driver of this site has been placed, it is an out
// of site source.
out_of_site_sources.push_back(SiteWire::make(site_info, PORT_OUT, net));
// out_of_site_sources.back().wire = ctx->getNetinfoSourceWire(net);
net_info.driver = out_of_site_sources.back();
net_driven_out_of_site = true;
} else {
net_info.driver = SiteWire::make(site_info, ctx->getNetinfoSourceWire(net));
}
}
if (net_driven_out_of_site) {
// Because this net is driven from a source out of the site,
// no out of site sink is required.
continue;
}
// Examine net to determine if it has any users not in this site.
bool net_used_out_of_site = false;
for (const PortRef &user : net->users) {
NPNR_ASSERT(user.cell != nullptr);
if (user.cell->bel == BelId()) {
// Because this net has a user that has not been placed,
// and this net is being driven from this site, make sure
// this net can be routed from this site.
net_used_out_of_site = true;
break;
}
if (!site_info->is_bel_in_site(user.cell->bel)) {
net_used_out_of_site = true;
break;
}
}
if (net_used_out_of_site) {
out_of_site_sinks.push_back(SiteWire::make(site_info, PORT_IN, net));
net_info.users.emplace(out_of_site_sinks.back());
}
}
// At this point all nets have a driver SiteWire, but user SiteWire's
// within the site are not present. Add them now.
for (auto &net_pair : nets) {
NetInfo *net = net_pair.first;
SiteNetInfo &net_info = net_pair.second;
for (const PortRef &user : net->users) {
if (!site_info->is_bel_in_site(user.cell->bel)) {
// Only care about BELs within the site at this point.
continue;
}
for (IdString bel_pin : ctx->getBelPinsForCellPin(user.cell, user.port)) {
SiteWire wire = getBelPinWire(user.cell->bel, bel_pin);
// Don't add users that are trivially routable!
if (wire != net_info.driver) {
#ifdef DEBUG_SITE_ARCH
if (ctx->debug) {
log_info("Add user %s because it isn't driver %s\n", nameOfWire(wire),
nameOfWire(net_info.driver));
}
#endif
net_info.users.emplace(wire);
}
}
}
}
for (auto &net_pair : nets) {
SiteNetInfo *net_info = &net_pair.second;
auto result = wire_to_nets.emplace(net_info->driver, SiteNetMap{net_info, 1});
// By this point, trivial congestion at sources should already by
// avoided, and there should be no duplicates in the
// driver/users data.
NPNR_ASSERT(result.second);
for (const auto &user : net_info->users) {
result = wire_to_nets.emplace(user, SiteNetMap{net_info, 1});
NPNR_ASSERT(result.second);
}
}
}
SiteWire SiteArch::getBelPinWire(BelId bel, IdString pin) const
{
WireId wire = ctx->getBelPinWire(bel, pin);
return SiteWire::make(site_info, wire);
}
PortType SiteArch::getBelPinType(BelId bel, IdString pin) const { return ctx->getBelPinType(bel, pin); }
const char *SiteArch::nameOfWire(const SiteWire &wire) const
{
switch (wire.type) {
case SiteWire::SITE_WIRE:
return ctx->nameOfWire(wire.wire);
case SiteWire::SITE_PORT_SINK:
return ctx->nameOfWire(wire.wire);
case SiteWire::SITE_PORT_SOURCE:
return ctx->nameOfWire(wire.wire);
case SiteWire::OUT_OF_SITE_SOURCE:
return "out of site source, implement me!";
case SiteWire::OUT_OF_SITE_SINK:
return "out of site sink, implement me!";
default:
// Unreachable!
NPNR_ASSERT(false);
}
}
const char *SiteArch::nameOfPip(const SitePip &pip) const
{
switch (pip.type) {
case SitePip::SITE_PIP:
return ctx->nameOfPip(pip.pip);
case SitePip::SITE_PORT:
return ctx->nameOfPip(pip.pip);
case SitePip::SOURCE_TO_SITE_PORT:
return "source to site port, implement me!";
case SitePip::SITE_PORT_TO_SINK:
return "site port to sink, implement me!";
case SitePip::SITE_PORT_TO_SITE_PORT:
return "site port to site port, implement me!";
default:
// Unreachable!
NPNR_ASSERT(false);
}
}
const char *SiteArch::nameOfNet(const SiteNetInfo *net) const { return net->net->name.c_str(ctx); }
bool SiteArch::debug() const { return ctx->debug; }
SitePipUphillRange::SitePipUphillRange(const SiteArch *site_arch, SiteWire site_wire)
: site_arch(site_arch), site_wire(site_wire)
{
switch (site_wire.type) {
case SiteWire::SITE_WIRE:
pip_range = site_arch->ctx->getPipsUphill(site_wire.wire);
break;
case SiteWire::OUT_OF_SITE_SOURCE:
// No normal pips!
break;
case SiteWire::OUT_OF_SITE_SINK:
// No normal pips!
break;
case SiteWire::SITE_PORT_SINK:
// No normal pips!
break;
case SiteWire::SITE_PORT_SOURCE:
// No normal pips!
break;
default:
// Unreachable!
NPNR_ASSERT(false);
}
}
SitePip SitePipUphillIterator::operator*() const
{
switch (state) {
case NORMAL_PIPS:
return SitePip::make(site_arch->site_info, *iter);
case PORT_SRC_TO_PORT_SINK:
return SitePip::make(site_arch->site_info, site_arch->output_site_ports.at(cursor), site_wire.pip);
case OUT_OF_SITE_SOURCES:
return SitePip::make(site_arch->site_info, site_arch->out_of_site_sources.at(cursor), site_wire.pip);
case OUT_OF_SITE_SINK_TO_PORT_SINK:
return SitePip::make(site_arch->site_info, site_arch->output_site_ports.at(cursor), site_wire);
case SITE_PORT:
return SitePip::make(site_arch->site_info, site_wire.pip);
default:
// Unreachable!
NPNR_ASSERT(false);
}
}
SiteWire SiteWireIterator::operator*() const
{
WireId wire;
PipId pip;
SiteWire site_wire;
switch (state) {
case NORMAL_WIRES:
wire.tile = site_arch->site_info->tile;
wire.index = cursor;
return SiteWire::make(site_arch->site_info, wire);
case INPUT_SITE_PORTS:
pip = site_arch->input_site_ports.at(cursor);
site_wire = SiteWire::make_site_port(site_arch->site_info, pip, /*dst_wire=*/false);
NPNR_ASSERT(site_wire.type == SiteWire::SITE_PORT_SOURCE);
return site_wire;
case OUTPUT_SITE_PORTS:
pip = site_arch->output_site_ports.at(cursor);
site_wire = SiteWire::make_site_port(site_arch->site_info, pip, /*dst_wire=*/true);
NPNR_ASSERT(site_wire.type == SiteWire::SITE_PORT_SINK);
return site_wire;
case OUT_OF_SITE_SOURCES:
return site_arch->out_of_site_sources.at(cursor);
case OUT_OF_SITE_SINKS:
return site_arch->out_of_site_sinks.at(cursor);
default:
// Unreachable!
NPNR_ASSERT(false);
}
}
SiteWireIterator SiteWireRange::begin() const
{
SiteWireIterator b;
b.state = SiteWireIterator::BEGIN;
b.site_arch = site_arch;
b.tile_type = &loc_info(&site_arch->site_info->chip_info(), *site_arch->site_info);
++b;
return b;
}
NEXTPNR_NAMESPACE_END

View File

@ -0,0 +1,819 @@
/*
* 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 SITE_ARCH_H
#define SITE_ARCH_H
#include <cstdint>
#include <unordered_set>
#include <vector>
#include "arch_iterators.h"
#include "chipdb.h"
#include "hash_table.h"
#include "log.h"
#include "nextpnr_namespaces.h"
#include "nextpnr_types.h"
NEXTPNR_NAMESPACE_BEGIN
struct Context;
struct SiteInformation
{
const Context *ctx;
const int32_t tile;
const int32_t tile_type;
const int32_t site;
const std::unordered_set<CellInfo *> &cells_in_site;
SiteInformation(const Context *ctx, int32_t tile, int32_t site,
const std::unordered_set<CellInfo *> &cells_in_site);
inline const ChipInfoPOD &chip_info() const NPNR_ALWAYS_INLINE;
inline bool is_wire_in_site(WireId wire) const NPNR_ALWAYS_INLINE;
inline bool is_bel_in_site(BelId bel) const NPNR_ALWAYS_INLINE;
inline bool is_pip_part_of_site(PipId pip) const NPNR_ALWAYS_INLINE;
inline bool is_site_port(PipId pip) const NPNR_ALWAYS_INLINE;
};
// Site routing needs a modification of the routing graph. Within the site,
// the arch can be consulted for edges. However the rest of the routing graph
// needs to be reduced for analysis purposes. Wires within the site are
// SITE_WIRE's. 4 additional nodes are introduced to model out of site
// routing:
// - OUT_OF_SITE_SOURCE / OUT_OF_SITE_SINK
// - These represent net sources and sinks that are only reachable via the
// routing graph (e.g. outside of the site).
// - SITE_PORT_SOURCE / SITE_PORT_SINK
// - These represent the routing resources connected to other side of site
// ports.
//
// The non-site wire graph is connected like:
//
// ┌─────────────────┐ ┌────────────────────┐
// │ │ │ │
// │ OUT_OF_SITE_SRC │ │ OUT_OF_SITE_SINK │◄────┐
// │ │ │ │ │
// └┬────────────────┘ └────────────────────┘ │
// │ │
// │ ┌─────────────────────────────────────────────────────┤
// │ │ │
// │ │ │
// │ │ │
// │ │ │
// │ ▼ │
// │ ┌─────────────────┐ ┌─────────────┐ ┌────────────────┐ │
// │ │ │ │ │ │ │ │
// └─────►│ SITE_PORT_SRC ├──►│ Site ├──────►│ SITE_PORT_SINK ├──┘
// │ │ │ │ │ │
// └─────────────────┘ └─────────────┘ └────────────────┘
//
struct SiteWire
{
enum Type
{
// This wire is just a plain site wire.
SITE_WIRE = 0,
// This wire is a source that is from outside of the site.
OUT_OF_SITE_SOURCE = 1,
// This wire is a sink that is from outside of the site.
OUT_OF_SITE_SINK = 2,
// This wire is the routing graph wire on the dst side of a site port.
SITE_PORT_SINK = 3,
// This wire is the routing graph wire on the src side of a site port.
SITE_PORT_SOURCE = 4,
NUMBER_SITE_WIRE_TYPES = 5,
};
static inline SiteWire make(const SiteInformation *site_info, WireId site_wire) NPNR_ALWAYS_INLINE;
static SiteWire make(const SiteInformation *site_info, PortType port_type, NetInfo *net) NPNR_ALWAYS_INLINE
{
SiteWire out;
if (port_type == PORT_OUT) {
out.type = OUT_OF_SITE_SOURCE;
out.net = net;
} else {
out.type = OUT_OF_SITE_SINK;
out.net = net;
}
return out;
}
static inline SiteWire make_site_port(const SiteInformation *site_info, PipId pip, bool dst_wire);
bool operator==(const SiteWire &other) const
{
return wire == other.wire && type == other.type && pip == other.pip && net == other.net;
}
bool operator!=(const SiteWire &other) const
{
return wire != other.wire || type != other.type || pip != other.pip || net != other.net;
}
bool operator<(const SiteWire &other) const
{
return std::make_tuple(type, wire, pip, net) < std::make_tuple(other.type, other.wire, other.pip, other.net);
}
Type type = NUMBER_SITE_WIRE_TYPES;
WireId wire;
PipId pip;
NetInfo *net = nullptr;
};
struct SitePip
{
enum Type
{
// This is a plain regular site pip.
SITE_PIP = 0,
// This pip is a site port, and connects a SITE_WIRE to a SITE_PORT_SINK/SITE_PORT_SRC
SITE_PORT = 1,
// This pip connects a OUT_OF_SITE_SOURCE to a SITE_PORT_SRC
SOURCE_TO_SITE_PORT = 2,
// This pip connects a SITE_PORT_SINK to a OUT_OF_SITE_SINK
SITE_PORT_TO_SINK = 3,
// This pip connects a SITE_PORT_SINK to a SITE_PORT_SRC.
SITE_PORT_TO_SITE_PORT = 4,
INVALID_TYPE = 5,
};
static inline SitePip make(const SiteInformation *site_info, PipId pip);
static SitePip make(const SiteInformation *site_info, SiteWire src, PipId dst)
{
NPNR_ASSERT(src.type == SiteWire::OUT_OF_SITE_SOURCE);
SitePip out;
out.type = SOURCE_TO_SITE_PORT;
out.pip = dst;
out.wire = src;
return out;
}
static SitePip make(const SiteInformation *site_info, PipId src, SiteWire dst)
{
NPNR_ASSERT(dst.type == SiteWire::OUT_OF_SITE_SINK);
SitePip out;
out.type = SITE_PORT_TO_SINK;
out.pip = src;
out.wire = dst;
return out;
}
static SitePip make(const SiteInformation *site_info, PipId src_pip, PipId dst_pip)
{
SitePip out;
out.type = SITE_PORT_TO_SITE_PORT;
out.pip = src_pip;
out.other_pip = dst_pip;
return out;
}
Type type = INVALID_TYPE;
// For SITE_PORT_TO_SITE_PORT connections, pip is the site -> routing pip.
PipId pip;
SiteWire wire;
// For SITE_PORT_TO_SITE_PORT connections, other_pip is the routing ->
// site pip.
PipId other_pip;
bool operator==(const SitePip &other) const
{
return type == other.type && pip == other.pip && wire == other.wire && other_pip == other.other_pip;
}
bool operator!=(const SitePip &other) const
{
return type != other.type || pip != other.pip || wire != other.wire || other_pip != other.other_pip;
}
};
NEXTPNR_NAMESPACE_END
template <> struct std::hash<NEXTPNR_NAMESPACE_PREFIX SiteWire>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX SiteWire &site_wire) const noexcept
{
std::size_t seed = 0;
boost::hash_combine(seed, std::hash<NEXTPNR_NAMESPACE_PREFIX SiteWire::Type>()(site_wire.type));
boost::hash_combine(seed, std::hash<NEXTPNR_NAMESPACE_PREFIX WireId>()(site_wire.wire));
boost::hash_combine(seed, std::hash<NEXTPNR_NAMESPACE_PREFIX PipId>()(site_wire.pip));
boost::hash_combine(seed, std::hash<NEXTPNR_NAMESPACE_PREFIX NetInfo *>()(site_wire.net));
return seed;
}
};
template <> struct std::hash<NEXTPNR_NAMESPACE_PREFIX SitePip>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX SitePip &site_pip) const noexcept
{
std::size_t seed = 0;
boost::hash_combine(seed, std::hash<NEXTPNR_NAMESPACE_PREFIX SitePip::Type>()(site_pip.type));
boost::hash_combine(seed, std::hash<NEXTPNR_NAMESPACE_PREFIX PipId>()(site_pip.pip));
boost::hash_combine(seed, std::hash<NEXTPNR_NAMESPACE_PREFIX SiteWire>()(site_pip.wire));
boost::hash_combine(seed, std::hash<NEXTPNR_NAMESPACE_PREFIX PipId>()(site_pip.other_pip));
return seed;
}
};
NEXTPNR_NAMESPACE_BEGIN
struct SitePipDownhillRange;
struct SitePipUphillRange;
struct SiteWireRange;
struct SiteNetInfo;
struct SitePipMap
{
SitePip pip;
size_t count;
};
struct SiteNetMap
{
SiteNetInfo *net;
size_t count;
};
struct SiteNetInfo
{
NetInfo *net;
SiteWire driver;
HashTables::HashSet<SiteWire> users;
HashTables::HashMap<SiteWire, SitePipMap> wires;
};
struct SiteArch
{
const Context *const ctx;
const SiteInformation *const site_info;
HashTables::HashMap<NetInfo *, SiteNetInfo> nets;
HashTables::HashMap<SiteWire, SiteNetMap> wire_to_nets;
std::vector<PipId> input_site_ports;
std::vector<PipId> output_site_ports;
std::vector<SiteWire> out_of_site_sources;
std::vector<SiteWire> out_of_site_sinks;
SiteArch(const SiteInformation *site_info);
inline SiteWire getPipSrcWire(const SitePip &site_pip) const NPNR_ALWAYS_INLINE;
inline SiteWire getPipDstWire(const SitePip &site_pip) const NPNR_ALWAYS_INLINE;
inline SitePipDownhillRange getPipsDownhill(const SiteWire &site_wire) const NPNR_ALWAYS_INLINE;
inline SitePipUphillRange getPipsUphill(const SiteWire &site_wire) const NPNR_ALWAYS_INLINE;
SiteWireRange getWires() const;
inline SiteWire getBelPinWire(BelId bel, IdString pin) const NPNR_ALWAYS_INLINE;
inline PortType getBelPinType(BelId bel, IdString pin) const NPNR_ALWAYS_INLINE;
const char *nameOfWire(const SiteWire &wire) const;
const char *nameOfPip(const SitePip &pip) const;
const char *nameOfNet(const SiteNetInfo *net) const;
bool debug() const;
bool bindWire(const SiteWire &wire, SiteNetInfo *net)
{
auto result = wire_to_nets.emplace(wire, SiteNetMap{net, 1});
if (result.first->second.net != net) {
if (debug()) {
log_info("Net conflict binding wire %s to net %s, conflicts with net %s\n", nameOfWire(wire),
nameOfNet(net), nameOfNet(result.first->second.net));
}
return false;
}
if (!result.second) {
result.first->second.count += 1;
}
return true;
}
SiteNetInfo *unbindWire(const SiteWire &wire)
{
auto iter = wire_to_nets.find(wire);
NPNR_ASSERT(iter != wire_to_nets.end());
NPNR_ASSERT(iter->second.count >= 1);
SiteNetInfo *net = iter->second.net;
iter->second.count -= 1;
if (iter->second.count == 0) {
wire_to_nets.erase(iter);
}
return net;
}
bool bindPip(const SitePip &pip, SiteNetInfo *net);
void unbindPip(const SitePip &pip);
void archcheck();
bool is_pip_synthetic(const SitePip &pip) const NPNR_ALWAYS_INLINE;
};
struct SitePipDownhillIterator
{
enum DownhillIteratorState
{
// Initial state
BEGIN = 0,
// Iterating over normal pips.
NORMAL_PIPS = 1,
// Iterating off all site port sources.
PORT_SINK_TO_PORT_SRC = 2,
// Iterating over out of site sinks.
OUT_OF_SITE_SINKS = 3,
// Iterating off all site port sources.
OUT_OF_SITE_SOURCE_TO_PORT_SRC = 4,
SITE_PORT = 5,
END = 6,
NUMBER_STATES = 7,
};
DownhillIteratorState state = BEGIN;
const SiteArch *site_arch;
SiteWire site_wire;
const RelSlice<int32_t> *pips_downhill;
size_t cursor;
bool advance_in_state() NPNR_ALWAYS_INLINE
{
switch (state) {
case BEGIN:
return false;
case NORMAL_PIPS:
++cursor;
return (cursor < pips_downhill->size());
case PORT_SINK_TO_PORT_SRC:
++cursor;
return (cursor < site_arch->input_site_ports.size());
case OUT_OF_SITE_SINKS:
++cursor;
return (cursor < site_arch->out_of_site_sinks.size());
case OUT_OF_SITE_SOURCE_TO_PORT_SRC:
++cursor;
return (cursor < site_arch->input_site_ports.size());
case SITE_PORT:
++cursor;
return false;
default:
// Unreachable!
NPNR_ASSERT(false);
}
}
bool check_first() const NPNR_ALWAYS_INLINE
{
switch (state) {
case BEGIN:
return false;
case NORMAL_PIPS:
return (cursor < pips_downhill->size());
case PORT_SINK_TO_PORT_SRC:
return (cursor < site_arch->input_site_ports.size());
case OUT_OF_SITE_SINKS:
return (cursor < site_arch->out_of_site_sinks.size());
case OUT_OF_SITE_SOURCE_TO_PORT_SRC:
return (cursor < site_arch->input_site_ports.size());
case SITE_PORT:
return true;
case END:
return true;
default:
// Unreachable!
NPNR_ASSERT(false);
}
}
const std::array<std::array<DownhillIteratorState, NUMBER_STATES>, SiteWire::NUMBER_SITE_WIRE_TYPES>
get_state_table() const
{
std::array<std::array<DownhillIteratorState, NUMBER_STATES>, SiteWire::NUMBER_SITE_WIRE_TYPES> state_table;
for (size_t j = 0; j < SiteWire::NUMBER_SITE_WIRE_TYPES; ++j) {
for (size_t i = 0; i < NUMBER_STATES; ++i) {
state_table[j][i] = NUMBER_STATES;
}
}
state_table[SiteWire::SITE_WIRE][BEGIN] = NORMAL_PIPS;
state_table[SiteWire::SITE_WIRE][NORMAL_PIPS] = END;
state_table[SiteWire::OUT_OF_SITE_SOURCE][BEGIN] = OUT_OF_SITE_SOURCE_TO_PORT_SRC;
state_table[SiteWire::OUT_OF_SITE_SOURCE][OUT_OF_SITE_SOURCE_TO_PORT_SRC] = END;
state_table[SiteWire::OUT_OF_SITE_SINK][BEGIN] = END;
state_table[SiteWire::SITE_PORT_SINK][BEGIN] = PORT_SINK_TO_PORT_SRC;
state_table[SiteWire::SITE_PORT_SINK][PORT_SINK_TO_PORT_SRC] = OUT_OF_SITE_SINKS;
state_table[SiteWire::SITE_PORT_SINK][OUT_OF_SITE_SINKS] = END;
state_table[SiteWire::SITE_PORT_SOURCE][BEGIN] = SITE_PORT;
state_table[SiteWire::SITE_PORT_SOURCE][SITE_PORT] = END;
return state_table;
}
void advance_state() NPNR_ALWAYS_INLINE
{
state = get_state_table().at(site_wire.type).at(state);
cursor = 0;
NPNR_ASSERT(state >= BEGIN && state <= END);
}
void operator++() NPNR_ALWAYS_INLINE
{
NPNR_ASSERT(state != END);
while (state != END) {
if (advance_in_state()) {
break;
} else {
advance_state();
if (check_first()) {
break;
}
}
}
}
bool operator!=(const SitePipDownhillIterator &other) const
{
return state != other.state || cursor != other.cursor;
}
inline SitePip operator*() const NPNR_ALWAYS_INLINE;
};
struct SitePipDownhillRange
{
const SiteArch *site_arch;
SiteWire site_wire;
SitePipDownhillRange(const SiteArch *site_arch, const SiteWire &site_wire)
: site_arch(site_arch), site_wire(site_wire)
{
}
inline const RelSlice<int32_t> *init_pip_range() const NPNR_ALWAYS_INLINE;
inline SitePipDownhillIterator begin() const NPNR_ALWAYS_INLINE;
SitePipDownhillIterator end() const NPNR_ALWAYS_INLINE
{
SitePipDownhillIterator e;
e.state = SitePipDownhillIterator::END;
e.cursor = 0;
return e;
}
};
struct SitePipUphillIterator
{
enum UphillIteratorState
{
// Initial state
BEGIN = 0,
// Iterating over normal pips.
NORMAL_PIPS = 1,
// Iterating off all site port sources.
PORT_SRC_TO_PORT_SINK = 2,
// Iterating over out of site sinks.
OUT_OF_SITE_SOURCES = 3,
// Iterating off all site port sources.
OUT_OF_SITE_SINK_TO_PORT_SINK = 4,
SITE_PORT = 5,
END = 6,
NUMBER_STATES = 7,
};
UphillIteratorState state = BEGIN;
const SiteArch *site_arch;
SiteWire site_wire;
size_t cursor;
UphillPipIterator iter;
UphillPipIterator uphill_end;
bool advance_in_state()
{
switch (state) {
case BEGIN:
return false;
case NORMAL_PIPS:
while (iter != uphill_end) {
++iter;
if (!(iter != uphill_end)) {
break;
}
}
return false;
case PORT_SRC_TO_PORT_SINK:
++cursor;
return (cursor < site_arch->output_site_ports.size());
case OUT_OF_SITE_SOURCES:
++cursor;
return (cursor < site_arch->out_of_site_sources.size());
case OUT_OF_SITE_SINK_TO_PORT_SINK:
++cursor;
return (cursor < site_arch->output_site_ports.size());
case SITE_PORT:
++cursor;
return false;
default:
// Unreachable!
NPNR_ASSERT(false);
}
}
bool check_first() const
{
switch (state) {
case BEGIN:
return false;
case NORMAL_PIPS:
if (!(iter != uphill_end)) {
return false;
} else {
return true;
}
case PORT_SRC_TO_PORT_SINK:
return (cursor < site_arch->output_site_ports.size());
case OUT_OF_SITE_SOURCES:
return (cursor < site_arch->out_of_site_sources.size());
case OUT_OF_SITE_SINK_TO_PORT_SINK:
return (cursor < site_arch->output_site_ports.size());
case SITE_PORT:
return true;
case END:
return true;
default:
// Unreachable!
NPNR_ASSERT(false);
}
}
const std::array<std::array<UphillIteratorState, NUMBER_STATES>, SiteWire::NUMBER_SITE_WIRE_TYPES>
get_state_table() const
{
std::array<std::array<UphillIteratorState, NUMBER_STATES>, SiteWire::NUMBER_SITE_WIRE_TYPES> state_table;
for (size_t j = 0; j < SiteWire::NUMBER_SITE_WIRE_TYPES; ++j) {
for (size_t i = 0; i < NUMBER_STATES; ++i) {
state_table[j][i] = NUMBER_STATES;
}
}
state_table[SiteWire::SITE_WIRE][BEGIN] = NORMAL_PIPS;
state_table[SiteWire::SITE_WIRE][NORMAL_PIPS] = END;
state_table[SiteWire::OUT_OF_SITE_SOURCE][BEGIN] = END;
state_table[SiteWire::OUT_OF_SITE_SINK][BEGIN] = OUT_OF_SITE_SINK_TO_PORT_SINK;
state_table[SiteWire::OUT_OF_SITE_SINK][OUT_OF_SITE_SINK_TO_PORT_SINK] = END;
state_table[SiteWire::SITE_PORT_SINK][BEGIN] = SITE_PORT;
state_table[SiteWire::SITE_PORT_SINK][SITE_PORT] = END;
state_table[SiteWire::SITE_PORT_SOURCE][BEGIN] = PORT_SRC_TO_PORT_SINK;
state_table[SiteWire::SITE_PORT_SOURCE][PORT_SRC_TO_PORT_SINK] = OUT_OF_SITE_SOURCES;
state_table[SiteWire::SITE_PORT_SOURCE][OUT_OF_SITE_SOURCES] = END;
return state_table;
}
void advance_state()
{
state = get_state_table().at(site_wire.type).at(state);
cursor = 0;
NPNR_ASSERT(state >= BEGIN && state <= END);
}
void operator++()
{
NPNR_ASSERT(state != END);
while (state != END) {
if (advance_in_state()) {
break;
} else {
advance_state();
if (check_first()) {
break;
}
}
}
}
bool operator!=(const SitePipUphillIterator &other) const
{
return state != other.state || cursor != other.cursor || iter != other.iter;
}
SitePip operator*() const;
};
struct SitePipUphillRange
{
const SiteArch *site_arch;
SiteWire site_wire;
UphillPipRange pip_range;
SitePipUphillRange(const SiteArch *site_arch, SiteWire site_wire);
SitePipUphillIterator begin() const
{
SitePipUphillIterator b;
b.state = SitePipUphillIterator::BEGIN;
b.site_arch = site_arch;
b.site_wire = site_wire;
b.cursor = 0;
b.iter = pip_range.b;
b.uphill_end = pip_range.e;
++b;
return b;
}
SitePipUphillIterator end() const
{
SitePipUphillIterator e;
e.state = SitePipUphillIterator::END;
e.site_arch = site_arch;
e.site_wire = site_wire;
e.cursor = 0;
e.iter = pip_range.e;
e.uphill_end = pip_range.e;
return e;
}
};
inline SitePipDownhillRange SiteArch::getPipsDownhill(const SiteWire &site_wire) const
{
return SitePipDownhillRange(this, site_wire);
}
inline SitePipUphillRange SiteArch::getPipsUphill(const SiteWire &site_wire) const
{
return SitePipUphillRange(this, site_wire);
}
struct SiteWireIterator
{
enum SiteWireIteratorState
{
// Initial state
BEGIN = 0,
NORMAL_WIRES = 1,
INPUT_SITE_PORTS = 2,
OUTPUT_SITE_PORTS = 3,
OUT_OF_SITE_SOURCES = 4,
OUT_OF_SITE_SINKS = 5,
END = 6,
};
SiteWireIteratorState state = BEGIN;
const SiteArch *site_arch;
const TileTypeInfoPOD *tile_type;
size_t cursor = 0;
bool advance_in_state()
{
switch (state) {
case BEGIN:
return false;
case NORMAL_WIRES:
while (true) {
++cursor;
if (cursor >= tile_type->wire_data.size()) {
return false;
}
if (tile_type->wire_data[cursor].site == site_arch->site_info->site) {
return true;
}
}
case INPUT_SITE_PORTS:
++cursor;
return (cursor < site_arch->input_site_ports.size());
case OUTPUT_SITE_PORTS:
++cursor;
return (cursor < site_arch->output_site_ports.size());
case OUT_OF_SITE_SOURCES:
++cursor;
return (cursor < site_arch->out_of_site_sources.size());
case OUT_OF_SITE_SINKS:
++cursor;
return (cursor < site_arch->out_of_site_sinks.size());
default:
// Unreachable!
NPNR_ASSERT(false);
}
}
// See if initial value in state is good.
bool check_first() const
{
switch (state) {
case BEGIN:
return false;
case NORMAL_WIRES:
if (cursor >= tile_type->wire_data.size()) {
return false;
}
return tile_type->wire_data[cursor].site == site_arch->site_info->site;
case INPUT_SITE_PORTS:
return (cursor < site_arch->input_site_ports.size());
case OUTPUT_SITE_PORTS:
return (cursor < site_arch->output_site_ports.size());
case OUT_OF_SITE_SOURCES:
return (cursor < site_arch->out_of_site_sources.size());
case OUT_OF_SITE_SINKS:
return (cursor < site_arch->out_of_site_sinks.size());
case END:
return true;
default:
// Unreachable!
NPNR_ASSERT(false);
}
}
void advance_state()
{
NPNR_ASSERT(state >= BEGIN && state < END);
state = static_cast<SiteWireIteratorState>(state + 1);
cursor = 0;
NPNR_ASSERT(state >= BEGIN && state <= END);
}
void operator++()
{
NPNR_ASSERT(state != END);
while (state != END) {
if (advance_in_state()) {
break;
} else {
advance_state();
if (check_first()) {
break;
}
}
}
}
bool operator!=(const SiteWireIterator &other) const { return state != other.state || cursor != other.cursor; }
SiteWire operator*() const;
};
struct SiteWireRange
{
const SiteArch *site_arch;
SiteWireRange(const SiteArch *site_arch) : site_arch(site_arch) {}
SiteWireIterator begin() const;
SiteWireIterator end() const
{
SiteWireIterator e;
e.state = SiteWireIterator::END;
return e;
}
};
inline SiteWireRange SiteArch::getWires() const { return SiteWireRange(this); }
NEXTPNR_NAMESPACE_END
#endif /* SITE_ARCH_H */

View File

@ -0,0 +1,255 @@
/*
* 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 SITE_ARCH_IMPL_H
#define SITE_ARCH_IMPL_H
#include "context.h"
#include "site_arch.h"
NEXTPNR_NAMESPACE_BEGIN
inline const ChipInfoPOD &SiteInformation::chip_info() const { return *ctx->chip_info; }
inline bool SiteInformation::is_wire_in_site(WireId wire) const
{
if (wire.tile != tile) {
return false;
}
return ctx->wire_info(wire).site == site;
}
inline bool SiteInformation::is_bel_in_site(BelId bel) const
{
if (bel.tile != tile) {
return false;
}
return bel_info(ctx->chip_info, bel).site == site;
}
inline bool SiteInformation::is_pip_part_of_site(PipId pip) const
{
if (pip.tile != tile) {
return false;
}
const auto &tile_type_data = ctx->chip_info->tile_types[tile_type];
const auto &pip_data = tile_type_data.pip_data[pip.index];
return pip_data.site == site;
}
inline bool SiteInformation::is_site_port(PipId pip) const
{
const auto &tile_type_data = ctx->chip_info->tile_types[tile_type];
const auto &pip_data = tile_type_data.pip_data[pip.index];
if (pip_data.site == -1) {
return false;
}
auto &bel_data = tile_type_data.bel_data[pip_data.bel];
return bel_data.category == BEL_CATEGORY_SITE_PORT;
}
inline SiteWire SiteWire::make(const SiteInformation *site_info, WireId site_wire)
{
NPNR_ASSERT(site_info->is_wire_in_site(site_wire));
SiteWire out;
out.type = SITE_WIRE;
out.wire = site_wire;
return out;
}
inline SiteWire SiteWire::make_site_port(const SiteInformation *site_info, PipId pip, bool dst_wire)
{
const auto &tile_type_data = site_info->chip_info().tile_types[site_info->tile_type];
const auto &pip_data = tile_type_data.pip_data[pip.index];
// This pip should definitely be part of this site
NPNR_ASSERT(pip_data.site == site_info->site);
SiteWire out;
const auto &src_data = tile_type_data.wire_data[pip_data.src_index];
const auto &dst_data = tile_type_data.wire_data[pip_data.dst_index];
if (dst_wire) {
if (src_data.site == site_info->site) {
NPNR_ASSERT(dst_data.site == -1);
out.type = SITE_PORT_SINK;
out.pip = pip;
out.wire = canonical_wire(&site_info->chip_info(), pip.tile, pip_data.dst_index);
} else {
NPNR_ASSERT(src_data.site == -1);
NPNR_ASSERT(dst_data.site == site_info->site);
out.type = SITE_WIRE;
out.wire.tile = pip.tile;
out.wire.index = pip_data.dst_index;
}
} else {
if (src_data.site == site_info->site) {
NPNR_ASSERT(dst_data.site == -1);
out.type = SITE_WIRE;
out.wire.tile = pip.tile;
out.wire.index = pip_data.src_index;
} else {
NPNR_ASSERT(src_data.site == -1);
NPNR_ASSERT(dst_data.site == site_info->site);
out.type = SITE_PORT_SOURCE;
out.pip = pip;
out.wire = canonical_wire(&site_info->chip_info(), pip.tile, pip_data.src_index);
}
}
return out;
}
inline SitePip SitePip::make(const SiteInformation *site_info, PipId pip)
{
SitePip out;
out.pip = pip;
if (site_info->is_site_port(pip)) {
out.type = SITE_PORT;
} else {
out.type = SITE_PIP;
}
return out;
}
inline SiteWire SiteArch::getPipSrcWire(const SitePip &site_pip) const
{
SiteWire site_wire;
switch (site_pip.type) {
case SitePip::Type::SITE_PIP:
return SiteWire::make(site_info, ctx->getPipSrcWire(site_pip.pip));
case SitePip::Type::SITE_PORT:
return SiteWire::make_site_port(site_info, site_pip.pip, /*dst_wire=*/false);
case SitePip::Type::SOURCE_TO_SITE_PORT:
NPNR_ASSERT(site_pip.wire.type == SiteWire::OUT_OF_SITE_SOURCE);
return site_pip.wire;
case SitePip::Type::SITE_PORT_TO_SINK:
site_wire = SiteWire::make_site_port(site_info, site_pip.pip, /*dst_wire=*/true);
NPNR_ASSERT(site_wire.type == SiteWire::SITE_PORT_SINK);
return site_wire;
case SitePip::Type::SITE_PORT_TO_SITE_PORT:
site_wire = SiteWire::make_site_port(site_info, site_pip.pip, /*dst_wire=*/true);
NPNR_ASSERT(site_wire.type == SiteWire::SITE_PORT_SINK);
return site_wire;
default:
// Unreachable!
NPNR_ASSERT(false);
}
}
inline SiteWire SiteArch::getPipDstWire(const SitePip &site_pip) const
{
switch (site_pip.type) {
case SitePip::Type::SITE_PIP:
return SiteWire::make(site_info, ctx->getPipDstWire(site_pip.pip));
case SitePip::Type::SITE_PORT:
return SiteWire::make_site_port(site_info, site_pip.pip, /*dst_wire=*/true);
case SitePip::Type::SOURCE_TO_SITE_PORT: {
SiteWire site_wire = SiteWire::make_site_port(site_info, site_pip.pip, /*dst_wire=*/false);
NPNR_ASSERT(site_wire.type == SiteWire::SITE_PORT_SOURCE);
return site_wire;
}
case SitePip::Type::SITE_PORT_TO_SINK:
NPNR_ASSERT(site_pip.wire.type == SiteWire::OUT_OF_SITE_SINK);
return site_pip.wire;
case SitePip::Type::SITE_PORT_TO_SITE_PORT: {
SiteWire site_wire = SiteWire::make_site_port(site_info, site_pip.other_pip, /*dst_wire=*/false);
NPNR_ASSERT(site_wire.type == SiteWire::SITE_PORT_SOURCE);
return site_wire;
}
default:
// Unreachable!
NPNR_ASSERT(false);
}
}
inline bool SiteArch::is_pip_synthetic(const SitePip &pip) const
{
if (pip.type != SitePip::SITE_PORT) {
// This isn't a site port, so its valid!
return false;
}
auto &tile_type = ctx->chip_info->tile_types[site_info->tile_type];
auto &pip_data = tile_type.pip_data[pip.pip.index];
if (pip_data.site == -1) {
return pip_data.extra_data == -1;
} else {
auto &bel_data = tile_type.bel_data[pip_data.bel];
return bel_data.synthetic != 0;
}
}
inline SitePip SitePipDownhillIterator::operator*() const
{
switch (state) {
case NORMAL_PIPS: {
PipId pip;
pip.tile = site_arch->site_info->tile;
pip.index = (*pips_downhill)[cursor];
return SitePip::make(site_arch->site_info, pip);
}
case PORT_SINK_TO_PORT_SRC:
return SitePip::make(site_arch->site_info, site_wire.pip, site_arch->input_site_ports.at(cursor));
case OUT_OF_SITE_SINKS:
return SitePip::make(site_arch->site_info, site_wire.pip, site_arch->out_of_site_sinks.at(cursor));
case OUT_OF_SITE_SOURCE_TO_PORT_SRC:
return SitePip::make(site_arch->site_info, site_wire, site_arch->input_site_ports.at(cursor));
case SITE_PORT:
return SitePip::make(site_arch->site_info, site_wire.pip);
default:
// Unreachable!
NPNR_ASSERT(false);
}
}
inline const RelSlice<int32_t> *SitePipDownhillRange::init_pip_range() const
{
NPNR_ASSERT(site_wire.type == SiteWire::SITE_WIRE);
NPNR_ASSERT(site_wire.wire.tile == site_arch->site_info->tile);
return &site_arch->ctx->chip_info->tile_types[site_arch->site_info->tile_type]
.wire_data[site_wire.wire.index]
.pips_downhill;
}
inline SitePipDownhillIterator SitePipDownhillRange::begin() const
{
SitePipDownhillIterator b;
b.state = SitePipDownhillIterator::BEGIN;
b.site_arch = site_arch;
b.site_wire = site_wire;
b.cursor = 0;
if (site_wire.type == SiteWire::SITE_WIRE) {
b.pips_downhill = init_pip_range();
}
++b;
return b;
}
NEXTPNR_NAMESPACE_END
#endif /* SITE_ARCH_H */

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,8 @@
#include "nextpnr_namespaces.h"
#include "nextpnr_types.h"
#include "site_arch.h"
#include "site_routing_storage.h"
NEXTPNR_NAMESPACE_BEGIN
@ -44,6 +46,7 @@ struct SiteRouter
void bindBel(CellInfo *cell);
void unbindBel(CellInfo *cell);
bool checkSiteRouting(const Context *ctx, const TileStatus &tile_status) const;
void bindSiteRouting(Context *ctx);
};
NEXTPNR_NAMESPACE_END

View File

@ -0,0 +1,193 @@
/*
* 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.
*
*/
#include "site_routing_cache.h"
#include "context.h"
#include "site_arch.impl.h"
NEXTPNR_NAMESPACE_BEGIN
void SiteRoutingSolution::store_solution(const SiteArch *ctx, const RouteNodeStorage *node_storage,
const SiteWire &driver, std::vector<size_t> solutions)
{
clear();
solution_sinks.reserve(solutions.size());
for (size_t route : solutions) {
SiteWire wire = node_storage->get_node(route)->wire;
solution_sinks.push_back(wire);
solution_offsets.push_back(solution_storage.size());
Node cursor = node_storage->get_node(route);
while (cursor.has_parent()) {
solution_storage.push_back(cursor->pip);
Node parent = cursor.parent();
NPNR_ASSERT(ctx->getPipDstWire(cursor->pip) == cursor->wire);
NPNR_ASSERT(ctx->getPipSrcWire(cursor->pip) == parent->wire);
cursor = parent;
}
NPNR_ASSERT(cursor->wire == driver);
}
solution_offsets.push_back(solution_storage.size());
}
void SiteRoutingSolution::verify(const SiteArch *ctx, const SiteNetInfo &net)
{
HashTables::HashSet<SiteWire> seen_users;
for (size_t i = 0; i < num_solutions(); ++i) {
SiteWire cursor = solution_sink(i);
NPNR_ASSERT(net.users.count(cursor) == 1);
seen_users.emplace(cursor);
auto begin = solution_begin(i);
auto end = solution_end(i);
for (auto iter = begin; iter != end; ++iter) {
SitePip pip = *iter;
NPNR_ASSERT(ctx->getPipDstWire(pip) == cursor);
cursor = ctx->getPipSrcWire(pip);
}
NPNR_ASSERT(net.driver == cursor);
}
NPNR_ASSERT(seen_users.size() == net.users.size());
}
SiteRoutingKey SiteRoutingKey::make(const SiteArch *ctx, const SiteNetInfo &site_net)
{
SiteRoutingKey out;
out.tile_type = ctx->site_info->tile_type;
out.site = ctx->site_info->site;
out.net_type = ctx->ctx->get_net_type(site_net.net);
out.driver_type = site_net.driver.type;
if (site_net.driver.type == SiteWire::SITE_WIRE) {
out.driver_index = site_net.driver.wire.index;
} else {
NPNR_ASSERT(site_net.driver.type == SiteWire::OUT_OF_SITE_SOURCE);
out.driver_index = -1;
}
out.user_types.reserve(site_net.users.size());
out.user_indicies.reserve(site_net.users.size());
std::vector<SiteWire> users;
users.reserve(site_net.users.size());
users.insert(users.begin(), site_net.users.begin(), site_net.users.end());
std::sort(users.begin(), users.end());
for (const SiteWire &user : users) {
out.user_types.push_back(user.type);
if (user.type == SiteWire::SITE_WIRE) {
out.user_indicies.push_back(user.wire.index);
} else {
NPNR_ASSERT(user.type == SiteWire::OUT_OF_SITE_SINK);
out.user_indicies.push_back(-1);
}
}
return out;
}
bool SiteRoutingCache::get_solution(const SiteArch *ctx, const SiteNetInfo &net, SiteRoutingSolution *solution) const
{
SiteRoutingKey key = SiteRoutingKey::make(ctx, net);
auto iter = cache_.find(key);
if (iter == cache_.end()) {
return false;
}
*solution = iter->second;
const auto &tile_type_data = ctx->site_info->chip_info().tile_types[ctx->site_info->tile_type];
for (SiteWire &wire : solution->solution_sinks) {
switch (wire.type) {
case SiteWire::SITE_WIRE:
wire.wire.tile = ctx->site_info->tile;
break;
case SiteWire::OUT_OF_SITE_SOURCE:
wire.net = net.net;
break;
case SiteWire::OUT_OF_SITE_SINK:
wire.net = net.net;
break;
case SiteWire::SITE_PORT_SINK: {
const auto &pip_data = tile_type_data.pip_data[wire.pip.index];
wire.pip.tile = ctx->site_info->tile;
wire.wire = canonical_wire(&ctx->site_info->chip_info(), ctx->site_info->tile, pip_data.dst_index);
break;
}
case SiteWire::SITE_PORT_SOURCE: {
const auto &pip_data = tile_type_data.pip_data[wire.pip.index];
wire.pip.tile = ctx->site_info->tile;
wire.wire = canonical_wire(&ctx->site_info->chip_info(), ctx->site_info->tile, pip_data.src_index);
break;
}
default:
NPNR_ASSERT(false);
}
}
for (SitePip &pip : solution->solution_storage) {
pip.pip.tile = ctx->site_info->tile;
switch (pip.type) {
case SitePip::SITE_PIP:
// Done!
break;
case SitePip::SITE_PORT:
// Done!
break;
case SitePip::SOURCE_TO_SITE_PORT:
NPNR_ASSERT(pip.wire.type == SiteWire::OUT_OF_SITE_SOURCE);
pip.wire.net = net.net;
break;
case SitePip::SITE_PORT_TO_SINK:
NPNR_ASSERT(pip.wire.type == SiteWire::OUT_OF_SITE_SINK);
pip.wire.net = net.net;
break;
case SitePip::SITE_PORT_TO_SITE_PORT:
pip.other_pip.tile = ctx->site_info->tile;
break;
default:
NPNR_ASSERT(false);
}
}
solution->verify(ctx, net);
return true;
}
void SiteRoutingCache::add_solutions(const SiteArch *ctx, const SiteNetInfo &net, const SiteRoutingSolution &solution)
{
SiteRoutingKey key = SiteRoutingKey::make(ctx, net);
cache_[key] = solution;
}
NEXTPNR_NAMESPACE_END

View File

@ -0,0 +1,134 @@
/*
* 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 SITE_ROUTING_CACHE_H
#define SITE_ROUTING_CACHE_H
#include "PhysicalNetlist.capnp.h"
#include "hash_table.h"
#include "nextpnr_namespaces.h"
#include "site_arch.h"
#include "site_routing_storage.h"
NEXTPNR_NAMESPACE_BEGIN
struct SiteRoutingSolution
{
void store_solution(const SiteArch *ctx, const RouteNodeStorage *node_storage, const SiteWire &driver,
std::vector<size_t> solutions);
void verify(const SiteArch *ctx, const SiteNetInfo &net);
void clear()
{
solution_offsets.clear();
solution_storage.clear();
solution_sinks.clear();
}
size_t num_solutions() const { return solution_sinks.size(); }
const SiteWire &solution_sink(size_t solution) const { return solution_sinks.at(solution); }
std::vector<SitePip>::const_iterator solution_begin(size_t solution) const
{
NPNR_ASSERT(solution + 1 < solution_offsets.size());
return solution_storage.begin() + solution_offsets.at(solution);
}
std::vector<SitePip>::const_iterator solution_end(size_t solution) const
{
NPNR_ASSERT(solution + 1 < solution_offsets.size());
return solution_storage.begin() + solution_offsets.at(solution + 1);
}
std::vector<size_t> solution_offsets;
std::vector<SitePip> solution_storage;
std::vector<SiteWire> solution_sinks;
};
struct SiteRoutingKey
{
int32_t tile_type;
int32_t site;
// The net type matters for site routing. Legal routes for VCC/GND/SIGNAL
// nets are different.
PhysicalNetlist::PhysNetlist::NetType net_type;
SiteWire::Type driver_type;
int32_t driver_index;
std::vector<SiteWire::Type> user_types;
std::vector<int32_t> user_indicies;
bool operator==(const SiteRoutingKey &other) const
{
return tile_type == other.tile_type && site == other.site && net_type == other.net_type &&
driver_type == other.driver_type && driver_index == other.driver_index &&
user_types == other.user_types && user_indicies == other.user_indicies;
}
bool operator!=(const SiteRoutingKey &other) const
{
return tile_type != other.tile_type || site != other.site || net_type != other.net_type ||
driver_type != other.driver_type || driver_index != other.driver_index ||
user_types != other.user_types || user_indicies != other.user_indicies;
}
static SiteRoutingKey make(const SiteArch *ctx, const SiteNetInfo &site_net);
};
NEXTPNR_NAMESPACE_END
template <> struct std::hash<NEXTPNR_NAMESPACE_PREFIX SiteRoutingKey>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX SiteRoutingKey &key) const noexcept
{
std::size_t seed = 0;
boost::hash_combine(seed, std::hash<int32_t>()(key.tile_type));
boost::hash_combine(seed, std::hash<int32_t>()(key.site));
boost::hash_combine(seed, std::hash<PhysicalNetlist::PhysNetlist::NetType>()(key.net_type));
boost::hash_combine(seed, std::hash<NEXTPNR_NAMESPACE_PREFIX SiteWire::Type>()(key.driver_type));
boost::hash_combine(seed, std::hash<int32_t>()(key.driver_index));
boost::hash_combine(seed, std::hash<std::size_t>()(key.user_types.size()));
for (NEXTPNR_NAMESPACE_PREFIX SiteWire::Type user_type : key.user_types) {
boost::hash_combine(seed, std::hash<NEXTPNR_NAMESPACE_PREFIX SiteWire::Type>()(user_type));
}
boost::hash_combine(seed, std::hash<std::size_t>()(key.user_indicies.size()));
for (int32_t index : key.user_indicies) {
boost::hash_combine(seed, std::hash<int32_t>()(index));
}
return seed;
}
};
NEXTPNR_NAMESPACE_BEGIN
// Provides an LRU cache for site routing solutions.
class SiteRoutingCache
{
public:
bool get_solution(const SiteArch *ctx, const SiteNetInfo &net, SiteRoutingSolution *solution) const;
void add_solutions(const SiteArch *ctx, const SiteNetInfo &net, const SiteRoutingSolution &solution);
private:
HashTables::HashMap<SiteRoutingKey, SiteRoutingSolution> cache_;
};
NEXTPNR_NAMESPACE_END
#endif /* SITE_ROUTING_CACHE_H */

View File

@ -0,0 +1,150 @@
/*
* 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 SITE_ROUTING_STORAGE
#define SITE_ROUTING_STORAGE
#include <limits>
#include "nextpnr_namespaces.h"
#include "site_arch.h"
NEXTPNR_NAMESPACE_BEGIN
struct RouteNode
{
void clear()
{
parent = std::numeric_limits<size_t>::max();
pip = SitePip();
wire = SiteWire();
flags = 0;
depth = 0;
}
enum Flags
{
// Has this path left the site?
LEFT_SITE = 0,
// Has this path entered the site?
ENTERED_SITE = 1,
};
bool has_left_site() const { return (flags & (1 << LEFT_SITE)) != 0; }
bool can_leave_site() const { return !has_left_site(); }
void mark_left_site() { flags |= (1 << LEFT_SITE); }
bool has_entered_site() const { return (flags & (1 << ENTERED_SITE)) != 0; }
bool can_enter_site() const { return !has_entered_site(); }
void mark_entered_site() { flags |= (1 << ENTERED_SITE); }
size_t parent;
SitePip pip; // What pip was taken to reach this node.
SiteWire wire; // What wire is this routing node located at?
int32_t flags;
int32_t depth;
};
struct RouteNodeStorage;
class Node
{
public:
Node(RouteNodeStorage *storage, size_t idx) : storage_(storage), idx_(idx) {}
size_t get_index() const { return idx_; }
RouteNode &operator*();
const RouteNode &operator*() const;
RouteNode *operator->();
const RouteNode *operator->() const;
bool has_parent() const;
Node parent();
private:
RouteNodeStorage *storage_;
size_t idx_;
};
struct RouteNodeStorage
{
// Free list of nodes.
std::vector<RouteNode> nodes;
std::vector<size_t> free_list;
// Either allocate a new node if no nodes are on the free list, or return
// an element from the free list.
Node alloc_node()
{
if (free_list.empty()) {
nodes.emplace_back();
nodes.back().clear();
return Node(this, nodes.size() - 1);
}
size_t idx = free_list.back();
free_list.pop_back();
nodes[idx].clear();
return Node(this, idx);
}
Node get_node(size_t idx)
{
NPNR_ASSERT(idx < nodes.size());
return Node(this, idx);
}
const Node get_node(size_t idx) const
{
NPNR_ASSERT(idx < nodes.size());
return Node(const_cast<RouteNodeStorage *>(this), idx);
}
// Return all node from the current owner to the free list.
void free_nodes(std::vector<size_t> &other_free_list)
{
free_list.insert(free_list.end(), other_free_list.begin(), other_free_list.end());
}
};
inline RouteNode &Node::operator*() { return storage_->nodes[idx_]; }
inline const RouteNode &Node::operator*() const { return storage_->nodes[idx_]; }
inline RouteNode *Node::operator->() { return &storage_->nodes[idx_]; }
inline const RouteNode *Node::operator->() const { return &storage_->nodes[idx_]; }
inline bool Node::has_parent() const { return storage_->nodes[idx_].parent < storage_->nodes.size(); }
inline Node Node::parent()
{
size_t parent_idx = storage_->nodes[idx_].parent;
NPNR_ASSERT(parent_idx < storage_->nodes.size());
return Node(storage_, parent_idx);
}
NEXTPNR_NAMESPACE_END
#endif /* SITE_ROUTING_STORAGE */