Refactor header structures in FPGA interchange Arch.

Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com>
This commit is contained in:
Keith Rothman 2021-03-19 17:35:29 -07:00
parent f52b522964
commit 2cd5bacca0
9 changed files with 1294 additions and 893 deletions

View File

@ -38,16 +38,19 @@
#define NPNR_NORETURN __attribute__((noreturn))
#define NPNR_DEPRECATED __attribute__((deprecated))
#define NPNR_PACKED_STRUCT(...) __VA_ARGS__ __attribute__((packed))
#define NPNR_ALWAYS_INLINE NPNR_ATTRIBUTE(__always_inline__)
#elif defined(_MSC_VER)
#define NPNR_ATTRIBUTE(...)
#define NPNR_NORETURN __declspec(noreturn)
#define NPNR_DEPRECATED __declspec(deprecated)
#define NPNR_PACKED_STRUCT(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop))
#define NPNR_ALWAYS_INLINE
#else
#define NPNR_ATTRIBUTE(...)
#define NPNR_NORETURN
#define NPNR_DEPRECATED
#define NPNR_PACKED_STRUCT(...) __VA_ARGS__
#define NPNR_ALWAYS_INLINE
#endif
#endif /* NEXTPNR_NAMESPACES_H */

View File

@ -19,6 +19,8 @@
*
*/
#include "arch.h"
#include <algorithm>
#include <boost/algorithm/string.hpp>
#include <boost/range/adaptor/reversed.hpp>
@ -37,6 +39,9 @@
#include "util.h"
#include "xdc.h"
// Include tcl.h late because it messed with defines and let them leave the
// scope of the header.
#include <tcl.h>
NEXTPNR_NAMESPACE_BEGIN
struct SiteBelPair
{
@ -90,6 +95,10 @@ Arch::Arch(ArchArgs args) : args(args)
log_error("Unable to read chipdb %s\n", args.chipdb.c_str());
}
if (chip_info->version != kExpectedChipInfoVersion) {
log_error("Expected chipdb with version %d found version %d\n", kExpectedChipInfoVersion, chip_info->version);
}
// Read strings from constids into IdString database, checking that list
// is unique and matches expected constid value.
const RelSlice<RelPtr<char>> &constids = *chip_info->constids;
@ -254,6 +263,8 @@ IdString Arch::archArgsToId(ArchArgs args) const { return IdString(); }
void Arch::setup_byname() const
{
by_name_mutex.lock();
if (tile_by_name.empty()) {
for (int i = 0; i < chip_info->tiles.ssize(); i++) {
tile_by_name[id(chip_info->tiles[i].name.get())] = i;
@ -270,6 +281,8 @@ void Arch::setup_byname() const
}
}
}
by_name_mutex.unlock();
}
BelId Arch::getBelByName(IdStringList name) const
@ -416,7 +429,8 @@ PipId Arch::getPipByName(IdStringList name) const
int tile;
int site;
std::tie(tile, site) = site_by_name.at(site_name);
auto &tile_info = chip_info->tile_types[chip_info->tiles[tile].type];
auto tile_type_idx = chip_info->tiles[tile].type;
auto &tile_info = chip_info->tile_types[tile_type_idx];
std::array<IdString, 2> ids{name.ids[0], belname};
BelId bel = getBelByName(IdStringList(ids));
@ -444,7 +458,8 @@ PipId Arch::getPipByName(IdStringList name) const
int tile;
int site;
std::tie(tile, site) = iter->second;
auto &tile_info = chip_info->tile_types[chip_info->tiles[tile].type];
auto tile_type_idx = chip_info->tiles[tile].type;
auto &tile_info = chip_info->tile_types[tile_type_idx];
std::string pip_second = name.ids[1].str(this);
auto split = pip_second.find('.');
@ -500,7 +515,8 @@ PipId Arch::getPipByName(IdStringList name) const
}
} else {
int tile = tile_by_name.at(name.ids[0]);
auto &tile_info = chip_info->tile_types[chip_info->tiles[tile].type];
size_t tile_type_idx = chip_info->tiles[tile].type;
auto &tile_info = chip_info->tile_types[tile_type_idx];
std::string pip_second = name.ids[1].str(this);
auto spn = split_identifier_name_dot(pip_second);
@ -618,11 +634,14 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const
int dst_tile = dst.tile == -1 ? chip_info->nodes[dst.index].tile_wires[0].tile : dst.tile;
int src_tile = src.tile == -1 ? chip_info->nodes[src.index].tile_wires[0].tile : src.tile;
int x0, x1, y0, y1;
x0 = src_tile % chip_info->width;
x1 = x0;
y0 = src_tile / chip_info->width;
y1 = y0;
int x0 = 0, x1 = 0, y0 = 0, y1 = 0;
int src_x, src_y;
get_tile_x_y(src_tile, &src_x, &src_y);
int dst_x, dst_y;
get_tile_x_y(dst_tile, &dst_x, &dst_y);
auto expand = [&](int x, int y) {
x0 = std::min(x0, x);
x1 = std::max(x1, x);
@ -630,7 +649,8 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const
y1 = std::max(y1, y);
};
expand(dst_tile % chip_info->width, dst_tile / chip_info->width);
expand(src_x, src_y);
expand(dst_x, dst_y);
if (source_locs.count(src))
expand(source_locs.at(src).x, source_locs.at(src).y);
@ -1331,6 +1351,262 @@ void Arch::decode_lut_cells()
}
}
void Arch::assign_net_to_wire(WireId wire, NetInfo *net, const char *src, bool require_empty)
{
#ifdef DEBUG_BINDING
if (getCtx()->verbose) {
log_info("Assigning wire %s to %s from %s\n", nameOfWire(wire), net->name.c_str(this), src);
}
#endif
NPNR_ASSERT(net != nullptr);
auto result = wire_to_net.emplace(wire, net);
if (!result.second) {
// This wire was already in the map, make sure this assignment was
// legal.
if (require_empty) {
NPNR_ASSERT(result.first->second == nullptr);
} else {
NPNR_ASSERT(result.first->second == nullptr || result.first->second == net);
}
result.first->second = net;
}
}
void Arch::unassign_wire(WireId wire)
{
NPNR_ASSERT(wire != WireId());
#ifdef DEBUG_BINDING
if (getCtx()->verbose) {
log_info("unassign_wire %s\n", nameOfWire(wire));
}
#endif
auto iter = wire_to_net.find(wire);
NPNR_ASSERT(iter != wire_to_net.end());
NetInfo *net = iter->second;
NPNR_ASSERT(net != nullptr);
auto &net_wires = net->wires;
auto it = net_wires.find(wire);
NPNR_ASSERT(it != net_wires.end());
auto pip = it->second.pip;
if (pip != PipId()) {
#ifdef DEBUG_BINDING
if (getCtx()->verbose) {
log_info("Removing pip %s because it was used to reach wire %s\n", nameOfPip(pip), nameOfWire(wire));
}
#endif
auto pip_iter = pip_to_net.find(pip);
NPNR_ASSERT(pip_iter != pip_to_net.end());
NPNR_ASSERT(pip_iter->second == net);
pip_iter->second = nullptr;
}
net_wires.erase(it);
#ifdef DEBUG_BINDING
if (getCtx()->verbose) {
log_info("Removing %s from net %s in unassign_wire\n", nameOfWire(wire), net->name.c_str(this));
}
#endif
iter->second = nullptr;
}
void Arch::unbindPip(PipId pip)
{
NPNR_ASSERT(pip != PipId());
#ifdef DEBUG_BINDING
if (getCtx()->verbose) {
log_info("unbindPip %s\n", nameOfPip(pip));
}
#endif
auto pip_iter = pip_to_net.find(pip);
NPNR_ASSERT(pip_iter != pip_to_net.end());
NetInfo *net = pip_iter->second;
NPNR_ASSERT(net != nullptr);
WireId dst = getPipDstWire(pip);
auto wire_iter = wire_to_net.find(dst);
NPNR_ASSERT(wire_iter != wire_to_net.end());
// Clear the net now.
pip_iter->second = nullptr;
#ifdef DEBUG_BINDING
if (getCtx()->verbose) {
log_info("Removing %s from net %s in unbindPip\n", nameOfWire(dst), net->name.c_str(this));
}
#endif
wire_iter->second = nullptr;
NPNR_ASSERT(net->wires.erase(dst) == 1);
refreshUiPip(pip);
refreshUiWire(dst);
}
void Arch::bindPip(PipId pip, NetInfo *net, PlaceStrength strength)
{
NPNR_ASSERT(pip != PipId());
#ifdef DEBUG_BINDING
if (getCtx()->verbose) {
log_info("bindPip %s (%d/%d) to net %s\n", nameOfPip(pip), pip.tile, pip.index, net->name.c_str(this));
}
#endif
WireId dst = getPipDstWire(pip);
NPNR_ASSERT(dst != WireId());
{
// Pip should not already be assigned to anything.
auto result = pip_to_net.emplace(pip, net);
if (!result.second) {
NPNR_ASSERT(result.first->second == nullptr);
result.first->second = net;
}
}
assign_net_to_wire(dst, net, "bindPip", /*require_empty=*/true);
{
auto result = net->wires.emplace(dst, PipMap{pip, strength});
NPNR_ASSERT(result.second);
}
refreshUiPip(pip);
refreshUiWire(dst);
}
void Arch::bindWire(WireId wire, NetInfo *net, PlaceStrength strength)
{
NPNR_ASSERT(wire != WireId());
#ifdef DEBUG_BINDING
if (getCtx()->verbose) {
log_info("bindWire %s to net %s\n", nameOfWire(wire), net->name.c_str(this));
}
#endif
assign_net_to_wire(wire, net, "bindWire", /*require_empty=*/true);
auto &pip_map = net->wires[wire];
pip_map.pip = PipId();
pip_map.strength = strength;
refreshUiWire(wire);
}
bool Arch::check_pip_avail_for_net(PipId pip, NetInfo *net) const
{
NPNR_ASSERT(pip != PipId());
auto pip_iter = pip_to_net.find(pip);
if (pip_iter != pip_to_net.end() && pip_iter->second != nullptr) {
bool pip_blocked = false;
if (net == nullptr) {
pip_blocked = true;
} else {
if (net != pip_iter->second) {
pip_blocked = true;
}
}
if (pip_blocked) {
#ifdef DEBUG_BINDING
if (getCtx()->verbose) {
log_info("Pip %s (%d/%d) is not available, tied to net %s\n", getCtx()->nameOfPip(pip), pip.tile,
pip.index, pip_iter->second->name.c_str(getCtx()));
}
#endif
NPNR_ASSERT(pip_iter->first == pip);
return false;
}
}
WireId dst = getPipDstWire(pip);
auto wire_iter = wire_to_net.find(dst);
if (wire_iter != wire_to_net.end()) {
NetInfo *wire_net = wire_iter->second;
if (wire_net != nullptr) {
auto net_iter = wire_net->wires.find(dst);
if (net_iter != wire_net->wires.end()) {
if (net == nullptr) {
#ifdef DEBUG_BINDING
if (getCtx()->verbose) {
log_info("Pip %s (%d/%d) is not available, dst wire %s is tied to net %s\n",
getCtx()->nameOfPip(pip), pip.tile, pip.index, getCtx()->nameOfWire(dst),
wire_net->name.c_str(getCtx()));
}
#endif
// dst is already driven in this net, do not allow!
return false;
} else {
#ifdef DEBUG_BINDING
if (getCtx()->verbose && net_iter->second.pip != pip) {
log_info("Pip %s (%d/%d) is not available, dst wire %s is tied to net %s\n",
getCtx()->nameOfPip(pip), pip.tile, pip.index, getCtx()->nameOfWire(dst),
wire_net->name.c_str(getCtx()));
}
#endif
// This pip is available if this pip is already bound to
// this.
return net_iter->second.pip == pip;
}
}
}
}
const PipInfoPOD &pip_data = pip_info(chip_info, pip);
if (pip_data.site != -1 && net != nullptr) {
NPNR_ASSERT(net->driver.cell != nullptr);
NPNR_ASSERT(net->driver.cell->bel != BelId());
bool valid_pip = false;
if (pip.tile == net->driver.cell->bel.tile) {
auto &bel_data = bel_info(chip_info, net->driver.cell->bel);
if (bel_data.site == pip_data.site) {
valid_pip = true;
}
}
if (!valid_pip) {
// See if one users can enter this site.
auto &tile_type = loc_info(chip_info, pip);
auto &src_wire_data = tile_type.wire_data[pip_data.src_index];
auto &dst_wire_data = tile_type.wire_data[pip_data.dst_index];
if (dst_wire_data.site == -1) {
// This is an output site port, but not for the driver net.
// Disallow.
NPNR_ASSERT(src_wire_data.site == pip_data.site);
} else {
// This might be a valid pip, scan users.
for (auto &user : net->users) {
NPNR_ASSERT(user.cell != nullptr);
if (user.cell->bel == BelId()) {
continue;
}
auto &bel_data = bel_info(chip_info, user.cell->bel);
if (bel_data.site == pip_data.site) {
valid_pip = true;
break;
}
}
}
}
if (!valid_pip) {
#ifdef DEBUG_BINDING
if (getCtx()->verbose) {
log_info("Pip %s is within a site and not available not right now\n", getCtx()->nameOfPip(pip));
}
#endif
return false;
}
}
return true;
}
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);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,497 @@
/*
* 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 ARCH_ITERATORS_H
#define ARCH_ITERATORS_H
#include "chipdb.h"
#include "nextpnr_namespaces.h"
#include "nextpnr_types.h"
NEXTPNR_NAMESPACE_BEGIN
struct BelIterator
{
const ChipInfoPOD *chip;
int cursor_index;
int cursor_tile;
BelIterator operator++()
{
cursor_index++;
while (cursor_tile < chip->tiles.ssize() && cursor_index >= tile_info(chip, cursor_tile).bel_data.ssize()) {
cursor_index = 0;
cursor_tile++;
}
return *this;
}
BelIterator operator++(int)
{
BelIterator prior(*this);
++(*this);
return prior;
}
bool operator!=(const BelIterator &other) const
{
return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile;
}
bool operator==(const BelIterator &other) const
{
return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile;
}
BelId operator*() const
{
BelId ret;
ret.tile = cursor_tile;
ret.index = cursor_index;
return ret;
}
};
struct BelRange
{
BelIterator b, e;
BelIterator begin() const { return b; }
BelIterator end() const { return e; }
};
struct FilteredBelIterator
{
std::function<bool(BelId)> filter;
BelIterator b, e;
FilteredBelIterator operator++()
{
++b;
while (b != e) {
if (filter(*b)) {
break;
}
++b;
}
return *this;
}
bool operator!=(const FilteredBelIterator &other) const
{
NPNR_ASSERT(e == other.e);
return b != other.b;
}
bool operator==(const FilteredBelIterator &other) const
{
NPNR_ASSERT(e == other.e);
return b == other.b;
}
BelId operator*() const
{
BelId bel = *b;
NPNR_ASSERT(filter(bel));
return bel;
}
};
struct FilteredBelRange
{
FilteredBelRange(BelIterator bel_b, BelIterator bel_e, std::function<bool(BelId)> filter)
{
b.filter = filter;
b.b = bel_b;
b.e = bel_e;
if (b.b != b.e && !filter(*b.b)) {
++b;
}
e.b = bel_e;
e.e = bel_e;
if (b != e) {
NPNR_ASSERT(filter(*b.b));
}
}
FilteredBelIterator b, e;
FilteredBelIterator begin() const { return b; }
FilteredBelIterator end() const { return e; }
};
// -----------------------------------------------------------------------
// Iterate over TileWires for a wire (will be more than one if nodal)
struct TileWireIterator
{
const ChipInfoPOD *chip;
WireId baseWire;
int cursor = -1;
void operator++() { cursor++; }
bool operator==(const TileWireIterator &other) const { return cursor == other.cursor; }
bool operator!=(const TileWireIterator &other) const { return cursor != other.cursor; }
// Returns a *denormalised* identifier always pointing to a tile wire rather than a node
WireId operator*() const
{
if (baseWire.tile == -1) {
WireId tw;
const auto &node_wire = chip->nodes[baseWire.index].tile_wires[cursor];
tw.tile = node_wire.tile;
tw.index = node_wire.index;
return tw;
} else {
return baseWire;
}
}
};
struct TileWireRange
{
TileWireIterator b, e;
TileWireIterator begin() const { return b; }
TileWireIterator end() const { return e; }
};
NPNR_ALWAYS_INLINE inline WireId canonical_wire(const ChipInfoPOD *chip_info, int32_t tile, int32_t wire)
{
WireId id;
if (wire >= chip_info->tiles[tile].tile_wire_to_node.ssize()) {
// Cannot be a nodal wire
id.tile = tile;
id.index = wire;
} else {
int32_t node = chip_info->tiles[tile].tile_wire_to_node[wire];
if (node == -1) {
// Not a nodal wire
id.tile = tile;
id.index = wire;
} else {
// Is a nodal wire, set tile to -1
id.tile = -1;
id.index = node;
}
}
return id;
}
// -----------------------------------------------------------------------
struct WireIterator
{
const ChipInfoPOD *chip;
int cursor_index = 0;
int cursor_tile = -1;
WireIterator operator++()
{
// Iterate over nodes first, then tile wires that aren't nodes
do {
cursor_index++;
if (cursor_tile == -1 && cursor_index >= chip->nodes.ssize()) {
cursor_tile = 0;
cursor_index = 0;
}
while (cursor_tile != -1 && cursor_tile < chip->tiles.ssize() &&
cursor_index >= chip->tile_types[chip->tiles[cursor_tile].type].wire_data.ssize()) {
cursor_index = 0;
cursor_tile++;
}
} while ((cursor_tile != -1 && cursor_tile < chip->tiles.ssize() &&
cursor_index < chip->tiles[cursor_tile].tile_wire_to_node.ssize() &&
chip->tiles[cursor_tile].tile_wire_to_node[cursor_index] != -1));
return *this;
}
WireIterator operator++(int)
{
WireIterator prior(*this);
++(*this);
return prior;
}
bool operator!=(const WireIterator &other) const
{
return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile;
}
bool operator==(const WireIterator &other) const
{
return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile;
}
WireId operator*() const
{
WireId ret;
ret.tile = cursor_tile;
ret.index = cursor_index;
return ret;
}
};
struct WireRange
{
WireIterator b, e;
WireIterator begin() const { return b; }
WireIterator end() const { return e; }
};
// -----------------------------------------------------------------------
struct AllPipIterator
{
const ChipInfoPOD *chip;
int cursor_index;
int cursor_tile;
AllPipIterator operator++()
{
cursor_index++;
while (cursor_tile < chip->tiles.ssize() &&
cursor_index >= chip->tile_types[chip->tiles[cursor_tile].type].pip_data.ssize()) {
cursor_index = 0;
cursor_tile++;
}
return *this;
}
AllPipIterator operator++(int)
{
AllPipIterator prior(*this);
++(*this);
return prior;
}
bool operator!=(const AllPipIterator &other) const
{
return cursor_index != other.cursor_index || cursor_tile != other.cursor_tile;
}
bool operator==(const AllPipIterator &other) const
{
return cursor_index == other.cursor_index && cursor_tile == other.cursor_tile;
}
PipId operator*() const
{
PipId ret;
ret.tile = cursor_tile;
ret.index = cursor_index;
return ret;
}
};
struct AllPipRange
{
AllPipIterator b, e;
AllPipIterator begin() const { return b; }
AllPipIterator end() const { return e; }
};
// -----------------------------------------------------------------------
struct UphillPipIterator
{
const ChipInfoPOD *chip;
TileWireIterator twi, twi_end;
int cursor = -1;
void operator++()
{
cursor++;
while (true) {
if (!(twi != twi_end))
break;
WireId w = *twi;
auto &tile = chip->tile_types[chip->tiles[w.tile].type];
if (cursor < tile.wire_data[w.index].pips_uphill.ssize())
break;
++twi;
cursor = 0;
}
}
bool operator!=(const UphillPipIterator &other) const { return twi != other.twi || cursor != other.cursor; }
PipId operator*() const
{
PipId ret;
WireId w = *twi;
ret.tile = w.tile;
ret.index = chip->tile_types[chip->tiles[w.tile].type].wire_data[w.index].pips_uphill[cursor];
return ret;
}
};
struct UphillPipRange
{
UphillPipIterator b, e;
UphillPipIterator begin() const { return b; }
UphillPipIterator end() const { return e; }
};
struct DownhillPipIterator
{
const ChipInfoPOD *chip;
TileWireIterator twi, twi_end;
int cursor = -1;
int32_t tile;
int32_t tile_type;
const RelSlice<int32_t> *pips_downhill = nullptr;
void operator++()
{
cursor++;
while (true) {
if (!(twi != twi_end))
break;
if (pips_downhill == nullptr) {
WireId w = *twi;
tile_type = chip->tiles[w.tile].type;
const TileTypeInfoPOD &type = chip->tile_types[tile_type];
tile = w.tile;
pips_downhill = &type.wire_data[w.index].pips_downhill;
}
if (cursor < pips_downhill->ssize())
break;
++twi;
cursor = 0;
pips_downhill = nullptr;
}
}
bool operator!=(const DownhillPipIterator &other) const { return twi != other.twi || cursor != other.cursor; }
PipId operator*() const
{
PipId ret;
ret.tile = tile;
ret.index = (*pips_downhill)[cursor];
return ret;
}
};
struct DownhillPipRange
{
DownhillPipIterator b, e;
DownhillPipIterator begin() const { return b; }
DownhillPipIterator end() const { return e; }
};
struct BelPinIterator
{
const ChipInfoPOD *chip;
TileWireIterator twi, twi_end;
int cursor = -1;
void operator++()
{
cursor++;
while (twi != twi_end) {
WireId w = *twi;
auto &tile = tile_info(chip, w.tile);
if (cursor < tile.wire_data[w.index].bel_pins.ssize())
break;
++twi;
cursor = 0;
}
}
bool operator!=(const BelPinIterator &other) const { return twi != other.twi || cursor != other.cursor; }
BelPin operator*() const
{
BelPin ret;
WireId w = *twi;
ret.bel.tile = w.tile;
ret.bel.index = tile_info(chip, w.tile).wire_data[w.index].bel_pins[cursor].bel_index;
ret.pin.index = tile_info(chip, w.tile).wire_data[w.index].bel_pins[cursor].port;
return ret;
}
};
struct BelPinRange
{
BelPinIterator b, e;
BelPinIterator begin() const { return b; }
BelPinIterator end() const { return e; }
};
struct IdStringIterator : std::iterator<std::forward_iterator_tag,
/*T=*/IdString,
/*Distance=*/ptrdiff_t,
/*pointer=*/IdString *,
/*reference=*/IdString>
{
const int32_t *cursor;
void operator++() { cursor += 1; }
bool operator!=(const IdStringIterator &other) const { return cursor != other.cursor; }
bool operator==(const IdStringIterator &other) const { return cursor == other.cursor; }
IdString operator*() const { return IdString(*cursor); }
};
struct IdStringRange
{
IdStringIterator b, e;
IdStringIterator begin() const { return b; }
IdStringIterator end() const { return e; }
};
struct BelBucketIterator
{
IdStringIterator cursor;
void operator++() { ++cursor; }
bool operator!=(const BelBucketIterator &other) const { return cursor != other.cursor; }
bool operator==(const BelBucketIterator &other) const { return cursor == other.cursor; }
BelBucketId operator*() const
{
BelBucketId bucket;
bucket.name = IdString(*cursor);
return bucket;
}
};
struct BelBucketRange
{
BelBucketIterator b, e;
BelBucketIterator begin() const { return b; }
BelBucketIterator end() const { return e; }
};
NEXTPNR_NAMESPACE_END
#endif /* ARCH_ITERATORS_H */

View File

@ -24,6 +24,7 @@
#include <boost/functional/hash.hpp>
#include <cstdint>
#include "hash_table.h"
#include "luts.h"
#include "nextpnr_namespaces.h"
@ -108,8 +109,8 @@ struct ArchCellInfo
ArchCellInfo() : cell_mapping(-1) {}
int32_t cell_mapping;
std::unordered_map<IdString, std::vector<IdString>> cell_bel_pins;
std::unordered_set<IdString> const_ports;
HashTables::HashMap<IdString, std::vector<IdString>> cell_bel_pins;
HashTables::HashSet<IdString> const_ports;
LutCell lut_cell;
};

310
fpga_interchange/chipdb.h Normal file
View File

@ -0,0 +1,310 @@
/*
* 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 CHIPDB_H
#define CHIPDB_H
#include "archdefs.h"
#include "nextpnr_namespaces.h"
#include "relptr.h"
NEXTPNR_NAMESPACE_BEGIN
/* !!! Everything in this section must be kept in sync !!!
* !!! with fpga_interchange/chip_info.py !!!
*
* When schema changes, bump version number in chip_info.py and
* kExpectedChipInfoVersion
*/
static constexpr int32_t kExpectedChipInfoVersion = 1;
// Flattened site indexing.
//
// To enable flat BelId.z spaces, every tile and sites within that tile are
// flattened.
//
// This has implications on BelId's, WireId's and PipId's.
// The flattened site space works as follows:
// - Objects that belong to the tile are first. BELs are always part of Sites,
// so no BEL objects are in this category.
// - All site alternative modes are exposed as a "full" site.
// - Each site appends it's BEL's, wires (site wires) and PIP's.
// - Sites add two types of pips. Sites will add pip data first for site
// pips, and then for site pin edges.
// 1. The first type is site pips, which connect site wires to other site
// wires.
// 2. The second type is site pin edges, which connect site wires to tile
// wires (or vise-versa).
NPNR_PACKED_STRUCT(struct BelInfoPOD {
int32_t name; // bel name (in site) constid
int32_t type; // Type name constid
int32_t bel_bucket; // BEL bucket constid.
int32_t num_bel_wires;
RelPtr<int32_t> ports; // port name constid
RelPtr<int32_t> types; // port type (IN/OUT/BIDIR)
RelPtr<int32_t> wires; // connected wire index in tile, or -1 if NA
int16_t site;
int16_t site_variant; // some sites have alternative types
int16_t category;
int8_t synthetic;
int8_t lut_element;
RelPtr<int32_t> pin_map; // Index into CellMapPOD::cell_bel_map
});
enum BELCategory
{
// BEL is a logic element
BEL_CATEGORY_LOGIC = 0,
// BEL is a site routing mux
BEL_CATEGORY_ROUTING = 1,
// BEL is a site port, e.g. boundry between site and routing graph.
BEL_CATEGORY_SITE_PORT = 2
};
NPNR_PACKED_STRUCT(struct BelPortPOD {
int32_t bel_index;
int32_t port;
});
NPNR_PACKED_STRUCT(struct TileWireInfoPOD {
int32_t name; // wire name constid
// Pip index inside tile
RelSlice<int32_t> pips_uphill;
// Pip index inside tile
RelSlice<int32_t> pips_downhill;
// Bel index inside tile
RelSlice<BelPortPOD> bel_pins;
int16_t site; // site index in tile
int16_t site_variant; // site variant index in tile
});
NPNR_PACKED_STRUCT(struct PipInfoPOD {
int32_t src_index, dst_index;
int16_t site; // site index in tile
int16_t site_variant; // site variant index in tile
int16_t bel; // BEL this pip belongs to if site pip.
int16_t extra_data;
});
NPNR_PACKED_STRUCT(struct ConstraintTagPOD {
int32_t tag_prefix; // constid
int32_t default_state; // constid
RelSlice<int32_t> states; // constid
});
NPNR_PACKED_STRUCT(struct LutBelPOD {
uint32_t name; // constid
RelSlice<int32_t> pins; // constid
uint32_t low_bit;
uint32_t high_bit;
});
NPNR_PACKED_STRUCT(struct LutElementPOD {
int32_t width;
RelSlice<LutBelPOD> lut_bels;
});
NPNR_PACKED_STRUCT(struct TileTypeInfoPOD {
int32_t name; // Tile type constid
RelSlice<BelInfoPOD> bel_data;
RelSlice<TileWireInfoPOD> wire_data;
RelSlice<PipInfoPOD> pip_data;
RelSlice<ConstraintTagPOD> tags;
RelSlice<LutElementPOD> lut_elements;
RelSlice<int32_t> site_types; // constid
});
NPNR_PACKED_STRUCT(struct SiteInstInfoPOD {
RelPtr<char> name;
RelPtr<char> site_name;
// Which site type is this site instance?
// constid
int32_t site_type;
});
NPNR_PACKED_STRUCT(struct TileInstInfoPOD {
// Name of this tile.
RelPtr<char> name;
// Index into root.tile_types.
int32_t type;
// This array is root.tile_types[type].site_types.size() long.
// Index into root.sites
RelSlice<int32_t> sites;
// Number of tile wires; excluding any site-internal wires
// which come after general wires and are not stored here
// as they will never be nodal
// -1 if a tile-local wire; node index if nodal wire
RelSlice<int32_t> tile_wire_to_node;
});
NPNR_PACKED_STRUCT(struct TileWireRefPOD {
int32_t tile;
int32_t index;
});
NPNR_PACKED_STRUCT(struct NodeInfoPOD { RelSlice<TileWireRefPOD> tile_wires; });
NPNR_PACKED_STRUCT(struct CellBelPinPOD {
int32_t cell_pin; // constid
int32_t bel_pin; // constid
});
NPNR_PACKED_STRUCT(struct ParameterPinsPOD {
int32_t key; // constid
int32_t value; // constid
RelSlice<CellBelPinPOD> pins;
});
NPNR_PACKED_STRUCT(struct CellConstraintPOD {
int32_t tag; // Tag index
int32_t constraint_type; // Constraint::ConstraintType
RelSlice<int32_t> states; // State indicies
});
NPNR_PACKED_STRUCT(struct CellBelMapPOD {
RelSlice<CellBelPinPOD> common_pins;
RelSlice<ParameterPinsPOD> parameter_pins;
RelSlice<CellConstraintPOD> constraints;
});
NPNR_PACKED_STRUCT(struct LutCellPOD {
int32_t cell; // constid
RelSlice<int32_t> input_pins; // constids
int32_t parameter;
});
NPNR_PACKED_STRUCT(struct CellMapPOD {
// Cell names supported in this arch.
RelSlice<int32_t> cell_names; // constids
RelSlice<int32_t> cell_bel_buckets; // constids
RelSlice<CellBelMapPOD> cell_bel_map;
RelSlice<LutCellPOD> lut_cells;
});
NPNR_PACKED_STRUCT(struct PackagePinPOD {
int32_t package_pin; // constid
int32_t site; // constid
int32_t bel; // constid
});
NPNR_PACKED_STRUCT(struct PackagePOD {
int32_t package; // constid
RelSlice<PackagePinPOD> pins;
});
NPNR_PACKED_STRUCT(struct ConstantsPOD {
// Cell type and port for the GND and VCC global source.
int32_t gnd_cell_name; // constid
int32_t gnd_cell_port; // constid
int32_t vcc_cell_name; // constid
int32_t vcc_cell_port; // constid
int32_t gnd_bel_tile;
int32_t gnd_bel_index;
int32_t gnd_bel_pin; // constid
int32_t vcc_bel_tile;
int32_t vcc_bel_index;
int32_t vcc_bel_pin; // constid
// Name to use for the global GND constant net
int32_t gnd_net_name; // constid
// Name to use for the global VCC constant net
int32_t vcc_net_name; // constid
});
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
RelPtr<char> name;
RelPtr<char> generator;
int32_t version;
int32_t width, height;
RelSlice<TileTypeInfoPOD> tile_types;
RelSlice<SiteInstInfoPOD> sites;
RelSlice<TileInstInfoPOD> tiles;
RelSlice<NodeInfoPOD> nodes;
RelSlice<PackagePOD> packages;
// BEL bucket constids.
RelSlice<int32_t> bel_buckets;
RelPtr<CellMapPOD> cell_map;
RelPtr<ConstantsPOD> constants;
// Constid string data.
RelPtr<RelSlice<RelPtr<char>>> constids;
});
/************************ End of chipdb section. ************************/
inline const TileTypeInfoPOD &tile_info(const ChipInfoPOD *chip_info, int32_t tile)
{
return chip_info->tile_types[chip_info->tiles[tile].type];
}
template <typename Id> const TileTypeInfoPOD &loc_info(const ChipInfoPOD *chip_info, Id &id)
{
return chip_info->tile_types[chip_info->tiles[id.tile].type];
}
NPNR_ALWAYS_INLINE inline const BelInfoPOD &bel_info(const ChipInfoPOD *chip_info, BelId bel)
{
NPNR_ASSERT(bel != BelId());
return loc_info(chip_info, bel).bel_data[bel.index];
}
inline const PipInfoPOD &pip_info(const ChipInfoPOD *chip_info, PipId pip)
{
NPNR_ASSERT(pip != PipId());
return loc_info(chip_info, pip).pip_data[pip.index];
}
inline const SiteInstInfoPOD &site_inst_info(const ChipInfoPOD *chip_info, int32_t tile, int32_t site)
{
return chip_info->sites[chip_info->tiles[tile].sites[site]];
}
NEXTPNR_NAMESPACE_END
#endif /* CHIPDB_H */

View File

@ -122,6 +122,8 @@ struct LutPin
bool operator<(const LutPin &other) const { return max_pin < other.max_pin; }
};
//#define DEBUG_LUT_ROTATION
bool LutMapper::remap_luts(const Context *ctx)
{
std::unordered_map<NetInfo *, LutPin> lut_pin_map;
@ -262,11 +264,25 @@ bool LutMapper::remap_luts(const Context *ctx)
for (size_t bel_pin_idx = 0; bel_pin_idx < lut_bel.pins.size(); ++bel_pin_idx) {
if ((used_pins & (1 << bel_pin_idx)) == 0) {
NPNR_ASSERT(bel_to_cell_pin_remaps[cell_idx][bel_pin_idx] == -1);
cell->lut_cell.vcc_pins.emplace(lut_bel.pins[bel_pin_idx]);
cell->lut_cell.vcc_pins.emplace(lut_bel.pins.at(bel_pin_idx));
}
}
}
#ifdef DEBUG_LUT_ROTATION
log_info("Final mapping:\n");
for (size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) {
CellInfo *cell = cells[cell_idx];
for (auto &cell_pin_pair : cell->cell_bel_pins) {
log_info("%s %s %s =>", cell->type.c_str(ctx), cell->name.c_str(ctx), cell_pin_pair.first.c_str(ctx));
for (auto bel_pin : cell_pin_pair.second) {
log(" %s", bel_pin.c_str(ctx));
}
log("\n");
}
}
#endif
return true;
}

View File

@ -19,9 +19,15 @@
*/
#include "xdc.h"
#include <string>
#include "context.h"
#include "log.h"
#include "nextpnr.h"
// Include tcl.h late because it messed with #define's and lets them leave the
// scope of the header.
#include <tcl.h>
NEXTPNR_NAMESPACE_BEGIN

View File

@ -17,11 +17,17 @@
*
*/
#include <tcl.h>
#include "nextpnr.h"
#ifndef XDC_H
#define XDC_H
#include "nextpnr_namespaces.h"
struct Tcl_Interp;
NEXTPNR_NAMESPACE_BEGIN
struct Context;
struct TclInterp
{
TclInterp(Context *ctx);
@ -31,3 +37,5 @@ struct TclInterp
};
NEXTPNR_NAMESPACE_END
#endif